Skip to content

Commit 92d8f9b

Browse files
committed
Dogfood: run SimpleCov against its own spec suite
Bootstrap Coverage in spec/helper.rb before requiring simplecov, then drive the formatter ourselves in an after(:suite) hook. Threshold defaults to 100% and the build exits MINIMUM_COVERAGE when under; SIMPLECOV_NO_DOGFOOD=1 skips the bootstrap. Two follow-ups it surfaced: HTMLFormatter#format and JSONFormatter#format now mkdir_p their output_dir, and simplecov_spec stubs Coverage.running? to false in the start_coverage_measurement examples (Coverage is already running under the dogfood bootstrap).
1 parent 53b8a73 commit 92d8f9b

4 files changed

Lines changed: 56 additions & 0 deletions

File tree

lib/simplecov/formatter/html_formatter.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ def initialize(silent: false, output_dir: nil)
2525
def format(result)
2626
json = JSON.pretty_generate(JSONFormatter.build_hash(result))
2727

28+
FileUtils.mkdir_p(output_path)
2829
File.write(File.join(output_path, JSONFormatter::FILENAME), json)
2930
File.write(File.join(output_path, DATA_FILENAME), "window.SIMPLECOV_DATA = #{json};\n", mode: "wb")
3031

lib/simplecov/formatter/json_formatter.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# frozen_string_literal: true
22

33
require_relative "json_formatter/result_hash_formatter"
4+
require "fileutils"
45
require "json"
56
require "time"
67

@@ -26,6 +27,7 @@ def self.build_hash(result)
2627
end
2728

2829
def format(result)
30+
FileUtils.mkdir_p(output_path)
2931
path = File.join(output_path, FILENAME)
3032
warn_if_concurrent_overwrite(path)
3133
File.write(path, JSON.pretty_generate(self.class.build_hash(result)))

spec/helper.rb

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
# frozen_string_literal: true
22

3+
# Dogfood: start Ruby's Coverage module *before* requiring simplecov so
4+
# simplecov's own lib/ files get tracked. Set SIMPLECOV_NO_DOGFOOD=1 to
5+
# skip — useful when running individual specs interactively or when a
6+
# dependency's behaviour around Coverage is being investigated.
7+
unless ENV["SIMPLECOV_NO_DOGFOOD"]
8+
require "coverage"
9+
Coverage.start(lines: true)
10+
end
11+
312
require "rspec"
413
require "stringio"
514
require "open3"
@@ -10,6 +19,40 @@
1019

1120
SimpleCov.coverage_dir("tmp/coverage")
1221

22+
unless ENV["SIMPLECOV_NO_DOGFOOD"]
23+
# `start_tracking` (not `start`) handles bookkeeping (pid,
24+
# process_start_time, fork hook) without auto-installing the at_exit
25+
# formatter — the after(:suite) hook below drives the report
26+
# ourselves at a controlled point. Filters are passed explicitly to
27+
# the dogfood `Result.new`, not into `SimpleCov.filters`, so they
28+
# don't leak into synthetic Results that unit tests build from
29+
# `spec/fixtures/*` paths.
30+
SimpleCov.start_tracking
31+
32+
DOGFOOD_OUTPUT_DIR = "tmp/dogfood"
33+
DOGFOOD_MINIMUM_COVERAGE = 100.0
34+
35+
RSpec.configure do |config|
36+
config.after(:suite) do
37+
extra_filters = %w[/spec/ /features/ /test_projects/ /tmp/].map { |path| SimpleCov::StringFilter.new(path) }
38+
raw = SimpleCov::UselessResultsRemover.call(Coverage.result)
39+
adapted = SimpleCov::ResultAdapter.call(raw)
40+
result = SimpleCov::Result.new(adapted, filters: SimpleCov.filters + extra_filters, groups: {})
41+
42+
SimpleCov::Formatter::HTMLFormatter.new(silent: true, output_dir: DOGFOOD_OUTPUT_DIR).format(result)
43+
percent = result.covered_percent
44+
next if percent >= DOGFOOD_MINIMUM_COVERAGE
45+
46+
$stdout.puts format(
47+
"Dogfood line coverage (%<actual>.2f%%) is below threshold (%<expected>.2f%%). " \
48+
"See %<dir>s/index.html",
49+
actual: percent, expected: DOGFOOD_MINIMUM_COVERAGE, dir: DOGFOOD_OUTPUT_DIR
50+
)
51+
Kernel.exit(SimpleCov::ExitCodes::MINIMUM_COVERAGE)
52+
end
53+
end
54+
end
55+
1356
def source_fixture(filename)
1457
File.join(source_fixture_base_directory, "fixtures", filename)
1558
end

spec/simplecov_spec.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,16 @@ def expect_merged
372372
described_class.clear_coverage_criteria
373373
end
374374

375+
before do
376+
# `start_coverage_with_criteria` short-circuits with `unless
377+
# Coverage.running?`. These tests verify the kwargs handed to
378+
# Coverage.start, which only fire when Coverage isn't already
379+
# running — so stub the running check to false. (Without this,
380+
# the test would fail when run with the dogfood bootstrap, which
381+
# starts Coverage before requiring simplecov.)
382+
allow(Coverage).to receive(:running?).and_return(false)
383+
end
384+
375385
it "starts coverage in lines mode by default" do
376386
allow(Coverage).to receive(:start)
377387

0 commit comments

Comments
 (0)