* [PATCH v2 0/3] rust: kunit: support #[should_panic] and same test name with different #[cfg(...)]
@ 2026-10-29 16:59 Nicolás Antinori via B4 Relay
2026-10-29 16:59 ` [PATCH v2 1/3] rust: kunit: add #[should_panic] support Nicolás Antinori via B4 Relay
` (2 more replies)
0 siblings, 3 replies; 6+ messages in thread
From: Nicolás Antinori via B4 Relay @ 2026-10-29 16:59 UTC (permalink / raw)
To: Alice Ryhl, Benno Lossin, Boqun Feng, Brendan Higgins,
Daniel Almeida, Danilo Krummrich, David Gow, Gary Guo,
Miguel Ojeda, Onur Özkan
Cc: Alexandre Courbot, Andreas Hindborg, Björn Roy Baron,
Burak Emir, Rae Moar, Tamir Duberstein, Trevor Gross, Yury Norov,
kunit-dev, linux-kernel, linux-kselftest, rust-for-linux,
Nicolás Antinori
This patch series intends to implement two features for KUnit tests
written in Rust. The work is based on a TODO comment made in the
`bitmap.rs` module [1].
Changelog:
v2:
- Added more tests in `rust/kernel/kunit.rs` testing Sashiko findings
and changes.
- Solved Sashiko: "[Low] kunit: spurious "try faulted" error logs for
successful #[should_panic] tests": Solved by adding
`rust_should_panic` in `kunit_try_catch` C struct to check if the test
is expected to panic and act accordingly. Dismissed what I first talked
about in [3].
- Solved Sashiko: "[Medium] kunit: assert! macro in #[should_panic]
tests causes unexpected failure": Solved by making `kunit_assert`
macro aware if the test should panic and act accordingly. Dismissed what
I first talked about in [3]
- Solved Sashiko: "[Low] kunit: missing diagnostic log for
#[should_panic] tests that fail to panic": Added an error message when
a test that was expected to panic do not
- Solved Sashiko: "[Low] kunit: dead_code error for test wrappers with
inactive cfg": Solved by moving the #[cfg(..)] attributes on top of
the wrapper functions
- Dismissed Sashiko: "[Medium] rust: bitmap: memory leak in
owned_bitmap_out_of_bounds() test panic": Not sure if can be done
anything. If I am not mistaken this is the same issue as stated here [4].
Sashiko report links: https://sashiko.dev/#/patchset/20260930-rust_kunit_v4-v1-0-e3b0d45311dd@gmail.com?part=1
v1:
- Removed 'stringification' of test names (to avoid clash of names for
different configurations now a number is appended).
- Added bool `rust_should_panic` attribute to `kunit_attributes` C
struct
- Removed #[panic_handler] in Rust side, checking for aborts in the C
side (`kunit_run_case_catch_errors` function). This allow to catch
panics triggered, for example in a Rust function that called a panicking
C function.
- Now the tests that have an inactive #[cfg(..)] do not appear un the
KUnit execution summary as ignored, they just do not run.
Link to v1: https://lore.kernel.org/all/20260930-rust_kunit_v4-v1-0-e3b0d45311dd@gmail.com/
- RFC patch: https://lore.kernel.org/rust-for-linux/cover.1789500084.git.nico.antinori.7@gmail.com/
1. Supporting `#[should_panic]` [2]:
KUnit tests in Rust follow the user-space syntax, but at the moment
`#[should_panic]` is not supported. The first patch of this series adds
support for the attribute (only in its basic form, `#[should_panic =
"message"]` is not supported yet).
The way it is supported is by adding a new boolean property called
`rust_should_panic` to `kunit_attributes` struct in kunit/test.h and in
`kunit_attributes` in kunit/try-catch.h and check for the
`try_catch->try_result` code and the named flag to detect if there was a
panic in `kunit_try_catch_run` function.
2. Allow same test name with different #[cfg(...)]:
When testing `#[should_panic]` in `bitmap.rs` (check the last patch of
the series) I found that the test that was supposed to panic had the
same name as another one, but they were run on different configurations.
This caused the following compilation error:
ERROR:root:error[E0428]: the name `kunit_rust_wrapper_owned_bitmap_out_of_bounds` is defined multiple times
--> ../rust/kernel/bitmap.rs:503:1
|
503 | #[macros::kunit_tests(rust_kernel_bitmap)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `kunit_rust_wrapper_owned_bitmap_out_of_bounds` redefined here
|
= note: `kunit_rust_wrapper_owned_bitmap_out_of_bounds` must be defined only once in the value namespace of this module
= note: this error originates in the attribute macro `macros::kunit_tests` (in Nightly builds, run with -Z macro-backtrace for more info)
Now, only the tests with an active `#[cfg(...)]` will be executed by the
runner. Tests with an inactive `#[cfg(...)]` will not appear in the
report since they are not added to the TEST_CASES array.
[1] https://github.com/Rust-for-Linux/linux/blob/fd73f4a6659897191fa0d40695fe370925dd3780/rust/kernel/bitmap.rs#L592-L600
[2] https://doc.rust-lang.org/rust-by-example/testing/unit_testing.html#testing-panics
[3] https://lore.kernel.org/all/DM41PHVITKOG.3I6P2X8048BEP@gmail.com/
[4] https://github.com/Rust-for-Linux/linux/blob/fd73f4a6659897191fa0d40695fe370925dd3780/rust/kernel/kunit.rs#L129-L136
Signed-off-by: Nicolás Antinori <nico.antinori.7@gmail.com>
---
Nicolás Antinori (3):
rust: kunit: add #[should_panic] support
rust: kunit: allow same test name with different #[cfg(...)]
rust: bitmap: kunit: uncomment owned_bitmap_out_of_bounds panic case
include/kunit/test.h | 1 +
include/kunit/try-catch.h | 3 ++
lib/kunit/attributes.c | 18 ++++++++++++
lib/kunit/kunit-test.c | 3 ++
lib/kunit/test.c | 2 ++
lib/kunit/try-catch-impl.h | 2 ++
lib/kunit/try-catch.c | 14 +++++----
rust/kernel/bitmap.rs | 21 +++++++------
rust/kernel/kunit.rs | 73 ++++++++++++++++++++++++++++++++++++++++++++--
rust/macros/kunit.rs | 67 +++++++++++++++++++++++++++++-------------
10 files changed, 164 insertions(+), 40 deletions(-)
---
base-commit: 65ba8f34ec886ec72b2c07ddc8cf3d815c32869d
change-id: 20261029-rust_kunit_v6_send-d0353c1615f9
Best regards,
--
Nicolás Antinori <nico.antinori.7@gmail.com>
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v2 1/3] rust: kunit: add #[should_panic] support
2026-10-29 16:59 [PATCH v2 0/3] rust: kunit: support #[should_panic] and same test name with different #[cfg(...)] Nicolás Antinori via B4 Relay
@ 2026-10-29 16:59 ` Nicolás Antinori via B4 Relay
2026-11-02 14:58 ` Nicolás Antinori
2026-10-29 16:59 ` [PATCH v2 2/3] rust: kunit: allow same test name with different #[cfg(...)] Nicolás Antinori via B4 Relay
2026-10-29 16:59 ` [PATCH v2 3/3] rust: bitmap: kunit: uncomment owned_bitmap_out_of_bounds panic case Nicolás Antinori via B4 Relay
2 siblings, 1 reply; 6+ messages in thread
From: Nicolás Antinori via B4 Relay @ 2026-10-29 16:59 UTC (permalink / raw)
To: Alice Ryhl, Benno Lossin, Boqun Feng, Brendan Higgins,
Daniel Almeida, Danilo Krummrich, David Gow, Gary Guo,
Miguel Ojeda, Onur Özkan
Cc: Alexandre Courbot, Andreas Hindborg, Björn Roy Baron,
Burak Emir, Rae Moar, Tamir Duberstein, Trevor Gross, Yury Norov,
kunit-dev, linux-kernel, linux-kselftest, rust-for-linux,
Nicolás Antinori
From: Nicolás Antinori <nico.antinori.7@gmail.com>
KUnit tests in Rust are written using user-space like syntax. This patch
adds support for the `#[should_panic]` attribute, enabling the user to
test conditions that are expected to cause a panic and report the test
as successful.
Signed-off-by: Nicolás Antinori <nico.antinori.7@gmail.com>
---
include/kunit/test.h | 1 +
include/kunit/try-catch.h | 3 +++
lib/kunit/attributes.c | 18 ++++++++++++++++
lib/kunit/kunit-test.c | 3 +++
lib/kunit/test.c | 2 ++
lib/kunit/try-catch-impl.h | 2 ++
lib/kunit/try-catch.c | 14 ++++++++-----
rust/kernel/kunit.rs | 51 +++++++++++++++++++++++++++++++++++++++++++---
rust/macros/kunit.rs | 35 ++++++++++++++++++++++++-------
9 files changed, 114 insertions(+), 15 deletions(-)
diff --git a/include/kunit/test.h b/include/kunit/test.h
index 31d549578f8ec113bef9b5d72bc9355c07fce3c0..15bd020d83722163bfc3899f64247afb2c4cefa4 100644
--- a/include/kunit/test.h
+++ b/include/kunit/test.h
@@ -83,6 +83,7 @@ enum kunit_speed {
/* Holds attributes for each test case and suite */
struct kunit_attributes {
enum kunit_speed speed;
+ bool rust_should_panic;
};
/**
diff --git a/include/kunit/try-catch.h b/include/kunit/try-catch.h
index d4e1a5b98ed67e4f71ff1bcc71422d5bada54b75..197029e19d759dfeb2700af13cfc8fc246768bb9 100644
--- a/include/kunit/try-catch.h
+++ b/include/kunit/try-catch.h
@@ -22,6 +22,8 @@ struct kunit;
* @try_result: Contains any errno obtained while running test case.
* @try: The function, the test case, to attempt to run.
* @catch: The function called if @try bails out.
+ * @rust_should_panic: Flag used to avoid printing spurious error logs when a
+ * Rust test is annotated with #[should_panic].
* @context: used to pass user data to the try and catch functions.
*
* kunit_try_catch provides a generic, architecture independent way to execute
@@ -48,6 +50,7 @@ struct kunit_try_catch {
kunit_try_catch_func_t try;
kunit_try_catch_func_t catch;
unsigned long timeout;
+ bool rust_should_panic;
void *context;
};
diff --git a/lib/kunit/attributes.c b/lib/kunit/attributes.c
index 6d7a53af94a9acf3cf336409462059b972efeb22..8a9dc526b3cb2d363d42087f8666a03e9bc2bc4b 100644
--- a/lib/kunit/attributes.c
+++ b/lib/kunit/attributes.c
@@ -246,6 +246,16 @@ static void *attr_is_init_get(void *test_or_suite, bool is_test)
return ((void *) suite->is_init);
}
+static void *attr_rust_should_panic_get(void *test_or_suite, bool is_test)
+{
+ struct kunit_case *test = is_test ? test_or_suite : NULL;
+
+ if (test)
+ return ((void *) test->attr.rust_should_panic);
+ else
+ return ((void *) NULL);
+}
+
/* List of all Test Attributes */
static struct kunit_attr kunit_attr_list[] = {
@@ -272,6 +282,14 @@ static struct kunit_attr kunit_attr_list[] = {
.filter = attr_bool_filter,
.attr_default = (void *)false,
.print = PRINT_SUITE,
+ },
+ {
+ .name = "rust_should_panic",
+ .get_attr = attr_rust_should_panic_get,
+ .to_string = attr_bool_to_string,
+ .filter = attr_bool_filter,
+ .attr_default = (void *)false,
+ .print = PRINT_NEVER,
}
};
diff --git a/lib/kunit/kunit-test.c b/lib/kunit/kunit-test.c
index 126e30879dad652d5b236aeb13d4406a4ddbab00..7cca586767cc711f7a829cc4c3bdec17bd8c7d9c 100644
--- a/lib/kunit/kunit-test.c
+++ b/lib/kunit/kunit-test.c
@@ -45,6 +45,7 @@ static void kunit_test_try_catch_successful_try_no_catch(struct kunit *test)
test,
kunit_test_successful_try,
kunit_test_no_catch,
+ false,
300 * msecs_to_jiffies(MSEC_PER_SEC));
kunit_try_catch_run(try_catch, test);
@@ -78,6 +79,7 @@ static void kunit_test_try_catch_unsuccessful_try_does_catch(struct kunit *test)
test,
kunit_test_unsuccessful_try,
kunit_test_catch,
+ false,
300 * msecs_to_jiffies(MSEC_PER_SEC));
kunit_try_catch_run(try_catch, test);
@@ -133,6 +135,7 @@ static void kunit_test_fault_null_dereference(struct kunit *test)
test,
kunit_test_null_dereference,
kunit_test_catch,
+ false,
300 * msecs_to_jiffies(MSEC_PER_SEC));
kunit_try_catch_run(try_catch, test);
diff --git a/lib/kunit/test.c b/lib/kunit/test.c
index 09e3dabfac0c945b2608feb88c1b1d46fb807edc..8b0072c66e1710c7faa7de2df7ff8c4bf48b8e88 100644
--- a/lib/kunit/test.c
+++ b/lib/kunit/test.c
@@ -584,6 +584,7 @@ static void kunit_run_case_catch_errors(struct kunit_suite *suite,
test,
kunit_try_run_case,
kunit_catch_run_case,
+ test_case->attr.rust_should_panic,
kunit_test_timeout(suite, test_case));
context.test = test;
context.suite = suite;
@@ -595,6 +596,7 @@ static void kunit_run_case_catch_errors(struct kunit_suite *suite,
test,
kunit_try_run_case_cleanup,
kunit_catch_run_case_cleanup,
+ false,
kunit_test_timeout(suite, test_case));
kunit_try_catch_run(try_catch, &context);
diff --git a/lib/kunit/try-catch-impl.h b/lib/kunit/try-catch-impl.h
index 6f401b97cd0b9842e7533f1b828f72f884450f74..9abe69bb8b396185afb77cb11ca651ba4c9b6a59 100644
--- a/lib/kunit/try-catch-impl.h
+++ b/lib/kunit/try-catch-impl.h
@@ -18,11 +18,13 @@ static inline void kunit_try_catch_init(struct kunit_try_catch *try_catch,
struct kunit *test,
kunit_try_catch_func_t try,
kunit_try_catch_func_t catch,
+ bool rust_should_panic,
unsigned long timeout)
{
try_catch->test = test;
try_catch->try = try;
try_catch->catch = catch;
+ try_catch->rust_should_panic = rust_should_panic;
try_catch->timeout = timeout;
}
diff --git a/lib/kunit/try-catch.c b/lib/kunit/try-catch.c
index d84a879f0a789f3625538735644910c84ac80e56..b9e29d4e7dd52d8d82d71a3f138f0313c772ee76 100644
--- a/lib/kunit/try-catch.c
+++ b/lib/kunit/try-catch.c
@@ -76,11 +76,15 @@ void kunit_try_catch_run(struct kunit_try_catch *try_catch, void *context)
if (exit_code == -EFAULT)
try_catch->try_result = 0;
else if (exit_code == -EINTR) {
- if (test->last_seen.file)
- kunit_err(test, "try faulted: last line seen %s:%d\n",
- test->last_seen.file, test->last_seen.line);
- else
- kunit_err(test, "try faulted\n");
+ if (try_catch->rust_should_panic) {
+ try_catch->try_result = 0;
+ } else {
+ if (test->last_seen.file)
+ kunit_err(test, "try faulted: last line seen %s:%d\n",
+ test->last_seen.file, test->last_seen.line);
+ else
+ kunit_err(test, "try faulted\n");
+ }
} else if (exit_code == -ETIMEDOUT)
kunit_err(test, "try timed out\n");
else if (exit_code)
diff --git a/rust/kernel/kunit.rs b/rust/kernel/kunit.rs
index 25ff272d29b2b3f192a3968481c1d5417df4335b..5e688b743ee2152867e2f99c7f18b86e1b7fbe3d 100644
--- a/rust/kernel/kunit.rs
+++ b/rust/kernel/kunit.rs
@@ -58,7 +58,7 @@ pub fn info(args: fmt::Arguments<'_>) {
#[doc(hidden)]
#[macro_export]
macro_rules! kunit_assert {
- ($name:literal, $condition:expr $(,)?) => {
+ ($name:literal, $test_should_panic:expr, $condition:expr $(,)?) => {
'out: {
// Do nothing if the condition is `true`.
if $condition {
@@ -93,6 +93,14 @@ macro_rules! kunit_assert {
));
break 'out;
}
+ // If condition fails but the test should panic, then we just abort.
+ else if $test_should_panic {
+ // SAFETY: FFI call. The `kunit_test` pointer is valid because we got it from
+ // `kunit_get_current_test()` and it was not null.
+ unsafe {
+ $crate::bindings::__kunit_abort(kunit_test);
+ }
+ }
#[repr(transparent)]
struct Location($crate::bindings::kunit_loc);
@@ -166,10 +174,10 @@ unsafe impl Sync for UnaryAssert {}
#[doc(hidden)]
#[macro_export]
macro_rules! kunit_assert_eq {
- ($name:literal, $left:expr, $right:expr $(,)?) => {{
+ ($name:literal, $test_should_panic:expr, $left:expr, $right:expr $(,)?) => {{
// For the moment, we just forward to the expression assert because, for binary asserts,
// KUnit supports only a few types (e.g. integers).
- $crate::kunit_assert!($name, $left == $right);
+ $crate::kunit_assert!($name, $test_should_panic, $left == $right);
}};
}
@@ -209,6 +217,7 @@ pub const fn kunit_case(
name: kernel::str::as_char_ptr_in_const_context(name),
attr: kernel::bindings::kunit_attributes {
speed: kernel::bindings::kunit_speed_KUNIT_SPEED_NORMAL,
+ rust_should_panic: false,
},
generate_params: None,
status: kernel::bindings::kunit_status_KUNIT_SUCCESS,
@@ -284,6 +293,7 @@ macro_rules! kunit_unsafe_test_suite {
exit: None,
attr: ::kernel::bindings::kunit_attributes {
speed: ::kernel::bindings::kunit_speed_KUNIT_SPEED_NORMAL,
+ rust_should_panic: false,
},
status_comment: [0; 256usize],
debugfs: ::core::ptr::null_mut(),
@@ -337,6 +347,16 @@ pub fn in_kunit_test() -> bool {
mod tests {
use super::*;
+ struct TestStruct;
+
+ impl TestStruct {
+ // This assert does not get replaced with kunit_assert, since this is not a #[test]
+ // function.
+ pub(crate) fn escaped_assert() {
+ assert!(false);
+ }
+ }
+
#[test]
fn rust_test_kunit_example_test() {
assert_eq!(1 + 1, 2);
@@ -347,6 +367,31 @@ fn rust_test_kunit_in_kunit_test() {
assert!(in_kunit_test());
}
+ #[test]
+ #[should_panic]
+ fn rust_test_kunit_panic_in_kunit_test() {
+ panic!("This test should panic and pass");
+ }
+
+ #[test]
+ #[should_panic]
+ fn rust_test_kunit_panic_in_kunit_test_bug() {
+ // The panic is produced in a C call. This should succeed.
+ unsafe { bindings::BUG() };
+ }
+
+ #[test]
+ #[should_panic]
+ fn rust_test_assert_false_success_with_should_panic() {
+ assert!(false)
+ }
+
+ #[test]
+ #[should_panic]
+ fn rust_test_should_panic_with_escaped_assert() {
+ TestStruct::escaped_assert();
+ }
+
#[test]
#[cfg(not(all()))]
fn rust_test_kunit_always_disabled_test() {
diff --git a/rust/macros/kunit.rs b/rust/macros/kunit.rs
index e9152b9d51f9450b7e549e2e1629b5fc5f485a28..288b53d21c4e7c8d9b5148d50c1a833f73e53015 100644
--- a/rust/macros/kunit.rs
+++ b/rust/macros/kunit.rs
@@ -106,6 +106,11 @@ pub(crate) fn kunit_tests(test_suite: Ident, mut module: ItemMod) -> Result<Toke
.cloned()
.collect();
+ let should_panic = f
+ .attrs
+ .iter()
+ .any(|attr| attr.path().is_ident("should_panic"));
+
// Before the test, override usual `assert!` and `assert_eq!` macros with ones that call
// KUnit instead.
let test_str = test.to_string();
@@ -113,7 +118,7 @@ pub(crate) fn kunit_tests(test_suite: Ident, mut module: ItemMod) -> Result<Toke
#[allow(unused)]
macro_rules! assert {
($cond:expr $(,)?) => {{
- kernel::kunit_assert!(#test_str, $cond);
+ kernel::kunit_assert!(#test_str, #should_panic, $cond);
}}
}
});
@@ -121,7 +126,7 @@ macro_rules! assert {
#[allow(unused)]
macro_rules! assert_eq {
($left:expr, $right:expr $(,)?) => {{
- kernel::kunit_assert_eq!(#test_str, $left, $right);
+ kernel::kunit_assert_eq!(#test_str, #should_panic, $left, $right);
}}
}
});
@@ -134,25 +139,41 @@ macro_rules! assert_eq {
&CString::new(test_str.as_str()).expect("identifier cannot contain NUL"),
test.span(),
);
+ let assertion = if should_panic {
+ quote!(
+ let _ = #test();
+ ::kernel::kunit::err(::kernel::prelude::fmt!(
+ " Expected {} to panic\n", #test_str
+ ));
+ (*_test).status = ::kernel::bindings::kunit_status_KUNIT_FAILURE;
+ )
+ } else {
+ quote!(
+ use ::kernel::kunit::is_test_result_ok;
+ assert!(is_test_result_ok(#test()));
+ )
+ };
processed_items.push(parse_quote! {
unsafe extern "C" fn #kunit_wrapper_fn_name(_test: *mut ::kernel::bindings::kunit) {
(*_test).status = ::kernel::bindings::kunit_status_KUNIT_SKIPPED;
+
// Append any `cfg` attributes the user might have written on their tests so we
// don't attempt to call them when they are `cfg`'d out. An extra `use` is used
// here to reduce the length of the assert message.
#(#cfg_attrs)*
{
(*_test).status = ::kernel::bindings::kunit_status_KUNIT_SUCCESS;
- use ::kernel::kunit::is_test_result_ok;
- assert!(is_test_result_ok(#test()));
+ #assertion
}
}
});
- test_cases.push(quote!(
- ::kernel::kunit::kunit_case(#test_cstr, #kunit_wrapper_fn_name)
- ));
+ test_cases.push(quote!({
+ let mut case = ::kernel::kunit::kunit_case(#test_cstr, #kunit_wrapper_fn_name);
+ case.attr.rust_should_panic = #should_panic;
+ case
+ }));
}
let num_tests_plus_1 = test_cases.len() + 1;
--
2.47.3
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v2 2/3] rust: kunit: allow same test name with different #[cfg(...)]
2026-10-29 16:59 [PATCH v2 0/3] rust: kunit: support #[should_panic] and same test name with different #[cfg(...)] Nicolás Antinori via B4 Relay
2026-10-29 16:59 ` [PATCH v2 1/3] rust: kunit: add #[should_panic] support Nicolás Antinori via B4 Relay
@ 2026-10-29 16:59 ` Nicolás Antinori via B4 Relay
2026-10-29 16:59 ` [PATCH v2 3/3] rust: bitmap: kunit: uncomment owned_bitmap_out_of_bounds panic case Nicolás Antinori via B4 Relay
2 siblings, 0 replies; 6+ messages in thread
From: Nicolás Antinori via B4 Relay @ 2026-10-29 16:59 UTC (permalink / raw)
To: Alice Ryhl, Benno Lossin, Boqun Feng, Brendan Higgins,
Daniel Almeida, Danilo Krummrich, David Gow, Gary Guo,
Miguel Ojeda, Onur Özkan
Cc: Alexandre Courbot, Andreas Hindborg, Björn Roy Baron,
Burak Emir, Rae Moar, Tamir Duberstein, Trevor Gross, Yury Norov,
kunit-dev, linux-kernel, linux-kselftest, rust-for-linux,
Nicolás Antinori
From: Nicolás Antinori <nico.antinori.7@gmail.com>
Sometimes it is necessary to test the same code paths under different
configurations. The `#[cfg(...)]` macro can be used to check if specific
configurations are enabled and run tests accordingly.
Currently, defining multiple tests with the same name under different
`#[cfg(...)]` attributes results in a compilation error. This patch
removes that restriction, allowing identical test names across different
configurations.
Signed-off-by: Nicolás Antinori <nico.antinori.7@gmail.com>
---
rust/kernel/kunit.rs | 22 ++++++++++++++++++++++
rust/macros/kunit.rs | 36 ++++++++++++++++++++----------------
2 files changed, 42 insertions(+), 16 deletions(-)
diff --git a/rust/kernel/kunit.rs b/rust/kernel/kunit.rs
index 5e688b743ee2152867e2f99c7f18b86e1b7fbe3d..ba3040d302b95af6d6e648a229ac8e9ea2c31a8c 100644
--- a/rust/kernel/kunit.rs
+++ b/rust/kernel/kunit.rs
@@ -367,6 +367,28 @@ fn rust_test_kunit_in_kunit_test() {
assert!(in_kunit_test());
}
+ // Tests with cfg have the same name on purpose because we are implicitly testing that
+ // tests with the same name but different configs do not throw a compilation error
+ #[test]
+ #[cfg(CONFIG_RUST_KUNIT_SELFTEST = "y")]
+ fn rust_test_kunit_run_only_active_cfg() {
+ assert!(in_kunit_test());
+ }
+
+ #[test]
+ #[cfg(CONFIG_RUST_KUNIT_SELFTEST = "n")]
+ fn rust_test_kunit_run_only_active_cfg() {
+ // This test should never run because of the `cfg`.
+ assert!(false)
+ }
+
+ #[test]
+ #[cfg(CONFIG_RUST_KUNIT_SELFTEST = "INVALID_VALUE")]
+ fn rust_test_kunit_run_only_active_cfg() {
+ // This test should never run because of the `cfg`.
+ assert!(false)
+ }
+
#[test]
#[should_panic]
fn rust_test_kunit_panic_in_kunit_test() {
diff --git a/rust/macros/kunit.rs b/rust/macros/kunit.rs
index 288b53d21c4e7c8d9b5148d50c1a833f73e53015..173792563ca8b42e39ec0a53798f7b3bb75bf9cf 100644
--- a/rust/macros/kunit.rs
+++ b/rust/macros/kunit.rs
@@ -45,6 +45,8 @@ pub(crate) fn kunit_tests(test_suite: Ident, mut module: ItemMod) -> Result<Toke
let mut processed_items = Vec::new();
let mut test_cases = Vec::new();
+ let mut test_cases_cfgs = Vec::new();
+ let mut test_case_number: u32 = 1;
// Generate the test KUnit test suite and a test case for each `#[test]`.
//
@@ -134,7 +136,7 @@ macro_rules! assert_eq {
// Add back the test item.
processed_items.push(Item::Fn(f));
- let kunit_wrapper_fn_name = format_ident!("kunit_rust_wrapper_{test}");
+ let kunit_wrapper_fn_name = format_ident!("kunit_rust_wrapper_{test}_{test_case_number}");
let test_cstr = LitCStr::new(
&CString::new(test_str.as_str()).expect("identifier cannot contain NUL"),
test.span(),
@@ -154,18 +156,12 @@ macro_rules! assert_eq {
)
};
processed_items.push(parse_quote! {
+ // Prepend any `cfg` attributes before the function declaration to activate it only in
+ // cases the configs are on
+ #(#cfg_attrs)*
unsafe extern "C" fn #kunit_wrapper_fn_name(_test: *mut ::kernel::bindings::kunit) {
- (*_test).status = ::kernel::bindings::kunit_status_KUNIT_SKIPPED;
-
-
- // Append any `cfg` attributes the user might have written on their tests so we
- // don't attempt to call them when they are `cfg`'d out. An extra `use` is used
- // here to reduce the length of the assert message.
- #(#cfg_attrs)*
- {
- (*_test).status = ::kernel::bindings::kunit_status_KUNIT_SUCCESS;
- #assertion
- }
+ (*_test).status = ::kernel::bindings::kunit_status_KUNIT_SUCCESS;
+ #assertion
}
});
@@ -174,15 +170,23 @@ macro_rules! assert_eq {
case.attr.rust_should_panic = #should_panic;
case
}));
+ test_cases_cfgs.push(cfg_attrs);
+ test_case_number += 1;
}
- let num_tests_plus_1 = test_cases.len() + 1;
+ // This dummy array is used to calculate the exact number of tests cases that are active
+ // depending on their #[cfg(...)] attribute.
+ processed_items.push(parse_quote!(
+ const TEST_CASES_COUNT: &[()] = &[#(#(#test_cases_cfgs)* (),)* ()];
+ ));
+
processed_items.push(parse_quote! {
- static mut TEST_CASES: [::kernel::bindings::kunit_case; #num_tests_plus_1] = [
- #(#test_cases,)*
- ::pin_init::zeroed(),
+ static mut TEST_CASES: [::kernel::bindings::kunit_case; TEST_CASES_COUNT.len()] = [
+ #(#(#test_cases_cfgs)* { #test_cases },)*
+ ::pin_init::zeroed()
];
});
+
processed_items.push(parse_quote! {
::kernel::kunit_unsafe_test_suite!(#test_suite, TEST_CASES);
});
--
2.47.3
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v2 3/3] rust: bitmap: kunit: uncomment owned_bitmap_out_of_bounds panic case
2026-10-29 16:59 [PATCH v2 0/3] rust: kunit: support #[should_panic] and same test name with different #[cfg(...)] Nicolás Antinori via B4 Relay
2026-10-29 16:59 ` [PATCH v2 1/3] rust: kunit: add #[should_panic] support Nicolás Antinori via B4 Relay
2026-10-29 16:59 ` [PATCH v2 2/3] rust: kunit: allow same test name with different #[cfg(...)] Nicolás Antinori via B4 Relay
@ 2026-10-29 16:59 ` Nicolás Antinori via B4 Relay
2 siblings, 0 replies; 6+ messages in thread
From: Nicolás Antinori via B4 Relay @ 2026-10-29 16:59 UTC (permalink / raw)
To: Alice Ryhl, Benno Lossin, Boqun Feng, Brendan Higgins,
Daniel Almeida, Danilo Krummrich, David Gow, Gary Guo,
Miguel Ojeda, Onur Özkan
Cc: Alexandre Courbot, Andreas Hindborg, Björn Roy Baron,
Burak Emir, Rae Moar, Tamir Duberstein, Trevor Gross, Yury Norov,
kunit-dev, linux-kernel, linux-kselftest, rust-for-linux,
Nicolás Antinori
From: Nicolás Antinori <nico.antinori.7@gmail.com>
Rust KUnit tests now support `#[should_panic]` attribute. Uncomment the
test so it can be run when CONFIG_RUST_BITMAP_HARDENED=y.
Signed-off-by: Nicolás Antinori <nico.antinori.7@gmail.com>
---
rust/kernel/bitmap.rs | 21 ++++++++++-----------
1 file changed, 10 insertions(+), 11 deletions(-)
diff --git a/rust/kernel/bitmap.rs b/rust/kernel/bitmap.rs
index 610d565535ccfba6af8f2ca4ac4028fa027a9644..8eecd669096713fc7f21431188959a921a1d5ca0 100644
--- a/rust/kernel/bitmap.rs
+++ b/rust/kernel/bitmap.rs
@@ -762,11 +762,10 @@ fn bitmap_set_clear_find() -> Result<(), AllocError> {
Ok(())
}
- #[test]
#[cfg(not(CONFIG_RUST_BITMAP_HARDENED))]
+ #[test]
fn owned_bitmap_out_of_bounds() -> Result<(), AllocError> {
let mut b = BitmapVec::new(128, GFP_KERNEL)?;
-
b.set_bit(2048);
b.set_bit_atomic(2048);
b.clear_bit(2048);
@@ -777,15 +776,15 @@ fn owned_bitmap_out_of_bounds() -> Result<(), AllocError> {
Ok(())
}
- // TODO: uncomment once kunit supports `#[should_panic]`.
- // #[cfg(CONFIG_RUST_BITMAP_HARDENED)]
- // #[test]
- // #[should_panic]
- // fn owned_bitmap_out_of_bounds() -> Result<(), AllocError> {
- // let mut b = BitmapVec::new(128, GFP_KERNEL)?;
- //
- // b.set_bit(2048);
- // }
+ #[cfg(CONFIG_RUST_BITMAP_HARDENED)]
+ #[test]
+ #[should_panic]
+ fn owned_bitmap_out_of_bounds() -> Result<(), AllocError> {
+ let mut b = BitmapVec::new(128, GFP_KERNEL)?;
+
+ b.set_bit(2048);
+ Ok(())
+ }
#[test]
fn bitmap_copy_and_extend() -> Result<(), AllocError> {
--
2.47.3
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v2 1/3] rust: kunit: add #[should_panic] support
2026-10-29 16:59 ` [PATCH v2 1/3] rust: kunit: add #[should_panic] support Nicolás Antinori via B4 Relay
@ 2026-11-02 14:58 ` Nicolás Antinori
2026-11-02 15:51 ` Miguel Ojeda
0 siblings, 1 reply; 6+ messages in thread
From: Nicolás Antinori @ 2026-11-02 14:58 UTC (permalink / raw)
To: nico.antinori.7, Alice Ryhl, Benno Lossin, Boqun Feng,
Brendan Higgins, Daniel Almeida, Danilo Krummrich, David Gow,
Gary Guo, Miguel Ojeda, Onur Özkan
Cc: Alexandre Courbot, Andreas Hindborg, Björn Roy Baron,
Burak Emir, Rae Moar, Tamir Duberstein, Trevor Gross, Yury Norov,
kunit-dev, linux-kernel, linux-kselftest, rust-for-linux
On Thu Oct 29, 2026 at 1:59 PM -03, Nicolás Antinori via B4 Relay wrote:
> From: Nicolás Antinori <nico.antinori.7@gmail.com>
>
> KUnit tests in Rust are written using user-space like syntax. This patch
> adds support for the `#[should_panic]` attribute, enabling the user to
> test conditions that are expected to cause a panic and report the test
> as successful.
>
> Signed-off-by: Nicolás Antinori <nico.antinori.7@gmail.com>
Hello, Sashiko found two issues [1] with this patch:
> [Severity: High]
> Will these new selftests crash the kernel or hang when
> CONFIG_RUST_KUNIT_SELFTEST is enabled under CONFIG_UML=y,
> CONFIG_PANIC_ON_OOPS=y, or CONFIG_BUG=n?
This is true, I tested this with:
./tools/testing/kunit/kunit.py run \
--make_options LLVM=1 \
--kconfig_add CONFIG_RUST=y \
--kconfig_add CONFIG_RUST_KUNIT_TESTS=y \
--kconfig_add CONFIG_KUNIT=y \
--kconfig_add CONFIG_UML=y \
--arch=um rust_kernel_kunit
./tools/testing/kunit/kunit.py run \
--make_options LLVM=1 \
--kconfig_add CONFIG_RUST=y \
--kconfig_add CONFIG_RUST_KUNIT_TESTS=y \
--kconfig_add CONFIG_KUNIT=y \
--kconfig_add CONFIG_PANIC_ON_OOPS=y \
--arch=x86_64 rust_kernel_kunit
And some other configs using the configs causing problems.
To solve this I created a new config "RUST_KUNIT_SHOULD_PANIC_ATTRIBUTE"
that depends on !UML && !PANIC_ON_OOPS && BUG. If the test is annotated
with `#[should_panic]` and CONFIG_RUST_KUNIT_SHOULD_PANIC_ATTRIBUTE=n,
the test won't run and won't show up in the summary.
> [Severity: Medium]
> Does defining macro_rules! assert and macro_rules! assert_eq at module scope
> in kunit_tests() leak the preceding test's #should_panic value into helper
> functions or impl blocks defined later in the module?
This is also true, the solution was redefining the `assert!` and
`assert_eq!` macros inside the function's body (as Sashiko suggested).
I'll wait a bit in case there are any feedback on the current series and
send a v3 with these changes.
Best Regards,
Nicolás
[1] https://sashiko.dev/#/patchset/20261029-rust_kunit_v6_send-v2-0-5ce551efc48e@gmail.com?part=1
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v2 1/3] rust: kunit: add #[should_panic] support
2026-11-02 14:58 ` Nicolás Antinori
@ 2026-11-02 15:51 ` Miguel Ojeda
0 siblings, 0 replies; 6+ messages in thread
From: Miguel Ojeda @ 2026-11-02 15:51 UTC (permalink / raw)
To: Nicolás Antinori
Cc: Alice Ryhl, Benno Lossin, Boqun Feng, Brendan Higgins,
Daniel Almeida, Danilo Krummrich, David Gow, Gary Guo,
Miguel Ojeda, Onur Özkan, Alexandre Courbot,
Andreas Hindborg, Björn Roy Baron, Burak Emir, Rae Moar,
Tamir Duberstein, Trevor Gross, Yury Norov, kunit-dev,
linux-kernel, linux-kselftest, rust-for-linux
On Mon, Nov 2, 2026 at 3:58 PM Nicolás Antinori
<nico.antinori.7@gmail.com> wrote:
>
> To solve this I created a new config "RUST_KUNIT_SHOULD_PANIC_ATTRIBUTE"
> that depends on !UML && !PANIC_ON_OOPS && BUG. If the test is annotated
> with `#[should_panic]` and CONFIG_RUST_KUNIT_SHOULD_PANIC_ATTRIBUTE=n,
> the test won't run and won't show up in the summary.
I was wondering if we needed the flexibility, i.e. if we could just
simply disable them when the conditions are not satisfied, but I guess
given `CONFIG_KUNIT_FAULT_TEST`'s existing description, it may make
sense to have a new Kconfig symbol -- do the ones added here look
similar in the kernel log to those fault ones?
(And I was wondering because, in general, adding Kconfig symbols has a
price: users may get confused etc.).
Cheers,
Miguel
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-11-02 15:51 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-29 16:59 [PATCH v2 0/3] rust: kunit: support #[should_panic] and same test name with different #[cfg(...)] Nicolás Antinori via B4 Relay
2026-10-29 16:59 ` [PATCH v2 1/3] rust: kunit: add #[should_panic] support Nicolás Antinori via B4 Relay
2026-11-02 14:58 ` Nicolás Antinori
2026-11-02 15:51 ` Miguel Ojeda
2026-10-29 16:59 ` [PATCH v2 2/3] rust: kunit: allow same test name with different #[cfg(...)] Nicolás Antinori via B4 Relay
2026-10-29 16:59 ` [PATCH v2 3/3] rust: bitmap: kunit: uncomment owned_bitmap_out_of_bounds panic case Nicolás Antinori via B4 Relay
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®