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}