substance_framework/
macros.rs

1/***********************************************************************************
2 * MIT License                                                                     *
3 *                                                                                 *
4 * Copyright (c) 2022 Tutul                                                        *
5 *                                                                                 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy    *
7 * of this software and associated documentation files (the "Software"), to deal   *
8 * in the Software without restriction, including without limitation the rights    *
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell       *
10 * copies of the Software, and to permit persons to whom the Software is           *
11 * furnished to do so, subject to the following conditions:                        *
12 *                                                                                 *
13 * The above copyright notice and this permission notice shall be included in all  *
14 * copies or substantial portions of the Software.                                 *
15 *                                                                                 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR      *
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,        *
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE     *
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER          *
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,   *
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE   *
22 * SOFTWARE.                                                                       *
23 ***********************************************************************************/
24
25/// Provide a panic-like macro that can be used in the framework to detect a test failure and save
26/// that status with a message for later screen printing.
27///
28/// std unit tests rely heavily on unwinding. Each unit test is run inside a catch_unwind block.
29/// If the unit test panics then the panic is caught and the test is marked as 'failed'
30/// (or as 'passed' if the unit test was marked with `#[should_panic]`).
31///
32/// The framework attempts to emulate this behavior by by-passing the panic macro to mark the test
33/// failed and then early return instead of unwind. Of course, this emulation doesn't work on panic!
34/// originates from outside the crate under test, because panic is not overridden in that scope.
35/// This macro provides a way to differentiate between true panic and test failure one.
36///
37/// This emulation has some limitations. For instance, it can only causes one panicking function to
38/// return. To remediate on that, any subsequent panic! call will directly return causing some panic
39/// to be invisible until they get the chance to be the first one. To provide a bit of insight, the
40/// macro also sets a `panicking` flag to let you know that another panic was thrown after the first
41/// one (but without any messages or context).
42/// If a panicking panic is triggered again, the framework will consider either the tests are ill written
43/// or the failure is too fatal. In this case real panic will cause an abort!
44#[macro_export]
45macro_rules! substance_panic {
46    () => (
47        substance_panic!("explicit panic")
48    );
49    ($fmt:expr) => ({
50        substance_panic!($fmt,)
51    });
52    ($fmt:expr, $($arg:tt)*) => ({
53        #[allow(improper_ctypes)]
54        {
55            let mut state = substance_framework::STATE.write();
56            if state.panic {
57                substance_framework::crash(file!(), line!(), column!(), Some(&format_args!("{}", "panic is panicking again")));
58            } else {
59                state.panic = true;
60                state.msg = format_args!($fmt, $($arg)*).as_str();
61                return
62            }
63        }
64    });
65}
66
67//////////////////////////////////////////////
68
69/// Bypass the default panic macro and redirect all its calls to our custom implementation.
70/// This is necessary to prevent unwinding and let us detect test failure with some assertions.
71///
72/// cfr. [substance_panic!]
73#[macro_export]
74macro_rules! panic {
75    ($($tt:tt)*) => {
76        substance_panic!($($tt)*);
77    };
78}
79
80//////////////////////////////////////////////
81
82/// Passes if Condition is true.
83///
84/// Evaluates the condition and passes if it is true.
85/// Otherwise, the test is marked as failure.
86/// The optional string is printed on failure.
87///
88/// # Examples
89///
90/// ```rust
91/// let my_variable = 37;
92/// sf_assert!(my_variable == 37);
93/// ```
94///
95/// ```rust
96/// fn my_function() -> bool {
97///     return false;
98/// }
99///
100/// sf_assert!(my_function(), "this test is a failure");
101/// ```
102#[macro_export]
103macro_rules! sf_assert {
104    ($cond:expr $(,)?) => ({
105        match (&$cond) {
106            cond_bool => {
107                if ! *cond_bool {
108                    substance_panic!("assertion failed: {} is true", &*cond_bool);
109                }
110            }
111        }
112    });
113    ($cond:expr, $($arg:tt)+) => ({
114        match (&$cond) {
115            cond_bool => {
116                if ! *cond_bool {
117                    substance_panic!("assertion failed: {} is true  >> {}", &*cond_bool, format_args!($($arg)+));
118                }
119            }
120        }
121    });
122}
123
124//////////////////////////////////////////////
125
126/// Passes if both arguments are equals.
127///
128/// Passes if the left argument is equal to the right argument.
129/// Otherwise, the test is marked as failure.
130///
131/// # Examples
132///
133/// ```rust
134/// let left = 37;
135/// let right = 37;
136/// sf_assert_eq!(left, right);
137/// ```
138///
139/// ```rust
140/// sf_assert_eq!(0, 5, "this test is a failure");
141/// ```
142#[macro_export]
143macro_rules! sf_assert_eq {
144    ($left:expr, $right:expr $(,)?) => ({
145        match (&$left, &$right) {
146            (left_val, right_val) => {
147                if !(*left_val == *right_val) {
148                    substance_panic!("assertion failed: {} == {}", &*left_val, &*right_val);
149                }
150            }
151        }
152    });
153    ($left:expr, $right:expr, $($arg:tt)+) => ({
154        match (&$left, &$right) {
155            (left_val, right_val) => {
156                if !(*left_val == *right_val) {
157                    substance_panic!("assertion failed: {} == {}  >> {}", &*left_val, &*right_val, format_args!($($arg)+));
158                }
159            }
160        }
161    });
162}
163
164//////////////////////////////////////////////
165
166/// Passes if both arguments are not equal.
167///
168/// Passes if the left argument is not equal to the right argument.
169/// Otherwise, the test is marked as failure.
170///
171/// # Examples
172///
173/// ```rust
174/// let left = 37;
175/// let right = 42;
176/// sf_assert_ne!(left, right);
177/// ```
178///
179/// ```rust
180/// sf_assert_ne!(5, 5, "this test is a failure");
181/// ```
182#[macro_export]
183macro_rules! sf_assert_ne {
184    ($left:expr, $right:expr $(,)?) => ({
185        match (&$left, &$right) {
186            (left_val, right_val) => {
187                if !(*left_val != *right_val) {
188                    substance_panic!("assertion failed: {} != {}", &*left_val, &*right_val);
189                }
190            }
191        }
192    });
193    ($left:expr, $right:expr, $($arg:tt)+) => ({
194        match (&($left), &($right)) {
195            (left_val, right_val) => {
196                if !(*left_val != *right_val) {
197                    substance_panic!("assertion failed: {} != {}  >> {}", &*left_val, &*right_val, format_args!($($arg)+));
198                }
199            }
200        }
201    });
202}
203
204//////////////////////////////////////////////
205
206/// Passes if the left argument is strictly smaller than the right one.
207///
208/// Passes if the left argument is strictly smaller than the right argument.
209/// Otherwise, the test is marked as failure.
210///
211/// # Examples
212///
213/// ```rust
214/// let left = -325;
215/// let right = 37;
216/// sf_assert_lt!(left, right);
217/// ```
218///
219/// ```rust
220/// sf_assert_lt!(0, -3, "this test is a failure");
221/// ```
222#[macro_export]
223macro_rules! sf_assert_lt {
224    ($left:expr, $right:expr $(,)?) => ({
225        match (&$left, &$right) {
226            (left_val, right_val) => {
227                if !(*left_val < *right_val) {
228                    substance_panic!("assertion failed: {} < {}", &*left_val, &*right_val);
229                }
230            }
231        }
232    });
233    ($left:expr, $right:expr, $($arg:tt)+) => ({
234        match (&($left), &($right)) {
235            (left_val, right_val) => {
236                if !(*left_val < *right_val) {
237                    substance_panic!("assertion failed: {} < {}  >> {}", &*left_val, &*right_val, format_args!($($arg)+));
238                }
239            }
240        }
241    });
242}
243
244//////////////////////////////////////////////
245
246/// Passes if the left argument is strictly smaller than or equal to the right one.
247///
248/// Passes if the left argument is strictly smaller than or equal to the right argument.
249/// Otherwise, the test is marked as failure.
250///
251/// # Examples
252///
253/// ```rust
254/// let left = -325;
255/// let right = 37;
256/// sf_assert_le!(left, right);
257/// ```
258///
259/// ```rust
260/// sf_assert_le!(0, 0);
261/// ```
262///
263/// ```rust
264/// sf_assert_le!(0, -3, "this test is a failure");
265/// ```
266#[macro_export]
267macro_rules! sf_assert_le {
268    ($left:expr, $right:expr $(,)?) => ({
269        match (&$left, &$right) {
270            (left_val, right_val) => {
271                if !(*left_val <= *right_val) {
272                    substance_panic!("assertion failed: {} <= {}", &*left_val, &*right_val);
273                }
274            }
275        }
276    });
277    ($left:expr, $right:expr, $($arg:tt)+) => ({
278        match (&($left), &($right)) {
279            (left_val, right_val) => {
280                if !(*left_val <= *right_val) {
281                    substance_panic!("assertion failed: {} <= {}  >> {}", &*left_val, &*right_val, format_args!($($arg)+));
282                }
283            }
284        }
285    });
286}
287
288//////////////////////////////////////////////
289
290/// Passes if the left argument is strictly greater than the right one.
291///
292/// Passes if the left argument is strictly greater than the right argument.
293/// Otherwise, the test is marked as failure.
294///
295/// # Examples
296///
297/// ```rust
298/// let left = 325;
299/// let right = 37;
300/// sf_assert_gt!(left, right);
301/// ```
302///
303/// ```rust
304/// sf_assert_gt!(-6, 32, "this test is a failure");
305/// ```
306#[macro_export]
307macro_rules! sf_assert_gt {
308    ($left:expr, $right:expr $(,)?) => ({
309        match (&$left, &$right) {
310            (left_val, right_val) => {
311                if !(*left_val > *right_val) {
312                    substance_panic!("assertion failed: {} > {}", &*left_val, &*right_val);
313                }
314            }
315        }
316    });
317    ($left:expr, $right:expr, $($arg:tt)+) => ({
318        match (&($left), &($right)) {
319            (left_val, right_val) => {
320                if !(*left_val > *right_val) {
321                    substance_panic!("assertion failed: {} > {}  >> {}", &*left_val, &*right_val, format_args!($($arg)+));
322                }
323            }
324        }
325    });
326}
327
328//////////////////////////////////////////////
329
330/// Passes if the left argument is strictly greater than or equals to the right one.
331///
332/// Passes if the left argument is strictly greater than or equals to the right argument.
333/// Otherwise, the test is marked as failure.
334///
335/// # Examples
336///
337/// ```rust
338/// let left = 325;
339/// let right = 37;
340/// sf_assert_ge!(left, right);
341/// ```
342///
343/// ```rust
344/// sf_assert_ge!(435, 435);
345/// ```
346///
347/// ```rust
348/// sf_assert_ge!(45, 645884, "this test is a failure");
349/// ```
350#[macro_export]
351macro_rules! sf_assert_ge {
352    ($left:expr, $right:expr $(,)?) => ({
353        match (&$left, &$right) {
354            (left_val, right_val) => {
355                if !(*left_val >= *right_val) {
356                    substance_panic!("assertion failed: {} >= {}", &*left_val, &*right_val);
357                }
358            }
359        }
360    });
361    ($left:expr, $right:expr, $($arg:tt)+) => ({
362        match (&($left), &($right)) {
363            (left_val, right_val) => {
364                if !(*left_val >= *right_val) {
365                    substance_panic!("assertion failed: {} >= {}  >> {}", &*left_val, &*right_val, format_args!($($arg)+));
366                }
367            }
368        }
369    });
370}
371
372//////////////////////////////////////////////
373
374/// Passes if the argument is an Ok result.
375///
376/// Passes if the argument is an instance of Result::Ok(T).
377/// Otherwise, the test is marked as failure.
378///
379/// # Examples
380///
381/// ```rust
382/// sf_assert_ok!(my_function_ok());
383/// ```
384///
385/// ```rust
386/// let empty = Result::Ok();
387/// sf_assert_ok!(empty);
388/// ```
389///
390/// ```rust
391/// let err = Result::Err();
392/// sf_assert_ok!(err, "this test is a failure");
393/// ```
394#[macro_export]
395macro_rules! sf_assert_ok {
396    ($cond:expr $(,)?) => ({
397        match (&$cond) {
398            cond_result => {
399                if ! *cond_result.is_ok() {
400                    substance_panic!("assertion failed: {} is Ok", &*cond_result);
401                }
402            }
403        }
404    });
405    ($cond:expr, $($arg:tt)+) => ({
406        match (&$cond) {
407            cond_bool => {
408                if ! *cond_result.is_ok() {
409                    substance_panic!("assertion failed: {} is Ok  >> {}", &*cond_result, format_args!($($arg)+));
410                }
411            }
412        }
413    });
414}
415
416//////////////////////////////////////////////
417
418/// Passes if the argument is an existing optional value.
419///
420/// Passes if the argument is an instance of Option::Some(T).
421/// Otherwise, the test is marked as failure.
422///
423/// # Examples
424///
425/// ```rust
426/// let empty = Option::Some();
427/// sf_assert_some!(empty);
428/// ```
429///
430/// ```rust
431/// let err = Option::None();
432/// sf_assert_some!(err, "this test is a failure");
433/// ```
434#[macro_export]
435macro_rules! sf_assert_some {
436    ($cond:expr $(,)?) => ({
437        match (&$cond) {
438            cond_result => {
439                if ! *cond_result.is_some() {
440                    substance_panic!("assertion failed: {} is Some", &*cond_result);
441                }
442            }
443        }
444    });
445    ($cond:expr, $($arg:tt)+) => ({
446        match (&$cond) {
447            cond_bool => {
448                if ! *cond_result.is_some() {
449                    substance_panic!("assertion failed: {} is Some  >> {}", &*cond_result, format_args!($($arg)+));
450                }
451            }
452        }
453    });
454}