simplecov 0.6.2 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -14,7 +14,7 @@ module SimpleCov::Configuration
14
14
  # Configure with SimpleCov.root('/my/project/path')
15
15
  #
16
16
  def root(root=nil)
17
- return @root if @root and root.nil?
17
+ return @root if defined? @root and root.nil?
18
18
  @root = File.expand_path(root || Dir.getwd)
19
19
  end
20
20
 
@@ -24,7 +24,7 @@ module SimpleCov::Configuration
24
24
  # Configure with SimpleCov.coverage_dir('cov')
25
25
  #
26
26
  def coverage_dir(dir=nil)
27
- return @coverage_dir if @coverage_dir and dir.nil?
27
+ return @coverage_dir if defined? @coverage_dir and dir.nil?
28
28
  @coverage_dir = (dir || 'coverage')
29
29
  end
30
30
 
@@ -60,14 +60,14 @@ module SimpleCov::Configuration
60
60
  @name ||= SimpleCov::CommandGuesser.guess
61
61
  @name
62
62
  end
63
-
63
+
64
64
  #
65
65
  # Gets or sets the configured formatter.
66
66
  #
67
67
  # Configure with: SimpleCov.formatter(SimpleCov::Formatter::SimpleFormatter)
68
68
  #
69
69
  def formatter(formatter=nil)
70
- return @formatter if @formatter and formatter.nil?
70
+ return @formatter if defined? @formatter and formatter.nil?
71
71
  @formatter = formatter
72
72
  raise "No formatter configured. Please specify a formatter using SimpleCov.formatter = SimpleCov::Formatter::SimpleFormatter" unless @formatter
73
73
  @formatter
@@ -81,7 +81,7 @@ module SimpleCov::Configuration
81
81
  # Configure with SimpleCov.nocov_token('skip') or it's alias SimpleCov.skip_token('skip')
82
82
  #
83
83
  def nocov_token(nocov_token=nil)
84
- return @nocov_token if @nocov_token and nocov_token.nil?
84
+ return @nocov_token if defined? @nocov_token and nocov_token.nil?
85
85
  @nocov_token = (nocov_token || 'nocov')
86
86
  end
87
87
  alias_method :skip_token, :nocov_token
@@ -108,7 +108,7 @@ module SimpleCov::Configuration
108
108
  # add_filter 'foobar'
109
109
  # end
110
110
  #
111
- # This is equivalent to SimpleCov.add_filter 'foobar' and thus makes it easier to set a buchn of configure
111
+ # This is equivalent to SimpleCov.add_filter 'foobar' and thus makes it easier to set a bunch of configure
112
112
  # options at once.
113
113
  #
114
114
  def configure(&block)
@@ -138,7 +138,7 @@ module SimpleCov::Configuration
138
138
  # the SimpleCov.root is this.
139
139
  #
140
140
  def project_name(new_name=nil)
141
- return @project_name if @project_name and new_name.nil?
141
+ return @project_name if defined? @project_name and @project_name and new_name.nil?
142
142
  @project_name = new_name if new_name.kind_of?(String)
143
143
  @project_name ||= File.basename(root.split('/').last).capitalize.gsub('_', ' ')
144
144
  end
@@ -148,8 +148,8 @@ module SimpleCov::Configuration
148
148
  # are joined and combined into a single coverage report
149
149
  #
150
150
  def use_merging(use=nil)
151
- @use_merging = use unless use.nil? # Set if param given
152
- @use_merging = true if @use_merging != false
151
+ @use_merging = use unless use.nil?
152
+ @use_merging = true unless defined? @use_merging and @use_merging == false
153
153
  end
154
154
 
155
155
  #
@@ -165,10 +165,38 @@ module SimpleCov::Configuration
165
165
  # Configure with SimpleCov.merge_timeout(3600) # 1hr
166
166
  #
167
167
  def merge_timeout(seconds=nil)
168
- @merge_timeout = seconds if !seconds.nil? and seconds.kind_of?(Fixnum)
168
+ @merge_timeout = seconds if seconds.kind_of?(Fixnum)
169
169
  @merge_timeout ||= 600
