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