diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2025-10-01 19:15:11 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2025-10-01 19:15:11 -0700 |
commit | 30bbcb44707a97fcb62246bebc8b413b5ab293f8 (patch) | |
tree | 1643c370c560ba2ea6f44944a817006160a15e98 /rust/macros | |
parent | d2b2fea3503e5e12b2e28784152937e48bcca6ff (diff) | |
parent | 285cae57a51664cc94e85de0ff994f9965b3aca8 (diff) |
Merge tag 'linux_kselftest-kunit-6.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest
Pull kunit updates from Shuah Khan:
- New parameterized test features
KUnit parameterized tests supported two primary methods for getting
parameters:
- Defining custom logic within a generate_params() function.
- Using the KUNIT_ARRAY_PARAM() and KUNIT_ARRAY_PARAM_DESC() macros
with a pre-defined static array and passing the created
*_gen_params() to KUNIT_CASE_PARAM().
These methods present limitations when dealing with dynamically
generated parameter arrays, or in scenarios where populating
parameters sequentially via generate_params() is inefficient or
overly complex.
These limitations are fixed with a parameterized test method
- Fix issues in kunit build artifacts cleanup
- Fix parsing skipped test problem in kselftest framework
- Enable PCI on UML without triggering WARN()
- a few other fixes and adds support for new configs such as MIPS
* tag 'linux_kselftest-kunit-6.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest:
kunit: Extend kconfig help text for KUNIT_UML_PCI
rust: kunit: allow `cfg` on `test`s
kunit: qemu_configs: Add MIPS configurations
kunit: Enable PCI on UML without triggering WARN()
Documentation: kunit: Document new parameterized test features
kunit: Add example parameterized test with direct dynamic parameter array setup
kunit: Add example parameterized test with shared resource management using the Resource API
kunit: Enable direct registration of parameter arrays to a KUnit test
kunit: Pass parameterized test context to generate_params()
kunit: Introduce param_init/exit for parameterized test context management
kunit: Add parent kunit for parameterized test context
kunit: tool: Accept --raw_output=full as an alias of 'all'
kunit: tool: Parse skipped tests from kselftest.h
kunit: Always descend into kunit directory during build
Diffstat (limited to 'rust/macros')
-rw-r--r-- | rust/macros/kunit.rs | 48 |
1 files changed, 36 insertions, 12 deletions
diff --git a/rust/macros/kunit.rs b/rust/macros/kunit.rs index 81d18149a0cc..b395bb053695 100644 --- a/rust/macros/kunit.rs +++ b/rust/macros/kunit.rs @@ -5,6 +5,7 @@ //! Copyright (c) 2023 José Expósito <jose.exposito89@gmail.com> use proc_macro::{Delimiter, Group, TokenStream, TokenTree}; +use std::collections::HashMap; use std::fmt::Write; pub(crate) fn kunit_tests(attr: TokenStream, ts: TokenStream) -> TokenStream { @@ -41,20 +42,32 @@ pub(crate) fn kunit_tests(attr: TokenStream, ts: TokenStream) -> TokenStream { // Get the functions set as tests. Search for `[test]` -> `fn`. let mut body_it = body.stream().into_iter(); let mut tests = Vec::new(); + let mut attributes: HashMap<String, TokenStream> = HashMap::new(); while let Some(token) = body_it.next() { match token { - TokenTree::Group(ident) if ident.to_string() == "[test]" => match body_it.next() { - Some(TokenTree::Ident(ident)) if ident.to_string() == "fn" => { - let test_name = match body_it.next() { - Some(TokenTree::Ident(ident)) => ident.to_string(), - _ => continue, - }; - tests.push(test_name); + TokenTree::Punct(ref p) if p.as_char() == '#' => match body_it.next() { + Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Bracket => { + if let Some(TokenTree::Ident(name)) = g.stream().into_iter().next() { + // Collect attributes because we need to find which are tests. We also + // need to copy `cfg` attributes so tests can be conditionally enabled. + attributes + .entry(name.to_string()) + .or_default() + .extend([token, TokenTree::Group(g)]); + } + continue; } - _ => continue, + _ => (), }, + TokenTree::Ident(i) if i.to_string() == "fn" && attributes.contains_key("test") => { + if let Some(TokenTree::Ident(test_name)) = body_it.next() { + tests.push((test_name, attributes.remove("cfg").unwrap_or_default())) + } + } + _ => (), } + attributes.clear(); } // Add `#[cfg(CONFIG_KUNIT="y")]` before the module declaration. @@ -100,11 +113,22 @@ pub(crate) fn kunit_tests(attr: TokenStream, ts: TokenStream) -> TokenStream { let mut test_cases = "".to_owned(); let mut assert_macros = "".to_owned(); let path = crate::helpers::file(); - for test in &tests { + let num_tests = tests.len(); + for (test, cfg_attr) in tests { let kunit_wrapper_fn_name = format!("kunit_rust_wrapper_{test}"); - // An extra `use` is used here to reduce the length of the message. + // 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. let kunit_wrapper = format!( - "unsafe extern \"C\" fn {kunit_wrapper_fn_name}(_test: *mut ::kernel::bindings::kunit) {{ use ::kernel::kunit::is_test_result_ok; assert!(is_test_result_ok({test}())); }}", + r#"unsafe extern "C" fn {kunit_wrapper_fn_name}(_test: *mut ::kernel::bindings::kunit) + {{ + (*_test).status = ::kernel::bindings::kunit_status_KUNIT_SKIPPED; + {cfg_attr} {{ + (*_test).status = ::kernel::bindings::kunit_status_KUNIT_SUCCESS; + use ::kernel::kunit::is_test_result_ok; + assert!(is_test_result_ok({test}())); + }} + }}"#, ); writeln!(kunit_macros, "{kunit_wrapper}").unwrap(); writeln!( @@ -139,7 +163,7 @@ macro_rules! assert_eq {{ writeln!( kunit_macros, "static mut TEST_CASES: [::kernel::bindings::kunit_case; {}] = [\n{test_cases} ::kernel::kunit::kunit_case_null(),\n];", - tests.len() + 1 + num_tests + 1 ) .unwrap(); |