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 pipeline(&mut self, commands: &str) -> Director {
138 self.build().pipeline(commands)
139 }
140
141 pub fn mkdir(&mut self, directory: &str) -> &mut Self {
142 self.cwd.push(directory);
143 std::fs::create_dir_all(&self.cwd).expect("can not create directory");
144 self.back_to_playground();
145 self
146 }
147
148 #[cfg(not(target_arch = "wasm32"))]
149 pub fn symlink(&mut self, from: impl AsRef<Path>, to: impl AsRef<Path>) -> &mut Self {
150 let from = self.cwd.join(from);
151 let to = self.cwd.join(to);
152
153 let create_symlink = {
154 #[cfg(unix)]
155 {
156 std::os::unix::fs::symlink
157 }
158
159 #[cfg(windows)]
160 {
161 if from.is_file() {
162 std::os::windows::fs::symlink_file
163 } else if from.is_dir() {
164 std::os::windows::fs::symlink_dir
165 } else {
166 panic!("symlink from must be a file or dir")
167 }
168 }
169 };
170
171 create_symlink(from, to).expect("can not create symlink");
172 self.back_to_playground();
173 self
174 }
175
176 pub fn with_files(&mut self, files: &[Stub]) -> &mut Self {
177 files
178 .iter()
179 .map(|f| {
180 let mut permission_set = false;
181 let mut write_able = true;
182 let (file_name, contents) = match *f {
183 Stub::EmptyFile(name) => (name, String::new()),
184 Stub::FileWithContent(name, content) => (name, content.to_string()),
185 Stub::FileWithContentToBeTrimmed(name, content) => (
186 name,
187 content
188 .lines()
189 .skip(1)
190 .map(|line| line.trim())
191 .collect::<Vec<&str>>()
192 .join(nu_utils::consts::LINE_SEPARATOR_STR),
193 ),
194 Stub::FileWithPermission(name, is_write_able) => {
195 permission_set = true;
196 write_able = is_write_able;
197 (name, "check permission".to_string())
198 }
199 };
200
201 let path = self.cwd.join(file_name);
202
203 std::fs::write(&path, contents.as_bytes()).expect("can not create file");
204 if permission_set {
205 let err_perm = "can not set permission";
206 let mut perm = std::fs::metadata(path.clone())
207 .expect(err_perm)
208 .permissions();
209 perm.set_readonly(!write_able);
210 std::fs::set_permissions(path, perm).expect(err_perm);
211 }
212 })
213 .for_each(drop);
214 self.back_to_playground();
215 self
216 }
217
218 pub fn within(&mut self, directory: &str) -> &mut Self {
219 self.cwd.push(directory);
220 if !(self.cwd.exists() && self.cwd.is_dir()) {
221 std::fs::create_dir(&self.cwd).expect("can not create directory");
222 }
223 self
224 }
225
226 pub fn glob_vec(pattern: &str) -> Vec<std::path::PathBuf> {
227 let cwd = std::env::current_dir().expect("current directory should be available");
228 if nu_experimental::DC_GLOB.get() {
229 let glob = nu_glob::dc_glob::glob_from(&cwd, pattern).expect("invalid pattern");
230 glob.map(|path| path.expect("glob entry should resolve"))
231 .collect()
232 } else {
233 let glob = nu_glob::glob(pattern, nu_glob::Uninterruptible).expect("invalid pattern");
234 glob.map(|path| path.expect("glob entry should resolve"))
235 .collect()
236 }
237 }
238}