mycotest/
assert.rs

1//! Non-panicking assertions.
2//!
3//! These assertions behave similarly to the assertion macros in the Rust
4//! standard library, but rather than panicking, they immediately return a
5//! `Result` with a [`Failed`] error.
6//!
7//! Since the kernel cannot currently unwind and recover from panics, this
8//! allows assertions to fail without halting the kernel, allowing other tests
9//! to run.
10
11/// An error that indicates that an assertion failed.
12#[derive(Clone, Default)]
13pub struct Failed {
14    pub expr: &'static str,
15    pub file: &'static str,
16    pub line: u32,
17    pub col: u32,
18}
19
20/// Returns a [`Failed`] error with the provided expression at the current
21/// source code location.
22#[macro_export]
23macro_rules! fail {
24    ($expr:expr) => {
25        return Err($crate::assert::Failed {
26            expr: $expr,
27            file: file!(),
28            line: line!(),
29            col: column!(),
30        });
31    };
32    () => {
33        $crate::fail!("test failed");
34    };
35}
36
37/// Asserts that a boolean expression is true.
38#[macro_export]
39macro_rules! assert {
40    (@inner $cond:expr, $msg:expr) => {
41        if !$cond {
42            let cond = stringify!($cond);
43            tracing::error!("assertion failed: `{cond}`{}, {}:{}:{}", $msg, file!(), line!(), column!());
44            $crate::fail!(cond)
45        }
46    };
47    ($cond:expr, $($msg:tt)+) => {
48        $crate::assert!(@inner $cond, format_args!("; ", format_args!($($msg)+)))
49    };
50    ($cond:expr $(,)?) => {
51        $crate::assert!(@inner $cond, "")
52    };
53}
54
55/// Asserts that two values are equal.
56#[macro_export]
57macro_rules! assert_eq {
58    ($left:expr, $right:expr $(,)?) => {
59        $crate::assert_binop!($left, $right, ==)
60    };
61    ($left:expr, $right:expr, $($msg:tt)+) => {
62        $crate::assert_binop!($left, $right, ==, $($msg)+)
63    };
64}
65
66/// Asserts that two values are not equal.
67#[macro_export]
68macro_rules! assert_ne {
69    ($left:expr, $right:expr $(,)?) => {
70        $crate::assert_binop!($left, $right, !=)
71    };
72    ($left:expr, $right:expr, $($msg:tt)+) => {
73        $crate::assert_binop!($left, $right, !=, $($msg)+)
74    };
75}
76
77/// Asserts that the left value is greater than the right value.
78#[macro_export]
79macro_rules! assert_gt {
80    ($left:expr, $right:expr $(,)?) => {
81        $crate::assert_binop!($left, $right, >)
82    };
83    ($left:expr, $right:expr, $($msg:tt)*) => {
84        $crate::assert_binop!($left, $right, >, $($msg)*)
85    };
86}
87
88/// Asserts that the left value is less than the right value.
89#[macro_export]
90macro_rules! assert_lt {
91    ($left:expr, $right:expr $(,)?) => {
92        $crate::assert_binop!($left, $right, <)
93    };
94    ($left:expr, $right:expr, $($msg:tt)*) => {
95        $crate::assert_binop!($left, $right, <, $($msg)*)
96    };
97}
98
99/// Asserts that the left value is greater than or equal to the right value.
100#[macro_export]
101macro_rules! assert_gte {
102    ($left:expr, $right:expr $(,)?) => {
103        $crate::assert_binop!($left, $right, >=)
104    };
105    ($left:expr, $right:expr, $($msg:tt)*) => {
106        $crate::assert_binop!($left, $right, >=, $($msg)*)
107    };
108}
109
110/// Asserts that the left value is less than or equal to the right value.
111#[macro_export]
112macro_rules! assert_lte {
113    ($left:expr, $right:expr $(,)?) => {
114        $crate::assert_binop!($left, $right, <=)
115    };
116    ($left:expr, $right:expr, $($msg:tt)*) => {
117        $crate::assert_binop!($left, $right, <=, $($msg)*)
118    };
119}
120
121#[doc(hidden)]
122#[macro_export]
123macro_rules! assert_binop {
124    ($left:expr, $right:expr, $binop:tt) => {
125        $crate::assert_binop!(@inner $left, $right, $binop, "")
126    };
127    ($left:expr, $right:expr, $binop:tt, $($msg:tt)+) => {
128        $crate::assert_binop!(@inner $left, $right, $binop, ": {}", format_args!($($msg)+))
129    };
130    (@inner $left:expr, $right:expr, $binop:tt, $($msg:tt)+) => {
131        let left = $left;
132        let right = $right;
133        if !(left $binop right) {
134            let condition = concat!(stringify!($left), " ", stringify!($binop), " ", stringify!($right));
135            tracing::error!(
136                "assertion failed: `{condition}`\n  left: `{:?}`,\n right: `{:?}`{}, {}:{}:{}",
137                left, right, format_args!($($msg)+), file!(), line!(), column!()
138            );
139            $crate::fail!(condition)
140        }
141    };
142}
143
144impl core::fmt::Debug for Failed {
145    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
146        let Self {
147            expr,
148            file,
149            line,
150            col,
151        } = self;
152        write!(f, "assertion failed: `{expr}`, {file}:{line}:{col}",)
153    }
154}