simplecov 1.1.0 → 1.2.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 (196) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +165 -57
  3. data/exe/simplecov +0 -3
  4. data/lib/minitest/simplecov_plugin.rb +12 -9
  5. data/lib/simplecov/atomic_file.rb +16 -19
  6. data/lib/simplecov/autostart.rb +5 -7
  7. data/lib/simplecov/baseline/parser.rb +58 -0
  8. data/lib/simplecov/baseline.rb +170 -0
  9. data/lib/simplecov/cli/affected/changed_files.rb +57 -0
  10. data/lib/simplecov/cli/affected/selection.rb +117 -0
  11. data/lib/simplecov/cli/affected.rb +148 -0
  12. data/lib/simplecov/cli/badge/svg.rb +70 -0
  13. data/lib/simplecov/cli/badge.rb +62 -0
  14. data/lib/simplecov/cli/clean.rb +23 -24
  15. data/lib/simplecov/cli/command_helpers.rb +55 -19
  16. data/lib/simplecov/cli/completions/scripts.rb +114 -0
  17. data/lib/simplecov/cli/completions.rb +83 -0
  18. data/lib/simplecov/cli/coverage.rb +25 -28
  19. data/lib/simplecov/cli/coverage_file.rb +58 -14
  20. data/lib/simplecov/cli/dead_code/output.rb +100 -0
  21. data/lib/simplecov/cli/dead_code.rb +139 -0
  22. data/lib/simplecov/cli/diff/output.rb +45 -0
  23. data/lib/simplecov/cli/diff.rb +35 -67
  24. data/lib/simplecov/cli/dotfile.rb +83 -38
  25. data/lib/simplecov/cli/git.rb +41 -0
  26. data/lib/simplecov/cli/history/output.rb +135 -0
  27. data/lib/simplecov/cli/history.rb +67 -0
  28. data/lib/simplecov/cli/merge.rb +34 -39
  29. data/lib/simplecov/cli/open.rb +12 -15
  30. data/lib/simplecov/cli/patch/changed_lines.rb +149 -0
  31. data/lib/simplecov/cli/patch/output.rb +135 -0
  32. data/lib/simplecov/cli/patch.rb +169 -0
  33. data/lib/simplecov/cli/ratchet/output.rb +44 -0
  34. data/lib/simplecov/cli/ratchet.rb +83 -0
  35. data/lib/simplecov/cli/report.rb +18 -24
  36. data/lib/simplecov/cli/run.rb +8 -9
  37. data/lib/simplecov/cli/serve/report_preparer.rb +7 -6
  38. data/lib/simplecov/cli/serve/static_file_handler.rb +62 -56
  39. data/lib/simplecov/cli/serve.rb +27 -24
  40. data/lib/simplecov/cli/show/annotator.rb +87 -0
  41. data/lib/simplecov/cli/show/sweep.rb +36 -0
  42. data/lib/simplecov/cli/show.rb +133 -0
  43. data/lib/simplecov/cli/status/facts.rb +87 -0
  44. data/lib/simplecov/cli/status.rb +93 -0
  45. data/lib/simplecov/cli/tests/redundancy.rb +78 -0
  46. data/lib/simplecov/cli/tests.rb +148 -0
  47. data/lib/simplecov/cli/uncovered/misses.rb +37 -0
  48. data/lib/simplecov/cli/uncovered.rb +70 -32
  49. data/lib/simplecov/cli/usage.rb +152 -0
  50. data/lib/simplecov/cli/watch/live_report.rb +73 -0
  51. data/lib/simplecov/cli/watch/narrator.rb +55 -0
  52. data/lib/simplecov/cli/watch/poller.rb +45 -0
  53. data/lib/simplecov/cli/watch/session.rb +134 -0
  54. data/lib/simplecov/cli/watch/test_plan.rb +43 -0
  55. data/lib/simplecov/cli/watch.rb +85 -0
  56. data/lib/simplecov/cli.rb +43 -92
  57. data/lib/simplecov/color.rb +21 -35
  58. data/lib/simplecov/combine/branches_combiner.rb +7 -31
  59. data/lib/simplecov/combine/coverage_accumulator.rb +49 -142
  60. data/lib/simplecov/combine/identity_interner.rb +10 -13
  61. data/lib/simplecov/combine/interned_counts.rb +7 -9
  62. data/lib/simplecov/combine/lines_combiner.rb +20 -44
  63. data/lib/simplecov/combine/methods_combiner.rb +9 -35
  64. data/lib/simplecov/combine/results_combiner.rb +4 -21
  65. data/lib/simplecov/combine.rb +0 -6
  66. data/lib/simplecov/command_guesser.rb +20 -34
  67. data/lib/simplecov/configuration/baseline.rb +31 -0
  68. data/lib/simplecov/configuration/coverage.rb +91 -79
  69. data/lib/simplecov/configuration/coverage_criteria.rb +43 -54
  70. data/lib/simplecov/configuration/deprecations.rb +21 -0
  71. data/lib/simplecov/configuration/eval_coverage.rb +6 -10
  72. data/lib/simplecov/configuration/filters.rb +40 -73
  73. data/lib/simplecov/configuration/formatting.rb +74 -83
  74. data/lib/simplecov/configuration/groups.rb +19 -13
  75. data/lib/simplecov/configuration/history.rb +44 -0
  76. data/lib/simplecov/configuration/ignored_entries.rb +31 -22
  77. data/lib/simplecov/configuration/merging.rb +94 -62
  78. data/lib/simplecov/configuration/missed_caps.rb +51 -0
  79. data/lib/simplecov/configuration/production.rb +23 -0
  80. data/lib/simplecov/configuration/test_tracking.rb +48 -0
  81. data/lib/simplecov/configuration/thresholds.rb +33 -62
  82. data/lib/simplecov/configuration/view_coverage.rb +38 -0
  83. data/lib/simplecov/configuration.rb +70 -76
  84. data/lib/simplecov/configuration_error.rb +5 -0
  85. data/lib/simplecov/context_map/union.rb +81 -0
  86. data/lib/simplecov/context_map.rb +187 -0
  87. data/lib/simplecov/coverage_json.rb +2 -5
  88. data/lib/simplecov/coverage_statistics.rb +14 -24
  89. data/lib/simplecov/coverage_violations.rb +157 -64
  90. data/lib/simplecov/current_run.rb +61 -0
  91. data/lib/simplecov/defaults.rb +4 -23
  92. data/lib/simplecov/deprecation.rb +45 -27
  93. data/lib/simplecov/directive.rb +46 -62
  94. data/lib/simplecov/exit_codes/baseline_check.rb +46 -0
  95. data/lib/simplecov/exit_codes/check.rb +17 -11
  96. data/lib/simplecov/exit_codes/exit_code_handling.rb +21 -8
  97. data/lib/simplecov/exit_codes/maximum_coverage_drop_check.rb +7 -10
  98. data/lib/simplecov/exit_codes/maximum_missed_check.rb +26 -0
  99. data/lib/simplecov/exit_codes/maximum_missed_per_file_check.rb +34 -0
  100. data/lib/simplecov/exit_codes/maximum_overall_coverage_check.rb +7 -12
  101. data/lib/simplecov/exit_codes/minimum_coverage_by_file_check.rb +11 -10
  102. data/lib/simplecov/exit_codes/minimum_coverage_by_group_check.rb +7 -9
  103. data/lib/simplecov/exit_codes/minimum_overall_coverage_check.rb +16 -17
  104. data/lib/simplecov/exit_codes.rb +17 -7
  105. data/lib/simplecov/exit_handling.rb +94 -59
  106. data/lib/simplecov/file_list.rb +20 -51
  107. data/lib/simplecov/filter.rb +43 -66
  108. data/lib/simplecov/formatter/base.rb +38 -51
  109. data/lib/simplecov/formatter/baseline_formatter.rb +98 -0
  110. data/lib/simplecov/formatter/coverage_json_writer.rb +29 -31
  111. data/lib/simplecov/formatter/html_formatter/public/index.html +17 -10
  112. data/lib/simplecov/formatter/html_formatter/viewer_data_validator.rb +74 -17
  113. data/lib/simplecov/formatter/html_formatter.rb +24 -41
  114. data/lib/simplecov/formatter/json_formatter/errors_formatter.rb +50 -16
  115. data/lib/simplecov/formatter/json_formatter/production_section_formatter.rb +48 -0
  116. data/lib/simplecov/formatter/json_formatter/result_hash_formatter.rb +43 -29
  117. data/lib/simplecov/formatter/json_formatter/source_file_formatter.rb +14 -10
  118. data/lib/simplecov/formatter/json_formatter.rb +7 -13
  119. data/lib/simplecov/formatter/multi_formatter.rb +5 -11
  120. data/lib/simplecov/formatter/simple_formatter.rb +2 -8
  121. data/lib/simplecov/formatter.rb +14 -10
  122. data/lib/simplecov/group_names.rb +13 -12
  123. data/lib/simplecov/history.rb +115 -0
  124. data/lib/simplecov/last_run.rb +6 -9
  125. data/lib/simplecov/lines_classifier.rb +31 -16
  126. data/lib/simplecov/load_global_config.rb +7 -7
  127. data/lib/simplecov/parallel_adapters/base.rb +13 -41
  128. data/lib/simplecov/parallel_adapters/generic.rb +7 -18
  129. data/lib/simplecov/parallel_adapters/parallel_tests.rb +21 -42
  130. data/lib/simplecov/parallel_adapters.rb +24 -53
  131. data/lib/simplecov/parallel_coordination.rb +39 -49
  132. data/lib/simplecov/parallel_result_merger/worker_payload.rb +33 -0
  133. data/lib/simplecov/parallel_result_merger.rb +67 -126
  134. data/lib/simplecov/process.rb +14 -32
  135. data/lib/simplecov/production/error.rb +11 -0
  136. data/lib/simplecov/production/file_sink.rb +130 -0
  137. data/lib/simplecov/production.rb +249 -0
  138. data/lib/simplecov/profiles/rails.rb +1 -12
  139. data/lib/simplecov/profiles/root_filter.rb +0 -4
  140. data/lib/simplecov/profiles/strict.rb +9 -19
  141. data/lib/simplecov/profiles.rb +11 -33
  142. data/lib/simplecov/report_deferral.rb +11 -15
  143. data/lib/simplecov/report_stamp.rb +6 -9
  144. data/lib/simplecov/result/filter_config.rb +19 -0
  145. data/lib/simplecov/result/missing_source_files_reporter.rb +8 -9
  146. data/lib/simplecov/result/serialization.rb +43 -0
  147. data/lib/simplecov/result/source_file_builder.rb +7 -12
  148. data/lib/simplecov/result.rb +68 -117
  149. data/lib/simplecov/result_adapter.rb +58 -68
  150. data/lib/simplecov/result_merger/contexts.rb +22 -0
  151. data/lib/simplecov/result_merger/legacy_format_adapter.rb +4 -8
  152. data/lib/simplecov/result_merger/resultset_file.rb +20 -27
  153. data/lib/simplecov/result_merger/resultset_run_identity.rb +23 -25
  154. data/lib/simplecov/result_merger/resultset_store.rb +23 -14
  155. data/lib/simplecov/result_merger/unloaded_files.rb +24 -32
  156. data/lib/simplecov/result_merger.rb +59 -58
  157. data/lib/simplecov/result_processing.rb +74 -119
  158. data/lib/simplecov/run_identity.rb +16 -18
  159. data/lib/simplecov/simulate_coverage.rb +35 -50
  160. data/lib/simplecov/source_file/branch.rb +8 -33
  161. data/lib/simplecov/source_file/branch_builder.rb +28 -47
  162. data/lib/simplecov/source_file/builder_context.rb +6 -13
  163. data/lib/simplecov/source_file/line.rb +13 -33
  164. data/lib/simplecov/source_file/line_builder.rb +9 -15
  165. data/lib/simplecov/source_file/method.rb +3 -7
  166. data/lib/simplecov/source_file/method_builder.rb +6 -15
  167. data/lib/simplecov/source_file/ruby_data_parser.rb +59 -66
  168. data/lib/simplecov/source_file/skip_chunks.rb +19 -28
  169. data/lib/simplecov/source_file/source_loader.rb +45 -35
  170. data/lib/simplecov/source_file/statistics.rb +10 -9
  171. data/lib/simplecov/source_file.rb +21 -51
  172. data/lib/simplecov/static_coverage_extractor/condition_folding.rb +63 -114
  173. data/lib/simplecov/static_coverage_extractor/location_conventions.rb +76 -121
  174. data/lib/simplecov/static_coverage_extractor/method_collector.rb +11 -24
  175. data/lib/simplecov/static_coverage_extractor/prism_compat.rb +15 -24
  176. data/lib/simplecov/static_coverage_extractor/value_position.rb +25 -38
  177. data/lib/simplecov/static_coverage_extractor/visitor.rb +47 -64
  178. data/lib/simplecov/static_coverage_extractor.rb +46 -62
  179. data/lib/simplecov/test_tracker/accessors.rb +34 -0
  180. data/lib/simplecov/test_tracker/constant_watch.rb +49 -0
  181. data/lib/simplecov/test_tracker/delta.rb +69 -0
  182. data/lib/simplecov/test_tracker/framework_hooks.rb +130 -0
  183. data/lib/simplecov/test_tracker.rb +162 -0
  184. data/lib/simplecov/unloaded_file_injector.rb +18 -31
  185. data/lib/simplecov/useless_results_remover.rb +7 -6
  186. data/lib/simplecov/version.rb +1 -1
  187. data/lib/simplecov/view_coverage/template_compiler.rb +83 -0
  188. data/lib/simplecov/view_coverage.rb +55 -0
  189. data/lib/simplecov-html.rb +1 -0
  190. data/lib/simplecov.rb +59 -114
  191. data/lib/simplecov_json_formatter.rb +1 -0
  192. data/man/simplecov.1 +349 -0
  193. data/sig/simplecov.rbs +523 -595
  194. metadata +72 -7
  195. data/schemas/coverage-v1.0.schema.json +0 -306
  196. data/schemas/coverage.schema.json +0 -306
