Skip to content

Commit 567e8d5

Browse files
skryukovsferik
authored andcommitted
Fix crash in oneshot coverage when file is missing or not valid Ruby
1 parent fd8a330 commit 567e8d5

File tree

2 files changed

+95
-9
lines changed

2 files changed

+95
-9
lines changed

lib/simplecov/result_adapter.rb

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,18 @@ def adapt
3131
private
3232

3333
def adapt_oneshot_lines_if_needed(file_name, cover_statistic)
34-
if cover_statistic.key?(:oneshot_lines)
35-
line_stub = Coverage.line_stub(file_name)
36-
oneshot_lines = cover_statistic.delete(:oneshot_lines)
37-
oneshot_lines.each do |covered_line|
38-
line_stub[covered_line - 1] = 1
39-
end
40-
cover_statistic[:lines] = line_stub
41-
else
42-
cover_statistic
34+
return unless cover_statistic.key?(:oneshot_lines)
35+
36+
oneshot_lines = cover_statistic.delete(:oneshot_lines)
37+
line_stub = begin
38+
Coverage.line_stub(file_name)
39+
rescue Errno::ENOENT, SyntaxError
40+
Array.new(oneshot_lines.max || 0, nil)
41+
end
42+
oneshot_lines.each do |covered_line|
43+
line_stub[covered_line - 1] = 1
4344
end
45+
cover_statistic[:lines] = line_stub
4446
end
4547
end
4648
end

spec/result_adapter_spec.rb

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# frozen_string_literal: true
2+
3+
require "helper"
4+
require "coverage"
5+
6+
describe SimpleCov::ResultAdapter do
7+
subject { described_class.call(result_set) }
8+
9+
let(:existing_file) { source_fixture("app/models/user.rb") }
10+
11+
describe "with oneshot_lines coverage" do
12+
before do
13+
skip "oneshot_lines coverage not supported" if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.6") || RUBY_ENGINE == "truffleruby"
14+
end
15+
16+
context "when all tracked files exist" do
17+
let(:result_set) do
18+
{
19+
existing_file => {oneshot_lines: [2, 3, 4]}
20+
}
21+
end
22+
23+
it "adapts the coverage data to lines format" do
24+
lines = subject[existing_file][:lines]
25+
expect(lines).to be_an(Array)
26+
expect(lines[1]).to eq(1)
27+
expect(lines[2]).to eq(1)
28+
expect(lines[3]).to eq(1)
29+
end
30+
end
31+
32+
context "when a tracked file no longer exists on disk" do
33+
let(:deleted_file) { File.join(SimpleCov.root, "lib/deleted_generated_file.rb") }
34+
35+
let(:result_set) do
36+
{
37+
existing_file => {oneshot_lines: [2, 3]},
38+
deleted_file => {oneshot_lines: [1, 3]}
39+
}
40+
end
41+
42+
it "builds a fallback line stub for the missing file" do
43+
lines = subject[deleted_file][:lines]
44+
expect(lines[0]).to eq(1)
45+
expect(lines[2]).to eq(1)
46+
end
47+
48+
it "still adapts the existing file normally" do
49+
lines = subject[existing_file][:lines]
50+
expect(lines[1]).to eq(1)
51+
expect(lines[2]).to eq(1)
52+
end
53+
end
54+
55+
context "when a tracked file is not valid Ruby" do
56+
let(:non_ruby_file) { source_fixture("non_ruby_config.yml") }
57+
58+
before do
59+
File.write(non_ruby_file, "development: &default\n adapter: mysql2\n")
60+
end
61+
62+
after do
63+
FileUtils.rm_f(non_ruby_file)
64+
end
65+
66+
let(:result_set) do
67+
{
68+
existing_file => {oneshot_lines: [2]},
69+
non_ruby_file => {oneshot_lines: [1]}
70+
}
71+
end
72+
73+
it "builds a fallback line stub for the non-parseable file" do
74+
lines = subject[non_ruby_file][:lines]
75+
expect(lines[0]).to eq(1)
76+
end
77+
78+
it "still adapts the existing file normally" do
79+
lines = subject[existing_file][:lines]
80+
expect(lines[1]).to eq(1)
81+
end
82+
end
83+
end
84+
end

0 commit comments

Comments
 (0)