diff --git a/DESIGN.md b/DESIGN.md index 1f0fdecf..8ad79af8 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -103,3 +103,9 @@ up the CPU will continue. Therefore we need to also intercept the signal to cargo-mutants and manually pass it on to the subprocess group. + +## Output directory handling + +Various output files, including the text output from all the cargo commands are +written into `mutants.out` within the directory specified by `--output`, or by +default the source directory. diff --git a/NEWS.md b/NEWS.md index 8f0931bc..90901d8c 100644 --- a/NEWS.md +++ b/NEWS.md @@ -9,7 +9,10 @@ `Default`. cargo-mutants can not yet generate good mutations for these so they are generally false positives. -- Better display of `::foo`. +- Improved: Better display of `::foo` and similar type paths. + +- New: `--output` directory to write `mutants.out` somewhere other than the + source directory. ## 0.2.4 diff --git a/src/lab.rs b/src/lab.rs index 6d9f3a2e..31cda1cc 100644 --- a/src/lab.rs +++ b/src/lab.rs @@ -70,7 +70,12 @@ pub fn test_unmutated_then_all_mutants( ) -> Result { let mut options: Options = options.clone(); let mut lab_outcome = LabOutcome::default(); - let output_dir = OutputDir::new(source_tree.root())?; + let output_in_dir = if let Some(o) = &options.output_in_dir { + o.as_path() + } else { + source_tree.root() + }; + let output_dir = OutputDir::new(output_in_dir)?; let mut lab_activity = LabActivity::new(&options); if options.build_source { diff --git a/src/main.rs b/src/main.rs index 5c95993c..0d1d9d0d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -84,6 +84,10 @@ struct Args { #[argh(switch)] no_times: bool, + /// create mutants.out within this directory. + #[argh(option, short = 'o')] + output: Option, + /// run mutants in random order. #[argh(switch)] shuffle: bool, diff --git a/src/options.rs b/src/options.rs index f9092c63..b36a981f 100644 --- a/src/options.rs +++ b/src/options.rs @@ -42,6 +42,9 @@ pub struct Options { /// Files to examine. pub globset: Option, + + /// Create `mutants.out` within this directory (by default, the source directory). + pub output_in_dir: Option, } impl Options { @@ -84,6 +87,7 @@ impl TryFrom<&Args> for Options { check_only: args.check, copy_target: !args.no_copy_target, globset, + output_in_dir: args.output.clone(), print_caught: args.caught, print_unviable: args.unviable, shuffle: !args.no_shuffle, diff --git a/src/output.rs b/src/output.rs index 8f0f6e13..1229c97a 100644 --- a/src/output.rs +++ b/src/output.rs @@ -24,10 +24,10 @@ impl OutputDir { /// /// If the directory already exists, it's rotated to `mutants.out.old`. If that directory /// exists, it's deleted. - pub fn new>(in_dir: P) -> Result { - let path: PathBuf = in_dir.as_ref().join(OUTDIR_NAME); + pub fn new(in_dir: &Path) -> Result { + let path = in_dir.join(OUTDIR_NAME); if path.exists() { - let rotated = in_dir.as_ref().join(ROTATED_NAME); + let rotated = in_dir.join(ROTATED_NAME); if rotated.exists() { fs::remove_dir_all(&rotated).with_context(|| format!("remove {:?}", &rotated))?; } @@ -104,7 +104,7 @@ mod test { let temp_dir = tempfile::TempDir::new().unwrap(); // Create an initial output dir with one log. - let output_dir = OutputDir::new(&temp_dir).unwrap(); + let output_dir = OutputDir::new(temp_dir.path()).unwrap(); output_dir.create_log(&Scenario::SourceTree).unwrap(); assert!(temp_dir .path() @@ -112,7 +112,7 @@ mod test { .is_file()); // The second time we create it in the same directory, the old one is moved away. - let output_dir = OutputDir::new(&temp_dir).unwrap(); + let output_dir = OutputDir::new(temp_dir.path()).unwrap(); output_dir.create_log(&Scenario::SourceTree).unwrap(); output_dir.create_log(&Scenario::Baseline).unwrap(); assert!(temp_dir @@ -129,7 +129,7 @@ mod test { .is_file()); // The third time (and later), the .old directory is removed. - let output_dir = OutputDir::new(&temp_dir).unwrap(); + let output_dir = OutputDir::new(temp_dir.path()).unwrap(); output_dir.create_log(&Scenario::SourceTree).unwrap(); assert!(temp_dir .path() diff --git a/tests/cli.rs b/tests/cli.rs index 082a4051..5841c908 100644 --- a/tests/cli.rs +++ b/tests/cli.rs @@ -362,6 +362,47 @@ fn factorial_mutants_no_copy_target() { })); } +#[test] +fn output_option() { + let tmp_src_dir = copy_of_testdata("factorial"); + let output_tmpdir = TempDir::new().unwrap(); + assert!( + !tmp_src_dir.path().join("mutants.out").exists(), + "mutants.out should not be in a clean copy of the test data" + ); + run_assert_cmd() + .arg("mutants") + .arg("--output") + .arg(&output_tmpdir.path()) + .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 after --output was given" + ); + assert!( + output_tmpdir.path().join("mutants.out").exists(), + "mutants.out is in --output directory" + ); + assert!( + output_tmpdir + .path() + .join("mutants.out/mutants.json") + .is_file(), + "mutants.out/mutants.json is in --output directory" + ); + assert!( + output_tmpdir + .path() + .join("mutants.out/outcomes.json") + .is_file(), + "mutants.out/outcomes.json is in --output directory" + ); +} + #[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.