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.3/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.3/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 assert_eq!(actual.unwrap(), (3, 2));
115 }
116 }
117
118 #[test]
119 fn gt_twice() {
120 let a: i8 = 10;
121 let b: i8 = 13;
122 let x: i8 = 2;
123 for _ in 0..1 {
124 let actual = assert_abs_diff_ge_x_as_result!(a, b, x);
125 assert_eq!(actual.unwrap(), (3, 2));
126 }
127 }
128
129 #[test]
130 fn gt_once() {
131 static A: Once = Once::new();
132 fn a() -> i8 {
133 if A.is_completed() {
134 panic!("A.is_completed()")
135 } else {
136 A.call_once(|| {})
137 }
138 10
139 }
140
141 static B: Once = Once::new();
142 fn b() -> i8 {
143 if B.is_completed() {
144 panic!("B.is_completed()")
145 } else {
146 B.call_once(|| {})
147 }
148 13
149 }
150
151 static X: Once = Once::new();
152 fn x() -> i8 {
153 if X.is_completed() {
154 panic!("X.is_completed()")
155 } else {
156 X.call_once(|| {})
157 }
158 2
159 }
160
161 assert_eq!(A.is_completed(), false);
162 assert_eq!(B.is_completed(), false);
163 assert_eq!(X.is_completed(), false);
164 let result = assert_abs_diff_ge_x_as_result!(a(), b(), x());
165 assert!(result.is_ok());
166 assert_eq!(A.is_completed(), true);
167 assert_eq!(B.is_completed(), true);
168 assert_eq!(X.is_completed(), true);
169 }
170
171 #[test]
172 fn eq() {
173 let a: i8 = 10;
174 let b: i8 = 13;
175 let x: i8 = 3;
176 for _ in 0..1 {
177 let actual = assert_abs_diff_ge_x_as_result!(a, b, x);
178 assert_eq!(actual.unwrap(), (3, 3));
179 }
180 }
181
182 #[test]
183 fn eq_once() {
184 static A: Once = Once::new();
185 fn a() -> i8 {
186 if A.is_completed() {
187 panic!("A.is_completed()")
188 } else {
189 A.call_once(|| {})
190 }
191 10
192 }
193
194 static B: Once = Once::new();
195 fn b() -> i8 {
196 if B.is_completed() {
197 panic!("B.is_completed()")
198 } else {
199 B.call_once(|| {})
200 }
201 13
202 }
203
204 static X: Once = Once::new();
205 fn x() -> i8 {
206 if X.is_completed() {
207 panic!("X.is_completed()")
208 } else {
209 X.call_once(|| {})
210 }
211 3
212 }
213
214 assert_eq!(A.is_completed(), false);
215 assert_eq!(B.is_completed(), false);
216 assert_eq!(X.is_completed(), false);
217 let result = assert_abs_diff_ge_x_as_result!(a(), b(), x());
218 assert!(result.is_ok());
219 assert_eq!(A.is_completed(), true);
220 assert_eq!(B.is_completed(), true);
221 assert_eq!(X.is_completed(), true);
222 }
223 #[test]
224 fn lt() {
225 let a: i8 = 10;
226 let b: i8 = 13;
227 let x: i8 = 4;
228 let actual = assert_abs_diff_ge_x_as_result!(a, b, x);
229 let message = concat!(
230 "assertion failed: `assert_abs_diff_ge_x!(a, b, x)`\n",
231 "https://docs.rs/assertables/9.8.3/assertables/macro.assert_abs_diff_ge_x.html\n",
232 " a label: `a`,\n",
233 " a debug: `10`,\n",
234 " b label: `b`,\n",
235 " b debug: `13`,\n",
236 " x label: `x`,\n",
237 " x debug: `4`,\n",
238 " |Δ|: `3`,\n",
239 " |Δ| ≥ x: false"
240 );
241 assert_eq!(actual.unwrap_err(), message);
242 }
243
244 #[test]
245 fn overflow() {
246 let a: i8 = i8::MAX;
247 let b: i8 = i8::MIN;
248 let x: i8 = 0;
249 let actual = assert_abs_diff_ge_x_as_result!(a, b, x);
250 let message = format!(
251 concat!(
252 "assertion failed: `assert_abs_diff_ge_x!(a, b, x)`\n",
253 "https://docs.rs/assertables/9.8.3/assertables/macro.assert_abs_diff_ge_x.html\n",
254 " a label: `a`,\n",
255 " a debug: `{}`,\n",
256 " b label: `b`,\n",
257 " b debug: `{}`,\n",
258 " x label: `x`,\n",
259 " x debug: `{}`,\n",
260 " |Δ|: panic"
261 ),
262 a, b, x
263 );
264 assert_eq!(actual.unwrap_err(), message);
265 }
266}
267
268/// Assert an absolute difference is greater than or equal to an expression.
269///
270/// Pseudocode:<br>
271/// |Δ| ≥ x
272///
273/// * If true, return `(lhs, rhs)`.
274///
275/// * Otherwise, call [`panic!`] with a message and the values of the
276/// expressions with their debug representations.
277///
278/// # Examples
279///
280/// ```rust
281/// use assertables::*;
282/// # use std::panic;
283///
284/// # fn main() {
285/// let a = 10;
286/// let b = 13;
287/// let x = 2;
288/// assert_abs_diff_ge_x!(a, b, x);
289///
290/// # let result = panic::catch_unwind(|| {
291/// // This will panic
292/// let a = 10;
293/// let b = 13;
294/// let x = 4;
295/// assert_abs_diff_ge_x!(a, b, x);
296/// # });
297/// // assertion failed: `assert_abs_diff_ge_x!(a, b)`
298/// // https://docs.rs/assertables/…/assertables/macro.assert_abs_diff_ge_x.html
299/// // a label: `a`,
300/// // a debug: `10`,
301/// // b label: `b`,
302/// // b debug: `13`,
303/// // x label: `x`,
304/// // x debug: `4`,
305/// // |Δ|: `3`,
306/// // |Δ| ≥ x: false
307/// # let actual = result.unwrap_err().downcast::<String>().unwrap().to_string();
308/// # let message = concat!(
309/// # "assertion failed: `assert_abs_diff_ge_x!(a, b, x)`\n",
310/// # "https://docs.rs/assertables/9.8.3/assertables/macro.assert_abs_diff_ge_x.html\n",
311/// # " a label: `a`,\n",
312/// # " a debug: `10`,\n",
313/// # " b label: `b`,\n",
314/// # " b debug: `13`,\n",
315/// # " x label: `x`,\n",
316/// # " x debug: `4`,\n",
317/// # " |Δ|: `3`,\n",
318/// # " |Δ| ≥ x: false"
319/// # );
320/// # assert_eq!(actual, message);
321/// # }
322/// ```
323///
324/// # Module macros
325///
326/// * [`assert_abs_diff_ge_x`](macro@crate::assert_abs_diff_ge_x)
327/// * [`assert_abs_diff_ge_x_as_result`](macro@crate::assert_abs_diff_ge_x_as_result)
328/// * [`debug_assert_abs_diff_ge_x`](macro@crate::debug_assert_abs_diff_ge_x)
329///
330#[macro_export]
331macro_rules! assert_abs_diff_ge_x {
332 ($a:expr, $b:expr, $x:expr $(,)?) => {
333 match $crate::assert_abs_diff_ge_x_as_result!($a, $b, $x) {
334 Ok(x) => x,
335 Err(err) => panic!("{}", err),
336 }
337 };
338 ($a:expr, $b:expr, $x:expr, $($message:tt)+) => {
339 match $crate::assert_abs_diff_ge_x_as_result!($a, $b, $x) {
340 Ok(x) => x,
341 Err(err) => panic!("{}\n{}", format_args!($($message)+), err),
342 }
343 };
344}
345
346#[cfg(test)]
347mod test_assert_abs_diff_ge_x {
348 use std::panic;
349
350 #[test]
351 fn gt() {
352 let a = 10;
353 let b = 13;
354 let x = 2;
355 for _ in 0..1 {
356 let actual = assert_abs_diff_ge_x!(a, b, x);
357 assert_eq!(actual, (3, 2));
358 }
359 }
360
361 #[test]
362 fn eq() {
363 let a = 10;
364 let b = 13;
365 let x = 3;
366 for _ in 0..1 {
367 let actual = assert_abs_diff_ge_x!(a, b, x);
368 assert_eq!(actual, (3, 3));
369 }
370 }
371
372 #[test]
373 fn lt() {
374 let result = panic::catch_unwind(|| {
375 let a = 10;
376 let b = 13;
377 let x = 4;
378 let _actual = assert_abs_diff_ge_x!(a, b, x);
379 });
380 let message = concat!(
381 "assertion failed: `assert_abs_diff_ge_x!(a, b, x)`\n",
382 "https://docs.rs/assertables/9.8.3/assertables/macro.assert_abs_diff_ge_x.html\n",
383 " a label: `a`,\n",
384 " a debug: `10`,\n",
385 " b label: `b`,\n",
386 " b debug: `13`,\n",
387 " x label: `x`,\n",
388 " x debug: `4`,\n",
389 " |Δ|: `3`,\n",
390 " |Δ| ≥ x: false"
391 );
392 assert_eq!(
393 result
394 .unwrap_err()
395 .downcast::<String>()
396 .unwrap()
397 .to_string(),
398 message
399 );
400 }
401
402 #[test]
403 fn overflow() {
404 let a: i8 = i8::MAX;
405 let b: i8 = i8::MIN;
406 let x: i8 = 0;
407 let result = panic::catch_unwind(|| {
408 let _actual = assert_abs_diff_ge_x!(a, b, x);
409 });
410 let message = format!(
411 concat!(
412 "assertion failed: `assert_abs_diff_ge_x!(a, b, x)`\n",
413 "https://docs.rs/assertables/9.8.3/assertables/macro.assert_abs_diff_ge_x.html\n",
414 " a label: `a`,\n",
415 " a debug: `{}`,\n",
416 " b label: `b`,\n",
417 " b debug: `{}`,\n",
418 " x label: `x`,\n",
419 " x debug: `{}`,\n",
420 " |Δ|: panic"
421 ),
422 a, b, x
423 );
424 assert_eq!(
425 result
426 .unwrap_err()
427 .downcast::<String>()
428 .unwrap()
429 .to_string(),
430 message
431 );
432 }
433}
434
435/// Assert an absolute difference is greater than or equal to an expression.
436///
437/// Pseudocode:<br>
438/// |Δ| ≥ c
439///
440/// This macro provides the same statements as [`assert_abs_diff_ge_x`](macro.assert_abs_diff_ge_x.html),
441/// except this macro's statements are only enabled in non-optimized
442/// builds by default. An optimized build will not execute this macro's
443/// statements unless `-x debug-assertions` is passed to the compiler.
444///
445/// This macro is useful for checks that are too expensive to be present
446/// in a release build but may be helpful during development.
447///
448/// The result of expanding this macro is always type checked.
449///
450/// An unchecked assertion allows a program in an inconsistent state to
451/// keep running, which might have unexpected consequences but does not
452/// introduce unsafety as long as this only happens in safe code. The
453/// performance cost of assertions, however, is not measurable in general.
454/// Replacing `assert*!` with `debug_assert*!` is thus only encouraged
455/// after thorough profiling, and more importantly, only in safe code!
456///
457/// This macro is intended to work in a similar way to
458/// [`::std::debug_assert`](https://doc.rust-lang.org/std/macro.debug_assert.html).
459///
460/// # Module macros
461///
462/// * [`assert_abs_diff_ge_x`](macro@crate::assert_abs_diff_ge_x)
463/// * [`assert_abs_diff_ge_x`](macro@crate::assert_abs_diff_ge_x)
464/// * [`debug_assert_abs_diff_ge_x`](macro@crate::debug_assert_abs_diff_ge_x)
465///
466#[macro_export]
467macro_rules! debug_assert_abs_diff_ge_x {
468 ($($arg:tt)*) => {
469 if $crate::cfg!(debug_assertions) {
470 $crate::assert_abs_diff_ge_x!($($arg)*);
471 }
472 };
473}