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}