Linuxカーネルのユニットテスト機構KUnitを活用するためのメモ

# Let's_start_KUnit!
## Agenda
 - [What is KUnit](#What_is_KUnit)
 - [Getting started](#Getting_started)
 - [Using KUnit](#Using_KUnit)
 - [Macros](#Macros)

## What_is_KUnit
バージョン5.5.0で追加された新機能らしい。
VM等の用意なしにカーネルのユニットテストが出来る軽量なフレームワーク。

[公式](https://kunit.dev/)
## Getting_started
Pythonのラッパー使ってシュッとやる。
気になったら`tools/testing/kunit.py`読めばわかる。
とりあえずカーネルのソースが置いてあるディレクトリへ行って

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

ってやると、シュッと色々生えてくる。早い

```
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
```

これやるとcwd下に`.kunitconfig`が生える。内容は

```
CONFIG_KUNIT=y
CONFIG_KUNIT_TEST=y
CONFIG_KUNIT_EXAMPLE_TEST=y
```

## Using_KUnit
さっきやったデフォルトのを元にやると良い。
とりあえずさっきので`.kunitconfig`が生えてるので、
`./tools/testing/kunit/kunit.py run`できる
が、現状では結果は当然さっきと同じ出力。
理解のため、シンプルなサンプルを作っていく。

PIDから`task_struct`を取得、そのtaskのPIDを返す関数を用意してやる
各ファイルの内容は

`drivers/misc/task_pid.h`に

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

`drivers/misc/task_pid.c`に

```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;
}
```

を書き込む。

`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
...
```

を、

`drivers/misc/Makefile`に

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

を書き足す。
これでもうテストが書けるようになる。

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

```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);

```

見ての通りCで書く。
見た感じいつもの超絶マクロなので解説は[後で](#Macros)

`drivers/misc/Makefile`に
```Makefile
obj-$(CONFIG_MISC_EXAMPLE_TEST) += example-test.o
```

を、

`.kunitconfig`に

```
CONFIG_TASK_FROM_PID=y
CONFIG_TASK_FROM_PID_TEST=y
```

を書き足すと完成

```bash
./tools/testing/kunit/kunit.py run
```

で出来る

結果(抜粋)

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

## Macros
とりあえず`include/kunit/test.h`を見てみる。
各種テスト用マクロが定義されてる

たとえばさっきの`KUNIT_ASSERT_EQ`

```c
/**
 * 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__)
```

他にも`KUNIT_ASSERT_GT`(Greater than)とか`KUNIT_ASSERT_PTR_EQ`(ポインタ比較)とか、1500行に渡って便利なマクロが定義されてる。

また、`KUNIT_CASE`もここで定義されており、その内容は簡単で
`kunit_case`のプライベートにしたい要素を直接触らせない工夫で、関数ポインタを渡せば勝手にポインタと名前のフィールドだけを埋めてくれる。

```c
/**
 * 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 }
```

テストの登録に使った`kunit_suite`は名前とテストケース以外にも2つの要素を持ち、それぞれ各テストケースの前後に実行する関数を設定できる。

```c
/**
 * 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;
};
```

随所に用いられている構造体`kunit`はこんな定義。

```c
/**
 * 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. */
};
```

詳しくはまた別でまとめる。

## 参考
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/