Skip to content

Commit 6251a35

Browse files
committed
rust: kunit: allow to know if we are in a test
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 <[email protected]> Signed-off-by: José Expósito <[email protected]>
1 parent 987c136 commit 6251a35

File tree

1 file changed

+77
-0
lines changed

1 file changed

+77
-0
lines changed

rust/kernel/kunit.rs

+77
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
//!
77
//! Reference: <https://www.kernel.org/doc/html/latest/dev-tools/kunit/index.html>
88
9+
use crate::task::Task;
10+
use core::ops::Deref;
911
use macros::kunit_tests;
1012

1113
/// Asserts that a boolean expression is `true` at runtime.
@@ -183,11 +185,86 @@ macro_rules! kunit_unsafe_test_suite {
183185
};
184186
}
185187

188+
/// In some cases, you need to call test-only code from outside the test case, for example, to
189+
/// create a function mock. This function can be invoked to know whether we are currently running a
190+
/// KUnit test or not.
191+
///
192+
/// # Examples
193+
///
194+
/// This example shows how a function can be mocked to return a well-known value while testing:
195+
///
196+
/// ```
197+
/// # use kernel::kunit::in_kunit_test;
198+
/// #
199+
/// fn fn_mock_example(n: i32) -> i32 {
200+
/// if in_kunit_test() {
201+
/// 100
202+
/// } else {
203+
/// n + 1
204+
/// }
205+
/// }
206+
///
207+
/// let mock_res = fn_mock_example(5);
208+
/// assert_eq!(mock_res, 100);
209+
/// ```
210+
///
211+
/// Sometimes, you don't control the code that needs to be mocked. This example shows how the
212+
/// `bindings` module can be mocked:
213+
///
214+
/// ```
215+
/// // Import our mock naming it as the real module.
216+
/// #[cfg(CONFIG_KUNIT)]
217+
/// use bindings_mock_example as bindings;
218+
///
219+
/// // This module mocks `bindings`.
220+
/// mod bindings_mock_example {
221+
/// use kernel::kunit::in_kunit_test;
222+
/// use kernel::bindings::u64_;
223+
///
224+
/// // Make the other binding functions available.
225+
/// pub(crate) use kernel::bindings::*;
226+
///
227+
/// // Mock `ktime_get_boot_fast_ns` to return a well-known value when running a KUnit test.
228+
/// pub(crate) unsafe fn ktime_get_boot_fast_ns() -> u64_ {
229+
/// if in_kunit_test() {
230+
/// 1234
231+
/// } else {
232+
/// unsafe { kernel::bindings::ktime_get_boot_fast_ns() }
233+
/// }
234+
/// }
235+
/// }
236+
///
237+
/// // This is the function we want to test. Since `bindings` has been mocked, we can use its
238+
/// // functions seamlessly.
239+
/// fn get_boot_ns() -> u64 {
240+
/// unsafe { bindings::ktime_get_boot_fast_ns() }
241+
/// }
242+
///
243+
/// let time = get_boot_ns();
244+
/// assert_eq!(time, 1234);
245+
/// ```
246+
pub fn in_kunit_test() -> bool {
247+
if cfg!(CONFIG_KUNIT) {
248+
let test = unsafe { (*Task::current().deref().0.get()).kunit_test };
249+
!test.is_null()
250+
} else {
251+
false
252+
}
253+
}
254+
186255
#[kunit_tests(rust_kernel_kunit)]
187256
mod tests {
257+
use super::*;
258+
188259
#[test]
189260
fn rust_test_kunit_kunit_tests() {
190261
let running = true;
191262
assert_eq!(running, true);
192263
}
264+
265+
#[test]
266+
fn rust_test_kunit_in_kunit_test() {
267+
let in_kunit = in_kunit_test();
268+
assert_eq!(in_kunit, true);
269+
}
193270
}

0 commit comments

Comments
 (0)