入门

本页包含 kunit_tool 和 KUnit 框架的概述,介绍如何运行现有测试,然后如何编写简单的测试用例,并涵盖用户首次使用 KUnit 时遇到的常见问题。

安装依赖项

KUnit 具有与 Linux 内核相同的依赖项。只要你可以构建内核,就可以运行 KUnit。

使用 kunit_tool 运行测试

kunit_tool 是一个 Python 脚本,它配置并构建内核、运行测试并格式化测试结果。从内核存储库中,你可以运行 kunit_tool

./tools/testing/kunit/kunit.py run

注意

你可能会看到以下错误:“源代码树不干净,请运行‘make ARCH=um mrproper’”

发生这种情况是因为内部 kunit.py 在命令 make O=output/dir 中通过参数 --build_dir 指定 .kunit (默认选项) 作为构建目录。因此,在开始树外构建之前,源代码树必须干净。

admin-guide 的“内核构建目录”部分中也提到了同样的注意事项,也就是说,它必须用于所有 make 调用。好消息是,它确实可以通过运行 make ARCH=um mrproper 来解决,只是要注意这将删除当前配置和所有生成的文件。

如果一切正常,你应该看到以下内容

Configuring KUnit Kernel ...
Building KUnit Kernel ...
Starting KUnit Kernel ...

测试将通过或失败。

注意

由于它第一次构建大量源代码,因此 Building KUnit Kernel 步骤可能需要一段时间。

有关此包装器的详细信息,请参阅:使用 kunit_tool 运行测试

选择要运行的测试

默认情况下,kunit_tool 使用最少的配置运行所有可访问的测试,也就是说,对大多数 kconfig 选项使用默认值。但是,你可以通过以下方式选择要运行的测试

自定义 Kconfig

.kunitconfig 的一个很好的起点是 KUnit 默认配置。如果你尚未运行 kunit.py run,则可以通过运行以下命令来生成它

cd $PATH_TO_LINUX_REPO
tools/testing/kunit/kunit.py config
cat .kunit/.kunitconfig

注意

.kunitconfig 位于 kunit.py 使用的 --build_dir 中,默认情况下为 .kunit

在运行测试之前,kunit_tool 确保 .kunitconfig 中设置的所有配置选项都在内核 .config 中设置。如果未包含所用选项的依赖项,它将警告你。

有多种方法可以自定义配置

  1. 编辑 .kunit/.kunitconfig。该文件应包含运行所需测试所需的 kconfig 选项列表,包括其依赖项。你可能想要从 .kunitconfig 中删除 CONFIG_KUNIT_ALL_TESTS,因为它将启用许多你可能不需要的其他测试。如果需要在 UML 以外的架构上运行,请参阅 在 QEMU 上运行测试

  2. .kunit/.kunitconfig 之上启用其他 kconfig 选项。例如,要包含内核的链表测试,你可以运行

    ./tools/testing/kunit/kunit.py run \
            --kconfig_add CONFIG_LIST_KUNIT_TEST=y
    
  3. 从树中提供一个或多个 .kunitconfig 文件的路径。例如,要仅运行 FAT_FSEXT4 测试,你可以运行

    ./tools/testing/kunit/kunit.py run \
            --kunitconfig ./fs/fat/.kunitconfig \
            --kunitconfig ./fs/ext4/.kunitconfig
    
  4. 如果你更改 .kunitconfig,kunit.py 将触发 .config 文件的重建。但是你可以直接编辑 .config 文件,也可以使用 make menuconfig O=.kunit 等工具进行编辑。只要它是 .kunitconfig 的超集,kunit.py 就不会覆盖你的更改。

注意

要在找到令人满意的配置后保存 .kunitconfig

make savedefconfig O=.kunit
cp .kunit/defconfig .kunit/.kunitconfig

按名称筛选测试

