nu_test_support/deprecated/
macros.rs1use kitest::println;
2
3#[macro_export]
55macro_rules! nu {
56 (
68 @options [ $($options:tt)* ]
69 cwd: $value:expr,
70 $($rest:tt)*
71 ) => {
72 nu!(@options [ $($options)* cwd => $crate::fs::in_directory($value) ; ] $($rest)*)
73 };
74 (
76 @options [ $($options:tt)* ]
77 $field:ident : $value:expr,
78 $($rest:tt)*
79 ) => {
80 nu!(@options [ $($options)* $field => $value.into() ; ] $($rest)*)
81 };
82
83 (
86 @options [ $($options:tt)* ]
87 $path:expr
88 $(, $part:expr)*
89 $(,)*
90 ) => {{
91 let opts = nu!(@nu_opts $($options)*);
93 let path = $path;
95 nu!(@main opts, path)
97 }};
98
99 (@nu_opts $( $field:ident => $value:expr ; )*) => {
101 $crate::macros::NuOpts{
102 $(
103 $field: Some($value),
104 )*
105 ..Default::default()
106 }
107 };
108
109 (@main $opts:expr, $path:expr) => {{
111 $crate::macros::nu_run_test($opts, $path, false)
112 }};
113
114 ($($token:tt)*) => {{
116
117 nu!(@options [ ] $($token)*)
118 }};
119}
120
121#[macro_export]
122macro_rules! nu_with_std {
123 (
135 @options [ $($options:tt)* ]
136 cwd: $value:expr,
137 $($rest:tt)*
138 ) => {
139 nu_with_std!(@options [ $($options)* cwd => $crate::fs::in_directory($value) ; ] $($rest)*)
140 };
141 (
143 @options [ $($options:tt)* ]
144 $field:ident : $value:expr,
145 $($rest:tt)*
146 ) => {
147 nu_with_std!(@options [ $($options)* $field => $value.into() ; ] $($rest)*)
148 };
149
150 (
153 @options [ $($options:tt)* ]
154 $path:expr
155 $(, $part:expr)*
156 $(,)*
157 ) => {{
158 let opts = nu_with_std!(@nu_opts $($options)*);
160 let path = nu_with_std!(@format_path $path, $($part),*);
162 nu_with_std!(@main opts, path)
164 }};
165
166 (@nu_opts $( $field:ident => $value:expr ; )*) => {
168 $crate::macros::NuOpts{
169 $(
170 $field: Some($value),
171 )*
172 ..Default::default()
173 }
174 };
175
176 (@format_path $path:expr $(,)?) => {
178 $path
180 };
181 (@format_path $path:expr, $($part:expr),* $(,)?) => {{
182 format!($path, $( $part ),*)
183 }};
184
185 (@main $opts:expr, $path:expr) => {{
187 $crate::macros::nu_run_test($opts, $path, true)
188 }};
189
190 ($($token:tt)*) => {{
192 nu_with_std!(@options [ ] $($token)*)
193 }};
194}
195
196#[macro_export]
197macro_rules! nu_with_plugins {
198 (cwd: $cwd:expr, plugins: [$(($plugin_name:expr)),*$(,)?], $command:expr) => {{
199 nu_with_plugins!(
200 cwd: $cwd,
201 envs: Vec::<(&str, &str)>::new(),
202 plugins: [$(($plugin_name)),*],
203 $command
204 )
205 }};
206 (cwd: $cwd:expr, plugin: ($plugin_name:expr), $command:expr) => {{
207 nu_with_plugins!(
208 cwd: $cwd,
209 envs: Vec::<(&str, &str)>::new(),
210 plugin: ($plugin_name),
211 $command
212 )
213 }};
214
215 (
216 cwd: $cwd:expr,
217 envs: $envs:expr,
218 plugins: [$(($plugin_name:expr)),*$(,)?],
219 $command:expr
220 ) => {{
221 $crate::macros::nu_with_plugin_run_test($cwd, $envs, &[$($plugin_name),*], $command)
222 }};
223 (cwd: $cwd:expr, envs: $envs:expr, plugin: ($plugin_name:expr), $command:expr) => {{
224 $crate::macros::nu_with_plugin_run_test($cwd, $envs, &[$plugin_name], $command)
225 }};
226
227}
228
229use crate::Outcome;
230use nu_path::{AbsolutePath, AbsolutePathBuf, Path, PathBuf};
231use nu_utils::consts::NATIVE_PATH_ENV_VAR;
232use std::{
233 ffi::OsStr,
234 process::{Command, Stdio},
235};
236use tempfile::tempdir;
237
238#[derive(Default)]
239pub struct NuOpts {
240 pub cwd: Option<AbsolutePathBuf>,
241 pub locale: Option<String>,
242 pub envs: Option<Vec<(String, String)>>,
243 pub experimental: Option<Vec<String>>,
244 pub collapse_output: Option<bool>,
245 pub env_config: Option<PathBuf>,
249}
250
251pub fn nu_run_test(opts: NuOpts, commands: impl AsRef<str>, with_std: bool) -> Outcome {
252 let test_bins = crate::fs::binaries()
253 .canonicalize()
254 .expect("Could not canonicalize dummy binaries path");
255
256 let mut paths = crate::shell_os_paths();
257 paths.insert(0, test_bins.into());
258
259 let commands = commands.as_ref();
260
261 let paths_joined = match std::env::join_paths(paths) {
262 Ok(all) => all,
263 Err(_) => panic!("Couldn't join paths for PATH var."),
264 };
265
266 let target_cwd = opts.cwd.unwrap_or_else(crate::fs::root);
267 let locale = opts.locale.unwrap_or("en_US.UTF-8".to_string());
268 let executable_path = crate::fs::executable_path();
269
270 let mut command = setup_command(&executable_path, &target_cwd);
271 command
272 .env(nu_utils::locale::LOCALE_OVERRIDE_ENV_VAR, locale)
273 .env(NATIVE_PATH_ENV_VAR, paths_joined);
274
275 if let Some(envs) = opts.envs {
276 command.envs(envs);
277 }
278
279 match opts.env_config {
280 Some(path) => command.arg("--env-config").arg(path),
281 None => command.arg("--no-config-file"),
285 };
286
287 if let Some(experimental_opts) = opts.experimental {
288 let opts = format!("[{}]", experimental_opts.join(","));
289 command.arg(format!("--experimental-options={opts}"));
290 }
291
292 if !with_std {
293 command.arg("--no-std-lib");
294 }
295 command.args(["--error-style", "plain", "--commands", commands]);
297 command.stdout(Stdio::piped()).stderr(Stdio::piped());
298
299 let process = match command.spawn() {
303 Ok(child) => child,
304 Err(why) => panic!("Can't run test {:?} {}", crate::fs::executable_path(), why),
305 };
306
307 let output = process
308 .wait_with_output()
309 .expect("couldn't read from stdout/stderr");
310
311 let out = String::from_utf8_lossy(&output.stdout);
312 let err = String::from_utf8_lossy(&output.stderr);
313
314 let out = if opts.collapse_output.unwrap_or(true) {
315 collapse_output(&out)
316 } else {
317 out.into_owned()
318 };
319
320 println!("=== stderr\n{err}");
321
322 Outcome::new(out, err.into_owned(), output.status)
323}
324
325pub fn nu_with_plugin_run_test<E, K, V>(
326 cwd: impl AsRef<Path>,
327 envs: E,
328 plugins: &[&str],
329 command: &str,
330) -> Outcome
331where
332 E: IntoIterator<Item = (K, V)>,
333 K: AsRef<OsStr>,
334 V: AsRef<OsStr>,
335{
336 let test_bins = crate::fs::binaries();
337 let test_bins = nu_path::canonicalize_with(&test_bins, ".").unwrap_or_else(|e| {
338 panic!(
339 "Couldn't canonicalize dummy binaries path {}: {:?}",
340 test_bins.display(),
341 e
342 )
343 });
344
345 let temp = tempdir().expect("couldn't create a temporary directory");
346 let [temp_config_file, temp_env_config_file] = ["config.nu", "env.nu"].map(|name| {
347 let temp_file = temp.path().join(name);
348 std::fs::File::create(&temp_file).expect("couldn't create temporary config file");
349 temp_file
350 });
351
352 let temp_plugin_file = temp.path().join("plugin.msgpackz");
354
355 crate::commands::ensure_plugins_built();
356
357 let plugin_paths: Vec<std::path::PathBuf> = plugins
358 .iter()
359 .map(|plugin_name| {
360 let plugin = with_exe(plugin_name);
361 nu_path::canonicalize_with(&plugin, &test_bins)
362 .unwrap_or_else(|_| panic!("failed to canonicalize plugin {} path", &plugin))
363 })
364 .collect();
365
366 let target_cwd = crate::fs::in_directory(&cwd);
367 let mut executable_path = crate::fs::executable_path();
370 if !executable_path.exists() {
371 executable_path = crate::fs::installed_nu_path();
372 }
373
374 let mut cmd = setup_command(&executable_path, &target_cwd);
375 cmd.envs(envs)
376 .arg("--commands")
377 .arg(command)
378 .args(["--error-style", "plain"])
380 .arg("--config")
381 .arg(temp_config_file)
382 .arg("--env-config")
383 .arg(temp_env_config_file)
384 .arg("--plugin-config")
385 .arg(temp_plugin_file);
386
387 if !plugin_paths.is_empty() {
389 cmd.arg("--plugins");
390 for path in &plugin_paths {
391 cmd.arg(path);
392 }
393 }
394
395 let process = match cmd.stdout(Stdio::piped()).stderr(Stdio::piped()).spawn() {
396 Ok(child) => child,
397 Err(why) => panic!("Can't run test {why}"),
398 };
399
400 let output = process
401 .wait_with_output()
402 .expect("couldn't read from stdout/stderr");
403
404 let out = collapse_output(&String::from_utf8_lossy(&output.stdout));
405 let err = String::from_utf8_lossy(&output.stderr);
406
407 println!("=== stderr\n{err}");
408
409 Outcome::new(out, err.into_owned(), output.status)
410}
411
412fn with_exe(name: &str) -> String {
413 #[cfg(windows)]
414 {
415 name.to_string() + ".exe"
416 }
417 #[cfg(not(windows))]
418 {
419 name.to_string()
420 }
421}
422
423fn collapse_output(out: &str) -> String {
424 let out = out.lines().collect::<Vec<_>>().join("\n");
425 let out = out.replace("\r\n", "");
426 out.replace('\n', "")
427}
428
429fn setup_command(executable_path: &AbsolutePath, target_cwd: &AbsolutePath) -> Command {
430 let mut command = Command::new(executable_path);
431
432 command
433 .env_clear()
434 .current_dir(target_cwd)
435 .env_remove("FILE_PWD")
436 .env("PWD", target_cwd); let envs: std::collections::HashMap<String, String> = std::env::vars()
440 .filter(|(n, _)| {
441 n.starts_with("System") || n == "NUSHELL_CARGO_PROFILE" || n == "PATHEXT" || n == "TMP"
445 || n == "TEMP"
446 || n == "USERPROFILE"
447 || n == "TMPDIR"
448 || n.starts_with("CARGO_")
449 || n.starts_with("RUSTUP_")
450 })
451 .collect();
452
453 #[cfg(windows)]
454 let mut envs = envs;
455
456 #[cfg(windows)]
457 if let Some(pathext) = envs.get_mut("PATHEXT")
458 && !pathext.to_uppercase().contains(".PS1")
459 {
460 pathext.push_str(";.PS1");
461 }
462
463 command.envs(envs);
464
465 command
466}