summaryrefslogtreecommitdiff
path: root/test_util/src/temp_dir.rs
blob: cab55cc14de5c9117bf6fd86ccb2852045583a84 (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
use std::fs;
use std::path::Path;
use std::path::PathBuf;
use std::sync::atomic::AtomicU32;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::time::SystemTime;

use anyhow::Context;
use once_cell::sync::OnceCell;

static TEMP_DIR_SESSION: OnceCell<TempDirSession> = OnceCell::new();

struct TempDirSession {
  default_prefix: String,
  counter: AtomicU32,
}

/// For creating temporary directories in tests.
///
/// This was done because `tempfiles::TempDir` was very slow on Windows.
///
/// Note: Do not use this in actual code as this does not protect against
/// "insecure temporary file" security vulnerabilities.
#[derive(Clone)]
pub struct TempDir(Arc<TempDirInner>);

struct TempDirInner(PathBuf);

impl Drop for TempDirInner {
  fn drop(&mut self) {
    let _ = std::fs::remove_dir_all(&self.0);
  }
}

impl Default for TempDir {
  fn default() -> Self {
    Self::new()
  }
}

impl TempDir {
  pub fn new() -> Self {
    Self::new_inner(&std::env::temp_dir(), None)
  }

  pub fn new_in(path: &Path) -> Self {
    Self::new_inner(path, None)
  }

  pub fn new_with_prefix(prefix: &str) -> Self {
    Self::new_inner(&std::env::temp_dir(), Some(prefix))
  }

  fn new_inner(parent_dir: &Path, prefix: Option<&str>) -> Self {
    let session = TEMP_DIR_SESSION.get_or_init(|| {
      let default_prefix = format!(
        "deno-cli-test-{}",
        SystemTime::now()
          .duration_since(SystemTime::UNIX_EPOCH)
          .unwrap()
          .as_millis()
      );
      TempDirSession {
        default_prefix,
        counter: Default::default(),
      }
    });
    Self({
      let count = session.counter.fetch_add(1, Ordering::SeqCst);
      let path = parent_dir.join(format!(
        "{}{}-{}",
        prefix.unwrap_or(""),
        session.default_prefix,
        count,
      ));
      std::fs::create_dir_all(&path)
        .with_context(|| format!("Error creating temp dir: {}", path.display()))
        .unwrap();
      Arc::new(TempDirInner(path))
    })
  }

  pub fn path(&self) -> &Path {
    let inner = &self.0;
    inner.0.as_path()
  }

  pub fn create_dir_all(&self, path: impl AsRef<Path>) {
    fs::create_dir_all(self.path().join(path)).unwrap();
  }

  pub fn read_to_string(&self, path: impl AsRef<Path>) -> String {
    let file_path = self.path().join(path);
    fs::read_to_string(&file_path)
      .with_context(|| format!("Could not find file: {}", file_path.display()))
      .unwrap()
  }

  pub fn rename(&self, from: impl AsRef<Path>, to: impl AsRef<Path>) {
    fs::rename(self.path().join(from), self.path().join(to)).unwrap();
  }

  pub fn write(&self, path: impl AsRef<Path>, text: impl AsRef<str>) {
    fs::write(self.path().join(path), text.as_ref()).unwrap();
  }
}