xref: /linux/rust/kernel/kunit.rs (revision 12d5fc96ebbb3b948861805b32a4c3a96105ccd9)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 //! KUnit-based macros for Rust unit tests.
4 //!
5 //! C header: [`include/kunit/test.h`](srctree/include/kunit/test.h)
6 //!
7 //! Reference: <https://docs.kernel.org/dev-tools/kunit/index.html>
8 
9 use crate::fmt;
10 use crate::prelude::*;
11 
12 /// Prints a KUnit error-level message.
13 ///
14 /// Public but hidden since it should only be used from KUnit generated code.
15 #[doc(hidden)]
16 pub fn err(args: fmt::Arguments<'_>) {
17     // `args` is unused if `CONFIG_PRINTK` is not set - this avoids a build-time warning.
18     #[cfg(not(CONFIG_PRINTK))]
19     let _ = args;
20 
21     // SAFETY: The format string is null-terminated and the `%pA` specifier matches the argument we
22     // are passing.
23     #[cfg(CONFIG_PRINTK)]
24     unsafe {
25         bindings::_printk(
26             c"\x013%pA".as_char_ptr(),
27             core::ptr::from_ref(&args).cast::<c_void>(),
28         );
29     }
30 }
31 
32 /// Prints a KUnit info-level message.
33 ///
34 /// Public but hidden since it should only be used from KUnit generated code.
35 #[doc(hidden)]
36 pub fn info(args: fmt::Arguments<'_>) {
37     // `args` is unused if `CONFIG_PRINTK` is not set - this avoids a build-time warning.
38     #[cfg(not(CONFIG_PRINTK))]
39     let _ = args;
40 
41     // SAFETY: The format string is null-terminated and the `%pA` specifier matches the argument we
42     // are passing.
43     #[cfg(CONFIG_PRINTK)]
44     unsafe {
45         bindings::_printk(
46             c"\x016%pA".as_char_ptr(),
47             core::ptr::from_ref(&args).cast::<c_void>(),
48         );
49     }
50 }
51 
52 /// Asserts that a boolean expression is `true` at runtime.
53 ///
54 /// Public but hidden since it should only be used from generated tests.
55 ///
56 /// Unlike the one in `core`, this one does not panic; instead, it is mapped to the KUnit
57 /// facilities. See [`assert!`] for more details.
58 #[doc(hidden)]
59 #[macro_export]
60 macro_rules! kunit_assert {
61     ($name:literal, $condition:expr $(,)?) => {
62         'out: {
63             // Do nothing if the condition is `true`.
64             if $condition {
65                 break 'out;
66             }
67 
68             // Use `file!()` instead of `::core::file!()` here so it can be overridden.
69             static FILE: &'static $crate::str::CStr = $crate::c_str!(file!());
70             // Use `line!()` instead of `::core::line!()` here so it can be overridden.
71             static LINE: i32 = line!() as i32;
72             static CONDITION: &'static $crate::str::CStr = $crate::c_str!(stringify!($condition));
73 
74             // SAFETY: FFI call without safety requirements.
75             let kunit_test = unsafe { $crate::bindings::kunit_get_current_test() };
76             if kunit_test.is_null() {
77                 // The assertion failed but this task is not running a KUnit test, so we cannot call
78                 // KUnit, but at least print an error to the kernel log. This may happen if this
79                 // macro is called from an spawned thread in a test (see
80                 // `scripts/rustdoc_test_gen.rs`) or if some non-test code calls this macro by
81                 // mistake (it is hidden to prevent that).
82                 //
83                 // This mimics KUnit's failed assertion format.
84                 $crate::kunit::err($crate::prelude::fmt!(
85                     "    # {}: ASSERTION FAILED at {FILE}:{LINE}\n",
86                     $name
87                 ));
88                 $crate::kunit::err($crate::prelude::fmt!(
89                     "    Expected {CONDITION} to be true, but is false\n"
90                 ));
91                 $crate::kunit::err($crate::prelude::fmt!(
92                     "    Failure not reported to KUnit since this is a non-KUnit task\n"
93                 ));
94                 break 'out;
95             }
96 
97             #[repr(transparent)]
98             struct Location($crate::bindings::kunit_loc);
99 
100             #[repr(transparent)]
101             struct UnaryAssert($crate::bindings::kunit_unary_assert);
102 
103             // SAFETY: There is only a static instance and in that one the pointer field points to
104             // an immutable C string.
105             unsafe impl Sync for Location {}
106 
107             // SAFETY: There is only a static instance and in that one the pointer field points to
108             // an immutable C string.
109             unsafe impl Sync for UnaryAssert {}
110 
111             static LOCATION: Location = Location($crate::bindings::kunit_loc {
112                 file: $crate::str::as_char_ptr_in_const_context(FILE),
113                 line: LINE,
114             });
115             static ASSERTION: UnaryAssert = UnaryAssert($crate::bindings::kunit_unary_assert {
116                 assert: $crate::bindings::kunit_assert {},
117                 condition: $crate::str::as_char_ptr_in_const_context(CONDITION),
118                 expected_true: true,
119             });
120 
121             // SAFETY:
122             //   - FFI call.
123             //   - The `kunit_test` pointer is valid because we got it from
124             //     `kunit_get_current_test()` and it was not null. This means we are in a KUnit
125             //     test, and that the pointer can be passed to KUnit functions and assertions.
126             //   - The string pointers (`file` and `condition` above) point to null-terminated
127             //     strings since they are `CStr`s.
128             //   - The function pointer (`format`) points to the proper function.
129             //   - The pointers passed will remain valid since they point to `static`s.
130             //   - The format string is allowed to be null.
131             //   - There are, however, problems with this: first of all, this will end up stopping
132             //     the thread, without running destructors. While that is problematic in itself,
133             //     it is considered UB to have what is effectively a forced foreign unwind
134             //     with `extern "C"` ABI. One could observe the stack that is now gone from
135             //     another thread. We should avoid pinning stack variables to prevent library UB,
136             //     too. For the moment, given that test failures are reported immediately before the
137             //     next test runs, that test failures should be fixed and that KUnit is explicitly
138             //     documented as not suitable for production environments, we feel it is reasonable.
139             unsafe {
140                 $crate::bindings::__kunit_do_failed_assertion(
141                     kunit_test,
142                     ::core::ptr::addr_of!(LOCATION.0),
143                     $crate::bindings::kunit_assert_type_KUNIT_ASSERTION,
144                     ::core::ptr::addr_of!(ASSERTION.0.assert),
145                     Some($crate::bindings::kunit_unary_assert_format),
146                     ::core::ptr::null(),
147                 );
148             }
149 
150             // SAFETY: FFI call; the `test` pointer is valid because this hidden macro should only
151             // be called by the generated documentation tests which forward the test pointer given
152             // by KUnit.
153             unsafe {
154                 $crate::bindings::__kunit_abort(kunit_test);
155             }
156         }
157     };
158 }
159 
160 /// Asserts that two expressions are equal to each other (using [`PartialEq`]).
161 ///
162 /// Public but hidden since it should only be used from generated tests.
163 ///
164 /// Unlike the one in `core`, this one does not panic; instead, it is mapped to the KUnit
165 /// facilities. See [`assert!`] for more details.
166 #[doc(hidden)]
167 #[macro_export]
168 macro_rules! kunit_assert_eq {
169     ($name:literal, $left:expr, $right:expr $(,)?) => {{
170         // For the moment, we just forward to the expression assert because, for binary asserts,
171         // KUnit supports only a few types (e.g. integers).
172         $crate::kunit_assert!($name, $left == $right);
173     }};
174 }
175 
176 trait TestResult {
177     fn is_test_result_ok(&self) -> bool;
178 }
179 
180 impl TestResult for () {
181     fn is_test_result_ok(&self) -> bool {
182         true
183     }
184 }
185 
186 impl<T, E> TestResult for Result<T, E> {
187     fn is_test_result_ok(&self) -> bool {
188         self.is_ok()
189     }
190 }
191 
192 /// Returns whether a test result is to be considered OK.
193 ///
194 /// This will be `assert!`ed from the generated tests.
195 #[doc(hidden)]
196 #[expect(private_bounds)]
197 pub fn is_test_result_ok(t: impl TestResult) -> bool {
198     t.is_test_result_ok()
199 }
200 
201 /// Represents an individual test case.
202 #[doc(hidden)]
203 pub const fn kunit_case(
204     name: &'static kernel::str::CStr,
205     run_case: unsafe extern "C" fn(*mut kernel::bindings::kunit),
206 ) -> kernel::bindings::kunit_case {
207     kernel::bindings::kunit_case {
208         run_case: Some(run_case),
209         name: kernel::str::as_char_ptr_in_const_context(name),
210         attr: kernel::bindings::kunit_attributes {
211             speed: kernel::bindings::kunit_speed_KUNIT_SPEED_NORMAL,
212         },
213         generate_params: None,
214         status: kernel::bindings::kunit_status_KUNIT_SUCCESS,
215         module_name: core::ptr::null_mut(),
216         log: core::ptr::null_mut(),
217         param_init: None,
218         param_exit: None,
219     }
220 }
221 
222 /// Registers a KUnit test suite.
223 ///
224 /// # Safety
225 ///
226 /// `test_cases` must be a `NULL` terminated array of valid test cases,
227 /// whose lifetime is at least that of the test suite (i.e., static).
228 ///
229 /// # Examples
230 ///
231 /// ```ignore
232 /// extern "C" fn test_fn(_test: *mut kernel::bindings::kunit) {
233 ///     let actual = 1 + 1;
234 ///     let expected = 2;
235 ///     assert_eq!(actual, expected);
236 /// }
237 ///
238 /// static mut KUNIT_TEST_CASES: [kernel::bindings::kunit_case; 2] = [
239 ///     kernel::kunit::kunit_case(c"name", test_fn),
240 ///     pin_init::zeroed(),
241 /// ];
242 /// kernel::kunit_unsafe_test_suite!(suite_name, KUNIT_TEST_CASES);
243 /// ```
244 #[doc(hidden)]
245 #[macro_export]
246 macro_rules! kunit_unsafe_test_suite {
247     ($name:ident, $test_cases:ident) => {
248         const _: () = {
249             const KUNIT_TEST_SUITE_NAME: [::kernel::ffi::c_char; 256] = {
250                 let name_u8 = ::core::stringify!($name).as_bytes();
251                 let mut ret = [0; 256];
252 
253                 if name_u8.len() > 255 {
254                     panic!(concat!(
255                         "The test suite name `",
256                         ::core::stringify!($name),
257                         "` exceeds the maximum length of 255 bytes."
258                     ));
259                 }
260 
261                 let mut i = 0;
262                 while i < name_u8.len() {
263                     ret[i] = name_u8[i] as ::kernel::ffi::c_char;
264                     i += 1;
265                 }
266 
267                 ret
268             };
269 
270             static mut KUNIT_TEST_SUITE: ::kernel::bindings::kunit_suite =
271                 ::kernel::bindings::kunit_suite {
272                     name: KUNIT_TEST_SUITE_NAME,
273                     #[allow(unused_unsafe)]
274                     // SAFETY: `$test_cases` is passed in by the user, and
275                     // (as documented) must be valid for the lifetime of
276                     // the suite (i.e., static).
277                     test_cases: unsafe {
278                         ::core::ptr::addr_of_mut!($test_cases)
279                             .cast::<::kernel::bindings::kunit_case>()
280                     },
281                     suite_init: None,
282                     suite_exit: None,
283                     init: None,
284                     exit: None,
285                     attr: ::kernel::bindings::kunit_attributes {
286                         speed: ::kernel::bindings::kunit_speed_KUNIT_SPEED_NORMAL,
287                     },
288                     status_comment: [0; 256usize],
289                     debugfs: ::core::ptr::null_mut(),
290                     log: ::core::ptr::null_mut(),
291                     suite_init_err: 0,
292                     is_init: false,
293                     status: kernel::bindings::kunit_status_KUNIT_SUCCESS,
294                 };
295 
296             #[used(compiler)]
297             #[allow(unused_unsafe)]
298             #[cfg_attr(not(target_os = "macos"), link_section = ".kunit_test_suites")]
299             static mut KUNIT_TEST_SUITE_ENTRY: *const ::kernel::bindings::kunit_suite =
300                 // SAFETY: `KUNIT_TEST_SUITE` is static.
301                 unsafe { ::core::ptr::addr_of_mut!(KUNIT_TEST_SUITE) };
302         };
303     };
304 }
305 
306 /// Returns whether we are currently running a KUnit test.
307 ///
308 /// In some cases, you need to call test-only code from outside the test case, for example, to
309 /// create a function mock. This function allows to change behavior depending on whether we are
310 /// currently running a KUnit test or not.
311 ///
312 /// # Examples
313 ///
314 /// This example shows how a function can be mocked to return a well-known value while testing:
315 ///
316 /// ```
317 /// # use kernel::kunit::in_kunit_test;
318 /// fn fn_mock_example(n: i32) -> i32 {
319 ///     if in_kunit_test() {
320 ///         return 100;
321 ///     }
322 ///
323 ///     n + 1
324 /// }
325 ///
326 /// let mock_res = fn_mock_example(5);
327 /// assert_eq!(mock_res, 100);
328 /// ```
329 pub fn in_kunit_test() -> bool {
330     // SAFETY: `kunit_get_current_test()` is always safe to call (it has fallbacks for
331     // when KUnit is not enabled).
332     !unsafe { bindings::kunit_get_current_test() }.is_null()
333 }
334 
335 #[cfg(CONFIG_RUST_KUNIT_SELFTEST)]
336 #[kunit_tests(rust_kernel_kunit)]
337 mod tests {
338     use super::*;
339 
340     #[test]
341     fn rust_test_kunit_example_test() {
342         assert_eq!(1 + 1, 2);
343     }
344 
345     #[test]
346     fn rust_test_kunit_in_kunit_test() {
347         assert!(in_kunit_test());
348     }
349 
350     #[test]
351     #[cfg(not(all()))]
352     fn rust_test_kunit_always_disabled_test() {
353         // This test should never run because of the `cfg`.
354         assert!(false);
355     }
356 }
357