深入了解KUnit:Linux内核新一代单元测试工具(上)

简介: 深入了解KUnit:Linux内核新一代单元测试工具

KUnit(内核单元测试框架)为 Linux 内核中的单元测试提供了一个通用框架。使用 KUnit,您可以定义称为测试套件的测试用例组。这些测试要么在内核启动时运行(如果内置),要么作为模块加载。 KUnit 自动在内核日志中标记并报告失败的测试用例。

KUnit 测试是内核的一部分,用 C(编程)语言编写,并且测试内核实现的部分(例如:C 语言函数)。排除构建时间,从调用到完成,KUnit 可以在不到 10 秒的时间内运行大约 100 个测试。 KUnit 可以测试任何内核组件,例如:文件系统、系统调用、内存管理、设备驱动程序等。

KUnit 遵循白盒测试方法。该测试可以访问内部系统功能。 KUnit 在内核空间中运行,并且不限于暴露于用户空间的事物。

此外,KUnit 还有 kunit_tool,一个tools/testing/kunit/kunit.py配置 Linux 内核的脚本 ( ),在 QEMU 或 UML(用户模式 Linux)下运行 KUnit 测试,解析测试结果并以用户友好的方式显示它们。

特点

  • 提供编写单元测试的框架。
  • 在任何内核架构上运行测试。
  • 以毫秒为单位运行测试。

先决条件

  • 任何 Linux 内核兼容的硬件。
  • 对于测试的内核,Linux 内核版本 5.5 或更高版本。

一、KUnit 入门

1.1安装依赖项

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

使用 kunit_tool 运行测试

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

./tools/testing/kunit/kunit.py run
笔记——您可能会看到以下错误:“源代码树不干净,请运行 'make ARCH=um mrproper'”发生这种情况是因为 kunit.py 在内部通过 参数指定.kunit (默认选项)作为命令中的构建目录。因此,在开始树外构建之前,源树必须是干净的。make O=output/dir--build_dir管理指南的“构建内核目录”部分中也提到了相同的警告,即它的使用,它必须用于make.好消息是,它确实可以通过运行来解决 ,只是要注意这会删除当前配置和所有生成的文件。make ARCH=um mrproper

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

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

测试将通过或失败。

笔记——由于这是第一次构建大量源,因此该步骤可能需要一段时间。Building KUnit Kernel

选择要运行的测试

默认情况下,kunit_tool 运行使用最小配置即可到达的所有测试,即使用大多数 kconfig 选项的默认值。但是,您可以选择要运行的测试:

  • 自定义用于编译内核的Kconfig ,或者
  • 按名称过滤测试以专门选择要运行的已编译测试。

1.2自定义Kconfig

.kunitconfigKUnit 默认配置是一个很好的起点。如果您尚未运行,可以通过运行以下命令来生成它:kunit.pyrun

cd $PATH_TO_LINUX_REPO
tools/testing/kunit/kunit.py config
cat .kunit/.kunitconfig
笔记 .kunitconfig存在于 --build_dirkunit.py 使用的 .kunit默认情况下。

在运行测试之前,kunit_tool 确保.kunitconfig在 kernel 中设置所有配置选项.config。如果您没有包含所使用选项的依赖项,它会警告您。

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

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

启用额外的 kconfig 选项.kunit/.kunitconfig。例如,要包含内核的链表测试,您可以运行:

./tools/testing/kunit/kunit.py run \
        --kconfig_add CONFIG_LIST_KUNIT_TEST=y

提供树中一个或多个 .kunitconfig 文件的路径。例如,要仅运行FAT_FSEXT4测试,您可以运行:

./tools/testing/kunit/kunit.py run \
        --kunitconfig ./fs/fat/.kunitconfig \
        --kunitconfig ./fs/ext4/.kunitconfig

如果更改.kunitconfig,kunit.py 将触发文件的重建.config。但您可以.config直接编辑文件或使用.只要它是 的超集,kunit.py 就不会覆盖您的更改。makemenuconfigO=.kunit.kunitconfig

笔记——找到满意的配置后保存 .kunitconfig:make savedefconfig O=.kunit cp .kunit/defconfig .kunit/.kunitconfig

按名称过滤测试

