simplecov 0.22.0 → 1.1.1

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 (137) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +1 -1
  3. data/README.md +71 -916
  4. data/exe/simplecov +11 -0
  5. data/lib/minitest/simplecov_plugin.rb +13 -5
  6. data/lib/simplecov/atomic_file.rb +70 -0
  7. data/lib/simplecov/autostart.rb +11 -0
  8. data/lib/simplecov/cli/clean.rb +87 -0
  9. data/lib/simplecov/cli/command_helpers.rb +55 -0
  10. data/lib/simplecov/cli/coverage.rb +86 -0
  11. data/lib/simplecov/cli/coverage_file.rb +65 -0
  12. data/lib/simplecov/cli/diff.rb +147 -0
  13. data/lib/simplecov/cli/dotfile.rb +111 -0
  14. data/lib/simplecov/cli/merge.rb +120 -0
  15. data/lib/simplecov/cli/open.rb +48 -0
  16. data/lib/simplecov/cli/report.rb +97 -0
  17. data/lib/simplecov/cli/run.rb +36 -0
  18. data/lib/simplecov/cli/serve/report_preparer.rb +29 -0
  19. data/lib/simplecov/cli/serve/static_file_handler.rb +122 -0
  20. data/lib/simplecov/cli/serve.rb +85 -0
  21. data/lib/simplecov/cli/uncovered.rb +103 -0
  22. data/lib/simplecov/cli.rb +163 -0
  23. data/lib/simplecov/color.rb +83 -0
  24. data/lib/simplecov/combine/branches_combiner.rb +54 -7
  25. data/lib/simplecov/combine/coverage_accumulator.rb +268 -0
  26. data/lib/simplecov/combine/identity_interner.rb +30 -0
  27. data/lib/simplecov/combine/interned_counts.rb +30 -0
  28. data/lib/simplecov/combine/lines_combiner.rb +53 -23
  29. data/lib/simplecov/combine/methods_combiner.rb +74 -0
  30. data/lib/simplecov/combine/results_combiner.rb +13 -36
  31. data/lib/simplecov/combine.rb +5 -23
  32. data/lib/simplecov/command_guesser.rb +104 -33
  33. data/lib/simplecov/configuration/coverage.rb +172 -0
  34. data/lib/simplecov/configuration/coverage_criteria.rb +154 -0
  35. data/lib/simplecov/configuration/eval_coverage.rb +41 -0
  36. data/lib/simplecov/configuration/filters.rb +164 -0
  37. data/lib/simplecov/configuration/formatting.rb +138 -0
  38. data/lib/simplecov/configuration/groups.rb +42 -0
  39. data/lib/simplecov/configuration/ignored_entries.rb +63 -0
  40. data/lib/simplecov/configuration/merging.rb +165 -0
  41. data/lib/simplecov/configuration/thresholds.rb +179 -0
  42. data/lib/simplecov/configuration.rb +83 -420
  43. data/lib/simplecov/coverage_json.rb +24 -0
  44. data/lib/simplecov/coverage_statistics.rb +16 -10
  45. data/lib/simplecov/coverage_violations.rb +159 -0
  46. data/lib/simplecov/defaults.rb +34 -23
  47. data/lib/simplecov/deprecation.rb +45 -0
  48. data/lib/simplecov/directive.rb +167 -0
  49. data/lib/simplecov/exit_codes/check.rb +33 -0
  50. data/lib/simplecov/exit_codes/exit_code_handling.rb +8 -2
  51. data/lib/simplecov/exit_codes/maximum_coverage_drop_check.rb +18 -65
  52. data/lib/simplecov/exit_codes/maximum_overall_coverage_check.rb +32 -0
  53. data/lib/simplecov/exit_codes/minimum_coverage_by_file_check.rb +17 -36
  54. data/lib/simplecov/exit_codes/minimum_coverage_by_group_check.rb +30 -0
  55. data/lib/simplecov/exit_codes/minimum_overall_coverage_check.rb +37 -33
  56. data/lib/simplecov/exit_codes.rb +20 -0
  57. data/lib/simplecov/exit_handling.rb +128 -0
  58. data/lib/simplecov/file_list.rb +70 -24
  59. data/lib/simplecov/filter.rb +105 -26
  60. data/lib/simplecov/formatter/base.rb +118 -0
  61. data/lib/simplecov/formatter/coverage_json_writer.rb +97 -0
  62. data/lib/simplecov/formatter/html_formatter/public/index.html +87 -0
  63. data/lib/simplecov/formatter/html_formatter/viewer_data_validator.rb +96 -0
  64. data/lib/simplecov/formatter/html_formatter.rb +105 -0
  65. data/lib/simplecov/formatter/json_formatter/errors_formatter.rb +88 -0
  66. data/lib/simplecov/formatter/json_formatter/result_hash_formatter.rb +118 -0
  67. data/lib/simplecov/formatter/json_formatter/source_file_formatter.rb +93 -0
  68. data/lib/simplecov/formatter/json_formatter.rb +46 -0
  69. data/lib/simplecov/formatter/multi_formatter.rb +12 -8
  70. data/lib/simplecov/formatter/simple_formatter.rb +14 -11
  71. data/lib/simplecov/formatter.rb +16 -0
  72. data/lib/simplecov/group_names.rb +32 -0
  73. data/lib/simplecov/last_run.rb +18 -4
  74. data/lib/simplecov/lines_classifier.rb +48 -15
  75. data/lib/simplecov/load_global_config.rb +12 -4
  76. data/lib/simplecov/parallel_adapters/base.rb +78 -0
  77. data/lib/simplecov/parallel_adapters/generic.rb +45 -0
  78. data/lib/simplecov/parallel_adapters/parallel_tests.rb +93 -0
  79. data/lib/simplecov/parallel_adapters.rb +85 -0
  80. data/lib/simplecov/parallel_coordination.rb +134 -0
  81. data/lib/simplecov/parallel_result_merger.rb +230 -0
  82. data/lib/simplecov/process.rb +52 -12
  83. data/lib/simplecov/profiles/bundler_filter.rb +1 -1
  84. data/lib/simplecov/profiles/hidden_filter.rb +1 -1
  85. data/lib/simplecov/profiles/rails.rb +24 -10
  86. data/lib/simplecov/profiles/root_filter.rb +6 -5
  87. data/lib/simplecov/profiles/strict.rb +32 -0
  88. data/lib/simplecov/profiles/test_frameworks.rb +1 -4
  89. data/lib/simplecov/profiles.rb +32 -3
  90. data/lib/simplecov/report_deferral.rb +49 -0
  91. data/lib/simplecov/report_stamp.rb +28 -0
  92. data/lib/simplecov/result/missing_source_files_reporter.rb +49 -0
  93. data/lib/simplecov/result/source_file_builder.rb +51 -0
  94. data/lib/simplecov/result.rb +145 -26
  95. data/lib/simplecov/result_adapter.rb +139 -8
  96. data/lib/simplecov/result_merger/legacy_format_adapter.rb +28 -0
  97. data/lib/simplecov/result_merger/resultset_file.rb +74 -0
  98. data/lib/simplecov/result_merger/resultset_run_identity.rb +67 -0
  99. data/lib/simplecov/result_merger/resultset_store.rb +53 -0
  100. data/lib/simplecov/result_merger/unloaded_files.rb +103 -0
  101. data/lib/simplecov/result_merger.rb +109 -114
  102. data/lib/simplecov/result_processing.rb +218 -0
  103. data/lib/simplecov/run_identity.rb +77 -0
  104. data/lib/simplecov/simulate_coverage.rb +83 -12
  105. data/lib/simplecov/source_file/branch.rb +1 -3
  106. data/lib/simplecov/source_file/branch_builder.rb +114 -0
  107. data/lib/simplecov/source_file/builder_context.rb +28 -0
  108. data/lib/simplecov/source_file/line.rb +9 -4
  109. data/lib/simplecov/source_file/line_builder.rb +43 -0
  110. data/lib/simplecov/source_file/method.rb +58 -0
  111. data/lib/simplecov/source_file/method_builder.rb +60 -0
  112. data/lib/simplecov/source_file/ruby_data_parser.rb +110 -0
  113. data/lib/simplecov/source_file/skip_chunks.rb +72 -0
  114. data/lib/simplecov/source_file/source_loader.rb +79 -0
  115. data/lib/simplecov/source_file/statistics.rb +65 -0
  116. data/lib/simplecov/source_file.rb +66 -232
  117. data/lib/simplecov/static_coverage_extractor/condition_folding.rb +239 -0
  118. data/lib/simplecov/static_coverage_extractor/location_conventions.rb +236 -0
  119. data/lib/simplecov/static_coverage_extractor/method_collector.rb +62 -0
  120. data/lib/simplecov/static_coverage_extractor/prism_compat.rb +55 -0
  121. data/lib/simplecov/static_coverage_extractor/value_position.rb +74 -0
  122. data/lib/simplecov/static_coverage_extractor/visitor.rb +183 -0
  123. data/lib/simplecov/static_coverage_extractor.rb +110 -0
  124. data/lib/simplecov/unloaded_file_injector.rb +75 -0
  125. data/lib/simplecov/useless_results_remover.rb +16 -7
  126. data/lib/simplecov/version.rb +1 -1
  127. data/lib/simplecov-html.rb +4 -0
  128. data/lib/simplecov.rb +185 -380
  129. data/lib/simplecov_json_formatter.rb +4 -0
  130. data/sig/simplecov.rbs +1777 -0
  131. metadata +111 -62
  132. data/CHANGELOG.md +0 -191
  133. data/doc/alternate-formatters.md +0 -71
  134. data/doc/commercial-services.md +0 -25
  135. data/doc/editor-integration.md +0 -18
  136. data/lib/simplecov/combine/files_combiner.rb +0 -24
  137. data/lib/simplecov/default_formatter.rb +0 -20
