On Sat, Feb 15, 2025 at 4:03 AM David Gow <david...@google.com> wrote: > > On Fri, 14 Feb 2025 at 22:41, Tamir Duberstein <tam...@gmail.com> wrote: > > > > On Fri, Feb 14, 2025 at 2:42 AM David Gow <david...@google.com> wrote: > > > > > > From: José Expósito <jose.exposit...@gmail.com> > > > > > > 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 <jose.exposit...@gmail.com> > > > Co-developed-by: David Gow <david...@google.com> > > > Signed-off-by: David Gow <david...@google.com> > > > Co-developed-by: Miguel Ojeda <oj...@kernel.org> > > > Signed-off-by: Miguel Ojeda <oj...@kernel.org> > > > --- > > > > > > Changes since v5: > > > https://lore.kernel.org/all/20241213081035.2069066-4-david...@google.com/ > > > - Greatly improved documentation, which is both clearer and better > > > matches the rustdoc norm. (Thanks, Miguel) > > > - The examples and safety comments are also both more idiomatic an > > > cleaner. (Thanks, Miguel) > > > - More things sit appropriately behind CONFIG_KUNIT (Thanks, Miguel) > > > > > > Changes since v4: > > > https://lore.kernel.org/linux-kselftest/20241101064505.3820737-4-david...@google.com/ > > > - Rebased against 6.13-rc1 > > > - Fix some missing safety comments, and remove some unneeded 'unsafe' > > > blocks. (Thanks Boqun) > > > > > > 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 | 66 ++++++++++++++++++++++++++++++++++++++++++++ > > > 1 file changed, 66 insertions(+) > > > > > > diff --git a/rust/kernel/kunit.rs b/rust/kernel/kunit.rs > > > index 9e27b74a605b..3aad7a281b6d 100644 > > > --- a/rust/kernel/kunit.rs > > > +++ b/rust/kernel/kunit.rs > > > @@ -286,11 +286,77 @@ macro_rules! kunit_unsafe_test_suite { > > > }; > > > } > > > > > > +/// Returns whether we are currently running a KUnit test. > > > +/// > > > +/// In some cases, you need to call test-only code from outside the test > > > case, for example, to > > > +/// create a function mock. This function allows to change behavior > > > depending on 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() { > > > +/// return 100; > > > +/// } > > > +/// > > > +/// 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: > > > > [`bindings`] here, please. There are two more instances below but > > those aren't doc comments, so I don't think bracketing them will do > > anything. > > > > Done in v7. Alas, I'll have to keep getting used to the differences > between kerneldoc and rustdoc... > > > > +/// > > > +/// ``` > > > +/// // Import our mock naming it as the real module. > > > +/// #[cfg(CONFIG_KUNIT)] > > > +/// use bindings_mock_example as bindings; > > > +/// #[cfg(not(CONFIG_KUNIT))] > > > +/// use kernel::bindings; > > > +/// > > > +/// // This module mocks `bindings`. > > > +/// #[cfg(CONFIG_KUNIT)] > > > +/// mod bindings_mock_example { > > > +/// /// Mock `ktime_get_boot_fast_ns` to return a well-known value > > > when running a KUnit test. > > > +/// pub(crate) fn ktime_get_boot_fast_ns() -> u64 { > > > +/// 1234 > > > +/// } > > > +/// } > > > +/// > > > +/// // This is the function we want to test. Since `bindings` has been > > > mocked, we can use its > > > +/// // functions seamlessly. > > > +/// fn get_boot_ns() -> u64 { > > > +/// // SAFETY: `ktime_get_boot_fast_ns()` is always safe to call. > > > +/// unsafe { bindings::ktime_get_boot_fast_ns() } > > > +/// } > > > +/// > > > +/// let time = get_boot_ns(); > > > +/// assert_eq!(time, 1234); > > > +/// ``` > > > > Isn't this swapping out the bindings module at compile time, and for > > the whole build? In other words cfg(CONFIG_KUNIT) will apply to all > > code, both test and non-test. > > > > I believe so, so this is probably something best done only in test files.
Why would you need conditional compilation of this kind in test files? What I was getting at with this comment is that this example might mislead a user to think that this is how they should imbue their code with test-specific behavior, which is not what this will do. Instead this would break kernels built with CONFIG_KUNIT, which I think is not where we want to be going. Right? > > Ideally, we'd have support for something like the KUnit function > mocking features here, but that's horribly C-specific at the moment. > > > > +pub fn in_kunit_test() -> bool { > > > + // SAFETY: `kunit_get_current_test()` is always safe to call (it has > > > fallbacks for > > > + // when KUnit is not enabled). > > > + unsafe { !bindings::kunit_get_current_test().is_null() } > > > > Nit if you care about reducing unsafe blocks: > > > > !unsafe { bindings::kunit_get_current_test() }.is_null() > > > > > > Huh, I thought this wouldn't work, but it's working fine for me here, > so I've made the change. > > Thanks! > > > > +} > > > + > > > #[kunit_tests(rust_kernel_kunit)] > > > mod tests { > > > + use super::*; > > > + > > > #[test] > > > fn rust_test_kunit_example_test() { > > > #![expect(clippy::eq_op)] > > > assert_eq!(1 + 1, 2); > > > } > > > + > > > + #[test] > > > + fn rust_test_kunit_in_kunit_test() { > > > + assert!(in_kunit_test()); > > > + } > > > } > > > -- > > > 2.48.1.601.g30ceb7b040-goog > > > > > > > > Thanks a lot, these should be fixed in v7. > > Cheers, > -- David