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.
66 67 68 69 70 71 72 73 74 75 |
# File 'lib/rigor/protection/mutation_scanner.rb', line 66 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
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/rigor/protection/mutation_scanner.rb', line 80 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.
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/rigor/protection/mutation_scanner.rb', line 104 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 |