Class: Henitai::CoverageBootstrapper

Inherits:
Object
  • Object
show all
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

Instance Method Summary collapse

Constructor Details

#initialize(static_filter: StaticFilter.new) ⇒ CoverageBootstrapper

Returns a new instance of CoverageBootstrapper.

Parameters:

  • static_filter: (StaticFilter) (defaults to: StaticFilter.new)


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_filterStaticFilter (readonly)

Returns the value of attribute static_filter.

Returns:



58
59
60
# File 'lib/henitai/coverage_bootstrapper.rb', line 58

def static_filter
  @static_filter
end

Instance Method Details

#bootstrap_coverage(integration, config, test_files = nil) ⇒ void

This method returns an undefined value.

Parameters:



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/henitai/coverage_bootstrapper.rb', line 125

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

#coverage_available?(source_files, config) ⇒ Boolean

Parameters:

Returns:

  • (Boolean)


60
61
62
63
64
65
# File 'lib/henitai/coverage_bootstrapper.rb', line 60

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.

Parameters:

Returns:

  • (Boolean)


86
87
88
89
90
91
92
# File 'lib/henitai/coverage_bootstrapper.rb', line 86

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

Parameters:

Returns:

  • (String)


117
118
119
# File 'lib/henitai/coverage_bootstrapper.rb', line 117

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.

Parameters:

  • source_files (Array<String>)

    lib files whose coverage must be present

  • config (Configuration)
  • integration (Integration::Base)
  • test_files (Array<String>, nil) (defaults to: nil)

    test files to run; defaults to all files reported by the integration when nil

  • source_files: (Array[String])
  • config: (Configuration)
  • integration: (Integration::Base)
  • test_files: (Array[String], nil) (defaults to: nil)

Raises:



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/henitai/coverage_bootstrapper.rb', line 36

def ensure!(source_files:, config:, integration:, test_files: nil)
  return if source_files.empty?

  resolved_test_files = resolve_test_files(integration, test_files)

  # 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

#source_file_paths(source_files) ⇒ Array[String]

Parameters:

  • (Array[String])

Returns:

  • (Array[String])


79
80
81
# File 'lib/henitai/coverage_bootstrapper.rb', line 79

def source_file_paths(source_files)
  Array(source_files).map { |path| File.expand_path(path) }
end