@@ -1,8 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SimpleCov
4
- # Inclusion and exclusion methods: `cover` and `skip`, plus the deprecated
5
- # `track_files` and `add_filter` aliases.
6
4
  module Configuration
7
5
  attr_writer :filters
8
6
 
@@ -12,59 +10,38 @@ module SimpleCov
12
10
  # when any `cover` matcher is configured the report drops every file that
13
11
  # doesn't match at least one of them.
14
12
  #
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.
13
+ # Strings are interpreted as shell globs, not substring matches, a
14
+ # deliberate departure from the legacy `add_filter` semantics.
19
15
  #
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
16
+ # A string glob is also expanded on disk, so files that exist but were
17
+ # never required during the run still appear in the report at 0%. This is
18
+ # the "include unloaded files" half of the legacy `track_files` behavior.
31
19
  #
32
20
  def cover(*args, &block)
33
21
  args.each { |arg| cover_filters << build_cover_filter(arg) }
34
- cover_filters << SimpleCov::BlockFilter.new(block) if block
22
+ cover_filters << BlockFilter.new(block) if block
35
23
  cover_filters
36
24
  end
37
25
 
38
- # Returns the list of configured inclusion filters added via `cover`.
39
26
  def cover_filters
40
27
  @cover_filters ||= []
41
28
  end
