Mutineer Lifecycle & Job Plan
Relevant source files
The following files were used as context for generating this wiki page:
Mutineer automates mutation testing for Ruby applications by modifying source code and verifying if test suites detect these changes. The lifecycle encompasses configuration, mutation generation (Job Plan), execution via isolated workers, and structured reporting.
Command Execution Lifecycle
The Mutineer entry point is the CLI class, which manages the transition from raw user input to the execution of specific commands like run, init, or migrate.
The diagram shows the high-level flow from the binary execution to the specific command logic.
Sources: lib/mutineer/cli.rb:39-170, lib/mutineer/cli.rb:259-281
Command Responsibilities
| Command | Description |
|---|---|
run | Expands sources, generates mutations, executes tests, and reports results. |
init | Writes a starter .mutineer.yml configuration and identifies source directories. |
migrate | Updates old-format mutant IDs in the configuration file to include file paths. |
triage | Generates ignore entries for survivors found in a JSON report. |
Sources: lib/mutineer/cli.rb:19-37, lib/mutineer/init.rb:14-16
Job Plan Generation
The Job Plan determines which mutations to attempt. It identifies "subjects" (mutable code units), applies active operators to generate mutations, and assigns stable IDs to each mutant.
Collection and Scoping
Mutineer uses JobPlan.collect_jobs to assemble the work queue. This process includes:
- Subject Discovery: Identifying mutable classes, modules, and methods in the source files.
- Mutation Generation: Applying registered operators (e.g.,
arithmetic,comparison) to the subjects. - Suppression Filtering: Checking for inline
# mutineer:disable-linecomments orignore:entries in the config. - Diff Scoping: If
--sinceis provided, Mutineer filters mutations to only include lines changed since a specific Git ref.
Sources: lib/mutineer/job_plan.rb, lib/mutineer/cli.rb:158-164, test/equivalent_mutant_test.rb:24-40
Mutant ID Stability
Mutant IDs are content-based hashes. Since version 1.4, IDs include the project-relative file path to prevent collisions between identical methods in different files.
Sources: lib/mutineer/reporter.rb:149-152, test/equivalent_mutant_test.rb:108-115
Execution Phase (Runner)
The Runner orchestrates the execution of the Job Plan. It manages test coverage mapping and uses a worker pool for parallel processing.
The sequence illustrates the interaction between the Runner, JobPlan, and WorkerPool during a run.
Sources: lib/mutineer/runner.rb, lib/mutineer/cli.rb:518-524
Isolation Strategies
Mutineer employs two primary isolation strategies:
- reload: Loads the entire mutated file into a fresh process.
- redefine: Surgically replaces only the mutated method, which is faster but limited to statically-named classes.
Sources: lib/mutineer/cli.rb:46-47, lib/mutineer/result.rb:18-29
Result Classification
Every mutation attempt results in an immutable Mutineer::Result object. The system tracks ten distinct states to ensure the mutation score is accurate and the test suite's health is visible.
Result States
| State | Category | Meaning |
|---|---|---|
killed | Verdict | A test failed, catching the mutation. |
survived | Verdict | All tests passed; the mutation was not caught. |
no_coverage | Exclusion | No tests exercise the mutated line. |
ignored | Exclusion | The mutant was suppressed by the user. |
error | Failure | The child process crashed during execution. |
timeout | Failure | The execution exceeded the time limit. |
uncapturable | Failure | Tests failed to run during the coverage capture phase. |
unplaceable | Exclusion | The mutant belongs to an anonymous/unnameable class. |
Sources: lib/mutineer/result.rb:4-53
Score Calculation
The mutation score is calculated as:
score = killed / (killed + survived)
Exclusion and Failure states are removed from the denominator. An empty denominator results in a nil score (N/A).
Sources: lib/mutineer/result.rb:128-140, lib/mutineer/reporter.rb:584-596
Reporting and Gating
The Reporter generates human-readable or machine-readable (JSON/HTML) output. It also enforces "gates" that determine the CLI's exit code, crucial for CI/CD integration.
Exit Code Logic
- Code 0: Score meets
--thresholdAND no baseline regression. - Code 1: Score below threshold, baseline regression detected, or the run was "broken" (too many errors/timeouts).
- Code 2: Usage error or configuration failure.
Sources: lib/mutineer/reporter.rb:99-119, lib/mutineer/cli.rb:21-27
Completeness Gate
If more than 10% of attempted mutants (or more than 1 mutant in small runs) produce "no verdict" (error, timeout, or uncapturable), the --threshold gate fails automatically. This prevents a broken test harness from reporting a misleadingly high mutation score.
Sources: lib/mutineer/reporter.rb:17-26, lib/mutineer/reporter.rb:605-611