Skip to main content

showcase_calculator/
lib.rs

1//! Showcase Calculator - 100% Test Coverage Demo
2//!
3//! This crate demonstrates achieving 100% test coverage across both
4//! TUI and WASM platforms using Probar's unified testing framework.
5//!
6//! # Probar Testing Principles
7//!
8//! - **Error prevention**: Type-safe operations prevent invalid states at compile time
9//! - **Anomaly**: Automatic anomaly detection during calculations
10//! - **Balanced testing**: Balanced test distribution across components
11//! - **Visual feedback**: History tracking for visibility into operations
12//! - **Kaizen**: Mutation testing for continuous improvement
13//!
14//! # Example
15//!
16//! ```rust
17//! use showcase_calculator::prelude::*;
18//!
19//! // Create an evaluator
20//! let mut eval = Evaluator::new();
21//!
22//! // Evaluate expressions
23//! let result = eval.evaluate_str("42 * (3 + 7)").unwrap();
24//! assert_eq!(result, 420.0);
25//!
26//! // With Anomaly validation
27//! let validator = AnomalyValidator::with_max_magnitude(100.0);
28//! let mut safe_eval = Evaluator::with_validator(validator);
29//! assert!(safe_eval.evaluate_str("50 * 3").is_err()); // Exceeds max
30//! ```
31
32// Allow common test patterns in this showcase crate
33#![cfg_attr(
34    test,
35    allow(
36        clippy::unwrap_used,
37        clippy::expect_used,
38        clippy::panic,
39        clippy::float_cmp
40    )
41)]
42// showcase test-tooling crate — unwrap/expect in the test driver is acceptable
43#![allow(clippy::disallowed_methods)]
44#![deny(missing_docs)]
45#![deny(missing_debug_implementations)]
46
47pub mod core;
48pub mod driver;
49
50#[cfg(feature = "tui")]
51pub mod tui;
52
53/// WASM module - always available for testing
54/// (Mock DOM allows testing without actual browser bindings)
55pub mod wasm;
56
57/// Probar Advanced Testing Module
58/// Demonstrates all Probar features: Page Objects, Accessibility,
59/// Visual Regression, Device Emulation, Fixtures, Replay, UX Coverage
60#[cfg(test)]
61mod probar_tests;
62
63/// Prelude for convenient imports
64pub mod prelude {
65    pub use crate::core::evaluator::Evaluator;
66    pub use crate::core::history::{History, HistoryEntry};
67    pub use crate::core::parser::{AstNode, Parser, Token, Tokenizer};
68    pub use crate::core::{
69        AnomalyValidator, AnomalyViolation, CalcError, CalcResult, Calculator, Operation,
70    };
71    pub use crate::driver::{CalculatorDriver, HistoryItem};
72
73    #[cfg(feature = "tui")]
74    pub use crate::driver::TuiDriver;
75
76    pub use crate::wasm::{DomElement, DomEvent, MockDom, WasmCalculator, WasmDriver};
77}
78
79#[cfg(test)]
80mod tests {
81    use super::prelude::*;
82
83    #[test]
84    fn test_prelude_imports() {
85        // Verify all prelude exports work
86        let mut eval = Evaluator::new();
87        let result = eval.evaluate_str("2 + 3").unwrap();
88        assert_eq!(result, 5.0);
89    }
90
91    #[test]
92    fn test_calculator_direct() {
93        let mut calc = Calculator::new();
94        let result = calc.calculate(6.0, 7.0, Operation::Multiply).unwrap();
95        assert_eq!(result, 42.0);
96    }
97
98    #[test]
99    fn test_parser_direct() {
100        let ast = Parser::parse_str("1 + 2 * 3").unwrap();
101        let mut eval = Evaluator::new();
102        assert_eq!(eval.evaluate(&ast).unwrap(), 7.0);
103    }
104
105    #[test]
106    fn test_history_tracking() {
107        let mut history = History::new();
108        history.record("10 / 2", 5.0);
109        assert_eq!(history.len(), 1);
110        assert_eq!(history.last().unwrap().display(), "10 / 2 = 5");
111    }
112
113    #[test]
114    fn test_jidoka_validation() {
115        let validator = AnomalyValidator::with_max_magnitude(50.0);
116        let mut eval = Evaluator::with_validator(validator);
117
118        // Within bounds
119        assert!(eval.evaluate_str("5 * 5").is_ok());
120
121        // Exceeds bounds
122        assert!(matches!(
123            eval.evaluate_str("10 * 10"),
124            Err(CalcError::AnomalyViolation(_))
125        ));
126    }
127
128    #[test]
129    fn test_error_handling() {
130        let mut eval = Evaluator::new();
131
132        // Division by zero
133        assert!(matches!(
134            eval.evaluate_str("1 / 0"),
135            Err(CalcError::DivisionByZero)
136        ));
137
138        // Empty expression
139        assert!(matches!(
140            eval.evaluate_str(""),
141            Err(CalcError::EmptyExpression)
142        ));
143
144        // Parse error
145        assert!(matches!(
146            eval.evaluate_str("1 + + 2"),
147            Err(CalcError::ParseError(_))
148        ));
149    }
150
151    #[test]
152    fn test_all_operations() {
153        let mut eval = Evaluator::new();
154
155        assert_eq!(eval.evaluate_str("10 + 5").unwrap(), 15.0);
156        assert_eq!(eval.evaluate_str("10 - 3").unwrap(), 7.0);
157        assert_eq!(eval.evaluate_str("6 * 7").unwrap(), 42.0);
158        assert_eq!(eval.evaluate_str("20 / 4").unwrap(), 5.0);
159        assert_eq!(eval.evaluate_str("17 % 5").unwrap(), 2.0);
160        assert_eq!(eval.evaluate_str("2 ^ 10").unwrap(), 1024.0);
161    }
162
163    #[test]
164    fn test_complex_expressions() {
165        let mut eval = Evaluator::new();
166
167        // PEMDAS: 2 + 3 * 4 = 2 + 12 = 14
168        assert_eq!(eval.evaluate_str("2 + 3 * 4").unwrap(), 14.0);
169
170        // Parentheses: (2 + 3) * 4 = 5 * 4 = 20
171        assert_eq!(eval.evaluate_str("(2 + 3) * 4").unwrap(), 20.0);
172
173        // Power right associative: 2^3^2 = 2^9 = 512
174        assert_eq!(eval.evaluate_str("2 ^ 3 ^ 2").unwrap(), 512.0);
175
176        // Complex: 42 * (3 + 7) = 42 * 10 = 420
177        assert_eq!(eval.evaluate_str("42 * (3 + 7)").unwrap(), 420.0);
178
179        // Unary minus: -5 + 10 = 5
180        assert_eq!(eval.evaluate_str("-5 + 10").unwrap(), 5.0);
181    }
182}