170
170
  end
171
171
 
172
+ #
173
+ # Defines the minimum overall coverage required for the testsuite to pass.
174
+ # SimpleCov will return non-zero if the current coverage is below this threshold.
175
+ #
176
+ # Default is 0% (disabled)
177
+ #
178
+ def minimum_coverage(coverage=nil)
179
+ @minimum_coverage ||= (coverage || 0).to_f.round(2)
180
+ end
181
+
182
+ #
183
+ # Defines the maximum coverage drop at once allowed for the testsuite to pass.
184
+ # SimpleCov will return non-zero if the coverage decreases by more than this threshold.
185
+ #
186
+ # Default is 100% (disabled)
187
+ #
188
+ def maximum_coverage_drop(coverage_drop=nil)
189
+ @maximum_coverage_drop ||= (coverage_drop || 100).to_f.round(2)
190
+ end
191
+
192
+ #
193
+ # Refuses any coverage drop. That is, coverage is only allowed to increase.
194
+ # SimpleCov will return non-zero if the coverage decreases.
195
+ #
196
+ def refuse_coverage_drop
197
+ maximum_coverage_drop 0
198
+ end
199
+
172
200
  #
173
201
  # Add a filter to the processing chain.
174
202
  # There are three ways to define a filter:
@@ -195,7 +223,7 @@ module SimpleCov::Configuration
195
223
  def add_group(group_name, filter_argument=nil, &filter_proc)
196
224
  groups[group_name] = parse_filter(filter_argument, &filter_proc)
197
225
  end
198
-
226
+
199
227
  private
200
228
 
201
229
  #
@@ -46,9 +46,38 @@ at_exit do
46
46
  #if it was a SystemExit, use the accompanying status
47
47
  #otherwise set a non-zero status representing termination by some other exception
48
48
  #(see github issue 41)
49
- @exit_status = $!.is_a?(SystemExit) ? $!.status : 1
49
+ @exit_status = $!.is_a?(SystemExit) ? $!.status : SimpleCov::ExitCodes::EXCEPTION
50
50
  end
51
+
51
52
  SimpleCov.at_exit.call
53
+
54
+ if SimpleCov.result? # Result has been computed
55
+ covered_percent = SimpleCov.result.covered_percent.round(2)
56
+
57
+ if @exit_status.to_i == 0 # No other errors
58
+ @exit_status = if covered_percent < SimpleCov.minimum_coverage
59
+ $stderr.puts "Coverage (%.2f%%) is below the expected minimum coverage (%.2f%%)." % \
60
+ [covered_percent, SimpleCov.minimum_coverage]
61
+
62
+ SimpleCov::ExitCodes::MINIMUM_COVERAGE
63
+
64
+ elsif (last_run = SimpleCov::LastRun.read)
65
+ diff = last_run['result']['covered_percent'] - covered_percent
66
+ if diff > SimpleCov.maximum_coverage_drop
67
+ $stderr.puts "Coverage has dropped by %.2f%% since the last time (maximum allowed: %.2f%%)." % \
68
+ [diff, SimpleCov.maximum_coverage_drop]
69
+
70
+ SimpleCov::ExitCodes::MAXIMUM_COVERAGE_DROP
71
+ end
72
+ end
73
+ end
74
+
75
+ metrics = {
76
+ :result => { :covered_percent => covered_percent }
77
+ }
78
+ SimpleCov::LastRun.write(metrics)
79
+ end
80
+
52
81
  exit @exit_status if @exit_status # Force exit with stored status (see github issue #5)
53
82
  end
54
83
 
@@ -0,0 +1,5 @@
1
+ module SimpleCov::ExitCodes
2
+ EXCEPTION = 1
3
+ MINIMUM_COVERAGE = 2
4
+ MAXIMUM_COVERAGE_DROP = 3
5
+ end
@@ -0,0 +1,25 @@
1
+ class SimpleCov::Formatter::MultiFormatter
2
+ def self.[](*args)
3
+ Class.new(self) do
4
+ define_method :formatters do
5
+ @formatters ||= args
6
+ end
7
+ end
8
+ end
9
+
10
+ def format(result)
11
+ formatters.map do |formatter|
12
+ begin
13
+ formatter.new.format(result)
14
+ rescue => e
15
+ STDERR.puts("Formatter #{formatter} failed with #{e.class}: #{e.message} (#{e.backtrace.first})")
16
+ nil
17
+ end
18
+ end
19
+ end
20
+
21
+ def formatters
22
+ @formatters ||= []
23
+ end
24
+
25
+ end
@@ -5,3 +5,4 @@ module SimpleCov
5
5
  end
