simplecov 0.6.0 → 0.22.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (131) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +163 -80
  3. data/LICENSE +1 -1
  4. data/README.md +776 -277
  5. data/doc/alternate-formatters.md +71 -0
  6. data/doc/commercial-services.md +25 -0
  7. data/doc/editor-integration.md +18 -0
  8. data/lib/minitest/simplecov_plugin.rb +15 -0
  9. data/lib/simplecov/combine/branches_combiner.rb +32 -0
  10. data/lib/simplecov/combine/files_combiner.rb +24 -0
  11. data/lib/simplecov/combine/lines_combiner.rb +43 -0
  12. data/lib/simplecov/combine/results_combiner.rb +60 -0
  13. data/lib/simplecov/combine.rb +30 -0
  14. data/lib/simplecov/command_guesser.rb +53 -38
  15. data/lib/simplecov/configuration.rb +478 -193
  16. data/lib/simplecov/coverage_statistics.rb +56 -0
  17. data/lib/simplecov/default_formatter.rb +20 -0
  18. data/lib/simplecov/defaults.rb +40 -44
  19. data/lib/simplecov/exit_codes/exit_code_handling.rb +29 -0
  20. data/lib/simplecov/exit_codes/maximum_coverage_drop_check.rb +83 -0
  21. data/lib/simplecov/exit_codes/minimum_coverage_by_file_check.rb +54 -0
  22. data/lib/simplecov/exit_codes/minimum_overall_coverage_check.rb +53 -0
  23. data/lib/simplecov/exit_codes.rb +15 -0
  24. data/lib/simplecov/file_list.rb +112 -36
  25. data/lib/simplecov/filter.rb +54 -4
  26. data/lib/simplecov/formatter/multi_formatter.rb +32 -0
  27. data/lib/simplecov/formatter/simple_formatter.rb +21 -15
  28. data/lib/simplecov/formatter.rb +4 -1
  29. data/lib/simplecov/last_run.rb +28 -0
  30. data/lib/simplecov/lines_classifier.rb +48 -0
  31. data/lib/simplecov/load_global_config.rb +8 -0
  32. data/lib/simplecov/no_defaults.rb +4 -0
  33. data/lib/simplecov/process.rb +19 -0
  34. data/lib/simplecov/profiles/bundler_filter.rb +5 -0
  35. data/lib/simplecov/profiles/hidden_filter.rb +5 -0
  36. data/lib/simplecov/profiles/rails.rb +18 -0
  37. data/lib/simplecov/profiles/root_filter.rb +10 -0
  38. data/lib/simplecov/profiles/test_frameworks.rb +8 -0
  39. data/lib/simplecov/profiles.rb +35 -0
  40. data/lib/simplecov/result.rb +33 -64
  41. data/lib/simplecov/result_adapter.rb +30 -0
  42. data/lib/simplecov/result_merger.rb +178 -64
  43. data/lib/simplecov/simulate_coverage.rb +29 -0
  44. data/lib/simplecov/source_file/branch.rb +84 -0
  45. data/lib/simplecov/source_file/line.rb +72 -0
  46. data/lib/simplecov/source_file.rb +304 -123
  47. data/lib/simplecov/useless_results_remover.rb +18 -0
  48. data/lib/simplecov/version.rb +4 -2
  49. data/lib/simplecov.rb +396 -49
  50. metadata +81 -242
  51. data/.gitignore +0 -30
  52. data/.rvmrc +0 -1
  53. data/.travis.yml +0 -13
  54. data/Gemfile +0 -9
  55. data/Rakefile +0 -16
  56. data/cucumber.yml +0 -13
  57. data/features/config_adapters.feature +0 -44
  58. data/features/config_autoload.feature +0 -46
  59. data/features/config_command_name.feature +0 -33
  60. data/features/config_coverage_dir.feature +0 -20
  61. data/features/config_deactivate_merging.feature +0 -42
  62. data/features/config_merge_timeout.feature +0 -38
  63. data/features/config_nocov_token.feature +0 -79
  64. data/features/config_project_name.feature +0 -27
  65. data/features/config_styles.feature +0 -93
  66. data/features/cucumber_basic.feature +0 -29
  67. data/features/merging_test_unit_and_rspec.feature +0 -44
  68. data/features/rspec_basic.feature +0 -31
  69. data/features/rspec_fails_on_initialization.feature +0 -14
  70. data/features/rspec_groups_and_filters_basic.feature +0 -29
  71. data/features/rspec_groups_and_filters_complex.feature +0 -35
  72. data/features/rspec_groups_using_filter_class.feature +0 -40
  73. data/features/rspec_without_simplecov.feature +0 -20
  74. data/features/skipping_code_blocks_manually.feature +0 -70
  75. data/features/step_definitions/html_steps.rb +0 -45
  76. data/features/step_definitions/simplecov_steps.rb +0 -61
  77. data/features/step_definitions/transformers.rb +0 -13
  78. data/features/step_definitions/web_steps.rb +0 -64
  79. data/features/support/env.rb +0 -26
  80. data/features/test_unit_basic.feature +0 -34
  81. data/features/test_unit_groups_and_filters_basic.feature +0 -29
  82. data/features/test_unit_groups_and_filters_complex.feature +0 -35
  83. data/features/test_unit_groups_using_filter_class.feature +0 -40
  84. data/features/test_unit_without_simplecov.feature +0 -20
  85. data/features/unicode_compatiblity.feature +0 -67
  86. data/lib/simplecov/adapters.rb +0 -29
  87. data/lib/simplecov/merge_helpers.rb +0 -39
  88. data/lib/simplecov/railtie.rb +0 -7
  89. data/lib/simplecov/railties/tasks.rake +0 -11
  90. data/simplecov.gemspec +0 -28
  91. data/test/faked_project/Gemfile +0 -6
  92. data/test/faked_project/Rakefile +0 -8
  93. data/test/faked_project/cucumber.yml +0 -13
  94. data/test/faked_project/features/step_definitions/my_steps.rb +0 -23
  95. data/test/faked_project/features/support/env.rb +0 -12
  96. data/test/faked_project/features/test_stuff.feature +0 -6
  97. data/test/faked_project/lib/faked_project/framework_specific.rb +0 -18
  98. data/test/faked_project/lib/faked_project/meta_magic.rb +0 -24
  99. data/test/faked_project/lib/faked_project/some_class.rb +0 -29
  100. data/test/faked_project/lib/faked_project.rb +0 -11
  101. data/test/faked_project/spec/faked_spec.rb +0 -11
  102. data/test/faked_project/spec/meta_magic_spec.rb +0 -10
  103. data/test/faked_project/spec/some_class_spec.rb +0 -10
  104. data/test/faked_project/spec/spec_helper.rb +0 -15
  105. data/test/faked_project/test/faked_test.rb +0 -11
  106. data/test/faked_project/test/meta_magic_test.rb +0 -13
  107. data/test/faked_project/test/some_class_test.rb +0 -15
  108. data/test/faked_project/test/test_helper.rb +0 -16
  109. data/test/fixtures/app/controllers/sample_controller.rb +0 -10
  110. data/test/fixtures/app/models/user.rb +0 -10
  111. data/test/fixtures/deleted_source_sample.rb +0 -15
  112. data/test/fixtures/frameworks/rspec_bad.rb +0 -9
  113. data/test/fixtures/frameworks/rspec_good.rb +0 -9
  114. data/test/fixtures/frameworks/testunit_bad.rb +0 -9
  115. data/test/fixtures/frameworks/testunit_good.rb +0 -9
  116. data/test/fixtures/resultset1.rb +0 -4
  117. data/test/fixtures/resultset2.rb +0 -5
  118. data/test/fixtures/sample.rb +0 -16
  119. data/test/fixtures/utf-8.rb +0 -3
  120. data/test/helper.rb +0 -35
  121. data/test/shoulda_macros.rb +0 -29
  122. data/test/test_1_8_fallbacks.rb +0 -33
  123. data/test/test_command_guesser.rb +0 -21
  124. data/test/test_deleted_source.rb +0 -16
  125. data/test/test_file_list.rb +0 -24
  126. data/test/test_filters.rb +0 -80
  127. data/test/test_merge_helpers.rb +0 -107
  128. data/test/test_result.rb +0 -147
  129. data/test/test_return_codes.rb +0 -39
  130. data/test/test_source_file.rb +0 -86
  131. data/test/test_source_file_line.rb +0 -110
