43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
|
# File 'lib/rail_verdict/analyzers/simplecov.rb', line 43
def run(repository_root, runner: nil, timeout_seconds: 10.0, probe_result: nil, configuration: nil)
config = load_config(repository_root, configuration: configuration)
coverage_path = config.fetch("coverage_path") { DEFAULT_COVERAGE_PATH }
freshness_window = config.fetch("freshness_window_seconds") { DEFAULT_FRESHNESS_WINDOW_SECONDS }
invocation = { "executable" => "simplecov", "argv" => ["read", coverage_path] }
full_path = File.expand_path(coverage_path, repository_root)
unless File.file?(full_path)
return [Shared.failure_result(analyzer_id: ANALYZER_ID, invocation: invocation, status: "unavailable", message: "coverage file is absent: #{coverage_path}"), []]
end
begin
bytes = File.binread(full_path)
rescue SystemCallError => error
return [Shared.failure_result(analyzer_id: ANALYZER_ID, invocation: invocation, status: "unavailable", message: Shared.bounded_message(error.message)), []]
end
if bytes.bytesize > 8 * 1024 * 1024
return [Shared.failure_result(analyzer_id: ANALYZER_ID, invocation: invocation, status: "truncated", message: "coverage file exceeds 8 MiB"), []]
end
text = bytes.dup.force_encoding(Encoding::UTF_8)
unless text.valid_encoding?
return [Shared.failure_result(analyzer_id: ANALYZER_ID, invocation: invocation, status: "parse_failed", message: "coverage file is not valid UTF-8"), []]
end
begin
document = JSON.parse(text)
rescue JSON::ParserError => error
return [Shared.failure_result(analyzer_id: ANALYZER_ID, invocation: invocation, status: "parse_failed", message: Shared.bounded_message(error.message)), []]
end
version = document["version"].to_s
probe_result ||= probe(repository_root, timeout_seconds: timeout_seconds)
unless version.start_with?("1")
return [Shared.failure_result(analyzer_id: ANALYZER_ID, invocation: invocation, status: "unsupported", message: "unsupported SimpleCov version #{version}", tool_version: probe_result.version || version), []]
end
errors = validate_coverage_schema(document)
unless errors.empty?
return [Shared.failure_result(analyzer_id: ANALYZER_ID, invocation: invocation, status: "malformed", message: errors.first, tool_version: probe_result.version || version), []]
end
mtime = File.mtime(full_path)
stale = (Time.now - mtime) > freshness_window
covered, executable = coverage_totals(document)
summary = {
"covered_lines" => covered,
"executable_lines" => executable,
"percent" => executable == 0 ? 100.0 : ((covered.to_f / executable) * 100).round(2),
"stale" => stale,
"freshness_window_seconds" => Integer(freshness_window),
"coverage_path" => coverage_path.to_s[0, 512],
"files" => document["files"]
}
result = AnalyzerResult.new(
analyzer: ANALYZER_ID,
tool_version: version,
invocation: invocation,
execution_status: "succeeded",
finding_ids: [],
evidence_summary: summary
)
[result, []]
rescue StandardError => error
[Shared.failure_result(analyzer_id: ANALYZER_ID, invocation: { "executable" => "simplecov", "argv" => ["read", DEFAULT_COVERAGE_PATH] }, status: "malformed", message: Shared.bounded_message(error.message)), []]
end
|