use nu_test_support::{
fs::Stub::{FileWithContent, FileWithContentToBeTrimmed},
prelude::*,
};
use pretty_assertions::assert_eq;
use rstest::rstest;
mod environment;
mod pipeline;
mod repl;
#[ignore]
#[test]
fn plugins_are_declared_with_wix() -> Result {
let code = r#"
open Cargo.toml
| get bin.name
| str replace "nu_plugin_(extra|core)_(.*)" "nu_plugin_$2"
| drop
| sort-by
| wrap cargo | merge {
open wix/main.wxs --raw | from xml
| get Wix.children.Product.children.0.Directory.children.0
| where Directory.attributes.Id == "$(var.PlatformProgramFilesFolder)"
| get Directory.children.Directory.children.0 | last
| get Directory.children.Component.children
| each { |it| echo $it | first }
| skip
| where File.attributes.Name =~ "nu_plugin"
| str substring [_, -4] File.attributes.Name
| get File.attributes.Name
| sort-by
| wrap wix
}
| default wix _
| each { |it| if $it.wix != $it.cargo { 1 } { 0 } }
| math sum
"#;
test().run(code).expect_value_eq(0)
}
#[test]
#[deps(NU, TESTBIN_FAIL)]
fn do_not_panic_if_broken_pipe() -> Result {
let result: CompleteResult = test().run("nu -h | ^fail | complete")?;
assert_eq!(result.exit_code, 1);
assert!(result.stderr.is_empty());
Ok(())
}
#[cfg(target_os = "linux")]
#[rstest]
#[case::stdout(std::fs::File::create("/dev/full").unwrap(), std::process::Stdio::null())]
#[case::stderr(std::process::Stdio::null(), std::fs::File::create("/dev/full").unwrap())]
#[timeout(std::time::Duration::from_secs(5))]
#[nu_test_support::test]
#[serial]
#[deps(NU)]
fn exit_failure_if_output_full(
#[case] stdout: impl Into<std::process::Stdio>,
#[case] stderr: impl Into<std::process::Stdio>,
) -> Result {
let output = std::process::Command::new(NU.path())
.arg("-n")
.stdout(stdout)
.stderr(stderr)
.output()?;
pretty_assertions::assert_matches!(output.status.code(), Some(code) if code != 0);
Ok(())
}
#[test]
fn nu_lib_dirs_repl() -> Result {
Playground::setup("nu_lib_dirs_repl", |dirs, sandbox| -> Result {
sandbox
.mkdir("scripts")
.with_files(&[FileWithContent("scripts/foo.nu", "$env.FOO = 'foo'")]);
let scripts = dirs.test().join("scripts");
let mut tester = test()
.cwd(dirs.test())
.env("NU_LIB_DIRS", [scripts.to_string_lossy().to_string()]);
let () = tester.run("source-env foo.nu")?;
tester.run("$env.FOO").expect_value_eq("foo")
})
}
#[test]
fn nu_lib_dirs_script() -> Result {
Playground::setup("nu_lib_dirs_script", |dirs, sandbox| -> Result {
sandbox
.mkdir("scripts")
.with_files(&[FileWithContentToBeTrimmed(
"scripts/foo.nu",
r#"
$env.FOO = "foo"
"#,
)])
.with_files(&[FileWithContentToBeTrimmed(
"main.nu",
"
source-env foo.nu
",
)]);
let scripts = dirs.test().join("scripts");
let mut tester = test()
.cwd(dirs.test())
.env("NU_LIB_DIRS", [scripts.to_string_lossy().to_string()]);
let () = tester.run("source-env main.nu")?;
tester.run("$env.FOO").expect_value_eq("foo")
})
}
#[test]
fn nu_lib_dirs_relative_repl() -> Result {
Playground::setup("nu_lib_dirs_relative_repl", |dirs, sandbox| -> Result {
sandbox
.mkdir("scripts")
.with_files(&[FileWithContentToBeTrimmed(
"scripts/foo.nu",
r#"
$env.FOO = "foo"
"#,
)]);
let mut tester = test().cwd(dirs.test()).env("NU_LIB_DIRS", ["scripts"]);
let () = tester.run("source-env foo.nu")?;
tester.run("$env.FOO").expect_value_eq("foo")
})
}
#[test]
fn const_nu_lib_dirs_relative() -> Result {
Playground::setup("const_nu_lib_dirs_relative", |dirs, sandbox| -> Result {
sandbox
.mkdir("scripts")
.with_files(&[FileWithContentToBeTrimmed(
"scripts/foo.nu",
r#"
$env.FOO = "foo"
"#,
)])
.with_files(&[FileWithContentToBeTrimmed(
"main.nu",
"
const NU_LIB_DIRS = [ 'scripts' ]
source-env foo.nu
$env.FOO
",
)]);
test()
.cwd(dirs.test())
.run("source main.nu")
.expect_value_eq("foo")
})
}
#[test]
fn nu_lib_dirs_relative_script() -> Result {
Playground::setup("nu_lib_dirs_relative_script", |dirs, sandbox| -> Result {
sandbox
.mkdir("scripts")
.with_files(&[FileWithContentToBeTrimmed(
"scripts/main.nu",
"
source-env ../foo.nu
",
)])
.with_files(&[FileWithContentToBeTrimmed(
"foo.nu",
r#"
$env.FOO = "foo"
"#,
)]);
test()
.cwd(dirs.test())
.run("$env.NU_LIB_DIRS = [ 'scripts' ]; source-env scripts/main.nu; $env.FOO")
.expect_value_eq("foo")
})
}
#[test]
fn run_script_that_looks_like_module() -> Result {
Playground::setup("run_script_that_looks_like_module", |dirs, _| {
let mut tester = test().cwd(dirs.test());
let () = tester.run("module spam { export def eggs [] { 'eggs' } }")?;
let () = tester.run("export use spam eggs")?;
let () = tester.run("export def foo [] { eggs }")?;
let () = tester.run("export alias bar = foo")?;
let () = tester.run("export def --env baz [] { bar }")?;
tester.run("baz").expect_value_eq("eggs")
})
}
#[test]
fn run_export_extern() -> Result {
Playground::setup("run_script_that_looks_like_module", |dirs, _| -> Result {
let code = "export extern foo []; help foo | to text";
let help_text: String = test().cwd(dirs.test()).run(code)?;
assert_contains("Usage", help_text);
Ok(())
})
}
#[test]
#[deps(NU)]
fn run_in_login_mode() {
let child_output = std::process::Command::new(NU.path())
.args(["-n", "-l", "-c", "echo $nu.is-login"])
.output()
.expect("failed to run nu");
assert_eq!("true\n", String::from_utf8_lossy(&child_output.stdout));
assert!(child_output.stderr.is_empty());
}
#[test]
#[deps(NU)]
fn run_in_not_login_mode() {
let child_output = std::process::Command::new(NU.path())
.args(["-n", "-c", "echo $nu.is-login"])
.output()
.expect("failed to run nu");
assert_eq!("false\n", String::from_utf8_lossy(&child_output.stdout));
assert!(child_output.stderr.is_empty());
}
#[test]
#[deps(NU)]
fn run_in_interactive_mode() {
let child_output = std::process::Command::new(NU.path())
.args(["-n", "-i", "-c", "echo $nu.is-interactive"])
.output()
.expect("failed to run nu");
assert_eq!("true\n", String::from_utf8_lossy(&child_output.stdout));
assert!(child_output.stderr.is_empty());
}
#[test]
#[deps(NU)]
fn run_in_noninteractive_mode() {
let child_output = std::process::Command::new(NU.path())
.args(["-n", "-c", "echo $nu.is-interactive"])
.output()
.expect("failed to run nu");
assert_eq!("false\n", String::from_utf8_lossy(&child_output.stdout));
assert!(child_output.stderr.is_empty());
}
#[test]
#[deps(NU)]
fn run_with_no_newline() {
let child_output = std::process::Command::new(NU.path())
.args(["-n", "--no-newline", "-c", "\"hello world\""])
.output()
.expect("failed to run nu");
assert_eq!("hello world", String::from_utf8_lossy(&child_output.stdout)); assert!(child_output.stderr.is_empty());
}
#[test]
#[deps(NU)]
fn main_script_can_have_subcommands1() -> Result {
Playground::setup("main_subcommands", |dirs, sandbox| -> Result {
sandbox.mkdir("main_subcommands");
sandbox.with_files(&[FileWithContent(
"script.nu",
r#"def "main foo" [x: int] {
print ($x + 100)
}
def "main" [] {
print "usage: script.nu <command name>"
}"#,
)]);
test()
.cwd(dirs.test())
.run("nu script.nu foo 123 | to text | str trim")
.expect_value_eq("223")
})
}
#[test]
#[deps(NU)]
fn main_script_can_have_subcommands2() -> Result {
Playground::setup("main_subcommands", |dirs, sandbox| -> Result {
sandbox.mkdir("main_subcommands");
sandbox.with_files(&[FileWithContent(
"script.nu",
r#"def "main foo" [x: int] {
print ($x + 100)
}
def "main" [] {
print "usage: script.nu <command name>"
}"#,
)]);
let out: String = test().cwd(dirs.test()).run("nu script.nu | to text")?;
assert_contains("usage: script.nu", out);
Ok(())
})
}
#[test]
#[deps(NU)]
fn script_with_newline_arg_does_not_split_commands() -> Result {
Playground::setup("script_newline_arg", |dirs, sandbox| -> Result {
sandbox.mkdir("script_newline_arg");
sandbox.with_files(&[FileWithContent(
"script.nu",
"def main [...args: string] { print ...($args) }",
)]);
let result: CompleteResult = test()
.cwd(dirs.test())
.run("nu script.nu a b \"c\\nd\" | complete")?;
assert_eq!(result.exit_code, 0);
assert_eq!(result.stdout, "a\nb\nc\nd\n");
assert!(result.stderr.is_empty());
Ok(())
})
}
#[test]
#[deps(NU)]
fn script_with_hash_arg_is_not_treated_as_comment() -> Result {
Playground::setup("script_hash_arg", |dirs, sandbox| -> Result {
sandbox.mkdir("script_hash_arg");
sandbox.with_files(&[FileWithContent(
"script.nu",
"def main [color: string] { print $color }",
)]);
let result: CompleteResult = test()
.cwd(dirs.test())
.run(r##"nu script.nu "#000000" | complete"##)?;
assert_eq!(result.exit_code, 0);
assert_eq!(result.stdout, "#000000\n");
assert!(result.stderr.is_empty());
let result: CompleteResult = test()
.cwd(dirs.test())
.run(r##"nu script.nu "#" | complete"##)?;
assert_eq!(result.exit_code, 0);
assert_eq!(result.stdout, "#\n");
assert!(result.stderr.is_empty());
Ok(())
})
}
#[test]
#[deps(NU)]
fn script_help_shows_single_subcommand() -> Result {
Playground::setup("main_subcommands_help", |dirs, sandbox| -> Result {
sandbox.mkdir("main_subcommands_help");
sandbox.with_files(&[FileWithContent(
"script.nu",
r#"def "main bar" [] {}
def "main" [] { help main }"#,
)]);
let out: String = test()
.cwd(dirs.test())
.run("nu script.nu --help | to text")?;
let count_script = out.matches("script.nu bar").count();
let count_main = out.matches("main bar").count();
assert_eq!(
count_script + count_main,
1,
"help output should list exactly one of 'script.nu bar' or 'main bar', got:\n{}",
out
);
Ok(())
})
}
#[test]
#[deps(NU)]
fn source_empty_file() -> Result {
Playground::setup("source_empty_file", |dirs, sandbox| -> Result {
sandbox.mkdir("source_empty_file");
sandbox.with_files(&[FileWithContent("empty.nu", "")]);
let out: String = test().cwd(dirs.test()).run("nu empty.nu | to text")?;
assert!(out.is_empty());
Ok(())
})
}
#[rstest]
#[case("source null; null | describe")]
#[case("source-env null; null | describe")]
#[case("use null; null | describe")]
#[case("overlay use null; null | describe")]
fn source_use_null(#[case] code: &str) -> Result {
test().run(code).expect_value_eq("nothing")
}
#[test]
#[deps(NU)]
fn source_script_with_let_variable() -> Result {
Playground::setup("source_script_with_let", |dirs, sandbox| -> Result {
sandbox.with_files(&[
FileWithContent("sss.nu", "print $xxx; print ($xxx | str length)"),
FileWithContent("lll.nu", "let xxx = 'let in script'\nsource sss.nu"),
]);
let out: String = test().cwd(dirs.test()).run("nu lll.nu | to text")?;
assert_eq!(out, "let in script\n13");
Ok(())
})
}
#[test]
#[deps(NU)]
fn source_script_with_mut_variable() -> Result {
Playground::setup("source_script_with_mut", |dirs, sandbox| -> Result {
sandbox.with_files(&[
FileWithContent("sss.nu", "print $xxx"),
FileWithContent("mmm.nu", "mut xxx = 'mut in script'\nsource sss.nu"),
]);
let out: String = test().cwd(dirs.test()).run("nu mmm.nu | to text")?;
assert_eq!(out, "mut in script");
Ok(())
})
}
#[test]
#[deps(NU)]
fn source_script_can_modify_outer_variable() -> Result {
Playground::setup(
"source_script_can_modify_outer",
|dirs, sandbox| -> Result {
sandbox.with_files(&[
FileWithContent("inc.nu", "$xxx = ($xxx + 1)"),
FileWithContent(
"counter.nu",
"mut xxx = 0\nsource inc.nu\nsource inc.nu\nprint $xxx",
),
]);
let out: String = test().cwd(dirs.test()).run("nu counter.nu | to text")?;
assert_eq!(out, "2");
Ok(())
},
)
}
#[test]
#[deps(NU)]
fn source_script_variable_visible_after_source() -> Result {
Playground::setup("source_var_visible_after", |dirs, sandbox| -> Result {
sandbox.with_files(&[
FileWithContent("helper.nu", "$y = 'set in source'"),
FileWithContent("main.nu", "mut y = 'original'\nsource helper.nu\nprint $y"),
]);
let out: String = test().cwd(dirs.test()).run("nu main.nu | to text")?;
assert_eq!(out, "set in source");
Ok(())
})
}
#[test]
fn source_redeclared_let_variable() -> Result {
Playground::setup("source_redeclared_let", |dirs, sandbox| -> Result {
sandbox.with_files(&[FileWithContent("sss.nu", "$xxx")]);
let mut tester = test().cwd(dirs.test());
let out1: String = tester.run("let xxx = 'first'; source sss.nu")?;
assert_eq!(out1, "first");
let out2: String = tester.run("let xxx = 'second'; source sss.nu")?;
assert_eq!(out2, "second");
Ok(())
})
}
#[test]
fn source_redeclared_mut_variable() -> Result {
Playground::setup("source_redeclared_mut", |dirs, sandbox| -> Result {
sandbox.with_files(&[FileWithContent("sss.nu", "$xxx")]);
let mut tester = test().cwd(dirs.test());
let out1: String = tester.run("mut xxx = 'first'; source sss.nu")?;
assert_eq!(out1, "first");
let out2: String = tester.run("mut xxx = 'second'; source sss.nu")?;
assert_eq!(out2, "second");
Ok(())
})
}
#[test]
#[deps(NU)]
fn source_script_with_let_and_main_command() -> Result {
Playground::setup("source_script_with_main", |dirs, sandbox| -> Result {
sandbox.with_files(&[
FileWithContent("lib.nu", "print $greeting"),
FileWithContent(
"app.nu",
"let greeting = 'hello'\nsource lib.nu\ndef main [] {}",
),
]);
let out: String = test().cwd(dirs.test()).run("nu app.nu | to text")?;
assert_eq!(out, "hello");
Ok(())
})
}
#[test]
#[deps(NU)]
fn source_nested_free_variable_visible() -> Result {
Playground::setup("source_nested_free_var", |dirs, sandbox| -> Result {
sandbox.with_files(&[
FileWithContent("c.nu", "print $xxx"),
FileWithContent("b.nu", "source c.nu"),
FileWithContent("a.nu", "let xxx = 'nested'\nsource b.nu"),
]);
let out: String = test().cwd(dirs.test()).run("nu a.nu | to text")?;
assert_eq!(out, "nested");
Ok(())
})
}
#[test]
fn source_env_redeclared_let_variable() -> Result {
Playground::setup("source_env_redeclared_let", |dirs, sandbox| -> Result {
sandbox.with_files(&[FileWithContent(
"env.nu",
"export-env { $env.FROM_SOURCE = $xxx }",
)]);
let mut tester = test().cwd(dirs.test());
let out1: String = tester.run("let xxx = 'first'; source-env env.nu; $env.FROM_SOURCE")?;
assert_eq!(out1, "first");
let out2: String = tester.run("let xxx = 'second'; source-env env.nu; $env.FROM_SOURCE")?;
assert_eq!(out2, "second");
Ok(())
})
}
#[test]
fn source_use_file_named_null() -> Result {
Playground::setup("source_file_named_null", |dirs, sandbox| -> Result {
sandbox.with_files(&[FileWithContent(
"null",
r#"export-env { $env.NULL_TEST_GREETING = "hello world" }"#,
)]);
test()
.cwd(dirs.test())
.run(r#"source "null"; $env.NULL_TEST_GREETING"#)
.expect_value_eq("hello world")?;
test()
.cwd(dirs.test())
.run(r#"source-env "null"; $env.NULL_TEST_GREETING"#)
.expect_value_eq("hello world")?;
test()
.cwd(dirs.test())
.run(r#"use "null"; $env.NULL_TEST_GREETING"#)
.expect_value_eq("hello world")?;
test()
.cwd(dirs.test())
.run(r#"overlay use "null"; $env.NULL_TEST_GREETING"#)
.expect_value_eq("hello world")?;
Ok(())
})
}
#[test]
#[deps(NU)]
fn main_script_help_uses_script_name1() -> Result {
Playground::setup("main_filename1", |dirs, sandbox| -> Result {
sandbox.mkdir("main_filename1");
sandbox.with_files(&[FileWithContent(
"script.nu",
"def main [] {}
",
)]);
let out: String = test()
.cwd(dirs.test())
.run("nu script.nu --help | to text")?;
assert!(out.contains("> script.nu"));
assert!(!out.contains("> main"));
Ok(())
})
}
#[test]
#[deps(NU)]
fn main_script_help_uses_script_name2() -> Result {
Playground::setup("main_filename2", |dirs, sandbox| -> Result {
sandbox.mkdir("main_filename2");
sandbox.with_files(&[FileWithContent(
"script.nu",
"def main [foo: string] {}
",
)]);
let stderr: String = test()
.cwd(dirs.test())
.run("nu script.nu | complete | get stderr")?;
assert!(stderr.contains("Usage: script.nu"));
assert!(!stderr.contains("Usage: main"));
Ok(())
})
}
#[test]
#[deps(NU)]
fn main_script_subcommand_help_uses_script_name1() -> Result {
Playground::setup("main_filename3", |dirs, sandbox| -> Result {
sandbox.mkdir("main_filename3");
sandbox.with_files(&[FileWithContent(
"script.nu",
"def main [] {}
def 'main foo' [] {}
",
)]);
let out: String = test()
.cwd(dirs.test())
.run("nu script.nu foo --help | to text")?;
assert!(out.contains("> script.nu foo"));
assert!(!out.contains("> main foo"));
Ok(())
})
}
#[test]
#[deps(NU)]
fn main_script_subcommand_help_uses_script_name2() -> Result {
Playground::setup("main_filename4", |dirs, sandbox| -> Result {
sandbox.mkdir("main_filename4");
sandbox.with_files(&[FileWithContent(
"script.nu",
"def main [] {}
def 'main foo' [bar: string] {}
",
)]);
let stderr: String = test()
.cwd(dirs.test())
.run("nu script.nu foo | complete | get stderr")?;
assert!(stderr.contains("Usage: script.nu foo"));
assert!(!stderr.contains("Usage: main foo"));
Ok(())
})
}
#[test]
#[deps(NU)]
fn script_file_not_found() -> Result {
let stderr: String = test().run("nu non-existent-script.nu foo bar | complete | get stderr")?;
assert!(
!stderr.contains(".rs"),
"internal rust source was mentioned"
);
assert!(
stderr.contains("non-existent-script.nu"),
"error did not include script name"
);
assert!(
stderr.contains("commandline"),
"source file for the error was not commandline"
);
Ok(())
}
#[test]
#[deps(NU)]
fn main_script_alias_persists() -> Result {
Playground::setup("alias_main", |dirs, sandbox| -> Result {
sandbox.with_files(&[FileWithContent("script.nu", "def main [] { 'ok' }")]);
test()
.cwd(dirs.test())
.run("nu script.nu | to text | str trim")
.expect_value_eq("ok")
})
}
#[test]
#[exp(nu_experimental::NATIVE_CLIP)]
fn builtin_commands_can_be_shadowed_and_extended() -> Result {
let outcome: String = test().run("use std/clip; clip")?;
assert_contains("clip copy52", &outcome);
assert_contains("clip prefix", &outcome);
Ok(())
}
#[cfg(not(target_arch = "wasm32"))]
#[test]
#[deps(NU)]
fn nu_env_pwd_symlink() {
Playground::setup("nu_env_pwd_symlink", |_, sandbox| {
let pwd = "linked_current_dir";
sandbox.symlink("./", pwd);
let pwd = sandbox.cwd().join(pwd);
let current_dir = std::fs::canonicalize(&pwd).unwrap();
let child_output = std::process::Command::new(NU.path())
.args(["-c", "echo $env.PWD"])
.current_dir(current_dir)
.env("PWD", &pwd)
.output()
.expect("failed to run nu");
let output = String::from_utf8(child_output.stdout).unwrap();
assert_eq!(output.trim_end(), pwd.to_str().unwrap());
let pwd = "linked_current_dir2";
sandbox.mkdir("new_current_dir");
sandbox.symlink("new_current_dir", pwd);
let pwd = sandbox.cwd().join(pwd);
let current_dir = sandbox.cwd().canonicalize().unwrap();
let child_output = std::process::Command::new(NU.path())
.args(["-c", "echo $env.PWD"])
.current_dir(¤t_dir)
.env("PWD", &pwd)
.output()
.expect("failed to run nu");
let output = String::from_utf8(child_output.stdout).unwrap();
assert_eq!(output.trim_end(), current_dir.to_str().unwrap());
})
}