Refactor and rename test macros (#415)

This commit is contained in:
Casey Rodarmor 2019-04-19 02:17:43 -07:00 committed by GitHub
parent d065d1c54f
commit 415c84ea39
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 295 additions and 260 deletions

59
Cargo.lock generated
View File

@ -96,6 +96,25 @@ dependencies = [
"vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "colored-diff"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ctor"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.31 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ctrlc"
version = "3.1.1"
@ -105,6 +124,11 @@ dependencies = [
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "difference"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "dotenv"
version = "0.13.0"
@ -180,6 +204,14 @@ dependencies = [
"quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "itertools"
version = "0.7.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "itertools"
version = "0.8.0"
@ -197,6 +229,7 @@ dependencies = [
"atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"brev 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
"colored-diff 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"ctrlc 3.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"dotenv 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
"edit-distance 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -206,6 +239,7 @@ dependencies = [
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"target 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
@ -246,6 +280,25 @@ dependencies = [
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "output_vt100"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "pretty_assertions"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ctor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
"difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"output_vt100 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "proc-macro2"
version = "0.4.27"
@ -499,7 +552,10 @@ dependencies = [
"checksum cc 1.0.35 (registry+https://github.com/rust-lang/crates.io-index)" = "5e5f3fee5eeb60324c2781f1e41286bdee933850fff9b3c672587fed5ec58c83"
"checksum cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11d43355396e872eefb45ce6342e4374ed7bc2b3a502d1b28e36d6e23c05d1f4"
"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
"checksum colored-diff 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8fcbd90537dc19162289fbce7e95c882dded30f4ed41044cf1470612528d7163"
"checksum ctor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3b4c17619643c1252b5f690084b82639dd7fac141c57c8e77a00e0148132092c"
"checksum ctrlc 3.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "630391922b1b893692c6334369ff528dcc3a9d8061ccf4c803aa8f83cb13db5e"
"checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
"checksum dotenv 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0d0a1279c96732bc6800ce6337b6a614697b0e74ae058dc03c62ebeb78b4d86"
"checksum edit-distance 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bbbaaaf38131deb9ca518a274a45bfdb8771f139517b073b16c2d3d32ae5037b"
"checksum either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5527cfe0d098f36e3f8839852688e63c8fff1c90b2b405aef730615f9a7bcf7b"
@ -510,12 +566,15 @@ dependencies = [
"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
"checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114"
"checksum itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)" = "0d47946d458e94a1b7bcabbf6521ea7c037062c81f534615abcad76e84d4970d"
"checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358"
"checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14"
"checksum libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)" = "bedcc7a809076656486ffe045abeeac163da1b558e963a31e29fbfbeba916917"
"checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6"
"checksum memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39"
"checksum nix 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d37e713a259ff641624b6cb20e3b12b2952313ba36b6823c0f16e6cfd9e5de17"
"checksum output_vt100 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9"
"checksum pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f81e1644e1b54f5a68959a29aa86cde704219254669da328ecfdf6a1f09d427"
"checksum proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)" = "4d317f9caece796be1980837fd5cb3dfec5613ebdb04ad0956deea83ce168915"
"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0"
"checksum quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "faf4799c5d274f3868a4aae320a0a182cbd2baee377b378f080e16a23e9d80db"

View File

@ -31,3 +31,5 @@ features = ["termination"]
[dev-dependencies]
executable-path = "1.0.0"
pretty_assertions = "0.6.1"
colored-diff = "0.2.1"

View File

@ -165,7 +165,7 @@ impl<'a, 'b> AssignmentEvaluator<'a, 'b> {
#[cfg(test)]
mod test {
use super::*;
use crate::testing::parse_success;
use crate::testing::parse;
use brev::OutputError;
fn no_cwd_err() -> Result<PathBuf, String> {
@ -174,7 +174,7 @@ mod test {
#[test]
fn backtick_code() {
match parse_success("a:\n echo {{`f() { return 100; }; f`}}")
match parse("a:\n echo {{`f() { return 100; }; f`}}")
.run(&no_cwd_err(), &["a"], &Default::default())
.unwrap_err()
{
@ -203,7 +203,7 @@ recipe:
..Default::default()
};
match parse_success(text)
match parse(text)
.run(&no_cwd_err(), &["recipe"], &configuration)
.unwrap_err()
{

View File

@ -93,7 +93,7 @@ impl<'a: 'b, 'b> AssignmentResolver<'a, 'b> {
mod test {
use super::*;
compilation_error_test! {
error_test! {
name: circular_variable_dependency,
input: "a = b\nb = a",
offset: 0,
@ -103,7 +103,7 @@ mod test {
kind: CircularVariableDependency{variable: "a", circle: vec!["a", "b", "a"]},
}
compilation_error_test! {
error_test! {
name: self_variable_dependency,
input: "a = a",
offset: 0,
@ -113,7 +113,7 @@ mod test {
kind: CircularVariableDependency{variable: "a", circle: vec!["a", "a"]},
}
compilation_error_test! {
error_test! {
name: unknown_expression_variable,
input: "x = yy",
offset: 4,
@ -123,7 +123,7 @@ mod test {
kind: UndefinedVariable{variable: "yy"},
}
compilation_error_test! {
error_test! {
name: unknown_function,
input: "a = foo()",
offset: 4,

View File

@ -64,3 +64,6 @@ pub(crate) use crate::command_ext::CommandExt;
#[allow(unused_imports)]
pub(crate) use crate::range_ext::RangeExt;
#[allow(unused_imports)]
pub(crate) use crate::ordinal::Ordinal;

View File

@ -118,8 +118,8 @@ impl<'a> Display for CompilationError<'a> {
f,
"Alias `{}` defined on `{}` shadows recipe defined on `{}`",
alias,
self.line + 1,
recipe_line + 1,
self.line.ordinal(),
recipe_line.ordinal(),
)?;
}
CircularRecipeDependency { recipe, ref circle } => {
@ -181,8 +181,8 @@ impl<'a> Display for CompilationError<'a> {
f,
"Alias `{}` first defined on line `{}` is redefined on line `{}`",
alias,
first + 1,
self.line + 1,
first.ordinal(),
self.line.ordinal(),
)?;
}
DuplicateDependency { recipe, dependency } => {
@ -197,8 +197,8 @@ impl<'a> Display for CompilationError<'a> {
f,
"Recipe `{}` first defined on line {} is redefined on line {}",
recipe,
first + 1,
self.line + 1
first.ordinal(),
self.line.ordinal()
)?;
}
DependencyHasParameters { recipe, dependency } => {

View File

@ -205,7 +205,7 @@ mod test {
use super::*;
use crate::runtime_error::RuntimeError::*;
use crate::testing::parse_success;
use crate::testing::parse;
fn no_cwd_err() -> Result<PathBuf, String> {
Err(String::from("no cwd in tests"))
@ -213,7 +213,7 @@ mod test {
#[test]
fn unknown_recipes() {
match parse_success("a:\nb:\nc:")
match parse("a:\nb:\nc:")
.run(&no_cwd_err(), &["a", "x", "y", "z"], &Default::default())
.unwrap_err()
{
@ -246,7 +246,7 @@ a:
x
";
match parse_success(text)
match parse(text)
.run(&no_cwd_err(), &["a"], &Default::default())
.unwrap_err()
{
@ -265,7 +265,7 @@ a:
#[test]
fn code_error() {
match parse_success("fail:\n @exit 100")
match parse("fail:\n @exit 100")
.run(&no_cwd_err(), &["fail"], &Default::default())
.unwrap_err()
{
@ -288,7 +288,7 @@ a:
a return code:
@x() { {{return}} {{code + "0"}}; }; x"#;
match parse_success(text)
match parse(text)
.run(&no_cwd_err(), &["a", "return", "15"], &Default::default())
.unwrap_err()
{
@ -307,7 +307,7 @@ a return code:
#[test]
fn missing_some_arguments() {
match parse_success("a b c d:")
match parse("a b c d:")
.run(&no_cwd_err(), &["a", "b", "c"], &Default::default())
.unwrap_err()
{
@ -331,7 +331,7 @@ a return code:
#[test]
fn missing_some_arguments_variadic() {
match parse_success("a b c +d:")
match parse("a b c +d:")
.run(&no_cwd_err(), &["a", "B", "C"], &Default::default())
.unwrap_err()
{
@ -355,7 +355,7 @@ a return code:
#[test]
fn missing_all_arguments() {
match parse_success("a b c d:\n echo {{b}}{{c}}{{d}}")
match parse("a b c d:\n echo {{b}}{{c}}{{d}}")
.run(&no_cwd_err(), &["a"], &Default::default())
.unwrap_err()
{
@ -379,7 +379,7 @@ a return code:
#[test]
fn missing_some_defaults() {
match parse_success("a b c d='hello':")
match parse("a b c d='hello':")
.run(&no_cwd_err(), &["a", "b"], &Default::default())
.unwrap_err()
{
@ -403,7 +403,7 @@ a return code:
#[test]
fn missing_all_defaults() {
match parse_success("a b c='r' d='h':")
match parse("a b c='r' d='h':")
.run(&no_cwd_err(), &["a"], &Default::default())
.unwrap_err()
{
@ -430,7 +430,7 @@ a return code:
let mut configuration: Configuration = Default::default();
configuration.overrides.insert("foo", "bar");
configuration.overrides.insert("baz", "bob");
match parse_success("a:\n echo {{`f() { return 100; }; f`}}")
match parse("a:\n echo {{`f() { return 100; }; f`}}")
.run(&no_cwd_err(), &["a"], &configuration)
.unwrap_err()
{
@ -458,7 +458,7 @@ wut:
..Default::default()
};
match parse_success(text)
match parse(text)
.run(&no_cwd_err(), &["wut"], &configuration)
.unwrap_err()
{

View File

@ -626,9 +626,40 @@ impl<'a> Lexer<'a> {
mod tests {
use super::*;
use crate::testing::token_summary;
fn summary(tokens: &[Token]) -> String {
use TokenKind::*;
macro_rules! summary_test {
tokens
.iter()
.map(|t| match t.kind {
At => "@",
Backtick => "`",
Colon => ":",
ColonEquals => ":=",
Comma => ",",
Comment => "#",
Dedent => "<",
Eof => ".",
Eol => "$",
Equals => "=",
Indent => ">",
InterpolationEnd => "}",
InterpolationStart => "{",
Line => "^",
Name => "N",
ParenL => "(",
ParenR => ")",
Plus => "+",
StringRaw => "'",
StringCooked => "\"",
Text => "_",
Whitespace => " ",
})
.collect::<Vec<&str>>()
.join("")
}
macro_rules! lex_test {
($name:ident, $input:expr, $expected:expr $(,)*) => {
#[test]
fn $name() {
@ -640,116 +671,78 @@ mod tests {
.map(Token::lexeme)
.collect::<Vec<&str>>()
.join("");
let actual = token_summary(&tokens);
if actual != expected {
panic!(
"token summary mismatch:\nexpected: {}\ngot: {}\n",
expected, actual
);
}
assert_eq!(input, roundtrip);
let actual = summary(&tokens);
use pretty_assertions::assert_eq;
assert_eq!(actual, expected, "token summary mismatch");
assert_eq!(input, roundtrip, "token round-trip not equal");
}
};
}
macro_rules! error_test {
(
name: $name:ident,
input: $input:expr,
offset: $offset:expr,
line: $line:expr,
column: $column:expr,
width: $width:expr,
kind: $kind:expr,
) => {
#[test]
fn $name() {
let input = $input;
let expected = CompilationError {
text: input,
offset: $offset,
line: $line,
column: $column,
width: $width,
kind: $kind,
};
if let Err(error) = Lexer::lex(input) {
assert_eq!(error.text, expected.text);
assert_eq!(error.offset, expected.offset);
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, input);
}
}
};
}
summary_test! {
lex_test! {
name,
"foo",
"N.",
}
summary_test! {
lex_test! {
comment,
"# hello",
"#.",
}
summary_test! {
lex_test! {
backtick,
"`echo`",
"`.",
}
summary_test! {
lex_test! {
raw_string,
"'hello'",
"'.",
}
summary_test! {
lex_test! {
cooked_string,
r#""hello""#,
r#""."#,
}
summary_test! {
lex_test! {
export_concatination,
"export foo = 'foo' + 'bar'",
"N N = ' + '.",
}
summary_test! {
lex_test! {
export_complex,
"export foo = ('foo' + 'bar') + `baz`",
"N N = (' + ') + `.",
}
summary_test! {
lex_test! {
eol_linefeed,
"\n",
"$.",
}
summary_test! {
lex_test! {
eol_carriage_return_linefeed,
"\r\n",
"$.",
}
summary_test! {
lex_test! {
indented_line,
"foo:\n a",
"N:$>^_<.",
}
summary_test! {
lex_test! {
indented_block,
r##"foo:
a
@ -759,7 +752,7 @@ mod tests {
"N:$>^_$ ^_$ ^_$<.",
}
summary_test! {
lex_test! {
indented_block_followed_by_item,
"foo:
a
@ -767,7 +760,7 @@ b:",
"N:$>^_$<N:.",
}
summary_test! {
lex_test! {
indented_block_followed_by_blank,
"foo:
a
@ -776,13 +769,13 @@ b:",
"N:$>^_$^$<N:.",
}
summary_test! {
lex_test! {
indented_line_containing_unpaired_carriage_return,
"foo:\n \r \n",
"N:$>^_$<.",
}
summary_test! {
lex_test! {
indented_blocks,
"
b: a
@ -800,19 +793,19 @@ c: b
"$N: N$>^_$^$<N:$>^_$ ^_$^$<N: N$>^_$^$<N: N$>^_<.",
}
summary_test! {
lex_test! {
interpolation_empty,
"hello:\n echo {{}}",
"N:$>^_{}<.",
}
summary_test! {
lex_test! {
interpolation_expression,
"hello:\n echo {{`echo hello` + `echo goodbye`}}",
"N:$>^_{` + `}<.",
}
summary_test! {
lex_test! {
tokenize_names,
"\
foo
@ -822,13 +815,13 @@ test123",
"N$N$N$N.",
}
summary_test! {
lex_test! {
tokenize_indented_line,
"foo:\n a",
"N:$>^_<.",
}
summary_test! {
lex_test! {
tokenize_indented_block,
r##"foo:
a
@ -838,13 +831,13 @@ test123",
"N:$>^_$ ^_$ ^_$<.",
}
summary_test! {
lex_test! {
tokenize_strings,
r#"a = "'a'" + '"b"' + "'c'" + '"d"'#echo hello"#,
r#"N = " + ' + " + '#."#,
}
summary_test! {
lex_test! {
tokenize_recipe_interpolation_eol,
"foo: # some comment
{{hello}}
@ -852,7 +845,7 @@ test123",
"N: #$>^{N}$<.",
}
summary_test! {
lex_test! {
tokenize_recipe_interpolation_eof,
"foo: # more comments
{{hello}}
@ -861,19 +854,19 @@ test123",
"N: #$>^{N}$<#$.",
}
summary_test! {
lex_test! {
tokenize_recipe_complex_interpolation_expression,
"foo: #lol\n {{a + b + \"z\" + blarg}}",
"N: #$>^{N + N + \" + N}<.",
}
summary_test! {
lex_test! {
tokenize_recipe_multiple_interpolations,
"foo:,#ok\n {{a}}0{{b}}1{{c}}",
"N:,#$>^{N}_{N}_{N}<.",
}
summary_test! {
lex_test! {
tokenize_junk,
"bob
@ -882,7 +875,7 @@ hello blah blah blah : a b c #whatever
"N$$N N N N : N N N #$ .",
}
summary_test! {
lex_test! {
tokenize_empty_lines,
"
# this does something
@ -899,7 +892,7 @@ hello:
"$#$N:$>^_$ ^_$^$ ^_$^$ ^_$^$<#$ .",
}
summary_test! {
lex_test! {
tokenize_comment_before_variable,
"
#
@ -910,25 +903,25 @@ echo:
"$#$N='$N:$>^_{N}$ <.",
}
summary_test! {
lex_test! {
tokenize_interpolation_backticks,
"hello:\n echo {{`echo hello` + `echo goodbye`}}",
"N:$>^_{` + `}<.",
}
summary_test! {
lex_test! {
tokenize_empty_interpolation,
"hello:\n echo {{}}",
"N:$>^_{}<.",
}
summary_test! {
lex_test! {
tokenize_assignment_backticks,
"a = `echo hello` + `echo goodbye`",
"N = ` + `.",
}
summary_test! {
lex_test! {
tokenize_multiple,
"
hello:
@ -947,19 +940,19 @@ bob:
"$N:$>^_$ ^_$^$ ^_$^$ ^_$^$<#$N:$>^_$ <.",
}
summary_test! {
lex_test! {
tokenize_comment,
"a:=#",
"N:=#."
}
summary_test! {
lex_test! {
tokenize_comment_with_bang,
"a:=#foo!",
"N:=#."
}
summary_test! {
lex_test! {
tokenize_order,
r"
b: a
@ -977,19 +970,19 @@ c: b
"$N: N$>^_$^$<N:$>^_$ ^_$^$<N: N$>^_$^$<N: N$>^_<.",
}
summary_test! {
lex_test! {
tokenize_parens,
r"((())) )abc(+",
"((())) )N(+.",
}
summary_test! {
lex_test! {
crlf_newline,
"#\r\n#asdf\r\n",
"#$#$.",
}
summary_test! {
lex_test! {
multiple_recipes,
"a:\n foo\nb:",
"N:$>^_$<N:.",

View File

@ -31,6 +31,7 @@ mod justfile;
mod lexer;
mod load_dotenv;
mod misc;
mod ordinal;
mod parameter;
mod parser;
mod platform;

View File

@ -65,7 +65,7 @@ pub fn write_error_context(
) -> Result<(), fmt::Error> {
let width = if width == 0 { 1 } else { width };
let line_number = line + 1;
let line_number = line.ordinal();
let red = Color::fmt(f).error();
match text.lines().nth(line) {
Some(line) => {

10
src/ordinal.rs Normal file
View File

@ -0,0 +1,10 @@
pub trait Ordinal {
/// Convert an index starting at 0 to an ordinal starting at 1
fn ordinal(self) -> Self;
}
impl Ordinal for usize {
fn ordinal(self) -> Self {
self + 1
}
}

View File

@ -523,34 +523,27 @@ impl<'a> Parser<'a> {
#[cfg(test)]
mod test {
use super::*;
use crate::testing::parse_success;
use crate::testing::parse;
macro_rules! summary_test {
macro_rules! parse_test {
($name:ident, $input:expr, $expected:expr $(,)*) => {
#[test]
fn $name() {
let input = $input;
let expected = $expected;
let justfile = parse_success(input);
let justfile = parse(input);
let actual = format!("{:#}", justfile);
if actual != expected {
println!("got:\n\"{}\"\n", actual);
println!("expected:\n\"{}\"", expected);
use pretty_assertions::assert_eq;
assert_eq!(actual, expected);
}
println!("Re-parsing...");
let reparsed = parse_success(&actual);
let reparsed = parse(&actual);
let redumped = format!("{:#}", reparsed);
if redumped != actual {
println!("reparsed:\n\"{}\"\n", redumped);
println!("expected:\n\"{}\"", actual);
assert_eq!(redumped, actual);
}
}
};
}
summary_test! {
parse_test! {
parse_empty,
"
@ -561,7 +554,7 @@ mod test {
"",
}
summary_test! {
parse_test! {
parse_string_default,
r#"
@ -572,7 +565,7 @@ foo a="b\t":
r#"foo a="b\t":"#,
}
summary_test! {
parse_test! {
parse_multiple,
r#"
a:
@ -583,7 +576,7 @@ b:
b:"#,
}
summary_test! {
parse_test! {
parse_variadic,
r#"
@ -594,7 +587,7 @@ foo +a:
r#"foo +a:"#,
}
summary_test! {
parse_test! {
parse_variadic_string_default,
r#"
@ -605,7 +598,7 @@ foo +a="Hello":
r#"foo +a="Hello":"#,
}
summary_test! {
parse_test! {
parse_raw_string_default,
r#"
@ -616,7 +609,7 @@ foo a='b\t':
r#"foo a='b\t':"#,
}
summary_test! {
parse_test! {
parse_export,
r#"
export a := "hello"
@ -625,7 +618,7 @@ export a := "hello"
r#"export a := "hello""#,
}
summary_test! {
parse_test! {
parse_alias_after_target,
r#"
foo:
@ -638,7 +631,7 @@ foo:
echo a"#
}
summary_test! {
parse_test! {
parse_alias_before_target,
r#"
alias f := foo
@ -651,7 +644,7 @@ foo:
echo a"#
}
summary_test! {
parse_test! {
parse_alias_with_comment,
r#"
alias f := foo #comment
@ -664,7 +657,7 @@ foo:
echo a"#
}
summary_test! {
parse_test! {
parse_complex,
"
x:
@ -702,7 +695,7 @@ y:
z:"
}
summary_test! {
parse_test! {
parse_shebang,
"
practicum := 'hello'
@ -721,13 +714,13 @@ install:
fi",
}
summary_test! {
parse_test! {
parse_simple_shebang,
"a:\n #!\n print(1)",
"a:\n #!\n print(1)",
}
summary_test! {
parse_test! {
parse_assignments,
r#"a := "0"
c := a + b + a + b
@ -740,7 +733,7 @@ b := "1"
c := a + b + a + b"#,
}
summary_test! {
parse_test! {
parse_assignment_backticks,
"a := `echo hello`
c := a + b + a + b
@ -752,7 +745,7 @@ b := `echo goodbye`
c := a + b + a + b",
}
summary_test! {
parse_test! {
parse_interpolation_backticks,
r#"a:
echo {{ `echo hello` + "blarg" }} {{ `echo bob` }}"#,
@ -760,25 +753,25 @@ c := a + b + a + b",
echo {{`echo hello` + "blarg"}} {{`echo bob`}}"#,
}
summary_test! {
parse_test! {
eof_test,
"x:\ny:\nz:\na b c: x y z",
"a b c: x y z\n\nx:\n\ny:\n\nz:",
}
summary_test! {
parse_test! {
string_quote_escape,
r#"a := "hello\"""#,
r#"a := "hello\"""#,
}
summary_test! {
parse_test! {
string_escapes,
r#"a := "\n\t\r\"\\""#,
r#"a := "\n\t\r\"\\""#,
}
summary_test! {
parse_test! {
parameters,
"a b c:
{{b}} {{c}}",
@ -786,7 +779,7 @@ c := a + b + a + b",
{{b}} {{c}}",
}
summary_test! {
parse_test! {
unary_functions,
"
x := arch()
@ -799,7 +792,7 @@ a:
{{os()}} {{os_family()}}",
}
summary_test! {
parse_test! {
env_functions,
r#"
x := env_var('foo',)
@ -812,7 +805,7 @@ a:
{{env_var_or_default('foo' + 'bar', 'baz')}} {{env_var(env_var("baz"))}}"#,
}
summary_test! {
parse_test! {
parameter_default_string,
r#"
f x="abc":
@ -820,7 +813,7 @@ f x="abc":
r#"f x="abc":"#,
}
summary_test! {
parse_test! {
parameter_default_raw_string,
r#"
f x='abc':
@ -828,7 +821,7 @@ f x='abc':
r#"f x='abc':"#,
}
summary_test! {
parse_test! {
parameter_default_backtick,
r#"
f x=`echo hello`:
@ -836,7 +829,7 @@ f x=`echo hello`:
r#"f x=`echo hello`:"#,
}
summary_test! {
parse_test! {
parameter_default_concatination_string,
r#"
f x=(`echo hello` + "foo"):
@ -844,7 +837,7 @@ f x=(`echo hello` + "foo"):
r#"f x=(`echo hello` + "foo"):"#,
}
summary_test! {
parse_test! {
parameter_default_concatination_variable,
r#"
x := "10"
@ -855,7 +848,7 @@ f y=(`echo hello` + x) +z="foo":
f y=(`echo hello` + x) +z="foo":"#,
}
summary_test! {
parse_test! {
parameter_default_multiple,
r#"
x := "10"
@ -866,20 +859,20 @@ f y=(`echo hello` + x) +z=("foo" + "bar"):
f y=(`echo hello` + x) +z=("foo" + "bar"):"#,
}
summary_test! {
parse_test! {
concatination_in_group,
"x := ('0' + '1')",
"x := ('0' + '1')",
}
summary_test! {
parse_test! {
string_in_group,
"x := ('0' )",
"x := ('0')",
}
#[rustfmt::skip]
summary_test! {
parse_test! {
escaped_dos_newlines,
"@spam:\r
\t{ \\\r
@ -896,7 +889,7 @@ f y=(`echo hello` + x) +z=("foo" + "bar"):"#,
} | less",
}
compilation_error_test! {
error_test! {
name: duplicate_alias,
input: "alias foo = bar\nalias foo = baz",
offset: 22,
@ -906,7 +899,7 @@ f y=(`echo hello` + x) +z=("foo" + "bar"):"#,
kind: DuplicateAlias { alias: "foo", first: 0 },
}
compilation_error_test! {
error_test! {
name: alias_syntax_multiple_rhs,
input: "alias foo = bar baz",
offset: 16,
@ -916,7 +909,7 @@ f y=(`echo hello` + x) +z=("foo" + "bar"):"#,
kind: UnexpectedToken { expected: vec![Eol, Eof], found: Name },
}
compilation_error_test! {
error_test! {
name: alias_syntax_no_rhs,
input: "alias foo = \n",
offset: 12,
@ -926,7 +919,7 @@ f y=(`echo hello` + x) +z=("foo" + "bar"):"#,
kind: UnexpectedToken {expected: vec![Name], found:Eol},
}
compilation_error_test! {
error_test! {
name: unknown_alias_target,
input: "alias foo = bar\n",
offset: 6,
@ -936,7 +929,7 @@ f y=(`echo hello` + x) +z=("foo" + "bar"):"#,
kind: UnknownAliasTarget {alias: "foo", target: "bar"},
}
compilation_error_test! {
error_test! {
name: alias_shadows_recipe_before,
input: "bar: \n echo bar\nalias foo = bar\nfoo:\n echo foo",
offset: 23,
@ -946,7 +939,7 @@ f y=(`echo hello` + x) +z=("foo" + "bar"):"#,
kind: AliasShadowsRecipe {alias: "foo", recipe_line: 3},
}
compilation_error_test! {
error_test! {
name: alias_shadows_recipe_after,
input: "foo:\n echo foo\nalias foo = bar\nbar:\n echo bar",
offset: 22,
@ -956,7 +949,7 @@ f y=(`echo hello` + x) +z=("foo" + "bar"):"#,
kind: AliasShadowsRecipe { alias: "foo", recipe_line: 0 },
}
compilation_error_test! {
error_test! {
name: missing_colon,
input: "a b c\nd e f",
offset: 5,
@ -966,7 +959,7 @@ f y=(`echo hello` + x) +z=("foo" + "bar"):"#,
kind: UnexpectedToken{expected: vec![Name, Plus, Colon], found: Eol},
}
compilation_error_test! {
error_test! {
name: missing_default_eol,
input: "hello arg=\n",
offset: 10,
@ -976,7 +969,7 @@ f y=(`echo hello` + x) +z=("foo" + "bar"):"#,
kind: UnexpectedToken{expected: vec![Name, StringCooked], found: Eol},
}
compilation_error_test! {
error_test! {
name: missing_default_eof,
input: "hello arg=",
offset: 10,
@ -986,7 +979,7 @@ f y=(`echo hello` + x) +z=("foo" + "bar"):"#,
kind: UnexpectedToken{expected: vec![Name, StringCooked], found: Eof},
}
compilation_error_test! {
error_test! {
name: parameter_after_variadic,
input: "foo +a bbb:",
offset: 7,
@ -996,7 +989,7 @@ f y=(`echo hello` + x) +z=("foo" + "bar"):"#,
kind: ParameterFollowsVariadicParameter{parameter: "bbb"},
}
compilation_error_test! {
error_test! {
name: required_after_default,
input: "hello arg='foo' bar:",
offset: 16,
@ -1006,7 +999,7 @@ f y=(`echo hello` + x) +z=("foo" + "bar"):"#,
kind: RequiredParameterFollowsDefaultParameter{parameter: "bar"},
}
compilation_error_test! {
error_test! {
name: missing_eol,
input: "a b c: z =",
offset: 9,
@ -1016,7 +1009,7 @@ f y=(`echo hello` + x) +z=("foo" + "bar"):"#,
kind: UnexpectedToken{expected: vec![Name, Eol, Eof], found: Equals},
}
compilation_error_test! {
error_test! {
name: duplicate_parameter,
input: "a b b:",
offset: 4,
@ -1026,7 +1019,7 @@ f y=(`echo hello` + x) +z=("foo" + "bar"):"#,
kind: DuplicateParameter{recipe: "a", parameter: "b"},
}
compilation_error_test! {
error_test! {
name: parameter_shadows_varible,
input: "foo = \"h\"\na foo:",
offset: 12,
@ -1036,7 +1029,7 @@ f y=(`echo hello` + x) +z=("foo" + "bar"):"#,
kind: ParameterShadowsVariable{parameter: "foo"},
}
compilation_error_test! {
error_test! {
name: dependency_has_parameters,
input: "foo arg:\nb: foo",
offset: 12,
@ -1046,7 +1039,7 @@ f y=(`echo hello` + x) +z=("foo" + "bar"):"#,
kind: DependencyHasParameters{recipe: "b", dependency: "foo"},
}
compilation_error_test! {
error_test! {
name: duplicate_dependency,
input: "a b c: b c z z",
offset: 13,
@ -1056,7 +1049,7 @@ f y=(`echo hello` + x) +z=("foo" + "bar"):"#,
kind: DuplicateDependency{recipe: "a", dependency: "z"},
}
compilation_error_test! {
error_test! {
name: duplicate_recipe,
input: "a:\nb:\na:",
offset: 6,
@ -1066,7 +1059,7 @@ f y=(`echo hello` + x) +z=("foo" + "bar"):"#,
kind: DuplicateRecipe{recipe: "a", first: 0},
}
compilation_error_test! {
error_test! {
name: duplicate_variable,
input: "a = \"0\"\na = \"0\"",
offset: 8,
@ -1076,7 +1069,7 @@ f y=(`echo hello` + x) +z=("foo" + "bar"):"#,
kind: DuplicateVariable{variable: "a"},
}
compilation_error_test! {
error_test! {
name: extra_whitespace,
input: "a:\n blah\n blarg",
offset: 10,
@ -1086,7 +1079,7 @@ f y=(`echo hello` + x) +z=("foo" + "bar"):"#,
kind: ExtraLeadingWhitespace,
}
compilation_error_test! {
error_test! {
name: interpolation_outside_of_recipe,
input: "{{",
offset: 0,
@ -1096,7 +1089,7 @@ f y=(`echo hello` + x) +z=("foo" + "bar"):"#,
kind: UnexpectedToken{expected: vec![Name, At], found: InterpolationStart},
}
compilation_error_test! {
error_test! {
name: unclosed_parenthesis_in_expression,
input: "x = foo(",
offset: 8,
@ -1106,7 +1099,7 @@ f y=(`echo hello` + x) +z=("foo" + "bar"):"#,
kind: UnexpectedToken{expected: vec![Name, StringCooked, ParenR], found: Eof},
}
compilation_error_test! {
error_test! {
name: unclosed_parenthesis_in_interpolation,
input: "a:\n echo {{foo(}}",
offset: 15,
@ -1116,7 +1109,7 @@ f y=(`echo hello` + x) +z=("foo" + "bar"):"#,
kind: UnexpectedToken{expected: vec![Name, StringCooked, ParenR], found: InterpolationEnd},
}
compilation_error_test! {
error_test! {
name: plus_following_parameter,
input: "a b c+:",
offset: 5,
@ -1126,7 +1119,7 @@ f y=(`echo hello` + x) +z=("foo" + "bar"):"#,
kind: UnexpectedToken{expected: vec![Name], found: Plus},
}
compilation_error_test! {
error_test! {
name: bad_export,
input: "export a",
offset: 8,
@ -1157,14 +1150,14 @@ f y=(`echo hello` + x) +z=("foo" + "bar"):"#,
}
for justfile in justfiles {
parse_success(&justfile);
parse(&justfile);
}
}
#[test]
fn empty_recipe_lines() {
let text = "a:";
let justfile = parse_success(&text);
let justfile = parse(&text);
assert_eq!(justfile.recipes["a"].lines.len(), 0);
}
@ -1172,7 +1165,7 @@ f y=(`echo hello` + x) +z=("foo" + "bar"):"#,
#[test]
fn simple_recipe_lines() {
let text = "a:\n foo";
let justfile = parse_success(&text);
let justfile = parse(&text);
assert_eq!(justfile.recipes["a"].lines.len(), 1);
}
@ -1185,7 +1178,7 @@ f y=(`echo hello` + x) +z=("foo" + "bar"):"#,
b:
";
let justfile = parse_success(&text);
let justfile = parse(&text);
assert_eq!(justfile.recipes["a"].lines.len(), 1);
}

View File

@ -154,7 +154,7 @@ impl<'a, 'b> RecipeResolver<'a, 'b> {
mod test {
use super::*;
compilation_error_test! {
error_test! {
name: circular_recipe_dependency,
input: "a: b\nb: a",
offset: 8,
@ -164,7 +164,7 @@ mod test {
kind: CircularRecipeDependency{recipe: "b", circle: vec!["a", "b", "a"]},
}
compilation_error_test! {
error_test! {
name: self_recipe_dependency,
input: "a: a",
offset: 3,
@ -174,7 +174,7 @@ mod test {
kind: CircularRecipeDependency{recipe: "a", circle: vec!["a", "a"]},
}
compilation_error_test! {
error_test! {
name: unknown_dependency,
input: "a: b",
offset: 3,
@ -184,7 +184,7 @@ mod test {
kind: UnknownDependency{recipe: "a", unknown: "b"},
}
compilation_error_test! {
error_test! {
name: unknown_interpolation_variable,
input: "x:\n {{ hello}}",
offset: 9,
@ -194,7 +194,7 @@ mod test {
kind: UndefinedVariable{variable: "hello"},
}
compilation_error_test! {
error_test! {
name: unknown_second_interpolation_variable,
input: "wtf=\"x\"\nx:\n echo\n foo {{wtf}} {{ lol }}",
offset: 33,
@ -204,7 +204,7 @@ mod test {
kind: UndefinedVariable{variable: "lol"},
}
compilation_error_test! {
error_test! {
name: unknown_function_in_interpolation,
input: "a:\n echo {{bar()}}",
offset: 11,
@ -214,7 +214,7 @@ mod test {
kind: UnknownFunction{function: "bar"},
}
compilation_error_test! {
error_test! {
name: unknown_function_in_default,
input: "a f=baz():",
offset: 4,
@ -224,7 +224,7 @@ mod test {
kind: UnknownFunction{function: "baz"},
}
compilation_error_test! {
error_test! {
name: unknown_variable_in_default,
input: "a f=foo:",
offset: 4,

View File

@ -1,46 +1,13 @@
use crate::common::*;
pub fn parse_success(text: &str) -> Justfile {
pub fn parse(text: &str) -> Justfile {
match Parser::parse(text) {
Ok(justfile) => justfile,
Err(error) => panic!("Expected successful parse but got error:\n{}", error),
Err(error) => panic!("Expected successful parse but got error:\n {}", error),
}
}
pub fn token_summary(tokens: &[Token]) -> String {
use TokenKind::*;
tokens
.iter()
.map(|t| match t.kind {
At => "@",
Backtick => "`",
Colon => ":",
ColonEquals => ":=",
Comma => ",",
Comment => "#",
Dedent => "<",
Eof => ".",
Eol => "$",
Equals => "=",
Indent => ">",
InterpolationEnd => "}",
InterpolationStart => "{",
Line => "^",
Name => "N",
ParenL => "(",
ParenR => ")",
Plus => "+",
StringRaw => "'",
StringCooked => "\"",
Text => "_",
Whitespace => " ",
})
.collect::<Vec<&str>>()
.join("")
}
macro_rules! compilation_error_test {
macro_rules! error_test {
(
name: $name:ident,
input: $input:expr,
@ -52,33 +19,28 @@ macro_rules! compilation_error_test {
) => {
#[test]
fn $name() {
let input = $input;
let text: &str = $input;
let offset: usize = $offset;
let column: usize = $column;
let width: usize = $width;
let line: usize = $line;
let kind: CompilationErrorKind = $kind;
let expected = crate::compilation_error::CompilationError {
text: input,
offset: $offset,
line: $line,
column: $column,
width: $width,
kind: $kind,
let expected = CompilationError {
text,
offset,
line,
column,
width,
kind,
};
let mut tokens = Lexer::lex(input).unwrap();
tokens.retain(|token| token.kind != TokenKind::Whitespace);
let parser = crate::parser::Parser::new(input, tokens);
if let Err(error) = parser.justfile() {
assert_eq!(error.text, expected.text);
assert_eq!(error.offset, expected.offset);
assert_eq!(error.line, expected.line);
assert_eq!(error.column, expected.column);
assert_eq!(error.width, expected.width);
assert_eq!(error.kind, expected.kind);
assert_eq!(error, expected);
} else {
panic!("parse succeeded but expected: {}\n{}", expected, input);
match Parser::parse(text) {
Ok(_) => panic!("Compilation succeeded but expected: {}\n{}", expected, text),
Err(actual) => {
use pretty_assertions::assert_eq;
assert_eq!(actual, expected);
}
}
}
};

View File

@ -1,3 +1,4 @@
use colored_diff::PrettyDifference;
use executable_path::executable_path;
use libc::{EXIT_FAILURE, EXIT_SUCCESS};
use std::{
@ -93,10 +94,14 @@ fn integration_test(
}
let stdout = str::from_utf8(&output.stdout).unwrap();
if stdout != expected_stdout {
println!(
"bad stdout:\ngot:\n{}\n\nexpected:\n{}",
stdout, expected_stdout
"bad stdout:\n {}",
PrettyDifference {
expected: expected_stdout,
actual: stdout
},
);
failure = true;
}
@ -104,8 +109,11 @@ fn integration_test(
let stderr = str::from_utf8(&output.stderr).unwrap();
if stderr != expected_stderr {
println!(
"bad stderr:\ngot:\n{}\n\nexpected:\n{}",
stderr, expected_stderr
"bad stderr: {}",
PrettyDifference {
expected: expected_stderr,
actual: stderr
},
);
failure = true;
}
@ -148,9 +156,13 @@ fn integration_test(
let reparsed = String::from_utf8(output.stdout).unwrap();
if reparsed != dumped {
print!("expected:\n{}", reparsed);
print!("got:\n{}", dumped);
assert_eq!(reparsed, dumped);
println!(
"reparse mismatch:\n {}",
PrettyDifference {
expected: &dumped,
actual: &reparsed
},
);
}
}
}