如果您想要比 Kconfig 提供的更具体,还可以通过传递 glob 过滤器来选择在引导时执行哪些测试(阅读联机帮助页glob(7)中有关模式的说明)。如果过滤器中有"."(句点),它将被解释为测试套件名称和测试用例之间的分隔符,否则,它将被解释为测试套件名称。例如,假设我们使用默认配置:

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

./tools/testing/kunit/kunit.py run "kunit_executor_test"

通知以其测试套件为前缀的测试用例的名称,例如"example.example_simple_test",以专门运行该测试用例:

./tools/testing/kunit/kunit.py run "example.example_simple_test"

使用通配符 ( *?[) 运行与该模式匹配的任何测试用例,就像运行任何测试套件中名称中"*.*64*"包含的测试用例一样:"64"

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

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

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

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

1.3配置内核

要启用 KUnit 本身,您需要启用CONFIG_KUNITKconfig 选项(在 内核黑客/内核测试和覆盖 下 menuconfig)。从那里,您可以启用任何 KUnit 测试。它们通常具有以_KUNIT_TEST.

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

运行测试(没有 KUnit 包装器)

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

笔记一些线路和/或数据可能散布在 TAP 输出中

编写你的第一个测试

在您的内核存储库中,我们添加一些可以测试的代码。

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

2.创建一个文件drivers/misc/example.c,其中包括:

#include "example.h"
int misc_example_add(int left, int right)
{
        return left + right;
}

3.将以下行添加到drivers/misc/Kconfig:

config MISC_EXAMPLE
        bool "My example"

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

obj-$(CONFIG_MISC_EXAMPLE) += example.o

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

  1. 添加以下测试用例drivers/misc/example_test.c
#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");

2.将以下行添加到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

注意:如果您的测试不支持构建为可加载模块(不鼓励这样做),请用 bool 替换 tristate,并依赖 KUNIT=y 而不是 KUNIT。

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

obj-$(CONFIG_MISC_EXAMPLE_TEST) += example_test.o

4.将以下行添加到.kunit/.kunitconfig

CONFIG_MISC_EXAMPLE=y
CONFIG_MISC_EXAMPLE_TEST=y

5.运行测试:

./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 测试。


【文章福利】小编推荐自己的Linux内核技术交流群:【 865977150】整理了一些个人觉得比较好的学习书籍、视频资料共享在群文件里面,有需要的可以自行添加哦!!!

二、KUnit架构

KUnit架构分为两部分:

  • 内核内测试框架
  • kunit_tool(命令行测试工具)

2.1内核内测试框架

内核测试库支持使用 KUnit 以 C 语言编写的 KUnit 测试。这些 KUnit 测试是内核代码。 KUnit 执行以下任务:

  • 组织测试
  • 报告测试结果
  • 提供测试实用程序

测试用例

测试用例是KUnit 中的基本单元。 KUnit 测试用例被组织成套件。 KUnit 测试用例是具有类型签名的函数 。这些测试用例函数包装在一个名为 的结构中。void (*)(struct kunit *test)struct kunit_case

每个 KUnit 测试用例都会接收一个跟踪正在运行的测试的上下文对象。 KUnit 断言宏和其他 KUnit 实用程序使用 上下文对象。作为例外,有两个字段:struct kunitstruct kunit

  • ->priv:设置功能可以使用它来存储任意测试用户数据。
  • ->param_value:它包含可以在参数化测试中检索的参数值。

测试套件

KUnit 套件包括一组测试用例。 KUnit 套件由.例如:structkunit_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。链接器部分由指向 的指针数组组成 ,并由宏填充 。 KUnit 执行器迭代链接器节数组,以便运行编译到内核中的所有测试。struct kunit_suitekunit_test_suites()。

KUnit套件内存图

在内核启动时,KUnit 执行器使用此部分的开始和结束地址来迭代并运行所有测试。当构建为模块时,kunit_test_suites()宏定义一个module_init()函数,该函数在编译单元中运行所有测试,而不是利用执行器。

在 KUnit 测试中,某些错误类不会影响其他测试或内核部分,每个 KUnit 案例都在单独的线程上下文中执行。

断言宏

KUnit 测试使用期望/断言来验证状态。所有期望/断言的格式如下:KUNIT_{EXPECT|ASSERT}_<op>[_MSG](kunit,property[,message])

{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 与 KUnit 和 Kselftest 配合使用。 KUnit 执行器将 KTAP 结果打印到 dmesg 和 debugfs(如果已配置)。

参数化测试

每个 KUnit 参数化测试都与一组参数相关联。该测试被多次调用,每个参数值调用一次,并且参数存储在字段中param_value。测试用例包含一个KUNIT_CASE_PARAM()接受生成器函数的宏。生成器函数传递前一个参数并返回下一个参数。它还包括一个用于生成基于数组的常见情况生成器的宏。

2.2kunit_tool(命令行测试工具)

kunit_tool是一个Python脚本,可以在tools/testing/kunit/kunit.py.它用于配置、构建、执行、解析测试结果并以正确的顺序运行所有先前的命令(即配置、构建、执行和解析)。您有两种运行 KUnit 测试的选项:在启用 KUnit 的情况下构建内核并手动解析结果。

configure.config命令从 文件(以及任何特定于体系结构的选项)生成内核.kunitconfig。qemu_configs文件夹(例如)中提供的 Python 脚本包含针对特定体系结构的其他配置选项。它解析现有文件和文件以确保它是.如果没有,它将结合两者并运行以重新生成文件。然后它检查是否已成为超集。这将验证文件中是否正确指定了所有 Kconfig 依赖项。该脚本包含解析 Kconfigs 的代码。运行的代码是脚本的一部分 。

您可以通过以下方式调用该命令:

tools/testing/kunit/qemu configs/powerpc.py.config.kunitconfig.config.kunitconfigmake 
olddefconfig.config.config.kunitconfigkunit_config.pymake 
olddefconfigkunit_kernel.py./tools/testing/kunit/kunit.py config并生成一个.config文件。

buildmake使用所需选项(取决于体系结构和某些选项,例如:build_dir)在内核树上运行并报告任何错误。要从当前的 构建 KUnit 内核.config,您可以使用 build参数:。./tools/testing/kunit/kunit.py build

exec命令直接(使用用户模式 Linux 配置)或通过 QEMU 等模拟器执行内核结果。它使用标准输出 (stdout) 从日志中读取结果,并将其传递给parse进行解析。如果您已经构建了带有内置 KUnit 测试的内核,则可以使用参数运行该内核并显示测试结果exec :。./tools/testing/kunit/kunit.py exec

parse从内核日志中提取 KTAP 输出,解析测试结果并打印摘要。对于失败的测试,将包括任何诊断输出。

1)使用 kunit_tool 运行测试

我们可以使用 kunit_tool 运行 KUnit 测试,也可以手动运行测试,然后使用 kunit_tool 解析结果。要手动运行测试,请参阅:不使用 kunit_tool 运行测试。只要我们能够构建内核,我们就可以运行 KUnit。

kunit_tool 是一个 Python 脚本,用于配置和构建内核、运行测试并格式化测试结果。

运行命令:

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

我们应该看到以下内容:

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

我们可能想要使用以下选项:

./tools/testing/kunit/kunit.py run --timeout=30 --jobs=`nproc --all`
  • --timeout设置测试运行的最长时间。
  • --jobs设置构建内核的线程数。

如果不存在其他文件(在构建目录中) , kunit_tool 将生成.kunitconfig具有默认配置的文件。.kunitconfig此外,它还验证生成的.config文件是否CONFIG包含 .kunitconfig.也可以将单独的.kunitconfig片段传递给 kunit_tool。如果我们有多个不同的测试组想要独立运行,或者如果我们想要对某些子系统使用预定义的测试配置,这非常有用。

要使用不同的.kunitconfig文件(例如为测试特定子系统而提供的文件),请将其作为选项传递:

./tools/testing/kunit/kunit.py run --kunitconfig=fs/ext4/.kunitconfig

要查看 kunit_tool 标志(可选命令行参数),请运行:

./tools/testing/kunit/kunit.py run --help

创建.kunitconfig文件

如果我们想运行一组特定的测试(而不是 KUnit 中列出的测试defconfig),我们可以在 .kunitconfig文件中提供 Kconfig 选项。

A.kunitconfig是 a minconfig(通过运行生成的 .config ),用于运行一组特定的测试。该文件包含具有特定测试目标的常规内核配置。它还包含测试所需的任何其他配置选项(例如:测试中功能的依赖项、启用/禁用某些代码块的配置、架构配置等)。

make savedefconfig.kunitconfig要创建.kunitconfig,使用 KUnit defconfig:

cd $PATH_TO_LINUX_REPO
cp tools/testing/kunit/configs/default.config .kunit/.kunitconfig

然后我们可以添加任何其他 Kconfig 选项。例如:

CONFIG_LIST_KUNIT_TEST=y

kunit_tool 确保在运行测试之前.kunitconfig在内核中设置所有配置选项。.config如果我们没有包含选项依赖项,它会发出警告。

笔记从中删除某些内容 .kunitconfig不会重建.仅当不是 的子集时才会更新配置。这意味着我们可以使用其他工具(例如:)来调整其他配置选项。需要设置构建目录才能工作,因此默认情况下使用. .config file.kunitconfig.configmake menuconfigmake menuconfigmake O=.kunit menuconfig

配置、构建和运行测试

如果我们想手动更改 KUnit 构建过程,我们可以独立运行部分 KUnit 构建过程。当运行 kunit_tool 时,.kunitconfig我们可以.config使用config参数从 a 生成 a:

./tools/testing/kunit/kunit.py config

要从当前构建 KUnit 内核.config,我们可以使用以下build参数:

./tools/testing/kunit/kunit.py build

如果我们已经构建了带有内置 KUnit 测试的 UML 内核,我们可以运行该内核,并使用参数显示测试结果exec

./tools/testing/kunit/kunit.py exec

使用 kunit_tool 运行测试run部分中讨论的命令相当于按顺序运行上述三个命令。

解析测试结果

KUnit 测试输出以 TAP(测试任何协议)格式显示结果。运行测试时,kunit_tool 会解析此输出并打印摘要。要查看 TAP 格式的原始测试结果,我们可以传递参数--raw_output:

./tools/testing/kunit/kunit.py run --raw_output

parse如果我们有原始 TAP 格式的 KUnit 结果,我们可以使用kunit_tool 命令解析它们并打印人类可读的摘要。这接受参数的文件名,或者将从标准输入读取。

# Reading from a file
./tools/testing/kunit/kunit.py parse /var/log/dmesg
# Reading from stdin
dmesg | ./tools/testing/kunit/kunit.py parse

过滤测试

通过将 bash 样式的全局过滤器传递给execorrun命令,我们可以运行内核中内置的测试子集。例如:如果我们只想运行 KUnit 资源测试,请使用:

./tools/testing/kunit/kunit.py run 'kunit-resource*'

这使用带有通配符的标准 glob 格式。

在QEMU上运行测试

kunit_tool 支持在 qemu 上以及通过 UML 运行测试。要在 qemu 上运行测试,默认情况下需要两个标志:

  • --arch:选择一个配置集合(Kconfig、qemu 配置选项等),允许 KUnit 测试以最小的方式在指定的架构上运行。架构参数与传递给ARCHKbuild 使用的变量的选项名称相同。当前并非所有架构都支持此标志,但我们可以用来 --qemu_config处理它。如果um通过(或者忽略该标志),测试将通过 UML 运行。非UML架构,例如:i386、x86_64、arm等;在 qemu 上运行。
  • --cross_compile:指定 Kbuild 工具链。它传递的参数与传递给CROSS_COMPILEKbuild 使用的变量的参数相同。提醒一下,这将是 GCC 等工具链二进制文件的前缀。例如:
  • sparc64-linux-gnu如果我们的系统上安装了 sparc 工具链。
  • $HOME/toolchains/microblaze/gcc-9.2.0-nolibc/microblaze-linux/bin/microblaze-linux 如果我们已经从 0day 网站下载了 microblaze 工具链到我们的主目录中名为 toolchains 的目录中。

这意味着对于大多数架构来说,在 qemu 下运行非常简单:

./tools/testing/kunit/kunit.py run --arch=x86_64

交叉编译时,我们可能需要指定不同的工具链,例如:

./tools/testing/kunit/kunit.py run \
        --arch=s390 \
        --cross_compile=s390x-linux-gnu-

如果我们想要在该--arch标志不支持的架构上运行 KUnit 测试,或者想要使用非默认配置在 qemu 上运行 KUnit 测试;然后我们可以编写自己的``QemuConfig``。这些QemuConfigs是用 Python 编写的。它们在文件顶部有一个导入行 。该文件必须包含一个名为 的变量,该变量已为其分配了一个实例。请参阅以下示例:

from..qemu_config import QemuArchParamsQEMU_ARCHQemuArchParamstools/testing/kunit/qemu_configs/x86_64.py

一旦我们有了一个QemuConfig,我们就可以使用该--qemu_config标志将其传递到 kunit_tool 中。使用时,该标志会替换--arch标志。例如:使用tools/testing/kunit/qemu_configs/x86_64.py,调用显示为

./tools/testing/kunit/kunit.py run \
        --timeout=60 \
        --jobs=12 \
        --qemu_config=./tools/testing/kunit/qemu_configs/x86_64.py

运行命令行参数

kunit_tool 有许多其他命令行参数,它们对我们的测试环境很有用。以下是最常用的命令行参数:

--help:列出所有可用选项。要列出常用选项,请放在--help命令之前。要列出特定于该命令的选项,请放在--help该命令之后。

笔记:不同的命令( configbuildrun等)具有不同的支持选项。
  • --build_dir:指定 kunit_tool 构建目录。它包括.kunitconfig.config文件和已编译的内核。
  • --make_options:指定在编译内核时(使用buildrun命令)传递给 make 的附加选项。例如:要启用编译器警告,我们可以传递.--make_optionsW=1
  • --alltests:启用一组预定义的选项,以便构建尽可能多的测试。
笔记——已启用选项的列表可以在 中找到 tools/testing/kunit/configs/all_tests.config。如果您只想启用具有其他满足的依赖项的所有测试,请将其添加 CONFIG_KUNIT_ALL_TESTS=y到您的 .kunitconfig.
  • --kunitconfig:指定文件的路径或目录.kunitconfig 。例如:
  • lib/kunit/.kunitconfig可以是文件的路径。
  • lib/kunit可以是文件所在的目录。

该文件用于构建和运行一组预定义的测试及其依赖项。例如,为给定子系统运行测试。

  • --kconfig_add:指定要附加到.kunitconfig文件的其他配置选项。例如:
    ./tools/testing/kunit/kunit.py run --kconfig_add CONFIG_KASAN=y
  • --arch:在指定的架构上运行测试。架构参数与 Kbuild ARCH 环境变量相同。例如,i386、x86_64、arm、um 等。非 UML 架构在 qemu 上运行。默认是嗯。
  • --cross_compile:指定 Kbuild 工具链。它传递的参数与传递给CROSS_COMPILEKbuild 使用的变量的参数相同。这将是 GCC 等工具链二进制文件的前缀。例如:
  • sparc64-linux-gnu-如果我们的系统上安装了 sparc 工具链。
  • $HOME/toolchains/microblaze/gcc-9.2.0-nolibc/microblaze-linux/bin/microblaze-linux 如果我们已经从 0day 网站下载了 microblaze 工具链到我们的主目录中名为 toolchains 的指定路径。
  • --qemu_config:指定包含自定义 qemu 架构定义的文件的路径。这应该是一个包含QemuArchParams对象的 python 文件。
  • --qemu_args:指定其他 qemu 参数,例如。-smp8
  • --jobs:指定同时运行的作业(命令)数量。默认情况下,这设置为系统上的核心数。
  • --timeout:指定允许所有测试运行的最大秒数。这不包括构建测试所需的时间。
  • --kernel_args:指定附加内核命令行参数。可能会重复。
  • --run_isolated:如果设置,则为每个单独的套件/测试启动内核。这对于调试非密封测试非常有用,该测试可能根据之前运行的内容而通过/失败。
  • --raw_output:如果设置,则从内核生成未格式化的输出。可能的选项有:
  • all:要查看完整的内核输出,请使用--raw_output=all.
  • kunit:这是默认选项并过滤到 KUnit 输出。使用--raw_output--raw_output=kunit.
  • --json:如果设置,则以 JSON 格式存储测试结果并打印到stdout或保存到文件(如果指定了文件名)。
  • --filter:指定测试属性的过滤器,例如speed!=slow。通过将输入括在引号中并用逗号分隔过滤器,可以使用多个过滤器。示例:.--filter"speed>slow,module=example"
  • --filter_action:如果设置为skip,过滤后的测试将在输出中显示为已跳过,而不是不显示输出。
  • --list_tests:如果设置,则列出将运行的所有测试。
  • --list_tests_attr:如果设置,则列出将运行的所有测试及其所有属性。

2)不使用 kunit_tool 运行测试