42
29
 
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.
30
+ # The string globs passed to `cover`, used by the disk-discovery pass in
31
+ # `SimpleCov.tracked_file_paths` so files matching a `cover` glob appear in
32
+ # the report even when they were never required during the suite.
51
33
  def cover_globs
52
34
  collect_cover_globs(cover_filters)
53
35
  end
54
36
 
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
37
  def track_files(glob)
59
- SimpleCov::Deprecation.warn("`SimpleCov.track_files` is deprecated. " \
60
- "#{track_files_replacement_hint(glob)}")
38
+ Deprecation.warn("`SimpleCov.track_files` is deprecated. " \
39
+ "#{track_files_replacement_hint(glob)}")
61
40
  @tracked_files = glob
62
41
  end
63
42
 
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.
43
+ # `track_files(nil)` is the documented way to clear a previously-set glob,
44
+ # but `cover(nil)` raises `ConfigurationError`, so don't point users at it.
68
45
  def track_files_replacement_hint(glob)
69
46
  if glob.nil?
70
47
  "Replace with `SimpleCov.cover_filters.clear` — clearing the inclusion list."
@@ -76,86 +53,76 @@ module SimpleCov
76
53
  end
77
54
  end
78
55
 
79
- # Returns the glob used to include files that were not explicitly required.
56
+ # Nil until `cover` names a glob: an unset ivar reads as nil, so the absence
57
+ # needs no guard of its own.
80
58
  def tracked_files
