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