diff --git a/axfs_devfs/src/dir.rs b/axfs_devfs/src/dir.rs index 69a6b83..2086a4e 100644 --- a/axfs_devfs/src/dir.rs +++ b/axfs_devfs/src/dir.rs @@ -88,7 +88,7 @@ impl VfsNodeOps for DirNode { } fn create(&self, path: &str, ty: VfsNodeType) -> VfsResult { - log::debug!("create {:?} at devfs: {}", ty, path); + log::debug!("create {ty:?} at devfs: {path}"); let (name, rest) = split_path(path); if let Some(rest) = rest { match name { @@ -109,7 +109,7 @@ impl VfsNodeOps for DirNode { } fn remove(&self, path: &str) -> VfsResult { - log::debug!("remove at devfs: {}", path); + log::debug!("remove at devfs: {path}"); let (name, rest) = split_path(path); if let Some(rest) = rest { match name { diff --git a/axfs_devfs/src/lib.rs b/axfs_devfs/src/lib.rs index 1e91629..08c08d6 100644 --- a/axfs_devfs/src/lib.rs +++ b/axfs_devfs/src/lib.rs @@ -10,9 +10,6 @@ mod dir; mod null; mod zero; -#[cfg(test)] -mod tests; - pub use self::dir::DirNode; pub use self::null::NullDev; pub use self::zero::ZeroDev; diff --git a/axfs_devfs/src/tests.rs b/axfs_devfs/src/tests.rs deleted file mode 100644 index cfc6e9f..0000000 --- a/axfs_devfs/src/tests.rs +++ /dev/null @@ -1,115 +0,0 @@ -use std::sync::Arc; - -use axfs_vfs::{VfsError, VfsNodeType, VfsResult}; - -use crate::*; - -fn test_devfs_ops(devfs: &DeviceFileSystem) -> VfsResult { - const N: usize = 32; - let mut buf = [1; N]; - - let root = devfs.root_dir(); - assert!(root.get_attr()?.is_dir()); - assert_eq!(root.get_attr()?.file_type(), VfsNodeType::Dir); - assert_eq!( - root.clone().lookup("urandom").err(), - Some(VfsError::NotFound) - ); - assert_eq!( - root.clone().lookup("zero/").err(), - Some(VfsError::NotADirectory) - ); - - let node = root.lookup("////null")?; - assert_eq!(node.get_attr()?.file_type(), VfsNodeType::CharDevice); - assert!(!node.get_attr()?.is_dir()); - assert_eq!(node.get_attr()?.size(), 0); - assert_eq!(node.read_at(0, &mut buf)?, 0); - assert_eq!(buf, [1; N]); - assert_eq!(node.write_at(N as _, &buf)?, N); - assert_eq!(node.lookup("/").err(), Some(VfsError::NotADirectory)); - - let node = devfs.root_dir().lookup(".///.//././/.////zero")?; - assert_eq!(node.get_attr()?.file_type(), VfsNodeType::CharDevice); - assert!(!node.get_attr()?.is_dir()); - assert_eq!(node.get_attr()?.size(), 0); - assert_eq!(node.read_at(10, &mut buf)?, N); - assert_eq!(buf, [0; N]); - assert_eq!(node.write_at(0, &buf)?, N); - - let foo = devfs.root_dir().lookup(".///.//././/.////foo")?; - assert!(foo.get_attr()?.is_dir()); - assert_eq!( - foo.read_at(10, &mut buf).err(), - Some(VfsError::IsADirectory) - ); - assert!(Arc::ptr_eq( - &foo.clone().lookup("/f2")?, - &devfs.root_dir().lookup(".//./foo///f2")?, - )); - assert_eq!( - foo.clone().lookup("/bar//f1")?.get_attr()?.file_type(), - VfsNodeType::CharDevice - ); - assert_eq!( - foo.lookup("/bar///")?.get_attr()?.file_type(), - VfsNodeType::Dir - ); - - Ok(()) -} - -fn test_get_parent(devfs: &DeviceFileSystem) -> VfsResult { - let root = devfs.root_dir(); - assert!(root.parent().is_none()); - - let node = root.clone().lookup("null")?; - assert!(node.parent().is_none()); - - let node = root.clone().lookup(".//foo/bar")?; - assert!(node.parent().is_some()); - let parent = node.parent().unwrap(); - assert!(Arc::ptr_eq(&parent, &root.clone().lookup("foo")?)); - assert!(parent.lookup("bar").is_ok()); - - let node = root.clone().lookup("foo/..")?; - assert!(Arc::ptr_eq(&node, &root.clone().lookup(".")?)); - - assert!(Arc::ptr_eq( - &root.clone().lookup("/foo/..")?, - &devfs.root_dir().lookup(".//./foo/././bar/../..")?, - )); - assert!(Arc::ptr_eq( - &root.clone().lookup("././/foo//./../foo//bar///..//././")?, - &devfs.root_dir().lookup(".//./foo/")?, - )); - assert!(Arc::ptr_eq( - &root.clone().lookup("///foo//bar///../f2")?, - &root.lookup("foo/.//f2")?, - )); - - Ok(()) -} - -#[test] -fn test_devfs() { - // . - // ├── foo - // │   ├── bar - // │   │   └── f1 (null) - // │   └── f2 (zero) - // ├── null - // └── zero - - let devfs = DeviceFileSystem::new(); - devfs.add("null", Arc::new(NullDev)); - devfs.add("zero", Arc::new(ZeroDev)); - - let dir_foo = devfs.mkdir("foo"); - dir_foo.add("f2", Arc::new(ZeroDev)); - let dir_bar = dir_foo.mkdir("bar"); - dir_bar.add("f1", Arc::new(NullDev)); - - test_devfs_ops(&devfs).unwrap(); - test_get_parent(&devfs).unwrap(); -} diff --git a/axfs_devfs/tests/test_axfs_devfs.rs b/axfs_devfs/tests/test_axfs_devfs.rs new file mode 100644 index 0000000..7b72d8c --- /dev/null +++ b/axfs_devfs/tests/test_axfs_devfs.rs @@ -0,0 +1,182 @@ +// In tests/test_axfs_devfs.rs + +// 1. Import dependencies as if this were an external crate. +// - `axfs_devfs` is the name of the crate we are testing. +// - `axfs_vfs` is a regular dependency. +use axfs_devfs::{DeviceFileSystem, NullDev, ZeroDev}; +use axfs_vfs::{VfsError, VfsNodeType, VfsOps}; +use std::sync::Arc; + +// 2. Helper function to build a complex filesystem instance for use in multiple test scenarios. +// . +// ├── foo +// │ ├── bar +// │ │ └── f1 (null) +// │ └── f2 (zero) +// ├── null +// └── zero +fn setup_complex_devfs() -> DeviceFileSystem { + let devfs = DeviceFileSystem::new(); + devfs.add("null", Arc::new(NullDev)); + devfs.add("zero", Arc::new(ZeroDev)); + + let dir_foo = devfs.mkdir("foo"); + dir_foo.add("f2", Arc::new(ZeroDev)); + let dir_bar = dir_foo.mkdir("bar"); + dir_bar.add("f1", Arc::new(NullDev)); + devfs +} + +// Scene 1: Test basic I/O operations and attributes of device nodes. +#[test] +fn test_root_device_operations() { + let devfs = setup_complex_devfs(); + let root = devfs.root_dir(); + + // --- Verify root directory attributes --- + assert!(root.get_attr().unwrap().is_dir()); + assert_eq!(root.get_attr().unwrap().file_type(), VfsNodeType::Dir); + + // --- Verify lookup of a non-existent node --- + assert_eq!( + root.clone().lookup("non_existent_file").err(), + Some(VfsError::NotFound) + ); + + // --- Test the /null device --- + let null_dev = root.clone().lookup("null").unwrap(); + assert_eq!( + null_dev.get_attr().unwrap().file_type(), + VfsNodeType::CharDevice + ); + assert!(!null_dev.get_attr().unwrap().is_dir()); + assert_eq!(null_dev.get_attr().unwrap().size(), 0); + + let mut buffer = [42u8; 16]; + assert_eq!( + null_dev.read_at(0, &mut buffer).unwrap(), + 0, + "Reading from null should return 0 bytes" + ); + assert_eq!( + buffer, [42u8; 16], + "Buffer should be unchanged after reading from null" + ); + assert_eq!( + null_dev.write_at(0, &[1, 2, 3]).unwrap(), + 3, + "Writing to null should succeed" + ); + + // --- Test the /zero device --- + let zero_dev = root.lookup("zero").unwrap(); + assert_eq!( + zero_dev.get_attr().unwrap().file_type(), + VfsNodeType::CharDevice + ); + + let mut buffer_for_zero = [42u8; 16]; + assert_eq!( + zero_dev.read_at(0, &mut buffer_for_zero).unwrap(), + buffer_for_zero.len() + ); + assert_eq!( + buffer_for_zero, [0u8; 16], + "Buffer should be filled with zeros after reading from zero" + ); + assert_eq!( + zero_dev.write_at(0, &[1, 2, 3]).unwrap(), + 3, + "Writing to zero should succeed" + ); +} + +// Scene 2: Test complex path traversal, parent directory lookup, and node identity. +#[test] +fn test_complex_path_traversal_and_parent_lookup() { + let devfs = setup_complex_devfs(); + let root = devfs.root_dir(); + let foo = root.clone().lookup("foo").unwrap(); + + // --- Perform strict node identity checks using Arc::ptr_eq --- + assert!(Arc::ptr_eq( + &foo.clone().lookup("f2").unwrap(), // Note: lookup from `foo`, not `/f2` + &root.clone().lookup(".//./foo///f2").unwrap(), + )); + assert!(Arc::ptr_eq( + &root.clone().lookup("foo/..").unwrap(), // Note: `lookup` handles `..` + &root.clone().lookup(".//./foo/././bar/../..").unwrap(), + )); + assert!(Arc::ptr_eq( + &root + .clone() + .lookup("././/foo//./../foo//bar///..//././") + .unwrap(), + &root.clone().lookup(".//./foo/").unwrap(), + )); + assert!(Arc::ptr_eq( + &root.clone().lookup("///foo//bar///../f2").unwrap(), + &root.clone().lookup("foo/.//f2").unwrap(), + )); + + // --- In-depth test of parent directory lookup (`..` and .parent()) --- + assert!(root.parent().is_none(), "Root should not have a parent"); + + let bar = root.clone().lookup("foo/bar").unwrap(); + let parent_of_bar = bar.parent().unwrap(); + assert!( + Arc::ptr_eq(&parent_of_bar, &foo), + "Parent of 'bar' should be 'foo'" + ); + + let foo_from_bar = bar.lookup("..").unwrap(); + assert!( + Arc::ptr_eq(&foo_from_bar, &foo), + "Lookup of '..' from 'bar' should return 'foo'" + ); +} + +// Scene 3: Verify various expected error conditions. +#[test] +fn test_error_conditions() { + let devfs = setup_complex_devfs(); + let root = devfs.root_dir(); + let null_dev = root.clone().lookup("null").unwrap(); + let foo_dir = root.clone().lookup("foo").unwrap(); + + // --- Perform directory operations on a file node --- + assert_eq!( + null_dev.lookup("any").err(), + Some(VfsError::NotADirectory), + "Lookup on a file node should fail" + ); + assert_eq!( + root.clone().lookup("null/some_file").err(), + Some(VfsError::NotADirectory), + "Path traversal through a file node should fail" + ); + + // --- Perform file operations on a directory node --- + assert_eq!( + foo_dir.read_at(0, &mut [0; 1]).err(), + Some(VfsError::IsADirectory), + "Reading a directory should fail" + ); + assert_eq!( + foo_dir.write_at(0, &[0; 1]).err(), + Some(VfsError::IsADirectory), + "Writing to a directory should fail" + ); + + // --- Verify the read-only nature of devfs (no runtime modifications) --- + assert_eq!( + root.clone().create("new_file", VfsNodeType::File).err(), + Some(VfsError::PermissionDenied), + "Creating files at runtime should be denied" + ); + assert_eq!( + root.remove("null").err(), + Some(VfsError::PermissionDenied), + "Removing nodes at runtime should be denied" + ); +} diff --git a/axfs_ramfs/src/dir.rs b/axfs_ramfs/src/dir.rs index b64707e..58f1723 100644 --- a/axfs_ramfs/src/dir.rs +++ b/axfs_ramfs/src/dir.rs @@ -43,7 +43,7 @@ impl DirNode { /// Creates a new node with the given name and type in this directory. pub fn create_node(&self, name: &str, ty: VfsNodeType) -> VfsResult { if self.exist(name) { - log::error!("AlreadyExists {}", name); + log::error!("AlreadyExists {name}"); return Err(VfsError::AlreadyExists); } let node: VfsNodeRef = match ty { @@ -118,7 +118,7 @@ impl VfsNodeOps for DirNode { } fn create(&self, path: &str, ty: VfsNodeType) -> VfsResult { - log::debug!("create {:?} at ramfs: {}", ty, path); + log::debug!("create {ty:?} at ramfs: {path}"); let (name, rest) = split_path(path); if let Some(rest) = rest { match name { @@ -142,7 +142,7 @@ impl VfsNodeOps for DirNode { } fn remove(&self, path: &str) -> VfsResult { - log::debug!("remove at ramfs: {}", path); + log::debug!("remove at ramfs: {path}"); let (name, rest) = split_path(path); if let Some(rest) = rest { match name { diff --git a/axfs_ramfs/src/lib.rs b/axfs_ramfs/src/lib.rs index 44bc1f3..e8937f9 100644 --- a/axfs_ramfs/src/lib.rs +++ b/axfs_ramfs/src/lib.rs @@ -9,9 +9,6 @@ extern crate alloc; mod dir; mod file; -#[cfg(test)] -mod tests; - pub use self::dir::DirNode; pub use self::file::FileNode; diff --git a/axfs_ramfs/src/tests.rs b/axfs_ramfs/src/tests.rs deleted file mode 100644 index fda7001..0000000 --- a/axfs_ramfs/src/tests.rs +++ /dev/null @@ -1,136 +0,0 @@ -use std::sync::Arc; - -use axfs_vfs::{VfsError, VfsNodeType, VfsResult}; - -use crate::*; - -fn test_ramfs_ops(devfs: &RamFileSystem) -> VfsResult { - const N: usize = 32; - const N_HALF: usize = N / 2; - let mut buf = [1; N]; - - let root = devfs.root_dir(); - assert!(root.get_attr()?.is_dir()); - assert_eq!(root.get_attr()?.file_type(), VfsNodeType::Dir); - assert_eq!( - root.clone().lookup("urandom").err(), - Some(VfsError::NotFound) - ); - assert_eq!( - root.clone().lookup("f1/").err(), - Some(VfsError::NotADirectory) - ); - - let node = root.lookup("////f1")?; - assert_eq!(node.get_attr()?.file_type(), VfsNodeType::File); - assert!(!node.get_attr()?.is_dir()); - assert_eq!(node.get_attr()?.size(), 0); - assert_eq!(node.read_at(0, &mut buf)?, 0); - assert_eq!(buf, [1; N]); - - assert_eq!(node.write_at(N_HALF as _, &buf[..N_HALF])?, N_HALF); - assert_eq!(node.read_at(0, &mut buf)?, N); - assert_eq!(buf[..N_HALF], [0; N_HALF]); - assert_eq!(buf[N_HALF..], [1; N_HALF]); - assert_eq!(node.lookup("/").err(), Some(VfsError::NotADirectory)); - - let foo = devfs.root_dir().lookup(".///.//././/.////foo")?; - assert!(foo.get_attr()?.is_dir()); - assert_eq!( - foo.read_at(10, &mut buf).err(), - Some(VfsError::IsADirectory) - ); - assert!(Arc::ptr_eq( - &foo.clone().lookup("/f3")?, - &devfs.root_dir().lookup(".//./foo///f3")?, - )); - assert_eq!( - foo.clone().lookup("/bar//f4")?.get_attr()?.file_type(), - VfsNodeType::File - ); - assert_eq!( - foo.lookup("/bar///")?.get_attr()?.file_type(), - VfsNodeType::Dir - ); - - Ok(()) -} - -fn test_get_parent(devfs: &RamFileSystem) -> VfsResult { - let root = devfs.root_dir(); - assert!(root.parent().is_none()); - - let node = root.clone().lookup("f1")?; - assert!(node.parent().is_none()); - - let node = root.clone().lookup(".//foo/bar")?; - assert!(node.parent().is_some()); - let parent = node.parent().unwrap(); - assert!(Arc::ptr_eq(&parent, &root.clone().lookup("foo")?)); - assert!(parent.lookup("bar").is_ok()); - - let node = root.clone().lookup("foo/..")?; - assert!(Arc::ptr_eq(&node, &root.clone().lookup(".")?)); - - assert!(Arc::ptr_eq( - &root.clone().lookup("/foo/..")?, - &devfs.root_dir().lookup(".//./foo/././bar/../..")?, - )); - assert!(Arc::ptr_eq( - &root.clone().lookup("././/foo//./../foo//bar///..//././")?, - &devfs.root_dir().lookup(".//./foo/")?, - )); - assert!(Arc::ptr_eq( - &root.clone().lookup("///foo//bar///../f3")?, - &root.lookup("foo/.//f3")?, - )); - - Ok(()) -} - -#[test] -fn test_ramfs() { - // . - // ├── foo - // │   ├── bar - // │   │   └── f4 - // │   └── f3 - // ├── f1 - // └── f2 - - let ramfs = RamFileSystem::new(); - let root = ramfs.root_dir(); - root.create("f1", VfsNodeType::File).unwrap(); - root.create("f2", VfsNodeType::File).unwrap(); - root.create("foo", VfsNodeType::Dir).unwrap(); - - let dir_foo = root.lookup("foo").unwrap(); - dir_foo.create("f3", VfsNodeType::File).unwrap(); - dir_foo.create("bar", VfsNodeType::Dir).unwrap(); - - let dir_bar = dir_foo.lookup("bar").unwrap(); - dir_bar.create("f4", VfsNodeType::File).unwrap(); - - let mut entries = ramfs.root_dir_node().get_entries(); - entries.sort(); - assert_eq!(entries, ["f1", "f2", "foo"]); - - test_ramfs_ops(&ramfs).unwrap(); - test_get_parent(&ramfs).unwrap(); - - let root = ramfs.root_dir(); - assert_eq!(root.remove("f1"), Ok(())); - assert_eq!(root.remove("//f2"), Ok(())); - assert_eq!(root.remove("f3").err(), Some(VfsError::NotFound)); - assert_eq!(root.remove("foo").err(), Some(VfsError::DirectoryNotEmpty)); - assert_eq!(root.remove("foo/..").err(), Some(VfsError::InvalidInput)); - assert_eq!( - root.remove("foo/./bar").err(), - Some(VfsError::DirectoryNotEmpty) - ); - assert_eq!(root.remove("foo/bar/f4"), Ok(())); - assert_eq!(root.remove("foo/bar"), Ok(())); - assert_eq!(root.remove("./foo//.//f3"), Ok(())); - assert_eq!(root.remove("./foo"), Ok(())); - assert!(ramfs.root_dir_node().get_entries().is_empty()); -} diff --git a/axfs_ramfs/tests/test_axfs_ramfs.rs b/axfs_ramfs/tests/test_axfs_ramfs.rs new file mode 100644 index 0000000..1dc8096 --- /dev/null +++ b/axfs_ramfs/tests/test_axfs_ramfs.rs @@ -0,0 +1,182 @@ +// In tests/test_axfs_ramfs.rs + +use std::sync::Arc; + +// FIX 1: Removed `RamDir` as it's not a public type. +use axfs_ramfs::RamFileSystem; +// FIX 2: Removed `VfsNodeOps` as it's an unused import according to the compiler. +use axfs_vfs::{VfsError, VfsNodeType, VfsOps, VfsResult}; + +// ================================================================= +// Helper Functions +// ================================================================= + +/// Helper: Creates a standard filesystem structure for multiple tests. +fn setup_complex_fs() -> RamFileSystem { + let ramfs = RamFileSystem::new(); + let root = ramfs.root_dir(); + + root.clone().create("file.txt", VfsNodeType::File).unwrap(); + root.clone().create("dir1", VfsNodeType::Dir).unwrap(); + + let dir1 = root.lookup("dir1").unwrap(); + dir1.clone().create("dir2", VfsNodeType::Dir).unwrap(); + + let dir2 = dir1.lookup("dir2").unwrap(); + dir2.create("nested_file.txt", VfsNodeType::File).unwrap(); + + ramfs +} + +/// Helper: Tests basic filesystem operations. +fn run_basic_ops_tests(ramfs: &RamFileSystem) -> VfsResult<()> { + const N: usize = 32; + const N_HALF: usize = N / 2; + let mut buf = [1; N]; + + let root = ramfs.root_dir(); + let f1 = root.lookup("f1")?; + + assert_eq!(f1.clone().get_attr()?.file_type(), VfsNodeType::File); + assert_eq!(f1.clone().get_attr()?.size(), 0); + + assert_eq!(f1.clone().write_at(N_HALF as u64, &buf[..N_HALF])?, N_HALF); + assert_eq!(f1.clone().get_attr()?.size(), N as u64); + assert_eq!(f1.read_at(0, &mut buf)?, N); + assert_eq!(buf[..N_HALF], [0; N_HALF]); + assert_eq!(buf[N_HALF..], [1; N_HALF]); + Ok(()) +} + +/// Helper: Tests parent directory and path resolution. +fn run_parent_and_path_tests(ramfs: &RamFileSystem) -> VfsResult<()> { + let root = ramfs.root_dir(); + let foo = root.clone().lookup("foo")?; + let bar = foo.clone().lookup("bar")?; + + assert!(bar.parent().is_some()); + assert!(Arc::ptr_eq(&bar.parent().unwrap(), &foo)); + + assert!(Arc::ptr_eq(&bar.clone().lookup("..")?, &foo)); + assert!(Arc::ptr_eq(&foo.clone().lookup("..")?, &root)); + + assert!(Arc::ptr_eq( + &root.clone().lookup("foo/bar/..")?, + &root.clone().lookup("foo")?, + )); + assert!(Arc::ptr_eq( + &root.clone().lookup("./foo/../foo/bar/f4")?, + &root.lookup("foo/bar/f4")?, + )); + Ok(()) +} + +// ================================================================= +// Test Scenarios +// ================================================================= + +#[test] +fn test_full_lifecycle() { + let ramfs = RamFileSystem::new(); + let root = ramfs.root_dir(); + root.clone().create("f1", VfsNodeType::File).unwrap(); + root.clone().create("f2", VfsNodeType::File).unwrap(); + root.clone().create("foo", VfsNodeType::Dir).unwrap(); + + let foo = root.clone().lookup("foo").unwrap(); + foo.clone().create("f3", VfsNodeType::File).unwrap(); + foo.clone().create("bar", VfsNodeType::Dir).unwrap(); + + let bar = foo.lookup("bar").unwrap(); + bar.create("f4", VfsNodeType::File).unwrap(); + + // This call works because `root_dir_node` on RamFileSystem and its `get_entries` + // method are public, without needing to name the concrete type. + let mut entries = ramfs.root_dir_node().get_entries(); + entries.sort(); + assert_eq!(entries, ["f1", "f2", "foo"]); + + run_basic_ops_tests(&ramfs).expect("Basic ops tests failed"); + run_parent_and_path_tests(&ramfs).expect("Parent and path tests failed"); + + assert_eq!(root.clone().remove("f1"), Ok(())); + assert_eq!(root.clone().remove("//f2"), Ok(())); + assert_eq!( + root.clone().remove("foo").err(), + Some(VfsError::DirectoryNotEmpty) + ); + assert_eq!(root.clone().remove("foo/bar/f4"), Ok(())); + assert_eq!(root.clone().remove("foo/bar"), Ok(())); + assert_eq!(root.clone().remove("./foo//.//f3"), Ok(())); + assert_eq!(root.clone().remove("./foo"), Ok(())); + assert!(ramfs.root_dir_node().get_entries().is_empty()); +} + +#[test] +fn test_path_manipulation_and_lookup() { + let ramfs = setup_complex_fs(); + let root = ramfs.root_dir(); + let dir1 = root.clone().lookup("dir1").unwrap(); + let dir2 = root.clone().lookup("dir1/dir2").unwrap(); + + assert!(Arc::ptr_eq( + &root.clone().lookup("///dir1//./dir2/..").unwrap(), + &dir1 + )); + assert!(Arc::ptr_eq(&dir2.lookup("..").unwrap(), &dir1)); + + let file = root.lookup("file.txt").unwrap(); + assert_eq!(file.lookup("any").err(), Some(VfsError::NotADirectory)); +} + +#[test] +fn test_io_edge_cases() { + let ramfs = RamFileSystem::new(); + let root = ramfs.root_dir(); + root.clone().create("data.dat", VfsNodeType::File).unwrap(); + let file = root.lookup("data.dat").unwrap(); + let mut buf = [0u8; 32]; + + file.clone().write_at(5, b"hello").unwrap(); + assert_eq!(file.clone().get_attr().unwrap().size(), 10); + file.clone().read_at(0, &mut buf[..10]).unwrap(); + assert_eq!(&buf[..10], b"\0\0\0\0\0hello"); + + file.clone().truncate(5).unwrap(); + assert_eq!(file.clone().get_attr().unwrap().size(), 5); + + file.clone().truncate(15).unwrap(); + assert_eq!(file.clone().get_attr().unwrap().size(), 15); + let bytes_read = file.read_at(0, &mut buf).unwrap(); + assert_eq!(bytes_read, 15); + // After truncating to 5, the "hello" part was cut off. + // Expanding to 15 should result in all zeros. + assert_eq!(&buf[..bytes_read], &[0; 15]); +} + +#[test] +fn test_error_conditions() { + let ramfs = setup_complex_fs(); + let root = ramfs.root_dir(); + let dir1 = root.clone().lookup("dir1").unwrap(); + + assert_eq!( + root.clone().create("dir1", VfsNodeType::Dir).err(), + Some(VfsError::AlreadyExists) + ); + assert_eq!( + root.clone().remove("dir1").err(), + Some(VfsError::DirectoryNotEmpty) + ); + assert_eq!( + dir1.clone().read_at(0, &mut [0]).err(), + Some(VfsError::IsADirectory) + ); + assert_eq!( + dir1.clone().write_at(0, &[0]).err(), + Some(VfsError::IsADirectory) + ); + assert_eq!(dir1.clone().remove(".").err(), Some(VfsError::InvalidInput)); + assert_eq!(dir1.remove("..").err(), Some(VfsError::InvalidInput)); + assert_eq!(root.lookup("no-such-file").err(), Some(VfsError::NotFound)); +} diff --git a/axfs_vfs/src/structs.rs b/axfs_vfs/src/structs.rs index 9d0dbfa..b666677 100644 --- a/axfs_vfs/src/structs.rs +++ b/axfs_vfs/src/structs.rs @@ -20,7 +20,7 @@ pub struct VfsNodeAttr { bitflags::bitflags! { /// Node (file/directory) permission mode. - #[derive(Debug, Clone, Copy)] + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct VfsNodePerm: u16 { /// Owner has read permission. const OWNER_READ = 0o400; diff --git a/axfs_vfs/tests/test_adfs_vfs.rs b/axfs_vfs/tests/test_adfs_vfs.rs new file mode 100644 index 0000000..03cda00 --- /dev/null +++ b/axfs_vfs/tests/test_adfs_vfs.rs @@ -0,0 +1,166 @@ +// tests/test_axfs_vfs.rs + +// Import all public components from the axfs_vfs crate +use axfs_vfs::{ + impl_vfs_dir_default, impl_vfs_non_dir_default, path, VfsError, VfsNodeAttr, VfsNodeOps, + VfsNodePerm, VfsNodeType, VfsResult, +}; +use std::sync::Arc; + +// ================================================================= +// Test Case 1: Validate data structures (from structs.rs) +// ================================================================= +#[test] +fn test_vfs_data_structures() { + println!("\n--- Test Case 1: Validating VFS Data Structures ---"); + + // Test VfsNodeAttr (node attributes) + let file_attr = VfsNodeAttr::new_file(1024, 2); + assert!(file_attr.is_file(), "Should be identified as a file"); + assert!( + !file_attr.is_dir(), + "Should not be identified as a directory" + ); + assert_eq!(file_attr.size(), 1024, "File size should be 1024"); + assert_eq!(file_attr.file_type(), VfsNodeType::File); + + let dir_attr = VfsNodeAttr::new_dir(4096, 8); + assert!(dir_attr.is_dir(), "Should be identified as a directory"); + // Now that VfsNodePerm implements PartialEq, this line works correctly + assert_eq!( + dir_attr.perm(), + VfsNodePerm::default_dir(), + "Permissions should be the default for a directory" + ); + + // Test VfsNodePerm (node permissions) + let perm = VfsNodePerm::from_bits_truncate(0o755); // rwxr-xr-x + assert!(perm.owner_readable() && perm.owner_writable() && perm.owner_executable()); + + // Fix: Use .contains() to check group and other permissions + assert!( + perm.contains(VfsNodePerm::GROUP_READ) + && !perm.contains(VfsNodePerm::GROUP_WRITE) + && perm.contains(VfsNodePerm::GROUP_EXEC) + ); + assert!( + perm.contains(VfsNodePerm::OTHER_READ) + && !perm.contains(VfsNodePerm::OTHER_WRITE) + && perm.contains(VfsNodePerm::OTHER_EXEC) + ); + + assert_eq!( + perm.rwx_buf(), + *b"rwxr-xr-x", + "rwx string representation should be correct" + ); + + // Test VfsNodeType (node type) + assert_eq!( + VfsNodeType::Dir.as_char(), + 'd', + "Character representation for Dir type should be 'd'" + ); + assert_eq!( + VfsNodeType::File.as_char(), + '-', + "Character representation for File type should be '-'" + ); + + println!("--- Test Case 1 Passed ---"); +} + +// ================================================================= +// Test Case 2: Validate path utilities (from path.rs) +// ================================================================= +#[test] +fn test_path_canonicalization() { + println!("\n--- Test Case 2: Validating Path Canonicalization ---"); + + // Complex absolute path containing '.', '..', and extra slashes + assert_eq!( + path::canonicalize("/a/b/./c/../d//e"), + "/a/b/d/e", + "Canonicalization of a complex absolute path failed" + ); + + // Absolute path that traverses up to the root + assert_eq!( + path::canonicalize("/a/b/../../c"), + "/c", + "Canonicalization of an absolute path traversing to root failed" + ); + + // Relative path + assert_eq!( + path::canonicalize("a/b/../c"), + "a/c", + "Canonicalization of a relative path failed" + ); + + // Edge cases: root directory and empty path + assert_eq!( + path::canonicalize("///"), + "/", + "Canonicalization of the root directory path failed" + ); + assert_eq!( + path::canonicalize(""), + "", + "An empty path should remain empty" + ); + + println!("--- Test Case 2 Passed ---"); +} + +// ================================================================= +// Test Case 3: Validate default implementations generated by macros (from macros.rs) +// ================================================================= +#[test] +fn test_default_impl_macros() { + println!("\n--- Test Case 3: Validating Macro Default Implementations ---"); + + // Define a mock "file" node type + struct MockFileNode; + impl VfsNodeOps for MockFileNode { + fn get_attr(&self) -> VfsResult { + Ok(VfsNodeAttr::new_file(0, 0)) + } + impl_vfs_non_dir_default!(); + } + + // Define a mock "directory" node type + struct MockDirNode; + impl VfsNodeOps for MockDirNode { + fn get_attr(&self) -> VfsResult { + Ok(VfsNodeAttr::new_dir(0, 0)) + } + impl_vfs_dir_default!(); + } + + let file_node: Arc = Arc::new(MockFileNode); + let dir_node: Arc = Arc::new(MockDirNode); + + // Verify: Calling a directory operation on a "file" node should return NotADirectory + assert_eq!( + file_node.clone().lookup("any").err(), + Some(VfsError::NotADirectory) + ); + assert_eq!( + file_node.create("any", VfsNodeType::File).err(), + Some(VfsError::NotADirectory) + ); + + // Verify: Calling a file operation on a "directory" node should return IsADirectory + let mut buffer = [0u8; 8]; + assert_eq!( + dir_node.read_at(0, &mut buffer).err(), + Some(VfsError::IsADirectory) + ); + assert_eq!( + dir_node.write_at(0, &buffer).err(), + Some(VfsError::IsADirectory) + ); + + println!("--- Test Case 3 Passed ---"); +}