Skip to content

Commit

Permalink
feat: add output directory config via environment variable and config…
Browse files Browse the repository at this point in the history
… file (#463)

This PR adds possibility to configure output directory using:
1. `output` key in the config file.
2. `CARGO_MUTANTS_OUTPUT` environment varible.
  • Loading branch information
sourcefrog authored Dec 3, 2024
2 parents 9d711fc + f387980 commit 4c5fe62
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 4 deletions.
3 changes: 2 additions & 1 deletion book/src/mutants-out.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# The `mutants.out` directory

A `mutants.out` directory is created in the original source directory. You can put the output directory elsewhere with the `--output` option.
A `mutants.out` directory is created in the original source directory. You can put the output directory elsewhere with the `--output` option
or using `CARGO_MUTANTS_OUTPUT` environment variable or via `output` directive in the config file.

On each run, any existing `mutants.out` is renamed to `mutants.out.old`, and any
existing `mutants.out.old` is deleted.
Expand Down
4 changes: 3 additions & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use std::path::Path;
use std::str::FromStr;

use anyhow::Context;
use camino::Utf8Path;
use camino::{Utf8Path, Utf8PathBuf};
use serde::Deserialize;

use crate::options::TestTool;
Expand Down Expand Up @@ -45,6 +45,8 @@ pub struct Config {
pub additional_cargo_test_args: Vec<String>,
/// Minimum test timeout, in seconds, as a floor on the autoset value.
pub minimum_test_timeout: Option<f64>,
/// Output directory.
pub output: Option<Utf8PathBuf>,
/// Cargo profile.
pub profile: Option<String>,
/// Skip calls to functions or methods with these names.
Expand Down
7 changes: 6 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,12 @@ pub struct Args {
line_col: bool,

/// Create mutants.out within this directory.
#[arg(long, short = 'o', help_heading = "Output")]
#[arg(
long,
short = 'o',
env = "CARGO_MUTANTS_OUTPUT",
help_heading = "Output"
)]
output: Option<Utf8PathBuf>,

/// Include only mutants in code touched by this diff.
Expand Down
2 changes: 1 addition & 1 deletion src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ impl Options {
jobserver_tasks: args.jobserver_tasks,
leak_dirs: args.leak_dirs,
minimum_test_timeout,
output_in_dir: args.output.clone(),
output_in_dir: args.output.clone().or(config.output.clone()),
print_caught: args.caught,
print_unviable: args.unviable,
profile: args.profile.as_ref().or(config.profile.as_ref()).cloned(),
Expand Down
48 changes: 48 additions & 0 deletions tests/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -263,3 +263,51 @@ fn additional_cargo_test_args() {
.assert()
.success();
}

#[test]
/// Set the `--output` directory via `output` config directive.
fn output_option_use_config() {
let output_tmpdir = TempDir::new().unwrap();
let output_via_config = output_tmpdir.path().join("output_via_config");
let testdata = copy_of_testdata("factorial");

let out_path_str = output_via_config
.to_string_lossy()
.escape_default()
.to_string();
write_config_file(&testdata, &format!("output = \"{out_path_str}\""));

assert!(
!testdata.path().join("mutants.out").exists(),
"mutants.out should not be in a clean copy of the test data"
);

run()
.arg("mutants")
.args(["--check", "--no-times"])
.arg("-d")
.arg(testdata.path())
.assert()
.success();

assert!(
!testdata.path().join("mutants.out").exists(),
"mutants.out should not be in the source directory"
);
let mutants_out = output_via_config.join("mutants.out");
assert!(
mutants_out.exists(),
"mutants.out is in changed `output` directory"
);
for name in [
"mutants.json",
"debug.log",
"outcomes.json",
"missed.txt",
"caught.txt",
"timeout.txt",
"unviable.txt",
] {
assert!(mutants_out.join(name).is_file(), "{name} is in mutants.out",);
}
}
40 changes: 40 additions & 0 deletions tests/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,46 @@ fn output_option() {
}
}

#[test]
/// Set the `--output` directory via environment variable `CARGO_MUTANTS_OUTPUT`
fn output_option_use_env() {
let tmp_src_dir = copy_of_testdata("factorial");
let output_tmpdir = TempDir::new().unwrap();
let output_via_env = output_tmpdir.path().join("output_via_env");
assert!(
!tmp_src_dir.path().join("mutants.out").exists(),
"mutants.out should not be in a clean copy of the test data"
);
run()
.env("CARGO_MUTANTS_OUTPUT", &output_via_env)
.arg("mutants")
.args(["--check", "--no-times"])
.arg("-d")
.arg(tmp_src_dir.path())
.assert()
.success();
assert!(
!tmp_src_dir.path().join("mutants.out").exists(),
"mutants.out should not be in the source directory"
);
let mutants_out = output_via_env.join("mutants.out");
assert!(
mutants_out.exists(),
"mutants.out is in $CARGO_MUTANTS_OUTPUT directory"
);
for name in [
"mutants.json",
"debug.log",
"outcomes.json",
"missed.txt",
"caught.txt",
"timeout.txt",
"unviable.txt",
] {
assert!(mutants_out.join(name).is_file(), "{name} is in mutants.out",);
}
}

#[test]
fn check_succeeds_in_tree_that_builds_but_fails_tests() {
// --check doesn't actually run the tests so won't discover that they fail.
Expand Down

0 comments on commit 4c5fe62

Please sign in to comment.