1use std::collections::BTreeMap;
4use std::path::{Path, PathBuf};
5
6use anyhow::anyhow;
7
8use crate::core::{FsBackend, Result, utils};
9use crate::{Entry, EntryType};
10
11pub struct MapFS {
74 root: PathBuf, cwd: PathBuf, entries: BTreeMap<PathBuf, Entry>, }
78
79impl MapFS {
80 pub fn new() -> Self {
83 Self {
84 root: PathBuf::from("/"),
85 cwd: PathBuf::from("/"),
86 entries: BTreeMap::new(),
87 }
88 }
89
90 pub fn set_root<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
94 let path = path.as_ref();
95 if !path.is_absolute() {
96 return Err(anyhow!("root path must be an absolute"));
97 }
98 self.root = path.to_path_buf();
99 Ok(())
100 }
101
102 fn to_inner<P: AsRef<Path>>(&self, inner_path: P) -> PathBuf {
103 utils::normalize(self.cwd.join(inner_path))
104 }
105}
106
107impl FsBackend for MapFS {
108 fn root(&self) -> &Path {
110 self.root.as_path()
111 }
112
113 fn cwd(&self) -> &Path {
115 self.cwd.as_path()
116 }
117
118 fn to_host<P: AsRef<Path>>(&self, inner_path: P) -> Result<PathBuf> {
121 let inner = self.to_inner(inner_path);
122 Ok(self.root.join(inner.strip_prefix("/")?))
123 }
124
125 fn cd<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
129 let target = self.to_inner(path);
130 if !self.is_dir(&target)? {
131 return Err(anyhow!("{} not a directory", target.display()));
132 }
133 self.cwd = target;
134 Ok(())
135 }
136
137 fn exists<P: AsRef<Path>>(&self, path: P) -> bool {
140 let inner = self.to_inner(path);
141 utils::is_virtual_root(&inner) || self.entries.contains_key(&inner)
142 }
143
144 fn is_dir<P: AsRef<Path>>(&self, path: P) -> Result<bool> {
146 let path = path.as_ref();
147 let inner = self.to_inner(path);
148 if !self.exists(&inner) {
149 return Err(anyhow!("{} does not exist", path.display()));
150 }
151 Ok(utils::is_virtual_root(&inner) || self.entries[&inner].is_dir())
152 }
153
154 fn is_file<P: AsRef<Path>>(&self, path: P) -> Result<bool> {
156 let path = path.as_ref();
157 let inner = self.to_inner(path);
158 if !self.exists(&inner) {
159 return Err(anyhow!("{} does not exist", path.display()));
160 }
161 Ok(!utils::is_virtual_root(&inner) && self.entries[&inner].is_file())
162 }
163
164 fn ls<P: AsRef<Path>>(&self, path: P) -> Result<impl Iterator<Item = &Path>> {
208 let inner_path = self.to_inner(path);
209 if !self.exists(&inner_path) {
210 return Err(anyhow!("{} does not exist", inner_path.display()));
211 }
212 let is_file = self.is_file(&inner_path)?;
213 let component_count = if is_file {
214 inner_path.components().count()
215 } else {
216 inner_path.components().count() + 1
217 };
218 Ok(self
219 .entries
220 .iter()
221 .map(|(pb, _)| pb.as_path())
222 .filter(move |&path| {
223 path.starts_with(&inner_path)
224 && (path != inner_path || is_file)
225 && path.components().count() == component_count
226 }))
227 }
228
229 fn tree<P: AsRef<Path>>(&self, path: P) -> Result<impl Iterator<Item = &Path>> {
276 let inner_path = self.to_inner(path);
277 if !self.exists(&inner_path) {
278 return Err(anyhow!("{} does not exist", inner_path.display()));
279 }
280 let is_file = self.is_file(&inner_path)?;
281 Ok(self
282 .entries
283 .iter()
284 .map(|(pb, _)| pb.as_path())
285 .filter(move |&path| path.starts_with(&inner_path) && (path != inner_path || is_file)))
286 }
287
288 fn mkdir<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
291 if path.as_ref().as_os_str().is_empty() {
292 return Err(anyhow!("invalid path: empty"));
293 }
294
295 let inner_path = self.to_inner(path);
296
297 if self.exists(&inner_path) {
298 return Err(anyhow!("path already exists: {}", inner_path.display()));
299 }
300
301 let mut existed_parent = inner_path.clone();
303 while let Some(parent) = existed_parent.parent() {
304 let parent_buf = parent.to_path_buf();
305 if self.exists(parent) {
306 existed_parent = parent_buf;
307 break;
308 }
309 existed_parent = parent_buf;
310 }
311
312 let need_to_create: Vec<_> = inner_path
314 .strip_prefix(&existed_parent)?
315 .components()
316 .collect();
317
318 let mut built = PathBuf::from(&existed_parent);
319 for component in need_to_create {
320 built.push(component);
321 if !self.exists(&built) {
322 self.entries
323 .insert(built.clone(), Entry::new(EntryType::Directory));
324 }
325 }
326
327 Ok(())
328 }
329
330 fn mkfile<P: AsRef<Path>>(&mut self, file_path: P, content: Option<&[u8]>) -> Result<()> {
335 let file_path = self.to_inner(file_path);
336 if self.exists(&file_path) {
337 return Err(anyhow!("{} already exist", file_path.display()));
338 }
339 if let Some(parent) = file_path.parent() {
340 if !self.exists(parent) {
341 self.mkdir(parent)?;
342 }
343 }
344
345 let mut entry = Entry::new(EntryType::File);
346 if let Some(content) = content {
347 entry.set_content(content);
348 }
349 self.entries.insert(file_path.clone(), entry);
350
351 Ok(())
352 }
353
354 fn read<P: AsRef<Path>>(&self, path: P) -> Result<Vec<u8>> {
367 let path = path.as_ref();
368 if self.is_dir(path)? {
369 return Err(anyhow!("{} is a directory", path.display()));
371 }
372 Ok(self.entries[path].content().cloned().unwrap_or(Vec::new()))
373 }
374
375 fn write<P: AsRef<Path>>(&mut self, path: P, content: &[u8]) -> Result<()> {
389 let path = path.as_ref();
390 if self.is_dir(path)? {
391 return Err(anyhow!("{} is a directory", path.display()));
393 }
394 self.entries.get_mut(path).unwrap().set_content(content); Ok(())
396 }
397
398 fn append<P: AsRef<Path>>(&mut self, path: P, content: &[u8]) -> Result<()> {
414 let path = path.as_ref();
415 if self.is_dir(path)? {
416 return Err(anyhow!("{} is a directory", path.display()));
418 }
419 self.entries.get_mut(path).unwrap().append_content(content); Ok(())
421 }
422
423 fn rm<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
432 if path.as_ref().as_os_str().is_empty() {
433 return Err(anyhow!("invalid path: empty"));
434 }
435 if utils::is_virtual_root(&path) {
436 return Err(anyhow!("invalid path: the root cannot be removed"));
437 }
438
439 let inner_path = self.to_inner(path); if !self.exists(&inner_path) {
443 return Err(anyhow!("{} does not exist", inner_path.display()));
444 }
445
446 let removed: Vec<PathBuf> = self
448 .entries
449 .iter()
450 .map(|(pb, _)| pb)
451 .filter(|&pb| pb.starts_with(&inner_path)) .cloned()
453 .collect();
454
455 for p in &removed {
457 self.entries.remove(p);
458 }
459
460 Ok(())
461 }
462
463 fn cleanup(&mut self) -> bool {
465 self.entries.clear();
466 true
467 }
468}
469
470#[cfg(test)]
471mod tests {
472 use super::*;
473
474 mod creations {
475 use super::*;
476
477 #[test]
478 fn test_new_map_fs() {
479 let mut fs = MapFS::new();
480 assert_eq!(fs.root(), "/");
481 assert_eq!(fs.cwd(), "/");
482
483 #[cfg(unix)]
484 {
485 fs.set_root("/new/root").unwrap();
486 assert_eq!(fs.root(), "/new/root");
487
488 let host_path = fs.to_host("/inner/path").unwrap();
489 assert_eq!(host_path.as_path(), "/new/root/inner/path");
490 }
491 #[cfg(windows)]
492 {
493 fs.set_root("c:\\new\\root").unwrap();
494 assert_eq!(fs.root(), "c:\\new\\root");
495
496 let host_path = fs.to_host("\\inner\\path").unwrap();
497 assert_eq!(host_path.as_path(), "c:\\new\\root\\inner\\path");
498 }
499
500 let result = fs.set_root("new/relative/root");
501 assert!(result.is_err());
502 }
503 }
504
505 mod cd {
506 use super::*;
507
508 fn setup_test_vfs() -> MapFS {
510 let mut vfs = MapFS::new(); vfs.mkdir("/home").unwrap();
514 vfs.mkdir("/home/user").unwrap();
515 vfs.mkdir("/etc").unwrap();
516 vfs.mkfile("/home/user/config.txt", Some(b"Config content"))
517 .unwrap();
518
519 vfs
520 }
521
522 #[test]
523 fn test_cd_absolute_path_success() -> Result<()> {
524 let mut vfs = setup_test_vfs();
525
526 assert_eq!(vfs.cwd, Path::new("/")); vfs.cd("/home/user")?;
529
530 assert_eq!(vfs.cwd, Path::new("/home/user"));
531 Ok(())
532 }
533
534 #[test]
535 fn test_cd_relative_path_success() -> Result<()> {
536 let mut vfs = setup_test_vfs();
537
538 vfs.cd("/home")?; assert_eq!(vfs.cwd, Path::new("/home"));
540
541 vfs.cd("user")?; assert_eq!(vfs.cwd, Path::new("/home/user"));
544 Ok(())
545 }
546
547 #[test]
548 fn test_cd_root_directory() -> Result<()> {
549 let mut vfs = setup_test_vfs();
550
551 vfs.cd("/")?;
552
553 assert_eq!(vfs.cwd, Path::new("/"));
554 Ok(())
555 }
556
557 #[test]
558 fn test_cd_nonexistent_path_error() -> Result<()> {
559 let mut vfs = setup_test_vfs();
560
561 let result = vfs.cd("/nonexistent/path");
562 assert!(result.is_err());
563 assert!(
564 result.unwrap_err().to_string().contains("does not exist"),
565 "Error message should indicate path does not exist"
566 );
567
568 assert_eq!(vfs.cwd, Path::new("/"));
570 Ok(())
571 }
572
573 #[test]
574 fn test_cd_file_path_error() -> Result<()> {
575 let mut vfs = setup_test_vfs();
576
577 let result = vfs.cd("/home/user/config.txt");
578 assert!(result.is_err());
579 assert!(
580 result.unwrap_err().to_string().contains("not a directory"),
581 "Even though the file exists, cd() should fail because it's not a directory"
582 );
583
584 assert_eq!(vfs.cwd, Path::new("/"));
586 Ok(())
587 }
588
589 #[test]
590 fn test_cd_same_directory() -> Result<()> {
591 let mut vfs = setup_test_vfs();
592
593 vfs.cd("/home")?;
594 assert_eq!(vfs.cwd, Path::new("/home"));
595
596 vfs.cd("/home")?; assert_eq!(vfs.cwd, Path::new("/home")); Ok(())
600 }
601
602 #[test]
603 fn test_cd_deep_nested_path() -> Result<()> {
604 let mut vfs = setup_test_vfs();
605
606 vfs.cd("/home/user")?;
607
608 assert_eq!(vfs.cwd, Path::new("/home/user"));
609 Ok(())
610 }
611
612 #[test]
613 fn test_cd_sequential_changes() -> Result<()> {
614 let mut vfs = setup_test_vfs();
615
616 vfs.cd("/etc")?;
617 assert_eq!(vfs.cwd, Path::new("/etc"));
618
619 vfs.cd("/home")?;
620 assert_eq!(vfs.cwd, Path::new("/home"));
621
622 vfs.cd("/")?;
623 assert_eq!(vfs.cwd, Path::new("/"));
624
625 Ok(())
626 }
627
628 #[test]
629 fn test_cd_with_trailing_slash() -> Result<()> {
630 let mut vfs = setup_test_vfs();
631
632 vfs.cd("/home/")?;
634 assert_eq!(vfs.cwd, Path::new("/home"));
635
636 vfs.cd("/home/user//")?;
637 assert_eq!(vfs.cwd, Path::new("/home/user"));
638 Ok(())
639 }
640 }
641
642 mod exists {
643 use super::*;
644
645 fn setup_test_vfs() -> MapFS {
647 let mut vfs = MapFS::new();
648
649 vfs.mkdir("/etc").unwrap();
651 vfs.mkdir("/home").unwrap();
652 vfs.mkdir("/home/user").unwrap();
653 vfs.mkfile("/home/user/file.txt", Some(b"Hello")).unwrap();
654 vfs.mkfile("/readme.md", Some(b"Project docs")).unwrap();
655
656 vfs
657 }
658
659 #[test]
660 fn test_exists_absolute_path_file() {
661 let vfs = setup_test_vfs();
662 assert!(vfs.exists("/home/user/file.txt"));
663 }
664
665 #[test]
666 fn test_exists_absolute_path_directory() {
667 let vfs = setup_test_vfs();
668 assert!(vfs.exists("/home/user"));
669 }
670
671 #[test]
672 fn test_exists_root_directory() {
673 let vfs = setup_test_vfs();
674 assert!(vfs.exists("/"));
675 }
676
677 #[test]
678 fn test_exists_relative_path_from_root() {
679 let vfs = setup_test_vfs();
680 assert!(vfs.exists("home/user/file.txt"));
682 }
683
684 #[test]
685 fn test_exists_relative_path_nested() {
686 let mut vfs = setup_test_vfs();
687 vfs.cd("/home/user").unwrap(); assert!(vfs.exists("file.txt")); }
690
691 #[test]
692 fn test_exists_nonexistent_file() {
693 let vfs = setup_test_vfs();
694 assert!(!vfs.exists("/home/user/nonexistent.txt"));
695 }
696
697 #[test]
698 fn test_exists_nonexistent_directory() {
699 let vfs = setup_test_vfs();
700 assert!(!vfs.exists("/tmp"));
701 }
702
703 #[test]
704 fn test_exists_partial_path() {
705 let vfs = setup_test_vfs();
706 assert!(!vfs.exists("/home/us"));
708 }
709
710 #[test]
711 fn test_exists_with_trailing_slash() {
712 let vfs = setup_test_vfs();
713 assert!(vfs.exists("/home/")); assert!(vfs.exists("/home/user/")); assert!(vfs.exists("/readme.md/")); }
717
718 #[test]
719 fn test_exists_case_sensitivity() {
720 #[cfg(unix)]
721 {
722 let mut vfs = setup_test_vfs();
723 vfs.mkdir("/Home/User").unwrap();
725
726 assert!(vfs.exists("/Home/User"));
727 assert!(!vfs.exists("/home/User")); }
729 }
730
731 #[test]
732 fn test_exists_empty_string() {
733 let vfs = setup_test_vfs();
734 assert!(vfs.exists(""));
736 }
737
738 #[test]
739 fn test_exists_dot_path() {
740 let vfs = setup_test_vfs();
741 assert!(vfs.exists(".")); assert!(vfs.exists("./home")); }
744
745 #[test]
746 fn test_exists_double_dot_path() {
747 let mut vfs = setup_test_vfs();
748 vfs.cd("/home/user").unwrap();
749 assert!(vfs.exists("..")); assert!(vfs.exists("../user")); assert!(vfs.exists("../../etc")); }
753 }
754
755 mod is_dir_file {
756 use super::*;
757
758 fn setup_test_vfs() -> MapFS {
760 let mut vfs = MapFS::new();
761
762 vfs.mkdir("/etc").unwrap();
764 vfs.mkdir("/home").unwrap();
765 vfs.mkdir("/home/user").unwrap();
766 vfs.mkfile("/home/user/file.txt", Some(b"Hello")).unwrap();
767 vfs.mkfile("/readme.md", Some(b"Project docs")).unwrap();
768 vfs.mkfile("/empty.bin", None).unwrap(); vfs
771 }
772
773 #[test]
774 fn test_is_dir_existing_directory_absolute() -> Result<()> {
775 let vfs = setup_test_vfs();
776 assert!(vfs.is_dir("/home")?);
777 assert!(vfs.is_dir("/home/user")?);
778 assert!(vfs.is_dir("/")?); Ok(())
780 }
781
782 #[test]
783 fn test_is_dir_existing_directory_relative() -> Result<()> {
784 let vfs = setup_test_vfs();
785 assert!(vfs.is_dir("home")?);
787 let mut vfs2 = setup_test_vfs();
789 vfs2.cd("/home").unwrap();
790 assert!(vfs2.is_dir("user")?);
791 Ok(())
792 }
793
794 #[test]
795 fn test_is_dir_file_path() -> Result<()> {
796 let vfs = setup_test_vfs();
797 assert!(!vfs.is_dir("/home/user/file.txt")?);
798 assert!(!vfs.is_dir("/readme.md")?);
799 Ok(())
800 }
801
802 #[test]
803 fn test_is_dir_nonexistent_path() {
804 let vfs = setup_test_vfs();
805 let result = vfs.is_dir("/nonexistent");
806 assert!(result.is_err());
807 assert!(
808 result.unwrap_err().to_string().contains("does not exist"),
809 "Error should mention path does not exist"
810 );
811 }
812
813 #[test]
814 fn test_is_file_existing_file_absolute() -> Result<()> {
815 let vfs = setup_test_vfs();
816 assert!(vfs.is_file("/home/user/file.txt")?);
817 assert!(vfs.is_file("/readme.md")?);
818 assert!(vfs.is_file("/empty.bin")?); Ok(())
820 }
821
822 #[test]
823 fn test_is_file_existing_file_relative() -> Result<()> {
824 let vfs = setup_test_vfs();
825 assert!(vfs.is_file("readme.md")?);
827 let mut vfs2 = setup_test_vfs();
829 vfs2.cd("/home/user").unwrap();
830 assert!(vfs2.is_file("file.txt")?);
831 Ok(())
832 }
833
834 #[test]
835 fn test_is_file_directory_path() -> Result<()> {
836 let vfs = setup_test_vfs();
837 assert!(!vfs.is_file("/home")?);
838 assert!(!vfs.is_file("/home/user")?);
839 assert!(!vfs.is_file("/")?); Ok(())
841 }
842
843 #[test]
844 fn test_is_file_nonexistent_path() {
845 let vfs = setup_test_vfs();
846 let result = vfs.is_file("/nonexistent.txt");
847 assert!(result.is_err());
848 assert!(
849 result.unwrap_err().to_string().contains("does not exist"),
850 "Error should mention path does not exist"
851 );
852 }
853
854 #[test]
855 fn test_is_dir_and_is_file_on_same_file() -> Result<()> {
856 let vfs = setup_test_vfs();
857 let file_path = "/home/user/file.txt";
858
859 assert!(!vfs.is_dir(file_path)?); assert!(vfs.is_file(file_path)?); Ok(())
863 }
864
865 #[test]
866 fn test_is_dir_and_is_file_on_same_directory() -> Result<()> {
867 let vfs = setup_test_vfs();
868 let dir_path = "/home/user";
869
870 assert!(vfs.is_dir(dir_path)?); assert!(!vfs.is_file(dir_path)?); Ok(())
874 }
875
876 #[test]
877 fn test_is_dir_with_trailing_slash() -> Result<()> {
878 let vfs = setup_test_vfs();
879 assert!(vfs.is_dir("/home/")?); assert!(vfs.is_dir("/home/user/")?);
881 Ok(())
882 }
883
884 #[test]
885 fn test_is_file_with_trailing_slash() -> Result<()> {
886 let vfs = setup_test_vfs();
887 assert!(vfs.is_file("/readme.md/")?);
889 assert!(vfs.is_file("/home/user/file.txt/")?);
890 Ok(())
891 }
892
893 #[test]
894 fn test_is_dir_dot_path() -> Result<()> {
895 let mut vfs = setup_test_vfs();
896 vfs.cd("/home").unwrap();
897
898 assert!(vfs.is_dir(".")?); assert!(vfs.is_dir("./user")?); Ok(())
901 }
902
903 #[test]
904 fn test_is_file_dot_path() -> Result<()> {
905 let mut vfs = setup_test_vfs();
906 vfs.cd("/home/user").unwrap();
907
908 assert!(vfs.is_file("./file.txt")?);
909 Ok(())
910 }
911
912 #[test]
913 fn test_is_dir_double_dot_path() -> Result<()> {
914 let mut vfs = setup_test_vfs();
915 vfs.cd("/home/user").unwrap();
916
917 assert!(vfs.is_dir("..")?); let result = vfs.is_dir("../etc");
920 assert!(result.is_err()); Ok(())
924 }
925 }
926
927 mod ls {
928 use super::*;
929
930 fn setup_test_vfs() -> MapFS {
932 let mut vfs = MapFS::new();
933
934 vfs.mkdir("/etc").unwrap();
936 vfs.mkdir("/home").unwrap();
937 vfs.mkdir("/home/user").unwrap();
938 vfs.mkdir("/home/guest").unwrap();
939 vfs.mkfile("/home/user/file1.txt", Some(b"Content 1"))
940 .unwrap();
941 vfs.mkfile("/home/user/file2.txt", Some(b"Content 2"))
942 .unwrap();
943 vfs.mkfile("/home/guest/note.txt", Some(b"Note")).unwrap();
944 vfs.mkfile("/readme.md", Some(b"Docs")).unwrap();
945
946 vfs
947 }
948
949 #[test]
950 fn test_ls_root_directory() -> Result<()> {
951 let vfs = setup_test_vfs();
952 let entries: Vec<_> = vfs.ls("/")?.collect();
953
954 assert_eq!(entries.len(), 3);
955 assert!(entries.contains(&Path::new("/etc")));
956 assert!(entries.contains(&Path::new("/home")));
957 assert!(entries.contains(&Path::new("/readme.md")));
958
959 Ok(())
960 }
961
962 #[test]
963 fn test_ls_home_directory() -> Result<()> {
964 let vfs = setup_test_vfs();
965 let entries: Vec<_> = vfs.ls("/home")?.collect();
966
967 assert_eq!(entries.len(), 2);
968 assert!(entries.contains(&Path::new("/home/user")));
969 assert!(entries.contains(&Path::new("/home/guest")));
970
971 Ok(())
972 }
973
974 #[test]
975 fn test_ls_user_directory() -> Result<()> {
976 let vfs = setup_test_vfs();
977 let entries: Vec<_> = vfs.ls("/home/user")?.collect();
978
979 assert_eq!(entries.len(), 2);
980 assert!(entries.contains(&Path::new("/home/user/file1.txt")));
981 assert!(entries.contains(&Path::new("/home/user/file2.txt")));
982
983 Ok(())
984 }
985
986 #[test]
987 fn test_ls_nonexistent_path() {
988 let vfs = setup_test_vfs();
989 let result: Result<Vec<_>> = vfs.ls("/nonexistent").map(|iter| iter.collect());
990 assert!(result.is_err());
991 assert!(
992 result.unwrap_err().to_string().contains("does not exist"),
993 "Error should mention path does not exist"
994 );
995 }
996
997 #[test]
998 fn test_ls_file_path() {
999 let vfs = setup_test_vfs();
1000 let result: Result<Vec<_>> = vfs.ls("/home/user/file1.txt").map(|iter| iter.collect());
1001 assert!(result.is_ok());
1002 assert_eq!(result.unwrap(), vec!["/home/user/file1.txt"]);
1003 }
1004
1005 #[test]
1006 fn test_ls_empty_directory() -> Result<()> {
1007 let mut vfs = setup_test_vfs();
1008 vfs.mkdir("/empty_dir").unwrap(); let entries: Vec<_> = vfs.ls("/empty_dir")?.collect();
1011 assert_eq!(entries.len(), 0); Ok(())
1014 }
1015
1016 #[test]
1017 fn test_ls_relative_path_from_root() -> Result<()> {
1018 let vfs = setup_test_vfs();
1019 let entries: Vec<_> = vfs.ls("home")?.collect(); assert_eq!(entries.len(), 2);
1022 assert!(entries.contains(&Path::new("/home/user")));
1023 assert!(entries.contains(&Path::new("/home/guest")));
1024
1025 Ok(())
1026 }
1027
1028 #[test]
1029 fn test_ls_relative_path_nested() -> Result<()> {
1030 let mut vfs = setup_test_vfs();
1031 vfs.cd("/home").unwrap();
1032
1033 let entries: Vec<_> = vfs.ls("user")?.collect();
1034
1035 assert_eq!(entries.len(), 2);
1036 assert!(entries.contains(&Path::new("/home/user/file1.txt")));
1037 assert!(entries.contains(&Path::new("/home/user/file2.txt")));
1038
1039 Ok(())
1040 }
1041
1042 #[test]
1043 fn test_ls_with_trailing_slash() -> Result<()> {
1044 let vfs = setup_test_vfs();
1045 let entries1: Vec<_> = vfs.ls("/home/")?.collect(); let entries2: Vec<_> = vfs.ls("/home")?.collect(); assert_eq!(entries1, entries2); Ok(())
1050 }
1051
1052 #[test]
1053 fn test_ls_dot_path() -> Result<()> {
1054 let mut vfs = setup_test_vfs();
1055 vfs.cd("/home/user").unwrap();
1056
1057 let entries: Vec<_> = vfs.ls(".")?.collect();
1058 assert_eq!(entries.len(), 2);
1059 assert!(entries.contains(&Path::new("/home/user/file1.txt")));
1060 assert!(entries.contains(&Path::new("/home/user/file2.txt")));
1061
1062 Ok(())
1063 }
1064
1065 #[test]
1066 fn test_ls_double_dot_path() -> Result<()> {
1067 let mut vfs = setup_test_vfs();
1068 vfs.cd("/home/user").unwrap();
1069
1070 let entries: Vec<_> = vfs.ls("..")?.collect(); assert_eq!(entries.len(), 2);
1072 assert!(entries.contains(&Path::new("/home/user")));
1073 assert!(entries.contains(&Path::new("/home/guest")));
1074
1075 Ok(())
1076 }
1077
1078 #[test]
1079 fn test_ls_iterator_lazy_evaluation() -> Result<()> {
1080 let vfs = setup_test_vfs();
1081 let mut iter = vfs.ls("/home/user")?;
1082
1083 assert!(iter.next().is_some());
1085
1086 let count = iter.count();
1088 assert_eq!(count + 1, 2); Ok(())
1091 }
1092 }
1093
1094 mod tree {
1095 use super::*;
1096
1097 fn setup_test_vfs() -> MapFS {
1099 let mut vfs = MapFS::new();
1100
1101 vfs.mkdir("/etc").unwrap();
1103 vfs.mkdir("/home").unwrap();
1104 vfs.mkdir("/home/user").unwrap();
1105 vfs.mkdir("/home/user/projects").unwrap();
1106 vfs.mkdir("/home/guest").unwrap();
1107 vfs.mkfile("/home/user/file1.txt", Some(b"Content 1"))
1108 .unwrap();
1109 vfs.mkfile("/home/user/projects/proj1.rs", Some(b"Code 1"))
1110 .unwrap();
1111 vfs.mkfile("/home/user/projects/proj2.rs", Some(b"Code 2"))
1112 .unwrap();
1113 vfs.mkfile("/home/guest/note.txt", Some(b"Note")).unwrap();
1114 vfs.mkfile("/readme.md", Some(b"Docs")).unwrap();
1115
1116 vfs
1117 }
1118
1119 #[test]
1120 fn test_tree_root() -> Result<()> {
1121 let vfs = setup_test_vfs();
1122 let entries: Vec<_> = vfs.tree("/")?.collect();
1123
1124 assert_eq!(entries.len(), 10);
1125 assert!(entries.contains(&Path::new("/etc")));
1126 assert!(entries.contains(&Path::new("/home")));
1127 assert!(entries.contains(&Path::new("/home/user")));
1128 assert!(entries.contains(&Path::new("/home/user/file1.txt")));
1129 assert!(entries.contains(&Path::new("/home/user/projects")));
1130 assert!(entries.contains(&Path::new("/home/user/projects/proj1.rs")));
1131 assert!(entries.contains(&Path::new("/home/user/projects/proj2.rs")));
1132 assert!(entries.contains(&Path::new("/home/guest")));
1133 assert!(entries.contains(&Path::new("/home/guest/note.txt")));
1134
1135 Ok(())
1136 }
1137
1138 #[test]
1139 fn test_tree_home_directory() -> Result<()> {
1140 let vfs = setup_test_vfs();
1141 let entries: Vec<_> = vfs.tree("/home")?.collect();
1142
1143 assert_eq!(entries.len(), 7);
1144 assert!(entries.contains(&Path::new("/home/user")));
1145 assert!(entries.contains(&Path::new("/home/user/file1.txt")));
1146 assert!(entries.contains(&Path::new("/home/user/projects")));
1147 assert!(entries.contains(&Path::new("/home/user/projects/proj1.rs")));
1148 assert!(entries.contains(&Path::new("/home/user/projects/proj2.rs")));
1149 assert!(entries.contains(&Path::new("/home/guest")));
1150 assert!(entries.contains(&Path::new("/home/guest/note.txt")));
1151
1152 Ok(())
1153 }
1154
1155 #[test]
1156 fn test_tree_user_directory() -> Result<()> {
1157 let vfs = setup_test_vfs();
1158 let entries: Vec<_> = vfs.tree("/home/user")?.collect();
1159
1160 assert_eq!(entries.len(), 4);
1161 assert!(entries.contains(&Path::new("/home/user/file1.txt")));
1162 assert!(entries.contains(&Path::new("/home/user/projects")));
1163 assert!(entries.contains(&Path::new("/home/user/projects/proj1.rs")));
1164 assert!(entries.contains(&Path::new("/home/user/projects/proj2.rs")));
1165
1166 Ok(())
1167 }
1168
1169 #[test]
1170 fn test_tree_nonexistent_path() {
1171 let vfs = setup_test_vfs();
1172 let result: Result<Vec<_>> = vfs.tree("/nonexistent").map(|iter| iter.collect());
1173 assert!(result.is_err());
1174 assert!(
1175 result.unwrap_err().to_string().contains("does not exist"),
1176 "Error should mention path does not exist"
1177 );
1178 }
1179
1180 #[test]
1181 fn test_tree_file_path() {
1182 let vfs = setup_test_vfs();
1183 let result: Result<Vec<_>> =
1184 vfs.tree("/home/user/file1.txt").map(|iter| iter.collect());
1185 assert!(result.is_ok());
1186 assert_eq!(result.unwrap(), vec!["/home/user/file1.txt"]);
1187 }
1188
1189 #[test]
1190 fn test_tree_empty_directory() -> Result<()> {
1191 let mut vfs = setup_test_vfs();
1192 vfs.mkdir("/empty_dir").unwrap();
1193
1194 let entries: Vec<_> = vfs.tree("/empty_dir")?.collect();
1195 assert_eq!(entries.len(), 0); Ok(())
1198 }
1199
1200 #[test]
1201 fn test_tree_relative_path_from_root() -> Result<()> {
1202 let vfs = setup_test_vfs();
1203 let entries: Vec<_> = vfs.tree("home")?.collect(); assert_eq!(entries.len(), 7);
1206 assert!(entries.contains(&Path::new("/home/user")));
1207 assert!(entries.contains(&Path::new("/home/user/file1.txt")));
1208 assert!(entries.contains(&Path::new("/home/user/projects")));
1209 assert!(entries.contains(&Path::new("/home/user/projects/proj1.rs")));
1210 assert!(entries.contains(&Path::new("/home/user/projects/proj2.rs")));
1211 assert!(entries.contains(&Path::new("/home/guest")));
1212 assert!(entries.contains(&Path::new("/home/guest/note.txt")));
1213
1214 Ok(())
1215 }
1216
1217 #[test]
1218 fn test_tree_relative_path_nested() -> Result<()> {
1219 let mut vfs = setup_test_vfs();
1220 vfs.cd("/home").unwrap();
1221
1222 let entries: Vec<_> = vfs.tree("user")?.collect();
1223
1224 assert_eq!(entries.len(), 4);
1225 assert!(entries.contains(&Path::new("/home/user/file1.txt")));
1226 assert!(entries.contains(&Path::new("/home/user/projects")));
1227 assert!(entries.contains(&Path::new("/home/user/projects/proj1.rs")));
1228 assert!(entries.contains(&Path::new("/home/user/projects/proj2.rs")));
1229
1230 Ok(())
1231 }
1232
1233 #[test]
1234 fn test_tree_with_trailing_slash() -> Result<()> {
1235 let vfs = setup_test_vfs();
1236 let entries1: Vec<_> = vfs.tree("/home/")?.collect(); let entries2: Vec<_> = vfs.tree("/home")?.collect(); assert_eq!(entries1, entries2); Ok(())
1241 }
1242
1243 #[test]
1244 fn test_tree_dot_path() -> Result<()> {
1245 let mut vfs = setup_test_vfs();
1246 vfs.cd("/home/user").unwrap();
1247
1248 let entries: Vec<_> = vfs.tree(".")?.collect();
1249 assert_eq!(entries.len(), 4);
1250 assert!(entries.contains(&Path::new("/home/user/file1.txt")));
1251 assert!(entries.contains(&Path::new("/home/user/projects")));
1252 assert!(entries.contains(&Path::new("/home/user/projects/proj1.rs")));
1253 assert!(entries.contains(&Path::new("/home/user/projects/proj2.rs")));
1254
1255 Ok(())
1256 }
1257
1258 #[test]
1259 fn test_tree_double_dot_path() -> Result<()> {
1260 let mut vfs = setup_test_vfs();
1261 vfs.cd("/home/user/projects").unwrap();
1262
1263 let entries: Vec<_> = vfs.tree("..")?.collect(); assert_eq!(entries.len(), 4);
1265 assert!(entries.contains(&Path::new("/home/user/file1.txt")));
1266 assert!(entries.contains(&Path::new("/home/user/projects")));
1267 assert!(entries.contains(&Path::new("/home/user/projects/proj1.rs")));
1268 assert!(entries.contains(&Path::new("/home/user/projects/proj2.rs")));
1269
1270 Ok(())
1271 }
1272
1273 #[test]
1274 fn test_tree_single_entry() -> Result<()> {
1275 let mut vfs = setup_test_vfs();
1276 vfs.mkdir("/single").unwrap();
1277
1278 let entries: Vec<_> = vfs.tree("/single")?.collect();
1279 assert_eq!(entries.len(), 0); Ok(())
1282 }
1283
1284 #[test]
1285 fn test_tree_iterator_lazy_evaluation() -> Result<()> {
1286 let vfs = setup_test_vfs();
1287 let mut iter = vfs.tree("/home/user")?;
1288
1289 assert!(iter.next().is_some());
1291
1292 let count = iter.count();
1294 assert_eq!(count + 1, 4); Ok(())
1297 }
1298
1299 #[test]
1300 fn test_tree_case_sensitivity() -> Result<()> {
1301 let mut vfs = setup_test_vfs();
1302 vfs.mkdir("/CASE_TEST").unwrap();
1303 vfs.mkfile("/CASE_TEST/file.txt", Some(b"Data")).unwrap();
1304
1305 let entries: Vec<_> = vfs.tree("/CASE_TEST")?.collect();
1306
1307 assert_eq!(entries.len(), 1);
1308 assert!(entries.contains(&Path::new("/CASE_TEST/file.txt")));
1309
1310 Ok(())
1311 }
1312 }
1313
1314 mod mkdir_mkfile {
1315 use super::*;
1316
1317 fn setup_vfs() -> MapFS {
1319 MapFS::new()
1320 }
1321
1322 #[test]
1323 fn test_mkdir_simple_directory() -> Result<()> {
1324 let mut vfs = setup_vfs();
1325 vfs.mkdir("/test")?;
1326
1327 assert!(vfs.exists("/test"));
1328 assert!(vfs.is_dir("/test")?);
1329
1330 Ok(())
1331 }
1332
1333 #[test]
1334 fn test_mkdir_nested_directories() -> Result<()> {
1335 let mut vfs = setup_vfs();
1336 vfs.mkdir("/a/b/c/d")?;
1337
1338 assert!(vfs.exists("/a"));
1339 assert!(vfs.exists("/a/b"));
1340 assert!(vfs.exists("/a/b/c"));
1341 assert!(vfs.exists("/a/b/c/d"));
1342
1343 Ok(())
1344 }
1345
1346 #[test]
1347 fn test_mkdir_existing_path() {
1348 let mut vfs = setup_vfs();
1349 vfs.mkdir("/existing").unwrap();
1350
1351 let result = vfs.mkdir("/existing");
1352 assert!(result.is_err());
1353 assert!(
1354 result
1355 .unwrap_err()
1356 .to_string()
1357 .contains("path already exists"),
1358 "Should error when path exists"
1359 );
1360 }
1361
1362 #[test]
1363 fn test_mkdir_empty_path() {
1364 let mut vfs = setup_vfs();
1365 let result = vfs.mkdir("");
1366 assert!(result.is_err());
1367 assert!(
1368 result
1369 .unwrap_err()
1370 .to_string()
1371 .contains("invalid path: empty"),
1372 "Empty path should be rejected"
1373 );
1374 }
1375
1376 #[test]
1377 fn test_mkdir_root_path() {
1378 let mut vfs = setup_vfs();
1379 let result = vfs.mkdir("/");
1380 assert!(result.is_err());
1381 assert!(
1382 result
1383 .unwrap_err()
1384 .to_string()
1385 .contains("path already exists"),
1386 "Root always exists, should error"
1387 );
1388 }
1389
1390 #[test]
1391 fn test_mkdir_with_trailing_slash() -> Result<()> {
1392 let mut vfs = setup_vfs();
1393 vfs.mkdir("/test/")?; assert!(vfs.exists("/test"));
1396 assert!(vfs.is_dir("/test")?);
1397
1398 Ok(())
1399 }
1400
1401 #[test]
1402 fn test_mkfile_simple_file() -> Result<()> {
1403 let mut vfs = setup_vfs();
1404 vfs.mkfile("/file.txt", Some(b"Hello World"))?;
1405
1406 assert!(vfs.exists("/file.txt"));
1407 assert!(vfs.is_file("/file.txt")?);
1408 assert_eq!(vfs.read("/file.txt")?, b"Hello World");
1409
1410 Ok(())
1411 }
1412
1413 #[test]
1414 fn test_mkfile_in_nested_directory() -> Result<()> {
1415 let mut vfs = setup_vfs();
1416 vfs.mkfile("/a/b/c/file.txt", Some(b"Content"))?;
1417
1418 assert!(vfs.exists("/a"));
1420 assert!(vfs.exists("/a/b"));
1421 assert!(vfs.exists("/a/b/c"));
1422 assert!(vfs.exists("/a/b/c/file.txt"));
1423
1424 assert_eq!(vfs.read("/a/b/c/file.txt")?, b"Content");
1425
1426 Ok(())
1427 }
1428
1429 #[test]
1430 fn test_mkfile_empty_content() -> Result<()> {
1431 let mut vfs = setup_vfs();
1432 vfs.mkfile("/empty.txt", None)?; assert!(vfs.exists("/empty.txt"));
1435 assert!(vfs.is_file("/empty.txt")?);
1436 assert_eq!(vfs.read("/empty.txt")?, &[]);
1437
1438 Ok(())
1439 }
1440
1441 #[test]
1442 fn test_mkfile_existing_file() -> Result<()> {
1443 let mut vfs = setup_vfs();
1444 vfs.mkfile("/test.txt", Some(b"Original"))?;
1445
1446 let result = vfs.mkfile("/test.txt", Some(b"New"));
1448
1449 assert!(result.is_err());
1450 assert_eq!(vfs.read("/test.txt")?, b"Original");
1451
1452 Ok(())
1453 }
1454
1455 #[test]
1456 fn test_mkfile_to_existing_directory() {
1457 let mut vfs = setup_vfs();
1458 vfs.mkdir("/dir").unwrap();
1459
1460 let result = vfs.mkfile("/dir", Some(b"Content"));
1461 assert!(result.is_err());
1462 }
1466
1467 #[test]
1468 fn test_mkfile_with_trailing_slash() -> Result<()> {
1469 let mut vfs = setup_vfs();
1470 vfs.mkfile("/file.txt/", Some(b"With slash"))?;
1471
1472 assert!(vfs.exists("/file.txt")); assert_eq!(vfs.read("/file.txt")?, b"With slash");
1474
1475 Ok(())
1476 }
1477
1478 #[test]
1479 fn test_mkfile_relative_path() -> Result<()> {
1480 let mut vfs = setup_vfs();
1481 vfs.mkdir("/home")?;
1482 vfs.cd("/home")?; vfs.mkfile("file.txt", Some(b"Relative"))?;
1485
1486 assert!(vfs.exists("/home/file.txt"));
1487 assert_eq!(vfs.read("/home/file.txt")?, b"Relative");
1488
1489 Ok(())
1490 }
1491
1492 #[test]
1493 fn test_mkdir_and_mkfile_combination() -> Result<()> {
1494 let mut vfs = setup_vfs();
1495
1496 vfs.mkdir("/projects")?;
1497 vfs.mkfile("/projects/main.rs", Some(b"fn main() {}"))?;
1498 vfs.mkdir("/projects/tests")?;
1499 vfs.mkfile("/projects/tests/test1.rs", Some(b"#[test]"))?;
1500
1501 assert!(vfs.exists("/projects"));
1502 assert!(vfs.exists("/projects/main.rs"));
1503 assert!(vfs.exists("/projects/tests"));
1504 assert!(vfs.exists("/projects/tests/test1.rs"));
1505
1506 Ok(())
1507 }
1508
1509 #[test]
1510 fn test_mkdir_case_sensitivity() -> Result<()> {
1511 let mut vfs = setup_vfs();
1512 vfs.mkdir("/CaseDir")?;
1513
1514 assert!(vfs.exists("/CaseDir"));
1515 assert!(!vfs.exists("/casedir")); Ok(())
1518 }
1519 }
1520
1521 mod read_write_append {
1522 use super::*;
1523
1524 fn setup_test_vfs() -> MapFS {
1526 let mut vfs = MapFS::new();
1527
1528 vfs.mkdir("/etc").unwrap();
1530 vfs.mkfile("/readme.md", Some(b"Project docs")).unwrap();
1531 vfs.mkfile("/data.bin", Some(b"\x00\x01\x02")).unwrap();
1532 vfs.mkfile("/empty.txt", None).unwrap(); vfs.mkfile("/home/user/file.txt", Some(b"Hello World"))
1534 .unwrap();
1535
1536 vfs
1537 }
1538
1539 #[test]
1540 fn test_read_existing_file() -> Result<()> {
1541 let vfs = setup_test_vfs();
1542 let content = vfs.read("/readme.md")?;
1543 assert_eq!(content, b"Project docs");
1544 Ok(())
1545 }
1546
1547 #[test]
1548 fn test_read_binary_file() -> Result<()> {
1549 let vfs = setup_test_vfs();
1550 let content = vfs.read("/data.bin")?;
1551 assert_eq!(content, vec![0x00, 0x01, 0x02]);
1552 Ok(())
1553 }
1554
1555 #[test]
1556 fn test_read_empty_file() -> Result<()> {
1557 let vfs = setup_test_vfs();
1558 let content = vfs.read("/empty.txt")?;
1559 assert!(content.is_empty());
1560 Ok(())
1561 }
1562
1563 #[test]
1564 fn test_read_nonexistent_file() {
1565 let vfs = setup_test_vfs();
1566 let result = vfs.read("/nonexistent.txt");
1567 assert!(result.is_err());
1568 assert!(
1569 result.unwrap_err().to_string().contains("does not exist"),
1570 "Error should mention file does not exist"
1571 );
1572 }
1573
1574 #[test]
1575 fn test_read_directory_as_file() {
1576 let vfs = setup_test_vfs();
1577 let result = vfs.read("/etc");
1578 assert!(result.is_err());
1579 assert!(
1580 result.unwrap_err().to_string().contains("is a directory"),
1581 "Reading directory as file should error"
1582 );
1583 }
1584
1585 #[test]
1586 fn test_write_existing_file() -> Result<()> {
1587 let mut vfs = setup_test_vfs();
1588 vfs.write("/readme.md", b"Updated content")?;
1589
1590 let content = vfs.read("/readme.md")?;
1591 assert_eq!(content, b"Updated content");
1592 Ok(())
1593 }
1594
1595 #[test]
1596 fn test_write_binary_content() -> Result<()> {
1597 let mut vfs = setup_test_vfs();
1598 vfs.write("/data.bin", &[0xFF, 0xFE, 0xFD])?;
1599
1600 let content = vfs.read("/data.bin")?;
1601 assert_eq!(content, vec![0xFF, 0xFE, 0xFD]);
1602 Ok(())
1603 }
1604
1605 #[test]
1606 fn test_write_empty_content() -> Result<()> {
1607 let mut vfs = setup_test_vfs();
1608 vfs.write("/empty.txt", &[])?;
1609
1610 let content = vfs.read("/empty.txt")?;
1611 assert!(content.is_empty());
1612 Ok(())
1613 }
1614
1615 #[test]
1616 fn test_write_nonexistent_file() {
1617 let mut vfs = setup_test_vfs();
1618 let result = vfs.write("/newfile.txt", b"Content");
1619 assert!(result.is_err());
1620 assert!(
1621 result.unwrap_err().to_string().contains("does not exist"),
1622 "Writing to nonexistent file should fail"
1623 );
1624 }
1625
1626 #[test]
1627 fn test_write_directory_as_file() {
1628 let mut vfs = setup_test_vfs();
1629 let result = vfs.write("/etc", b"Content");
1630 assert!(result.is_err());
1631 assert!(
1632 result.unwrap_err().to_string().contains("is a directory"),
1633 "Writing to directory should error"
1634 );
1635 }
1636
1637 #[test]
1638 fn test_append_to_file() -> Result<()> {
1639 let mut vfs = setup_test_vfs();
1640 vfs.append("/readme.md", b" - appended")?;
1641
1642 let content = vfs.read("/readme.md")?;
1643 assert_eq!(content, b"Project docs - appended");
1644 Ok(())
1645 }
1646
1647 #[test]
1648 fn test_append_binary_data() -> Result<()> {
1649 let mut vfs = setup_test_vfs();
1650 vfs.append("/data.bin", &[0xAA, 0xBB])?;
1651
1652 let content = vfs.read("/data.bin")?;
1653 assert_eq!(content, vec![0x00, 0x01, 0x02, 0xAA, 0xBB]);
1654 Ok(())
1655 }
1656
1657 #[test]
1658 fn test_append_empty_slice() -> Result<()> {
1659 let mut vfs = setup_test_vfs();
1660 vfs.append("/empty.txt", &[])?; let content = vfs.read("/empty.txt")?;
1663 assert!(content.is_empty()); Ok(())
1665 }
1666
1667 #[test]
1668 fn test_append_nonexistent_file() {
1669 let mut vfs = setup_test_vfs();
1670 let result = vfs.append("/newfile.txt", b"More content");
1671 assert!(result.is_err());
1672 assert!(
1673 result.unwrap_err().to_string().contains("does not exist"),
1674 "Appending to nonexistent file should fail"
1675 );
1676 }
1677
1678 #[test]
1679 fn test_append_directory_as_file() {
1680 let mut vfs = setup_test_vfs();
1681 let result = vfs.append("/etc", b"Data");
1682 assert!(result.is_err());
1683 assert!(
1684 result.unwrap_err().to_string().contains("is a directory"),
1685 "Appending to directory should error"
1686 );
1687 }
1688
1689 #[test]
1690 fn test_write_and_append_sequence() -> Result<()> {
1691 let mut vfs = setup_test_vfs();
1692
1693 vfs.mkfile("/test.txt", None)?;
1695 vfs.write("/test.txt", b"Initial")?;
1696
1697 vfs.append("/test.txt", b" + appended")?;
1699
1700 vfs.write("/test.txt", b"Overwritten")?;
1702
1703 let final_content = vfs.read("/test.txt")?;
1704 assert_eq!(final_content, b"Overwritten");
1705
1706 Ok(())
1707 }
1708
1709 #[test]
1710 fn test_read_after_write_and_append() -> Result<()> {
1711 let mut vfs = setup_test_vfs();
1712
1713 vfs.mkfile("/log.txt", None)?;
1714 vfs.write("/log.txt", b"Entry 1\n")?;
1715 vfs.append("/log.txt", b"Entry 2\n")?;
1716 vfs.write("/log.txt", b"Overwritten log\n")?;
1717 vfs.append("/log.txt", b"Final entry\n")?;
1718
1719 let content = vfs.read("/log.txt")?;
1720 assert_eq!(content, b"Overwritten log\nFinal entry\n");
1721
1722 Ok(())
1723 }
1724 }
1725
1726 mod rm {
1727 use super::*;
1728
1729 fn setup_test_vfs() -> MapFS {
1731 let mut vfs = MapFS::new();
1732
1733 vfs.mkdir("/etc").unwrap();
1735 vfs.mkdir("/home").unwrap();
1736 vfs.mkdir("/home/user").unwrap();
1737 vfs.mkdir("/home/user/projects").unwrap();
1738 vfs.mkfile("/home/user/file1.txt", Some(b"Content 1"))
1739 .unwrap();
1740 vfs.mkfile("/home/user/projects/proj1.rs", Some(b"Code 1"))
1741 .unwrap();
1742 vfs.mkfile("/readme.md", Some(b"Docs")).unwrap();
1743 vfs.mkfile("/data.bin", Some(b"\x00\x01")).unwrap();
1744
1745 vfs
1746 }
1747
1748 #[test]
1749 fn test_rm_file() -> Result<()> {
1750 let mut vfs = setup_test_vfs();
1751 vfs.rm("/readme.md")?;
1752
1753 assert!(!vfs.exists("/readme.md"));
1754 assert!(vfs.exists("/data.bin")); Ok(())
1757 }
1758
1759 #[test]
1760 fn test_rm_directory_recursive() -> Result<()> {
1761 let mut vfs = setup_test_vfs();
1762 vfs.rm("/home/user")?;
1763
1764 assert!(!vfs.exists("/home/user"));
1766 assert!(!vfs.exists("/home/user/file1.txt"));
1767 assert!(!vfs.exists("/home/user/projects"));
1768 assert!(!vfs.exists("/home/user/projects/proj1.rs"));
1769
1770 assert!(vfs.exists("/home"));
1772 assert!(vfs.exists("/etc"));
1773
1774 Ok(())
1775 }
1776
1777 #[test]
1778 fn test_rm_nonexistent_path() {
1779 let mut vfs = setup_test_vfs();
1780 let result = vfs.rm("/nonexistent");
1781 assert!(result.is_err());
1782 assert!(
1783 result.unwrap_err().to_string().contains("does not exist"),
1784 "Should error for non‑existent path"
1785 );
1786 }
1787
1788 #[test]
1789 fn test_rm_empty_path() {
1790 let mut vfs = setup_test_vfs();
1791 let result = vfs.rm("");
1792 assert!(result.is_err());
1793 assert!(
1794 result
1795 .unwrap_err()
1796 .to_string()
1797 .contains("invalid path: empty"),
1798 "Empty path should be rejected"
1799 );
1800 }
1801
1802 #[test]
1803 fn test_rm_root_directory() {
1804 let mut vfs = setup_test_vfs();
1805 let result = vfs.rm("/");
1806 assert!(result.is_err());
1807 assert!(
1808 result
1809 .unwrap_err()
1810 .to_string()
1811 .contains("invalid path: the root cannot be removed"),
1812 "Root directory cannot be removed"
1813 );
1814 }
1815
1816 #[test]
1817 fn test_rm_with_trailing_slash() -> Result<()> {
1818 let mut vfs = setup_test_vfs();
1819 vfs.rm("/etc/")?; assert!(!vfs.exists("/etc"));
1822 Ok(())
1823 }
1824
1825 #[test]
1826 fn test_rm_relative_path_from_root() -> Result<()> {
1827 let mut vfs = setup_test_vfs();
1828 vfs.cd("/home").unwrap(); vfs.rm("user")?; assert!(!vfs.exists("/home/user"));
1833 assert!(vfs.exists("/etc")); Ok(())
1836 }
1837
1838 #[test]
1839 fn test_rm_relative_path_nested() -> Result<()> {
1840 let mut vfs = setup_test_vfs();
1841 vfs.cd("/home/user/projects").unwrap();
1842
1843 vfs.rm("../file1.txt")?; assert!(!vfs.exists("/home/user/file1.txt"));
1846 assert!(vfs.exists("/home/user/projects/proj1.rs")); Ok(())
1849 }
1850
1851 #[test]
1852 fn test_rm_dot_path() -> Result<()> {
1853 let mut vfs = setup_test_vfs();
1854 vfs.cd("/home/user")?;
1855
1856 let result = vfs.rm(".");
1857 assert!(result.is_ok());
1858 assert!(!vfs.exists("/home/user"));
1859
1860 Ok(())
1861 }
1862
1863 #[test]
1864 fn test_rm_double_dot_path() -> Result<()> {
1865 let mut vfs = setup_test_vfs();
1866 vfs.cd("/home/user/projects").unwrap();
1867
1868 let result = vfs.rm("..");
1869 assert!(result.is_ok());
1870 assert!(!vfs.exists("/home/user"));
1871 assert!(vfs.exists("/home"));
1872
1873 Ok(())
1874 }
1875
1876 #[test]
1877 fn test_rm_single_file_in_dir() -> Result<()> {
1878 let mut vfs = setup_test_vfs();
1879 vfs.rm("/home/user/file1.txt")?;
1880
1881 assert!(!vfs.exists("/home/user/file1.txt"));
1882 assert!(vfs.exists("/home/user/projects/proj1.rs")); assert!(vfs.exists("/home/user")); Ok(())
1886 }
1887
1888 #[test]
1889 fn test_rm_idempotent() -> Result<()> {
1890 let mut vfs = setup_test_vfs();
1891
1892 vfs.rm("/data.bin")?;
1894
1895 let result = vfs.rm("/data.bin");
1897 assert!(result.is_err());
1898
1899 Ok(())
1900 }
1901
1902 #[test]
1903 fn test_rm_case_sensitivity() -> Result<()> {
1904 let mut vfs = setup_test_vfs();
1905 vfs.mkdir("/CaseDir").unwrap();
1906 vfs.mkfile("/CaseDir/file.txt", Some(b"Content")).unwrap();
1907
1908 vfs.rm("/CaseDir")?; assert!(!vfs.exists("/CaseDir"));
1911 assert!(!vfs.exists("/casedir")); Ok(())
1914 }
1915 }
1916
1917 mod cleanup {
1918 use super::*;
1919
1920 fn setup_test_vfs() -> MapFS {
1922 let mut vfs = MapFS::new();
1923
1924 vfs.mkdir("/etc").unwrap();
1926 vfs.mkdir("/home").unwrap();
1927 vfs.mkdir("/home/user").unwrap();
1928 vfs.mkdir("/home/user/projects").unwrap();
1929 vfs.mkfile("/home/user/file1.txt", Some(b"Content 1"))
1930 .unwrap();
1931 vfs.mkfile("/home/user/projects/proj1.rs", Some(b"Code 1"))
1932 .unwrap();
1933 vfs.mkfile("/readme.md", Some(b"Docs")).unwrap();
1934 vfs.mkfile("/data.bin", Some(b"\x00\x01")).unwrap();
1935 vfs.mkfile("/empty.txt", None).unwrap();
1936
1937 vfs
1938 }
1939
1940 #[test]
1941 fn test_cleanup_removes_all_entries() {
1942 let mut vfs = setup_test_vfs();
1943 let result = vfs.cleanup();
1944
1945 assert!(result); assert_eq!(vfs.entries.len(), 0); }
1948
1949 #[test]
1950 fn test_cleanup_preserves_root() {
1951 let mut vfs = setup_test_vfs();
1952 vfs.cleanup();
1953
1954 assert!(vfs.exists("/"));
1955 }
1956
1957 #[test]
1958 fn test_cleanup_empty_vfs() {
1959 let mut vfs = MapFS::new(); let result = vfs.cleanup();
1961
1962 assert!(result);
1963 assert_eq!(vfs.entries.len(), 0);
1964 }
1965
1966 #[test]
1967 fn test_cleanup_after_partial_removal() {
1968 let mut vfs = setup_test_vfs();
1969
1970 vfs.rm("/readme.md").unwrap();
1972 vfs.rm("/home/user/projects").unwrap();
1973
1974 let result = vfs.cleanup();
1975
1976 assert!(result);
1977 assert_eq!(vfs.entries.len(), 0);
1978 }
1979
1980 #[test]
1981 fn test_cleanup_idempotent() {
1982 let mut vfs = setup_test_vfs();
1983
1984 assert!(vfs.cleanup());
1986
1987 assert!(vfs.cleanup());
1989
1990 assert_eq!(vfs.entries.len(), 0);
1991 }
1992
1993 #[test]
1994 fn test_cleanup_with_nested_structure() {
1995 let mut vfs = setup_test_vfs();
1996
1997 vfs.mkdir("/a/b/c/d").unwrap();
1999 vfs.mkfile("/a/b/c/d/file.txt", Some(b"Deep file")).unwrap();
2000
2001 vfs.cleanup();
2002
2003 assert_eq!(vfs.entries.len(), 0);
2004 }
2005
2006 #[test]
2007 fn test_cleanup_returns_true_always() {
2008 let mut vfs1 = setup_test_vfs();
2009 let mut vfs2 = MapFS::new();
2010
2011 assert!(vfs1.cleanup()); assert!(vfs2.cleanup()); }
2014 }
2015}