assertables/assert_abs_diff/assert_abs_diff_ge_x.rs
1//! Assert an absolute difference is greater than or equal to an expression.
2//!
3//! Pseudocode:<br>
4//! |Δ| ≥ x
5//!
6//! # Example
7//!
8//! ```rust
9//! use assertables::*;
10//!
11//! let a = 10;
12//! let b = 13;
13//! let x = 2;
14//! assert_abs_diff_ge_x!(a, b, x);
15//! ```
16//!
17//! # Module macros
18//!
19//! * [`assert_abs_diff_ge_x`](macro@crate::assert_abs_diff_ge_x)
20//! * [`assert_abs_diff_ge_x_as_result`](macro@crate::assert_abs_diff_ge_x_as_result)
21//! * [`debug_assert_abs_diff_ge_x`](macro@crate::debug_assert_abs_diff_ge_x)
22
23/// Assert an absolute difference is greater than or equal to an expression.
24///
25/// Pseudocode:<br>
26/// |Δ| ≥ x
27///
28/// * If true, return `Ok((lhs, rhs))`.
29///
30/// * Otherwise, return [`Err`] with a message and the values of the
31/// expressions with their debug representations.
32///
33/// This macro is useful for runtime checks, such as checking parameters,
34/// or sanitizing inputs, or handling different results in different ways.
35///
36/// # Module macros
37///
38/// * [`assert_abs_diff_ge_x`](macro@crate::assert_abs_diff_ge_x)
39/// * [`assert_abs_diff_ge_x_as_result`](macro@crate::assert_abs_diff_ge_x_as_result)
40/// * [`debug_assert_abs_diff_ge_x`](macro@crate::debug_assert_abs_diff_ge_x)
41///
42#[macro_export]
43macro_rules! assert_abs_diff_ge_x_as_result {
44 ($a:expr, $b:expr, $x:expr $(,)?) => {
45 match (&$a, &$b, &$x) {
46 (a, b, x) => {
47 match ::std::panic::catch_unwind(|| if (a >= b) { a - b } else { b - a }) {
48 Ok(abs_diff) => {
49 if abs_diff >= *x {
50 Ok((abs_diff, *x))
51 } else {
52 Err(format!(
53 concat!(
54 "assertion failed: `assert_abs_diff_ge_x!(a, b, x)`\n",
55 "https://docs.rs/assertables/9.8.6/assertables/macro.assert_abs_diff_ge_x.html\n",
56 " a label: `{}`,\n",
57 " a debug: `{:?}`,\n",
58 " b label: `{}`,\n",
59 " b debug: `{:?}`,\n",
60 " x label: `{}`,\n",
61 " x debug: `{:?}`,\n",
62 " |Δ|: `{:?}`,\n",
63 " |Δ| ≥ x: {}"
64 ),
65 stringify!($a),
66 a,
67 stringify!($b),
68 b,
69 stringify!($x),
70 x,
71 abs_diff,
72 false
73 ))
74 }
75 }
76 Err(_err) => {
77 Err(format!(
78 concat!(
79 "assertion failed: `assert_abs_diff_ge_x!(a, b, x)`\n",
80 "https://docs.rs/assertables/9.8.6/assertables/macro.assert_abs_diff_ge_x.html\n",
81 " a label: `{}`,\n",
82 " a debug: `{:?}`,\n",
83 " b label: `{}`,\n",
84 " b debug: `{:?}`,\n",
85 " x label: `{}`,\n",
86 " x debug: `{:?}`,\n",
87 " |Δ|: panic", //TODO add the panic message
88 ),
89 stringify!($a),
90 a,
91 stringify!($b),
92 b,
93 stringify!($x),
94 x
95 ))
96 }
97 }
98 }
99 }
100 };
101}
102
103#[cfg(test)]
104mod test_assert_abs_diff_ge_x_as_result {
105 use std::sync::Once;
106
107 #[test]
108 fn gt() {
109 let a: i8 = 10;
110 let b: i8 = 13;
111 let x: i8 = 2;
112 for _ in 0..1 {
113 let actual = assert_abs_diff_ge_x_as_result!(a, b, x);
114 let expect = (3, 2);
115 assert_eq!(actual.unwrap(), expect);
116 }
117 }
118
119 #[test]
120 fn gt_twice() {
121 let a: i8 = 10;
122 let b: i8 = 13;
123 let x: i8 = 2;
124 for _ in 0..1 {
125 let actual = assert_abs_diff_ge_x_as_result!(a, b, x);
126 let expect = (3, 2);
127 assert_eq!(actual.unwrap(), expect);
128 }
129 }
130
131 #[test]
132 fn gt_once() {
133 static A: Once = Once::new();
134 fn a() -> i8 {
135 if A.is_completed() {
136 panic!("A.is_completed()")
137 } else {
138 A.call_once(|| {})
139 }
140 10
141 }
142
143 static B: Once = Once::new();
144 fn b() -> i8 {
145 if B.is_completed() {
146 panic!("B.is_completed()")
147 } else {
148 B.call_once(|| {})
149 }
150 13
151 }
152
153 static X: Once = Once::new();
154 fn x() -> i8 {
155 if X.is_completed() {
156 panic!("X.is_completed()")
157 } else {
158 X.call_once(|| {})
159 }
160 2
161 }
162
163 assert_eq!(A.is_completed(), false);
164 assert_eq!(B.is_completed(), false);
165 assert_eq!(X.is_completed(), false);
166 let result = assert_abs_diff_ge_x_as_result!(a(), b(), x());
167 assert!(result.is_ok());
168 assert_eq!(A.is_completed(), true);
169 assert_eq!(B.is_completed(), true);
170 assert_eq!(X.is_completed(), true);
171 }
172
173 #[test]
174 fn eq() {
175 let a: i8 = 10;
176 let b: i8 = 13;
177 let x: i8 = 3;
178 for _ in 0..1 {
179 let actual = assert_abs_diff_ge_x_as_result!(a, b, x);
180 let expect = (3, 3);
181 assert_eq!(actual.unwrap(), expect);
182 }
183 }
184
185 #[test]
186 fn eq_once() {
187 static A: Once = Once::new();
188 fn a() -> i8 {
189 if A.is_completed() {
190 panic!("A.is_completed()")
191 } else {
192 A.call_once(|| {})
193 }
194 10
195 }
196
197 static B: Once = Once::new();
198 fn b() -> i8 {
199 if B.is_completed() {
200 panic!("B.is_completed()")
201 } else {
202 B.call_once(|| {})
203 }
204 13
205 }
206
207 static X: Once = Once::new();
208 fn x() -> i8 {
209 if X.is_completed() {
210 panic!("X.is_completed()")
211 } else {
212 X.call_once(|| {})
213 }
214 3
215 }
216
217 assert_eq!(A.is_completed(), false);
218 assert_eq!(B.is_completed(), false);
219 assert_eq!(X.is_completed(), false);
220 let result = assert_abs_diff_ge_x_as_result!(a(), b(), x());
221 assert!(result.is_ok());
222 assert_eq!(A.is_completed(), true);
223 assert_eq!(B.is_completed(), true);
224 assert_eq!(X.is_completed(), true);
225 }
226 #[test]
227 fn lt() {
228 let a: i8 = 10;
229 let b: i8 = 13;
230 let x: i8 = 4;
231 let actual = assert_abs_diff_ge_x_as_result!(a, b, x);
232 let message = concat!(
233 "assertion failed: `assert_abs_diff_ge_x!(a, b, x)`\n",
234 "https://docs.rs/assertables/9.8.6/assertables/macro.assert_abs_diff_ge_x.html\n",
235 " a label: `a`,\n",
236 " a debug: `10`,\n",
237 " b label: `b`,\n",
238 " b debug: `13`,\n",
239 " x label: `x`,\n",
240 " x debug: `4`,\n",
241 " |Δ|: `3`,\n",
242 " |Δ| ≥ x: false"
243 );
244 assert_eq!(actual.unwrap_err(), message);
245 }
246
247 #[test]
248 fn overflow() {
249 let a: i8 = i8::MAX;
250 let b: i8 = i8::MIN;
251 let x: i8 = 0;
252 let actual = assert_abs_diff_ge_x_as_result!(a, b, x);
253 let message = format!(
254 concat!(
255 "assertion failed: `assert_abs_diff_ge_x!(a, b, x)`\n",
256 "https://docs.rs/assertables/9.8.6/assertables/macro.assert_abs_diff_ge_x.html\n",
257 " a label: `a`,\n",
258 " a debug: `{}`,\n",
259 " b label: `b`,\n",
260 " b debug: `{}`,\n",
261 " x label: `x`,\n",
262 " x debug: `{}`,\n",
263 " |Δ|: panic"
264 ),
265 a, b, x
266 );
267 assert_eq!(actual.unwrap_err(), message);
268 }
269}
270
271/// Assert an absolute difference is greater than or equal to an expression.
272///
273/// Pseudocode:<br>
274/// |Δ| ≥ x
275///
276/// * If true, return `(lhs, rhs)`.
277///
278/// * Otherwise, call [`panic!`] with a message and the values of the
279/// expressions with their debug representations.
280///
281/// # Examples
282///
283/// ```rust
284/// use assertables::*;
285/// # use std::panic;
286///
287/// # fn main() {
288/// let a = 10;
289/// let b = 13;
290/// let x = 2;
291/// assert_abs_diff_ge_x!(a, b, x);
292///
293/// # let result = panic::catch_unwind(|| {
294/// // This will panic
295/// let a = 10;
296/// let b = 13;
297/// let x = 4;
298/// assert_abs_diff_ge_x!(a, b, x);
299/// # });
300/// // assertion failed: `assert_abs_diff_ge_x!(a, b)`
301/// // https://docs.rs/assertables/…/assertables/macro.assert_abs_diff_ge_x.html
302/// // a label: `a`,
303/// // a debug: `10`,
304/// // b label: `b`,
305/// // b debug: `13`,
306/// // x label: `x`,
307/// // x debug: `4`,
308/// // |Δ|: `3`,
309/// // |Δ| ≥ x: false
310/// # let actual = result.unwrap_err().downcast::<String>().unwrap().to_string();
311/// # let message = concat!(
312/// # "assertion failed: `assert_abs_diff_ge_x!(a, b, x)`\n",
313/// # "https://docs.rs/assertables/9.8.6/assertables/macro.assert_abs_diff_ge_x.html\n",
314/// # " a label: `a`,\n",
315/// # " a debug: `10`,\n",
316/// # " b label: `b`,\n",
317/// # " b debug: `13`,\n",
318/// # " x label: `x`,\n",
319/// # " x debug: `4`,\n",
320/// # " |Δ|: `3`,\n",
321/// # " |Δ| ≥ x: false"
322/// # );
323/// # assert_eq!(actual, message);
324/// # }
325/// ```
326///
327/// # Module macros
328///
329/// * [`assert_abs_diff_ge_x`](macro@crate::assert_abs_diff_ge_x)
330/// * [`assert_abs_diff_ge_x_as_result`](macro@crate::assert_abs_diff_ge_x_as_result)
331/// * [`debug_assert_abs_diff_ge_x`](macro@crate::debug_assert_abs_diff_ge_x)
332///
333#[macro_export]
334macro_rules! assert_abs_diff_ge_x {
335 ($a:expr, $b:expr, $x:expr $(,)?) => {
336 match $crate::assert_abs_diff_ge_x_as_result!($a, $b, $x) {
337 Ok(x) => x,
338 Err(err) => panic!("{}", err),
339 }
340 };
341 ($a:expr, $b:expr, $x:expr, $($message:tt)+) => {
342 match $crate::assert_abs_diff_ge_x_as_result!($a, $b, $x) {
343 Ok(x) => x,
344 Err(err) => panic!("{}\n{}", format_args!($($message)+), err),
345 }
346 };
347}
348
349#[cfg(test)]
350mod test_assert_abs_diff_ge_x {
351 use std::panic;
352
353 #[test]
354 fn gt() {
355 let a = 10;
356 let b = 13;
357 let x = 2;
358 for _ in 0..1 {
359 let actual = assert_abs_diff_ge_x!(a, b, x);
360 let expect = (3, 2);
361 assert_eq!(actual, expect);
362 }
363 }
364
365 #[test]
366 fn eq() {
367 let a = 10;
368 let b = 13;
369 let x = 3;
370 for _ in 0..1 {
371 let actual = assert_abs_diff_ge_x!(a, b, x);
372 let expect = (3, 3);
373 assert_eq!(actual, expect);
374 }
375 }
376
377 #[test]
378 fn lt() {
379 let result = panic::catch_unwind(|| {
380 let a = 10;
381 let b = 13;
382 let x = 4;
383 let _actual = assert_abs_diff_ge_x!(a, b, x);
384 });
385 let message = concat!(
386 "assertion failed: `assert_abs_diff_ge_x!(a, b, x)`\n",
387 "https://docs.rs/assertables/9.8.6/assertables/macro.assert_abs_diff_ge_x.html\n",
388 " a label: `a`,\n",
389 " a debug: `10`,\n",
390 " b label: `b`,\n",
391 " b debug: `13`,\n",
392 " x label: `x`,\n",
393 " x debug: `4`,\n",
394 " |Δ|: `3`,\n",
395 " |Δ| ≥ x: false"
396 );
397 assert_eq!(
398 result
399 .unwrap_err()
400 .downcast::<String>()
401 .unwrap()
402 .to_string(),
403 message
404 );
405 }
406
407 #[test]
408 fn overflow() {
409 let a: i8 = i8::MAX;
410 let b: i8 = i8::MIN;
411 let x: i8 = 0;
412 let result = panic::catch_unwind(|| {
413 let _actual = assert_abs_diff_ge_x!(a, b, x);
414 });
415 let message = format!(
416 concat!(
417 "assertion failed: `assert_abs_diff_ge_x!(a, b, x)`\n",
418 "https://docs.rs/assertables/9.8.6/assertables/macro.assert_abs_diff_ge_x.html\n",
419 " a label: `a`,\n",
420 " a debug: `{}`,\n",
421 " b label: `b`,\n",
422 " b debug: `{}`,\n",
423 " x label: `x`,\n",
424 " x debug: `{}`,\n",
425 " |Δ|: panic"
426 ),
427 a, b, x
428 );
429 assert_eq!(
430 result
431 .unwrap_err()
432 .downcast::<String>()
433 .unwrap()
434 .to_string(),
435 message
436 );
437 }
438}
439
440/// Assert an absolute difference is greater than or equal to an expression.
441///
442/// Pseudocode:<br>
443/// |Δ| ≥ c
444///
445/// This macro provides the same statements as [`assert_abs_diff_ge_x`](macro.assert_abs_diff_ge_x.html),
446/// except this macro's statements are only enabled in non-optimized
447/// builds by default. An optimized build will not execute this macro's
448/// statements unless `-x debug-assertions` is passed to the compiler.
449///
450/// This macro is useful for checks that are too expensive to be present
451/// in a release build but may be helpful during development.
452///
453/// The result of expanding this macro is always type checked.
454///
455/// An unchecked assertion allows a program in an inconsistent state to
456/// keep running, which might have unexpected consequences but does not
457/// introduce unsafety as long as this only happens in safe code. The
458/// performance cost of assertions, however, is not measurable in general.
459/// Replacing `assert*!` with `debug_assert*!` is thus only encouraged
460/// after thorough profiling, and more importantly, only in safe code!
461///
462/// This macro is intended to work in a similar way to
463/// [`::std::debug_assert`](https://doc.rust-lang.org/std/macro.debug_assert.html).
464///
465/// # Module macros
466///
467/// * [`assert_abs_diff_ge_x`](macro@crate::assert_abs_diff_ge_x)
468/// * [`assert_abs_diff_ge_x`](macro@crate::assert_abs_diff_ge_x)
469/// * [`debug_assert_abs_diff_ge_x`](macro@crate::debug_assert_abs_diff_ge_x)
470///
471#[macro_export]
472macro_rules! debug_assert_abs_diff_ge_x {
473 ($($arg:tt)*) => {
474 if cfg!(debug_assertions) {
475 $crate::assert_abs_diff_ge_x!($($arg)*);
476 }
477 };
478}
479
480#[cfg(test)]
481mod test_debug_assert_abs_diff_ge_x {
482 use std::panic;
483
484 #[test]
485 fn gt() {
486 let a = 10;
487 let b = 13;
488 let x = 2;
489 for _ in 0..1 {
490 let _actual = debug_assert_abs_diff_ge_x!(a, b, x);
491 let _expect = (3, 2);
492 // assert_eq!(actual, expect);
493 }
494 }
495
496 #[test]
497 fn eq() {
498 let a = 10;
499 let b = 13;
500 let x = 3;
501 for _ in 0..1 {
502 let _actual = debug_assert_abs_diff_ge_x!(a, b, x);
503 let _expect = (3, 3);
504 // assert_eq!(actual, expect);
505 }
506 }
507
508 #[test]
509 fn lt() {
510 let result = panic::catch_unwind(|| {
511 let a = 10;
512 let b = 13;
513 let x = 4;
514 let _actual = debug_assert_abs_diff_ge_x!(a, b, x);
515 });
516 let message = concat!(
517 "assertion failed: `assert_abs_diff_ge_x!(a, b, x)`\n",
518 "https://docs.rs/assertables/9.8.6/assertables/macro.assert_abs_diff_ge_x.html\n",
519 " a label: `a`,\n",
520 " a debug: `10`,\n",
521 " b label: `b`,\n",
522 " b debug: `13`,\n",
523 " x label: `x`,\n",
524 " x debug: `4`,\n",
525 " |Δ|: `3`,\n",
526 " |Δ| ≥ x: false"
527 );
528 assert_eq!(
529 result
530 .unwrap_err()
531 .downcast::<String>()
532 .unwrap()
533 .to_string(),
534 message
535 );
536 }
537
538 #[test]
539 fn overflow() {
540 let a: i8 = i8::MAX;
541 let b: i8 = i8::MIN;
542 let x: i8 = 0;
543 let result = panic::catch_unwind(|| {
544 let _actual = debug_assert_abs_diff_ge_x!(a, b, x);
545 });
546 let message = format!(
547 concat!(
548 "assertion failed: `assert_abs_diff_ge_x!(a, b, x)`\n",
549 "https://docs.rs/assertables/9.8.6/assertables/macro.assert_abs_diff_ge_x.html\n",
550 " a label: `a`,\n",
551 " a debug: `{}`,\n",
552 " b label: `b`,\n",
553 " b debug: `{}`,\n",
554 " x label: `x`,\n",
555 " x debug: `{}`,\n",
556 " |Δ|: panic"
557 ),
558 a, b, x
559 );
560 assert_eq!(
561 result
562 .unwrap_err()
563 .downcast::<String>()
564 .unwrap()
565 .to_string(),
566 message
567 );
568 }
569}