Remarques sur l'utilisation de KUnit, le mécanisme de test unitaire du noyau Linux

Let's_start_KUnit! Agenda

What_is_KUnit Cela semble être une nouvelle fonctionnalité ajoutée dans la version 5.5.0. Un framework léger qui permet des tests unitaires du noyau sans avoir besoin d'une VM.

Officiel Getting_started Je vais le faire avec un wrapper Python. Si vous êtes curieux, lisez tools / testing / kunit.py pour le savoir. Pour le moment, allez dans le répertoire où se trouve la source du noyau

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

Ensuite, diverses choses vont grandir. de bonne heure

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

Si vous faites cela, .kunitconfig grandira sous cwd. Contenu

CONFIG_KUNIT=y
CONFIG_KUNIT_TEST=y
CONFIG_KUNIT_EXAMPLE_TEST=y

Using_KUnit Il est bon de le faire en fonction de la valeur par défaut que vous avez faite précédemment. Pour le moment, puisque .kunitconfig grandit, ./tools/testing/kunit/kunit.py run peut être effectué Cependant, à l'heure actuelle, le résultat est bien sûr le même résultat qu'auparavant. Pour comprendre, je vais faire un échantillon simple.

Obtenez task_struct du PID et préparez une fonction pour renvoyer le PID de cette tâche Le contenu de chaque fichier

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

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

Écrire.

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

,

Dans drivers / misc / Makefile

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

Ajouter. Vous pouvez maintenant rédiger le 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);

Comme vous pouvez le voir, écrivez en C. Cela ressemble à la macro transcendantale habituelle, donc l'explication est plus tard

Dans drivers / misc / Makefile

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

,

Dans .kunitconfig

CONFIG_TASK_FROM_PID=y
CONFIG_TASK_FROM_PID_TEST=y

Terminé en ajoutant

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

Peut être fait avec

Résultat (extrait)

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

Macros Pour le moment, jetez un œil à ʻinclude / kunit / test.h`. Différentes macros de test sont définies

Par exemple, le précédent 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__)

D'autres macros utiles telles que KUNIT_ASSERT_GT (Supérieur à) et KUNIT_ASSERT_PTR_EQ (comparaison de pointeurs) sont définies sur 1500 lignes.

De plus, KUNIT_CASE est défini ici et son contenu est simple. Si vous passez un pointeur de fonction, il ne remplira que les champs de pointeur et de nom sans aucun contact direct avec les éléments que vous voulez garder privés dans 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 }

La kunit_suite utilisée pour enregistrer le test a deux éléments autres que le nom et le cas de test, et chacun peut définir la fonction à exécuter avant et après chaque cas de test.

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

La structure «kunit» utilisée partout est définie comme ceci.

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

Les détails seront résumés séparément.

référence

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

Remarques sur l'utilisation de KUnit, le mécanisme de test unitaire du noyau Linux
Éviter les pièges de l'utilisation d'un Mac (pour les utilisateurs Linux?)
Essayez le mécanisme de verrouillage du noyau Linux
Un mémo expliquant la spécification de l'axe de l'axe
[AWS] Lançons un test unitaire de la fonction Lambda dans l'environnement local
Un mémo pour comprendre visuellement l'axe des pandas.
Créez un code QR pour l'URL sous Linux
Remarques sur l'installation de Chainer 1.5 pour GPU sous Windows
Un bref résumé du logiciel antivirus pour Linux personnel
[Pour mémoire] Linux Partie 2
L'histoire de la création d'un canal VIP dans le chatwork en interne
Une note sur le comportement de bowtie2 lors de plusieurs coups
[Linux] [module du noyau] Spécifier / limiter le CPU d'exécution de kthread
Un résumé approximatif des différences entre Windows et Linux
Remarque sur la compilation du noyau
Compilation du noyau Linux pour DE10nano
Pour se préparer au test G 2020 # 2
Un bref résumé de Linux
Mémorandum de l'outil de gestion de paquets Python ez_setup
Sous Linux, l'horodatage d'un fichier est un peu dépassé.
Expliquer le mécanisme de Linux que vous ne connaissez pas de manière inattendue
L'histoire de la création d'un pilote standard pour db avec python.
Comment afficher le résultat de sortie de la commande man Linux dans un fichier
Vérifiez la protection de la mémoire de Linux Kerne avec le code pour ARM
Comment faire un test unitaire Part.1 Modèle de conception pour l'introduction
Héberger la bibliothèque réseau Mirror for Unity sur un serveur Linux