@@ -1,215 +1,500 @@
1
- require 'fileutils'
2
- #
3
- # Bundles the configuration options used for SimpleCov. All methods
4
- # defined here are usable from SimpleCov directly. Please check out
5
- # SimpleCov documentation for further info.
6
- #
7
- module SimpleCov::Configuration
8
- attr_writer :filters, :groups, :formatter
1
+ # frozen_string_literal: true
9
2
 
10
- #
11
- # The root for the project. This defaults to the
12
- # current working directory.
13
- #
14
- # Configure with SimpleCov.root('/my/project/path')
15
- #
16
- def root(root=nil)
17
- return @root if @root and root.nil?
18
- @root = File.expand_path(root || Dir.getwd)
19
- end
3
+ require "fileutils"
4
+ require "docile"
5
+ require_relative "formatter/multi_formatter"
20
6
 
7
+ module SimpleCov
21
8
  #
22
- # The name of the output and cache directory. Defaults to 'coverage'
23
- #
24
- # Configure with SimpleCov.coverage_dir('cov')
9
+ # Bundles the configuration options used for SimpleCov. All methods
10
+ # defined here are usable from SimpleCov directly. Please check out
11
+ # SimpleCov documentation for further info.
25
12
  #
26
- def coverage_dir(dir=nil)
27
- return @coverage_dir if @coverage_dir and dir.nil?
28
- @coverage_dir = (dir || 'coverage')
29
- end
13
+ module Configuration
14
+ attr_writer :filters, :groups, :formatter, :print_error_status
30
15
 
