From 19fe0a0b5312805450f0ecaa7658d983a04a263b Mon Sep 17 00:00:00 2001 From: Pavel Kurdikov Date: Thu, 6 Nov 2025 14:59:31 +0300 Subject: [PATCH] fix: flaky PollWatcher tests --- notify/src/poll.rs | 21 ++++++++++++++++++ notify/src/test.rs | 54 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/notify/src/poll.rs b/notify/src/poll.rs index b30d04b1..a9fa4fec 100644 --- a/notify/src/poll.rs +++ b/notify/src/poll.rs @@ -700,7 +700,9 @@ mod tests { let path = tmpdir.path().join("entry"); std::fs::File::create_new(&path).expect("Unable to create"); + rx.sleep_until_parent_contains(&path); rx.sleep_until_exists(&path); + rx.wait_ordered_exact([expected(&path).create_any()]); } @@ -713,7 +715,9 @@ mod tests { let path = tmpdir.path().join("entry"); std::fs::create_dir(&path).expect("Unable to create"); + rx.sleep_until_parent_contains(&path); rx.sleep_until_exists(&path); + rx.wait_ordered_exact([expected(&path).create_any()]); } @@ -724,6 +728,8 @@ mod tests { let path = tmpdir.path().join("entry"); std::fs::File::create_new(&path).expect("Unable to create"); + rx.sleep_until_parent_contains(&path); + watcher.watch_recursively(&tmpdir); std::fs::write(&path, b"123").expect("Unable to write"); @@ -741,10 +747,15 @@ mod tests { let path = tmpdir.path().join("entry"); std::fs::File::create_new(&path).expect("Unable to create"); + rx.sleep_until_parent_contains(&path); + watcher.watch_recursively(&tmpdir); + std::fs::remove_file(&path).expect("Unable to remove"); rx.sleep_while_exists(&path); + rx.sleep_while_parent_contains(&path); + rx.wait_ordered_exact([expected(&path).remove_any()]); } @@ -756,12 +767,18 @@ mod tests { let new_path = tmpdir.path().join("new_entry"); std::fs::File::create_new(&path).expect("Unable to create"); + rx.sleep_until_parent_contains(&path); + watcher.watch_recursively(&tmpdir); + std::fs::rename(&path, &new_path).expect("Unable to remove"); rx.sleep_while_exists(&path); rx.sleep_until_exists(&new_path); + rx.sleep_while_parent_contains(&path); + rx.sleep_until_parent_contains(&new_path); + rx.wait_unordered_exact([ expected(&path).remove_any(), expected(&new_path).create_any(), @@ -776,6 +793,8 @@ mod tests { let overwriting_file = tmpdir.path().join("overwriting_file"); std::fs::write(&overwritten_file, "123").expect("write1"); + rx.sleep_until_parent_contains(&overwritten_file); + watcher.watch_nonrecursively(&tmpdir); std::fs::File::create(&overwriting_file).expect("create"); @@ -783,6 +802,8 @@ mod tests { std::fs::rename(&overwriting_file, &overwritten_file).expect("rename"); rx.sleep_while_exists(&overwriting_file); + rx.sleep_while_parent_contains(&overwriting_file); + assert!( rx.sleep_until( || std::fs::read_to_string(&overwritten_file).is_ok_and(|cnt| cnt == "321") diff --git a/notify/src/test.rs b/notify/src/test.rs index c51c848d..f56ad6b4 100644 --- a/notify/src/test.rs +++ b/notify/src/test.rs @@ -174,6 +174,60 @@ impl Receiver { self.timeout ) } + + /// Sleeps, while the parent directory of the provided path + /// does not contain the provided path in the [`std::fs::read_dir`] result. + /// + /// Errors will be ignored + /// + /// It is useful for the [`PollWatcher`], because on some file systems the directory + /// may contain a DirEntry after deletion + pub fn sleep_until_parent_contains(&self, path: impl AsRef) { + let path = path.as_ref(); + let parent = path + .parent() + .expect("The path {path:?} does not have a parent"); + + assert!( + self.sleep_until(|| { + std::fs::read_dir(parent) + .into_iter() + .flatten() + .flatten() + .any(|r| r.path() == path) + }), + "the path {parent:?} has not contained an expected entry {:?} after timeout {:?}", + path.file_name(), + self.timeout + ) + } + + /// Sleeps, while the parent directory of the provided path + /// contains the provided path in the [`std::fs::read_dir`] result. + /// + /// Errors will be ignored + /// + /// It is useful for the [`PollWatcher`], because on some file systems the directory + /// may contain a DirEntry after deletion + pub fn sleep_while_parent_contains(&self, path: impl AsRef) { + let path = path.as_ref(); + let parent = path + .parent() + .expect("The path {path:?} does not have a parent"); + + assert!( + self.sleep_until(|| { + !std::fs::read_dir(parent) + .into_iter() + .flatten() + .flatten() + .any(|r| r.path() == path) + }), + "the path {parent:?} has contained an expected entry {:?} yet after timeout {:?}", + path.file_name(), + self.timeout + ) + } } /// Result of a `wait` call on a [`Receiver`]