Class: Henitai::Runner

Inherits:
Object
  • Object
show all
Defined in:
lib/henitai/runner.rb

Overview

Orchestrates the full mutation testing pipeline.

Pipeline phases (Phase-Gate model):

Gate 1 — Subject selection
  Resolve source files from includes, apply --since filter (incremental),
  build Subject list from AST.

Gate 2 — Mutant generation
  Apply operators to each Subject's AST. Filter arid (non-productive)
  nodes via ignore_patterns. Produces the initial mutant list.

Gate 3 — Static filtering
  Remove ignored mutants (pattern matches), compile-time errors.
  Apply per-test coverage data: mark :no_coverage for uncovered mutants.

Gate 4 — Mutant execution
  Run surviving mutants in isolated child processes (fork isolation).
  Each child process loads the test suite with the mutated method
  injected via Module#define_method. Collect kill/survive/timeout results.

Gate 5 — Reporting
  Write results to configured reporters (terminal, html, json, dashboard).

rubocop:disable Metrics/ClassLength

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config: Configuration.load, subjects: nil, since: nil, survivors_from: nil) ⇒ Runner

Returns a new instance of Runner.



32
33
34
35
36
37
# File 'lib/henitai/runner.rb', line 32

def initialize(config: Configuration.load, subjects: nil, since: nil, survivors_from: nil)
  @config         = config
  @subjects       = subjects
  @since          = since
  @survivors_from = survivors_from
end

Instance Attribute Details

#configObject (readonly)

Returns the value of attribute config.



30
31
32
# File 'lib/henitai/runner.rb', line 30

def config
  @config
end

#resultObject (readonly)

Returns the value of attribute result.



30
31
32
# File 'lib/henitai/runner.rb', line 30

def result
  @result
end

Instance Method Details

#runResult

Entry point — runs the full pipeline and returns a Result.

Fast path (recipe rerun): when --survivors-from is given and an activation-recipes.json file exists beside the report with entries for all survivor IDs, stub Mutants are built from the recipes and the full source-parse / mutant-generation pipeline is skipped entirely.

Normal path: Coverage bootstrap (Gate 0) runs in a background thread so that Gate 1 (subject resolution) and Gate 2 (mutant generation) proceed concurrently. The thread is joined before Gate 3 (static filtering).

Returns:



51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/henitai/runner.rb', line 51

def run
  started_at = Time.now

  mutants = if survivor_rerun? && (fast_mutants = try_recipe_run)
              execute_mutants(fast_mutants)
            else
              source_files = self.source_files
              subjects = resolve_subjects(source_files)
              execute_mutants(mutants_for(subjects, source_files))
            end

  build_result(mutants, started_at, Time.now)
end