simplecov 0.22.0 → 1.0.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 (115) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +1 -1
  3. data/README.md +1058 -508
  4. data/doc/alternate-formatters.md +0 -5
  5. data/doc/commercial-services.md +5 -5
  6. data/exe/simplecov +11 -0
  7. data/lib/minitest/simplecov_plugin.rb +13 -5
  8. data/lib/simplecov/autostart.rb +11 -0
  9. data/lib/simplecov/cli/clean.rb +47 -0
  10. data/lib/simplecov/cli/coverage.rb +91 -0
  11. data/lib/simplecov/cli/diff.rb +151 -0
  12. data/lib/simplecov/cli/dotfile.rb +100 -0
  13. data/lib/simplecov/cli/merge.rb +116 -0
  14. data/lib/simplecov/cli/open.rb +50 -0
  15. data/lib/simplecov/cli/report.rb +84 -0
  16. data/lib/simplecov/cli/run.rb +36 -0
  17. data/lib/simplecov/cli/serve.rb +139 -0
  18. data/lib/simplecov/cli/uncovered.rb +107 -0
  19. data/lib/simplecov/cli.rb +150 -0
  20. data/lib/simplecov/color.rb +74 -0
  21. data/lib/simplecov/combine/branches_combiner.rb +27 -5
  22. data/lib/simplecov/combine/files_combiner.rb +7 -1
  23. data/lib/simplecov/combine/lines_combiner.rb +19 -17
  24. data/lib/simplecov/combine/methods_combiner.rb +26 -0
  25. data/lib/simplecov/combine/results_combiner.rb +5 -4
  26. data/lib/simplecov/command_guesser.rb +46 -32
  27. data/lib/simplecov/configuration/coverage.rb +171 -0
  28. data/lib/simplecov/configuration/coverage_criteria.rb +156 -0
  29. data/lib/simplecov/configuration/filters.rb +197 -0
  30. data/lib/simplecov/configuration/formatting.rb +119 -0
  31. data/lib/simplecov/configuration/ignored_entries.rb +63 -0
  32. data/lib/simplecov/configuration/merging.rb +164 -0
  33. data/lib/simplecov/configuration/thresholds.rb +174 -0
  34. data/lib/simplecov/configuration.rb +91 -407
  35. data/lib/simplecov/coverage_statistics.rb +12 -9
  36. data/lib/simplecov/coverage_violations.rb +148 -0
  37. data/lib/simplecov/defaults.rb +27 -20
  38. data/lib/simplecov/deprecation.rb +45 -0
  39. data/lib/simplecov/directive.rb +162 -0
  40. data/lib/simplecov/exit_codes/exit_code_handling.rb +8 -2
  41. data/lib/simplecov/exit_codes/maximum_coverage_drop_check.rb +19 -57
  42. data/lib/simplecov/exit_codes/maximum_overall_coverage_check.rb +45 -0
  43. data/lib/simplecov/exit_codes/minimum_coverage_by_file_check.rb +17 -27
  44. data/lib/simplecov/exit_codes/minimum_coverage_by_group_check.rb +41 -0
  45. data/lib/simplecov/exit_codes/minimum_overall_coverage_check.rb +38 -21
  46. data/lib/simplecov/exit_codes.rb +3 -0
  47. data/lib/simplecov/exit_handling.rb +158 -0
  48. data/lib/simplecov/file_list.rb +61 -17
  49. data/lib/simplecov/filter.rb +69 -24
  50. data/lib/simplecov/formatter/base.rb +101 -0
  51. data/lib/simplecov/formatter/html_formatter/public/application.css +1 -0
  52. data/lib/simplecov/formatter/html_formatter/public/application.js +18 -0
  53. data/lib/simplecov/formatter/html_formatter/public/favicon_green.png +0 -0
  54. data/lib/simplecov/formatter/html_formatter/public/favicon_red.png +0 -0
  55. data/lib/simplecov/formatter/html_formatter/public/favicon_yellow.png +0 -0
  56. data/lib/simplecov/formatter/html_formatter/public/index.html +56 -0
  57. data/lib/simplecov/formatter/html_formatter.rb +79 -0
  58. data/lib/simplecov/formatter/json_formatter/errors_formatter.rb +84 -0
  59. data/lib/simplecov/formatter/json_formatter/result_hash_formatter.rb +128 -0
  60. data/lib/simplecov/formatter/json_formatter/source_file_formatter.rb +99 -0
  61. data/lib/simplecov/formatter/json_formatter.rb +87 -0
  62. data/lib/simplecov/formatter/multi_formatter.rb +4 -5
  63. data/lib/simplecov/formatter/simple_formatter.rb +9 -11
  64. data/lib/simplecov/formatter.rb +4 -0
  65. data/lib/simplecov/last_run.rb +10 -3
  66. data/lib/simplecov/lines_classifier.rb +25 -13
  67. data/lib/simplecov/load_global_config.rb +9 -4
  68. data/lib/simplecov/parallel_adapters/base.rb +61 -0
  69. data/lib/simplecov/parallel_adapters/generic.rb +44 -0
  70. data/lib/simplecov/parallel_adapters/parallel_tests.rb +93 -0
  71. data/lib/simplecov/parallel_adapters.rb +83 -0
  72. data/lib/simplecov/parallel_coordination.rb +129 -0
  73. data/lib/simplecov/process.rb +31 -13
  74. data/lib/simplecov/profiles/bundler_filter.rb +1 -1
  75. data/lib/simplecov/profiles/hidden_filter.rb +1 -1
  76. data/lib/simplecov/profiles/rails.rb +24 -10
  77. data/lib/simplecov/profiles/root_filter.rb +6 -5
  78. data/lib/simplecov/profiles/strict.rb +32 -0
  79. data/lib/simplecov/profiles/test_frameworks.rb +1 -4
  80. data/lib/simplecov/profiles.rb +32 -3
  81. data/lib/simplecov/result/missing_source_files_reporter.rb +49 -0
  82. data/lib/simplecov/result/source_file_builder.rb +51 -0
  83. data/lib/simplecov/result.rb +108 -19
  84. data/lib/simplecov/result_adapter.rb +70 -8
  85. data/lib/simplecov/result_merger/legacy_format_adapter.rb +28 -0
  86. data/lib/simplecov/result_merger/resultset_file.rb +38 -0
  87. data/lib/simplecov/result_merger/resultset_store.rb +50 -0
  88. data/lib/simplecov/result_merger.rb +59 -91
  89. data/lib/simplecov/result_processing.rb +179 -0
  90. data/lib/simplecov/simulate_coverage.rb +54 -8
  91. data/lib/simplecov/source_file/branch.rb +1 -3
  92. data/lib/simplecov/source_file/branch_builder.rb +114 -0
  93. data/lib/simplecov/source_file/builder_context.rb +28 -0
  94. data/lib/simplecov/source_file/line.rb +7 -2
  95. data/lib/simplecov/source_file/line_builder.rb +43 -0
  96. data/lib/simplecov/source_file/method.rb +52 -0
  97. data/lib/simplecov/source_file/method_builder.rb +58 -0
  98. data/lib/simplecov/source_file/ruby_data_parser.rb +88 -0
  99. data/lib/simplecov/source_file/skip_chunks.rb +75 -0
  100. data/lib/simplecov/source_file/source_loader.rb +63 -0
  101. data/lib/simplecov/source_file/statistics.rb +57 -0
  102. data/lib/simplecov/source_file.rb +66 -232
  103. data/lib/simplecov/static_coverage_extractor/method_collector.rb +55 -0
  104. data/lib/simplecov/static_coverage_extractor/visitor.rb +171 -0
  105. data/lib/simplecov/static_coverage_extractor.rb +110 -0
  106. data/lib/simplecov/useless_results_remover.rb +16 -7
  107. data/lib/simplecov/version.rb +1 -1
  108. data/lib/simplecov-html.rb +4 -0
  109. data/lib/simplecov.rb +156 -371
  110. data/lib/simplecov_json_formatter.rb +4 -0
  111. data/schemas/coverage-v1.0.schema.json +306 -0
  112. data/schemas/coverage.schema.json +306 -0
  113. metadata +90 -57
  114. data/CHANGELOG.md +0 -191
  115. data/lib/simplecov/default_formatter.rb +0 -20