6
6
 
7
7
  require 'simplecov/formatter/simple_formatter'
8
+ require 'simplecov/formatter/multi_formatter'
@@ -0,0 +1,27 @@
1
+ require 'multi_json'
2
+
3
+ module SimpleCov::JSON
4
+ class << self
5
+ def parse(json)
6
+ # Detect and use available MultiJson API - it changed in v1.3
7
+ if MultiJson.respond_to?(:adapter)
8
+ MultiJson.load(json)
9
+ else
10
+ MultiJson.decode(json)
11
+ end
12
+ end
13
+
14
+ def dump(string)
15
+ if defined? ::JSON
16
+ ::JSON.pretty_generate(string)
17
+ else
18
+ # Detect and use available MultiJson API - it changed in v1.3
19
+ if MultiJson.respond_to?(:adapter)
20
+ MultiJson.dump(string)
21
+ else
22
+ MultiJson.encode(string)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,20 @@
1
+ module SimpleCov::LastRun
2
+ class << self
3
+ def last_run_path
4
+ File.join(SimpleCov.coverage_path, '.last_run.json')
5
+ end
6
+
7
+ def read
8
+ return nil unless File.exist?(last_run_path)
9
+
10
+ SimpleCov::JSON.parse(File.read(last_run_path))
11
+ end
12
+
13
+ def write(json)
14
+ File.open(last_run_path, "w+") do |f|
15
+ f.puts SimpleCov::JSON.dump(json)
16
+ end
17
+ end
18
+ end
19
+ end
20
+
@@ -44,6 +44,7 @@ module SimpleCov
44
44
 
45
45
  # The multiple of coverage for this result
46
46
  def covered_strength
47
+ return 0 if total_lines.zero?
47
48
  return @covered_strength if @covered_strength
48
49
  m = 0
49
50
  @files.each do |file|
@@ -58,7 +59,7 @@ module SimpleCov
58
59
 
59
60
  # Returns the count of lines that are covered
60
61
  def covered_lines
61
- return @covered_lines if @covered_lines
62
+ return @covered_lines if defined? @covered_lines
62
63
  @covered_lines = 0
63
64
  @files.each do |file|
64
65
  original_result[file.filename].each do |line_result|
@@ -70,7 +71,7 @@ module SimpleCov
70
71
 
71
72
  # Returns the count of missed lines
72
73
  def missed_lines
73
- return @missed_lines if @missed_lines
74
+ return @missed_lines if defined? @missed_lines
74
75
  @missed_lines = 0
75
76
  @files.each do |file|
76
77
  original_result[file.filename].each do |line_result|
@@ -1,5 +1,3 @@
1
- require 'multi_json'
2
-
3
1
  #
4
2
  # Singleton that is responsible for caching, loading and merging
5
3
  # SimpleCov::Results into a single result for coverage analysis based
@@ -7,7 +5,7 @@ require 'multi_json'
7
5
  #
8
6
  module SimpleCov::ResultMerger
9
7
  class << self
10
- # The path to the resultset.yml cache file
8
+ # The path to the .resultset.json cache file
11
9
  def resultset_path
12
10
  File.join(SimpleCov.coverage_path, '.resultset.json')
13
11
  end
@@ -15,7 +13,7 @@ module SimpleCov::ResultMerger
15
13
  # Loads the cached resultset from YAML and returns it as a Hash
16
14
  def resultset
17
15
  if stored_data
18
- MultiJson.load(stored_data)
16
+ SimpleCov::JSON.parse(stored_data)
19
17
  else
20
18
  {}
21
19
  end
