mirror of
https://github.com/nushell/nushell.git
synced 2024-12-26 00:50:03 +01:00
8e2917b9ae
# Description This makes assignment operations and `const` behave the same way `let` and `mut` do, absorbing the rest of the pipeline. Changes the lexer to be able to recognize assignment operators as a separate token, and then makes the lite parser continue to push spans into the same command regardless of any redirections or pipes if an assignment operator is encountered. Because the pipeline is no longer split up by the lite parser at this point, it's trivial to just parse the right hand side as if it were a subexpression not contained within parentheses. # User-Facing Changes Big breaking change. These are all now possible: ```nushell const path = 'a' | path join 'b' mut x = 2 $x = random int $x = [1 2 3] | math sum $env.FOO = random chars ``` In the past, these would have led to (an attempt at) bare word string parsing. So while `$env.FOO = bar` would have previously set the environment variable `FOO` to the string `"bar"`, it now tries to run the command named `bar`, hence the major breaking change. However, this is desirable because it is very consistent - if you see the `=`, you can just assume it absorbs everything else to the right of it. # Tests + Formatting Added tests for the new behaviour. Adjusted some existing tests that depended on the right hand side of assignments being parsed as barewords. # After Submitting - [ ] release notes (breaking change!)
890 lines
19 KiB
Rust
890 lines
19 KiB
Rust
use crate::repl::tests::{fail_test, run_test, run_test_contains, run_test_with_env, TestResult};
|
|
use nu_test_support::{nu, nu_repl_code};
|
|
use std::collections::HashMap;
|
|
|
|
#[test]
|
|
fn env_shorthand() -> TestResult {
|
|
run_test("FOO=BAR if false { 3 } else { 4 }", "4")
|
|
}
|
|
|
|
#[test]
|
|
fn subcommand() -> TestResult {
|
|
run_test("def foo [] {}; def \"foo bar\" [] {3}; foo bar", "3")
|
|
}
|
|
|
|
#[test]
|
|
fn alias_1() -> TestResult {
|
|
run_test("def foo [$x] { $x + 10 }; alias f = foo; f 100", "110")
|
|
}
|
|
|
|
#[test]
|
|
fn ints_with_underscores() -> TestResult {
|
|
run_test("1_0000_0000_0000 + 10", "1000000000010")
|
|
}
|
|
|
|
#[test]
|
|
fn floats_with_underscores() -> TestResult {
|
|
run_test("3.1415_9265_3589_793 * 2", "6.283185307179586")
|
|
}
|
|
|
|
#[test]
|
|
fn bin_ints_with_underscores() -> TestResult {
|
|
run_test("0b_10100_11101_10010", "21426")
|
|
}
|
|
|
|
#[test]
|
|
fn oct_ints_with_underscores() -> TestResult {
|
|
run_test("0o2443_6442_7652_0044", "90422533333028")
|
|
}
|
|
|
|
#[test]
|
|
fn hex_ints_with_underscores() -> TestResult {
|
|
run_test("0x68__9d__6a", "6856042")
|
|
}
|
|
|
|
#[test]
|
|
fn alias_2() -> TestResult {
|
|
run_test(
|
|
"def foo [$x $y] { $x + $y + 10 }; alias f = foo 33; f 100",
|
|
"143",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn alias_2_multi_word() -> TestResult {
|
|
run_test(
|
|
r#"def "foo bar" [$x $y] { $x + $y + 10 }; alias f = foo bar 33; f 100"#,
|
|
"143",
|
|
)
|
|
}
|
|
|
|
#[ignore = "TODO: Allow alias to alias existing command with the same name"]
|
|
#[test]
|
|
fn alias_recursion() -> TestResult {
|
|
run_test_contains(r#"alias ls = ls -a; ls"#, " ")
|
|
}
|
|
|
|
#[test]
|
|
fn block_param1() -> TestResult {
|
|
run_test("[3] | each { |it| $it + 10 } | get 0", "13")
|
|
}
|
|
|
|
#[test]
|
|
fn block_param2() -> TestResult {
|
|
run_test("[3] | each { |y| $y + 10 } | get 0", "13")
|
|
}
|
|
|
|
#[test]
|
|
fn block_param3_list_iteration() -> TestResult {
|
|
run_test("[1,2,3] | each { |it| $it + 10 } | get 1", "12")
|
|
}
|
|
|
|
#[test]
|
|
fn block_param4_list_iteration() -> TestResult {
|
|
run_test("[1,2,3] | each { |y| $y + 10 } | get 2", "13")
|
|
}
|
|
|
|
#[test]
|
|
fn range_iteration1() -> TestResult {
|
|
run_test("1..4 | each { |y| $y + 10 } | get 0", "11")
|
|
}
|
|
|
|
#[test]
|
|
fn range_iteration2() -> TestResult {
|
|
run_test("4..1 | each { |y| $y + 100 } | get 3", "101")
|
|
}
|
|
|
|
#[test]
|
|
fn simple_value_iteration() -> TestResult {
|
|
run_test("4 | each { |it| $it + 10 }", "14")
|
|
}
|
|
|
|
#[test]
|
|
fn comment_multiline() -> TestResult {
|
|
run_test(
|
|
r#"def foo [] {
|
|
let x = 1 + 2 # comment
|
|
let y = 3 + 4 # another comment
|
|
$x + $y
|
|
}; foo"#,
|
|
"10",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn comment_skipping_1() -> TestResult {
|
|
run_test(
|
|
r#"let x = {
|
|
y: 20
|
|
# foo
|
|
}; $x.y"#,
|
|
"20",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn comment_skipping_2() -> TestResult {
|
|
run_test(
|
|
r#"let x = {
|
|
y: 20
|
|
# foo
|
|
z: 40
|
|
}; $x.z"#,
|
|
"40",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn comment_skipping_in_pipeline_1() -> TestResult {
|
|
run_test(
|
|
r#"[1,2,3] | #comment
|
|
each { |$it| $it + 2 } | # foo
|
|
math sum #bar"#,
|
|
"12",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn comment_skipping_in_pipeline_2() -> TestResult {
|
|
run_test(
|
|
r#"[1,2,3] #comment
|
|
| #comment2
|
|
each { |$it| $it + 2 } #foo
|
|
| # bar
|
|
math sum #baz"#,
|
|
"12",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn comment_skipping_in_pipeline_3() -> TestResult {
|
|
run_test(
|
|
r#"[1,2,3] | #comment
|
|
#comment2
|
|
each { |$it| $it + 2 } #foo
|
|
| # bar
|
|
#baz
|
|
math sum #foobar"#,
|
|
"12",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn bad_var_name() -> TestResult {
|
|
fail_test(r#"let $"foo bar" = 4"#, "can't contain")
|
|
}
|
|
|
|
#[test]
|
|
fn bad_var_name2() -> TestResult {
|
|
fail_test(r#"let $foo-bar = 4"#, "valid variable")
|
|
}
|
|
|
|
#[test]
|
|
fn assignment_with_no_var() -> TestResult {
|
|
let cases = [
|
|
"let = if $",
|
|
"mut = if $",
|
|
"const = if $",
|
|
"let = 'foo' | $in; $x | describe",
|
|
"mut = 'foo' | $in; $x | describe",
|
|
];
|
|
|
|
let expected = "valid variable";
|
|
|
|
for case in cases {
|
|
fail_test(case, expected)?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn long_flag() -> TestResult {
|
|
run_test(
|
|
r#"([a, b, c] | enumerate | each --keep-empty { |e| if $e.index != 1 { 100 }}).1 | to nuon"#,
|
|
"null",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn for_in_missing_var_name() -> TestResult {
|
|
fail_test("for in", "missing")
|
|
}
|
|
|
|
#[test]
|
|
fn multiline_pipe_in_block() -> TestResult {
|
|
run_test(
|
|
r#"do {
|
|
echo hello |
|
|
str length
|
|
}"#,
|
|
"5",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn bad_short_flag() -> TestResult {
|
|
fail_test(r#"def foo3 [-l?:int] { $l }"#, "short flag")
|
|
}
|
|
|
|
#[test]
|
|
fn quotes_with_equals() -> TestResult {
|
|
run_test(
|
|
r#"let query_prefix = "https://api.github.com/search/issues?q=repo:nushell/"; $query_prefix"#,
|
|
"https://api.github.com/search/issues?q=repo:nushell/",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn string_interp_with_equals() -> TestResult {
|
|
run_test(
|
|
r#"let query_prefix = $"https://api.github.com/search/issues?q=repo:nushell/"; $query_prefix"#,
|
|
"https://api.github.com/search/issues?q=repo:nushell/",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn recursive_parse() -> TestResult {
|
|
run_test(r#"def c [] { c }; echo done"#, "done")
|
|
}
|
|
|
|
#[test]
|
|
fn commands_have_usage() -> TestResult {
|
|
run_test_contains(
|
|
r#"
|
|
# This is a test
|
|
#
|
|
# To see if I have cool usage
|
|
def foo [] {}
|
|
help foo"#,
|
|
"cool usage",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn commands_from_crlf_source_have_short_usage() -> TestResult {
|
|
run_test_contains(
|
|
"# This is a test\r\n#\r\n# To see if I have cool usage\r\ndef foo [] {}\r\nscope commands | where name == foo | get usage.0",
|
|
"This is a test",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn commands_from_crlf_source_have_extra_usage() -> TestResult {
|
|
run_test_contains(
|
|
"# This is a test\r\n#\r\n# To see if I have cool usage\r\ndef foo [] {}\r\nscope commands | where name == foo | get extra_usage.0",
|
|
"To see if I have cool usage",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn equals_separates_long_flag() -> TestResult {
|
|
run_test(
|
|
r#"'nushell' | fill --alignment right --width=10 --character='-'"#,
|
|
"---nushell",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn assign_expressions() -> TestResult {
|
|
let env = HashMap::from([("VENV_OLD_PATH", "Foobar"), ("Path", "Quux")]);
|
|
run_test_with_env(
|
|
r#"$env.Path = (if ($env | columns | "VENV_OLD_PATH" in $in) { $env.VENV_OLD_PATH } else { $env.Path }); echo $env.Path"#,
|
|
"Foobar",
|
|
&env,
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn assign_takes_pipeline() -> TestResult {
|
|
run_test(
|
|
r#"mut foo = 'bar'; $foo = $foo | str upcase | str reverse; $foo"#,
|
|
"RAB",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn append_assign_takes_pipeline() -> TestResult {
|
|
run_test(
|
|
r#"mut foo = 'bar'; $foo ++= $foo | str upcase; $foo"#,
|
|
"barBAR",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn string_interpolation_paren_test() -> TestResult {
|
|
run_test(r#"$"('(')(')')""#, "()")
|
|
}
|
|
|
|
#[test]
|
|
fn string_interpolation_paren_test2() -> TestResult {
|
|
run_test(r#"$"('(')test(')')""#, "(test)")
|
|
}
|
|
|
|
#[test]
|
|
fn string_interpolation_paren_test3() -> TestResult {
|
|
run_test(r#"$"('(')("test")test(')')""#, "(testtest)")
|
|
}
|
|
|
|
#[test]
|
|
fn string_interpolation_escaping() -> TestResult {
|
|
run_test(r#"$"hello\nworld" | lines | length"#, "2")
|
|
}
|
|
|
|
#[test]
|
|
fn capture_multiple_commands() -> TestResult {
|
|
run_test(
|
|
r#"
|
|
let CONST_A = 'Hello'
|
|
|
|
def 'say-hi' [] {
|
|
echo (call-me)
|
|
}
|
|
|
|
def 'call-me' [] {
|
|
echo $CONST_A
|
|
}
|
|
|
|
[(say-hi) (call-me)] | str join
|
|
|
|
"#,
|
|
"HelloHello",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn capture_multiple_commands2() -> TestResult {
|
|
run_test(
|
|
r#"
|
|
let CONST_A = 'Hello'
|
|
|
|
def 'call-me' [] {
|
|
echo $CONST_A
|
|
}
|
|
|
|
def 'say-hi' [] {
|
|
echo (call-me)
|
|
}
|
|
|
|
[(say-hi) (call-me)] | str join
|
|
|
|
"#,
|
|
"HelloHello",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn capture_multiple_commands3() -> TestResult {
|
|
run_test(
|
|
r#"
|
|
let CONST_A = 'Hello'
|
|
|
|
def 'say-hi' [] {
|
|
echo (call-me)
|
|
}
|
|
|
|
def 'call-me' [] {
|
|
echo $CONST_A
|
|
}
|
|
|
|
[(call-me) (say-hi)] | str join
|
|
|
|
"#,
|
|
"HelloHello",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn capture_multiple_commands4() -> TestResult {
|
|
run_test(
|
|
r#"
|
|
let CONST_A = 'Hello'
|
|
|
|
def 'call-me' [] {
|
|
echo $CONST_A
|
|
}
|
|
|
|
def 'say-hi' [] {
|
|
echo (call-me)
|
|
}
|
|
|
|
[(call-me) (say-hi)] | str join
|
|
|
|
"#,
|
|
"HelloHello",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn capture_row_condition() -> TestResult {
|
|
run_test(
|
|
r#"let name = "foo"; [foo] | where $'($name)' =~ $it | str join"#,
|
|
"foo",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn starts_with_operator_succeeds() -> TestResult {
|
|
run_test(
|
|
r#"[Moe Larry Curly] | where $it starts-with L | str join"#,
|
|
"Larry",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn ends_with_operator_succeeds() -> TestResult {
|
|
run_test(
|
|
r#"[Moe Larry Curly] | where $it ends-with ly | str join"#,
|
|
"Curly",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn proper_missing_param() -> TestResult {
|
|
fail_test(r#"def foo [x y z w] { }; foo a b c"#, "missing w")
|
|
}
|
|
|
|
#[test]
|
|
fn block_arity_check1() -> TestResult {
|
|
fail_test(r#"ls | each { |x, y| 1}"#, "expected 1 closure parameter")
|
|
}
|
|
|
|
// deprecating former support for escapes like `/uNNNN`, dropping test.
|
|
#[test]
|
|
fn string_escape_unicode_extended() -> TestResult {
|
|
run_test(r#""\u{015B}\u{1f10b}""#, "ś🄋")
|
|
}
|
|
|
|
#[test]
|
|
fn string_escape_interpolation() -> TestResult {
|
|
run_test(r#"$"\u{015B}(char hamburger)abc""#, "ś≡abc")
|
|
}
|
|
|
|
#[test]
|
|
fn string_escape_interpolation2() -> TestResult {
|
|
run_test(r#"$"2 + 2 is \(2 + 2)""#, "2 + 2 is (2 + 2)")
|
|
}
|
|
|
|
#[test]
|
|
fn proper_rest_types() -> TestResult {
|
|
run_test(
|
|
r#"def foo [--verbose(-v), # my test flag
|
|
...rest: int # my rest comment
|
|
] { if $verbose { print "verbose!" } else { print "not verbose!" } }; foo"#,
|
|
"not verbose!",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn single_value_row_condition() -> TestResult {
|
|
run_test(
|
|
r#"[[a, b]; [true, false], [true, true]] | where a | length"#,
|
|
"2",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn performance_nested_lists() -> TestResult {
|
|
// Parser used to be exponential on deeply nested lists
|
|
// TODO: Add a timeout
|
|
fail_test(r#"[[[[[[[[[[[[[[[[[[[[[[[[[[[["#, "Unexpected end of code")
|
|
}
|
|
|
|
#[test]
|
|
fn unary_not_1() -> TestResult {
|
|
run_test(r#"not false"#, "true")
|
|
}
|
|
|
|
#[test]
|
|
fn unary_not_2() -> TestResult {
|
|
run_test(r#"not (false)"#, "true")
|
|
}
|
|
|
|
#[test]
|
|
fn unary_not_3() -> TestResult {
|
|
run_test(r#"(not false)"#, "true")
|
|
}
|
|
|
|
#[test]
|
|
fn unary_not_4() -> TestResult {
|
|
run_test(r#"if not false { "hello" } else { "world" }"#, "hello")
|
|
}
|
|
|
|
#[test]
|
|
fn unary_not_5() -> TestResult {
|
|
run_test(
|
|
r#"if not not not not false { "hello" } else { "world" }"#,
|
|
"world",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn unary_not_6() -> TestResult {
|
|
run_test(
|
|
r#"[[name, present]; [abc, true], [def, false]] | where not present | get name.0"#,
|
|
"def",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn comment_in_multiple_pipelines() -> TestResult {
|
|
run_test(
|
|
r#"[[name, present]; [abc, true], [def, false]]
|
|
# | where not present
|
|
| get name.0"#,
|
|
"abc",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn date_literal() -> TestResult {
|
|
run_test(r#"2022-09-10 | date to-record | get day"#, "10")
|
|
}
|
|
|
|
#[test]
|
|
fn and_and_or() -> TestResult {
|
|
run_test(r#"true and false or true"#, "true")
|
|
}
|
|
|
|
#[test]
|
|
fn and_and_xor() -> TestResult {
|
|
// Assumes the precedence NOT > AND > XOR > OR
|
|
run_test(r#"true and true xor true and false"#, "true")
|
|
}
|
|
|
|
#[test]
|
|
fn or_and_xor() -> TestResult {
|
|
// Assumes the precedence NOT > AND > XOR > OR
|
|
run_test(r#"true or false xor true or false"#, "true")
|
|
}
|
|
|
|
#[test]
|
|
fn unbalanced_delimiter() -> TestResult {
|
|
fail_test(r#"{a:{b:5}}}"#, "unbalanced { and }")
|
|
}
|
|
|
|
#[test]
|
|
fn unbalanced_delimiter2() -> TestResult {
|
|
fail_test(r#"{}#.}"#, "unbalanced { and }")
|
|
}
|
|
|
|
#[test]
|
|
fn unbalanced_delimiter3() -> TestResult {
|
|
fail_test(r#"{"#, "Unexpected end of code")
|
|
}
|
|
|
|
#[test]
|
|
fn unbalanced_delimiter4() -> TestResult {
|
|
fail_test(r#"}"#, "unbalanced { and }")
|
|
}
|
|
|
|
#[test]
|
|
fn unbalanced_parens1() -> TestResult {
|
|
fail_test(r#")"#, "unbalanced ( and )")
|
|
}
|
|
|
|
#[test]
|
|
fn unbalanced_parens2() -> TestResult {
|
|
fail_test(r#"("("))"#, "unbalanced ( and )")
|
|
}
|
|
|
|
#[test]
|
|
fn plugin_use_with_string_literal() -> TestResult {
|
|
fail_test(
|
|
r#"plugin use 'nu-plugin-math'"#,
|
|
"Plugin registry file not set",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn plugin_use_with_string_constant() -> TestResult {
|
|
let input = "\
|
|
const file = 'nu-plugin-math'
|
|
plugin use $file
|
|
";
|
|
// should not fail with `not a constant`
|
|
fail_test(input, "Plugin registry file not set")
|
|
}
|
|
|
|
#[test]
|
|
fn plugin_use_with_string_variable() -> TestResult {
|
|
let input = "\
|
|
let file = 'nu-plugin-math'
|
|
plugin use $file
|
|
";
|
|
fail_test(input, "Value is not a parse-time constant")
|
|
}
|
|
|
|
#[test]
|
|
fn plugin_use_with_non_string_constant() -> TestResult {
|
|
let input = "\
|
|
const file = 6
|
|
plugin use $file
|
|
";
|
|
fail_test(input, "expected string, found int")
|
|
}
|
|
|
|
#[test]
|
|
fn extern_errors_with_no_space_between_params_and_name_1() -> TestResult {
|
|
fail_test("extern cmd[]", "expected space")
|
|
}
|
|
|
|
#[test]
|
|
fn extern_errors_with_no_space_between_params_and_name_2() -> TestResult {
|
|
fail_test("extern cmd(--flag)", "expected space")
|
|
}
|
|
|
|
#[test]
|
|
fn duration_with_underscores_1() -> TestResult {
|
|
run_test("420_min", "7hr")
|
|
}
|
|
|
|
#[test]
|
|
fn duration_with_underscores_2() -> TestResult {
|
|
run_test("1_000_000sec", "1wk 4day 13hr 46min 40sec")
|
|
}
|
|
|
|
#[test]
|
|
fn duration_with_underscores_3() -> TestResult {
|
|
fail_test("1_000_d_ay", "Command `1_000_d_ay` not found")
|
|
}
|
|
|
|
#[test]
|
|
fn duration_with_faulty_number() -> TestResult {
|
|
fail_test("sleep 4-ms", "duration value must be a number")
|
|
}
|
|
|
|
#[test]
|
|
fn filesize_with_underscores_1() -> TestResult {
|
|
run_test("420_mb", "400.5 MiB")
|
|
}
|
|
|
|
#[test]
|
|
fn filesize_with_underscores_2() -> TestResult {
|
|
run_test("1_000_000B", "976.6 KiB")
|
|
}
|
|
|
|
#[test]
|
|
fn filesize_with_underscores_3() -> TestResult {
|
|
fail_test("42m_b", "Command `42m_b` not found")
|
|
}
|
|
|
|
#[test]
|
|
fn filesize_is_not_hex() -> TestResult {
|
|
run_test("0x42b", "1067")
|
|
}
|
|
|
|
#[test]
|
|
fn let_variable_type_mismatch() -> TestResult {
|
|
fail_test(r#"let x: int = "foo""#, "expected int, found string")
|
|
}
|
|
|
|
#[test]
|
|
fn let_variable_disallows_completer() -> TestResult {
|
|
fail_test(
|
|
r#"let x: int@completer = 42"#,
|
|
"Unexpected custom completer",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn def_with_input_output() -> TestResult {
|
|
run_test(r#"def foo []: nothing -> int { 3 }; foo"#, "3")
|
|
}
|
|
|
|
#[test]
|
|
fn def_with_input_output_with_line_breaks() -> TestResult {
|
|
run_test(
|
|
r#"def foo []: [
|
|
nothing -> int
|
|
] { 3 }; foo"#,
|
|
"3",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn def_with_multi_input_output_with_line_breaks() -> TestResult {
|
|
run_test(
|
|
r#"def foo []: [
|
|
nothing -> int
|
|
string -> int
|
|
] { 3 }; foo"#,
|
|
"3",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn def_with_multi_input_output_without_commas() -> TestResult {
|
|
run_test(
|
|
r#"def foo []: [nothing -> int string -> int] { 3 }; foo"#,
|
|
"3",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn def_with_multi_input_output_called_with_first_sig() -> TestResult {
|
|
run_test(
|
|
r#"def foo []: [int -> int, string -> int] { 3 }; 10 | foo"#,
|
|
"3",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn def_with_multi_input_output_called_with_second_sig() -> TestResult {
|
|
run_test(
|
|
r#"def foo []: [int -> int, string -> int] { 3 }; "bob" | foo"#,
|
|
"3",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn def_with_input_output_mismatch_1() -> TestResult {
|
|
fail_test(
|
|
r#"def foo []: [int -> int, string -> int] { 3 }; foo"#,
|
|
"command doesn't support",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn def_with_input_output_mismatch_2() -> TestResult {
|
|
fail_test(
|
|
r#"def foo []: [int -> int, string -> int] { 3 }; {x: 2} | foo"#,
|
|
"command doesn't support",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn def_with_input_output_broken_1() -> TestResult {
|
|
fail_test(r#"def foo []: int { 3 }"#, "expected arrow")
|
|
}
|
|
|
|
#[test]
|
|
fn def_with_input_output_broken_2() -> TestResult {
|
|
fail_test(r#"def foo []: int -> { 3 }"#, "expected type")
|
|
}
|
|
|
|
#[test]
|
|
fn def_with_input_output_broken_3() -> TestResult {
|
|
fail_test(
|
|
r#"def foo []: int -> int@completer {}"#,
|
|
"Unexpected custom completer",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn def_with_input_output_broken_4() -> TestResult {
|
|
fail_test(
|
|
r#"def foo []: int -> list<int@completer> {}"#,
|
|
"Unexpected custom completer",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn def_with_in_var_let_1() -> TestResult {
|
|
run_test(
|
|
r#"def foo []: [int -> int, string -> int] { let x = $in; if ($x | describe) == "int" { 3 } else { 4 } }; "100" | foo"#,
|
|
"4",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn def_with_in_var_let_2() -> TestResult {
|
|
run_test(
|
|
r#"def foo []: [int -> int, string -> int] { let x = $in; if ($x | describe) == "int" { 3 } else { 4 } }; 100 | foo"#,
|
|
"3",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn def_with_in_var_mut_1() -> TestResult {
|
|
run_test(
|
|
r#"def foo []: [int -> int, string -> int] { mut x = $in; if ($x | describe) == "int" { 3 } else { 4 } }; "100" | foo"#,
|
|
"4",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn def_with_in_var_mut_2() -> TestResult {
|
|
run_test(
|
|
r#"def foo []: [int -> int, string -> int] { mut x = $in; if ($x | describe) == "int" { 3 } else { 4 } }; 100 | foo"#,
|
|
"3",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn properly_nest_captures() -> TestResult {
|
|
run_test(r#"do { let b = 3; def c [] { $b }; c }"#, "3")
|
|
}
|
|
|
|
#[test]
|
|
fn properly_nest_captures_call_first() -> TestResult {
|
|
run_test(r#"do { let b = 3; c; def c [] { $b }; c }"#, "3")
|
|
}
|
|
|
|
#[test]
|
|
fn properly_typecheck_rest_param() -> TestResult {
|
|
run_test(
|
|
r#"def foo [...rest: string] { $rest | length }; foo "a" "b" "c""#,
|
|
"3",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn implied_collect_has_compatible_type() -> TestResult {
|
|
run_test(r#"let idx = 3 | $in; $idx < 1"#, "false")
|
|
}
|
|
|
|
#[test]
|
|
fn record_expected_colon() -> TestResult {
|
|
fail_test(r#"{ a: 2 b }"#, "expected ':'")?;
|
|
fail_test(r#"{ a: 2 b 3 }"#, "expected ':'")
|
|
}
|
|
|
|
#[test]
|
|
fn record_missing_value() -> TestResult {
|
|
fail_test(r#"{ a: 2 b: }"#, "expected value for record field")
|
|
}
|
|
|
|
#[test]
|
|
fn def_requires_body_closure() -> TestResult {
|
|
fail_test("def a [] (echo 4)", "expected definition body closure")
|
|
}
|
|
|
|
#[test]
|
|
fn not_panic_with_recursive_call() {
|
|
let result = nu!(nu_repl_code(&[
|
|
"def px [] { if true { 3 } else { px } }",
|
|
"let x = 1",
|
|
"$x | px",
|
|
]));
|
|
assert_eq!(result.out, "3");
|
|
|
|
let result = nu!(nu_repl_code(&[
|
|
"def px [n=0] { let l = $in; if $n == 0 { return false } else { $l | px ($n - 1) } }",
|
|
"let x = 1",
|
|
"$x | px"
|
|
]));
|
|
assert_eq!(result.out, "false");
|
|
|
|
let result = nu!(nu_repl_code(&[
|
|
"def px [n=0] { let l = $in; if $n == 0 { return false } else { $l | px ($n - 1) } }",
|
|
"let x = 1",
|
|
"def foo [] { $x }",
|
|
"foo | px"
|
|
]));
|
|
assert_eq!(result.out, "false");
|
|
|
|
let result = nu!(nu_repl_code(&[
|
|
"def px [n=0] { let l = $in; if $n == 0 { return false } else { $l | px ($n - 1) } }",
|
|
"let x = 1",
|
|
"do {|| $x } | px"
|
|
]));
|
|
assert_eq!(result.out, "false");
|
|
|
|
let result = nu!(
|
|
cwd: "tests/parsing/samples",
|
|
"nu recursive_func_with_alias.nu"
|
|
);
|
|
assert!(result.status.success());
|
|
}
|