use nu_test_support::fs::Stub::FileWithContentToBeTrimmed; use nu_test_support::playground::Playground; use nu_test_support::{nu, pipeline}; use pretty_assertions::assert_eq; #[test] fn takes_rows_of_nu_value_strings_and_pipes_it_to_stdin_of_external() { let sample = r#" [[name, rusty_luck, origin]; [Jason, 1, Canada], [JT, 1, "New Zealand"], [Andrés, 1, Ecuador], [AndKitKatz, 1, "Estados Unidos"]] "#; let actual = nu!(pipeline(&format!( " {sample} | get origin | each {{ |it| nu --testbin cococo $it | nu --testbin chop }} | get 2 " ))); // chop will remove the last escaped double quote from \"Estados Unidos\" assert_eq!(actual.out, "Ecuado"); } #[test] fn treats_dot_dot_as_path_not_range() { Playground::setup("dot_dot_dir", |dirs, sandbox| { sandbox.with_files(&[FileWithContentToBeTrimmed( "nu_times.csv", " name,rusty_luck,origin Jason,1,Canada ", )]); let actual = nu!( cwd: dirs.test(), pipeline( " mkdir temp; cd temp; print (open ../nu_times.csv).name.0 | table; cd ..; rmdir temp " )); // chop will remove the last escaped double quote from \"Estados Unidos\" assert_eq!(actual.out, "Jason"); }) } #[test] fn subexpression_properly_redirects() { let actual = nu!(r#" echo (nu --testbin cococo "hello") | str join "#); assert_eq!(actual.out, "hello"); } #[test] fn argument_subexpression() { let actual = nu!(r#" echo "foo" | each { |it| echo (echo $it) } "#); assert_eq!(actual.out, "foo"); } #[test] fn for_loop() { let actual = nu!(" for i in 1..3 { print $i } "); assert_eq!(actual.out, "123"); } #[test] fn subexpression_handles_dot() { Playground::setup("subexpression_handles_dot", |dirs, sandbox| { sandbox.with_files(&[FileWithContentToBeTrimmed( "nu_times.csv", " name,rusty_luck,origin Jason,1,Canada JT,1,New Zealand Andrés,1,Ecuador AndKitKatz,1,Estados Unidos ", )]); let actual = nu!( cwd: dirs.test(), pipeline( " echo (open nu_times.csv) | get name | each { |it| nu --testbin chop $it } | get 3 " )); assert_eq!(actual.out, "AndKitKat"); }); } #[test] fn string_interpolation_with_it() { let actual = nu!(r#" echo "foo" | each { |it| echo $"($it)" } "#); assert_eq!(actual.out, "foo"); } #[test] fn string_interpolation_with_it_column_path() { let actual = nu!(r#" echo [[name]; [sammie]] | each { |it| echo $"($it.name)" } | get 0 "#); assert_eq!(actual.out, "sammie"); } #[test] fn string_interpolation_shorthand_overlap() { let actual = nu!(r#" $"3 + 4 = (3 + 4)" "#); assert_eq!(actual.out, "3 + 4 = 7"); } // FIXME: jt - we don't currently have a way to escape the single ticks easily #[ignore] #[test] fn string_interpolation_and_paren() { let actual = nu!(r#" $"a paren is ('(')" "#); assert_eq!(actual.out, "a paren is ("); } #[test] fn string_interpolation_with_unicode() { //カ = U+30AB : KATAKANA LETTER KA let actual = nu!(r#" $"カ" "#); assert_eq!(actual.out, "カ"); } #[test] fn run_custom_command() { let actual = nu!(" def add-me [x y] { $x + $y}; add-me 10 5 "); assert_eq!(actual.out, "15"); } #[test] fn run_custom_command_with_flag() { let actual = nu!(r#" def foo [--bar:number] { if ($bar | is-empty) { echo "empty" } else { echo $bar } }; foo --bar 10 "#); assert_eq!(actual.out, "10"); } #[test] fn run_custom_command_with_flag_missing() { let actual = nu!(r#" def foo [--bar:number] { if ($bar | is-empty) { echo "empty" } else { echo $bar } }; foo "#); assert_eq!(actual.out, "empty"); } #[test] fn run_custom_subcommand() { let actual = nu!(r#" def "str double" [x] { echo $x $x | str join }; str double bob "#); assert_eq!(actual.out, "bobbob"); } #[test] fn run_inner_custom_command() { let actual = nu!(" def outer [x] { def inner [y] { echo $y }; inner $x }; outer 10 "); assert_eq!(actual.out, "10"); } #[test] fn run_broken_inner_custom_command() { let actual = nu!(" def outer [x] { def inner [y] { echo $y }; inner $x }; inner 10 "); assert!(!actual.err.is_empty()); } #[test] fn run_custom_command_with_rest() { let actual = nu!(r#" def rest-me [...rest: string] { echo $rest.1 $rest.0}; rest-me "hello" "world" | to json --raw "#); assert_eq!(actual.out, r#"["world","hello"]"#); } #[test] fn run_custom_command_with_rest_and_arg() { let actual = nu!(r#" def rest-me-with-arg [name: string, ...rest: string] { echo $rest.1 $rest.0 $name}; rest-me-with-arg "hello" "world" "yay" | to json --raw "#); assert_eq!(actual.out, r#"["yay","world","hello"]"#); } #[test] fn run_custom_command_with_rest_and_flag() { let actual = nu!(r#" def rest-me-with-flag [--name: string, ...rest: string] { echo $rest.1 $rest.0 $name}; rest-me-with-flag "hello" "world" --name "yay" | to json --raw "#); assert_eq!(actual.out, r#"["world","hello","yay"]"#); } #[test] fn run_custom_command_with_empty_rest() { let actual = nu!(" def rest-me-with-empty-rest [...rest: string] { $rest }; rest-me-with-empty-rest | is-empty "); assert_eq!(actual.out, "true"); assert_eq!(actual.err, ""); } //FIXME: jt: blocked on https://github.com/nushell/engine-q/issues/912 #[ignore] #[test] fn run_custom_command_with_rest_other_name() { let actual = nu!(r#" def say-hello [ greeting:string, ...names:string # All of the names ] { echo $"($greeting), ($names | sort-by | str join)" } say-hello Salutations E D C A B "#); assert_eq!(actual.out, "Salutations, ABCDE"); assert_eq!(actual.err, ""); } #[test] fn alias_a_load_env() { let actual = nu!(" def activate-helper [] { {BOB: SAM} }; alias activate = load-env (activate-helper); activate; $env.BOB "); assert_eq!(actual.out, "SAM"); } #[test] fn let_variable() { let actual = nu!(" let x = 5 let y = 12 $x + $y "); assert_eq!(actual.out, "17"); } #[test] fn let_doesnt_leak() { let actual = nu!(" do { let x = 5 }; echo $x "); assert!(actual.err.contains("variable not found")); } #[test] fn mutate_env_variable() { let actual = nu!(r#" $env.TESTENVVAR = "hello world" echo $env.TESTENVVAR "#); assert_eq!(actual.out, "hello world"); } #[test] fn mutate_env_hides_variable() { let actual = nu!(r#" $env.TESTENVVAR = "hello world" print $env.TESTENVVAR hide-env TESTENVVAR print $env.TESTENVVAR "#); assert_eq!(actual.out, "hello world"); assert!(actual.err.contains("not_found")); } #[test] fn mutate_env_hides_variable_in_parent_scope() { let actual = nu!(r#" $env.TESTENVVAR = "hello world" print $env.TESTENVVAR do { hide-env TESTENVVAR print $env.TESTENVVAR } print $env.TESTENVVAR "#); assert_eq!(actual.out, "hello world"); assert!(actual.err.contains("not_found")); } #[test] fn unlet_env_variable() { let actual = nu!(r#" $env.TEST_VAR = "hello world" hide-env TEST_VAR echo $env.TEST_VAR "#); assert!(actual.err.contains("not_found")); } #[test] #[ignore] fn unlet_nonexistent_variable() { let actual = nu!(" hide-env NONEXISTENT_VARIABLE "); assert!(actual.err.contains("did not find")); } #[test] fn unlet_variable_in_parent_scope() { let actual = nu!(r#" $env.DEBUG = "1" print $env.DEBUG do { $env.DEBUG = "2" print $env.DEBUG hide-env DEBUG print $env.DEBUG } print $env.DEBUG "#); assert_eq!(actual.out, "1211"); } #[test] fn mutate_env_doesnt_leak() { let actual = nu!(r#" do { $env.xyz = "my message" }; echo $env.xyz "#); assert!(actual.err.contains("not_found")); } #[test] fn proper_shadow_mutate_env_aliases() { let actual = nu!(r#" $env.DEBUG = "true"; print $env.DEBUG | table; do { $env.DEBUG = "false"; print $env.DEBUG } | table; print $env.DEBUG "#); assert_eq!(actual.out, "truefalsetrue"); } #[test] fn load_env_variable() { let actual = nu!(r#" echo {TESTENVVAR: "hello world"} | load-env echo $env.TESTENVVAR "#); assert_eq!(actual.out, "hello world"); } #[test] fn load_env_variable_arg() { let actual = nu!(r#" load-env {TESTENVVAR: "hello world"} echo $env.TESTENVVAR "#); assert_eq!(actual.out, "hello world"); } #[test] fn load_env_doesnt_leak() { let actual = nu!(r#" do { echo { name: xyz, value: "my message" } | load-env }; echo $env.xyz "#); assert!(actual.err.contains("not_found")); } #[test] fn proper_shadow_load_env_aliases() { let actual = nu!(r#" $env.DEBUG = "true"; print $env.DEBUG | table; do { echo {DEBUG: "false"} | load-env; print $env.DEBUG } | table; print $env.DEBUG "#); assert_eq!(actual.out, "truefalsetrue"); } //FIXME: jt: load-env can not currently hide variables because null no longer hides #[ignore] #[test] fn load_env_can_hide_var_envs() { let actual = nu!(r#" $env.DEBUG = "1" echo $env.DEBUG load-env [[name, value]; [DEBUG null]] echo $env.DEBUG "#); assert_eq!(actual.out, "1"); assert!(actual.err.contains("error")); assert!(actual.err.contains("Unknown column")); } //FIXME: jt: load-env can not currently hide variables because null no longer hides #[ignore] #[test] fn load_env_can_hide_var_envs_in_parent_scope() { let actual = nu!(r#" $env.DEBUG = "1" echo $env.DEBUG do { load-env [[name, value]; [DEBUG null]] echo $env.DEBUG } echo $env.DEBUG "#); assert_eq!(actual.out, "11"); assert!(actual.err.contains("error")); assert!(actual.err.contains("Unknown column")); } #[test] fn proper_shadow_let_aliases() { let actual = nu!(" let DEBUG = false; print $DEBUG | table; do { let DEBUG = true; print $DEBUG } | table; print $DEBUG "); assert_eq!(actual.out, "falsetruefalse"); } #[test] fn block_params_override() { let actual = nu!(" [1, 2, 3] | each { |a| echo $it } "); assert!(actual.err.contains("variable not found")); } #[test] fn alias_reuse() { let actual = nu!("alias foo = echo bob; foo; foo"); assert!(actual.out.contains("bob")); assert!(actual.err.is_empty()); } #[test] fn block_params_override_correct() { let actual = nu!(" [1, 2, 3] | each { |a| echo $a } | to json --raw "); assert_eq!(actual.out, "[1,2,3]"); } #[test] fn hex_number() { let actual = nu!(" 0x10 "); assert_eq!(actual.out, "16"); } #[test] fn binary_number() { let actual = nu!(" 0b10 "); assert_eq!(actual.out, "2"); } #[test] fn octal_number() { let actual = nu!(" 0o10 "); assert_eq!(actual.out, "8"); } #[test] fn run_dynamic_closures() { let actual = nu!(r#" let closure = {|| echo "holaaaa" }; do $closure "#); assert_eq!(actual.out, "holaaaa"); } #[test] fn dynamic_closure_type_check() { let actual = nu!(r#"let closure = {|x: int| echo $x}; do $closure "aa""#); assert!(actual.err.contains("can't convert string to int")) } #[test] fn dynamic_closure_optional_arg() { let actual = nu!(r#"let closure = {|x: int = 3| echo $x}; do $closure"#); assert_eq!(actual.out, "3"); let actual = nu!(r#"let closure = {|x: int = 3| echo $x}; do $closure 10"#); assert_eq!(actual.out, "10"); } #[test] fn dynamic_closure_rest_args() { let actual = nu!(r#"let closure = {|...args| $args | str join ""}; do $closure 1 2 3"#); assert_eq!(actual.out, "123"); let actual = nu!( r#"let closure = {|required, ...args| $"($required), ($args | str join "")"}; do $closure 1 2 3"# ); assert_eq!(actual.out, "1, 23"); let actual = nu!( r#"let closure = {|required, optional?, ...args| $"($required), ($optional), ($args | str join "")"}; do $closure 1 2 3"# ); assert_eq!(actual.out, "1, 2, 3"); } #[test] fn argument_subexpression_reports_errors() { let actual = nu!("echo (ferris_is_not_here.exe)"); assert!(!actual.err.is_empty()); } #[test] fn can_process_one_row_from_internal_and_pipes_it_to_stdin_of_external() { let actual = nu!(r#""nushelll" | nu --testbin chop"#); assert_eq!(actual.out, "nushell"); } #[test] fn bad_operator() { let actual = nu!(" 2 $ 2 "); assert!(actual.err.contains("operator")); } #[test] fn index_out_of_bounds() { let actual = nu!(" let foo = [1, 2, 3]; echo $foo.5 "); assert!(actual.err.contains("too large")); } #[test] fn negative_float_start() { let actual = nu!(" -1.3 + 4 "); assert_eq!(actual.out, "2.7"); } #[test] fn string_inside_of() { let actual = nu!(r#" "bob" in "bobby" "#); assert_eq!(actual.out, "true"); } #[test] fn string_not_inside_of() { let actual = nu!(r#" "bob" not-in "bobby" "#); assert_eq!(actual.out, "false"); } #[test] fn index_row() { let actual = nu!(" let foo = [[name]; [joe] [bob]]; echo $foo.1 | to json --raw "); assert_eq!(actual.out, r#"{"name":"bob"}"#); } #[test] fn index_cell() { let actual = nu!(" let foo = [[name]; [joe] [bob]]; echo $foo.name.1 "); assert_eq!(actual.out, "bob"); } #[test] fn index_cell_alt() { let actual = nu!(" let foo = [[name]; [joe] [bob]]; echo $foo.1.name "); assert_eq!(actual.out, "bob"); } #[test] fn not_echoing_ranges_without_numbers() { let actual = nu!(" echo .. "); assert_eq!(actual.out, ".."); } #[test] fn not_echoing_exclusive_ranges_without_numbers() { let actual = nu!(" echo ..< "); assert_eq!(actual.out, "..<"); } #[test] fn echoing_ranges() { let actual = nu!(" echo 1..3 | math sum "); assert_eq!(actual.out, "6"); } #[test] fn echoing_exclusive_ranges() { let actual = nu!(" echo 1..<4 | math sum "); assert_eq!(actual.out, "6"); } #[test] fn table_literals1() { let actual = nu!(" echo [[name age]; [foo 13]] | get age.0 "); assert_eq!(actual.out, "13"); } #[test] fn table_literals2() { let actual = nu!(" echo [[name age] ; [bob 13] [sally 20]] | get age | math sum "); assert_eq!(actual.out, "33"); } #[test] fn list_with_commas() { let actual = nu!(" echo [1, 2, 3] | math sum "); assert_eq!(actual.out, "6"); } #[test] fn range_with_left_var() { let actual = nu!(" ({ size: 3}.size)..10 | math sum "); assert_eq!(actual.out, "52"); } #[test] fn range_with_right_var() { let actual = nu!(" 4..({ size: 30}.size) | math sum "); assert_eq!(actual.out, "459"); } #[test] fn range_with_open_left() { let actual = nu!(" echo ..30 | math sum "); assert_eq!(actual.out, "465"); } #[test] fn exclusive_range_with_open_left() { let actual = nu!(" echo ..<31 | math sum "); assert_eq!(actual.out, "465"); } #[test] fn range_with_open_right() { let actual = nu!(" echo 5.. | first 10 | math sum "); assert_eq!(actual.out, "95"); } #[test] fn exclusive_range_with_open_right() { let actual = nu!(" echo 5..< | first 10 | math sum "); assert_eq!(actual.out, "95"); } #[test] fn range_with_mixed_types() { let actual = nu!(" echo 1..10.5 | math sum "); assert_eq!(actual.out, "55"); } #[test] fn filesize_math() { let actual = nu!(" 100 * 10kib "); assert_eq!(actual.out, "1000.0 KiB"); // why 1000.0 KB instead of 1.0 MB? // looks like `byte.get_appropriate_unit(false)` behaves this way } #[test] fn filesize_math2() { let actual = nu!(" 100 / 10kb "); assert!(actual.err.contains("doesn't support")); } #[test] fn filesize_math3() { let actual = nu!(" 100kib / 10 "); assert_eq!(actual.out, "10.0 KiB"); } #[test] fn filesize_math4() { let actual = nu!(" 100kib * 5 "); assert_eq!(actual.out, "500.0 KiB"); } #[test] fn filesize_math5() { let actual = nu!(" 1000 * 1kib "); assert_eq!(actual.out, "1000.0 KiB"); } #[test] fn filesize_math6() { let actual = nu!(" 1000 * 1mib "); assert_eq!(actual.out, "1000.0 MiB"); } #[test] fn filesize_math7() { let actual = nu!(" 1000 * 1gib "); assert_eq!(actual.out, "1000.0 GiB"); } #[test] fn exclusive_range_with_mixed_types() { let actual = nu!(" echo 1..<10.5 | math sum "); assert_eq!(actual.out, "55"); } #[test] fn table_with_commas() { let actual = nu!(" echo [[name, age, height]; [JT, 42, 185] [Unknown, 99, 99]] | get age | math sum "); assert_eq!(actual.out, "141"); } #[test] fn duration_overflow() { let actual = nu!(pipeline( " ls | get modified | each { |it| $it + 10000000000000000day } " )); assert!(actual.err.contains("duration too large")); } #[test] fn date_and_duration_overflow() { let actual = nu!(pipeline( " ls | get modified | each { |it| $it + 1000000000day } " )); // assert_eq!(actual.err, "overflow"); assert!(actual.err.contains("duration too large")); } #[test] fn pipeline_params_simple() { let actual = nu!(pipeline( " echo 1 2 3 | $in.1 * $in.2 " )); assert_eq!(actual.out, "6"); } #[test] fn pipeline_params_inner() { let actual = nu!(pipeline( " echo 1 2 3 | (echo $in.2 6 7 | $in.0 * $in.1 * $in.2) " )); assert_eq!(actual.out, "126"); } #[test] fn better_table_lex() { let actual = nu!(pipeline( " let table = [ [name, size]; [small, 7] [medium, 10] [large, 12] ]; $table.1.size " )); assert_eq!(actual.out, "10"); } #[test] fn better_subexpr_lex() { let actual = nu!(pipeline( " (echo boo sam | str length | math sum) " )); assert_eq!(actual.out, "6"); } #[test] fn subsubcommand() { let actual = nu!(pipeline( r#" def "aws s3 rb" [url] { $url + " loaded" }; aws s3 rb localhost "# )); assert_eq!(actual.out, "localhost loaded"); } #[test] fn manysubcommand() { let actual = nu!(pipeline( r#" def "aws s3 rb ax vf qqqq rrrr" [url] { $url + " loaded" }; aws s3 rb ax vf qqqq rrrr localhost "# )); assert_eq!(actual.out, "localhost loaded"); } #[test] fn nothing_string_1() { let actual = nu!(pipeline( r#" null == "foo" "# )); assert_eq!(actual.out, "false"); } #[test] fn hide_alias_shadowing() { let actual = nu!(pipeline( " def test-shadowing [] { alias greet = echo hello; let xyz = {|| greet }; hide greet; do $xyz }; test-shadowing " )); assert_eq!(actual.out, "hello"); } // FIXME: Seems like subexpression are no longer scoped. Should we remove this test? #[ignore] #[test] fn hide_alias_does_not_escape_scope() { let actual = nu!(pipeline( " def test-alias [] { alias greet = echo hello; (hide greet); greet }; test-alias " )); assert_eq!(actual.out, "hello"); } #[test] fn hide_alias_hides_alias() { let actual = nu!(pipeline( " def test-alias [] { alias ll = ls -l; hide ll; ll }; test-alias " )); assert!( actual.err.contains("Command `ll` not found") && actual.err.contains("Did you mean `all`?") ); } mod parse { use nu_test_support::nu; /* The debug command's signature is: Usage: > debug {flags} flags: -h, --help: Display the help message for this command -r, --raw: Prints the raw value representation. */ #[test] fn errors_if_flag_passed_is_not_exact() { let actual = nu!("debug -ra"); assert!(actual.err.contains("unknown flag"),); let actual = nu!("debug --rawx"); assert!(actual.err.contains("unknown flag"),); } #[test] fn errors_if_flag_is_not_supported() { let actual = nu!("debug --ferris"); assert!(actual.err.contains("unknown flag"),); } #[test] fn errors_if_passed_an_unexpected_argument() { let actual = nu!("debug ferris"); assert!(actual.err.contains("extra positional argument"),); } #[test] fn ensure_backticks_are_bareword_command() { let actual = nu!("`8abc123`"); assert!(actual.err.contains("Command `8abc123` not found"),); } } mod tilde_expansion { use nu_test_support::nu; #[test] #[should_panic] fn as_home_directory_when_passed_as_argument_and_begins_with_tilde() { let actual = nu!(" echo ~ "); assert!(!actual.out.contains('~'),); } #[test] fn does_not_expand_when_passed_as_argument_and_does_not_start_with_tilde() { let actual = nu!(r#" echo "1~1" "#); assert_eq!(actual.out, "1~1"); } } mod variable_scoping { use nu_test_support::nu; fn test_variable_scope(code: &str, expected: &str) { let actual = nu!(code); assert_eq!(actual.out, expected); } fn test_variable_scope_list(code: &str, expected: &[&str]) { let actual = nu!(code); let result: Vec<&str> = actual.out.matches("ZZZ").collect(); assert_eq!(result, expected); } #[test] fn access_variables_in_scopes() { test_variable_scope( " def test [input] { echo [0 1 2] | do { do { echo $input } } } test ZZZ ", "ZZZ", ); test_variable_scope( r#" def test [input] { echo [0 1 2] | do { do { if $input == "ZZZ" { echo $input } else { echo $input } } } } test ZZZ "#, "ZZZ", ); test_variable_scope( r#" def test [input] { echo [0 1 2] | do { do { if $input == "ZZZ" { echo $input } else { echo $input } } } } test ZZZ "#, "ZZZ", ); test_variable_scope( " def test [input] { echo [0 1 2] | do { echo $input } } test ZZZ ", "ZZZ", ); test_variable_scope( " def test [input] { echo [0 1 2] | do { if $input == $input { echo $input } else { echo $input } } } test ZZZ ", "ZZZ" ); test_variable_scope_list( " def test [input] { echo [0 1 2] | each { |_| echo $input } } test ZZZ ", &["ZZZ", "ZZZ", "ZZZ"], ); test_variable_scope_list( " def test [input] { echo [0 1 2] | each { |it| if $it > 0 {echo $input} else {echo $input}} } test ZZZ ", &["ZZZ", "ZZZ", "ZZZ"], ); test_variable_scope_list( " def test [input] { echo [0 1 2] | each { |_| if $input == $input {echo $input} else {echo $input}} } test ZZZ ", &["ZZZ", "ZZZ", "ZZZ"], ); } } #[test] fn pipe_input_to_print() { let actual = nu!(r#""foo" | print"#); assert_eq!(actual.out, "foo"); assert!(actual.err.is_empty()); } #[test] fn err_pipe_input_to_print() { let actual = nu!(r#""foo" e>| print"#); assert!(actual.err.contains("only works on external commands")); } #[test] fn outerr_pipe_input_to_print() { let actual = nu!(r#""foo" o+e>| print"#); assert!(actual.err.contains("only works on external commands")); } #[test] fn command_not_found_error_shows_not_found_2() { let actual = nu!(r#" export def --wrapped my-foo [...rest] { foo }; my-foo "#); assert!( actual.err.contains("Command `foo` not found") && actual.err.contains("Did you mean `for`?") ); } #[test] fn error_on_out_greater_pipe() { let actual = nu!(r#""foo" o>| print"#); assert!(actual .err .contains("Redirecting stdout to a pipe is the same as normal piping")) }