diff --git a/Cargo.lock b/Cargo.lock index 1d2d707..c2cce0e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,11 @@ name = "ansi_term" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "assert_matches" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "atty" version = "0.2.3" @@ -99,6 +104,7 @@ name = "just" version = "0.3.3" dependencies = [ "ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "assert_matches 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "atty 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "brev 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.27.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -263,6 +269,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] "checksum aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "500909c4f87a9e52355b26626d890833e9e1d53ac566db76c36faa984b889699" "checksum ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23ac7c30002a5accbf7e8987d0632fa6de155b7c3d39d0067317a391e00a2ef6" +"checksum assert_matches 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9e772942dccdf11b368c31e044e4fca9189f80a773d2f0808379de65894cbf57" "checksum atty 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "21e50800ec991574876040fff8ee46b136a53e985286fbe6a3bdfe6421b78860" "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" "checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" diff --git a/Cargo.toml b/Cargo.toml index 01ffcf1..c635ca5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,17 +8,18 @@ homepage = "https://github.com/casey/just" readme = "crates-io-readme.md" [dependencies] -ansi_term = "0.9.0" -atty = "0.2.1" -brev = "0.1.6" -clap = "2.0.0" -edit-distance = "2.0.0" -itertools = "0.6.2" -lazy_static = "0.2.1" -libc = "0.2.21" -regex = "0.2.2" -tempdir = "0.3.5" -unicode-width = "0.1.3" +ansi_term = "0.9.0" +assert_matches = "1.1.0" +atty = "0.2.1" +brev = "0.1.6" +clap = "2.0.0" +edit-distance = "2.0.0" +itertools = "0.6.2" +lazy_static = "0.2.1" +libc = "0.2.21" +regex = "0.2.2" +tempdir = "0.3.5" +unicode-width = "0.1.3" [dev-dependencies.utilities] path = "utilities" diff --git a/src/assignment_resolver.rs b/src/assignment_resolver.rs index a745def..72c0a76 100644 --- a/src/assignment_resolver.rs +++ b/src/assignment_resolver.rs @@ -87,49 +87,46 @@ impl<'a: 'b, 'b> AssignmentResolver<'a, 'b> { #[cfg(test)] mod test { - use testing::parse_error; use super::*; + use TokenKind::*; - #[test] - fn circular_variable_dependency() { - let text = "a = b\nb = a"; - let variable = "a"; - let circle = vec!["a", "b", "a"]; - parse_error(text, CompilationError { - text: text, - index: 0, - line: 0, - column: 0, - width: Some(1), - kind: CircularVariableDependency{variable, circle} - }); + compilation_error_test! { + name: unclosed_interpolation_delimiter, + input: "a:\n echo {{ foo", + index: 15, + line: 1, + column: 12, + width: Some(0), + kind: UnexpectedToken{expected: vec![Plus, Eol, InterpolationEnd], found: Dedent}, } - #[test] - fn self_variable_dependency() { - let text = "a = a"; - let variable = "a"; - let circle = vec!["a", "a"]; - parse_error(text, CompilationError { - text: text, - index: 0, - line: 0, - column: 0, - width: Some(1), - kind: CircularVariableDependency{variable, circle} - }); + compilation_error_test! { + name: circular_variable_dependency, + input: "a = b\nb = a", + index: 0, + line: 0, + column: 0, + width: Some(1), + kind: CircularVariableDependency{variable: "a", circle: vec!["a", "b", "a"]}, } - #[test] - fn unknown_expression_variable() { - let text = "x = yy"; - parse_error(text, CompilationError { - text: text, - index: 4, - line: 0, - column: 4, - width: Some(2), - kind: UndefinedVariable{variable: "yy"}, - }); + compilation_error_test! { + name: self_variable_dependency, + input: "a = a", + index: 0, + line: 0, + column: 0, + width: Some(1), + kind: CircularVariableDependency{variable: "a", circle: vec!["a", "a"]}, + } + + compilation_error_test! { + name: unknown_expression_variable, + input: "x = yy", + index: 4, + line: 0, + column: 4, + width: Some(2), + kind: UndefinedVariable{variable: "yy"}, } } diff --git a/src/cooked_string.rs b/src/cooked_string.rs index 7fcad5e..8b6eccd 100644 --- a/src/cooked_string.rs +++ b/src/cooked_string.rs @@ -44,5 +44,3 @@ impl<'a> CookedString<'a> { } } } - - diff --git a/src/fragment.rs b/src/fragment.rs index 323e5f0..d603cf0 100644 --- a/src/fragment.rs +++ b/src/fragment.rs @@ -14,4 +14,3 @@ impl<'a> Fragment<'a> { } } } - diff --git a/src/justfile.rs b/src/justfile.rs index a86dd02..379cc1d 100644 --- a/src/justfile.rs +++ b/src/justfile.rs @@ -165,11 +165,12 @@ impl<'a> Display for Justfile<'a> { mod test { use super::*; use testing::parse_success; + use RuntimeError::*; #[test] fn unknown_recipes() { match parse_success("a:\nb:\nc:").run(&["a", "x", "y", "z"], &Default::default()).unwrap_err() { - RuntimeError::UnknownRecipes{recipes, suggestion} => { + UnknownRecipes{recipes, suggestion} => { assert_eq!(recipes, &["x", "y", "z"]); assert_eq!(suggestion, None); } @@ -197,7 +198,7 @@ a: "; match parse_success(text).run(&["a"], &Default::default()).unwrap_err() { - RuntimeError::Code{recipe, line_number, code} => { + Code{recipe, line_number, code} => { assert_eq!(recipe, "a"); assert_eq!(code, 200); assert_eq!(line_number, None); @@ -206,18 +207,18 @@ a: } } -#[test] -fn code_error() { - match parse_success("fail:\n @exit 100") - .run(&["fail"], &Default::default()).unwrap_err() { - RuntimeError::Code{recipe, line_number, code} => { - assert_eq!(recipe, "fail"); - assert_eq!(code, 100); - assert_eq!(line_number, Some(2)); - }, - other => panic!("expected a code run error, but got: {}", other), + #[test] + fn code_error() { + match parse_success("fail:\n @exit 100") + .run(&["fail"], &Default::default()).unwrap_err() { + Code{recipe, line_number, code} => { + assert_eq!(recipe, "fail"); + assert_eq!(code, 100); + assert_eq!(line_number, Some(2)); + }, + other => panic!("expected a code run error, but got: {}", other), + } } -} #[test] fn run_args() { @@ -226,7 +227,7 @@ a return code: @x() { {{return}} {{code + "0"}}; }; x"#; match parse_success(text).run(&["a", "return", "15"], &Default::default()).unwrap_err() { - RuntimeError::Code{recipe, line_number, code} => { + Code{recipe, line_number, code} => { assert_eq!(recipe, "a"); assert_eq!(code, 150); assert_eq!(line_number, Some(3)); @@ -238,7 +239,7 @@ a return code: #[test] fn missing_some_arguments() { match parse_success("a b c d:").run(&["a", "b", "c"], &Default::default()).unwrap_err() { - RuntimeError::ArgumentCountMismatch{recipe, found, min, max} => { + ArgumentCountMismatch{recipe, found, min, max} => { assert_eq!(recipe, "a"); assert_eq!(found, 2); assert_eq!(min, 3); @@ -251,7 +252,7 @@ a return code: #[test] fn missing_some_arguments_variadic() { match parse_success("a b c +d:").run(&["a", "B", "C"], &Default::default()).unwrap_err() { - RuntimeError::ArgumentCountMismatch{recipe, found, min, max} => { + ArgumentCountMismatch{recipe, found, min, max} => { assert_eq!(recipe, "a"); assert_eq!(found, 2); assert_eq!(min, 3); @@ -265,7 +266,7 @@ a return code: fn missing_all_arguments() { match parse_success("a b c d:\n echo {{b}}{{c}}{{d}}") .run(&["a"], &Default::default()).unwrap_err() { - RuntimeError::ArgumentCountMismatch{recipe, found, min, max} => { + ArgumentCountMismatch{recipe, found, min, max} => { assert_eq!(recipe, "a"); assert_eq!(found, 0); assert_eq!(min, 3); @@ -278,7 +279,7 @@ a return code: #[test] fn missing_some_defaults() { match parse_success("a b c d='hello':").run(&["a", "b"], &Default::default()).unwrap_err() { - RuntimeError::ArgumentCountMismatch{recipe, found, min, max} => { + ArgumentCountMismatch{recipe, found, min, max} => { assert_eq!(recipe, "a"); assert_eq!(found, 1); assert_eq!(min, 2); @@ -291,7 +292,7 @@ a return code: #[test] fn missing_all_defaults() { match parse_success("a b c='r' d='h':").run(&["a"], &Default::default()).unwrap_err() { - RuntimeError::ArgumentCountMismatch{recipe, found, min, max} => { + ArgumentCountMismatch{recipe, found, min, max} => { assert_eq!(recipe, "a"); assert_eq!(found, 0); assert_eq!(min, 1); @@ -308,7 +309,7 @@ a return code: configuration.overrides.insert("baz", "bob"); match parse_success("a:\n echo {{`f() { return 100; }; f`}}") .run(&["a"], &configuration).unwrap_err() { - RuntimeError::UnknownOverrides{overrides} => { + UnknownOverrides{overrides} => { assert_eq!(overrides, &["baz", "foo"]); }, other => panic!("expected a code run error, but got: {}", other), @@ -333,7 +334,7 @@ wut: }; match parse_success(text).run(&["wut"], &configuration).unwrap_err() { - RuntimeError::Code{code: _, line_number, recipe} => { + Code{code: _, line_number, recipe} => { assert_eq!(recipe, "wut"); assert_eq!(line_number, Some(8)); }, diff --git a/src/main.rs b/src/main.rs index f69cf4e..7b3aa2d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,6 +10,10 @@ extern crate regex; extern crate tempdir; extern crate unicode_width; +#[cfg(test)] +#[macro_use] +mod testing; + mod assignment_evaluator; mod assignment_resolver; mod color; @@ -33,8 +37,6 @@ mod shebang; mod token; mod tokenizer; -#[cfg(test)] mod testing; - use tokenizer::tokenize; mod common { diff --git a/src/parameter.rs b/src/parameter.rs index aecf64b..54cdd30 100644 --- a/src/parameter.rs +++ b/src/parameter.rs @@ -22,4 +22,3 @@ impl<'a> Display for Parameter<'a> { Ok(()) } } - diff --git a/src/parser.rs b/src/parser.rs index edcbb16..dc94016 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -384,7 +384,6 @@ mod test { use super::*; use brev; use testing::parse_success; - use tokenize; macro_rules! summary_test { ($name:ident, $input:expr, $expected:expr $(,)*) => { @@ -403,47 +402,6 @@ mod test { } } - macro_rules! error_test { - ( - name: $name:ident, - input: $input:expr, - index: $index:expr, - line: $line:expr, - column: $column:expr, - width: $width:expr, - kind: $kind:expr, - ) => { - #[test] - fn $name() { - let input = $input; - - let expected = CompilationError { - text: input, - index: $index, - line: $line, - column: $column, - width: $width, - kind: $kind, - }; - - let tokens = tokenize(input).unwrap(); - let parser = Parser::new(input, tokens); - - if let Err(error) = parser.justfile() { - 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.width, expected.width); - assert_eq!(error.kind, expected.kind); - assert_eq!(error, expected); - } else { - panic!("parse succeeded but expected: {}\n{}", expected, input); - } - } - } - } - summary_test! { parse_empty, " @@ -630,7 +588,7 @@ c = a + b + a + b", {{b}} {{c}}", } - error_test! { + compilation_error_test! { name: missing_colon, input: "a b c\nd e f", index: 5, @@ -640,7 +598,7 @@ c = a + b + a + b", kind: UnexpectedToken{expected: vec![Name, Plus, Colon], found: Eol}, } - error_test! { + compilation_error_test! { name: missing_default_eol, input: "hello arg=\n", index: 10, @@ -650,7 +608,7 @@ c = a + b + a + b", kind: UnexpectedToken{expected: vec![StringToken, RawString], found: Eol}, } - error_test! { + compilation_error_test! { name: missing_default_eof, input: "hello arg=", index: 10, @@ -660,7 +618,7 @@ c = a + b + a + b", kind: UnexpectedToken{expected: vec![StringToken, RawString], found: Eof}, } - error_test! { + compilation_error_test! { name: missing_default_colon, input: "hello arg=:", index: 10, @@ -670,7 +628,7 @@ c = a + b + a + b", kind: UnexpectedToken{expected: vec![StringToken, RawString], found: Colon}, } - error_test! { + compilation_error_test! { name: missing_default_backtick, input: "hello arg=`hello`", index: 10, @@ -680,7 +638,7 @@ c = a + b + a + b", kind: UnexpectedToken{expected: vec![StringToken, RawString], found: Backtick}, } - error_test! { + compilation_error_test! { name: parameter_after_variadic, input: "foo +a bbb:", index: 7, @@ -690,7 +648,7 @@ c = a + b + a + b", kind: ParameterFollowsVariadicParameter{parameter: "bbb"}, } - error_test! { + compilation_error_test! { name: required_after_default, input: "hello arg='foo' bar:", index: 16, @@ -700,7 +658,7 @@ c = a + b + a + b", kind: RequiredParameterFollowsDefaultParameter{parameter: "bar"}, } - error_test! { + compilation_error_test! { name: missing_eol, input: "a b c: z =", index: 9, @@ -710,7 +668,7 @@ c = a + b + a + b", kind: UnexpectedToken{expected: vec![Name, Eol, Eof], found: Equals}, } - error_test! { + compilation_error_test! { name: duplicate_parameter, input: "a b b:", index: 4, @@ -720,7 +678,7 @@ c = a + b + a + b", kind: DuplicateParameter{recipe: "a", parameter: "b"}, } - error_test! { + compilation_error_test! { name: parameter_shadows_varible, input: "foo = \"h\"\na foo:", index: 12, @@ -730,7 +688,7 @@ c = a + b + a + b", kind: ParameterShadowsVariable{parameter: "foo"}, } - error_test! { + compilation_error_test! { name: dependency_has_parameters, input: "foo arg:\nb: foo", index: 12, @@ -740,7 +698,7 @@ c = a + b + a + b", kind: DependencyHasParameters{recipe: "b", dependency: "foo"}, } - error_test! { + compilation_error_test! { name: duplicate_dependency, input: "a b c: b c z z", index: 13, @@ -750,7 +708,7 @@ c = a + b + a + b", kind: DuplicateDependency{recipe: "a", dependency: "z"}, } - error_test! { + compilation_error_test! { name: duplicate_recipe, input: "a:\nb:\na:", index: 6, @@ -760,7 +718,7 @@ c = a + b + a + b", kind: DuplicateRecipe{recipe: "a", first: 0}, } - error_test! { + compilation_error_test! { name: duplicate_variable, input: "a = \"0\"\na = \"0\"", index: 8, @@ -770,7 +728,7 @@ c = a + b + a + b", kind: DuplicateVariable{variable: "a"}, } - error_test! { + compilation_error_test! { name: extra_whitespace, input: "a:\n blah\n blarg", index: 10, @@ -780,7 +738,7 @@ c = a + b + a + b", kind: ExtraLeadingWhitespace, } - error_test! { + compilation_error_test! { name: interpolation_outside_of_recipe, input: "{{", index: 0, @@ -790,7 +748,7 @@ c = a + b + a + b", kind: UnexpectedToken{expected: vec![Name, At], found: InterpolationStart}, } - error_test! { + compilation_error_test! { name: unclosed_interpolation_delimiter, input: "a:\n echo {{ foo", index: 15, @@ -800,7 +758,7 @@ c = a + b + a + b", kind: UnexpectedToken{expected: vec![Plus, Eol, InterpolationEnd], found: Dedent}, } - error_test! { + compilation_error_test! { name: plus_following_parameter, input: "a b c+:", index: 5, diff --git a/src/recipe_resolver.rs b/src/recipe_resolver.rs index fac5ce7..5973833 100644 --- a/src/recipe_resolver.rs +++ b/src/recipe_resolver.rs @@ -102,72 +102,54 @@ impl<'a, 'b> RecipeResolver<'a, 'b> { #[cfg(test)] mod test { use super::*; - use testing::parse_error; - #[test] - fn circular_recipe_dependency() { - let text = "a: b\nb: a"; - let recipe = "b"; - let circle = vec!["a", "b", "a"]; - parse_error(text, CompilationError { - text: text, - index: 8, - line: 1, - column: 3, - width: Some(1), - kind: CircularRecipeDependency{recipe, circle} - }); + compilation_error_test! { + name: circular_recipe_dependency, + input: "a: b\nb: a", + index: 8, + line: 1, + column: 3, + width: Some(1), + kind: CircularRecipeDependency{recipe: "b", circle: vec!["a", "b", "a"]}, } - #[test] - fn self_recipe_dependency() { - let text = "a: a"; - parse_error(text, CompilationError { - text: text, - index: 3, - line: 0, - column: 3, - width: Some(1), - kind: CircularRecipeDependency{recipe: "a", circle: vec!["a", "a"]} - }); + compilation_error_test! { + name: self_recipe_dependency, + input: "a: a", + index: 3, + line: 0, + column: 3, + width: Some(1), + kind: CircularRecipeDependency{recipe: "a", circle: vec!["a", "a"]}, } - #[test] - fn unknown_dependency() { - let text = "a: b"; - parse_error(text, CompilationError { - text: text, - index: 3, - line: 0, - column: 3, - width: Some(1), - kind: UnknownDependency{recipe: "a", unknown: "b"} - }); + compilation_error_test! { + name: unknown_dependency, + input: "a: b", + index: 3, + line: 0, + column: 3, + width: Some(1), + kind: UnknownDependency{recipe: "a", unknown: "b"}, } - #[test] - fn unknown_interpolation_variable() { - let text = "x:\n {{ hello}}"; - parse_error(text, CompilationError { - text: text, - index: 9, - line: 1, - column: 6, - width: Some(5), - kind: UndefinedVariable{variable: "hello"}, - }); + compilation_error_test! { + name: unknown_interpolation_variable, + input: "x:\n {{ hello}}", + index: 9, + line: 1, + column: 6, + width: Some(5), + kind: UndefinedVariable{variable: "hello"}, } - #[test] - fn unknown_second_interpolation_variable() { - let text = "wtf=\"x\"\nx:\n echo\n foo {{wtf}} {{ lol }}"; - parse_error(text, CompilationError { - text: text, - index: 33, - line: 3, - column: 16, - width: Some(3), - kind: UndefinedVariable{variable: "lol"}, - }); + compilation_error_test! { + name: unknown_second_interpolation_variable, + input: "wtf=\"x\"\nx:\n echo\n foo {{wtf}} {{ lol }}", + index: 33, + line: 3, + column: 16, + width: Some(3), + kind: UndefinedVariable{variable: "lol"}, } } diff --git a/src/testing.rs b/src/testing.rs index be632f2..d609a5b 100644 --- a/src/testing.rs +++ b/src/testing.rs @@ -1,7 +1,6 @@ use common::*; use compile; -use tokenizer::tokenize; pub fn parse_success(text: &str) -> Justfile { match compile(text) { @@ -10,20 +9,43 @@ pub fn parse_success(text: &str) -> Justfile { } } -pub fn parse_error(text: &str, expected: CompilationError) { - let tokens = tokenize(text).unwrap(); - let parser = Parser::new(text, tokens); +macro_rules! compilation_error_test { + ( + name: $name:ident, + input: $input:expr, + index: $index:expr, + line: $line:expr, + column: $column:expr, + width: $width:expr, + kind: $kind:expr, + ) => { + #[test] + fn $name() { + let input = $input; - if let Err(error) = parser.justfile() { - 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); + let expected = ::CompilationError { + text: input, + index: $index, + line: $line, + column: $column, + width: $width, + kind: $kind, + }; + + let tokens = ::tokenizer::tokenize(input).unwrap(); + let parser = ::Parser::new(input, tokens); + + if let Err(error) = parser.justfile() { + 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.width, expected.width); + assert_eq!(error.kind, expected.kind); + assert_eq!(error, expected); + } else { + panic!("parse succeeded but expected: {}\n{}", expected, input); + } + } } } -