如果我们不想使用kunit_tool(例如:我们想与其他系统集成,或者在真实硬件上运行测试),我们可以将KUnit包含在任何内核中,读出结果并手动解析。

笔记——KUnit 并非设计用于生产系统。测试可能会降低系统的稳定性或安全性。

配置内核

KUnit 测试可以在没有 kunit_tool 的情况下运行。如果满足以下条件,这可能很有用:

  • 我们有一个现有的内核配置需要测试。
  • 需要在真实硬件上运行(或者使用模拟器/VM kunit_tool 不支持)。
  • 希望与一些现有的测试系统集成。

KUnit 使用该CONFIG_KUNIT选项进行配置,并且还可以通过在我们的.config. KUnit 测试通常(但并不总是)具有以_KUNIT_TEST.大多数测试可以构建为模块,也可以构建到内核中。

笔记——我们可以启用 KUNIT_ALL_TESTS配置选项来自动启用具有满足依赖关系的所有测试。这是快速测试适用于当前配置的所有内容的好方法。

一旦我们构建了内核(和/或模块),运行测试就很简单。如果测试是内置的,它们将在内核启动时自动运行。结果将以dmesgTAP 格式写入内核日志 ( )。

如果测试构建为模块,则它们将在加载模块时运行。

# modprobe example-test

