Skip to main content

fallow_api/mutation/
mod.rs

1//! The dynamic half of mutation testing: the runner protocol, the embedded
2//! companion, the transport, the scheduler and the result model.
3//!
4//! The static half (selection, the test map, pruning, ranking and the
5//! instrumented code) is `fallow_engine::mutation`. The entry points are
6//! [`crate::run_mutation`] and [`crate::plan_mutation`].
7
8pub(crate) mod companion;
9pub(crate) mod discovery;
10pub(crate) mod journal;
11pub(crate) mod last_report;
12pub(crate) mod manifest;
13pub(crate) mod node_args;
14pub(crate) mod protocol;
15pub(crate) mod report;
16pub(crate) mod result_cache;
17pub(crate) mod run_dir;
18pub(crate) mod runner_select;
19pub(crate) mod scheduler;
20pub(crate) mod transport;
21pub(crate) mod typecheck;
22pub(crate) mod verdicts;
23pub(crate) mod worktree;
24
25#[cfg(test)]
26#[expect(clippy::unwrap_used, reason = "tests unwrap to keep assertions short")]
27mod e2e_tests;
28
29use std::path::PathBuf;
30use std::time::Duration;
31
32pub use fallow_engine::mutation::Operator;
33pub use last_report::{
34    last_mutation_report_path, remove_finished_mutation_journal, save_last_mutation_report,
35};
36pub use report::baseline_blocked_run_error;
37pub use scheduler::control::split_control_run_failure;
38pub use verdicts::{MAX_VERDICT_INPUT_BYTES, apply_mutation_verdicts, validate_mutation_verdicts};
39
40use crate::AnalysisOptions;
41
42/// The code that a mutation run mutates.
43#[derive(Debug, Clone, PartialEq, Eq)]
44pub enum MutationRunScope {
45    /// The added lines against a base ref. `None` detects the base ref the
46    /// same way as `audit`. An explicit `analysis.diff_file` wins over the
47    /// base ref.
48    Changed {
49        /// The base ref.
50        base: Option<String>,
51    },
52    /// Each source file of the project.
53    All,
54    /// The files at these paths, or below these directories. A relative
55    /// path is relative to the project root.
56    Paths(Vec<PathBuf>),
57}
58
59impl Default for MutationRunScope {
60    fn default() -> Self {
61        Self::Changed { base: None }
62    }
63}
64
65/// A phase event of a mutation run, for a caller that shows progress.
66///
67/// [`crate::run_mutation_with_progress`] sends the events in run order. A
68/// run with no covered mutant and no test file skips the runner phases.
69#[derive(Debug, Clone, Copy, PartialEq, Eq)]
70#[non_exhaustive]
71pub enum MutationProgress {
72    /// The plan is ready. The runner setup starts: the runtime, the
73    /// companion install and the runner start.
74    Setup {
75        /// The planned mutants.
76        mutants: usize,
77    },
78    /// The baseline run starts: the test files run with no active mutant.
79    Baseline {
80        /// The test files of the baseline run.
81        test_files: usize,
82    },
83    /// The mutant runs. The first event has `done` 0, and each finished
84    /// mutant sends one event.
85    Mutants {
86        /// The mutants that finished.
87        done: usize,
88        /// The mutants that this phase runs. A cached result is not in it.
89        total: usize,
90    },
91    /// The retries of survivors and timeouts. The first event has `done` 0.
92    Retries {
93        /// The retries that finished.
94        done: usize,
95        /// The retries that this phase runs.
96        total: usize,
97    },
98    /// The tests stopped. The worktree check, the survivor type check and
99    /// the report start.
100    Finalize,
101}
102
103/// Receives the [`MutationProgress`] events of a run. The scheduler sends
104/// the mutant events from its worker threads.
105pub type MutationProgressSink<'a> = &'a (dyn Fn(MutationProgress) + Sync);
106
107/// Options of [`crate::run_mutation`] and [`crate::plan_mutation`].
108#[derive(Debug, Clone, Default)]
109pub struct MutationRunOptions {
110    /// Shared project, config and cache options.
111    pub analysis: AnalysisOptions,
112    /// The code to mutate.
113    pub scope: MutationRunScope,
114    /// The operators to use. `None` uses each operator.
115    pub operators: Option<Vec<Operator>>,
116    /// Keep mutants near a file system, process, network or database sink.
117    /// Only an explicit caller option can set this, never project config,
118    /// because the run has no sandbox copy.
119    pub allow_side_effects: bool,
120    /// The maximum number of covered mutants to run, in rank order. A
121    /// mutant that no dry-run test covers is `no_coverage` and does not
122    /// count. The others become `pending`.
123    pub max_mutants: Option<usize>,
124    /// Run in a dirty git worktree, or outside a git worktree. The run still
125    /// compares the worktree after the run.
126    pub allow_dirty: bool,
127    /// The JavaScript runtime. `None` uses the runtime of the runner from
128    /// `PATH`: `node` for `node:test`, `bun` for `bun test`, and `node`,
129    /// then `bun`, for Vitest.
130    pub runtime: Option<PathBuf>,
131    /// The number of companion processes. `None` uses half the logical
132    /// cores.
133    pub concurrency: Option<usize>,
134    /// The time budget of the run. Mutants that did not start before it
135    /// ends become `pending`.
136    pub time_budget: Option<Duration>,
137    /// The timeout of the companion `init` and the dry run. `None` uses
138    /// 120 seconds.
139    pub runner_timeout: Option<Duration>,
140    /// The config file of the test runner, for example a separate Vitest
141    /// config of the integration tests. A relative path is relative to the
142    /// project root. The file must exist inside the project root. `None`
143    /// uses the `mutation.runnerConfig` config key, else the default config
144    /// discovery of the runner. Only the Vitest runner reads a config file.
145    pub runner_config: Option<PathBuf>,
146    /// The files that the caller writes after the run, for example the
147    /// `--output-file` report. The worktree check leaves them out, so an old
148    /// report does not make the worktree dirty. A relative path is relative
149    /// to the process directory, as the caller writes it.
150    pub output_paths: Vec<PathBuf>,
151    /// Type-check each survived mutant through the TypeScript-Go companion
152    /// of `--type-aware`. A survivor whose mutated file does not type-check
153    /// becomes `compile_error` and leaves the score. The run fails before
154    /// the tests start when no trusted companion exists.
155    pub typecheck: bool,
156    /// Reuse the final results in the outcome journal of an interrupted
157    /// earlier run, and run only the other mutants. A journal of other
158    /// inputs stops the run with `FALLOW_MUTATION_RESUME_MISMATCH`. With no
159    /// journal, the run starts from the beginning and the report says so.
160    /// Without this option, the run starts a new journal.
161    pub resume: bool,
162}