feat(tests): add tests
added tests for the struct displays, eval function, lexer, and parser
This commit is contained in:
9 files changed
+312
-3
No files matched your search
@@ -6,6 +6,11 @@ _default:
|
||||
run *args:
|
||||
cargo run {{ args }}
|
||||
|
||||
# Run the tests
|
||||
[group('dev')]
|
||||
test *args:
|
||||
cargo test {{ args }}
|
||||
|
||||
# Clean up previous builds
|
||||
[group('build')]
|
||||
clean:
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ use std::str::FromStr;
|
||||
use crate::math::types::Operator;
|
||||
use rust_decimal::Decimal;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Token {
|
||||
Number(Decimal),
|
||||
Identifier(String),
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
use crate::math::types::Expression;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
mod display;
|
||||
mod eval;
|
||||
mod functions;
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
use rust_decimal::{Decimal, dec};
|
||||
|
||||
use crate::math::types::{Expression, Operator};
|
||||
|
||||
#[test]
|
||||
fn test_operator_display() {
|
||||
let cases = vec![
|
||||
(Operator::Add, "+"),
|
||||
(Operator::Subtract, "-"),
|
||||
(Operator::Multiply, "*"),
|
||||
(Operator::Divide, "/"),
|
||||
(Operator::Power, "^"),
|
||||
(Operator::Modulo, "%"),
|
||||
];
|
||||
|
||||
for (operation, expected) in cases {
|
||||
assert_eq!(operation.to_string(), expected);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expr_display_number() {
|
||||
let cases = vec![
|
||||
(Expression::Number(dec!(1)), "1"),
|
||||
(Expression::Number(dec!(0.5)), "0.5"),
|
||||
(
|
||||
Expression::Number(Decimal::PI),
|
||||
"3.1415926535897932384626433833",
|
||||
),
|
||||
];
|
||||
|
||||
for (expr, expected) in cases {
|
||||
assert_eq!(expr.to_string(), expected);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expr_display_variable() {
|
||||
let expr = Expression::Variable("x".to_owned());
|
||||
assert_eq!(expr.to_string(), "x");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expr_display_fraction() {
|
||||
let cases = vec![
|
||||
(
|
||||
Expression::Fraction(
|
||||
Box::new(Expression::Number(dec!(1))),
|
||||
Box::new(Expression::Number(dec!(2))),
|
||||
),
|
||||
"(1 / 2)",
|
||||
),
|
||||
(
|
||||
Expression::Fraction(
|
||||
Box::new(Expression::Number(dec!(5))),
|
||||
Box::new(Expression::Number(dec!(3))),
|
||||
),
|
||||
"(5 / 3)",
|
||||
),
|
||||
];
|
||||
|
||||
for (expr, expected) in cases {
|
||||
assert_eq!(expr.to_string(), expected);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expr_display_operation() {
|
||||
let cases = vec![
|
||||
(
|
||||
Expression::Operation(
|
||||
Operator::Multiply,
|
||||
Box::new(Expression::Number(dec!(1))),
|
||||
Box::new(Expression::Number(dec!(2))),
|
||||
),
|
||||
"(1 * 2)",
|
||||
),
|
||||
(
|
||||
Expression::Operation(
|
||||
Operator::Modulo,
|
||||
Box::new(Expression::Number(dec!(6))),
|
||||
Box::new(Expression::Number(dec!(3))),
|
||||
),
|
||||
"(6 % 3)",
|
||||
),
|
||||
];
|
||||
|
||||
for (expr, expected) in cases {
|
||||
assert_eq!(expr.to_string(), expected);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expr_display_function() {
|
||||
let expr = Expression::Function("sin".to_owned(), vec![Expression::Number(dec!(1))]);
|
||||
assert_eq!(expr.to_string(), "sin(1)");
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
use rust_decimal::{Decimal, dec};
|
||||
|
||||
use crate::math::{
|
||||
eval::eval_expr,
|
||||
types::{Expression, Operator},
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_eval_expr_number() {
|
||||
let expr = Expression::Number(dec!(1));
|
||||
assert_eq!(eval_expr(expr), Expression::Number(dec!(1)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_eval_expr_variable() {
|
||||
let expr = Expression::Variable("x".to_owned());
|
||||
assert_eq!(eval_expr(expr), Expression::Variable("x".to_owned()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_eval_expr_variable_constant() {
|
||||
let expr = Expression::Variable("pi".to_owned());
|
||||
assert_eq!(eval_expr(expr), Expression::Number(Decimal::PI));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_eval_expr_fraction() {
|
||||
let expr = Expression::Fraction(
|
||||
Box::new(Expression::Number(dec!(1))),
|
||||
Box::new(Expression::Number(dec!(2))),
|
||||
);
|
||||
assert_eq!(eval_expr(expr), Expression::Number(dec!(0.5)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_eval_expr_function() {
|
||||
let expr = Expression::Function("sqrt".to_owned(), vec![Expression::Number(dec!(4))]);
|
||||
assert_eq!(eval_expr(expr), Expression::Number(dec!(2)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_eval_operation() {
|
||||
let expr = Expression::Operation(
|
||||
Operator::Power,
|
||||
Box::new(Expression::Number(dec!(2))),
|
||||
Box::new(Expression::Number(dec!(3))),
|
||||
);
|
||||
assert_eq!(eval_expr(expr), Expression::Number(dec!(8)));
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
use rust_decimal::dec;
|
||||
|
||||
use crate::math::{
|
||||
lexer::{self, Token},
|
||||
types::Operator,
|
||||
};
|
||||
|
||||
#[test]
|
||||
pub fn test_lexer() {
|
||||
let lexer = lexer::from("(1 + 2) * 3 + (5 ^ 8 % 7)");
|
||||
let tokens = lexer.collect::<Vec<_>>();
|
||||
let expected_tokens = [
|
||||
Token::OpenParen,
|
||||
Token::Number(dec!(1)),
|
||||
Token::Operator(Operator::Add),
|
||||
Token::Number(dec!(2)),
|
||||
Token::CloseParen,
|
||||
Token::Operator(Operator::Multiply),
|
||||
Token::Number(dec!(3)),
|
||||
Token::Operator(Operator::Add),
|
||||
Token::OpenParen,
|
||||
Token::Number(dec!(5)),
|
||||
Token::Operator(Operator::Power),
|
||||
Token::Number(dec!(8)),
|
||||
Token::Operator(Operator::Modulo),
|
||||
Token::Number(dec!(7)),
|
||||
Token::CloseParen,
|
||||
];
|
||||
|
||||
for i in 0..tokens.len() {
|
||||
assert_eq!(tokens[i], expected_tokens[i]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
mod display;
|
||||
mod eval;
|
||||
mod lexer;
|
||||
mod parser;
|
||||
@@ -0,0 +1,118 @@
|
||||
use rust_decimal::dec;
|
||||
|
||||
use crate::math::{
|
||||
lexer::Token,
|
||||
parser,
|
||||
types::{Expression, Operator},
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_parser_operation() {
|
||||
let tokens = vec![
|
||||
Token::Number(dec!(1)),
|
||||
Token::Operator(Operator::Add),
|
||||
Token::Number(dec!(3)),
|
||||
];
|
||||
let expected = Expression::Operation(
|
||||
Operator::Add,
|
||||
Box::new(Expression::Number(dec!(1))),
|
||||
Box::new(Expression::Number(dec!(3))),
|
||||
);
|
||||
|
||||
let result = parser::from(tokens).parse();
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parser_order_of_operations() {
|
||||
let tokens = vec![
|
||||
Token::Number(dec!(1)),
|
||||
Token::Operator(Operator::Add),
|
||||
Token::Number(dec!(2)),
|
||||
Token::Operator(Operator::Multiply),
|
||||
Token::Number(dec!(3)),
|
||||
Token::Operator(Operator::Power),
|
||||
Token::OpenParen,
|
||||
Token::Number(dec!(4)),
|
||||
Token::Operator(Operator::Add),
|
||||
Token::Number(dec!(5)),
|
||||
Token::CloseParen,
|
||||
];
|
||||
let expected = Expression::Operation(
|
||||
Operator::Add,
|
||||
Box::new(Expression::Number(dec!(1))),
|
||||
Box::new(Expression::Operation(
|
||||
Operator::Multiply,
|
||||
Box::new(Expression::Number(dec!(2))),
|
||||
Box::new(Expression::Operation(
|
||||
Operator::Power,
|
||||
Box::new(Expression::Number(dec!(3))),
|
||||
Box::new(Expression::Operation(
|
||||
Operator::Add,
|
||||
Box::new(Expression::Number(dec!(4))),
|
||||
Box::new(Expression::Number(dec!(5))),
|
||||
)),
|
||||
)),
|
||||
)),
|
||||
);
|
||||
|
||||
let result = parser::from(tokens).parse();
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parser_negative_primary() {
|
||||
let tokens = vec![Token::Operator(Operator::Subtract), Token::Number(dec!(1))];
|
||||
let expected = Expression::Operation(
|
||||
Operator::Multiply,
|
||||
Box::new(Expression::Number(dec!(-1))),
|
||||
Box::new(Expression::Number(dec!(1))),
|
||||
);
|
||||
|
||||
let result = parser::from(tokens).parse();
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parser_negative_add() {
|
||||
let tokens = vec![
|
||||
Token::Number(dec!(1)),
|
||||
Token::Operator(Operator::Add),
|
||||
Token::Operator(Operator::Subtract),
|
||||
Token::Number(dec!(2)),
|
||||
];
|
||||
let expected = Expression::Operation(
|
||||
Operator::Add,
|
||||
Box::new(Expression::Number(dec!(1))),
|
||||
Box::new(Expression::Operation(
|
||||
Operator::Multiply,
|
||||
Box::new(Expression::Number(dec!(-1))),
|
||||
Box::new(Expression::Number(dec!(2))),
|
||||
)),
|
||||
);
|
||||
|
||||
let result = parser::from(tokens).parse();
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parser_negative_exponent() {
|
||||
let tokens = vec![
|
||||
Token::Number(dec!(1)),
|
||||
Token::Operator(Operator::Power),
|
||||
Token::Operator(Operator::Subtract),
|
||||
Token::Number(dec!(2)),
|
||||
];
|
||||
let expected = Expression::Operation(
|
||||
Operator::Power,
|
||||
Box::new(Expression::Number(dec!(1))),
|
||||
Box::new(Expression::Operation(
|
||||
Operator::Multiply,
|
||||
Box::new(Expression::Number(dec!(-1))),
|
||||
Box::new(Expression::Number(dec!(2))),
|
||||
)),
|
||||
);
|
||||
|
||||
let result = parser::from(tokens).parse();
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
use rust_decimal::Decimal;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Expression {
|
||||
Number(Decimal),
|
||||
Fraction(Box<Expression>, Box<Expression>),
|
||||
@@ -9,7 +9,7 @@ pub enum Expression {
|
||||
Function(String, Vec<Expression>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Operator {
|
||||
Add,
|
||||
Subtract,
|
||||
|
||||
Reference in new issue
Block a user