Class: Rigor::Protection::MutationScanner
- Inherits:
-
Object
- Object
- Rigor::Protection::MutationScanner
- Defined in:
- lib/rigor/protection/mutation_scanner.rb
Overview
ADR-63 Tier 2 — the mutation effectiveness tier (the truth tier behind Tier 1’s static Inference::ProtectionScanner proxy). For one file it answers the question Tier 1 only bounds: when a type-visible bug is introduced at a dispatch site, does Rigor actually catch it?
Mechanism (the ADR-62 warm loop, narrowed to per-file measurement): generate the type-visible mutations (Mutator), keep only those whose receiver Rigor holds a concrete type for (the type-aware filter — the FP-safe meaning-maker; an unresolved receiver is kept), then for each ask the **kill oracle** whether the mutant is caught. The oracle is the ADR-69 seam: #scan_file uses the DiagnosticOracle (a *new Rigor diagnostic* = a kill); #scan_file_fused additionally consults a TestSuiteOracle on the type-survivors (ADR-70 — the dynamic protection axis).
The expensive builds (RBS environment + the whole-project pre-pass scan) are paid ONCE by the caller and threaded into the DiagnosticOracle; each mutant reuses them through ‘Runner.new(prebuilt:)#run_source` (in-memory overlay, no disk write).
Defined Under Namespace
Classes: FileResult, FusedFileResult, FusedSite, SurvivingSite
Instance Method Summary collapse
-
#initialize(configuration:, environment:, project_scan:, limit: nil, seed: 1, oracle: nil, site_selector: :biteable) ⇒ MutationScanner
constructor
A new instance of MutationScanner.
- #scan_file(path, source: nil) ⇒ FileResult
-
#scan_file_fused(path, test_oracle:, source: nil) ⇒ FusedFileResult
ADR-70 — the fused static∪dynamic measurement.
Constructor Details
#initialize(configuration:, environment:, project_scan:, limit: nil, seed: 1, oracle: nil, site_selector: :biteable) ⇒ MutationScanner
Returns a new instance of MutationScanner.
72 73 74 75 76 77 78 79 80 81 |
# File 'lib/rigor/protection/mutation_scanner.rb', line 72 def initialize(configuration:, environment:, project_scan:, limit: nil, seed: 1, oracle: nil, site_selector: :biteable) @environment = environment @limit = limit @seed = seed @site_selector = site_selector @oracle = oracle || DiagnosticOracle.new( configuration: configuration, environment: environment, project_scan: project_scan ) end |
Instance Method Details
#scan_file(path, source: nil) ⇒ FileResult
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
# File 'lib/rigor/protection/mutation_scanner.rb', line 86 def scan_file(path, source: nil) source ||= File.read(path, encoding: Encoding::UTF_8) kept = kept_mutations(source, path) return FileResult.new(path: path, killed: 0, survived: 0, sites: []) if kept.empty? baseline = @oracle.baseline(source: source, path: path) killed = 0 sites = [] kept.each do |mut| case classify(source, path, mut, baseline) when :killed then killed += 1 when :survived then sites << surviving_site(mut) # :invalid — a parse-broken mutant; not a measurement, skip it. end end FileResult.new(path: path, killed: killed, survived: sites.size, sites: sites) end |
#scan_file_fused(path, test_oracle:, source: nil) ⇒ FusedFileResult
ADR-70 — the fused static∪dynamic measurement. Runs the type pass (the DiagnosticOracle); for every mutant the type checker did not kill, asks ‘test_oracle` whether the project’s test suite catches it. The expensive suite run is paid only for type-survivors (the gradual short-circuit), so the cost is proportional to the protection hole.
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
# File 'lib/rigor/protection/mutation_scanner.rb', line 111 def scan_file_fused(path, test_oracle:, source: nil) source ||= File.read(path, encoding: Encoding::UTF_8) kept = kept_mutations(source, path) return FusedFileResult.new(path: path, type_killed: 0, test_killed: 0, sites: []) if kept.empty? baseline = @oracle.baseline(source: source, path: path) type_killed = 0 test_killed = 0 sites = [] kept.each do |mut| case classify(source, path, mut, baseline) when :killed then type_killed += 1 when :survived if test_oracle.killed?(path: path, original: source, mutant_source: mut.apply(source)) test_killed += 1 else sites << fused_site(mut, :none) end # :invalid — a parse-broken mutant; not a measurement, skip it. end end FusedFileResult.new(path: path, type_killed: type_killed, test_killed: test_killed, sites: sites) end |