Module: SimpleCov::CLI::Show
- Extended by:
- CommandHelpers, Show
- Included in:
- Show
- Defined in:
- lib/simplecov/cli/show.rb,
lib/simplecov/cli/show/sweep.rb,
lib/simplecov/cli/show/annotator.rb
Overview
simplecov show <path>: annotated source in the terminal, the way
go tool cover and llvm-cov show print it. --uncovered-only collapses
the answer to path:ranges, a form that greps, fits in a commit message,
and hands a coding agent exactly the lines whose tests are missing.
Defined Under Namespace
Modules: Annotator, Sweep
Constant Summary
CommandHelpers::STATS_ROW_FORMAT
Instance Method Summary
collapse
-
#emit_json(entry, opts, stdout) ⇒ Object
-
#locate_or_report(coverage, opts, stderr) ⇒ Object
A line-less report has no per-line hits to put in a gutter, so it is reported like an unresolvable path is.
-
#options(parser, opts) ⇒ Object
-
#parse(args) ⇒ Object
-
#relevant_lines(entry) ⇒ Object
-
#render(located, opts, stdout, stderr) ⇒ Object
-
#run(args, stdout:, stderr:) ⇒ Object
-
#run_project(opts, stdout, stderr) ⇒ Object
No path sweeps the whole project, in the compact forms only.
-
#show_uncovered(entry, opts, stdout, stderr) ⇒ Object
-
#source_for(filename, entry, opts, stderr) ⇒ Object
The report's own source when it carries one; otherwise the file on disk, accepted only while its line count still matches the report's, since annotating drifted source would put hit counts on the wrong lines.
build_parser, command_name, common_options, error, error_nil, on_help, one?, parse_common, quiet_option, recorded_contexts, stats_row
Instance Method Details
#emit_json(entry, opts, stdout) ⇒ Object
90
91
92
93
94
95
96
|
# File 'lib/simplecov/cli/show.rb', line 90
def emit_json(entry, opts, stdout)
stdout.puts(JSON.pretty_generate(
path: opts.fetch(:path), missed: Annotator.missed_lines(entry),
lines: relevant_lines(entry), markers: Annotator.markers_for(entry)
))
0
end
|
#locate_or_report(coverage, opts, stderr) ⇒ Object
A line-less report has no per-line hits to put in a gutter, so it is
reported like an unresolvable path is.
64
65
66
67
68
69
70
71
72
73
74
75
76
|
# File 'lib/simplecov/cli/show.rb', line 64
def locate_or_report(coverage, opts, stderr)
path, input = opts.fetch_values(:path, :input)
match = CoverageFile.lookup(coverage, path)
return error_nil(stderr, CoverageFile.not_found_message(coverage, path, input)) unless match
entry = match.last
unless entry.instance_of?(Hash)
return CoverageFile.report_invalid(stderr, "show", input, "entry for #{path} must be an object")
end
return match if entry["lines"].instance_of?(Array)
error_nil(stderr, "no line coverage for #{path} in #{input}")
end
|
#options(parser, opts) ⇒ Object
55
56
57
58
59
60
|
# File 'lib/simplecov/cli/show.rb', line 55
def options(parser, opts)
parser.on("--input PATH") { |v| opts[:input] = v }
parser.on("--no-color") { opts[:no_color] = true }
parser.on("--uncovered-only") { opts[:uncovered_only] = true }
parser.on("--json") { opts[:json] = true }
end
|
#parse(args) ⇒ Object
47
48
49
50
51
52
53
|
# File 'lib/simplecov/cli/show.rb', line 47
def parse(args)
opts = {input: CLI.default_input, no_color: false,
uncovered_only: false, json: false}
rest = build_parser { |parser| options(parser, opts) }.parse(args)
opts[:path] = rest.first
opts
end
|
#relevant_lines(entry) ⇒ Object
98
99
100
101
102
|
# File 'lib/simplecov/cli/show.rb', line 98
def relevant_lines(entry)
entry.fetch("lines").each_with_index.filter_map do |hit, index|
{number: index + 1, hits: hit} if hit.instance_of?(Integer)
end
end
|
#render(located, opts, stdout, stderr) ⇒ Object
78
79
80
81
82
83
84
85
86
87
88
|
# File 'lib/simplecov/cli/show.rb', line 78
def render(located, opts, stdout, stderr)
filename, entry = located
return emit_json(entry, opts, stdout) if opts.fetch(:json)
return show_uncovered(entry, opts, stdout, stderr) if opts.fetch(:uncovered_only)
source = source_for(filename, entry, opts, stderr)
return 1 unless source
Annotator.call(source, entry, stdout, color: CLI.color_enabled?(opts, stdout))
0
end
|
#run(args, stdout:, stderr:) ⇒ Object
21
22
23
24
25
26
27
28
29
30
31
32
|
# File 'lib/simplecov/cli/show.rb', line 21
def run(args, stdout:, stderr:)
opts = parse(args)
return run_project(opts, stdout, stderr) unless opts.fetch(:path)
coverage = CoverageFile.load_coverage(opts.fetch(:input), command: "show", stderr: stderr)
return 1 unless coverage
located = locate_or_report(coverage, opts, stderr)
return 1 unless located
render(located, opts, stdout, stderr)
end
|
#run_project(opts, stdout, stderr) ⇒ Object
No path sweeps the whole project, in the compact forms only. The annotated
text form still wants one file.
36
37
38
39
40
41
42
43
44
45
|
# File 'lib/simplecov/cli/show.rb', line 36
def run_project(opts, stdout, stderr)
unless opts.fetch(:uncovered_only) || opts.fetch(:json)
return error(stderr, "missing path (annotating needs one file; --uncovered-only sweeps the whole project)")
end
coverage = CoverageFile.load_coverage(opts.fetch(:input), command: "show", stderr: stderr)
return 1 unless coverage
Sweep.emit(Sweep.misses(coverage), opts, stdout, stderr)
end
|
#show_uncovered(entry, opts, stdout, stderr) ⇒ Object
104
105
106
107
108
109
110
111
112
|
# File 'lib/simplecov/cli/show.rb', line 104
def show_uncovered(entry, opts, stdout, stderr)
missed = Annotator.missed_lines(entry)
if missed.empty?
stderr.puts("simplecov show: nothing uncovered in #{opts.fetch(:path)}")
else
stdout.puts("#{opts.fetch(:path)}:#{Patch::Output.ranges(missed, ",")}")
end
0
end
|
#source_for(filename, entry, opts, stderr) ⇒ Object
The report's own source when it carries one; otherwise the file on disk,
accepted only while its line count still matches the report's, since
annotating drifted source would put hit counts on the wrong lines.
117
118
119
120
121
122
123
124
125
126
127
128
129
130
|
# File 'lib/simplecov/cli/show.rb', line 117
def source_for(filename, entry, opts, stderr)
embedded = entry["source"]
return embedded if embedded.instance_of?(Array) && embedded.all?(String)
unless File.file?(filename)
return error_nil(stderr, "no source for #{opts.fetch(:path)} in #{opts.fetch(:input)} and no file " \
"at #{filename}, regenerate the report with `source_in_json true`")
end
lines = File.readlines(filename, chomp: true)
return lines if lines.size.eql?(entry.fetch("lines").size)
error_nil(stderr, "#{filename} has changed since the report (#{lines.size} lines now, " \
"#{entry.fetch("lines").size} recorded), regenerate the report")
end
|