assertr/assertions/num/
mod.rs1use crate::AssertThat;
2use crate::mode::Mode;
3use crate::tracking::AssertionTracking;
4use core::fmt::Debug;
5use core::fmt::Write;
6use indoc::writedoc;
7use num::{Float, Num, Signed};
8
9pub trait NumAssertions<T: Num> {
12 fn is_zero(self) -> Self;
14
15 fn is_additive_identity(self) -> Self;
16
17 fn is_one(self) -> Self;
19
20 fn is_multiplicative_identity(self) -> Self;
21
22 fn is_negative(self) -> Self
23 where
24 T: Signed;
25
26 fn is_positive(self) -> Self
27 where
28 T: Signed;
29
30 fn is_close_to(self, expected: T, allowed_deviation: T) -> Self
33 where
34 T: PartialOrd,
35 T: Clone;
36
37 #[cfg(any(feature = "std", feature = "libm"))]
38 fn is_nan(self) -> Self
39 where
40 T: Float;
41
42 #[cfg(any(feature = "std", feature = "libm"))]
43 fn is_finite(self) -> Self
44 where
45 T: Float;
46
47 #[cfg(any(feature = "std", feature = "libm"))]
48 fn is_infinite(self) -> Self
49 where
50 T: Float;
51
52 }
55
56impl<T: Num + Debug, M: Mode> NumAssertions<T> for AssertThat<'_, T, M> {
57 #[track_caller]
58 fn is_zero(self) -> Self {
59 self.track_assertion();
60 let actual = self.actual();
61 if !actual.is_zero() {
62 self.add_detail_message(format!(
63 "Expecting additive identity of type '{}'",
64 core::any::type_name::<T>()
65 ));
66 let expected = T::zero();
67 self.fail(|w: &mut String| {
68 writedoc! {w, r#"
69 Expected: {expected:#?}
70
71 Actual: {actual:#?}
72 "#}
73 });
74 }
75 self
76 }
77
78 #[track_caller]
79 fn is_additive_identity(self) -> Self {
80 self.is_zero()
81 }
82
83 #[track_caller]
84 fn is_one(self) -> Self {
85 self.track_assertion();
86 let actual = self.actual();
87 if !actual.is_one() {
88 self.add_detail_message(format!(
89 "Expecting multiplicative identity of type '{}'",
90 core::any::type_name::<T>()
91 ));
92 let expected = T::one();
93 self.fail(|w: &mut String| {
94 writedoc! {w, r#"
95 Expected: {expected:#?}
96
97 Actual: {actual:#?}
98 "#}
99 });
100 }
101 self
102 }
103
104 #[track_caller]
105 fn is_multiplicative_identity(self) -> Self {
106 self.is_one()
107 }
108
109 #[track_caller]
110 fn is_negative(self) -> Self
111 where
112 T: Signed,
113 {
114 self.track_assertion();
115 let actual = self.actual();
116 if !actual.is_negative() {
117 self.fail(|w: &mut String| {
118 writedoc! {w, r#"
119 Expected value to be negative. But was
120
121 Actual: {actual:#?}
122 "#}
123 });
124 }
125 self
126 }
127
128 #[track_caller]
129 fn is_positive(self) -> Self
130 where
131 T: Signed,
132 {
133 self.track_assertion();
134 let actual = self.actual();
135 if !actual.is_positive() {
136 self.fail(|w: &mut String| {
137 writedoc! {w, r#"
138 Expected value to be positive. But was
139
140 Actual: {actual:#?}
141 "#}
142 });
143 }
144 self
145 }
146
147 #[track_caller]
148 fn is_close_to(self, expected: T, allowed_deviation: T) -> Self
149 where
150 T: PartialOrd,
151 T: Clone,
152 {
153 self.track_assertion();
154 let actual = self.actual();
155 let min = expected.clone() - allowed_deviation.clone();
156 let max = expected.clone() + allowed_deviation.clone();
157 if !(actual >= &min && actual <= &max) {
158 self.fail(|w: &mut String| {
159 writedoc! {w, r#"
160 Expected value to be close to: {expected:#?},
161 with allowed deviation being: {allowed_deviation:#?},
162 but value was outside range: [{min:?}, {max:?}]
163
164 Actual: {actual:#?}
165 "#}
166 });
167 }
168 self
169 }
170
171 #[track_caller]
172 #[cfg(any(feature = "std", feature = "libm"))]
173 fn is_nan(self) -> Self
174 where
175 T: Float,
176 {
177 self.track_assertion();
178 let actual = self.actual();
179 if !actual.is_nan() {
180 let nan = T::nan();
181 self.fail(|w: &mut String| {
182 writedoc! {w, r#"
183 Expected: {nan:#?}
184
185 Actual: {actual:#?}
186 "#}
187 });
188 }
189 self
190 }
191
192 #[track_caller]
193 #[cfg(any(feature = "std", feature = "libm"))]
194 fn is_finite(self) -> Self
195 where
196 T: Float,
197 {
198 self.track_assertion();
199 let actual = self.actual();
200 if !actual.is_finite() {
201 self.fail(|w: &mut String| {
202 writedoc! {w, r#"
203 Expected a finite value, but was
204
205 Actual: {actual:#?}
206 "#}
207 });
208 }
209 self
210 }
211
212 #[track_caller]
213 #[cfg(any(feature = "std", feature = "libm"))]
214 fn is_infinite(self) -> Self
215 where
216 T: Float,
217 {
218 self.track_assertion();
219 let actual = self.actual();
220 if !actual.is_infinite() {
221 let inf = T::infinity();
222 self.fail(|w: &mut String| {
223 writedoc! {w, r#"
224 Expected: +/- {inf:#?}
225
226 Actual: {actual:#?}
227 "#}
228 });
229 }
230 self
231 }
232}
233
234#[cfg(test)]
235mod tests {
236 #[test]
237 fn quick_type_check() {
238 use crate::prelude::*;
239 use ::num::Float;
240
241 assert_that(0u8).is_zero();
242 assert_that(0i8).is_zero();
243 assert_that(0u16).is_zero();
244 assert_that(0i16).is_zero();
245 assert_that(0u32).is_zero();
246 assert_that(0i32).is_zero();
247 assert_that(0u64).is_zero();
248 assert_that(0i64).is_zero();
249 assert_that(0u128).is_zero();
250 assert_that(0i128).is_zero();
251 assert_that(0.0f32).is_zero();
252 assert_that(0.0f64).is_zero();
253
254 assert_that(1u8).is_one();
255 assert_that(1i8).is_one();
256 assert_that(1u16).is_one();
257 assert_that(1i16).is_one();
258 assert_that(1u32).is_one();
259 assert_that(1i32).is_one();
260 assert_that(1u64).is_one();
261 assert_that(1i64).is_one();
262 assert_that(1u128).is_one();
263 assert_that(1i128).is_one();
264 assert_that(1.0f32).is_one();
265 assert_that(1.0f64).is_one();
266
267 assert_that(42u8).is_close_to(42, 0);
268 assert_that(42i8).is_close_to(42, 0);
269 assert_that(42u16).is_close_to(42, 0);
270 assert_that(42i16).is_close_to(42, 0);
271 assert_that(42u32).is_close_to(42, 0);
272 assert_that(42i32).is_close_to(42, 0);
273 assert_that(42u64).is_close_to(42, 0);
274 assert_that(42i64).is_close_to(42, 0);
275 assert_that(42u128).is_close_to(42, 0);
276 assert_that(42i128).is_close_to(42, 0);
277 assert_that(0.2f32 + 0.1f32).is_close_to(0.3, 0.0001);
278 assert_that(0.2f64 + 0.1f64).is_close_to(0.3, 0.0001);
279
280 assert_that(f32::nan()).is_nan();
281 assert_that(f64::nan()).is_nan();
282
283 assert_that(f32::infinity()).is_infinite();
284 assert_that(f64::infinity()).is_infinite();
285 }
286
287 mod is_zero {
288 use crate::prelude::*;
289 use indoc::formatdoc;
290
291 #[test]
292 fn succeeds_when_zero() {
293 assert_that(0).is_zero();
294 }
295
296 #[test]
297 fn panics_when_not_zero() {
298 assert_that_panic_by(|| assert_that(3).with_location(false).is_zero())
299 .has_type::<String>()
300 .is_equal_to(formatdoc! {r#"
301 -------- assertr --------
302 Expected: 0
303
304 Actual: 3
305
306 Details: [
307 Expecting additive identity of type 'i32',
308 ]
309 -------- assertr --------
310 "#});
311 }
312 }
313
314 mod is_one {
315 use crate::prelude::*;
316 use indoc::formatdoc;
317
318 #[test]
319 fn succeeds_when_one() {
320 assert_that(1).is_one();
321 }
322
323 #[test]
324 fn panics_when_not_one() {
325 assert_that_panic_by(|| assert_that(3).with_location(false).is_one())
326 .has_type::<String>()
327 .is_equal_to(formatdoc! {r#"
328 -------- assertr --------
329 Expected: 1
330
331 Actual: 3
332
333 Details: [
334 Expecting multiplicative identity of type 'i32',
335 ]
336 -------- assertr --------
337 "#});
338 }
339 }
340
341 mod is_negative {
342 use crate::prelude::*;
343 use indoc::formatdoc;
344
345 #[test]
346 fn succeeds_when_zero() {
347 assert_that(-0.01).is_negative();
348 }
349
350 #[test]
351 fn panics_when_zero() {
352 assert_that_panic_by(|| assert_that(0.0).with_location(false).is_negative())
353 .has_type::<String>()
354 .is_equal_to(formatdoc! {r#"
355 -------- assertr --------
356 Expected value to be negative. But was
357
358 Actual: 0.0
359 -------- assertr --------
360 "#});
361 }
362
363 #[test]
364 fn panics_when_positive() {
365 assert_that_panic_by(|| assert_that(1.23).with_location(false).is_negative())
366 .has_type::<String>()
367 .is_equal_to(formatdoc! {r#"
368 -------- assertr --------
369 Expected value to be negative. But was
370
371 Actual: 1.23
372 -------- assertr --------
373 "#});
374 }
375 }
376
377 mod is_positive {
378 use crate::prelude::*;
379 use indoc::formatdoc;
380
381 #[test]
382 fn succeeds_when_positive() {
383 assert_that(0.01).is_positive();
384 }
385
386 #[test]
387 fn succeeds_when_zero() {
388 assert_that(0.0).is_positive();
389 }
390
391 #[test]
392 fn panics_when_negative() {
393 assert_that_panic_by(|| assert_that(-1.23).with_location(false).is_positive())
394 .has_type::<String>()
395 .is_equal_to(formatdoc! {r#"
396 -------- assertr --------
397 Expected value to be positive. But was
398
399 Actual: -1.23
400 -------- assertr --------
401 "#});
402 }
403 }
404
405 mod is_close_to {
406 use crate::prelude::*;
407 use indoc::formatdoc;
408
409 #[test]
410 fn panics_when_below_allowed_range() {
411 assert_that_panic_by(|| {
412 assert_that(0.3319)
413 .with_location(false)
414 .is_close_to(0.333, 0.001)
415 })
416 .has_type::<String>()
417 .is_equal_to(formatdoc! {r#"
418 -------- assertr --------
419 Expected value to be close to: 0.333,
420 with allowed deviation being: 0.001,
421 but value was outside range: [0.332, 0.334]
422
423 Actual: 0.3319
424 -------- assertr --------
425 "#});
426 }
427
428 #[test]
429 fn succeeds_when_actual_is_in_allowed_range() {
430 assert_that(0.332).is_close_to(0.333, 0.001);
431 assert_that(0.333).is_close_to(0.333, 0.001);
432 assert_that(0.334).is_close_to(0.333, 0.001);
433 }
434
435 #[test]
436 fn panics_when_above_allowed_range() {
437 assert_that_panic_by(|| {
438 assert_that(0.3341)
439 .with_location(false)
440 .is_close_to(0.333, 0.001)
441 })
442 .has_type::<String>()
443 .is_equal_to(formatdoc! {r#"
444 -------- assertr --------
445 Expected value to be close to: 0.333,
446 with allowed deviation being: 0.001,
447 but value was outside range: [0.332, 0.334]
448
449 Actual: 0.3341
450 -------- assertr --------
451 "#});
452 }
453 }
454
455 mod is_nan {
456 use crate::prelude::*;
457 use ::num::Float;
458 use indoc::formatdoc;
459
460 #[test]
461 fn succeeds_when_nan() {
462 assert_that(f32::nan()).is_nan();
463 }
464
465 #[test]
466 fn panics_when_not_nan() {
467 assert_that_panic_by(|| assert_that(1.23).with_location(false).is_nan())
468 .has_type::<String>()
469 .is_equal_to(formatdoc! {r#"
470 -------- assertr --------
471 Expected: NaN
472
473 Actual: 1.23
474 -------- assertr --------
475 "#});
476 }
477 }
478
479 mod is_finite {
480 use crate::prelude::*;
481 use indoc::formatdoc;
482 use num::Float;
483
484 #[test]
485 fn succeeds_when_finite() {
486 assert_that(0.3f32).is_finite();
487 }
488
489 #[test]
490 fn panics_when_positive_infinity() {
491 assert_that_panic_by(|| {
492 assert_that(f32::infinity())
493 .with_location(false)
494 .is_finite()
495 })
496 .has_type::<String>()
497 .is_equal_to(formatdoc! {r#"
498 -------- assertr --------
499 Expected a finite value, but was
500
501 Actual: inf
502 -------- assertr --------
503 "#});
504 }
505
506 #[test]
507 fn panics_when_negative_infinity() {
508 assert_that_panic_by(|| {
509 assert_that(f32::neg_infinity())
510 .with_location(false)
511 .is_finite()
512 })
513 .has_type::<String>()
514 .is_equal_to(formatdoc! {r#"
515 -------- assertr --------
516 Expected a finite value, but was
517
518 Actual: -inf
519 -------- assertr --------
520 "#});
521 }
522 }
523
524 mod is_infinite {
525 use crate::prelude::*;
526 use ::num::Float;
527 use indoc::formatdoc;
528
529 #[test]
530 fn succeeds_when_positive_infinity() {
531 assert_that(f32::infinity()).is_infinite();
532 }
533
534 #[test]
535 fn succeeds_when_negative_infinity() {
536 assert_that(f32::neg_infinity()).is_infinite();
537 }
538
539 #[test]
540 fn panics_when_not_infinity() {
541 assert_that_panic_by(|| assert_that(1.23).with_location(false).is_infinite())
542 .has_type::<String>()
543 .is_equal_to(formatdoc! {r#"
544 -------- assertr --------
545 Expected: +/- inf
546
547 Actual: 1.23
548 -------- assertr --------
549 "#});
550 }
551 }
552}