Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Marie Zhussupova and committed by
Shuah Khan
93cf798e f9687f35

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