//! 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); } }