API: Layer::stat(path, follow), Layer::readlink, Open::follow, Stat::symlink and Entry::symlink. Process::idle documents the shutdown order: abort, await the task, await idle, release locks.
93 lines
3.2 KiB
Rust
93 lines
3.2 KiB
Rust
//! Regression tests for review findings about memory files and process shutdown.
|
|
use sausage_vfs::*;
|
|
use std::{
|
|
io::ErrorKind,
|
|
time::{Duration, SystemTime},
|
|
};
|
|
fn rw() -> Open {
|
|
Open {
|
|
write: true,
|
|
create: true,
|
|
..Open::default()
|
|
}
|
|
}
|
|
/// A capacity overflow returns an error without poisoning the shared memory tree.
|
|
#[tokio::test]
|
|
async fn memory_capacity_failure() {
|
|
let memory = Memory::default();
|
|
let file = memory.open("/file", rw()).await.unwrap();
|
|
let copy = file.clone();
|
|
let result =
|
|
tokio::spawn(async move { copy.write(i64::MAX as u64 - 10, vec![0; 32]).await }).await;
|
|
assert_eq!(result.unwrap().unwrap_err().kind(), ErrorKind::FileTooLarge);
|
|
assert_eq!(
|
|
file.write(1u64 << 62, vec![1]).await.unwrap_err().kind(),
|
|
ErrorKind::FileTooLarge
|
|
);
|
|
assert_eq!(
|
|
file.set_size(1u64 << 62).await.unwrap_err().kind(),
|
|
ErrorKind::FileTooLarge
|
|
);
|
|
file.write(0, b"usable".to_vec()).await.unwrap();
|
|
assert_eq!(memory.stat("/file", true).await.unwrap().size, 6);
|
|
memory.mkdir("/still-usable").await.unwrap();
|
|
}
|
|
/// Memory metadata distinguishes a missing parent from a regular-file parent.
|
|
#[tokio::test]
|
|
async fn memory_parent_errors() {
|
|
let memory = Memory::default();
|
|
memory.open("/file", rw()).await.unwrap();
|
|
assert_eq!(
|
|
memory.stat("/file/child", true).await.unwrap_err().kind(),
|
|
ErrorKind::NotADirectory
|
|
);
|
|
assert_eq!(
|
|
memory
|
|
.open("/file/child", Open::default())
|
|
.await
|
|
.err()
|
|
.unwrap()
|
|
.kind(),
|
|
ErrorKind::NotADirectory
|
|
);
|
|
}
|
|
/// Memory writes update modification time and read handles can set timestamps.
|
|
#[tokio::test]
|
|
async fn memory_timestamps() {
|
|
let memory = Memory::default();
|
|
let file = memory.open("/file", rw()).await.unwrap();
|
|
file.write(0, vec![1]).await.unwrap();
|
|
assert!(file.stat().await.unwrap().modified.is_some());
|
|
let read = memory.open("/file", Open::default()).await.unwrap();
|
|
let time = SystemTime::UNIX_EPOCH + Duration::from_secs(123);
|
|
read.set_times(None, Some(time)).await.unwrap();
|
|
assert_eq!(read.stat().await.unwrap().modified, Some(time));
|
|
}
|
|
/// Awaiting an aborted writer before idle prevents writes after lock release.
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn abort_join_then_idle() {
|
|
let temp = tempfile::tempdir().unwrap();
|
|
let process = Process::default();
|
|
let host = Host::new(temp.path(), process.clone()).unwrap();
|
|
let file = host.open("/file", rw()).await.unwrap();
|
|
let (started_tx, started_rx) = tokio::sync::oneshot::channel();
|
|
let copy = file.clone();
|
|
let task = tokio::spawn(async move {
|
|
copy.append(vec![1]).await.unwrap();
|
|
started_tx.send(()).unwrap();
|
|
loop {
|
|
copy.append(vec![1]).await.unwrap();
|
|
}
|
|
});
|
|
started_rx.await.unwrap();
|
|
task.abort();
|
|
assert!(task.await.unwrap_err().is_cancelled());
|
|
process.idle().await;
|
|
let before = file.stat().await.unwrap().size;
|
|
assert!(before >= 1);
|
|
for _ in 0..100 {
|
|
tokio::task::yield_now().await;
|
|
assert_eq!(file.stat().await.unwrap().size, before);
|
|
}
|
|
}
|