Class: Rigor::Protection::MutationScanner

Inherits:
Object
  • Object
show all
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: re-analyse the mutated SOURCE against a clean baseline and read whether a NEW diagnostic appears. A killed mutation is a caught breakage; a survived one is a breakage Rigor missed — an “add a type here” site.

The expensive builds (RBS environment + the whole-project pre-pass scan) are paid ONCE by the caller and threaded in via ‘environment:` / `project_scan:`; each mutant reuses them through `Runner.new(prebuilt:)#run_source` (in-memory overlay, no disk write). Passing `prebuilt:` disables the run-result cache (whose key digests the disk file), so a mutant is never served a stale clean hit.

Defined Under Namespace

Classes: FileResult, SurvivingSite

Instance Method Summary collapse

Constructor Details

#initialize(configuration:, environment:, project_scan:, limit: nil, seed: 1) ⇒ MutationScanner

Returns a new instance of MutationScanner.

Parameters:

  • configuration (Rigor::Configuration)
  • environment (Rigor::Environment)

    pre-built once by the caller

  • project_scan (Rigor::Analysis::ProjectScan)

    pre-built once

  • limit (Integer, nil) (defaults to: nil)

    optional per-file mutation cap (sampled with ‘seed`); nil analyses every type-relevant mutation (deterministic).

  • seed (Integer) (defaults to: 1)

    RNG seed for the optional sample.



48
49
50
51
52
53
54
# File 'lib/rigor/protection/mutation_scanner.rb', line 48

def initialize(configuration:, environment:, project_scan:, limit: nil, seed: 1)
  @configuration = configuration
  @environment = environment
  @project_scan = project_scan
  @limit = limit
  @seed = seed
end

Instance Method Details

#scan_file(path, source: nil) ⇒ FileResult

Parameters:

  • path (String)

    the file to measure (used as the in-memory bind path)

  • source (String, nil) (defaults to: nil)

    the file’s source; read from disk when nil

Returns:



59
60
61
62
63
64
65
66
67
68
# File 'lib/rigor/protection/mutation_scanner.rb', line 59

def scan_file(path, source: nil)
  source ||= File.read(path, encoding: Encoding::UTF_8)
  mutator = Mutator.new(source)
  kept, = mutator.filter_by_type(mutator.mutations, environment: @environment, path: path)
  kept = sample(kept)
  return FileResult.new(path: path, killed: 0, survived: 0, sites: []) if kept.empty?

  baseline = signatures(analyse(source, path))
  measure(source, path, kept, baseline)
end