首页 > 其他分享 >KUnit

KUnit

时间:2024-09-06 09:35:36浏览次数:8  
标签:kunit region KUNIT KUnit ctx test EXPECT

基本介绍

KUnit Suite Memory

每一个kUnit都有自己的上下文,所以一个崩溃了,不会影响到其他的suit。

开关

目前可以有下面几种方法控制case测试。

menuconfig控制
使用.kunitconfig控制编译的测试文件
使用宏选择性开启某些case
使用debugfs
kunit_get_current_test

总开关

CONFIG_KUNIT=y
CONFIG_KUNIT_DEFAULT_ENABLED=y

CONFIG_KUNIT: 将KUNIT框架编入内核。
CONFIG_KUNIT_DEFAULT_ENABLED:开启测试,如果是n会关闭测试。

测试文件开关

使用menuconfig

通过在Kconfig中配置测试文件的构建选项来控制是否加入测试。

例如,有Kunit测试文件 kunit-example-test.c , 可以通过在Kconfig中加入 CONFIG_KUNIT_EXAMPLE_TEST选项可以是y/m/n,并且在Makefile中添加:

obj-$(CONFIG_KUNIT_EXAMPLE_TEST) += kunit-example-test.o

如果设置成m就会编译出.ko的驱动文件。在挂载的时候进行测试。

使用.kunitconfig

.kunitconfig是 a minconfig(通过运行生成的 .config ),用于运行一组特定的测试。如果想运行一组特定的测试,可以在.kunitconfig中提供Kconfig选项,以及所依赖的其他配置选项(架构配置、启动/禁用默写代码块配置、依赖项)。其根本也是使用Kconfig用来配置构建选项,具体可以见这里.

使用debugfs

可以通过配置 CONFIG_KUNIT_DEBUGFS=y ,开启Kunit提供的debugfs的功能。Kunit会在 /sys/kernel/debug/kunit// 下创建两个文件result和run。

cat results 就可以输出测试结果
echo anything > run 可以再次运行测试并输出测试结果

测试函数开关

使用宏来选择性开启case

可以在代码段中通过宏条件编译的方式选择性开启case。或者如果不想将测试的函数或者变量导出给测试文件使用,也可以使用下面的方法

static int do_interesting_thing();
 
#ifdef CONFIG_MY_KUNIT_TEST
#include "my_kunit_test.c"
#endif

kunit_get_current_test()

当需要用测试文件外部调用仅测试代码时,可以使用 kunit_get_current_test 。该函数可以通过task_struct中kunit_test来分析出当前的kunit上下文,并且如果Kunit没有启用,该函数返回null,也可以安全调用,并且对性能的影响忽略不计。所以可以通过这个特性,在待测函数中植入kunit测试,通过启用Kunit测试当做开关。例如:

#include <kunit/test-bug.h> /* for kunit_get_current_test */
 
struct test_data {
        int foo_result;
        int want_foo_called_with;
};
 
static int fake_foo(int arg)
{
// 本函数从外部得到kunit上下文进行测试
        struct kunit *test = kunit_get_current_test();
        struct test_data *test_data = test->priv;
 
        KUNIT_EXPECT_EQ(test, test_data->want_foo_called_with, arg);
        return test_data->foo_result;
}
 
static void example_simple_test(struct kunit *test)
{
        /* Assume priv (private, a member used to pass test data from
         * the init function) is allocated in the suite's .init */
        struct test_data *test_data = test->priv;
 
        test_data->foo_result = 42;
        test_data->want_foo_called_with = 1;
 
        /* In a real test, we'd probably pass a pointer to fake_foo somewhere
         * like an ops struct, etc. instead of calling it directly. */
        KUNIT_EXPECT_EQ(test, fake_foo(1), 42);
}

使用kunit提供的一些状态函数

kunit_skip:跳过当前test

一些测试模板

最简测试模板

