Skip to content

Commit 4d7f8cf

Browse files
Merge SimpleCov sessions across rake spec:ci buckets
rake spec:ci runs ~9 sequential rspec processes via spec_organizer. With SimpleCov.command_name set to a static 'rspec' in every process, each bucket overwrote the previous one's session in coverage/.resultset.json and only the last process's coverage survived. On Evergreen the last bucket is connection-pool-stress, all of whose examples are pending, so the resultset ended up with 0% coverage on every tracked file. Make command_name unique per PID so each bucket stores a separate session, and merge line hits across all sessions in CoverageGate.
1 parent f612aac commit 4d7f8cf

3 files changed

Lines changed: 44 additions & 5 deletions

File tree

.evergreen/lib/coverage_gate.rb

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,17 +68,30 @@ def load_current
6868
end
6969

7070
def parse_resultset(data)
71-
_, run = data.first
72-
coverage = run.fetch('coverage')
73-
coverage.each_with_object({}) do |(abs_path, file_data), out|
71+
merged = data.each_value.with_object({}) do |run, acc|
72+
run.fetch('coverage').each do |abs_path, file_data|
73+
lines = file_data.is_a?(Hash) ? file_data['lines'] : file_data
74+
existing = acc[abs_path]
75+
acc[abs_path] = existing ? merge_lines(existing, lines) : lines.dup
76+
end
77+
end
78+
79+
merged.each_with_object({}) do |(abs_path, lines), out|
7480
rel = relative_path(abs_path)
7581
next unless rel
7682

77-
lines = file_data.is_a?(Hash) ? file_data['lines'] : file_data
7883
out[rel] = count_lines(lines)
7984
end
8085
end
8186

87+
def merge_lines(a, b)
88+
a.zip(b).map do |x, y|
89+
next nil if x.nil? && y.nil?
90+
91+
(x.is_a?(Integer) ? x : 0) + (y.is_a?(Integer) ? y : 0)
92+
end
93+
end
94+
8295
def load_baseline
8396
return { 'files' => {} } unless File.exist?(@baseline_path)
8497

spec/lite_spec_helper.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
require 'simplecov'
55
require 'simplecov-json'
66

7-
SimpleCov.command_name 'rspec'
7+
# Unique per process so concurrent buckets in rake spec:ci do not overwrite
8+
# each other's session in coverage/.resultset.json.
9+
SimpleCov.command_name "rspec-#{Process.pid}"
810
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new(
911
[
1012
SimpleCov::Formatter::HTMLFormatter,

spec/mongo/coverage_gate_spec.rb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,15 @@ def write_resultset(files)
3636
File.write(resultset_path, JSON.dump('rspec' => { 'coverage' => coverage }))
3737
end
3838

39+
def write_multi_session_resultset(sessions)
40+
payload = sessions.each_with_index.with_object({}) do |(files, idx), out|
41+
coverage = files.transform_keys { |rel| File.join(project_root, rel) }
42+
.transform_values { |lines| { 'lines' => lines } }
43+
out["rspec-#{idx}"] = { 'coverage' => coverage }
44+
end
45+
File.write(resultset_path, JSON.dump(payload))
46+
end
47+
3948
def write_baseline(files)
4049
File.write(baseline_path, JSON.pretty_generate('files' => files))
4150
end
@@ -112,6 +121,21 @@ def write_baseline(files)
112121
expect(output.string).to include('missing')
113122
end
114123
end
124+
125+
context 'when the resultset has multiple sessions (parallel buckets)' do
126+
it 'merges line hits across sessions before comparing' do
127+
write_multi_session_resultset(
128+
[
129+
# bucket A covers lines 1 and 2
130+
{ 'lib/mongo/foo.rb' => [ nil, 1, 1, 0, nil ] },
131+
# bucket B covers line 3 (but not 1 or 2)
132+
{ 'lib/mongo/foo.rb' => [ nil, 0, 0, 1, nil ] },
133+
]
134+
)
135+
write_baseline('lib/mongo/foo.rb' => { 'covered' => 3, 'total' => 3 })
136+
expect(gate.check).to eq(0)
137+
end
138+
end
115139
end
116140

117141
describe '#update_baseline' do

0 commit comments

Comments
 (0)