From 62070919eb7c8eea36311e3caaf7c5ef4b097e39 Mon Sep 17 00:00:00 2001 From: ozzy <2+ozzy@noreply.localhost> Date: Sun, 27 Sep 2026 14:23:23 -0500 Subject: [PATCH] feat(tests): add tests added tests for the struct displays, eval function, lexer, and parser --- justfile | 5 ++ src/math/lexer.rs | 2 +- src/math/mod.rs | 3 + src/math/tests/display.rs | 97 +++++++++++++++++++++++++++++++ src/math/tests/eval.rs | 49 ++++++++++++++++ src/math/tests/lexer.rs | 33 +++++++++++ src/math/tests/mod.rs | 4 ++ src/math/tests/parser.rs | 118 ++++++++++++++++++++++++++++++++++++++ src/math/types.rs | 4 +- 9 files changed, 312 insertions(+), 3 deletions(-) create mode 100644 src/math/tests/display.rs create mode 100644 src/math/tests/eval.rs create mode 100644 src/math/tests/lexer.rs create mode 100644 src/math/tests/mod.rs create mode 100644 src/math/tests/parser.rs diff --git a/justfile b/justfile index 677a286..8fce985 100644 --- a/justfile +++ b/justfile @@ -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: diff --git a/src/math/lexer.rs b/src/math/lexer.rs index 7d02fe2..086d473 100644 --- a/src/math/lexer.rs +++ b/src/math/lexer.rs @@ -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), diff --git a/src/math/mod.rs b/src/math/mod.rs index e8e4b3a..8058061 100644 --- a/src/math/mod.rs +++ b/src/math/mod.rs @@ -1,5 +1,8 @@ use crate::math::types::Expression; +#[cfg(test)] +mod tests; + mod display; mod eval; mod functions; diff --git a/src/math/tests/display.rs b/src/math/tests/display.rs new file mode 100644 index 0000000..af16610 --- /dev/null +++ b/src/math/tests/display.rs @@ -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)"); +} diff --git a/src/math/tests/eval.rs b/src/math/tests/eval.rs new file mode 100644 index 0000000..958f721 --- /dev/null +++ b/src/math/tests/eval.rs @@ -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))); +} diff --git a/src/math/tests/lexer.rs b/src/math/tests/lexer.rs new file mode 100644 index 0000000..9e943c4 --- /dev/null +++ b/src/math/tests/lexer.rs @@ -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::>(); + 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]); + } +} diff --git a/src/math/tests/mod.rs b/src/math/tests/mod.rs new file mode 100644 index 0000000..8ff4e01 --- /dev/null +++ b/src/math/tests/mod.rs @@ -0,0 +1,4 @@ +mod display; +mod eval; +mod lexer; +mod parser; diff --git a/src/math/tests/parser.rs b/src/math/tests/parser.rs new file mode 100644 index 0000000..1d32b68 --- /dev/null +++ b/src/math/tests/parser.rs @@ -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); +} diff --git a/src/math/types.rs b/src/math/types.rs index e4ae196..680a140 100644 --- a/src/math/types.rs +++ b/src/math/types.rs @@ -1,6 +1,6 @@ use rust_decimal::Decimal; -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum Expression { Number(Decimal), Fraction(Box, Box), @@ -9,7 +9,7 @@ pub enum Expression { Function(String, Vec), } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum Operator { Add, Subtract,