[PATCH 2/2] samples: rust: add `SgTable` and `ScatterList` selftests

Qingsong Chen posted 2 patches 2 years, 8 months ago
There is a newer version of this series
[PATCH 2/2] samples: rust: add `SgTable` and `ScatterList` selftests
Posted by Qingsong Chen 2 years, 8 months ago
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
Re: [PATCH 2/2] samples: rust: add `SgTable` and `ScatterList` selftests
Posted by Miguel Ojeda 2 years, 8 months ago
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
Re: [PATCH 2/2] samples: rust: add `SgTable` and `ScatterList` selftests
Posted by Greg KH 2 years, 8 months ago
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
Re: [PATCH 2/2] samples: rust: add `SgTable` and `ScatterList` selftests
Posted by Boqun Feng 2 years, 8 months ago
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
Re: [PATCH 2/2] samples: rust: add `SgTable` and `ScatterList` selftests
Posted by Qingsong Chen 2 years, 8 months ago
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.
Re: [PATCH 2/2] samples: rust: add `SgTable` and `ScatterList` selftests
Posted by Miguel Ojeda 2 years, 8 months ago
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