simplecov 0.22.0 → 1.3.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.
Files changed (210) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +1 -1
  3. data/README.md +173 -875
  4. data/exe/simplecov +8 -0
  5. data/lib/minitest/simplecov_plugin.rb +18 -7
  6. data/lib/simplecov/atomic_file.rb +67 -0
  7. data/lib/simplecov/autostart.rb +9 -0
  8. data/lib/simplecov/baseline/parser.rb +58 -0
  9. data/lib/simplecov/baseline.rb +170 -0
  10. data/lib/simplecov/cli/affected/changed_files.rb +57 -0
  11. data/lib/simplecov/cli/affected/selection.rb +117 -0
  12. data/lib/simplecov/cli/affected.rb +148 -0
  13. data/lib/simplecov/cli/annotations.rb +166 -0
  14. data/lib/simplecov/cli/badge/svg.rb +70 -0
  15. data/lib/simplecov/cli/badge.rb +62 -0
  16. data/lib/simplecov/cli/clean.rb +86 -0
  17. data/lib/simplecov/cli/command_helpers.rb +91 -0
  18. data/lib/simplecov/cli/completions/scripts.rb +114 -0
  19. data/lib/simplecov/cli/completions.rb +83 -0
  20. data/lib/simplecov/cli/coverage.rb +83 -0
  21. data/lib/simplecov/cli/coverage_file.rb +109 -0
  22. data/lib/simplecov/cli/dead_code/output.rb +100 -0
  23. data/lib/simplecov/cli/dead_code.rb +139 -0
  24. data/lib/simplecov/cli/diff/output.rb +45 -0
  25. data/lib/simplecov/cli/diff.rb +115 -0
  26. data/lib/simplecov/cli/dotfile.rb +156 -0
  27. data/lib/simplecov/cli/git.rb +41 -0
  28. data/lib/simplecov/cli/history/output.rb +135 -0
  29. data/lib/simplecov/cli/history.rb +67 -0
  30. data/lib/simplecov/cli/merge.rb +115 -0
  31. data/lib/simplecov/cli/open.rb +45 -0
  32. data/lib/simplecov/cli/patch/changed_lines.rb +149 -0
  33. data/lib/simplecov/cli/patch/output.rb +151 -0
  34. data/lib/simplecov/cli/patch.rb +175 -0
  35. data/lib/simplecov/cli/ratchet/output.rb +44 -0
  36. data/lib/simplecov/cli/ratchet.rb +83 -0
  37. data/lib/simplecov/cli/report.rb +91 -0
  38. data/lib/simplecov/cli/run.rb +35 -0
  39. data/lib/simplecov/cli/serve/report_preparer.rb +30 -0
  40. data/lib/simplecov/cli/serve/static_file_handler.rb +128 -0
  41. data/lib/simplecov/cli/serve.rb +88 -0
  42. data/lib/simplecov/cli/show/annotator.rb +87 -0
  43. data/lib/simplecov/cli/show/sweep.rb +36 -0
  44. data/lib/simplecov/cli/show.rb +133 -0
  45. data/lib/simplecov/cli/status/facts.rb +87 -0
  46. data/lib/simplecov/cli/status.rb +93 -0
  47. data/lib/simplecov/cli/tests/redundancy.rb +78 -0
  48. data/lib/simplecov/cli/tests.rb +148 -0
  49. data/lib/simplecov/cli/uncovered/misses.rb +33 -0
  50. data/lib/simplecov/cli/uncovered.rb +127 -0
  51. data/lib/simplecov/cli/usage.rb +153 -0
  52. data/lib/simplecov/cli/watch/live_report.rb +73 -0
  53. data/lib/simplecov/cli/watch/narrator.rb +55 -0
  54. data/lib/simplecov/cli/watch/poller.rb +45 -0
  55. data/lib/simplecov/cli/watch/session.rb +134 -0
  56. data/lib/simplecov/cli/watch/test_plan.rb +43 -0
  57. data/lib/simplecov/cli/watch.rb +85 -0
  58. data/lib/simplecov/cli.rb +114 -0
  59. data/lib/simplecov/color.rb +65 -0
  60. data/lib/simplecov/combine/branches_combiner.rb +44 -21
  61. data/lib/simplecov/combine/coverage_accumulator.rb +175 -0
  62. data/lib/simplecov/combine/identity_interner.rb +27 -0
  63. data/lib/simplecov/combine/interned_counts.rb +28 -0
  64. data/lib/simplecov/combine/lines_combiner.rb +35 -29
  65. data/lib/simplecov/combine/methods_combiner.rb +48 -0
  66. data/lib/simplecov/combine/results_combiner.rb +9 -49
  67. data/lib/simplecov/combine.rb +0 -24
  68. data/lib/simplecov/command_guesser.rb +99 -42
  69. data/lib/simplecov/configuration/baseline.rb +31 -0
  70. data/lib/simplecov/configuration/coverage.rb +184 -0
  71. data/lib/simplecov/configuration/coverage_criteria.rb +143 -0
  72. data/lib/simplecov/configuration/deprecations.rb +21 -0
  73. data/lib/simplecov/configuration/eval_coverage.rb +37 -0
  74. data/lib/simplecov/configuration/filters.rb +131 -0
  75. data/lib/simplecov/configuration/formatting.rb +129 -0
  76. data/lib/simplecov/configuration/groups.rb +48 -0
  77. data/lib/simplecov/configuration/history.rb +44 -0
  78. data/lib/simplecov/configuration/ignored_entries.rb +72 -0
  79. data/lib/simplecov/configuration/merging.rb +197 -0
  80. data/lib/simplecov/configuration/missed_caps.rb +51 -0
  81. data/lib/simplecov/configuration/production.rb +23 -0
  82. data/lib/simplecov/configuration/test_tracking.rb +48 -0
  83. data/lib/simplecov/configuration/thresholds.rb +150 -0
  84. data/lib/simplecov/configuration/view_coverage.rb +37 -0
  85. data/lib/simplecov/configuration.rb +100 -443
  86. data/lib/simplecov/configuration_error.rb +5 -0
  87. data/lib/simplecov/context_map/union.rb +81 -0
  88. data/lib/simplecov/context_map.rb +187 -0
  89. data/lib/simplecov/coverage_json.rb +21 -0
  90. data/lib/simplecov/coverage_statistics.rb +24 -28
  91. data/lib/simplecov/coverage_violations.rb +254 -0
  92. data/lib/simplecov/current_run.rb +61 -0
  93. data/lib/simplecov/defaults.rb +20 -28
  94. data/lib/simplecov/deprecation.rb +63 -0
  95. data/lib/simplecov/directive/erb.rb +44 -0
  96. data/lib/simplecov/directive/haml.rb +33 -0
  97. data/lib/simplecov/directive/indented_template.rb +37 -0
  98. data/lib/simplecov/directive/slim.rb +35 -0
  99. data/lib/simplecov/directive/template.rb +29 -0
  100. data/lib/simplecov/directive.rb +152 -0
  101. data/lib/simplecov/exit_codes/baseline_check.rb +46 -0
  102. data/lib/simplecov/exit_codes/check.rb +39 -0
  103. data/lib/simplecov/exit_codes/exit_code_handling.rb +24 -5
  104. data/lib/simplecov/exit_codes/maximum_coverage_drop_check.rb +17 -67
  105. data/lib/simplecov/exit_codes/maximum_missed_check.rb +26 -0
  106. data/lib/simplecov/exit_codes/maximum_missed_per_file_check.rb +34 -0
  107. data/lib/simplecov/exit_codes/maximum_overall_coverage_check.rb +27 -0
  108. data/lib/simplecov/exit_codes/minimum_coverage_by_file_check.rb +20 -38
  109. data/lib/simplecov/exit_codes/minimum_coverage_by_group_check.rb +28 -0
  110. data/lib/simplecov/exit_codes/minimum_overall_coverage_check.rb +37 -34
  111. data/lib/simplecov/exit_codes.rb +26 -0
  112. data/lib/simplecov/exit_handling.rb +163 -0
  113. data/lib/simplecov/file_list.rb +63 -48
  114. data/lib/simplecov/filter.rb +98 -42
  115. data/lib/simplecov/formatter/base.rb +100 -0
  116. data/lib/simplecov/formatter/baseline_formatter.rb +98 -0
  117. data/lib/simplecov/formatter/coverage_json_writer.rb +95 -0
  118. data/lib/simplecov/formatter/html_formatter/public/index.html +93 -0
  119. data/lib/simplecov/formatter/html_formatter/viewer_data_validator.rb +153 -0
  120. data/lib/simplecov/formatter/html_formatter.rb +88 -0
  121. data/lib/simplecov/formatter/json_formatter/errors_formatter.rb +122 -0
  122. data/lib/simplecov/formatter/json_formatter/production_section_formatter.rb +48 -0
  123. data/lib/simplecov/formatter/json_formatter/result_hash_formatter.rb +132 -0
  124. data/lib/simplecov/formatter/json_formatter/source_file_formatter.rb +97 -0
  125. data/lib/simplecov/formatter/json_formatter.rb +40 -0
  126. data/lib/simplecov/formatter/multi_formatter.rb +8 -10
  127. data/lib/simplecov/formatter/simple_formatter.rb +12 -15
  128. data/lib/simplecov/formatter.rb +21 -1
  129. data/lib/simplecov/group_names.rb +33 -0
  130. data/lib/simplecov/history.rb +115 -0
  131. data/lib/simplecov/last_run.rb +16 -5
  132. data/lib/simplecov/lines_classifier.rb +67 -19
  133. data/lib/simplecov/load_global_config.rb +12 -4
  134. data/lib/simplecov/parallel_adapters/base.rb +50 -0
  135. data/lib/simplecov/parallel_adapters/generic.rb +33 -0
  136. data/lib/simplecov/parallel_adapters/parallel_tests.rb +72 -0
  137. data/lib/simplecov/parallel_adapters.rb +54 -0
  138. data/lib/simplecov/parallel_coordination.rb +124 -0
  139. data/lib/simplecov/parallel_result_merger/worker_payload.rb +33 -0
  140. data/lib/simplecov/parallel_result_merger.rb +171 -0
  141. data/lib/simplecov/process.rb +34 -12
  142. data/lib/simplecov/production/error.rb +11 -0
  143. data/lib/simplecov/production/file_sink.rb +130 -0
  144. data/lib/simplecov/production.rb +249 -0
  145. data/lib/simplecov/profiles/bundler_filter.rb +1 -1
  146. data/lib/simplecov/profiles/hidden_filter.rb +1 -1
  147. data/lib/simplecov/profiles/rails.rb +13 -10
  148. data/lib/simplecov/profiles/root_filter.rb +2 -5
  149. data/lib/simplecov/profiles/strict.rb +22 -0
  150. data/lib/simplecov/profiles/test_frameworks.rb +1 -4
  151. data/lib/simplecov/profiles.rb +26 -19
  152. data/lib/simplecov/report_deferral.rb +45 -0
  153. data/lib/simplecov/report_stamp.rb +25 -0
  154. data/lib/simplecov/result/filter_config.rb +19 -0
  155. data/lib/simplecov/result/missing_source_files_reporter.rb +48 -0
  156. data/lib/simplecov/result/serialization.rb +43 -0
  157. data/lib/simplecov/result/source_file_builder.rb +46 -0
  158. data/lib/simplecov/result.rb +123 -47
  159. data/lib/simplecov/result_adapter.rb +130 -11
  160. data/lib/simplecov/result_merger/contexts.rb +22 -0
  161. data/lib/simplecov/result_merger/legacy_format_adapter.rb +24 -0
  162. data/lib/simplecov/result_merger/resultset_file.rb +67 -0
  163. data/lib/simplecov/result_merger/resultset_run_identity.rb +65 -0
  164. data/lib/simplecov/result_merger/resultset_store.rb +62 -0
  165. data/lib/simplecov/result_merger/unloaded_files.rb +95 -0
  166. data/lib/simplecov/result_merger.rb +120 -124
  167. data/lib/simplecov/result_processing.rb +188 -0
  168. data/lib/simplecov/run_identity.rb +75 -0
  169. data/lib/simplecov/simulate_coverage.rb +72 -17
  170. data/lib/simplecov/source_file/branch.rb +8 -35
  171. data/lib/simplecov/source_file/branch_builder.rb +95 -0
  172. data/lib/simplecov/source_file/builder_context.rb +28 -0
  173. data/lib/simplecov/source_file/line.rb +17 -32
  174. data/lib/simplecov/source_file/line_builder.rb +37 -0
  175. data/lib/simplecov/source_file/method.rb +54 -0
  176. data/lib/simplecov/source_file/method_builder.rb +51 -0
  177. data/lib/simplecov/source_file/ruby_data_parser.rb +103 -0
  178. data/lib/simplecov/source_file/skip_chunks.rb +72 -0
  179. data/lib/simplecov/source_file/source_loader.rb +89 -0
  180. data/lib/simplecov/source_file/statistics.rb +76 -0
  181. data/lib/simplecov/source_file.rb +64 -260
  182. data/lib/simplecov/static_coverage_extractor/condition_folding.rb +155 -0
  183. data/lib/simplecov/static_coverage_extractor/location_conventions.rb +191 -0
  184. data/lib/simplecov/static_coverage_extractor/method_collector.rb +45 -0
  185. data/lib/simplecov/static_coverage_extractor/prism_compat.rb +46 -0
  186. data/lib/simplecov/static_coverage_extractor/value_position.rb +61 -0
  187. data/lib/simplecov/static_coverage_extractor/visitor.rb +163 -0
  188. data/lib/simplecov/static_coverage_extractor.rb +78 -0
  189. data/lib/simplecov/test_tracker/accessors.rb +34 -0
  190. data/lib/simplecov/test_tracker/constant_watch.rb +49 -0
  191. data/lib/simplecov/test_tracker/delta.rb +69 -0
  192. data/lib/simplecov/test_tracker/framework_hooks.rb +130 -0
  193. data/lib/simplecov/test_tracker.rb +162 -0
  194. data/lib/simplecov/unloaded_file_injector.rb +62 -0
  195. data/lib/simplecov/useless_results_remover.rb +17 -7
  196. data/lib/simplecov/version.rb +1 -1
  197. data/lib/simplecov/view_coverage/template_compiler.rb +83 -0
  198. data/lib/simplecov/view_coverage.rb +55 -0
  199. data/lib/simplecov-html.rb +5 -0
  200. data/lib/simplecov.rb +143 -394
  201. data/lib/simplecov_json_formatter.rb +5 -0
  202. data/man/simplecov.1 +354 -0
  203. data/sig/simplecov.rbs +1710 -0
  204. metadata +183 -61
  205. data/CHANGELOG.md +0 -191
  206. data/doc/alternate-formatters.md +0 -71
  207. data/doc/commercial-services.md +0 -25
  208. data/doc/editor-integration.md +0 -18
  209. data/lib/simplecov/combine/files_combiner.rb +0 -24
  210. data/lib/simplecov/default_formatter.rb +0 -20
