diff --git a/src/find/mod.rs b/src/find/mod.rs index f3008faf..08ccc05b 100644 --- a/src/find/mod.rs +++ b/src/find/mod.rs @@ -179,6 +179,7 @@ fn process_dir( // As WalkDir seems not providing a function to check its stack, // using current_dir is a workaround to check leaving directory. let mut current_dir: Option = None; + let mut depth = 0; while let Some(result) = it.next() { match WalkEntry::from_walkdir(result, config.follow) { Err(err) => { @@ -188,11 +189,16 @@ fn process_dir( Ok(entry) => { let mut matcher_io = matchers::MatcherIO::new(deps); - let new_dir = entry.path().parent().map(|x| x.to_path_buf()); - if new_dir != current_dir { + let new_dir = entry + .path() + .parent() + .or_else(|| Some(entry.path())) + .map(|x| x.to_path_buf()); + if entry.depth() != depth || new_dir != current_dir { if let Some(dir) = current_dir.take() { matcher.finished_dir(dir.as_path(), &mut matcher_io); } + depth = entry.depth(); current_dir = new_dir; } @@ -327,6 +333,7 @@ pub fn find_main(args: &[&str], deps: &dyn Dependencies) -> i32 { #[cfg(test)] mod tests { + use std::cell::Cell; use std::fs; use std::io::{Cursor, ErrorKind, Read}; use std::time::Duration; @@ -341,6 +348,7 @@ mod tests { use crate::find::matchers::time::ChangeTime; use crate::find::matchers::MatcherIO; + use super::matchers::Matcher; use super::*; #[cfg(windows)] @@ -397,6 +405,37 @@ mod tests { } } + // A fake matcher struct that records finished and finished_dir arguments. + pub struct FakeMatcher { + pub finished_dir_result: RefCell>, + pub finished_result: Cell, + } + + impl FakeMatcher { + pub fn new() -> Self { + Self { + finished_dir_result: RefCell::new(Vec::new()), + finished_result: Cell::new(0), + } + } + } + + impl Matcher for FakeMatcher { + fn matches(&self, _: &WalkEntry, _: &mut MatcherIO) -> bool { + true + } + + fn finished_dir(&self, finished_directory: &std::path::Path, _: &mut MatcherIO) { + self.finished_dir_result + .borrow_mut() + .push(finished_directory.to_path_buf()); + } + + fn finished(&self, _: &mut MatcherIO) { + self.finished_result.set(self.finished_result.get() + 1); + } + } + fn create_file_link() { #[cfg(unix)] if let Err(e) = symlink("abbbc", "test_data/links/link-f") { @@ -1534,4 +1573,22 @@ mod tests { assert_eq!(rc, 0); } + + #[test] + fn test_finished_in_root_directory() { + let config = Config { + max_depth: 1, + ..Default::default() + }; + let deps = FakeDependencies::new(); + let mut quit = false; + let matcher = FakeMatcher::new(); + process_dir("/", &config, &deps, &matcher, &mut quit); + + let dir_result = matcher.finished_dir_result.borrow(); + assert_eq!(dir_result.len(), 2); + assert_eq!(dir_result[0], PathBuf::from("/")); + assert_eq!(dir_result[1], PathBuf::from("/")); + assert_eq!(matcher.finished_result.get(), 1); + } } diff --git a/tests/find_exec_tests.rs b/tests/find_exec_tests.rs index 510a6939..5629f86f 100644 --- a/tests/find_exec_tests.rs +++ b/tests/find_exec_tests.rs @@ -208,3 +208,57 @@ fn find_execdir_multi() { )) ); } + +#[test] +fn find_execdir_multi_in_root_directory() { + let temp_dir = Builder::new() + .prefix("find_execdir_multi_in_root_directory") + .tempdir() + .unwrap(); + let temp_dir_path = temp_dir.path().to_string_lossy(); + let deps = FakeDependencies::new(); + + let cwd = env::current_dir().expect("no current directory"); + let root_dir = cwd + .ancestors() + .last() + .expect("current directory has no root"); + + // only look at files because the "size" of a directory is a system (and filesystem) + // dependent thing and we want these tests to be universal. + let rc = find_main( + &[ + "find", + &fix_up_slashes(&root_dir.to_string_lossy()), + "-maxdepth", + "0", + "-execdir", + &path_to_testing_commandline(), + temp_dir_path.as_ref(), + "--sort", + ")", + "{}", + "+", + ], + &deps, + ); + + assert_eq!(rc, 0); + // exec has side effects, so we won't output anything unless -print is + // explicitly passed in. + assert_eq!(deps.get_output_as_string(), ""); + + // check the executable ran as expected + let mut f = File::open(temp_dir.path().join("1.txt")).expect("Failed to open output file"); + let mut s = String::new(); + f.read_to_string(&mut s) + .expect("failed to read output file"); + assert_eq!( + s, + fix_up_slashes(&format!( + "cwd={}\nargs=\n)\n--sort\n{}\n", + root_dir.to_string_lossy(), + root_dir.to_string_lossy(), + )) + ); +}