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 (Page Objects, Accessibility, Visual Regression,
58// Device Emulation, Fixtures, Replay, UX Coverage) moved to
59// tests/probar_tests.rs (PMAT-1098).
60//
61// `jugar-probar` is a PATH-ONLY dev-dependency, which `cargo publish` OMITS
62// while publishing this crate's `#[cfg(test)]` code anyway -- so as a `src/`
63// module it made the PUBLISHED crate's lib tests uncompilable (clean-room
64// GATE B2). An integration target is the one that legitimately owns a test
65// whose subject is the (showcase, jugar-probar) pair; tests/gui_coverage_tests.rs
66// already lives there for the same reason.
67
68/// Prelude for convenient imports
69pub mod prelude {
70    pub use crate::core::evaluator::Evaluator;
71    pub use crate::core::history::{History, HistoryEntry};
72    pub use crate::core::parser::{AstNode, Parser, Token, Tokenizer};
73    pub use crate::core::{
74        AnomalyValidator, AnomalyViolation, CalcError, CalcResult, Calculator, Operation,
75    };
76    pub use crate::driver::{CalculatorDriver, HistoryItem};
77
78    #[cfg(feature = "tui")]
79    pub use crate::driver::TuiDriver;
80
81    pub use crate::wasm::{DomElement, DomEvent, MockDom, WasmCalculator, WasmDriver};
82}
83
84#[cfg(test)]
85mod tests {
86    use super::prelude::*;
87
88    #[test]
89    fn test_prelude_imports() {
90        // Verify all prelude exports work
91        let mut eval = Evaluator::new();
92        let result = eval.evaluate_str("2 + 3").unwrap();
93        assert_eq!(result, 5.0);
94    }
95
96    #[test]
97    fn test_calculator_direct() {
98        let mut calc = Calculator::new();
99        let result = calc.calculate(6.0, 7.0, Operation::Multiply).unwrap();
100        assert_eq!(result, 42.0);
101    }
102
103    #[test]
104    fn test_parser_direct() {
105        let ast = Parser::parse_str("1 + 2 * 3").unwrap();
106        let mut eval = Evaluator::new();
107        assert_eq!(eval.evaluate(&ast).unwrap(), 7.0);
108    }
109
110    #[test]
111    fn test_history_tracking() {
112        let mut history = History::new();
113        history.record("10 / 2", 5.0);
114        assert_eq!(history.len(), 1);
115        assert_eq!(history.last().unwrap().display(), "10 / 2 = 5");
116    }
117
118    #[test]
119    fn test_jidoka_validation() {
120        let validator = AnomalyValidator::with_max_magnitude(50.0);
121        let mut eval = Evaluator::with_validator(validator);
122
123        // Within bounds
124        assert!(eval.evaluate_str("5 * 5").is_ok());
125
126        // Exceeds bounds
127        assert!(matches!(
128            eval.evaluate_str("10 * 10"),
129            Err(CalcError::AnomalyViolation(_))
130        ));
131    }
132
133    #[test]
134    fn test_error_handling() {
135        let mut eval = Evaluator::new();
136
137        // Division by zero
138        assert!(matches!(
139            eval.evaluate_str("1 / 0"),
140            Err(CalcError::DivisionByZero)
141        ));
142
143        // Empty expression
144        assert!(matches!(
145            eval.evaluate_str(""),
146            Err(CalcError::EmptyExpression)
147        ));
148
149        // Parse error
150        assert!(matches!(
151            eval.evaluate_str("1 + + 2"),
152            Err(CalcError::ParseError(_))
153        ));
154    }
155
156    #[test]
157    fn test_all_operations() {
158        let mut eval = Evaluator::new();
159
160        assert_eq!(eval.evaluate_str("10 + 5").unwrap(), 15.0);
161        assert_eq!(eval.evaluate_str("10 - 3").unwrap(), 7.0);
162        assert_eq!(eval.evaluate_str("6 * 7").unwrap(), 42.0);
163        assert_eq!(eval.evaluate_str("20 / 4").unwrap(), 5.0);
164        assert_eq!(eval.evaluate_str("17 % 5").unwrap(), 2.0);
165        assert_eq!(eval.evaluate_str("2 ^ 10").unwrap(), 1024.0);
166    }
167
168    #[test]
169    fn test_complex_expressions() {
170        let mut eval = Evaluator::new();
171
172        // PEMDAS: 2 + 3 * 4 = 2 + 12 = 14
173        assert_eq!(eval.evaluate_str("2 + 3 * 4").unwrap(), 14.0);
174
175        // Parentheses: (2 + 3) * 4 = 5 * 4 = 20
176        assert_eq!(eval.evaluate_str("(2 + 3) * 4").unwrap(), 20.0);
177
178        // Power right associative: 2^3^2 = 2^9 = 512
179        assert_eq!(eval.evaluate_str("2 ^ 3 ^ 2").unwrap(), 512.0);
180
181        // Complex: 42 * (3 + 7) = 42 * 10 = 420
182        assert_eq!(eval.evaluate_str("42 * (3 + 7)").unwrap(), 420.0);
183
184        // Unary minus: -5 + 10 = 5
185        assert_eq!(eval.evaluate_str("-5 + 10").unwrap(), 5.0);
186    }
187}