@@ -1,500 +1,157 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "fileutils"
4
- require "docile"
5
- require_relative "formatter/multi_formatter"
6
4
 
7
5
  module SimpleCov
8
- #
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.
12
- #
13
6
  module Configuration
14
- attr_writer :filters, :groups, :formatter, :print_error_status
15
-
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
7
  def root(root = nil)
23
- return @root if defined?(@root) && root.nil?
24
-
25
- @coverage_path = nil # invalidate cache
26
- @root = File.expand_path(root || Dir.getwd)
27
- end
28
-
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?
8
+ return @root if instance_variable_defined?(:@root) && root.nil?
36
9
 
37
- @coverage_path = nil # invalidate cache
38
- @coverage_dir = (dir || "coverage")
10
+ self.root = root
11
+ @root
39
12
  end
40
13
 
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
53
-
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
14
+ # nil means the working directory, the default the reader would have derived.
15
+ def root=(root)
16
+ @coverage_path = nil unless @coverage_path_explicit # invalidate cache
17
+ @root = File.expand_path(root || Dir.getwd)
61
18
  end
62
19
 
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
20
+ def coverage_dir(dir = nil)
21
+ return @coverage_dir if instance_variable_defined?(:@coverage_dir) && dir.nil?
70
22
 
71
- #
72
- # Returns the list of configured filters. Add filters using SimpleCov.add_filter.
73
- #
74
- def filters
75
- @filters ||= []
23
+ self.coverage_dir = dir
24
+ @coverage_dir
76
25
  end
