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