1use crate::{
2 cache::{self, fs::Metadata},
3 ext::{BoolExt as _, OptionExt as _},
4};
5use itertools::{Itertools, Position};
6use std::{
7 cell::RefCell,
8 collections::{BTreeMap, HashSet},
9 error,
10 ffi::{OsStr, OsString},
11 fmt::Debug,
12 path::{Component, Path, PathBuf},
13 rc::Rc,
14 result, slice, vec,
15};
16use strum::Display;
17
18#[macro_export]
51macro_rules! fs {
52 (@expand [] -> [$($expanded:tt)*]) => {
53 [$($expanded)*]
54 };
55 (@expand [$name:ident($contents:expr) $(,$($tail:tt)*)?] -> [$($($expanded:tt)+)?]) => {
56 fs!(
57 @expand
58 [$($($tail)*)?] -> [
59 $($($expanded)+,)?
60 (stringify!($name), $crate::cache::fs::test::Entry::file($contents))
61 ]
62 )
63 };
64 (@expand [$name:literal($contents:expr) $(,$($tail:tt)*)?] -> [$($($expanded:tt)+)?]) => {
65 fs!(
66 @expand
67 [$($($tail)*)?] -> [
68 $($($expanded)+,)?
69 ($name, $crate::cache::fs::test::Entry::file($contents))
70 ]
71 )
72 };
73 (@expand [$name:ident -> $target:literal $(,$($tail:tt)*)?] -> [$($($expanded:tt)+)?]) => {
74 fs!(
75 @expand
76 [$($($tail)*)?] -> [
77 $($($expanded)+,)?
78 (stringify!($name), $crate::cache::fs::test::Entry::symlink($target))
79 ]
80 )
81 };
82 (@expand [$name:literal -> $target:literal $(,$($tail:tt)*)?] -> [$($($expanded:tt)+)?]) => {
83 fs!(
84 @expand
85 [$($($tail)*)?] -> [
86 $($($expanded)+,)?
87 ($name, $crate::cache::fs::test::Entry::symlink($target))
88 ]
89 )
90 };
91 (@expand [$name:ident { $($dirents:tt)* } $(,$($tail:tt)*)?] -> [$($($expanded:tt)+)?]) => {
92 fs!(
93 @expand
94 [$($($tail)*)?] -> [
95 $($($expanded)+,)?
96 (stringify!($name), fs!($($dirents)*))
97 ]
98 )
99 };
100 (@expand [$name:literal { $($dirents:tt)* } $(,$($tail:tt)*)?] -> [$($($expanded:tt)+)?]) => {
101 fs!(
102 @expand
103 [$($($tail)*)?] -> [
104 $($($expanded)+,)?
105 ($name, fs!($($dirents)*))
106 ]
107 )
108 };
109 ($($body:tt)*) => ($crate::cache::fs::test::Entry::directory(fs!(@expand [$($body)*] -> [])));
110}
111
112#[derive(Clone, Debug)]
113pub struct Fs {
114 state: Rc<RefCell<State>>,
115}
116
117impl Fs {
118 pub fn new(root: Entry) -> Self {
119 Self {
120 state: Rc::new(RefCell::new(State::new(root))),
121 }
122 }
123
124 #[track_caller]
125 pub fn assert_tree(&self, expected: Entry) {
126 self.state.borrow().assert_tree(expected)
127 }
128
129 #[track_caller]
130 pub fn assert_entry(&self, path: &Path, expected: Entry) {
131 self.state.borrow().assert_entry(path, expected)
132 }
133
134 #[track_caller]
135 pub fn assert_recursive_rmdirs(&self, expected: HashSet<String>) {
136 self.state.borrow().assert_recursive_rmdirs(expected)
137 }
138
139 pub fn complete_recursive_rmdir(&self, path: &str) {
140 self.state.borrow_mut().complete_recursive_rmdir(path)
141 }
142
143 pub fn graft(&self, path: impl AsRef<Path>, entry: Entry) {
144 self.state.borrow_mut().graft(path.as_ref(), entry)
145 }
146
147 pub fn set_failure(&self, fail: bool) {
148 self.state.borrow_mut().set_failure(fail)
149 }
150}
151
152impl super::Fs for Fs {
153 type Error = Error;
154
155 fn rand_u64(&self) -> u64 {
156 self.state.borrow_mut().rand_u64()
157 }
158
159 fn metadata(&self, path: &Path) -> Result<Option<Metadata>> {
160 self.state.borrow().metadata(path)
161 }
162
163 fn read_file(&self, path: &Path, contents_out: &mut [u8]) -> Result<usize> {
164 self.state.borrow().read_file(path, contents_out)
165 }
166
167 fn read_dir(&self, path: &Path) -> Result<impl Iterator<Item = Result<(OsString, Metadata)>>> {
168 self.state.borrow().read_dir(path)
169 }
170
171 fn create_file(&self, path: &Path, contents: &[u8]) -> Result<()> {
172 self.state.borrow_mut().create_file(path, contents)
173 }
174
175 type FileLock = FileLock;
176
177 fn possibly_create_file_and_try_exclusive_lock(
178 &self,
179 path: &Path,
180 ) -> Result<Option<Self::FileLock>> {
181 match self
182 .state
183 .borrow_mut()
184 .possibly_create_file_and_try_exclusive_lock(path)
185 {
186 Err(err) => Err(err),
187 Ok(None) => Ok(None),
188 Ok(Some(component_path)) => Ok(Some(FileLock {
189 state: self.state.clone(),
190 component_path,
191 })),
192 }
193 }
194
195 fn symlink(&self, target: &Path, link: &Path) -> Result<()> {
196 self.state.borrow_mut().symlink(target, link)
197 }
198
199 fn mkdir(&self, path: &Path) -> Result<()> {
200 self.state.borrow_mut().mkdir(path)
201 }
202
203 fn mkdir_recursively(&self, path: &Path) -> Result<()> {
204 self.state.borrow_mut().mkdir_recursively(path)
205 }
206
207 fn remove(&self, path: &Path) -> Result<()> {
208 self.state.borrow_mut().remove(path)
209 }
210
211 fn rmdir_recursively_on_thread(&self, path: PathBuf) -> Result<()> {
212 self.state.borrow_mut().rmdir_recursively_on_thread(path)
213 }
214
215 fn rename(&self, source_path: &Path, dest_path: &Path) -> Result<()> {
216 self.state.borrow_mut().rename(source_path, dest_path)
217 }
218
219 type TempFile = TempFile;
220
221 fn temp_file(&self, parent: &Path) -> Result<TempFile> {
222 self.state.borrow_mut().temp_file(parent)
223 }
224
225 fn persist_temp_file(&self, temp_file: TempFile, target: &Path) -> Result<()> {
226 self.state.borrow_mut().persist_temp_file(temp_file, target)
227 }
228
229 type TempDir = TempDir;
230
231 fn temp_dir(&self, parent: &Path) -> Result<TempDir> {
232 self.state.borrow_mut().temp_dir(parent)
233 }
234
235 fn persist_temp_dir(&self, temp_dir: TempDir, target: &Path) -> Result<()> {
236 self.state.borrow_mut().persist_temp_dir(temp_dir, target)
237 }
238}
239
240#[derive(Debug, Display, PartialEq)]
243pub enum Error {
244 Exists,
245 IsDir,
246 Inval,
247 NoEnt,
248 NotDir,
249 NotEmpty,
250 Test,
251}
252
253impl error::Error for Error {}
254
255pub type Result<T> = result::Result<T, Error>;
256
257#[derive(Debug)]
258pub struct FileLock {
259 state: Rc<RefCell<State>>,
260 component_path: ComponentPath,
261}
262
263impl Drop for FileLock {
264 fn drop(&mut self) {
265 self.state
266 .borrow_mut()
267 .release_exclusive_lock(&self.component_path);
268 }
269}
270
271#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
272pub struct TempFile(PathBuf);
273
274impl TempFile {
275 pub fn new(path: PathBuf) -> Self {
276 Self(path)
277 }
278}
279
280impl From<&str> for TempFile {
281 fn from(temp_file: &str) -> Self {
282 Self::new(temp_file.into())
283 }
284}
285
286impl cache::fs::TempFile for TempFile {
287 fn path(&self) -> &Path {
288 &self.0
289 }
290}
291
292#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
293pub struct TempDir(PathBuf);
294
295impl TempDir {
296 pub fn new(path: PathBuf) -> Self {
297 Self(path)
298 }
299}
300
301impl From<&str> for TempDir {
302 fn from(temp_dir: &str) -> Self {
303 Self::new(temp_dir.into())
304 }
305}
306
307impl cache::fs::TempDir for TempDir {
308 fn path(&self) -> &Path {
309 &self.0
310 }
311}
312
313#[derive(Debug)]
314struct State {
315 root: Entry,
316 last_random_number: u64,
317 recursive_rmdirs: HashSet<String>,
318 exclusive_locks: HashSet<ComponentPath>,
319 fail: bool,
320}
321
322impl State {
323 fn new(root: Entry) -> Self {
324 assert!(root.is_directory());
325 Self {
326 root,
327 last_random_number: 0,
328 recursive_rmdirs: Default::default(),
329 exclusive_locks: Default::default(),
330 fail: false,
331 }
332 }
333
334 #[track_caller]
335 pub fn assert_tree(&self, expected: Entry) {
336 self.assert_entry(Path::new("/"), expected);
337 }
338
339 #[track_caller]
340 pub fn assert_entry(&self, path: &Path, expected: Entry) {
341 let Lookup::Found(entry, _) = self.root.lookup(path) else {
342 panic!("couldn't reolve {path:?}");
343 };
344 assert_eq!(entry, &expected);
345 }
346
347 #[track_caller]
348 pub fn assert_recursive_rmdirs(&self, expected: HashSet<String>) {
349 assert_eq!(self.recursive_rmdirs, expected);
350 }
351
352 pub fn complete_recursive_rmdir(&mut self, path: &str) {
353 self.recursive_rmdirs.remove(path).assert_is_true();
354
355 match self.root.lookup(Path::new(path)) {
356 Lookup::FileAncestor => Err(Error::NotDir),
357 Lookup::DanglingSymlink | Lookup::NotFound | Lookup::FoundParent(_) => {
358 Err(Error::NoEnt)
359 }
360 Lookup::Found(Entry::Directory { .. }, component_path) => {
361 self.root.remove_component_path(component_path);
362 Ok(())
363 }
364 Lookup::Found(_, _) => Err(Error::NotDir),
365 }
366 .unwrap();
367 }
368
369 pub fn graft(&mut self, path: &Path, entry: Entry) {
370 let parent_component_path = match self.root.lookup(path) {
371 err @ (Lookup::DanglingSymlink | Lookup::FileAncestor) => {
372 panic!("error {err:?} looking up graft location {path:?}");
373 }
374 Lookup::FoundParent(parent_component_path) => parent_component_path,
375 Lookup::NotFound => {
376 self.mkdir_recursively(path.parent().unwrap()).unwrap();
377 self.graft(path, entry);
378 return;
379 }
380 Lookup::Found(_, component_path) => self.root.remove_component_path(component_path).1,
381 };
382 self.root.append_entry_to_directory(
383 &parent_component_path,
384 path.file_name().unwrap(),
385 entry,
386 );
387 }
388
389 pub fn set_failure(&mut self, fail: bool) {
390 self.fail = fail;
391 }
392
393 fn check_failure(&self) -> Result<()> {
394 if self.fail {
395 Err(Error::Test)
396 } else {
397 Ok(())
398 }
399 }
400
401 fn rand_u64(&mut self) -> u64 {
402 self.last_random_number += 1;
403 self.last_random_number
404 }
405
406 fn metadata(&self, path: &Path) -> Result<Option<Metadata>> {
407 self.check_failure()?;
408 match self.root.lookup(path) {
409 Lookup::Found(entry, _) => Ok(Some(entry.metadata())),
410 Lookup::FoundParent(_) | Lookup::NotFound => Ok(None),
411 Lookup::DanglingSymlink => Err(Error::NoEnt),
412 Lookup::FileAncestor => Err(Error::NotDir),
413 }
414 }
415
416 fn read_file(&self, path: &Path, contents_out: &mut [u8]) -> Result<usize> {
417 self.check_failure()?;
418 match self.root.lookup_leaf(path)? {
419 FollowSymlinks::FoundDirectory(_, _) => Err(Error::IsDir),
420 FollowSymlinks::DanglingSymlink => Err(Error::NoEnt),
421 FollowSymlinks::FileAncestor => Err(Error::NotDir),
422 FollowSymlinks::FoundFile(contents, _) => {
423 let to_read = contents.len().min(contents_out.len());
424 contents_out[..to_read].copy_from_slice(&contents[..to_read]);
425 Ok(to_read)
426 }
427 }
428 }
429
430 fn read_dir(&self, path: &Path) -> Result<impl Iterator<Item = Result<(OsString, Metadata)>>> {
431 self.check_failure()?;
432 match self.root.lookup_leaf(path)? {
433 FollowSymlinks::FoundFile(_, _) | FollowSymlinks::FileAncestor => Err(Error::NotDir),
434 FollowSymlinks::DanglingSymlink => Err(Error::NoEnt),
435 FollowSymlinks::FoundDirectory(entries, _) => Ok(entries
436 .iter()
437 .map(|(name, entry)| Ok((name.into(), entry.metadata())))
438 .collect_vec()
439 .into_iter()),
440 }
441 }
442
443 fn create_file(&mut self, path: &Path, contents: &[u8]) -> Result<()> {
444 self.check_failure()?;
445 let parent_component_path = self.root.lookup_parent(path)?;
446 self.root.append_entry_to_directory(
447 &parent_component_path,
448 path.file_name().unwrap(),
449 Entry::file(contents),
450 );
451 Ok(())
452 }
453
454 fn possibly_create_file_and_try_exclusive_lock(
455 &mut self,
456 path: &Path,
457 ) -> Result<Option<ComponentPath>> {
458 self.check_failure()?;
459 let component_path = match self.root.lookup(path) {
460 Lookup::NotFound | Lookup::DanglingSymlink => Err(Error::NoEnt),
461 Lookup::FileAncestor => Err(Error::NotDir),
462 Lookup::Found(entry, component_path) => {
463 match self.root.follow_leaf_symlink(entry, component_path) {
464 FollowSymlinks::DanglingSymlink => Err(Error::NoEnt),
465 FollowSymlinks::FileAncestor => Err(Error::NotDir),
466 FollowSymlinks::FoundDirectory(_, _) => Err(Error::IsDir),
467 FollowSymlinks::FoundFile(_, component_path) => Ok(component_path),
468 }
469 }
470 Lookup::FoundParent(parent_component_path) => {
471 let file_name = path.file_name().unwrap();
472 self.root.append_entry_to_directory(
473 &parent_component_path,
474 file_name,
475 Entry::file(b""),
476 );
477 Ok(parent_component_path.push(file_name.to_str().unwrap()))
478 }
479 }?;
480 if self.exclusive_locks.insert(component_path.clone()) {
481 Ok(Some(component_path))
482 } else {
483 Ok(None)
484 }
485 }
486
487 fn release_exclusive_lock(&mut self, component_path: &ComponentPath) {
488 self.exclusive_locks.remove(component_path).assert_is_true();
489 }
490
491 fn symlink(&mut self, target: &Path, link: &Path) -> Result<()> {
492 self.check_failure()?;
493 let parent_component_path = self.root.lookup_parent(link)?;
494 self.root.append_entry_to_directory(
495 &parent_component_path,
496 link.file_name().unwrap(),
497 Entry::symlink(target.to_str().unwrap()),
498 );
499 Ok(())
500 }
501
502 fn mkdir(&mut self, path: &Path) -> Result<()> {
503 self.check_failure()?;
504 let parent_component_path = self.root.lookup_parent(path)?;
505 self.root.append_entry_to_directory(
506 &parent_component_path,
507 path.file_name().unwrap(),
508 Entry::directory([]),
509 );
510 Ok(())
511 }
512
513 fn mkdir_recursively(&mut self, path: &Path) -> Result<()> {
514 self.check_failure()?;
515 match self.root.lookup(path) {
516 Lookup::Found(Entry::Directory { .. }, _) => Ok(()),
517 Lookup::Found(_, _) => Err(Error::Exists),
518 Lookup::DanglingSymlink => Err(Error::NoEnt),
519 Lookup::FileAncestor => Err(Error::NotDir),
520 Lookup::FoundParent(_) => self.mkdir(path),
521 Lookup::NotFound => {
522 self.mkdir_recursively(path.parent().unwrap())?;
523 self.mkdir(path)
524 }
525 }
526 }
527
528 fn remove(&mut self, path: &Path) -> Result<()> {
529 self.check_failure()?;
530 match self.root.lookup(path) {
531 Lookup::Found(Entry::Directory { .. }, _) => Err(Error::IsDir),
532 Lookup::FoundParent(_) | Lookup::NotFound | Lookup::DanglingSymlink => {
533 Err(Error::NoEnt)
534 }
535 Lookup::FileAncestor => Err(Error::NotDir),
536 Lookup::Found(Entry::File { .. } | Entry::Symlink { .. }, component_path) => {
537 self.root.remove_component_path(component_path);
538 Ok(())
539 }
540 }
541 }
542
543 fn rmdir_recursively_on_thread(&mut self, path: PathBuf) -> Result<()> {
544 self.check_failure()?;
545 match self.root.lookup(&path) {
546 Lookup::Found(Entry::File { .. } | Entry::Symlink { .. }, _) => Err(Error::NotDir),
547 Lookup::FoundParent(_) | Lookup::NotFound | Lookup::DanglingSymlink => {
548 Err(Error::NoEnt)
549 }
550 Lookup::FileAncestor => Err(Error::NotDir),
551 Lookup::Found(Entry::Directory { .. }, component_path) => {
552 if component_path.is_empty() {
553 Err(Error::Inval)
555 } else {
556 self.recursive_rmdirs
557 .insert(path.into_os_string().into_string().unwrap())
558 .assert_is_true();
559 Ok(())
560 }
561 }
562 }
563 }
564
565 fn rename(&mut self, source_path: &Path, dest_path: &Path) -> Result<()> {
566 self.check_failure()?;
567
568 let (source_entry, source_component_path) = match self.root.lookup(source_path) {
569 Lookup::FoundParent(_) | Lookup::NotFound | Lookup::DanglingSymlink => {
570 Err(Error::NoEnt)
571 }
572 Lookup::FileAncestor => Err(Error::NotDir),
573 Lookup::Found(source_entry, source_component_path) => {
574 Ok((source_entry, source_component_path))
575 }
576 }?;
577
578 let dest_parent_component_path = match self.root.lookup(dest_path) {
579 Lookup::NotFound | Lookup::DanglingSymlink => {
580 return Err(Error::NoEnt);
581 }
582 Lookup::FileAncestor => {
583 return Err(Error::NotDir);
584 }
585 Lookup::Found(dest_entry, dest_component_path) => {
586 if source_entry.is_directory() {
587 if let Entry::Directory { entries } = dest_entry {
588 if !entries.is_empty() {
589 return Err(Error::NotEmpty);
591 } else if source_component_path == dest_component_path {
592 return Ok(());
597 } else if dest_component_path.is_descendant_of(&source_component_path) {
598 return Err(Error::Inval);
600 }
601 } else {
602 return Err(Error::NotDir);
604 }
605 } else if dest_entry.is_directory() {
606 return Err(Error::IsDir);
608 } else if source_component_path == dest_component_path {
609 return Ok(());
611 }
612
613 self.root.remove_component_path(dest_component_path).1
615 }
616 Lookup::FoundParent(dest_parent_component_path) => {
617 if source_entry.is_directory()
618 && dest_parent_component_path.is_descendant_of(&source_component_path)
619 {
620 return Err(Error::Inval);
622 } else {
623 dest_parent_component_path
624 }
625 }
626 };
627
628 let source_entry = self.root.remove_component_path(source_component_path).0;
629 self.root.append_entry_to_directory(
630 &dest_parent_component_path,
631 dest_path.file_name().unwrap(),
632 source_entry,
633 );
634
635 Ok(())
636 }
637
638 fn temp_file(&mut self, parent: &Path) -> Result<TempFile> {
639 for i in 0.. {
640 let path = parent.join(format!("{i:03}"));
641 match self.create_file(&path, b"") {
642 Ok(()) => {
643 return Ok(TempFile(path));
644 }
645 Err(Error::Exists) => {
646 continue;
647 }
648 Err(err) => {
649 return Err(err);
650 }
651 }
652 }
653 unreachable!();
654 }
655
656 fn persist_temp_file(&mut self, temp_file: TempFile, target: &Path) -> Result<()> {
657 self.rename(&temp_file.0, target)
658 }
659
660 fn temp_dir(&mut self, parent: &Path) -> Result<TempDir> {
661 for i in 0.. {
662 let path = parent.join(format!("{i:03}"));
663 match self.mkdir(&path) {
664 Ok(()) => {
665 return Ok(TempDir(path));
666 }
667 Err(Error::Exists) => {
668 continue;
669 }
670 Err(err) => {
671 return Err(err);
672 }
673 }
674 }
675 unreachable!();
676 }
677
678 fn persist_temp_dir(&mut self, temp_dir: TempDir, target: &Path) -> Result<()> {
679 self.rename(&temp_dir.0, target)
680 }
681}
682
683#[derive(Clone, Debug, Eq, PartialEq)]
686pub enum Entry {
687 File { contents: Box<[u8]> },
688 Directory { entries: BTreeMap<String, Entry> },
689 Symlink { target: String },
690}
691
692impl Entry {
693 pub fn file<'a>(contents: impl IntoIterator<Item = &'a u8>) -> Self {
695 Self::File {
696 contents: contents
697 .into_iter()
698 .copied()
699 .collect::<Vec<_>>()
700 .into_boxed_slice(),
701 }
702 }
703
704 pub fn directory<const N: usize>(entries: [(&str, Self); N]) -> Self {
706 Self::Directory {
707 entries: entries
708 .into_iter()
709 .map(|(name, entry)| (name.to_owned(), entry))
710 .collect(),
711 }
712 }
713
714 pub fn symlink(target: impl ToString) -> Self {
716 Self::Symlink {
717 target: target.to_string(),
718 }
719 }
720
721 fn metadata(&self) -> Metadata {
723 match self {
724 Self::File { contents } => Metadata::file(contents.len().try_into().unwrap()),
725 Self::Symlink { target } => Metadata::symlink(target.len().try_into().unwrap()),
726 Self::Directory { entries } => Metadata::directory(entries.len().try_into().unwrap()),
727 }
728 }
729
730 fn is_directory(&self) -> bool {
732 matches!(self, Self::Directory { .. })
733 }
734
735 fn directory_entries(&self) -> &BTreeMap<String, Self> {
737 let Self::Directory { entries } = self else {
738 panic!("expected entry {self:?} to be a directory");
739 };
740 entries
741 }
742
743 fn directory_entries_mut(&mut self) -> &mut BTreeMap<String, Self> {
745 let Self::Directory { entries } = self else {
746 panic!("expected entry {self:?} to be a directory");
747 };
748 entries
749 }
750
751 fn lookup<'state>(&'state self, path: &Path) -> Lookup<'state> {
753 self.lookup_helper(path, self, Default::default())
754 }
755
756 fn lookup_helper<'state>(
760 &'state self,
761 path: &Path,
762 mut cur: &'state Self,
763 mut component_path: ComponentPath,
764 ) -> Lookup<'state> {
765 for (position, component) in path.components().with_position() {
766 let is_last_component = matches!(position, Position::Last | Position::Only);
767 (cur, component_path) = match component {
768 Component::Prefix(_) => {
769 unimplemented!("prefix components don't occur in Unix")
770 }
771 Component::RootDir => (self, Default::default()),
772 Component::CurDir => (cur, component_path),
773 Component::ParentDir => {
774 component_path = component_path.try_pop().0;
775 (self.resolve_component_path(&component_path), component_path)
776 }
777 Component::Normal(name) => match self.follow_leaf_symlink(cur, component_path) {
778 FollowSymlinks::FoundDirectory(entries, component_path) => {
779 let name = name.to_str().unwrap();
780 match entries.get(name) {
781 Some(entry) => (entry, component_path.push(name)),
782 None => {
783 if is_last_component {
784 return Lookup::FoundParent(component_path);
785 } else {
786 return Lookup::NotFound;
787 }
788 }
789 }
790 }
791 FollowSymlinks::FoundFile(_, _) | FollowSymlinks::FileAncestor => {
792 return Lookup::FileAncestor;
793 }
794 FollowSymlinks::DanglingSymlink => {
795 return Lookup::DanglingSymlink;
796 }
797 },
798 };
799 }
800 Lookup::Found(cur, component_path)
801 }
802
803 fn follow_leaf_symlink<'state>(
806 &'state self,
807 mut cur: &'state Self,
808 mut component_path: ComponentPath,
809 ) -> FollowSymlinks<'state> {
810 loop {
811 match cur {
812 Self::Directory { entries } => {
813 return FollowSymlinks::FoundDirectory(entries, component_path);
814 }
815 Self::File { contents } => {
816 return FollowSymlinks::FoundFile(contents, component_path);
817 }
818 Self::Symlink { target } => {
819 component_path = component_path.pop().0;
820 match self.lookup_helper(
821 Path::new(target),
822 self.resolve_component_path(&component_path),
823 component_path,
824 ) {
825 Lookup::Found(new_cur, new_component_path) => {
826 cur = new_cur;
827 component_path = new_component_path;
828 }
829 Lookup::FoundParent(_) | Lookup::DanglingSymlink | Lookup::NotFound => {
830 return FollowSymlinks::DanglingSymlink;
831 }
832 Lookup::FileAncestor => {
833 return FollowSymlinks::FileAncestor;
834 }
835 }
836 }
837 }
838 }
839 }
840
841 fn lookup_parent(&self, path: &Path) -> Result<ComponentPath> {
847 match self.lookup(path) {
848 Lookup::FileAncestor => Err(Error::NotDir),
849 Lookup::DanglingSymlink => Err(Error::NoEnt),
850 Lookup::NotFound => Err(Error::NoEnt),
851 Lookup::Found(_, _) => Err(Error::Exists),
852 Lookup::FoundParent(component_path) => Ok(component_path),
853 }
854 }
855
856 fn lookup_leaf(&self, path: &Path) -> Result<FollowSymlinks> {
859 match self.lookup(path) {
860 Lookup::FoundParent(_) | Lookup::NotFound | Lookup::DanglingSymlink => {
861 Err(Error::NoEnt)
862 }
863 Lookup::FileAncestor => Err(Error::NotDir),
864 Lookup::Found(entry, component_path) => {
865 Ok(self.follow_leaf_symlink(entry, component_path))
866 }
867 }
868 }
869
870 fn resolve_component_path(&self, component_path: &ComponentPath) -> &Self {
873 let mut cur = self;
874 for component in component_path {
875 cur = cur.directory_entries().get(component).unwrap();
876 }
877 cur
878 }
879
880 fn resolve_component_path_mut(&mut self, component_path: &ComponentPath) -> &mut Self {
883 let mut cur = self;
884 for component in component_path {
885 cur = cur.directory_entries_mut().get_mut(component).unwrap();
886 }
887 cur
888 }
889
890 fn remove_component_path(&mut self, component_path: ComponentPath) -> (Self, ComponentPath) {
894 let (component_path, component) = component_path.pop();
895 let entry = self
896 .resolve_component_path_mut(&component_path)
897 .directory_entries_mut()
898 .remove(&component)
899 .unwrap();
900 (entry, component_path)
901 }
902
903 fn append_entry_to_directory(
907 &mut self,
908 directory_component_path: &ComponentPath,
909 name: &OsStr,
910 entry: Self,
911 ) {
912 self.resolve_component_path_mut(directory_component_path)
913 .directory_entries_mut()
914 .insert(name.to_str().unwrap().to_owned(), entry)
915 .assert_is_none();
916 }
917}
918
919#[derive(Debug)]
920enum Lookup<'state> {
921 Found(&'state Entry, ComponentPath),
923
924 FoundParent(ComponentPath),
927
928 NotFound,
931
932 DanglingSymlink,
934
935 FileAncestor,
937}
938
939enum FollowSymlinks<'state> {
940 FoundFile(&'state [u8], ComponentPath),
942
943 FoundDirectory(&'state BTreeMap<String, Entry>, ComponentPath),
945
946 DanglingSymlink,
948
949 FileAncestor,
951}
952
953#[derive(Clone, Debug, Default, Hash, Eq, PartialEq)]
963struct ComponentPath(Vec<String>);
964
965impl ComponentPath {
966 fn push(mut self, component: &str) -> Self {
967 self.0.push(component.to_owned());
968 self
969 }
970
971 fn pop(self) -> (Self, String) {
972 let (head, tail) = self.try_pop();
973 (head, tail.unwrap())
974 }
975
976 fn try_pop(mut self) -> (Self, Option<String>) {
977 let last = self.0.pop();
978 (self, last)
979 }
980
981 fn len(&self) -> usize {
982 self.0.len()
983 }
984
985 fn is_empty(&self) -> bool {
986 self.0.is_empty()
987 }
988
989 fn iter(&self) -> impl Iterator<Item = &'_ String> {
990 self.0.iter()
991 }
992
993 fn is_descendant_of(&self, potential_ancestor: &ComponentPath) -> bool {
996 self.iter()
997 .zip(potential_ancestor)
998 .take_while(|(l, r)| *l == *r)
999 .count()
1000 == potential_ancestor.len()
1001 }
1002}
1003
1004impl IntoIterator for ComponentPath {
1005 type Item = String;
1006 type IntoIter = vec::IntoIter<String>;
1007 fn into_iter(self) -> Self::IntoIter {
1008 self.0.into_iter()
1009 }
1010}
1011
1012impl<'a> IntoIterator for &'a ComponentPath {
1013 type Item = &'a String;
1014 type IntoIter = slice::Iter<'a, String>;
1015 fn into_iter(self) -> Self::IntoIter {
1016 self.0.iter()
1017 }
1018}
1019
1020#[cfg(test)]
1021mod tests {
1022 use super::{
1023 super::{Fs as _, TempDir as _, TempFile as _},
1024 *,
1025 };
1026 use assert_matches::assert_matches;
1027 use std::collections::HashMap;
1028
1029 mod fs_macro {
1030 use super::*;
1031
1032 #[test]
1033 fn empty() {
1034 assert_eq!(fs! {}, Entry::directory([]));
1035 }
1036
1037 #[test]
1038 fn one_file_no_comma() {
1039 assert_eq!(
1040 fs! {
1041 foo(b"abcd")
1042 },
1043 Entry::directory([("foo", Entry::file(b"abcd"))])
1044 );
1045 }
1046
1047 #[test]
1048 fn one_file_comma() {
1049 assert_eq!(
1050 fs! {
1051 foo(b"abcd"),
1052 },
1053 Entry::directory([("foo", Entry::file(b"abcd"))])
1054 );
1055 }
1056
1057 #[test]
1058 fn one_symlink_no_comma() {
1059 assert_eq!(
1060 fs! {
1061 foo -> "/target"
1062 },
1063 Entry::directory([("foo", Entry::symlink("/target"))])
1064 );
1065 }
1066
1067 #[test]
1068 fn one_symlink_comma() {
1069 assert_eq!(
1070 fs! {
1071 foo -> "/target",
1072 },
1073 Entry::directory([("foo", Entry::symlink("/target"))])
1074 );
1075 }
1076
1077 #[test]
1078 fn one_directory_no_comma() {
1079 assert_eq!(
1080 fs! {
1081 foo {}
1082 },
1083 Entry::directory([("foo", Entry::directory([]))])
1084 );
1085 }
1086
1087 #[test]
1088 fn one_directory_comma() {
1089 assert_eq!(
1090 fs! {
1091 foo {},
1092 },
1093 Entry::directory([("foo", Entry::directory([]))])
1094 );
1095 }
1096
1097 #[test]
1098 fn one_directory_with_no_files() {
1099 assert_eq!(
1100 fs! {
1101 foo {},
1102 },
1103 Entry::directory([("foo", Entry::directory([]))])
1104 );
1105 }
1106
1107 #[test]
1108 fn non_identifier_keys() {
1109 assert_eq!(
1110 fs! {
1111 "foo"(b"abcd"),
1112 "bar" -> "/target/1",
1113 "baz" { "empty" {} },
1114 },
1115 Entry::directory([
1116 ("foo", Entry::file(b"abcd")),
1117 ("bar", Entry::symlink("/target/1")),
1118 ("baz", Entry::directory([("empty", Entry::directory([]))])),
1119 ])
1120 );
1121 }
1122
1123 #[test]
1124 fn kitchen_sink() {
1125 assert_eq!(
1126 fs! {
1127 foo(b"abcd"),
1128 bar -> "/target/1",
1129 baz {
1130 zero {},
1131 one {
1132 foo(b"abcde")
1133 },
1134 two {
1135 foo(b"abcdef"),
1136 bar -> "/target/2"
1137 },
1138 }
1139 },
1140 Entry::directory([
1141 ("foo", Entry::file(b"abcd")),
1142 ("bar", Entry::symlink("/target/1")),
1143 (
1144 "baz",
1145 Entry::directory([
1146 ("zero", Entry::directory([])),
1147 ("one", Entry::directory([("foo", Entry::file(b"abcde"))])),
1148 (
1149 "two",
1150 Entry::directory([
1151 ("foo", Entry::file(b"abcdef")),
1152 ("bar", Entry::symlink("/target/2")),
1153 ])
1154 ),
1155 ])
1156 )
1157 ])
1158 );
1159 }
1160 }
1161
1162 #[test]
1163 fn rand_u64() {
1164 let fs = Fs::new(fs! {});
1165 assert_eq!(fs.rand_u64(), 1);
1166 assert_eq!(fs.rand_u64(), 2);
1167 assert_eq!(fs.rand_u64(), 3);
1168 }
1169
1170 #[test]
1171 fn metadata_empty() {
1172 let fs = Fs::new(fs! {});
1173 assert_eq!(
1174 fs.metadata(Path::new("/")),
1175 Ok(Some(Metadata::directory(0)))
1176 );
1177 assert_eq!(fs.metadata(Path::new("/foo")), Ok(None));
1178 }
1179
1180 #[test]
1181 fn metadata() {
1182 let fs = Fs::new(fs! {
1183 foo(b"contents"),
1184 bar {
1185 baz -> "/target",
1186 root -> "/",
1187 a -> "b",
1188 b -> "../bar/c",
1189 c -> "/bar/d",
1190 d -> ".",
1191 },
1192 });
1193 assert_eq!(
1194 fs.metadata(Path::new("/")),
1195 Ok(Some(Metadata::directory(2)))
1196 );
1197 assert_eq!(fs.metadata(Path::new("/foo")), Ok(Some(Metadata::file(8))));
1198 assert_eq!(
1199 fs.metadata(Path::new("/bar")),
1200 Ok(Some(Metadata::directory(6)))
1201 );
1202 assert_eq!(
1203 fs.metadata(Path::new("/bar/baz")),
1204 Ok(Some(Metadata::symlink(7)))
1205 );
1206 assert_eq!(
1207 fs.metadata(Path::new("/bar/root/foo")),
1208 Ok(Some(Metadata::file(8)))
1209 );
1210 assert_eq!(
1211 fs.metadata(Path::new("/bar/root/bar/a")),
1212 Ok(Some(Metadata::symlink(1)))
1213 );
1214 assert_eq!(
1215 fs.metadata(Path::new("/bar/a/baz")),
1216 Ok(Some(Metadata::symlink(7)))
1217 );
1218
1219 assert_eq!(fs.metadata(Path::new("/foo2")), Ok(None));
1220 assert_eq!(fs.metadata(Path::new("/bar/baz/foo")), Err(Error::NoEnt));
1221 assert_eq!(fs.metadata(Path::new("/bar/baz2")), Ok(None));
1222 assert_eq!(fs.metadata(Path::new("/bar/baz2/blah")), Ok(None));
1223 assert_eq!(fs.metadata(Path::new("/foo/bar")), Err(Error::NotDir));
1224 }
1225
1226 #[test]
1227 fn metadata_forced_failure() {
1228 let fs = Fs::new(fs! {});
1229 fs.set_failure(true);
1230 assert_eq!(fs.metadata(Path::new("/")), Err(Error::Test));
1231 }
1232
1233 #[test]
1234 fn read_file() {
1235 let fs = Fs::new(fs! {
1236 foo(b"foo"),
1237 empty(b""),
1238 subdir {
1239 subdir {
1240 root -> "../dotdot",
1241 },
1242 dotdot -> "..",
1243 },
1244 symlink -> "symlink2",
1245 symlink2 -> "subdir/subdir/root/foo",
1246 bad_symlink -> "dangle",
1247 });
1248
1249 let mut buf = [0u8; 10];
1250
1251 assert_eq!(fs.read_file(Path::new("/foo"), &mut buf[..]), Ok(3));
1252 assert_eq!(&buf[..3], b"foo");
1253 assert_eq!(fs.read_file(Path::new("/foo"), &mut buf[..1]), Ok(1));
1254 assert_eq!(&buf[..1], b"f");
1255 assert_eq!(fs.read_file(Path::new("/foo"), &mut buf[..0]), Ok(0));
1256
1257 assert_eq!(fs.read_file(Path::new("/empty"), &mut buf[..]), Ok(0));
1258
1259 assert_eq!(
1260 fs.read_file(Path::new("/subdir/subdir/root/foo"), &mut buf[..]),
1261 Ok(3)
1262 );
1263 assert_eq!(&buf[..3], b"foo");
1264
1265 assert_eq!(fs.read_file(Path::new("/symlink"), &mut buf[..]), Ok(3));
1266 assert_eq!(&buf[..3], b"foo");
1267
1268 assert_eq!(
1269 fs.read_file(Path::new("/missing"), &mut buf[..]),
1270 Err(Error::NoEnt)
1271 );
1272 assert_eq!(
1273 fs.read_file(Path::new("/subdir/missing"), &mut buf[..]),
1274 Err(Error::NoEnt)
1275 );
1276 assert_eq!(
1277 fs.read_file(Path::new("/dangle/missing"), &mut buf[..]),
1278 Err(Error::NoEnt)
1279 );
1280 assert_eq!(
1281 fs.read_file(Path::new("/foo/bar"), &mut buf[..]),
1282 Err(Error::NotDir)
1283 );
1284 assert_eq!(
1285 fs.read_file(Path::new("/subdir"), &mut buf[..]),
1286 Err(Error::IsDir)
1287 );
1288 assert_eq!(
1289 fs.read_file(Path::new("/subdir/dotdot"), &mut buf[..]),
1290 Err(Error::IsDir)
1291 );
1292 assert_eq!(
1293 fs.read_file(Path::new("/bad_symlink"), &mut buf[..]),
1294 Err(Error::NoEnt)
1295 );
1296 }
1297
1298 #[test]
1299 fn read_file_forced_failure() {
1300 let fs = Fs::new(fs! { foo(b"contents")});
1301 fs.set_failure(true);
1302 let mut buf = [0u8; 10];
1303 assert_eq!(
1304 fs.read_file(Path::new("/foo"), &mut buf[..]),
1305 Err(Error::Test)
1306 );
1307 }
1308
1309 #[test]
1310 fn read_dir() {
1311 let fs = Fs::new(fs! {
1312 foo(b"abcd"),
1313 bar {
1314 baz -> "/target",
1315 root -> "/",
1316 subdir {},
1317 },
1318 });
1319 assert_eq!(
1320 fs.read_dir(Path::new("/"))
1321 .unwrap()
1322 .map(Result::unwrap)
1323 .collect::<HashMap<_, _>>(),
1324 HashMap::from([
1325 ("foo".into(), Metadata::file(4)),
1326 ("bar".into(), Metadata::directory(3)),
1327 ]),
1328 );
1329 assert_eq!(
1330 fs.read_dir(Path::new("/bar"))
1331 .unwrap()
1332 .map(Result::unwrap)
1333 .collect::<HashMap<_, _>>(),
1334 HashMap::from([
1335 ("baz".into(), Metadata::symlink(7)),
1336 ("root".into(), Metadata::symlink(1)),
1337 ("subdir".into(), Metadata::directory(0)),
1338 ]),
1339 );
1340 assert_eq!(
1341 fs.read_dir(Path::new("/bar/root/bar/subdir"))
1342 .unwrap()
1343 .map(Result::unwrap)
1344 .collect::<HashMap<_, _>>(),
1345 HashMap::default(),
1346 );
1347 assert_eq!(
1348 fs.read_dir(Path::new("/bar/root"))
1349 .unwrap()
1350 .map(Result::unwrap)
1351 .collect::<HashMap<_, _>>(),
1352 HashMap::from([
1353 ("foo".into(), Metadata::file(4)),
1354 ("bar".into(), Metadata::directory(3)),
1355 ]),
1356 );
1357
1358 assert!(matches!(fs.read_dir(Path::new("/foo")), Err(Error::NotDir)));
1359 assert!(matches!(
1360 fs.read_dir(Path::new("/foo/foo")),
1361 Err(Error::NotDir)
1362 ));
1363 assert!(matches!(
1364 fs.read_dir(Path::new("/bar/baz")),
1365 Err(Error::NoEnt)
1366 ));
1367 assert!(matches!(
1368 fs.read_dir(Path::new("/bar/baz/foo")),
1369 Err(Error::NoEnt)
1370 ));
1371 assert!(matches!(fs.read_dir(Path::new("/blah")), Err(Error::NoEnt)));
1372 assert!(matches!(
1373 fs.read_dir(Path::new("/blah/blah")),
1374 Err(Error::NoEnt)
1375 ));
1376 }
1377
1378 #[test]
1379 fn read_dir_forced_failure() {
1380 let fs = Fs::new(fs! {});
1381 fs.set_failure(true);
1382 assert!(matches!(fs.read_dir(Path::new("/")), Err(Error::Test)));
1383 }
1384
1385 #[test]
1386 fn create_file() {
1387 let fs = Fs::new(fs! {
1388 foo(b"abcd"),
1389 bar {
1390 baz -> "/target",
1391 root -> "/",
1392 a -> "b",
1393 b -> "../bar/c",
1394 c -> "/bar/d",
1395 d -> ".",
1396 },
1397 });
1398
1399 assert_eq!(fs.create_file(Path::new("/new_file"), b"contents"), Ok(()));
1400 assert_eq!(
1401 fs.metadata(Path::new("/new_file")),
1402 Ok(Some(Metadata::file(8)))
1403 );
1404
1405 assert_eq!(
1406 fs.create_file(Path::new("/bar/root/bar/a/new_file"), b"contents-2"),
1407 Ok(())
1408 );
1409 assert_eq!(
1410 fs.metadata(Path::new("/bar/new_file")),
1411 Ok(Some(Metadata::file(10)))
1412 );
1413
1414 assert_eq!(
1415 fs.create_file(Path::new("/new_file"), b"contents-3"),
1416 Err(Error::Exists)
1417 );
1418 assert_eq!(
1419 fs.create_file(Path::new("/foo"), b"contents-3"),
1420 Err(Error::Exists)
1421 );
1422 assert_eq!(
1423 fs.create_file(Path::new("/blah/new_file"), b"contents-3"),
1424 Err(Error::NoEnt)
1425 );
1426 assert_eq!(
1427 fs.create_file(Path::new("/foo/new_file"), b"contents-3"),
1428 Err(Error::NotDir)
1429 );
1430 assert_eq!(
1431 fs.create_file(Path::new("/bar/baz/new_file"), b"contents-3"),
1432 Err(Error::NoEnt)
1433 );
1434 }
1435
1436 #[test]
1437 fn create_file_forced_failure() {
1438 let fs = Fs::new(fs! {});
1439 fs.set_failure(true);
1440 assert_eq!(
1441 fs.create_file(Path::new("/foo"), b"contents"),
1442 Err(Error::Test)
1443 );
1444 }
1445
1446 #[test]
1447 fn possibly_create_file_and_try_exclusive_lock_lookup() {
1448 let fs = Fs::new(fs! {
1449 symlink -> "subdir/foo",
1450 subdir {
1451 foo(b"abcd"),
1452 },
1453 subdir_symlink -> "subdir",
1454 dangle -> "nowhere",
1455 bad_symlink -> "subdir/foo/bar",
1456 });
1457
1458 assert_matches!(
1459 fs.possibly_create_file_and_try_exclusive_lock(Path::new("/subdir")),
1460 Err(Error::IsDir)
1461 );
1462 assert_matches!(
1463 fs.possibly_create_file_and_try_exclusive_lock(Path::new("/subdir_symlink")),
1464 Err(Error::IsDir)
1465 );
1466 assert_matches!(
1467 fs.possibly_create_file_and_try_exclusive_lock(Path::new("/dangle/and/more")),
1468 Err(Error::NoEnt)
1469 );
1470 assert_matches!(
1471 fs.possibly_create_file_and_try_exclusive_lock(Path::new("/does/not/exist")),
1472 Err(Error::NoEnt)
1473 );
1474 assert_matches!(
1475 fs.possibly_create_file_and_try_exclusive_lock(Path::new("/dangle")),
1476 Err(Error::NoEnt)
1477 );
1478 assert_matches!(
1479 fs.possibly_create_file_and_try_exclusive_lock(Path::new("/subdir/foo/bar")),
1480 Err(Error::NotDir)
1481 );
1482 assert_matches!(
1483 fs.possibly_create_file_and_try_exclusive_lock(Path::new("/bad_symlink")),
1484 Err(Error::NotDir)
1485 );
1486
1487 assert_matches!(
1488 fs.possibly_create_file_and_try_exclusive_lock(Path::new("/subdir/foo")),
1489 Ok(Some(_))
1490 );
1491 assert_matches!(
1492 fs.possibly_create_file_and_try_exclusive_lock(Path::new("/symlink")),
1493 Ok(Some(_))
1494 );
1495 assert_matches!(
1496 fs.possibly_create_file_and_try_exclusive_lock(Path::new("/subdir/bar")),
1497 Ok(Some(_))
1498 );
1499
1500 fs.assert_entry(
1501 Path::new("/subdir"),
1502 fs! {
1503 foo(b"abcd"),
1504 bar(b""),
1505 },
1506 );
1507 }
1508
1509 #[test]
1510 fn possibly_create_file_and_try_exclusive_lock_contend() {
1511 let fs = Fs::new(fs! {
1512 symlink -> "foo",
1513 foo(b"abcd"),
1514 });
1515
1516 let file_lock = fs
1517 .possibly_create_file_and_try_exclusive_lock(Path::new("/foo"))
1518 .unwrap()
1519 .unwrap();
1520 assert_matches!(
1521 fs.possibly_create_file_and_try_exclusive_lock(Path::new("/foo")),
1522 Ok(None)
1523 );
1524 drop(file_lock);
1525 let file_lock = fs
1526 .possibly_create_file_and_try_exclusive_lock(Path::new("/foo"))
1527 .unwrap()
1528 .unwrap();
1529 assert_matches!(
1530 fs.possibly_create_file_and_try_exclusive_lock(Path::new("/symlink")),
1531 Ok(None)
1532 );
1533 drop(file_lock);
1534 assert_matches!(
1535 fs.possibly_create_file_and_try_exclusive_lock(Path::new("/symlink")),
1536 Ok(Some(_))
1537 );
1538 assert_matches!(
1539 fs.possibly_create_file_and_try_exclusive_lock(Path::new("/bar")),
1540 Ok(Some(_))
1541 );
1542 }
1543
1544 #[test]
1545 fn possibly_create_file_and_try_exclusive_lock_forced_failure() {
1546 let fs = Fs::new(fs! {});
1547 fs.set_failure(true);
1548 assert_matches!(
1549 fs.possibly_create_file_and_try_exclusive_lock(Path::new("/foo")),
1550 Err(Error::Test)
1551 );
1552 }
1553
1554 #[test]
1555 fn symlink() {
1556 let fs = Fs::new(fs! {
1557 foo(b"abcd"),
1558 bar {
1559 baz -> "/target",
1560 root -> "/",
1561 a -> "b",
1562 b -> "../bar/c",
1563 c -> "/bar/d",
1564 d -> ".",
1565 },
1566 });
1567
1568 assert_eq!(
1569 fs.symlink(Path::new("new-target"), Path::new("/new_symlink")),
1570 Ok(())
1571 );
1572 assert_eq!(
1573 fs.metadata(Path::new("/new_symlink")),
1574 Ok(Some(Metadata::symlink(10)))
1575 );
1576
1577 assert_eq!(
1578 fs.symlink(
1579 Path::new("new-target-2"),
1580 Path::new("/bar/root/bar/a/new_symlink"),
1581 ),
1582 Ok(())
1583 );
1584 assert_eq!(
1585 fs.metadata(Path::new("/bar/new_symlink")),
1586 Ok(Some(Metadata::symlink(12)))
1587 );
1588
1589 assert_eq!(
1590 fs.symlink(Path::new("new-target-3"), Path::new("/new_symlink")),
1591 Err(Error::Exists)
1592 );
1593 assert_eq!(
1594 fs.symlink(Path::new("new-target-3"), Path::new("/foo")),
1595 Err(Error::Exists)
1596 );
1597 assert_eq!(
1598 fs.symlink(Path::new("new-target-3"), Path::new("/blah/new_symlink")),
1599 Err(Error::NoEnt)
1600 );
1601 assert_eq!(
1602 fs.symlink(Path::new("new-target-3"), Path::new("/foo/new_symlink")),
1603 Err(Error::NotDir)
1604 );
1605 assert_eq!(
1606 fs.symlink(Path::new("new-target-3"), Path::new("/bar/baz/new_symlink")),
1607 Err(Error::NoEnt)
1608 );
1609 }
1610
1611 #[test]
1612 fn symlink_forced_failure() {
1613 let fs = Fs::new(fs! {});
1614 fs.set_failure(true);
1615 assert_eq!(
1616 fs.symlink(Path::new("target"), Path::new("/symlink")),
1617 Err(Error::Test)
1618 );
1619 }
1620
1621 #[test]
1622 fn mkdir() {
1623 let fs = Fs::new(fs! {
1624 foo(b"abcd"),
1625 bar {
1626 baz -> "/target",
1627 root -> "/",
1628 },
1629 });
1630
1631 assert_eq!(fs.mkdir(Path::new("/")), Err(Error::Exists));
1632 assert_eq!(fs.mkdir(Path::new("/bar/blah/foo")), Err(Error::NoEnt));
1633 assert_eq!(fs.mkdir(Path::new("/bar/baz/blah")), Err(Error::NoEnt));
1634 assert_eq!(fs.mkdir(Path::new("/blah/baz")), Err(Error::NoEnt));
1635 assert_eq!(fs.mkdir(Path::new("/foo/bar/baz")), Err(Error::NotDir));
1636
1637 assert_eq!(fs.mkdir(Path::new("/bar/blah")), Ok(()));
1638 assert_eq!(fs.mkdir(Path::new("/bar/root/dir1")), Ok(()));
1639 assert_eq!(fs.mkdir(Path::new("/bar/root/dir1/dir2")), Ok(()));
1640
1641 fs.assert_tree(fs! {
1642 foo(b"abcd"),
1643 bar {
1644 baz -> "/target",
1645 root -> "/",
1646 blah {},
1647 },
1648 dir1 {
1649 dir2 {},
1650 },
1651 });
1652 }
1653
1654 #[test]
1655 fn mkdir_forced_failure() {
1656 let fs = Fs::new(fs! {});
1657 fs.set_failure(true);
1658 assert_eq!(fs.mkdir(Path::new("/foo")), Err(Error::Test));
1659 }
1660
1661 #[test]
1662 fn mkdir_recursively() {
1663 let fs = Fs::new(fs! {
1664 foo(b"abcd"),
1665 bar {
1666 baz -> "/target",
1667 root -> "/",
1668 a -> "b",
1669 b -> "../bar/c",
1670 c -> "/bar/d",
1671 d -> ".",
1672 },
1673 });
1674
1675 assert_eq!(fs.mkdir_recursively(Path::new("/")), Ok(()));
1676 assert_eq!(fs.mkdir_recursively(Path::new("/bar")), Ok(()));
1677
1678 assert_eq!(
1679 fs.mkdir_recursively(Path::new("/new/directory/and/subdirectory")),
1680 Ok(())
1681 );
1682 assert_eq!(
1683 fs.metadata(Path::new("/new")),
1684 Ok(Some(Metadata::directory(1)))
1685 );
1686 assert_eq!(
1687 fs.metadata(Path::new("/new/directory")),
1688 Ok(Some(Metadata::directory(1)))
1689 );
1690 assert_eq!(
1691 fs.metadata(Path::new("/new/directory/and")),
1692 Ok(Some(Metadata::directory(1)))
1693 );
1694 assert_eq!(
1695 fs.metadata(Path::new("/new/directory/and/subdirectory")),
1696 Ok(Some(Metadata::directory(0)))
1697 );
1698
1699 assert_eq!(
1700 fs.mkdir_recursively(Path::new("/bar/root/bar/a/new/directory")),
1701 Ok(())
1702 );
1703 assert_eq!(
1704 fs.metadata(Path::new("/bar/new")),
1705 Ok(Some(Metadata::directory(1)))
1706 );
1707 assert_eq!(
1708 fs.metadata(Path::new("/bar/new/directory")),
1709 Ok(Some(Metadata::directory(0)))
1710 );
1711
1712 assert_eq!(fs.mkdir_recursively(Path::new("/foo")), Err(Error::Exists));
1713 assert_eq!(
1714 fs.mkdir_recursively(Path::new("/foo/baz")),
1715 Err(Error::NotDir)
1716 );
1717 assert_eq!(
1718 fs.mkdir_recursively(Path::new("/bar/baz/new/directory")),
1719 Err(Error::NoEnt)
1720 );
1721 }
1722
1723 #[test]
1724 fn mkdir_recursively_forced_failure() {
1725 let fs = Fs::new(fs! {bar{}});
1726 fs.set_failure(true);
1727 assert_eq!(fs.mkdir_recursively(Path::new("/foo")), Err(Error::Test));
1728 assert_eq!(fs.mkdir_recursively(Path::new("/bar")), Err(Error::Test));
1729 assert_eq!(
1730 fs.mkdir_recursively(Path::new("/foo/bar")),
1731 Err(Error::Test)
1732 );
1733 assert_eq!(
1734 fs.mkdir_recursively(Path::new("/bar/foo")),
1735 Err(Error::Test)
1736 );
1737 }
1738
1739 #[test]
1740 fn remove() {
1741 let fs = Fs::new(fs! {
1742 foo(b"abcd"),
1743 bar {
1744 baz -> "/target",
1745 root -> "/",
1746 a -> "b",
1747 b -> "../bar/c",
1748 c -> "/bar/d",
1749 d -> ".",
1750 },
1751 });
1752
1753 assert_eq!(fs.remove(Path::new("/")), Err(Error::IsDir));
1754 assert_eq!(fs.remove(Path::new("/bar")), Err(Error::IsDir));
1755 assert_eq!(fs.remove(Path::new("/foo/baz")), Err(Error::NotDir));
1756 assert_eq!(fs.remove(Path::new("/bar/baz/blah")), Err(Error::NoEnt));
1757
1758 assert_eq!(fs.remove(Path::new("/bar/baz")), Ok(()));
1759 assert_eq!(fs.metadata(Path::new("/bar/baz")), Ok(None));
1760
1761 assert_eq!(fs.remove(Path::new("/bar/a")), Ok(()));
1762 assert_eq!(fs.metadata(Path::new("/bar/a")), Ok(None));
1763 assert_eq!(
1764 fs.metadata(Path::new("/bar/b")),
1765 Ok(Some(Metadata::symlink(8)))
1766 );
1767
1768 assert_eq!(fs.remove(Path::new("/foo")), Ok(()));
1769 assert_eq!(fs.metadata(Path::new("/foo")), Ok(None));
1770 }
1771
1772 #[test]
1773 fn remove_forced_failure() {
1774 let fs = Fs::new(fs! {foo(b"contents")});
1775 fs.set_failure(true);
1776 assert_eq!(fs.remove(Path::new("/foo")), Err(Error::Test));
1777 }
1778
1779 #[test]
1780 fn rmdir_recursively_on_thread() {
1781 let fs = Fs::new(fs! {
1782 dir1 {
1783 dir2 {
1784 file1(b"1"),
1785 symlink1 -> "file1",
1786 },
1787 },
1788 dir3 {
1789 dir4 {
1790 file2(b"2"),
1791 symlink2 -> "file2",
1792 },
1793 },
1794 });
1795 assert_eq!(fs.rmdir_recursively_on_thread("/dir1".into()), Ok(()));
1796 assert_eq!(fs.rmdir_recursively_on_thread("/dir3".into()), Ok(()));
1797 fs.assert_tree(fs! {
1798 dir1 {
1799 dir2 {
1800 file1(b"1"),
1801 symlink1 -> "file1",
1802 },
1803 },
1804 dir3 {
1805 dir4 {
1806 file2(b"2"),
1807 symlink2 -> "file2",
1808 },
1809 },
1810 });
1811 fs.assert_recursive_rmdirs(HashSet::from(["/dir1".into(), "/dir3".into()]));
1812 fs.complete_recursive_rmdir("/dir1");
1813 fs.assert_tree(fs! {
1814 dir3 {
1815 dir4 {
1816 file2(b"2"),
1817 symlink2 -> "file2",
1818 },
1819 },
1820 });
1821 fs.complete_recursive_rmdir("/dir3");
1822 fs.assert_tree(fs! {});
1823 }
1824
1825 #[test]
1826 fn rmdir_recursively_on_thread_errors() {
1827 let fs = Fs::new(fs! {
1828 foo(b"a"),
1829 bar {
1830 baz -> "/target",
1831 },
1832 });
1833
1834 assert_eq!(
1835 fs.rmdir_recursively_on_thread("/".into()),
1836 Err(Error::Inval)
1837 );
1838 assert_eq!(
1839 fs.rmdir_recursively_on_thread("/foo".into()),
1840 Err(Error::NotDir)
1841 );
1842 assert_eq!(
1843 fs.rmdir_recursively_on_thread("/bar/baz".into()),
1844 Err(Error::NotDir)
1845 );
1846 assert_eq!(
1847 fs.rmdir_recursively_on_thread("/bar/frob".into()),
1848 Err(Error::NoEnt)
1849 );
1850 assert_eq!(
1851 fs.rmdir_recursively_on_thread("/bar/frob/blah".into()),
1852 Err(Error::NoEnt)
1853 );
1854 assert_eq!(
1855 fs.rmdir_recursively_on_thread("/bar/baz/blah".into()),
1856 Err(Error::NoEnt)
1857 );
1858 }
1859
1860 #[test]
1861 fn rmdir_forced_failure() {
1862 let fs = Fs::new(fs! {foo{}});
1863 fs.set_failure(true);
1864 assert_eq!(
1865 fs.rmdir_recursively_on_thread("/foo".into()),
1866 Err(Error::Test)
1867 );
1868 }
1869
1870 #[test]
1871 fn rename_source_path_with_file_ancestor() {
1872 let expected = fs! { foo(b"abcd") };
1873 let fs = Fs::new(expected.clone());
1874 assert_eq!(
1875 fs.rename(Path::new("/foo/bar"), Path::new("/bar")),
1876 Err(Error::NotDir)
1877 );
1878 fs.assert_tree(expected);
1879 }
1880
1881 #[test]
1882 fn rename_source_path_with_dangling_symlink() {
1883 let expected = fs! { foo -> "/dangle" };
1884 let fs = Fs::new(expected.clone());
1885 assert_eq!(
1886 fs.rename(Path::new("/foo/bar"), Path::new("/bar")),
1887 Err(Error::NoEnt)
1888 );
1889 fs.assert_tree(expected);
1890 }
1891
1892 #[test]
1893 fn rename_source_path_not_found() {
1894 let expected = fs! {};
1895 let fs = Fs::new(expected.clone());
1896 assert_eq!(
1897 fs.rename(Path::new("/foo"), Path::new("/bar")),
1898 Err(Error::NoEnt)
1899 );
1900 assert_eq!(
1901 fs.rename(Path::new("/foo/bar"), Path::new("/bar")),
1902 Err(Error::NoEnt)
1903 );
1904 fs.assert_tree(expected);
1905 }
1906
1907 #[test]
1908 fn rename_destination_path_with_file_ancestor() {
1909 let expected = fs! { foo(b"a"), bar(b"b") };
1910 let fs = Fs::new(expected.clone());
1911 assert_eq!(
1912 fs.rename(Path::new("/foo"), Path::new("/bar/baz")),
1913 Err(Error::NotDir)
1914 );
1915 fs.assert_tree(expected);
1916 }
1917
1918 #[test]
1919 fn rename_destination_path_with_dangling_symlink() {
1920 let expected = fs! { foo(b"a"), bar -> "dangle" };
1921 let fs = Fs::new(expected.clone());
1922 assert_eq!(
1923 fs.rename(Path::new("/foo"), Path::new("/bar/baz")),
1924 Err(Error::NoEnt)
1925 );
1926 fs.assert_tree(expected);
1927 }
1928
1929 #[test]
1930 fn rename_destination_path_not_found() {
1931 let expected = fs! { foo(b"a") };
1932 let fs = Fs::new(expected.clone());
1933 assert_eq!(
1934 fs.rename(Path::new("/foo"), Path::new("/bar/baz")),
1935 Err(Error::NoEnt)
1936 );
1937 fs.assert_tree(expected);
1938 }
1939
1940 #[test]
1941 fn rename_directory_into_new_entry_in_itself() {
1942 let expected = fs! { foo { bar { baz {} } } };
1943 let fs = Fs::new(expected.clone());
1944 assert_eq!(
1945 fs.rename(Path::new("/foo"), Path::new("/foo/bar/baz/a")),
1946 Err(Error::Inval)
1947 );
1948 fs.assert_tree(expected);
1949 }
1950
1951 #[test]
1952 fn rename_directory_into_new_entry_in_descendant_of_itself() {
1953 let expected = fs! { dir {} };
1954 let fs = Fs::new(expected.clone());
1955 assert_eq!(
1956 fs.rename(Path::new("/dir"), Path::new("/dir/a")),
1957 Err(Error::Inval)
1958 );
1959 fs.assert_tree(expected);
1960 }
1961
1962 #[test]
1963 fn rename_directory_onto_nonempty_directory() {
1964 let expected = fs! { dir1 {}, dir2 { foo(b"a") } };
1965 let fs = Fs::new(expected.clone());
1966 assert_eq!(
1967 fs.rename(Path::new("/dir1"), Path::new("/dir2")),
1968 Err(Error::NotEmpty)
1969 );
1970 fs.assert_tree(expected);
1971 }
1972
1973 #[test]
1974 fn rename_nonempty_directory_onto_itself() {
1975 let expected = fs! { dir { foo(b"a") } };
1976 let fs = Fs::new(expected.clone());
1977 assert_eq!(
1978 fs.rename(Path::new("/dir"), Path::new("/dir")),
1979 Err(Error::NotEmpty)
1980 );
1981 fs.assert_tree(expected);
1982 }
1983
1984 #[test]
1985 fn rename_nonempty_root_onto_itself() {
1986 let expected = fs! { foo(b"a") };
1987 let fs = Fs::new(expected.clone());
1988 assert_eq!(
1989 fs.rename(Path::new("/"), Path::new("/")),
1990 Err(Error::NotEmpty)
1991 );
1992 fs.assert_tree(expected);
1993 }
1994
1995 #[test]
1996 fn rename_directory_onto_descendant() {
1997 let expected = fs! { dir1 { dir2 {} } };
1998 let fs = Fs::new(expected.clone());
1999 assert_eq!(
2000 fs.rename(Path::new("/dir1"), Path::new("/dir1/dir2")),
2001 Err(Error::Inval)
2002 );
2003 fs.assert_tree(expected);
2004 }
2005
2006 #[test]
2007 fn rename_directory_onto_nondirectory() {
2008 let expected = fs! { dir {}, file(b"a"), symlink -> "file" };
2009 let fs = Fs::new(expected.clone());
2010 assert_eq!(
2011 fs.rename(Path::new("/dir"), Path::new("/file")),
2012 Err(Error::NotDir)
2013 );
2014 assert_eq!(
2015 fs.rename(Path::new("/dir"), Path::new("/symlink")),
2016 Err(Error::NotDir)
2017 );
2018 fs.assert_tree(expected);
2019 }
2020
2021 #[test]
2022 fn rename_nondirectory_onto_directory() {
2023 let expected = fs! { dir {}, file(b"a"), symlink -> "file" };
2024 let fs = Fs::new(expected.clone());
2025 assert_eq!(
2026 fs.rename(Path::new("/file"), Path::new("/dir")),
2027 Err(Error::IsDir)
2028 );
2029 assert_eq!(
2030 fs.rename(Path::new("/symlink"), Path::new("/dir")),
2031 Err(Error::IsDir)
2032 );
2033 fs.assert_tree(expected);
2034 }
2035
2036 #[test]
2037 fn rename_empty_root_onto_itself() {
2038 let fs = Fs::new(fs! {});
2039 assert_eq!(fs.rename(Path::new("/"), Path::new("/")), Ok(()));
2040 fs.assert_tree(fs! {});
2041 }
2042
2043 #[test]
2044 fn rename_empty_directory_onto_itself() {
2045 let expected = fs! {
2046 dir {},
2047 root -> "/",
2048 root2 -> ".",
2049 };
2050 let fs = Fs::new(expected.clone());
2051 assert_eq!(fs.rename(Path::new("/dir"), Path::new("/dir")), Ok(()));
2052 fs.assert_tree(expected.clone());
2053 assert_eq!(fs.rename(Path::new("/dir"), Path::new("/root/dir")), Ok(()));
2054 fs.assert_tree(expected.clone());
2055 assert_eq!(fs.rename(Path::new("/root/dir"), Path::new("/dir")), Ok(()));
2056 fs.assert_tree(expected.clone());
2057 assert_eq!(
2058 fs.rename(Path::new("/root/root2/dir"), Path::new("/root2/root/dir")),
2059 Ok(())
2060 );
2061 fs.assert_tree(expected);
2062 }
2063
2064 #[test]
2065 fn rename_file_onto_itself() {
2066 let expected = fs! {
2067 file(b"a"),
2068 root -> "/",
2069 root2 -> ".",
2070 };
2071 let fs = Fs::new(expected.clone());
2072 assert_eq!(fs.rename(Path::new("/file"), Path::new("/file")), Ok(()));
2073 fs.assert_tree(expected.clone());
2074 assert_eq!(
2075 fs.rename(Path::new("/file"), Path::new("/root/file")),
2076 Ok(())
2077 );
2078 fs.assert_tree(expected.clone());
2079 assert_eq!(
2080 fs.rename(Path::new("/root/file"), Path::new("/file")),
2081 Ok(())
2082 );
2083 fs.assert_tree(expected.clone());
2084 assert_eq!(
2085 fs.rename(Path::new("/root/root2/file"), Path::new("/root2/root/file")),
2086 Ok(())
2087 );
2088 fs.assert_tree(expected);
2089 }
2090
2091 #[test]
2092 fn rename_symlink_onto_itself() {
2093 let expected = fs! {
2094 symlink -> "dangle",
2095 root -> "/",
2096 root2 -> ".",
2097 };
2098 let fs = Fs::new(expected.clone());
2099 assert_eq!(
2100 fs.rename(Path::new("/symlink"), Path::new("/symlink")),
2101 Ok(())
2102 );
2103 fs.assert_tree(expected.clone());
2104 assert_eq!(
2105 fs.rename(Path::new("/symlink"), Path::new("/root/symlink")),
2106 Ok(())
2107 );
2108 fs.assert_tree(expected.clone());
2109 assert_eq!(
2110 fs.rename(Path::new("/root/symlink"), Path::new("/symlink")),
2111 Ok(())
2112 );
2113 fs.assert_tree(expected.clone());
2114 assert_eq!(
2115 fs.rename(
2116 Path::new("/root/root2/symlink"),
2117 Path::new("/root2/root/symlink")
2118 ),
2119 Ok(())
2120 );
2121 fs.assert_tree(expected.clone());
2122 assert_eq!(
2123 fs.rename(Path::new("/root"), Path::new("/root2/root")),
2124 Ok(())
2125 );
2126 fs.assert_tree(expected);
2127 }
2128
2129 #[test]
2130 fn rename_file_onto_file() {
2131 let fs = Fs::new(fs! { foo(b"a"), bar(b"b") });
2132 assert_eq!(fs.rename(Path::new("/foo"), Path::new("/bar")), Ok(()));
2133 fs.assert_tree(fs! { bar(b"a") });
2134 }
2135
2136 #[test]
2137 fn rename_file_onto_symlink() {
2138 let fs = Fs::new(fs! { foo(b"a"), bar -> "foo" });
2139 assert_eq!(fs.rename(Path::new("/foo"), Path::new("/bar")), Ok(()));
2140 fs.assert_tree(fs! { bar(b"a") });
2141 }
2142
2143 #[test]
2144 fn rename_symlink_onto_file() {
2145 let fs = Fs::new(fs! { foo -> "bar", bar(b"a") });
2146 assert_eq!(fs.rename(Path::new("/foo"), Path::new("/bar")), Ok(()));
2147 fs.assert_tree(fs! { bar -> "bar" });
2148 }
2149
2150 #[test]
2151 fn rename_symlink_onto_symlink() {
2152 let fs = Fs::new(fs! { foo -> "bar", bar -> "foo" });
2153 assert_eq!(fs.rename(Path::new("/foo"), Path::new("/bar")), Ok(()));
2154 fs.assert_tree(fs! { bar -> "bar" });
2155 }
2156
2157 #[test]
2158 fn rename_into_new_entries_in_directory() {
2159 let fs = Fs::new(fs! {
2160 a {
2161 file(b"a"),
2162 symlink -> "/dangle",
2163 dir { c(b"b"), d -> "c", e {} },
2164 },
2165 b {},
2166 });
2167 assert_eq!(
2168 fs.rename(Path::new("/a/file"), Path::new("/b/xile")),
2169 Ok(())
2170 );
2171 assert_eq!(
2172 fs.rename(Path::new("/a/symlink"), Path::new("/b/xymlink")),
2173 Ok(())
2174 );
2175 assert_eq!(fs.rename(Path::new("/a/dir"), Path::new("/b/xir")), Ok(()));
2176 fs.assert_tree(fs! {
2177 a {},
2178 b {
2179 xile(b"a"),
2180 xymlink -> "/dangle",
2181 xir { c(b"b"), d -> "c", e {} },
2182 },
2183 });
2184 }
2185
2186 #[test]
2187 fn rename_directory() {
2188 let fs = Fs::new(fs! {
2189 a {
2190 b {
2191 c(b"a"),
2192 d -> "c",
2193 e {},
2194 },
2195 },
2196 f {},
2197 });
2198 assert_eq!(fs.rename(Path::new("/a/b"), Path::new("/f/g")), Ok(()));
2199 fs.assert_tree(fs! {
2200 a {},
2201 f {
2202 g {
2203 c(b"a"),
2204 d -> "c",
2205 e {},
2206 },
2207 },
2208 });
2209 }
2210
2211 #[test]
2212 fn rename_destination_in_ancestor_of_source() {
2213 let fs = Fs::new(fs! {
2214 before(b"a"),
2215 target(b"b"),
2216 dir1 {
2217 dir2 {
2218 source(b"c"),
2219 },
2220 },
2221 after(b"d"),
2222 });
2223 assert_eq!(
2224 fs.rename(Path::new("/dir1/dir2/source"), Path::new("/target")),
2225 Ok(())
2226 );
2227 fs.assert_tree(fs! {
2228 before(b"a"),
2229 dir1 {
2230 dir2 {},
2231 },
2232 after(b"d"),
2233 target(b"c"),
2234 });
2235 }
2236
2237 #[test]
2238 fn rename_source_in_ancestor_of_target() {
2239 let fs = Fs::new(fs! {
2240 before(b"a"),
2241 source(b"b"),
2242 dir1 {
2243 dir2 {
2244 target(b"c"),
2245 },
2246 },
2247 after(b"d"),
2248 });
2249 assert_eq!(
2250 fs.rename(Path::new("/source"), Path::new("/dir1/dir2/target")),
2251 Ok(())
2252 );
2253 fs.assert_tree(fs! {
2254 before(b"a"),
2255 dir1 {
2256 dir2 {
2257 target(b"b"),
2258 },
2259 },
2260 after(b"d"),
2261 });
2262 }
2263
2264 #[test]
2265 fn rename_forced_failure() {
2266 let fs = Fs::new(fs! {foo(b"contents")});
2267 fs.set_failure(true);
2268 assert_eq!(
2269 fs.rename(Path::new("/foo"), Path::new("/bar")),
2270 Err(Error::Test)
2271 );
2272 }
2273
2274 #[test]
2275 fn temp_file() {
2276 let fs = Fs::new(fs! {
2277 "000"(b"a"),
2278 dir {
2279 "000"(b"b"),
2280 "001" -> "000",
2281 "002" {
2282 },
2283 },
2284 persist {
2285 },
2286 });
2287
2288 let temp_file_1 = fs.temp_file(Path::new("/")).unwrap();
2289 assert_eq!(temp_file_1.path().to_str().unwrap(), "/001");
2290
2291 let temp_file_2 = fs.temp_file(Path::new("/dir")).unwrap();
2292 assert_eq!(temp_file_2.path().to_str().unwrap(), "/dir/003");
2293
2294 let temp_file_3 = fs.temp_file(Path::new("/dir/002")).unwrap();
2295 assert_eq!(temp_file_3.path().to_str().unwrap(), "/dir/002/000");
2296
2297 fs.assert_tree(fs! {
2298 "000"(b"a"),
2299 "001"(b""),
2300 dir {
2301 "000"(b"b"),
2302 "001" -> "000",
2303 "002" {
2304 "000"(b""),
2305 },
2306 "003"(b""),
2307 },
2308 persist {
2309 },
2310 });
2311
2312 fs.persist_temp_file(temp_file_1, Path::new("/persist/1"))
2313 .unwrap();
2314 fs.assert_tree(fs! {
2315 "000"(b"a"),
2316 dir {
2317 "000"(b"b"),
2318 "001" -> "000",
2319 "002" {
2320 "000"(b""),
2321 },
2322 "003"(b""),
2323 },
2324 persist {
2325 "1"(b""),
2326 },
2327 });
2328
2329 fs.persist_temp_file(temp_file_2, Path::new("/persist/2"))
2330 .unwrap();
2331 fs.assert_tree(fs! {
2332 "000"(b"a"),
2333 dir {
2334 "000"(b"b"),
2335 "001" -> "000",
2336 "002" {
2337 "000"(b""),
2338 },
2339 },
2340 persist {
2341 "1"(b""),
2342 "2"(b""),
2343 },
2344 });
2345
2346 fs.persist_temp_file(temp_file_3, Path::new("/persist/3"))
2347 .unwrap();
2348 fs.assert_tree(fs! {
2349 "000"(b"a"),
2350 dir {
2351 "000"(b"b"),
2352 "001" -> "000",
2353 "002" {},
2354 },
2355 persist {
2356 "1"(b""),
2357 "2"(b""),
2358 "3"(b""),
2359 },
2360 });
2361
2362 assert_eq!(
2363 fs.temp_file(Path::new("/nonexistent")).unwrap_err(),
2364 Error::NoEnt
2365 );
2366 }
2367
2368 #[test]
2369 fn temp_file_forced_failure() {
2370 let fs = Fs::new(fs! {});
2371 fs.set_failure(true);
2372 assert!(matches!(fs.temp_file(Path::new("/")), Err(Error::Test)));
2373 }
2374
2375 #[test]
2376 fn persist_temp_file_error() {
2377 let fs = Fs::new(fs! {
2378 tmp {},
2379 file(b"a"),
2380 bad_symlink -> "/foo",
2381 });
2382
2383 let temp_file_1 = fs.temp_file(Path::new("/tmp")).unwrap();
2384 let temp_file_2 = fs.temp_file(Path::new("/tmp")).unwrap();
2385 let temp_file_3 = fs.temp_file(Path::new("/tmp")).unwrap();
2386
2387 assert_eq!(
2388 fs.persist_temp_file(temp_file_1, Path::new("/foo/bar")),
2389 Err(Error::NoEnt)
2390 );
2391 assert_eq!(
2392 fs.persist_temp_file(temp_file_2, Path::new("/file/file")),
2393 Err(Error::NotDir)
2394 );
2395 assert_eq!(
2396 fs.persist_temp_file(temp_file_3, Path::new("/bad_symlink/bar")),
2397 Err(Error::NoEnt)
2398 );
2399 }
2400
2401 #[test]
2402 fn persist_temp_file_forced_failure() {
2403 let fs = Fs::new(fs! {});
2404 let temp_file = fs.temp_file(Path::new("/")).unwrap();
2405 fs.set_failure(true);
2406 assert_eq!(
2407 fs.persist_temp_file(temp_file, Path::new("/")),
2408 Err(Error::Test)
2409 );
2410 }
2411
2412 #[test]
2413 fn temp_dir() {
2414 let fs = Fs::new(fs! {
2415 "000"(b"a"),
2416 dir {
2417 "000"(b"b"),
2418 "001" -> "000",
2419 "002" {
2420 },
2421 },
2422 persist {
2423 },
2424 });
2425
2426 let temp_dir_1 = fs.temp_dir(Path::new("/")).unwrap();
2427 assert_eq!(temp_dir_1.path().to_str().unwrap(), "/001");
2428
2429 let temp_dir_2 = fs.temp_dir(Path::new("/dir")).unwrap();
2430 assert_eq!(temp_dir_2.path().to_str().unwrap(), "/dir/003");
2431
2432 let temp_dir_3 = fs.temp_dir(Path::new("/dir/002")).unwrap();
2433 assert_eq!(temp_dir_3.path().to_str().unwrap(), "/dir/002/000");
2434
2435 fs.assert_tree(fs! {
2436 "000"(b"a"),
2437 "001" {},
2438 dir {
2439 "000"(b"b"),
2440 "001" -> "000",
2441 "002" {
2442 "000" {},
2443 },
2444 "003" {},
2445 },
2446 persist {
2447 },
2448 });
2449
2450 fs.persist_temp_dir(temp_dir_1, Path::new("/persist/1"))
2451 .unwrap();
2452 fs.assert_tree(fs! {
2453 "000"(b"a"),
2454 dir {
2455 "000"(b"b"),
2456 "001" -> "000",
2457 "002" {
2458 "000" {},
2459 },
2460 "003" {},
2461 },
2462 persist {
2463 "1" {},
2464 },
2465 });
2466
2467 fs.persist_temp_dir(temp_dir_2, Path::new("/persist/2"))
2468 .unwrap();
2469 fs.assert_tree(fs! {
2470 "000"(b"a"),
2471 dir {
2472 "000"(b"b"),
2473 "001" -> "000",
2474 "002" {
2475 "000" {},
2476 },
2477 },
2478 persist {
2479 "1" {},
2480 "2" {},
2481 },
2482 });
2483
2484 fs.persist_temp_dir(temp_dir_3, Path::new("/persist/3"))
2485 .unwrap();
2486 fs.assert_tree(fs! {
2487 "000"(b"a"),
2488 dir {
2489 "000"(b"b"),
2490 "001" -> "000",
2491 "002" {},
2492 },
2493 persist {
2494 "1" {},
2495 "2" {},
2496 "3" {},
2497 },
2498 });
2499
2500 assert_eq!(
2501 fs.temp_dir(Path::new("/nonexistent")).unwrap_err(),
2502 Error::NoEnt,
2503 );
2504 }
2505
2506 #[test]
2507 fn temp_dir_forced_failure() {
2508 let fs = Fs::new(fs! {});
2509 fs.set_failure(true);
2510 assert!(matches!(fs.temp_dir(Path::new("/")), Err(Error::Test)));
2511 }
2512
2513 #[test]
2514 fn persist_temp_dir_error() {
2515 let fs = Fs::new(fs! {
2516 tmp {},
2517 file(b"a"),
2518 bad_symlink -> "/foo",
2519 });
2520
2521 let temp_dir_1 = fs.temp_dir(Path::new("/tmp")).unwrap();
2522 let temp_dir_2 = fs.temp_dir(Path::new("/tmp")).unwrap();
2523 let temp_dir_3 = fs.temp_dir(Path::new("/tmp")).unwrap();
2524
2525 assert_eq!(
2526 fs.persist_temp_dir(temp_dir_1, Path::new("/foo/bar")),
2527 Err(Error::NoEnt)
2528 );
2529 assert_eq!(
2530 fs.persist_temp_dir(temp_dir_2, Path::new("/file/file")),
2531 Err(Error::NotDir)
2532 );
2533 assert_eq!(
2534 fs.persist_temp_dir(temp_dir_3, Path::new("/bad_symlink/bar")),
2535 Err(Error::NoEnt)
2536 );
2537 }
2538
2539 #[test]
2540 fn persist_temp_dir_forced_failure() {
2541 let fs = Fs::new(fs! {});
2542 let temp_dir = fs.temp_dir(Path::new("/")).unwrap();
2543 fs.set_failure(true);
2544 assert_eq!(
2545 fs.persist_temp_dir(temp_dir, Path::new("/")),
2546 Err(Error::Test)
2547 );
2548 }
2549
2550 #[test]
2553 fn graft() {
2554 let fs = Fs::new(fs! {});
2555
2556 fs.graft(
2557 "/foo/bar/baz",
2558 fs! {
2559 file(b"file"),
2560 symlink -> "file",
2561 },
2562 );
2563 fs.assert_tree(fs! {
2564 foo {
2565 bar {
2566 baz {
2567 file(b"file"),
2568 symlink -> "file",
2569 }
2570 }
2571 }
2572 });
2573
2574 fs.graft("/foo", Entry::file(b"abc"));
2575 fs.assert_tree(fs! { foo(b"abc") });
2576
2577 fs.graft("/symlink", Entry::symlink("foo"));
2578 fs.assert_tree(fs! { foo(b"abc"), symlink -> "foo" });
2579
2580 fs.graft("/symlink", fs! {});
2581 fs.assert_tree(fs! { foo(b"abc"), symlink {} });
2582
2583 fs.graft("/foo", fs! {});
2584 fs.assert_tree(fs! { foo {}, symlink {} });
2585 }
2586}