[PATCH 6/9] kunit: Enable direct registration of parameter arrays to a KUnit test
Rae Moar
rmoar at google.com
Tue Aug 5 15:18:42 UTC 2025
On Tue, Jul 29, 2025 at 3:37 PM Marie Zhussupova <marievic at google.com> wrote:
>
> KUnit parameterized tests currently support two
> primary methods for getting parameters:
> 1. Defining custom logic within a `generate_params`
> function.
> 2. Using the KUNIT_ARRAY_PARAM and KUNIT_ARRAY_PARAM_DESC
> macros with pre-defined static arrays.
>
> 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.
>
> This patch addresses these limitations by adding a new
> `params_data` field to `struct kunit`, of the type
> `kunit_params`. The struct `kunit_params` is designed to
> store the parameter array itself, along with essential metadata
> including the parameter count, parameter size, and a
> `get_description` function for providing custom descriptions
> for individual parameters.
>
> The `params_data` field can be populated by calling the new
> `kunit_register_params_array` macro from within a
> `param_init` function. By attaching the parameter array
> directly to the parent kunit test instance, these parameters
> can be iterated over in kunit_run_tests() behind the scenes.
>
> This modification provides greater flexibility to the
> KUnit framework, allowing testers to easily register and
> utilize both dynamic and static parameter arrays.
>
> Signed-off-by: Marie Zhussupova <marievic at google.com>
Hello!
Very excited by the prospect of setting up an array dynamically
instead of statically for parameterized tests. In general, I am happy
to see this framework is becoming more flexible and therefore more
tailored to our test author's needs.
I already commented on the modpost error but I have a few more
comments and ideas below. Let me know what you think.
Thanks!
-Rae
> ---
> include/kunit/test.h | 54 ++++++++++++++++++++++++++++++++++++++++----
> lib/kunit/test.c | 26 ++++++++++++++++++++-
> 2 files changed, 75 insertions(+), 5 deletions(-)
>
> diff --git a/include/kunit/test.h b/include/kunit/test.h
> index 4ba65dc35710..9143f0e22323 100644
> --- a/include/kunit/test.h
> +++ b/include/kunit/test.h
> @@ -245,7 +245,8 @@ static inline char *kunit_status_to_ok_not_ok(enum kunit_status status)
> */
> #define KUNIT_CASE_PARAM_WITH_INIT(test_name, gen_params, init, exit) \
> { .run_case = test_name, .name = #test_name, \
> - .generate_params = gen_params, \
> + .generate_params = (gen_params) \
> + ?: kunit_get_next_param_and_desc, \
> .param_init = init, .param_exit = exit, \
> .module_name = KBUILD_MODNAME}
>
> @@ -294,6 +295,21 @@ struct kunit_suite_set {
> struct kunit_suite * const *end;
> };
>
> +/* Stores the pointer to the parameter array and its metadata. */
> +struct kunit_params {
> + /*
> + * Reference to the parameter array for the parameterized tests. This
> + * is NULL if a parameter array wasn't directly passed to the
> + * parent kunit struct via the kunit_register_params_array macro.
> + */
> + const void *params;
> + /* Reference to a function that gets the description of a parameter. */
> + void (*get_description)(const void *param, char *desc);
> +
> + int num_params;
Since in some cases we know the number of params within a series/suite
of the parameterized tests, is it possible for us to print a test plan
line in KTAP when this number is known? This would be helpful for
reading test results but also the parser could verify the number of
subtests is the number expected.
> + size_t elem_size;
> +};
> +
> /**
> * struct kunit - represents a running instance of a test.
> *
> @@ -302,12 +318,14 @@ struct kunit_suite_set {
> * @parent: for user to store data that they want to shared across
> * parameterized tests. Typically, the data is provided in
> * the param_init function (see &struct kunit_case).
> + * @params_data: for users to directly store the parameter array.
> *
> * 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 two exceptions are @priv and @parent
> - * which can be used by the test writer to store arbitrary data or data that is
> - * available to all parameter test executions, respectively.
> + * indirectly via public functions. There are three exceptions to this: @priv,
> + * @parent, and @params_data. These members can be used by the test writer to
> + * store arbitrary data, data available to all parameter test executions, and
> + * the parameter array, respectively.
> */
> struct kunit {
> void *priv;
> @@ -316,6 +334,8 @@ struct kunit {
> * during parameterized testing.
> */
> struct kunit *parent;
> + /* Stores the params array and all data related to it. */
> + struct kunit_params params_data;
I might slightly prefer the term params_array rather than params_data.
Up to what you prefer.
>
> /* private: internal use only. */
> const char *name; /* Read only after initialization! */
> @@ -386,6 +406,8 @@ void kunit_exec_list_tests(struct kunit_suite_set *suite_set, bool include_attr)
> struct kunit_suite_set kunit_merge_suite_sets(struct kunit_suite_set init_suite_set,
> struct kunit_suite_set suite_set);
>
> +const void *kunit_get_next_param_and_desc(struct kunit *test, const void *prev, char *desc);
> +
> #if IS_BUILTIN(CONFIG_KUNIT)
> int kunit_run_all_tests(void);
> #else
> @@ -1735,6 +1757,30 @@ do { \
> return NULL; \
> }
>
> +/**
> + * kunit_register_params_array() - Register parameters for a KUnit test.
> + * @test: The KUnit test structure to which parameters will be added.
> + * @params_arr: An array of test parameters.
> + * @param_cnt: Number of parameters.
> + * @get_desc: A pointer to a function that generates a string description for
> + * a given parameter element.
> + *
> + * This macro initializes the @test's parameter array data, storing information
> + * including the parameter array, its count, the element size, and the parameter
> + * description function within `test->params_data`. KUnit's built-in
> + * `kunit_get_next_param_and_desc` function will automatically read this
> + * data when a custom `generate_params` function isn't provided.
> + */
> +#define kunit_register_params_array(test, params_arr, param_cnt, get_desc) \
I also might slightly prefer params_array and param_count here instead
of params_arr and param_cnt. Again this is definitely a nitpick so up
to you.
> + do { \
> + struct kunit *_test = (test); \
> + const typeof((params_arr)[0]) * _params_ptr = &(params_arr)[0]; \
> + _test->params_data.params = _params_ptr; \
> + _test->params_data.num_params = (param_cnt); \
> + _test->params_data.elem_size = sizeof(*_params_ptr); \
> + _test->params_data.get_description = (get_desc); \
> + } while (0)
> +
> // TODO(dlatypov at google.com): consider eventually migrating users to explicitly
> // include resource.h themselves if they need it.
> #include <kunit/resource.h>
> diff --git a/lib/kunit/test.c b/lib/kunit/test.c
> index f50ef82179c4..2f4b7087db3f 100644
> --- a/lib/kunit/test.c
> +++ b/lib/kunit/test.c
> @@ -337,6 +337,13 @@ void __kunit_do_failed_assertion(struct kunit *test,
> }
> EXPORT_SYMBOL_GPL(__kunit_do_failed_assertion);
>
> +static void __kunit_init_params(struct kunit *test)
> +{
> + test->params_data.params = NULL;
> + test->params_data.num_params = 0;
> + test->params_data.elem_size = 0;
> +}
> +
> void kunit_init_test(struct kunit *test, const char *name, struct string_stream *log)
> {
> spin_lock_init(&test->lock);
> @@ -347,6 +354,7 @@ void kunit_init_test(struct kunit *test, const char *name, struct string_stream
> string_stream_clear(log);
> test->status = KUNIT_SUCCESS;
> test->status_comment[0] = '\0';
> + __kunit_init_params(test);
> }
> EXPORT_SYMBOL_GPL(kunit_init_test);
>
> @@ -641,6 +649,22 @@ static void kunit_accumulate_stats(struct kunit_result_stats *total,
> total->total += add.total;
> }
>
> +const void *kunit_get_next_param_and_desc(struct kunit *test, const void *prev, char *desc)
> +{
> + struct kunit_params *params_arr = &test->params_data;
> + const void *param;
> +
> + if (test->param_index < params_arr->num_params) {
> + param = (char *)params_arr->params
> + + test->param_index * params_arr->elem_size;
> +
> + if (params_arr->get_description)
> + params_arr->get_description(param, desc);
> + return param;
> + }
> + return NULL;
> +}
I also agree with David that it should definitely be considered: 1 -
whether to utilize struct kunit_params for the case of using
KUNIT_ARRAY_PARAM and 2 - whether the user should actively input this
function instead of setting generate_params to NULL.
Another idea that just popped into my head is if we have access to
struct kunit* test now in all of the generate_params functions,
instead of setting a "desc" could we just set the test->name field?
> +
> static void __kunit_init_parent_test(struct kunit_case *test_case, struct kunit *test)
> {
> if (test_case->param_init) {
> @@ -687,7 +711,7 @@ int kunit_run_tests(struct kunit_suite *suite)
> /* Test marked as skip */
> test.status = KUNIT_SKIPPED;
> kunit_update_stats(¶m_stats, test.status);
> - } else if (!test_case->generate_params) {
> + } else if (!test_case->generate_params && !test.params_data.params) {
I agree with David that it is helpful to have one check for whether a
test is a parameterized test rather than two. My instinct is that if
test_case->generate_params is NULL it should be safe to assume the
test isn't a parameterized test.
However, as an alternative or even as a helpful addition, I like the
idea of a simple kunit_test_is_param function that can pass in the
test and it will return a bool whether the test is parameterized or
not.
> /* Non-parameterised test. */
> test_case->status = KUNIT_SKIPPED;
> kunit_run_case_catch_errors(suite, test_case, &test);
> --
> 2.50.1.552.g942d659e1b-goog
>
More information about the Intel-xe
mailing list