Class: Henitai::CoverageBootstrapper
- Inherits:
-
Object
- Object
- Henitai::CoverageBootstrapper
- Defined in:
- lib/henitai/coverage_bootstrapper.rb,
sig/henitai.rbs
Overview
Ensures coverage data exists before the mutation pipeline starts.
Constant Summary collapse
- DEPENDENCY_MANIFEST_FILE =
Sidecar recording which dependency files existed when the coverage artifacts were produced. Deletions drop a path from the current set but leave every surviving file's mtime untouched, so freshness must compare the recorded path set, not just watch existing files.
"henitai_dependency_manifest.json"
Instance Attribute Summary collapse
-
#static_filter ⇒ StaticFilter
readonly
Returns the value of attribute static_filter.
Instance Method Summary collapse
- #bootstrap_coverage(integration, config, test_files = nil) ⇒ void
-
#configured_test_excludes(config) ⇒ Array[String]?
Specs pass bare config doubles exposing only what the example needs.
- #coverage_available?(source_files, config) ⇒ Boolean
-
#coverage_fresh?(source_files, config, test_files) ⇒ Boolean
Returns true when a coverage report already exists, is newer than every watched source and test file, and the dependency path set is unchanged since the report was produced.
- #coverage_report_path(config) ⇒ String
-
#ensure!(source_files:, config:, integration:, test_files: nil) ⇒ void
Runs the test suite to collect coverage, unless a fresh report already exists.
-
#initialize(static_filter: StaticFilter.new) ⇒ CoverageBootstrapper
constructor
A new instance of CoverageBootstrapper.
-
#record_dependency_manifest(config) ⇒ Object
Writes the current dependency path set next to the coverage artifacts.
-
#resolve_test_files(integration, test_files, config) ⇒ Array[String]
Excluded tests are dropped here rather than in
bootstrap_coverage, so the same filtered list feeds both the suite run and the freshness watch list: a test that no longer contributes to the baseline must not invalidate it either. - #source_file_paths(source_files) ⇒ Array[String]
Constructor Details
#initialize(static_filter: StaticFilter.new) ⇒ CoverageBootstrapper
Returns a new instance of CoverageBootstrapper.
17 18 19 |
# File 'lib/henitai/coverage_bootstrapper.rb', line 17 def initialize(static_filter: StaticFilter.new) @static_filter = static_filter end |
Instance Attribute Details
#static_filter ⇒ StaticFilter (readonly)
Returns the value of attribute static_filter.
60 61 62 |
# File 'lib/henitai/coverage_bootstrapper.rb', line 60 def static_filter @static_filter end |
Instance Method Details
#bootstrap_coverage(integration, config, test_files = nil) ⇒ void
This method returns an undefined value.
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
# File 'lib/henitai/coverage_bootstrapper.rb', line 127 def bootstrap_coverage(integration, config, test_files = nil) test_files ||= integration.test_files with_reports_dir(config) do with_coverage_dir(config) do result = integration.run_suite(test_files) if result == :survived record_dependency_manifest(config) return end raise CoverageError, build_bootstrap_error(result) end end end |
#configured_test_excludes(config) ⇒ Array[String]?
Specs pass bare config doubles exposing only what the example needs.
246 247 248 |
# File 'lib/henitai/coverage_bootstrapper.rb', line 246 def configured_test_excludes(config) config.respond_to?(:test_excludes) ? config.test_excludes : nil end |
#coverage_available?(source_files, config) ⇒ Boolean
62 63 64 65 66 67 |
# File 'lib/henitai/coverage_bootstrapper.rb', line 62 def coverage_available?(source_files, config) coverage_lines = static_filter.coverage_lines_for(config) covered_sources = covered_source_files(source_files, coverage_lines) covered_sources.any? end |
#coverage_fresh?(source_files, config, test_files) ⇒ Boolean
Returns true when a coverage report already exists, is newer than every watched source and test file, and the dependency path set is unchanged since the report was produced. Stale or absent reports return false.
88 89 90 91 92 93 94 |
# File 'lib/henitai/coverage_bootstrapper.rb', line 88 def coverage_fresh?(source_files, config, test_files) watched_files_fresh?( coverage_report_path(config), source_files, test_files ) && dependency_manifest_current?(config) end |
#coverage_report_path(config) ⇒ String
119 120 121 |
# File 'lib/henitai/coverage_bootstrapper.rb', line 119 def coverage_report_path(config) File.join(coverage_dir(config), ".resultset.json") end |
#ensure!(source_files:, config:, integration:, test_files: nil) ⇒ void
This method returns an undefined value.
Runs the test suite to collect coverage, unless a fresh report already exists.
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
# File 'lib/henitai/coverage_bootstrapper.rb', line 38 def ensure!(source_files:, config:, integration:, test_files: nil) return if source_files.empty? resolved_test_files = resolve_test_files(integration, test_files, config) # Skip the bootstrap only when the coverage artifacts are both newer than # all watched files and actually cover the configured sources. A fresh # but irrelevant report (e.g. from a different working directory) must # still trigger a re-bootstrap rather than silently proceeding with no # usable coverage. unless coverage_ready?(source_files, config, integration, resolved_test_files) bootstrap_coverage(integration, config, resolved_test_files) end return if coverage_available?(source_files, config) raise CoverageError, "Coverage data is unavailable for the configured source files" end |
#record_dependency_manifest(config) ⇒ Object
Writes the current dependency path set next to the coverage artifacts. Called after every bootstrap; exposed so tests can seed a manifest.
23 24 25 26 |
# File 'lib/henitai/coverage_bootstrapper.rb', line 23 def record_dependency_manifest(config) FileUtils.mkdir_p(reports_dir(config)) File.write(dependency_manifest_path(config), JSON.generate(current_dependency_paths)) end |
#resolve_test_files(integration, test_files, config) ⇒ Array[String]
Excluded tests are dropped here rather than in bootstrap_coverage, so
the same filtered list feeds both the suite run and the freshness watch
list: a test that no longer contributes to the baseline must not
invalidate it either.
234 235 236 237 238 239 240 241 242 243 |
# File 'lib/henitai/coverage_bootstrapper.rb', line 234 def resolve_test_files(integration, test_files, config) candidates = test_files.nil? ? integration.test_files : test_files patterns = configured_test_excludes(config) retained = ExcludedTestFilter.new(patterns:).reject(candidates) return retained unless retained.empty? && candidates.any? raise ConfigurationError, "test_excludes #{Array(patterns).inspect} excludes every test file; " \ "coverage cannot be generated" end |
#source_file_paths(source_files) ⇒ Array[String]
81 82 83 |
# File 'lib/henitai/coverage_bootstrapper.rb', line 81 def source_file_paths(source_files) Array(source_files).map { |path| File.(path) } end |