History

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

Linus Torvalds committed 1 year ago

Commit 30bbcb42 parents d2b2fea 285cae520 files · +880 −63Browse files

Showing the changes to lib (5 of 20 files). Show all

Unified · Split

5 files
  1. lib/Makefile +0 −4
  2. lib/kunit/Kconfig +11 −0
  3. lib/kunit/Makefile +1 −1
  4. lib/kunit/kunit-example-test.c +217 −0
  5. lib/kunit/test.c +79 −15
lib/Makefile+0−4
@@ -109,11 +109,7 @@ test_fpu-y := test_fpu_glue.o test_fpu_impl.o · 108 unmodified lines
109109 CFLAGS_test_fpu_impl.o += $(CC_FLAGS_FPU)
110110 CFLAGS_REMOVE_test_fpu_impl.o += $(CC_FLAGS_NO_FPU)
111111
112−# Some KUnit files (hooks.o) need to be built-in even when KUnit is a module,
113−# so we can't just use obj-$(CONFIG_KUNIT).
114−ifdef CONFIG_KUNIT
115112 obj-y += kunit/
116−endif
117113
118114 ifeq ($(CONFIG_DEBUG_KOBJECT),y)
119115 CFLAGS_kobject.o += -DDEBUG
lib/kunit/Kconfig+11−0
@@ -106,4 +106,15 @@ config KUNIT_DEFAULT_TIMEOUT · 105 unmodified lines
106106 If unsure, the default timeout of 300 seconds is suitable for most
107107 cases.
108108
109+config KUNIT_UML_PCI
110+ bool "KUnit UML PCI Support"
111+ depends on UML
112+ select UML_PCI
113+ help
114+ Enables the PCI subsystem on UML for use by KUnit tests.
115+ Some KUnit tests require the PCI core which is not enabled by
116+ default on UML.
117+
118+ If unsure, say N.
119+
109120 endif # KUNIT
lib/kunit/Makefile+1−1
@@ -17,7 +17,7 @@ kunit-objs += debugfs.o · 16 unmodified lines
1717 endif
1818
1919 # KUnit 'hooks' are built-in even when KUnit is built as a module.
20−obj-y += hooks.o
20+obj-$(if $(CONFIG_KUNIT),y) += hooks.o
2121
2222 obj-$(CONFIG_KUNIT_TEST) += kunit-test.o
2323 obj-$(CONFIG_KUNIT_TEST) += platform-test.o
lib/kunit/kunit-example-test.c+217−0
@@ -277,6 +277,218 @@ static void example_slow_test(struct kunit *test) · 276 unmodified lines
277277 KUNIT_EXPECT_EQ(test, 1 + 1, 2);
278278 }
279279
280+/*
281+ * This custom function allocates memory and sets the information we want
282+ * stored in the kunit_resource->data field.
283+ */
284+static int example_resource_init(struct kunit_resource *res, void *context)
285+{
286+ int *info = kmalloc(sizeof(*info), GFP_KERNEL);
287+
288+ if (!info)
289+ return -ENOMEM;
290+ *info = *(int *)context;
291+ res->data = info;
292+ return 0;
293+}
294+
295+/*
296+ * This function deallocates memory for the kunit_resource->data field.
297+ */
298+static void example_resource_free(struct kunit_resource *res)
299+{
300+ kfree(res->data);
301+}
302+
303+/*
304+ * This match function is invoked by kunit_find_resource() to locate
305+ * a test resource based on certain criteria.
306+ */
307+static bool example_resource_alloc_match(struct kunit *test,
308+ struct kunit_resource *res,
309+ void *match_data)
310+{
311+ return res->data && res->free == example_resource_free;
312+}
313+
314+/*
315+ * This is an example of a function that provides a description for each of the
316+ * parameters in a parameterized test.
317+ */
318+static void example_param_array_get_desc(struct kunit *test, const void *p, char *desc)
319+{
320+ const struct example_param *param = p;
321+
322+ snprintf(desc, KUNIT_PARAM_DESC_SIZE,
323+ "example check if %d is less than or equal to 3", param->value);
324+}
325+
326+/*
327+ * This function gets passed in the parameterized test context i.e. the
328+ * struct kunit belonging to the parameterized test. You can use this function
329+ * to add resources you want shared across the whole parameterized test or
330+ * for additional setup.
331+ */
332+static int example_param_init(struct kunit *test)
333+{
334+ int ctx = 3; /* Data to be stored. */
335+ size_t arr_size = ARRAY_SIZE(example_params_array);
336+
337+ /*
338+ * This allocates a struct kunit_resource, sets its data field to
339+ * ctx, and adds it to the struct kunit's resources list. Note that
340+ * this is parameterized test managed. So, it doesn't need to have
341+ * a custom exit function to deallocation as it will get cleaned up at
342+ * the end of the parameterized test.
343+ */
344+ void *data = kunit_alloc_resource(test, example_resource_init, example_resource_free,
345+ GFP_KERNEL, &ctx);
346+
347+ if (!data)
348+ return -ENOMEM;
349+ /*
350+ * Pass the parameter array information to the parameterized test context
351+ * struct kunit. Note that you will need to provide kunit_array_gen_params()
352+ * as the generator function to KUNIT_CASE_PARAM_WITH_INIT() when registering
353+ * a parameter array this route.
354+ */
355+ kunit_register_params_array(test, example_params_array, arr_size,
356+ example_param_array_get_desc);
357+ return 0;
358+}
359+
360+/*
361+ * This is an example of a test that uses shared resources available in the
362+ * parameterized test context.
363+ */
364+static void example_params_test_with_init(struct kunit *test)
365+{
366+ int threshold;
367+ struct kunit_resource *res;
368+ const struct example_param *param = test->param_value;
369+
370+ /* By design, param pointer will not be NULL. */
371+ KUNIT_ASSERT_NOT_NULL(test, param);
372+
373+ /*
374+ * Here we pass test->parent to search for shared resources in the
375+ * parameterized test context.
376+ */
377+ res = kunit_find_resource(test->parent, example_resource_alloc_match, NULL);
378+
379+ KUNIT_ASSERT_NOT_NULL(test, res);
380+
381+ /* Since kunit_resource->data is a void pointer we need to typecast it. */
382+ threshold = *((int *)res->data);
383+
384+ /* Assert that the parameter is less than or equal to a certain threshold. */
385+ KUNIT_ASSERT_LE(test, param->value, threshold);
386+
387+ /* This decreases the reference count after calling kunit_find_resource(). */
388+ kunit_put_resource(res);
389+}
390+
391+/*
392+ * Helper function to create a parameter array of Fibonacci numbers. This example
393+ * highlights a parameter generation scenario that is:
394+ * 1. Not feasible to fully pre-generate at compile time.
395+ * 2. Challenging to implement with a standard generate_params() function,
396+ * as it only provides the previous parameter, while Fibonacci requires
397+ * access to two preceding values for calculation.
398+ */
399+static void *make_fibonacci_params(struct kunit *test, size_t seq_size)
400+{
401+ int *seq;
402+
403+ if (seq_size <= 0)
404+ return NULL;
405+ /*
406+ * Using kunit_kmalloc_array here ties the lifetime of the array to
407+ * the parameterized test i.e. it will get automatically cleaned up
408+ * by KUnit after the parameterized test finishes.
409+ */
410+ seq = kunit_kmalloc_array(test, seq_size, sizeof(int), GFP_KERNEL);
411+
412+ if (!seq)
413+ return NULL;
414+ if (seq_size >= 1)
415+ seq[0] = 0;
416+ if (seq_size >= 2)
417+ seq[1] = 1;
418+ for (int i = 2; i < seq_size; i++)
419+ seq[i] = seq[i - 1] + seq[i - 2];
420+ return seq;
421+}
422+
423+/*
424+ * This is an example of a function that provides a description for each of the
425+ * parameters.
426+ */
427+static void example_param_dynamic_arr_get_desc(struct kunit *test, const void *p, char *desc)
428+{
429+ const int *fib_num = p;
430+
431+ snprintf(desc, KUNIT_PARAM_DESC_SIZE, "fibonacci param: %d", *fib_num);
432+}
433+
434+/*
435+ * Example of a parameterized test param_init() function that registers a dynamic
436+ * array of parameters.
437+ */
438+static int example_param_init_dynamic_arr(struct kunit *test)
439+{
440+ size_t seq_size;
441+ int *fibonacci_params;
442+
443+ kunit_info(test, "initializing parameterized test\n");
444+
445+ seq_size = 6;
446+ fibonacci_params = make_fibonacci_params(test, seq_size);
447+
448+ if (!fibonacci_params)
449+ return -ENOMEM;
450+
451+ /*
452+ * Passes the dynamic parameter array information to the parameterized test
453+ * context struct kunit. The array and its metadata will be stored in
454+ * test->parent->params_array. The array itself will be located in
455+ * params_data.params.
456+ *
457+ * Note that you will need to pass kunit_array_gen_params() as the
458+ * generator function to KUNIT_CASE_PARAM_WITH_INIT() when registering
459+ * a parameter array this route.
460+ */
461+ kunit_register_params_array(test, fibonacci_params, seq_size,
462+ example_param_dynamic_arr_get_desc);
463+ return 0;
464+}
465+
466+/*
467+ * Example of a parameterized test param_exit() function that outputs a log
468+ * at the end of the parameterized test. It could also be used for any other
469+ * teardown logic.
470+ */
471+static void example_param_exit_dynamic_arr(struct kunit *test)
472+{
473+ kunit_info(test, "exiting parameterized test\n");
474+}
475+
476+/*
477+ * Example of test that uses the registered dynamic array to perform assertions
478+ * and expectations.
479+ */
480+static void example_params_test_with_init_dynamic_arr(struct kunit *test)
481+{
482+ const int *param = test->param_value;
483+ int param_val;
484+
485+ /* By design, param pointer will not be NULL. */
486+ KUNIT_ASSERT_NOT_NULL(test, param);
487+
488+ param_val = *param;
489+ KUNIT_EXPECT_EQ(test, param_val - param_val, 0);
490+}
491+
280492 /*
281493 * Here we make a list of all the test cases we want to add to the test suite
282494 * below.
@@ -296,6 +508,11 @@ static struct kunit_case example_test_cases[] = { · 13 unmodified lines
296508 KUNIT_CASE(example_static_stub_using_fn_ptr_test),
297509 KUNIT_CASE(example_priv_test),
298510 KUNIT_CASE_PARAM(example_params_test, example_gen_params),
511+ KUNIT_CASE_PARAM_WITH_INIT(example_params_test_with_init, kunit_array_gen_params,
512+ example_param_init, NULL),
513+ KUNIT_CASE_PARAM_WITH_INIT(example_params_test_with_init_dynamic_arr,
514+ kunit_array_gen_params, example_param_init_dynamic_arr,
515+ example_param_exit_dynamic_arr),
299516 KUNIT_CASE_SLOW(example_slow_test),
300517 {}
301518 };
lib/kunit/test.c+79−15
@@ -337,6 +337,14 @@ void __kunit_do_failed_assertion(struct kunit *test, · 336 unmodified lines
337337 }
338338 EXPORT_SYMBOL_GPL(__kunit_do_failed_assertion);
339339
340+static void kunit_init_params(struct kunit *test)
341+{
342+ test->params_array.params = NULL;
343+ test->params_array.get_description = NULL;
344+ test->params_array.num_params = 0;
345+ test->params_array.elem_size = 0;
346+}
347+
340348 void kunit_init_test(struct kunit *test, const char *name, struct string_stream *log)
341349 {
342350 spin_lock_init(&test->lock);
@@ -347,6 +355,7 @@ void kunit_init_test(struct kunit *test, const char *name, struct string_stream · 4 unmodified lines
347355 string_stream_clear(log);
348356 test->status = KUNIT_SUCCESS;
349357 test->status_comment[0] = '\0';
358+ kunit_init_params(test);
350359 }
351360 EXPORT_SYMBOL_GPL(kunit_init_test);
352361
@@ -641,12 +650,44 @@ static void kunit_accumulate_stats(struct kunit_result_stats *total, · 288 unmodified lines
641650 total->total += add.total;
642651 }
643652
653+const void *kunit_array_gen_params(struct kunit *test, const void *prev, char *desc)
654+{
655+ struct kunit_params *params_arr = &test->params_array;
656+ const void *param;
657+
658+ if (test->param_index < params_arr->num_params) {
659+ param = (char *)params_arr->params
660+ + test->param_index * params_arr->elem_size;
661+
662+ if (params_arr->get_description)
663+ params_arr->get_description(test, param, desc);
664+ return param;
665+ }
666+ return NULL;
667+}
668+EXPORT_SYMBOL_GPL(kunit_array_gen_params);
669+
670+static void kunit_init_parent_param_test(struct kunit_case *test_case, struct kunit *test)
671+{
672+ if (test_case->param_init) {
673+ int err = test_case->param_init(test);
674+
675+ if (err) {
676+ kunit_err(test_case, KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT
677+ "# failed to initialize parent parameter test (%d)", err);
678+ test->status = KUNIT_FAILURE;
679+ test_case->status = KUNIT_FAILURE;
680+ }
681+ }
682+}
683+
644684 int kunit_run_tests(struct kunit_suite *suite)
645685 {
646686 char param_desc[KUNIT_PARAM_DESC_SIZE];
647687 struct kunit_case *test_case;
648688 struct kunit_result_stats suite_stats = { 0 };
649689 struct kunit_result_stats total_stats = { 0 };
690+ const void *curr_param;
650691
651692 /* Taint the kernel so we know we've run tests. */
652693 add_taint(TAINT_TEST, LOCKDEP_STILL_OK);
@@ -677,41 +718,64 @@ int kunit_run_tests(struct kunit_suite *suite) · 24 unmodified lines
677718 kunit_run_case_catch_errors(suite, test_case, &test);
678719 kunit_update_stats(&param_stats, test.status);
679720 } else {
721+ kunit_init_parent_param_test(test_case, &test);
722+ if (test_case->status == KUNIT_FAILURE) {
723+ kunit_update_stats(&param_stats, test.status);
724+ goto test_case_end;
725+ }
680726 /* Get initial param. */
681727 param_desc[0] = '\0';
682− test.param_value = test_case->generate_params(NULL, param_desc);
728+ /* TODO: Make generate_params try-catch */
729+ curr_param = test_case->generate_params(&test, NULL, param_desc);
683730 test_case->status = KUNIT_SKIPPED;
684731 kunit_log(KERN_INFO, &test, KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT
685732 "KTAP version 1\n");
686733 kunit_log(KERN_INFO, &test, KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT
687734 "# Subtest: %s", test_case->name);
735+ if (test.params_array.params &&
736+ test_case->generate_params == kunit_array_gen_params) {
737+ kunit_log(KERN_INFO, &test, KUNIT_SUBTEST_INDENT
738+ KUNIT_SUBTEST_INDENT "1..%zd\n",
739+ test.params_array.num_params);
740+ }
688741
689− while (test.param_value) {
690− kunit_run_case_catch_errors(suite, test_case, &test);
742+ while (curr_param) {
743+ struct kunit param_test = {
744+ .param_value = curr_param,
745+ .param_index = ++test.param_index,
746+ .parent = &test,
747+ };
748+ kunit_init_test(&param_test, test_case->name, test_case->log);
749+ kunit_run_case_catch_errors(suite, test_case, &param_test);
691750
692751 if (param_desc[0] == '\0') {
693752 snprintf(param_desc, sizeof(param_desc),
694− "param-%d", test.param_index);
753+ "param-%d", param_test.param_index);
695754 }
696755
697− kunit_print_ok_not_ok(&test, KUNIT_LEVEL_CASE_PARAM,
698− test.status,
699− test.param_index + 1,
756+ kunit_print_ok_not_ok(&param_test, KUNIT_LEVEL_CASE_PARAM,
757+ param_test.status,
758+ param_test.param_index,
700759 param_desc,
701− test.status_comment);
760+ param_test.status_comment);
702761
703− kunit_update_stats(&param_stats, test.status);
762+ kunit_update_stats(&param_stats, param_test.status);
704763
705764 /* Get next param. */
706765 param_desc[0] = '\0';
707− test.param_value = test_case->generate_params(test.param_value, param_desc);
708− test.param_index++;
709− test.status = KUNIT_SUCCESS;
710− test.status_comment[0] = '\0';
711− test.priv = NULL;
766+ curr_param = test_case->generate_params(&test, curr_param,
767+ param_desc);
712768 }
769+ /*
770+ * TODO: Put into a try catch. Since we don't need suite->exit
771+ * for it we can't reuse kunit_try_run_cleanup for this yet.
772+ */
773+ if (test_case->param_exit)
774+ test_case->param_exit(&test);
775+ /* TODO: Put this kunit_cleanup into a try-catch. */
776+ kunit_cleanup(&test);
713777 }
714−
778+test_case_end:
715779 kunit_print_attr((void *)test_case, true, KUNIT_LEVEL_CASE);
716780
717781 kunit_print_test_stats(&test, param_stats);

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t