31
- #
32
- # Returns the full path to the output directory using SimpleCov.root
33
- # and SimpleCov.coverage_dir, so you can adjust this by configuring those
34
- # values. Will create the directory if it's missing
35
- #
36
- def coverage_path
37
- coverage_path = File.join(root, coverage_dir)
38
- FileUtils.mkdir_p coverage_path
39
- coverage_path
40
- end
16
+ #
17
+ # The root for the project. This defaults to the
18
+ # current working directory.
19
+ #
20
+ # Configure with SimpleCov.root('/my/project/path')
21
+ #
22
+ def root(root = nil)
23
+ return @root if defined?(@root) && root.nil?
41
24
 
42
- #
43
- # Returns the list of configured filters. Add filters using SimpleCov.add_filter.
44
- #
45
- def filters
46
- @filters ||= []
47
- end
25
+ @coverage_path = nil # invalidate cache
26
+ @root = File.expand_path(root || Dir.getwd)
27
+ end
48
28
 
49
- # The name of the command (a.k.a. Test Suite) currently running. Used for result
50
- # merging and caching. It first tries to make a guess based upon the command line
51
- # arguments the current test suite is running on and should automatically detect
52
- # unit tests, functional tests, integration tests, rpsec and cucumber and label
53
- # them properly. If it fails to recognize the current command, the command name
54
- # is set to the shell command that the current suite is running on.
55
- #
56
- # You can specify it manually with SimpleCov.command_name("test:units") - please
57
- # also check out the corresponding section in README.rdoc
58
- def command_name(name=nil)
59
- @name = name unless name.nil?
60
- @name ||= SimpleCov::CommandGuesser.guess
61
- @name
62
- end
63
-
64
- #
65
- # Gets or sets the configured formatter.
66
- #
67
- # Configure with: SimpleCov.formatter(SimpleCov::Formatter::SimpleFormatter)
68
- #
69
- def formatter(formatter=nil)
70
- return @formatter if @formatter and formatter.nil?
71
- @formatter = formatter
72
- raise "No formatter configured. Please specify a formatter using SimpleCov.formatter = SimpleCov::Formatter::SimpleFormatter" unless @formatter
73
- @formatter
74
- end
29
+ #
30
+ # The name of the output and cache directory. Defaults to 'coverage'
31
+ #
32
+ # Configure with SimpleCov.coverage_dir('cov')
33
+ #
34
+ def coverage_dir(dir = nil)
35
+ return @coverage_dir if defined?(@coverage_dir) && dir.nil?
75
36
 
