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