summaryrefslogtreecommitdiff
path: root/crates/fs-mistrust/src/testing.rs
blob: d1f587c5b8289a57d1591086e99b64cf0a13673f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
//! Testing support functions, to more easily make a bunch of directories and
//! links.
//!
//! This module is only built when compiling tests.

use std::{
    fs::{self, File},
    io::Write,
    path::{Path, PathBuf},
};

#[cfg(target_family = "unix")]
use std::os::unix::{self, fs::PermissionsExt};
#[cfg(target_family = "windows")]
use std::os::windows;

/// A temporary directory with convenience functions to build items inside it.
#[derive(Debug)]
pub(crate) struct Dir {
    /// The temporary directory
    toplevel: tempfile::TempDir,
    /// Canonicalized path to the temporary directory
    canonical_root: PathBuf,
}

/// When creating a link, are we creating a directory link or a file link?
///
/// (These are the same on Unix, and different on windows.)
#[derive(Copy, Clone, Debug)]
pub(crate) enum LinkType {
    Dir,
    File,
}

impl Dir {
    /// Make a new temporary directory
    pub(crate) fn new() -> Self {
        let toplevel = tempfile::TempDir::new().expect("Can't get tempfile");
        let canonical_root = toplevel.path().canonicalize().expect("Can't canonicalize");

        Dir {
            toplevel,
            canonical_root,
        }
    }

    /// Return the canonical path of the directory's root.
    pub(crate) fn canonical_root(&self) -> &Path {
        self.canonical_root.as_path()
    }

    /// Return the path to the temporary directory's root relative to our working directory.
    pub(crate) fn relative_root(&self) -> PathBuf {
        let mut cwd = std::env::current_dir().expect("no cwd");
        let mut relative = PathBuf::new();
        // TODO(nickm): I am reasonably confident that this will not work
        // correctly on windows.
        while !self.toplevel.path().starts_with(&cwd) {
            assert!(cwd.pop());
            relative.push("..");
        }
        relative.join(
            self.toplevel
                .path()
                .strip_prefix(cwd)
                .expect("error computing common ancestor"),
        )
    }

    /// Return the path of `p` within this temporary directory.
    ///
    /// Requires that `p` is a relative path.
    pub(crate) fn path(&self, p: impl AsRef<Path>) -> PathBuf {
        let p = p.as_ref();
        assert!(p.is_relative());
        self.canonical_root.join(p)
    }

    /// Make a  directory at `p` within this temporary directory, creating
    /// parent directories as needed.
    ///
    /// Requires that `p` is a relative path.
    pub(crate) fn dir(&self, p: impl AsRef<Path>) {
        fs::create_dir_all(self.path(p)).expect("Can't create directory.");
    }

    /// Make a small file at `p` within this temporary directory, creating
    /// parent directories as needed.
    ///
    /// Requires that `p` is a relative path.
    pub(crate) fn file(&self, p: impl AsRef<Path>) {
        self.dir(p.as_ref().parent().expect("Tempdir had no parent"));
        let mut f = File::create(self.path(p)).expect("Can't create file");
        f.write_all(&b"This space is intentionally left blank"[..])
            .expect("Can't write");
    }

    /// Make a relative link from "original" to "link" within this temporary
    /// directory, where `original` is relative
    /// to the directory containing `link`, and `link` is relative to the temporary directory.
    pub(crate) fn link_rel(
        &self,
        link_type: LinkType,
        original: impl AsRef<Path>,
        link: impl AsRef<Path>,
    ) {
        #[cfg(target_family = "unix")]
        {
            let _ = link_type;
            unix::fs::symlink(original.as_ref(), self.path(link)).expect("Can't symlink");
        }

        #[cfg(target_family = "windows")]
        match link_type {
            LinkType::Dir => windows::fs::symlink_dir(original.as_ref(), self.path(link)),
            LinkType::File => windows::fs::symlink_file(original.as_ref(), self.path(link)),
        }
        .expect("Can't symlink");
    }

    /// As `link_rel`, but create an absolute link.  `original` is now relative
    /// to the temporary directory.
    pub(crate) fn link_abs(
        &self,
        link_type: LinkType,
        original: impl AsRef<Path>,
        link: impl AsRef<Path>,
    ) {
        self.link_rel(link_type, self.path(original), link);
    }

    /// Change the unix permissions of a file.
    ///
    /// Requires that `p` is a relative path.
    ///
    /// Does nothing on windows.
    pub(crate) fn chmod(&self, p: impl AsRef<Path>, mode: u32) {
        #[cfg(target_family = "unix")]
        {
            let perm = fs::Permissions::from_mode(mode);
            fs::set_permissions(self.path(p), perm).expect("can't chmod");
        }
        #[cfg(not(target_family = "unix"))]
        {
            let (_, _) = (p, mode);
        }
    }
}