simplecov 1.1.1 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (200) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +148 -5
  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/annotations.rb +166 -0
  13. data/lib/simplecov/cli/badge/svg.rb +70 -0
  14. data/lib/simplecov/cli/badge.rb +62 -0
  15. data/lib/simplecov/cli/clean.rb +23 -24
  16. data/lib/simplecov/cli/command_helpers.rb +55 -19
  17. data/lib/simplecov/cli/completions/scripts.rb +114 -0
  18. data/lib/simplecov/cli/completions.rb +83 -0
  19. data/lib/simplecov/cli/coverage.rb +25 -28
  20. data/lib/simplecov/cli/coverage_file.rb +58 -14
  21. data/lib/simplecov/cli/dead_code/output.rb +100 -0
  22. data/lib/simplecov/cli/dead_code.rb +139 -0
  23. data/lib/simplecov/cli/diff/output.rb +45 -0
  24. data/lib/simplecov/cli/diff.rb +35 -67
  25. data/lib/simplecov/cli/dotfile.rb +83 -38
  26. data/lib/simplecov/cli/git.rb +41 -0
  27. data/lib/simplecov/cli/history/output.rb +135 -0
  28. data/lib/simplecov/cli/history.rb +67 -0
  29. data/lib/simplecov/cli/merge.rb +34 -39
  30. data/lib/simplecov/cli/open.rb +12 -15
  31. data/lib/simplecov/cli/patch/changed_lines.rb +149 -0
  32. data/lib/simplecov/cli/patch/output.rb +151 -0
  33. data/lib/simplecov/cli/patch.rb +175 -0
  34. data/lib/simplecov/cli/ratchet/output.rb +44 -0
  35. data/lib/simplecov/cli/ratchet.rb +83 -0
  36. data/lib/simplecov/cli/report.rb +18 -24
  37. data/lib/simplecov/cli/run.rb +8 -9
  38. data/lib/simplecov/cli/serve/report_preparer.rb +7 -6
  39. data/lib/simplecov/cli/serve/static_file_handler.rb +62 -56
  40. data/lib/simplecov/cli/serve.rb +27 -24
  41. data/lib/simplecov/cli/show/annotator.rb +87 -0
  42. data/lib/simplecov/cli/show/sweep.rb +36 -0
  43. data/lib/simplecov/cli/show.rb +133 -0
  44. data/lib/simplecov/cli/status/facts.rb +87 -0
  45. data/lib/simplecov/cli/status.rb +93 -0
  46. data/lib/simplecov/cli/tests/redundancy.rb +78 -0
  47. data/lib/simplecov/cli/tests.rb +148 -0
  48. data/lib/simplecov/cli/uncovered/misses.rb +33 -0
  49. data/lib/simplecov/cli/uncovered.rb +58 -34
  50. data/lib/simplecov/cli/usage.rb +153 -0
  51. data/lib/simplecov/cli/watch/live_report.rb +73 -0
  52. data/lib/simplecov/cli/watch/narrator.rb +55 -0
  53. data/lib/simplecov/cli/watch/poller.rb +45 -0
  54. data/lib/simplecov/cli/watch/session.rb +134 -0
  55. data/lib/simplecov/cli/watch/test_plan.rb +43 -0
  56. data/lib/simplecov/cli/watch.rb +85 -0
  57. data/lib/simplecov/cli.rb +43 -92
  58. data/lib/simplecov/color.rb +19 -37
  59. data/lib/simplecov/combine/branches_combiner.rb +7 -31
  60. data/lib/simplecov/combine/coverage_accumulator.rb +49 -142
  61. data/lib/simplecov/combine/identity_interner.rb +10 -13
  62. data/lib/simplecov/combine/interned_counts.rb +7 -9
  63. data/lib/simplecov/combine/lines_combiner.rb +20 -44
  64. data/lib/simplecov/combine/methods_combiner.rb +9 -35
  65. data/lib/simplecov/combine/results_combiner.rb +4 -21
  66. data/lib/simplecov/combine.rb +0 -6
  67. data/lib/simplecov/command_guesser.rb +20 -34
  68. data/lib/simplecov/configuration/baseline.rb +31 -0
  69. data/lib/simplecov/configuration/coverage.rb +91 -79
  70. data/lib/simplecov/configuration/coverage_criteria.rb +43 -54
  71. data/lib/simplecov/configuration/deprecations.rb +21 -0
  72. data/lib/simplecov/configuration/eval_coverage.rb +7 -11
  73. data/lib/simplecov/configuration/filters.rb +40 -73
  74. data/lib/simplecov/configuration/formatting.rb +74 -83
  75. data/lib/simplecov/configuration/groups.rb +19 -13
  76. data/lib/simplecov/configuration/history.rb +44 -0
  77. data/lib/simplecov/configuration/ignored_entries.rb +31 -22
  78. data/lib/simplecov/configuration/merging.rb +94 -62
  79. data/lib/simplecov/configuration/missed_caps.rb +51 -0
  80. data/lib/simplecov/configuration/production.rb +23 -0
  81. data/lib/simplecov/configuration/test_tracking.rb +48 -0
  82. data/lib/simplecov/configuration/thresholds.rb +33 -62
  83. data/lib/simplecov/configuration/view_coverage.rb +37 -0
  84. data/lib/simplecov/configuration.rb +70 -76
  85. data/lib/simplecov/configuration_error.rb +5 -0
  86. data/lib/simplecov/context_map/union.rb +81 -0
  87. data/lib/simplecov/context_map.rb +187 -0
  88. data/lib/simplecov/coverage_json.rb +2 -5
  89. data/lib/simplecov/coverage_statistics.rb +14 -24
  90. data/lib/simplecov/coverage_violations.rb +160 -65
  91. data/lib/simplecov/current_run.rb +61 -0
  92. data/lib/simplecov/defaults.rb +4 -23
  93. data/lib/simplecov/deprecation.rb +45 -27
  94. data/lib/simplecov/directive/erb.rb +44 -0
  95. data/lib/simplecov/directive/haml.rb +33 -0
  96. data/lib/simplecov/directive/indented_template.rb +37 -0
  97. data/lib/simplecov/directive/slim.rb +35 -0
  98. data/lib/simplecov/directive/template.rb +29 -0
  99. data/lib/simplecov/directive.rb +47 -62
  100. data/lib/simplecov/exit_codes/baseline_check.rb +46 -0
  101. data/lib/simplecov/exit_codes/check.rb +17 -11
  102. data/lib/simplecov/exit_codes/exit_code_handling.rb +21 -8
  103. data/lib/simplecov/exit_codes/maximum_coverage_drop_check.rb +7 -10
  104. data/lib/simplecov/exit_codes/maximum_missed_check.rb +26 -0
  105. data/lib/simplecov/exit_codes/maximum_missed_per_file_check.rb +34 -0
  106. data/lib/simplecov/exit_codes/maximum_overall_coverage_check.rb +7 -12
  107. data/lib/simplecov/exit_codes/minimum_coverage_by_file_check.rb +11 -10
  108. data/lib/simplecov/exit_codes/minimum_coverage_by_group_check.rb +7 -9
  109. data/lib/simplecov/exit_codes/minimum_overall_coverage_check.rb +16 -17
  110. data/lib/simplecov/exit_codes.rb +16 -10
  111. data/lib/simplecov/exit_handling.rb +94 -59
  112. data/lib/simplecov/file_list.rb +20 -51
  113. data/lib/simplecov/filter.rb +43 -66
  114. data/lib/simplecov/formatter/base.rb +37 -55
  115. data/lib/simplecov/formatter/baseline_formatter.rb +98 -0
  116. data/lib/simplecov/formatter/coverage_json_writer.rb +29 -31
  117. data/lib/simplecov/formatter/html_formatter/public/index.html +15 -9
  118. data/lib/simplecov/formatter/html_formatter/viewer_data_validator.rb +74 -17
  119. data/lib/simplecov/formatter/html_formatter.rb +24 -41
  120. data/lib/simplecov/formatter/json_formatter/errors_formatter.rb +50 -16
  121. data/lib/simplecov/formatter/json_formatter/production_section_formatter.rb +48 -0
  122. data/lib/simplecov/formatter/json_formatter/result_hash_formatter.rb +43 -29
  123. data/lib/simplecov/formatter/json_formatter/source_file_formatter.rb +14 -10
  124. data/lib/simplecov/formatter/json_formatter.rb +7 -13
  125. data/lib/simplecov/formatter/multi_formatter.rb +5 -11
  126. data/lib/simplecov/formatter/simple_formatter.rb +2 -8
  127. data/lib/simplecov/formatter.rb +14 -10
  128. data/lib/simplecov/group_names.rb +13 -12
  129. data/lib/simplecov/history.rb +115 -0
  130. data/lib/simplecov/last_run.rb +6 -9
  131. data/lib/simplecov/lines_classifier.rb +31 -16
  132. data/lib/simplecov/load_global_config.rb +7 -7
  133. data/lib/simplecov/parallel_adapters/base.rb +13 -41
  134. data/lib/simplecov/parallel_adapters/generic.rb +7 -19
  135. data/lib/simplecov/parallel_adapters/parallel_tests.rb +21 -42
  136. data/lib/simplecov/parallel_adapters.rb +24 -55
  137. data/lib/simplecov/parallel_coordination.rb +39 -49
  138. data/lib/simplecov/parallel_result_merger/worker_payload.rb +33 -0
  139. data/lib/simplecov/parallel_result_merger.rb +67 -126
  140. data/lib/simplecov/process.rb +14 -32
  141. data/lib/simplecov/production/error.rb +11 -0
  142. data/lib/simplecov/production/file_sink.rb +130 -0
  143. data/lib/simplecov/production.rb +249 -0
  144. data/lib/simplecov/profiles/rails.rb +4 -15
  145. data/lib/simplecov/profiles/root_filter.rb +0 -4
  146. data/lib/simplecov/profiles/strict.rb +11 -21
  147. data/lib/simplecov/profiles.rb +11 -33
  148. data/lib/simplecov/report_deferral.rb +11 -15
  149. data/lib/simplecov/report_stamp.rb +6 -9
  150. data/lib/simplecov/result/filter_config.rb +19 -0
  151. data/lib/simplecov/result/missing_source_files_reporter.rb +8 -9
  152. data/lib/simplecov/result/serialization.rb +43 -0
  153. data/lib/simplecov/result/source_file_builder.rb +7 -12
  154. data/lib/simplecov/result.rb +74 -117
  155. data/lib/simplecov/result_adapter.rb +56 -68
  156. data/lib/simplecov/result_merger/contexts.rb +22 -0
  157. data/lib/simplecov/result_merger/legacy_format_adapter.rb +4 -8
  158. data/lib/simplecov/result_merger/resultset_file.rb +20 -27
  159. data/lib/simplecov/result_merger/resultset_run_identity.rb +23 -25
  160. data/lib/simplecov/result_merger/resultset_store.rb +23 -14
  161. data/lib/simplecov/result_merger/unloaded_files.rb +24 -32
  162. data/lib/simplecov/result_merger.rb +59 -58
  163. data/lib/simplecov/result_processing.rb +92 -122
  164. data/lib/simplecov/run_identity.rb +16 -18
  165. data/lib/simplecov/simulate_coverage.rb +34 -50
  166. data/lib/simplecov/source_file/branch.rb +8 -33
  167. data/lib/simplecov/source_file/branch_builder.rb +28 -47
  168. data/lib/simplecov/source_file/builder_context.rb +13 -13
  169. data/lib/simplecov/source_file/line.rb +13 -33
  170. data/lib/simplecov/source_file/line_builder.rb +9 -15
  171. data/lib/simplecov/source_file/method.rb +3 -7
  172. data/lib/simplecov/source_file/method_builder.rb +6 -15
  173. data/lib/simplecov/source_file/ruby_data_parser.rb +59 -66
  174. data/lib/simplecov/source_file/skip_chunks.rb +29 -29
  175. data/lib/simplecov/source_file/source_loader.rb +45 -35
  176. data/lib/simplecov/source_file/statistics.rb +20 -9
  177. data/lib/simplecov/source_file.rb +21 -51
  178. data/lib/simplecov/static_coverage_extractor/condition_folding.rb +55 -139
  179. data/lib/simplecov/static_coverage_extractor/location_conventions.rb +76 -121
  180. data/lib/simplecov/static_coverage_extractor/method_collector.rb +9 -26
  181. data/lib/simplecov/static_coverage_extractor/prism_compat.rb +15 -24
  182. data/lib/simplecov/static_coverage_extractor/value_position.rb +25 -38
  183. data/lib/simplecov/static_coverage_extractor/visitor.rb +45 -65
  184. data/lib/simplecov/static_coverage_extractor.rb +43 -75
  185. data/lib/simplecov/test_tracker/accessors.rb +34 -0
  186. data/lib/simplecov/test_tracker/constant_watch.rb +49 -0
  187. data/lib/simplecov/test_tracker/delta.rb +69 -0
  188. data/lib/simplecov/test_tracker/framework_hooks.rb +130 -0
  189. data/lib/simplecov/test_tracker.rb +162 -0
  190. data/lib/simplecov/unloaded_file_injector.rb +18 -31
  191. data/lib/simplecov/useless_results_remover.rb +7 -6
  192. data/lib/simplecov/version.rb +1 -1
  193. data/lib/simplecov/view_coverage/template_compiler.rb +83 -0
  194. data/lib/simplecov/view_coverage.rb +55 -0
  195. data/lib/simplecov-html.rb +1 -0
  196. data/lib/simplecov.rb +57 -113
  197. data/lib/simplecov_json_formatter.rb +1 -0
  198. data/man/simplecov.1 +354 -0
  199. data/sig/simplecov.rbs +528 -595
  200. metadata +79 -6