81
- @tracked_files if defined?(@tracked_files)
59
+ @tracked_files
82
60
  end
83
61
 
84
- # Returns the list of configured exclusion filters added via `skip`
85
- # (or the deprecated `add_filter`).
86
62
  def filters
87
63
  @filters ||= []
88
64
  end
89
65
 
90
66
  #
91
67
  # 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.
68
+ # Accepts a String (path-segment substring), Regexp, block predicate, or
69
+ # Array of any of those.
95
70
  #
96
71
  def skip(filter_argument = nil, &)
97
72
  filters << parse_filter(filter_argument, &)
98
73
  end
99
74
 
100
- # DEPRECATED: alias for `skip`. Same matcher grammar, identical behavior.
101
75
  def add_filter(filter_argument = nil, &block)
102
76
  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}.")
77
+ Deprecation.warn("`SimpleCov.add_filter` is deprecated. " \
78
+ "Replace with `SimpleCov.skip` (same arguments, same behavior). Example: #{example}.")
105
79
  skip(filter_argument, &block)
106
80
  end
107
81
 
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
82
+ # `reject!` answers nil when it rejected nothing, which is the whole of
83
+ # "was anything removed".
84
+ def remove_filter(filter_argument)
85
+ rejected = filters.reject! do |filter|
86
+ filter.respond_to?(:filter_argument) && filter.filter_argument.eql?(filter_argument)
87
+ end
88
+ !rejected.nil?
114
89
  end
