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