mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-22 02:01:55 +07:00
Add watcher tests
This commit is contained in:
Generated
+16
@@ -2119,6 +2119,7 @@ dependencies = [
|
|||||||
"tracy-client",
|
"tracy-client",
|
||||||
"url",
|
"url",
|
||||||
"xcursor",
|
"xcursor",
|
||||||
|
"xshell",
|
||||||
"zbus",
|
"zbus",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -4146,6 +4147,21 @@ version = "0.8.19"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a"
|
checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xshell"
|
||||||
|
version = "0.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ce2107fe03e558353b4c71ad7626d58ed82efaf56c54134228608893c77023ad"
|
||||||
|
dependencies = [
|
||||||
|
"xshell-macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xshell-macros"
|
||||||
|
version = "0.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7e2c411759b501fb9501aac2b1b2d287a6e93e5bdcf13c25306b23e1b716dd0e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yansi-term"
|
name = "yansi-term"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ features = [
|
|||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
proptest = "1.4.0"
|
proptest = "1.4.0"
|
||||||
proptest-derive = "0.4.0"
|
proptest-derive = "0.4.0"
|
||||||
|
xshell = "0.2.5"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["dbus", "xdp-gnome-screencast"]
|
default = ["dbus", "xdp-gnome-screencast"]
|
||||||
|
|||||||
+250
-1
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::{mpsc, Arc};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
@@ -20,6 +20,14 @@ impl Drop for Watcher {
|
|||||||
|
|
||||||
impl Watcher {
|
impl Watcher {
|
||||||
pub fn new(path: PathBuf, changed: SyncSender<()>) -> Self {
|
pub fn new(path: PathBuf, changed: SyncSender<()>) -> Self {
|
||||||
|
Self::with_start_notification(path, changed, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_start_notification(
|
||||||
|
path: PathBuf,
|
||||||
|
changed: SyncSender<()>,
|
||||||
|
started: Option<mpsc::SyncSender<()>>,
|
||||||
|
) -> Self {
|
||||||
let should_stop = Arc::new(AtomicBool::new(false));
|
let should_stop = Arc::new(AtomicBool::new(false));
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -40,6 +48,10 @@ impl Watcher {
|
|||||||
.and_then(|canon| Ok((canon.metadata()?.modified()?, canon)))
|
.and_then(|canon| Ok((canon.metadata()?.modified()?, canon)))
|
||||||
.ok();
|
.ok();
|
||||||
|
|
||||||
|
if let Some(started) = started {
|
||||||
|
let _ = started.send(());
|
||||||
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
thread::sleep(Duration::from_millis(500));
|
thread::sleep(Duration::from_millis(500));
|
||||||
|
|
||||||
@@ -72,3 +84,240 @@ impl Watcher {
|
|||||||
Self { should_stop }
|
Self { should_stop }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::sync::atomic::AtomicU8;
|
||||||
|
use std::time::SystemTime;
|
||||||
|
|
||||||
|
use calloop::channel::sync_channel;
|
||||||
|
use calloop::EventLoop;
|
||||||
|
use xshell::{cmd, Shell};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn check(
|
||||||
|
setup: impl FnOnce(&Shell) -> Result<(), Box<dyn Error>>,
|
||||||
|
change: impl FnOnce(&Shell) -> Result<(), Box<dyn Error>>,
|
||||||
|
) {
|
||||||
|
let sh = Shell::new().unwrap();
|
||||||
|
let temp_dir = sh.create_temp_dir().unwrap();
|
||||||
|
sh.change_dir(temp_dir.path());
|
||||||
|
// let dir = sh.create_dir("xshell").unwrap();
|
||||||
|
// sh.change_dir(dir);
|
||||||
|
|
||||||
|
let mut config_path = sh.current_dir();
|
||||||
|
config_path.push("niri");
|
||||||
|
config_path.push("config.kdl");
|
||||||
|
|
||||||
|
setup(&sh).unwrap();
|
||||||
|
|
||||||
|
let changed = AtomicU8::new(0);
|
||||||
|
|
||||||
|
let mut event_loop = EventLoop::try_new().unwrap();
|
||||||
|
let loop_handle = event_loop.handle();
|
||||||
|
|
||||||
|
let (tx, rx) = sync_channel(1);
|
||||||
|
let (started_tx, started_rx) = mpsc::sync_channel(1);
|
||||||
|
let _watcher = Watcher::with_start_notification(config_path.clone(), tx, Some(started_tx));
|
||||||
|
loop_handle
|
||||||
|
.insert_source(rx, |_, _, _| {
|
||||||
|
changed.fetch_add(1, Ordering::SeqCst);
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
started_rx.recv().unwrap();
|
||||||
|
|
||||||
|
// HACK: if we don't sleep, files might have the same mtime.
|
||||||
|
thread::sleep(Duration::from_millis(100));
|
||||||
|
|
||||||
|
change(&sh).unwrap();
|
||||||
|
|
||||||
|
event_loop
|
||||||
|
.dispatch(Duration::from_millis(750), &mut ())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(changed.load(Ordering::SeqCst), 1);
|
||||||
|
|
||||||
|
// Verify that the watcher didn't break.
|
||||||
|
sh.write_file(&config_path, "c").unwrap();
|
||||||
|
|
||||||
|
event_loop
|
||||||
|
.dispatch(Duration::from_millis(750), &mut ())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(changed.load(Ordering::SeqCst), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn change_file() {
|
||||||
|
check(
|
||||||
|
|sh| {
|
||||||
|
sh.write_file("niri/config.kdl", "a")?;
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
|sh| {
|
||||||
|
sh.write_file("niri/config.kdl", "b")?;
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn create_file() {
|
||||||
|
check(
|
||||||
|
|sh| {
|
||||||
|
sh.create_dir("niri")?;
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
|sh| {
|
||||||
|
sh.write_file("niri/config.kdl", "a")?;
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn create_dir_and_file() {
|
||||||
|
check(
|
||||||
|
|_sh| Ok(()),
|
||||||
|
|sh| {
|
||||||
|
sh.write_file("niri/config.kdl", "a")?;
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn change_linked_file() {
|
||||||
|
check(
|
||||||
|
|sh| {
|
||||||
|
sh.write_file("niri/config2.kdl", "a")?;
|
||||||
|
cmd!(sh, "ln -s config2.kdl niri/config.kdl").run()?;
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
|sh| {
|
||||||
|
sh.write_file("niri/config2.kdl", "b")?;
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn change_file_in_linked_dir() {
|
||||||
|
check(
|
||||||
|
|sh| {
|
||||||
|
sh.write_file("niri2/config.kdl", "a")?;
|
||||||
|
cmd!(sh, "ln -s niri2 niri").run()?;
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
|sh| {
|
||||||
|
sh.write_file("niri2/config.kdl", "b")?;
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn recreate_file() {
|
||||||
|
check(
|
||||||
|
|sh| {
|
||||||
|
sh.write_file("niri/config.kdl", "a")?;
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
|sh| {
|
||||||
|
sh.remove_path("niri/config.kdl")?;
|
||||||
|
sh.write_file("niri/config.kdl", "b")?;
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn recreate_dir() {
|
||||||
|
check(
|
||||||
|
|sh| {
|
||||||
|
sh.write_file("niri/config.kdl", "a")?;
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
|sh| {
|
||||||
|
sh.remove_path("niri")?;
|
||||||
|
sh.write_file("niri/config.kdl", "b")?;
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn swap_dir() {
|
||||||
|
check(
|
||||||
|
|sh| {
|
||||||
|
sh.write_file("niri/config.kdl", "a")?;
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
|sh| {
|
||||||
|
sh.write_file("niri2/config.kdl", "b")?;
|
||||||
|
sh.remove_path("niri")?;
|
||||||
|
cmd!(sh, "mv niri2 niri").run()?;
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn swap_just_link() {
|
||||||
|
// NixOS setup: link path changes, mtime stays constant.
|
||||||
|
check(
|
||||||
|
|sh| {
|
||||||
|
let mut dir = sh.current_dir();
|
||||||
|
dir.push("niri");
|
||||||
|
sh.create_dir(&dir)?;
|
||||||
|
|
||||||
|
let mut d2 = dir.clone();
|
||||||
|
d2.push("config2.kdl");
|
||||||
|
let mut c2 = File::create(d2).unwrap();
|
||||||
|
write!(c2, "a")?;
|
||||||
|
c2.flush()?;
|
||||||
|
c2.set_modified(SystemTime::UNIX_EPOCH)?;
|
||||||
|
c2.sync_all()?;
|
||||||
|
drop(c2);
|
||||||
|
|
||||||
|
let mut d3 = dir.clone();
|
||||||
|
d3.push("config3.kdl");
|
||||||
|
let mut c3 = File::create(d3).unwrap();
|
||||||
|
write!(c3, "b")?;
|
||||||
|
c3.flush()?;
|
||||||
|
c3.set_modified(SystemTime::UNIX_EPOCH)?;
|
||||||
|
c3.sync_all()?;
|
||||||
|
drop(c3);
|
||||||
|
|
||||||
|
cmd!(sh, "ln -s config2.kdl niri/config.kdl").run()?;
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
|sh| {
|
||||||
|
cmd!(sh, "unlink niri/config.kdl").run()?;
|
||||||
|
cmd!(sh, "ln -s config3.kdl niri/config.kdl").run()?;
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn swap_dir_link() {
|
||||||
|
check(
|
||||||
|
|sh| {
|
||||||
|
sh.write_file("niri2/config.kdl", "a")?;
|
||||||
|
cmd!(sh, "ln -s niri2 niri").run()?;
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
|sh| {
|
||||||
|
sh.write_file("niri3/config.kdl", "b")?;
|
||||||
|
cmd!(sh, "unlink niri").run()?;
|
||||||
|
cmd!(sh, "ln -s niri3 niri").run()?;
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user