76
- #
77
- # Certain code blocks (i.e. Ruby-implementation specific code) can be excluded from
78
- # the coverage metrics by wrapping it inside # :nocov: comment blocks. The nocov token
79
- # can be configured to be any other string using this.
80
- #
81
- # Configure with SimpleCov.nocov_token('skip') or it's alias SimpleCov.skip_token('skip')
82
- #
83
- def nocov_token(nocov_token=nil)
84
- return @nocov_token if @nocov_token and nocov_token.nil?
85
- @nocov_token = (nocov_token || 'nocov')
86
- end
87
- alias_method :skip_token, :nocov_token
37
+ @coverage_path = nil # invalidate cache
38
+ @coverage_dir = (dir || "coverage")
39
+ end
88
40
 
89
- #
90
- # Returns the configured groups. Add groups using SimpleCov.add_group
91
- #
92
- def groups
93
- @groups ||= {}
94
- end
41
+ #
42
+ # Returns the full path to the output directory using SimpleCov.root
43
+ # and SimpleCov.coverage_dir, so you can adjust this by configuring those
44
+ # values. Will create the directory if it's missing
45
+ #
46
+ def coverage_path
47
+ @coverage_path ||= begin
48
+ coverage_path = File.expand_path(coverage_dir, root)
49
+ FileUtils.mkdir_p coverage_path
50
+ coverage_path
51
+ end
52
+ end
95
53
 
96
- #
97
- # Returns the hash of available adapters
98
- #
99
- def adapters
100
- @adapters ||= SimpleCov::Adapters.new
101
- end
54
+ #
55
+ # Coverage results will always include files matched by this glob, whether
56
+ # or not they were explicitly required. Without this, un-required files
57
+ # will not be present in the final report.
58
+ #
59
+ def track_files(glob)
60
+ @tracked_files = glob
61
+ end
102
62
 
103
- #
104
- # Allows you to configure simplecov in a block instead of prepending SimpleCov to all config methods
105
- # you're calling.
106
- #
107
- # SimpleCov.configure do
108
- # add_filter 'foobar'
109
- # end
110
- #
111
- # This is equivalent to SimpleCov.add_filter 'foobar' and thus makes it easier to set a buchn of configure
112
- # options at once.
113
- #
114
- def configure(&block)
115
- return false unless SimpleCov.usable?
116
- instance_exec(&block)
117
- end
63
+ #
64
+ # Returns the glob that will be used to include files that were not
65
+ # explicitly required.
66
+ #
67
+ def tracked_files
68
+ @tracked_files if defined?(@tracked_files)
69
+ end
118
70
 
119
- #
120
- # Gets or sets the behavior to process coverage results.
121
- #
122
- # By default, it will call SimpleCov.result.format!
123
- #
124
- # Configure with:
125
- # SimpleCov.at_exit do
126
- # puts "Coverage done"
127
- # SimpleCov.result.format!
128
- # end
129
- #
130
- def at_exit(&block)
131
- return Proc.new {} unless running or block_given?
132
- @at_exit = block if block_given?
133
- @at_exit ||= Proc.new { SimpleCov.result.format! }
134
- end
71
+ #
72
+ # Returns the list of configured filters. Add filters using SimpleCov.add_filter.
73
+ #
74
+ def filters
75
+ @filters ||= []
76
+ end
135
77
 
136
- #
137
- # Returns the project name - currently assuming the last dirname in
138
- # the SimpleCov.root is this.
139
- #
140
- def project_name(new_name=nil)
141
- return @project_name if @project_name and new_name.nil?
142
- @project_name = new_name if new_name.kind_of?(String)
143
- @project_name ||= File.basename(root.split('/').last).capitalize.gsub('_', ' ')
144
- end
78
+ # The name of the command (a.k.a. Test Suite) currently running. Used for result
79
+ # merging and caching. It first tries to make a guess based upon the command line
80
+ # arguments the current test suite is running on and should automatically detect
81
+ # unit tests, functional tests, integration tests, rpsec and cucumber and label
82
+ # them properly. If it fails to recognize the current command, the command name
83
+ # is set to the shell command that the current suite is running on.
84
+ #
85
+ # You can specify it manually with SimpleCov.command_name("test:units") - please
86
+ # also check out the corresponding section in README.rdoc
87
+ def command_name(name = nil)
88
+ @name = name unless name.nil?
89
+ @name ||= SimpleCov::CommandGuesser.guess
90
+ @name
91
+ end
145
92
 