77
26
 
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
27
+ # nil means the default 'coverage', recorded as a derivation rather than a
28
+ # choice.
29
+ def coverage_dir=(dir)
30
+ @coverage_path = nil unless @coverage_path_explicit # invalidate cache
31
+ @coverage_dir_explicit = true unless dir.nil?
32
+ @coverage_dir = dir || "coverage"
91
33
  end
92
34
 
93
35
  #
94
- # Gets or sets the configured formatter.
36
+ # Defaults to `SimpleCov.root` + `SimpleCov.coverage_dir`, but callers can
37
+ # override with an arbitrary absolute path, handy for out-of-tree build
38
+ # directories (#716).
95
39
  #
96
- # Configure with: SimpleCov.formatter(SimpleCov::Formatter::SimpleFormatter)
40
+ # Reading is pure: the directory is only created when a path is explicitly
41
+ # assigned. The codepaths that write into it ensure existence themselves,
42
+ # so read-only CLI subcommands that interpolate the path into status text
43
+ # don't materialize a stray `coverage/` directory.
97
44
  #
98
- def formatter(formatter = nil)
99
- return @formatter if defined?(@formatter) && formatter.nil?
100
-
101
- @formatter = formatter
102
- raise "No formatter configured. Please specify a formatter using SimpleCov.formatter = SimpleCov::Formatter::SimpleFormatter" unless @formatter
45
+ def coverage_path(path = nil)
46
+ self.coverage_path = path if path
103
47
 
