Module: SimpleCov::CoverageViolations
| Relationships & Source Files | |
| Defined in: | lib/simplecov/coverage_violations.rb |
Overview
Computes coverage threshold violations for a given result. Shared by
the exit-code checks and the JSON formatter's errors section.
Each method returns an array of violation hashes. All percents are rounded via round_coverage so downstream consumers don't need to round again.
Class Method Summary
- .maximum_drop(result, thresholds, last_run: SimpleCov::LastRun.read) ⇒ Array<Hash>
-
.maximum_overall(result, thresholds) ⇒ Array<Hash>
Tolerance: .percent_for floors the actual percent to two decimal places (matching the existing minimum-coverage behavior), so an actual of e.g. 95.4287 is treated as 95.42 — meaning a maximum of 95.42 still passes.
-
.minimum_by_file(result, defaults, overrides = {}) ⇒ Array<Hash>
defaultsis the criterion-keyed Hash applied to every file. - .minimum_by_group(result, thresholds) ⇒ Array<Hash>
- .minimum_overall(result, thresholds) ⇒ Array<Hash>
- .compute_drop(criterion, result, last_run) private
-
.effective_per_file_thresholds(file, defaults, overrides)
private
Walk the overrides in declaration order, merging each one that matches the file's project path into the running effective threshold (so the most-specific or latest-declared override wins per criterion).
- .file_minimum_violation(file, criterion, expected) private
- .group_minimum_violations(group_name, group, minimums) private
- .lookup_group(result, group_name) private
-
.path_matches?(project_filename, pattern) ⇒ Boolean
private
Per-path matching for
minimum_coverage_by_fileoverrides. -
.percent_for(stats_source, criterion)
private
Look up a criterion's percent on any coverage_statistics-bearing object (Result,
SourceFile,FileList). - .round(percent) private
Class Method Details
.compute_drop(criterion, result, last_run) (private)
[ GitHub ]# File 'lib/simplecov/coverage_violations.rb', line 133
def compute_drop(criterion, result, last_run) stats_key = SimpleCov.coverage_statistics_key(criterion) last_coverage_percent = last_run.dig(:result, stats_key) last_coverage_percent ||= last_run.dig(:result, :covered_percent) if stats_key == :line return unless last_coverage_percent current = percent_for(result, criterion) or return (last_coverage_percent - current).floor(10) end
.effective_per_file_thresholds(file, defaults, overrides) (private)
Walk the overrides in declaration order, merging each one that matches the file's project path into the running effective threshold (so the most-specific or latest-declared override wins per criterion). Returns the defaults Hash unchanged when nothing matches.
# File 'lib/simplecov/coverage_violations.rb', line 83
def effective_per_file_thresholds(file, defaults, overrides) return defaults if overrides.empty? path = file.project_filename overrides.reduce(defaults) do |acc, (pattern, criterion_thresholds)| path_matches?(path, pattern) ? acc.merge(criterion_thresholds) : acc end end
.file_minimum_violation(file, criterion, expected) (private)
[ GitHub ]# File 'lib/simplecov/coverage_violations.rb', line 104
def file_minimum_violation(file, criterion, expected) actual = percent_for(file, criterion) or return return unless actual < expected { criterion: criterion, expected: expected, actual: actual, filename: file.filename, project_filename: file.project_filename } end
.group_minimum_violations(group_name, group, minimums) (private)
[ GitHub ]# File 'lib/simplecov/coverage_violations.rb', line 117
def group_minimum_violations(group_name, group, minimums) minimums.filter_map do |criterion, expected| actual = percent_for(group, criterion) or next {group_name: group_name, criterion: criterion, expected: expected, actual: actual} if actual < expected end end
.lookup_group(result, group_name) (private)
[ GitHub ]
.maximum_drop(result, thresholds, last_run: SimpleCov::LastRun.read) ⇒ Array<Hash>
# File 'lib/simplecov/coverage_violations.rb', line 55
def maximum_drop(result, thresholds, last_run: SimpleCov::LastRun.read) return [] unless last_run thresholds.filter_map do |criterion, maximum| actual = compute_drop(criterion, result, last_run) {criterion: criterion, maximum: maximum, actual: actual} if actual && actual > maximum end end
.maximum_overall(result, thresholds) ⇒ Array<Hash>
Tolerance: .percent_for floors the actual percent to two decimal
places (matching the existing minimum-coverage behavior), so an
actual of e.g. 95.4287 is treated as 95.42 — meaning a maximum of
95.42 still passes. See issue #187 for the rationale.
# File 'lib/simplecov/coverage_violations.rb', line 25
def maximum_overall(result, thresholds) thresholds.filter_map do |criterion, expected| actual = percent_for(result, criterion) or next {criterion: criterion, expected: expected, actual: actual} if actual > expected end end
.minimum_by_file(result, defaults, overrides = {}) ⇒ Array<Hash>
defaults is the criterion-keyed Hash applied to every file.
overrides is an ordered Hash<pattern, criterion_thresholds> of per-path
overrides; for each file, defaults are merged with every matching override
(later wins per criterion, overrides win over defaults).
# File 'lib/simplecov/coverage_violations.rb', line 38
def minimum_by_file(result, defaults, overrides = {}) result.files.flat_map do |file| effective = effective_per_file_thresholds(file, defaults, overrides) effective.filter_map { |criterion, expected| file_minimum_violation(file, criterion, expected) } end end
.minimum_by_group(result, thresholds) ⇒ Array<Hash>
# File 'lib/simplecov/coverage_violations.rb', line 46
def minimum_by_group(result, thresholds) thresholds.flat_map do |group_name, minimums| group = lookup_group(result, group_name) group ? group_minimum_violations(group_name, group, minimums) : [] end end
.minimum_overall(result, thresholds) ⇒ Array<Hash>
# File 'lib/simplecov/coverage_violations.rb', line 13
def minimum_overall(result, thresholds) thresholds.filter_map do |criterion, expected| actual = percent_for(result, criterion) or next {criterion: criterion, expected: expected, actual: actual} if actual < expected end end
.path_matches?(project_filename, pattern) ⇒ Boolean (private)
Per-path matching for minimum_coverage_by_file overrides. Strings
ending in / are treated as directory prefixes; otherwise they must
match project_filename exactly. Regexps are tested via match?.
The configuration setter rejects anything other than String/Regexp,
so no dead else branch is needed here.
# File 'lib/simplecov/coverage_violations.rb', line 97
def path_matches?(project_filename, pattern) return project_filename.match?(pattern) if pattern.is_a?(Regexp) return project_filename.start_with?(pattern) if pattern.end_with?("/") project_filename == pattern end
.percent_for(stats_source, criterion) (private)
Look up a criterion's percent on any coverage_statistics-bearing
object (Result, SourceFile, FileList). Returns nil — and the
caller silently skips — when the criterion was configured but not
actually measured by the runtime (e.g. minimum_coverage branch: 100 under the "strict" profile on JRuby, where the Coverage
module doesn't emit branch data). The config-time
raise_if_criterion_disabled check still catches the genuine
"forgot to enable the criterion" mistake before we ever get here.
# File 'lib/simplecov/coverage_violations.rb', line 74
def percent_for(stats_source, criterion) stats = stats_source.coverage_statistics[SimpleCov.coverage_statistics_key(criterion)] round(stats.percent) if stats end
.round(percent) (private)
[ GitHub ]# File 'lib/simplecov/coverage_violations.rb', line 143
def round(percent) SimpleCov.round_coverage(percent) end