Linux驱动是基于Kbuild框架开发的,一般情况下只会生成一个ko
文件,如果想添加单元测试
(Unit Test即UT
),用户要么在模块入口函数的末尾添加UT代码,要么额外创建一个单独的UT工程,前者把测试代码跟驱动代码放置于同一个文件比较混乱,后者创建额外的工程维护比较麻烦。
能否既避免混乱,又避免麻烦呢?可以的。
Kbuild支持生成多个ko,只需要给Makefile变量obj-m
追加一个模块名即可。
MODULE_NAME := your_drv
MODULE_UT_NAME := your_drv_ut
SRCS := your_drv_part1.c your_drv_part2.c
SRCS_UT := your_drv_ut_suite1.c
$(MODULE_NAME)-objs := $(SRCS:%.c=%.o)
$(MODULE_UT_NAME)-objs := $(SRCS_UT:%.c=%.o)
obj-m := $(MODULE_NAME).o
obj-m += $(MODULE_UT_NAME).o
clean-objs := $(SRCS:%.c=%.o)
clean-objs += $(join $(dir $(SRCS)), $(patsubst %.c, .%.o.cmd, $(notdir $(SRCS))))
clean-objs += $(SRCS_UT:%.c=%.o)
clean-objs += $(join $(dir $(SRCS_UT)), $(patsubst %.c, .%.o.cmd, $(notdir $(SRCS_UT))))
注意:
+=
操作符实现的ld
会报no input files
,原因是同名导致的循环依赖被make程序检测到,从而丢弃该源文件。如果你是5.5以上版本的内核,则建议使用内核自带的kunit框架,不用引入额外依赖,功能也基本够用。
#include
void your_drv_test_basic(struct kunit *test)
{
int reg_value = 0;
// ...
KUNIT_EXPECT_EQ(test, reg_value, 42);
}
AX_S32 your_drv_test_init(struct kunit *test)
{
// 一些资源分配
}
AX_S32 your_drv_test_exit(struct kunit *test)
{
// 一些资源回收
}
static struct kunit_case your_drv_test_cases[] = {
KUNIT_CASE(your_drv_test_basic),
{}
};
struct kunit_suite your_drv_test_suite1 = {
.name = "your_drv kunit",
.init = your_drv_test_init,
.exit = your_drv_test_exit,
.test_cases = your_drv_test_cases,
};
kunit_test_suites(&your_drv_test_suite1);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("your name");
MODULE_DESCRIPTION("your_drv unit test");
# 先加载kunit和你的驱动ko
insmod kunit.ko
insmod you_drv.ko
# 再加载ut的ko,ut代码会在模块加载时执行
insmod you_drv_ut.ko
kunit_suite
结构体(必要的话将每个suite拆分到单独的.c文件,这样的话记得给UT模块添加多个.c文件),并将其一并传递给kunit_test_suites
函数(该函数支持可变数目的参数)