A memo for utilizing the unit test mechanism KUnit of the Linux kernel

Let's_start_KUnit! Agenda

What_is_KUnit It seems to be a new feature added in version 5.5.0. A lightweight framework that allows kernel unit testing without the need for a VM.

Official Getting_started I'll do it with a Python wrapper. If you're curious, read tools / testing / kunit.py to find out. For the time being, go to the directory where the kernel source is located

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

Then, various things will grow up. early

Generating .config ...
[00:00:39] Building KUnit Kernel ...
[00:00:50] Starting KUnit Kernel ...
[00:00:50] ============================================================
[00:00:50] ======== [PASSED] kunit-try-catch-test ========
[00:00:50] [PASSED] kunit_test_try_catch_successful_try_no_catch
[00:00:50] [PASSED] kunit_test_try_catch_unsuccessful_try_does_catch
[00:00:50] ============================================================
[00:00:50] ======== [PASSED] kunit-resource-test ========
[00:00:50] [PASSED] kunit_resource_test_init_resources
[00:00:50] [PASSED] kunit_resource_test_alloc_resource
[00:00:50] [PASSED] kunit_resource_test_destroy_resource
[00:00:50] [PASSED] kunit_resource_test_cleanup_resources
[00:00:50] [PASSED] kunit_resource_test_proper_free_ordering
[00:00:50] ============================================================
[00:00:50] ======== [PASSED] string-stream-test ========
[00:00:50] [PASSED] string_stream_test_empty_on_creation
[00:00:50] [PASSED] string_stream_test_not_empty_after_add
[00:00:50] [PASSED] string_stream_test_get_string
[00:00:50] ============================================================
[00:00:50] ======== [PASSED] example ========
[00:00:50] [PASSED] example_simple_test
[00:00:50] ============================================================
[00:00:50] Testing complete. 11 tests run. 0 failed. 0 crashed.
[00:00:50] Elapsed time: 74.073s total, 2.343s configuring, 71.565s building, 0.166s running

If you do this, .kunitconfig will grow under cwd. Contents

CONFIG_KUNIT=y
CONFIG_KUNIT_TEST=y
CONFIG_KUNIT_EXAMPLE_TEST=y

Using_KUnit You should do it based on the default you did earlier. For the time being, since .kunitconfig is growing, You can ./tools/testing/kunit/kunit.py run However, at present, the result is of course the same output as before. For understanding, I will make a simple sample.

Get task_struct from PID and prepare a function to return the PID of that task The contents of each file

In drivers / misc / task_pid.h

#include <linux/sched.h>
#include <linux/fs_struct.h>
#include <linux/dcache.h>
int dump_task_by_pid(int nr, struct task_struct *taskbuf);

In drivers / misc / task_pid.c

#include <linux/pid.h>

#include "task_pid.h"

int dump_task_by_pid(int nr, struct task_struct *taskbuf)
{
    struct pid *pid = find_get_pid(nr);
    if(!pid)
        return -1;

    struct task_struct *tmp = pid_task(pid, PIDTYPE_PID);
    if(!tmp)
        return -1;
    else
        *taskbuf = *tmp;

    return taskbuf->pid;
}

To write.

In drivers / misc / Kconfig

...
config TASK_FROM_PID
    bool "Get task from pid and return task's pid"

config TASK_FROM_PID_TEST
    bool "Test get task from pid"
    depends on TASK_FROM_PID && KUNIT
...

,

In drivers / misc / Makefile

...
obj-$(CONFIG_TASK_FROM_PID)      += task_pid.o
obj-$(CONFIG_TASK_FROM_PID_TEST) += task-from-pid-test.o
...

Add. Now you can write the test.

drivers/misc/task-from-pid-test.c

#include <kunit/test.h>

#include "task_pid.h"

static void task_from_pid_test(struct kunit *test)
{
    struct task_struct taskbuf;
    KUNIT_EXPECT_EQ(test, 1, dump_task_by_pid(1, &taskbuf));
    KUNIT_EXPECT_EQ(test, 1, taskbuf.pid);
    KUNIT_EXPECT_EQ(test, '/', taskbuf.fs->root.dentry->d_name.name[0]);
}