146
- #
147
- # Defines whether to use result merging so all your test suites (test:units, test:functionals, cucumber, ...)
148
- # are joined and combined into a single coverage report
149
- #
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
153
- end
93
+ #
94
+ # Gets or sets the configured formatter.
95
+ #
96
+ # Configure with: SimpleCov.formatter(SimpleCov::Formatter::SimpleFormatter)
97
+ #
98
+ def formatter(formatter = nil)
99
+ return @formatter if defined?(@formatter) && formatter.nil?
154
100
 
155
- #
156
- # Defines them maximum age (in seconds) of a resultset to still be included in merged results.
157
- # i.e. If you run cucumber features, then later rake test, if the stored cucumber resultset is
158
- # more seconds ago than specified here, it won't be taken into account when merging (and is also
159
- # purged from the resultset cache)
160
- #
161
- # Of course, this only applies when merging is active (e.g. SimpleCov.use_merging is not false!)
162
- #
163
- # Default is 600 seconds (10 minutes)
164
- #
165
- # Configure with SimpleCov.merge_timeout(3600) # 1hr
166
- #
167
- def merge_timeout(seconds=nil)
168
- @merge_timeout = seconds if !seconds.nil? and seconds.kind_of?(Fixnum)
169
- @merge_timeout ||= 600
170
- end
101
+ @formatter = formatter
102
+ raise "No formatter configured. Please specify a formatter using SimpleCov.formatter = SimpleCov::Formatter::SimpleFormatter" unless @formatter
171
103
 
