nu_test_support/playground/
play.rs1use super::Director;
2use crate::fs::{self, Stub};
3use nu_glob::{Uninterruptible, glob};
4#[cfg(not(target_arch = "wasm32"))]
5use nu_path::Path;
6use nu_path::{AbsolutePath, AbsolutePathBuf};
7use std::str;
8use tempfile::{TempDir, tempdir};
9
10#[derive(Default, Clone, Debug)]
11pub struct EnvironmentVariable {
12 pub name: String,
13 pub value: String,
14}
15
16impl EnvironmentVariable {
17 fn new(name: &str, value: &str) -> Self {
18 Self {
19 name: name.to_string(),
20 value: value.to_string(),
21 }
22 }
23}
24
25pub struct Playground<'a> {
26 _root: TempDir,
27 tests: String,
28 cwd: AbsolutePathBuf,
29 config: Option<AbsolutePathBuf>,
30 environment_vars: Vec<EnvironmentVariable>,
31 dirs: &'a Dirs,
32}
33
34#[derive(Clone)]
35pub struct Dirs {
36 pub root: AbsolutePathBuf,
37 pub test: AbsolutePathBuf,
38 pub fixtures: AbsolutePathBuf,
39}
40
41impl Dirs {
42 pub fn formats(&self) -> AbsolutePathBuf {
43 self.fixtures.join("formats")
44 }
45
46 pub fn root(&self) -> &AbsolutePath {
47 &self.root
48 }
49
50 pub fn test(&self) -> &AbsolutePath {
51 &self.test
52 }
53}
54
55impl Playground<'_> {
56 pub fn root(&self) -> &AbsolutePath {
57 &self.dirs.root
58 }
59
60 pub fn cwd(&self) -> &AbsolutePath {
61 &self.cwd
62 }
63
64 pub fn back_to_playground(&mut self) -> &mut Self {
65 self.cwd = self.root().join(&self.tests);
66 self
67 }
68
69 pub fn play(&mut self) -> &mut Self {
70 self
71 }
72
73 pub fn setup(topic: &str, block: impl FnOnce(Dirs, &mut Playground)) {
74 let temp = tempdir().expect("Could not create a tempdir");
75
76 let root = AbsolutePathBuf::try_from(temp.path())
77 .expect("Tempdir is not an absolute path")
78 .canonicalize()
79 .expect("Could not canonicalize tempdir");
80
81 let test = root.join(topic);
82 if test.exists() {
83 std::fs::remove_dir_all(&test).expect("Could not remove directory");
84 }
85 std::fs::create_dir(&test).expect("Could not create directory");
86 let test = test
87 .canonicalize()
88 .expect("Could not canonicalize test path");
89
90 let fixtures = fs::fixtures()
91 .canonicalize()
92 .expect("Could not canonicalize fixtures path");
93
94 let dirs = Dirs {
95 root: root.into(),
96 test: test.as_path().into(),
97 fixtures: fixtures.into(),
98 };
99
100 let mut playground = Playground {
101 _root: temp,
102 tests: topic.to_string(),
103 cwd: test.into(),
104 config: None,
105 environment_vars: Vec::default(),
106 dirs: &dirs,
107 };
108
109 block(dirs.clone(), &mut playground);
110 }
111
112 pub fn with_config(&mut self, source_file: AbsolutePathBuf) -> &mut Self {
113 self.config = Some(source_file);
114 self
115 }
116
117 pub fn with_env(&mut self, name: &str, value: &str) -> &mut Self {
118 self.environment_vars
119 .push(EnvironmentVariable::new(name, value));
120 self
121 }
122
123 pub fn get_config(&self) -> Option<&str> {
124 self.config
125 .as_ref()
126 .map(|cfg| cfg.to_str().expect("could not convert path."))
127 }
128
129 pub fn build(&mut self) -> Director {
130 Director {
131 cwd: Some(self.dirs.test().into()),
132 config: self.config.clone().map(|cfg| cfg.into()),
133 environment_vars: self.environment_vars.clone(),
134 ..Default::default()
135 }
136 }
137
138 pub fn cococo(&mut self, arg: &str) -> Director {
139 self.build().cococo(arg)
140 }
141
142 pub fn pipeline(&mut self, commands: &str) -> Director {
143 self.build().pipeline(commands)
144 }
145
146 pub fn mkdir(&mut self, directory: &str) -> &mut Self {
147 self.cwd.push(directory);
148 std::fs::create_dir_all(&self.cwd).expect("can not create directory");
149 self.back_to_playground();
150 self
151 }
152
153 #[cfg(not(target_arch = "wasm32"))]
154 pub fn symlink(&mut self, from: impl AsRef<Path>, to: impl AsRef<Path>) -> &mut Self {
155 let from = self.cwd.join(from);
156 let to = self.cwd.join(to);
157
158 let create_symlink = {
159 #[cfg(unix)]
160 {
161 std::os::unix::fs::symlink
162 }
163
164 #[cfg(windows)]
165 {
166 if from.is_file() {
167 std::os::windows::fs::symlink_file
168 } else if from.is_dir() {
169 std::os::windows::fs::symlink_dir
170 } else {
171 panic!("symlink from must be a file or dir")
172 }
173 }
174 };
175
176 create_symlink(from, to).expect("can not create symlink");
177 self.back_to_playground();
178 self
179 }
180
181 pub fn with_files(&mut self, files: &[Stub]) -> &mut Self {
182 let endl = fs::line_ending();
183
184 files
185 .iter()
186 .map(|f| {
187 let mut permission_set = false;
188 let mut write_able = true;
189 let (file_name, contents) = match *f {
190 Stub::EmptyFile(name) => (name, String::new()),
191 Stub::FileWithContent(name, content) => (name, content.to_string()),
192 Stub::FileWithContentToBeTrimmed(name, content) => (
193 name,
194 content
195 .lines()
196 .skip(1)
197 .map(|line| line.trim())
198 .collect::<Vec<&str>>()
199 .join(&endl),
200 ),
201 Stub::FileWithPermission(name, is_write_able) => {
202 permission_set = true;
203 write_able = is_write_able;
204 (name, "check permission".to_string())
205 }
206 };
207
208 let path = self.cwd.join(file_name);
209
210 std::fs::write(&path, contents.as_bytes()).expect("can not create file");
211 if permission_set {
212 let err_perm = "can not set permission";
213 let mut perm = std::fs::metadata(path.clone())
214 .expect(err_perm)
215 .permissions();
216 perm.set_readonly(!write_able);
217 std::fs::set_permissions(path, perm).expect(err_perm);
218 }
219 })
220 .for_each(drop);
221 self.back_to_playground();
222 self
223 }
224
225 pub fn within(&mut self, directory: &str) -> &mut Self {
226 self.cwd.push(directory);
227 if !(self.cwd.exists() && self.cwd.is_dir()) {
228 std::fs::create_dir(&self.cwd).expect("can not create directory");
229 }
230 self
231 }
232
233 pub fn glob_vec(pattern: &str) -> Vec<std::path::PathBuf> {
234 let glob = glob(pattern, Uninterruptible);
235
236 glob.expect("invalid pattern")
237 .map(|path| {
238 if let Ok(path) = path {
239 path
240 } else {
241 unreachable!()
242 }
243 })
244 .collect()
245 }
246}