Class: Mutineer::CoverageMap
- Inherits:
-
Object
- Object
- Mutineer::CoverageMap
- Defined in:
- lib/mutineer/coverage_map.rb
Overview
Maps (source_file, line) -> [test_files] so each mutant runs only against
the tests that actually exercise its line. Built once (Phase A), then queried
per mutant (Phase B via #tests_for). Persisted to .mutineer/coverage.json with
a content-based digest that rebuilds the map whenever any tracked file changes.
Keys are "file:line" strings (relative to project_root) everywhere — in memory and on disk — so load/save needs no key transformation (KTD4).
Constant Summary collapse
- DEFAULT_CAPTURE_TIMEOUT =
seconds, per coverage subprocess (R3)
120
Instance Attribute Summary collapse
-
#failed_test_files ⇒ Object
readonly
Returns the value of attribute failed_test_files.
-
#phase_a_ran ⇒ Object
readonly
Returns the value of attribute phase_a_ran.
-
#project_root ⇒ Object
readonly
Returns the value of attribute project_root.
Instance Method Summary collapse
-
#build_or_load ⇒ Object
Phase A entry point (standalone): load the cached map when the content digest matches, otherwise rebuild from subprocesses and overwrite the cache.
-
#build_via_fork(rails: false) ⇒ Object
Boot-mode Phase A: Coverage is already running in the parent (started before the app booted, so booted source lines are instrumented).
-
#initialize(source_paths:, test_paths:, cache_dir: ".mutineer", load_paths: ["lib"], project_root: Dir.pwd, capture_timeout: DEFAULT_CAPTURE_TIMEOUT, boot_path: nil, framework: "minitest") ⇒ CoverageMap
constructor
A new instance of CoverageMap.
-
#tests_for(file, line) ⇒ Object
Phase B lookup: the test files that cover
file:line, or [] when none do.
Constructor Details
#initialize(source_paths:, test_paths:, cache_dir: ".mutineer", load_paths: ["lib"], project_root: Dir.pwd, capture_timeout: DEFAULT_CAPTURE_TIMEOUT, boot_path: nil, framework: "minitest") ⇒ CoverageMap
Returns a new instance of CoverageMap.
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
# File 'lib/mutineer/coverage_map.rb', line 25 def initialize(source_paths:, test_paths:, cache_dir: ".mutineer", load_paths: ["lib"], project_root: Dir.pwd, capture_timeout: DEFAULT_CAPTURE_TIMEOUT, boot_path: nil, framework: "minitest") @source_paths = Array(source_paths) @test_paths = Array(test_paths) @cache_dir = cache_dir @load_paths = Array(load_paths) @project_root = project_root @capture_timeout = capture_timeout @boot_path = boot_path @framework = framework || "minitest" @map = {} @failed_test_files = [] @phase_a_ran = false end |
Instance Attribute Details
#failed_test_files ⇒ Object (readonly)
Returns the value of attribute failed_test_files.
23 24 25 |
# File 'lib/mutineer/coverage_map.rb', line 23 def failed_test_files @failed_test_files end |
#phase_a_ran ⇒ Object (readonly)
Returns the value of attribute phase_a_ran.
23 24 25 |
# File 'lib/mutineer/coverage_map.rb', line 23 def phase_a_ran @phase_a_ran end |
#project_root ⇒ Object (readonly)
Returns the value of attribute project_root.
23 24 25 |
# File 'lib/mutineer/coverage_map.rb', line 23 def project_root @project_root end |
Instance Method Details
#build_or_load ⇒ Object
Phase A entry point (standalone): load the cached map when the content digest matches, otherwise rebuild from subprocesses and overwrite the cache.
44 45 46 47 |
# File 'lib/mutineer/coverage_map.rb', line 44 def build_or_load warn_external_sources cached_or { run_phase_a } end |
#build_via_fork(rails: false) ⇒ Object
Boot-mode Phase A: Coverage is already running in the parent (started before
the app booted, so booted source lines are instrumented). A clean ruby
subprocess has no booted env, so per-test coverage is captured by FORKING
the booted parent instead. Inverts into the same map #tests_for reads, and
reuses the digest cache (the digest mixes in the boot file so a boot cache
never collides with a standalone one).
55 56 57 58 |
# File 'lib/mutineer/coverage_map.rb', line 55 def build_via_fork(rails: false) warn_external_sources cached_or { run_phase_a_via_fork(rails: rails) } end |
#tests_for(file, line) ⇒ Object
Phase B lookup: the test files that cover file:line, or [] when none do.
ponytail: per-file granularity; upgrade to per-method when throughput
warrants (requires Minitest method isolation + finer Coverage tracking).
63 64 65 |
# File 'lib/mutineer/coverage_map.rb', line 63 def tests_for(file, line) @map["#{relativize(file)}:#{line}"] || [] end |