On Fri, Aug 15, 2025 at 6:36 AM Marie Zhussupova <marie...@google.com> wrote: > > KUnit parameterized tests currently support two primary methods f > or getting parameters: > 1. Defining custom logic within a generate_params() function. > 2. 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. > > This patch addresses these limitations by adding a new `params_array` > 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_array` field can be populated by calling the new > kunit_register_params_array() macro from within a param_init() function. > This will register the array as part of the parameterized test context. > The user will then need to pass kunit_array_gen_params() to the > KUNIT_CASE_PARAM_WITH_INIT() macro as the generator function, if not > providing their own. kunit_array_gen_params() is a KUnit helper that will > use the registered array to generate parameters. > > The arrays passed to KUNIT_ARRAY_PARAM(,DESC) will also be registered to > the parameterized test context for consistency as well as for higher > availability of the parameter count that will be used for outputting a KTAP > test plan for a parameterized test. > > This modification provides greater flexibility to the KUnit framework, > allowing testers to easily register and utilize both dynamic and static > parameter arrays. > > Reviewed-by: David Gow <david...@google.com> > Signed-off-by: Marie Zhussupova <marie...@google.com>
Hello! This patch series is looking great! Happy to add this as an improvement to the KUnit framework. Reviewed-by: Rae Moar <rm...@google.com> Thanks! -Rae > --- > > Changes in v3: > v2: https://lore.kernel.org/all/20250811221739.2694336-5-marie...@google.com/ > - Commit message formatting. > > Changes in v2: > v1: https://lore.kernel.org/all/20250729193647.3410634-7-marie...@google.com/ > - If the parameter count is available for a parameterized test, the > kunit_run_tests() function will now output the KTAP test plan for it. > - The name of the struct kunit_params field in struct kunit was changed > from params_data to params_array. This name change better reflects its > purpose, which is to encapsulate both the parameter array and its > associated metadata. > - The name of `kunit_get_next_param_and_desc` was changed to > `kunit_array_gen_params` to make it simpler and to better fit its purpose > of being KUnit's built-in generator function that uses arrays to generate > parameters. > - The signature of get_description() in `struct params_array` was changed to > accept the parameterized test context, as well. This way test users can > potentially use information available in the parameterized test context, > such as the parameterized test name for setting the parameter descriptions. > - The type of `num_params` in `struct params_array` was changed from int to > size_t for better handling of the array size. > - The name of __kunit_init_params() was changed to be kunit_init_params(). > Logic that sets the get_description() function pointer to NULL was also > added in there. > - `kunit_array_gen_params` is now exported to make it available to use > with modules. > - Instead of allowing NULL to be passed in as the parameter generator > function in the KUNIT_CASE_PARAM_WITH_INIT macro, users will now be asked > to provide `kunit_array_gen_params` as the generator function. This will > ensure that a parameterized test remains defined by the existence of a > parameter generation function. > - KUNIT_ARRAY_PARAM(,DESC) will now additionally register the passed in array > in struct kunit_params. This will make things more consistent i.e. if a > parameter array is available then the struct kunit_params field in parent > struct kunit is populated. Additionally, this will increase the > availability of the KTAP test plan. > - The comments and the commit message were changed to reflect the > parameterized testing terminology. See the patch series cover letter > change log for the definitions. > > --- > include/kunit/test.h | 65 ++++++++++++++++++++++++++++++++++++++++---- > lib/kunit/test.c | 30 ++++++++++++++++++++ > 2 files changed, 89 insertions(+), 6 deletions(-) > > diff --git a/include/kunit/test.h b/include/kunit/test.h > index b527189d2d1c..8cc9614a88d5 100644 > --- a/include/kunit/test.h > +++ b/include/kunit/test.h > @@ -234,9 +234,13 @@ static inline char *kunit_status_to_ok_not_ok(enum > kunit_status status) > * Provides the option to register param_init() and param_exit() functions. > * param_init/exit will be passed the parameterized test context and run once > * before and once after the parameterized test. The init function can be > used > - * to add resources to share between parameter runs, and any other setup > logic. > - * The exit function can be used to clean up resources that were not managed > by > - * the parameterized test, and any other teardown logic. > + * to add resources to share between parameter runs, pass parameter arrays, > + * and any other setup logic. The exit function can be used to clean up > resources > + * that were not managed by the parameterized test, and any other teardown > logic. > + * > + * Note: If you are registering a parameter array in param_init() with > + * kunit_register_param_array() then you need to pass > kunit_array_gen_params() > + * to this as the generator function. > */ > #define KUNIT_CASE_PARAM_WITH_INIT(test_name, gen_params, init, exit) > \ > { .run_case = test_name, .name = #test_name, > \ > @@ -289,6 +293,20 @@ 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 a parameterized test. This > + * is NULL if a parameter array wasn't directly passed to the > + * parameterized test context struct kunit via > kunit_register_params_array(). > + */ > + const void *params; > + /* Reference to a function that gets the description of a parameter. > */ > + void (*get_description)(struct kunit *test, const void *param, char > *desc); > + size_t num_params; > + size_t elem_size; > +}; > + > /** > * struct kunit - represents a running instance of a test. > * > @@ -296,16 +314,18 @@ struct kunit_suite_set { > * created in the init function (see &struct kunit_suite). > * @parent: reference to the parent context of type struct kunit that can > * be used for storing shared resources. > + * @params_array: for storing 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 and access > the > - * parent context, respectively. > + * indirectly via public functions; the exceptions are @priv, @parent and > + * @params_array which can be used by the test writer to store arbitrary > data, > + * access the parent context, and to store the parameter array, respectively. > */ > struct kunit { > void *priv; > struct kunit *parent; > + struct kunit_params params_array; > > /* private: internal use only. */ > const char *name; /* Read only after initialization! */ > @@ -376,6 +396,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_array_gen_params(struct kunit *test, const void *prev, > char *desc); > + > #if IS_BUILTIN(CONFIG_KUNIT) > int kunit_run_all_tests(void); > #else > @@ -1696,6 +1718,8 @@ do { > \ > const void *prev, char *desc) > \ > { > \ > typeof((array)[0]) *__next = prev ? ((typeof(__next)) prev) + > 1 : (array); \ > + if (!prev) > \ > + kunit_register_params_array(test, array, > ARRAY_SIZE(array), NULL); \ > if (__next - (array) < ARRAY_SIZE((array))) { > \ > void (*__get_desc)(typeof(__next), char *) = > get_desc; \ > if (__get_desc) > \ > @@ -1718,6 +1742,8 @@ do { > \ > const void *prev, char *desc) > \ > { > \ > typeof((array)[0]) *__next = prev ? ((typeof(__next)) prev) + > 1 : (array); \ > + if (!prev) > \ > + kunit_register_params_array(test, array, > ARRAY_SIZE(array), NULL); \ > if (__next - (array) < ARRAY_SIZE((array))) { > \ > strscpy(desc, __next->desc_member, > KUNIT_PARAM_DESC_SIZE); \ > return __next; > \ > @@ -1725,6 +1751,33 @@ do { > \ > return NULL; > \ > } > > +/** > + * kunit_register_params_array() - Register parameter array for a KUnit test. > + * @test: The KUnit test structure to which parameters will be added. > + * @array: An array of test parameters. > + * @param_count: Number of parameters. > + * @get_desc: 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_array`. > + * > + * Note: If using this macro in param_init(), kunit_array_gen_params() > + * will then need to be manually provided as the parameter generator > function to > + * KUNIT_CASE_PARAM_WITH_INIT(). kunit_array_gen_params() is a KUnit > + * function that uses the registered array to generate parameters > + */ > +#define kunit_register_params_array(test, array, param_count, get_desc) > \ > + do { > \ > + struct kunit *_test = (test); > \ > + const typeof((array)[0]) * _params_ptr = &(array)[0]; > \ > + _test->params_array.params = _params_ptr; > \ > + _test->params_array.num_params = (param_count); > \ > + _test->params_array.elem_size = sizeof(*_params_ptr); > \ > + _test->params_array.get_description = (get_desc); > \ > + } while (0) > + > // TODO(dlaty...@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 ac8fa8941a6a..ce4bb93f09f4 100644 > --- a/lib/kunit/test.c > +++ b/lib/kunit/test.c > @@ -337,6 +337,14 @@ 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_array.params = NULL; > + test->params_array.get_description = NULL; > + test->params_array.num_params = 0; > + test->params_array.elem_size = 0; > +} > + > void kunit_init_test(struct kunit *test, const char *name, struct > string_stream *log) > { > spin_lock_init(&test->lock); > @@ -347,6 +355,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 +650,23 @@ static void kunit_accumulate_stats(struct > kunit_result_stats *total, > total->total += add.total; > } > > +const void *kunit_array_gen_params(struct kunit *test, const void *prev, > char *desc) > +{ > + struct kunit_params *params_arr = &test->params_array; > + 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(test, param, desc); > + return param; > + } > + return NULL; > +} > +EXPORT_SYMBOL_GPL(kunit_array_gen_params); > + > static void kunit_init_parent_param_test(struct kunit_case *test_case, > struct kunit *test) > { > if (test_case->param_init) { > @@ -706,6 +732,10 @@ int kunit_run_tests(struct kunit_suite *suite) > "KTAP version 1\n"); > kunit_log(KERN_INFO, &test, KUNIT_SUBTEST_INDENT > KUNIT_SUBTEST_INDENT > "# Subtest: %s", test_case->name); > + if (test.params_array.params) > + kunit_log(KERN_INFO, &test, > KUNIT_SUBTEST_INDENT > + KUNIT_SUBTEST_INDENT "1..%zd\n", > + test.params_array.num_params); > > while (curr_param) { > struct kunit param_test = { > -- > 2.51.0.rc1.167.g924127e9c0-goog >