@@ -1,16 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SimpleCov
4
- # Coverage threshold configuration: `minimum_coverage`,
5
- # `maximum_coverage`, `expected_coverage`, `maximum_coverage_drop`,
6
- # `minimum_coverage_by_file`, `minimum_coverage_by_group`,
7
- # `refuse_coverage_drop`, and friends.
8
4
  module Configuration
9
- #
10
- # Defines the minimum overall coverage required for the testsuite to pass.
11
- # Returns non-zero if the current coverage is below this threshold.
12
- # Default is 0% (disabled).
13
- #
14
5
  def minimum_coverage(coverage = nil)
15
6
  return @minimum_coverage ||= {} unless coverage
16
7
 
@@ -25,10 +16,10 @@ module SimpleCov
25
16
  end
26
17
 
27
18
  #
28
- # Defines the maximum overall coverage allowed for the testsuite to
29
- # pass. Useful paired with `minimum_coverage` (or via
30
- # `expected_coverage`) to pin coverage to an exact value, so an
31
- # unexpected jump up fails the build. See #187.
19
+ # The maximum overall coverage allowed for the testsuite to pass. Useful
20
+ # paired with `minimum_coverage` (or via `expected_coverage`) to pin
21
+ # coverage to an exact value, so an unexpected jump up fails the build
22
+ # (#187).
32
23
  #
