Backtick tests done!

This commit is contained in:
Casey Rodarmor 2016-10-30 01:27:05 -07:00
parent 21320d36ee
commit 25c6432fa3
5 changed files with 127 additions and 18 deletions

9
notes
View File

@ -1,15 +1,6 @@
notes notes
----- -----
- unit tests for as many backtick error types as possible
- integration tests for as many error types as possible, errors should underline backtick token
- test shebang recipe `` evaluation order
make sure that nothing happens if a `` fails on a later line
- test command recipe `` evaluation order
do some stuff, then have a line with a `` that fails
make sure that stuff before that actually happened
make sure that result error code is correct
- --debug tests that: - --debug tests that:
- should consider renaming it to --evaluate if it actually evaluates stuff - should consider renaming it to --evaluate if it actually evaluates stuff
- test values of assignments - test values of assignments

View File

@ -133,6 +133,10 @@ pub fn app() {
if let Err(run_error) = justfile.run(&arguments) { if let Err(run_error) = justfile.run(&arguments) {
warn!("{}", run_error); warn!("{}", run_error);
process::exit(if let super::RunError::Code{code, ..} = run_error { code } else { -1 }); match run_error {
super::RunError::Code{code, .. } => process::exit(code),
super::RunError::BacktickCode{code, ..} => process::exit(code),
_ => process::exit(-1),
}
} }
} }

View File

@ -261,3 +261,91 @@ fn backtick_trimming() {
"echo 'Hello, world!'\n", "echo 'Hello, world!'\n",
); );
} }
#[test]
fn backtick_code_assignment() {
integration_test(
"backtick_code_assignment",
&[],
"b = a\na = `function f { return 100; }; f`\nbar:\n echo '{{`function f { return 200; }; f`}}'",
100,
"",
"backtick failed with exit code 100
|
2 | a = `function f { return 100; }; f`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
",
);
}
#[test]
fn backtick_code_interpolation() {
integration_test(
"backtick_code_interpolation",
&[],
"b = a\na = `echo hello`\nbar:\n echo '{{`function f { return 200; }; f`}}'",
200,
"",
"backtick failed with exit code 200
|
4 | echo '{{`function f { return 200; }; f`}}'
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
",
);
}
#[test]
fn shebang_backtick_failure() {
integration_test(
"shebang_backtick_failure",
&[],
"foo:
#!/bin/sh
echo hello
echo {{`exit 123`}}",
123,
"",
"backtick failed with exit code 123
|
4 | echo {{`exit 123`}}
| ^^^^^^^^^^
",
);
}
#[test]
fn command_backtick_failure() {
integration_test(
"command_backtick_failure",
&[],
"foo:
echo hello
echo {{`exit 123`}}",
123,
"hello\n",
"echo hello\nbacktick failed with exit code 123
|
3 | echo {{`exit 123`}}
| ^^^^^^^^^^
",
);
}
#[test]
fn assignment_backtick_failure() {
integration_test(
"assignment_backtick_failure",
&[],
"foo:
echo hello
echo {{`exit 111`}}
a = `exit 222`",
222,
"",
"backtick failed with exit code 222
|
4 | a = `exit 222`
| ^^^^^^^^^^
",
);
}

View File

