Module: SimpleCov::CLI::Patch::Output
Constant Summary collapse
- OPTIONAL_CRITERIA =
i[branch method].freeze
Instance Method Summary collapse
-
#criterion_cell(label, stats, color) ⇒ Object
A "lines" cell always, "branches" and "methods" cells only when the touched lines actually carried one: a hollow 0/0 cell is noise.
- #criterion_cells(row, color) ⇒ Object
- #emit(stdout, rows, opts) ⇒ Object
- #emit_text(stdout, rows, color) ⇒ Object
- #format_row(row, color) ⇒ Object
- #format_total(rows, color) ⇒ Object
-
#gate(rows, minimum) ⇒ Object
No --minimum is report-only.
- #json_rows(rows) ⇒ Object
-
#measured?(stats) ⇒ Boolean
Branch and method stats are nil when the report never measured them, and 0/0 when the change touched none.
- #missing_note(row) ⇒ Object
-
#only(run) ⇒ Object
mutant:disable.
- #pct(stats) ⇒ Object
-
#ranges(numbers, separator = ", ") ⇒ Object
Collapses a sorted line list into ranges: [41, 42, 43, 47] -> "41-43, 47".
-
#short?(stats, minimum) ⇒ Boolean
Cross-multiplied rather than compared as percentages so neither rounding nor float division can shift the verdict at the boundary: reading the displayed
pctwould round a 19_999/20_000 patch up to 100 and pass it against--minimum 100, while dividing makes 23/40 compute as 57.4999... - #span(run) ⇒ Object
- #sum_stats(rows, criterion) ⇒ Object
Instance Method Details
#criterion_cell(label, stats, color) ⇒ Object
A "lines" cell always, "branches" and "methods" cells only when the touched lines actually carried one: a hollow 0/0 cell is noise.
44 45 46 47 48 |
# File 'lib/simplecov/cli/patch/output.rb', line 44 def criterion_cell(label, stats, color) percent = pct(stats) cell = Color.colorize(format("%6.2f%%", percent), (percent >= 100) ? :green : :red, enabled: color) "#{cell} (#{stats.fetch(:covered)}/#{stats.fetch(:relevant)}) #{label}" end |
#criterion_cells(row, color) ⇒ Object
35 36 37 38 39 40 |
# File 'lib/simplecov/cli/patch/output.rb', line 35 def criterion_cells(row, color) cells = [criterion_cell("lines", row.fetch(:line), color)] cells << criterion_cell("branches", row.fetch(:branch), color) if measured?(row.fetch(:branch)) cells << criterion_cell("methods", row.fetch(:method), color) if measured?(row.fetch(:method)) cells end |
#emit(stdout, rows, opts) ⇒ Object
13 14 15 16 17 18 19 |
# File 'lib/simplecov/cli/patch/output.rb', line 13 def emit(stdout, rows, opts) if opts.fetch(:json) stdout.puts(JSON.pretty_generate(json_rows(rows))) else emit_text(stdout, rows, CLI.color_enabled?(opts, stdout)) end end |
#emit_text(stdout, rows, color) ⇒ Object
21 22 23 24 25 26 27 |
# File 'lib/simplecov/cli/patch/output.rb', line 21 def emit_text(stdout, rows, color) return stdout.puts("simplecov patch: no coverable lines changed") if rows.empty? rows.sort_by! { |row| [pct(row.fetch(:line)), row.fetch(:file)] } rows.each { |row| stdout.puts(format_row(row, color)) } stdout.puts(format_total(rows, color)) end |
#format_row(row, color) ⇒ Object
29 30 31 32 33 |
# File 'lib/simplecov/cli/patch/output.rb', line 29 def format_row(row, color) line = " #{criterion_cells(row, color).join(" ")} #{row.fetch(:file)}" note = missing_note(row) note.empty? ? line : "#{line} #{note}" end |
#format_total(rows, color) ⇒ Object
60 61 62 63 |
# File 'lib/simplecov/cli/patch/output.rb', line 60 def format_total(rows, color) totals = {line: sum_stats(rows, :line), branch: sum_stats(rows, :branch), method: sum_stats(rows, :method)} " Patch coverage: #{criterion_cells(totals, color).join(", ")}" end |
#gate(rows, minimum) ⇒ Object
No --minimum is report-only. With one, every measured criterion must clear
the floor, and short? never fails an unmeasured one.
115 116 117 118 119 120 |
# File 'lib/simplecov/cli/patch/output.rb', line 115 def gate(rows, minimum) return 0 unless minimum below = i[line branch method].any? { |criterion| short?(sum_stats(rows, criterion), minimum) } below ? 1 : 0 end |
#json_rows(rows) ⇒ Object
65 66 67 68 69 70 71 72 73 74 |
# File 'lib/simplecov/cli/patch/output.rb', line 65 def json_rows(rows) rows.map do |row| data = {file: row.fetch(:file), line: row.fetch(:line).merge(percent: pct(row.fetch(:line)))} OPTIONAL_CRITERIA.each do |criterion| stats = row.fetch(criterion) data[criterion] = stats.merge(percent: pct(stats)) if measured?(stats) end data end end |
#measured?(stats) ⇒ Boolean
Branch and method stats are nil when the report never measured them, and 0/0 when the change touched none.
78 79 80 |
# File 'lib/simplecov/cli/patch/output.rb', line 78 def measured?(stats) stats.is_a?(Hash) && stats.fetch(:relevant).positive? end |
#missing_note(row) ⇒ Object
50 51 52 53 54 55 56 57 58 |
# File 'lib/simplecov/cli/patch/output.rb', line 50 def missing_note(row) parts = [] #: Array[String] parts << "missing #{ranges(row.fetch(:line).fetch(:missing))}" if row.fetch(:line).fetch(:missing).any? OPTIONAL_CRITERIA.each do |criterion| stats = row.fetch(criterion) parts << "#{criterion} #{ranges(stats.fetch(:missing))}" if measured?(stats) && stats.fetch(:missing).any? end parts.join(" ") end |
#only(run) ⇒ Object
mutant:disable
93 94 95 |
# File 'lib/simplecov/cli/patch/output.rb', line 93 def only(run) format("%<only>d", only: run.first) end |
#pct(stats) ⇒ Object
106 107 108 109 110 111 |
# File 'lib/simplecov/cli/patch/output.rb', line 106 def pct(stats) relevant = stats.fetch(:relevant) return 100.0 if relevant.zero? (stats.fetch(:covered).to_f / relevant * 100).round(2) end |
#ranges(numbers, separator = ", ") ⇒ Object
Collapses a sorted line list into ranges: [41, 42, 43, 47] -> "41-43, 47".
The show subcommand borrows this with a bare-comma separator for its
greppable path:40,52-58,71 form.
85 86 87 88 89 90 |
# File 'lib/simplecov/cli/patch/output.rb', line 85 def ranges(numbers, separator = ", ") numbers.slice_when { |prev, curr| curr > prev + 1 }.each_with_object(+"") do |run, out| out << separator unless out.empty? out << (run.one? ? only(run) : span(run)) end end |
#short?(stats, minimum) ⇒ Boolean
Cross-multiplied rather than compared as percentages so neither rounding
nor float division can shift the verdict at the boundary: reading the
displayed pct would round a 19_999/20_000 patch up to 100 and pass it
against --minimum 100, while dividing makes 23/40 compute as 57.4999...
and fail an exactly-57.5% patch. The minimum becomes an exact Rational
from its own text, and covered * 100 is an integer, so the comparison is
exact. A criterion with nothing relevant never falls short.
129 130 131 |
# File 'lib/simplecov/cli/patch/output.rb', line 129 def short?(stats, minimum) stats.fetch(:covered) * 100 < Rational(minimum.to_s) * stats.fetch(:relevant) end |
#span(run) ⇒ Object
97 98 99 |
# File 'lib/simplecov/cli/patch/output.rb', line 97 def span(run) format("%<from>d-%<to>d", from: run.first, to: run.last) end |
#sum_stats(rows, criterion) ⇒ Object
101 102 103 104 |
# File 'lib/simplecov/cli/patch/output.rb', line 101 def sum_stats(rows, criterion) stats = rows.filter_map { |row| row.fetch(criterion) } {covered: stats.sum { |stat| stat.fetch(:covered) }, relevant: stats.sum { |stat| stat.fetch(:relevant) }} end |