115
90
 
116
- # Remove every filter from the chain, including the defaults installed
117
- # by `SimpleCov.start`.
118
91
  def clear_filters
119
92
  @filters = []
120
93
  end
121
94
 
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.
95
+ # Order matters: call this before your own `skip` invocations.
126
96
  def no_default_skips
127
97
  clear_filters
128
98
  end
129
99
 
130
- private
100
+ private
131
101
 
132
- # The actual filter processor. Not meant for direct use.
133
102
  def parse_filter(filter_argument = nil, &filter_proc)
134
103
  filter = filter_argument || filter_proc
135
104
 
136
105
  raise ArgumentError, "Please specify either a filter or a block to filter with" unless filter
137
106
 
138
- SimpleCov::Filter.build_filter(filter)
107
+ Filter.build_filter(filter)
139
108
  end
140
109
 
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`.
110
+ # Strings are treated as globs, not substrings (that's `skip`'s
111
+ # semantics); everything else dispatches exactly like `add_filter`.
144
112
  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."
113
+ Filter.build_filter(arg, string_filter: GlobFilter)
114
+ rescue ConfigurationError
115
+ raise ConfigurationError, "Unsupported `cover` argument #{arg.inspect}; " \
116
+ "expected a String glob, Regexp, Proc, " \
117
+ "SimpleCov::Filter, or Array of those."
150
118
  end
151
119
 
152
- # Walk a list of cover filters and return the string globs they hold,
153
- # descending into `ArrayFilter` wrappers built by `cover(["a", "b"])`.
120
+ # Descends into `ArrayFilter` wrappers built by `cover(["a", "b"])`.
154
121
  def collect_cover_globs(filter_list)
155
122
  filter_list.flat_map do |filter|
156
123
  case filter
157
- when SimpleCov::GlobFilter then filter.filter_argument
158
- when SimpleCov::ArrayFilter then collect_cover_globs(filter.filter_argument)
124
+ when GlobFilter then filter.filter_argument
125
+ when ArrayFilter then collect_cover_globs(filter.filter_argument)
159
126
  else []
160
127
  end
161
128
  end
@@ -3,35 +3,33 @@
3
3
  require_relative "../formatter/multi_formatter"
4
4
 
5
5
  module SimpleCov
6
- # Formatter selection (`formatter` / `formatters`), reporting toggles
7
- # (`print_errors`), and the deprecated `# :nocov:` token hook.
8
6
  module Configuration
9
7
  attr_writer :formatter, :print_error_status
10
8
 
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
- #
9
+ BUILT_IN_FORMATS = {
10
+ html: :HTMLFormatter, json: :JSONFormatter, simple: :SimpleFormatter, baseline: :BaselineFormatter
11
+ }.freeze
12
+ private_constant :BUILT_IN_FORMATS
13
+
14
+ LAZY_FORMAT_REQUIRES = {html: "../../simplecov-html"}.freeze
15
+ private_constant :LAZY_FORMAT_REQUIRES
16
+
17
+ def formats(*names)
18
+ return formatters if names.empty?
19
+
20
+ self.formatters = names.map { |name| resolve_format(name) }
21
+ formatters
22
+ end
23
+
24
24
  def formatter(formatter = :__no_arg__)