@@ -0,0 +1,197 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleCov
4
+ # Inclusion / exclusion / grouping methods: `cover`, `skip`, `group`,
5
+ # plus the deprecated `track_files` / `add_filter` / `add_group`
6
+ # aliases. Mutates the same `filters`, `groups`, and `cover_filters`
7
+ # collections the main Configuration module exposes.
8
+ module Configuration
9
+ attr_writer :filters, :groups
10
+
11
+ #
12
+ # Restrict the universe of files in the coverage report to those matching
13
+ # one or more globs, regexps, or block predicates. Multiple calls union;
14
+ # when any `cover` matcher is configured the report drops every file that
15
+ # doesn't match at least one of them.
16
+ #
17
+ # Strings are interpreted as shell globs (e.g. "lib/**/*.rb"), not
18
+ # substring matches — this is a deliberate departure from the legacy
19
+ # `add_filter` semantics and matches the way `track_files` already
20
+ # interprets its argument.
21
+ #
22
+ # When the matcher is a string-glob, `cover` also expands the glob on
23
+ # disk so files that exist but were never required during the run still
24
+ # appear in the report (at 0% coverage). This is the "include unloaded
25
+ # files" half of the legacy `track_files` behavior, rolled into the
26
+ # same call.
27
+ #
28
+ # SimpleCov.start do
29
+ # cover "lib/**/*.rb", "app/**/*.rb"
30
+ # cover(/_helper\.rb\z/)
31
+ # cover { |sf| sf.lines.count > 5 }
32
+ # end
33
+ #
34
+ def cover(*args, &block)
35
+ args.each { |arg| cover_filters << build_cover_filter(arg) }
36
+ cover_filters << SimpleCov::BlockFilter.new(block) if block
37
+ cover_filters
38
+ end
39
+
40
+ # Returns the list of configured inclusion filters added via `cover`.
41
+ def cover_filters
42
+ @cover_filters ||= []
43
+ end
44
+
45
+ # Returns the list of string globs passed to `cover` — used by the
46
+ # disk-discovery pass in `SimpleCov.add_not_loaded_files` so files
47
+ # matching a `cover` glob appear in the report even when they were
48
+ # never required during the suite.
49
+ #
50
+ # Walks into `ArrayFilter` entries (built when a caller passes an
51
+ # array to `cover`) so a glob nested inside `cover(["lib/**/*.rb",
52
+ # /helper\.rb\z/])` still drives unloaded-file discovery.
53
+ def cover_globs
54
+ collect_cover_globs(cover_filters)
55
+ end
56
+
57
+ # DEPRECATED: prefer `cover`, which both includes unloaded files (the
58
+ # historical `track_files` behavior) and restricts the report to the
59
+ # matching set.
60
+ def track_files(glob)
61
+ SimpleCov::Deprecation.warn("`SimpleCov.track_files` is deprecated. " \
62
+ "#{track_files_replacement_hint(glob)}")
63
+ @tracked_files = glob
64
+ end
65
+
66
+ # `track_files(nil)` is the documented way to clear a previously-set
67
+ # glob, but `cover(nil)` raises `ConfigurationError`, so don't point
68
+ # users at it. The `cover` API has no direct equivalent for "reset
69
+ # the inclusion list" — point users at the `@cover_filters` reset.
70
+ def track_files_replacement_hint(glob)
71
+ if glob.nil?
72
+ "Replace with `SimpleCov.cover_filters.clear` — clearing the inclusion list."
73
+ else
74
+ "Replace with `SimpleCov.cover #{glob.inspect}` — `cover` includes unloaded files on disk " \
75
+ "(the historical `track_files` behavior) and also restricts the report to the matching set. " \
76
+ "If you want to keep additional files outside #{glob.inspect} in the report, pass every " \
77
+ "directory you care about, e.g. `cover #{glob.inspect}, \"app/**/*.rb\"`."
78
+ end
79
+ end
80
+
81
+ # Returns the glob used to include files that were not explicitly required.
82
+ def tracked_files
83
+ @tracked_files if defined?(@tracked_files)
84
+ end
85
+
86
+ # Returns the list of configured exclusion filters added via `skip`
87
+ # (or the deprecated `add_filter`).
88
+ def filters
89
+ @filters ||= []
90
+ end
91
+
92
+ #
93
+ # Drop matching files from the coverage report. The inverse of `cover`.
94
+ #
95
+ # See README for the full grammar; `skip` accepts a String (path-segment
96
+ # substring), Regexp, block predicate, or Array of any of those.
97
+ #
98
+ def skip(filter_argument = nil, &)
99
+ filters << parse_filter(filter_argument, &)
100
+ end
101
+
102
+ # DEPRECATED: alias for `skip`. Same matcher grammar, identical behavior.
103
+ def add_filter(filter_argument = nil, &block)
104
+ example = block ? "`SimpleCov.skip { ... }`" : "`SimpleCov.skip #{filter_argument.inspect}`"
105
+ SimpleCov::Deprecation.warn("`SimpleCov.add_filter` is deprecated. " \
106
+ "Replace with `SimpleCov.skip` (same arguments, same behavior). Example: #{example}.")
107
+ skip(filter_argument, &block)
108
+ end
109
+
110
+ # Remove any filters whose `filter_argument` equals the given value.
111
+ # Returns true when at least one filter was removed, false otherwise.
112
+ def remove_filter(filter_argument) # rubocop:disable Naming/PredicateMethod
113
+ before = filters.size
114
+ filters.reject! { |filter| filter.respond_to?(:filter_argument) && filter.filter_argument == filter_argument }
115
+ filters.size != before
116
+ end
117
+
118
+ # Remove every filter from the chain, including the defaults installed
119
+ # by `SimpleCov.start`.
120
+ def clear_filters
121
+ @filters = []
122
+ end
123
+
124
+ # Returns the configured groups. Add groups using SimpleCov.group.
125
+ def groups
126
+ @groups ||= {}
127
+ end
128
+
129
+ # Define a display group for files. Same matcher grammar as `skip`,
130
+ # but instead of dropping the matching files it bins them under
131
+ # `group_name` for the formatter. Files matched by no group fall
132
+ # into the implicit "Ungrouped" bucket.
133
+ def group(group_name, filter_argument = nil, &)
134
+ groups[group_name] = parse_filter(filter_argument, &)
135
+ end
136
+
137
+ # DEPRECATED: alias for `group`. Same arguments, same behavior.
138
+ def add_group(group_name, filter_argument = nil, &block)
139
+ example = if block
140
+ "`SimpleCov.group #{group_name.inspect} { ... }`"
141
+ else
142
+ "`SimpleCov.group #{group_name.inspect}, #{filter_argument.inspect}`"
143
+ end
144
+ SimpleCov::Deprecation.warn(
145
+ "`SimpleCov.add_group` is deprecated. " \
146
+ "Replace with `SimpleCov.group` (same arguments, same behavior). Example: #{example}."
147
+ )
148
+ group(group_name, filter_argument, &block)
149
+ end
150
+
151
+ # Drop every filter previously installed (defaults plus anything
152
+ # earlier in this block) so subsequent `skip` calls start from a
153
+ # clean slate. Order matters — call this before your own `skip`
154
+ # invocations.
155
+ def no_default_skips
156
+ clear_filters
157
+ end
158
+
159
+ private
160
+
161
+ # The actual filter processor. Not meant for direct use.
162
+ def parse_filter(filter_argument = nil, &filter_proc)
163
+ filter = filter_argument || filter_proc
164
+
165
+ raise ArgumentError, "Please specify either a filter or a block to filter with" unless filter
166
+
167
+ SimpleCov::Filter.build_filter(filter)
168
+ end
169
+
170
+ # Build a filter for a `cover` argument. Strings are treated as
171
+ # globs (not substrings — that's `skip`/`add_filter`'s semantics).
172
+ def build_cover_filter(arg)
173
+ case arg
174
+ when String then SimpleCov::GlobFilter.new(arg)
175
+ when Regexp then SimpleCov::RegexFilter.new(arg)
176
+ when Proc then SimpleCov::BlockFilter.new(arg)
177
+ when SimpleCov::Filter then arg
178
+ when Array then SimpleCov::ArrayFilter.new(arg.map { |a| build_cover_filter(a) })
179
+ else raise SimpleCov::ConfigurationError, "Unsupported `cover` argument #{arg.inspect}; " \
180
+ "expected a String glob, Regexp, Proc, " \
181
+ "SimpleCov::Filter, or Array of those."
182
+ end
183
+ end
184
+
185
+ # Walk a list of cover filters and return the string globs they hold,
186
+ # descending into `ArrayFilter` wrappers built by `cover(["a", "b"])`.
187
+ def collect_cover_globs(filter_list)
188
+ filter_list.flat_map do |filter|
189
+ case filter
190
+ when SimpleCov::GlobFilter then filter.filter_argument
191
+ when SimpleCov::ArrayFilter then collect_cover_globs(filter.filter_argument)
192
+ else []
193
+ end
194
+ end
195
+ end
196
+ end
197
+ end
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../formatter/multi_formatter"
4
+
5
+ module SimpleCov
6
+ # Formatter selection (`formatter` / `formatters`), reporting toggles
7
+ # (`print_errors`), and the deprecated `# :nocov:` token hook.
8
+ module Configuration
9
+ attr_writer :formatter, :print_error_status
10
+
11
+ #
12
+ # Gets or sets the configured formatter. Pass `false` (or `nil`) to
13
+ # opt out of formatting entirely — worker processes in big parallel
14
+ # CI setups (see #964) only need their `.resultset.json` on disk so
15
+ # a final `SimpleCov.collate` job can produce the report; running
16
+ # them without a formatter saves the per-job HTML/multi-formatter
17
+ # overhead.
18
+ #
19
+ def formatter(formatter = :__no_arg__)
20
+ return @formatter if formatter == :__no_arg__
21
+
22
+ @formatter = formatter || nil # normalize `false` to `nil`
23
+ end
24
+
25
+ # Sets the configured formatters. Pass `[]` to opt out of
26
+ # formatting entirely; see `formatter` for the rationale.
27
+ def formatters(formatters = :__no_arg__)
28
+ return Array(formatter) if formatters == :__no_arg__
29
+
30
+ self.formatters = formatters
31
+ formatters
32
+ end
33
+
34
+ # Sets the configured formatters. Equivalent to `formatters [...]`.
35
+ def formatters=(formatters)
36
+ @formatter = formatters.empty? ? nil : SimpleCov::Formatter::MultiFormatter.new(formatters)
37
+ end
38
+
39
+ #
40
+ # Get or set whether SimpleCov colorizes its stderr diagnostics. Accepts
41
+ # `true` (always on), `false` (always off), or `:auto` (default: defer
42
+ # to `SimpleCov::Color`, which checks `$stderr.tty?` with `NO_COLOR`
43
+ # and `FORCE_COLOR` overrides). An explicit `true`/`false` wins over
44
+ # both auto-detection and the env vars, which is the right escape
45
+ # hatch when stderr is being piped through a wrapper that still
46
+ # renders ANSI in its own terminal (parallel_tests with
47
+ # `--combine-stderr`, log multiplexers, some CI runners). See #1157.
48
+ #
49
+ def color(value = :__no_arg__)
50
+ return defined?(@color) ? @color : :auto if value == :__no_arg__
51
+
52
+ @color = value
53
+ end
54
+
55
+ #
56
+ # Get or set whether SimpleCov prints its own diagnostic warnings to
57
+ # stderr. Covers per-check threshold violations, the trailing
58
+ # "SimpleCov failed with exit ..." summary, and the deferred-report /
59
+ # previous-error notices. Defaults to true. Set to false to silence
60
+ # SimpleCov entirely when parsing tooling output (see issue #1155).
61
+ #
62
+ def print_errors(value = :__no_arg__)
63
+ return defined?(@print_error_status) ? @print_error_status : true if value == :__no_arg__
64
+
65
+ @print_error_status = value
66
+ end
67
+
68
+ #
69
+ # Get or set whether `coverage.json` includes the full source-text
70
+ # array for every file. Defaults to true. Set to false when a
71
+ # downstream tool reads the project's source files directly and
72
+ # only needs the coverage metrics, so `coverage.json` doesn't carry
73
+ # a copy of the source tree (which dominates the payload on larger
74
+ # projects).
75
+ #
76
+ # The HTML viewer's `coverage_data.js` always includes source —
77
+ # the client-side renderer needs it. Only `coverage.json` honors
78
+ # this setting.
79
+ #
80
+ # SimpleCov.start do
81
+ # source_in_json false
82
+ # end
83
+ #
84
+ def source_in_json(value = :__no_arg__)
85
+ return defined?(@source_in_json) ? @source_in_json : true if value == :__no_arg__
86
+
87
+ @source_in_json = value
88
+ end
89
+
90
+ # DEPRECATED: alias for `print_errors`. Same value, same behavior.
91
+ def print_error_status
92
+ SimpleCov::Deprecation.warn("`SimpleCov.print_error_status` is deprecated. " \
93
+ "Replace with `SimpleCov.print_errors` (same value).")
94
+ defined?(@print_error_status) ? @print_error_status : true
95
+ end
96
+
97
+ #
98
+ # DEPRECATED: configure `# :nocov:` token override. Prefer
99
+ # `# simplecov:disable` / `# simplecov:enable` block comments (see
100
+ # SimpleCov::Directive). The `# :nocov:` toggle and this hook will
101
+ # be removed in a future release.
102
+ #
103
+ def nocov_token(nocov_token = nil)
104
+ SimpleCov::Deprecation.warn("`SimpleCov.nocov_token` and `SimpleCov.skip_token` are deprecated. " \
105
+ "Replace with `# simplecov:disable` / `# simplecov:enable` block comments.")
106
+ current_nocov_token(nocov_token)
107
+ end
108
+ alias skip_token nocov_token
109
+
110
+ # Internal accessor used by SimpleCov to recognise `# :nocov:`
111
+ # markers without emitting the public-API deprecation warning. Will
112
+ # be removed alongside the deprecated `nocov_token` setter.
113
+ def current_nocov_token(value = nil)
114
+ return @nocov_token if defined?(@nocov_token) && value.nil?
115
+
116
+ @nocov_token = value || "nocov"
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleCov
4
+ # Coverage-entry filters scoped to branch / method criteria. See
5
+ # `ignore_branches` and `ignore_methods`, and #1033 /
6
+ # #1046 for the synthetic-entry detection rationale.
7
+ module Configuration
8
+ # Branch types accepted by `ignore_branches`.
9
+ IGNORABLE_BRANCH_TYPES = %i[implicit_else eval_generated].freeze
10
+ # Method types accepted by `ignore_methods`.
11
+ IGNORABLE_METHOD_TYPES = %i[eval_generated].freeze
12
+
13
+ # Variadic; multiple calls union. Setting is recorded regardless
14
+ # of whether branch coverage is enabled at call time. See #1033, #1046.
15
+ def ignore_branches(*types)
16
+ types.each { |type| raise_if_branch_type_unsupported(type) }
17
+ ignored_branches.concat(types).uniq!
18
+ ignored_branches
19
+ end
20
+
21
+ def ignored_branches
22
+ @ignored_branches ||= []
23
+ end
24
+
25
+ def ignored_branch?(type)
26
+ ignored_branches.include?(type)
27
+ end
28
+
29
+ # See `ignore_branches`. The only supported method-type token today
30
+ # is `:eval_generated`; see #1046 for the rationale.
31
+ def ignore_methods(*types)
32
+ types.each { |type| raise_if_method_type_unsupported(type) }
33
+ ignored_methods.concat(types).uniq!
34
+ ignored_methods
35
+ end
36
+
37
+ def ignored_methods
38
+ @ignored_methods ||= []
39
+ end
40
+
41
+ def ignored_method?(type)
42
+ ignored_methods.include?(type)
43
+ end
44
+
45
+ private
46
+
47
+ def raise_if_branch_type_unsupported(type)
48
+ return if IGNORABLE_BRANCH_TYPES.member?(type)
49
+
50
+ raise SimpleCov::ConfigurationError,
51
+ "Unsupported branch type #{type.inspect} for `ignore_branches`. " \
52
+ "Supported values are #{IGNORABLE_BRANCH_TYPES.inspect}"
53
+ end
54
+
55
+ def raise_if_method_type_unsupported(type)
56
+ return if IGNORABLE_METHOD_TYPES.member?(type)
57
+
58
+ raise SimpleCov::ConfigurationError,
59
+ "Unsupported method type #{type.inspect} for `ignore_methods`. " \
60
+ "Supported values are #{IGNORABLE_METHOD_TYPES.inspect}"
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,164 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleCov
4
+ # Result merging and subprocess / parallel-test coordination:
5
+ # `merging`, `merge_subprocesses`, `merge_timeout`, `parallel_tests`.
6
+ module Configuration
7
+ #
8
+ # Get or set whether SimpleCov should hook `Process._fork` to
9
+ # attach itself to subprocesses. Required when the suite uses
10
+ # parallel test workers (e.g. Rails' `parallelize(workers:)`).
11
+ # Defaults to false.
12
+ #
13
+ def merge_subprocesses(value = nil)
14
+ return @enable_for_subprocesses if defined?(@enable_for_subprocesses) && value.nil?
15
+
16
+ @enable_for_subprocesses = value || false
17
+ end
18
+
19
+ # @api private — predicate used by `start_tracking` to decide
20
+ # whether to install the fork hook.
21
+ def enabled_for_subprocesses?
22
+ defined?(@enable_for_subprocesses) ? @enable_for_subprocesses : false
23
+ end
24
+
25
+ #
26
+ # Get or set whether SimpleCov should auto-require the
27
+ # `parallel_tests` gem when it sees `TEST_ENV_NUMBER` /
28
+ # `PARALLEL_TEST_GROUPS` in the environment. Defaults to auto-detect
29
+ # (nil). See #1018.
30
+ #
31
+ def parallel_tests(value = :__no_arg__)
32
+ return defined?(@parallel_tests) ? @parallel_tests : nil if value == :__no_arg__
33
+
34
+ @parallel_tests = value
35
+ end
36
+
37
+ # DEPRECATED: alias for `merge_subprocesses`. Same value/behavior.
38
+ def enable_for_subprocesses(value = nil)
39
+ SimpleCov::Deprecation.warn("`SimpleCov.enable_for_subprocesses` is deprecated. " \
40
+ "Replace with `SimpleCov.merge_subprocesses` (same value, same behavior).")
41
+ return @enable_for_subprocesses if defined?(@enable_for_subprocesses) && value.nil?
42
+
43
+ @enable_for_subprocesses = value || false
44
+ end
45
+
46
+ #
47
+ # Get or set whether to merge results from multiple test suites
48
+ # (test:units, test:functionals, cucumber, ...) into a single
49
+ # coverage report. Defaults to true.
50
+ #
51
+ def merging(use = nil)
52
+ @use_merging = use unless use.nil?
53
+ @use_merging = true unless defined?(@use_merging) && @use_merging == false
54
+ @use_merging
55
+ end
56
+
57
+ #
58
+ # Get or set whether this process owns final merge processing:
59
+ # waiting for sibling workers, building the merged result, formatting,
60
+ # enforcing thresholds, and writing `.last_run.json`.
61
+ #
62
+ # Defaults to true, except for recognized multi-worker parallel runs
63
+ # that explicitly write to a custom coverage destination while merging
64
+ # is enabled. Those runs are likely using an external `SimpleCov.collate`
65
+ # step to finalize the merge.
66
+ #
67
+ def finalize_merge(value = :__no_arg__)
68
+ unless value == :__no_arg__
69
+ @finalize_merge = value
70
+ @finalize_merge_explicit = true
71
+ end
72
+
73
+ return @finalize_merge if defined?(@finalize_merge_explicit) && @finalize_merge_explicit
74
+
75
+ inferred = inferred_finalize_merge?
76
+ warn_about_inferred_finalize_merge unless inferred
77
+ inferred
78
+ end
79
+
80
+ def finalize_merge?
81
+ finalize_merge
82
+ end
83
+
84
+ # @api private
85
+ def merge_finalization_owner?
86
+ collating_result? || finalize_merge?
87
+ end
88
+
89
+ # DEPRECATED: alias for `merging`. Same value, same behavior.
90
+ def use_merging(use = nil)
91
+ SimpleCov::Deprecation.warn("`SimpleCov.use_merging` is deprecated. " \
92
+ "Replace with `SimpleCov.merging` (same value, same behavior).")
93
+ @use_merging = use unless use.nil?
94
+ @use_merging = true unless defined?(@use_merging) && @use_merging == false
95
+ end
96
+
97
+ #
98
+ # Defines the maximum age (in seconds) of a resultset to still be
99
+ # included in merged results. Default is 600 seconds (10 minutes).
100
+ #
101
+ def merge_timeout(seconds = nil)
102
+ @merge_timeout = seconds if seconds.is_a?(Integer)
103
+ @merge_timeout ||= 600
104
+ end
105
+
106
+ #
107
+ # Defines how long (in seconds) the reporting process waits for the
108
+ # remaining parallel-test workers to write their resultsets before it
109
+ # proceeds with a partial merge. Default is 60 seconds. Raise it when a
110
+ # slow worker routinely finishes well after the others, so its coverage
111
+ # is still included and the minimum / maximum coverage checks aren't
112
+ # skipped against a partial total.
113
+ #
114
+ def parallel_wait_timeout(seconds = nil)
115
+ @parallel_wait_timeout = seconds if seconds.is_a?(Integer)
116
+ @parallel_wait_timeout ||= 60
117
+ end
118
+
119
+ private
120
+
121
+ def inferred_finalize_merge?
122
+ return true unless merging
123
+
124
+ adapter = SimpleCov::ParallelAdapters.current
125
+ return true unless adapter
126
+ return true unless adapter.expected_worker_count > 1
127
+ return true unless parallel_worker_environment?
128
+ return true unless explicit_custom_coverage_destination?
129
+
130
+ false
131
+ end
132
+
133
+ def parallel_worker_environment?
134
+ ENV.key?("TEST_ENV_NUMBER") || ENV.key?("PARALLEL_TEST_GROUPS")
135
+ end
136
+
137
+ def explicit_custom_coverage_destination?
138
+ return false unless explicit_coverage_destination?
139
+
140
+ coverage_path != File.expand_path("coverage", root)
141
+ end
142
+
143
+ def explicit_coverage_destination?
144
+ (defined?(@coverage_path_explicit) && @coverage_path_explicit) ||
145
+ (defined?(@coverage_dir_explicit) && @coverage_dir_explicit)
146
+ end
147
+
148
+ def warn_about_inferred_finalize_merge
149
+ return if defined?(@finalize_merge_inference_warned) && @finalize_merge_inference_warned
150
+ return unless print_errors
151
+
152
+ @finalize_merge_inference_warned = true
153
+ warn SimpleCov::Color.colorize(inferred_finalize_merge_warning, :yellow)
154
+ end
155
+
156
+ def inferred_finalize_merge_warning
157
+ "SimpleCov inferred `finalize_merge false` because this parallel worker is merging " \
158
+ "into a custom coverage destination. Set `SimpleCov.finalize_merge false` to keep " \
159
+ "external collation ownership, or `SimpleCov.finalize_merge true` if this worker " \
160
+ "should wait, merge, format, enforce thresholds, and write `.last_run.json`. " \
161
+ "See https://github.com/simplecov-ruby/simplecov#merge-finalization-ownership."
162
+ end
163
+ end
164
+ end