From: Boqun Feng <boqun.feng@gmail.com>
To: David Gow <davidgow@google.com>
Cc: "Brendan Higgins" <brendan.higgins@linux.dev>,
"Miguel Ojeda" <ojeda@kernel.org>,
"Alex Gaynor" <alex.gaynor@gmail.com>,
"Wedson Almeida Filho" <wedsonaf@gmail.com>,
"Gary Guo" <gary@garyguo.net>,
"Benno Lossin" <benno.lossin@proton.me>,
"José Expósito" <jose.exposito89@gmail.com>,
"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com,
rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH 3/3] rust: kunit: allow to know if we are in a test
Date: Tue, 25 Jul 2023 16:34:43 -0700 [thread overview]
Message-ID: <ZMBcE8WABtx7GD2R@boqun-archlinux> (raw)
In-Reply-To: <20230720-rustbind-v1-3-c80db349e3b5@google.com>
On Thu, Jul 20, 2023 at 02:38:54PM +0800, David Gow wrote:
> From: José Expósito <jose.exposito89@gmail.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@google.com>
> Signed-off-by: José Expósito <jose.exposito89@gmail.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 };
Note here are two unsafe operations: `Task::current()` and the pointer
dereference. You can use the `current!()` macro here to avoid the first
unsafe operation here. Besides I think it'll be better if
in_kunit_test() is a safe method for `Task`? That will be easier for us
to track the usage of task_struct fields in Rust side. But I'm OK with
either way.
Regards,
Boqun
> + !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
>
prev parent reply other threads:[~2023-07-25 23:35 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-07-20 6:38 [PATCH 0/3] rust: kunit: Support KUnit tests with a user-space like syntax David Gow
2023-07-20 6:38 ` [PATCH 1/3] rust: kunit: add KUnit case and suite macros David Gow
2023-07-25 18:07 ` Boqun Feng
2023-07-20 6:38 ` [PATCH 2/3] rust: macros: add macro to easily run KUnit tests David Gow
2023-07-30 21:49 ` Boqun Feng
2023-08-01 14:44 ` Miguel Ojeda
2023-07-20 6:38 ` [PATCH 3/3] rust: kunit: allow to know if we are in a test David Gow
2023-07-25 23:34 ` Boqun Feng [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=ZMBcE8WABtx7GD2R@boqun-archlinux \
--to=boqun.feng@gmail.com \
--cc=alex.gaynor@gmail.com \
--cc=benno.lossin@proton.me \
--cc=bjorn3_gh@protonmail.com \
--cc=brendan.higgins@linux.dev \
--cc=davidgow@google.com \
--cc=gary@garyguo.net \
--cc=jose.exposito89@gmail.com \
--cc=kunit-dev@googlegroups.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=ojeda@kernel.org \
--cc=rust-for-linux@vger.kernel.org \
--cc=wedsonaf@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.