From 2622dcf8e89b798f37414c12b4d12a358b6163fe Mon Sep 17 00:00:00 2001 From: Cosmic Horror Date: Sat, 22 Jul 2023 12:32:12 -0600 Subject: [PATCH] Retry watcher test with increased delays --- src/watcher/tests.rs | 87 +++++++++++++++++++++++++++++++------------- 1 file changed, 61 insertions(+), 26 deletions(-) diff --git a/src/watcher/tests.rs b/src/watcher/tests.rs index 10fc478c..9bb03bb9 100644 --- a/src/watcher/tests.rs +++ b/src/watcher/tests.rs @@ -12,33 +12,68 @@ impl Callback for mpsc::Sender<()> { } } -const DELAY: Duration = Duration::from_millis(100); -const LONG_TIMEOUT: Duration = Duration::from_millis(2_000); -const SHORT_TIMEOUT: Duration = Duration::from_millis(50); - -fn delay() { - std::thread::sleep(DELAY); -} - fn touch(file: &Path) { let now = filetime::FileTime::now(); filetime::set_file_mtime(file, now).unwrap(); } -#[track_caller] -fn assert_no_message(callback: &mpsc::Receiver<()>) { - assert!(callback.recv_timeout(SHORT_TIMEOUT).is_err()); +#[derive(Clone)] +struct Delays { + delay: Duration, + short_timeout: Duration, + long_timeout: Duration, } -#[track_caller] -fn assert_at_least_one_message(callback: &mpsc::Receiver<()>) { - assert!(callback.recv_timeout(LONG_TIMEOUT).is_ok()); - while callback.recv_timeout(SHORT_TIMEOUT).is_ok() {} +impl Delays { + fn new() -> Self { + Self { + delay: Duration::from_millis(100), + short_timeout: Duration::from_millis(50), + long_timeout: Duration::from_millis(2_000), + } + } + + fn increase_delays(&mut self) { + self.delay *= 2; + self.short_timeout *= 2; + self.long_timeout += Duration::from_millis(1); + } + + fn delay(&self) { + std::thread::sleep(self.delay); + } + + #[track_caller] + fn assert_no_message(&self, callback: &mpsc::Receiver<()>) { + assert!(callback.recv_timeout(self.short_timeout).is_err()); + } + + #[track_caller] + fn assert_at_least_one_message(&self, callback: &mpsc::Receiver<()>) { + assert!(callback.recv_timeout(self.long_timeout).is_ok()); + while callback.recv_timeout(self.short_timeout).is_ok() {} + } } -// Unfortunately this needs to be littered with sleeps/timeouts to work right :/ #[test] fn the_gauntlet() { + // This test can be flaky, so give it a few chances to succeed + let mut last_panic = None; + let mut delays = Delays::new(); + for _ in 0..3 { + let result = std::panic::catch_unwind(|| the_gauntlet_flaky(delays.clone())); + let Err(panic) = result else { + return; + }; + last_panic = Some(panic); + delays.increase_delays(); + } + + std::panic::resume_unwind(last_panic.unwrap()); +} + +// Unfortunately this needs to be littered with sleeps/timeouts to work right :/ +fn the_gauntlet_flaky(delays: Delays) { // Create our dummy test env let temp_dir = tempfile::Builder::new() .prefix("inlyne-tests-") @@ -58,30 +93,30 @@ fn the_gauntlet() { let watcher = Watcher::spawn_inner(callback_tx, main_file.clone()); // Give the watcher time to get comfy :) - delay(); + delays.delay(); // Sanity check watching touch(&main_file); - assert_at_least_one_message(&callback_rx); + delays.assert_at_least_one_message(&callback_rx); // Updating a file follows the new file and not the old one watcher.update_file(&rel_file, fs::read_to_string(&rel_file).unwrap()); - assert_at_least_one_message(&callback_rx); + delays.assert_at_least_one_message(&callback_rx); touch(&main_file); - assert_no_message(&callback_rx); + delays.assert_no_message(&callback_rx); touch(&rel_file); - assert_at_least_one_message(&callback_rx); + delays.assert_at_least_one_message(&callback_rx); // We can slowly swap out the file and it will only follow the file it's supposed to fs::rename(&rel_file, &swapped_out_file).unwrap(); touch(&swapped_out_file); - assert_no_message(&callback_rx); + delays.assert_no_message(&callback_rx); // The "slowly" part of this (give the watcher time to fail and start polling) - delay(); + delays.delay(); fs::rename(&swapped_in_file, &rel_file).unwrap(); - assert_at_least_one_message(&callback_rx); + delays.assert_at_least_one_message(&callback_rx); fs::remove_file(&swapped_out_file).unwrap(); - assert_no_message(&callback_rx); + delays.assert_no_message(&callback_rx); touch(&rel_file); - assert_at_least_one_message(&callback_rx); + delays.assert_at_least_one_message(&callback_rx); }