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}