104
- @formatter
48
+ @coverage_path ||= File.expand_path(coverage_dir, root)
105
49
  end
106
50
 
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
51
+ # Assigning is the signal the caller intends to write there, so the
52
+ # directory is created.
53
+ def coverage_path=(path)
54
+ expanded = File.expand_path(path)
55
+ @coverage_path = expanded
56
+ @coverage_path_explicit = true
57
+ FileUtils.mkdir_p expanded
123
58
  end
124
59
 
125
60
  #
126
- # Whether we should print non-success status codes. This can be
127
- # configured with the #print_error_status= method.
61
+ # The name of the command (a.k.a. Test Suite) currently running. Used for
62
+ # result merging and caching. Auto-detected.
128
63
  #
129
- def print_error_status
130
- defined?(@print_error_status) ? @print_error_status : true
64
+ def command_name(name = nil)
65
+ self.command_name = name unless name.nil?
66
+ @command_name ||= CommandGuesser.guess
131
67
  end
132
68
 
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")
69
+ def command_name=(name)
70
+ @command_name = name
144
71
  end
145
- alias skip_token nocov_token
146
72
 
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
73
  def profiles
158
- @profiles ||= SimpleCov::Profiles.new
74
+ @profiles ||= Profiles.new
159
75
  end
160
76
 
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
77
  #
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.
78
+ # Configure simplecov in a block instead of prepending SimpleCov to each
79
+ # config method. Parameterized blocks retain their caller context and
80
+ # receive this configuration target explicitly.
176
81
  #