结果将以 TAP 格式显示在dmesg

笔记——如果 CONFIG_KUNIT_DEBUGFS启用,则可以从 debugfs文件系统(如果已安装)访问 KUnit 测试结果。它们将采用 /sys/kernel/debug/kunit/<test_suite>/resultsTAP 格式。

三、Kunit编写测试

3.1测试用例

KUnit 中的基本单元是测试用例。测试用例是带有签名的函数。它调用被测试的函数,然后设置对应该发生的情况的期望。例如:void(*)(structkunit*test)

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

在上面的例子中,example_test_success总是通过,因为它什么也没做;没有设定任何期望,因此所有期望都会过去。另一方面,example_test_failure总是会失败,因为它调用KUNIT_FAIL,这是一种特殊的期望,会记录消息并导致测试用例失败。

期望

期望指定我们期望一段代码在测试中执行某些操作期望像函数一样被调用。测试是通过设置对被测代码的行为的期望来进行的。当一个或多个期望失败时,测试用例就会失败并记录有关失败的信息。例如:

void add_test_basic(struct kunit *test)
{
        KUNIT_EXPECT_EQ(test, 1, add(1, 0));
        KUNIT_EXPECT_EQ(test, 2, add(1, 1));
}

在上面的示例中,add_test_basic对名为 的函数的行为做出了许多断言add。第一个参数始终为 类型 ,其中包含有关当前测试上下文的信息。在本例中,第二个参数是预期值。最后一个值是实际值。如果满足所有这些期望,则测试用例将通过;如果这些期望中的任何一个失败,测试用例就会失败。struct kunit *addadd_test_basic

