simplecov 0.19.0 → 0.21.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -73,9 +73,10 @@ Getting started
73
73
  analysis to happen on. When testing a server process (e.g. a JSON API
74
74
  endpoint) via a separate test process (e.g. when using Selenium) where you
75
75
  want to see all code executed by the `rails server`, and not just code
76
- executed in your actual test files, you'll want to add something like this
76
+ executed in your actual test files, you need to require SimpleCov in the
77
+ server process. For rails for instance, you'll want to add something like this
77
78
  to the top of `bin/rails`, but below the "shebang" line (`#! /usr/bin/env
78
- ruby`):
79
+ ruby`) and after config/boot is required:
79
80
 
80
81
  ```ruby
81
82
  if ENV['RAILS_ENV'] == 'test'
@@ -294,7 +295,6 @@ information to be lost.
294
295
  Add branch coverage measurement statistics to your results. Supported in CRuby versions 2.5+.
295
296
 
296
297
  ```ruby
297
- # or in configure or just SimpleCov.enable_coverage :branch
298
298
  SimpleCov.start do
299
299
  enable_coverage :branch
300
300
  end
@@ -340,6 +340,22 @@ Hence, we recommend looking at both metrics together. Branch coverage might also
340
340
  overall metric to look at - while you might be missing only 10% of your lines that might
341
341
  account for 50% of your branches for instance.
342
342
 
343
+ ## Primary Coverage
344
+
345
+ By default, the primary coverage type is `line`. To set the primary coverage to something else, use the following:
346
+
347
+ ```ruby
348
+ # or in configure SimpleCov.primary_coverage :branch
349
+ SimpleCov.start do
350
+ enable_coverage :branch
351
+ primary_coverage :branch
352
+ end
353
+ ```
354
+
355
+ Primary coverage determines what will come in first all output, and the type of coverage to check if you don't specify the type of coverage when customizing exit behavior (`SimpleCov.minimum_coverage 90`).
356
+
357
+ Note that coverage must first be enabled for non-default coverage types.
358
+
343
359
  ## Filters
344
360
 
345
361
  Filters can be used to remove selected files from your coverage data. By default, a filter is applied that removes all
@@ -786,30 +802,36 @@ to help ensure coverage is relatively consistent, rather than being skewed by pa
786
802
 
787
803
  ```ruby
788
804
  SimpleCov.minimum_coverage_by_file 80
805
+ # same as above (the default is to check line coverage by file)
806
+ SimpleCov.minimum_coverage_by_file line: 80
807
+ # check for a minimum line coverage by file of 90% and minimum 80% branch coverage
808
+ SimpleCov.minimum_coverage_by_file line: 90, branch: 80
789
809
  ```
790
810
 
791
- (not yet supported for branch coverage)
792
-
793
811
  ### Maximum coverage drop
794
812
 
795
813
  You can define the maximum coverage drop percentage at once. SimpleCov will return non-zero if exceeded.
796
814
 
797
815
  ```ruby
798
816
  SimpleCov.maximum_coverage_drop 5
817
+ # same as above (the default is to check line drop)
818
+ SimpleCov.maximum_coverage_drop line: 5
819
+ # check for a maximum line drop of 5% and maximum 10% branch drop
820
+ SimpleCov.maximum_coverage_drop line: 5, branch: 10
799
821
  ```
800
822
 
801
- (not yet supported for branch coverage)
802
-
803
823
  ### Refuse dropping coverage
804
824
 
805
825
  You can also entirely refuse dropping coverage between test runs:
806
826
 
807
827
  ```ruby
808
828
  SimpleCov.refuse_coverage_drop
829
+ # same as above (the default is to only refuse line drop)
830
+ SimpleCov.refuse_coverage_drop :line
831
+ # refuse drop for line and branch
832
+ SimpleCov.refuse_coverage_drop :line, :branch
809
833
  ```
810
834
 
811
- (not yet supported for branch coverage)
812
-
813
835
  ## Using your own formatter
814
836
 
815
837
  You can use your own formatter with:
@@ -833,6 +855,20 @@ SimpleCov.formatters = SimpleCov::Formatter::MultiFormatter.new([
833
855
  ])
834
856
  ```