33
24
  def maximum_coverage(coverage = nil)
34
25
  return @maximum_coverage ||= {} unless coverage
@@ -36,10 +27,6 @@ module SimpleCov
36
27
  @maximum_coverage = normalized_threshold(coverage, "maximum_coverage")
37
28
  end
38
29
 
39
- #
40
- # Pins the suite to an exact coverage figure by setting both
41
- # `minimum_coverage` and `maximum_coverage`. See #187.
42
- #
43
30
  def expected_coverage(coverage = nil)
44
31
  return minimum_coverage if coverage.nil?
45
32
 
@@ -47,10 +34,6 @@ module SimpleCov
47
34
  maximum_coverage(coverage)
48
35
  end
49
36
 
50
- #
51
- # Defines the maximum coverage drop at once allowed for the
52
- # testsuite to pass. Default is 100% (disabled).
53
- #
54
37
  def maximum_coverage_drop(coverage_drop = nil)
55
38
  return @maximum_coverage_drop ||= {} unless coverage_drop
56
39
 
@@ -58,11 +41,10 @@ module SimpleCov
58
41
  end
59
42
 
60
43
  #
61
- # Defines the minimum coverage per file required for the testsuite
62
- # to pass. Accepts a Numeric (global threshold on the primary
63
- # criterion), a Symbol-keyed Hash (per-criterion globals), or a
64
- # Hash mixing Symbol keys with String / Regexp keys to declare
65
- # per-path overrides. See README and #575.
44
+ # The minimum coverage per file required for the testsuite to pass.
45
+ # Accepts a Numeric (global threshold on the primary criterion), a
46
+ # Symbol-keyed Hash (per-criterion globals), or a Hash mixing Symbol keys
47
+ # with String / Regexp keys to declare per-path overrides (#575).
66
48
  #