当任何期望被违反时,测试用例就会失败;但是,测试将继续运行,并尝试其他期望,直到测试用例结束或以其他方式终止。这与稍后讨论的断言相反。

笔记——单个测试用例应该简短、易于理解并且专注于单个行为。

例如,如果我们想严格测试add上面的函数,请创建额外的测试用例来测试函数应add具有的每个属性,如下所示:

void add_test_basic(struct kunit *test)
{
        KUNIT_EXPECT_EQ(test, 1, add(1, 0));
        KUNIT_EXPECT_EQ(test, 2, add(1, 1));
}
void add_test_negative(struct kunit *test)
{
        KUNIT_EXPECT_EQ(test, 0, add(-1, 1));
}
void add_test_max(struct kunit *test)
{
        KUNIT_EXPECT_EQ(test, INT_MAX, add(0, INT_MAX));
        KUNIT_EXPECT_EQ(test, -1, add(INT_MAX, INT_MIN));
}
void add_test_overflow(struct kunit *test)
{
        KUNIT_EXPECT_EQ(test, INT_MIN, add(INT_MAX, 1));
}

断言

断言类似于期望,只不过如果条件不满足,断言会立即终止测试用例。例如:

static void test_sort(struct kunit *test)
{
        int *a, i, r = 1;
        a = kunit_kmalloc_array(test, TEST_LEN, sizeof(*a), GFP_KERNEL);
        KUNIT_ASSERT_NOT_ERR_OR_NULL(test, a);
        for (i = 0; i < TEST_LEN; i++) {
                r = (r * 725861) % 6599;
                a[i] = r;
        }
        sort(a, TEST_LEN, sizeof(*a), cmpint, NULL);
        for (i = 0; i < TEST_LEN-1; i++)
                KUNIT_EXPECT_LE(test, a[i], a[i + 1]);
}

