mirror of
https://github.com/casey/just.git
synced 2025-07-07 17:45:00 +00:00
2810 lines
42 KiB
Rust
2810 lines
42 KiB
Rust
use super::*;
|
|
|
|
#[test]
|
|
fn alias_listing() {
|
|
Test::new()
|
|
.arg("--list")
|
|
.justfile(
|
|
"
|
|
foo:
|
|
echo foo
|
|
|
|
alias f := foo
|
|
",
|
|
)
|
|
.stdout(
|
|
"
|
|
Available recipes:
|
|
foo # [alias: f]
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn alias_listing_with_doc() {
|
|
Test::new()
|
|
.justfile(
|
|
"
|
|
# foo command
|
|
foo:
|
|
echo foo
|
|
|
|
alias f := foo
|
|
",
|
|
)
|
|
.arg("--list")
|
|
.stdout(
|
|
"
|
|
Available recipes:
|
|
foo # foo command [alias: f]
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn alias_listing_multiple_aliases() {
|
|
Test::new()
|
|
.arg("--list")
|
|
.justfile("foo:\n echo foo\nalias f := foo\nalias fo := foo")
|
|
.stdout(
|
|
"
|
|
Available recipes:
|
|
foo # [aliases: f, fo]
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn alias_listing_parameters() {
|
|
Test::new()
|
|
.args(["--list"])
|
|
.justfile("foo PARAM='foo':\n echo {{PARAM}}\nalias f := foo")
|
|
.stdout(
|
|
"
|
|
Available recipes:
|
|
foo PARAM='foo' # [alias: f]
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn alias_listing_private() {
|
|
Test::new()
|
|
.arg("--list")
|
|
.justfile("foo PARAM='foo':\n echo {{PARAM}}\nalias _f := foo")
|
|
.stdout(
|
|
"
|
|
Available recipes:
|
|
foo PARAM='foo'
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn alias() {
|
|
Test::new()
|
|
.arg("f")
|
|
.justfile("foo:\n echo foo\nalias f := foo")
|
|
.stdout("foo\n")
|
|
.stderr("echo foo\n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn alias_with_parameters() {
|
|
Test::new()
|
|
.arg("f")
|
|
.arg("bar")
|
|
.justfile("foo value='foo':\n echo {{value}}\nalias f := foo")
|
|
.stdout("bar\n")
|
|
.stderr("echo bar\n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn bad_setting() {
|
|
Test::new()
|
|
.justfile(
|
|
"
|
|
set foo
|
|
",
|
|
)
|
|
.stderr(
|
|
"
|
|
error: Unknown setting `foo`
|
|
——▶ justfile:1:5
|
|
│
|
|
1 │ set foo
|
|
│ ^^^
|
|
",
|
|
)
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn bad_setting_with_keyword_name() {
|
|
Test::new()
|
|
.justfile(
|
|
"
|
|
set if := 'foo'
|
|
",
|
|
)
|
|
.stderr(
|
|
"
|
|
error: Unknown setting `if`
|
|
——▶ justfile:1:5
|
|
│
|
|
1 │ set if := 'foo'
|
|
│ ^^
|
|
",
|
|
)
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn alias_with_dependencies() {
|
|
Test::new()
|
|
.arg("b")
|
|
.justfile("foo:\n echo foo\nbar: foo\nalias b := bar")
|
|
.stdout("foo\n")
|
|
.stderr("echo foo\n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn duplicate_alias() {
|
|
Test::new()
|
|
.justfile("alias foo := bar\nalias foo := baz\n")
|
|
.stderr(
|
|
"
|
|
error: Alias `foo` first defined on line 1 is redefined on line 2
|
|
——▶ justfile:2:7
|
|
│
|
|
2 │ alias foo := baz
|
|
│ ^^^
|
|
",
|
|
)
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn unknown_alias_target() {
|
|
Test::new()
|
|
.justfile("alias foo := bar\n")
|
|
.stderr(
|
|
"
|
|
error: Alias `foo` has an unknown target `bar`
|
|
——▶ justfile:1:7
|
|
│
|
|
1 │ alias foo := bar
|
|
│ ^^^
|
|
",
|
|
)
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn alias_shadows_recipe() {
|
|
Test::new()
|
|
.justfile("bar:\n echo bar\nalias foo := bar\nfoo:\n echo foo")
|
|
.stderr(
|
|
"
|
|
error: Alias `foo` defined on line 3 is redefined as a recipe on line 4
|
|
——▶ justfile:4:1
|
|
│
|
|
4 │ foo:
|
|
│ ^^^
|
|
",
|
|
)
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn default() {
|
|
Test::new()
|
|
.justfile("default:\n echo hello\nother: \n echo bar")
|
|
.stdout("hello\n")
|
|
.stderr("echo hello\n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn quiet() {
|
|
Test::new()
|
|
.justfile("default:\n @echo hello")
|
|
.stdout("hello\n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn verbose() {
|
|
Test::new()
|
|
.arg("--verbose")
|
|
.justfile("default:\n @echo hello")
|
|
.stdout("hello\n")
|
|
.stderr("===> Running recipe `default`...\necho hello\n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn order() {
|
|
Test::new()
|
|
.arg("a")
|
|
.arg("d")
|
|
.justfile(
|
|
"
|
|
b: a
|
|
echo b
|
|
@mv a b
|
|
|
|
a:
|
|
echo a
|
|
@touch F
|
|
@touch a
|
|
|
|
d: c
|
|
echo d
|
|
@rm c
|
|
|
|
c: b
|
|
echo c
|
|
@mv b c",
|
|
)
|
|
.stdout("a\nb\nc\nd\n")
|
|
.stderr("echo a\necho b\necho c\necho d\n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn select() {
|
|
Test::new()
|
|
.arg("d")
|
|
.arg("c")
|
|
.justfile("b:\n @echo b\na:\n @echo a\nd:\n @echo d\nc:\n @echo c")
|
|
.stdout("d\nc\n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn print() {
|
|
Test::new()
|
|
.arg("d")
|
|
.arg("c")
|
|
.justfile("b:\n echo b\na:\n echo a\nd:\n echo d\nc:\n echo c")
|
|
.stdout("d\nc\n")
|
|
.stderr("echo d\necho c\n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn status_passthrough() {
|
|
Test::new()
|
|
.arg("recipe")
|
|
.justfile(
|
|
"
|
|
|
|
hello:
|
|
|
|
recipe:
|
|
@exit 100",
|
|
)
|
|
.stderr("error: Recipe `recipe` failed on line 5 with exit code 100\n")
|
|
.status(100)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn unknown_dependency() {
|
|
Test::new()
|
|
.justfile("bar:\nhello:\nfoo: bar baaaaaaaz hello")
|
|
.stderr(
|
|
"
|
|
error: Recipe `foo` has unknown dependency `baaaaaaaz`
|
|
——▶ justfile:3:10
|
|
│
|
|
3 │ foo: bar baaaaaaaz hello
|
|
│ ^^^^^^^^^
|
|
",
|
|
)
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn backtick_success() {
|
|
Test::new()
|
|
.justfile("a := `printf Hello,`\nbar:\n printf '{{a + `printf ' world.'`}}'")
|
|
.stdout("Hello, world.")
|
|
.stderr("printf 'Hello, world.'\n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn backtick_trimming() {
|
|
Test::new()
|
|
.justfile("a := `echo Hello,`\nbar:\n echo '{{a + `echo ' world.'`}}'")
|
|
.stdout("Hello, world.\n")
|
|
.stderr("echo 'Hello, world.'\n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn backtick_code_assignment() {
|
|
Test::new()
|
|
.justfile("b := a\na := `exit 100`\nbar:\n echo '{{`exit 200`}}'")
|
|
.stderr(
|
|
"
|
|
error: Backtick failed with exit code 100
|
|
——▶ justfile:2:6
|
|
│
|
|
2 │ a := `exit 100`
|
|
│ ^^^^^^^^^^
|
|
",
|
|
)
|
|
.status(100)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn backtick_code_interpolation() {
|
|
Test::new()
|
|
.justfile("b := a\na := `echo hello`\nbar:\n echo '{{`exit 200`}}'")
|
|
.stderr(
|
|
"
|
|
error: Backtick failed with exit code 200
|
|
——▶ justfile:4:10
|
|
│
|
|
4 │ echo '{{`exit 200`}}'
|
|
│ ^^^^^^^^^^
|
|
",
|
|
)
|
|
.status(200)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn backtick_code_interpolation_mod() {
|
|
Test::new()
|
|
.justfile("f:\n 無{{`exit 200`}}")
|
|
.stderr(
|
|
"
|
|
error: Backtick failed with exit code 200
|
|
——▶ justfile:2:7
|
|
│
|
|
2 │ 無{{`exit 200`}}
|
|
│ ^^^^^^^^^^
|
|
",
|
|
)
|
|
.status(200)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn backtick_code_interpolation_tab() {
|
|
Test::new()
|
|
.justfile(
|
|
"
|
|
backtick-fail:
|
|
\techo {{`exit 200`}}
|
|
",
|
|
)
|
|
.stderr(
|
|
" error: Backtick failed with exit code 200
|
|
——▶ justfile:2:9
|
|
│
|
|
2 │ echo {{`exit 200`}}
|
|
│ ^^^^^^^^^^
|
|
",
|
|
)
|
|
.status(200)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn backtick_code_interpolation_tabs() {
|
|
Test::new()
|
|
.justfile(
|
|
"
|
|
backtick-fail:
|
|
\techo {{\t`exit 200`}}
|
|
",
|
|
)
|
|
.stderr(
|
|
"error: Backtick failed with exit code 200
|
|
——▶ justfile:2:10
|
|
│
|
|
2 │ echo {{ `exit 200`}}
|
|
│ ^^^^^^^^^^
|
|
",
|
|
)
|
|
.status(200)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn backtick_code_interpolation_inner_tab() {
|
|
Test::new()
|
|
.justfile(
|
|
"
|
|
backtick-fail:
|
|
\techo {{\t`exit\t\t200`}}
|
|
",
|
|
)
|
|
.stderr(
|
|
"
|
|
error: Backtick failed with exit code 200
|
|
——▶ justfile:2:10
|
|
│
|
|
2 │ echo {{ `exit 200`}}
|
|
│ ^^^^^^^^^^^^^^^^^
|
|
",
|
|
)
|
|
.status(200)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn backtick_code_interpolation_leading_emoji() {
|
|
Test::new()
|
|
.justfile(
|
|
"
|
|
backtick-fail:
|
|
\techo 😬{{`exit 200`}}
|
|
",
|
|
)
|
|
.stderr(
|
|
"
|
|
error: Backtick failed with exit code 200
|
|
——▶ justfile:2:13
|
|
│
|
|
2 │ echo 😬{{`exit 200`}}
|
|
│ ^^^^^^^^^^
|
|
",
|
|
)
|
|
.status(200)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn backtick_code_interpolation_unicode_hell() {
|
|
Test::new()
|
|
.justfile(
|
|
"
|
|
backtick-fail:
|
|
\techo \t\t\t😬鎌鼬{{\t\t`exit 200 # \t\t\tabc`}}\t\t\t😬鎌鼬
|
|
",
|
|
)
|
|
.stderr(
|
|
"
|
|
error: Backtick failed with exit code 200
|
|
——▶ justfile:2:24
|
|
│
|
|
2 │ echo 😬鎌鼬{{ `exit 200 # abc`}} 😬鎌鼬
|
|
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
",
|
|
)
|
|
.status(200)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn backtick_code_long() {
|
|
Test::new()
|
|
.justfile(
|
|
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
b := a
|
|
a := `echo hello`
|
|
bar:
|
|
echo '{{`exit 200`}}'
|
|
",
|
|
)
|
|
.stderr(
|
|
"
|
|
error: Backtick failed with exit code 200
|
|
——▶ justfile:10:10
|
|
│
|
|
10 │ echo '{{`exit 200`}}'
|
|
│ ^^^^^^^^^^
|
|
",
|
|
)
|
|
.status(200)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn shebang_backtick_failure() {
|
|
Test::new()
|
|
.justfile(
|
|
"foo:
|
|
#!/bin/sh
|
|
echo hello
|
|
echo {{`exit 123`}}",
|
|
)
|
|
.stderr(
|
|
"
|
|
error: Backtick failed with exit code 123
|
|
——▶ justfile:4:9
|
|
│
|
|
4 │ echo {{`exit 123`}}
|
|
│ ^^^^^^^^^^
|
|
",
|
|
)
|
|
.status(123)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn command_backtick_failure() {
|
|
Test::new()
|
|
.justfile(
|
|
"foo:
|
|
echo hello
|
|
echo {{`exit 123`}}",
|
|
)
|
|
.stdout("hello\n")
|
|
.stderr(
|
|
"
|
|
echo hello
|
|
error: Backtick failed with exit code 123
|
|
——▶ justfile:3:9
|
|
│
|
|
3 │ echo {{`exit 123`}}
|
|
│ ^^^^^^^^^^
|
|
",
|
|
)
|
|
.status(123)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn assignment_backtick_failure() {
|
|
Test::new()
|
|
.justfile(
|
|
"foo:
|
|
echo hello
|
|
echo {{`exit 111`}}
|
|
a := `exit 222`",
|
|
)
|
|
.stderr(
|
|
"
|
|
error: Backtick failed with exit code 222
|
|
——▶ justfile:4:6
|
|
│
|
|
4 │ a := `exit 222`
|
|
│ ^^^^^^^^^^
|
|
",
|
|
)
|
|
.status(222)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn unknown_override_options() {
|
|
Test::new()
|
|
.arg("--set")
|
|
.arg("foo")
|
|
.arg("bar")
|
|
.arg("--set")
|
|
.arg("baz")
|
|
.arg("bob")
|
|
.arg("--set")
|
|
.arg("a")
|
|
.arg("b")
|
|
.arg("a")
|
|
.arg("b")
|
|
.justfile(
|
|
"foo:
|
|
echo hello
|
|
echo {{`exit 111`}}
|
|
a := `exit 222`",
|
|
)
|
|
.status(EXIT_FAILURE)
|
|
.stderr(
|
|
"error: Variables `baz` and `foo` overridden on the command line but not present \
|
|
in justfile\n",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn unknown_override_args() {
|
|
Test::new()
|
|
.arg("foo=bar")
|
|
.arg("baz=bob")
|
|
.arg("a=b")
|
|
.arg("a")
|
|
.arg("b")
|
|
.justfile(
|
|
"foo:
|
|
echo hello
|
|
echo {{`exit 111`}}
|
|
a := `exit 222`",
|
|
)
|
|
.stderr(
|
|
"error: Variables `baz` and `foo` overridden on the command line but not present \
|
|
in justfile\n",
|
|
)
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn unknown_override_arg() {
|
|
Test::new()
|
|
.arg("foo=bar")
|
|
.arg("a=b")
|
|
.arg("a")
|
|
.arg("b")
|
|
.justfile(
|
|
"foo:
|
|
echo hello
|
|
echo {{`exit 111`}}
|
|
a := `exit 222`",
|
|
)
|
|
.stderr("error: Variable `foo` overridden on the command line but not present in justfile\n")
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn overrides_first() {
|
|
Test::new()
|
|
.arg("foo=bar")
|
|
.arg("a=b")
|
|
.arg("recipe")
|
|
.arg("baz=bar")
|
|
.justfile(
|
|
r#"
|
|
foo := "foo"
|
|
a := "a"
|
|
baz := "baz"
|
|
|
|
recipe arg:
|
|
echo arg={{arg}}
|
|
echo {{foo + a + baz}}"#,
|
|
)
|
|
.stdout("arg=baz=bar\nbarbbaz\n")
|
|
.stderr("echo arg=baz=bar\necho barbbaz\n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn overrides_not_evaluated() {
|
|
Test::new()
|
|
.arg("foo=bar")
|
|
.arg("a=b")
|
|
.arg("recipe")
|
|
.arg("baz=bar")
|
|
.justfile(
|
|
r#"
|
|
foo := `exit 1`
|
|
a := "a"
|
|
baz := "baz"
|
|
|
|
recipe arg:
|
|
echo arg={{arg}}
|
|
echo {{foo + a + baz}}"#,
|
|
)
|
|
.stdout("arg=baz=bar\nbarbbaz\n")
|
|
.stderr("echo arg=baz=bar\necho barbbaz\n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn dry_run() {
|
|
Test::new()
|
|
.arg("--dry-run")
|
|
.arg("shebang")
|
|
.arg("command")
|
|
.justfile(
|
|
r"
|
|
var := `echo stderr 1>&2; echo backtick`
|
|
|
|
command:
|
|
@touch /this/is/not/a/file
|
|
{{var}}
|
|
echo {{`echo command interpolation`}}
|
|
|
|
shebang:
|
|
#!/bin/sh
|
|
touch /this/is/not/a/file
|
|
{{var}}
|
|
echo {{`echo shebang interpolation`}}",
|
|
)
|
|
.stderr(
|
|
"#!/bin/sh
|
|
touch /this/is/not/a/file
|
|
`echo stderr 1>&2; echo backtick`
|
|
echo `echo shebang interpolation`
|
|
touch /this/is/not/a/file
|
|
`echo stderr 1>&2; echo backtick`
|
|
echo `echo command interpolation`
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn line_error_spacing() {
|
|
Test::new()
|
|
.justfile(
|
|
r"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
^^^
|
|
",
|
|
)
|
|
.stderr(
|
|
"error: Unknown start of token '^'
|
|
——▶ justfile:10:1
|
|
│
|
|
10 │ ^^^
|
|
│ ^
|
|
",
|
|
)
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn argument_single() {
|
|
Test::new()
|
|
.arg("foo")
|
|
.arg("ARGUMENT")
|
|
.justfile(
|
|
"
|
|
foo A:
|
|
echo {{A}}
|
|
",
|
|
)
|
|
.stdout("ARGUMENT\n")
|
|
.stderr("echo ARGUMENT\n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn argument_multiple() {
|
|
Test::new()
|
|
.arg("foo")
|
|
.arg("ONE")
|
|
.arg("TWO")
|
|
.justfile(
|
|
"
|
|
foo A B:
|
|
echo A:{{A}} B:{{B}}
|
|
",
|
|
)
|
|
.stdout("A:ONE B:TWO\n")
|
|
.stderr("echo A:ONE B:TWO\n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn argument_mismatch_more() {
|
|
Test::new()
|
|
.arg("foo")
|
|
.arg("ONE")
|
|
.arg("TWO")
|
|
.arg("THREE")
|
|
.stderr("error: Justfile does not contain recipe `THREE`\n")
|
|
.status(EXIT_FAILURE)
|
|
.justfile(
|
|
"
|
|
foo A B:
|
|
echo A:{{A}} B:{{B}}
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn argument_mismatch_fewer() {
|
|
Test::new()
|
|
.arg("foo")
|
|
.arg("ONE")
|
|
.justfile(
|
|
"
|
|
foo A B:
|
|
echo A:{{A}} B:{{B}}
|
|
",
|
|
)
|
|
.stderr("error: Recipe `foo` got 1 argument but takes 2\nusage:\n just foo A B\n")
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn argument_mismatch_more_with_default() {
|
|
Test::new()
|
|
.arg("foo")
|
|
.arg("ONE")
|
|
.arg("TWO")
|
|
.arg("THREE")
|
|
.justfile(
|
|
"
|
|
foo A B='B':
|
|
echo A:{{A}} B:{{B}}
|
|
",
|
|
)
|
|
.stderr("error: Justfile does not contain recipe `THREE`\n")
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn argument_mismatch_fewer_with_default() {
|
|
Test::new()
|
|
.arg("foo")
|
|
.arg("bar")
|
|
.justfile(
|
|
"
|
|
foo A B C='C':
|
|
echo A:{{A}} B:{{B}} C:{{C}}
|
|
",
|
|
)
|
|
.stderr(
|
|
"
|
|
error: Recipe `foo` got 1 argument but takes at least 2
|
|
usage:
|
|
just foo A B C='C'
|
|
",
|
|
)
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn unknown_recipe() {
|
|
Test::new()
|
|
.arg("foo")
|
|
.justfile("hello:")
|
|
.stderr("error: Justfile does not contain recipe `foo`\n")
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn unknown_recipes() {
|
|
Test::new()
|
|
.arg("foo")
|
|
.arg("bar")
|
|
.justfile("hello:")
|
|
.stderr("error: Justfile does not contain recipe `foo`\n")
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn color_always() {
|
|
Test::new()
|
|
.arg("--color")
|
|
.arg("always")
|
|
.justfile("b := a\na := `exit 100`\nbar:\n echo '{{`exit 200`}}'")
|
|
.status(100)
|
|
.stderr("\u{1b}[1;31merror\u{1b}[0m: \u{1b}[1mBacktick failed with exit code 100\u{1b}[0m\n \u{1b}[1;34m——▶\u{1b}[0m justfile:2:6\n \u{1b}[1;34m│\u{1b}[0m\n\u{1b}[1;34m2 │\u{1b}[0m a := `exit 100`\n \u{1b}[1;34m│\u{1b}[0m \u{1b}[1;31m^^^^^^^^^^\u{1b}[0m\n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn color_never() {
|
|
Test::new()
|
|
.arg("--color")
|
|
.arg("never")
|
|
.justfile("b := a\na := `exit 100`\nbar:\n echo '{{`exit 200`}}'")
|
|
.stderr(
|
|
"error: Backtick failed with exit code 100
|
|
——▶ justfile:2:6
|
|
│
|
|
2 │ a := `exit 100`
|
|
│ ^^^^^^^^^^
|
|
",
|
|
)
|
|
.status(100)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn color_auto() {
|
|
Test::new()
|
|
.arg("--color")
|
|
.arg("auto")
|
|
.justfile("b := a\na := `exit 100`\nbar:\n echo '{{`exit 200`}}'")
|
|
.stderr(
|
|
"error: Backtick failed with exit code 100
|
|
——▶ justfile:2:6
|
|
│
|
|
2 │ a := `exit 100`
|
|
│ ^^^^^^^^^^
|
|
",
|
|
)
|
|
.status(100)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn colors_no_context() {
|
|
Test::new()
|
|
.arg("--color=always")
|
|
.stderr(
|
|
"\u{1b}[1;31merror\u{1b}[0m: \u{1b}[1m\
|
|
Recipe `recipe` failed on line 2 with exit code 100\u{1b}[0m\n",
|
|
)
|
|
.status(100)
|
|
.justfile(
|
|
"
|
|
recipe:
|
|
@exit 100",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn dump() {
|
|
Test::new()
|
|
.arg("--dump")
|
|
.justfile(
|
|
r"
|
|
# this recipe does something
|
|
recipe a b +d:
|
|
@exit 100",
|
|
)
|
|
.stdout(
|
|
"# this recipe does something
|
|
recipe a b +d:
|
|
@exit 100
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn mixed_whitespace() {
|
|
Test::new()
|
|
.justfile("bar:\n\t echo hello")
|
|
.stderr(
|
|
"error: Found a mix of tabs and spaces in leading whitespace: `␉␠`
|
|
Leading whitespace may consist of tabs or spaces, but not both
|
|
——▶ justfile:2:1
|
|
│
|
|
2 │ echo hello
|
|
│ ^^^^^
|
|
",
|
|
)
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn extra_leading_whitespace() {
|
|
Test::new()
|
|
.justfile("bar:\n\t\techo hello\n\t\t\techo goodbye")
|
|
.stderr(
|
|
"error: Recipe line has extra leading whitespace
|
|
——▶ justfile:3:3
|
|
│
|
|
3 │ echo goodbye
|
|
│ ^^^^^^^^^^^^^^^^
|
|
",
|
|
)
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn inconsistent_leading_whitespace() {
|
|
Test::new()
|
|
.justfile("bar:\n\t\techo hello\n\t echo goodbye")
|
|
.stderr(
|
|
"error: Recipe line has inconsistent leading whitespace. \
|
|
Recipe started with `␉␉` but found line with `␉␠`
|
|
——▶ justfile:3:1
|
|
│
|
|
3 │ echo goodbye
|
|
│ ^^^^^
|
|
",
|
|
)
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn required_after_default() {
|
|
Test::new()
|
|
.justfile("bar:\nhello baz arg='foo' bar:")
|
|
.stderr(
|
|
"error: Non-default parameter `bar` follows default parameter
|
|
——▶ justfile:2:21
|
|
│
|
|
2 │ hello baz arg='foo' bar:
|
|
│ ^^^
|
|
",
|
|
)
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn required_after_plus_variadic() {
|
|
Test::new()
|
|
.justfile("bar:\nhello baz +arg bar:")
|
|
.stderr(
|
|
"error: Parameter `bar` follows variadic parameter
|
|
——▶ justfile:2:16
|
|
│
|
|
2 │ hello baz +arg bar:
|
|
│ ^^^
|
|
",
|
|
)
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn required_after_star_variadic() {
|
|
Test::new()
|
|
.justfile("bar:\nhello baz *arg bar:")
|
|
.stderr(
|
|
"error: Parameter `bar` follows variadic parameter
|
|
——▶ justfile:2:16
|
|
│
|
|
2 │ hello baz *arg bar:
|
|
│ ^^^
|
|
",
|
|
)
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn use_string_default() {
|
|
Test::new()
|
|
.arg("hello")
|
|
.arg("ABC")
|
|
.justfile(
|
|
r#"
|
|
bar:
|
|
hello baz arg="XYZ\t\" ":
|
|
echo '{{baz}}...{{arg}}'
|
|
"#,
|
|
)
|
|
.stdout("ABC...XYZ\t\"\t\n")
|
|
.stderr("echo 'ABC...XYZ\t\"\t'\n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn use_raw_string_default() {
|
|
Test::new()
|
|
.arg("hello")
|
|
.arg("ABC")
|
|
.justfile(
|
|
r#"
|
|
bar:
|
|
hello baz arg='XYZ" ':
|
|
printf '{{baz}}...{{arg}}'
|
|
"#,
|
|
)
|
|
.stdout("ABC...XYZ\"\t")
|
|
.stderr("printf 'ABC...XYZ\"\t'\n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn supply_use_default() {
|
|
Test::new()
|
|
.arg("hello")
|
|
.arg("0")
|
|
.arg("1")
|
|
.justfile(
|
|
r"
|
|
hello a b='B' c='C':
|
|
echo {{a}} {{b}} {{c}}
|
|
",
|
|
)
|
|
.stdout("0 1 C\n")
|
|
.stderr("echo 0 1 C\n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn supply_defaults() {
|
|
Test::new()
|
|
.arg("hello")
|
|
.arg("0")
|
|
.arg("1")
|
|
.arg("2")
|
|
.justfile(
|
|
r"
|
|
hello a b='B' c='C':
|
|
echo {{a}} {{b}} {{c}}
|
|
",
|
|
)
|
|
.stdout("0 1 2\n")
|
|
.stderr("echo 0 1 2\n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn list() {
|
|
Test::new()
|
|
.arg("--list")
|
|
.justfile(
|
|
r#"
|
|
|
|
# this does a thing
|
|
hello a b='B ' c='C':
|
|
echo {{a}} {{b}} {{c}}
|
|
|
|
# this comment will be ignored
|
|
|
|
a Z="\t z":
|
|
|
|
# this recipe will not appear
|
|
_private-recipe:
|
|
"#,
|
|
)
|
|
.stdout(
|
|
r#"
|
|
Available recipes:
|
|
a Z="\t z"
|
|
hello a b='B ' c='C' # this does a thing
|
|
"#,
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn list_alignment() {
|
|
Test::new()
|
|
.arg("--list")
|
|
.justfile(
|
|
r#"
|
|
|
|
# this does a thing
|
|
hello a b='B ' c='C':
|
|
echo {{a}} {{b}} {{c}}
|
|
|
|
# something else
|
|
a Z="\t z":
|
|
|
|
# this recipe will not appear
|
|
_private-recipe:
|
|
"#,
|
|
)
|
|
.stdout(
|
|
r#"
|
|
Available recipes:
|
|
a Z="\t z" # something else
|
|
hello a b='B ' c='C' # this does a thing
|
|
"#,
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn list_alignment_long() {
|
|
Test::new()
|
|
.arg("--list")
|
|
.justfile(
|
|
r#"
|
|
|
|
# this does a thing
|
|
hello a b='B ' c='C':
|
|
echo {{a}} {{b}} {{c}}
|
|
|
|
# this does another thing
|
|
x a b='B ' c='C':
|
|
echo {{a}} {{b}} {{c}}
|
|
|
|
# something else
|
|
this-recipe-is-very-very-very-very-very-very-very-very-important Z="\t z":
|
|
|
|
# this recipe will not appear
|
|
_private-recipe:
|
|
"#,
|
|
)
|
|
.stdout(
|
|
r#"
|
|
Available recipes:
|
|
hello a b='B ' c='C' # this does a thing
|
|
this-recipe-is-very-very-very-very-very-very-very-very-important Z="\t z" # something else
|
|
x a b='B ' c='C' # this does another thing
|
|
"#,
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn list_sorted() {
|
|
Test::new()
|
|
.arg("--list")
|
|
.justfile(
|
|
r"
|
|
alias c := b
|
|
b:
|
|
a:
|
|
",
|
|
)
|
|
.stdout(
|
|
r"
|
|
Available recipes:
|
|
a
|
|
b # [alias: c]
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn list_unsorted() {
|
|
Test::new()
|
|
.arg("--list")
|
|
.arg("--unsorted")
|
|
.justfile(
|
|
r"
|
|
alias c := b
|
|
b:
|
|
a:
|
|
",
|
|
)
|
|
.stdout(
|
|
r"
|
|
Available recipes:
|
|
b # [alias: c]
|
|
a
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn list_heading() {
|
|
Test::new()
|
|
.arg("--list")
|
|
.arg("--list-heading")
|
|
.arg("Cool stuff…\n")
|
|
.justfile(
|
|
r"
|
|
a:
|
|
b:
|
|
",
|
|
)
|
|
.stdout(
|
|
r"
|
|
Cool stuff…
|
|
a
|
|
b
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn list_prefix() {
|
|
Test::new()
|
|
.arg("--list")
|
|
.arg("--list-prefix")
|
|
.arg("····")
|
|
.justfile(
|
|
r"
|
|
a:
|
|
b:
|
|
",
|
|
)
|
|
.stdout(
|
|
r"
|
|
Available recipes:
|
|
····a
|
|
····b
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn list_empty_prefix_and_heading() {
|
|
Test::new()
|
|
.arg("--list")
|
|
.arg("--list-heading")
|
|
.arg("")
|
|
.arg("--list-prefix")
|
|
.arg("")
|
|
.justfile(
|
|
r"
|
|
a:
|
|
b:
|
|
",
|
|
)
|
|
.stdout(
|
|
r"
|
|
a
|
|
b
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn run_suggestion() {
|
|
Test::new()
|
|
.arg("hell")
|
|
.justfile(
|
|
r#"
|
|
hello a b='B ' c='C':
|
|
echo {{a}} {{b}} {{c}}
|
|
|
|
a Z="\t z":
|
|
"#,
|
|
)
|
|
.stderr("error: Justfile does not contain recipe `hell`\nDid you mean `hello`?\n")
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn line_continuation_with_space() {
|
|
Test::new()
|
|
.justfile(
|
|
r"
|
|
foo:
|
|
echo a\
|
|
b \
|
|
c
|
|
",
|
|
)
|
|
.stdout("ab c\n")
|
|
.stderr("echo ab c\n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn line_continuation_with_quoted_space() {
|
|
Test::new()
|
|
.justfile(
|
|
r"
|
|
foo:
|
|
echo 'a\
|
|
b \
|
|
c'
|
|
",
|
|
)
|
|
.stdout("ab c\n")
|
|
.stderr("echo 'ab c'\n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn line_continuation_no_space() {
|
|
Test::new()
|
|
.justfile(
|
|
r"
|
|
foo:
|
|
echo a\
|
|
b\
|
|
c
|
|
",
|
|
)
|
|
.stdout("abc\n")
|
|
.stderr("echo abc\n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn infallible_command() {
|
|
Test::new()
|
|
.justfile(
|
|
r"
|
|
infallible:
|
|
-exit 101
|
|
",
|
|
)
|
|
.stderr("exit 101\n")
|
|
.status(EXIT_SUCCESS)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn infallible_with_failing() {
|
|
Test::new()
|
|
.justfile(
|
|
r"
|
|
infallible:
|
|
-exit 101
|
|
exit 202
|
|
",
|
|
)
|
|
.stderr(
|
|
r"exit 101
|
|
exit 202
|
|
error: Recipe `infallible` failed on line 3 with exit code 202
|
|
",
|
|
)
|
|
.status(202)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn quiet_recipe() {
|
|
Test::new()
|
|
.justfile(
|
|
r"
|
|
@quiet:
|
|
# a
|
|
# b
|
|
@echo c
|
|
",
|
|
)
|
|
.stdout("c\n")
|
|
.stderr("echo c\n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn quiet_shebang_recipe() {
|
|
Test::new()
|
|
.justfile(
|
|
r"
|
|
@quiet:
|
|
#!/bin/sh
|
|
echo hello
|
|
",
|
|
)
|
|
.stdout("hello\n")
|
|
.stderr("#!/bin/sh\necho hello\n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn complex_dependencies() {
|
|
Test::new()
|
|
.arg("b")
|
|
.justfile(
|
|
r"
|
|
a: b
|
|
b:
|
|
c: b a
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn unknown_function_in_assignment() {
|
|
Test::new()
|
|
.arg("bar")
|
|
.justfile(
|
|
r#"foo := foo() + "hello"
|
|
bar:"#,
|
|
)
|
|
.stderr(
|
|
r#"error: Call to unknown function `foo`
|
|
——▶ justfile:1:8
|
|
│
|
|
1 │ foo := foo() + "hello"
|
|
│ ^^^
|
|
"#,
|
|
)
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn dependency_takes_arguments_exact() {
|
|
Test::new()
|
|
.arg("b")
|
|
.justfile(
|
|
"
|
|
a FOO:
|
|
b: a
|
|
",
|
|
)
|
|
.stderr(
|
|
"error: Dependency `a` got 0 arguments but takes 1 argument
|
|
——▶ justfile:2:4
|
|
│
|
|
2 │ b: a
|
|
│ ^
|
|
",
|
|
)
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn dependency_takes_arguments_at_least() {
|
|
Test::new()
|
|
.arg("b")
|
|
.justfile(
|
|
"
|
|
a FOO LUZ='hello':
|
|
b: a
|
|
",
|
|
)
|
|
.stderr(
|
|
"error: Dependency `a` got 0 arguments but takes at least 1 argument
|
|
——▶ justfile:2:4
|
|
│
|
|
2 │ b: a
|
|
│ ^
|
|
",
|
|
)
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn dependency_takes_arguments_at_most() {
|
|
Test::new()
|
|
.arg("b")
|
|
.justfile(
|
|
"
|
|
a FOO LUZ='hello':
|
|
b: (a '0' '1' '2')
|
|
",
|
|
)
|
|
.stderr(
|
|
"error: Dependency `a` got 3 arguments but takes at most 2 arguments
|
|
——▶ justfile:2:5
|
|
│
|
|
2 │ b: (a '0' '1' '2')
|
|
│ ^
|
|
",
|
|
)
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn duplicate_parameter() {
|
|
Test::new()
|
|
.arg("a")
|
|
.justfile("a foo foo:")
|
|
.stderr(
|
|
"error: Recipe `a` has duplicate parameter `foo`
|
|
——▶ justfile:1:7
|
|
│
|
|
1 │ a foo foo:
|
|
│ ^^^
|
|
",
|
|
)
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn duplicate_recipe() {
|
|
Test::new()
|
|
.arg("b")
|
|
.justfile("b:\nb:")
|
|
.stderr(
|
|
"error: Recipe `b` first defined on line 1 is redefined on line 2
|
|
——▶ justfile:2:1
|
|
│
|
|
2 │ b:
|
|
│ ^
|
|
",
|
|
)
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn duplicate_variable() {
|
|
Test::new()
|
|
.arg("foo")
|
|
.justfile("a := 'hello'\na := 'hello'\nfoo:")
|
|
.status(EXIT_FAILURE)
|
|
.stderr(
|
|
"error: Variable `a` has multiple definitions
|
|
——▶ justfile:2:1
|
|
│
|
|
2 │ a := 'hello'
|
|
│ ^
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn unexpected_token_in_dependency_position() {
|
|
Test::new()
|
|
.arg("foo")
|
|
.justfile("foo: 'bar'")
|
|
.stderr(
|
|
"error: Expected '&&', comment, end of file, end of line, \
|
|
identifier, or '(', but found string
|
|
——▶ justfile:1:6
|
|
│
|
|
1 │ foo: 'bar'
|
|
│ ^^^^^
|
|
",
|
|
)
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn unexpected_token_after_name() {
|
|
Test::new()
|
|
.arg("foo")
|
|
.justfile("foo 'bar'")
|
|
.stderr(
|
|
"error: Expected '*', ':', '$', identifier, or '+', but found string
|
|
——▶ justfile:1:5
|
|
│
|
|
1 │ foo 'bar'
|
|
│ ^^^^^
|
|
",
|
|
)
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn self_dependency() {
|
|
Test::new()
|
|
.arg("a")
|
|
.justfile("a: a")
|
|
.stderr(
|
|
"error: Recipe `a` depends on itself
|
|
——▶ justfile:1:4
|
|
│
|
|
1 │ a: a
|
|
│ ^
|
|
",
|
|
)
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn long_circular_recipe_dependency() {
|
|
Test::new()
|
|
.arg("a")
|
|
.justfile("a: b\nb: c\nc: d\nd: a")
|
|
.stderr(
|
|
"error: Recipe `d` has circular dependency `a -> b -> c -> d -> a`
|
|
——▶ justfile:4:4
|
|
│
|
|
4 │ d: a
|
|
│ ^
|
|
",
|
|
)
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn variable_self_dependency() {
|
|
Test::new()
|
|
.arg("a")
|
|
.justfile("z := z\na:")
|
|
.stderr(
|
|
"error: Variable `z` is defined in terms of itself
|
|
——▶ justfile:1:1
|
|
│
|
|
1 │ z := z
|
|
│ ^
|
|
",
|
|
)
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn variable_circular_dependency() {
|
|
Test::new()
|
|
.arg("a")
|
|
.justfile("x := y\ny := z\nz := x\na:")
|
|
.status(EXIT_FAILURE)
|
|
.stderr(
|
|
"error: Variable `x` depends on its own value: `x -> y -> z -> x`
|
|
——▶ justfile:1:1
|
|
│
|
|
1 │ x := y
|
|
│ ^
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn variable_circular_dependency_with_additional_variable() {
|
|
Test::new()
|
|
.arg("a")
|
|
.justfile(
|
|
"
|
|
a := ''
|
|
x := y
|
|
y := x
|
|
|
|
a:
|
|
",
|
|
)
|
|
.stderr(
|
|
"error: Variable `x` depends on its own value: `x -> y -> x`
|
|
——▶ justfile:2:1
|
|
│
|
|
2 │ x := y
|
|
│ ^
|
|
",
|
|
)
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn plus_variadic_recipe() {
|
|
Test::new()
|
|
.arg("a")
|
|
.arg("0")
|
|
.arg("1")
|
|
.arg("2")
|
|
.arg("3")
|
|
.arg(" 4 ")
|
|
.justfile(
|
|
"
|
|
a x y +z:
|
|
echo {{x}} {{y}} {{z}}
|
|
",
|
|
)
|
|
.stdout("0 1 2 3 4\n")
|
|
.stderr("echo 0 1 2 3 4 \n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn plus_variadic_ignore_default() {
|
|
Test::new()
|
|
.arg("a")
|
|
.arg("0")
|
|
.arg("1")
|
|
.arg("2")
|
|
.arg("3")
|
|
.arg(" 4 ")
|
|
.justfile(
|
|
"
|
|
a x y +z='HELLO':
|
|
echo {{x}} {{y}} {{z}}
|
|
",
|
|
)
|
|
.stdout("0 1 2 3 4\n")
|
|
.stderr("echo 0 1 2 3 4 \n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn plus_variadic_use_default() {
|
|
Test::new()
|
|
.arg("a")
|
|
.arg("0")
|
|
.arg("1")
|
|
.justfile(
|
|
"
|
|
a x y +z='HELLO':
|
|
echo {{x}} {{y}} {{z}}
|
|
",
|
|
)
|
|
.stdout("0 1 HELLO\n")
|
|
.stderr("echo 0 1 HELLO\n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn plus_variadic_too_few() {
|
|
Test::new()
|
|
.arg("a")
|
|
.arg("0")
|
|
.arg("1")
|
|
.justfile(
|
|
"
|
|
a x y +z:
|
|
echo {{x}} {{y}} {{z}}
|
|
",
|
|
)
|
|
.stderr("error: Recipe `a` got 2 arguments but takes at least 3\nusage:\n just a x y +z\n")
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn star_variadic_recipe() {
|
|
Test::new()
|
|
.arg("a")
|
|
.arg("0")
|
|
.arg("1")
|
|
.arg("2")
|
|
.arg("3")
|
|
.arg(" 4 ")
|
|
.justfile(
|
|
"
|
|
a x y *z:
|
|
echo {{x}} {{y}} {{z}}
|
|
",
|
|
)
|
|
.stdout("0 1 2 3 4\n")
|
|
.stderr("echo 0 1 2 3 4 \n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn star_variadic_none() {
|
|
Test::new()
|
|
.arg("a")
|
|
.arg("0")
|
|
.arg("1")
|
|
.justfile(
|
|
"
|
|
a x y *z:
|
|
echo {{x}} {{y}} {{z}}
|
|
",
|
|
)
|
|
.stdout("0 1\n")
|
|
.stderr("echo 0 1 \n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn star_variadic_ignore_default() {
|
|
Test::new()
|
|
.arg("a")
|
|
.arg("0")
|
|
.arg("1")
|
|
.arg("2")
|
|
.arg("3")
|
|
.arg(" 4 ")
|
|
.justfile(
|
|
"
|
|
a x y *z='HELLO':
|
|
echo {{x}} {{y}} {{z}}
|
|
",
|
|
)
|
|
.stdout("0 1 2 3 4\n")
|
|
.stderr("echo 0 1 2 3 4 \n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn star_variadic_use_default() {
|
|
Test::new()
|
|
.arg("a")
|
|
.arg("0")
|
|
.arg("1")
|
|
.justfile(
|
|
"
|
|
a x y *z='HELLO':
|
|
echo {{x}} {{y}} {{z}}
|
|
",
|
|
)
|
|
.stdout("0 1 HELLO\n")
|
|
.stderr("echo 0 1 HELLO\n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn star_then_plus_variadic() {
|
|
Test::new()
|
|
.justfile(
|
|
"
|
|
foo *a +b:
|
|
echo {{a}} {{b}}
|
|
",
|
|
)
|
|
.stderr(
|
|
"error: Expected \':\' or \'=\', but found \'+\'
|
|
——▶ justfile:1:8
|
|
│
|
|
1 │ foo *a +b:
|
|
│ ^
|
|
",
|
|
)
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn plus_then_star_variadic() {
|
|
Test::new()
|
|
.justfile(
|
|
"
|
|
foo +a *b:
|
|
echo {{a}} {{b}}
|
|
",
|
|
)
|
|
.stderr(
|
|
"error: Expected \':\' or \'=\', but found \'*\'
|
|
——▶ justfile:1:8
|
|
│
|
|
1 │ foo +a *b:
|
|
│ ^
|
|
",
|
|
)
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn argument_grouping() {
|
|
Test::new()
|
|
.arg("BAR")
|
|
.arg("0")
|
|
.arg("FOO")
|
|
.arg("1")
|
|
.arg("2")
|
|
.arg("BAZ")
|
|
.arg("3")
|
|
.arg("4")
|
|
.arg("5")
|
|
.justfile(
|
|
"
|
|
FOO A B='blarg':
|
|
echo foo: {{A}} {{B}}
|
|
|
|
BAR X:
|
|
echo bar: {{X}}
|
|
|
|
BAZ +Z:
|
|
echo baz: {{Z}}
|
|
",
|
|
)
|
|
.stdout("bar: 0\nfoo: 1 2\nbaz: 3 4 5\n")
|
|
.stderr("echo bar: 0\necho foo: 1 2\necho baz: 3 4 5\n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn missing_second_dependency() {
|
|
Test::new()
|
|
.justfile(
|
|
"
|
|
x:
|
|
|
|
a: x y
|
|
",
|
|
)
|
|
.stderr(
|
|
"error: Recipe `a` has unknown dependency `y`
|
|
——▶ justfile:3:6
|
|
│
|
|
3 │ a: x y
|
|
│ ^
|
|
",
|
|
)
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn list_colors() {
|
|
Test::new()
|
|
.arg("--color")
|
|
.arg("always")
|
|
.arg("--list")
|
|
.justfile(
|
|
"
|
|
# comment
|
|
a B C +D='hello':
|
|
echo {{B}} {{C}} {{D}}
|
|
",
|
|
)
|
|
.stdout(
|
|
"
|
|
Available recipes:
|
|
a \
|
|
\u{1b}[36mB\u{1b}[0m \u{1b}[36mC\u{1b}[0m \u{1b}[35m+\
|
|
\u{1b}[0m\u{1b}[36mD\u{1b}[0m=\u{1b}[32m'hello'\u{1b}[0m \
|
|
\u{1b}[34m#\u{1b}[0m \u{1b}[34mcomment\u{1b}[0m
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn run_colors() {
|
|
Test::new()
|
|
.arg("--color")
|
|
.arg("always")
|
|
.arg("--highlight")
|
|
.arg("--verbose")
|
|
.justfile(
|
|
"
|
|
# comment
|
|
a:
|
|
echo hi
|
|
",
|
|
)
|
|
.stdout("hi\n")
|
|
.stderr("\u{1b}[1;36m===> Running recipe `a`...\u{1b}[0m\n\u{1b}[1mecho hi\u{1b}[0m\n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn no_highlight() {
|
|
Test::new()
|
|
.arg("--color")
|
|
.arg("always")
|
|
.arg("--highlight")
|
|
.arg("--no-highlight")
|
|
.arg("--verbose")
|
|
.justfile(
|
|
"
|
|
# comment
|
|
a:
|
|
echo hi
|
|
",
|
|
)
|
|
.stdout("hi\n")
|
|
.stderr("\u{1b}[1;36m===> Running recipe `a`...\u{1b}[0m\necho hi\n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn trailing_flags() {
|
|
Test::new()
|
|
.arg("echo")
|
|
.arg("--some")
|
|
.arg("--awesome")
|
|
.arg("--flags")
|
|
.justfile(
|
|
"
|
|
echo A B C:
|
|
echo {{A}} {{B}} {{C}}
|
|
",
|
|
)
|
|
.stdout("--some --awesome --flags\n")
|
|
.stderr("echo --some --awesome --flags\n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn comment_before_variable() {
|
|
Test::new()
|
|
.arg("echo")
|
|
.justfile(
|
|
"
|
|
#
|
|
A:='1'
|
|
echo:
|
|
echo {{A}}
|
|
",
|
|
)
|
|
.stdout("1\n")
|
|
.stderr("echo 1\n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn invalid_escape_sequence_message() {
|
|
Test::new()
|
|
.justfile(
|
|
r#"
|
|
X := "\'"
|
|
"#,
|
|
)
|
|
.stderr(
|
|
r#"error: `\'` is not a valid escape sequence
|
|
——▶ justfile:1:6
|
|
│
|
|
1 │ X := "\'"
|
|
│ ^^^^
|
|
"#,
|
|
)
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn unknown_variable_in_default() {
|
|
Test::new()
|
|
.justfile(
|
|
"
|
|
foo x=bar:
|
|
",
|
|
)
|
|
.stderr(
|
|
r"error: Variable `bar` not defined
|
|
——▶ justfile:1:7
|
|
│
|
|
1 │ foo x=bar:
|
|
│ ^^^
|
|
",
|
|
)
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn unknown_function_in_default() {
|
|
Test::new()
|
|
.justfile(
|
|
"
|
|
foo x=bar():
|
|
",
|
|
)
|
|
.stderr(
|
|
r"error: Call to unknown function `bar`
|
|
——▶ justfile:1:7
|
|
│
|
|
1 │ foo x=bar():
|
|
│ ^^^
|
|
",
|
|
)
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn default_string() {
|
|
Test::new()
|
|
.justfile(
|
|
"
|
|
foo x='bar':
|
|
echo {{x}}
|
|
",
|
|
)
|
|
.stdout("bar\n")
|
|
.stderr("echo bar\n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn default_concatenation() {
|
|
Test::new()
|
|
.justfile(
|
|
"
|
|
foo x=(`echo foo` + 'bar'):
|
|
echo {{x}}
|
|
",
|
|
)
|
|
.stdout("foobar\n")
|
|
.stderr("echo foobar\n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn default_backtick() {
|
|
Test::new()
|
|
.justfile(
|
|
"
|
|
foo x=`echo foo`:
|
|
echo {{x}}
|
|
",
|
|
)
|
|
.stdout("foo\n")
|
|
.stderr("echo foo\n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn default_variable() {
|
|
Test::new()
|
|
.justfile(
|
|
"
|
|
y := 'foo'
|
|
foo x=y:
|
|
echo {{x}}
|
|
",
|
|
)
|
|
.stdout("foo\n")
|
|
.stderr("echo foo\n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn unterminated_interpolation_eol() {
|
|
Test::new()
|
|
.justfile(
|
|
"
|
|
foo:
|
|
echo {{
|
|
",
|
|
)
|
|
.stderr(
|
|
r"
|
|
error: Unterminated interpolation
|
|
——▶ justfile:2:8
|
|
│
|
|
2 │ echo {{
|
|
│ ^^
|
|
",
|
|
)
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn unterminated_interpolation_eof() {
|
|
Test::new()
|
|
.justfile(
|
|
"
|
|
foo:
|
|
echo {{
|
|
",
|
|
)
|
|
.stderr(
|
|
r"
|
|
error: Unterminated interpolation
|
|
——▶ justfile:2:8
|
|
│
|
|
2 │ echo {{
|
|
│ ^^
|
|
",
|
|
)
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn unknown_start_of_token() {
|
|
Test::new()
|
|
.justfile(
|
|
"
|
|
assembly_source_files = %(wildcard src/arch/$(arch)/*.s)
|
|
",
|
|
)
|
|
.stderr(
|
|
r"
|
|
error: Unknown start of token '%'
|
|
——▶ justfile:1:25
|
|
│
|
|
1 │ assembly_source_files = %(wildcard src/arch/$(arch)/*.s)
|
|
│ ^
|
|
",
|
|
)
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn unknown_start_of_token_invisible_unicode() {
|
|
Test::new()
|
|
.justfile(
|
|
"
|
|
\u{200b}foo := 'bar'
|
|
",
|
|
)
|
|
.stderr(
|
|
"
|
|
error: Unknown start of token '\u{200b}' (U+200B)
|
|
——▶ justfile:1:1
|
|
│
|
|
1 │ \u{200b}foo := 'bar'
|
|
│ ^
|
|
",
|
|
)
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn unknown_start_of_token_ascii_control_char() {
|
|
Test::new()
|
|
.justfile(
|
|
"
|
|
\0foo := 'bar'
|
|
",
|
|
)
|
|
.stderr(
|
|
"
|
|
error: Unknown start of token '\0' (U+0000)
|
|
——▶ justfile:1:1
|
|
│
|
|
1 │ \0foo := 'bar'
|
|
│ ^
|
|
",
|
|
)
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn backtick_variable_cat() {
|
|
Test::new()
|
|
.justfile(
|
|
"
|
|
stdin := `cat`
|
|
|
|
default:
|
|
echo {{stdin}}
|
|
",
|
|
)
|
|
.stdin("STDIN")
|
|
.stdout("STDIN\n")
|
|
.stderr("echo STDIN\n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn backtick_default_cat_stdin() {
|
|
Test::new()
|
|
.justfile(
|
|
"
|
|
default stdin = `cat`:
|
|
echo {{stdin}}
|
|
",
|
|
)
|
|
.stdin("STDIN")
|
|
.stdout("STDIN\n")
|
|
.stderr("echo STDIN\n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn backtick_default_cat_justfile() {
|
|
Test::new()
|
|
.justfile(
|
|
"
|
|
default stdin = `cat justfile`:
|
|
echo '{{stdin}}'
|
|
",
|
|
)
|
|
.stdout(
|
|
"
|
|
default stdin = `cat justfile`:
|
|
echo {{stdin}}
|
|
",
|
|
)
|
|
.stderr(
|
|
"
|
|
echo 'default stdin = `cat justfile`:
|
|
echo '{{stdin}}''
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn backtick_variable_read_single() {
|
|
Test::new()
|
|
.justfile(
|
|
"
|
|
password := `read PW && echo $PW`
|
|
|
|
default:
|
|
echo {{password}}
|
|
",
|
|
)
|
|
.stdin("foobar\n")
|
|
.stdout("foobar\n")
|
|
.stderr("echo foobar\n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn backtick_variable_read_multiple() {
|
|
Test::new()
|
|
.justfile(
|
|
"
|
|
a := `read A && echo $A`
|
|
b := `read B && echo $B`
|
|
|
|
default:
|
|
echo {{a}}
|
|
echo {{b}}
|
|
",
|
|
)
|
|
.stdin("foo\nbar\n")
|
|
.stdout("foo\nbar\n")
|
|
.stderr("echo foo\necho bar\n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn backtick_default_read_multiple() {
|
|
Test::new()
|
|
.justfile(
|
|
"
|
|
|
|
default a=`read A && echo $A` b=`read B && echo $B`:
|
|
echo {{a}}
|
|
echo {{b}}
|
|
",
|
|
)
|
|
.stdin("foo\nbar\n")
|
|
.stdout("foo\nbar\n")
|
|
.stderr("echo foo\necho bar\n")
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn old_equals_assignment_syntax_produces_error() {
|
|
Test::new()
|
|
.justfile(
|
|
"
|
|
foo = 'bar'
|
|
|
|
default:
|
|
echo {{foo}}
|
|
",
|
|
)
|
|
.stderr(
|
|
"
|
|
error: Expected '*', ':', '$', identifier, or '+', but found '='
|
|
——▶ justfile:1:5
|
|
│
|
|
1 │ foo = 'bar'
|
|
│ ^
|
|
",
|
|
)
|
|
.status(EXIT_FAILURE)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn dependency_argument_string() {
|
|
Test::new()
|
|
.justfile(
|
|
"
|
|
release: (build 'foo') (build 'bar')
|
|
|
|
build target:
|
|
echo 'Building {{target}}...'
|
|
",
|
|
)
|
|
.stdout("Building foo...\nBuilding bar...\n")
|
|
.stderr("echo 'Building foo...'\necho 'Building bar...'\n")
|
|
.shell(false)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn dependency_argument_parameter() {
|
|
Test::new()
|
|
.justfile(
|
|
"
|
|
default: (release '1.0')
|
|
|
|
release version: (build 'foo' version) (build 'bar' version)
|
|
|
|
build target version:
|
|
echo 'Building {{target}}@{{version}}...'
|
|
",
|
|
)
|
|
.stdout("Building foo@1.0...\nBuilding bar@1.0...\n")
|
|
.stderr("echo 'Building foo@1.0...'\necho 'Building bar@1.0...'\n")
|
|
.shell(false)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn dependency_argument_function() {
|
|
Test::new()
|
|
.justfile(
|
|
"
|
|
foo: (bar env_var_or_default('x', 'y'))
|
|
|
|
bar arg:
|
|
echo {{arg}}
|
|
",
|
|
)
|
|
.stdout("y\n")
|
|
.stderr("echo y\n")
|
|
.shell(false)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn env_function_as_env_var() {
|
|
Test::new()
|
|
.env("x", "z")
|
|
.justfile(
|
|
"
|
|
foo: (bar env('x'))
|
|
|
|
bar arg:
|
|
echo {{arg}}
|
|
",
|
|
)
|
|
.stdout("z\n")
|
|
.stderr("echo z\n")
|
|
.shell(false)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn env_function_as_env_var_or_default() {
|
|
Test::new()
|
|
.env("x", "z")
|
|
.justfile(
|
|
"
|
|
foo: (bar env('x', 'y'))
|
|
|
|
bar arg:
|
|
echo {{arg}}
|
|
",
|
|
)
|
|
.stdout("z\n")
|
|
.stderr("echo z\n")
|
|
.shell(false)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn env_function_as_env_var_with_existing_env_var() {
|
|
Test::new()
|
|
.env("x", "z")
|
|
.justfile(
|
|
"
|
|
foo: (bar env('x'))
|
|
|
|
bar arg:
|
|
echo {{arg}}
|
|
",
|
|
)
|
|
.stdout("z\n")
|
|
.stderr("echo z\n")
|
|
.shell(false)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn env_function_as_env_var_or_default_with_existing_env_var() {
|
|
Test::new()
|
|
.env("x", "z")
|
|
.justfile(
|
|
"
|
|
foo: (bar env('x', 'y'))
|
|
|
|
bar arg:
|
|
echo {{arg}}
|
|
",
|
|
)
|
|
.stdout("z\n")
|
|
.stderr("echo z\n")
|
|
.shell(false)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn dependency_argument_backtick() {
|
|
Test::new()
|
|
.justfile(
|
|
"
|
|
export X := 'X'
|
|
|
|
foo: (bar `echo $X`)
|
|
|
|
bar arg:
|
|
echo {{arg}}
|
|
echo $X
|
|
",
|
|
)
|
|
.stdout("X\nX\n")
|
|
.stderr("echo X\necho $X\n")
|
|
.shell(false)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn dependency_argument_assignment() {
|
|
Test::new()
|
|
.justfile(
|
|
"
|
|
v := '1.0'
|
|
|
|
default: (release v)
|
|
|
|
release version:
|
|
echo Release {{version}}...
|
|
",
|
|
)
|
|
.stdout("Release 1.0...\n")
|
|
.stderr("echo Release 1.0...\n")
|
|
.shell(false)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn dependency_argument_plus_variadic() {
|
|
Test::new()
|
|
.justfile(
|
|
"
|
|
foo: (bar 'A' 'B' 'C')
|
|
|
|
bar +args:
|
|
echo {{args}}
|
|
",
|
|
)
|
|
.stdout("A B C\n")
|
|
.stderr("echo A B C\n")
|
|
.shell(false)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn duplicate_dependency_no_args() {
|
|
Test::new()
|
|
.justfile(
|
|
"
|
|
foo: bar bar bar bar
|
|
|
|
bar:
|
|
echo BAR
|
|
",
|
|
)
|
|
.stdout("BAR\n")
|
|
.stderr("echo BAR\n")
|
|
.shell(false)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn duplicate_dependency_argument() {
|
|
Test::new()
|
|
.justfile(
|
|
"
|
|
foo: (bar 'BAR') (bar `echo BAR`)
|
|
|
|
bar bar:
|
|
echo {{bar}}
|
|
",
|
|
)
|
|
.stdout("BAR\n")
|
|
.stderr("echo BAR\n")
|
|
.shell(false)
|
|
.run();
|
|
}
|
|
|
|
#[cfg(windows)]
|
|
#[test]
|
|
fn pwsh_invocation_directory() {
|
|
Test::new()
|
|
.justfile(
|
|
r#"
|
|
set shell := ["pwsh", "-NoProfile", "-c"]
|
|
|
|
pwd:
|
|
@Test-Path {{invocation_directory()}} > result.txt
|
|
"#,
|
|
)
|
|
.status(EXIT_SUCCESS)
|
|
.shell(false)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn variables() {
|
|
Test::new()
|
|
.arg("--variables")
|
|
.justfile(
|
|
"
|
|
z := 'a'
|
|
a := 'z'
|
|
",
|
|
)
|
|
.stdout("a z\n")
|
|
.shell(false)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn interpolation_evaluation_ignore_quiet() {
|
|
Test::new()
|
|
.justfile(
|
|
r#"
|
|
foo:
|
|
{{"@echo foo 2>/dev/null"}}
|
|
"#,
|
|
)
|
|
.stderr(
|
|
"
|
|
@echo foo 2>/dev/null
|
|
error: Recipe `foo` failed on line 2 with exit code 127
|
|
",
|
|
)
|
|
.status(127)
|
|
.shell(false)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn interpolation_evaluation_ignore_quiet_continuation() {
|
|
Test::new()
|
|
.justfile(
|
|
r#"
|
|
foo:
|
|
{{""}}\
|
|
@echo foo 2>/dev/null
|
|
"#,
|
|
)
|
|
.stderr(
|
|
"
|
|
@echo foo 2>/dev/null
|
|
error: Recipe `foo` failed on line 3 with exit code 127
|
|
",
|
|
)
|
|
.status(127)
|
|
.shell(false)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn brace_escape() {
|
|
Test::new()
|
|
.justfile(
|
|
"
|
|
foo:
|
|
echo '{{{{'
|
|
",
|
|
)
|
|
.stdout("{{\n")
|
|
.stderr(
|
|
"
|
|
echo '{{'
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn brace_escape_extra() {
|
|
Test::new()
|
|
.justfile(
|
|
"
|
|
foo:
|
|
echo '{{{{{'
|
|
",
|
|
)
|
|
.stdout("{{{\n")
|
|
.stderr(
|
|
"
|
|
echo '{{{'
|
|
",
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[test]
|
|
fn multi_line_string_in_interpolation() {
|
|
Test::new()
|
|
.justfile(
|
|
"
|
|
foo:
|
|
echo {{'a
|
|
echo b
|
|
echo c'}}z
|
|
echo baz
|
|
",
|
|
)
|
|
.stdout("a\nb\ncz\nbaz\n")
|
|
.stderr("echo a\n echo b\n echo cz\necho baz\n")
|
|
.run();
|
|
}
|
|
|
|
#[cfg(windows)]
|
|
#[test]
|
|
fn windows_interpreter_path_no_base() {
|
|
Test::new()
|
|
.justfile(
|
|
r#"
|
|
foo:
|
|
#!powershell
|
|
|
|
exit 0
|
|
"#,
|
|
)
|
|
.run();
|
|
}
|