use anyhow::Result;
use fs_usage_sys::FsUsageMonitorBuilder;
use std::fs;
use std::path::PathBuf;
use std::thread;
use std::time::Duration;
#[test]
#[cfg(target_os = "macos")]
#[ignore = "requires sudo/root permissions to run fs_usage"]
fn test_captures_write_operations() -> Result<()> {
let test_dir = PathBuf::from("target/test_fs_events");
fs::create_dir_all(&test_dir)?;
let test_file = test_dir.join("test.txt");
let _ = fs::remove_file(&test_file);
println!("Test directory: {}", test_dir.display());
println!("Test file: {}", test_file.display());
let mut monitor = FsUsageMonitorBuilder::new()
.watch_path(test_dir.canonicalize()?.to_str().unwrap())
.exclude_process("mds")
.exclude_process("mdworker")
.exclude_process("Spotlight")
.exclude_process("fseventsd")
.build()?;
match monitor.start() {
Ok(_) => {}
Err(e) => {
let error_msg = e.to_string();
if error_msg.contains("Resource busy") || error_msg.contains("ktrace_start") {
eprintln!("Test skipped: Another fs_usage or ktrace process is already running");
return Ok(());
}
return Err(e);
}
}
let events = monitor.events();
println!("Waiting for monitor to start...");
thread::sleep(Duration::from_secs(3));
println!("Writing to file...");
fs::write(&test_file, "test content")?;
println!("First write completed");
thread::sleep(Duration::from_millis(500));
println!("Writing updated content...");
fs::write(&test_file, "updated content")?;
println!("Second write completed");
println!("Waiting for events to be captured...");
thread::sleep(Duration::from_secs(3));
let mut found_write = false;
let mut found_create = false;
let mut event_count = 0;
println!("\nCaptured events:");
while let Ok(event) = events.try_recv() {
event_count += 1;
println!(
" {} [{}] {} -> {}",
event.operation, event.process_name, event.path, event.result
);
if event.path.contains("test.txt") {
if event.operation.contains("write")
|| event.operation.contains("WrData")
|| event.operation.contains("WrMeta")
{
found_write = true;
println!(" -> Found write operation!");
}
if event.operation.contains("open") || event.operation.contains("creat") {
found_create = true;
println!(" -> Found create/open operation!");
}
}
}
monitor.stop()?;
println!("\nTotal events captured: {}", event_count);
println!("Found write operation: {}", found_write);
println!("Found create operation: {}", found_create);
let _ = fs::remove_file(&test_file);
if event_count == 0 {
eprintln!("WARNING: No events were captured. This might be due to:");
eprintln!(" - fs_usage needs time to start up");
eprintln!(" - The monitored path might not match exactly");
eprintln!(" - System permissions or security policies");
eprintln!("Consider running the test manually with: sudo cargo test test_captures_write_operations -- --ignored --nocapture");
}
assert!(
event_count > 0,
"No events were captured - fs_usage might need more startup time or path matching issues"
);
assert!(
found_create || found_write,
"Failed to capture any write or create operations for the test file"
);
Ok(())
}
#[test]
#[cfg(target_os = "macos")]
#[ignore = "requires sudo/root permissions to run fs_usage"]
fn test_write_only_filter() -> Result<()> {
let test_dir = PathBuf::from("target/test_fs_events_write");
fs::create_dir_all(&test_dir)?;
let test_file = test_dir.join("write_test.txt");
let _ = fs::remove_file(&test_file);
println!("Test directory: {}", test_dir.display());
println!("Test file: {}", test_file.display());
let mut monitor = FsUsageMonitorBuilder::new()
.watch_writes_only()
.watch_path(test_dir.canonicalize()?.to_str().unwrap())
.exclude_process("mds")
.exclude_process("mdworker")
.exclude_process("fseventsd")
.build()?;
match monitor.start() {
Ok(_) => {}
Err(e) => {
let error_msg = e.to_string();
if error_msg.contains("Resource busy") || error_msg.contains("ktrace_start") {
eprintln!("Test skipped: Another fs_usage or ktrace process is already running");
return Ok(());
}
return Err(e);
}
}
let events = monitor.events();
println!("Waiting for monitor to start...");
thread::sleep(Duration::from_secs(3));
println!("Writing initial content...");
fs::write(&test_file, "initial content")?;
thread::sleep(Duration::from_millis(500));
println!("Reading file (should not be captured)...");
let _ = fs::read(&test_file)?; thread::sleep(Duration::from_millis(500));
println!("Writing modified content...");
fs::write(&test_file, "modified content")?;
println!("Waiting for events to be captured...");
thread::sleep(Duration::from_secs(3));
let mut has_read_operation = false;
let mut has_write_operation = false;
while let Ok(event) = events.try_recv() {
if event.path.contains("write_test.txt") {
println!("Captured: {} [{}]", event.operation, event.process_name);
if event.operation.contains("read") || event.operation.contains("RdData") {
has_read_operation = true;
}
if event.operation.contains("write")
|| event.operation.contains("WrData")
|| event.operation.contains("open")
|| event.operation.contains("creat")
{
has_write_operation = true;
}
}
}
monitor.stop()?;
let _ = fs::remove_file(&test_file);
if !has_write_operation {
eprintln!("WARNING: No write operations were captured. This might be due to:");
eprintln!(" - fs_usage needs time to start up");
eprintln!(" - The monitored path might not match exactly");
eprintln!(" - System permissions or security policies");
}
assert!(
!has_read_operation,
"Read operations should not be captured with watch_writes_only()"
);
assert!(
has_write_operation,
"Write operations should be captured - fs_usage might need more startup time"
);
Ok(())
}