测试

本文档包含有关如何在内核中测试 Rust 代码的有用信息。

共有三种测试

  • KUnit 测试。

  • #[test] 测试。

  • Kselftests。

KUnit 测试

这些测试来自 Rust 文档中的示例。它们被转换为 KUnit 测试。

用法

这些测试可以通过 KUnit 运行。例如,通过命令行上的 kunit_tool (kunit.py)

./tools/testing/kunit/kunit.py run --make_options LLVM=1 --arch x86_64 --kconfig_add CONFIG_RUST=y

或者,KUnit 可以在启动时将它们作为内核内置运行。有关常规 KUnit 文档,请参阅 KUnit - Linux 内核单元测试,有关内核内置与命令行测试的详细信息,请参阅 KUnit 架构

要使用这些 KUnit doctest,必须在内核配置系统中启用以下内容

CONFIG_KUNIT
   Kernel hacking -> Kernel Testing and Coverage -> KUnit - Enable support for unit tests
CONFIG_RUST_KERNEL_DOCTESTS
   Kernel hacking -> Rust hacking -> Doctests for the `kernel` crate

KUnit 测试是文档测试

这些文档测试通常是任何项目(例如函数、结构、模块等)的用法示例。

它们非常方便,因为它们只是与文档一起编写的。例如

/// Sums two numbers.
///
/// ```
/// assert_eq!(mymod::f(10, 20), 30);
/// ```
pub fn f(a: i32, b: i32) -> i32 {
    a + b
}

在用户空间中,测试是通过 rustdoc 收集和运行的。按原样使用该工具已经很有用,因为它允许验证示例是否编译(从而强制它们与它们记录的代码保持同步)以及运行那些不依赖于内核 API 的示例。

但是,对于内核,这些测试会转换为 KUnit 测试套件。这意味着 doctest 被编译为 Rust 内核对象,允许它们针对已构建的内核运行。

这种 KUnit 集成的一个好处是 Rust doctest 可以重用现有的测试工具。例如,内核日志将如下所示

KTAP version 1
1..1
    KTAP version 1
    # Subtest: rust_doctests_kernel
    1..59
    # rust_doctest_kernel_build_assert_rs_0.location: rust/kernel/build_assert.rs:13
    ok 1 rust_doctest_kernel_build_assert_rs_0
    # rust_doctest_kernel_build_assert_rs_1.location: rust/kernel/build_assert.rs:56
    ok 2 rust_doctest_kernel_build_assert_rs_1
    # rust_doctest_kernel_init_rs_0.location: rust/kernel/init.rs:122
    ok 3 rust_doctest_kernel_init_rs_0
    ...
    # rust_doctest_kernel_types_rs_2.location: rust/kernel/types.rs:150
    ok 59 rust_doctest_kernel_types_rs_2
# rust_doctests_kernel: pass:59 fail:0 skip:0 total:59
# Totals: pass:59 fail:0 skip:0 total:59
ok 1 rust_doctests_kernel

还像往常一样支持使用 ? 运算符的测试,例如

/// ```
/// # use kernel::{spawn_work_item, workqueue};
/// spawn_work_item!(workqueue::system(), || pr_info!("x"))?;
/// # Ok::<(), Error>(())
/// ```

这些测试也像普通代码一样在 CLIPPY=1 下使用 Clippy 进行编译,因此也受益于额外的 linting。

为了方便开发人员轻松查看 doctest 代码的哪一行导致了失败,KTAP 诊断行会打印到日志中。这包含原始测试的位置(文件和行)(即,而不是生成的 Rust 文件中的位置)

# rust_doctest_kernel_types_rs_2.location: rust/kernel/types.rs:150

Rust 测试似乎使用来自 Rust 标准库 (core) 的常用 assert!assert_eq! 宏进行断言。我们提供了一个自定义版本,该版本将调用转发到 KUnit。重要的是,这些宏不需要传递上下文,这与 KUnit 测试的宏不同(即 struct kunit *)。这使它们更易于使用,并且文档的读者无需关心使用哪个测试框架。此外,它可能允许我们将来更容易地测试第三方代码。

当前的限制是 KUnit 不支持在其他任务中进行断言。因此,如果断言实际上失败,我们目前只是在内核日志中打印一个错误。此外,不会为非公共函数运行 doctest。

#[test] 测试

此外,还有 #[test] 测试。这些可以使用 rusttest Make 目标运行

make LLVM=1 rusttest

这需要内核 .config。它在主机上(目前)运行 #[test] 测试,因此这些测试可以测试的内容相当有限。

Kselftests

Kselftests 也可在 tools/testing/selftests/rust 文件夹中使用。

测试所需的内核配置选项列在 tools/testing/selftests/rust/config 文件中,并且可以使用 merge_config.sh 脚本进行包含

./scripts/kconfig/merge_config.sh .config tools/testing/selftests/rust/config

kselftests 在内核源代码树中构建,旨在在运行相同内核的系统上执行。

安装并启动与源代码树匹配的内核后,可以使用以下命令编译和执行测试

make TARGETS="rust" kselftest

有关常规 Kselftest 文档,请参阅 Linux 内核自测试