file_system/operations/
file_system.rs

1use crate::{
2    Attributes, Context, DirectoryOperations, FileOperations, Flags, MountOperations, Path, Result,
3};
4
5pub trait FileSystemOperations: FileOperations + DirectoryOperations + MountOperations {
6    fn lookup_directory(&self, context: &mut Context, path: &Path) -> Result<()>;
7
8    fn lookup_file(&self, context: &mut Context, path: &Path, flags: Flags) -> Result<()>;
9
10    fn create_directory(&self, path: &Path) -> Result<()>;
11
12    fn create_file(&self, path: &Path) -> Result<()>;
13
14    /// Remove a file or directory from the file system.
15    ///
16    /// Permanently deletes the specified file or directory. For directories,
17    /// they must be empty before they can be removed.
18    ///
19    /// # Arguments
20    ///
21    /// * `context` - File system context
22    /// * `path` - Path to the file or directory to remove
23    ///
24    /// # Returns
25    ///
26    /// * `Ok(())` - File or directory successfully removed
27    /// * `Err(Error)` - Error during removal
28    ///
29    /// # Errors
30    ///
31    /// * [`crate::Error::NotFound`] - File or directory doesn't exist
32    /// * [`crate::Error::PermissionDenied`] - Insufficient permissions
33    /// * [`crate::Error::DirectoryNotEmpty`] - Directory contains files
34    /// * [`crate::Error::RessourceBusy`] - File is currently in use
35    fn remove(&self, path: &Path) -> Result<()>;
36
37    /// Rename or move a file or directory.
38    ///
39    /// Changes the name or location of a file or directory. This can be used
40    /// for both renaming within the same directory and moving between directories.
41    ///
42    /// # Arguments
43    ///
44    /// * `context` - File system context
45    /// * `source` - Current path of the file or directory
46    /// * `destination` - New path for the file or directory
47    ///
48    /// # Returns
49    ///
50    /// * `Ok(())` - File or directory successfully renamed/moved
51    /// * `Err(Error)` - Error during rename operation
52    ///
53    /// # Errors
54    ///
55    /// * [`crate::Error::NotFound`] - Source file doesn't exist
56    /// * [`crate::Error::AlreadyExists`] - Destination already exists
57    /// * [`crate::Error::PermissionDenied`] - Insufficient permissions
58    fn rename(&self, source: &Path, destination: &Path) -> Result<()>;
59
60    // - Directory
61
62    // - Statistics
63
64    fn get_attributes(&self, path: &Path, attributes: &mut Attributes) -> Result<()>;
65
66    fn set_attributes(&self, path: &Path, attributes: &Attributes) -> Result<()>;
67}
68
69pub mod tests {
70    //! Generic test suite for file system operations.
71    //!
72    //! This module provides comprehensive tests for any implementation of the
73    //! `FileSystemOperations` trait. It includes tests for:
74    //!
75    //! - File operations (create, lookup, remove)
76    //! - Directory operations (create, lookup, remove, nested)
77    //! - Rename and move operations
78    //! - Attribute get/set operations
79    //! - Error handling and edge cases
80    //! - File system consistency
81    //! - Special cases (root directory, long names, special characters)
82    //!
83    //! # Usage
84    //!
85    //! ## Using the Macro (Recommended)
86    //!
87    //! The easiest way to test your file system implementation is to use the
88    //! `implement_file_system_tests!` macro, which generates individual test
89    //! functions for each test category:
90    //!
91    //! ```rust,ignore
92    //! use file_system::implement_file_system_tests;
93    //!
94    //! mod tests {
95    //!     use super::*;
96    //!     
97    //!     implement_file_system_tests! {
98    //!         instance: MyFileSystem::new()
99    //!     }
100    //! }
101    //! ```
102    //!
103    //! This will generate 32+ individual `#[test]` functions, each testing a specific
104    //! aspect of your file system implementation.
105    //!
106    //! ## Using Individual Test Functions
107    //!
108    //! You can also call individual test functions directly:
109    //!
110    //! ```rust,ignore
111    //! use file_system::operations::file_system::tests;
112    //!
113    //! #[test]
114    //! fn test_my_file_system() {
115    //!     let fs = MyFileSystem::new();
116    //!     
117    //!     // Run all tests
118    //!     tests::run_all_tests(&fs);
119    //!     
120    //!     // Or run individual test categories
121    //!     tests::test_file_operations(&fs);
122    //!     tests::test_directory_operations(&fs);
123    //!     tests::test_rename_operations(&fs);
124    //! }
125    //! ```
126    //!
127    //! # Test Categories
128    //!
129    //! ## File System Structure Tests
130    //! - `test_file_operations` - Basic file CRUD operations
131    //! - `test_directory_operations` - Basic directory operations
132    //! - `test_nested_directories` - Nested directory hierarchy
133    //! - `test_rename_operations` - File/directory rename and move
134    //! - `test_attribute_operations` - Metadata get/set operations
135    //!
136    //! ## File I/O Tests
137    //! - `test_file_read_operations` - Reading data from files
138    //! - `test_file_write_operations` - Writing data to files
139    //! - `test_file_write_pattern` - Pattern-based writing
140    //! - `test_file_write_vectored` - Vectored (scatter-gather) I/O
141    //! - `test_file_position_operations` - Position-based read/write
142    //! - `test_file_read_until` - Reading until delimiter
143    //! - `test_file_flush` - Flushing buffered data
144    //! - `test_large_file_operations` - Large file handling
145    //!
146    //! ## Directory I/O Tests
147    //! - `test_directory_read_operations` - Reading directory entries
148    //! - `test_directory_position_operations` - Directory position management
149    //! - `test_directory_rewind` - Rewinding directory stream
150    //! - `test_empty_directory_read` - Reading empty directories
151    //!
152    //! ## Error Handling & Edge Cases
153    //! - `test_error_handling` - Error case validation
154    //! - `test_file_system_consistency` - Consistency checks
155    //! - `test_root_directory_operations` - Root directory special cases
156    //! - `test_invalid_paths` - Invalid path handling
157    //!
158    //! ## Test Execution
159    //! - `implement_file_system_tests!` - Macro to generate all test functions (recommended)
160    //! - `run_all_tests` - Function to execute all test categories sequentially
161
162    use super::*;
163    use crate::{
164        AccessFlags, Attributes, BaseOperations, Context, Error, Kind, Permissions, Size, Time,
165    };
166
167    /// Generic test suite for file system operations.
168    ///
169    /// This provides a comprehensive set of tests that can be used to verify
170    /// any implementation of the `FileSystemOperations` trait.
171    pub fn test_file_system_operations<F>(fs: &F)
172    where
173        F: FileSystemOperations,
174    {
175        test_file_operations(fs);
176        test_directory_operations(fs);
177        test_attribute_operations(fs);
178        test_error_handling(fs);
179    }
180
181    /// Test basic file operations: create, lookup, remove
182    pub fn test_file_operations<F>(fs: &F)
183    where
184        F: FileSystemOperations,
185    {
186        let test_file = Path::new("/test_file.txt");
187        let mut context = Context::new_empty();
188
189        // Test file creation
190        fs.create_file(test_file).unwrap();
191
192        // Test file lookup
193        fs.lookup_file(&mut context, test_file, AccessFlags::Read.into())
194            .unwrap();
195
196        // Test file removal
197        fs.remove(test_file).unwrap();
198
199        // Test lookup after removal should fail
200        fs.lookup_file(&mut context, test_file, AccessFlags::Read.into())
201            .unwrap_err();
202    }
203
204    /// Test file creation with various edge cases
205    pub fn test_file_creation_edge_cases<F>(fs: &F)
206    where
207        F: FileSystemOperations,
208    {
209        // Test creating file that already exists
210        let test_file = Path::new("/duplicate.txt");
211        fs.create_file(test_file).unwrap();
212
213        let result = fs.create_file(test_file);
214        assert!(
215            matches!(result, Err(Error::AlreadyExists)),
216            "Creating duplicate file should return AlreadyExists error"
217        );
218
219        // Cleanup
220        fs.remove(test_file).unwrap();
221    }
222
223    /// Test directory operations: create, lookup, remove
224    pub fn test_directory_operations<F>(fs: &F)
225    where
226        F: FileSystemOperations,
227    {
228        let test_dir = Path::new("/test_directory");
229        let mut context = Context::new_empty();
230
231        // Test directory creation
232        fs.create_directory(test_dir).unwrap();
233
234        // Test directory lookup
235        fs.lookup_directory(&mut context, test_dir).unwrap();
236
237        DirectoryOperations::close(fs, &mut context).unwrap();
238
239        // Test directory removal
240        fs.remove(test_dir).unwrap();
241
242        let mut context = Context::new_empty();
243
244        // Test lookup after removal should fail
245        fs.lookup_directory(&mut context, test_dir).unwrap_err();
246    }
247
248    /// Test nested directory operations
249    pub fn test_nested_directories<F>(fs: &F)
250    where
251        F: FileSystemOperations,
252    {
253        let parent_dir = Path::new("/parent");
254        let child_dir = Path::new("/parent/child");
255
256        // Create parent directory
257        fs.create_directory(parent_dir).unwrap();
258
259        // Create child directory
260        fs.create_directory(child_dir).unwrap();
261
262        // Try to remove parent with child (should fail)
263        let result = fs.remove(parent_dir);
264        assert!(
265            matches!(result, Err(Error::DirectoryNotEmpty)),
266            "Removing non-empty directory should fail with DirectoryNotEmpty"
267        );
268
269        // Remove child first
270        fs.remove(child_dir).unwrap();
271
272        // Now parent removal should succeed
273        fs.remove(parent_dir).unwrap();
274    }
275
276    /// Test rename/move operations
277    pub fn test_rename_operations<F>(fs: &F)
278    where
279        F: FileSystemOperations,
280    {
281        let source = Path::new("/source_file.txt");
282        let destination = Path::new("/destination_file.txt");
283        let mut context = Context::new_empty();
284
285        // Create source file
286        fs.create_file(source).unwrap();
287
288        // Test rename
289        fs.rename(source, destination).unwrap();
290
291        // Verify source no longer exists
292        fs.lookup_file(&mut context, source, AccessFlags::Read.into())
293            .unwrap_err();
294
295        // Verify destination exists
296        assert!(
297            fs.lookup_file(&mut context, destination, AccessFlags::Read.into())
298                .is_ok(),
299            "Destination file should exist after rename"
300        );
301
302        // Cleanup
303        fs.remove(destination).unwrap();
304    }
305
306    /// Test rename edge cases
307    pub fn test_rename_edge_cases<F>(fs: &F)
308    where
309        F: FileSystemOperations,
310    {
311        let source = Path::new("/rename_source.txt");
312        let destination = Path::new("/rename_dest.txt");
313
314        // Test renaming non-existent file
315        let result = fs.rename(source, destination);
316        assert_eq!(result, Err(Error::NotFound),);
317
318        // Create both files
319        fs.create_file(source).unwrap();
320        fs.create_file(destination).unwrap();
321
322        // Test renaming to existing file
323        //let _ = fs.rename(source, destination);
324        //assert_eq!(
325        //    result,
326        //    Err(Error::AlreadyExists),
327        //);
328
329        // Cleanup
330        fs.remove(source).unwrap();
331        fs.remove(destination).unwrap();
332    }
333
334    /// Test moving files between directories
335    pub fn test_move_between_directories<F>(fs: &F)
336    where
337        F: FileSystemOperations,
338    {
339        let dir1 = Path::new("/dir1");
340        let dir2 = Path::new("/dir2");
341        let file_in_dir1 = Path::new("/dir1/file.txt");
342        let file_in_dir2 = Path::new("/dir2/file.txt");
343
344        // Create directories
345        fs.create_directory(dir1).unwrap();
346        fs.create_directory(dir2).unwrap();
347
348        // Create file in dir1
349        fs.create_file(file_in_dir1).unwrap();
350
351        // Move file to dir2
352        fs.rename(file_in_dir1, file_in_dir2).unwrap();
353
354        // Verify file is in dir2
355        let mut context = Context::new_empty();
356        fs.lookup_file(&mut context, file_in_dir2, AccessFlags::Read.into())
357            .unwrap();
358
359        // Cleanup
360        fs.remove(file_in_dir2).unwrap();
361        fs.remove(dir2).unwrap();
362        fs.remove(dir1).unwrap();
363    }
364
365    /// Test attribute get/set operations
366    pub fn test_attribute_operations<F>(fs: &F)
367    where
368        F: FileSystemOperations,
369    {
370        let test_file = Path::new("/attr_test.txt");
371
372        // Create test file
373        fs.create_file(test_file).unwrap();
374
375        // Modify and set attributes
376        let new_attrs = Attributes::new()
377            .set_permissions(Permissions::ALL_READ_WRITE)
378            .set_kind(Kind::File)
379            .set_user(UserIdentifier::ROOT)
380            .set_inode(42)
381            .set_status(Time::new(0));
382
383        FileSystemOperations::set_attributes(fs, test_file, &new_attrs).unwrap();
384
385        // Get attributes
386        let mut attrs = Attributes::new();
387        FileSystemOperations::get_attributes(fs, test_file, &mut attrs).unwrap();
388
389        // Verify attributes were changed
390        let mut updated_attrs = Attributes::new();
391        FileSystemOperations::get_attributes(fs, test_file, &mut updated_attrs).unwrap();
392
393        // Cleanup
394        fs.remove(test_file).unwrap();
395    }
396
397    /// Test attribute operations on directories
398    pub fn test_directory_attributes<F>(fs: &F)
399    where
400        F: FileSystemOperations,
401    {
402        let test_dir = Path::new("/attr_dir");
403
404        // Create test directory
405        fs.create_directory(test_dir).unwrap();
406
407        // Get directory attributes
408        let mut attrs = Attributes::new();
409        FileSystemOperations::get_attributes(fs, test_dir, &mut attrs).unwrap();
410
411        // Verify it's a directory
412        if let Some(kind) = attrs.get_kind() {
413            assert_eq!(*kind, Kind::Directory, "Should be a directory type");
414        }
415
416        // Cleanup
417        fs.remove(test_dir).unwrap();
418    }
419
420    /// Test error handling for various scenarios
421    pub fn test_error_handling<F>(fs: &F)
422    where
423        F: FileSystemOperations,
424    {
425        let mut context = Context::new_empty();
426
427        // Test NotFound errors
428        let nonexistent = Path::new("/nonexistent.txt");
429        fs.lookup_file(&mut context, nonexistent, AccessFlags::Read.into())
430            .unwrap_err();
431
432        assert!(
433            fs.remove(nonexistent).is_err(),
434            "Should return error when removing non-existent file"
435        );
436
437        // Test getting attributes of non-existent file
438        let mut attrs = Attributes::new();
439        FileSystemOperations::get_attributes(fs, nonexistent, &mut attrs).unwrap_err();
440    }
441
442    /// Test invalid path handling
443    pub fn test_invalid_paths<F>(fs: &F)
444    where
445        F: FileSystemOperations,
446    {
447        // Note: Path validation typically happens at Path::new(),
448        // but we test file system behavior with edge cases
449
450        // Test empty filename (directory operations might handle this differently)
451        let path = Path::new("/.txt");
452        // If path is valid, file system should handle it appropriately
453        let file_result = fs.create_file(path);
454        // Result depends on implementation - may succeed or fail
455        if file_result.is_ok() {
456            fs.remove(path).unwrap();
457        }
458    }
459
460    /// Test concurrent operations (if file system supports it)
461    pub fn test_multiple_operations<F>(fs: &F)
462    where
463        F: FileSystemOperations,
464    {
465        // Create multiple files
466        let files = [
467            "/multi_test_1.txt",
468            "/multi_test_2.txt",
469            "/multi_test_3.txt",
470        ];
471
472        for file_path in &files {
473            let path = Path::new(file_path);
474            fs.create_file(path).unwrap();
475        }
476
477        // Verify all files exist
478        for file_path in &files {
479            let path = Path::new(file_path);
480            let mut context = Context::new_empty();
481            fs.lookup_file(&mut context, path, AccessFlags::Read.into())
482                .unwrap();
483            BaseOperations::close(fs, &mut context).unwrap();
484        }
485
486        // Remove all files
487        for file_path in &files {
488            let path = Path::new(file_path);
489            fs.remove(path).unwrap();
490        }
491    }
492
493    /// Test file system consistency
494    pub fn test_file_system_consistency<F>(fs: &F)
495    where
496        F: FileSystemOperations,
497    {
498        let dir = Path::new("/consistency_test");
499        let file = Path::new("/consistency_test/file.txt");
500
501        // Create directory and file
502        fs.create_directory(dir).unwrap();
503        fs.create_file(file).unwrap();
504
505        // Attempt to create file with same name as directory should fail
506        let result = fs.create_file(dir);
507        result.unwrap_err();
508
509        // Attempt to create directory with same name as file should fail
510        let result = fs.create_directory(file);
511        result.unwrap_err();
512
513        // Cleanup
514        fs.remove(file).unwrap();
515        fs.remove(dir).unwrap();
516    }
517
518    /// Test file lookup with different flags
519    pub fn test_lookup_with_flags<F>(fs: &F)
520    where
521        F: FileSystemOperations,
522    {
523        let test_file = Path::new("/flags_test.txt");
524        let mut context = Context::new_empty();
525
526        fs.create_file(test_file).unwrap();
527
528        // Test with different flag combinations
529        fs.lookup_file(&mut context, test_file, AccessFlags::Read.into())
530            .unwrap();
531        let mut context = Context::new_empty();
532
533        fs.lookup_file(&mut context, test_file, AccessFlags::Write.into())
534            .unwrap();
535        let mut context = Context::new_empty();
536
537        fs.lookup_file(&mut context, test_file, AccessFlags::READ_WRITE.into())
538            .unwrap();
539
540        // Cleanup
541        fs.remove(test_file).unwrap();
542    }
543
544    /// Test operations on root directory
545    pub fn test_root_directory_operations<F>(fs: &F)
546    where
547        F: FileSystemOperations,
548    {
549        let root = Path::new("/");
550        let mut context = Context::new_empty();
551
552        // Root should always exist
553        fs.lookup_directory(&mut context, root).unwrap();
554
555        // Should not be able to remove root
556        let result = fs.remove(root);
557        result.unwrap_err();
558
559        // Should be able to get root attributes
560        let mut attrs = Attributes::new();
561        FileSystemOperations::get_attributes(fs, root, &mut attrs).unwrap();
562    }
563
564    /// Test long file names
565    pub fn test_long_filenames<F>(fs: &F)
566    where
567        F: FileSystemOperations,
568    {
569        // Create a reasonably long filename
570        let long_name = "/this_is_a_very_long_filename_that_tests_the_limits.txt";
571        let path = Path::new(long_name);
572
573        let result = fs.create_file(path);
574        // May succeed or fail depending on file system limits
575        if result.is_ok() {
576            fs.remove(path).unwrap();
577        }
578    }
579
580    /// Test special characters in filenames
581    pub fn test_special_characters_in_names<F>(fs: &F)
582    where
583        F: FileSystemOperations,
584    {
585        let special_chars = [
586            "/file-with-dash.txt",
587            "/file_with_underscore.txt",
588            "/file.multiple.dots.txt",
589        ];
590
591        for name in &special_chars {
592            let path = Path::new(name);
593            let result = fs.create_file(path);
594            if result.is_ok() {
595                fs.remove(path).unwrap();
596            }
597        }
598    }
599
600    /// Test file read operations (when FileOperations::read is available)
601    pub fn test_file_read_operations<F>(fs: &F)
602    where
603        F: FileSystemOperations,
604    {
605        use crate::BaseOperations;
606
607        let test_file = Path::new("/read_test.txt");
608        let mut context = Context::new_empty();
609
610        // Create and open file for writing
611        fs.create_file(test_file).unwrap();
612        fs.lookup_file(&mut context, test_file, AccessFlags::Write.into())
613            .unwrap();
614
615        // Write test data
616        let test_data = b"Hello, File System!";
617        let write_result = fs.write(&mut context, test_data, 0);
618        write_result.as_ref().unwrap();
619        assert_eq!(
620            write_result.unwrap(),
621            test_data.len(),
622            "Should write all bytes"
623        );
624
625        // Close and reopen for reading
626        BaseOperations::close(fs, &mut context).unwrap();
627        fs.lookup_file(&mut context, test_file, AccessFlags::Read.into())
628            .unwrap();
629
630        // Read data back
631        let mut buffer = alloc::vec![0u8; test_data.len()];
632        let read_result = BaseOperations::read(fs, &mut context, &mut buffer, 0);
633        read_result.as_ref().unwrap();
634        assert_eq!(
635            read_result.unwrap(),
636            test_data.len(),
637            "Should read all bytes"
638        );
639        assert_eq!(
640            &buffer[..],
641            test_data,
642            "Read data should match written data"
643        );
644
645        // Cleanup
646        BaseOperations::close(fs, &mut context).unwrap();
647        fs.remove(test_file).unwrap();
648    }
649
650    /// Test file write operations with various patterns
651    pub fn test_file_write_operations<F>(fs: &F)
652    where
653        F: FileSystemOperations,
654    {
655        use crate::BaseOperations;
656
657        let test_file = Path::new("/write_test.txt");
658        let mut context = Context::new_empty();
659
660        fs.create_file(test_file).unwrap();
661        fs.lookup_file(&mut context, test_file, AccessFlags::Write.into())
662            .unwrap();
663
664        // Test single write
665        let data1 = b"First write. ";
666        let result = BaseOperations::write(fs, &mut context, data1, 0);
667        result.unwrap();
668
669        // Test sequential write
670        let data2 = b"Second write.";
671        let result = BaseOperations::write(fs, &mut context, data2, data1.len() as Size);
672        result.unwrap();
673
674        // Test overwrite
675        let overwrite_data = b"OVERWRITE";
676        let result = BaseOperations::write(fs, &mut context, overwrite_data, 0);
677        result.unwrap();
678
679        // Cleanup
680        BaseOperations::close(fs, &mut context).unwrap();
681        fs.remove(test_file).unwrap();
682    }
683
684    /// Test file write pattern operations
685    pub fn test_file_write_pattern<F>(fs: &F)
686    where
687        F: FileSystemOperations,
688    {
689        use crate::BaseOperations;
690
691        let test_file = Path::new("/pattern_test.txt");
692        let mut context = Context::new_empty();
693
694        fs.create_file(test_file).unwrap();
695        fs.lookup_file(&mut context, test_file, AccessFlags::Write.into())
696            .unwrap();
697
698        // Write a pattern multiple times
699        let pattern = b"ABC";
700        let count = 5;
701        let result = BaseOperations::write_pattern(fs, &mut context, pattern, count, 0);
702
703        if let Ok(bytes_written) = result {
704            assert!(
705                bytes_written <= pattern.len() * count,
706                "Should not write more than requested"
707            );
708
709            // Read back and verify pattern
710            BaseOperations::close(fs, &mut context).unwrap();
711            fs.lookup_file(&mut context, test_file, AccessFlags::Read.into())
712                .unwrap();
713
714            let mut buffer = alloc::vec![0u8; bytes_written];
715            let read_result = BaseOperations::read(fs, &mut context, &mut buffer, 0);
716            read_result.unwrap();
717        }
718
719        // Cleanup
720        BaseOperations::close(fs, &mut context).unwrap();
721        fs.remove(test_file).unwrap();
722    }
723
724    /// Test file write vectored operations
725    pub fn test_file_write_vectored<F>(fs: &F)
726    where
727        F: FileSystemOperations,
728    {
729        use crate::BaseOperations;
730
731        let test_file = Path::new("/vectored_test.txt");
732        let mut context = Context::new_empty();
733
734        fs.create_file(test_file).unwrap();
735        fs.lookup_file(&mut context, test_file, AccessFlags::Write.into())
736            .unwrap();
737
738        // Write multiple buffers at once
739        let buf1 = b"First ";
740        let buf2 = b"Second ";
741        let buf3 = b"Third";
742        let buffers = [buf1.as_slice(), buf2.as_slice(), buf3.as_slice()];
743
744        let result = BaseOperations::write_vectored(fs, &mut context, &buffers, 0);
745        if let Ok(total_written) = result {
746            let expected_total = buf1.len() + buf2.len() + buf3.len();
747            assert!(
748                total_written <= expected_total,
749                "Should write all buffers or partial"
750            );
751
752            // Read back and verify
753            BaseOperations::close(fs, &mut context).unwrap();
754            fs.lookup_file(&mut context, test_file, AccessFlags::Read.into())
755                .unwrap();
756
757            let mut buffer = alloc::vec![0u8; total_written];
758            let read_result = BaseOperations::read(fs, &mut context, &mut buffer, 0);
759            read_result.unwrap();
760        }
761
762        // Cleanup
763        BaseOperations::close(fs, &mut context).unwrap();
764        fs.remove(test_file).unwrap();
765    }
766
767    /// Test file read/write at specific positions
768    pub fn test_file_position_operations<F>(fs: &F)
769    where
770        F: FileSystemOperations,
771    {
772        use crate::BaseOperations;
773
774        let test_file = Path::new("/position_test.txt");
775        let mut context = Context::new_empty();
776
777        fs.create_file(test_file).unwrap();
778        fs.lookup_file(&mut context, test_file, AccessFlags::READ_WRITE.into())
779            .unwrap();
780
781        // Write data at different positions
782        let data1 = b"AAAA";
783        let data2 = b"BBBB";
784        let data3 = b"CCCC";
785
786        BaseOperations::write(fs, &mut context, data1, 0).unwrap();
787        BaseOperations::write(fs, &mut context, data2, 4).unwrap();
788        BaseOperations::write(fs, &mut context, data3, 8).unwrap();
789
790        // Read from specific positions
791        let mut buffer = alloc::vec![0u8; 4];
792
793        let result = BaseOperations::read(fs, &mut context, &mut buffer, 0);
794        result.unwrap();
795        assert_eq!(&buffer[..], b"AAAA", "Should read first chunk");
796
797        let result = BaseOperations::read(fs, &mut context, &mut buffer, 4);
798        result.unwrap();
799        assert_eq!(&buffer[..], b"BBBB", "Should read second chunk");
800
801        let result = BaseOperations::read(fs, &mut context, &mut buffer, 8);
802        result.unwrap();
803        assert_eq!(&buffer[..], b"CCCC", "Should read third chunk");
804
805        // Cleanup
806        BaseOperations::close(fs, &mut context).unwrap();
807        fs.remove(test_file).unwrap();
808    }
809
810    /// Test file read until delimiter
811    pub fn test_file_read_until<F>(fs: &F)
812    where
813        F: FileSystemOperations,
814    {
815        use crate::BaseOperations;
816
817        let test_file = Path::new("/read_until_test.txt");
818        let mut context = Context::new_empty();
819
820        fs.create_file(test_file).unwrap();
821        fs.lookup_file(&mut context, test_file, AccessFlags::READ_WRITE.into())
822            .unwrap();
823
824        // Write data with delimiter
825        let test_data = b"Line 1\nLine 2\nLine 3\n";
826        BaseOperations::write(fs, &mut context, test_data, 0).unwrap();
827
828        // Read until newline
829        let mut buffer = alloc::vec![0u8; 100];
830        let result = BaseOperations::read_until(fs, &mut context, &mut buffer, 0, b"\n");
831
832        if let Ok(bytes_read) = result {
833            assert!(bytes_read > 0, "Should read some bytes");
834            assert!(bytes_read <= 7, "Should stop at first newline");
835        }
836
837        // Cleanup
838        BaseOperations::close(fs, &mut context).unwrap();
839        fs.remove(test_file).unwrap();
840    }
841
842    /// Test file flush operation
843    pub fn test_file_flush<F>(fs: &F)
844    where
845        F: FileSystemOperations,
846    {
847        use crate::BaseOperations;
848
849        let test_file = Path::new("/flush_test.txt");
850        let mut context = Context::new_empty();
851
852        fs.create_file(test_file).unwrap();
853        fs.lookup_file(&mut context, test_file, AccessFlags::Write.into())
854            .unwrap();
855
856        // Write data
857        let data = b"Data to flush";
858        BaseOperations::write(fs, &mut context, data, 0).unwrap();
859
860        // Flush should succeed or be unsupported
861        let result = BaseOperations::flush(fs, &mut context);
862        assert!(
863            result.is_ok() || matches!(result, Err(Error::UnsupportedOperation)),
864            "Flush should succeed or be unsupported"
865        );
866
867        // Cleanup
868        BaseOperations::close(fs, &mut context).unwrap();
869        fs.remove(test_file).unwrap();
870    }
871
872    /// Test empty file operations
873    pub fn test_empty_file_operations<F>(fs: &F)
874    where
875        F: FileSystemOperations,
876    {
877        use crate::BaseOperations;
878
879        let test_file = Path::new("/empty_test.txt");
880        let mut context = Context::new_empty();
881
882        fs.create_file(test_file).unwrap();
883        fs.lookup_file(&mut context, test_file, AccessFlags::Read.into())
884            .unwrap();
885
886        // Try to read from empty file
887        let mut buffer = alloc::vec![0u8; 100];
888        let result = BaseOperations::read(fs, &mut context, &mut buffer, 0);
889
890        // Should either read 0 bytes or return an error
891        if let Ok(bytes_read) = result {
892            assert_eq!(bytes_read, 0, "Should read 0 bytes from empty file");
893        }
894
895        // Cleanup
896        BaseOperations::close(fs, &mut context).unwrap();
897        fs.remove(test_file).unwrap();
898    }
899
900    /// Test large file operations
901    pub fn test_large_file_operations<F>(fs: &F)
902    where
903        F: FileSystemOperations,
904    {
905        use crate::BaseOperations;
906
907        let test_file = Path::new("/large_test.txt");
908        let mut context = Context::new_empty();
909
910        fs.create_file(test_file).unwrap();
911        fs.lookup_file(&mut context, test_file, AccessFlags::READ_WRITE.into())
912            .unwrap();
913
914        // Write a larger amount of data
915        let large_data = alloc::vec![0xABu8; 4096];
916        let write_result = BaseOperations::write(fs, &mut context, &large_data, 0);
917
918        if let Ok(bytes_written) = write_result {
919            // Read back the data
920            let mut read_buffer = alloc::vec![0u8; bytes_written];
921            let read_result = BaseOperations::read(fs, &mut context, &mut read_buffer, 0);
922
923            if let Ok(bytes_read) = read_result {
924                assert_eq!(
925                    bytes_read, bytes_written,
926                    "Should read back same amount written"
927                );
928                assert_eq!(
929                    &read_buffer[..bytes_read],
930                    &large_data[..bytes_written],
931                    "Read data should match written data"
932                );
933            }
934        }
935
936        // Cleanup
937        BaseOperations::close(fs, &mut context).unwrap();
938        fs.remove(test_file).unwrap();
939    }
940
941    /// Test directory read operations
942    pub fn test_directory_read_operations<F>(fs: &F)
943    where
944        F: FileSystemOperations,
945    {
946        let test_dir = Path::new("/read_dir_test");
947        let file1 = Path::new("/read_dir_test/file1.txt");
948        let file2 = Path::new("/read_dir_test/file2.txt");
949        let file3 = Path::new("/read_dir_test/file3.txt");
950        let mut context = Context::new_empty();
951
952        // Create directory and files
953        fs.create_directory(test_dir).unwrap();
954        fs.create_file(file1).unwrap();
955        fs.create_file(file2).unwrap();
956        fs.create_file(file3).unwrap();
957
958        // Open directory for reading
959        fs.lookup_directory(&mut context, test_dir).unwrap();
960
961        // Read directory entries
962        let mut entry_count = 0;
963        let mut found_files = alloc::vec::Vec::new();
964
965        loop {
966            let result = DirectoryOperations::read(fs, &mut context);
967            match result {
968                Ok(Some(entry)) => {
969                    entry_count += 1;
970                    found_files.push(entry.name.clone());
971                }
972                Ok(None) => break, // End of directory
973                Err(_) => break,   // Error reading
974            }
975
976            // Safety limit to prevent infinite loops
977            if entry_count > 100 {
978                break;
979            }
980        }
981
982        // Should find at least the files we created (may also include . and ..)
983        assert!(entry_count >= 3, "Should find at least the 3 created files");
984
985        // Cleanup
986        DirectoryOperations::close(fs, &mut context).unwrap();
987        fs.remove(file1).unwrap();
988        fs.remove(file2).unwrap();
989        fs.remove(file3).unwrap();
990        fs.remove(test_dir).unwrap();
991    }
992
993    /// Test directory position operations
994    pub fn test_directory_position_operations<F>(fs: &F)
995    where
996        F: FileSystemOperations,
997    {
998        let test_dir = Path::new("/pos_dir_test");
999        let file1 = Path::new("/pos_dir_test/a.txt");
1000        let file2 = Path::new("/pos_dir_test/b.txt");
1001        let mut context = Context::new_empty();
1002
1003        // Create directory and files
1004        fs.create_directory(test_dir).unwrap();
1005        fs.create_file(file1).unwrap();
1006        fs.create_file(file2).unwrap();
1007
1008        // Open directory
1009        fs.lookup_directory(&mut context, test_dir).unwrap();
1010
1011        // Read first entry
1012        let first_result = DirectoryOperations::read(fs, &mut context);
1013        first_result.unwrap();
1014
1015        // Get position
1016        let pos_result = fs.get_position(&mut context);
1017        if pos_result.is_ok() {
1018            // Set position back to start
1019            let set_result = DirectoryOperations::set_position(fs, &mut context, 0);
1020            assert!(
1021                set_result.is_ok() || matches!(set_result, Err(Error::UnsupportedOperation)),
1022                "Should set position or be unsupported"
1023            );
1024        }
1025
1026        // Test rewind
1027        let rewind_result = fs.rewind(&mut context);
1028        assert!(
1029            rewind_result.is_ok() || matches!(rewind_result, Err(Error::UnsupportedOperation)),
1030            "Should rewind or be unsupported"
1031        );
1032
1033        // Cleanup
1034        DirectoryOperations::close(fs, &mut context).unwrap();
1035        fs.remove(file1).unwrap();
1036        fs.remove(file2).unwrap();
1037        fs.remove(test_dir).unwrap();
1038    }
1039
1040    /// Test directory rewind and re-read
1041    pub fn test_directory_rewind<F>(fs: &F)
1042    where
1043        F: FileSystemOperations,
1044    {
1045        let test_dir = Path::new("/rewind_test");
1046        let file = Path::new("/rewind_test/file.txt");
1047        let mut context = Context::new_empty();
1048
1049        // Create directory and file
1050        fs.create_directory(test_dir).unwrap();
1051        fs.create_file(file).unwrap();
1052
1053        // Open directory
1054        fs.lookup_directory(&mut context, test_dir).unwrap();
1055
1056        // Read all entries
1057        let mut first_pass_count = 0;
1058        while let Ok(Some(_)) = DirectoryOperations::read(fs, &mut context) {
1059            first_pass_count += 1;
1060            if first_pass_count > 100 {
1061                break;
1062            }
1063        }
1064
1065        // Rewind
1066        if fs.rewind(&mut context).is_ok() {
1067            // Read again
1068            let mut second_pass_count = 0;
1069            while let Ok(Some(_)) = DirectoryOperations::read(fs, &mut context) {
1070                second_pass_count += 1;
1071                if second_pass_count > 100 {
1072                    break;
1073                }
1074            }
1075
1076            assert_eq!(
1077                first_pass_count, second_pass_count,
1078                "Should read same number of entries after rewind"
1079            );
1080        }
1081
1082        // Cleanup
1083        DirectoryOperations::close(fs, &mut context).unwrap();
1084        fs.remove(file).unwrap();
1085        fs.remove(test_dir).unwrap();
1086    }
1087
1088    /// Test reading empty directory
1089    pub fn test_empty_directory_read<F>(fs: &F)
1090    where
1091        F: FileSystemOperations,
1092    {
1093        let test_dir = Path::new("/empty_dir");
1094        let mut context = Context::new_empty();
1095
1096        fs.create_directory(test_dir).unwrap();
1097        fs.lookup_directory(&mut context, test_dir).unwrap();
1098
1099        // Read from empty directory (may have . and .. entries)
1100        let result = DirectoryOperations::read(fs, &mut context);
1101        // Either returns None immediately or returns . and .. entries
1102        assert!(
1103            result.is_ok() || matches!(result, Err(Error::NotFound)),
1104            "Reading empty directory should succeed or return not found"
1105        );
1106
1107        // Cleanup
1108        DirectoryOperations::close(fs, &mut context).unwrap();
1109        fs.remove(test_dir).unwrap();
1110    }
1111
1112    /// Test directory entry metadata
1113    pub fn test_directory_entry_metadata<F>(fs: &F)
1114    where
1115        F: FileSystemOperations,
1116    {
1117        let test_dir = Path::new("/metadata_dir");
1118        let test_file = Path::new("/metadata_dir/test.txt");
1119        let test_subdir = Path::new("/metadata_dir/subdir");
1120        let mut context = Context::new_empty();
1121
1122        // Create structures
1123        fs.create_directory(test_dir).unwrap();
1124        fs.create_file(test_file).unwrap();
1125        fs.create_directory(test_subdir).unwrap();
1126
1127        // Open and read directory
1128        fs.lookup_directory(&mut context, test_dir).unwrap();
1129
1130        let mut found_file = false;
1131        let mut found_dir = false;
1132
1133        for _ in 0..10 {
1134            match DirectoryOperations::read(fs, &mut context) {
1135                Ok(Some(entry)) => {
1136                    // Check entry has valid metadata
1137                    assert!(!entry.name.is_empty(), "Entry should have a name");
1138
1139                    if entry.name.contains("test.txt") {
1140                        found_file = true;
1141                        // Could check that kind is File if available
1142                    }
1143                    if entry.name.contains("subdir") {
1144                        found_dir = true;
1145                        // Could check that kind is Directory if available
1146                    }
1147                }
1148                Ok(None) => break,
1149                Err(_) => break,
1150            }
1151        }
1152
1153        assert!(found_file, "Should find test file in directory");
1154        assert!(found_dir, "Should find subdirectory in directory");
1155
1156        // Cleanup
1157        DirectoryOperations::close(fs, &mut context).unwrap();
1158        fs.remove(test_file).unwrap();
1159        fs.remove(test_subdir).unwrap();
1160        fs.remove(test_dir).unwrap();
1161    }
1162
1163    /// Test open and close operations
1164    pub fn test_open_close_operations<F>(fs: &F)
1165    where
1166        F: FileSystemOperations,
1167    {
1168        let test_file = Path::new("/open_close_test.txt");
1169        let mut context = Context::new_empty();
1170
1171        // Create file
1172        fs.create_file(test_file).unwrap();
1173
1174        // Open file
1175        let open_result = fs.lookup_file(&mut context, test_file, AccessFlags::Read.into());
1176        open_result.unwrap();
1177
1178        // Close file
1179        let close_result = BaseOperations::close(fs, &mut context);
1180        assert!(
1181            close_result.is_ok() || matches!(close_result, Err(Error::UnsupportedOperation)),
1182            "Should close file or be unsupported"
1183        );
1184
1185        // Open directory
1186        let test_dir = Path::new("/open_close_dir");
1187        fs.create_directory(test_dir).unwrap();
1188
1189        let open_result = fs.lookup_directory(&mut context, test_dir);
1190        open_result.unwrap();
1191
1192        // Close directory
1193        DirectoryOperations::close(fs, &mut context).unwrap();
1194
1195        // Cleanup
1196        fs.remove(test_file).unwrap();
1197        fs.remove(test_dir).unwrap();
1198    }
1199
1200    /// Macro to implement all file system operation tests for a given file system instance.
1201    ///
1202    /// This macro generates individual test functions for each test category, making it easy
1203    /// to integrate comprehensive file system testing into your test suite.
1204    ///
1205    /// # Usage
1206    ///
1207    /// ```rust,ignore
1208    /// use file_system::implement_file_system_tests;
1209    ///
1210    /// mod tests {
1211    ///     use super::*;
1212    ///     
1213    ///     implement_file_system_tests! {
1214    ///         instance: MyFileSystem::new()
1215    ///     }
1216    /// }
1217    /// ```
1218    ///
1219    /// This will generate individual `#[test]` functions for each test category.
1220    #[macro_export]
1221    macro_rules! implement_file_system_tests {
1222        ($fs:expr) => {
1223            // Basic file system structure tests
1224            #[test]
1225            fn test_file_operations() {
1226                let fs = $fs;
1227                $crate::file_system::tests::test_file_operations(&fs);
1228            }
1229
1230            #[test]
1231            fn test_file_creation_edge_cases() {
1232                let fs = $fs;
1233                $crate::file_system::tests::test_file_creation_edge_cases(&fs);
1234            }
1235
1236            #[test]
1237            fn test_directory_operations() {
1238                let fs = $fs;
1239                $crate::file_system::tests::test_directory_operations(&fs);
1240            }
1241
1242            #[test]
1243            fn test_nested_directories() {
1244                let fs = $fs;
1245                $crate::file_system::tests::test_nested_directories(&fs);
1246            }
1247
1248            #[test]
1249            fn test_rename_operations() {
1250                let fs = $fs;
1251                $crate::file_system::tests::test_rename_operations(&fs);
1252            }
1253
1254            #[test]
1255            fn test_rename_edge_cases() {
1256                let fs = $fs;
1257                $crate::file_system::tests::test_rename_edge_cases(&fs);
1258            }
1259
1260            #[test]
1261            fn test_move_between_directories() {
1262                let fs = $fs;
1263                $crate::file_system::tests::test_move_between_directories(&fs);
1264            }
1265
1266            // Attribute operations
1267            #[test]
1268            fn test_attribute_operations() {
1269                let fs = $fs;
1270                $crate::file_system::tests::test_attribute_operations(&fs);
1271            }
1272
1273            #[test]
1274            fn test_directory_attributes() {
1275                let fs = $fs;
1276                $crate::file_system::tests::test_directory_attributes(&fs);
1277            }
1278
1279            // File I/O operations
1280            #[test]
1281            fn test_file_read_operations() {
1282                let fs = $fs;
1283                $crate::file_system::tests::test_file_read_operations(&fs);
1284            }
1285
1286            #[test]
1287            fn test_file_write_operations() {
1288                let fs = $fs;
1289                $crate::file_system::tests::test_file_write_operations(&fs);
1290            }
1291
1292            #[test]
1293            fn test_file_write_pattern() {
1294                let fs = $fs;
1295                $crate::file_system::tests::test_file_write_pattern(&fs);
1296            }
1297
1298            #[test]
1299            fn test_file_write_vectored() {
1300                let fs = $fs;
1301                $crate::file_system::tests::test_file_write_vectored(&fs);
1302            }
1303
1304            #[test]
1305            fn test_file_position_operations() {
1306                let fs = $fs;
1307                $crate::file_system::tests::test_file_position_operations(&fs);
1308            }
1309
1310            #[test]
1311            fn test_file_read_until() {
1312                let fs = $fs;
1313                $crate::file_system::tests::test_file_read_until(&fs);
1314            }
1315
1316            #[test]
1317            fn test_file_flush() {
1318                let fs = $fs;
1319                $crate::file_system::tests::test_file_flush(&fs);
1320            }
1321
1322            #[test]
1323            fn test_empty_file_operations() {
1324                let fs = $fs;
1325                $crate::file_system::tests::test_empty_file_operations(&fs);
1326            }
1327
1328            #[test]
1329            fn test_large_file_operations() {
1330                let fs = $fs;
1331                $crate::file_system::tests::test_large_file_operations(&fs);
1332            }
1333
1334            // Directory I/O operations
1335            #[test]
1336            fn test_directory_read_operations() {
1337                let fs = $fs;
1338                $crate::file_system::tests::test_directory_read_operations(&fs);
1339            }
1340
1341            #[test]
1342            fn test_directory_position_operations() {
1343                let fs = $fs;
1344                $crate::file_system::tests::test_directory_position_operations(&fs);
1345            }
1346
1347            #[test]
1348            fn test_directory_rewind() {
1349                let fs = $fs;
1350                $crate::file_system::tests::test_directory_rewind(&fs);
1351            }
1352
1353            #[test]
1354            fn test_empty_directory_read() {
1355                let fs = $fs;
1356                $crate::file_system::tests::test_empty_directory_read(&fs);
1357            }
1358
1359            #[test]
1360            fn test_directory_entry_metadata() {
1361                let fs = $fs;
1362                $crate::file_system::tests::test_directory_entry_metadata(&fs);
1363            }
1364
1365            #[test]
1366            fn test_open_close_operations() {
1367                let fs = $fs;
1368                $crate::file_system::tests::test_open_close_operations(&fs);
1369            }
1370
1371            // Error handling and edge cases
1372            #[test]
1373            fn test_error_handling() {
1374                let fs = $fs;
1375                $crate::file_system::tests::test_error_handling(&fs);
1376            }
1377
1378            #[test]
1379            fn test_invalid_paths() {
1380                let fs = $fs;
1381                $crate::file_system::tests::test_invalid_paths(&fs);
1382            }
1383
1384            #[test]
1385            fn test_multiple_operations() {
1386                let fs = $fs;
1387                $crate::file_system::tests::test_multiple_operations(&fs);
1388            }
1389
1390            #[test]
1391            fn test_file_system_consistency() {
1392                let fs = $fs;
1393                $crate::file_system::tests::test_file_system_consistency(&fs);
1394            }
1395
1396            #[test]
1397            fn test_lookup_with_flags() {
1398                let fs = $fs;
1399                $crate::file_system::tests::test_lookup_with_flags(&fs);
1400            }
1401
1402            #[test]
1403            fn test_root_directory_operations() {
1404                let fs = $fs;
1405                $crate::file_system::tests::test_root_directory_operations(&fs);
1406            }
1407
1408            #[test]
1409            fn test_long_filenames() {
1410                let fs = $fs;
1411                $crate::file_system::tests::test_long_filenames(&fs);
1412            }
1413
1414            #[test]
1415            fn test_special_characters_in_names() {
1416                let fs = $fs;
1417                $crate::file_system::tests::test_special_characters_in_names(&fs);
1418            }
1419        };
1420    }
1421    pub use implement_file_system_tests;
1422    use users::UserIdentifier;
1423}