在此示例中,我们需要能够分配一个数组来测试该sort()函数。因此,如果出现分配错误,我们通常KUNIT_ASSERT_NOT_ERR_OR_NULL()会中止测试。

笔记——在其他测试框架中, ASSERT宏通常通过调用来实现 return,因此它们只能在测试函数中工作。在 KUnit 中,我们在失败时停止当前的 kthread,因此您可以从任何地方调用它们。警告:上述规则有一个例外。您不应在套件的 exit() 函数或资源的 free 函数中使用断言。它们在测试关闭时运行,并且此处的断言会阻止进一步的清理代码运行,从而可能导致内存泄漏。


相关文章
|
1天前
|
存储 算法 Linux
【Linux】线程的内核级理解&&详谈页表以及虚拟地址到物理地址之间的转化
【Linux】线程的内核级理解&&详谈页表以及虚拟地址到物理地址之间的转化
|
1天前
|
安全 Linux
【Linux】详解用户态和内核态&&内核中信号被处理的时机&&sigaction信号自定义处理方法
【Linux】详解用户态和内核态&&内核中信号被处理的时机&&sigaction信号自定义处理方法
|
1天前
|
存储 Linux
【Linux】对信号产生的内核级理解
【Linux】对信号产生的内核级理解
|
1天前
|
消息中间件 算法 Linux
【Linux】对system V本地通信的内核级理解
【Linux】对system V本地通信的内核级理解
|
1天前
|
Linux 编译器 调度
xenomai内核解析--双核系统调用(二)--应用如何区分xenomai/linux系统调用或服务
本文介绍了如何将POSIX应用程序编译为在Xenomai实时内核上运行的程序。
16 1
xenomai内核解析--双核系统调用(二)--应用如何区分xenomai/linux系统调用或服务
|
1天前
|
算法 Linux 调度
xenomai内核解析--xenomai与普通linux进程之间通讯XDDP(一)--实时端socket创建流程
xenomai与普通linux进程之间通讯XDDP(一)--实时端socket创建流程
6 1
xenomai内核解析--xenomai与普通linux进程之间通讯XDDP(一)--实时端socket创建流程
|
1天前
|
Linux 调度 数据库
|
1天前
|
存储 缓存 Linux
xenomai内核解析--xenomai与普通linux进程之间通讯XDDP(三)--实时与非实时数据交互
本文介绍了Xenomai中的XDDP(Xenomai Distributed Data Protocol)通信机制,XDDP用于实时和非实时进程之间的数据交换。XDDP在Xenomai内核中涉及的数据结构和管理方式,以及创建XDDP通道后的实时端和非实时端连接过程。
7 0
xenomai内核解析--xenomai与普通linux进程之间通讯XDDP(三)--实时与非实时数据交互
|
1天前
|
消息中间件 测试技术 Linux
linux实时操作系统xenomai x86平台基准测试(benchmark)
本文是关于Xenomai实时操作系统的基准测试,旨在评估其在低端x86平台上的性能。测试模仿了VxWorks的方法,关注CPU结构、指令集等因素对系统服务耗时的影响。测试项目包括信号量、互斥量、消息队列、任务切换等,通过比较操作前后的时戳来测量耗时,并排除中断和上下文切换的干扰。测试结果显示了各项操作的最小、平均和最大耗时,为程序优化提供参考。注意,所有数据基于特定硬件环境,测试用例使用Alchemy API编写。
8 0
linux实时操作系统xenomai x86平台基准测试(benchmark)
|
2天前
|
缓存 安全 网络协议
Linux内核详解,什么是linux内核?
Linux内核详解,什么是linux内核?
12 0