// 假测试函数
static void foo_test_fake(struct kunit *test)
{
   const u32 array1[] = { 0x0F, 0xFF };
    const u32 array2[] = { 0x1F, 0xFF };
 
    /* Boolean assertions */
    KUNIT_EXPECT_TRUE(test, true);
    KUNIT_EXPECT_FALSE(test, false);
 
    /* Integer assertions */
    KUNIT_EXPECT_EQ(test, 1, 1); /* check == */
    KUNIT_EXPECT_GE(test, 1, 1); /* check >= */
    KUNIT_EXPECT_LE(test, 1, 1); /* check <= */
    KUNIT_EXPECT_NE(test, 1, 0); /* check != */
    KUNIT_EXPECT_GT(test, 1, 0); /* check >  */
    KUNIT_EXPECT_LT(test, 0, 1); /* check <  */
 
    /* Pointer assertions */
    KUNIT_EXPECT_NOT_ERR_OR_NULL(test, test);
    KUNIT_EXPECT_PTR_EQ(test, NULL, NULL);
    KUNIT_EXPECT_PTR_NE(test, test, NULL);
    KUNIT_EXPECT_NULL(test, NULL);
    KUNIT_EXPECT_NOT_NULL(test, test);
 
    /* String assertions */
    KUNIT_EXPECT_STREQ(test, "hi", "hi");
    KUNIT_EXPECT_STRNEQ(test, "hi", "bye");
 
    /* Memory block assertions */
    KUNIT_EXPECT_MEMEQ(test, array1, array1, sizeof(array1));
    KUNIT_EXPECT_MEMNEQ(test, array1, array2, sizeof(array1));
 
    /*
     * There are also ASSERT variants of all of the above that abort test
     * execution if they fail. Useful for memory allocations, etc.
     */
    KUNIT_ASSERT_GT(test, sizeof(char), 0);
 
    /*
     * There are also _MSG variants of all of the above that let you include
     * additional text on failure.
     */
    KUNIT_EXPECT_GT_MSG(test, sizeof(int), 0, "Your ints are 0-bit?!");
    KUNIT_ASSERT_GT_MSG(test, sizeof(int), 0, "Your ints are 0-bit?!");
}
  
// 需要执行的cases
static struct kunit_case sample_tests[] = {
    KUNIT_CASE(foo_test_fake),
    { /* sentinel */ }
};
  
static struct kunit_suite sample_test_suite = {
    .name = "sample-test",
    .test_cases = sample_tests,
};
  
kunit_test_suite(sample_test_suite);

driver需要fake device

可以使用 kunit_device_register 来创建一个fake device,当测试结束时会自动清理。同时在init中使用test→priv 传递device到各个cases中。

static void test_my_device(struct kunit *test)
{
    struct device *fake_device;
    const char *dev_managed_string;
 
    // Create a fake device.
    fake_device = kunit_device_register(test, "my_device");
    KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fake_device)
 
    // Pass it to functions which need a device.
    dev_managed_string = devm_kstrdup(fake_device, "Hello, World!");
 
    // Everything is cleaned up automatically when the test ends.
}


在 linux-next/drivers/fpga/tests/fpga-region-test.c中有

static int fpga_region_test_init(struct kunit *test)
{
    struct test_ctx *ctx;
    struct fpga_region_info region_info = { 0 };
 
 
    ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
    KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
 
    ctx->mgr_dev = kunit_device_register(test, "fpga-manager-test-dev");
    KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->mgr_dev);
 
    ctx->mgr = devm_fpga_mgr_register(ctx->mgr_dev, "Fake FPGA Manager",
                      &fake_mgr_ops, &ctx->mgr_stats);
    KUNIT_ASSERT_FALSE(test, IS_ERR_OR_NULL(ctx->mgr));
.....
 
    ctx->region = fpga_region_register_full(ctx->region_dev, &region_info);
    KUNIT_ASSERT_FALSE(test, IS_ERR_OR_NULL(ctx->region));
 
    test->priv = ctx;
 
    return 0;
}
 
//cases中
static void fpga_region_test_class_find(struct kunit *test)
{
//使用传入的fake device
    struct test_ctx *ctx = test->priv;
    struct fpga_region *region;
 
    region = fpga_region_class_find(NULL, ctx->region_dev, fake_region_match);
    KUNIT_EXPECT_PTR_EQ(test, region, ctx->region);
 
    put_device(region->dev);
}

标签:kunit,region,KUNIT,KUnit,ctx,test,EXPECT
From: https://www.cnblogs.com/alanli07/p/18399622

相关文章