Skip to content

Commit 22097b9

Browse files
JoseExpositoojeda
authored andcommitted
rust: kunit: add KUnit case and suite macros
Add a couple of Rust const functions and macros to allow to develop KUnit tests without relying on generated C code: - The `kunit_unsafe_test_suite!` Rust macro is similar to the `kunit_test_suite` C macro. It requires a NULL-terminated array of test cases (see below). - The `kunit_case` Rust function is similar to the `KUNIT_CASE` C macro. It generates as case from the name and function. - The `kunit_case_null` Rust function generates a NULL test case, which is to be used as delimiter in `kunit_test_suite!`. While these functions and macros can be used on their own, a future patch will introduce another macro to create KUnit tests using a user-space like syntax. Signed-off-by: José Expósito <[email protected]> Co-developed-by: Matt Gilbride <[email protected]> Signed-off-by: Matt Gilbride <[email protected]> Co-developed-by: Miguel Ojeda <[email protected]> Signed-off-by: Miguel Ojeda <[email protected]> Co-developed-by: David Gow <[email protected]> Signed-off-by: David Gow <[email protected]> Link: https://lore.kernel.org/r/[email protected] [ Applied Markdown in comment. - Miguel ] Signed-off-by: Miguel Ojeda <[email protected]>
1 parent fb62522 commit 22097b9

File tree

1 file changed

+124
-0
lines changed

1 file changed

+124
-0
lines changed

rust/kernel/kunit.rs

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,3 +161,127 @@ macro_rules! kunit_assert_eq {
161161
$crate::kunit_assert!($name, $file, $diff, $left == $right);
162162
}};
163163
}
164+
165+
/// Represents an individual test case.
166+
///
167+
/// The [`kunit_unsafe_test_suite!`] macro expects a NULL-terminated list of valid test cases.
168+
/// Use [`kunit_case_null`] to generate such a delimiter.
169+
#[doc(hidden)]
170+
pub const fn kunit_case(
171+
name: &'static kernel::str::CStr,
172+
run_case: unsafe extern "C" fn(*mut kernel::bindings::kunit),
173+
) -> kernel::bindings::kunit_case {
174+
kernel::bindings::kunit_case {
175+
run_case: Some(run_case),
176+
name: name.as_char_ptr(),
177+
attr: kernel::bindings::kunit_attributes {
178+
speed: kernel::bindings::kunit_speed_KUNIT_SPEED_NORMAL,
179+
},
180+
generate_params: None,
181+
status: kernel::bindings::kunit_status_KUNIT_SUCCESS,
182+
module_name: core::ptr::null_mut(),
183+
log: core::ptr::null_mut(),
184+
}
185+
}
186+
187+
/// Represents the NULL test case delimiter.
188+
///
189+
/// The [`kunit_unsafe_test_suite!`] macro expects a NULL-terminated list of test cases. This
190+
/// function returns such a delimiter.
191+
#[doc(hidden)]
192+
pub const fn kunit_case_null() -> kernel::bindings::kunit_case {
193+
kernel::bindings::kunit_case {
194+
run_case: None,
195+
name: core::ptr::null_mut(),
196+
generate_params: None,
197+
attr: kernel::bindings::kunit_attributes {
198+
speed: kernel::bindings::kunit_speed_KUNIT_SPEED_NORMAL,
199+
},
200+
status: kernel::bindings::kunit_status_KUNIT_SUCCESS,
201+
module_name: core::ptr::null_mut(),
202+
log: core::ptr::null_mut(),
203+
}
204+
}
205+
206+
/// Registers a KUnit test suite.
207+
///
208+
/// # Safety
209+
///
210+
/// `test_cases` must be a NULL terminated array of valid test cases,
211+
/// whose lifetime is at least that of the test suite (i.e., static).
212+
///
213+
/// # Examples
214+
///
215+
/// ```ignore
216+
/// extern "C" fn test_fn(_test: *mut kernel::bindings::kunit) {
217+
/// let actual = 1 + 1;
218+
/// let expected = 2;
219+
/// assert_eq!(actual, expected);
220+
/// }
221+
///
222+
/// static mut KUNIT_TEST_CASES: [kernel::bindings::kunit_case; 2] = [
223+
/// kernel::kunit::kunit_case(kernel::c_str!("name"), test_fn),
224+
/// kernel::kunit::kunit_case_null(),
225+
/// ];
226+
/// kernel::kunit_unsafe_test_suite!(suite_name, KUNIT_TEST_CASES);
227+
/// ```
228+
#[doc(hidden)]
229+
#[macro_export]
230+
macro_rules! kunit_unsafe_test_suite {
231+
($name:ident, $test_cases:ident) => {
232+
const _: () = {
233+
const KUNIT_TEST_SUITE_NAME: [::kernel::ffi::c_char; 256] = {
234+
let name_u8 = ::core::stringify!($name).as_bytes();
235+
let mut ret = [0; 256];
236+
237+
if name_u8.len() > 255 {
238+
panic!(concat!(
239+
"The test suite name `",
240+
::core::stringify!($name),
241+
"` exceeds the maximum length of 255 bytes."
242+
));
243+
}
244+
245+
let mut i = 0;
246+
while i < name_u8.len() {
247+
ret[i] = name_u8[i] as ::kernel::ffi::c_char;
248+
i += 1;
249+
}
250+
251+
ret
252+
};
253+
254+
static mut KUNIT_TEST_SUITE: ::kernel::bindings::kunit_suite =
255+
::kernel::bindings::kunit_suite {
256+
name: KUNIT_TEST_SUITE_NAME,
257+
#[allow(unused_unsafe)]
258+
// SAFETY: `$test_cases` is passed in by the user, and
259+
// (as documented) must be valid for the lifetime of
260+
// the suite (i.e., static).
261+
test_cases: unsafe {
262+
::core::ptr::addr_of_mut!($test_cases)
263+
.cast::<::kernel::bindings::kunit_case>()
264+
},
265+
suite_init: None,
266+
suite_exit: None,
267+
init: None,
268+
exit: None,
269+
attr: ::kernel::bindings::kunit_attributes {
270+
speed: ::kernel::bindings::kunit_speed_KUNIT_SPEED_NORMAL,
271+
},
272+
status_comment: [0; 256usize],
273+
debugfs: ::core::ptr::null_mut(),
274+
log: ::core::ptr::null_mut(),
275+
suite_init_err: 0,
276+
is_init: false,
277+
};
278+
279+
#[used]
280+
#[allow(unused_unsafe)]
281+
#[cfg_attr(not(target_os = "macos"), link_section = ".kunit_test_suites")]
282+
static mut KUNIT_TEST_SUITE_ENTRY: *const ::kernel::bindings::kunit_suite =
283+
// SAFETY: `KUNIT_TEST_SUITE` is static.
284+
unsafe { ::core::ptr::addr_of_mut!(KUNIT_TEST_SUITE) };
285+
};
286+
};
287+
}

0 commit comments

Comments
 (0)