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