Skip to content

Commit 93cf798

Browse files
Marie Zhussupovashuahkh
authored andcommitted
Documentation: kunit: Document new parameterized test features
This patch updates the KUnit docs to show how to use the new parameterized test context to share resources between parameter runs. It documents and show examples of different ways the test user can pass parameter arrays to a parameterized test. Finally, it specifies the parameterized testing terminology. Link: https://lore.kernel.org/r/20250826091341.1427123-8-davidgow@google.com Reviewed-by: Rae Moar <rmoar@google.com> Reviewed-by: David Gow <davidgow@google.com> Signed-off-by: Marie Zhussupova <marievic@google.com> Signed-off-by: David Gow <davidgow@google.com> Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
1 parent f9687f3 commit 93cf798

1 file changed

Lines changed: 337 additions & 5 deletions

File tree

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)