assertables/assert_in/
assert_in_delta.rs

1//! Assert a number is within delta of another.
2//!
3//! Pseudocode:<br>
4//! | a - b | ≤ Δ
5//!
6//! # Example
7//!
8//! ```rust
9//! use assertables::*;
10//!
11//! let a: i8 = 10;
12//! let b: i8 = 11;
13//! let delta: i8 = 1;
14//! assert_in_delta!(a, b, delta);
15//! ```
16//!
17//!
18//! ## Comparisons
19//!
20//! This crate provides macro groups that test approximations and nearness:
21//!
22//! * [`assert_approx_eq`](macro@crate::assert_approx_eq) and
23//!   [`assert_approx_ne`](macro@crate::assert_approx_ne) test the approximate
24//!   equality within 1e-6. The macro name and the approximate value are chosen
25//!   to be similar to the longtime popular rust crate `assert_approx_eq`.
26//!
27//! * [`assert_in_delta`](macro@crate::assert_in_delta) tests the absolute error
28//!   (i.e. delta). This is the magnitude of the difference between the exact
29//!   value and the approximation.
30//!
31//! * [`assert_in_epsilon`](macro@crate::assert_in_epsilon) tests the relative
32//!   error (i.e. epsilon). This is the absolute error divided by the magnitude
33//!   of the exact value. This can be used to compare approximations of numbers
34//!   of wildly differing size.
35//!
36//! Examples:
37//!
38//! * Approximating the number 100 and 103 has an absolute error (delta) of 3
39//!   and a relative error (epsilon) of 0.03.
40//!
41//! * Approximating the number 1,000,000 and 1,000,003 has an absolute error
42//!   (delta) of 3, and a relative error (epsilon) of 0.000003.
43//!
44//! * For many kinds of applications, the relative error is more important than
45//!   the absolute error.
46//!
47//!
48//! ## Thanks
49//!
50//! * Thanks to [Ashley Williams](https://github.com/ashleygwilliams) for
51//!   creating and maintaining the `assert_approx_eq` crate.
52//!
53//! * Thanks to [Ryan Davis](https://github.com/zenspider) and Ruby minitest for
54//!   creating and maintaining `assert_in_delta` and `assert_in_epsilon` code.
55//!
56//!
57//! # Module macros
58//!
59//! * [`assert_in_delta`](macro@crate::assert_in_delta)
60//! * [`assert_in_delta_as_result`](macro@crate::assert_in_delta_as_result)
61//! * [`debug_assert_in_delta`](macro@crate::debug_assert_in_delta)
62
63/// Assert a number is within delta of another.
64///
65/// Pseudocode:<br>
66/// | a - b | ≤ Δ
67///
68/// * If true, return Result `Ok((lhs, rhs))`.
69///
70/// * When false, return [`Err`] with a message and the values of the
71///   expressions with their debug representations.
72///
73/// This macro provides the same statements as [`assert_`](macro.assert_.html), except this macro
74/// returns a Result, rather than doing a panic.
75///
76/// This macro is useful for runtime checks, such as checking parameters, or
77/// sanitizing inputs, or handling different results in different ways.
78///
79/// # Module macros
80///
81/// * [`assert_in_delta`](macro@crate::assert_in_delta)
82/// * [`assert_in_delta_as_result`](macro@crate::assert_in_delta_as_result)
83/// * [`debug_assert_in_delta`](macro@crate::debug_assert_in_delta)
84///
85#[macro_export]
86macro_rules! assert_in_delta_as_result {
87    ($a:expr, $b:expr, $delta:expr $(,)?) => {{
88        match (&$a, &$b, &$delta) {
89            (a, b, delta) => {
90                let abs_diff = if (a >= b) { a - b } else { b - a };
91                if abs_diff <= *delta {
92                    Ok((abs_diff, *delta))
93                } else {
94                    Err(
95                        format!(
96                            concat!(
97                                "assertion failed: `assert_in_delta!(a, b, Δ)`\n",
98                                "https://docs.rs/assertables/9.5.1/assertables/macro.assert_in_delta.html\n",
99                                "       a label: `{}`,\n",
100                                "       a debug: `{:?}`,\n",
101                                "       b label: `{}`,\n",
102                                "       b debug: `{:?}`,\n",
103                                "       Δ label: `{}`,\n",
104                                "       Δ debug: `{:?}`,\n",
105                                "     | a - b |: `{:?}`,\n",
106                                " | a - b | ≤ Δ: {}"
107                            ),
108                            stringify!($a),
109                            a,
110                            stringify!($b),
111                            b,
112                            stringify!($delta),
113                            delta,
114                            abs_diff,
115                            false
116                        )
117                    )
118                }
119            }
120        }
121    }};
122}
123
124#[cfg(test)]
125mod test_assert_in_delta_as_result {
126
127    #[test]
128    fn success() {
129        let a: i8 = 10;
130        let b: i8 = 11;
131        let delta: i8 = 1;
132        let actual = assert_in_delta_as_result!(a, b, delta);
133        assert_eq!(actual.unwrap(), (1 as i8, 1 as i8));
134    }
135
136    #[test]
137    fn failure() {
138        let a: i8 = 10;
139        let b: i8 = 12;
140        let delta: i8 = 1;
141        let actual = assert_in_delta_as_result!(a, b, delta);
142        let message = concat!(
143            "assertion failed: `assert_in_delta!(a, b, Δ)`\n",
144            "https://docs.rs/assertables/9.5.1/assertables/macro.assert_in_delta.html\n",
145            "       a label: `a`,\n",
146            "       a debug: `10`,\n",
147            "       b label: `b`,\n",
148            "       b debug: `12`,\n",
149            "       Δ label: `delta`,\n",
150            "       Δ debug: `1`,\n",
151            "     | a - b |: `2`,\n",
152            " | a - b | ≤ Δ: false"
153        );
154        assert_eq!(actual.unwrap_err(), message);
155    }
156}
157
158/// Assert a number is within delta of another.
159///
160/// Pseudocode:<br>
161/// | a - b | ≤ Δ
162///
163/// * If true, return `(lhs, rhs)`.
164///
165/// * Otherwise, call [`panic!`] with a message and the values of the
166///   expressions with their debug representations.
167///
168/// # Examples
169///
170/// ```rust
171/// use assertables::*;
172/// # use std::panic;
173///
174/// # fn main() {
175/// let a: i8 = 10;
176/// let b: i8 = 11;
177/// let delta: i8 = 1;
178/// assert_in_delta!(a, b, delta);
179///
180/// # let result = panic::catch_unwind(|| {
181/// // This will panic
182/// let a: i8 = 10;
183/// let b: i8 = 12;
184/// let delta: i8 = 1;
185/// assert_in_delta!(a, b, delta);
186/// # });
187/// // assertion failed: `assert_in_delta!(a, b, Δ)`
188/// // https://docs.rs/assertables/9.5.1/assertables/macro.assert_in_delta.html
189/// //        a label: `a`,
190/// //        a debug: `10`,
191/// //        b label: `b`,
192/// //        b debug: `12`,
193/// //        Δ label: `delta`,
194/// //        Δ debug: `1`,
195/// //      | a - b |: `2`,
196/// //  | a - b | ≤ Δ: false
197/// # let actual = result.unwrap_err().downcast::<String>().unwrap().to_string();
198/// # let message = concat!(
199/// #     "assertion failed: `assert_in_delta!(a, b, Δ)`\n",
200/// #     "https://docs.rs/assertables/9.5.1/assertables/macro.assert_in_delta.html\n",
201/// #     "       a label: `a`,\n",
202/// #     "       a debug: `10`,\n",
203/// #     "       b label: `b`,\n",
204/// #     "       b debug: `12`,\n",
205/// #     "       Δ label: `delta`,\n",
206/// #     "       Δ debug: `1`,\n",
207/// #     "     | a - b |: `2`,\n",
208/// #     " | a - b | ≤ Δ: false",
209/// # );
210/// # assert_eq!(actual, message);
211/// # }
212/// ```
213///
214/// The macros `assert_in_delta` and `assert_in_epsilon` can test
215/// approximations:
216///
217/// * For an approximation, the absolute error (i.e. delta) is the magnitude of
218///   the difference between the exact value and the approximation. For this,
219///  use the macro
220///
221/// * For an approximation, the relative error (i.e. epsilon) is the absolute
222///   error divided by the magnitude of the exact value. This can be used to
223///   compare approximations of numbers of wildly differing size.
224///
225/// * For example, approximating the number 1,000 with an absolute error of 3
226///   is, in most applications, much worse than approximating the number
227///   1,000,000 with an absolute error of 3; in the first case the relative
228///   error is 0.003 and in the second it is only 0.000003.
229///
230/// * Thanks to Ruby minitest for the example and documentation.
231///
232/// # Module macros
233///
234/// * [`assert_in_delta`](macro@crate::assert_in_delta)
235/// * [`assert_in_delta_as_result`](macro@crate::assert_in_delta_as_result)
236/// * [`debug_assert_in_delta`](macro@crate::debug_assert_in_delta)
237///
238#[macro_export]
239macro_rules! assert_in_delta {
240    ($a:expr, $b:expr, $delta:expr $(,)?) => {{
241        match $crate::assert_in_delta_as_result!($a, $b, $delta) {
242            Ok(x) => x,
243            Err(err) => panic!("{}", err),
244        }
245    }};
246    ($a:expr, $b:expr, $delta:expr, $($message:tt)+) => {{
247        match $crate::assert_in_delta_as_result!($a, $b, $delta) {
248            Ok(x) => x,
249            Err(err) => panic!("{}\n{}", format_args!($($message)+), err),
250        }
251    }};
252}
253
254#[cfg(test)]
255mod test_assert_in_delta {
256    use std::panic;
257
258    #[test]
259    fn success() {
260        let a: i8 = 10;
261        let b: i8 = 11;
262        let delta: i8 = 1;
263        let actual = assert_in_delta!(a, b, delta);
264        assert_eq!(actual, (1 as i8, 1 as i8));
265    }
266
267    #[test]
268    fn failure() {
269        let a: i8 = 10;
270        let b: i8 = 12;
271        let delta: i8 = 1;
272        let result = panic::catch_unwind(|| {
273            let _actual = assert_in_delta!(a, b, delta);
274        });
275        let message = concat!(
276            "assertion failed: `assert_in_delta!(a, b, Δ)`\n",
277            "https://docs.rs/assertables/9.5.1/assertables/macro.assert_in_delta.html\n",
278            "       a label: `a`,\n",
279            "       a debug: `10`,\n",
280            "       b label: `b`,\n",
281            "       b debug: `12`,\n",
282            "       Δ label: `delta`,\n",
283            "       Δ debug: `1`,\n",
284            "     | a - b |: `2`,\n",
285            " | a - b | ≤ Δ: false"
286        );
287        assert_eq!(
288            result
289                .unwrap_err()
290                .downcast::<String>()
291                .unwrap()
292                .to_string(),
293            message
294        );
295    }
296}
297
298/// Assert a number is within delta of another.
299///
300/// Pseudocode:<br>
301/// | a - b | ≤ Δ
302///
303/// This macro provides the same statements as [`assert_in_delta`](macro.assert_in_delta.html),
304/// except this macro's statements are only enabled in non-optimized
305/// builds by default. An optimized build will not execute this macro's
306/// statements unless `-C debug-assertions` is passed to the compiler.
307///
308/// This macro is useful for checks that are too expensive to be present
309/// in a release build but may be helpful during development.
310///
311/// The result of expanding this macro is always type checked.
312///
313/// An unchecked assertion allows a program in an inconsistent state to
314/// keep running, which might have unexpected consequences but does not
315/// introduce unsafety as long as this only happens in safe code. The
316/// performance cost of assertions, however, is not measurable in general.
317/// Replacing `assert*!` with `debug_assert*!` is thus only encouraged
318/// after thorough profiling, and more importantly, only in safe code!
319///
320/// This macro is intended to work in a similar way to
321/// [`::std::debug_assert`](https://doc.rust-lang.org/std/macro.debug_assert.html).
322///
323/// # Module macros
324///
325/// * [`assert_in_delta`](macro@crate::assert_in_delta)
326/// * [`assert_in_delta`](macro@crate::assert_in_delta)
327/// * [`debug_assert_in_delta`](macro@crate::debug_assert_in_delta)
328///
329#[macro_export]
330macro_rules! debug_assert_in_delta {
331    ($($arg:tt)*) => {
332        if $crate::cfg!(debug_assertions) {
333            $crate::assert_in_delta!($($arg)*);
334        }
335    };
336}