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