chicago_tdd_tools/core/macros/assert/
equality.rs

1//! Equality Assertion Macros
2//!
3//! Assertions for testing equality with enhanced error messages and approximate comparisons.
4
5/// Assert equality with detailed error message and diff output
6///
7/// Provides better error messages for equality assertions with automatic diff generation.
8///
9/// # Example
10///
11/// ```rust,should_panic
12/// use chicago_tdd_tools::assert_eq_msg;
13///
14/// let actual = 42;
15/// let expected = 43;
16/// assert_eq_msg!(actual, expected, "Values should match");
17/// // Panics with: "Values should match: expected 43, got 42"
18/// ```
19#[macro_export]
20macro_rules! assert_eq_msg {
21    ($actual:expr, $expected:expr, $msg:expr) => {{
22        let actual_val = &$actual;
23        let expected_val = &$expected;
24        if actual_val != expected_val {
25            panic!("{}: expected {:?}, got {:?}", $msg, expected_val, actual_val);
26        }
27    }};
28}
29
30/// Assert equality with automatic type inference and diff output
31///
32/// Enhanced version that provides better error messages with context.
33#[macro_export]
34macro_rules! assert_eq_enhanced {
35    ($actual:expr, $expected:expr $(,)?) => {
36        {
37            let actual_val = &$actual;
38            let expected_val = &$expected;
39            if actual_val != expected_val {
40                panic!(
41                    "assertion failed: `(left == right)`\n  left: `{:?}`\n right: `{:?}`",
42                    actual_val, expected_val
43                );
44            }
45        }
46    };
47    ($actual:expr, $expected:expr, $($arg:tt)+) => {
48        {
49            let actual_val = &$actual;
50            let expected_val = &$expected;
51            if actual_val != expected_val {
52                panic!(
53                    "assertion failed: `(left == right)`\n  left: `{:?}`\n right: `{:?}`\n{}",
54                    actual_val, expected_val, format!($($arg)+)
55                );
56            }
57        }
58    };
59}
60
61/// Assert that two floating-point values are approximately equal
62///
63/// **New in v1.3.0**: Floating-point comparison with configurable tolerance.
64///
65/// Compares floating-point values within a specified epsilon (tolerance).
66/// Works with `f32` and `f64` types.
67/// Provides clear failure messages showing the actual difference.
68///
69/// # Example
70///
71/// ```rust
72/// use chicago_tdd_tools::assert_approx_eq;
73///
74/// let pi = 3.14159265_f64;
75/// let approx_pi = 3.14_f64;
76/// assert_approx_eq!(pi, approx_pi, 0.01);
77///
78/// // With custom message
79/// let calculated = 2.0_f64 / 3.0_f64;
80/// let expected = 0.6667_f64;
81/// assert_approx_eq!(calculated, expected, 0.0001, "Division result should be close");
82/// ```
83#[macro_export]
84macro_rules! assert_approx_eq {
85    ($actual:expr, $expected:expr, $epsilon:expr) => {{
86        #[allow(clippy::float_cmp)] // Intentional approximate comparison
87        {
88            let actual_val = $actual as f64;
89            let expected_val = $expected as f64;
90            let epsilon_val = $epsilon as f64;
91            let diff = (actual_val - expected_val).abs();
92            if diff > epsilon_val {
93                panic!(
94                    "Values not approximately equal.\n  actual: {}\n  expected: {}\n  epsilon: {}\n  difference: {}",
95                    actual_val, expected_val, epsilon_val, diff
96                );
97            }
98        }
99    }};
100    ($actual:expr, $expected:expr, $epsilon:expr, $msg:expr) => {{
101        #[allow(clippy::float_cmp)] // Intentional approximate comparison
102        {
103            let actual_val = $actual as f64;
104            let expected_val = $expected as f64;
105            let epsilon_val = $epsilon as f64;
106            let diff = (actual_val - expected_val).abs();
107            if diff > epsilon_val {
108                panic!(
109                    "{}: Values not approximately equal.\n  actual: {}\n  expected: {}\n  epsilon: {}\n  difference: {}",
110                    $msg, actual_val, expected_val, epsilon_val, diff
111                );
112            }
113        }
114    }};
115}
116
117#[cfg(test)]
118#[allow(clippy::panic)] // Test code - panic is appropriate for test failures
119mod tests {
120    use crate::test;
121
122    test!(test_assert_eq_msg_macro, {
123        // Arrange: Equal values
124        let actual = 42;
125        let expected = 42;
126
127        // Act & Assert: Verify equality with message
128        assert_eq_msg!(actual, expected, "Values should match");
129    });
130
131    #[test]
132    #[should_panic(expected = "Values should match")]
133    fn test_assert_eq_msg_macro_fails() {
134        // Arrange: Unequal values
135        let actual = 41;
136        let expected = 42;
137
138        // Act & Assert: Should panic
139        assert_eq_msg!(actual, expected, "Values should match");
140    }
141
142    test!(test_assert_approx_eq_macro, {
143        // Arrange: Floating-point values
144        let pi = 3.14159265;
145        let approx_pi = 3.14;
146        let calculated = 2.0 / 3.0;
147        let expected = 0.6667;
148
149        // Act & Assert: Verify assert_approx_eq! macro works
150        assert_approx_eq!(pi, approx_pi, 0.01);
151        assert_approx_eq!(calculated, expected, 0.0001);
152        assert_approx_eq!(calculated, expected, 0.0001, "Division result should be close");
153    });
154
155    #[test]
156    #[should_panic(expected = "Values not approximately equal")]
157    fn test_assert_approx_eq_macro_fails() {
158        // Arrange: Values not within epsilon
159        let actual = 3.14159;
160        let expected = 2.71828;
161
162        // Act & Assert: Should panic
163        assert_approx_eq!(actual, expected, 0.01);
164    }
165}