Re: [PATCH v4 3/3] rust: kunit: allow to know if we are in a test

2024-11-01 Thread Boqun Feng
On Fri, Nov 01, 2024 at 02:45:02PM +0800, David Gow wrote:
[...]
> +/// ```
> +/// // Import our mock naming it as the real module.
> +/// #[cfg(CONFIG_KUNIT)]
> +/// use bindings_mock_example as bindings;
> +///
> +/// // This module mocks `bindings`.
> +/// mod bindings_mock_example {
> +/// use kernel::kunit::in_kunit_test;
> +/// use kernel::bindings::u64_;
> +///
> +/// // Make the other binding functions available.
> +/// pub(crate) use kernel::bindings::*;
> +///
> +/// // Mock `ktime_get_boot_fast_ns` to return a well-known value when 
> running a KUnit test.
> +/// pub(crate) unsafe fn ktime_get_boot_fast_ns() -> u64_ {

Clippy complains this `unsafe` pub function doesn't have a "# Safety"
section. Actually this function is not necessarily to be `unsafe`.

> +/// if in_kunit_test() {
> +/// 1234
> +/// } else {
> +/// unsafe { kernel::bindings::ktime_get_boot_fast_ns() }

Need safety comments here,

> +/// }
> +/// }
> +/// }
> +///
> +/// // This is the function we want to test. Since `bindings` has been 
> mocked, we can use its
> +/// // functions seamlessly.
> +/// fn get_boot_ns() -> u64 {
> +/// unsafe { bindings::ktime_get_boot_fast_ns() }

and here. If you make ktime_get_boot_fast_ns() safe, then no unsafe
block is needed here.

Regards,
Boqun

> +/// }
> +///
> +/// let time = get_boot_ns();
> +/// assert_eq!(time, 1234);
> +/// ```
> +pub fn in_kunit_test() -> bool {
> +// SAFETY: kunit_get_current_test() is always safe to call from C (it 
> has fallbacks for
> +// when KUnit is not enabled), and we're only comparing the result to 
> NULL.
> +unsafe { !bindings::kunit_get_current_test().is_null() }
> +}
> +
>  #[kunit_tests(rust_kernel_kunit)]
>  mod tests {
> +use super::*;
> +
>  #[test]
>  fn rust_test_kunit_example_test() {
>  assert_eq!(1 + 1, 2);
>  }
> +
> +#[test]
> +fn rust_test_kunit_in_kunit_test() {
> +let in_kunit = in_kunit_test();
> +assert!(in_kunit);
> +}
>  }
> -- 
> 2.47.0.199.ga7371fff76-goog
> 



[PATCH v4 3/3] rust: kunit: allow to know if we are in a test

2024-10-31 Thread David Gow
From: José Expósito 

In some cases, we need to call test-only code from outside the test
case, for example, to mock a function or a module.

In order to check whether we are in a test or not, we need to test if
`CONFIG_KUNIT` is set.
Unfortunately, we cannot rely only on this condition because:
- a test could be running in another thread,
- some distros compile KUnit in production kernels, so checking at runtime
  that `current->kunit_test != NULL` is required.

Forturately, KUnit provides an optimised check in
`kunit_get_current_test()`, which checks CONFIG_KUNIT, a global static
key, and then the current thread's running KUnit test.

Add a safe wrapper function around this to know whether or not we are in
a KUnit test and examples showing how to mock a function and a module.

Signed-off-by: José Expósito 
Co-developed-by: David Gow 
Signed-off-by: David Gow 
---

Changes since v3:
https://lore.kernel.org/linux-kselftest/20241030045719.3085147-8-david...@google.com/
- The example test has been updated to no longer use assert_eq!() with
  a constant bool argument (fixes a clippy warning).

No changes since v2:
https://lore.kernel.org/linux-kselftest/20241029092422.2884505-4-david...@google.com/

Changes since v1:
https://lore.kernel.org/lkml/20230720-rustbind-v1-3-c80db349e...@google.com/
- Rebased on top of rust-next.
- Use the `kunit_get_current_test()` C function, which wasn't previously
  available, instead of rolling our own.
- (Thanks also to Boqun for suggesting a nicer way of implementing this,
  which I tried, but the `kunit_get_current_test()` version obsoleted.)

---
 rust/kernel/kunit.rs | 72 
 1 file changed, 72 insertions(+)

diff --git a/rust/kernel/kunit.rs b/rust/kernel/kunit.rs
index 71ce1d145be8..ad38d6d62446 100644
--- a/rust/kernel/kunit.rs
+++ b/rust/kernel/kunit.rs
@@ -276,10 +276,82 @@ macro_rules! kunit_unsafe_test_suite {
 };
 }
 
+/// In some cases, you need to call test-only code from outside the test case, 
for example, to
+/// create a function mock. This function can be invoked to know whether we 
are currently running a
+/// KUnit test or not.
+///
+/// # Examples
+///
+/// This example shows how a function can be mocked to return a well-known 
value while testing:
+///
+/// ```
+/// # use kernel::kunit::in_kunit_test;
+/// #
+/// fn fn_mock_example(n: i32) -> i32 {
+/// if in_kunit_test() {
+/// 100
+/// } else {
+/// n + 1
+/// }
+/// }
+///
+/// let mock_res = fn_mock_example(5);
+/// assert_eq!(mock_res, 100);
+/// ```
+///
+/// Sometimes, you don't control the code that needs to be mocked. This 
example shows how the
+/// `bindings` module can be mocked:
+///
+/// ```
+/// // Import our mock naming it as the real module.
+/// #[cfg(CONFIG_KUNIT)]
+/// use bindings_mock_example as bindings;
+///
+/// // This module mocks `bindings`.
+/// mod bindings_mock_example {
+/// use kernel::kunit::in_kunit_test;
+/// use kernel::bindings::u64_;
+///
+/// // Make the other binding functions available.
+/// pub(crate) use kernel::bindings::*;
+///
+/// // Mock `ktime_get_boot_fast_ns` to return a well-known value when 
running a KUnit test.
+/// pub(crate) unsafe fn ktime_get_boot_fast_ns() -> u64_ {
+/// if in_kunit_test() {
+/// 1234
+/// } else {
+/// unsafe { kernel::bindings::ktime_get_boot_fast_ns() }
+/// }
+/// }
+/// }
+///
+/// // This is the function we want to test. Since `bindings` has been mocked, 
we can use its
+/// // functions seamlessly.
+/// fn get_boot_ns() -> u64 {
+/// unsafe { bindings::ktime_get_boot_fast_ns() }
+/// }
+///
+/// let time = get_boot_ns();
+/// assert_eq!(time, 1234);
+/// ```
+pub fn in_kunit_test() -> bool {
+// SAFETY: kunit_get_current_test() is always safe to call from C (it has 
fallbacks for
+// when KUnit is not enabled), and we're only comparing the result to NULL.
+unsafe { !bindings::kunit_get_current_test().is_null() }
+}
+
 #[kunit_tests(rust_kernel_kunit)]
 mod tests {
+use super::*;
+
 #[test]
 fn rust_test_kunit_example_test() {
 assert_eq!(1 + 1, 2);
 }
+
+#[test]
+fn rust_test_kunit_in_kunit_test() {
+let in_kunit = in_kunit_test();
+assert!(in_kunit);
+}
 }
-- 
2.47.0.199.ga7371fff76-goog