diff --git a/casr/src/bin/casr-cluster.rs b/casr/src/bin/casr-cluster.rs index 521e420e..dbeea187 100644 --- a/casr/src/bin/casr-cluster.rs +++ b/casr/src/bin/casr-cluster.rs @@ -279,7 +279,9 @@ fn deduplication(indir: &Path, outdir: Option, jobs: usize) -> Result<( Ok((before, after)) } -/// Merge new reports from input directory into output directory +/// Merge unique reports from `input` directory into `output` directory. +/// If `diff` directory is set, unique (`input` \ `output`) reports are saved +/// in `diff` directory. /// /// # Arguments /// @@ -287,10 +289,12 @@ fn deduplication(indir: &Path, outdir: Option, jobs: usize) -> Result<( /// /// * `output` - path to output directory with CASR reports /// +/// * `diff` - optional: path to save unique (`input` \ `output`) reports +/// /// # Return value /// /// Number of merged reports -fn merge_dirs(input: &Path, output: &Path) -> Result { +fn merge_or_diff(input: &Path, output: &Path, diff: Option<&Path>) -> Result { let dir = fs::read_dir(output).with_context(|| { format!( "Error occurred while opening directory with CASR reports. Directory: {}", @@ -316,12 +320,19 @@ fn merge_dirs(input: &Path, output: &Path) -> Result { ) })?; + let save_dir = if let Some(diff) = diff { + fs::create_dir(diff)?; + diff + } else { + output + }; + let mut new: u64 = 0; for entry in dir.flatten() { if entry.path().extension().is_some() && entry.path().extension().unwrap() == "casrep" { if let Ok(trace) = stacktrace(entry.path().as_path()) { if mainhash.insert(trace) { - let target = Path::new(&output).join(entry.file_name()); + let target = Path::new(&save_dir).join(entry.file_name()); if target.exists() { eprintln!( "File with name {} already exists in OUTPUT_DIR.", @@ -408,6 +419,18 @@ fn main() -> Result<()> { INPUT_DIR will be added to OUTPUT_DIR.", ), ) + .arg( + Arg::new("diff") + .long("diff") + .action(ArgAction::Set) + .num_args(3) + .value_parser(clap::value_parser!(PathBuf)) + .value_names(["NEW_DIR", "PREV_DIR", "DIFF_DIR"]) + .help( + "Compute report sets difference NEW_DIR \\ PREV_DIR. \ + Copy new CASR reports from NEW_DIR into DIFF_DIR.", + ), + ) .arg( Arg::new("ignore") .long("ignore") @@ -474,12 +497,20 @@ fn main() -> Result<()> { println!("Number of reports after deduplication: {after}"); } else if matches.contains_id("merge") { let paths: Vec<&PathBuf> = matches.get_many::("merge").unwrap().collect(); - let new = merge_dirs(paths[0], paths[1])?; + let new = merge_or_diff(paths[0], paths[1], None)?; println!( "Merged {} new reports into {} directory", new, paths[1].display() ); + } else if matches.contains_id("diff") { + let paths: Vec<&PathBuf> = matches.get_many::("diff").unwrap().collect(); + let new = merge_or_diff(paths[0], paths[1], Some(paths[2]))?; + println!( + "Diff of {} new reports is saved into {} directory", + new, + paths[2].display() + ); } Ok(()) diff --git a/casr/tests/tests.rs b/casr/tests/tests.rs index 606baafc..5ba7a27d 100644 --- a/casr/tests/tests.rs +++ b/casr/tests/tests.rs @@ -2603,9 +2603,11 @@ fn test_casr_cluster_d_and_m() { let paths = [ abs_path("tests/casr_tests/casrep/dedup/in"), abs_path("tests/tmp_tests_casr/dedup_out"), + abs_path("tests/tmp_tests_casr/dedup_diff"), ]; let _ = fs::remove_dir_all(&paths[1]); + let _ = fs::remove_dir_all(&paths[2]); let output = Command::new(*EXE_CASR_CLUSTER.read().unwrap()) .args(["-d", &paths[0], &paths[1]]) @@ -2648,7 +2650,24 @@ fn test_casr_cluster_d_and_m() { "Something went wrong while merging directories" ); - let _ = std::fs::remove_dir_all(&paths[1]); + // Test --diff option + dirvec = match fs::read_dir(&paths[1]) { + Ok(vec) => vec, + Err(why) => { + panic!("{:?}", why.kind()); + } + }; + let casrep = dirvec.next().unwrap().unwrap().path(); + let _ = std::fs::remove_file(casrep); + let output = Command::new(*EXE_CASR_CLUSTER.read().unwrap()) + .args(["--diff", &paths[0], &paths[1], &paths[2]]) + .output() + .expect("failed to start casr-cluster"); + let out = String::from_utf8_lossy(&output.stdout); + assert!( + out.contains("Diff of 1 new reports") && (fs::read_dir(&paths[2]).unwrap().count() == 1), + "Something went wrong while diffing directories" + ); } #[test] diff --git a/docs/usage.md b/docs/usage.md index b36e8d16..db1e7722 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -170,7 +170,7 @@ Run casr-java: ## casr-js -Create CASR reports (.casrep) from JavaScript reports +Create CASR reports (.casrep) from JavaScript crash reports Usage: casr-js [OPTIONS] <--stdout|--output > [-- ...] @@ -178,16 +178,16 @@ Create CASR reports (.casrep) from JavaScript reports [ARGS]... Add "-- " to run Options: - -o, --output Path to save report. Path can be a directory, then report - name is generated - --stdout Print CASR report to stdout - --stdin Stdin file for program - -t, --timeout Timeout (in seconds) for target execution, 0 value means - that timeout is disabled [default: 0] - --ignore File with regular expressions for functions and file paths - that should be ignored - -h, --help Print help - -V, --version Print version + -o, --output Path to save report. Path can be a directory, then report name + is generated + --stdout Print CASR report to stdout + --stdin Stdin file for program + -t, --timeout Timeout (in seconds) for target execution, 0 value means that + timeout is disabled [default: 0] + --ignore File with regular expressions for functions and file paths that + should be ignored + -h, --help Print help + -V, --version Print version Run casr-js: @@ -261,6 +261,9 @@ Tool for clustering CASR reports -m, --merge Merge INPUT_DIR into OUTPUT_DIR. Only new CASR reports from INPUT_DIR will be added to OUTPUT_DIR. + --diff + Compute report sets difference NEW_DIR \ PREV_DIR. Copy new CASR reports from + NEW_DIR into DIFF_DIR. --ignore File with regular expressions for functions and file paths that should be ignored