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 fn remove(&self, path: &Path) -> Result<()>;
36
37 fn rename(&self, source: &Path, destination: &Path) -> Result<()>;
59
60 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 use super::*;
163 use crate::{
164 AccessFlags, Attributes, BaseOperations, Context, Error, Kind, Permissions, Size, Time,
165 };
166
167 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 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 fs.create_file(test_file).unwrap();
191
192 fs.lookup_file(&mut context, test_file, AccessFlags::Read.into())
194 .unwrap();
195
196 fs.remove(test_file).unwrap();
198
199 fs.lookup_file(&mut context, test_file, AccessFlags::Read.into())
201 .unwrap_err();
202 }
203
204 pub fn test_file_creation_edge_cases<F>(fs: &F)
206 where
207 F: FileSystemOperations,
208 {
209 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 fs.remove(test_file).unwrap();
221 }
222
223 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 fs.create_directory(test_dir).unwrap();
233
234 fs.lookup_directory(&mut context, test_dir).unwrap();
236
237 DirectoryOperations::close(fs, &mut context).unwrap();
238
239 fs.remove(test_dir).unwrap();
241
242 let mut context = Context::new_empty();
243
244 fs.lookup_directory(&mut context, test_dir).unwrap_err();
246 }
247
248 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 fs.create_directory(parent_dir).unwrap();
258
259 fs.create_directory(child_dir).unwrap();
261
262 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 fs.remove(child_dir).unwrap();
271
272 fs.remove(parent_dir).unwrap();
274 }
275
276 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 fs.create_file(source).unwrap();
287
288 fs.rename(source, destination).unwrap();
290
291 fs.lookup_file(&mut context, source, AccessFlags::Read.into())
293 .unwrap_err();
294
295 assert!(
297 fs.lookup_file(&mut context, destination, AccessFlags::Read.into())
298 .is_ok(),
299 "Destination file should exist after rename"
300 );
301
302 fs.remove(destination).unwrap();
304 }
305
306 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 let result = fs.rename(source, destination);
316 assert_eq!(result, Err(Error::NotFound),);
317
318 fs.create_file(source).unwrap();
320 fs.create_file(destination).unwrap();
321
322 fs.remove(source).unwrap();
331 fs.remove(destination).unwrap();
332 }
333
334 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 fs.create_directory(dir1).unwrap();
346 fs.create_directory(dir2).unwrap();
347
348 fs.create_file(file_in_dir1).unwrap();
350
351 fs.rename(file_in_dir1, file_in_dir2).unwrap();
353
354 let mut context = Context::new_empty();
356 fs.lookup_file(&mut context, file_in_dir2, AccessFlags::Read.into())
357 .unwrap();
358
359 fs.remove(file_in_dir2).unwrap();
361 fs.remove(dir2).unwrap();
362 fs.remove(dir1).unwrap();
363 }
364
365 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 fs.create_file(test_file).unwrap();
374
375 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 let mut attrs = Attributes::new();
387 FileSystemOperations::get_attributes(fs, test_file, &mut attrs).unwrap();
388
389 let mut updated_attrs = Attributes::new();
391 FileSystemOperations::get_attributes(fs, test_file, &mut updated_attrs).unwrap();
392
393 fs.remove(test_file).unwrap();
395 }
396
397 pub fn test_directory_attributes<F>(fs: &F)
399 where
400 F: FileSystemOperations,
401 {
402 let test_dir = Path::new("/attr_dir");
403
404 fs.create_directory(test_dir).unwrap();
406
407 let mut attrs = Attributes::new();
409 FileSystemOperations::get_attributes(fs, test_dir, &mut attrs).unwrap();
410
411 if let Some(kind) = attrs.get_kind() {
413 assert_eq!(*kind, Kind::Directory, "Should be a directory type");
414 }
415
416 fs.remove(test_dir).unwrap();
418 }
419
420 pub fn test_error_handling<F>(fs: &F)
422 where
423 F: FileSystemOperations,
424 {
425 let mut context = Context::new_empty();
426
427 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 let mut attrs = Attributes::new();
439 FileSystemOperations::get_attributes(fs, nonexistent, &mut attrs).unwrap_err();
440 }
441
442 pub fn test_invalid_paths<F>(fs: &F)
444 where
445 F: FileSystemOperations,
446 {
447 let path = Path::new("/.txt");
452 let file_result = fs.create_file(path);
454 if file_result.is_ok() {
456 fs.remove(path).unwrap();
457 }
458 }
459
460 pub fn test_multiple_operations<F>(fs: &F)
462 where
463 F: FileSystemOperations,
464 {
465 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 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 for file_path in &files {
488 let path = Path::new(file_path);
489 fs.remove(path).unwrap();
490 }
491 }
492
493 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 fs.create_directory(dir).unwrap();
503 fs.create_file(file).unwrap();
504
505 let result = fs.create_file(dir);
507 result.unwrap_err();
508
509 let result = fs.create_directory(file);
511 result.unwrap_err();
512
513 fs.remove(file).unwrap();
515 fs.remove(dir).unwrap();
516 }
517
518 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 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 fs.remove(test_file).unwrap();
542 }
543
544 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 fs.lookup_directory(&mut context, root).unwrap();
554
555 let result = fs.remove(root);
557 result.unwrap_err();
558
559 let mut attrs = Attributes::new();
561 FileSystemOperations::get_attributes(fs, root, &mut attrs).unwrap();
562 }
563
564 pub fn test_long_filenames<F>(fs: &F)
566 where
567 F: FileSystemOperations,
568 {
569 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 if result.is_ok() {
576 fs.remove(path).unwrap();
577 }
578 }
579
580 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 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 fs.create_file(test_file).unwrap();
612 fs.lookup_file(&mut context, test_file, AccessFlags::Write.into())
613 .unwrap();
614
615 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 BaseOperations::close(fs, &mut context).unwrap();
627 fs.lookup_file(&mut context, test_file, AccessFlags::Read.into())
628 .unwrap();
629
630 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 BaseOperations::close(fs, &mut context).unwrap();
647 fs.remove(test_file).unwrap();
648 }
649
650 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 let data1 = b"First write. ";
666 let result = BaseOperations::write(fs, &mut context, data1, 0);
667 result.unwrap();
668
669 let data2 = b"Second write.";
671 let result = BaseOperations::write(fs, &mut context, data2, data1.len() as Size);
672 result.unwrap();
673
674 let overwrite_data = b"OVERWRITE";
676 let result = BaseOperations::write(fs, &mut context, overwrite_data, 0);
677 result.unwrap();
678
679 BaseOperations::close(fs, &mut context).unwrap();
681 fs.remove(test_file).unwrap();
682 }
683
684 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 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 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 BaseOperations::close(fs, &mut context).unwrap();
721 fs.remove(test_file).unwrap();
722 }
723
724 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 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 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 BaseOperations::close(fs, &mut context).unwrap();
764 fs.remove(test_file).unwrap();
765 }
766
767 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 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 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 BaseOperations::close(fs, &mut context).unwrap();
807 fs.remove(test_file).unwrap();
808 }
809
810 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 let test_data = b"Line 1\nLine 2\nLine 3\n";
826 BaseOperations::write(fs, &mut context, test_data, 0).unwrap();
827
828 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 BaseOperations::close(fs, &mut context).unwrap();
839 fs.remove(test_file).unwrap();
840 }
841
842 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 let data = b"Data to flush";
858 BaseOperations::write(fs, &mut context, data, 0).unwrap();
859
860 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 BaseOperations::close(fs, &mut context).unwrap();
869 fs.remove(test_file).unwrap();
870 }
871
872 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 let mut buffer = alloc::vec![0u8; 100];
888 let result = BaseOperations::read(fs, &mut context, &mut buffer, 0);
889
890 if let Ok(bytes_read) = result {
892 assert_eq!(bytes_read, 0, "Should read 0 bytes from empty file");
893 }
894
895 BaseOperations::close(fs, &mut context).unwrap();
897 fs.remove(test_file).unwrap();
898 }
899
900 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 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 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 BaseOperations::close(fs, &mut context).unwrap();
938 fs.remove(test_file).unwrap();
939 }
940
941 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 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 fs.lookup_directory(&mut context, test_dir).unwrap();
960
961 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, Err(_) => break, }
975
976 if entry_count > 100 {
978 break;
979 }
980 }
981
982 assert!(entry_count >= 3, "Should find at least the 3 created files");
984
985 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 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 fs.create_directory(test_dir).unwrap();
1005 fs.create_file(file1).unwrap();
1006 fs.create_file(file2).unwrap();
1007
1008 fs.lookup_directory(&mut context, test_dir).unwrap();
1010
1011 let first_result = DirectoryOperations::read(fs, &mut context);
1013 first_result.unwrap();
1014
1015 let pos_result = fs.get_position(&mut context);
1017 if pos_result.is_ok() {
1018 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 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 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 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 fs.create_directory(test_dir).unwrap();
1051 fs.create_file(file).unwrap();
1052
1053 fs.lookup_directory(&mut context, test_dir).unwrap();
1055
1056 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 if fs.rewind(&mut context).is_ok() {
1067 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 DirectoryOperations::close(fs, &mut context).unwrap();
1084 fs.remove(file).unwrap();
1085 fs.remove(test_dir).unwrap();
1086 }
1087
1088 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 let result = DirectoryOperations::read(fs, &mut context);
1101 assert!(
1103 result.is_ok() || matches!(result, Err(Error::NotFound)),
1104 "Reading empty directory should succeed or return not found"
1105 );
1106
1107 DirectoryOperations::close(fs, &mut context).unwrap();
1109 fs.remove(test_dir).unwrap();
1110 }
1111
1112 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 fs.create_directory(test_dir).unwrap();
1124 fs.create_file(test_file).unwrap();
1125 fs.create_directory(test_subdir).unwrap();
1126
1127 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 assert!(!entry.name.is_empty(), "Entry should have a name");
1138
1139 if entry.name.contains("test.txt") {
1140 found_file = true;
1141 }
1143 if entry.name.contains("subdir") {
1144 found_dir = true;
1145 }
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 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 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 fs.create_file(test_file).unwrap();
1173
1174 let open_result = fs.lookup_file(&mut context, test_file, AccessFlags::Read.into());
1176 open_result.unwrap();
1177
1178 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 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 DirectoryOperations::close(fs, &mut context).unwrap();
1194
1195 fs.remove(test_file).unwrap();
1197 fs.remove(test_dir).unwrap();
1198 }
1199
1200 #[macro_export]
1221 macro_rules! implement_file_system_tests {
1222 ($fs:expr) => {
1223 #[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 #[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 #[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 #[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 #[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}