mod common;
use assert_fs::fixture::ChildPath;
use assert_fs::prelude::{FileTouch, FileWriteBin, PathChild};
use assert_fs::TempDir;
use common::*;
use std::env;
use std::fs::remove_file;
use std::path::PathBuf;
use std::thread::{self, sleep};
use std::time::Duration;
use xvc::error::Result;
use xvc::watch;
use xvc_core::util::xvcignore::COMMON_IGNORE_PATTERNS;
use xvc_core::XVCIGNORE_FILENAME;
use xvc_walker::notify::{make_polling_watcher, PathEvent};
use xvc_walker::{walk_serial, IgnoreRules, WalkOptions};
#[test]
fn test_notify() -> Result<()> {
let temp_dir = TempDir::new()?;
env::set_current_dir(&temp_dir)?;
watch!(temp_dir);
test_logging(log::LevelFilter::Trace);
let initial_rules = IgnoreRules::try_from_patterns(&temp_dir, COMMON_IGNORE_PATTERNS)?;
let walk_options = WalkOptions {
ignore_filename: Some(XVCIGNORE_FILENAME.to_string()),
include_dirs: true,
};
let (created_paths_snd, created_paths_rec) = crossbeam_channel::unbounded();
let (updated_paths_snd, _) = crossbeam_channel::unbounded();
let (deleted_paths_snd, deleted_paths_rec) = crossbeam_channel::unbounded();
let files: Vec<ChildPath> = (1..10)
.map(|n| temp_dir.child(format!("file-000{n}.bin")))
.collect();
let files_len = files.len();
files.iter().for_each(|f| {
watch!(f.path());
f.touch().unwrap();
});
const MAX_ERROR_COUNT: usize = 10;
let (output_sender, output_receiver) = crossbeam_channel::unbounded();
let (initial_paths, all_rules) =
walk_serial(&output_sender, initial_rules, &temp_dir, &walk_options)?;
watch!(all_rules);
assert!(output_receiver.is_empty());
let (watcher, receiver) = make_polling_watcher(all_rules)?;
watch!(watcher);
watch!(initial_paths.len());
let event_handler = thread::spawn(move || {
let mut err_counter = MAX_ERROR_COUNT;
loop {
watch!(receiver);
let r = receiver.try_recv();
watch!(r);
if let Ok(Some(pe)) = r {
err_counter = MAX_ERROR_COUNT;
match pe {
PathEvent::Create { path, .. } => {
created_paths_snd.send(path).unwrap();
}
PathEvent::Update { path, .. } => {
updated_paths_snd.send(path).unwrap();
}
PathEvent::Delete { path } => {
deleted_paths_snd.send(path).unwrap();
}
}
} else if err_counter > 0 {
err_counter -= 1;
} else {
break;
}
watch!(err_counter);
sleep(Duration::from_millis(
((MAX_ERROR_COUNT - err_counter) * 100) as u64,
));
}
watch!(err_counter);
watch!(receiver);
});
sleep(Duration::from_millis(3000));
let size_updated = 20;
files.iter().for_each(|f| {
watch!(f.path());
f.write_binary(&vec![0; size_updated]).unwrap();
});
sleep(Duration::from_millis(3000));
files.iter().for_each(|f| {
watch!(f.path());
remove_file(f.path()).unwrap();
});
sleep(Duration::from_millis(2000));
event_handler.join().unwrap();
{
let created_paths = created_paths_rec.iter().collect::<Vec<PathBuf>>();
watch!(created_paths);
let len = created_paths.len();
watch!(len);
assert!(len == files_len + 1);
}
{
let deleted_paths = deleted_paths_rec.iter().collect::<Vec<PathBuf>>();
watch!(deleted_paths);
assert_eq!(deleted_paths.len(), files_len);
}
Ok(())
}