25
25
  case formatter
26
26
  when :__no_arg__
27
27
  @formatter
28
28
  else
29
- @formatter = formatter || nil # normalize `false` to `nil`
29
+ @formatter = formatter || nil
30
30
  end
31
31
  end
32
32
 
33
- # Sets the configured formatters. Pass `[]` to opt out of
34
- # formatting entirely; see `formatter` for the rationale.
35
33
  def formatters(formatters = :__no_arg__)
36
34
  case formatters
37
35
  when :__no_arg__
@@ -39,100 +37,93 @@ module SimpleCov
39
37
  configured ? [configured] : []
40
38
  else
41
39
  self.formatters = formatters
42
- formatters
43
40
  end
44
41
  end
45
42
 
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.
43
+ # `nil`, `false`, and `[]` all opt out of formatting entirely. `false` is
44
+ # normalized first, since `Array(false)` would otherwise smuggle it in as a
45
+ # "formatter" that can only fail.
53
46
  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
- #
47
+ @formatter = combined_formatter(Array(formatters || nil))
48
+ end
49
+
68
50
  def color(value = :__no_arg__)
69
- return defined?(@color) ? @color : :auto if value == :__no_arg__
51
+ return instance_variable_defined?(:@color) ? @color : :auto if value.eql?(:__no_arg__)
52
+
53
+ self.color = _ = value
54
+ end
70
55
 
56
+ def color=(value)
71
57
  @color = value
72
58
  end
73
59
 
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
60
  def print_errors(value = :__no_arg__)
82
- return defined?(@print_error_status) ? @print_error_status : true if value == :__no_arg__
61
+ return instance_variable_defined?(:@print_error_status) ? @print_error_status : true if value.eql?(:__no_arg__)
83
62
 
63
+ self.print_errors = _ = value
64
+ end
65
+
66
+ def print_errors=(value)
84
67
  @print_error_status = value
85
68
  end
86
69
 
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
70
  def source_in_json(value = :__no_arg__)
104
- return defined?(@source_in_json) ? @source_in_json : true if value == :__no_arg__
71
+ return instance_variable_defined?(:@source_in_json) ? @source_in_json : true if value.eql?(:__no_arg__)
72
+
73
+ self.source_in_json = _ = value
74
+ end
105
75
 
76
+ def source_in_json=(value)
106
77
  @source_in_json = value
107
78
  end
108
79
 
109
- # DEPRECATED: alias for `print_errors`. Same value, same behavior.
110
80
  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
81
+ Deprecation.warn("`SimpleCov.print_error_status` is deprecated. " \
82
+ "Replace with `SimpleCov.print_errors` (same value).")
83
+ instance_variable_defined?(:@print_error_status) ? @print_error_status : true
114
84
  end
115
85
 
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
86
  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.")
87
+ Deprecation.warn("`SimpleCov.nocov_token` and `SimpleCov.skip_token` are deprecated. " \
88
+ "Replace with `# simplecov:disable` / `# simplecov:enable` block comments.")
125
89
  current_nocov_token(nocov_token)
126
90
  end
127
- alias skip_token nocov_token
91
+ alias_method :skip_token, :nocov_token
128
92
 
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
93
  def current_nocov_token(value = nil)
133
- return @nocov_token if defined?(@nocov_token) && value.nil?
94
+ return @nocov_token if instance_variable_defined?(:@nocov_token) && value.nil?
134
95
 
135
96
  @nocov_token = value || "nocov"
136
97
  end
