Increase test coverage.

This commit is contained in:
Sebastian Schmidt 2021-06-22 11:41:51 +02:00
parent 936960ee3a
commit bd9a314baa
6 changed files with 73 additions and 7 deletions

View File

@ -50,6 +50,7 @@ pub trait GetFunctionContext: Context {
}*/ }*/
/// A context that returns `None` for each identifier. /// A context that returns `None` for each identifier.
#[derive(Debug, Default)]
pub struct EmptyContext; pub struct EmptyContext;
impl Context for 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 // 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: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)*)) .and($crate::context_map!(($ctx) $($tt)*))
}}; }};
// add a value, and chain the eventual error with the ones in the next values // add a value, and chain the eventual error with the ones in the next values
( ($ctx:expr) $k:expr => $v:expr , $($tt:tt)*) => {{ ( ($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)*)) .and($crate::context_map!(($ctx) $($tt)*))
}}; }};
// Create a context, then recurse to add the values in it // Create a context, then recurse to add the values in it
( $($tt:tt)* ) => {{ ( $($tt:tt)* ) => {{
use $crate::Context;
let mut context = $crate::HashMapContext::new(); let mut context = $crate::HashMapContext::new();
$crate::context_map!((context) $($tt)*) $crate::context_map!((&mut context) $($tt)*)
.map(|_| context) .map(|_| context)
}}; }};
} }

View File

@ -517,6 +517,7 @@ pub use crate::{
operator::Operator, operator::Operator,
tree::Node, tree::Node,
value::{value_type::ValueType, EmptyType, FloatType, IntType, TupleType, Value, EMPTY_VALUE}, value::{value_type::ValueType, EmptyType, FloatType, IntType, TupleType, Value, EMPTY_VALUE},
token::PartialToken,
}; };
mod context; mod context;

View File

@ -1,3 +1,5 @@
#![cfg(not(tarpaulin_include))]
use std::fmt::{Display, Error, Formatter}; use std::fmt::{Display, Error, Formatter};
use crate::operator::*; use crate::operator::*;

View File

@ -53,22 +53,38 @@ pub enum Token {
String(String), String(String),
} }
/// A partial token is an input character whose meaning depends on the characters around it.
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum PartialToken { pub enum PartialToken {
/// A partial token that unambiguously maps to a single token.
Token(Token), Token(Token),
/// A partial token that is a literal.
Literal(String), Literal(String),
/// A plus character '+'.
Plus, Plus,
/// A minus character '-'.
Minus, Minus,
/// A star character '*'.
Star, Star,
/// A slash character '/'.
Slash, Slash,
/// A percent character '%'.
Percent, Percent,
/// A hat character '^'.
Hat, Hat,
/// A whitespace character, e.g. ' '.
Whitespace, Whitespace,
/// An equal-to character '='.
Eq, Eq,
/// An exclamation mark character '!'.
ExclamationMark, ExclamationMark,
/// A greater-than character '>'.
Gt, Gt,
/// A lower-than character '<'.
Lt, Lt,
/// An ampersand character '&'.
Ampersand, Ampersand,
/// A vertical bar character '|'.
VerticalBar, VerticalBar,
} }

View File

@ -1,5 +1,3 @@
extern crate evalexpr;
use evalexpr::{error::*, *}; use evalexpr::{error::*, *};
#[test] #[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 }));
}

View File

@ -12,3 +12,10 @@ fn test_serde() {
assert_eq!(manual_tree.eval(), serde_tree.eval()); assert_eq!(manual_tree.eval(), serde_tree.eval());
} }
} }
#[test]
fn test_serde_errors() {
assert_eq!(ron::de::from_str::<Node>("[\"5==5\"]"), Err(ron::de::Error::Parser(ron::de::ParseError::ExpectedString, ron::de::Position{col:1,line:1})));
assert_eq!(ron::de::from_str::<Node>("\"&\""), Err(ron::de::Error::Message("Found a partial token '&' that should be followed by another partial token.".to_owned())));
}