67
49
  def minimum_coverage_by_file(coverage = nil)
68
50
  return @minimum_coverage_by_file ||= {} unless coverage
@@ -70,8 +52,8 @@ module SimpleCov
70
52
  coverage = {primary_coverage => coverage} if coverage.is_a?(Numeric)
71
53
  defaults, overrides = partition_per_file_thresholds(coverage)
72
54
 
73
- SimpleCov::Deprecation.warn("`SimpleCov.minimum_coverage_by_file` is deprecated. " \
74
- "Replace it with:\n#{per_file_coverage_replacement(defaults, overrides)}")
55
+ Deprecation.warn("`SimpleCov.minimum_coverage_by_file` is deprecated. " \
56
+ "Replace it with:\n#{per_file_coverage_replacement(defaults, overrides)}")
75
57
 
76
58
  raise_on_invalid_coverage(defaults, "minimum_coverage_by_file")
77
59
  overrides.each_value { |criteria| raise_on_invalid_coverage(criteria, "minimum_coverage_by_file") }
@@ -80,91 +62,80 @@ module SimpleCov
80
62
  @minimum_coverage_by_file_overrides = overrides
81
63
  end
82
64
 
83
- # Returns the per-path overrides set via `minimum_coverage_by_file`.
84
65
  def minimum_coverage_by_file_overrides
85
66
  @minimum_coverage_by_file_overrides ||= {}
86
67
  end
87
68
 
88
- #
89
- # Defines the minimum coverage per group required for the testsuite
90
- # to pass. Default is 0% (disabled).
91
- #
92
69
  def minimum_coverage_by_group(coverage = nil)
93
70
  return @minimum_coverage_by_group ||= {} unless coverage
94
71
 
95
- SimpleCov::Deprecation.warn("`SimpleCov.minimum_coverage_by_group` is deprecated. " \
96
- "Replace it with:\n#{per_group_coverage_replacement(coverage)}")
72
+ Deprecation.warn("`SimpleCov.minimum_coverage_by_group` is deprecated. " \
73
+ "Replace it with:\n#{per_group_coverage_replacement(coverage)}")
97
74
  @minimum_coverage_by_group = coverage.to_h do |group_name, group_coverage|
98
75
  [GroupNames.normalize(group_name), normalized_threshold(group_coverage, "minimum_coverage_by_group")]
99
76
  end
100
77
  end
101
78
 