如果你想要比 Kconfig 可以提供的更具体的信息,也可以通过传递 glob 过滤器(请阅读有关 manpage glob(7) 中模式的说明)来选择在启动时执行的测试。如果过滤器中存在 "."(句点),则将其解释为测试套件名称和测试用例之间的分隔符,否则,将其解释为测试套件的名称。例如,假设我们正在使用默认配置

  1. 告知测试套件的名称,例如 "kunit_executor_test",以运行它包含的每个测试用例

    ./tools/testing/kunit/kunit.py run "kunit_executor_test"
    
  2. 告知以其测试套件为前缀的测试用例的名称,例如 "example.example_simple_test",以专门运行该测试用例

    ./tools/testing/kunit/kunit.py run "example.example_simple_test"
    
  3. 使用通配符 (*?[) 运行与模式匹配的任何测试用例,例如 "*.*64*" 以在任何测试套件中运行名称中包含 "64" 的测试用例

    ./tools/testing/kunit/kunit.py run "*.*64*"
    

在没有 KUnit 包装器的情况下运行测试

如果你不想使用 KUnit 包装器(例如:你希望被测试的代码与其他系统集成,或使用不同的/不支持的架构或配置),则可以将 KUnit 包含在任何内核中,并且可以手动读出和解析结果。

注意

不应在生产环境中启用 CONFIG_KUNIT。启用 KUnit 会禁用内核地址空间布局随机化 (KASLR),并且测试可能会以不适合生产的方式影响内核的状态。

配置内核

要启用 KUnit 本身,你需要启用 CONFIG_KUNIT Kconfig 选项(在 menuconfig 中的“内核黑客/内核测试和覆盖率”下)。从那里,你可以启用任何 KUnit 测试。它们的配置选项通常以 _KUNIT_TEST 结尾。

KUnit 和 KUnit 测试可以编译为模块。模块中的测试将在模块加载时运行。

运行测试(不使用 KUnit 包装器)

构建并运行你的内核。在内核日志中,测试输出以 TAP 格式打印出来。默认情况下,只有在内置 KUnit/测试时才会发生这种情况。否则,将需要加载该模块。

注意

一些行和/或数据可能会散布在 TAP 输出中。

编写你的第一个测试

在你的内核仓库中,让我们添加一些可以测试的代码。

  1. 创建一个文件 drivers/misc/example.h,其中包含

int misc_example_add(int left, int right);
  1. 创建一个文件 drivers/misc/example.c,其中包含

#include <linux/errno.h>

#include "example.h"

int misc_example_add(int left, int right)
{
        return left + right;
}
  1. 将以下行添加到 drivers/misc/Kconfig

config MISC_EXAMPLE
        bool "My example"
  1. 将以下行添加到 drivers/misc/Makefile

obj-$(CONFIG_MISC_EXAMPLE) += example.o

现在我们准备编写测试用例了。

  1. drivers/misc/example_test.c 中添加以下测试用例

#include <kunit/test.h>
#include "example.h"

/* Define the test cases. */

static void misc_example_add_test_basic(struct kunit *test)
{
        KUNIT_EXPECT_EQ(test, 1, misc_example_add(1, 0));
        KUNIT_EXPECT_EQ(test, 2, misc_example_add(1, 1));
        KUNIT_EXPECT_EQ(test, 0, misc_example_add(-1, 1));
        KUNIT_EXPECT_EQ(test, INT_MAX, misc_example_add(0, INT_MAX));
        KUNIT_EXPECT_EQ(test, -1, misc_example_add(INT_MAX, INT_MIN));
}

static void misc_example_test_failure(struct kunit *test)
{
        KUNIT_FAIL(test, "This test never passes.");
}

static struct kunit_case misc_example_test_cases[] = {
        KUNIT_CASE(misc_example_add_test_basic),
        KUNIT_CASE(misc_example_test_failure),
        {}
};

static struct kunit_suite misc_example_test_suite = {
        .name = "misc-example",
        .test_cases = misc_example_test_cases,
};
kunit_test_suite(misc_example_test_suite);

MODULE_LICENSE("GPL");
  1. 将以下行添加到 drivers/misc/Kconfig

config MISC_EXAMPLE_TEST
        tristate "Test for my example" if !KUNIT_ALL_TESTS
        depends on MISC_EXAMPLE && KUNIT
        default KUNIT_ALL_TESTS

注意:如果你的测试不支持作为可加载模块构建(不推荐),请将 tristate 替换为 bool,并依赖于 KUNIT=y 而不是 KUNIT。

  1. 将以下行添加到 drivers/misc/Makefile

obj-$(CONFIG_MISC_EXAMPLE_TEST) += example_test.o
  1. 将以下行添加到 .kunit/.kunitconfig

CONFIG_MISC_EXAMPLE=y
CONFIG_MISC_EXAMPLE_TEST=y
  1. 运行测试

./tools/testing/kunit/kunit.py run

你应该看到以下失败

...
[16:08:57] [PASSED] misc-example:misc_example_add_test_basic
[16:08:57] [FAILED] misc-example:misc_example_test_failure
[16:08:57] EXPECTATION FAILED at drivers/misc/example-test.c:17
[16:08:57]      This test never passes.
...

恭喜!你刚刚编写了你的第一个 KUnit 测试。

下一步

如果你有兴趣使用 kunit.py 的一些更高级的功能,请查看 使用 kunit_tool 运行测试

如果你想在不使用 kunit.py 的情况下运行测试,请查看 在不使用 kunit_tool 的情况下运行测试

有关编写 KUnit 测试的更多信息(包括测试不同内容的一些常用技术),请参阅 编写测试