@@ -68,11 +66,7 @@ module SimpleCov::ResultMerger
68
66
  command_name, data = result.to_hash.first
69
67
  new_set[command_name] = data
70
68
  File.open(resultset_path, "w+") do |f|
71
- if defined? ::JSON
72
- f.puts JSON.pretty_generate(new_set)
73
- else
74
- f.puts MultiJson.dump(new_set)
75
- end
69
+ f.puts SimpleCov::JSON.dump(new_set)
76
70
  end
77
71
  true
78
72
  end
@@ -30,6 +30,7 @@ module SimpleCov
30
30
  raise ArgumentError, "Only Fixnum and nil accepted for coverage" unless coverage.kind_of?(Fixnum) or coverage.nil?
31
31
  @src, @line_number, @coverage = src, line_number, coverage
32
32
  @skipped = false
33
+ @src.encode!('UTF-8', 'UTF-8', :invalid => :replace) if @src.respond_to?(:encode!)
33
34
  end
34
35
 
35
36
  # Returns true if this is a line that should have been covered, but was not
@@ -84,7 +85,7 @@ module SimpleCov
84
85
  # Returns all source lines for this file as instances of SimpleCov::SourceFile::Line,
85
86
  # and thus including coverage data. Aliased as :source_lines
86
87
  def lines
87
- return @lines unless @lines.nil?
88
+ return @lines if defined? @lines
88
89
 
89
90
  # Warning to identify condition from Issue #56
90
91
  if coverage.size > src.size
@@ -109,18 +110,30 @@ module SimpleCov
109
110
  # The coverage for this file in percent. 0 if the file has no relevant lines
110
111
  def covered_percent
111
112
  return 100.0 if lines.length == 0 or lines.length == never_lines.count
112
- (covered_lines.count) * 100 / (lines.count - never_lines.count - skipped_lines.count).to_f
113
+ relevant_lines = lines.count - never_lines.count - skipped_lines.count
114
+ if relevant_lines == 0
115
+ 0
116
+ else
117
+ (covered_lines.count) * 100 / relevant_lines.to_f
118
+ end
113
119
  end
114
120
 
115
121
  def covered_strength
116
122
  return 0 if lines.length == 0 or lines.length == never_lines.count
123
+
117
124
  lines_strength = 0
118
125
  lines.each do |c|
119
126
  lines_strength += c.coverage if c.coverage
120
127
  end
128
+
121
129
  effective_lines_count = (lines.count - never_lines.count - skipped_lines.count).to_f
122
- strength = lines_strength / effective_lines_count
123
- round_float(strength, 1)
130
+
131
+ if effective_lines_count == 0
132
+ 0
133
+ else
134
+ strength = lines_strength / effective_lines_count
135
+ round_float(strength, 1)
136
+ end
124
137
  end
125
138
 
126
139
  # Returns all covered lines as SimpleCov::SourceFile::Line
@@ -172,3 +185,4 @@ module SimpleCov
172
185
  end
173
186
  end
174
187
  end
188
+
@@ -1,3 +1,3 @@
1
1
  module SimpleCov
2
- VERSION = "0.6.2"
2
+ VERSION = "0.7.0"
3
3
  end
data/lib/simplecov.rb CHANGED
@@ -48,12 +48,20 @@ module SimpleCov
48
48
  SimpleCov::ResultMerger.store_result(@result) if @result
49
49
  return SimpleCov::ResultMerger.merged_result
50
50
  else
51
- return @result
51
+ return @result if defined? @result
52
52
  end
53
53
  ensure
54
54
  self.running = false
55
55
  end
56
56
 
57
+ #
58
+ # Returns nil if the result has not been computed
59
+ # Otherwise, returns the result
60
+ #
61
+ def result?
62
+ defined? @result and @result
63
+ end
64
+
57
65
  #
58
66
  # Applies the configured filters to the given array of SimpleCov::SourceFile items
59
67
  #
@@ -105,12 +113,15 @@ end
105
113
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__)))
106
114
  require 'simplecov/configuration'
107
115
  SimpleCov.send :extend, SimpleCov::Configuration
