feat(tests): add tests

added tests for the struct displays, eval function, lexer, and parser
This commit is contained in:
ozzy committed 2026-09-27 14:23:23 -05:00
1 parent d5397d1591
commit 62070919eb
9 files changed
+312 -3

No files matched your search

+5
View File
@@ -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
View File
@@ -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),
+3
View File
@@ -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;
+97
View File
@@ -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)");
}
+49
View File
@@ -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)));
}
+33
View File
@@ -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]);
}
}
+4
View File
@@ -0,0 +1,4 @@
mod display;
mod eval;
mod lexer;
mod parser;
+118
View File
@@ -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
View File
@@ -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,