98
+
99
+ private
100
+
101
+ # An empty list is how a project opts out of formatting.
102
+ def combined_formatter(formatters)
103
+ return if formatters.empty?
104
+
105
+ Formatter::MultiFormatter.new(formatters)
106
+ end
107
+
108
+ def resolve_format(name)
109
+ return name unless name.instance_of?(Symbol)
110
+
111
+ constant = BUILT_IN_FORMATS[name]
112
+ unless constant
113
+ raise ConfigurationError,
114
+ "Unknown format #{name.inspect}. Built-in formats are :html, :json, :simple, and :baseline; " \
115
+ "pass a formatter class or instance for anything else."
116
+ end
117
+
118
+ require_html_formatter(name)
119
+ Formatter.const_get(constant)
120
+ end
121
+
122
+ # `formats :html` has to work in a process that never required the HTML
123
+ # formatter, which under SIMPLECOV_NO_DEFAULTS nothing else has.
124
+ def require_html_formatter(name)
125
+ path = LAZY_FORMAT_REQUIRES[name]
126
+ require_relative(path) if path
127
+ end
137
128
  end
138
129
  end
@@ -1,38 +1,44 @@
1
1
  # frozen_string_literal: true
2
2
 
3
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
4
  module Configuration
7
- # Returns the configured groups. Add groups using SimpleCov.group.
8
5
  def groups
9
6
  @groups ||= {}
10
7
  end
11
8
 
9
+ # The group set `SimpleCov.grouped` bins against when its caller names none.
10
+ # It reads through a method of its own because the keyword it defaults
11
+ # shadows `groups`, so the default has to name a receiver, and every
12
+ # spelling of that receiver is the same call.
13
+ #
14
+ # mutant:disable — for that reason: `SimpleCov.groups`, `self.groups` and
15
+ # `groups()` cannot be told apart.
16
+ def default_groups
17
+ groups
18
+ end
19
+
12
20
  def groups=(new_groups)
13
21
  new_groups = new_groups.to_h { |name, filter| [GroupNames.normalize(name), filter] }
14
22
  GroupNames.validate!(new_groups.keys)
15
23
  @groups = new_groups
16
24
  end
17
25
 
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.
26
+ # Same matcher grammar as `skip`, but instead of dropping the matching files
27
+ # it bins them under `group_name` for the formatter. Files matched by no
28
+ # group fall into the implicit "Ungrouped" bucket.
22
29
  def group(group_name, filter_argument = nil, &)
23
30
  group_name = GroupNames.normalize(group_name)
24
31
  GroupNames.validate!([group_name])
25
32
  groups[group_name] = parse_filter(filter_argument, &)
26
33
  end
27
34
 
28
- # DEPRECATED: alias for `group`. Same arguments, same behavior.
29
35
  def add_group(group_name, filter_argument = nil, &block)
