From 2068063a5b2a25f22564963b66be7db8f1c5cadd Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Fri, 1 Feb 2019 19:17:31 -0600 Subject: [PATCH] FIX: Better error reporting. Also adds some testing functions to check build failures. Begins to address Issue #34 --- Cargo.lock | 6 +- Cargo.toml | 2 +- example_errors/bad_assert.ucg | 0 src/build/compile_test.rs | 105 ++++++++++++++++++++++++++++++++++ src/build/mod.rs | 8 +-- src/error.rs | 2 +- src/iter.rs | 8 ++- src/parse/mod.rs | 4 +- src/tokenizer/mod.rs | 2 +- 9 files changed, 123 insertions(+), 14 deletions(-) delete mode 100644 example_errors/bad_assert.ucg diff --git a/Cargo.lock b/Cargo.lock index 7a7b2b2..4c9359e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ [[package]] name = "abortable_parser" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -443,7 +443,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "ucg" version = "0.5.1" dependencies = [ - "abortable_parser 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "abortable_parser 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "base64 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "bencher 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.26.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -549,7 +549,7 @@ dependencies = [ ] [metadata] -"checksum abortable_parser 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "685d99bbca3566d6b7f34b09d68039089ce4a36226f6f99f61ed8495850e3213" +"checksum abortable_parser 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f1b5820556138ca74cc120d93d6724fbbdf4c8e130e640333d589a3f7ae747e4" "checksum aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9a933f4e58658d7b12defcf96dc5c720f20832deebe3e0a19efd3b6aaeeb9e" "checksum ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23ac7c30002a5accbf7e8987d0632fa6de155b7c3d39d0067317a391e00a2ef6" "checksum argon2rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3f67b0b6a86dae6e67ff4ca2b6201396074996379fba2b92ff649126f37cb392" diff --git a/Cargo.toml b/Cargo.toml index f0df924..ef26b5a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ include = [ ] [dependencies] -abortable_parser = "~0.2.2" +abortable_parser = "~0.2.3" clap = "~2.26.0" serde_json = "~1.0.9" simple-error = "0.1" diff --git a/example_errors/bad_assert.ucg b/example_errors/bad_assert.ucg deleted file mode 100644 index e69de29..0000000 diff --git a/src/build/compile_test.rs b/src/build/compile_test.rs index 313ffa5..0141f23 100644 --- a/src/build/compile_test.rs +++ b/src/build/compile_test.rs @@ -15,6 +15,8 @@ use std::cell::RefCell; use std::rc::Rc; +use regex::Regex; + use super::assets::MemoryCache; use super::FileBuilder; @@ -29,6 +31,39 @@ fn assert_build(input: &str) { } } +fn assert_build_failure(input: &str, expect: Vec) { + let i_paths = Vec::new(); + let cache = MemoryCache::new(); + let mut b = FileBuilder::new("", &i_paths, Rc::new(RefCell::new(cache))); + b.enable_validate_mode(); + let err = b.eval_string(input); + match err { + Ok(_) => { + for r in expect.iter() { + if !b.assert_collector.success { + if let None = r.find(&b.assert_collector.failures) { + panic!( + "[{}] was not found in Assertion Failures:\n{}", + r, b.assert_collector.failures + ); + } + } else { + panic!("Building input Did not panic!"); + } + } + } + Err(ref err) => { + for r in expect.iter() { + let stack_trace = format!("{}", err); + // Look for each expect to match the string. + if let None = r.find(&stack_trace) { + panic!("[{}] was not found in stacktrace:\n{}", r, stack_trace); + } + } + } + } +} + #[test] fn test_tuples() { assert_build(include_str!("../../integration_tests/tuple_test.ucg")); @@ -119,3 +154,73 @@ fn test_declarative_failures_are_caused_by_msg() { fn test_declarative_failures_can_with_format_expr() { assert_build("fail \"@ is a failure!\" % (1);"); } + +#[test] +fn test_assert_just_keyword_compile_failures() { + assert_build_failure( + "assert ", + vec![ + Regex::new(r"line: 1, column: 1").unwrap(), + Regex::new(r"Expected Tuple \{ok=, desc=\}: at line: 1, column: 8") + .unwrap(), + Regex::new(r"Expected Expression: at line: 1, column: 8").unwrap(), + ], + ); +} + +#[test] +fn test_assert_partial_tuple_compile_failures() { + assert_build_failure( + "assert {", + vec![ + Regex::new(r"line: 1, column: 1").unwrap(), + Regex::new(r"Expected Tuple \{ok=, desc=\}: at line: 1, column: 8") + .unwrap(), + Regex::new(r"Expected \(\}\) Instead is \(\): at line: 1, column: 9").unwrap(), + ], + ); +} + +#[test] +fn test_assert_partial_tuple_missing_ok_compile_failures() { + assert_build_failure( + "assert {};", + vec![ + Regex::new(r"0 - NOT OK: TYPE FAIL - Expected Boolean field ok in tuple \{").unwrap(), + Regex::new(r"line: 1, column: 8").unwrap(), + ], + ); +} + +#[test] +fn test_assert_partial_tuple_bad_ok_compile_failures() { + assert_build_failure( + "assert { ok = 1, };", + vec![ + Regex::new(r"0 - NOT OK: TYPE FAIL - Expected Boolean field ok in tuple \{").unwrap(), + Regex::new(r"line: 1, column: 8").unwrap(), + ], + ); +} + +#[test] +fn test_assert_partial_tuple_missing_desc_compile_failures() { + assert_build_failure( + "assert { ok=true, };", + vec![ + Regex::new(r"0 - NOT OK: TYPE FAIL - Expected String field desc in tuple \{").unwrap(), + Regex::new(r"line: 1, column: 8").unwrap(), + ], + ); +} + +#[test] +fn test_assert_partial_tuple_bad_desc_compile_failures() { + assert_build_failure( + "assert { ok=true, desc = 1 };", + vec![ + Regex::new(r"0 - NOT OK: TYPE FAIL - Expected String field desc in tuple \{").unwrap(), + Regex::new(r"line: 1, column: 8").unwrap(), + ], + ); +} diff --git a/src/build/mod.rs b/src/build/mod.rs index 9ff2c61..a221b58 100644 --- a/src/build/mod.rs +++ b/src/build/mod.rs @@ -1535,7 +1535,7 @@ impl<'a> FileBuilder<'a> { &Val::Boolean(b) => b, _ => { let msg = format!( - "TYPE FAIL - Expected Boolean field ok in tuple {}, line: {} column: {}", + "TYPE FAIL - Expected Boolean field ok in tuple {}, line: {}, column: {}", ok.as_ref(), expr.pos().line, expr.pos().column ); self.record_assert_result(&msg, false); @@ -1544,7 +1544,7 @@ impl<'a> FileBuilder<'a> { }, None => { let msg = format!( - "TYPE FAIL - Expected Boolean field ok in tuple {}, line: {} column: {}", + "TYPE FAIL - Expected Boolean field ok in tuple {}, line: {}, column: {}", ok.as_ref(), expr.pos().line, expr.pos().column ); self.record_assert_result(&msg, false); @@ -1556,7 +1556,7 @@ impl<'a> FileBuilder<'a> { Val::Str(ref s) => s.clone(), _ => { let msg = format!( - "TYPE FAIL - Expected Boolean field desc in tuple {} line: {} column: {}", + "TYPE FAIL - Expected String field desc in tuple {} line: {}, column: {}", ok, expr.pos().line, expr.pos().column ); self.record_assert_result(&msg, false); @@ -1565,7 +1565,7 @@ impl<'a> FileBuilder<'a> { }, None => { let msg = format!( - "TYPE FAIL - Expected Boolean field desc in tuple {} line: {} column: {}\n", + "TYPE FAIL - Expected String field desc in tuple {} line: {}, column: {}\n", ok, expr.pos().line, expr.pos().column ); self.record_assert_result(&msg, false); diff --git a/src/error.rs b/src/error.rs index 9aac3e0..b4d84e6 100644 --- a/src/error.rs +++ b/src/error.rs @@ -81,7 +81,7 @@ impl BuildError { }; write!( w, - "{} at {} line: {} column: {}\nCaused By:\n\t{} ", + "{} at {} line: {}, column: {}\nCaused By:\n\t{} ", self.err_type, file, self.pos.line, self.pos.column, self.msg )?; Ok(()) diff --git a/src/iter.rs b/src/iter.rs index 478dae7..199c8d6 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -100,13 +100,17 @@ impl<'a> Positioned for OffsetStrIter<'a> { } } -impl<'a> InputIter for OffsetStrIter<'a> {} +impl<'a> InputIter for OffsetStrIter<'a> { + fn curr(&self) -> Self::Item { + self.clone().peek_next().unwrap() + } +} impl<'a> From<&'a SliceIter<'a, Token>> for Position { fn from(source: &'a SliceIter<'a, Token>) -> Self { match source.peek_next() { Some(t) => t.pos.clone(), - None => Position::new(0, 0, 0), + None => source.curr().pos.clone(), } } } diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 1534458..4b874c8 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -733,7 +733,7 @@ fn expression(input: SliceIter) -> ParseResult { let _input = input.clone(); match trace_parse!(_input, op_expression) { Result::Incomplete(i) => Result::Incomplete(i), - Result::Fail(_) => trace_parse!(input, non_op_expression), + Result::Fail(_) => trace_parse!(input, wrap_err!(non_op_expression, "Expected Expression")), Result::Abort(e) => Result::Abort(e), Result::Complete(rest, expr) => Result::Complete(rest, expr), } @@ -782,7 +782,7 @@ make_fn!( assert_statement, Statement>, do_each!( _ => word!("assert"), - expr => must!(expression), + expr => wrap_err!(must!(expression), "Expected Tuple {ok=, desc=}"), _ => must!(punct!(";")), (Statement::Assert(expr)) ) diff --git a/src/tokenizer/mod.rs b/src/tokenizer/mod.rs index b0deee7..8e35080 100644 --- a/src/tokenizer/mod.rs +++ b/src/tokenizer/mod.rs @@ -630,7 +630,7 @@ macro_rules! match_token { } else { Result::Fail(Error::new( format!("Expected {} Instead is ({})", $msg, tok.fragment), - Box::new(i_), + Box::new($i.clone()), )) } } else {