Module: SimpleCov::CLI::Uncovered

Extended by:
CommandHelpers, Uncovered
Included in:
Uncovered
Defined in:
lib/simplecov/cli/uncovered.rb,
lib/simplecov/cli/uncovered/misses.rb

Overview

simplecov uncovered: the lowest-coverage files by the chosen criterion, ascending, so a developer can answer "where should I add tests next?" without opening a browser.

Defined Under Namespace

Modules: Misses

Constant Summary collapse

DEFAULT_TOP =
10

Constants included from CommandHelpers

CommandHelpers::STATS_ROW_FORMAT

Instance Method Summary collapse

Methods included from CommandHelpers

build_parser, command_name, common_options, error, error_nil, on_help, one?, parse_common, quiet_option, recorded_contexts, stats_row

Instance Method Details

#build_row(fname, pct, payload, opts, keys) ⇒ Object



114
115
116
117
118
# File 'lib/simplecov/cli/uncovered.rb', line 114

def build_row(fname, pct, payload, opts, keys)
  row = [fname, pct, payload[keys.fetch(:covered)].to_i, payload.fetch(keys.fetch(:total)).to_i]
  row << Misses.missed_for(payload, opts.fetch(:criterion)) if opts.fetch(:missing)
  row
end

#emit(stdout, files, opts) ⇒ Object



48
49
50
51
52
53
54
# File 'lib/simplecov/cli/uncovered.rb', line 48

def emit(stdout, files, opts)
  kind = opts.fetch(:annotate)
  return Misses.annotate(stdout, files, opts.fetch(:criterion), kind) if kind
  return stdout.puts(empty_message(opts.fetch(:json))) if files.empty?

  opts.fetch(:json) ? emit_json(stdout, files) : emit_text(stdout, files, CLI.color_enabled?(opts, stdout))
end

#emit_json(stdout, files) ⇒ Object



87
88
89
90
91
92
93
94
# File 'lib/simplecov/cli/uncovered.rb', line 87

def emit_json(stdout, files)
  rows = files.map do |fname, pct, covered, total, missed|
    row = {"file" => fname, "percent" => pct, "covered" => covered, "total" => total}
    row["missing"] = missed if missed
    row
  end
  stdout.puts(JSON.pretty_generate(rows))
end

#emit_text(stdout, files, color) ⇒ Object



79
80
81
82
83
84
85
# File 'lib/simplecov/cli/uncovered.rb', line 79

def emit_text(stdout, files, color)
  files.each do |fname, pct, covered, total, missed|
    row = format_row(fname, pct, covered, total, color)
    row += "  missing #{Patch::Output.ranges(missed, ",")}" if missed&.any?
    stdout.puts(row)
  end
end

#empty_message(json) ⇒ Object



96
97
98
# File 'lib/simplecov/cli/uncovered.rb', line 96

def empty_message(json)
  json ? "[]" : "simplecov uncovered: nothing to report"
end

#format_row(fname, pct, covered, total, color) ⇒ Object



120
121
122
123
124
# File 'lib/simplecov/cli/uncovered.rb', line 120

def format_row(fname, pct, covered, total, color)
  format("%<pct>s  %<covered>d/%<total>d  %<fname>s",
    pct: Color.colorize_percent(pct, format("%6.2f%%", pct), enabled: color),
    covered: covered, total: total, fname: fname)
end

#own_options(parser, options) ⇒ Object



62
63
64
65
66
67
68
# File 'lib/simplecov/cli/uncovered.rb', line 62

def own_options(parser, options)
  parser.on("--threshold N", Float) { |v| options[:threshold] = v }
  parser.on("--top N", Integer) { |v| options[:top] = validate_top(v) }
  parser.on("--criterion C") { |v| options[:criterion] = v.to_sym }
  parser.on("--missing") { options[:missing] = true }
  parser.on("--annotate KIND") { |v| options[:annotate] = v }
end

#parse(args) ⇒ Object



56
57
58
59
60
# File 'lib/simplecov/cli/uncovered.rb', line 56

def parse(args)
  opts, = parse_common(args, threshold: 100.0, top: DEFAULT_TOP, criterion: :line,
    missing: false, annotate: nil) { |o, options| own_options(o, options) }
  opts
end

#rank(coverage_hash, opts, keys) ⇒ Object



100
101
102
103
# File 'lib/simplecov/cli/uncovered.rb', line 100

def rank(coverage_hash, opts, keys)
  rows = coverage_hash.filter_map { |fname, payload| row_for(fname, payload, opts, keys) }
  rows.sort_by { |_fname, pct, _c, _t| pct }
end

#report(opts, keys, stdout, stderr) ⇒ Object



35
36
37
38
39
40
41
# File 'lib/simplecov/cli/uncovered.rb', line 35

def report(opts, keys, stdout, stderr)
  coverage = CoverageFile.load_coverage(opts.fetch(:input), command: "uncovered", stderr: stderr)
  return 1 unless coverage

  emit(stdout, rank(coverage, opts, keys).first(opts.fetch(:top)), opts)
  0
end

#row_for(fname, payload, opts, keys) ⇒ Object



105
106
107
108
109
110
111
112
# File 'lib/simplecov/cli/uncovered.rb', line 105

def row_for(fname, payload, opts, keys)
  return unless payload.instance_of?(Hash) && payload[keys.fetch(:total)].to_i.positive?

  pct = payload[keys.fetch(:percent)].to_f
  return if pct >= opts.fetch(:threshold)

  build_row(fname, pct, payload, opts, keys)
end

#run(args, stdout:, stderr:) ⇒ Object



23
24
25
26
27
28
29
30
31
32
33
# File 'lib/simplecov/cli/uncovered.rb', line 23

def run(args, stdout:, stderr:, **)
  opts = parse(args)
  issue = Annotations.issue(opts)
  return error(stderr, issue) if issue

  keys = CoverageFile::CRITERIA[opts.fetch(:criterion)]
  return unknown_criterion(opts.fetch(:criterion), stderr) unless keys

  opts[:missing] = true if opts.fetch(:annotate)
  report(opts, keys, stdout, stderr)
end

#unknown_criterion(criterion, stderr) ⇒ Object



43
44
45
46
# File 'lib/simplecov/cli/uncovered.rb', line 43

def unknown_criterion(criterion, stderr)
  stderr.puts("simplecov uncovered: unknown --criterion #{criterion.inspect} (expected line, branch, or method)")
  1
end

#validate_top(value) ⇒ Object

A negative count would raise from Array#first, so it is reported as the parse error it is. --top 0 is allowed and shows nothing. OptionParser prepends the offending option's name, so the raise carries only the reason.

Raises:

  • (OptionParser::InvalidArgument)


73
74
75
76
77
# File 'lib/simplecov/cli/uncovered.rb', line 73

def validate_top(value)
  raise OptionParser::InvalidArgument, "must not be negative" if value.negative?

  value
end