mycotest/
assert.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
//! Non-panicking assertions.
//!
//! These assertions behave similarly to the assertion macros in the Rust
//! standard library, but rather than panicking, they immediately return a
//! `Result` with a [`Failed`] error.
//!
//! Since the kernel cannot currently unwind and recover from panics, this
//! allows assertions to fail without halting the kernel, allowing other tests
//! to run.

/// An error that indicates that an assertion failed.
#[derive(Clone, Default)]
pub struct Failed {
    pub expr: &'static str,
    pub file: &'static str,
    pub line: u32,
    pub col: u32,
}

/// Returns a [`Failed`] error with the provided expression at the current
/// source code location.
#[macro_export]
macro_rules! fail {
    ($expr:expr) => {
        return Err($crate::assert::Failed {
            expr: $expr,
            file: file!(),
            line: line!(),
            col: column!(),
        });
    };
    () => {
        $crate::fail!("test failed");
    };
}

/// Asserts that a boolean expression is true.
#[macro_export]
macro_rules! assert {
    (@inner $cond:expr, $msg:expr) => {
        if !$cond {
            let cond = stringify!($cond);
            tracing::error!("assertion failed: `{cond}`{}, {}:{}:{}", $msg, file!(), line!(), column!());
            $crate::fail!(cond)
        }
    };
    ($cond:expr, $($msg:tt)+) => {
        $crate::assert!(@inner $cond, format_args!("; ", format_args!($($msg)+)))
    };
    ($cond:expr $(,)?) => {
        $crate::assert!(@inner $cond, "")
    };
}

/// Asserts that two values are equal.
#[macro_export]
macro_rules! assert_eq {
    ($left:expr, $right:expr $(,)?) => {
        $crate::assert_binop!($left, $right, ==)
    };
    ($left:expr, $right:expr, $($msg:tt)+) => {
        $crate::assert_binop!($left, $right, ==, $($msg)+)
    };
}

/// Asserts that two values are not equal.
#[macro_export]
macro_rules! assert_ne {
    ($left:expr, $right:expr $(,)?) => {
        $crate::assert_binop!($left, $right, !=)
    };
    ($left:expr, $right:expr, $($msg:tt)+) => {
        $crate::assert_binop!($left, $right, !=, $($msg)+)
    };
}

/// Asserts that the left value is greater than the right value.
#[macro_export]
macro_rules! assert_gt {
    ($left:expr, $right:expr $(,)?) => {
        $crate::assert_binop!($left, $right, >)
    };
    ($left:expr, $right:expr, $($msg:tt)*) => {
        $crate::assert_binop!($left, $right, >, $($msg)*)
    };
}

/// Asserts that the left value is less than the right value.
#[macro_export]
macro_rules! assert_lt {
    ($left:expr, $right:expr $(,)?) => {
        $crate::assert_binop!($left, $right, <)
    };
    ($left:expr, $right:expr, $($msg:tt)*) => {
        $crate::assert_binop!($left, $right, <, $($msg)*)
    };
}

/// Asserts that the left value is greater than or equal to the right value.
#[macro_export]
macro_rules! assert_gte {
    ($left:expr, $right:expr $(,)?) => {
        $crate::assert_binop!($left, $right, >=)
    };
    ($left:expr, $right:expr, $($msg:tt)*) => {
        $crate::assert_binop!($left, $right, >=, $($msg)*)
    };
}

/// Asserts that the left value is less than or equal to the right value.
#[macro_export]
macro_rules! assert_lte {
    ($left:expr, $right:expr $(,)?) => {
        $crate::assert_binop!($left, $right, <=)
    };
    ($left:expr, $right:expr, $($msg:tt)*) => {
        $crate::assert_binop!($left, $right, <=, $($msg)*)
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! assert_binop {
    ($left:expr, $right:expr, $binop:tt) => {
        $crate::assert_binop!(@inner $left, $right, $binop, "")
    };
    ($left:expr, $right:expr, $binop:tt, $($msg:tt)+) => {
        $crate::assert_binop!(@inner $left, $right, $binop, ": {}", format_args!($($msg)+))
    };
    (@inner $left:expr, $right:expr, $binop:tt, $($msg:tt)+) => {
        let left = $left;
        let right = $right;
        if !(left $binop right) {
            let condition = concat!(stringify!($left), " ", stringify!($binop), " ", stringify!($right));
            tracing::error!(
                "assertion failed: `{condition}`\n  left: `{:?}`,\n right: `{:?}`{}, {}:{}:{}",
                left, right, format_args!($($msg)+), file!(), line!(), column!()
            );
            $crate::fail!(condition)
        }
    };
}

impl core::fmt::Debug for Failed {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        let Self {
            expr,
            file,
            line,
            col,
        } = self;
        write!(f, "assertion failed: `{expr}`, {file}:{line}:{col}",)
    }
}