102
- #
103
- # Refuses any coverage drop. Coverage is only allowed to increase.
104
- #
105
79
  def refuse_coverage_drop(*criteria)
106
80
  criteria = coverage_criteria if criteria.empty?
107
81
  maximum_coverage_drop(criteria.to_h { |c| [c, 0] })
108
82
  end
109
83
 
110
- private
84
+ private
111
85
 
112
- # Shared normalize-and-validate step behind every threshold setter:
113
- # a bare Numeric targets the primary criterion, and the resulting
86
+ # A bare Numeric targets the primary criterion, and the resulting
114
87
  # per-criterion hash is validated before it is stored.
115
88
  def normalized_threshold(coverage, setting)
116
89
  coverage = {primary_coverage => coverage} if coverage.is_a?(Numeric)
117
- raise_on_invalid_coverage(coverage, setting)
118
- coverage
90
+ coverage.tap { |thresholds| raise_on_invalid_coverage(thresholds, setting) }
119
91
  end
120
92
 
121
- # Split a `minimum_coverage_by_file` argument into Symbol-keyed
122
- # criterion defaults and String/Regexp-keyed per-path overrides;
123
- # normalize Numeric override values to `{primary_coverage => N}`
124
- # so downstream code only has one shape to handle.
93
+ # Splits into Symbol-keyed criterion defaults and String/Regexp-keyed
94
+ # per-path overrides, normalizing Numeric override values to
95
+ # `{primary_coverage => N}` so downstream code has one shape to handle.
125
96
  def partition_per_file_thresholds(coverage)
126
97
  coverage.each_key { |key| validate_per_file_key(key) }
127
- pairs = coverage.partition { |key, _| key.is_a?(Symbol) }
128
- # The assertions restate what the partition predicate guarantees:
129
- # Symbol keys carry per-criterion Numeric defaults, the rest are paths.
130
- defaults = pairs[0].to_h #: coverage_thresholds
131
- raw = pairs[1].to_h #: Hash[String | Regexp, Numeric | coverage_thresholds]
98
+ # The assertions restate what the partition predicate guarantees: Symbol
99
+ # keys carry per-criterion Numeric defaults, the rest are paths.
100
+ symbol_pairs, path_pairs = coverage.partition { |key, _| key.instance_of?(Symbol) }
101
+ defaults = symbol_pairs.to_h #: coverage_thresholds
102
+ raw = path_pairs.to_h #: Hash[String | Regexp, Numeric | coverage_thresholds]
132
103
  overrides = raw.transform_values { |value| value.is_a?(Numeric) ? {primary_coverage => value} : value }
133
104
  [defaults, overrides]
134
105
  end
135
106
 
136
107
  def validate_per_file_key(key)
137
- return if key.is_a?(Symbol) || key.is_a?(String) || key.is_a?(Regexp)
108
+ # Symbols have no subclasses; paths and patterns may.
109
+ return if key.instance_of?(Symbol) || key.is_a?(String) || key.is_a?(Regexp)
138
110
 
139
- raise SimpleCov::ConfigurationError,
140
- "minimum_coverage_by_file keys must be Symbol (criterion), String, or Regexp; got #{key.inspect}"
111
+ raise ConfigurationError,
112
+ "minimum_coverage_by_file keys must be Symbol (criterion), String, or Regexp; got #{key.inspect}"
141
113
  end
142
114
 
143
115
  def minimum_possible_coverage_exceeded(coverage_option)
144
116
  warn "The coverage you set for #{coverage_option} is greater than 100%"
145
117
  end
146
118
 
147
- # Render the `coverage` configuration equivalent to a (deprecated)
148
- # `minimum_coverage_by_file` argument so the deprecation warning can be
119
+ # Renders the `coverage` configuration equivalent to a deprecated
120
+ # `minimum_coverage_by_file` argument, so the deprecation warning can be
149
121
  # copy-pasted verbatim into the user's config.
150
122
  def per_file_coverage_replacement(defaults, overrides)
151
123
  by_criterion = {} #: Hash[Symbol, Array[String]]
152
- defaults.each { |criterion, percent| (by_criterion[criterion] ||= []) << "minimum_per_file #{percent}" }
124
+ defaults.each { |criterion, percent| (by_criterion[criterion] ||= []) << "minimum #{percent}, per: :file" }
153
125
  overrides.each do |target, criteria|
154
126
  criteria.each do |criterion, percent|
155
- (by_criterion[criterion] ||= []) << "minimum_per_file #{percent}, only: #{target.inspect}"
127
+ (by_criterion[criterion] ||= []) << "minimum #{percent}, per: #{target.inspect}"
156
128
  end
157
129
  end
158
130
  render_coverage_blocks(by_criterion)
159
131
  end
160
132
 
161
- # Same, for a (deprecated) `minimum_coverage_by_group` argument.
162
133
  def per_group_coverage_replacement(coverage)
163
134
  by_criterion = {} #: Hash[Symbol, Array[String]]
