Add a selftest module to provide a temporary place to put "pure tests"
for Rust funtionality and wrappers.
Add test cases for `SgTable` and `ScatterList`.
Co-developed-by: Boqun Feng <boqun.feng@gmail.com>
Signed-off-by: Boqun Feng <boqun.feng@gmail.com>
Signed-off-by: Qingsong Chen <changxian.cqs@antgroup.com>
---
rust/kernel/error.rs | 2 +-
samples/rust/Kconfig | 10 ++
samples/rust/Makefile | 1 +
samples/rust/rust_selftests.rs | 186 +++++++++++++++++++++++++++++++++
4 files changed, 198 insertions(+), 1 deletion(-)
create mode 100644 samples/rust/rust_selftests.rs
diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs
index 5f4114b30b94..40f2db9d57a6 100644
--- a/rust/kernel/error.rs
+++ b/rust/kernel/error.rs
@@ -68,7 +68,7 @@ pub mod code {
/// # Invariants
///
/// The value is a valid `errno` (i.e. `>= -MAX_ERRNO && < 0`).
-#[derive(Clone, Copy, PartialEq, Eq)]
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct Error(core::ffi::c_int);
impl Error {
diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig
index b0f74a81c8f9..a1f29e5aaf83 100644
--- a/samples/rust/Kconfig
+++ b/samples/rust/Kconfig
@@ -30,6 +30,16 @@ config SAMPLE_RUST_PRINT
If unsure, say N.
+config SAMPLE_RUST_SELFTESTS
+ tristate "Self tests"
+ help
+ This option builds the self test cases for Rust.
+
+ To compile this as a module, choose M here:
+ the module will be called rust_selftests.
+
+ If unsure, say N.
+
config SAMPLE_RUST_HOSTPROGS
bool "Host programs"
help
diff --git a/samples/rust/Makefile b/samples/rust/Makefile
index 03086dabbea4..4519a567db7c 100644
--- a/samples/rust/Makefile
+++ b/samples/rust/Makefile
@@ -2,5 +2,6 @@
obj-$(CONFIG_SAMPLE_RUST_MINIMAL) += rust_minimal.o
obj-$(CONFIG_SAMPLE_RUST_PRINT) += rust_print.o
+obj-$(CONFIG_SAMPLE_RUST_SELFTESTS) += rust_selftests.o
subdir-$(CONFIG_SAMPLE_RUST_HOSTPROGS) += hostprogs
diff --git a/samples/rust/rust_selftests.rs b/samples/rust/rust_selftests.rs
new file mode 100644
index 000000000000..843d53159ac8
--- /dev/null
+++ b/samples/rust/rust_selftests.rs
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Self test cases for Rust.
+
+use kernel::prelude::*;
+// Keep the `use` for a test in its test function. Module-level `use`s are only for the test
+// framework.
+
+module! {
+ type: RustSelftests,
+ name: "rust_selftests",
+ author: "Rust for Linux Contributors",
+ description: "Self test cases for Rust",
+ license: "GPL",
+}
+
+struct RustSelftests;
+
+/// A summary of testing.
+///
+/// A test can
+///
+/// * pass (successfully), or
+/// * fail (without hitting any error), or
+/// * hit an error (interrupted).
+///
+/// This is the type that differentiates the first two (pass and fail) cases.
+///
+/// When a test hits an error, the test function should skip and return the error. Note that this
+/// doesn't mean the test fails, for example if the system doesn't have enough memory for
+/// testing, the test function may return an `Err(ENOMEM)` and skip.
+#[allow(dead_code)]
+enum TestSummary {
+ Pass,
+ Fail,
+}
+
+use TestSummary::Fail;
+use TestSummary::Pass;
+
+macro_rules! do_tests {
+ ($($name:ident),*) => {
+ let mut total = 0;
+ let mut pass = 0;
+ let mut fail = 0;
+
+ $({
+ total += 1;
+
+ match $name() {
+ Ok(Pass) => {
+ pass += 1;
+ pr_info!("{} passed!", stringify!($name));
+ },
+ Ok(Fail) => {
+ fail += 1;
+ pr_info!("{} failed!", stringify!($name));
+ },
+ Err(err) => {
+ pr_info!("{} hit error {:?}", stringify!($name), err);
+ }
+ }
+ })*
+
+ pr_info!("{} tests run, {} passed, {} failed, {} hit errors\n",
+ total, pass, fail, total - pass - fail);
+
+ if total == pass {
+ pr_info!("All tests passed. Congratulations!\n");
+ }
+ }
+}
+
+/// An example of test.
+#[allow(dead_code)]
+fn test_example() -> Result<TestSummary> {
+ // `use` declarations for the test can be put here, e.g. `use foo::bar;`.
+
+ // Always pass.
+ Ok(Pass)
+}
+
+/// A selftest case for `SgTable`.
+fn test_sgtable() -> Result<TestSummary> {
+ use core::pin::pin;
+ use kernel::scatterlist::*;
+
+ // Prepare memory buffers.
+ let buf0: Pin<&mut [u8]> = pin!([0u8; 512]);
+ let buf1: Pin<&mut [u8]> = pin!([1u8; 512]);
+ let mut entries = Vec::<Pin<&mut [u8]>>::new();
+ entries.try_push(buf0)?;
+ entries.try_push(buf1)?;
+
+ // Construct two `SgTable` instances. And the first is chainable, which
+ // requires one entry reserved.
+ let mut sgt0: Pin<Box<SgTable<'_, 3>>> =
+ Box::pin_init(SgTable::<'_, 3>::new(entries.as_slice(), true))?;
+ let mut sgt1: Pin<Box<SgTable<'_, 2>>> =
+ Box::pin_init(SgTable::<'_, 2>::new(entries.as_slice(), false))?;
+
+ // Assert the total count of entries in the table.
+ assert_eq!(sgt0.count(), 2);
+ assert_eq!(sgt1.count(), 2);
+
+ // Chain two `SgTable` together.
+ sgt0.chain_sgt(sgt1.as_mut())?;
+ assert_eq!(sgt0.count(), 4);
+ assert_eq!(sgt1.count(), 2);
+
+ // Get the immutable reference to the entry in the table.
+ let sgl1: Pin<&ScatterList<'_>> = sgt0.get(1).ok_or(EINVAL)?;
+ assert_eq!(sgl1.count(), 3);
+
+ // Get the mutable reference to the entry in the table.
+ let sgl3: Pin<&mut ScatterList<'_>> = sgt0.get_mut(3).ok_or(EINVAL)?;
+ assert_eq!(sgl3.count(), 1);
+
+ // Try to get a non-exist entry from the table.
+ let sgl4 = sgt0.get(4);
+ assert_eq!(sgl4.is_none(), true);
+
+ // Split the first table from chained scatterlist.
+ sgt0.split()?;
+ assert_eq!(sgt0.count(), 2);
+ assert_eq!(sgt1.count(), 2);
+
+ // Chain `SgTable` with single `ScatterList`.
+ let mut sgl: Pin<Box<ScatterList<'_>>> = Box::pin_init(ScatterList::new(&entries[0]))?;
+ sgt0.chain_sgl(sgl.as_mut())?;
+ assert_eq!(sgt0.count(), 3);
+
+ Ok(Pass)
+}
+
+/// A selftest case for `ScatterList`.
+fn test_scatterlist() -> Result<TestSummary> {
+ use core::pin::pin;
+ use kernel::scatterlist::*;
+
+ // Prepare memory buffers.
+ let buf0: Pin<&mut [u8]> = pin!([0u8; 512]);
+ let buf1: Pin<&mut [u8]> = pin!([1u8; 512]);
+
+ // Construct a `ScatterList` instance. And assert its attributes.
+ let mut sgl: Pin<Box<ScatterList<'_>>> = Box::pin_init(ScatterList::new(&buf0))?;
+ assert_eq!(sgl.length(), 512);
+ assert_eq!(sgl.is_dma_bus_address(), false);
+ assert_eq!(sgl.dma_address(), 0);
+ assert_eq!(sgl.dma_length(), 0);
+ assert_eq!(sgl.is_chain(), false);
+ assert_eq!(sgl.is_last(), true);
+ assert_eq!(sgl.count(), 1);
+
+ // Get an immutable reference to the memory buffer.
+ assert_eq!(sgl.get_buf(), [0u8; 512]);
+
+ // Reset the memory buffer.
+ sgl.set_buf(&buf1);
+ assert_eq!(sgl.get_buf(), [1u8; 512]);
+
+ // Get a mutable reference to memory buffer.
+ sgl.get_mut_buf().fill(2);
+ assert_eq!(sgl.get_buf(), [2u8; 512]);
+
+ Ok(Pass)
+}
+
+impl kernel::Module for RustSelftests {
+ fn init(_module: &'static ThisModule) -> Result<Self> {
+ pr_info!("Rust self tests (init)\n");
+
+ do_tests! {
+ test_sgtable,
+ test_scatterlist
+ };
+
+ Ok(RustSelftests)
+ }
+}
+
+impl Drop for RustSelftests {
+ fn drop(&mut self) {
+ pr_info!("Rust self tests (exit)\n");
+ }
+}
--
2.40.1
On Tue, May 30, 2023 at 8:54 AM Qingsong Chen <changxian.cqs@antgroup.com> wrote: > > Add a selftest module to provide a temporary place to put "pure tests" > for Rust funtionality and wrappers. Thanks for the patch series! Quick note on this: the `selftest` sample module was a temporary place we had in the `rust` branch to put some non-doctest tests until we had a better way to run them. Even if we wanted to keep this approach for tests like this, this part should be in its own patch -- that way you can credit Boqun properly and avoid adding his SoB on code he did not write (which he may not agree with). Cheers, Miguel
On Tue, May 30, 2023 at 02:48:21PM +0800, Qingsong Chen wrote: > Add a selftest module to provide a temporary place to put "pure tests" > for Rust funtionality and wrappers. Is this for in-kernel tests, or userspace tests? If userspace, you should follow the proper test reporting protocol the rest of the kernel uses. If in-kernel, it should follow the format that the in-kernel test currently has to be consistent. From what I could tell here, you aren't following either, but I might be totally wrong. thanks, greg k-h
On Tue, May 30, 2023 at 08:31:26AM +0100, Greg KH wrote: > On Tue, May 30, 2023 at 02:48:21PM +0800, Qingsong Chen wrote: > > Add a selftest module to provide a temporary place to put "pure tests" > > for Rust funtionality and wrappers. > > Is this for in-kernel tests, or userspace tests? If userspace, you > should follow the proper test reporting protocol the rest of the kernel > uses. If in-kernel, it should follow the format that the in-kernel test > currently has to be consistent. From what I could tell here, you aren't > following either, but I might be totally wrong. > It is for in-kernel tests, and you're right, we should follow the other in-kernel test format. Some explanation about the background: when I was working on this little "test framework", the Github CI of Rust-for-Linux (ab)used sample/rust/ for testing, that's why it was put there. Now my understanding is that Rust KUnit support is coming, so we should use kunit tests if possible. Despite where the tests are put, I'm personally happy that this patchset comes up with some tests, which help the review ;-) Regards, Boqun > thanks, > > greg k-h
On 5/31/23 2:33 AM, Boqun Feng wrote: > On Tue, May 30, 2023 at 08:31:26AM +0100, Greg KH wrote: >> On Tue, May 30, 2023 at 02:48:21PM +0800, Qingsong Chen wrote: >>> Add a selftest module to provide a temporary place to put "pure tests" >>> for Rust funtionality and wrappers. >> Is this for in-kernel tests, or userspace tests? If userspace, you >> should follow the proper test reporting protocol the rest of the kernel >> uses. If in-kernel, it should follow the format that the in-kernel test >> currently has to be consistent. From what I could tell here, you aren't >> following either, but I might be totally wrong. >> > It is for in-kernel tests, and you're right, we should follow the other > in-kernel test format. > > Some explanation about the background: when I was working on this little > "test framework", the Github CI of Rust-for-Linux (ab)used sample/rust/ > for testing, that's why it was put there. > > Now my understanding is that Rust KUnit support is coming, so we should > use kunit tests if possible. Maybe I should place those use cases in the doc ( `Examples` section) and remove this commit in next version. Thanks.
On Wed, May 31, 2023 at 7:35 AM Qingsong Chen <changxian.cqs@antgroup.com> wrote: > > Maybe I should place those use cases in the doc ( `Examples` section) > and remove this commit If you think they would help readers of the docs, then that is definitely the best approach, because they double as tests and docs at the same time. Cheers, Miguel
© 2016 - 2026 Red Hat, Inc.