177
82
  def configure(&block)
178
- Docile.dsl_eval(self, &block)
83
+ raise ArgumentError, "configuration block required" unless block
84
+
85
+ if block.parameters.empty?
86
+ instance_exec(&(_ = block))
87
+ else
88
+ yield self
89
+ end
90
+ self
179
91
  end
180
92
 
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
93
  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! }
94
+ @at_exit = block if block
95
+ configured = @at_exit
96
+ return configured if configured
97
+ return -> {} unless active_session?
98
+
99
+ @at_exit = lambda do
100
+ result = SimpleCov.result
101
+ result.format! if result && SimpleCov.merge_finalization_owner?
102
+ end
198
103
  end
199
104
 
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
105
+ # The Coverage module is actively tracking, or a `@result` has already been
106
+ # assembled (`SimpleCov.collate` never starts Coverage).
107
+ def active_session?
108
+ !!SimpleCov.result? || coverage_running?
206
109
  end
207
110
 
208
- # gets the enabled_for_subprocess configuration
209
- def enabled_for_subprocesses?
210
- enable_for_subprocesses
111
+ # A configuration loaded on its own may have no Coverage to ask, which is
112
+ # not something a suite that measures coverage can be.
113
+ def coverage_running?
114
+ defined?(Coverage) ? Coverage.running? : false
211
115
  end
212
116
 
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
117
  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
118
+ @at_fork = block if block
119
+ @at_fork ||= lambda { |_pid|
120
+ # Needs a name that's unique per worker within a run yet identical across
121
+ # runs. Built from SimpleCov's stable fork serial rather than the OS pid:
122
+ # with the pid, every run produced uniquely-named results that never
123
+ # overwrote the previous run's, so they piled up in .resultset.json
124
+ # until merge_timeout and the merged report's file set drifted from run
125
+ # to run (#1171).
126
+ SimpleCov.command_name "#{SimpleCov.command_name} (subprocess: #{SimpleCov.subprocess_serial})"
127
+ SimpleCov.print_errors false
128
+ SimpleCov.formatter Formatter::SimpleFormatter
241
129
  SimpleCov.minimum_coverage 0
242
- # start
243
130
  SimpleCov.start
244
131
  }