172
- #
173
- # Add a filter to the processing chain.
174
- # There are three ways to define a filter:
175
- #
176
- # * as a String that will then be matched against all source files' file paths,
177
- # SimpleCov.add_filter 'app/models' # will reject all your models
178
- # * as a block which will be passed the source file in question and should either
179
- # return a true or false value, depending on whether the file should be removed
180
- # SimpleCov.add_filter do |src_file|
181
- # File.basename(src_file.filename) == 'environment.rb'
182
- # end # Will exclude environment.rb files from the results
183
- # * as an instance of a subclass of SimpleCov::Filter. See the documentation there
184
- # on how to define your own filter classes
185
- #
186
- def add_filter(filter_argument=nil, &filter_proc)
187
- filters << parse_filter(filter_argument, &filter_proc)
188
- end
104
+ @formatter
105
+ end
106
+
107
+ #
108
+ # Sets the configured formatters.
109
+ #
110
+ def formatters=(formatters)
111
+ @formatter = SimpleCov::Formatter::MultiFormatter.new(formatters)
112
+ end
113
+
114
+ #
115
+ # Gets the configured formatters.
116
+ #
117
+ def formatters
118
+ if @formatter.is_a?(SimpleCov::Formatter::MultiFormatter)
119
+ @formatter.formatters
120
+ else
121
+ Array(formatter)
122
+ end
123
+ end
124
+
125
+ #
126
+ # Whether we should print non-success status codes. This can be
127
+ # configured with the #print_error_status= method.
128
+ #
129
+ def print_error_status
130
+ defined?(@print_error_status) ? @print_error_status : true
131
+ end
132
+
133
+ #
134
+ # Certain code blocks (i.e. Ruby-implementation specific code) can be excluded from
135
+ # the coverage metrics by wrapping it inside # :nocov: comment blocks. The nocov token
136
+ # can be configured to be any other string using this.
137
+ #
138
+ # Configure with SimpleCov.nocov_token('skip') or it's alias SimpleCov.skip_token('skip')
139
+ #
140
+ def nocov_token(nocov_token = nil)
141
+ return @nocov_token if defined?(@nocov_token) && nocov_token.nil?
142
+
143
+ @nocov_token = (nocov_token || "nocov")
144
+ end
145
+ alias skip_token nocov_token
146
+
147
+ #
148
+ # Returns the configured groups. Add groups using SimpleCov.add_group
149
+ #
150
+ def groups
151
+ @groups ||= {}
152
+ end
153
+
154
+ #
155
+ # Returns the hash of available profiles
156
+ #
157
+ def profiles
158
+ @profiles ||= SimpleCov::Profiles.new
159
+ end
160
+
161
+ def adapters
162
+ warn "#{Kernel.caller.first}: [DEPRECATION] #adapters is deprecated. Use #profiles instead."
163
+ profiles
164
+ end
165
+
166
+ #
167
+ # Allows you to configure simplecov in a block instead of prepending SimpleCov to all config methods
168
+ # you're calling.
169
+ #
170
+ # SimpleCov.configure do
171
+ # add_filter 'foobar'
172
+ # end
173
+ #
174
+ # This is equivalent to SimpleCov.add_filter 'foobar' and thus makes it easier to set a bunch of configure
175
+ # options at once.
176
+ #
177
+ def configure(&block)
178
+ Docile.dsl_eval(self, &block)
179
+ end
180
+
181
+ #
182
+ # Gets or sets the behavior to process coverage results.
183
+ #
184
+ # By default, it will call SimpleCov.result.format!
185
+ #
186
+ # Configure with:
187
+ #
188
+ # SimpleCov.at_exit do
189
+ # puts "Coverage done"
190
+ # SimpleCov.result.format!
191
+ # end
192
+ #
193
+ def at_exit(&block)
194
+ return Proc.new unless running || block_given?
195
+
196
+ @at_exit = block if block_given?
197
+ @at_exit ||= proc { SimpleCov.result.format! }
198
+ end
199
+
200
+ # gets or sets the enabled_for_subprocess configuration
201
+ # when true, this will inject SimpleCov code into Process.fork
202
+ def enable_for_subprocesses(value = nil)
203
+ return @enable_for_subprocesses if defined?(@enable_for_subprocesses) && value.nil?
204
+
205
+ @enable_for_subprocesses = value || false
206
+ end
207
+
208
+ # gets the enabled_for_subprocess configuration
209
+ def enabled_for_subprocesses?
210
+ enable_for_subprocesses
211
+ end
212
+
213
+ #
214
+ # Gets or sets the behavior to start a new forked Process.
215
+ #
216
+ # By default, it will add " (Process #{pid})" to the command_name, and start SimpleCov in quiet mode
217
+ #
218
+ # Configure with:
219
+ #
220
+ # SimpleCov.at_fork do |pid|
221
+ # SimpleCov.start do
222
+ # # This needs a unique name so it won't be ovewritten
223
+ # SimpleCov.command_name "#{SimpleCov.command_name} (subprocess: #{pid})"
224
+ # # be quiet, the parent process will be in charge of using the regular formatter and checking coverage totals
225
+ # SimpleCov.print_error_status = false
226
+ # SimpleCov.formatter SimpleCov::Formatter::SimpleFormatter
227
+ # SimpleCov.minimum_coverage 0
228
+ # # start
229
+ # SimpleCov.start
230
+ # end
231
+ # end
232
+ #
233
+ def at_fork(&block)
234
+ @at_fork = block if block_given?
235
+ @at_fork ||= lambda { |pid|
236
+ # This needs a unique name so it won't be ovewritten
237
+ SimpleCov.command_name "#{SimpleCov.command_name} (subprocess: #{pid})"
238
+ # be quiet, the parent process will be in charge of using the regular formatter and checking coverage totals
239
+ SimpleCov.print_error_status = false
240
+ SimpleCov.formatter SimpleCov::Formatter::SimpleFormatter
241
+ SimpleCov.minimum_coverage 0
242
+ # start
243
+ SimpleCov.start
244
+ }
245
+ end
246
+
247
+ #
248
+ # Returns the project name - currently assuming the last dirname in
249
+ # the SimpleCov.root is this.
250
+ #
251
+ def project_name(new_name = nil)
252
+ return @project_name if defined?(@project_name) && @project_name && new_name.nil?
253
+
254
+ @project_name = new_name if new_name.is_a?(String)
255
+ @project_name ||= File.basename(root.split("/").last).capitalize.tr("_", " ")
256
+ end
257
+
258
+ #
259
+ # Defines whether to use result merging so all your test suites (test:units, test:functionals, cucumber, ...)
260
+ # are joined and combined into a single coverage report
261
+ #
262
+ def use_merging(use = nil)
263
+ @use_merging = use unless use.nil?
264
+ @use_merging = true unless defined?(@use_merging) && @use_merging == false
265
+ end
266
+
267
+ #
268
+ # Defines the maximum age (in seconds) of a resultset to still be included in merged results.
269
+ # i.e. If you run cucumber features, then later rake test, if the stored cucumber resultset is
270
+ # more seconds ago than specified here, it won't be taken into account when merging (and is also
271
+ # purged from the resultset cache)
272
+ #
273
+ # Of course, this only applies when merging is active (e.g. SimpleCov.use_merging is not false!)
274
+ #
275
+ # Default is 600 seconds (10 minutes)
276
+ #
277
+ # Configure with SimpleCov.merge_timeout(3600) # 1hr
278
+ #
279
+ def merge_timeout(seconds = nil)
280
+ @merge_timeout = seconds if seconds.is_a?(Integer)
281
+ @merge_timeout ||= 600
282
+ end
283
+
284
+ #
285
+ # Defines the minimum overall coverage required for the testsuite to pass.
286
+ # SimpleCov will return non-zero if the current coverage is below this threshold.
287
+ #
288
+ # Default is 0% (disabled)
289
+ #
290
+ def minimum_coverage(coverage = nil)
291
+ return @minimum_coverage ||= {} unless coverage
292
+
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)
301
+ coverage.each_key { |criterion| raise_if_criterion_disabled(criterion) }
302
+ coverage.each_value do |percent|
303
+ minimum_possible_coverage_exceeded(coverage_setting) if percent && percent > 100
304
+ end
305
+ end
306
+
307
+ #
308
+ # Defines the maximum coverage drop at once allowed for the testsuite to pass.
309
+ # SimpleCov will return non-zero if the coverage decreases by more than this threshold.
310
+ #
311
+ # Default is 100% (disabled)
312
+ #
313
+ def maximum_coverage_drop(coverage_drop = nil)
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
321
+ end
322
+
323
+ #
324
+ # Defines the minimum coverage per file required for the testsuite to pass.
325
+ # SimpleCov will return non-zero if the current coverage of the least covered file
326
+ # is below this threshold.
327
+ #
328
+ # Default is 0% (disabled)
329
+ #
330
+ def minimum_coverage_by_file(coverage = nil)
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
338
+ end
339
+
340
+ #
341
+ # Refuses any coverage drop. That is, coverage is only allowed to increase.
342
+ # SimpleCov will return non-zero if the coverage decreases.
343
+ #
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)
348
+ end
349
+
350
+ #
351
+ # Add a filter to the processing chain.
352
+ # There are four ways to define a filter:
353
+ #
354
+ # * as a String that will then be matched against all source files' file paths,
355
+ # SimpleCov.add_filter 'app/models' # will reject all your models
356
+ # * as a block which will be passed the source file in question and should either
357
+ # return a true or false value, depending on whether the file should be removed
358
+ # SimpleCov.add_filter do |src_file|
359
+ # File.basename(src_file.filename) == 'environment.rb'
360
+ # end # Will exclude environment.rb files from the results
361
+ # * as an array of strings that are matched against all sorce files' file
362
+ # paths and then ignored (basically string filter multiple times)
363
+ # SimpleCov.add_filter ['app/models', 'app/helpers'] # ignores both dirs
364
+ # * as an instance of a subclass of SimpleCov::Filter. See the documentation there
365
+ # on how to define your own filter classes
366
+ #
367
+ def add_filter(filter_argument = nil, &filter_proc)
368
+ filters << parse_filter(filter_argument, &filter_proc)
369
+ end
370
+
371
+ #
372
+ # Define a group for files. Works similar to add_filter, only that the first
373
+ # argument is the desired group name and files PASSING the filter end up in the group
374
+ # (while filters exclude when the filter is applicable).
375
+ #
376
+ def add_group(group_name, filter_argument = nil, &filter_proc)
377
+ groups[group_name] = parse_filter(filter_argument, &filter_proc)
378
+ end
379
+
380
+ SUPPORTED_COVERAGE_CRITERIA = %i[line branch].freeze
381
+ DEFAULT_COVERAGE_CRITERION = :line
382
+ #
383
+ # Define which coverage criterion should be evaluated.
384
+ #
385
+ # Possible coverage criteria:
386
+ # * :line - coverage based on lines aka has this line been executed?
387
+ # * :branch - coverage based on branches aka has this branch (think conditions) been executed?
388
+ #
389
+ # If not set the default is `:line`
390
+ #
391
+ # @param [Symbol] criterion
392
+ #
393
+ def coverage_criterion(criterion = nil)
394
+ return @coverage_criterion ||= primary_coverage unless criterion
395
+
396
+ raise_if_criterion_unsupported(criterion)
397
+
398
+ @coverage_criterion = criterion
399
+ end
400
+
401
+ def enable_coverage(criterion)
402
+ raise_if_criterion_unsupported(criterion)
403
+
404
+ coverage_criteria << criterion
405
+ end
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
+
416
+ def coverage_criteria
417
+ @coverage_criteria ||= Set[primary_coverage]
418
+ end
419
+
420
+ def coverage_criterion_enabled?(criterion)
421
+ coverage_criteria.member?(criterion)
422
+ end
423
+
424
+ def clear_coverage_criteria
425
+ @coverage_criteria = nil
426
+ end
427
+
428
+ def branch_coverage?
429
+ branch_coverage_supported? && coverage_criterion_enabled?(:branch)
430
+ end
431
+
432
+ def coverage_start_arguments_supported?
433
+ # safe to cache as within one process this value should never
434
+ # change
435
+ return @coverage_start_arguments_supported if defined?(@coverage_start_arguments_supported)
436
+
437
+ @coverage_start_arguments_supported = begin
438
+ require "coverage"
439
+ !Coverage.method(:start).arity.zero?
440
+ end
441
+ end
442
+
443
+ def branch_coverage_supported?
444
+ coverage_start_arguments_supported? && RUBY_ENGINE != "jruby"
445
+ end
446
+
447
+ def coverage_for_eval_supported?
448
+ require "coverage"
449
+ defined?(Coverage.supported?) && Coverage.supported?(:eval)
450
+ end
451
+
452
+ def coverage_for_eval_enabled?
453
+ @coverage_for_eval_enabled ||= false
454
+ end
455
+
456
+ def enable_coverage_for_eval
457
+ if coverage_for_eval_supported?
458
+ @coverage_for_eval_enabled = true
459
+ else
460
+ warn "Coverage for eval is not available; Use Ruby 3.2.0 or later"
461
+ end
462
+ end
189
463
 
