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 // 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_char_ptr(), 23 core::ptr::from_ref(&args).cast::<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_char_ptr(), 39 core::ptr::from_ref(&args).cast::<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 = $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($crate::prelude::fmt!( 75 " # {}: ASSERTION FAILED at {FILE}:{LINE}\n", 76 $name 77 )); 78 $crate::kunit::err($crate::prelude::fmt!( 79 " Expected {CONDITION} to be true, but is false\n" 80 )); 81 $crate::kunit::err($crate::prelude::fmt!( 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: $crate::str::as_char_ptr_in_const_context(FILE), 103 line: LINE, 104 }); 105 static ASSERTION: UnaryAssert = UnaryAssert($crate::bindings::kunit_unary_assert { 106 assert: $crate::bindings::kunit_assert {}, 107 condition: $crate::str::as_char_ptr_in_const_context(CONDITION), 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 #[doc(hidden)] 193 pub const fn kunit_case( 194 name: &'static kernel::str::CStr, 195 run_case: unsafe extern "C" fn(*mut kernel::bindings::kunit), 196 ) -> kernel::bindings::kunit_case { 197 kernel::bindings::kunit_case { 198 run_case: Some(run_case), 199 name: kernel::str::as_char_ptr_in_const_context(name), 200 attr: kernel::bindings::kunit_attributes { 201 speed: kernel::bindings::kunit_speed_KUNIT_SPEED_NORMAL, 202 }, 203 generate_params: None, 204 status: kernel::bindings::kunit_status_KUNIT_SUCCESS, 205 module_name: core::ptr::null_mut(), 206 log: core::ptr::null_mut(), 207 param_init: None, 208 param_exit: None, 209 } 210 } 211 212 /// Registers a KUnit test suite. 213 /// 214 /// # Safety 215 /// 216 /// `test_cases` must be a `NULL` terminated array of valid test cases, 217 /// whose lifetime is at least that of the test suite (i.e., static). 218 /// 219 /// # Examples 220 /// 221 /// ```ignore 222 /// extern "C" fn test_fn(_test: *mut kernel::bindings::kunit) { 223 /// let actual = 1 + 1; 224 /// let expected = 2; 225 /// assert_eq!(actual, expected); 226 /// } 227 /// 228 /// static mut KUNIT_TEST_CASES: [kernel::bindings::kunit_case; 2] = [ 229 /// kernel::kunit::kunit_case(c"name", test_fn), 230 /// pin_init::zeroed(), 231 /// ]; 232 /// kernel::kunit_unsafe_test_suite!(suite_name, KUNIT_TEST_CASES); 233 /// ``` 234 #[doc(hidden)] 235 #[macro_export] 236 macro_rules! kunit_unsafe_test_suite { 237 ($name:ident, $test_cases:ident) => { 238 const _: () = { 239 const KUNIT_TEST_SUITE_NAME: [::kernel::ffi::c_char; 256] = { 240 let name_u8 = ::core::stringify!($name).as_bytes(); 241 let mut ret = [0; 256]; 242 243 if name_u8.len() > 255 { 244 panic!(concat!( 245 "The test suite name `", 246 ::core::stringify!($name), 247 "` exceeds the maximum length of 255 bytes." 248 )); 249 } 250 251 let mut i = 0; 252 while i < name_u8.len() { 253 ret[i] = name_u8[i] as ::kernel::ffi::c_char; 254 i += 1; 255 } 256 257 ret 258 }; 259 260 static mut KUNIT_TEST_SUITE: ::kernel::bindings::kunit_suite = 261 ::kernel::bindings::kunit_suite { 262 name: KUNIT_TEST_SUITE_NAME, 263 #[allow(unused_unsafe)] 264 // SAFETY: `$test_cases` is passed in by the user, and 265 // (as documented) must be valid for the lifetime of 266 // the suite (i.e., static). 267 test_cases: unsafe { 268 ::core::ptr::addr_of_mut!($test_cases) 269 .cast::<::kernel::bindings::kunit_case>() 270 }, 271 suite_init: None, 272 suite_exit: None, 273 init: None, 274 exit: None, 275 attr: ::kernel::bindings::kunit_attributes { 276 speed: ::kernel::bindings::kunit_speed_KUNIT_SPEED_NORMAL, 277 }, 278 status_comment: [0; 256usize], 279 debugfs: ::core::ptr::null_mut(), 280 log: ::core::ptr::null_mut(), 281 suite_init_err: 0, 282 is_init: false, 283 }; 284 285 #[used(compiler)] 286 #[allow(unused_unsafe)] 287 #[cfg_attr(not(target_os = "macos"), link_section = ".kunit_test_suites")] 288 static mut KUNIT_TEST_SUITE_ENTRY: *const ::kernel::bindings::kunit_suite = 289 // SAFETY: `KUNIT_TEST_SUITE` is static. 290 unsafe { ::core::ptr::addr_of_mut!(KUNIT_TEST_SUITE) }; 291 }; 292 }; 293 } 294 295 /// Returns whether we are currently running a KUnit test. 296 /// 297 /// In some cases, you need to call test-only code from outside the test case, for example, to 298 /// create a function mock. This function allows to change behavior depending on whether we are 299 /// currently running a KUnit test or not. 300 /// 301 /// # Examples 302 /// 303 /// This example shows how a function can be mocked to return a well-known value while testing: 304 /// 305 /// ``` 306 /// # use kernel::kunit::in_kunit_test; 307 /// fn fn_mock_example(n: i32) -> i32 { 308 /// if in_kunit_test() { 309 /// return 100; 310 /// } 311 /// 312 /// n + 1 313 /// } 314 /// 315 /// let mock_res = fn_mock_example(5); 316 /// assert_eq!(mock_res, 100); 317 /// ``` 318 pub fn in_kunit_test() -> bool { 319 // SAFETY: `kunit_get_current_test()` is always safe to call (it has fallbacks for 320 // when KUnit is not enabled). 321 !unsafe { bindings::kunit_get_current_test() }.is_null() 322 } 323 324 #[kunit_tests(rust_kernel_kunit)] 325 mod tests { 326 use super::*; 327 328 #[test] 329 fn rust_test_kunit_example_test() { 330 assert_eq!(1 + 1, 2); 331 } 332 333 #[test] 334 fn rust_test_kunit_in_kunit_test() { 335 assert!(in_kunit_test()); 336 } 337 338 #[test] 339 #[cfg(not(all()))] 340 fn rust_test_kunit_always_disabled_test() { 341 // This test should never run because of the `cfg`. 342 assert!(false); 343 } 344 } 345