External Runner Integration
Relevant source files
The following files were used as context for generating this wiki page:
External Runner Integration allows Mutineer to execute mutation tests for applications pinned to Ruby versions older than 3.4. If you provide the --test-command flag, Mutineer runs the target suite in the application's own runtime as a subprocess. This integration bypasses the standard in-process execution, enabling compatibility with legacy environments while utilizing the modern Prism-based mutation engine.
Sources: lib/mutineer/cli.rb:97-98, lib/mutineer/cli.rb:269-272
CLI Configuration and Validation
You enable the external runner by passing a command string to the --test-command argument. This command must contain the %{files} placeholder, where Mutineer substitutes the relevant test files for each mutant.
The CLI enforces the following constraints when you use the external runner:
- It forces serial execution (
--jobs 1) because the external backend lacks per-worker database isolation. - It permits only the
reloadstrategy; surgicalredefinestrategy is not supported for external subprocesses. - It forbids combining the command with
--boot,--rails, or--daemonflags. - It disables the
--matrixflag, as external suites report only overall pass/fail status rather than individual test results.
Sources: lib/mutineer/cli.rb:273-286, lib/mutineer/cli.rb:333-335, test/cli_test.rb:134-150
Configuration Parameters
| Parameter | Type | Description |
|---|---|---|
--test-command | String | Subprocess command template containing %{files}. |
--jobs | Integer | Forced to 1 when an external runner is active. |
--strategy | String | Forced to reload for external execution. |
Sources: lib/mutineer/cli.rb:97-101, lib/mutineer/cli.rb:273-286
Subprocess Execution Logic
The integration manages the lifecycle of the external test suite. It spawns the process using the provided command template.
Process Flow
- The runner identifies the test files covering a specific mutation.
- It substitutes these files into the
%{files}placeholder. - It spawns the subprocess and monitors the exit status.
- It interprets the result: exit codes 126 and 127 are treated as errors, while other non-zero exit codes are interpreted as successful "kills" of the mutant.
Sources: lib/mutineer/cli.rb:444-448, lib/mutineer/reporter.rb:497-502
The diagram above shows the decision logic used to route mutation tests to the external subprocess.
Scoring and Limitations
Scores generated via the external runner are considered "upper bounds." Because the external runner does not support coverage narrowing, every mutant is run against the entire test list provided or inferred. This can result in mutants that are not truly covered by tests being reported as survivors rather than being excluded from the score.
Usage Restrictions Table
| Feature | Status | Reason |
|---|---|---|
| Parallel Workers | Disabled | No per-worker database or environment isolation. |
| Redefine Strategy | Disabled | Surgical redefinition requires a shared VM. |
| Kill Matrix | Disabled | External processes do not report per-test results. |
| Coverage Narrowing | Disabled | Subprocess cannot share fine-grained coverage maps. |
Sources: lib/mutineer/cli.rb:444-448, lib/mutineer/reporter.rb:497-502
GitHub Action Integration
The Mutineer GitHub Action integrates the external runner through the extra-args input. However, the action explicitly forbids passing --format or --output within extra-args because these parameters are handled by the action's internal plumbing to generate PR annotations and step summaries.
Sources: action.yml:81-84, action.yml:219-228
This sequence diagram illustrates the communication between the Mutineer CLI and the external application runtime.
External Runner Integration provides a critical bridge for legacy Ruby applications to benefit from mutation testing. While it imposes serial execution and lacks fine-grained coverage narrowing, it ensures that applications not yet on Ruby 3.4 can still validate test suite effectiveness.
Sources: lib/mutineer/cli.rb:97-101, lib/mutineer/reporter.rb:497-502