@@ -0,0 +1,154 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleCov
4
+ # Selection and validation of the coverage criteria Ruby's `Coverage`
5
+ # library should track. Supports `:line` (the historical default),
6
+ # `:branch`, `:method`, and `:oneshot_line`. The standalone `:eval`
7
+ # toggle lives in `eval_coverage.rb`.
8
+ module Configuration
9
+ SUPPORTED_COVERAGE_CRITERIA = %i[line branch method oneshot_line].freeze
10
+ DEFAULT_COVERAGE_CRITERION = :line
11
+ ONESHOT_LINE_COVERAGE_CRITERION = :oneshot_line
12
+ LINE_COVERAGE_ALTERNATIVES = {line: :oneshot_line, oneshot_line: :line}.freeze #: line_coverage_alternatives
13
+ private_constant :LINE_COVERAGE_ALTERNATIVES
14
+
15
+ # Enable one or more coverage criteria. `:eval` is accepted as a
16
+ # shorthand for the standalone eval-coverage toggle.
17
+ def enable_coverage(*criteria)
18
+ criteria.each { |criterion| criterion == :eval ? enable_eval_coverage : add_coverage_criterion(criterion) }
19
+ end
20
+
21
+ # Remove `criterion` from the set of enabled coverage criteria.
22
+ # `:eval` turns the standalone eval-coverage toggle back off,
23
+ # mirroring `enable_coverage`. Disabling every criterion raises at
24
+ # `start_tracking` (not here), so config files that toggle criteria
25
+ # in arbitrary order don't have to worry about transient empty states.
26
+ def disable_coverage(criterion)
27
+ return disable_eval_coverage if criterion == :eval
28
+
29
+ raise_if_criterion_unsupported(criterion)
30
+ coverage_criteria.delete(criterion)
31
+ @primary_coverage = nil if @primary_coverage == criterion
32
+ end
33
+
34
+ def primary_coverage(criterion = nil)
35
+ if criterion.nil?
36
+ @primary_coverage ||= default_primary_coverage
37
+ else
38
+ raise_if_criterion_disabled(criterion)
39
+ @primary_coverage = criterion
40
+ end
41
+ end
42
+
43
+ def coverage_criteria
44
+ @coverage_criteria ||= Set[DEFAULT_COVERAGE_CRITERION]
45
+ end
46
+
47
+ def coverage_criterion_enabled?(criterion)
48
+ coverage_criteria.member?(criterion)
49
+ end
50
+
51
+ # Reset the criteria back to the lazy default (`Set[:line]`).
52
+ def clear_coverage_criteria
53
+ @coverage_criteria = nil
54
+ @primary_coverage = nil
55
+ end
56
+
57
+ # @api private — called from `SimpleCov.start_tracking` to fail
58
+ # fast when the user has disabled every coverage criterion.
59
+ def validate_coverage_criteria!
60
+ return unless coverage_criteria.empty?
61
+
62
+ raise SimpleCov::ConfigurationError,
63
+ "At least one coverage criterion must be enabled. " \
64
+ "Re-enable one with `enable_coverage :line`, `:branch`, or `:method`."
65
+ end
66
+
67
+ # Whether this run produces line data at all. The oneshot variant counts:
68
+ # `ResultAdapter` turns its executed-line list back into a line array. A
69
+ # branch-only or method-only run produces none, and `Coverage.result`
70
+ # entries for the files it loaded carry no `"lines"` key.
71
+ def line_coverage?
72
+ coverage_criterion_enabled?(DEFAULT_COVERAGE_CRITERION) ||
73
+ coverage_criterion_enabled?(ONESHOT_LINE_COVERAGE_CRITERION)
74
+ end
75
+
76
+ def branch_coverage?
77
+ branch_coverage_supported? && coverage_criterion_enabled?(:branch)
78
+ end
79
+
80
+ def branch_coverage_supported?
81
+ coverage_criterion_supported?(:branches)
82
+ end
83
+
84
+ def method_coverage?
85
+ method_coverage_supported? && coverage_criterion_enabled?(:method)
86
+ end
87
+
88
+ def method_coverage_supported?
89
+ coverage_criterion_supported?(:methods)
90
+ end
91
+
92
+ # Ask the Coverage runtime itself whether a criterion is supported
93
+ # (Ruby >= 3.2). Older Rubies don't expose `Coverage.supported?`, so
94
+ # fall back to the historical engine check that line/branch/method
95
+ # were unavailable on JRuby. `:eval` was added later, so on older
96
+ # Rubies its fallback is "always unsupported" rather than the
97
+ # JRuby-only one above. The fallback arm is unreachable from the
98
+ # dogfood report, which runs on a newer Ruby.
99
+ # simplecov:disable
100
+ def coverage_criterion_supported?(criterion)
101
+ require "coverage"
102
+ return Coverage.supported?(criterion) if Coverage.respond_to?(:supported?)
103
+
104
+ criterion != :eval && RUBY_ENGINE != "jruby"
105
+ end
106
+ # simplecov:enable
107
+
108
+ private
109
+
110
+ def add_coverage_criterion(criterion)
111
+ raise_if_criterion_unsupported(criterion)
112
+ incompatible = LINE_COVERAGE_ALTERNATIVES[criterion]
113
+ disable_coverage(incompatible) if incompatible
114
+ coverage_criteria << criterion
115
+ end
116
+
117
+ # If `:line` is enabled, it's the default primary; otherwise fall
118
+ # back to whichever criterion the user actually enabled (in
119
+ # insertion order). Returning `:line` even when disabled would
120
+ # propagate broken state into `minimum_coverage 90`.
121
+ def default_primary_coverage
122
+ return DEFAULT_COVERAGE_CRITERION if coverage_criterion_enabled?(DEFAULT_COVERAGE_CRITERION)
123
+
124
+ # Set#first types as nilable, but an empty criteria set is rejected at
125
+ # start_tracking before any caller can observe a nil here.
126
+ _ = coverage_criteria.first
127
+ end
128
+
129
+ def raise_if_criterion_disabled(criterion)
130
+ # `coverage :eval` by itself IS supported — it's a standalone
131
+ # toggle, never in the enabled-criteria set — so the generic
132
+ # "unsupported criterion" message below would mislead here.
133
+ if criterion == :eval
134
+ raise SimpleCov::ConfigurationError,
135
+ "Coverage criterion :eval only toggles measuring eval'd code; " \
136
+ "it cannot carry thresholds or serve as the primary criterion"
137
+ end
138
+
139
+ raise_if_criterion_unsupported(criterion)
140
+ return if coverage_criterion_enabled?(criterion)
141
+
142
+ raise SimpleCov::ConfigurationError,
143
+ "Coverage criterion #{criterion}, is disabled! " \
144
+ "Please enable it first through enable_coverage #{criterion} (if supported)"
145
+ end
146
+
147
+ def raise_if_criterion_unsupported(criterion)
148
+ return if SUPPORTED_COVERAGE_CRITERIA.member?(criterion)
149
+
150
+ raise SimpleCov::ConfigurationError,
151
+ "Unsupported coverage criterion #{criterion}, supported values are #{SUPPORTED_COVERAGE_CRITERIA}"
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleCov
4
+ # The standalone eval-coverage toggle (`enable_coverage :eval`),
5
+ # instrumenting `eval`'d code on Rubies that support it. Kept separate
6
+ # from the criteria-set logic in `coverage_criteria.rb` because `:eval`
7
+ # is a boolean toggle, never a member of the enabled-criteria set.
8
+ module Configuration
9
+ def coverage_for_eval_supported?
10
+ coverage_criterion_supported?(:eval)
11
+ end
12
+
13
+ def coverage_for_eval_enabled?
14
+ @coverage_for_eval_enabled ||= false
15
+ end
16
+
17
+ # DEPRECATED: prefer `enable_coverage :eval`.
18
+ def enable_coverage_for_eval
19
+ SimpleCov::Deprecation.warn("`SimpleCov.enable_coverage_for_eval` is deprecated. " \
20
+ "Replace with `SimpleCov.enable_coverage :eval`.")
21
+ enable_eval_coverage
22
+ end
23
+
24
+ private
25
+
26
+ # Shared implementation backing both `enable_coverage :eval` and
27
+ # the deprecated `enable_coverage_for_eval`.
28
+ def enable_eval_coverage
29
+ if coverage_for_eval_supported?
30
+ @coverage_for_eval_enabled = true
31
+ else
32
+ warn "Coverage for eval is not available; Use Ruby 3.2.0 or later"
33
+ end
34
+ end
35
+
36
+ # No support check needed: off is a safe state on every Ruby.
37
+ def disable_eval_coverage
38
+ @coverage_for_eval_enabled = false
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,164 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleCov
4
+ # Inclusion and exclusion methods: `cover` and `skip`, plus the deprecated
5
+ # `track_files` and `add_filter` aliases.
6
+ module Configuration
7
+ attr_writer :filters
8
+
9
+ #
10
+ # Restrict the universe of files in the coverage report to those matching
11
+ # one or more globs, regexps, or block predicates. Multiple calls union;
12
+ # when any `cover` matcher is configured the report drops every file that
13
+ # doesn't match at least one of them.
14
+ #
15
+ # Strings are interpreted as shell globs (e.g. "lib/**/*.rb"), not
16
+ # substring matches — this is a deliberate departure from the legacy
17
+ # `add_filter` semantics and matches the way `track_files` already
18
+ # interprets its argument.
19
+ #
20
+ # When the matcher is a string-glob, `cover` also expands the glob on
21
+ # disk so files that exist but were never required during the run still
22
+ # appear in the report (at 0% coverage). This is the "include unloaded
23
+ # files" half of the legacy `track_files` behavior, rolled into the
24
+ # same call.
25
+ #
26
+ # SimpleCov.start do
27
+ # cover "lib/**/*.rb", "app/**/*.rb"
28
+ # cover(/_helper\.rb\z/)
29
+ # cover { |sf| sf.lines.count > 5 }
30
+ # end
31
+ #
32
+ def cover(*args, &block)
33
+ args.each { |arg| cover_filters << build_cover_filter(arg) }
34
+ cover_filters << SimpleCov::BlockFilter.new(block) if block
35
+ cover_filters
36
+ end
37
+
38
+ # Returns the list of configured inclusion filters added via `cover`.
39
+ def cover_filters
40
+ @cover_filters ||= []
41
+ end
42
+
43
+ # Returns the list of string globs passed to `cover` — used by the
44
+ # disk-discovery pass in `SimpleCov.tracked_file_paths` so files
45
+ # matching a `cover` glob appear in the report even when they were
46
+ # never required during the suite.
47
+ #
48
+ # Walks into `ArrayFilter` entries (built when a caller passes an
49
+ # array to `cover`) so a glob nested inside `cover(["lib/**/*.rb",
50
+ # /helper\.rb\z/])` still drives unloaded-file discovery.
51
+ def cover_globs
52
+ collect_cover_globs(cover_filters)
53
+ end
54
+
55
+ # DEPRECATED: prefer `cover`, which both includes unloaded files (the
56
+ # historical `track_files` behavior) and restricts the report to the
57
+ # matching set.
58
+ def track_files(glob)
59
+ SimpleCov::Deprecation.warn("`SimpleCov.track_files` is deprecated. " \
60
+ "#{track_files_replacement_hint(glob)}")
61
+ @tracked_files = glob
62
+ end
63
+
64
+ # `track_files(nil)` is the documented way to clear a previously-set
65
+ # glob, but `cover(nil)` raises `ConfigurationError`, so don't point
66
+ # users at it. The `cover` API has no direct equivalent for "reset
67
+ # the inclusion list" — point users at the `@cover_filters` reset.
68
+ def track_files_replacement_hint(glob)
69
+ if glob.nil?
70
+ "Replace with `SimpleCov.cover_filters.clear` — clearing the inclusion list."
71
+ else
72
+ "Replace with `SimpleCov.cover #{glob.inspect}` — `cover` includes unloaded files on disk " \
73
+ "(the historical `track_files` behavior) and also restricts the report to the matching set. " \
74
+ "If you want to keep additional files outside #{glob.inspect} in the report, pass every " \
75
+ "directory you care about, e.g. `cover #{glob.inspect}, \"app/**/*.rb\"`."
76
+ end
77
+ end
78
+
79
+ # Returns the glob used to include files that were not explicitly required.
80
+ def tracked_files
81
+ @tracked_files if defined?(@tracked_files)
82
+ end
83
+
84
+ # Returns the list of configured exclusion filters added via `skip`
85
+ # (or the deprecated `add_filter`).
86
+ def filters
87
+ @filters ||= []
88
+ end
89
+
90
+ #
91
+ # Drop matching files from the coverage report. The inverse of `cover`.
92
+ #
93
+ # See README for the full grammar; `skip` accepts a String (path-segment
94
+ # substring), Regexp, block predicate, or Array of any of those.
95
+ #
96
+ def skip(filter_argument = nil, &)
97
+ filters << parse_filter(filter_argument, &)
98
+ end
99
+
100
+ # DEPRECATED: alias for `skip`. Same matcher grammar, identical behavior.
101
+ def add_filter(filter_argument = nil, &block)
102
+ example = block ? "`SimpleCov.skip { ... }`" : "`SimpleCov.skip #{filter_argument.inspect}`"
103
+ SimpleCov::Deprecation.warn("`SimpleCov.add_filter` is deprecated. " \
104
+ "Replace with `SimpleCov.skip` (same arguments, same behavior). Example: #{example}.")
105
+ skip(filter_argument, &block)
106
+ end
107
+
108
+ # Remove any filters whose `filter_argument` equals the given value.
109
+ # Returns true when at least one filter was removed, false otherwise.
110
+ def remove_filter(filter_argument) # rubocop:disable Naming/PredicateMethod
111
+ before = filters.size
112
+ filters.reject! { |filter| filter.respond_to?(:filter_argument) && filter.filter_argument == filter_argument }
113
+ filters.size != before
114
+ end
115
+
116
+ # Remove every filter from the chain, including the defaults installed
117
+ # by `SimpleCov.start`.
118
+ def clear_filters
119
+ @filters = []
120
+ end
121
+
122
+ # Drop every filter previously installed (defaults plus anything
123
+ # earlier in this block) so subsequent `skip` calls start from a
124
+ # clean slate. Order matters — call this before your own `skip`
125
+ # invocations.
126
+ def no_default_skips
127
+ clear_filters
128
+ end
129
+
130
+ private
131
+
132
+ # The actual filter processor. Not meant for direct use.
133
+ def parse_filter(filter_argument = nil, &filter_proc)
134
+ filter = filter_argument || filter_proc
135
+
136
+ raise ArgumentError, "Please specify either a filter or a block to filter with" unless filter
137
+
138
+ SimpleCov::Filter.build_filter(filter)
139
+ end
140
+
141
+ # Build a filter for a `cover` argument. Strings are treated as
142
+ # globs (not substrings — that's `skip`/`add_filter`'s semantics);
143
+ # everything else dispatches exactly like `add_filter`.
144
+ def build_cover_filter(arg)
145
+ SimpleCov::Filter.build_filter(arg, string_filter: SimpleCov::GlobFilter)
146
+ rescue SimpleCov::ConfigurationError
147
+ raise SimpleCov::ConfigurationError, "Unsupported `cover` argument #{arg.inspect}; " \
148
+ "expected a String glob, Regexp, Proc, " \
149
+ "SimpleCov::Filter, or Array of those."
150
+ end
151
+
152
+ # Walk a list of cover filters and return the string globs they hold,
153
+ # descending into `ArrayFilter` wrappers built by `cover(["a", "b"])`.
154
+ def collect_cover_globs(filter_list)
155
+ filter_list.flat_map do |filter|
156
+ case filter
157
+ when SimpleCov::GlobFilter then filter.filter_argument
158
+ when SimpleCov::ArrayFilter then collect_cover_globs(filter.filter_argument)
159
+ else []
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,138 @@
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. Accepts a formatter class
13
+ # (instantiated fresh for every report) or a ready-built instance,
14
+ # which is how constructor options are passed — e.g.
15
+ # `formatter SimpleCov::Formatter::HTMLFormatter.new(silent: true)`
16
+ # to suppress the "Coverage report generated" status line (see
17
+ # #1240). Pass `false` (or `nil`) to opt out of formatting
18
+ # entirely — worker processes in big parallel CI setups (see #964)
19
+ # only need their `.resultset.json` on disk so a final
20
+ # `SimpleCov.collate` job can produce the report; running them
21
+ # without a formatter saves the per-job HTML/multi-formatter
22
+ # overhead.
23
+ #
24
+ def formatter(formatter = :__no_arg__)
25
+ case formatter
26
+ when :__no_arg__
27
+ @formatter
28
+ else
29
+ @formatter = formatter || nil # normalize `false` to `nil`
30
+ end
31
+ end
32
+
33
+ # Sets the configured formatters. Pass `[]` to opt out of
34
+ # formatting entirely; see `formatter` for the rationale.
35
+ def formatters(formatters = :__no_arg__)
36
+ case formatters
37
+ when :__no_arg__
38
+ configured = formatter
39
+ configured ? [configured] : []
40
+ else
41
+ self.formatters = formatters
42
+ formatters
43
+ end
44
+ end
45
+
46
+ # Sets the configured formatters. Equivalent to `formatters [...]`.
47
+ # Accepts a single formatter as well as an Array, matching the pre-1.0 behavior
48
+ # where `MultiFormatter.new` normalized its input. Elements may be
49
+ # formatter classes or ready-built instances; see `formatter`.
50
+ # `nil`, `false`, and `[]` all opt out of formatting entirely —
51
+ # `false` normalized like `formatter false` does, since `Array(false)`
52
+ # would otherwise smuggle it in as a "formatter" that can only fail.
53
+ def formatters=(formatters)
54
+ formatters = Array(formatters || nil)
55
+ @formatter = formatters.empty? ? nil : SimpleCov::Formatter::MultiFormatter.new(formatters)
56
+ end
57
+
58
+ #
59
+ # Get or set whether SimpleCov colorizes its stderr diagnostics. Accepts
60
+ # `true` (always on), `false` (always off), or `:auto` (default: defer
61
+ # to `SimpleCov::Color`, which checks `$stderr.tty?` with `NO_COLOR`
62
+ # and `FORCE_COLOR` overrides). An explicit `true`/`false` wins over
63
+ # both auto-detection and the env vars, which is the right escape
64
+ # hatch when stderr is being piped through a wrapper that still
65
+ # renders ANSI in its own terminal (parallel_tests with
66
+ # `--combine-stderr`, log multiplexers, some CI runners). See #1157.
67
+ #
68
+ def color(value = :__no_arg__)
69
+ return defined?(@color) ? @color : :auto if value == :__no_arg__
70
+
71
+ @color = value
72
+ end
73
+
74
+ #
75
+ # Get or set whether SimpleCov prints its own diagnostic warnings to
76
+ # stderr. Covers per-check threshold violations, the trailing
77
+ # "SimpleCov failed with exit ..." summary, and the deferred-report /
78
+ # previous-error notices. Defaults to true. Set to false to silence
79
+ # SimpleCov entirely when parsing tooling output (see issue #1155).
80
+ #
81
+ def print_errors(value = :__no_arg__)
82
+ return defined?(@print_error_status) ? @print_error_status : true if value == :__no_arg__
83
+
84
+ @print_error_status = value
85
+ end
86
+
87
+ #
88
+ # Get or set whether `coverage.json` includes the full source-text
89
+ # array for every file. Defaults to true. Set to false when a
90
+ # downstream tool reads the project's source files directly and
91
+ # only needs the coverage metrics, so `coverage.json` doesn't carry
92
+ # a copy of the source tree (which dominates the payload on larger
93
+ # projects).
94
+ #
95
+ # The HTML report's embedded data always includes source — the
96
+ # client-side renderer needs it. Only `coverage.json` honors
97
+ # this setting.
98
+ #
99
+ # SimpleCov.start do
100
+ # source_in_json false
101
+ # end
102
+ #
103
+ def source_in_json(value = :__no_arg__)
104
+ return defined?(@source_in_json) ? @source_in_json : true if value == :__no_arg__
105
+
106
+ @source_in_json = value
107
+ end
108
+
109
+ # DEPRECATED: alias for `print_errors`. Same value, same behavior.
110
+ def print_error_status
111
+ SimpleCov::Deprecation.warn("`SimpleCov.print_error_status` is deprecated. " \
112
+ "Replace with `SimpleCov.print_errors` (same value).")
113
+ defined?(@print_error_status) ? @print_error_status : true
114
+ end
115
+
116
+ #
117
+ # DEPRECATED: configure `# :nocov:` token override. Prefer
118
+ # `# simplecov:disable` / `# simplecov:enable` block comments (see
119
+ # SimpleCov::Directive). The `# :nocov:` toggle and this hook will
120
+ # be removed in a future release.
121
+ #
122
+ def nocov_token(nocov_token = nil)
123
+ SimpleCov::Deprecation.warn("`SimpleCov.nocov_token` and `SimpleCov.skip_token` are deprecated. " \
124
+ "Replace with `# simplecov:disable` / `# simplecov:enable` block comments.")
125
+ current_nocov_token(nocov_token)
126
+ end
127
+ alias skip_token nocov_token
128
+
129
+ # Internal accessor used by SimpleCov to recognise `# :nocov:`
130
+ # markers without emitting the public-API deprecation warning. Will
131
+ # be removed alongside the deprecated `nocov_token` setter.
132
+ def current_nocov_token(value = nil)
133
+ return @nocov_token if defined?(@nocov_token) && value.nil?
134
+
135
+ @nocov_token = value || "nocov"
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleCov
4
+ # Group configuration is kept separate from inclusion and exclusion filters,
5
+ # while sharing their matcher parser through the main Configuration module.
6
+ module Configuration
7
+ # Returns the configured groups. Add groups using SimpleCov.group.
8
+ def groups
9
+ @groups ||= {}
10
+ end
11
+
12
+ def groups=(new_groups)
13
+ new_groups = new_groups.to_h { |name, filter| [GroupNames.normalize(name), filter] }
14
+ GroupNames.validate!(new_groups.keys)
15
+ @groups = new_groups
16
+ end
17
+
18
+ # Define a display group for files. Same matcher grammar as `skip`,
19
+ # but instead of dropping the matching files it bins them under
20
+ # `group_name` for the formatter. Files matched by no group fall
21
+ # into the implicit "Ungrouped" bucket.
22
+ def group(group_name, filter_argument = nil, &)
23
+ group_name = GroupNames.normalize(group_name)
24
+ GroupNames.validate!([group_name])
25
+ groups[group_name] = parse_filter(filter_argument, &)
26
+ end
27
+
28
+ # DEPRECATED: alias for `group`. Same arguments, same behavior.
29
+ def add_group(group_name, filter_argument = nil, &block)
30
+ example = if block
31
+ "`SimpleCov.group #{group_name.inspect} { ... }`"
32
+ else
33
+ "`SimpleCov.group #{group_name.inspect}, #{filter_argument.inspect}`"
34
+ end
35
+ SimpleCov::Deprecation.warn(
36
+ "`SimpleCov.add_group` is deprecated. " \
37
+ "Replace with `SimpleCov.group` (same arguments, same behavior). Example: #{example}."
38
+ )
39
+ group(group_name, filter_argument, &block)
40
+ end
41
+ end
42
+ 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