just/src/integration.rs

1540 lines
23 KiB
Rust
Raw Normal View History

2016-10-28 22:25:59 +00:00
extern crate tempdir;
extern crate brev;
2016-11-01 04:53:31 +00:00
extern crate regex;
2016-10-28 22:25:59 +00:00
use tempdir::TempDir;
use std::{env, fs, path, process, str};
2016-10-28 22:25:59 +00:00
fn integration_test(
args: &[&str],
justfile: &str,
expected_status: i32,
expected_stdout: &str,
expected_stderr: &str,
) {
2016-10-30 23:15:18 +00:00
let tmp = TempDir::new("just-integration")
.unwrap_or_else(
|err| panic!("integration test: failed to create temporary directory: {}", err));
2016-10-28 22:25:59 +00:00
let mut path = tmp.path().to_path_buf();
path.push("justfile");
brev::dump(path, justfile);
let mut binary = env::current_dir().unwrap();
binary.push("./target/debug/just");
let output = process::Command::new(binary)
2016-10-28 22:25:59 +00:00
.current_dir(tmp.path())
.args(args)
.output()
.expect("just invocation failed");
2016-10-28 22:25:59 +00:00
let mut failure = false;
let status = output.status.code().unwrap();
if status != expected_status {
println!("bad status: {} != {}", status, expected_status);
failure = true;
}
let stdout = str::from_utf8(&output.stdout).unwrap();
2016-10-28 22:25:59 +00:00
if stdout != expected_stdout {
2016-10-29 02:38:03 +00:00
println!("bad stdout:\ngot:\n{}\n\nexpected:\n{}", stdout, expected_stdout);
2016-10-28 22:25:59 +00:00
failure = true;
}
let stderr = str::from_utf8(&output.stderr).unwrap();
2016-10-28 22:25:59 +00:00
if stderr != expected_stderr {
println!("bad stderr:\ngot:\n{}\n\nexpected:\n{}", stderr, expected_stderr);
2016-10-28 22:25:59 +00:00
failure = true;
}
if failure {
panic!("test failed");
}
}
fn search_test<P: AsRef<path::Path>>(path: P) {
let mut binary = env::current_dir().unwrap();
binary.push("./target/debug/just");
let output = process::Command::new(binary)
.current_dir(path)
.output()
.expect("just invocation failed");
assert_eq!(output.status.code().unwrap(), 0);
let stdout = str::from_utf8(&output.stdout).unwrap();
assert_eq!(stdout, "ok\n");
let stderr = str::from_utf8(&output.stderr).unwrap();
assert_eq!(stderr, "echo ok\n");
}
#[test]
fn test_justfile_search() {
let tmp = TempDir::new("just-test-justfile-search")
.expect("test justfile search: failed to create temporary directory");
let mut path = tmp.path().to_path_buf();
path.push("justfile");
brev::dump(&path, "default:\n\techo ok");
path.pop();
path.push("a");
fs::create_dir(&path).expect("test justfile search: failed to create intermediary directory");
path.push("b");
fs::create_dir(&path).expect("test justfile search: failed to create intermediary directory");
path.push("c");
fs::create_dir(&path).expect("test justfile search: failed to create intermediary directory");
path.push("d");
fs::create_dir(&path).expect("test justfile search: failed to create intermediary directory");
search_test(path);
}
#[test]
fn test_capitalized_justfile_search() {
let tmp = TempDir::new("just-test-justfile-search")
.expect("test justfile search: failed to create temporary directory");
let mut path = tmp.path().to_path_buf();
path.push("Justfile");
brev::dump(&path, "default:\n\techo ok");
path.pop();
path.push("a");
fs::create_dir(&path).expect("test justfile search: failed to create intermediary directory");
path.push("b");
fs::create_dir(&path).expect("test justfile search: failed to create intermediary directory");
path.push("c");
fs::create_dir(&path).expect("test justfile search: failed to create intermediary directory");
path.push("d");
fs::create_dir(&path).expect("test justfile search: failed to create intermediary directory");
search_test(path);
}
#[test]
fn test_capitalization_priority() {
let tmp = TempDir::new("just-test-justfile-search")
.expect("test justfile search: failed to create temporary directory");
let mut path = tmp.path().to_path_buf();
path.push("justfile");
brev::dump(&path, "default:\n\techo ok");
path.pop();
path.push("Justfile");
brev::dump(&path, "default:\n\techo fail");
path.pop();
// if we see "default\n\techo fail" in `justfile` then we're running
// in a case insensitive filesystem, so just bail
path.push("justfile");
if brev::slurp(&path) == "default:\n\techo fail" {
return;
}
path.pop();
path.push("a");
fs::create_dir(&path).expect("test justfile search: failed to create intermediary directory");
path.push("b");
fs::create_dir(&path).expect("test justfile search: failed to create intermediary directory");
path.push("c");
fs::create_dir(&path).expect("test justfile search: failed to create intermediary directory");
path.push("d");
fs::create_dir(&path).expect("test justfile search: failed to create intermediary directory");
search_test(path);
}
2016-10-28 22:25:59 +00:00
#[test]
2016-10-28 22:59:50 +00:00
fn default() {
2016-10-28 22:25:59 +00:00
integration_test(
&[],
2016-10-28 22:59:50 +00:00
"default:\n echo hello\nother: \n echo bar",
2016-10-28 22:25:59 +00:00
0,
"hello\n",
"echo hello\n",
)
}
2016-10-28 22:34:01 +00:00
#[test]
fn quiet() {
integration_test(
&[],
"default:\n @echo hello",
0,
"hello\n",
"",
)
}
#[test]
fn verbose() {
integration_test(
&["--verbose"],
"default:\n @echo hello",
0,
"hello\n",
"===> Running recipe `default`...\necho hello\n",
)
}
2016-10-28 22:34:01 +00:00
#[test]
fn order() {
let text = "
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";
integration_test(
&["a", "d"],
text,
0,
"a\nb\nc\nd\n",
"echo a\necho b\necho c\necho d\n",
);
}
2016-10-28 22:59:50 +00:00
#[test]
fn summary() {
2016-10-28 22:59:50 +00:00
let text =
"b: a
a:
d: c
c: b";
integration_test(
&["--summary"],
2016-10-28 22:59:50 +00:00
text,
0,
"a b c d\n",
"",
);
}
#[test]
fn select() {
let text =
"b:
@echo b
a:
@echo a
d:
@echo d
c:
@echo c";
integration_test(
&["d", "c"],
text,
0,
"d\nc\n",
"",
);
}
#[test]
fn print() {
let text =
"b:
echo b
a:
echo a
d:
echo d
c:
echo c";
integration_test(
&["d", "c"],
text,
0,
"d\nc\n",
"echo d\necho c\n",
);
}
#[test]
fn show() {
let text =
r#"hello = "foo"
bar = hello + hello
recipe:
echo {{hello + "bar" + bar}}"#;
integration_test(
&["--show", "recipe"],
text,
0,
r#"recipe:
echo {{hello + "bar" + bar}}
"#,
"",
);
}
2016-10-29 02:38:03 +00:00
2016-10-28 22:59:50 +00:00
#[test]
fn status_passthrough() {
2016-10-28 22:59:50 +00:00
let text =
"
hello:
2016-10-28 22:59:50 +00:00
recipe:
@exit 100";
2016-10-28 22:59:50 +00:00
integration_test(
&["recipe"],
2016-10-28 22:59:50 +00:00
text,
100,
"",
"error: Recipe `recipe` failed on line 6 with exit code 100\n",
2016-10-28 22:59:50 +00:00
);
}
2016-10-29 03:34:25 +00:00
#[test]
fn error() {
integration_test(
&[],
"bar:\nhello:\nfoo: bar baaaaaaaz hello",
255,
"",
2016-10-29 03:40:16 +00:00
"error: recipe `foo` has unknown dependency `baaaaaaaz`
2016-10-29 03:34:25 +00:00
|
3 | foo: bar baaaaaaaz hello
| ^^^^^^^^^
",
);
}
#[test]
fn backtick_success() {
integration_test(
&[],
"a = `printf Hello,`\nbar:\n printf '{{a + `printf ' world!'`}}'",
0,
"Hello, world!",
"printf 'Hello, world!'\n",
);
}
#[test]
fn backtick_trimming() {
integration_test(
&[],
"a = `echo Hello,`\nbar:\n echo '{{a + `echo ' world!'`}}'",
0,
"Hello, world!\n",
"echo 'Hello, world!'\n",
);
}
2016-10-30 08:27:05 +00:00
#[test]
fn backtick_code_assignment() {
integration_test(
&[],
"b = a\na = `exit 100`\nbar:\n echo '{{`exit 200`}}'",
2016-10-30 08:27:05 +00:00
100,
"",
"error: backtick failed with exit code 100
2016-10-30 08:27:05 +00:00
|
2 | a = `exit 100`
| ^^^^^^^^^^
2016-10-30 08:27:05 +00:00
",
);
}
#[test]
fn backtick_code_interpolation() {
integration_test(
&[],
"b = a\na = `echo hello`\nbar:\n echo '{{`exit 200`}}'",
2016-10-30 08:27:05 +00:00
200,
"",
"error: backtick failed with exit code 200
2016-10-30 08:27:05 +00:00
|
4 | echo '{{`exit 200`}}'
| ^^^^^^^^^^
2016-10-30 08:27:05 +00:00
",
);
}
#[test]
fn backtick_code_interpolation_tab() {
integration_test(
&[],
"
backtick-fail:
\techo {{`exit 1`}}
",
1,
"",
"error: backtick failed with exit code 1
|
3 | echo {{`exit 1`}}
| ^^^^^^^^
",
);
}
#[test]
fn backtick_code_interpolation_tabs() {
integration_test(
&[],
"
backtick-fail:
\techo {{\t`exit 1`}}
",
1,
"",
"error: backtick failed with exit code 1
|
3 | echo {{ `exit 1`}}
| ^^^^^^^^
",
);
}
#[test]
fn backtick_code_interpolation_inner_tab() {
integration_test(
&[],
"
backtick-fail:
\techo {{\t`exit\t\t1`}}
",
1,
"",
"error: backtick failed with exit code 1
|
3 | echo {{ `exit 1`}}
| ^^^^^^^^^^^^^^^
",
);
}
#[test]
fn backtick_code_interpolation_leading_emoji() {
integration_test(
&[],
"
backtick-fail:
\techo 😬{{`exit 1`}}
",
1,
"",
"error: backtick failed with exit code 1
|
3 | echo 😬{{`exit 1`}}
| ^^^^^^^^
",
);
}
#[test]
fn backtick_code_interpolation_unicode_hell() {
integration_test(
&[],
"
backtick-fail:
\techo \t\t\t😬{{\t\t`exit 1 # \t\t\t😬`}}\t\t\t😬
",
1,
"",
"error: backtick failed with exit code 1
|
3 | echo 😬{{ `exit 1 # 😬`}} 😬
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
",
);
}
#[test]
fn backtick_code_long() {
integration_test(
&[],
"\n\n\n\n\n\nb = a\na = `echo hello`\nbar:\n echo '{{`exit 200`}}'",
200,
"",
"error: backtick failed with exit code 200
|
10 | echo '{{`exit 200`}}'
| ^^^^^^^^^^
",
);
}
2016-10-30 08:27:05 +00:00
#[test]
fn shebang_backtick_failure() {
integration_test(
&[],
"foo:
#!/bin/sh
echo hello
echo {{`exit 123`}}",
123,
"",
"error: backtick failed with exit code 123
2016-10-30 08:27:05 +00:00
|
4 | echo {{`exit 123`}}
| ^^^^^^^^^^
",
);
}
#[test]
fn command_backtick_failure() {
integration_test(
&[],
"foo:
echo hello
echo {{`exit 123`}}",
123,
"hello\n",
"echo hello\nerror: backtick failed with exit code 123
2016-10-30 08:27:05 +00:00
|
3 | echo {{`exit 123`}}
| ^^^^^^^^^^
",
);
}
#[test]
fn assignment_backtick_failure() {
integration_test(
&[],
"foo:
echo hello
echo {{`exit 111`}}
a = `exit 222`",
222,
"",
"error: backtick failed with exit code 222
2016-10-30 08:27:05 +00:00
|
4 | a = `exit 222`
| ^^^^^^^^^^
",
);
}
2016-10-30 10:08:28 +00:00
#[test]
fn unknown_override_options() {
integration_test(
&["--set", "foo", "bar", "a", "b", "--set", "baz", "bob", "--set", "a", "b"],
"foo:
echo hello
echo {{`exit 111`}}
a = `exit 222`",
255,
"",
2016-11-12 17:28:30 +00:00
"error: Variables `baz` and `foo` overridden on the command line but not present \
in justfile\n",
2016-10-30 10:08:28 +00:00
);
}
#[test]
fn unknown_override_args() {
integration_test(
&["foo=bar", "baz=bob", "a=b", "a", "b"],
"foo:
echo hello
echo {{`exit 111`}}
a = `exit 222`",
255,
"",
2016-11-12 17:28:30 +00:00
"error: Variables `baz` and `foo` overridden on the command line but not present \
in justfile\n",
);
}
#[test]
fn unknown_override_arg() {
integration_test(
&["foo=bar", "a=b", "a", "b"],
"foo:
echo hello
echo {{`exit 111`}}
a = `exit 222`",
255,
"",
"error: Variable `foo` overridden on the command line but not present in justfile\n",
2016-10-30 10:08:28 +00:00
);
}
#[test]
fn overrides_first() {
integration_test(
&["foo=bar", "a=b", "recipe", "baz=bar"],
r#"
foo = "foo"
a = "a"
baz = "baz"
2016-10-30 10:08:28 +00:00
recipe arg:
echo arg={{arg}}
echo {{foo + a + baz}}"#,
0,
"arg=baz=bar\nbarbbaz\n",
"echo arg=baz=bar\necho barbbaz\n",
);
}
2016-10-30 20:14:39 +00:00
#[test]
fn overrides_not_evaluated() {
integration_test(
&["foo=bar", "a=b", "recipe", "baz=bar"],
r#"
foo = `exit 1`
a = "a"
baz = "baz"
recipe arg:
echo arg={{arg}}
echo {{foo + a + baz}}"#,
0,
"arg=baz=bar\nbarbbaz\n",
"echo arg=baz=bar\necho barbbaz\n",
);
}
2016-10-30 20:14:39 +00:00
#[test]
fn dry_run() {
integration_test(
&["--dry-run", "shebang", "command"],
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`}}"#,
0,
"",
"stderr
#!/bin/sh
touch /this/is/not/a/file
backtick
echo shebang interpolation
touch /this/is/not/a/file
backtick
echo command interpolation
",
);
}
#[test]
fn evaluate() {
integration_test(
&["--evaluate"],
r#"
foo = "a\t"
hello = "c"
2016-10-30 20:14:39 +00:00
bar = "b\t"
ab = foo + bar + hello
2016-10-30 20:14:39 +00:00
wut:
touch /this/is/not/a/file
"#,
0,
r#"ab = "a b c"
bar = "b "
foo = "a "
hello = "c"
2016-10-30 20:14:39 +00:00
"#,
"",
);
}
2016-10-30 23:15:18 +00:00
#[test]
fn export_success() {
integration_test(
&[],
r#"
export foo = "a"
baz = "c"
export bar = "b"
export abc = foo + bar + baz
wut:
echo $foo $bar $abc
"#,
0,
"a b abc\n",
"echo $foo $bar $abc\n",
);
}
#[test]
fn export_override() {
integration_test(
&["foo=hello", "--set", "bar", "bye"],
r#"
export foo = "a"
baz = "c"
export bar = "b"
export abc = foo + "-" + bar + "-" + baz
wut:
echo $foo $bar $abc
"#,
0,
"hello bye hello-bye-c\n",
"echo $foo $bar $abc\n",
);
}
2016-10-30 23:15:18 +00:00
#[test]
fn export_shebang() {
integration_test(
&[],
r#"
export foo = "a"
baz = "c"
export bar = "b"
export abc = foo + bar + baz
wut:
#!/bin/sh
echo $foo $bar $abc
"#,
0,
"a b abc\n",
"",
);
}
#[test]
fn export_recipe_backtick() {
integration_test(
&[],
r#"
export exported_variable = "A-IS-A"
recipe:
echo {{`echo recipe $exported_variable`}}
"#,
0,
"recipe A-IS-A\n",
"echo recipe A-IS-A\n",
);
}
2016-10-30 23:56:22 +00:00
#[test]
fn raw_string() {
integration_test(
&[],
r#"
export exported_variable = '\\\\\\"'
recipe:
echo {{`echo recipe $exported_variable`}}
"#,
0,
"recipe \\\"\n",
"echo recipe \\\\\\\"\n",
);
}
2016-11-01 04:53:31 +00:00
#[test]
fn line_error_spacing() {
integration_test(
&[],
r#"
???
"#,
255,
"",
"error: unknown start of token:
|
10 | ???
| ^
",
);
}
#[test]
fn quiet_flag_no_stdout() {
integration_test(
&["--quiet"],
r#"
default:
@echo hello
"#,
0,
"",
"",
);
}
#[test]
fn quiet_flag_no_stderr() {
integration_test(
&["--quiet"],
r#"
default:
@echo hello 1>&2
"#,
0,
"",
"",
);
}
#[test]
fn quiet_flag_no_command_echoing() {
integration_test(
&["--quiet"],
r#"
default:
exit
"#,
0,
"",
"",
);
}
#[test]
fn quiet_flag_no_error_messages() {
integration_test(
&["--quiet"],
r#"
default:
exit 100
"#,
100,
"",
"",
);
}
#[test]
fn quiet_flag_no_assignment_backtick_stderr() {
integration_test(
&["--quiet"],
r#"
a = `echo hello 1>&2`
default:
exit 100
"#,
100,
"",
"",
);
}
#[test]
fn quiet_flag_no_interpolation_backtick_stderr() {
integration_test(
&["--quiet"],
r#"
default:
echo `echo hello 1>&2`
exit 100
"#,
100,
"",
"",
);
}
#[test]
fn quiet_flag_or_dry_run_flag() {
integration_test(
&["--quiet", "--dry-run"],
"",
1,
"",
"error: The argument '--dry-run' cannot be used with '--quiet'
USAGE:
just --quiet --color <color>
For more information try --help\n",
);
}
#[test]
fn argument_single() {
integration_test(
&["foo", "ARGUMENT"],
"
foo A:
echo {{A}}
",
0,
"ARGUMENT\n",
"echo ARGUMENT\n",
);
}
#[test]
fn argument_multiple() {
integration_test(
&["foo", "ONE", "TWO"],
"
foo A B:
echo A:{{A}} B:{{B}}
",
0,
"A:ONE B:TWO\n",
"echo A:ONE B:TWO\n",
);
}
#[test]
fn argument_mismatch_more() {
integration_test(
&["foo", "ONE", "TWO", "THREE"],
"
foo A B:
echo A:{{A}} B:{{B}}
",
255,
"",
"error: Recipe `foo` got 3 arguments but only takes 2\n",
);
}
#[test]
fn argument_mismatch_fewer() {
integration_test(
&["foo", "ONE"],
"
foo A B:
echo A:{{A}} B:{{B}}
",
255,
"",
"error: Recipe `foo` got 1 argument but takes 2\n"
);
}
#[test]
fn argument_mismatch_more_with_default() {
integration_test(
&["foo", "ONE", "TWO", "THREE"],
"
foo A B='B':
echo A:{{A}} B:{{B}}
",
255,
"",
"error: Recipe `foo` got 3 arguments but takes at most 2\n",
);
}
#[test]
fn argument_mismatch_fewer_with_default() {
integration_test(
&["foo", "bar"],
"
foo A B C='C':
echo A:{{A}} B:{{B}} C:{{C}}
",
255,
"",
"error: Recipe `foo` got 1 argument but takes at least 2\n",
);
}
#[test]
fn unknown_recipe() {
integration_test(
&["foo"],
"hello:",
255,
"",
"error: Justfile does not contain recipe `foo`.\n",
);
}
#[test]
fn unknown_recipes() {
integration_test(
&["foo", "bar"],
"hello:",
255,
"",
"error: Justfile does not contain recipes `foo` or `bar`.\n",
);
}
#[test]
fn color_always() {
integration_test(
&["--color", "always"],
"b = a\na = `exit 100`\nbar:\n echo '{{`exit 200`}}'",
100,
"",
2016-11-12 17:28:30 +00:00
"\u{1b}[1;31merror:\u{1b}[0m \u{1b}[1mbacktick failed with exit code 100
\u{1b}[0m |\n2 | a = `exit 100`\n | \u{1b}[1;31m^^^^^^^^^^\u{1b}[0m\n",
);
}
#[test]
fn color_never() {
integration_test(
&["--color", "never"],
"b = a\na = `exit 100`\nbar:\n echo '{{`exit 200`}}'",
100,
"",
"error: backtick failed with exit code 100
|
2 | a = `exit 100`
| ^^^^^^^^^^
",
);
}
#[test]
fn color_auto() {
integration_test(
&["--color", "auto"],
"b = a\na = `exit 100`\nbar:\n echo '{{`exit 200`}}'",
100,
"",
"error: backtick failed with exit code 100
|
2 | a = `exit 100`
| ^^^^^^^^^^
",
);
}
#[test]
fn colors_no_context() {
let text ="
recipe:
@exit 100";
integration_test(
&["--color=always"],
text,
100,
"",
"\u{1b}[1;31merror:\u{1b}[0m \u{1b}[1m\
Recipe `recipe` failed on line 3 with exit code 100\u{1b}[0m\n",
);
}
#[test]
fn dump() {
let text = r#"
# this recipe does something
recipe a b +d:
@exit 100"#;
integration_test(
&["--dump"],
text,
0,
"# this recipe does something
recipe a b +d:
@exit 100
",
"",
);
}
#[test]
fn required_after_default() {
integration_test(
&[],
"bar:\nhello baz arg='foo' bar:",
255,
"",
"error: non-default parameter `bar` follows default parameter
|
2 | hello baz arg='foo' bar:
| ^^^
",
);
}
#[test]
fn use_string_default() {
integration_test(
&["hello", "ABC"],
r#"
bar:
hello baz arg="XYZ\t\" ":
echo '{{baz}}...{{arg}}'
"#,
0,
"ABC...XYZ\t\"\t\n",
"echo 'ABC...XYZ\t\"\t'\n",
);
}
#[test]
fn use_raw_string_default() {
integration_test(
&["hello", "ABC"],
r#"
bar:
hello baz arg='XYZ\t\" ':
echo '{{baz}}...{{arg}}'
"#,
0,
"ABC...XYZ\t\\\"\t\n",
"echo 'ABC...XYZ\\t\\\"\t'\n",
);
}
#[test]
fn supply_use_default() {
integration_test(
&["hello", "0", "1"],
r#"
hello a b='B' c='C':
echo {{a}} {{b}} {{c}}
"#,
0,
"0 1 C\n",
"echo 0 1 C\n",
);
}
#[test]
fn supply_defaults() {
integration_test(
&["hello", "0", "1", "2"],
r#"
hello a b='B' c='C':
echo {{a}} {{b}} {{c}}
"#,
0,
"0 1 2\n",
"echo 0 1 2\n",
);
}
#[test]
fn list() {
integration_test(
&["--list"],
r#"
# this does a thing
hello a b='B ' c='C':
echo {{a}} {{b}} {{c}}
# this comment will be ignored
a Z="\t z":
"#,
0,
r"Available recipes:
a Z='\t z'
hello a b='B\t' c='C' # this does a thing
",
"",
);
}
#[test]
fn show_suggestion() {
integration_test(
&["--show", "hell"],
r#"
hello a b='B ' c='C':
echo {{a}} {{b}} {{c}}
a Z="\t z":
"#,
255,
"",
"Justfile does not contain recipe `hell`.\nDid you mean `hello`?\n",
);
}
#[test]
fn show_no_suggestion() {
integration_test(
&["--show", "hell"],
r#"
helloooooo a b='B ' c='C':
echo {{a}} {{b}} {{c}}
a Z="\t z":
"#,
255,
"",
"Justfile does not contain recipe `hell`.\n",
);
}
#[test]
fn run_suggestion() {
integration_test(
&["hell"],
r#"
hello a b='B ' c='C':
echo {{a}} {{b}} {{c}}
a Z="\t z":
"#,
255,
"",
"error: Justfile does not contain recipe `hell`.\nDid you mean `hello`?\n",
);
}
#[test]
fn line_continuation_with_space() {
integration_test(
&[],
r#"
foo:
echo a\
b \
c
"#,
0,
"a b c\n",
"echo a b c\n",
);
}
#[test]
fn line_continuation_with_quoted_space() {
integration_test(
&[],
r#"
foo:
echo 'a\
b \
c'
"#,
0,
"a b c\n",
"echo 'a b c'\n",
);
}
#[test]
fn line_continuation_no_space() {
integration_test(
&[],
r#"
foo:
echo a\
b\
c
"#,
0,
"abc\n",
"echo abc\n",
);
}
#[test]
fn quiet_recipe() {
integration_test(
&[],
r#"
@quiet:
# a
# b
@echo c
"#,
0,
"c\n",
"echo c\n",
);
}
#[test]
fn quiet_shebang_recipe() {
integration_test(
&[],
r#"
@quiet:
#!/bin/sh
echo hello
"#,
0,
"hello\n",
"#!/bin/sh\necho hello\n",
);
}
#[test]
fn shebang_line_numbers() {
integration_test(
&[],
r#"
quiet:
#!/usr/bin/env cat
a
b
c
"#,
0,
"#!/usr/bin/env cat
a
b
c
",
"",
);
}
#[test]
fn complex_dependencies() {
integration_test(
&["b"],
r#"
a: b
b:
c: b a
"#,
0,
"",
""
);
}
#[test]
fn long_circular_recipe_dependency() {
integration_test(
&["a"],
"a: b\nb: c\nc: d\nd: a",
255,
"",
"error: recipe `d` has circular dependency `a -> b -> c -> d -> a`
|
4 | d: a
| ^
",
);
}
#[test]
fn multiline_raw_string() {
integration_test(
&["a"],
"
string = 'hello
whatever'
a:
echo '{{string}}'
",
0,
"hello
whatever
",
"echo 'hello
whatever'
",
);
}
#[test]
fn error_line_after_multiline_raw_string() {
integration_test(
&["a"],
"
string = 'hello
whatever' + 'yo'
a:
echo '{{foo}}'
",
255,
"",
"error: variable `foo` not defined
|
7 | echo '{{foo}}'
| ^^^
",
);
}
#[test]
fn error_column_after_multiline_raw_string() {
integration_test(
&["a"],
"
string = 'hello
whatever' + bar
a:
echo '{{string}}'
",
255,
"",
"error: variable `bar` not defined
|
4 | whatever' + bar
| ^^^
",
);
}
#[test]
fn multiline_raw_string_in_interpolation() {
integration_test(
&["a"],
r#"
a:
echo '{{"a" + '
' + "b"}}'
"#,
0,
"a
b
",
"echo 'a
b'
",
);
}
#[test]
fn error_line_after_multiline_raw_string_in_interpolation() {
integration_test(
&["a"],
r#"
a:
echo '{{"a" + '
' + "b"}}'
echo {{b}}
"#,
255,
"",
"error: variable `b` not defined
|
6 | echo {{b}}
| ^
",
);
}
#[test]
fn unterminated_raw_string() {
integration_test(
&["a"],
"
a b=':
",
255,
"",
"error: unterminated string
|
2 | a b=':
| ^
",
);
}
#[test]
fn unterminated_string() {
integration_test(
&["a"],
r#"
a b=":
"#,
255,
"",
r#"error: unterminated string
|
2 | a b=":
| ^
"#,
);
}
#[test]
fn variadic_recipe() {
integration_test(
&["a", "0", "1", "2", "3", " 4 "],
"
a x y +z:
echo {{x}} {{y}} {{z}}
",
0,
"0 1 2 3 4\n",
"echo 0 1 2 3 4 \n",
);
}
#[test]
fn variadic_ignore_default() {
integration_test(
&["a", "0", "1", "2", "3", " 4 "],
"
a x y +z='HELLO':
echo {{x}} {{y}} {{z}}
",
0,
"0 1 2 3 4\n",
"echo 0 1 2 3 4 \n",
);
}
#[test]
fn variadic_use_default() {
integration_test(
&["a", "0", "1"],
"
a x y +z='HELLO':
echo {{x}} {{y}} {{z}}
",
0,
"0 1 HELLO\n",
"echo 0 1 HELLO\n",
);
}
#[test]
fn variadic_too_few() {
integration_test(
&["a", "0", "1"],
"
a x y +z:
echo {{x}} {{y}} {{z}}
",
255,
"",
"error: Recipe `a` got 2 arguments but takes at least 3\n",
);
}