assertables/assert_diff/
assert_diff_eq_x.rs

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