164
135
  coverage.each do |group_name, thresholds|
165
136
  normalized = (thresholds.is_a?(Numeric) ? {primary_coverage => thresholds} : thresholds) #: coverage_thresholds
166
137
  normalized.each do |criterion, percent|
167
- (by_criterion[criterion] ||= []) << "minimum_per_group #{percent}, only: #{group_name.inspect}"
138
+ (by_criterion[criterion] ||= []) << "minimum #{percent}, per: group(#{group_name.inspect})"
168
139
  end
169
140
  end
170
141
  render_coverage_blocks(by_criterion)
@@ -172,7 +143,7 @@ module SimpleCov
172
143
 
173
144
  def render_coverage_blocks(by_criterion)
174
145
  by_criterion.map do |criterion, statements|
175
- " coverage(#{criterion.inspect}) { #{statements.join('; ')} }"
146
+ " coverage(#{criterion.inspect}) { #{statements.join("; ")} }"
176
147
  end.join("\n")
177
148
  end
178
149
  end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleCov
4
+ module Configuration
5
+ DEFAULT_VIEW_GLOBS = %w[app/views/**/*.{erb,haml,slim}].freeze
6
+
7
+ # Reports coverage for ActionView templates, defaulting to a Rails app's
8
+ # views. Globs are expanded against `SimpleCov.root`, and one naming an
9
+ # extension no handler is registered for (as the default does whenever a
10
+ # project has no Haml or Slim) leaves those files out rather than reporting
11
+ # them as empty.
12
+ #
13
+ # Templates the suite renders are measured through eval coverage, which this
14
+ # enables. Templates it never renders are compiled at the end of the run so
15
+ # they appear at 0% instead of going missing.
16
+ def cover_views(*globs)
17
+ globs = globs.flatten.compact
18
+ globs = DEFAULT_VIEW_GLOBS.dup if globs.empty?
19
+ @view_globs = globs
20
+ enable_eval_coverage
21
+ globs
22
+ end
23
+
24
+ # Nil rather than an empty Array when `cover_views` was never called, so
25
+ # "not asked for" stays distinguishable from "asked for, with nothing to
26
+ # match".
27
+ def view_globs
28
+ @view_globs
29
+ end
30
+
31
+ # False when eval coverage couldn't be enabled, where `cover_views` has
32
+ # already warned and the templates would come back empty rather than at 0%.
33
+ def view_coverage?
34
+ !view_globs.nil? && coverage_for_eval_enabled?
35
+ end
36
+ end
37
+ end
@@ -3,82 +3,81 @@
3
3
  require "fileutils"
4
4
 
5
5
  module SimpleCov
6
- #
7
- # Bundles the configuration options used for SimpleCov. All methods
8
- # defined here are usable from SimpleCov directly. Please check out
9
- # SimpleCov documentation for further info.
10
- #
11
6
  module Configuration
12
- #
13
- # The root for the project. This defaults to the
14
- # current working directory.
15
- #
16
- # Configure with SimpleCov.root('/my/project/path')
17
- #
18
7
  def root(root = nil)
19
- return @root if defined?(@root) && root.nil?
8
+ return @root if instance_variable_defined?(:@root) && root.nil?
20
9
 
10
+ self.root = root
11
+ @root
12
+ end
13
+
14
+ # nil means the working directory, the default the reader would have derived.
15
+ def root=(root)
21
16
  @coverage_path = nil unless @coverage_path_explicit # invalidate cache
22
17
  @root = File.expand_path(root || Dir.getwd)
23
18
  end
24
19
 
25
- #
26
- # The name of the output and cache directory. Defaults to 'coverage'
27
- #
28
- # Configure with SimpleCov.coverage_dir('cov')
29
- #
30
20
  def coverage_dir(dir = nil)
31
- return @coverage_dir if defined?(@coverage_dir) && dir.nil?
21
+ return @coverage_dir if instance_variable_defined?(:@coverage_dir) && dir.nil?
22
+
23
+ self.coverage_dir = dir
24
+ @coverage_dir
25
+ end
32
26
 
27
+ # nil means the default 'coverage', recorded as a derivation rather than a
28
+ # choice.
29
+ def coverage_dir=(dir)
33
30
  @coverage_path = nil unless @coverage_path_explicit # invalidate cache
34
31
  @coverage_dir_explicit = true unless dir.nil?
35
32
  @coverage_dir = dir || "coverage"
36
33
  end
37
34
 
38
35
  #
39
- # Returns the full path to the output directory. By default
40
- # constructed from `SimpleCov.root` + `SimpleCov.coverage_dir`, but
41
- # callers can override with an arbitrary absolute path — handy for
42
- # out-of-tree build directories. See #716.
36
+ # Defaults to `SimpleCov.root` + `SimpleCov.coverage_dir`, but callers can
37
+ # override with an arbitrary absolute path, handy for out-of-tree build
38
+ # directories (#716).
43
39
  #
