maelstrom_util/cache/fs/
test.rs

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/// This macro is used to create a file-system tree to be passed to various functions in this
19/// module. It expands to an [`Entry::Directory`].
20///
21/// Here is an example that illustrates most of the features:
22/// ```
23/// use maelstrom_util::fs;
24/// let entry = fs! {
25///     subdir {
26///         file_in_subdir(b"123"),
27///         symlink_in_subdir -> "../subdir2",
28///         nested_subdir {
29///             file_in_nested_subdir(b"contents"),
30///         },
31///     },
32///     "subdir2" {},
33///     "file"(b"456"),
34///     "symlink" -> "subdir",
35/// };
36/// ```
37///
38/// More specifically, this macro expects an "entry list". An entry list consists of zero or more
39/// "entries", separated by ","s, with an optional trailing ",".
40///
41/// Each entry must be one of:
42///   - A file entry, which consists of the file name, plus the contents of the file in parenthesis.
43///     The file name can either be a bare word, if it's a valid identifier, or a quoted string.
44///   - A symlink entry, which consists of the symlink name, the token "->", then the target of the
45///     link in quotes. The symlink name can either be a bare word, if it's a valid identifier, or
46///     a quoted string.
47///   - A directory entry, which consists of the directory name, plus an entry list enclosed in
48///     curly braces. The directory name can either be a bare word, if it's a valid identifier, or
49///     a quoted string.
50#[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/// The potential errors that can happen in a file-system operation. These are modelled off of
241/// errnos.
242#[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                    // Can't rmdir /.
554                    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                            // A directory can't be moved on top of a non-empty directory.
590                            return Err(Error::NotEmpty);
591                        } else if source_component_path == dest_component_path {
592                            // This is a weird edge case, but we allow it. We're moving an
593                            // empty directory on top of itself. It's unknown if this matches
594                            // Linux behavior, but it doesn't really matter as we don't
595                            // imagine ever seeing this in our cache code.
596                            return Ok(());
597                        } else if dest_component_path.is_descendant_of(&source_component_path) {
598                            // We can't move a directory into one of its descendants.
599                            return Err(Error::Inval);
600                        }
601                    } else {
602                        // A directory can't be moved on top of a non-directory.
603                        return Err(Error::NotDir);
604                    }
605                } else if dest_entry.is_directory() {
606                    // A non-directory can't be moved on top of a directory.
607                    return Err(Error::IsDir);
608                } else if source_component_path == dest_component_path {
609                    // Moving something onto itself is an accepted edge case.
610                    return Ok(());
611                }
612
613                // Remove the destination entry and proceed like it wasn't there to begin with.
614                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                    // We can't move a directory into one of its descendants, including itself.
621                    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/// A file-system entry. There's no notion of hard links or things like sockets, devices, etc. in
684/// this test file system.
685#[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    /// Create a new [`Entry::File`] of the given `size`.
694    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    /// Create a new [`Entry::Directory`] with the given `entries`.
705    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    /// Create a new [`Entry::Symlink`] that points to `target`.
715    pub fn symlink(target: impl ToString) -> Self {
716        Self::Symlink {
717            target: target.to_string(),
718        }
719    }
720
721    /// Return the [`FileMetadata`] for an [`Entry`].
722    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    /// Return true iff [`Entry`] is a directory.
731    fn is_directory(&self) -> bool {
732        matches!(self, Self::Directory { .. })
733    }
734
735    /// Assume [`Entry`] is a directory, and return a reference to its entries.
736    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    /// Assume [`Entry`] is a directory, and return a mutable reference to its entries.
744    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    /// Treating `self` as the root of the file system, attempt to resolve `path`.
752    fn lookup<'state>(&'state self, path: &Path) -> Lookup<'state> {
753        self.lookup_helper(path, self, Default::default())
754    }
755
756    /// Treating `self` as the root of the file system, attempt to resolve `path`. `cur` indicates
757    /// the directory the resolution should be done relative to, while `component_path` represents
758    /// the component path to `cur`.
759    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    /// Treating `self` as the root of the file system, resolve `cur` and `component` by
804    /// repeatedly expanding `cur` if it's a symlink.
805    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    /// Treating `self` as the root of the file system, resolve `path` to its parent directory.
842    /// Return an error if `path` exists, or if its parent directory doesn't exist. We don't return
843    /// the entry itself, only the component path. This is because this method is only called when
844    /// we're going to want to modify the parent, which means we're going to have to re-resolve the
845    /// component path again as mutable.
846    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    /// Treating `self` as the root of the file system, resolve `path`. If `path` resolves to a
857    /// symlink, follow symlinks until a non-symlink is found.
858    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    /// Treating `self` as the root of the file system, resolve `component_path` to its
871    /// corresponding [`Entry`].
872    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    /// Treating `self` as the root of the file system, resolve `component_path` to its
881    /// corresponding [`Entry`], an return that entry as a mutable reference.
882    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    /// Treating `self` as the root of the file system, resolve `component_path` to its
891    /// corresponding directory [`Entry`], then remove that entry from its parent and return the
892    /// entry. `component_path` must not be empty: you can't remove the root from its parent.
893    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    /// Treating `self` as the root of the file system, resolve `directory_component_path` to its
904    /// corresponding directory [`Entry`], then insert `entry` into the directory. The path
905    /// component `name` must not already be in the directory.
906    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    /// The path resolved to an actual entry. This contains the entry and the component path to it.
922    Found(&'state Entry, ComponentPath),
923
924    /// There was entry at the given path, but the parent directory exists. This contains the
925    /// component path to the parent, which is guaranteed to be a directory.
926    FoundParent(ComponentPath),
927
928    /// There was no entry at the given path, nor does its parent directory exist. However, all
929    /// ancestors that do exist are directories.
930    NotFound,
931
932    /// A symlink in the path didn't resolve.
933    DanglingSymlink,
934
935    /// An ancestor in the path was a file.
936    FileAncestor,
937}
938
939enum FollowSymlinks<'state> {
940    /// The symlink resolved to a file entry. This contains the entry and the path to it.
941    FoundFile(&'state [u8], ComponentPath),
942
943    /// The symlink resolved to a directory entry. This contains the entry and the path to it.
944    FoundDirectory(&'state BTreeMap<String, Entry>, ComponentPath),
945
946    /// A symlink in the path didn't resolve.
947    DanglingSymlink,
948
949    /// An ancestor in the path was a file.
950    FileAncestor,
951}
952
953/// A component path represents a path to an [`Entry`] that can be reached only through
954/// directories. No symlinks are allowed. Put another way, this is the canonical path to an entry
955/// in a file-system tree. Every entry in the component path, except for the last, must correspond
956/// to a directory.
957///
958/// The motivation for using this struct is to keep the Rust code safe in modifying operations. We
959/// will generally resolve a [`Path`] into a [`ComponentPath`] at the beginning of an operation,
960/// and then operate using the [`ComponentPath`] for the rest of the operation. The assumption is
961/// that one we've got it, we can always resolve a [`ComponentPath`] into an [`Entry`].
962#[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    /// Return true iff `potential_ancestor` is an actual ancestor of this component path. What
994    /// that means in practice is that `potential_ancestor` is a prefix of this component path.
995    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    /******/
2551
2552    #[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}