static struct kunit_case task_dump_test_cases[] = {
    KUNIT_CASE(task_from_pid_test),
    {}
};

static struct kunit_suite task_dump_test_suite = {
    .name = "dump task from pid",
    .test_cases = task_dump_test_cases,
};
kunit_test_suite(task_dump_test_suite);

As you can see, write in C. It looks like the usual transcendental macro, so I will explain it later [#Macros)

In drivers / misc / Makefile

obj-$(CONFIG_MISC_EXAMPLE_TEST) += example-test.o

,

In .kunitconfig

CONFIG_TASK_FROM_PID=y
CONFIG_TASK_FROM_PID_TEST=y

Completed by adding

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

Can be done with

Result (excerpt)

[00:00:38] ============================================================
[00:00:38] ======== [PASSED] dump task from pid ========
[00:00:38] [PASSED] task_from_pid_test
[00:00:38] ============================================================

Macros For the time being, take a look at ʻinclude / kunit / test.h`. Various test macros are defined

For example, the previous KUNIT_ASSERT_EQ

/**
 * KUNIT_ASSERT_EQ() - Sets an assertion that @left and @right are equal.
 * @test: The test context object.
 * @left: an arbitrary expression that evaluates to a primitive C type.
 * @right: an arbitrary expression that evaluates to a primitive C type.
 *
 * Sets an assertion that the values that @left and @right evaluate to are
 * equal. This is the same as KUNIT_EXPECT_EQ(), except it causes an assertion
 * failure (see KUNIT_ASSERT_TRUE()) when the assertion is not met.
 */
#define KUNIT_ASSERT_EQ(test, left, right) \
	KUNIT_BINARY_EQ_ASSERTION(test, KUNIT_ASSERTION, left, right)

#define KUNIT_ASSERT_EQ_MSG(test, left, right, fmt, ...)		       \
	KUNIT_BINARY_EQ_MSG_ASSERTION(test,				       \
				      KUNIT_ASSERTION,			       \
				      left,				       \
				      right,				       \
				      fmt,				       \
				      ##__VA_ARGS__)

Other useful macros such as KUNIT_ASSERT_GT (Greater than) and KUNIT_ASSERT_PTR_EQ (pointer comparison) are defined over 1500 lines.

Also, KUNIT_CASE is defined here, and its contents are simple. If you pass a function pointer, it will fill only the pointer and name fields without any direct contact with the elements you want to keep private in kunit_case.

/**
 * struct kunit_case - represents an individual test case.
 *
 * @run_case: the function representing the actual test case.
 * @name:     the name of the test case.
 *
 * A test case is a function with the signature,
 * ``void (*)(struct kunit *)``
 * that makes expectations and assertions (see KUNIT_EXPECT_TRUE() and
 * KUNIT_ASSERT_TRUE()) about code under test. Each test case is associated
 * with a &struct kunit_suite and will be run after the suite's init
 * function and followed by the suite's exit function.
 *
 * A test case should be static and should only be created with the
 * KUNIT_CASE() macro; additionally, every array of test cases should be
 * terminated with an empty test case.
 *
 * Example:
 *
 * .. code-block:: c
 *
 *	void add_test_basic(struct kunit *test)
 *	{
 *		KUNIT_EXPECT_EQ(test, 1, add(1, 0));
 *		KUNIT_EXPECT_EQ(test, 2, add(1, 1));
 *		KUNIT_EXPECT_EQ(test, 0, add(-1, 1));
 *		KUNIT_EXPECT_EQ(test, INT_MAX, add(0, INT_MAX));
 *		KUNIT_EXPECT_EQ(test, -1, add(INT_MAX, INT_MIN));
 *	}
 *
 *	static struct kunit_case example_test_cases[] = {
 *		KUNIT_CASE(add_test_basic),
 *		{}
 *	};
 *
 */
struct kunit_case {
	void (*run_case)(struct kunit *test);
	const char *name;

	/* private: internal use only. */
	bool success;
};

/**
 * KUNIT_CASE - A helper for creating a &struct kunit_case
 *
 * @test_name: a reference to a test case function.
 *
 * Takes a symbol for a function representing a test case and creates a
 * &struct kunit_case object from it. See the documentation for
 * &struct kunit_case for an example on how to use it.
 */
#define KUNIT_CASE(test_name) { .run_case = test_name, .name = #test_name }

The kunit_suite used to register the test has two elements other than the name and test case, and each can set the function to be executed before and after each test case.

/**
 * struct kunit_suite - describes a related collection of &struct kunit_case
 *
 * @name:	the name of the test. Purely informational.
 * @init:	called before every test case.
 * @exit:	called after every test case.
 * @test_cases:	a null terminated array of test cases.
 *
 * A kunit_suite is a collection of related &struct kunit_case s, such that
 * @init is called before every test case and @exit is called after every
 * test case, similar to the notion of a *test fixture* or a *test class*
 * in other unit testing frameworks like JUnit or Googletest.
 *
 * Every &struct kunit_case must be associated with a kunit_suite for KUnit
 * to run it.
 */
struct kunit_suite {
	const char name[256];
	int (*init)(struct kunit *test);
	void (*exit)(struct kunit *test);
	struct kunit_case *test_cases;
};

The structure kunit used everywhere is defined like this.

/**
 * struct kunit - represents a running instance of a test.
 *
 * @priv: for user to store arbitrary data. Commonly used to pass data
 *	  created in the init function (see &struct kunit_suite).
 *
 * Used to store information about the current context under which the test
 * is running. Most of this data is private and should only be accessed
 * indirectly via public functions; the one exception is @priv which can be
 * used by the test writer to store arbitrary data.
 */
struct kunit {
	void *priv;

	/* private: internal use only. */
	const char *name; /* Read only after initialization! */
	struct kunit_try_catch try_catch;
	/*
	 * success starts as true, and may only be set to false during a
	 * test case; thus, it is safe to update this across multiple
	 * threads using WRITE_ONCE; however, as a consequence, it may only
	 * be read after the test case finishes once all threads associated
	 * with the test case have terminated.
	 */
	bool success; /* Read only after test_case finishes! */
	spinlock_t lock; /* Guards all mutable test state. */
	/*
	 * Because resources is a list that may be updated multiple times (with
	 * new resources) from any thread associated with a test case, we must
	 * protect it with some type of lock.
	 */
	struct list_head resources; /* Protected by lock. */
};

Details will be summarized separately.

reference

https://github.com/torvalds/linux https://kunit.dev/usage/index.html https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/testing/kunit?h=v5.5 https://kunit.dev/third_party/kernel/docs/ https://lwn.net/Articles/780985/

Recommended Posts

A memo for utilizing the unit test mechanism KUnit of the Linux kernel
A quick overview of the Linux kernel
Avoiding the pitfalls of using a Mac (for Linux users?)
Try the Linux kernel lockdown mechanism
A memo explaining the axis specification of axis
[AWS] Let's run a unit test of Lambda function in the local environment
For Debian users who are having trouble with a bug in the Linux kernel 5.10
A memo to visually understand the axis of pandas.Panel
Create a QR code for the URL on Linux
A memo of installing Chainer 1.5 for GPU on Windows
A brief summary of Linux antivirus software for individuals
[For memo] Linux Part 2
The story of creating a VIP channel for in-house chatwork
A memo about the behavior of bowtie2 during multiple hits
[Linux] [kernel module] Specify / limit the execution CPU of kthread
A rough summary of the differences between Windows and Linux
A memorandum of kernel compilation
Linux Kernel Build for DE10nano
For the G test 2020 # 2 exam
What is the Linux kernel?
A brief summary of Linux
A memorandum of understanding for the Python package management tool ez_setup
On Linux, the time stamp of a file is a little past.
Explaining the mechanism of Linux that you do not know unexpectedly
The story of making a standard driver for db with python.
How to output the output result of the Linux man command to a file
Check the memory protection of linux kerne with the code for ARM
How to make a unit test Part.1 Design pattern for introduction
Can the F1C100s dream of embedded development for mediocre Linux users?
Host the network library Mirror for Unity on a Linux server