From 792b7a249cb4ed2878242e20bf1de7b0fb2cbf9f Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Tue, 16 Apr 2019 19:52:16 -0700 Subject: [PATCH] Pass stdin handle to backtick process (#409) --- src/assignment_evaluator.rs | 4 +- tests/integration.rs | 271 ++++++++++++++++++++++++++++++++++-- 2 files changed, 266 insertions(+), 9 deletions(-) diff --git a/src/assignment_evaluator.rs b/src/assignment_evaluator.rs index 5bdb649..5b6a204 100644 --- a/src/assignment_evaluator.rs +++ b/src/assignment_evaluator.rs @@ -141,9 +141,11 @@ impl<'a, 'b> AssignmentEvaluator<'a, 'b> { ) -> RunResult<'a, String> { let mut cmd = Command::new(self.shell); + cmd.arg("-cu").arg(raw); + cmd.export_environment_variables(self.scope, dotenv, self.exports)?; - cmd.arg("-cu").arg(raw); + cmd.stdin(process::Stdio::inherit()); cmd.stderr(if self.quiet { process::Stdio::null() diff --git a/tests/integration.rs b/tests/integration.rs index a346d85..395858d 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -1,6 +1,11 @@ use executable_path::executable_path; use libc::{EXIT_FAILURE, EXIT_SUCCESS}; -use std::{env, fs, process, str}; +use std::{ + env, fs, + io::Write, + process::{Command, Stdio}, + str, +}; use tempdir::TempDir; /// Instantiate integration tests for a given test case using @@ -15,6 +20,7 @@ macro_rules! integration_test { name: $name:ident, justfile: $text:tt, args: ($($arg:tt)*), + stdin: $stdin:expr, stdout: $stdout:expr, stderr: $stderr:expr, status: $status:expr, @@ -25,9 +31,9 @@ macro_rules! integration_test { // silence unused import warnings const __: i32 = EXIT_SUCCESS; - #[test] fn sh() { integration_test("sh", $text, &[$($arg)*], $stdout, $stderr, $status); } - #[test] fn dash() { integration_test("dash", $text, &[$($arg)*], $stdout, $stderr, $status); } - #[test] fn bash() { integration_test("bash", $text, &[$($arg)*], $stdout, $stderr, $status); } + #[test] fn sh() { integration_test("sh", $text, &[$($arg)*], $stdin, $stdout, $stderr, $status); } + #[test] fn dash() { integration_test("dash", $text, &[$($arg)*], $stdin, $stdout, $stderr, $status); } + #[test] fn bash() { integration_test("bash", $text, &[$($arg)*], $stdin, $stdout, $stderr, $status); } } } } @@ -36,6 +42,7 @@ fn integration_test( shell: &str, justfile: &str, args: &[&str], + stdin: &str, expected_stdout: &str, expected_stderr: &str, expected_status: i32, @@ -55,13 +62,28 @@ fn integration_test( dotenv_path.push(".env"); brev::dump(dotenv_path, "DOTENV_KEY=dotenv-value"); - let output = process::Command::new(&executable_path("just")) + let mut child = Command::new(&executable_path("just")) .current_dir(tmp.path()) .args(&["--shell", shell]) .args(args) - .output() + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() .expect("just invocation failed"); + { + let mut stdin_handle = child.stdin.take().expect("failed to unwrap stdin handle"); + + stdin_handle + .write_all(stdin.as_bytes()) + .expect("failed to write stdin to just process"); + } + + let output = child + .wait_with_output() + .expect("failed to wait for just process"); + let mut failure = false; let status = output.status.code().unwrap(); @@ -95,7 +117,7 @@ fn integration_test( if expected_status == EXIT_SUCCESS { println!("Reparsing..."); - let output = process::Command::new(&executable_path("just")) + let output = Command::new(&executable_path("just")) .current_dir(tmp.path()) .arg("--dump") .output() @@ -111,7 +133,7 @@ fn integration_test( fs::write(&reparsed_path, &dumped).unwrap(); - let output = process::Command::new(&executable_path("just")) + let output = Command::new(&executable_path("just")) .current_dir(tmp.path()) .arg("--justfile") .arg(&reparsed_path) @@ -137,6 +159,7 @@ integration_test! { name: alias_listing, justfile: "foo:\n echo foo\nalias f = foo", args: ("--list"), + stdin: "", stdout: "Available recipes: foo f # alias for `foo` @@ -149,6 +172,7 @@ integration_test! { name: alias_listing_multiple_aliases, justfile: "foo:\n echo foo\nalias f = foo\nalias fo = foo", args: ("--list"), + stdin: "", stdout: "Available recipes: foo f # alias for `foo` @@ -162,6 +186,7 @@ integration_test! { name: alias_listing_parameters, justfile: "foo PARAM='foo':\n echo {{PARAM}}\nalias f = foo", args: ("--list"), + stdin: "", stdout: "Available recipes: foo PARAM='foo' f PARAM='foo' # alias for `foo` @@ -174,6 +199,7 @@ integration_test! { name: alias_listing_private, justfile: "foo PARAM='foo':\n echo {{PARAM}}\nalias _f = foo", args: ("--list"), + stdin: "", stdout: "Available recipes: foo PARAM='foo' ", @@ -185,6 +211,7 @@ integration_test! { name: alias, justfile: "foo:\n echo foo\nalias f = foo", args: ("f"), + stdin: "", stdout: "foo\n", stderr: "echo foo\n", status: EXIT_SUCCESS, @@ -194,6 +221,7 @@ integration_test! { name: alias_with_parameters, justfile: "foo value='foo':\n echo {{value}}\nalias f = foo", args: ("f", "bar"), + stdin: "", stdout: "bar\n", stderr: "echo bar\n", status: EXIT_SUCCESS, @@ -203,6 +231,7 @@ integration_test! { name: alias_with_dependencies, justfile: "foo:\n echo foo\nbar: foo\nalias b = bar", args: ("b"), + stdin: "", stdout: "foo\n", stderr: "echo foo\n", status: EXIT_SUCCESS, @@ -212,6 +241,7 @@ integration_test! { name: duplicate_alias, justfile: "alias foo = bar\nalias foo = baz\n", args: (), + stdin: "", stdout: "" , stderr: "error: Alias `foo` first defined on line `1` is redefined on line `2` | @@ -225,6 +255,7 @@ integration_test! { name: unknown_alias_target, justfile: "alias foo = bar\n", args: (), + stdin: "", stdout: "", stderr: "error: Alias `foo` has an unknown target `bar` | @@ -238,6 +269,7 @@ integration_test! { name: alias_shadows_recipe, justfile: "bar:\n echo bar\nalias foo = bar\nfoo:\n echo foo", args: (), + stdin: "", stdout: "", stderr: "error: Alias `foo` defined on `3` shadows recipe defined on `4` | @@ -251,6 +283,7 @@ integration_test! { name: default, justfile: "default:\n echo hello\nother: \n echo bar", args: (), + stdin: "", stdout: "hello\n", stderr: "echo hello\n", status: EXIT_SUCCESS, @@ -260,6 +293,7 @@ integration_test! { name: quiet, justfile: "default:\n @echo hello", args: (), + stdin: "", stdout: "hello\n", stderr: "", status: EXIT_SUCCESS, @@ -269,6 +303,7 @@ integration_test! { name: verbose, justfile: "default:\n @echo hello", args: ("--verbose"), + stdin: "", stdout: "hello\n", stderr: "===> Running recipe `default`...\necho hello\n", status: EXIT_SUCCESS, @@ -294,6 +329,7 @@ c: b echo c @mv b c", args: ("a", "d"), + stdin: "", stdout: "a\nb\nc\nd\n", stderr: "echo a\necho b\necho c\necho d\n", status: EXIT_SUCCESS, @@ -309,6 +345,7 @@ _z: _y _y: ", args: ("--summary"), + stdin: "", stdout: "a b c d\n", stderr: "", status: EXIT_SUCCESS, @@ -325,6 +362,7 @@ d: c: @echo c", args: ("d", "c"), + stdin: "", stdout: "d\nc\n", stderr: "", status: EXIT_SUCCESS, @@ -341,6 +379,7 @@ d: c: echo c", args: ("d", "c"), + stdin: "", stdout: "d\nc\n", stderr: "echo d\necho c\n", status: EXIT_SUCCESS, @@ -353,6 +392,7 @@ bar = hello + hello recipe: echo {{hello + "bar" + bar}}"#, args: ("--show", "recipe"), + stdin: "", stdout: r#"recipe: echo {{hello + "bar" + bar}} "#, @@ -369,6 +409,7 @@ hello: recipe: @exit 100", args: ("recipe"), + stdin: "", stdout: "", stderr: "error: Recipe `recipe` failed on line 6 with exit code 100\n", status: 100, @@ -378,6 +419,7 @@ integration_test! { name: unknown_dependency, justfile: "bar:\nhello:\nfoo: bar baaaaaaaz hello", args: (), + stdin: "", stdout: "", stderr: "error: Recipe `foo` has unknown dependency `baaaaaaaz` | @@ -391,6 +433,7 @@ integration_test! { name: backtick_success, justfile: "a = `printf Hello,`\nbar:\n printf '{{a + `printf ' world.'`}}'", args: (), + stdin: "", stdout: "Hello, world.", stderr: "printf 'Hello, world.'\n", status: EXIT_SUCCESS, @@ -400,6 +443,7 @@ integration_test! { name: backtick_trimming, justfile: "a = `echo Hello,`\nbar:\n echo '{{a + `echo ' world.'`}}'", args: (), + stdin: "", stdout: "Hello, world.\n", stderr: "echo 'Hello, world.'\n", status: EXIT_SUCCESS, @@ -409,6 +453,7 @@ integration_test! { name: backtick_code_assignment, justfile: "b = a\na = `exit 100`\nbar:\n echo '{{`exit 200`}}'", args: (), + stdin: "", stdout: "", stderr: "error: Backtick failed with exit code 100 | @@ -422,6 +467,7 @@ integration_test! { name: backtick_code_interpolation, justfile: "b = a\na = `echo hello`\nbar:\n echo '{{`exit 200`}}'", args: (), + stdin: "", stdout: "", stderr: "error: Backtick failed with exit code 200 | @@ -436,6 +482,7 @@ integration_test! { name: backtick_code_interpolation_mod, justfile: "f:\n η„‘{{`exit 200`}}", args: (), + stdin: "", stdout: "", stderr: "error: Backtick failed with exit code 200 | @@ -452,6 +499,7 @@ backtick-fail: \techo {{`exit 1`}} ", args: (), + stdin: "", stdout: "", stderr: "error: Backtick failed with exit code 1 | @@ -468,6 +516,7 @@ backtick-fail: \techo {{\t`exit 1`}} ", args: (), + stdin: "", stdout: "", stderr: "error: Backtick failed with exit code 1 | @@ -484,6 +533,7 @@ backtick-fail: \techo {{\t`exit\t\t1`}} ", args: (), + stdin: "", stdout: "", stderr: "error: Backtick failed with exit code 1 | @@ -500,6 +550,7 @@ backtick-fail: \techo 😬{{`exit 1`}} ", args: (), + stdin: "", stdout: "", stderr: "error: Backtick failed with exit code 1 | @@ -516,6 +567,7 @@ backtick-fail: \techo \t\t\t😬鎌鼬{{\t\t`exit 1 # \t\t\tabc`}}\t\t\t😬鎌鼬 ", args: (), + stdin: "", stdout: "", stderr: "error: Backtick failed with exit code 1 | @@ -529,6 +581,7 @@ integration_test! { name: backtick_code_long, justfile: "\n\n\n\n\n\nb = a\na = `echo hello`\nbar:\n echo '{{`exit 200`}}'", args: (), + stdin: "", stdout: "", stderr: "error: Backtick failed with exit code 200 | @@ -545,6 +598,7 @@ integration_test! { echo hello echo {{`exit 123`}}", args: (), + stdin: "", stdout: "", stderr: "error: Backtick failed with exit code 123 | @@ -560,6 +614,7 @@ integration_test! { echo hello echo {{`exit 123`}}", args: (), + stdin: "", stdout: "hello\n", stderr: "echo hello\nerror: Backtick failed with exit code 123 | @@ -576,6 +631,7 @@ integration_test! { echo {{`exit 111`}} a = `exit 222`", args: (), + stdin: "", stdout: "", stderr: "error: Backtick failed with exit code 222 | @@ -592,6 +648,7 @@ integration_test! { echo {{`exit 111`}} a = `exit 222`", args: ("--set", "foo", "bar", "--set", "baz", "bob", "--set", "a", "b", "a", "b"), + stdin: "", stdout: "", stderr: "error: Variables `baz` and `foo` overridden on the command line but not present \ in justfile\n", @@ -605,6 +662,7 @@ integration_test! { echo {{`exit 111`}} a = `exit 222`", args: ("foo=bar", "baz=bob", "a=b", "a", "b"), + stdin: "", stdout: "", stderr: "error: Variables `baz` and `foo` overridden on the command line but not present \ in justfile\n", @@ -618,6 +676,7 @@ integration_test! { echo {{`exit 111`}} a = `exit 222`", args: ("foo=bar", "a=b", "a", "b"), + stdin: "", stdout: "", stderr: "error: Variable `foo` overridden on the command line but not present in justfile\n", status: EXIT_FAILURE, @@ -634,6 +693,7 @@ recipe arg: echo arg={{arg}} echo {{foo + a + baz}}"#, args: ("foo=bar", "a=b", "recipe", "baz=bar"), + stdin: "", stdout: "arg=baz=bar\nbarbbaz\n", stderr: "echo arg=baz=bar\necho barbbaz\n", status: EXIT_SUCCESS, @@ -650,6 +710,7 @@ recipe arg: echo arg={{arg}} echo {{foo + a + baz}}"#, args: ("foo=bar", "a=b", "recipe", "baz=bar"), + stdin: "", stdout: "arg=baz=bar\nbarbbaz\n", stderr: "echo arg=baz=bar\necho barbbaz\n", status: EXIT_SUCCESS, @@ -671,6 +732,7 @@ shebang: {{var}} echo {{`echo shebang interpolation`}}"#, args: ("--dry-run", "shebang", "command"), + stdin: "", stdout: "", stderr: "#!/bin/sh touch /this/is/not/a/file @@ -695,6 +757,7 @@ wut: touch /this/is/not/a/file "#, args: ("--evaluate"), + stdin: "", stdout: r#"ab = "a b c" bar = "b " foo = "a " @@ -716,6 +779,7 @@ wut: echo $FOO $BAR $ABC "#, args: (), + stdin: "", stdout: "a b abc\n", stderr: "echo $FOO $BAR $ABC\n", status: EXIT_SUCCESS, @@ -733,6 +797,7 @@ wut: echo $FOO $BAR $ABC "#, args: ("--set", "BAR", "bye", "FOO=hello"), + stdin: "", stdout: "hello bye hello-bye-c\n", stderr: "echo $FOO $BAR $ABC\n", status: EXIT_SUCCESS, @@ -751,6 +816,7 @@ wut: echo $FOO $BAR $ABC "#, args: (), + stdin: "", stdout: "a b abc\n", stderr: "", status: EXIT_SUCCESS, @@ -765,6 +831,7 @@ recipe: echo {{`echo recipe $EXPORTED_VARIABLE`}} "#, args: (), + stdin: "", stdout: "recipe A-IS-A\n", stderr: "echo recipe A-IS-A\n", status: EXIT_SUCCESS, @@ -779,6 +846,7 @@ recipe: printf "$EXPORTED_VARIABLE" "#, args: (), + stdin: "", stdout: "\\z", stderr: "printf \"$EXPORTED_VARIABLE\"\n", status: EXIT_SUCCESS, @@ -798,6 +866,7 @@ integration_test! { ??? "#, args: (), + stdin: "", stdout: "", stderr: "error: Unknown start of token: | @@ -814,6 +883,7 @@ default: @echo hello "#, args: ("--quiet"), + stdin: "", stdout: "", stderr: "", status: EXIT_SUCCESS, @@ -826,6 +896,7 @@ default: @echo hello 1>&2 "#, args: ("--quiet"), + stdin: "", stdout: "", stderr: "", status: EXIT_SUCCESS, @@ -838,6 +909,7 @@ default: exit "#, args: ("--quiet"), + stdin: "", stdout: "", stderr: "", status: EXIT_SUCCESS, @@ -850,6 +922,7 @@ default: exit 100 "#, args: ("--quiet"), + stdin: "", stdout: "", stderr: "", status: 100, @@ -863,6 +936,7 @@ default: exit 100 "#, args: ("--quiet"), + stdin: "", stdout: "", stderr: "", status: 100, @@ -876,6 +950,7 @@ default: exit 100 "#, args: ("--quiet"), + stdin: "", stdout: "", stderr: "", status: 100, @@ -888,6 +963,7 @@ foo A: echo {{A}} ", args: ("foo", "ARGUMENT"), + stdin: "", stdout: "ARGUMENT\n", stderr: "echo ARGUMENT\n", status: EXIT_SUCCESS, @@ -900,6 +976,7 @@ foo A B: echo A:{{A}} B:{{B}} ", args: ("foo", "ONE", "TWO"), + stdin: "", stdout: "A:ONE B:TWO\n", stderr: "echo A:ONE B:TWO\n", status: EXIT_SUCCESS, @@ -912,6 +989,7 @@ foo A B: echo A:{{A}} B:{{B}} ", args: ("foo", "ONE", "TWO", "THREE"), + stdin: "", stdout: "", stderr: "error: Justfile does not contain recipe `THREE`.\n", status: EXIT_FAILURE, @@ -924,6 +1002,7 @@ foo A B: echo A:{{A}} B:{{B}} ", args: ("foo", "ONE"), + stdin: "", stdout: "", stderr: "error: Recipe `foo` got 1 argument but takes 2\nusage:\n just foo A B\n", status: EXIT_FAILURE, @@ -936,6 +1015,7 @@ foo A B='B': echo A:{{A}} B:{{B}} ", args: ("foo", "ONE", "TWO", "THREE"), + stdin: "", stdout: "", stderr: "error: Justfile does not contain recipe `THREE`.\n", status: EXIT_FAILURE, @@ -948,6 +1028,7 @@ foo A B C='C': echo A:{{A}} B:{{B}} C:{{C}} ", args: ("foo", "bar"), + stdin: "", stdout: "", stderr: "error: Recipe `foo` got 1 argument but takes at least 2\nusage:\n just foo A B C='C'\n", status: EXIT_FAILURE, @@ -957,6 +1038,7 @@ integration_test! { name: unknown_recipe, justfile: "hello:", args: ("foo"), + stdin: "", stdout: "", stderr: "error: Justfile does not contain recipe `foo`.\n", status: EXIT_FAILURE, @@ -966,6 +1048,7 @@ integration_test! { name: unknown_recipes, justfile: "hello:", args: ("foo", "bar"), + stdin: "", stdout: "", stderr: "error: Justfile does not contain recipes `foo` or `bar`.\n", status: EXIT_FAILURE, @@ -975,6 +1058,7 @@ integration_test! { name: color_always, justfile: "b = a\na = `exit 100`\nbar:\n echo '{{`exit 200`}}'", args: ("--color", "always"), + stdin: "", stdout: "", stderr: "\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", @@ -985,6 +1069,7 @@ integration_test! { name: color_never, justfile: "b = a\na = `exit 100`\nbar:\n echo '{{`exit 200`}}'", args: ("--color", "never"), + stdin: "", stdout: "", stderr: "error: Backtick failed with exit code 100 | @@ -998,6 +1083,7 @@ integration_test! { name: color_auto, justfile: "b = a\na = `exit 100`\nbar:\n echo '{{`exit 200`}}'", args: ("--color", "auto"), + stdin: "", stdout: "", stderr: "error: Backtick failed with exit code 100 | @@ -1013,6 +1099,7 @@ integration_test! { recipe: @exit 100", args: ("--color=always"), + stdin: "", stdout: "", stderr: "\u{1b}[1;31merror:\u{1b}[0m \u{1b}[1m\ Recipe `recipe` failed on line 3 with exit code 100\u{1b}[0m\n", @@ -1026,6 +1113,7 @@ integration_test! { recipe a b +d: @exit 100"#, args: ("--dump"), + stdin: "", stdout: "# this recipe does something recipe a b +d: @exit 100 @@ -1038,6 +1126,7 @@ integration_test! { name: mixed_whitespace, justfile: "bar:\n\t echo hello", args: (), + stdin: "", stdout: "", stderr: "error: Found a mix of tabs and spaces in leading whitespace: `␉␠` Leading whitespace may consist of tabs or spaces, but not both @@ -1052,6 +1141,7 @@ integration_test! { name: extra_leading_whitespace, justfile: "bar:\n\t\techo hello\n\t\t\techo goodbye", args: (), + stdin: "", stdout: "", stderr: "error: Recipe line has extra leading whitespace | @@ -1065,6 +1155,7 @@ integration_test! { name: inconsistent_leading_whitespace, justfile: "bar:\n\t\techo hello\n\t echo goodbye", args: (), + stdin: "", stdout: "", stderr: "error: Recipe line has inconsistent leading whitespace. \ Recipe started with `␉␉` but found line with `␉␠` @@ -1079,6 +1170,7 @@ integration_test! { name: required_after_default, justfile: "bar:\nhello baz arg='foo' bar:", args: (), + stdin: "", stdout: "", stderr: "error: Non-default parameter `bar` follows default parameter | @@ -1092,6 +1184,7 @@ integration_test! { name: required_after_variadic, justfile: "bar:\nhello baz +arg bar:", args: (), + stdin: "", stdout: "", stderr: "error: Parameter `bar` follows variadic parameter | @@ -1109,6 +1202,7 @@ hello baz arg="XYZ\t\" ": echo '{{baz}}...{{arg}}' "#, args: ("hello", "ABC"), + stdin: "", stdout: "ABC...XYZ\t\"\t\n", stderr: "echo 'ABC...XYZ\t\"\t'\n", status: EXIT_SUCCESS, @@ -1122,6 +1216,7 @@ hello baz arg='XYZ" ': printf '{{baz}}...{{arg}}' "#, args: ("hello", "ABC"), + stdin: "", stdout: "ABC...XYZ\"\t", stderr: "printf 'ABC...XYZ\"\t'\n", status: EXIT_SUCCESS, @@ -1134,6 +1229,7 @@ hello a b='B' c='C': echo {{a}} {{b}} {{c}} "#, args: ("hello", "0", "1"), + stdin: "", stdout: "0 1 C\n", stderr: "echo 0 1 C\n", status: EXIT_SUCCESS, @@ -1146,6 +1242,7 @@ hello a b='B' c='C': echo {{a}} {{b}} {{c}} "#, args: ("hello", "0", "1", "2"), + stdin: "", stdout: "0 1 2\n", stderr: "echo 0 1 2\n", status: EXIT_SUCCESS, @@ -1167,6 +1264,7 @@ a Z="\t z": _private-recipe: "#, args: ("--list"), + stdin: "", stdout: r#"Available recipes: a Z="\t z" hello a b='B ' c='C' # this does a thing @@ -1190,6 +1288,7 @@ a Z="\t z": _private-recipe: "#, args: ("--list"), + stdin: "", stdout: r#"Available recipes: a Z="\t z" # something else hello a b='B ' c='C' # this does a thing @@ -1217,6 +1316,7 @@ this-recipe-is-very-very-very-important Z="\t z": _private-recipe: "#, args: ("--list"), + stdin: "", stdout: r#"Available recipes: hello a b='B ' c='C' # this does a thing this-recipe-is-very-very-very-important Z="\t z" # something else @@ -1235,6 +1335,7 @@ hello a b='B ' c='C': a Z="\t z": "#, args: ("--show", "hell"), + stdin: "", stdout: "", stderr: "Justfile does not contain recipe `hell`.\nDid you mean `hello`?\n", status: EXIT_FAILURE, @@ -1249,6 +1350,7 @@ helloooooo a b='B ' c='C': a Z="\t z": "#, args: ("--show", "hell"), + stdin: "", stdout: "", stderr: "Justfile does not contain recipe `hell`.\n", status: EXIT_FAILURE, @@ -1263,6 +1365,7 @@ hello a b='B ' c='C': a Z="\t z": "#, args: ("hell"), + stdin: "", stdout: "", stderr: "error: Justfile does not contain recipe `hell`.\nDid you mean `hello`?\n", status: EXIT_FAILURE, @@ -1277,6 +1380,7 @@ foo: c "#, args: (), + stdin: "", stdout: "a b c\n", stderr: "echo a b c\n", status: EXIT_SUCCESS, @@ -1291,6 +1395,7 @@ foo: c' "#, args: (), + stdin: "", stdout: "a b c\n", stderr: "echo 'a b c'\n", status: EXIT_SUCCESS, @@ -1305,6 +1410,7 @@ foo: c "#, args: (), + stdin: "", stdout: "abc\n", stderr: "echo abc\n", status: EXIT_SUCCESS, @@ -1317,6 +1423,7 @@ foo: echo {{arch()}} {{os()}} {{os_family()}} "#, args: (), + stdin: "", stdout: format!("{} {} {}\n", target::arch(), target::os(), target::os_family()).as_str(), stderr: format!("echo {} {} {}\n", target::arch(), target::os(), target::os_family()).as_str(), status: EXIT_SUCCESS, @@ -1333,6 +1440,7 @@ foo: echo {{a}} {{o}} {{f}} "#, args: (), + stdin: "", stdout: format!("{} {} {}\n", target::arch(), target::os(), target::os_family()).as_str(), stderr: format!("echo {} {} {}\n", target::arch(), target::os(), target::os_family()).as_str(), status: EXIT_SUCCESS, @@ -1350,6 +1458,7 @@ foo: /bin/echo '{{p}}' '{{b}}' '{{x}}' "#, args: (), + stdin: "", stdout: format!("{} HTAP ABC\n", env::var("USER").unwrap()).as_str(), stderr: format!("/bin/echo '{}' 'HTAP' 'ABC'\n", env::var("USER").unwrap()).as_str(), status: EXIT_SUCCESS, @@ -1367,6 +1476,7 @@ foo: /bin/echo '{{p}}' '{{b}}' '{{x}}' "#, args: (), + stdin: "", stdout: format!("{} HTAP ABC\n", env::var("USERNAME").unwrap()).as_str(), stderr: format!("/bin/echo '{}' 'HTAP' 'ABC'\n", env::var("USERNAME").unwrap()).as_str(), status: EXIT_SUCCESS, @@ -1376,6 +1486,7 @@ integration_test! { name: env_var_failure, justfile: "a:\n echo {{env_var('ZADDY')}}", args: ("a"), + stdin: "", stdout: "", stderr: "error: Call to function `env_var` failed: environment variable `ZADDY` not present | @@ -1394,6 +1505,7 @@ integration_test! { @echo c "#, args: (), + stdin: "", stdout: "c\n", stderr: "echo c\n", status: EXIT_SUCCESS, @@ -1407,6 +1519,7 @@ integration_test! { echo hello "#, args: (), + stdin: "", stdout: "hello\n", stderr: "#!/bin/sh\necho hello\n", status: EXIT_SUCCESS, @@ -1428,6 +1541,7 @@ quiet: "#, args: (), + stdin: "", stdout: "#!/usr/bin/env cat @@ -1451,6 +1565,7 @@ b: c: b a "#, args: ("b"), + stdin: "", stdout: "", stderr: "", status: EXIT_SUCCESS, @@ -1460,6 +1575,7 @@ integration_test! { name: parameter_shadows_variable, justfile: "FOO = 'hello'\na FOO:", args: ("a"), + stdin: "", stdout: "", stderr: "error: Parameter `FOO` shadows variable of the same name | @@ -1474,6 +1590,7 @@ integration_test! { justfile: r#"foo = foo() + "hello" bar:"#, args: ("bar"), + stdin: "", stdout: "", stderr: r#"error: Call to unknown function `foo` | @@ -1487,6 +1604,7 @@ integration_test! { name: dependency_takes_arguments, justfile: "b: a\na FOO:", args: ("b"), + stdin: "", stdout: "", stderr: "error: Recipe `b` depends on `a` which requires arguments. \ Dependencies may not require arguments @@ -1501,6 +1619,7 @@ integration_test! { name: duplicate_parameter, justfile: "a foo foo:", args: ("a"), + stdin: "", stdout: "", stderr: "error: Recipe `a` has duplicate parameter `foo` | @@ -1514,6 +1633,7 @@ integration_test! { name: duplicate_dependency, justfile: "b:\na: b b", args: ("a"), + stdin: "", stdout: "", stderr: "error: Recipe `a` has duplicate dependency `b` | @@ -1527,6 +1647,7 @@ integration_test! { name: duplicate_recipe, justfile: "b:\nb:", args: ("b"), + stdin: "", stdout: "", stderr: "error: Recipe `b` first defined on line 1 is redefined on line 2 | @@ -1540,6 +1661,7 @@ integration_test! { name: duplicate_variable, justfile: "a = 'hello'\na = 'hello'\nfoo:", args: ("foo"), + stdin: "", stdout: "", stderr: "error: Variable `a` has multiple definitions | @@ -1553,6 +1675,7 @@ integration_test! { name: unexpected_token_in_dependency_position, justfile: "foo: 'bar'", args: ("foo"), + stdin: "", stdout: "", stderr: "error: Expected name, end of line, or end of file, but found raw string | @@ -1566,6 +1689,7 @@ integration_test! { name: unexpected_token_after_name, justfile: "foo 'bar'", args: ("foo"), + stdin: "", stdout: "", stderr: "error: Expected name, '+', ':', or '=', but found raw string | @@ -1579,6 +1703,7 @@ integration_test! { name: self_dependency, justfile: "a: a", args: ("a"), + stdin: "", stdout: "", stderr: "error: Recipe `a` depends on itself | @@ -1592,6 +1717,7 @@ integration_test! { name: long_circular_recipe_dependency, justfile: "a: b\nb: c\nc: d\nd: a", args: ("a"), + stdin: "", stdout: "", stderr: "error: Recipe `d` has circular dependency `a -> b -> c -> d -> a` | @@ -1605,6 +1731,7 @@ integration_test! { name: variable_self_dependency, justfile: "z = z\na:", args: ("a"), + stdin: "", stdout: "", stderr: "error: Variable `z` is defined in terms of itself | @@ -1618,6 +1745,7 @@ integration_test! { name: variable_circular_dependency, justfile: "x = y\ny = z\nz = x\na:", args: ("a"), + stdin: "", stdout: "", stderr: "error: Variable `x` depends on its own value: `x -> y -> z -> x` | @@ -1632,6 +1760,7 @@ integration_test! { justfile: r#"x = "\q" a:"#, args: ("a"), + stdin: "", stdout: "", stderr: "error: `\\q` is not a valid escape sequence | @@ -1651,6 +1780,7 @@ a: echo '{{string}}' ", args: ("a"), + stdin: "", stdout: "hello whatever ", @@ -1671,6 +1801,7 @@ a: echo '{{foo}}' ", args: ("a"), + stdin: "", stdout: "", stderr: "error: Variable `foo` not defined | @@ -1691,6 +1822,7 @@ a: echo '{{string}}' ", args: ("a"), + stdin: "", stdout: "", stderr: "error: Variable `bar` not defined | @@ -1708,6 +1840,7 @@ a: ' + "b"}}' "#, args: ("a"), + stdin: "", stdout: "a b ", @@ -1727,6 +1860,7 @@ a: echo {{b}} "#, args: ("a"), + stdin: "", stdout: "", stderr: "error: Variable `b` not defined | @@ -1742,6 +1876,7 @@ integration_test! { a b= ': ", args: ("a"), + stdin: "", stdout: "", stderr: "error: Unterminated string | @@ -1757,6 +1892,7 @@ integration_test! { a b= ": "#, args: ("a"), + stdin: "", stdout: "", stderr: r#"error: Unterminated string | @@ -1773,6 +1909,7 @@ a x y +z: echo {{x}} {{y}} {{z}} ", args: ("a", "0", "1", "2", "3", " 4 "), + stdin: "", stdout: "0 1 2 3 4\n", stderr: "echo 0 1 2 3 4 \n", status: EXIT_SUCCESS, @@ -1785,6 +1922,7 @@ a x y +z='HELLO': echo {{x}} {{y}} {{z}} ", args: ("a", "0", "1", "2", "3", " 4 "), + stdin: "", stdout: "0 1 2 3 4\n", stderr: "echo 0 1 2 3 4 \n", status: EXIT_SUCCESS, @@ -1797,6 +1935,7 @@ a x y +z='HELLO': echo {{x}} {{y}} {{z}} ", args: ("a", "0", "1"), + stdin: "", stdout: "0 1 HELLO\n", stderr: "echo 0 1 HELLO\n", status: EXIT_SUCCESS, @@ -1809,6 +1948,7 @@ a x y +z: echo {{x}} {{y}} {{z}} ", args: ("a", "0", "1"), + stdin: "", stdout: "", stderr: "error: Recipe `a` got 2 arguments but takes at least 3\nusage:\n just a x y +z\n", status: EXIT_FAILURE, @@ -1827,6 +1967,7 @@ BAZ +Z: echo baz: {{Z}} ", args: ("BAR", "0", "FOO", "1", "2", "BAZ", "3", "4", "5"), + stdin: "", 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", status: EXIT_SUCCESS, @@ -1840,6 +1981,7 @@ x: a: x y ", args: (), + stdin: "", stdout: "", stderr: "error: Recipe `a` has unknown dependency `y` | @@ -1857,6 +1999,7 @@ a B C +D='hello': echo {{B}} {{C}} {{D}} ", args: ("--color", "always", "--list"), + stdin: "", stdout: "Available recipes:\n 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 \ @@ -1873,6 +2016,7 @@ a: echo hi ", args: ("--color", "always", "--highlight", "--verbose"), + stdin: "", stdout: "hi\n", stderr: "\u{1b}[1;36m===> Running recipe `a`...\u{1b}[0m\n\u{1b}[1mecho hi\u{1b}[0m\n", status: EXIT_SUCCESS, @@ -1885,6 +2029,7 @@ echo A B C: echo {{A}} {{B}} {{C}} ", args: ("echo", "--some", "--awesome", "--flags"), + stdin: "", stdout: "--some --awesome --flags\n", stderr: "echo --some --awesome --flags\n", status: EXIT_SUCCESS, @@ -1899,6 +2044,7 @@ echo: echo {{A}} ", args: ("echo"), + stdin: "", stdout: "1\n", stderr: "echo 1\n", status: EXIT_SUCCESS, @@ -1912,6 +2058,7 @@ echo: echo $DOTENV_KEY ", args: (), + stdin: "", stdout: "dotenv-value\n", stderr: "echo $DOTENV_KEY\n", status: EXIT_SUCCESS, @@ -1926,6 +2073,7 @@ echo: echo {{X}} ", args: (), + stdin: "", stdout: "dotenv-value\n", stderr: "echo dotenv-value\n", status: EXIT_SUCCESS, @@ -1939,6 +2087,7 @@ echo: echo {{env_var('DOTENV_KEY')}} ", args: (), + stdin: "", stdout: "dotenv-value\ndotenv-value\n", stderr: "echo dotenv-value\necho dotenv-value\n", status: EXIT_SUCCESS, @@ -1955,6 +2104,7 @@ echo: echo {{Y}} ", args: (), + stdin: "", stdout: "dotenv-value\ndotenv-value\n", stderr: "echo dotenv-value\necho dotenv-value\n", status: EXIT_SUCCESS, @@ -1966,6 +2116,7 @@ integration_test! { X = "\'" "#, args: (), + stdin: "", stdout: "", stderr: r#"error: `\'` is not a valid escape sequence | @@ -1981,6 +2132,7 @@ integration_test! { foo x=bar: ", args: (), + stdin: "", stdout: "", stderr: r#"error: Variable `bar` not defined | @@ -1996,6 +2148,7 @@ integration_test! { foo x=bar(): ", args: (), + stdin: "", stdout: "", stderr: r#"error: Call to unknown function `bar` | @@ -2012,6 +2165,7 @@ foo x='bar': echo {{x}} ", args: (), + stdin: "", stdout: "bar\n", stderr: "echo bar\n", status: EXIT_SUCCESS, @@ -2024,6 +2178,7 @@ foo x=(`echo foo` + 'bar'): echo {{x}} ", args: (), + stdin: "", stdout: "foobar\n", stderr: "echo foobar\n", status: EXIT_SUCCESS, @@ -2036,6 +2191,7 @@ foo x=`echo foo`: echo {{x}} ", args: (), + stdin: "", stdout: "foo\n", stderr: "echo foo\n", status: EXIT_SUCCESS, @@ -2049,6 +2205,7 @@ foo x=y: echo {{x}} ", args: (), + stdin: "", stdout: "foo\n", stderr: "echo foo\n", status: EXIT_SUCCESS, @@ -2061,6 +2218,7 @@ foo a=arch() o=os() f=os_family(): echo {{a}} {{o}} {{f}} "#, args: (), + stdin: "", stdout: format!("{} {} {}\n", target::arch(), target::os(), target::os_family()).as_str(), stderr: format!("echo {} {} {}\n", target::arch(), target::os(), target::os_family()).as_str(), status: EXIT_SUCCESS, @@ -2073,6 +2231,7 @@ foo: echo {{ ", args: (), + stdin: "", stdout: "", stderr: r#"error: Unterminated interpolation | @@ -2088,6 +2247,7 @@ integration_test! { foo: echo {{", args: (), + stdin: "", stdout: "", stderr: r#"error: Unterminated interpolation | @@ -2103,6 +2263,7 @@ integration_test! { foo a=\t`echo blaaaaaah: echo {{a}}", args: (), + stdin: "", stdout: "", stderr: r#"error: Unterminated backtick | @@ -2118,6 +2279,7 @@ integration_test! { assembly_source_files = $(wildcard src/arch/$(arch)/*.s) ", args: (), + stdin: "", stdout: "", stderr: r#"error: Unknown start of token: | @@ -2126,3 +2288,96 @@ assembly_source_files = $(wildcard src/arch/$(arch)/*.s) "#, status: EXIT_FAILURE, } + +integration_test! { + name: backtick_variable_cat, + justfile: " +stdin = `cat` + +default: + echo {{stdin}} +", + args: (), + stdin: "STDIN", + stdout: "STDIN\n", + stderr: "echo STDIN\n", + status: EXIT_SUCCESS, +} + +integration_test! { + name: backtick_default_cat_stdin, + justfile: " +default stdin = `cat`: + echo {{stdin}} +", + args: (), + stdin: "STDIN", + stdout: "STDIN\n", + stderr: "echo STDIN\n", + status: EXIT_SUCCESS, +} + +integration_test! { + name: backtick_default_cat_justfile, + justfile: " +default stdin = `cat justfile`: + echo '{{stdin}}' +", + args: (), + stdin: "", + stdout: " +default stdin = `cat justfile`: + echo {{stdin}} +", + stderr: "echo ' +default stdin = `cat justfile`: + echo '{{stdin}}''\n", + status: EXIT_SUCCESS, +} + +integration_test! { + name: backtick_variable_read_single, + justfile: " +password = `read PW && echo $PW` + +default: + echo {{password}} +", + args: (), + stdin: "foobar\n", + stdout: "foobar\n", + stderr: "echo foobar\n", + status: EXIT_SUCCESS, +} + +integration_test! { + name: backtick_variable_read_multiple, + justfile: " +a = `read A && echo $A` +b = `read B && echo $B` + +default: + echo {{a}} + echo {{b}} +", + args: (), + stdin: "foo\nbar\n", + stdout: "foo\nbar\n", + stderr: "echo foo\necho bar\n", + status: EXIT_SUCCESS, +} + +integration_test! { + name: backtick_default_read_multiple, + justfile: " + +default a=`read A && echo $A` b=`read B && echo $B`: + echo {{a}} + echo {{b}} +", + args: (), + stdin: "foo\nbar\n", + stdout: "foo\nbar\n", + stderr: "echo foo\necho bar\n", + status: EXIT_SUCCESS, +}