lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20230720-rustbind-v1-3-c80db349e3b5@google.com>
Date:   Thu, 20 Jul 2023 14:38:54 +0800
From:   David Gow <davidgow@...gle.com>
To:     Brendan Higgins <brendan.higgins@...ux.dev>,
        Miguel Ojeda <ojeda@...nel.org>,
        Alex Gaynor <alex.gaynor@...il.com>,
        Wedson Almeida Filho <wedsonaf@...il.com>,
        Boqun Feng <boqun.feng@...il.com>, Gary Guo <gary@...yguo.net>,
        Benno Lossin <benno.lossin@...ton.me>
Cc:     David Gow <davidgow@...gle.com>,
        "José Expósito" <jose.exposito89@...il.com>,
        "Björn Roy Baron" <bjorn3_gh@...tonmail.com>,
        linux-kselftest@...r.kernel.org, kunit-dev@...glegroups.com,
        rust-for-linux@...r.kernel.org, linux-kernel@...r.kernel.org
Subject: [PATCH 3/3] rust: kunit: allow to know if we are in a test

From: José Expósito <jose.exposito89@...il.com>

In some cases, you 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 some
distros compile KUnit in production kernels, so checking at runtime
that `current->kunit_test != NULL` is required.

Note that the C function `kunit_get_current_test()` can not be used
because it is not present in the current Rust tree yet. Once it is
available we might want to change our Rust wrapper to use it.

This patch adds a function to know whether we are in a KUnit test or
not and examples showing how to mock a function and a module.

Reviewed-by: David Gow <davidgow@...gle.com>
Signed-off-by: José Expósito <jose.exposito89@...il.com>
---
 rust/kernel/kunit.rs | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/rust/kernel/kunit.rs b/rust/kernel/kunit.rs
index 44ea67028316..dcaac19bb108 100644
--- a/rust/kernel/kunit.rs
+++ b/rust/kernel/kunit.rs
@@ -40,6 +40,8 @@ pub fn info(args: fmt::Arguments<'_>) {
     }
 }
 
+use crate::task::Task;
+use core::ops::Deref;
 use macros::kunit_tests;
 
 /// Asserts that a boolean expression is `true` at runtime.
@@ -256,11 +258,87 @@ 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 {
+    if cfg!(CONFIG_KUNIT) {
+        // SAFETY: By the type invariant, we know that `*Task::current().deref().0` is valid.
+        let test = unsafe { (*Task::current().deref().0.get()).kunit_test };
+        !test.is_null()
+    } else {
+        false
+    }
+}
+
 #[kunit_tests(rust_kernel_kunit)]
 mod tests {
+    use super::*;
+
     #[test]
     fn rust_test_kunit_kunit_tests() {
         let running = true;
         assert_eq!(running, true);
     }
+
+    #[test]
+    fn rust_test_kunit_in_kunit_test() {
+        let in_kunit = in_kunit_test();
+        assert_eq!(in_kunit, true);
+    }
 }

-- 
2.41.0.255.g8b1d071c50-goog

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