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:
|
run *args:
|
||||||
cargo run {{ args }}
|
cargo run {{ args }}
|
||||||
|
|
||||||
|
# Run the tests
|
||||||
|
[group('dev')]
|
||||||
|
test *args:
|
||||||
|
cargo test {{ args }}
|
||||||
|
|
||||||
# Clean up previous builds
|
# Clean up previous builds
|
||||||
[group('build')]
|
[group('build')]
|
||||||
clean:
|
clean:
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@ use std::str::FromStr;
|
|||||||
use crate::math::types::Operator;
|
use crate::math::types::Operator;
|
||||||
use rust_decimal::Decimal;
|
use rust_decimal::Decimal;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum Token {
|
pub enum Token {
|
||||||
Number(Decimal),
|
Number(Decimal),
|
||||||
Identifier(String),
|
Identifier(String),
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
use crate::math::types::Expression;
|
use crate::math::types::Expression;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
|
||||||
mod display;
|
mod display;
|
||||||
mod eval;
|
mod eval;
|
||||||
mod functions;
|
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;
|
use rust_decimal::Decimal;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum Expression {
|
pub enum Expression {
|
||||||
Number(Decimal),
|
Number(Decimal),
|
||||||
Fraction(Box<Expression>, Box<Expression>),
|
Fraction(Box<Expression>, Box<Expression>),
|
||||||
@@ -9,7 +9,7 @@ pub enum Expression {
|
|||||||
Function(String, Vec<Expression>),
|
Function(String, Vec<Expression>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum Operator {
|
pub enum Operator {
|
||||||
Add,
|
Add,
|
||||||
Subtract,
|
Subtract,
|
||||||
|
|||||||
Reference in new issue
Block a user