| 1 | |
| 2 |
|
| 3 | Testing
|
| 4 | =======
|
| 5 |
|
| 6 | This document contains useful information how to test the Rust code in the
|
| 7 | kernel.
|
| 8 |
|
| 9 | There are three sorts of tests:
|
| 10 |
|
| 11 | - The KUnit tests.
|
| 12 | - The ``#[test]`` tests.
|
| 13 | - The Kselftests.
|
| 14 |
|
| 15 | The KUnit tests
|
| 16 | ---------------
|
| 17 |
|
| 18 | These are the tests that come from the examples in the Rust documentation. They
|
| 19 | get transformed into KUnit tests.
|
| 20 |
|
| 21 | Usage
|
| 22 | *****
|
| 23 |
|
| 24 | These tests can be run via KUnit. For example via ``kunit_tool`` (``kunit.py``)
|
| 25 | on the command line::
|
| 26 |
|
| 27 | ./tools/testing/kunit/kunit.py run --make_options LLVM=1 --arch x86_64 --kconfig_add CONFIG_RUST=y
|
| 28 |
|
| 29 | Alternatively, KUnit can run them as kernel built-in at boot. Refer to
|
| 30 | Documentation/dev-tools/kunit/index.rst for the general KUnit documentation
|
| 31 | and Documentation/dev-tools/kunit/architecture.rst for the details of kernel
|
| 32 | built-in vs. command line testing.
|
| 33 |
|
| 34 | To use these KUnit doctests, the following must be enabled::
|
| 35 |
|
| 36 | CONFIG_KUNIT
|
| 37 | Kernel hacking -> Kernel Testing and Coverage -> KUnit - Enable support for unit tests
|
| 38 | CONFIG_RUST_KERNEL_DOCTESTS
|
| 39 | Kernel hacking -> Rust hacking -> Doctests for the `kernel` crate
|
| 40 |
|
| 41 | in the kernel config system.
|
| 42 |
|
| 43 | KUnit tests are documentation tests
|
| 44 | ***********************************
|
| 45 |
|
| 46 | These documentation tests are typically examples of usage of any item (e.g.
|
| 47 | function, struct, module...).
|
| 48 |
|
| 49 | They are very convenient because they are just written alongside the
|
| 50 | documentation. For instance:
|
| 51 |
|
| 52 | .. code-block:: rust
|
| 53 |
|
| 54 | /// Sums two numbers.
|
| 55 | ///
|
| 56 | /// ```
|
| 57 | /// assert_eq!(mymod::f(10, 20), 30);
|
| 58 | /// ```
|
| 59 | pub fn f(a: i32, b: i32) -> i32 {
|
| 60 | a + b
|
| 61 | }
|
| 62 |
|
| 63 | In userspace, the tests are collected and run via ``rustdoc``. Using the tool
|
| 64 | as-is would be useful already, since it allows verifying that examples compile
|
| 65 | (thus enforcing they are kept in sync with the code they document) and as well
|
| 66 | as running those that do not depend on in-kernel APIs.
|
| 67 |
|
| 68 | For the kernel, however, these tests get transformed into KUnit test suites.
|
| 69 | This means that doctests get compiled as Rust kernel objects, allowing them to
|
| 70 | run against a built kernel.
|
| 71 |
|
| 72 | A benefit of this KUnit integration is that Rust doctests get to reuse existing
|
| 73 | testing facilities. For instance, the kernel log would look like::
|
| 74 |
|
| 75 | KTAP version 1
|
| 76 | 1..1
|
| 77 | KTAP version 1
|
| 78 | # Subtest: rust_doctests_kernel
|
| 79 | 1..59
|
| 80 | # rust_doctest_kernel_build_assert_rs_0.location: rust/kernel/build_assert.rs:13
|
| 81 | ok 1 rust_doctest_kernel_build_assert_rs_0
|
| 82 | # rust_doctest_kernel_build_assert_rs_1.location: rust/kernel/build_assert.rs:56
|
| 83 | ok 2 rust_doctest_kernel_build_assert_rs_1
|
| 84 | # rust_doctest_kernel_init_rs_0.location: rust/kernel/init.rs:122
|
| 85 | ok 3 rust_doctest_kernel_init_rs_0
|
| 86 | ...
|
| 87 | # rust_doctest_kernel_types_rs_2.location: rust/kernel/types.rs:150
|
| 88 | ok 59 rust_doctest_kernel_types_rs_2
|
| 89 | # rust_doctests_kernel: pass:59 fail:0 skip:0 total:59
|
| 90 | # Totals: pass:59 fail:0 skip:0 total:59
|
| 91 | ok 1 rust_doctests_kernel
|
| 92 |
|
| 93 | Tests using the `? <https://doc.rust-lang.org/reference/expressions/operator-expr.html#the-question-mark-operator>`_
|
| 94 | operator are also supported as usual, e.g.:
|
| 95 |
|
| 96 | .. code-block:: rust
|
| 97 |
|
| 98 | /// ```
|
| 99 | /// # use kernel::{spawn_work_item, workqueue};
|
| 100 | /// spawn_work_item!(workqueue::system(), || pr_info!("x\n"))?;
|
| 101 | /// # Ok::<(), Error>(())
|
| 102 | /// ```
|
| 103 |
|
| 104 | The tests are also compiled with Clippy under ``CLIPPY=1``, just like normal
|
| 105 | code, thus also benefitting from extra linting.
|
| 106 |
|
| 107 | In order for developers to easily see which line of doctest code caused a
|
| 108 | failure, a KTAP diagnostic line is printed to the log. This contains the
|
| 109 | location (file and line) of the original test (i.e. instead of the location in
|
| 110 | the generated Rust file)::
|
| 111 |
|
| 112 | # rust_doctest_kernel_types_rs_2.location: rust/kernel/types.rs:150
|
| 113 |
|
| 114 | Rust tests appear to assert using the usual ``assert!`` and ``assert_eq!``
|
| 115 | macros from the Rust standard library (``core``). We provide a custom version
|
| 116 | that forwards the call to KUnit instead. Importantly, these macros do not
|
| 117 | require passing context, unlike those for KUnit testing (i.e.
|
| 118 | ``struct kunit *``). This makes them easier to use, and readers of the
|
| 119 | documentation do not need to care about which testing framework is used. In
|
| 120 | addition, it may allow us to test third-party code more easily in the future.
|
| 121 |
|
| 122 | A current limitation is that KUnit does not support assertions in other tasks.
|
| 123 | Thus, we presently simply print an error to the kernel log if an assertion
|
| 124 | actually failed. Additionally, doctests are not run for nonpublic functions.
|
| 125 |
|
| 126 | Since these tests are examples, i.e. they are part of the documentation, they
|
| 127 | should generally be written like "real code". Thus, for example, instead of
|
| 128 | using ``unwrap()`` or ``expect()``, use the ``?`` operator. For more background,
|
| 129 | please see:
|
| 130 |
|
| 131 | https://rust.docs.kernel.org/kernel/error/type.Result.html#error-codes-in-c-and-rust
|
| 132 |
|
| 133 | The ``#[test]`` tests
|
| 134 | ---------------------
|
| 135 |
|
| 136 | Additionally, there are the ``#[test]`` tests. Like for documentation tests,
|
| 137 | these are also fairly similar to what you would expect from userspace, and they
|
| 138 | are also mapped to KUnit.
|
| 139 |
|
| 140 | These tests are introduced by the ``kunit_tests`` procedural macro, which takes
|
| 141 | the name of the test suite as an argument.
|
| 142 |
|
| 143 | Each test suite should be guarded by a Kconfig option in
|
| 144 | ``rust/kernel/Kconfig.test``.
|
| 145 |
|
| 146 | For instance, assume we want to test the function ``f`` from the documentation
|
| 147 | tests section. We could write, in the same file where we have our function:
|
| 148 |
|
| 149 | .. code-block:: rust
|
| 150 |
|
| 151 | #[cfg(CONFIG_RUST_MYMOD_KUNIT_TEST)]
|
| 152 | #[kunit_tests(rust_kernel_mymod)]
|
| 153 | mod tests {
|
| 154 | use super::*;
|
| 155 |
|
| 156 | #[test]
|
| 157 | fn test_f() {
|
| 158 | assert_eq!(f(10, 20), 30);
|
| 159 | }
|
| 160 | }
|
| 161 |
|
| 162 | And if we run it, the kernel log would look like::
|
| 163 |
|
| 164 | KTAP version 1
|
| 165 | # Subtest: rust_kernel_mymod
|
| 166 | # speed: normal
|
| 167 | 1..1
|
| 168 | # test_f.speed: normal
|
| 169 | ok 1 test_f
|
| 170 | ok 1 rust_kernel_mymod
|
| 171 |
|
| 172 | Like documentation tests, the ``assert!`` and ``assert_eq!`` macros are mapped
|
| 173 | back to KUnit and do not panic. Similarly, the
|
| 174 | `? <https://doc.rust-lang.org/reference/expressions/operator-expr.html#the-question-mark-operator>`_
|
| 175 | operator is supported, i.e. the test functions may return either nothing (i.e.
|
| 176 | the unit type ``()``) or ``Result`` (i.e. any ``Result<T, E>``). For instance:
|
| 177 |
|
| 178 | .. code-block:: rust
|
| 179 |
|
| 180 | #[cfg(CONFIG_RUST_MYMOD_KUNIT_TEST)]
|
| 181 | #[kunit_tests(rust_kernel_mymod)]
|
| 182 | mod tests {
|
| 183 | use super::*;
|
| 184 |
|
| 185 | #[test]
|
| 186 | fn test_g() -> Result {
|
| 187 | let x = g()?;
|
| 188 | assert_eq!(x, 30);
|
| 189 | Ok(())
|
| 190 | }
|
| 191 | }
|
| 192 |
|
| 193 | If we run the test and the call to ``g`` fails, then the kernel log would show::
|
| 194 |
|
| 195 | KTAP version 1
|
| 196 | # Subtest: rust_kernel_mymod
|
| 197 | # speed: normal
|
| 198 | 1..1
|
| 199 | # test_g: ASSERTION FAILED at rust/kernel/lib.rs:335
|
| 200 | Expected is_test_result_ok(test_g()) to be true, but is false
|
| 201 | # test_g.speed: normal
|
| 202 | not ok 1 test_g
|
| 203 | not ok 1 rust_kernel_mymod
|
| 204 |
|
| 205 | If a ``#[test]`` test could be useful as an example for the user, then please
|
| 206 | use a documentation test instead. Even edge cases of an API, e.g. error or
|
| 207 | boundary cases, can be interesting to show in examples.
|
| 208 |
|
| 209 | The ``rusttest`` host tests
|
| 210 | ---------------------------
|
| 211 |
|
| 212 | These are userspace tests that can be built and run in the host (i.e. the one
|
| 213 | that performs the kernel build) using the ``rusttest`` Make target::
|
| 214 |
|
| 215 | make LLVM=1 rusttest
|
| 216 |
|
| 217 | This requires the kernel ``.config``.
|
| 218 |
|
| 219 | Currently, they are mostly used for testing the ``macros`` crate's examples.
|
| 220 |
|
| 221 | The Kselftests
|
| 222 | --------------
|
| 223 |
|
| 224 | Kselftests are also available in the ``tools/testing/selftests/rust`` folder.
|
| 225 |
|
| 226 | The kernel config options required for the tests are listed in the
|
| 227 | ``tools/testing/selftests/rust/config`` file and can be included with the aid
|
| 228 | of the ``merge_config.sh`` script::
|
| 229 |
|
| 230 | ./scripts/kconfig/merge_config.sh .config tools/testing/selftests/rust/config
|
| 231 |
|
| 232 | The kselftests are built within the kernel source tree and are intended to
|
| 233 | be executed on a system that is running the same kernel.
|
| 234 |
|
| 235 | Once a kernel matching the source tree has been installed and booted, the
|
| 236 | tests can be compiled and executed using the following command::
|
| 237 |
|
| 238 | make TARGETS="rust" kselftest
|
| 239 |
|
| 240 | Refer to Documentation/dev-tools/kselftest.rst for the general Kselftest
|
| 241 | documentation.
|