Refactor and rename test macros (#415)
This commit is contained in:
parent
d065d1c54f
commit
415c84ea39
59
Cargo.lock
generated
59
Cargo.lock
generated
@ -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"
|
||||
|
@ -31,3 +31,5 @@ features = ["termination"]
|
||||
|
||||
[dev-dependencies]
|
||||
executable-path = "1.0.0"
|
||||
pretty_assertions = "0.6.1"
|
||||
colored-diff = "0.2.1"
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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 } => {
|
||||
|
@ -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()
|
||||
{
|
||||
|
163
src/lexer.rs
163
src/lexer.rs
@ -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:.",
|
||||
|
@ -31,6 +31,7 @@ mod justfile;
|
||||
mod lexer;
|
||||
mod load_dotenv;
|
||||
mod misc;
|
||||
mod ordinal;
|
||||
mod parameter;
|
||||
mod parser;
|
||||
mod platform;
|
||||
|
@ -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
10
src/ordinal.rs
Normal 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
|
||||
}
|
||||
}
|
135
src/parser.rs
135
src/parser.rs
@ -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);
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -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
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user