mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 16:48:26 +02:00 
			
		
		
		
	 36174d16f3
			
		
	
	
		36174d16f3
		
	
	
	
	
		
			
			The KUnit `#[test]` support that landed recently is very basic and does
not map the `assert*!` macros into KUnit like the doctests do, so they
panic at the moment.
Thus implement the custom mapping in a similar way to doctests, reusing
the infrastructure there.
In Rust 1.88.0, the `file()` method in `Span` may be stable [1]. However,
it was changed recently (from `SourceFile`), so we need to do something
different in previous versions. Thus create a helper for it and use it
to get the path.
With this, a failing test suite like:
    #[kunit_tests(my_test_suite)]
    mod tests {
        use super::*;
        #[test]
        fn my_first_test() {
            assert_eq!(42, 43);
        }
        #[test]
        fn my_second_test() {
            assert!(42 >= 43);
        }
    }
will properly map back to KUnit, printing something like:
    [    1.924325]     KTAP version 1
    [    1.924421]     # Subtest: my_test_suite
    [    1.924506]     # speed: normal
    [    1.924525]     1..2
    [    1.926385]     # my_first_test: ASSERTION FAILED at rust/kernel/lib.rs:251
    [    1.926385]     Expected 42 == 43 to be true, but is false
    [    1.928026]     # my_first_test.speed: normal
    [    1.928075]     not ok 1 my_first_test
    [    1.928723]     # my_second_test: ASSERTION FAILED at rust/kernel/lib.rs:256
    [    1.928723]     Expected 42 >= 43 to be true, but is false
    [    1.929834]     # my_second_test.speed: normal
    [    1.929868]     not ok 2 my_second_test
    [    1.930032] # my_test_suite: pass:0 fail:2 skip:0 total:2
    [    1.930153] # Totals: pass:0 fail:2 skip:0 total
Link: https://github.com/rust-lang/rust/pull/140514 [1]
Reviewed-by: David Gow <davidgow@google.com>
Acked-by: Danilo Krummrich <dakr@kernel.org>
Link: https://lore.kernel.org/r/20250502215133.1923676-2-ojeda@kernel.org
[ Required `KUNIT=y` like for doctests. Used the `cfg_attr` from the
  TODO comment and clarified its comment now that the stabilization is
  in beta and thus quite likely stable in Rust 1.88.0. Simplified the
  `new_body` code by introducing a new variable. Added
  `#[allow(clippy::incompatible_msrv)]`. - Miguel ]
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
		
	
			
		
			
				
	
	
		
			105 lines
		
	
	
	
		
			3 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			105 lines
		
	
	
	
		
			3 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0
 | |
| 
 | |
| use proc_macro::{token_stream, Group, Ident, TokenStream, TokenTree};
 | |
| 
 | |
| pub(crate) fn try_ident(it: &mut token_stream::IntoIter) -> Option<String> {
 | |
|     if let Some(TokenTree::Ident(ident)) = it.next() {
 | |
|         Some(ident.to_string())
 | |
|     } else {
 | |
|         None
 | |
|     }
 | |
| }
 | |
| 
 | |
| pub(crate) fn try_literal(it: &mut token_stream::IntoIter) -> Option<String> {
 | |
|     if let Some(TokenTree::Literal(literal)) = it.next() {
 | |
|         Some(literal.to_string())
 | |
|     } else {
 | |
|         None
 | |
|     }
 | |
| }
 | |
| 
 | |
| pub(crate) fn try_string(it: &mut token_stream::IntoIter) -> Option<String> {
 | |
|     try_literal(it).and_then(|string| {
 | |
|         if string.starts_with('\"') && string.ends_with('\"') {
 | |
|             let content = &string[1..string.len() - 1];
 | |
|             if content.contains('\\') {
 | |
|                 panic!("Escape sequences in string literals not yet handled");
 | |
|             }
 | |
|             Some(content.to_string())
 | |
|         } else if string.starts_with("r\"") {
 | |
|             panic!("Raw string literals are not yet handled");
 | |
|         } else {
 | |
|             None
 | |
|         }
 | |
|     })
 | |
| }
 | |
| 
 | |
| pub(crate) fn expect_ident(it: &mut token_stream::IntoIter) -> String {
 | |
|     try_ident(it).expect("Expected Ident")
 | |
| }
 | |
| 
 | |
| pub(crate) fn expect_punct(it: &mut token_stream::IntoIter) -> char {
 | |
|     if let TokenTree::Punct(punct) = it.next().expect("Reached end of token stream for Punct") {
 | |
|         punct.as_char()
 | |
|     } else {
 | |
|         panic!("Expected Punct");
 | |
|     }
 | |
| }
 | |
| 
 | |
| pub(crate) fn expect_string(it: &mut token_stream::IntoIter) -> String {
 | |
|     try_string(it).expect("Expected string")
 | |
| }
 | |
| 
 | |
| pub(crate) fn expect_string_ascii(it: &mut token_stream::IntoIter) -> String {
 | |
|     let string = try_string(it).expect("Expected string");
 | |
|     assert!(string.is_ascii(), "Expected ASCII string");
 | |
|     string
 | |
| }
 | |
| 
 | |
| pub(crate) fn expect_group(it: &mut token_stream::IntoIter) -> Group {
 | |
|     if let TokenTree::Group(group) = it.next().expect("Reached end of token stream for Group") {
 | |
|         group
 | |
|     } else {
 | |
|         panic!("Expected Group");
 | |
|     }
 | |
| }
 | |
| 
 | |
| pub(crate) fn expect_end(it: &mut token_stream::IntoIter) {
 | |
|     if it.next().is_some() {
 | |
|         panic!("Expected end");
 | |
|     }
 | |
| }
 | |
| 
 | |
| /// Given a function declaration, finds the name of the function.
 | |
| pub(crate) fn function_name(input: TokenStream) -> Option<Ident> {
 | |
|     let mut input = input.into_iter();
 | |
|     while let Some(token) = input.next() {
 | |
|         match token {
 | |
|             TokenTree::Ident(i) if i.to_string() == "fn" => {
 | |
|                 if let Some(TokenTree::Ident(i)) = input.next() {
 | |
|                     return Some(i);
 | |
|                 }
 | |
|                 return None;
 | |
|             }
 | |
|             _ => continue,
 | |
|         }
 | |
|     }
 | |
|     None
 | |
| }
 | |
| 
 | |
| pub(crate) fn file() -> String {
 | |
|     #[cfg(not(CONFIG_RUSTC_HAS_SPAN_FILE))]
 | |
|     {
 | |
|         proc_macro::Span::call_site()
 | |
|             .source_file()
 | |
|             .path()
 | |
|             .to_string_lossy()
 | |
|             .into_owned()
 | |
|     }
 | |
| 
 | |
|     #[cfg(CONFIG_RUSTC_HAS_SPAN_FILE)]
 | |
|     #[allow(clippy::incompatible_msrv)]
 | |
|     {
 | |
|         proc_macro::Span::call_site().file()
 | |
|     }
 | |
| }
 |