mirror of
https://github.com/nushell/nushell
synced 2024-12-27 05:23:11 +00:00
8200831b07
# Description Old code was comparing remaining positional arguments with total number of arguments, where it should've compared remaining positional with with remaining arguments of any kind. This means that if a function was given too few arguments, `calculate_end_span` would believe that it actually had too many arguments, since after parsing the first few arguments, the number of remaining arguments needed were fewer than the *total* number of arguments, of which we had used several. Fixes #9072 Fixes: https://github.com/nushell/nushell/issues/13930 Fixes: https://github.com/nushell/nushell/issues/12069 Fixes: https://github.com/nushell/nushell/issues/8385 Extracted from #10381 ## Bonus It also improves the error handling on missing positional arguments before keywords (no longer crashing since #9851). Instead of just giving the keyword to the parser for the missing positional, we give an explicit error about a missing positional argument. I would like better descriptions than "missing var_name" though, but I'm not sure if that's available without Old error ``` Error: nu::parser::parse_mismatch × Parse mismatch during operation. ╭─[entry #1:1:1] 1 │ let = if foo · ┬ · ╰── expected valid variable name ╰──── ``` New error ``` Error: nu::parser::missing_positional × Missing required positional argument. ╭─[entry #18:1:1] 1 │ let = foo · ┬ · ╰── missing var_name ╰──── help: Usage: let <var_name> = <initial_value> ``` # User-Facing Changes The program `alias = = =` is no longer accepted by the parser
939 lines
20 KiB
Rust
939 lines
20 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 expecteds = [
|
|
"missing var_name",
|
|
"missing var_name",
|
|
"missing const_name",
|
|
"missing var_name",
|
|
"missing var_name",
|
|
];
|
|
|
|
for (case, expected) in std::iter::zip(cases, expecteds) {
|
|
fail_test(case, expected)?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn too_few_arguments() -> TestResult {
|
|
// Test for https://github.com/nushell/nushell/issues/9072
|
|
let cases = [
|
|
"def a [b: bool, c: bool, d: float, e: float, f: float] {}; a true true 1 1",
|
|
"def a [b: bool, c: bool, d: float, e: float, f: float, g: float] {}; a true true 1 1",
|
|
"def a [b: bool, c: bool, d: float, e: float, f: float, g: float, h: float] {}; a true true 1 1",
|
|
];
|
|
|
|
let expected = "missing f";
|
|
|
|
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_description() -> TestResult {
|
|
run_test_contains(
|
|
r#"
|
|
# This is a test
|
|
#
|
|
# To see if I have cool description
|
|
def foo [] {}
|
|
help foo"#,
|
|
"cool description",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn commands_from_crlf_source_have_short_description() -> TestResult {
|
|
run_test_contains(
|
|
"# This is a test\r\n#\r\n# To see if I have cool description\r\ndef foo [] {}\r\nscope commands | where name == foo | get description.0",
|
|
"This is a test",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn commands_from_crlf_source_have_extra_description() -> TestResult {
|
|
run_test_contains(
|
|
"# This is a test\r\n#\r\n# To see if I have cool description\r\ndef foo [] {}\r\nscope commands | where name == foo | get extra_description.0",
|
|
"To see if I have cool description",
|
|
)
|
|
}
|
|
|
|
#[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 assign_bare_external_fails() {
|
|
let result = nu!("$env.FOO = nu --testbin cococo");
|
|
assert!(!result.status.success());
|
|
assert!(result.err.contains("must be explicit"));
|
|
}
|
|
|
|
#[test]
|
|
fn assign_bare_external_with_caret() {
|
|
let result = nu!("$env.FOO = ^nu --testbin cococo");
|
|
assert!(result.status.success());
|
|
}
|
|
|
|
#[test]
|
|
fn assign_backtick_quoted_external_fails() {
|
|
let result = nu!("$env.FOO = `nu` --testbin cococo");
|
|
assert!(!result.status.success());
|
|
assert!(result.err.contains("must be explicit"));
|
|
}
|
|
|
|
#[test]
|
|
fn assign_backtick_quoted_external_with_caret() {
|
|
let result = nu!("$env.FOO = ^`nu` --testbin cococo");
|
|
assert!(result.status.success());
|
|
}
|
|
|
|
#[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());
|
|
}
|