44
- # Reading is pure: the directory is only created when a path is
45
- # explicitly assigned (the user has signaled they intend to write
46
- # there). The codepaths that actually write into the directory
47
- # (formatters, `LastRun`, `ResultsetStore`) ensure existence
48
- # themselves, so read-only CLI subcommands that interpolate the
49
- # path into status text don't materialize a stray `coverage/`
50
- # directory.
40
+ # Reading is pure: the directory is only created when a path is explicitly
41
+ # assigned. The codepaths that write into it ensure existence themselves,
42
+ # so read-only CLI subcommands that interpolate the path into status text
43
+ # don't materialize a stray `coverage/` directory.
51
44
  #
52
45
  def coverage_path(path = nil)
53
- if path
54
- expanded = File.expand_path(path)
55
- @coverage_path = expanded
56
- @coverage_path_explicit = true
57
- FileUtils.mkdir_p expanded
58
- end
46
+ self.coverage_path = path if path
59
47
 
60
48
  @coverage_path ||= File.expand_path(coverage_dir, root)
61
49
  end
62
50
 
51
+ # Assigning is the signal the caller intends to write there, so the
52
+ # directory is created.
53
+ def coverage_path=(path)
54
+ expanded = File.expand_path(path)
55
+ @coverage_path = expanded
56
+ @coverage_path_explicit = true
57
+ FileUtils.mkdir_p expanded
58
+ end
59
+
63
60
  #
64
- # The name of the command (a.k.a. Test Suite) currently running.
65
- # Used for result merging and caching. Auto-detected; set explicitly
66
- # with SimpleCov.command_name("test:units").
61
+ # The name of the command (a.k.a. Test Suite) currently running. Used for
62
+ # result merging and caching. Auto-detected.
67
63
  #
68
64
  def command_name(name = nil)
69
- @command_name = name unless name.nil?
70
- @command_name ||= SimpleCov::CommandGuesser.guess
65
+ self.command_name = name unless name.nil?
66
+ @command_name ||= CommandGuesser.guess
67
+ end
68
+
69
+ def command_name=(name)
70
+ @command_name = name
71
71
  end
72
72
 
73
- # Returns the hash of available profiles
74
73
  def profiles
75
- @profiles ||= SimpleCov::Profiles.new
74
+ @profiles ||= Profiles.new
76
75
  end
77
76
 
78
77
  #
79
- # Allows you to configure simplecov in a block instead of
80
- # prepending SimpleCov to each config method. Parameterized blocks retain
81
- # their caller context and receive this configuration target explicitly.
78
+ # Configure simplecov in a block instead of prepending SimpleCov to each
79
+ # config method. Parameterized blocks retain their caller context and
80
+ # receive this configuration target explicitly.
82
81
  #
83
82
  def configure(&block)
84
83
  raise ArgumentError, "configuration block required" unless block
@@ -91,61 +90,49 @@ module SimpleCov
91
90
  self
92
91
  end
93
92
 
94
- #
95
- # Gets or sets the behavior to process coverage results.
96
- # By default, it stores/merges the current result and formats only
97
- # from the final reporting process.
98
- #
99
93
  def at_exit(&block)
100
94
  @at_exit = block if block
101
95
  configured = @at_exit
102
96
  return configured if configured
103
- return proc {} unless active_session?
97
+ return -> {} unless active_session?
104
98
 
105
- @at_exit = proc do
99
+ @at_exit = lambda do
106
100
  result = SimpleCov.result
107
101
  result.format! if result && SimpleCov.merge_finalization_owner?
108
102
  end
109
103
  end
110
104
 
111
- # Whether SimpleCov has anything to do at exit: the Coverage module
112
- # is actively tracking, or a `@result` has already been assembled
113
- # (e.g. by `SimpleCov.collate`, which never starts Coverage).
105
+ # The Coverage module is actively tracking, or a `@result` has already been
106
+ # assembled (`SimpleCov.collate` never starts Coverage).
114
107
  def active_session?
115
- SimpleCov.result? || (defined?(Coverage) && Coverage.running?)
108
+ !!SimpleCov.result? || coverage_running?
109
+ end
110
+
111
+ # A configuration loaded on its own may have no Coverage to ask, which is
112
+ # not something a suite that measures coverage can be.
113
+ def coverage_running?
114
+ defined?(Coverage) ? Coverage.running? : false
116
115
  end
117
116
 
118
- #
119
- # Gets or sets the behavior to start a new forked Process.
120
- # Defaults to adding " (subprocess: #{serial})" to command_name and
121
- # starting SimpleCov in quiet mode.
122
- #
123
117
  def at_fork(&block)
124
118
  @at_fork = block if block