30
36
  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(
37
+ "`SimpleCov.group #{group_name.inspect} { ... }`"
38
+ else
39
+ "`SimpleCov.group #{group_name.inspect}, #{filter_argument.inspect}`"
40
+ end
41
+ Deprecation.warn(
36
42
  "`SimpleCov.add_group` is deprecated. " \
37
43
  "Replace with `SimpleCov.group` (same arguments, same behavior). Example: #{example}."
38
44
  )
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleCov
4
+ module Configuration
5
+ DROP_BASELINES = %i[last_run median branch].freeze
6
+
7
+ # How many runs coverage/.history.json keeps, newest kept. 0 disables
8
+ # recording entirely, and anything but a count of runs is refused.
9
+ def history_limit(limit = nil)
10
+ return @history_limit ||= 100 if limit.nil?
11
+
12
+ self.history_limit = limit
13
+ _ = @history_limit
14
+ end
15
+
16
+ def history_limit=(limit)
17
+ unless limit.instance_of?(Integer) && limit >= 0
18
+ raise ConfigurationError, "history_limit takes a non-negative integer, got #{limit.inspect}"
19
+ end
20
+
21
+ @history_limit = limit
22
+ end
23
+
24
+ # What `maximum_coverage_drop` measures the drop against: `:last_run` (the
25
+ # default) the previous run whatever it was, `:median` the median of the
26
+ # recorded history so one run that dipped for an unrelated reason cannot
27
+ # quietly become the baseline, and `:branch` the newest recorded run on the
28
+ # current git branch so a feature branch is compared with itself.
29
+ #
30
+ # The latter two read coverage/.history.json and find nothing to compare
31
+ # against while it is empty, which counts as "no previous run" the way a
32
+ # missing .last_run.json does.
33
+ def drop_baseline(mode = nil)
34
+ return @drop_baseline ||= :last_run if mode.nil?
35
+
36
+ unless DROP_BASELINES.include?(mode)
37
+ raise ConfigurationError,
38
+ "drop_baseline takes one of #{DROP_BASELINES}, got #{mode.inspect}"
39
+ end
40
+
41
+ @drop_baseline = mode
42
+ end
43
+ end
44
+ end
@@ -1,21 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
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
4
  module Configuration
8
- # Branch types accepted by `ignore_branches`.
9
5
  IGNORABLE_BRANCH_TYPES = %i[implicit_else eval_generated].freeze
10
- # Method types accepted by `ignore_methods`.
11
6
  IGNORABLE_METHOD_TYPES = %i[eval_generated].freeze
12
7
 
13
- # Variadic; multiple calls union. Setting is recorded regardless
14
- # of whether branch coverage is enabled at call time. See #1033, #1046.
8
+ # Deprecated in favour of `coverage(:branch) { ignore :implicit_else }`,
9
+ # which fixes the criterion by context the way the threshold verbs do. One
10
+ # difference rides out the deprecation period: the coverage block enables the
11
+ # criterion it names, while this legacy setter records without enabling.
15
12
  def ignore_branches(*types)
16
- types.each { |type| raise_if_branch_type_unsupported(type) }
17
- ignored_branches.concat(types).uniq!
18
- ignored_branches
13
+ Deprecation.warn("`SimpleCov.ignore_branches` is deprecated. " \
14
+ "Replace with `coverage(:branch) { ignore #{types.map(&:inspect).join(", ")} }`.")
15
+ store_ignored_branches(types)
19
16
  end
20
17
 
21
18
  def ignored_branches
@@ -26,12 +23,10 @@ module SimpleCov
26
23
  ignored_branches.include?(type)
27
24
  end
28
25
 
29
- # See `ignore_branches`. The only supported method-type token today
30
- # is `:eval_generated`; see #1046 for the rationale.
31
26
  def ignore_methods(*types)
32
- types.each { |type| raise_if_method_type_unsupported(type) }
33
- ignored_methods.concat(types).uniq!
34
- ignored_methods
27
+ Deprecation.warn("`SimpleCov.ignore_methods` is deprecated. " \
28
+ "Replace with `coverage(:method) { ignore #{types.map(&:inspect).join(", ")} }`.")
29
+ store_ignored_methods(types)
35
30
  end
36
31
 
37
32
  def ignored_methods
@@ -42,22 +37,36 @@ module SimpleCov
42
37
  ignored_methods.include?(type)
43
38
  end
44
39
 
45
- private
40
+ private
41
+
42
+ # Variadic semantics: multiple calls union, duplicates are no-ops, unknown
43
+ # tokens raise.
44
+ def store_ignored_branches(types)
45
+ types.each { |type| raise_if_branch_type_unsupported(type) }
46
+ ignored_branches.concat(types).uniq!
47
+ ignored_branches
48
+ end
49
+
50
+ def store_ignored_methods(types)
51
+ types.each { |type| raise_if_method_type_unsupported(type) }
52
+ ignored_methods.concat(types).uniq!
53
+ ignored_methods
54
+ end
46
55
 
47
56
  def raise_if_branch_type_unsupported(type)
48
57
  return if IGNORABLE_BRANCH_TYPES.member?(type)
49
58
 
50
- raise SimpleCov::ConfigurationError,
51
- "Unsupported branch type #{type.inspect} for `ignore_branches`. " \
52
- "Supported values are #{IGNORABLE_BRANCH_TYPES.inspect}"
59
+ raise ConfigurationError,
60
+ "Unsupported branch type #{type.inspect} for `ignore_branches`. " \
61
+ "Supported values are #{IGNORABLE_BRANCH_TYPES.inspect}"
53
62
  end
54
63
 
55
64
  def raise_if_method_type_unsupported(type)
56
65
  return if IGNORABLE_METHOD_TYPES.member?(type)
57
66
 
58
- raise SimpleCov::ConfigurationError,
59
- "Unsupported method type #{type.inspect} for `ignore_methods`. " \
60
- "Supported values are #{IGNORABLE_METHOD_TYPES.inspect}"
67
+ raise ConfigurationError,
68
+ "Unsupported method type #{type.inspect} for `ignore_methods`. " \
69
+ "Supported values are #{IGNORABLE_METHOD_TYPES.inspect}"
61
70
  end
62
71
  end
63
72
  end