Message ID | 20241101064505.3820737-4-davidgow@google.com (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | rust: kunit: Support KUnit tests with a user-space like syntax | expand |
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 >
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); + } }