Skip to content

Commit 30bbcb4

Browse files
committed
Merge tag 'linux_kselftest-kunit-6.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest
Pull kunit updates from Shuah Khan: - New parameterized test features KUnit parameterized tests supported two primary methods for getting parameters: - Defining custom logic within a generate_params() function. - Using the KUNIT_ARRAY_PARAM() and KUNIT_ARRAY_PARAM_DESC() macros with a pre-defined static array and passing the created *_gen_params() to KUNIT_CASE_PARAM(). These methods present limitations when dealing with dynamically generated parameter arrays, or in scenarios where populating parameters sequentially via generate_params() is inefficient or overly complex. These limitations are fixed with a parameterized test method - Fix issues in kunit build artifacts cleanup - Fix parsing skipped test problem in kselftest framework - Enable PCI on UML without triggering WARN() - a few other fixes and adds support for new configs such as MIPS * tag 'linux_kselftest-kunit-6.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest: kunit: Extend kconfig help text for KUNIT_UML_PCI rust: kunit: allow `cfg` on `test`s kunit: qemu_configs: Add MIPS configurations kunit: Enable PCI on UML without triggering WARN() Documentation: kunit: Document new parameterized test features kunit: Add example parameterized test with direct dynamic parameter array setup kunit: Add example parameterized test with shared resource management using the Resource API kunit: Enable direct registration of parameter arrays to a KUnit test kunit: Pass parameterized test context to generate_params() kunit: Introduce param_init/exit for parameterized test context management kunit: Add parent kunit for parameterized test context kunit: tool: Accept --raw_output=full as an alias of 'all' kunit: tool: Parse skipped tests from kselftest.h kunit: Always descend into kunit directory during build
2 parents d2b2fea + 285cae5 commit 30bbcb4

File tree

20 files changed

+880
-63
lines changed

20 files changed

+880
-63
lines changed

Documentation/dev-tools/kunit/usage.rst

Lines changed: 337 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -542,11 +542,31 @@ There is more boilerplate code involved, but it can:
542542
Parameterized Testing
543543
~~~~~~~~~~~~~~~~~~~~~
544544

545-
The table-driven testing pattern is common enough that KUnit has special
546-
support for it.
545+
To run a test case against multiple inputs, KUnit provides a parameterized
546+
testing framework. This feature formalizes and extends the concept of
547+
table-driven tests discussed previously.
547548

548-
By reusing the same ``cases`` array from above, we can write the test as a
549-
"parameterized test" with the following.
549+
A KUnit test is determined to be parameterized if a parameter generator function
550+
is provided when registering the test case. A test user can either write their
551+
own generator function or use one that is provided by KUnit. The generator
552+
function is stored in ``kunit_case->generate_params`` and can be set using the
553+
macros described in the section below.
554+
555+
To establish the terminology, a "parameterized test" is a test which is run
556+
multiple times (once per "parameter" or "parameter run"). Each parameter run has
557+
both its own independent ``struct kunit`` (the "parameter run context") and
558+
access to a shared parent ``struct kunit`` (the "parameterized test context").
559+
560+
Passing Parameters to a Test
561+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
562+
There are three ways to provide the parameters to a test:
563+
564+
Array Parameter Macros:
565+
566+
KUnit provides special support for the common table-driven testing pattern.
567+
By applying either ``KUNIT_ARRAY_PARAM`` or ``KUNIT_ARRAY_PARAM_DESC`` to the
568+
``cases`` array from the previous section, we can create a parameterized test
569+
as shown below:
550570

