[PATCH v3 23/25] rust: str: test: replace `alloc::format`

Danilo Krummrich
2024-08-01T00:08:58Z

The current implementation of tests in str.rs use `format!` to format
strings for comparison, which, internally, creates a new `String`.

In order to prepare for getting rid of Rust's alloc crate, we have to
cut this dependency. Instead, we could implement `format!` for
`CString`, however `CString` uses the kernel `Vec` type and hence the
kernel allocators, which can't be used for tests run in userspace.

Instead, implement `fmt_assert_eq`, which formats strings into a stack
allocated buffer for comparison with a `&str`.

Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/str.rs | 66 ++++++++++++++++++++++++++++++++--------------
1 file changed, 46 insertions(+), 20 deletions(-)

diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs
index 0b6ffbade521..539be40754f4 100644
--- a/rust/kernel/str.rs
+++ b/rust/kernel/str.rs
@@ -523,7 +523,6 @@ macro_rules! c_str {
#[cfg(test)]
mod tests {
use super::*;
- use alloc::format;

const ALL_ASCII_CHARS: &'static str =
"\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\\x0a\\x0b\\x0c\\x0d\\x0e\\x0f\
@@ -539,6 +538,33 @@ mod tests {
\\xe0\\xe1\\xe2\\xe3\\xe4\\xe5\\xe6\\xe7\\xe8\\xe9\\xea\\xeb\\xec\\xed\\xee\\xef\
\\xf0\\xf1\\xf2\\xf3\\xf4\\xf5\\xf6\\xf7\\xf8\\xf9\\xfa\\xfb\\xfc\\xfd\\xfe\\xff";

+ fn format_into_buf<'a>(args: fmt::Arguments<'_>, buf: &'a mut [u8]) -> Result<&'a str, Error> {
+ let mut f = RawFormatter::new();
+ f.write_fmt(args)?;
+ let size = f.bytes_written();
+
+ assert!(buf.len() >= size);
+
+ // SAFETY: `buf` has at least a size of `size` bytes and is valid for writes.
+ let mut f = unsafe { Formatter::from_buffer(buf.as_mut_ptr(), size) };
+ f.write_fmt(args)?;
+
+ Ok(core::str::from_utf8(&buf[0..size])?)
+ }
+
+ macro_rules! fmt_assert_eq {
+ ($str:expr, $($f:tt)*) => ({
+ let mut buf = [0_u8; ALL_ASCII_CHARS.len()];
+
+ let s = match format_into_buf(kernel::fmt!($($f)*), &mut buf) {
+ Ok(s) => s,
+ Err(_) => panic!("Could not format into buffer."),
+ };
+
+ assert_eq!($str, s);
+ })
+ }
+
#[test]
fn test_cstr_to_str() {
let good_bytes = b"\xf0\x9f\xa6\x80\0";
@@ -566,13 +592,13 @@ fn test_cstr_as_str_unchecked() {
#[test]
fn test_cstr_display() {
let hello_world = CStr::from_bytes_with_nul(b"hello, world!\0").unwrap();
- assert_eq!(format!("{}", hello_world), "hello, world!");
+ fmt_assert_eq!("hello, world!", "{}", hello_world);
let non_printables = CStr::from_bytes_with_nul(b"\x01\x09\x0a\0").unwrap();
- assert_eq!(format!("{}", non_printables), "\\x01\\x09\\x0a");
+ fmt_assert_eq!("\\x01\\x09\\x0a", "{}", non_printables);
let non_ascii = CStr::from_bytes_with_nul(b"d\xe9j\xe0 vu\0").unwrap();
- assert_eq!(format!("{}", non_ascii), "d\\xe9j\\xe0 vu");
+ fmt_assert_eq!("d\\xe9j\\xe0 vu", "{}", non_ascii);
let good_bytes = CStr::from_bytes_with_nul(b"\xf0\x9f\xa6\x80\0").unwrap();
- assert_eq!(format!("{}", good_bytes), "\\xf0\\x9f\\xa6\\x80");
+ fmt_assert_eq!("\\xf0\\x9f\\xa6\\x80", "{}", good_bytes);
}

#[test]
@@ -583,47 +609,47 @@ fn test_cstr_display_all_bytes() {
bytes[i as usize] = i.wrapping_add(1);
}
let cstr = CStr::from_bytes_with_nul(&bytes).unwrap();
- assert_eq!(format!("{}", cstr), ALL_ASCII_CHARS);
+ fmt_assert_eq!(ALL_ASCII_CHARS, "{}", cstr);
}

#[test]
fn test_cstr_debug() {
let hello_world = CStr::from_bytes_with_nul(b"hello, world!\0").unwrap();
- assert_eq!(format!("{:?}", hello_world), "\"hello, world!\"");
+ fmt_assert_eq!("\"hello, world!\"", "{:?}", hello_world);
let non_printables = CStr::from_bytes_with_nul(b"\x01\x09\x0a\0").unwrap();
- assert_eq!(format!("{:?}", non_printables), "\"\\x01\\x09\\x0a\"");
+ fmt_assert_eq!("\"\\x01\\x09\\x0a\"", "{:?}", non_printables);
let non_ascii = CStr::from_bytes_with_nul(b"d\xe9j\xe0 vu\0").unwrap();
- assert_eq!(format!("{:?}", non_ascii), "\"d\\xe9j\\xe0 vu\"");
+ fmt_assert_eq!("\"d\\xe9j\\xe0 vu\"", "{:?}", non_ascii);
let good_bytes = CStr::from_bytes_with_nul(b"\xf0\x9f\xa6\x80\0").unwrap();
- assert_eq!(format!("{:?}", good_bytes), "\"\\xf0\\x9f\\xa6\\x80\"");
+ fmt_assert_eq!("\"\\xf0\\x9f\\xa6\\x80\"", "{:?}", good_bytes);
}

#[test]
fn test_bstr_display() {
let hello_world = BStr::from_bytes(b"hello, world!");
- assert_eq!(format!("{}", hello_world), "hello, world!");
+ fmt_assert_eq!("hello, world!", "{}", hello_world);
let escapes = BStr::from_bytes(b"_\t_\n_\r_\\_\'_\"_");
- assert_eq!(format!("{}", escapes), "_\\t_\\n_\\r_\\_'_\"_");
+ fmt_assert_eq!("_\\t_\\n_\\r_\\_'_\"_", "{}", escapes);
let others = BStr::from_bytes(b"\x01");
- assert_eq!(format!("{}", others), "\\x01");
+ fmt_assert_eq!("\\x01", "{}", others);
let non_ascii = BStr::from_bytes(b"d\xe9j\xe0 vu");
- assert_eq!(format!("{}", non_ascii), "d\\xe9j\\xe0 vu");
+ fmt_assert_eq!("d\\xe9j\\xe0 vu", "{}", non_ascii);
let good_bytes = BStr::from_bytes(b"\xf0\x9f\xa6\x80");
- assert_eq!(format!("{}", good_bytes), "\\xf0\\x9f\\xa6\\x80");
+ fmt_assert_eq!("\\xf0\\x9f\\xa6\\x80", "{}", good_bytes);
}

#[test]
fn test_bstr_debug() {
let hello_world = BStr::from_bytes(b"hello, world!");
- assert_eq!(format!("{:?}", hello_world), "\"hello, world!\"");
+ fmt_assert_eq!("\"hello, world!\"", "{:?}", hello_world);
let escapes = BStr::from_bytes(b"_\t_\n_\r_\\_\'_\"_");
- assert_eq!(format!("{:?}", escapes), "\"_\\t_\\n_\\r_\\\\_'_\\\"_\"");
+ fmt_assert_eq!("\"_\\t_\\n_\\r_\\\\_'_\\\"_\"", "{:?}", escapes);
let others = BStr::from_bytes(b"\x01");
- assert_eq!(format!("{:?}", others), "\"\\x01\"");
+ fmt_assert_eq!("\"\\x01\"", "{:?}", others);
let non_ascii = BStr::from_bytes(b"d\xe9j\xe0 vu");
- assert_eq!(format!("{:?}", non_ascii), "\"d\\xe9j\\xe0 vu\"");
+ fmt_assert_eq!("\"d\\xe9j\\xe0 vu\"", "{:?}", non_ascii);
let good_bytes = BStr::from_bytes(b"\xf0\x9f\xa6\x80");
- assert_eq!(format!("{:?}", good_bytes), "\"\\xf0\\x9f\\xa6\\x80\"");
+ fmt_assert_eq!("\"\\xf0\\x9f\\xa6\\x80\"", "{:?}", good_bytes);
}
}

--
2.45.2