From bd9a314baaa3de0d9ddc81c8dab28d92686a8d6e Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Tue, 22 Jun 2021 11:41:51 +0200 Subject: [PATCH] Increase test coverage. --- src/context/mod.rs | 10 +++++----- src/lib.rs | 1 + src/operator/display.rs | 2 ++ src/token/mod.rs | 16 +++++++++++++++ tests/integration.rs | 44 +++++++++++++++++++++++++++++++++++++++-- tests/serde.rs | 7 +++++++ 6 files changed, 73 insertions(+), 7 deletions(-) diff --git a/src/context/mod.rs b/src/context/mod.rs index 1cac94c..ea8bcd3 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -50,6 +50,7 @@ pub trait GetFunctionContext: Context { }*/ /// A context that returns `None` for each identifier. +#[derive(Debug, Default)] pub struct EmptyContext; impl Context for EmptyContext { @@ -150,20 +151,19 @@ macro_rules! context_map { // The user has to specify a literal 'Function::new' in order to create a function ( ($ctx:expr) $k:expr => Function::new($($v:tt)*) , $($tt:tt)*) => {{ - $ctx.set_function($k.into(), $crate::Function::new($($v)*)) + $crate::ContextWithMutableFunctions::set_function($ctx, $k.into(), $crate::Function::new($($v)*)) .and($crate::context_map!(($ctx) $($tt)*)) }}; // add a value, and chain the eventual error with the ones in the next values ( ($ctx:expr) $k:expr => $v:expr , $($tt:tt)*) => {{ - $ctx.set_value($k.into(), $v.into()) + $crate::ContextWithMutableVariables::set_value($ctx, $k.into(), $v.into()) .and($crate::context_map!(($ctx) $($tt)*)) }}; // Create a context, then recurse to add the values in it ( $($tt:tt)* ) => {{ - use $crate::Context; let mut context = $crate::HashMapContext::new(); - $crate::context_map!((context) $($tt)*) + $crate::context_map!((&mut context) $($tt)*) .map(|_| context) }}; -} +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 9939a67..7688bd7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -517,6 +517,7 @@ pub use crate::{ operator::Operator, tree::Node, value::{value_type::ValueType, EmptyType, FloatType, IntType, TupleType, Value, EMPTY_VALUE}, + token::PartialToken, }; mod context; diff --git a/src/operator/display.rs b/src/operator/display.rs index b30a561..d4291e7 100644 --- a/src/operator/display.rs +++ b/src/operator/display.rs @@ -1,3 +1,5 @@ +#![cfg(not(tarpaulin_include))] + use std::fmt::{Display, Error, Formatter}; use crate::operator::*; diff --git a/src/token/mod.rs b/src/token/mod.rs index 9da2c7b..26478c2 100644 --- a/src/token/mod.rs +++ b/src/token/mod.rs @@ -53,22 +53,38 @@ pub enum Token { String(String), } +/// A partial token is an input character whose meaning depends on the characters around it. #[derive(Clone, Debug, PartialEq)] pub enum PartialToken { + /// A partial token that unambiguously maps to a single token. Token(Token), + /// A partial token that is a literal. Literal(String), + /// A plus character '+'. Plus, + /// A minus character '-'. Minus, + /// A star character '*'. Star, + /// A slash character '/'. Slash, + /// A percent character '%'. Percent, + /// A hat character '^'. Hat, + /// A whitespace character, e.g. ' '. Whitespace, + /// An equal-to character '='. Eq, + /// An exclamation mark character '!'. ExclamationMark, + /// A greater-than character '>'. Gt, + /// A lower-than character '<'. Lt, + /// An ampersand character '&'. Ampersand, + /// A vertical bar character '|'. VerticalBar, } diff --git a/tests/integration.rs b/tests/integration.rs index c93934c..3401199 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -1,5 +1,3 @@ -extern crate evalexpr; - use evalexpr::{error::*, *}; #[test] @@ -764,3 +762,45 @@ fn test_type_errors_in_binary_operators() { )) ); } + +#[test] +fn test_empty_context() { + let context = EmptyContext; + assert_eq!(context.get_value("abc"), None); + assert_eq!(context.call_function("abc", &Value::Empty), Err(EvalexprError::FunctionIdentifierNotFound("abc".to_owned()))); +} + +#[test] +fn test_hashmap_context_type_safety() { + let mut context = context_map! {"a" => 5, "b" => 5.0}.unwrap(); + assert_eq!(eval_with_context_mut("a = 4", &mut context), Ok(Value::Empty)); + assert_eq!(eval_with_context_mut("a = 4.0", &mut context), Err(EvalexprError::ExpectedInt {actual: Value::Float(4.0)})); + assert_eq!(eval_with_context_mut("a += 4.0", &mut context), Err(EvalexprError::ExpectedInt {actual: Value::Float(8.0)})); + assert_eq!(eval_with_context_mut("a -= 4.0", &mut context), Err(EvalexprError::ExpectedInt {actual: Value::Float(0.0)})); + assert_eq!(eval_with_context_mut("a *= 4.0", &mut context), Err(EvalexprError::ExpectedInt {actual: Value::Float(16.0)})); + assert_eq!(eval_with_context_mut("a /= 4.0", &mut context), Err(EvalexprError::ExpectedInt {actual: Value::Float(1.0)})); + assert_eq!(eval_with_context_mut("a %= 4.0", &mut context), Err(EvalexprError::ExpectedInt {actual: Value::Float(0.0)})); + assert_eq!(eval_with_context_mut("a ^= 4.0", &mut context), Err(EvalexprError::ExpectedInt {actual: Value::Float(256.0)})); + + assert_eq!(eval_with_context_mut("b = 4.0", &mut context), Ok(Value::Empty)); + assert_eq!(eval_with_context_mut("b = 4", &mut context), Err(EvalexprError::ExpectedFloat {actual: Value::Int(4)})); + assert_eq!(eval_with_context_mut("b += 4", &mut context), Ok(Value::Empty)); + assert_eq!(eval_with_context_mut("b -= 4", &mut context), Ok(Value::Empty)); + assert_eq!(eval_with_context_mut("b *= 4", &mut context), Ok(Value::Empty)); + assert_eq!(eval_with_context_mut("b /= 4", &mut context), Ok(Value::Empty)); + assert_eq!(eval_with_context_mut("b %= 4", &mut context), Ok(Value::Empty)); + assert_eq!(eval_with_context_mut("b ^= 4", &mut context), Ok(Value::Empty)); +} + +#[test] +fn test_error_constructors() { + assert_eq!(eval("a = true + \"4\""), Err(EvalexprError::ExpectedNumberOrString { actual: Value::Boolean(true) })); + assert_eq!(eval("a = true && \"4\""), Err(EvalexprError::ExpectedBoolean { actual: Value::from("4") })); + assert_eq!(eval_tuple("4"), Err(EvalexprError::ExpectedTuple { actual: Value::Int(4) })); + assert_eq!(Value::Tuple(vec![Value::Int(4), Value::Int(5)]).as_fixed_len_tuple(3), Err(EvalexprError::ExpectedFixedLenTuple { expected_len: 3, actual: Value::Tuple(vec![Value::Int(4), Value::Int(5)]) })); + assert_eq!(eval_empty("4"), Err(EvalexprError::ExpectedEmpty {actual: Value::Int(4)})); + assert_eq!(eval("&"), Err(EvalexprError::UnmatchedPartialToken { first: PartialToken::Ampersand, second: None })); + + assert_eq!(expect_function_argument_amount(2, 2), Ok(())); + assert_eq!(expect_function_argument_amount(2, 3), Err(EvalexprError::WrongFunctionArgumentAmount { expected: 3, actual: 2 })); +} \ No newline at end of file diff --git a/tests/serde.rs b/tests/serde.rs index ecf7414..d29ec46 100644 --- a/tests/serde.rs +++ b/tests/serde.rs @@ -12,3 +12,10 @@ fn test_serde() { assert_eq!(manual_tree.eval(), serde_tree.eval()); } } + + +#[test] +fn test_serde_errors() { + assert_eq!(ron::de::from_str::("[\"5==5\"]"), Err(ron::de::Error::Parser(ron::de::ParseError::ExpectedString, ron::de::Position{col:1,line:1}))); + assert_eq!(ron::de::from_str::("\"&\""), Err(ron::de::Error::Message("Found a partial token '&' that should be followed by another partial token.".to_owned()))); +} \ No newline at end of file