From 21260b9770e3ef94654233ec144e97b43513b209 Mon Sep 17 00:00:00 2001 From: Oleksii Karpenko Date: Mon, 25 Nov 2024 21:42:46 +0200 Subject: [PATCH] feat: add output directory config via environment variable and config file --- book/src/mutants-out.md | 3 ++- src/config.rs | 4 +++- src/main.rs | 7 +++++- src/options.rs | 2 +- tests/config.rs | 47 +++++++++++++++++++++++++++++++++++++++++ tests/main.rs | 40 +++++++++++++++++++++++++++++++++++ 6 files changed, 99 insertions(+), 4 deletions(-) diff --git a/book/src/mutants-out.md b/book/src/mutants-out.md index 20182c09..e7cfff62 100644 --- a/book/src/mutants-out.md +++ b/book/src/mutants-out.md @@ -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. diff --git a/src/config.rs b/src/config.rs index 65df1599..796d6094 100644 --- a/src/config.rs +++ b/src/config.rs @@ -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; @@ -45,6 +45,8 @@ pub struct Config { pub additional_cargo_test_args: Vec, /// Minimum test timeout, in seconds, as a floor on the autoset value. pub minimum_test_timeout: Option, + /// Output directory. + pub output: Option, /// Cargo profile. pub profile: Option, /// Skip calls to functions or methods with these names. diff --git a/src/main.rs b/src/main.rs index c9fe8a21..74629093 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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, /// Include only mutants in code touched by this diff. diff --git a/src/options.rs b/src/options.rs index 5e62e9b5..dfccdc7d 100644 --- a/src/options.rs +++ b/src/options.rs @@ -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(), diff --git a/tests/config.rs b/tests/config.rs index 504c30ef..3ce44852 100644 --- a/tests/config.rs +++ b/tests/config.rs @@ -263,3 +263,50 @@ 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"); + write_config_file( + &testdata, + &format!("output = \"{}\"\n", output_via_config.to_str().unwrap()), + ); + + 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",); + } +} diff --git a/tests/main.rs b/tests/main.rs index fd1bd89d..f428de98 100644 --- a/tests/main.rs +++ b/tests/main.rs @@ -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.