fluent_test/backend/matchers/
numeric.rs

1use crate::backend::Assertion;
2use crate::backend::assertions::sentence::AssertionSentence;
3use std::fmt::Debug;
4use std::ops::Range;
5
6/// Trait for numeric assertions
7pub trait NumericMatchers<T> {
8    fn to_be_positive(self) -> Self;
9    fn to_be_negative(self) -> Self;
10    fn to_be_zero(self) -> Self;
11    fn to_be_greater_than(self, expected: T) -> Self;
12    fn to_be_greater_than_or_equal(self, expected: T) -> Self;
13    fn to_be_less_than(self, expected: T) -> Self;
14    fn to_be_less_than_or_equal(self, expected: T) -> Self;
15    fn to_be_in_range(self, range: Range<T>) -> Self;
16    fn to_be_even(self) -> Self;
17    fn to_be_odd(self) -> Self;
18}
19
20/// Helper trait for numeric-like types
21trait AsNumeric {
22    fn is_positive(&self) -> bool;
23    fn is_negative(&self) -> bool;
24    fn is_zero(&self) -> bool;
25    fn is_greater_than(&self, expected: i32) -> bool;
26    fn is_greater_than_or_equal(&self, expected: i32) -> bool;
27    fn is_less_than(&self, expected: i32) -> bool;
28    fn is_less_than_or_equal(&self, expected: i32) -> bool;
29    fn is_in_range(&self, range: Range<i32>) -> bool;
30    fn is_even(&self) -> bool;
31    fn is_odd(&self) -> bool;
32}
33
34// Implementation for i32
35impl AsNumeric for i32 {
36    fn is_positive(&self) -> bool {
37        *self > 0
38    }
39
40    fn is_negative(&self) -> bool {
41        *self < 0
42    }
43
44    fn is_zero(&self) -> bool {
45        *self == 0
46    }
47
48    fn is_greater_than(&self, expected: i32) -> bool {
49        *self > expected
50    }
51
52    fn is_greater_than_or_equal(&self, expected: i32) -> bool {
53        *self >= expected
54    }
55
56    fn is_less_than(&self, expected: i32) -> bool {
57        *self < expected
58    }
59
60    fn is_less_than_or_equal(&self, expected: i32) -> bool {
61        *self <= expected
62    }
63
64    fn is_in_range(&self, range: Range<i32>) -> bool {
65        range.contains(self)
66    }
67
68    fn is_even(&self) -> bool {
69        *self % 2 == 0
70    }
71
72    fn is_odd(&self) -> bool {
73        *self % 2 != 0
74    }
75}
76
77// Implementation for &i32
78impl AsNumeric for &i32 {
79    fn is_positive(&self) -> bool {
80        **self > 0
81    }
82
83    fn is_negative(&self) -> bool {
84        **self < 0
85    }
86
87    fn is_zero(&self) -> bool {
88        **self == 0
89    }
90
91    fn is_greater_than(&self, expected: i32) -> bool {
92        **self > expected
93    }
94
95    fn is_greater_than_or_equal(&self, expected: i32) -> bool {
96        **self >= expected
97    }
98
99    fn is_less_than(&self, expected: i32) -> bool {
100        **self < expected
101    }
102
103    fn is_less_than_or_equal(&self, expected: i32) -> bool {
104        **self <= expected
105    }
106
107    fn is_in_range(&self, range: Range<i32>) -> bool {
108        range.contains(*self)
109    }
110
111    fn is_even(&self) -> bool {
112        **self % 2 == 0
113    }
114
115    fn is_odd(&self) -> bool {
116        **self % 2 != 0
117    }
118}
119
120// Single implementation for any type that implements AsNumeric
121impl<V> NumericMatchers<i32> for Assertion<V>
122where
123    V: AsNumeric + Debug + Clone,
124{
125    fn to_be_positive(self) -> Self {
126        let result = self.value.is_positive();
127        let sentence = AssertionSentence::new("be", "positive");
128
129        return self.add_step(sentence, result);
130    }
131
132    fn to_be_negative(self) -> Self {
133        let result = self.value.is_negative();
134        let sentence = AssertionSentence::new("be", "negative");
135
136        return self.add_step(sentence, result);
137    }
138
139    fn to_be_zero(self) -> Self {
140        let result = self.value.is_zero();
141        let sentence = AssertionSentence::new("be", "zero");
142
143        return self.add_step(sentence, result);
144    }
145
146    fn to_be_greater_than(self, expected: i32) -> Self {
147        let result = self.value.is_greater_than(expected);
148        let sentence = AssertionSentence::new("be", format!("greater than {}", expected));
149
150        return self.add_step(sentence, result);
151    }
152
153    fn to_be_greater_than_or_equal(self, expected: i32) -> Self {
154        let result = self.value.is_greater_than_or_equal(expected);
155        let sentence = AssertionSentence::new("be", format!("greater than or equal to {}", expected));
156
157        return self.add_step(sentence, result);
158    }
159
160    fn to_be_less_than(self, expected: i32) -> Self {
161        let result = self.value.is_less_than(expected);
162        let sentence = AssertionSentence::new("be", format!("less than {}", expected));
163
164        return self.add_step(sentence, result);
165    }
166
167    fn to_be_less_than_or_equal(self, expected: i32) -> Self {
168        let result = self.value.is_less_than_or_equal(expected);
169        let sentence = AssertionSentence::new("be", format!("less than or equal to {}", expected));
170
171        return self.add_step(sentence, result);
172    }
173
174    fn to_be_in_range(self, range: Range<i32>) -> Self {
175        let result = self.value.is_in_range(range.clone());
176        let sentence = AssertionSentence::new("be", format!("in range {}..{}", range.start, range.end));
177
178        return self.add_step(sentence, result);
179    }
180
181    fn to_be_even(self) -> Self {
182        let result = self.value.is_even();
183        let sentence = AssertionSentence::new("be", "even");
184
185        return self.add_step(sentence, result);
186    }
187
188    fn to_be_odd(self) -> Self {
189        let result = self.value.is_odd();
190        let sentence = AssertionSentence::new("be", "odd");
191
192        return self.add_step(sentence, result);
193    }
194}
195
196#[cfg(test)]
197mod tests {
198    use crate::prelude::*;
199
200    #[test]
201    fn test_numeric_matchers() {
202        // Disable deduplication for tests
203        crate::Reporter::disable_deduplication();
204
205        // These should pass
206        expect!(5).to_be_positive();
207        expect!(-5).to_be_negative();
208        expect!(0).to_be_zero();
209        expect!(5).to_be_greater_than(2);
210        expect!(5).to_be_greater_than_or_equal(5);
211        expect!(5).to_be_less_than(10);
212        expect!(5).to_be_less_than_or_equal(5);
213        expect!(5).to_be_in_range(0..10);
214        expect!(6).to_be_even();
215        expect!(7).to_be_odd();
216
217        // These with negation should also pass
218        expect!(5).not().to_be_negative();
219        expect!(-5).not().to_be_positive();
220        expect!(5).not().to_be_zero();
221        expect!(2).not().to_be_greater_than(5);
222        expect!(4).not().to_be_greater_than_or_equal(5);
223        expect!(10).not().to_be_less_than(5);
224        expect!(6).not().to_be_less_than_or_equal(5);
225        expect!(15).not().to_be_in_range(0..10);
226        expect!(7).not().to_be_even();
227        expect!(6).not().to_be_odd();
228    }
229
230    #[test]
231    #[should_panic(expected = "be positive")]
232    fn test_not_positive_fails() {
233        expect!(-5).to_be_positive();
234    }
235
236    #[test]
237    #[should_panic(expected = "be negative")]
238    fn test_not_negative_fails() {
239        expect!(5).to_be_negative();
240    }
241
242    #[test]
243    #[should_panic(expected = "be zero")]
244    fn test_not_zero_fails() {
245        expect!(5).to_be_zero();
246    }
247
248    #[test]
249    #[should_panic(expected = "be greater than")]
250    fn test_not_greater_fails() {
251        expect!(2).to_be_greater_than(5);
252    }
253
254    #[test]
255    #[should_panic(expected = "be greater than or equal to")]
256    fn test_not_greater_equal_fails() {
257        expect!(4).to_be_greater_than_or_equal(5);
258    }
259
260    #[test]
261    #[should_panic(expected = "be less than")]
262    fn test_not_less_fails() {
263        expect!(10).to_be_less_than(5);
264    }
265
266    #[test]
267    #[should_panic(expected = "be less than or equal to")]
268    fn test_not_less_equal_fails() {
269        expect!(6).to_be_less_than_or_equal(5);
270    }
271
272    #[test]
273    #[should_panic(expected = "be in range")]
274    fn test_not_in_range_fails() {
275        expect!(15).to_be_in_range(0..10);
276    }
277
278    #[test]
279    #[should_panic(expected = "be even")]
280    fn test_not_even_fails() {
281        expect!(7).to_be_even();
282    }
283
284    #[test]
285    #[should_panic(expected = "be odd")]
286    fn test_not_odd_fails() {
287        expect!(6).to_be_odd();
288    }
289}