Skip to main content

positive/
tests.rs

1/// Asserts that two `Positive` values are relatively equal within a given epsilon.
2///
3/// This macro compares two `Positive` values and checks if their relative difference
4/// is within the specified `epsilon`. It handles cases where one or both values
5/// are zero, ensuring that the non-zero value is less than or equal to epsilon.
6///
7/// # Examples
8///
9/// ```
10/// use positive::Positive;
11/// use positive::{assert_pos_relative_eq, pos_or_panic};
12///
13/// let a = pos_or_panic!(1.0);
14/// let b = pos_or_panic!(1.0001);
15/// let epsilon = pos_or_panic!(0.001);
16/// assert_pos_relative_eq!(a, b, epsilon); // Passes
17/// ```
18///
19/// ```should_panic
20/// use positive::{assert_pos_relative_eq, pos_or_panic};
21///
22/// let c = pos_or_panic!(1.0);
23/// let d = pos_or_panic!(2.0);
24/// let epsilon = pos_or_panic!(0.001);
25/// assert_pos_relative_eq!(c, d, epsilon); // Panics
26/// ```
27///
28/// # Panics
29///
30/// This macro panics if the relative difference between the two values is greater than
31/// the specified `epsilon`, or if one value is zero and the other is greater than epsilon.
32/// The panic message includes the values being compared, the expected relative difference,
33/// and the actual relative difference.
34#[macro_export]
35macro_rules! assert_pos_relative_eq {
36    ($left:expr, $right:expr, $epsilon:expr) => {{
37        let left: $crate::Positive = $left;
38        let right: $crate::Positive = $right;
39        let epsilon: $crate::Positive = $epsilon;
40        let diff_f64 = (left.to_f64() - right.to_f64()).abs();
41
42        // If the difference is exactly zero, the values are equal — always passes.
43        if diff_f64 == 0.0 {
44            // exact match, nothing to check
45        } else {
46            // `diff_f64` is an `abs()` of a finite difference and the zero
47            // case is handled above, so it is strictly positive here. This
48            // macro is a test assertion helper; panicking is its contract.
49            let abs_diff: $crate::Positive = $crate::Positive::new(diff_f64)
50                .expect("abs_diff must be positive"); // scan-banned: allow
51            let max_abs = left.max(right);
52
53            if left.is_zero() || right.is_zero() {
54                let non_zero_value = if left.is_zero() {
55                    right
56                } else {
57                    left
58                };
59                assert!(
60                    non_zero_value <= epsilon,
61                    "assertion failed: `(left == right)` \
62                     (left: `{}`, right: `{}`, expected max value: `{}`, actual value: `{}`)",
63                    left,
64                    right,
65                    epsilon,
66                    non_zero_value
67                );
68            } else {
69                let relative_diff = abs_diff / max_abs;
70                assert!(
71                    relative_diff <= epsilon,
72                    "assertion failed: `(left ≈ right)` \
73                     (left: `{}`, right: `{}`, expected relative diff: `{}`, real relative diff: `{}`)",
74                    left,
75                    right,
76                    epsilon,
77                    relative_diff
78                );
79            }
80        }
81    }};
82}
83
84#[cfg(test)]
85mod tests_assert_positivef64_relative_eq {
86    use crate::Positive;
87
88    #[test]
89    fn test_exact_equality() {
90        let a = pos_or_panic!(1.0);
91        let b = pos_or_panic!(1.0);
92        let epsilon = pos_or_panic!(0.0001);
93        assert_pos_relative_eq!(a, b, epsilon);
94    }
95
96    #[test]
97    fn test_close_values() {
98        let a = pos_or_panic!(1.0);
99        let b = pos_or_panic!(1.0001);
100        let epsilon = pos_or_panic!(0.001);
101        assert_pos_relative_eq!(a, b, epsilon);
102    }
103
104    #[test]
105    fn test_zero_values() {
106        let a = Positive::ZERO;
107        let b = Positive::ZERO;
108        let epsilon = pos_or_panic!(0.0001);
109        assert_pos_relative_eq!(a, b, epsilon);
110    }
111
112    #[test]
113    fn test_zero_and_small_value() {
114        let a = Positive::ZERO;
115        let b = pos_or_panic!(0.00001);
116        let epsilon = pos_or_panic!(0.00001);
117        assert_pos_relative_eq!(a, b, epsilon);
118    }
119
120    #[test]
121    #[should_panic(expected = "assertion failed")]
122    fn test_values_exceeding_epsilon() {
123        let a = pos_or_panic!(1.0);
124        let b = pos_or_panic!(1.002);
125        let epsilon = pos_or_panic!(0.001);
126        assert_pos_relative_eq!(a, b, epsilon);
127    }
128
129    #[test]
130    fn test_large_values() {
131        let a = pos_or_panic!(1000000.0);
132        let b = pos_or_panic!(1000001.0);
133        let epsilon = pos_or_panic!(0.000002);
134        assert_pos_relative_eq!(a, b, epsilon);
135    }
136
137    #[test]
138    fn test_very_small_values() {
139        let a = pos_or_panic!(0.0000001);
140        let b = pos_or_panic!(0.0000001000001);
141        let epsilon = pos_or_panic!(0.000002);
142        assert_pos_relative_eq!(a, b, epsilon);
143    }
144
145    #[test]
146    #[should_panic(expected = "assertion failed")]
147    fn test_significantly_different_values() {
148        let a = pos_or_panic!(1.0);
149        let b = pos_or_panic!(2.0);
150        let epsilon = pos_or_panic!(0.1);
151        assert_pos_relative_eq!(a, b, epsilon);
152    }
153
154    #[test]
155    fn test_equal_within_epsilon() {
156        let a = pos_or_panic!(100.0);
157        let b = pos_or_panic!(100.1);
158        let epsilon = pos_or_panic!(0.002);
159        assert_pos_relative_eq!(a, b, epsilon);
160    }
161
162    #[test]
163    #[should_panic(expected = "assertion failed")]
164    fn test_zero_and_large_value() {
165        let a = Positive::ZERO;
166        let b = pos_or_panic!(1.0);
167        let epsilon = pos_or_panic!(0.0001);
168        assert_pos_relative_eq!(a, b, epsilon);
169    }
170}