245
132
  end
246
133
 
247
- #
248
- # Returns the project name - currently assuming the last dirname in
249
- # the SimpleCov.root is this.
250
- #
134
+ # Only a String renames, and a name already chosen survives anything else.
251
135
  def project_name(new_name = nil)
252
- return @project_name if defined?(@project_name) && @project_name && new_name.nil?
253
-
254
136
  @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
463
-
464
- private
465
-
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
137
+ @project_name ||= File.basename(root).capitalize.tr("_", " ")
498
138
  end
499
139
  end
500
140
  end
141
+
142
+ require_relative "configuration/coverage"
143
+ require_relative "configuration/coverage_criteria"
144
+ require_relative "configuration/eval_coverage"
145
+ require_relative "configuration/filters"
146
+ require_relative "configuration/groups"
147
+ require_relative "configuration/formatting"
148
+ require_relative "configuration/ignored_entries"
149
+ require_relative "configuration/merging"
150
+ require_relative "configuration/test_tracking"
151
+ require_relative "configuration/thresholds"
152
+ require_relative "configuration/baseline"
153
+ require_relative "configuration/deprecations"
154
+ require_relative "configuration/history"
155
+ require_relative "configuration/missed_caps"
156
+ require_relative "configuration/production"
157
+ require_relative "configuration/view_coverage"
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleCov
4
+ class ConfigurationError < StandardError; end
5
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleCov
4
+ class ContextMap
5
+ # Accumulates the context maps of every result a merge absorbs, under the
6
+ # all-or-nothing rule the resultset format needs: the merged result carries
7
+ # the union of the maps when every merged result recorded one, and no map at
8
+ # all otherwise. Keeping a partial union would present one worker's map as
9
+ # the whole run's, silently omitting the tests of the results that didn't
10
+ # record.
11
+ class Union
12
+ attr_reader :carrying, :entries
13
+
14
+ def initialize
15
+ @map = ContextMap.new
16
+ @complete = true
17
+ @carrying = 0
18
+ @entries = 0
19
+ end
20
+
21
+ def collector
22
+ ->(surviving) { absorb_resultset(surviving) }
23
+ end
24
+
25
+ def absorb_resultset(resultset)
26
+ resultset.each_value { |data| absorb_entry(data) }
27
+ end
28
+
29
+ def absorb_entry(data)
30
+ @entries += 1
31
+ map = ContextMap.from_hash(data["contexts"])
32
+ return @complete = false unless map
33
+
34
+ @carrying += 1
35
+ @map.absorb(map)
36
+ end
37
+
38
+ # The parallel merge builds one union per worker over its slice and ships it
39
+ # back, so the parent combines unions rather than re-reading entries.
40
+ def absorb_union(other)
41
+ @entries += other.entries
42
+ @carrying += other.carrying
43
+ @complete &&= other.complete?
44
+ @map.absorb(other.partial_map)
45
+ self
46
+ end
47
+
48
+ def complete?
49
+ @complete
50
+ end
51
+
52
+ # The union when every absorbed result carried a map, nil otherwise. Dropping
53
+ # warns when the merge mixed results with and without maps, since that
54
+ # usually means `track_tests` is on in some workers and not others; a merge
55
+ # where nothing recorded stays quiet.
56
+ def map
57
+ return @map if @complete
58
+
59
+ warn_about_partial_maps
60
+ end
61
+
62
+ protected
63
+
64
+ # Protected: only another union folding this one in may read it.
65
+ def partial_map
66
+ @map
67
+ end
68
+
69
+ private
70
+
71
+ def warn_about_partial_maps
72
+ return unless @carrying.positive? && SimpleCov.print_errors
73
+
74
+ warn "[SimpleCov]: Dropped the per-test map from the merged result: " \
75
+ "only #{@carrying} of #{@entries} merged results carry one. The map is " \
76
+ "kept only when every merged result records it, so enable `track_tests` " \
77
+ "in every suite that should contribute."
78
+ end
79
+ end
80
+ end
81
+ end