use std::os::unix::fs::{FileExt, OpenOptionsExt};
use std::time::{Duration, UNIX_EPOCH};
use tokio::time::sleep;
use turmoil::fs::shim::std::fs::{
create_dir, create_dir_all, exists, metadata, remove_file, symlink, symlink_metadata, write,
OpenOptions,
};
use turmoil::{Builder, Result};
#[test]
fn metadata_is_file_is_dir() -> Result {
let mut sim = Builder::new().build();
sim.client("test", async {
create_dir_all("/data")?;
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open("/data/file.txt")?;
let meta = file.metadata()?;
assert!(meta.is_file());
assert!(!meta.is_dir());
Ok(())
});
sim.run()
}
#[test]
fn metadata_free_function() -> Result {
let mut sim = Builder::new().build();
sim.client("test", async {
create_dir_all("/data")?;
write("/data/file.txt", b"hello world")?;
let file_meta = metadata("/data/file.txt")?;
assert!(file_meta.is_file());
assert_eq!(file_meta.len(), 11);
let dir_meta = metadata("/data")?;
assert!(dir_meta.is_dir());
let root_meta = metadata("/")?;
assert!(root_meta.is_dir());
assert!(metadata("/nonexistent").is_err());
Ok(())
});
sim.run()
}
#[test]
fn timestamp_on_creation() -> Result {
let mut sim = Builder::new().build();
sim.client("test", async {
create_dir("/data")?;
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open("/data/file.txt")?;
let meta = file.metadata()?;
let created = meta.created()?;
let modified = meta.modified()?;
assert!(created > UNIX_EPOCH);
assert_eq!(created, modified);
Ok(())
});
sim.run()
}
#[test]
fn mtime_updates_on_write() -> Result {
let mut sim = Builder::new().build();
sim.client("test", async {
create_dir("/data")?;
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open("/data/file.txt")?;
let initial = file.metadata()?.modified()?;
sleep(Duration::from_secs(1)).await;
file.write_all_at(b"hello", 0)?;
let after = file.metadata()?.modified()?;
assert!(after > initial);
Ok(())
});
sim.run()
}
#[test]
fn mtime_updates_on_truncate() -> Result {
let mut sim = Builder::new().build();
sim.client("test", async {
create_dir("/data")?;
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open("/data/file.txt")?;
file.write_all_at(b"hello world", 0)?;
file.sync_all()?;
let before = file.metadata()?.modified()?;
sleep(Duration::from_secs(1)).await;
file.set_len(5)?;
let after = file.metadata()?.modified()?;
assert!(after > before);
Ok(())
});
sim.run()
}
#[test]
fn noatime_behavior() -> Result {
let mut sim = Builder::new().build();
sim.client("test", async {
create_dir("/data")?;
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open("/data/file.txt")?;
file.write_all_at(b"hello", 0)?;
let before = file.metadata()?.accessed()?;
sleep(Duration::from_secs(1)).await;
let mut buf = [0u8; 5];
file.read_at(&mut buf, 0)?;
let after = file.metadata()?.accessed()?;
assert_eq!(before, after, "noatime: accessed should not change on read");
Ok(())
});
sim.run()
}
#[test]
fn directory_timestamps() -> Result {
let mut sim = Builder::new().build();
sim.client("test", async {
create_dir("/mydir")?;
let meta = metadata("/mydir")?;
assert!(meta.is_dir());
assert!(meta.created()? > UNIX_EPOCH);
sleep(Duration::from_secs(1)).await;
create_dir("/mydir/subdir")?;
let sub_meta = metadata("/mydir/subdir")?;
assert!(sub_meta.created()? > meta.created()?);
Ok(())
});
sim.run()
}
#[test]
fn dir_mtime_updates_on_content_change() -> Result {
let mut sim = Builder::new().build();
sim.client("test", async {
create_dir("/data")?;
let initial = metadata("/data")?.modified()?;
sleep(Duration::from_secs(1)).await;
write("/data/file.txt", b"hello")?;
let after_create = metadata("/data")?.modified()?;
assert!(after_create > initial);
sleep(Duration::from_secs(1)).await;
remove_file("/data/file.txt")?;
let after_remove = metadata("/data")?.modified()?;
assert!(after_remove >= after_create);
Ok(())
});
sim.run()
}
#[test]
fn permissions_from_metadata() -> Result {
let mut sim = Builder::new().build();
sim.client("test", async {
create_dir("/data")?;
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open("/data/default.txt")?;
let perms = file.metadata()?.permissions();
assert_eq!(perms.mode() & 0o777, 0o644);
let file2 = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.mode(0o600)
.open("/data/custom.txt")?;
let perms2 = file2.metadata()?.permissions();
assert_eq!(perms2.mode() & 0o777, 0o600);
let dir_meta = metadata("/data")?;
assert_eq!(dir_meta.permissions().mode() & 0o777, 0o755);
Ok(())
});
sim.run()
}
#[test]
fn exists_function() -> Result {
let mut sim = Builder::new().build();
sim.client("test", async {
assert!(!exists("/nonexistent"));
assert!(!exists("/data/file.txt"));
create_dir("/data")?;
assert!(exists("/data"));
write("/data/file.txt", b"hello")?;
assert!(exists("/data/file.txt"));
symlink("/data/file.txt", "/link")?;
assert!(exists("/link"));
symlink("/nonexistent", "/broken_link")?;
assert!(!exists("/broken_link"));
Ok(())
});
sim.run()
}
#[test]
fn symlink_metadata_timestamps() -> Result {
let mut sim = Builder::new().build();
sim.client("test", async {
write("/target.txt", b"hello")?;
sleep(Duration::from_secs(1)).await;
symlink("/target.txt", "/link")?;
let link_meta = symlink_metadata("/link")?;
assert!(link_meta.is_symlink());
assert!(!link_meta.is_file());
assert!(!link_meta.is_dir());
let link_created = link_meta.created()?;
assert!(link_created > UNIX_EPOCH);
let target_meta = metadata("/target.txt")?;
assert!(target_meta.created()? < link_created);
Ok(())
});
sim.run()
}