KUnit 架构¶
KUnit 架构分为两部分
内核内测试框架¶
内核测试库使用 KUnit 支持用 C 编写的 KUnit 测试。这些 KUnit 测试是内核代码。KUnit 执行以下任务
组织测试
报告测试结果
提供测试实用程序
测试用例¶
测试用例是 KUnit 中的基本单元。KUnit 测试用例被组织成套件。KUnit 测试用例是具有类型签名 void (*)(struct kunit *test)
的函数。这些测试用例函数被封装在一个名为 struct kunit_case
的结构体中。
每个 KUnit 测试用例都接收一个 struct kunit
上下文对象,用于跟踪正在运行的测试。KUnit 断言宏和其他 KUnit 实用程序使用 struct kunit
上下文对象。作为例外,有两个字段
->priv
:设置函数可以使用它来存储任意测试用户数据。->param_value
:它包含参数值,可以在参数化测试中检索。
测试套件¶
KUnit 套件包括一组测试用例。KUnit 套件由 struct kunit_suite
表示。例如
static struct kunit_case example_test_cases[] = {
KUNIT_CASE(example_test_foo),
KUNIT_CASE(example_test_bar),
KUNIT_CASE(example_test_baz),
{}
};
static struct kunit_suite example_test_suite = {
.name = "example",
.init = example_test_init,
.exit = example_test_exit,
.test_cases = example_test_cases,
};
kunit_test_suite(example_test_suite);
在上面的示例中,测试套件 example_test_suite
运行测试用例 example_test_foo
、example_test_bar
和 example_test_baz
。在运行测试之前,会调用 example_test_init
,在运行测试之后,会调用 example_test_exit
。kunit_test_suite(example_test_suite)
将测试套件注册到 KUnit 测试框架。
执行器¶
KUnit 执行器可以在启动时列出并运行内置的 KUnit 测试。测试套件存储在名为 .kunit_test_suites
的链接器节中。有关代码,请参见 include/asm-generic/vmlinux.lds.h 中的 KUNIT_TABLE()
宏定义。链接器节由指向 struct kunit_suite
的指针数组组成,并通过 kunit_test_suites()
宏填充。KUnit 执行器迭代链接器节数组,以便运行编译到内核中的所有测试。
在内核启动时,KUnit 执行器使用此节的起始和结束地址来迭代并运行所有测试。有关执行器的实现,请参见 lib/kunit/executor.c。当作为模块构建时,kunit_test_suites()
宏会定义一个 module_init()
函数,该函数运行编译单元中的所有测试,而不是利用执行器。
在 KUnit 测试中,一些错误类不会影响其他测试或内核的其他部分,每个 KUnit 用例都在单独的线程上下文中执行。请参见 lib/kunit/try-catch.c 中的 kunit_try_catch_run()
函数。
断言宏¶
KUnit 测试使用期望/断言来验证状态。所有期望/断言的格式如下:KUNIT_{EXPECT|ASSERT}_<op>[_MSG](kunit, 属性[, 消息])
{EXPECT|ASSERT}
确定检查是断言还是期望。如果发生故障,测试流程会有所不同,如下所示对于期望,测试被标记为失败,并且会记录失败。
另一方面,失败的断言会导致测试用例立即终止。
断言调用以下函数:
void __noreturn __kunit_abort(struct kunit *)
。__kunit_abort
调用以下函数:void __noreturn kunit_try_catch_throw(struct kunit_try_catch *try_catch)
。kunit_try_catch_throw
调用以下函数:void kthread_complete_and_exit(struct completion *, long) __noreturn;
并终止特殊的线程上下文。
<op>
表示带有选项的检查:TRUE
(提供的属性具有布尔值 “true”)、EQ
(两个提供的属性相等)、NOT_ERR_OR_NULL
(提供的指针不为空,并且不包含 “err” 值)。[_MSG]
在失败时打印自定义消息。
测试结果报告¶
KUnit 以 KTAP 格式打印测试结果。KTAP 基于 TAP14,请参见 内核测试协议 (KTAP),版本 1。KTAP 与 KUnit 和 Kselftest 一起使用。KUnit 执行器将 KTAP 结果打印到 dmesg 和 debugfs (如果已配置)。
参数化测试¶
每个 KUnit 参数化测试都与一组参数相关联。该测试会多次调用,每个参数值调用一次,并且该参数存储在 param_value
字段中。测试用例包括一个 KUNIT_CASE_PARAM()
宏,它接受一个生成器函数。生成器函数会传递前一个参数并返回下一个参数。它还包括一个用于生成基于数组的通用用例生成器的宏。
kunit_tool (命令行测试工具)¶
kunit_tool
是一个 Python 脚本,位于 tools/testing/kunit/kunit.py
中。它用于配置、构建、执行、解析测试结果并按正确的顺序运行所有先前的命令(即,配置、构建、执行和解析)。您有两个选项来运行 KUnit 测试:要么启用 KUnit 构建内核并手动解析结果(请参见 不使用 kunit_tool 运行测试),要么使用 kunit_tool
(请参见 使用 kunit_tool 运行测试)。
configure
命令从.kunitconfig
文件(以及任何特定于架构的选项)生成内核.config
文件。qemu_configs
文件夹中提供的 Python 脚本(例如,tools/testing/kunit/qemu configs/powerpc.py
)包含特定架构的附加配置选项。它会解析现有的.config
和.kunitconfig
文件,以确保.config
是.kunitconfig
的超集。如果不是,它将合并这两个文件并运行make olddefconfig
来重新生成.config
文件。然后它会检查.config
是否已成为超集。这验证了所有 Kconfig 依赖项是否在.kunitconfig
文件中正确指定。kunit_config.py
脚本包含用于解析 Kconfig 的代码。运行make olddefconfig
的代码是kunit_kernel.py
脚本的一部分。您可以通过以下命令调用此命令:./tools/testing/kunit/kunit.py config
并生成一个.config
文件。build
使用所需的选项(取决于架构和一些选项,例如:build_dir)在内核树上运行make
并报告任何错误。要从当前的.config
构建 KUnit 内核,您可以使用build
参数:./tools/testing/kunit/kunit.py build
。exec
命令直接(使用用户模式 Linux 配置)或通过 QEMU 等模拟器执行内核结果。它使用标准输出(stdout)从日志中读取结果,并将它们传递给parse
进行解析。如果您已经构建了一个包含内置 KUnit 测试的内核,则可以使用exec
参数运行内核并显示测试结果:./tools/testing/kunit/kunit.py exec
。parse
从内核日志中提取 KTAP 输出,解析测试结果,并打印摘要。对于失败的测试,将包含任何诊断输出。