551571
.. code-block:: c
552572
@@ -555,7 +575,7 @@ By reusing the same ``cases`` array from above, we can write the test as a
555575
const char *str;
556576
const char *sha1;
557577
};
558-
const struct sha1_test_case cases[] = {
578+
static const struct sha1_test_case cases[] = {
559579
{
560580
.str = "hello world",
561581
.sha1 = "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed",
@@ -590,6 +610,318 @@ By reusing the same ``cases`` array from above, we can write the test as a
590610
{}
591611
};
592612
613+
Custom Parameter Generator Function:
614+
615+
The generator function is responsible for generating parameters one-by-one
616+
and has the following signature:
617+
``const void* (*)(struct kunit *test, const void *prev, char *desc)``.
618+
You can pass the generator function to the ``KUNIT_CASE_PARAM``
619+
or ``KUNIT_CASE_PARAM_WITH_INIT`` macros.
620+
621+
The function receives the previously generated parameter as the ``prev`` argument
622+
(which is ``NULL`` on the first call) and can also access the parameterized
623+
test context passed as the ``test`` argument. KUnit calls this function
624+
repeatedly until it returns ``NULL``, which signifies that a parameterized
625+
test ended.
626+
627+
Below is an example of how it works:
628+
629+
.. code-block:: c
630+
631+
#define MAX_TEST_BUFFER_SIZE 8
632+
633+
// Example generator function. It produces a sequence of buffer sizes that
634+
// are powers of two, starting at 1 (e.g., 1, 2, 4, 8).
635+
static const void *buffer_size_gen_params(struct kunit *test, const void *prev, char *desc)
636+
{
637+
long prev_buffer_size = (long)prev;
638+
long next_buffer_size = 1; // Start with an initial size of 1.
639+
640+
// Stop generating parameters if the limit is reached or exceeded.
641+
if (prev_buffer_size >= MAX_TEST_BUFFER_SIZE)
642+
return NULL;
643+
644+
// For subsequent calls, calculate the next size by doubling the previous one.
645+
if (prev)
646+
next_buffer_size = prev_buffer_size << 1;
647+
648+
return (void *)next_buffer_size;
649+
}
650+
651+
// Simple test to validate that kunit_kzalloc provides zeroed memory.
652+
static void buffer_zero_test(struct kunit *test)
653+
{
654+
long buffer_size = (long)test->param_value;
655+
// Use kunit_kzalloc to allocate a zero-initialized buffer. This makes the
656+
// memory "parameter run managed," meaning it's automatically cleaned up at
657+
// the end of each parameter run.
658+
int *buf = kunit_kzalloc(test, buffer_size * sizeof(int), GFP_KERNEL);
659+
660+
// Ensure the allocation was successful.
661+
KUNIT_ASSERT_NOT_NULL(test, buf);
662+
663+
// Loop through the buffer and confirm every element is zero.
664+
for (int i = 0; i < buffer_size; i++)
665+
KUNIT_EXPECT_EQ(test, buf[i], 0);
666+
}
667+
668+
static struct kunit_case buffer_test_cases[] = {
669+
KUNIT_CASE_PARAM(buffer_zero_test, buffer_size_gen_params),
670+
{}
671+
};
672+
673+
Runtime Parameter Array Registration in the Init Function:
674+
675+
For scenarios where you might need to initialize a parameterized test, you
676+
can directly register a parameter array to the parameterized test context.
677+
678+
To do this, you must pass the parameterized test context, the array itself,
679+
the array size, and a ``get_description()`` function to the
680+
``kunit_register_params_array()`` macro. This macro populates
681+
``struct kunit_params`` within the parameterized test context, effectively
682+
storing a parameter array object. The ``get_description()`` function will
683+
be used for populating parameter descriptions and has the following signature:
684+
``void (*)(struct kunit *test, const void *param, char *desc)``. Note that it
685+
also has access to the parameterized test context.
686+
687+
.. important::
688+
When using this way to register a parameter array, you will need to
689+
manually pass ``kunit_array_gen_params()`` as the generator function to
690+
``KUNIT_CASE_PARAM_WITH_INIT``. ``kunit_array_gen_params()`` is a KUnit
691+
helper that will use the registered array to generate the parameters.
692+
693+
If needed, instead of passing the KUnit helper, you can also pass your
694+
own custom generator function that utilizes the parameter array. To
695+
access the parameter array from within the parameter generator
696+
function use ``test->params_array.params``.
697+
698+
The ``kunit_register_params_array()`` macro should be called within a
699+
``param_init()`` function that initializes the parameterized test and has
700+
the following signature ``int (*)(struct kunit *test)``. For a detailed
701+
explanation of this mechanism please refer to the "Adding Shared Resources"
702+
section that is after this one. This method supports registering both
703+
dynamically built and static parameter arrays.
704+
705+
The code snippet below shows the ``example_param_init_dynamic_arr`` test that
706+
utilizes ``make_fibonacci_params()`` to create a dynamic array, which is then
707+
registered using ``kunit_register_params_array()``. To see the full code
708+
please refer to lib/kunit/kunit-example-test.c.
709+
710+
.. code-block:: c
711+
712+
/*
713+
* Example of a parameterized test param_init() function that registers a dynamic
714+
* array of parameters.
715+
*/
716+
static int example_param_init_dynamic_arr(struct kunit *test)
717+
{
718+
size_t seq_size;
719+
int *fibonacci_params;
720+
721+
kunit_info(test, "initializing parameterized test\n");
722+
723+
seq_size = 6;
724+
fibonacci_params = make_fibonacci_params(test, seq_size);
725+
if (!fibonacci_params)
726+
return -ENOMEM;
727+
/*
728+
* Passes the dynamic parameter array information to the parameterized test
729+
* context struct kunit. The array and its metadata will be stored in
730+
* test->parent->params_array. The array itself will be located in
731+
* params_data.params.
732+
*/
733+
kunit_register_params_array(test, fibonacci_params, seq_size,
734+
example_param_dynamic_arr_get_desc);
735+
return 0;
736+
}
737+
738+
static struct kunit_case example_test_cases[] = {
739+
/*
740+
* Note how we pass kunit_array_gen_params() to use the array we
741+
* registered in example_param_init_dynamic_arr() to generate
742+
* parameters.
743+
*/
744+
KUNIT_CASE_PARAM_WITH_INIT(example_params_test_with_init_dynamic_arr,
745+
kunit_array_gen_params,
746+
example_param_init_dynamic_arr,
747+
example_param_exit_dynamic_arr),
748+
{}
749+
};
750+
751+
Adding Shared Resources
752+
^^^^^^^^^^^^^^^^^^^^^^^
753+
All parameter runs in this framework hold a reference to the parameterized test
754+
context, which can be accessed using the parent ``struct kunit`` pointer. The
755+
parameterized test context is not used to execute any test logic itself; instead,
756+
it serves as a container for shared resources.
757+
758+
It's possible to add resources to share between parameter runs within a
759+
parameterized test by using ``KUNIT_CASE_PARAM_WITH_INIT``, to which you pass
760+
custom ``param_init()`` and ``param_exit()`` functions. These functions run once
761+
before and once after the parameterized test, respectively.
762+
763+
The ``param_init()`` function, with the signature ``int (*)(struct kunit *test)``,
764+
can be used for adding resources to the ``resources`` or ``priv`` fields of
765+
the parameterized test context, registering the parameter array, and any other
766+
initialization logic.
767+
768+
The ``param_exit()`` function, with the signature ``void (*)(struct kunit *test)``,
769+
can be used to release any resources that were not parameterized test managed (i.e.
770+
not automatically cleaned up after the parameterized test ends) and for any other
771+
exit logic.
772+
773+
Both ``param_init()`` and ``param_exit()`` are passed the parameterized test
774+
context behind the scenes. However, the test case function receives the parameter
775+
run context. Therefore, to manage and access shared resources from within a test
776+
case function, you must use ``test->parent``.
777+
778+
For instance, finding a shared resource allocated by the Resource API requires
779+
passing ``test->parent`` to ``kunit_find_resource()``. This principle extends to
780+
all other APIs that might be used in the test case function, including
781+
``kunit_kzalloc()``, ``kunit_kmalloc_array()``, and others (see
782+
Documentation/dev-tools/kunit/api/test.rst and the
783+
Documentation/dev-tools/kunit/api/resource.rst).
784+
785+
.. note::
786+
The ``suite->init()`` function, which executes before each parameter run,
787+
receives the parameter run context. Therefore, any resources set up in
788+
``suite->init()`` are cleaned up after each parameter run.
789+
790+
The code below shows how you can add the shared resources. Note that this code
791+
utilizes the Resource API, which you can read more about here:
792+
Documentation/dev-tools/kunit/api/resource.rst. To see the full version of this
793+
code please refer to lib/kunit/kunit-example-test.c.
794+
795+
.. code-block:: c
796+
797+
static int example_resource_init(struct kunit_resource *res, void *context)
798+
{
799+
... /* Code that allocates memory and stores context in res->data. */
800+
}
801+
802+
/* This function deallocates memory for the kunit_resource->data field. */
803+
static void example_resource_free(struct kunit_resource *res)
804+
{
805+
kfree(res->data);
806+
}
807+
808+
/* This match function locates a test resource based on defined criteria. */
809+
static bool example_resource_alloc_match(struct kunit *test, struct kunit_resource *res,
810+
void *match_data)
811+
{
812+
return res->data && res->free == example_resource_free;
813+
}
814+
815+
/* Function to initialize the parameterized test. */
816+
static int example_param_init(struct kunit *test)
817+
{
818+
int ctx = 3; /* Data to be stored. */
819+
void *data = kunit_alloc_resource(test, example_resource_init,
820+
example_resource_free,
821+
GFP_KERNEL, &ctx);
822+
if (!data)
823+
return -ENOMEM;
824+
kunit_register_params_array(test, example_params_array,
825+
ARRAY_SIZE(example_params_array));
826+
return 0;
827+
}
828+
829+
/* Example test that uses shared resources in test->resources. */
830+
static void example_params_test_with_init(struct kunit *test)
831+
{
832+
int threshold;
833+
const struct example_param *param = test->param_value;
834+
/* Here we pass test->parent to access the parameterized test context. */
835+
struct kunit_resource *res = kunit_find_resource(test->parent,
836+
example_resource_alloc_match,
837+
NULL);
838+
839+
threshold = *((int *)res->data);
840+
KUNIT_ASSERT_LE(test, param->value, threshold);
841+
kunit_put_resource(res);
842+
}
843+
844+
static struct kunit_case example_test_cases[] = {
845+
KUNIT_CASE_PARAM_WITH_INIT(example_params_test_with_init, kunit_array_gen_params,
846+
example_param_init, NULL),
847+
{}
848+
};
849+
850+
As an alternative to using the KUnit Resource API for sharing resources, you can
851+
place them in ``test->parent->priv``. This serves as a more lightweight method
852+
for resource storage, best for scenarios where complex resource management is
853+
not required.
854+
855+
As stated previously ``param_init()`` and ``param_exit()`` get the parameterized
856+
test context. So, you can directly use ``test->priv`` within ``param_init/exit``
857+
to manage shared resources. However, from within the test case function, you must
858+
navigate up to the parent ``struct kunit`` i.e. the parameterized test context.
859+
Therefore, you need to use ``test->parent->priv`` to access those same
860+
resources.
861+
862+
The resources placed in ``test->parent->priv`` will need to be allocated in
863+
memory to persist across the parameter runs. If memory is allocated using the
864+
KUnit memory allocation APIs (described more in the "Allocating Memory" section
865+
below), you won't need to worry about deallocation. The APIs will make the memory
866+
parameterized test 'managed', ensuring that it will automatically get cleaned up
867+
after the parameterized test concludes.
868+
869+
The code below demonstrates example usage of the ``priv`` field for shared
870+
resources:
871+
872+
.. code-block:: c
873+
874+
static const struct example_param {
875+
int value;
876+
} example_params_array[] = {
877+
{ .value = 3, },
878+
{ .value = 2, },
879+
{ .value = 1, },
880+
{ .value = 0, },
881+
};
882+
883+
/* Initialize the parameterized test context. */
884+
static int example_param_init_priv(struct kunit *test)
885+
{
886+
int ctx = 3; /* Data to be stored. */
887+
int arr_size = ARRAY_SIZE(example_params_array);
888+
889+
/*
890+
* Allocate memory using kunit_kzalloc(). Since the `param_init`
891+
* function receives the parameterized test context, this memory
892+
* allocation will be scoped to the lifetime of the parameterized test.
893+
*/
894+
test->priv = kunit_kzalloc(test, sizeof(int), GFP_KERNEL);
895+
896+
/* Assign the context value to test->priv.*/
897+
*((int *)test->priv) = ctx;
898+
899+
/* Register the parameter array. */
900+
kunit_register_params_array(test, example_params_array, arr_size, NULL);
901+
return 0;
902+
}
903+
904+
static void example_params_test_with_init_priv(struct kunit *test)
905+
{
906+
int threshold;
907+
const struct example_param *param = test->param_value;
908+
909+
/* By design, test->parent will not be NULL. */
910+
KUNIT_ASSERT_NOT_NULL(test, test->parent);
911+
912+
/* Here we use test->parent->priv to access the shared resource. */
913+
threshold = *(int *)test->parent->priv;
914+
915+
KUNIT_ASSERT_LE(test, param->value, threshold);
916+
}
917+
918+
static struct kunit_case example_tests[] = {
919+
KUNIT_CASE_PARAM_WITH_INIT(example_params_test_with_init_priv,
920+
kunit_array_gen_params,
921+
example_param_init_priv, NULL),
922+
{}
923+
};
924+
593925
Allocating Memory
594926
-----------------
595927

0 commit comments

Comments
 (0)