190
- #
191
- # Define a group for files. Works similar to add_filter, only that the first
192
- # argument is the desired group name and files PASSING the filter end up in the group
193
- # (while filters exclude when the filter is applicable).
194
- #
195
- def add_group(group_name, filter_argument=nil, &filter_proc)
196
- groups[group_name] = parse_filter(filter_argument, &filter_proc)
197
- end
198
-
199
464
  private
200
465
 
201
- #
202
- # The actal filter processor. Not meant for direct use
203
- #
204
- def parse_filter(filter_argument=nil, &filter_proc)
205
- if filter_argument.kind_of?(SimpleCov::Filter)
206
- filter_argument
207
- elsif filter_argument.kind_of?(String)
208
- SimpleCov::StringFilter.new(filter_argument)
209
- elsif filter_proc
210
- SimpleCov::BlockFilter.new(filter_proc)
211
- else
212
- raise ArgumentError, "Please specify either a string or a block to filter with"
466
+ def raise_if_criterion_disabled(criterion)
467
+ raise_if_criterion_unsupported(criterion)
468
+ # rubocop:disable Style/IfUnlessModifier
469
+ unless coverage_criterion_enabled?(criterion)
470
+ raise "Coverage criterion #{criterion}, is disabled! Please enable it first through enable_coverage #{criterion} (if supported)"
471
+ end
472
+ # rubocop:enable Style/IfUnlessModifier
473
+ end
474
+
475
+ def raise_if_criterion_unsupported(criterion)
476
+ # rubocop:disable Style/IfUnlessModifier
477
+ unless SUPPORTED_COVERAGE_CRITERIA.member?(criterion)
478
+ raise "Unsupported coverage criterion #{criterion}, supported values are #{SUPPORTED_COVERAGE_CRITERIA}"
479
+ end
480
+ # rubocop:enable Style/IfUnlessModifier
481
+ end
482
+
483
+ def minimum_possible_coverage_exceeded(coverage_option)
484
+ warn "The coverage you set for #{coverage_option} is greater than 100%"
485
+ end
486
+
487
+ #
488
+ # The actual filter processor. Not meant for direct use
489
+ #
490
+ def parse_filter(filter_argument = nil, &filter_proc)
491
+ filter = filter_argument || filter_proc
492
+
493
+ if filter
494
+ SimpleCov::Filter.build_filter(filter)
495
+ else
496
+ raise ArgumentError, "Please specify either a filter or a block to filter with"
497
+ end
213
498
  end
214
499
  end
215
500
  end