@ -148,7 +148,7 @@ fn backtick_error_from_signal(exit_status: process::ExitStatus) -> RunError<'sta
RunError::BacktickUnknownFailure RunError::BacktickUnknownFailure
} }
fn run_backtick<'a>(raw: &str, _token: &Token<'a>) -> Result<String, RunError<'a>> { fn run_backtick<'a>(raw: &str, token: &Token<'a>) -> Result<String, RunError<'a>> {
let output = process::Command::new("sh") let output = process::Command::new("sh")
.arg("-cu") .arg("-cu")
.arg(raw) .arg(raw)
@ -160,6 +160,7 @@ fn run_backtick<'a>(raw: &str, _token: &Token<'a>) -> Result<String, RunError<'a
if let Some(code) = output.status.code() { if let Some(code) = output.status.code() {
if code != 0 { if code != 0 {
return Err(RunError::BacktickCode { return Err(RunError::BacktickCode {
token: token.clone(),
code: code, code: code,
}); });
} }
@ -877,7 +878,7 @@ enum RunError<'a> {
TmpdirIoError{recipe: &'a str, io_error: io::Error}, TmpdirIoError{recipe: &'a str, io_error: io::Error},
UnknownFailure{recipe: &'a str}, UnknownFailure{recipe: &'a str},
UnknownRecipes{recipes: Vec<&'a str>}, UnknownRecipes{recipes: Vec<&'a str>},
BacktickCode{code: i32}, BacktickCode{code: i32, token: Token<'a>},
BacktickIoError{io_error: io::Error}, BacktickIoError{io_error: io::Error},
BacktickSignal{signal: i32}, BacktickSignal{signal: i32},
BacktickUtf8Error{utf8_error: std::str::Utf8Error}, BacktickUtf8Error{utf8_error: std::str::Utf8Error},
@ -920,14 +921,27 @@ impl<'a> Display for RunError<'a> {
}, },
RunError::TmpdirIoError{recipe, ref io_error} => RunError::TmpdirIoError{recipe, ref io_error} =>
try!(write!(f, "Recipe \"{}\" could not be run because of an IO error while trying to create a temporary directory or write a file to that directory`:\n{}", recipe, io_error)), try!(write!(f, "Recipe \"{}\" could not be run because of an IO error while trying to create a temporary directory or write a file to that directory`:\n{}", recipe, io_error)),
RunError::BacktickCode{code} => { RunError::BacktickCode{code, ref token} => {
try!(writeln!(f, "backtick failed with exit code {}", code)); try!(write!(f, "backtick failed with exit code {}\n", code));
match token.text.lines().nth(token.line) {
Some(line) => {
let line_number_width = token.line.to_string().len();
try!(write!(f, "{0:1$} |\n", "", line_number_width));
try!(write!(f, "{} | {}\n", token.line + 1, line));
try!(write!(f, "{0:1$} |", "", line_number_width));
try!(write!(f, " {0:1$}{2:^<3$}", "",
token.column + token.prefix.len(), "", token.lexeme.len()));
},
None => if token.index != token.text.len() {
try!(write!(f, "internal error: Error has invalid line number: {}", token.line + 1))
},
}
} }
RunError::BacktickSignal{signal} => { RunError::BacktickSignal{signal} => {
try!(writeln!(f, "backtick was terminated by signal {}", signal)); try!(write!(f, "backtick was terminated by signal {}", signal));
} }
RunError::BacktickUnknownFailure => { RunError::BacktickUnknownFailure => {
try!(writeln!(f, "backtick failed for an uknown reason")); try!(write!(f, "backtick failed for an uknown reason"));
} }
RunError::BacktickIoError{ref io_error} => { RunError::BacktickIoError{ref io_error} => {
try!(match io_error.kind() { try!(match io_error.kind() {
@ -940,14 +954,15 @@ impl<'a> Display for RunError<'a> {
try!(write!(f, "backtick succeeded but stdout was not utf8: {}", utf8_error)); try!(write!(f, "backtick succeeded but stdout was not utf8: {}", utf8_error));
} }
RunError::InternalError{ref message} => { RunError::InternalError{ref message} => {
try!(writeln!(f, "internal error, this may indicate a bug in j: {}\n consider filing an issue: https://github.com/casey/j/issues/new", message)); try!(write!(f, "internal error, this may indicate a bug in j: {}\n consider filing an issue: https://github.com/casey/j/issues/new", message));
} }
} }
Ok(()) Ok(())
} }
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
struct Token<'a> { struct Token<'a> {
index: usize, index: usize,
line: usize, line: usize,

View File

@ -797,3 +797,14 @@ fn missing_default() {
other => panic!("expected an code run error, but got: {}", other), other => panic!("expected an code run error, but got: {}", other),
} }
} }
#[test]
fn backtick_code() {
match parse_success("a:\n echo {{`function f { return 100; }; f`}}").run(&["a"]).unwrap_err() {
super::RunError::BacktickCode{code, token} => {
assert_eq!(code, 100);
assert_eq!(token.lexeme, "`function f { return 100; }; f`");
},
other => panic!("expected an code run error, but got: {}", other),
}
}