116
+ require 'simplecov/exit_codes'
117
+ require 'simplecov/json'
108
118
  require 'simplecov/adapters'
109
119
  require 'simplecov/source_file'
110
120
  require 'simplecov/file_list'
111
121
  require 'simplecov/result'
112
122
  require 'simplecov/filter'
113
123
  require 'simplecov/formatter'
124
+ require 'simplecov/last_run'
114
125
  require 'simplecov/merge_helpers'
115
126
  require 'simplecov/result_merger'
116
127
  require 'simplecov/command_guesser'
data/simplecov.gemspec CHANGED
@@ -12,10 +12,11 @@ Gem::Specification.new do |gem|
12
12
  gem.description = %Q{Code coverage for Ruby 1.9 with a powerful configuration library and automatic merging of coverage across test suites}
13
13
  gem.summary = gem.description
14
14
 
15
- gem.add_dependency 'multi_json', '~> 1.3'
16
- gem.add_dependency 'simplecov-html', '~> 0.5.3'
15
+ gem.add_dependency 'multi_json', '~> 1.0'
16
+ gem.add_dependency 'simplecov-html', '~> 0.7.0'
17
17
  gem.add_development_dependency 'aruba'
18
18
  gem.add_development_dependency 'capybara'
19
+ gem.add_development_dependency 'appraisal'
19
20
  gem.add_development_dependency 'cucumber', '>= 1.1.4'
20
21
  gem.add_development_dependency 'rake'
21
22
  gem.add_development_dependency 'rspec'
@@ -0,0 +1,3 @@
1
+
2
+ # localized to Espa�ol thus:
3
+
@@ -10,7 +10,7 @@ class Foo
10
10
 
11
11
  #:nocov:
12
12
  def skipped
13
- @foo * 2
13
+ @foo * 2
14
14
  end
15
15
  #:nocov:
16
16
  end
data/test/test_result.rb CHANGED
@@ -21,7 +21,7 @@ class TestResult < Test::Unit::TestCase
21
21
 
22
22
  should "have 3 source files" do
23
23
  assert_equal 3, @result.source_files.count
24
- assert @result.source_files.all? {|s| s.instance_of?(SimpleCov::SourceFile)}, "Not alL instances are of SimpleCov::SourceFile type"
24
+ assert @result.source_files.all? {|s| s.instance_of?(SimpleCov::SourceFile)}, "Not all instances are of SimpleCov::SourceFile type"
25
25
  end
26
26
 
27
27
  should "return an instance of SimpleCov::FileList for source_files and files" do
@@ -111,6 +111,21 @@ class TestResult < Test::Unit::TestCase
111
111
  assert_equal String, @result.format!.class
112
112
  end
113
113
  end
114
+
115
+ context "and multi formatter being used" do
116
+ setup do
117
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
118
+ SimpleCov::Formatter::SimpleFormatter,
119
+ SimpleCov::Formatter::SimpleFormatter,
120
+ ]
121
+ end
122
+
123
+ should "return an array containing formatted string with result.format!" do
124
+ formated = @result.format!
125
+ assert_equal 2, formated.count
126
+ assert_equal String, formated.first.class
127
+ end
128
+ end
114
129
  end
115
130
 
116
131
  context "with groups set up that do not match all files" do
@@ -68,7 +68,7 @@ class TestSourceFile < Test::Unit::TestCase
68
68
  @source_file.lines
69
69
  end
70
70
 
71
- assert_match /^Warning: coverage data provided/, captured_output
71
+ assert_match(/^Warning: coverage data provided/, captured_output)
72
72
  end
73
73
  end
74
74
 
@@ -80,7 +80,16 @@ class TestSourceFile < Test::Unit::TestCase
80
80
  source_file.process_skipped_lines!
81
81
  end
82
82
  end
83
+
84
+ should "handle iso-8859 encoded source files" do
85
+ source_file = SimpleCov::SourceFile.new(source_fixture('iso-8859.rb'), [nil, nil, 1])
86
+
87
+ assert_nothing_raised do
88
+ source_file.process_skipped_lines!
89
+ end
90
+ end
83
91
  end
84
92
 
85
93
  end
86
94
  end
95
+