Hinweise zur Verwendung von KUnit, dem Unit-Test-Mechanismus des Linux-Kernels

Let's_start_KUnit! Agenda

What_is_KUnit Es scheint eine neue Funktion zu sein, die in Version 5.5.0 hinzugefügt wurde. Ein leichtes Framework, das Unit-Tests des Kernels ermöglicht, ohne dass eine VM erforderlich ist.

Offiziell Getting_started Ich mache es mit einem Python-Wrapper. Wenn Sie neugierig sind, lesen Sie tools / testing / kunit.py, um dies herauszufinden. Wechseln Sie vorerst in das Verzeichnis, in dem sich die Kernelquelle befindet

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

Dann werden verschiedene Dinge erwachsen. früh

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

Wenn Sie dies tun, wächst .kunitconfig unter cwd. Inhalt

CONFIG_KUNIT=y
CONFIG_KUNIT_TEST=y
CONFIG_KUNIT_EXAMPLE_TEST=y

Using_KUnit Es ist gut, dies basierend auf der Standardeinstellung zu tun, die Sie zuvor vorgenommen haben. Vorerst, da .kunitconfig wächst, ./tools/testing/kunit/kunit.py run kann durchgeführt werden Gegenwärtig ist das Ergebnis natürlich die gleiche Ausgabe wie zuvor. Zum Verständnis werde ich ein einfaches Beispiel machen.

Holen Sie sich task_struct von der PID und bereiten Sie eine Funktion vor, um die PID dieser Task zurückzugeben Der Inhalt jeder Datei

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

Schreiben.

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

Hinzufügen. Jetzt können Sie den Test schreiben.

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

Wie Sie sehen können, schreiben Sie in C. Es sieht aus wie das übliche transzendentale Makro, daher lautet die Erklärung später

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

Abgeschlossen durch Hinzufügen

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

Kann mit gemacht werden

Ergebnis (Auszug)

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

Macros Werfen wir vorerst einen Blick auf include / kunit / test.h. Es werden verschiedene Testmakros definiert

Zum Beispiel das vorherige 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__)

Andere nützliche Makros wie "KUNIT_ASSERT_GT" (größer als) und "KUNIT_ASSERT_PTR_EQ" (Zeigervergleich) werden über 1500 Zeilen definiert.

Außerdem wird hier "KUNIT_CASE" definiert, und sein Inhalt ist einfach. Wenn Sie einen Funktionszeiger übergeben, werden nur die Zeiger- und Namensfelder ohne direkten Kontakt mit den Elementen gefüllt, die Sie in kunit_case privat halten möchten.

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

Die zur Registrierung des Tests verwendete kunit_suite enthält zwei andere Elemente als den Namen und den Testfall. Jedes kann die Funktion festlegen, die vor und nach jedem Testfall ausgeführt werden soll.

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

Die überall verwendete Struktur "Kunit" ist wie folgt definiert.

/**
 * 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 werden separat zusammengefasst.

Referenz

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

Hinweise zur Verwendung von KUnit, dem Unit-Test-Mechanismus des Linux-Kernels
Vermeiden Sie die Fallstricke bei der Verwendung eines Mac (für Linux-Benutzer?)
Probieren Sie den Linux-Kernel-Sperrmechanismus aus
Ein Memo, das die Achsenspezifikation der Achse erklärt
[AWS] Lassen Sie uns einen Komponententest der Lambda-Funktion in der lokalen Umgebung durchführen
Ein Memo zum visuellen Verstehen der Achse von Pandas.Panel
Erstellen Sie unter Linux einen QR-Code für die URL
Hinweise zur Installation von Chainer 1.5 für GPU unter Windows
Eine kurze Zusammenfassung der Antivirensoftware für persönliches Linux
[Für Memo] Linux Teil 2
Die Geschichte der Einrichtung eines VIP-Kanals im internen Chatwork
Ein Hinweis zum Verhalten von bowtie2 bei mehreren Treffern
[Linux] [Kernelmodul] Geben Sie die Ausführungs-CPU von kthread an / begrenzen Sie sie
Eine grobe Zusammenfassung der Unterschiede zwischen Windows und Linux
Hinweis zur Kernel-Kompilierung
Linux Kernel Build für DE10nano
Für die Prüfung G-Test 2020 # 2
Eine kurze Zusammenfassung von Linux
Memorandum des Python-Paketverwaltungstools ez_setup
Unter Linux ist der Zeitstempel einer Datei etwas vorbei.
Erklären Sie den Mechanismus von Linux, den Sie nicht unerwartet kennen
Die Geschichte, einen Standardtreiber für db mit Python zu erstellen.
So geben Sie das Ausgabeergebnis des Linux-Befehls man in eine Datei aus
Überprüfen Sie den Speicherschutz von Linux Kern mit Code für ARM
So führen Sie einen Komponententest durch Teil 1 Entwurfsmuster zur Einführung
Hosten Sie die Netzwerkbibliothek Mirror for Unity auf einem Linux-Server