2016-10-05 13:58:18 -07:00
|
|
|
extern crate tempdir;
|
2016-10-31 21:53:31 -07:00
|
|
|
extern crate brev;
|
2016-10-05 13:58:18 -07:00
|
|
|
|
2016-11-12 14:24:33 -08:00
|
|
|
use super::{Token, CompileError, ErrorKind, Justfile, RunError, RunOptions};
|
2016-10-23 18:46:04 -07:00
|
|
|
use super::TokenKind::*;
|
2016-10-23 16:43:52 -07:00
|
|
|
|
2016-10-16 18:59:49 -07:00
|
|
|
fn tokenize_success(text: &str, expected_summary: &str) {
|
|
|
|
let tokens = super::tokenize(text).unwrap();
|
|
|
|
let roundtrip = tokens.iter().map(|t| {
|
|
|
|
let mut s = String::new();
|
|
|
|
s += t.prefix;
|
|
|
|
s += t.lexeme;
|
|
|
|
s
|
|
|
|
}).collect::<Vec<_>>().join("");
|
2016-10-26 22:04:12 -07:00
|
|
|
let summary = token_summary(&tokens);
|
|
|
|
if summary != expected_summary {
|
|
|
|
panic!("token summary mismatch:\nexpected: {}\ngot: {}\n", expected_summary, summary);
|
|
|
|
}
|
2016-10-16 18:59:49 -07:00
|
|
|
assert_eq!(text, roundtrip);
|
|
|
|
}
|
|
|
|
|
2016-11-12 14:24:33 -08:00
|
|
|
fn tokenize_error(text: &str, expected: CompileError) {
|
2016-10-22 23:18:26 -07:00
|
|
|
if let Err(error) = super::tokenize(text) {
|
|
|
|
assert_eq!(error.text, expected.text);
|
|
|
|
assert_eq!(error.index, expected.index);
|
|
|
|
assert_eq!(error.line, expected.line);
|
|
|
|
assert_eq!(error.column, expected.column);
|
|
|
|
assert_eq!(error.kind, expected.kind);
|
|
|
|
assert_eq!(error, expected);
|
|
|
|
} else {
|
|
|
|
panic!("tokenize() succeeded but expected: {}\n{}", expected, text);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn token_summary(tokens: &[Token]) -> String {
|
2016-10-16 18:59:49 -07:00
|
|
|
tokens.iter().map(|t| {
|
2016-10-27 18:48:55 -07:00
|
|
|
match t.kind {
|
2016-11-12 16:12:00 -08:00
|
|
|
At => "@",
|
|
|
|
Backtick => "`",
|
|
|
|
Colon => ":",
|
|
|
|
Comment{..} => "#",
|
|
|
|
Dedent => "<",
|
|
|
|
Eof => ".",
|
|
|
|
Eol => "$",
|
|
|
|
Equals => "=",
|
|
|
|
Indent{..} => ">",
|
|
|
|
InterpolationEnd => "}",
|
|
|
|
InterpolationStart => "{",
|
|
|
|
Line{..} => "^",
|
|
|
|
Name => "N",
|
|
|
|
Plus => "+",
|
|
|
|
RawString => "'",
|
|
|
|
StringToken => "\"",
|
|
|
|
Text => "_",
|
2016-10-16 18:59:49 -07:00
|
|
|
}
|
|
|
|
}).collect::<Vec<_>>().join("")
|
|
|
|
}
|
|
|
|
|
2016-10-22 23:18:26 -07:00
|
|
|
fn parse_success(text: &str) -> Justfile {
|
|
|
|
match super::parse(text) {
|
|
|
|
Ok(justfile) => justfile,
|
2016-10-23 16:43:52 -07:00
|
|
|
Err(error) => panic!("Expected successful parse but got error:\n{}", error),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parse_summary(input: &str, output: &str) {
|
|
|
|
let justfile = parse_success(input);
|
2016-10-28 16:32:13 -07:00
|
|
|
let s = format!("{:#}", justfile);
|
2016-10-23 23:38:49 -07:00
|
|
|
if s != output {
|
|
|
|
println!("got:\n\"{}\"\n", s);
|
|
|
|
println!("\texpected:\n\"{}\"", output);
|
2016-10-25 19:11:58 -07:00
|
|
|
assert_eq!(s, output);
|
2016-10-23 23:38:49 -07:00
|
|
|
}
|
2016-10-23 16:43:52 -07:00
|
|
|
}
|
|
|
|
|
2016-11-12 14:24:33 -08:00
|
|
|
fn parse_error(text: &str, expected: CompileError) {
|
2016-10-23 16:43:52 -07:00
|
|
|
if let Err(error) = super::parse(text) {
|
|
|
|
assert_eq!(error.text, expected.text);
|
|
|
|
assert_eq!(error.index, expected.index);
|
|
|
|
assert_eq!(error.line, expected.line);
|
|
|
|
assert_eq!(error.column, expected.column);
|
|
|
|
assert_eq!(error.kind, expected.kind);
|
|
|
|
assert_eq!(error.width, expected.width);
|
|
|
|
assert_eq!(error, expected);
|
|
|
|
} else {
|
|
|
|
panic!("Expected {:?} but parse succeeded", expected.kind);
|
2016-10-22 23:18:26 -07:00
|
|
|
}
|
|
|
|
}
|
2016-10-26 22:04:12 -07:00
|
|
|
|
2016-10-30 16:56:22 -07:00
|
|
|
#[test]
|
|
|
|
fn tokanize_strings() {
|
|
|
|
tokenize_success(
|
|
|
|
r#"a = "'a'" + '"b"' + "'c'" + '"d"'"#,
|
|
|
|
r#"N="+'+"+'."#
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2016-10-26 22:04:12 -07:00
|
|
|
#[test]
|
|
|
|
fn tokenize_recipe_interpolation_eol() {
|
|
|
|
let text = "foo:
|
|
|
|
{{hello}}
|
|
|
|
";
|
|
|
|
tokenize_success(text, "N:$>^{N}$<.");
|
|
|
|
}
|
2016-10-22 23:18:26 -07:00
|
|
|
|
2016-10-16 18:59:49 -07:00
|
|
|
#[test]
|
2016-10-26 22:04:12 -07:00
|
|
|
fn tokenize_recipe_interpolation_eof() {
|
|
|
|
let text = "foo:
|
|
|
|
{{hello}}";
|
|
|
|
tokenize_success(text, "N:$>^{N}<.");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn tokenize_recipe_complex_interpolation_expression() {
|
|
|
|
let text = "foo:\n {{a + b + \"z\" + blarg}}";
|
2016-10-30 16:56:22 -07:00
|
|
|
tokenize_success(text, "N:$>^{N+N+\"+N}<.");
|
2016-10-26 22:04:12 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn tokenize_recipe_multiple_interpolations() {
|
|
|
|
let text = "foo:\n {{a}}0{{b}}1{{c}}";
|
|
|
|
tokenize_success(text, "N:$>^{N}_{N}_{N}<.");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn tokenize_junk() {
|
2016-10-16 18:59:49 -07:00
|
|
|
let text = "bob
|
|
|
|
|
|
|
|
hello blah blah blah : a b c #whatever
|
|
|
|
";
|
|
|
|
tokenize_success(text, "N$$NNNN:NNN#$.");
|
2016-10-26 22:04:12 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn tokenize_empty_lines() {
|
|
|
|
let text = "
|
|
|
|
hello:
|
|
|
|
asdf
|
|
|
|
bsdf
|
|
|
|
|
|
|
|
csdf
|
2016-10-16 18:59:49 -07:00
|
|
|
|
2016-10-26 22:04:12 -07:00
|
|
|
dsdf
|
|
|
|
";
|
|
|
|
|
|
|
|
tokenize_success(text, "$N:$>^_$^_$$^_$$^_$<.");
|
|
|
|
}
|
|
|
|
|
2016-10-29 23:39:12 -07:00
|
|
|
#[test]
|
|
|
|
fn tokenize_interpolation_backticks() {
|
|
|
|
tokenize_success(
|
|
|
|
"hello:\n echo {{`echo hello` + `echo goodbye`}}",
|
|
|
|
"N:$>^_{`+`}<."
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn tokenize_assignment_backticks() {
|
|
|
|
tokenize_success(
|
|
|
|
"a = `echo hello` + `echo goodbye`",
|
|
|
|
"N=`+`."
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2016-10-26 22:04:12 -07:00
|
|
|
#[test]
|
|
|
|
fn tokenize_multiple() {
|
2016-10-16 18:59:49 -07:00
|
|
|
let text = "
|
|
|
|
hello:
|
|
|
|
a
|
|
|
|
b
|
|
|
|
|
|
|
|
c
|
|
|
|
|
|
|
|
d
|
|
|
|
|
|
|
|
bob:
|
|
|
|
frank
|
|
|
|
";
|
2016-10-22 23:18:26 -07:00
|
|
|
|
2016-10-26 22:04:12 -07:00
|
|
|
tokenize_success(text, "$N:$>^_$^_$$^_$$^_$$<N:$>^_$<.");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn tokenize_comment() {
|
2016-10-22 23:18:26 -07:00
|
|
|
tokenize_success("a:=#", "N:=#.")
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2016-10-26 22:04:12 -07:00
|
|
|
fn tokenize_space_then_tab() {
|
2016-10-22 23:18:26 -07:00
|
|
|
let text = "a:
|
|
|
|
0
|
|
|
|
1
|
|
|
|
\t2
|
|
|
|
";
|
2016-11-12 14:24:33 -08:00
|
|
|
tokenize_error(text, CompileError {
|
2016-10-22 23:18:26 -07:00
|
|
|
text: text,
|
|
|
|
index: 9,
|
|
|
|
line: 3,
|
|
|
|
column: 0,
|
2016-10-23 16:43:52 -07:00
|
|
|
width: None,
|
2016-10-22 23:18:26 -07:00
|
|
|
kind: ErrorKind::InconsistentLeadingWhitespace{expected: " ", found: "\t"},
|
|
|
|
});
|
2016-10-26 22:04:12 -07:00
|
|
|
}
|
2016-10-22 23:18:26 -07:00
|
|
|
|
2016-10-26 22:04:12 -07:00
|
|
|
#[test]
|
|
|
|
fn tokenize_tabs_then_tab_space() {
|
2016-10-22 23:18:26 -07:00
|
|
|
let text = "a:
|
|
|
|
\t\t0
|
|
|
|
\t\t 1
|
|
|
|
\t 2
|
|
|
|
";
|
2016-11-12 14:24:33 -08:00
|
|
|
tokenize_error(text, CompileError {
|
2016-10-22 23:18:26 -07:00
|
|
|
text: text,
|
|
|
|
index: 12,
|
|
|
|
line: 3,
|
|
|
|
column: 0,
|
2016-10-23 16:43:52 -07:00
|
|
|
width: None,
|
2016-10-22 23:18:26 -07:00
|
|
|
kind: ErrorKind::InconsistentLeadingWhitespace{expected: "\t\t", found: "\t "},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2016-10-27 00:13:10 -07:00
|
|
|
fn tokenize_outer_shebang() {
|
2016-10-22 23:18:26 -07:00
|
|
|
let text = "#!/usr/bin/env bash";
|
2016-11-12 14:24:33 -08:00
|
|
|
tokenize_error(text, CompileError {
|
2016-10-22 23:18:26 -07:00
|
|
|
text: text,
|
|
|
|
index: 0,
|
|
|
|
line: 0,
|
|
|
|
column: 0,
|
2016-10-23 16:43:52 -07:00
|
|
|
width: None,
|
2016-10-22 23:18:26 -07:00
|
|
|
kind: ErrorKind::OuterShebang
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2016-10-27 00:13:10 -07:00
|
|
|
fn tokenize_unknown() {
|
2016-10-22 23:18:26 -07:00
|
|
|
let text = "~";
|
2016-11-12 14:24:33 -08:00
|
|
|
tokenize_error(text, CompileError {
|
2016-10-22 23:18:26 -07:00
|
|
|
text: text,
|
|
|
|
index: 0,
|
|
|
|
line: 0,
|
|
|
|
column: 0,
|
2016-10-23 16:43:52 -07:00
|
|
|
width: None,
|
2016-10-22 23:18:26 -07:00
|
|
|
kind: ErrorKind::UnknownStartOfToken
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2016-10-26 20:54:44 -07:00
|
|
|
fn parse_empty() {
|
2016-10-23 16:43:52 -07:00
|
|
|
parse_summary("
|
2016-10-22 23:18:26 -07:00
|
|
|
|
|
|
|
# hello
|
|
|
|
|
|
|
|
|
2016-10-23 16:43:52 -07:00
|
|
|
", "");
|
2016-10-26 20:54:44 -07:00
|
|
|
}
|
2016-10-23 16:43:52 -07:00
|
|
|
|
2016-11-12 09:15:13 -08:00
|
|
|
#[test]
|
|
|
|
fn parse_string_default() {
|
|
|
|
parse_summary(r#"
|
|
|
|
|
|
|
|
foo a="b\t":
|
|
|
|
|
|
|
|
|
2016-11-12 11:40:52 -08:00
|
|
|
"#, r#"foo a='b\t':"#);
|
2016-11-12 09:15:13 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn parse_raw_string_default() {
|
|
|
|
parse_summary(r#"
|
|
|
|
|
|
|
|
foo a='b\t':
|
|
|
|
|
|
|
|
|
2016-11-12 11:40:52 -08:00
|
|
|
"#, r#"foo a='b\\t':"#);
|
2016-11-12 09:15:13 -08:00
|
|
|
}
|
|
|
|
|
2016-10-30 16:15:18 -07:00
|
|
|
#[test]
|
|
|
|
fn parse_export() {
|
|
|
|
parse_summary(r#"
|
|
|
|
export a = "hello"
|
|
|
|
|
|
|
|
"#, r#"export a = "hello""#);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-10-26 20:54:44 -07:00
|
|
|
#[test]
|
|
|
|
fn parse_complex() {
|
2016-10-23 16:43:52 -07:00
|
|
|
parse_summary("
|
|
|
|
x:
|
|
|
|
y:
|
|
|
|
z:
|
2016-10-27 18:48:55 -07:00
|
|
|
foo = \"xx\"
|
2016-10-27 00:13:10 -07:00
|
|
|
bar = foo
|
2016-10-25 19:11:58 -07:00
|
|
|
goodbye = \"y\"
|
2016-10-23 16:43:52 -07:00
|
|
|
hello a b c : x y z #hello
|
|
|
|
#! blah
|
|
|
|
#blarg
|
2016-10-27 00:13:10 -07:00
|
|
|
{{ foo + bar}}abc{{ goodbye\t + \"x\" }}xyz
|
2016-10-23 16:43:52 -07:00
|
|
|
1
|
|
|
|
2
|
|
|
|
3
|
2016-10-29 21:51:39 -07:00
|
|
|
", "bar = foo
|
2016-10-28 16:41:46 -07:00
|
|
|
|
2016-10-29 21:51:39 -07:00
|
|
|
foo = \"xx\"
|
2016-10-28 16:41:46 -07:00
|
|
|
|
2016-10-29 21:51:39 -07:00
|
|
|
goodbye = \"y\"
|
2016-10-28 16:41:46 -07:00
|
|
|
|
2016-10-25 19:11:58 -07:00
|
|
|
hello a b c: x y z
|
2016-10-23 16:43:52 -07:00
|
|
|
#! blah
|
|
|
|
#blarg
|
2016-10-29 21:51:39 -07:00
|
|
|
{{foo + bar}}abc{{goodbye + \"x\"}}xyz
|
2016-10-23 16:43:52 -07:00
|
|
|
1
|
|
|
|
2
|
|
|
|
3
|
2016-10-28 16:41:46 -07:00
|
|
|
|
2016-10-23 16:43:52 -07:00
|
|
|
x:
|
2016-10-28 16:41:46 -07:00
|
|
|
|
2016-10-23 16:43:52 -07:00
|
|
|
y:
|
2016-10-28 16:41:46 -07:00
|
|
|
|
2016-10-25 19:11:58 -07:00
|
|
|
z:");
|
2016-10-26 20:54:44 -07:00
|
|
|
}
|
2016-10-23 16:43:52 -07:00
|
|
|
|
2016-11-02 22:01:38 -07:00
|
|
|
#[test]
|
|
|
|
fn parse_shebang() {
|
|
|
|
parse_summary("
|
|
|
|
practicum = 'hello'
|
|
|
|
install:
|
|
|
|
\t#!/bin/sh
|
|
|
|
\tif [[ -f {{practicum}} ]]; then
|
|
|
|
\t\treturn
|
|
|
|
\tfi
|
|
|
|
", "practicum = \"hello\"
|
|
|
|
|
|
|
|
install:
|
|
|
|
#!/bin/sh
|
|
|
|
if [[ -f {{practicum}} ]]; then
|
|
|
|
\treturn
|
|
|
|
fi"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2016-10-26 20:54:44 -07:00
|
|
|
#[test]
|
|
|
|
fn parse_assignments() {
|
2016-10-25 19:11:58 -07:00
|
|
|
parse_summary(
|
|
|
|
r#"a = "0"
|
|
|
|
c = a + b + a + b
|
|
|
|
b = "1"
|
|
|
|
"#,
|
2016-10-23 16:43:52 -07:00
|
|
|
|
2016-10-29 21:51:39 -07:00
|
|
|
r#"a = "0"
|
2016-10-28 16:41:46 -07:00
|
|
|
|
2016-10-29 21:51:39 -07:00
|
|
|
b = "1"
|
2016-10-28 16:41:46 -07:00
|
|
|
|
2016-10-29 21:51:39 -07:00
|
|
|
c = a + b + a + b"#);
|
2016-10-23 16:43:52 -07:00
|
|
|
}
|
|
|
|
|
2016-10-29 23:39:12 -07:00
|
|
|
#[test]
|
|
|
|
fn parse_assignment_backticks() {
|
|
|
|
parse_summary(
|
|
|
|
"a = `echo hello`
|
|
|
|
c = a + b + a + b
|
|
|
|
b = `echo goodbye`",
|
|
|
|
|
|
|
|
"a = `echo hello`
|
|
|
|
|
|
|
|
b = `echo goodbye`
|
|
|
|
|
|
|
|
c = a + b + a + b");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn parse_interpolation_backticks() {
|
|
|
|
parse_summary(
|
|
|
|
r#"a:
|
|
|
|
echo {{ `echo hello` + "blarg" }} {{ `echo bob` }}"#,
|
|
|
|
r#"a:
|
|
|
|
echo {{`echo hello` + "blarg"}} {{`echo bob`}}"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2016-10-23 16:43:52 -07:00
|
|
|
#[test]
|
|
|
|
fn missing_colon() {
|
|
|
|
let text = "a b c\nd e f";
|
2016-11-12 14:24:33 -08:00
|
|
|
parse_error(text, CompileError {
|
2016-10-23 16:43:52 -07:00
|
|
|
text: text,
|
|
|
|
index: 5,
|
|
|
|
line: 0,
|
|
|
|
column: 5,
|
|
|
|
width: Some(1),
|
|
|
|
kind: ErrorKind::UnexpectedToken{expected: vec![Name, Colon], found: Eol},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-11-12 09:15:13 -08:00
|
|
|
#[test]
|
|
|
|
fn missing_default_eol() {
|
|
|
|
let text = "hello arg=\n";
|
2016-11-12 14:24:33 -08:00
|
|
|
parse_error(text, CompileError {
|
2016-11-12 09:15:13 -08:00
|
|
|
text: text,
|
|
|
|
index: 10,
|
|
|
|
line: 0,
|
|
|
|
column: 10,
|
|
|
|
width: Some(1),
|
|
|
|
kind: ErrorKind::UnexpectedToken{expected: vec![StringToken, RawString], found: Eol},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn missing_default_eof() {
|
|
|
|
let text = "hello arg=";
|
2016-11-12 14:24:33 -08:00
|
|
|
parse_error(text, CompileError {
|
2016-11-12 09:15:13 -08:00
|
|
|
text: text,
|
|
|
|
index: 10,
|
|
|
|
line: 0,
|
|
|
|
column: 10,
|
|
|
|
width: Some(0),
|
|
|
|
kind: ErrorKind::UnexpectedToken{expected: vec![StringToken, RawString], found: Eof},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn missing_default_colon() {
|
|
|
|
let text = "hello arg=:";
|
2016-11-12 14:24:33 -08:00
|
|
|
parse_error(text, CompileError {
|
2016-11-12 09:15:13 -08:00
|
|
|
text: text,
|
|
|
|
index: 10,
|
|
|
|
line: 0,
|
|
|
|
column: 10,
|
|
|
|
width: Some(1),
|
|
|
|
kind: ErrorKind::UnexpectedToken{expected: vec![StringToken, RawString], found: Colon},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn missing_default_backtick() {
|
|
|
|
let text = "hello arg=`hello`";
|
2016-11-12 14:24:33 -08:00
|
|
|
parse_error(text, CompileError {
|
2016-11-12 09:15:13 -08:00
|
|
|
text: text,
|
|
|
|
index: 10,
|
|
|
|
line: 0,
|
|
|
|
column: 10,
|
|
|
|
width: Some(7),
|
|
|
|
kind: ErrorKind::UnexpectedToken{expected: vec![StringToken, RawString], found: Backtick},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn required_after_default() {
|
|
|
|
let text = "hello arg='foo' bar:";
|
2016-11-12 14:24:33 -08:00
|
|
|
parse_error(text, CompileError {
|
2016-11-12 09:15:13 -08:00
|
|
|
text: text,
|
|
|
|
index: 16,
|
|
|
|
line: 0,
|
|
|
|
column: 16,
|
|
|
|
width: Some(3),
|
|
|
|
kind: ErrorKind::RequiredParameterFollowsDefaultParameter{parameter: "bar"},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-10-23 16:43:52 -07:00
|
|
|
#[test]
|
|
|
|
fn missing_eol() {
|
|
|
|
let text = "a b c: z =";
|
2016-11-12 14:24:33 -08:00
|
|
|
parse_error(text, CompileError {
|
2016-10-23 16:43:52 -07:00
|
|
|
text: text,
|
|
|
|
index: 9,
|
|
|
|
line: 0,
|
|
|
|
column: 9,
|
|
|
|
width: Some(1),
|
|
|
|
kind: ErrorKind::UnexpectedToken{expected: vec![Name, Eol, Eof], found: Equals},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn eof_test() {
|
2016-10-28 16:41:46 -07:00
|
|
|
parse_summary("x:\ny:\nz:\na b c: x y z", "a b c: x y z\n\nx:\n\ny:\n\nz:");
|
2016-10-23 16:43:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2016-10-31 19:11:27 -07:00
|
|
|
fn duplicate_parameter() {
|
2016-10-23 16:43:52 -07:00
|
|
|
let text = "a b b:";
|
2016-11-12 14:24:33 -08:00
|
|
|
parse_error(text, CompileError {
|
2016-10-23 16:43:52 -07:00
|
|
|
text: text,
|
|
|
|
index: 4,
|
|
|
|
line: 0,
|
|
|
|
column: 4,
|
|
|
|
width: Some(1),
|
2016-10-31 19:11:27 -07:00
|
|
|
kind: ErrorKind::DuplicateParameter{recipe: "a", parameter: "b"}
|
2016-10-23 16:43:52 -07:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-10-25 19:11:58 -07:00
|
|
|
#[test]
|
2016-10-31 19:11:27 -07:00
|
|
|
fn parameter_shadows_varible() {
|
2016-10-25 19:11:58 -07:00
|
|
|
let text = "foo = \"h\"\na foo:";
|
2016-11-12 14:24:33 -08:00
|
|
|
parse_error(text, CompileError {
|
2016-10-25 19:11:58 -07:00
|
|
|
text: text,
|
|
|
|
index: 12,
|
|
|
|
line: 1,
|
|
|
|
column: 2,
|
|
|
|
width: Some(3),
|
2016-10-31 19:11:27 -07:00
|
|
|
kind: ErrorKind::ParameterShadowsVariable{parameter: "foo"}
|
2016-10-25 19:11:58 -07:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-10-29 00:14:41 -07:00
|
|
|
#[test]
|
2016-10-31 19:11:27 -07:00
|
|
|
fn dependency_has_parameters() {
|
2016-10-29 00:14:41 -07:00
|
|
|
let text = "foo arg:\nb: foo";
|
2016-11-12 14:24:33 -08:00
|
|
|
parse_error(text, CompileError {
|
2016-10-29 00:14:41 -07:00
|
|
|
text: text,
|
|
|
|
index: 12,
|
|
|
|
line: 1,
|
|
|
|
column: 3,
|
|
|
|
width: Some(3),
|
2016-10-31 19:11:27 -07:00
|
|
|
kind: ErrorKind::DependencyHasParameters{recipe: "b", dependency: "foo"}
|
2016-10-29 00:14:41 -07:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-10-23 16:43:52 -07:00
|
|
|
#[test]
|
|
|
|
fn duplicate_dependency() {
|
|
|
|
let text = "a b c: b c z z";
|
2016-11-12 14:24:33 -08:00
|
|
|
parse_error(text, CompileError {
|
2016-10-23 16:43:52 -07:00
|
|
|
text: text,
|
|
|
|
index: 13,
|
|
|
|
line: 0,
|
|
|
|
column: 13,
|
|
|
|
width: Some(1),
|
|
|
|
kind: ErrorKind::DuplicateDependency{recipe: "a", dependency: "z"}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn duplicate_recipe() {
|
|
|
|
let text = "a:\nb:\na:";
|
2016-11-12 14:24:33 -08:00
|
|
|
parse_error(text, CompileError {
|
2016-10-23 16:43:52 -07:00
|
|
|
text: text,
|
|
|
|
index: 6,
|
|
|
|
line: 2,
|
|
|
|
column: 0,
|
|
|
|
width: Some(1),
|
|
|
|
kind: ErrorKind::DuplicateRecipe{recipe: "a", first: 0}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2016-10-25 19:11:58 -07:00
|
|
|
fn circular_recipe_dependency() {
|
2016-10-23 16:43:52 -07:00
|
|
|
let text = "a: b\nb: a";
|
2016-11-12 14:24:33 -08:00
|
|
|
parse_error(text, CompileError {
|
2016-10-23 16:43:52 -07:00
|
|
|
text: text,
|
|
|
|
index: 8,
|
|
|
|
line: 1,
|
|
|
|
column: 3,
|
|
|
|
width: Some(1),
|
2016-10-25 19:11:58 -07:00
|
|
|
kind: ErrorKind::CircularRecipeDependency{recipe: "b", circle: vec!["a", "b", "a"]}
|
2016-10-23 16:43:52 -07:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-10-23 23:39:56 -07:00
|
|
|
#[test]
|
2016-10-25 19:11:58 -07:00
|
|
|
fn circular_variable_dependency() {
|
|
|
|
let text = "a = b\nb = a";
|
2016-11-12 14:24:33 -08:00
|
|
|
parse_error(text, CompileError {
|
2016-10-25 19:11:58 -07:00
|
|
|
text: text,
|
|
|
|
index: 0,
|
|
|
|
line: 0,
|
|
|
|
column: 0,
|
|
|
|
width: Some(1),
|
|
|
|
kind: ErrorKind::CircularVariableDependency{variable: "a", circle: vec!["a", "b", "a"]}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn duplicate_variable() {
|
|
|
|
let text = "a = \"0\"\na = \"0\"";
|
2016-11-12 14:24:33 -08:00
|
|
|
parse_error(text, CompileError {
|
2016-10-25 19:11:58 -07:00
|
|
|
text: text,
|
|
|
|
index: 8,
|
|
|
|
line: 1,
|
|
|
|
column: 0,
|
|
|
|
width: Some(1),
|
|
|
|
kind: ErrorKind::DuplicateVariable{variable: "a"}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-10-28 00:06:36 -07:00
|
|
|
#[test]
|
|
|
|
fn unterminated_string() {
|
|
|
|
let text = r#"a = ""#;
|
2016-11-12 14:24:33 -08:00
|
|
|
parse_error(text, CompileError {
|
2016-10-28 00:06:36 -07:00
|
|
|
text: text,
|
|
|
|
index: 3,
|
|
|
|
line: 0,
|
|
|
|
column: 3,
|
|
|
|
width: None,
|
|
|
|
kind: ErrorKind::UnterminatedString,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn unterminated_string_with_escapes() {
|
|
|
|
let text = r#"a = "\n\t\r\"\\"#;
|
2016-11-12 14:24:33 -08:00
|
|
|
parse_error(text, CompileError {
|
2016-10-28 00:06:36 -07:00
|
|
|
text: text,
|
|
|
|
index: 3,
|
|
|
|
line: 0,
|
|
|
|
column: 3,
|
|
|
|
width: None,
|
|
|
|
kind: ErrorKind::UnterminatedString,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn string_quote_escape() {
|
|
|
|
parse_summary(
|
|
|
|
r#"a = "hello\"""#,
|
2016-10-29 21:51:39 -07:00
|
|
|
r#"a = "hello\"""#
|
2016-10-28 00:06:36 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn string_escapes() {
|
|
|
|
parse_summary(
|
2016-10-29 21:51:39 -07:00
|
|
|
r#"a = "\n\t\r\"\\""#,
|
|
|
|
r#"a = "\n\t\r\"\\""#
|
2016-10-28 00:06:36 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2016-10-29 00:14:41 -07:00
|
|
|
#[test]
|
2016-10-31 19:11:27 -07:00
|
|
|
fn parameters() {
|
2016-10-29 00:14:41 -07:00
|
|
|
parse_summary(
|
|
|
|
"a b c:
|
|
|
|
{{b}} {{c}}",
|
|
|
|
"a b c:
|
2016-10-29 21:51:39 -07:00
|
|
|
{{b}} {{c}}",
|
2016-10-29 00:14:41 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2016-10-25 19:11:58 -07:00
|
|
|
#[test]
|
|
|
|
fn self_recipe_dependency() {
|
2016-10-23 23:39:56 -07:00
|
|
|
let text = "a: a";
|
2016-11-12 14:24:33 -08:00
|
|
|
parse_error(text, CompileError {
|
2016-10-23 23:39:56 -07:00
|
|
|
text: text,
|
|
|
|
index: 3,
|
|
|
|
line: 0,
|
|
|
|
column: 3,
|
|
|
|
width: Some(1),
|
2016-10-25 19:11:58 -07:00
|
|
|
kind: ErrorKind::CircularRecipeDependency{recipe: "a", circle: vec!["a", "a"]}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn self_variable_dependency() {
|
|
|
|
let text = "a = a";
|
2016-11-12 14:24:33 -08:00
|
|
|
parse_error(text, CompileError {
|
2016-10-25 19:11:58 -07:00
|
|
|
text: text,
|
|
|
|
index: 0,
|
|
|
|
line: 0,
|
|
|
|
column: 0,
|
|
|
|
width: Some(1),
|
|
|
|
kind: ErrorKind::CircularVariableDependency{variable: "a", circle: vec!["a", "a"]}
|
2016-10-23 23:39:56 -07:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-10-23 16:43:52 -07:00
|
|
|
#[test]
|
|
|
|
fn unknown_dependency() {
|
|
|
|
let text = "a: b";
|
2016-11-12 14:24:33 -08:00
|
|
|
parse_error(text, CompileError {
|
2016-10-23 16:43:52 -07:00
|
|
|
text: text,
|
|
|
|
index: 3,
|
|
|
|
line: 0,
|
|
|
|
column: 3,
|
|
|
|
width: Some(1),
|
|
|
|
kind: ErrorKind::UnknownDependency{recipe: "a", unknown: "b"}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn mixed_leading_whitespace() {
|
|
|
|
let text = "a:\n\t echo hello";
|
2016-11-12 14:24:33 -08:00
|
|
|
parse_error(text, CompileError {
|
2016-10-23 16:43:52 -07:00
|
|
|
text: text,
|
|
|
|
index: 3,
|
|
|
|
line: 1,
|
|
|
|
column: 0,
|
|
|
|
width: None,
|
|
|
|
kind: ErrorKind::MixedLeadingWhitespace{whitespace: "\t "}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2016-10-30 03:08:28 -07:00
|
|
|
fn conjoin_or() {
|
2016-10-23 16:43:52 -07:00
|
|
|
assert_eq!("1", super::Or(&[1 ]).to_string());
|
|
|
|
assert_eq!("1 or 2", super::Or(&[1,2 ]).to_string());
|
|
|
|
assert_eq!("1, 2, or 3", super::Or(&[1,2,3 ]).to_string());
|
|
|
|
assert_eq!("1, 2, 3, or 4", super::Or(&[1,2,3,4]).to_string());
|
|
|
|
}
|
|
|
|
|
2016-10-30 03:08:28 -07:00
|
|
|
#[test]
|
|
|
|
fn conjoin_and() {
|
|
|
|
assert_eq!("1", super::And(&[1 ]).to_string());
|
|
|
|
assert_eq!("1 and 2", super::And(&[1,2 ]).to_string());
|
|
|
|
assert_eq!("1, 2, and 3", super::And(&[1,2,3 ]).to_string());
|
|
|
|
assert_eq!("1, 2, 3, and 4", super::And(&[1,2,3,4]).to_string());
|
|
|
|
}
|
|
|
|
|
2016-11-12 09:15:13 -08:00
|
|
|
#[test]
|
|
|
|
fn range() {
|
|
|
|
assert!(super::contains(&(0..1), 0));
|
|
|
|
assert!(super::contains(&(10..20), 15));
|
|
|
|
assert!(!super::contains(&(0..0), 0));
|
|
|
|
assert!(!super::contains(&(1..10), 0));
|
|
|
|
assert!(!super::contains(&(1..10), 10));
|
|
|
|
}
|
|
|
|
|
2016-10-23 16:43:52 -07:00
|
|
|
#[test]
|
|
|
|
fn unknown_recipes() {
|
2016-11-05 00:31:38 -07:00
|
|
|
match parse_success("a:\nb:\nc:").run(&["a", "x", "y", "z"], &Default::default()).unwrap_err() {
|
2016-11-12 12:36:12 -08:00
|
|
|
RunError::UnknownRecipes{recipes, suggestion} => {
|
|
|
|
assert_eq!(recipes, &["x", "y", "z"]);
|
|
|
|
assert_eq!(suggestion, None);
|
|
|
|
}
|
2016-10-30 03:08:28 -07:00
|
|
|
other => panic!("expected an unknown recipe error, but got: {}", other),
|
2016-10-23 16:43:52 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn extra_whitespace() {
|
|
|
|
let text = "a:\n blah\n blarg";
|
2016-11-12 14:24:33 -08:00
|
|
|
parse_error(text, CompileError {
|
2016-10-23 16:43:52 -07:00
|
|
|
text: text,
|
|
|
|
index: 10,
|
|
|
|
line: 2,
|
|
|
|
column: 1,
|
|
|
|
width: Some(6),
|
|
|
|
kind: ErrorKind::ExtraLeadingWhitespace
|
|
|
|
});
|
|
|
|
|
|
|
|
// extra leading whitespace is okay in a shebang recipe
|
|
|
|
parse_success("a:\n #!\n print(1)");
|
|
|
|
}
|
|
|
|
|
2016-10-27 09:44:07 -07:00
|
|
|
#[test]
|
|
|
|
fn interpolation_outside_of_recipe() {
|
|
|
|
let text = "{{";
|
2016-11-12 14:24:33 -08:00
|
|
|
parse_error(text, CompileError {
|
2016-10-27 09:44:07 -07:00
|
|
|
text: text,
|
|
|
|
index: 0,
|
|
|
|
line: 0,
|
|
|
|
column: 0,
|
|
|
|
width: Some(2),
|
2016-11-12 16:12:00 -08:00
|
|
|
kind: ErrorKind::UnexpectedToken{expected: vec![Name, At], found: InterpolationStart},
|
2016-10-27 09:44:07 -07:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-10-23 23:38:49 -07:00
|
|
|
#[test]
|
2016-10-26 20:54:44 -07:00
|
|
|
fn unclosed_interpolation_delimiter() {
|
2016-10-27 00:13:10 -07:00
|
|
|
let text = "a:\n echo {{ foo";
|
2016-11-12 14:24:33 -08:00
|
|
|
parse_error(text, CompileError {
|
2016-10-23 23:38:49 -07:00
|
|
|
text: text,
|
2016-10-27 00:13:10 -07:00
|
|
|
index: 15,
|
2016-10-23 23:38:49 -07:00
|
|
|
line: 1,
|
2016-10-27 00:13:10 -07:00
|
|
|
column: 12,
|
|
|
|
width: Some(0),
|
|
|
|
kind: ErrorKind::UnexpectedToken{expected: vec![Plus, Eol, InterpolationEnd], found: Dedent},
|
2016-10-26 20:54:44 -07:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn unknown_expression_variable() {
|
|
|
|
let text = "x = yy";
|
2016-11-12 14:24:33 -08:00
|
|
|
parse_error(text, CompileError {
|
2016-10-26 20:54:44 -07:00
|
|
|
text: text,
|
|
|
|
index: 4,
|
|
|
|
line: 0,
|
|
|
|
column: 4,
|
|
|
|
width: Some(2),
|
2016-10-29 22:56:47 -07:00
|
|
|
kind: ErrorKind::UndefinedVariable{variable: "yy"},
|
2016-10-26 20:54:44 -07:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn unknown_interpolation_variable() {
|
|
|
|
let text = "x:\n {{ hello}}";
|
2016-11-12 14:24:33 -08:00
|
|
|
parse_error(text, CompileError {
|
2016-10-26 20:54:44 -07:00
|
|
|
text: text,
|
|
|
|
index: 9,
|
|
|
|
line: 1,
|
|
|
|
column: 6,
|
|
|
|
width: Some(5),
|
2016-10-29 22:56:47 -07:00
|
|
|
kind: ErrorKind::UndefinedVariable{variable: "hello"},
|
2016-10-26 20:54:44 -07:00
|
|
|
});
|
2016-10-27 00:13:10 -07:00
|
|
|
}
|
2016-10-26 20:54:44 -07:00
|
|
|
|
2016-10-27 00:13:10 -07:00
|
|
|
#[test]
|
|
|
|
fn unknown_second_interpolation_variable() {
|
|
|
|
let text = "wtf=\"x\"\nx:\n echo\n foo {{wtf}} {{ lol }}";
|
2016-11-12 14:24:33 -08:00
|
|
|
parse_error(text, CompileError {
|
2016-10-27 00:13:10 -07:00
|
|
|
text: text,
|
|
|
|
index: 33,
|
|
|
|
line: 3,
|
|
|
|
column: 16,
|
|
|
|
width: Some(3),
|
2016-10-29 22:56:47 -07:00
|
|
|
kind: ErrorKind::UndefinedVariable{variable: "lol"},
|
2016-10-27 00:13:10 -07:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2016-10-29 00:14:41 -07:00
|
|
|
fn tokenize_order() {
|
2016-10-27 00:13:10 -07:00
|
|
|
let text = r"
|
|
|
|
b: a
|
|
|
|
@mv a b
|
|
|
|
|
|
|
|
a:
|
|
|
|
@touch F
|
|
|
|
@touch a
|
|
|
|
|
|
|
|
d: c
|
|
|
|
@rm c
|
|
|
|
|
|
|
|
c: b
|
|
|
|
@mv b c";
|
|
|
|
tokenize_success(text, "$N:N$>^_$$<N:$>^_$^_$$<N:N$>^_$$<N:N$>^_<.");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn run_shebang() {
|
|
|
|
// this test exists to make sure that shebang recipes
|
|
|
|
// run correctly. although this script is still
|
2016-11-05 01:19:54 -07:00
|
|
|
// executed by a shell its behavior depends on the value of a
|
2016-10-27 00:13:10 -07:00
|
|
|
// variable and continuing even though a command fails,
|
|
|
|
// whereas in plain recipes variables are not available
|
|
|
|
// in subsequent lines and execution stops when a line
|
|
|
|
// fails
|
|
|
|
let text = "
|
|
|
|
a:
|
|
|
|
#!/usr/bin/env sh
|
|
|
|
code=200
|
2016-11-05 01:19:54 -07:00
|
|
|
x() { return $code; }
|
2016-10-30 19:37:22 -07:00
|
|
|
x
|
|
|
|
x
|
2016-10-27 00:13:10 -07:00
|
|
|
";
|
|
|
|
|
2016-11-05 00:31:38 -07:00
|
|
|
match parse_success(text).run(&["a"], &Default::default()).unwrap_err() {
|
2016-10-30 03:08:28 -07:00
|
|
|
RunError::Code{recipe, code} => {
|
2016-10-27 00:13:10 -07:00
|
|
|
assert_eq!(recipe, "a");
|
|
|
|
assert_eq!(code, 200);
|
|
|
|
},
|
|
|
|
other => panic!("expected an code run error, but got: {}", other),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn code_error() {
|
2016-11-05 01:19:54 -07:00
|
|
|
match parse_success("fail:\n @exit 100")
|
2016-11-05 00:31:38 -07:00
|
|
|
.run(&["fail"], &Default::default()).unwrap_err() {
|
2016-10-30 03:08:28 -07:00
|
|
|
RunError::Code{recipe, code} => {
|
2016-10-27 00:13:10 -07:00
|
|
|
assert_eq!(recipe, "fail");
|
|
|
|
assert_eq!(code, 100);
|
|
|
|
},
|
2016-10-30 03:08:28 -07:00
|
|
|
other => panic!("expected a code run error, but got: {}", other),
|
2016-10-27 00:13:10 -07:00
|
|
|
}
|
2016-10-23 23:38:49 -07:00
|
|
|
}
|
2016-10-29 00:14:41 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn run_args() {
|
|
|
|
let text = r#"
|
|
|
|
a return code:
|
2016-11-05 01:19:54 -07:00
|
|
|
@x() { {{return}} {{code + "0"}}; }; x"#;
|
2016-10-29 00:14:41 -07:00
|
|
|
|
2016-11-05 00:31:38 -07:00
|
|
|
match parse_success(text).run(&["a", "return", "15"], &Default::default()).unwrap_err() {
|
2016-10-30 03:08:28 -07:00
|
|
|
RunError::Code{recipe, code} => {
|
2016-10-29 00:14:41 -07:00
|
|
|
assert_eq!(recipe, "a");
|
|
|
|
assert_eq!(code, 150);
|
|
|
|
},
|
|
|
|
other => panic!("expected an code run error, but got: {}", other),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-29 00:55:47 -07:00
|
|
|
#[test]
|
2016-11-12 09:15:13 -08:00
|
|
|
fn missing_some_arguments() {
|
2016-11-05 00:31:38 -07:00
|
|
|
match parse_success("a b c d:").run(&["a", "b", "c"], &Default::default()).unwrap_err() {
|
2016-11-12 09:15:13 -08:00
|
|
|
RunError::ArgumentCountMismatch{recipe, found, min, max} => {
|
2016-10-29 00:55:47 -07:00
|
|
|
assert_eq!(recipe, "a");
|
|
|
|
assert_eq!(found, 2);
|
2016-11-12 09:15:13 -08:00
|
|
|
assert_eq!(min, 3);
|
|
|
|
assert_eq!(max, 3);
|
2016-10-29 00:55:47 -07:00
|
|
|
},
|
|
|
|
other => panic!("expected an code run error, but got: {}", other),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2016-11-12 09:15:13 -08:00
|
|
|
fn missing_all_arguments() {
|
2016-11-05 00:31:38 -07:00
|
|
|
match parse_success("a b c d:\n echo {{b}}{{c}}{{d}}")
|
|
|
|
.run(&["a"], &Default::default()).unwrap_err() {
|
2016-11-12 09:15:13 -08:00
|
|
|
RunError::ArgumentCountMismatch{recipe, found, min, max} => {
|
|
|
|
assert_eq!(recipe, "a");
|
|
|
|
assert_eq!(found, 0);
|
|
|
|
assert_eq!(min, 3);
|
|
|
|
assert_eq!(max, 3);
|
|
|
|
},
|
|
|
|
other => panic!("expected an code run error, but got: {}", other),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn missing_some_defaults() {
|
|
|
|
match parse_success("a b c d='hello':").run(&["a", "b"], &Default::default()).unwrap_err() {
|
|
|
|
RunError::ArgumentCountMismatch{recipe, found, min, max} => {
|
|
|
|
assert_eq!(recipe, "a");
|
|
|
|
assert_eq!(found, 1);
|
|
|
|
assert_eq!(min, 2);
|
|
|
|
assert_eq!(max, 3);
|
|
|
|
},
|
|
|
|
other => panic!("expected an code run error, but got: {}", other),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn missing_all_defaults() {
|
|
|
|
match parse_success("a b c='r' d='h':").run(&["a"], &Default::default()).unwrap_err() {
|
|
|
|
RunError::ArgumentCountMismatch{recipe, found, min, max} => {
|
2016-10-29 00:55:47 -07:00
|
|
|
assert_eq!(recipe, "a");
|
|
|
|
assert_eq!(found, 0);
|
2016-11-12 09:15:13 -08:00
|
|
|
assert_eq!(min, 1);
|
|
|
|
assert_eq!(max, 3);
|
2016-10-29 00:55:47 -07:00
|
|
|
},
|
|
|
|
other => panic!("expected an code run error, but got: {}", other),
|
|
|
|
}
|
|
|
|
}
|
2016-10-30 01:27:05 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn backtick_code() {
|
2016-11-05 01:19:54 -07:00
|
|
|
match parse_success("a:\n echo {{`f() { return 100; }; f`}}")
|
2016-11-05 00:31:38 -07:00
|
|
|
.run(&["a"], &Default::default()).unwrap_err() {
|
2016-10-30 03:08:28 -07:00
|
|
|
RunError::BacktickCode{code, token} => {
|
2016-10-30 01:27:05 -07:00
|
|
|
assert_eq!(code, 100);
|
2016-11-05 01:19:54 -07:00
|
|
|
assert_eq!(token.lexeme, "`f() { return 100; }; f`");
|
2016-10-30 01:27:05 -07:00
|
|
|
},
|
|
|
|
other => panic!("expected an code run error, but got: {}", other),
|
|
|
|
}
|
|
|
|
}
|
2016-10-30 03:08:28 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn unknown_overrides() {
|
2016-11-05 00:31:38 -07:00
|
|
|
let mut options: RunOptions = Default::default();
|
|
|
|
options.overrides.insert("foo", "bar");
|
|
|
|
options.overrides.insert("baz", "bob");
|
2016-11-05 01:19:54 -07:00
|
|
|
match parse_success("a:\n echo {{`f() { return 100; }; f`}}")
|
|
|
|
.run(&["a"], &options).unwrap_err() {
|
2016-10-30 03:08:28 -07:00
|
|
|
RunError::UnknownOverrides{overrides} => {
|
|
|
|
assert_eq!(overrides, &["baz", "foo"]);
|
|
|
|
},
|
|
|
|
other => panic!("expected an code run error, but got: {}", other),
|
|
|
|
}
|
|
|
|
}
|
2016-10-31 21:53:31 -07:00
|
|
|
|
2016-11-05 01:19:54 -07:00
|
|
|
#[test]
|
|
|
|
fn export_assignment_backtick() {
|
|
|
|
let text = r#"
|
|
|
|
export exported_variable = "A"
|
|
|
|
b = `echo $exported_variable`
|
|
|
|
|
|
|
|
recipe:
|
|
|
|
echo {{b}}
|
|
|
|
"#;
|
|
|
|
|
|
|
|
let options = RunOptions {
|
|
|
|
quiet: true,
|
|
|
|
..Default::default()
|
|
|
|
};
|
|
|
|
|
|
|
|
match parse_success(text).run(&["recipe"], &options).unwrap_err() {
|
|
|
|
RunError::BacktickCode{code: _, token} => {
|
|
|
|
assert_eq!(token.lexeme, "`echo $exported_variable`");
|
|
|
|
},
|
|
|
|
other => panic!("expected a backtick code errror, but got: {}", other),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn export_failure() {
|
|
|
|
let text = r#"
|
|
|
|
export foo = "a"
|
|
|
|
baz = "c"
|
|
|
|
export bar = "b"
|
|
|
|
export abc = foo + bar + baz
|
|
|
|
|
|
|
|
wut:
|
|
|
|
echo $foo $bar $baz
|
|
|
|
"#;
|
|
|
|
|
|
|
|
let options = RunOptions {
|
|
|
|
quiet: true,
|
|
|
|
..Default::default()
|
|
|
|
};
|
|
|
|
|
|
|
|
match parse_success(text).run(&["wut"], &options).unwrap_err() {
|
|
|
|
RunError::Code{code: _, recipe} => {
|
|
|
|
assert_eq!(recipe, "wut");
|
|
|
|
},
|
|
|
|
other => panic!("expected a recipe code errror, but got: {}", other),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-31 21:53:31 -07:00
|
|
|
#[test]
|
|
|
|
fn readme_test() {
|
|
|
|
let mut justfiles = vec![];
|
|
|
|
let mut current = None;
|
|
|
|
|
|
|
|
for line in brev::slurp("README.md").lines() {
|
|
|
|
if let Some(mut justfile) = current {
|
|
|
|
if line == "```" {
|
|
|
|
justfiles.push(justfile);
|
|
|
|
current = None;
|
|
|
|
} else {
|
|
|
|
justfile += line;
|
|
|
|
justfile += "\n";
|
|
|
|
current = Some(justfile);
|
|
|
|
}
|
|
|
|
} else if line == "```make" {
|
|
|
|
current = Some(String::new());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for justfile in justfiles {
|
|
|
|
parse_success(&justfile);
|
|
|
|
}
|
|
|
|
}
|