835
857
 
858
+ ## JSON formatter
859
+
860
+ SimpleCov is packaged with a separate gem called [simplecov_json_formatter](https://github.com/codeclimate-community/simplecov_json_formatter) that provides you with a JSON formatter, this formatter could be useful for different use cases, such as for CI consumption or for reporting to external services.
861
+
862
+ In order to use it you will need to manually load the installed gem like so:
863
+
864
+ ```ruby
865
+ require "simplecov_json_formatter"
866
+ SimpleCov.formatter = SimpleCov::Formatter::JSONFormatter
867
+ ```
868
+
869
+ > _Note:_ In case you plan to report your coverage results to CodeClimate services, know that SimpleCov will automatically use the
870
+ > JSON formatter along with the HTML formatter when the `CC_TEST_REPORTER_ID` variable is present in the environment.
871
+
836
872
  ## Available formatters, editor integrations and hosted services
837
873
 
838
874
  * [Open Source formatter and integration plugins for SimpleCov](doc/alternate-formatters.md)
@@ -60,6 +60,11 @@ A formatter that prints the coverage of the file under test when you run a singl
60
60
 
61
61
  t_wada AA formatter for SimpleCov
62
62
 
63
+ #### [simplecov-tailwindcss](https://github.com/chiefpansancolt/simplecov-tailwindcss)
64
+ *by [Chiefpansancolt](https://github.com/chiefpansancolt)*
65
+
66
+ A TailwindCSS & TailwindUI Designed HTML formatter with clean and easy search of files with a tabular left Navigation.
67
+
63
68
  #### [simplecov-material](https://github.com/chiefpansancolt/simplecov-material)
64
69
  *by [Chiefpansancolt](https://github.com/chiefpansancolt)*
65
70
 
@@ -3,7 +3,7 @@
3
3
  require "English"
4
4
 
5
5
  # Coverage may be inaccurate under JRUBY.
6
- if defined?(JRUBY_VERSION) && defined?(JRuby)
6
+ if defined?(JRUBY_VERSION) && defined?(JRuby) && !org.jruby.RubyInstanceConfig.FULL_TRACE_ENABLED
7
7
 
8
8
  # @see https://github.com/jruby/jruby/issues/1196
9
9
  # @see https://github.com/metricfu/metric_fu/pull/226
@@ -11,11 +11,9 @@ if defined?(JRUBY_VERSION) && defined?(JRuby)
11
11
  # @see https://github.com/simplecov-ruby/simplecov/issues/86
12
12
  # @see https://jira.codehaus.org/browse/JRUBY-6106
13
13
 
14
- unless org.jruby.RubyInstanceConfig.FULL_TRACE_ENABLED
15
- warn 'Coverage may be inaccurate; set the "--debug" command line option,' \
16
- ' or do JRUBY_OPTS="--debug"' \
17
- ' or set the "debug.fullTrace=true" option in your .jrubyrc'
18
- end
14
+ warn 'Coverage may be inaccurate; set the "--debug" command line option,' \
15
+ ' or do JRUBY_OPTS="--debug"' \
16
+ ' or set the "debug.fullTrace=true" option in your .jrubyrc'
19
17
  end
20
18
 
21
19
  #
@@ -50,7 +48,9 @@ module SimpleCov
50
48
  def start(profile = nil, &block)
51
49
  require "coverage"
52
50
  initial_setup(profile, &block)
53
- require_relative "./simplecov/process" if SimpleCov.enabled_for_subprocesses?
51
+ require_relative "./simplecov/process" if SimpleCov.enabled_for_subprocesses? &&
52
+ ::Process.respond_to?(:fork)
53
+
54
54
  make_parallel_tests_available
55
55
 
56
56
  @result = nil
@@ -61,6 +61,7 @@ module SimpleCov
61
61
 
62
62
  #
63
63
  # Collate a series of SimpleCov result files into a single SimpleCov output.
64
+ #
64
65
  # You can optionally specify configuration with a block:
65
66
  # SimpleCov.collate Dir["simplecov-resultset-*/.resultset.json"]
66
67
  # OR
@@ -78,18 +79,17 @@ module SimpleCov
78
79
  # available config options, or checkout the README for more in-depth
79
80
  # information about coverage collation
80
81
  #
81
- def collate(result_filenames, profile = nil, &block)
82
- raise "There's no reports to be merged" if result_filenames.empty?
82
+ # By default `collate` ignores the merge_timeout so all results of all files specified will be
83
+ # merged together. If you want to honor the merge_timeout then provide the keyword argument
84
+ # `ignore_timeout: false`.
85
+ #
86
+ def collate(result_filenames, profile = nil, ignore_timeout: true, &block)
87
+ raise "There are no reports to be merged" if result_filenames.empty?
83
88
 
84
89
  initial_setup(profile, &block)
85
90
 
86
- results = result_filenames.flat_map do |filename|
87
- # Re-create each included instance of SimpleCov::Result from the stored run data.
88
- Result.from_hash(JSON.parse(File.read(filename)) || {})
89
- end
90
-
91
91
  # Use the ResultMerger to produce a single, merged result, ready to use.
92
- @result = ResultMerger.merge_and_store(*results)
92
+ @result = ResultMerger.merge_and_store(*result_filenames, ignore_timeout: ignore_timeout)
93
93
 
94
94
  run_exit_tasks!
95
95
  end
@@ -283,7 +283,10 @@ module SimpleCov
283
283
  # @api private
284
284
  #
285
285
  def write_last_run(result)
286
- SimpleCov::LastRun.write(result: {covered_percent: round_coverage(result.covered_percent)})
286
+ SimpleCov::LastRun.write(result:
287
+ result.coverage_statistics.transform_values do |stats|
288
+ round_coverage(stats.percent)
289
+ end)
287
290
  end
288
291
 
289
292
  #
@@ -10,7 +10,7 @@ module SimpleCov
10
10
  # defined here are usable from SimpleCov directly. Please check out
11
11
  # SimpleCov documentation for further info.
12
12
  #
13
- module Configuration # rubocop:disable Metrics/ModuleLength
13
+ module Configuration
14
14
  attr_writer :filters, :groups, :formatter, :print_error_status
15
15
 
16
16
  #
@@ -22,6 +22,7 @@ module SimpleCov
22
22
  def root(root = nil)
23
23
  return @root if defined?(@root) && root.nil?
24
24
 
25
+ @coverage_path = nil # invalidate cache
25
26
  @root = File.expand_path(root || Dir.getwd)
26
27
  end
27
28
 
@@ -190,7 +191,7 @@ module SimpleCov
190
191
  # end
191
192
  #
192
193
  def at_exit(&block)
193
- return proc {} unless running || block_given?
194
+ return Proc.new unless running || block_given?
194
195
 
195
196
  @at_exit = block if block_given?
196
197
  @at_exit ||= proc { SimpleCov.result.format! }
@@ -199,8 +200,9 @@ module SimpleCov
199
200
  # gets or sets the enabled_for_subprocess configuration
200
201
  # when true, this will inject SimpleCov code into Process.fork
201
202
  def enable_for_subprocesses(value = nil)
202
- @enable_for_subprocesses = value unless value.nil?
203
- @enable_for_subprocesses || false
203
+ return @enable_for_subprocesses if defined?(@enable_for_subprocesses) && value.nil?
204
+
205
+ @enable_for_subprocesses = value || false
204
206
  end
205
207
 
206
208
  # gets the enabled_for_subprocess configuration
@@ -285,20 +287,22 @@ module SimpleCov
285
287
  #
286
288
  # Default is 0% (disabled)
287
289
  #
288
-
289
- # rubocop:disable Metrics/CyclomaticComplexity
290
290
  def minimum_coverage(coverage = nil)
291
291
  return @minimum_coverage ||= {} unless coverage
292
292
 
293
- coverage = {DEFAULT_COVERAGE_CRITERION => coverage} if coverage.is_a?(Numeric)
293
+ coverage = {primary_coverage => coverage} if coverage.is_a?(Numeric)
294
+
295
+ raise_on_invalid_coverage(coverage, "minimum_coverage")
296
+
297
+ @minimum_coverage = coverage
298
+ end
299
+
300
+ def raise_on_invalid_coverage(coverage, coverage_setting)
294
301
  coverage.each_key { |criterion| raise_if_criterion_disabled(criterion) }
295
302
  coverage.each_value do |percent|
296
- minimum_possible_coverage_exceeded("minimum_coverage") if percent && percent > 100
303
+ minimum_possible_coverage_exceeded(coverage_setting) if percent && percent > 100
297
304
  end
298
-
299
- @minimum_coverage = coverage
300
305
  end
301
- # rubocop:enable Metrics/CyclomaticComplexity
302
306
 
303
307
  #
304
308
  # Defines the maximum coverage drop at once allowed for the testsuite to pass.
@@ -307,7 +311,13 @@ module SimpleCov
307
311
  # Default is 100% (disabled)
308
312
  #
309
313
  def maximum_coverage_drop(coverage_drop = nil)
310
- @maximum_coverage_drop ||= (coverage_drop || 100).to_f.round(2)
314
+ return @maximum_coverage_drop ||= {} unless coverage_drop
315
+
316
+ coverage_drop = {primary_coverage => coverage_drop} if coverage_drop.is_a?(Numeric)
317
+
318
+ raise_on_invalid_coverage(coverage_drop, "maximum_coverage_drop")
319
+
320
+ @maximum_coverage_drop = coverage_drop
311
321
  end
312
322
 
313
323
  #
@@ -318,16 +328,23 @@ module SimpleCov
318
328
  # Default is 0% (disabled)
319
329
  #
320
330
  def minimum_coverage_by_file(coverage = nil)
321
- minimum_possible_coverage_exceeded("minimum_coverage_by_file") if coverage && coverage > 100
322
- @minimum_coverage_by_file ||= (coverage || 0).to_f.round(2)
331
+ return @minimum_coverage_by_file ||= {} unless coverage
332
+
333
+ coverage = {primary_coverage => coverage} if coverage.is_a?(Numeric)
334
+
335
+ raise_on_invalid_coverage(coverage, "minimum_coverage_by_file")
336
+
337
+ @minimum_coverage_by_file = coverage
323
338
  end
324
339
 
325
340
  #
326
341
  # Refuses any coverage drop. That is, coverage is only allowed to increase.
327
342
  # SimpleCov will return non-zero if the coverage decreases.
328
343
  #
329
- def refuse_coverage_drop
330
- maximum_coverage_drop 0
344
+ def refuse_coverage_drop(*criteria)
345
+ criteria = coverage_criteria if criteria.empty?
346
+
347
+ maximum_coverage_drop(criteria.map { |c| [c, 0] }.to_h)
331
348
  end
332
349
 
333
350
  #
@@ -374,7 +391,7 @@ module SimpleCov
374
391
  # @param [Symbol] criterion
375
392
  #
376
393
  def coverage_criterion(criterion = nil)
377
- return @coverage_criterion ||= DEFAULT_COVERAGE_CRITERION unless criterion
394
+ return @coverage_criterion ||= primary_coverage unless criterion
378
395
 
379
396
  raise_if_criterion_unsupported(criterion)
380
397
 
@@ -387,8 +404,17 @@ module SimpleCov
387
404
  coverage_criteria << criterion
388
405
  end
389
406
 
407
+ def primary_coverage(criterion = nil)
408
+ if criterion.nil?
409
+ @primary_coverage ||= DEFAULT_COVERAGE_CRITERION
410
+ else
411
+ raise_if_criterion_disabled(criterion)
412
+ @primary_coverage = criterion
413
+ end
414
+ end
415
+
390
416
  def coverage_criteria
391
- @coverage_criteria ||= Set[DEFAULT_COVERAGE_CRITERION]
417
+ @coverage_criteria ||= Set[primary_coverage]
392
418
  end
393
419
 
394
420
  def coverage_criterion_enabled?(criterion)
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "simplecov-html"
4
+ module SimpleCov
5
+ module Formatter
6
+ class << self
7
+ def from_env(env)
8
+ formatters = [SimpleCov::Formatter::HTMLFormatter]
9
+
10
+ # When running under a CI that uses CodeClimate, JSON output is expected
11
+ if env.fetch("CC_TEST_REPORTER_ID", nil)
12
+ require "simplecov_json_formatter"
13
+ formatters.push(SimpleCov::Formatter::JSONFormatter)
14
+ end
15
+
16
+ formatters
17
+ end
18
+ end
19
+ end
20
+ end
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Load default formatter gem
4
- require "simplecov-html"
5
4
  require "pathname"
5
+ require_relative "default_formatter"
6
6
  require_relative "profiles/root_filter"
7
7
  require_relative "profiles/test_frameworks"
8
8
  require_relative "profiles/bundler_filter"
@@ -11,7 +11,10 @@ require_relative "profiles/rails"
11
11
 
12
12
  # Default configuration
13
13
  SimpleCov.configure do
14
- formatter SimpleCov::Formatter::HTMLFormatter
14
+ formatter SimpleCov::Formatter::MultiFormatter.new(
15
+ SimpleCov::Formatter.from_env(ENV)
16
+ )
17
+
15
18
  load_profile "bundler_filter"
16
19
  load_profile "hidden_filter"
17
20
  # Exclude files outside of SimpleCov.root
@@ -11,15 +11,18 @@ module SimpleCov
11
11
  def failing?
12
12
  return false unless maximum_coverage_drop && last_run
13
13
 
14
- coverage_diff > maximum_coverage_drop
14
+ coverage_drop_violations.any?
15
15
  end
16
16
 
17
17
  def report
18
- $stderr.printf(
19
- "Coverage has dropped by %<drop_percent>.2f%% since the last time (maximum allowed: %<max_drop>.2f%%).\n",
20
- drop_percent: coverage_diff,
21
- max_drop: maximum_coverage_drop
22
- )
18
+ coverage_drop_violations.each do |violation|
19
+ $stderr.printf(
20
+ "%<criterion>s coverage has dropped by %<drop_percent>.2f%% since the last time (maximum allowed: %<max_drop>.2f%%).\n",
21
+ criterion: violation[:criterion].capitalize,
22
+ drop_percent: SimpleCov.round_coverage(violation[:drop_percent]),
23
+ max_drop: violation[:max_drop]
24
+ )
25
+ end
23
26
  end
24
27
 
25
28
  def exit_code
@@ -36,14 +39,44 @@ module SimpleCov
36
39
  @last_run = SimpleCov::LastRun.read
37
40
  end
38
41
 
39
- def coverage_diff
40
- raise "Trying to access coverage_diff although there is no last run" unless last_run
42
+ def coverage_drop_violations
43
+ @coverage_drop_violations ||=
44
+ compute_coverage_drop_data.select do |achieved|
45
+ achieved.fetch(:max_drop) < achieved.fetch(:drop_percent)
46
+ end
47
+ end
48
+
49
+ def compute_coverage_drop_data
50
+ maximum_coverage_drop.map do |criterion, percent|
51
+ {
52
+ criterion: criterion,
53
+ max_drop: percent,
54
+ drop_percent: drop_percent(criterion)
55
+ }
56
+ end
57
+ end
41
58
 
42
- @coverage_diff ||= last_run[:result][:covered_percent] - covered_percent
59
+ # if anyone says "max_coverage_drop 0.000000000000000001" I appologize. Please don't.
60
+ MAX_DROP_ACCURACY = 10
61
+ def drop_percent(criterion)
62
+ drop = last_coverage(criterion) -
63
+ SimpleCov.round_coverage(
64
+ result.coverage_statistics.fetch(criterion).percent
65
+ )
66
+
67
+ # floats, I tell ya.
68
+ # irb(main):001:0* 80.01 - 80.0
69
+ # => 0.010000000000005116
70
+ drop.floor(MAX_DROP_ACCURACY)
43
71
  end
44
72
 
45
- def covered_percent
46
- SimpleCov.round_coverage(result.covered_percent)
73
+ def last_coverage(criterion)
74
+ last_coverage_percent = last_run[:result][criterion]
75
+
76
+ # fallback for old file format
77
+ last_coverage_percent = last_run[:result][:covered_percent] if !last_coverage_percent && criterion == :line
78
+
79
+ last_coverage_percent || 0
47
80
  end
48
81
  end
49
82
  end