125
119
  @at_fork ||= lambda { |_pid|
126
- # Needs a name that's unique per worker within a run yet identical
127
- # across runs. Build it from SimpleCov's stable fork serial rather
128
- # than the OS pid: with the pid, every run produced uniquely-named
129
- # results that never overwrote the previous run's, so they piled up
130
- # in .resultset.json until merge_timeout and the merged report's
131
- # file set drifted from run to run. See issue #1171.
120
+ # Needs a name that's unique per worker within a run yet identical across
121
+ # runs. Built from SimpleCov's stable fork serial rather than the OS pid:
122
+ # with the pid, every run produced uniquely-named results that never
123
+ # overwrote the previous run's, so they piled up in .resultset.json
124
+ # until merge_timeout and the merged report's file set drifted from run
125
+ # to run (#1171).
132
126
  SimpleCov.command_name "#{SimpleCov.command_name} (subprocess: #{SimpleCov.subprocess_serial})"
133
- # be quiet, the parent process will use the regular formatter
134
127
  SimpleCov.print_errors false
135
- SimpleCov.formatter SimpleCov::Formatter::SimpleFormatter
128
+ SimpleCov.formatter Formatter::SimpleFormatter
136
129
  SimpleCov.minimum_coverage 0
137
130
  SimpleCov.start
138
131
  }
139
132
  end
140
133
 
141
- #
142
- # Returns the project name — defaults to the last dirname in
143
- # SimpleCov.root, capitalized with underscores → spaces.
144
- #
134
+ # Only a String renames, and a name already chosen survives anything else.
145
135
  def project_name(new_name = nil)
146
- current = defined?(@project_name) ? @project_name : nil
147
- return current if current && new_name.nil?
148
-
149
136
  @project_name = new_name if new_name.is_a?(String)
150
137
  @project_name ||= File.basename(root).capitalize.tr("_", " ")
151
138
  end
@@ -160,4 +147,11 @@ require_relative "configuration/groups"
160
147
  require_relative "configuration/formatting"
161
148
  require_relative "configuration/ignored_entries"
162
149
  require_relative "configuration/merging"
150
+ require_relative "configuration/test_tracking"
163
151
  require_relative "configuration/thresholds"
152
+ require_relative "configuration/baseline"
153
+ require_relative "configuration/deprecations"
154
+ require_relative "configuration/history"
155
+ require_relative "configuration/missed_caps"
156
+ require_relative "configuration/production"
157
+ require_relative "configuration/view_coverage"
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleCov
4
+ class ConfigurationError < StandardError; end
5
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleCov
4
+ class ContextMap
5
+ # Accumulates the context maps of every result a merge absorbs, under the
6
+ # all-or-nothing rule the resultset format needs: the merged result carries
7
+ # the union of the maps when every merged result recorded one, and no map at
8
+ # all otherwise. Keeping a partial union would present one worker's map as
9
+ # the whole run's, silently omitting the tests of the results that didn't
10
+ # record.
11
+ class Union
12
+ attr_reader :carrying, :entries
13
+
14
+ def initialize
15
+ @map = ContextMap.new
16
+ @complete = true
17
+ @carrying = 0
18
+ @entries = 0
19
+ end
20
+
21
+ def collector
22
+ ->(surviving) { absorb_resultset(surviving) }
23
+ end
24
+
25
+ def absorb_resultset(resultset)
26
+ resultset.each_value { |data| absorb_entry(data) }
27
+ end
28
+
29
+ def absorb_entry(data)
30
+ @entries += 1
31
+ map = ContextMap.from_hash(data["contexts"])
32
+ return @complete = false unless map
33
+
34
+ @carrying += 1
35
+ @map.absorb(map)
36
+ end
37
+
38
+ # The parallel merge builds one union per worker over its slice and ships it
39
+ # back, so the parent combines unions rather than re-reading entries.
40
+ def absorb_union(other)
41
+ @entries += other.entries
42
+ @carrying += other.carrying
43
+ @complete &&= other.complete?
44
+ @map.absorb(other.partial_map)
45
+ self
46
+ end
47
+
48
+ def complete?
49
+ @complete
50
+ end
51
+
52
+ # The union when every absorbed result carried a map, nil otherwise. Dropping
53
+ # warns when the merge mixed results with and without maps, since that
54
+ # usually means `track_tests` is on in some workers and not others; a merge
55
+ # where nothing recorded stays quiet.
56
+ def map
57
+ return @map if @complete
58
+
59
+ warn_about_partial_maps
60
+ end
61
+
62
+ protected
63
+
64
+ # Protected: only another union folding this one in may read it.
65
+ def partial_map
66
+ @map
67
+ end
68
+
69
+ private
70
+
71
+ def warn_about_partial_maps
72
+ return unless @carrying.positive? && SimpleCov.print_errors
73
+
74
+ warn "[SimpleCov]: Dropped the per-test map from the merged result: " \
75
+ "only #{@carrying} of #{@entries} merged results carry one. The map is " \
76
+ "kept only when every merged result records it, so enable `track_tests` " \
77
+ "in every suite that should contribute."
78
+ end
79
+ end
80
+ end
81
+ end