From e30f65c4b3d671115bf2a9d9ef142285387f2aff Mon Sep 17 00:00:00 2001 From: Daniel Latypov Date: Mon, 1 May 2023 11:16:10 -0700 Subject: kunit: tool: undo type subscripts for subprocess.Popen Writing `subprocess.Popen[str]` requires python 3.9+. kunit.py has an assertion that the python version is 3.7+, so we should try to stay backwards compatible. This conflicts a bit with commit 1da2e6220e11 ("kunit: tool: fix pre-existing `mypy --strict` errors and update run_checks.py"), since mypy complains like so > kunit_kernel.py:95: error: Missing type parameters for generic type "Popen" [type-arg] Note: `mypy --strict --python-version 3.7` does not work. We could annotate each file with comments like `# mypy: disable-error-code="type-arg" but then we might still get nudged to break back-compat in other files. This patch adds a `mypy.ini` file since it seems like the only way to disable specific error codes for all our files. Note: run_checks.py doesn't need to specify `--config_file mypy.ini`, but I think being explicit is better, particularly since most kernel devs won't be familiar with how mypy works. Fixes: 695e26030858 ("kunit: tool: add subscripts for type annotations where appropriate") Reported-by: SeongJae Park Link: https://lore.kernel.org/linux-kselftest/20230501171520.138753-1-sj@kernel.org Signed-off-by: Daniel Latypov Tested-by: SeongJae Park Reviewed-by: David Gow Signed-off-by: Shuah Khan --- tools/testing/kunit/kunit_kernel.py | 6 +++--- tools/testing/kunit/mypy.ini | 6 ++++++ tools/testing/kunit/run_checks.py | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) create mode 100644 tools/testing/kunit/mypy.ini diff --git a/tools/testing/kunit/kunit_kernel.py b/tools/testing/kunit/kunit_kernel.py index f01f94106129..7f648802caf6 100644 --- a/tools/testing/kunit/kunit_kernel.py +++ b/tools/testing/kunit/kunit_kernel.py @@ -92,7 +92,7 @@ class LinuxSourceTreeOperations: if stderr: # likely only due to build warnings print(stderr.decode()) - def start(self, params: List[str], build_dir: str) -> subprocess.Popen[str]: + def start(self, params: List[str], build_dir: str) -> subprocess.Popen: raise RuntimeError('not implemented!') @@ -113,7 +113,7 @@ class LinuxSourceTreeOperationsQemu(LinuxSourceTreeOperations): kconfig.merge_in_entries(base_kunitconfig) return kconfig - def start(self, params: List[str], build_dir: str) -> subprocess.Popen[str]: + def start(self, params: List[str], build_dir: str) -> subprocess.Popen: kernel_path = os.path.join(build_dir, self._kernel_path) qemu_command = ['qemu-system-' + self._qemu_arch, '-nodefaults', @@ -142,7 +142,7 @@ class LinuxSourceTreeOperationsUml(LinuxSourceTreeOperations): kconfig.merge_in_entries(base_kunitconfig) return kconfig - def start(self, params: List[str], build_dir: str) -> subprocess.Popen[str]: + def start(self, params: List[str], build_dir: str) -> subprocess.Popen: """Runs the Linux UML binary. Must be named 'linux'.""" linux_bin = os.path.join(build_dir, 'linux') params.extend(['mem=1G', 'console=tty', 'kunit_shutdown=halt']) diff --git a/tools/testing/kunit/mypy.ini b/tools/testing/kunit/mypy.ini new file mode 100644 index 000000000000..ddd288309efa --- /dev/null +++ b/tools/testing/kunit/mypy.ini @@ -0,0 +1,6 @@ +[mypy] +strict = True + +# E.g. we can't write subprocess.Popen[str] until Python 3.9+. +# But kunit.py tries to support Python 3.7+, so let's disable it. +disable_error_code = type-arg diff --git a/tools/testing/kunit/run_checks.py b/tools/testing/kunit/run_checks.py index 8208c3b3135e..c6d494ea3373 100755 --- a/tools/testing/kunit/run_checks.py +++ b/tools/testing/kunit/run_checks.py @@ -23,7 +23,7 @@ commands: Dict[str, Sequence[str]] = { 'kunit_tool_test.py': ['./kunit_tool_test.py'], 'kunit smoke test': ['./kunit.py', 'run', '--kunitconfig=lib/kunit', '--build_dir=kunit_run_checks'], 'pytype': ['/bin/sh', '-c', 'pytype *.py'], - 'mypy': ['mypy', '--strict', '--exclude', '_test.py$', '--exclude', 'qemu_configs/', '.'], + 'mypy': ['mypy', '--config-file', 'mypy.ini', '--exclude', '_test.py$', '--exclude', 'qemu_configs/', '.'], } # The user might not have mypy or pytype installed, skip them if so. -- cgit v1.2.3 From 78227fa03c6854e8ca23911309c763d909653c6e Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 2 May 2023 12:06:38 +0200 Subject: Documentation: kunit: Modular tests should not depend on KUNIT=y When the documentation was updated for modular tests, the dependency on "KUNIT=y" was forgotten to be updated, now encouraging people to create tests that cannot be enabled when the KUNIT framework itself is modular. Fix this by changing the dependency to "KUNIT". Document when it is appropriate (and required) to depend on "KUNIT=y". Fixes: c9ef2d3e3f3b ("KUnit: Docs: make start.rst example Kconfig follow style.rst") Signed-off-by: Geert Uytterhoeven Reviewed-by: David Gow Signed-off-by: Shuah Khan --- Documentation/dev-tools/kunit/start.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Documentation/dev-tools/kunit/start.rst b/Documentation/dev-tools/kunit/start.rst index c736613c9b19..9619a0440930 100644 --- a/Documentation/dev-tools/kunit/start.rst +++ b/Documentation/dev-tools/kunit/start.rst @@ -256,9 +256,12 @@ Now we are ready to write the test cases. config MISC_EXAMPLE_TEST tristate "Test for my example" if !KUNIT_ALL_TESTS - depends on MISC_EXAMPLE && KUNIT=y + depends on MISC_EXAMPLE && KUNIT default KUNIT_ALL_TESTS +Note: If your test does not support being built as a loadable module (which is +discouraged), replace tristate by bool, and depend on KUNIT=y instead of KUNIT. + 3. Add the following lines to ``drivers/misc/Makefile``: .. code-block:: make -- cgit v1.2.3 From 55e8c1b49ac5a7f5567430cc83f2d270d8b7ce46 Mon Sep 17 00:00:00 2001 From: David Gow Date: Wed, 10 May 2023 13:38:29 +0800 Subject: kunit: Always run cleanup from a test kthread KUnit tests run in a kthread, with the current->kunit_test pointer set to the test's context. This allows the kunit_get_current_test() and kunit_fail_current_test() macros to work. Normally, this pointer is still valid during test shutdown (i.e., the suite->exit function, and any resource cleanup). However, if the test has exited early (e.g., due to a failed assertion), the cleanup is done in the parent KUnit thread, which does not have an active context. Instead, in the event test terminates early, run the test exit and cleanup from a new 'cleanup' kthread, which sets current->kunit_test, and better isolates the rest of KUnit from issues which arise in test cleanup. If a test cleanup function itself aborts (e.g., due to an assertion failing), there will be no further attempts to clean up: an error will be logged and the test failed. For example: # example_simple_test: test aborted during cleanup. continuing without cleaning up This should also make it easier to get access to the KUnit context, particularly from within resource cleanup functions, which may, for example, need access to data in test->priv. Reviewed-by: Benjamin Berg Reviewed-by: Maxime Ripard Tested-by: Maxime Ripard Signed-off-by: David Gow Signed-off-by: Shuah Khan --- lib/kunit/test.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 48 insertions(+), 8 deletions(-) diff --git a/lib/kunit/test.c b/lib/kunit/test.c index e2910b261112..f5e4ceffd282 100644 --- a/lib/kunit/test.c +++ b/lib/kunit/test.c @@ -419,15 +419,54 @@ static void kunit_try_run_case(void *data) * thread will resume control and handle any necessary clean up. */ kunit_run_case_internal(test, suite, test_case); - /* This line may never be reached. */ +} + +static void kunit_try_run_case_cleanup(void *data) +{ + struct kunit_try_catch_context *ctx = data; + struct kunit *test = ctx->test; + struct kunit_suite *suite = ctx->suite; + + current->kunit_test = test; + kunit_run_case_cleanup(test, suite); } +static void kunit_catch_run_case_cleanup(void *data) +{ + struct kunit_try_catch_context *ctx = data; + struct kunit *test = ctx->test; + int try_exit_code = kunit_try_catch_get_result(&test->try_catch); + + /* It is always a failure if cleanup aborts. */ + kunit_set_failure(test); + + if (try_exit_code) { + /* + * Test case could not finish, we have no idea what state it is + * in, so don't do clean up. + */ + if (try_exit_code == -ETIMEDOUT) { + kunit_err(test, "test case cleanup timed out\n"); + /* + * Unknown internal error occurred preventing test case from + * running, so there is nothing to clean up. + */ + } else { + kunit_err(test, "internal error occurred during test case cleanup: %d\n", + try_exit_code); + } + return; + } + + kunit_err(test, "test aborted during cleanup. continuing without cleaning up\n"); +} + + static void kunit_catch_run_case(void *data) { struct kunit_try_catch_context *ctx = data; struct kunit *test = ctx->test; - struct kunit_suite *suite = ctx->suite; int try_exit_code = kunit_try_catch_get_result(&test->try_catch); if (try_exit_code) { @@ -448,12 +487,6 @@ static void kunit_catch_run_case(void *data) } return; } - - /* - * Test case was run, but aborted. It is the test case's business as to - * whether it failed or not, we just need to clean up. - */ - kunit_run_case_cleanup(test, suite); } /* @@ -478,6 +511,13 @@ static void kunit_run_case_catch_errors(struct kunit_suite *suite, context.test_case = test_case; kunit_try_catch_run(try_catch, &context); + /* Now run the cleanup */ + kunit_try_catch_init(try_catch, + test, + kunit_try_run_case_cleanup, + kunit_catch_run_case_cleanup); + kunit_try_catch_run(try_catch, &context); + /* Propagate the parameter result to the test case. */ if (test->status == KUNIT_FAILURE) test_case->status = KUNIT_FAILURE; -- cgit v1.2.3 From 410f07492eaef600fe817eef9fd272f6692a451a Mon Sep 17 00:00:00 2001 From: David Gow Date: Wed, 10 May 2023 13:38:30 +0800 Subject: Documentation: kunit: Note that assertions should not be used in cleanup As assertions abort the test cleanup process, they should be avoided from within a suite's exit function, or from within resource 'free' functions. Unlike with initialisation or main test execution, no further cleanup will be performed after a failed assertion, potentially causing a leak of resources. Reviewed-by: Sadiya Kazi Signed-off-by: David Gow Signed-off-by: Shuah Khan --- Documentation/dev-tools/kunit/usage.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/dev-tools/kunit/usage.rst b/Documentation/dev-tools/kunit/usage.rst index 9faf2b4153fc..9f720f1317d3 100644 --- a/Documentation/dev-tools/kunit/usage.rst +++ b/Documentation/dev-tools/kunit/usage.rst @@ -121,6 +121,12 @@ there's an allocation error. ``return`` so they only work from the test function. In KUnit, we stop the current kthread on failure, so you can call them from anywhere. +.. note:: + Warning: There is an exception to the above rule. You shouldn't use assertions + in the suite's exit() function, or in the free function for a resource. These + run when a test is shutting down, and an assertion here prevents further + cleanup code from running, potentially leading to a memory leak. + Customizing error messages -------------------------- -- cgit v1.2.3 From cdc87bda607518e70b6a6745cd593458d725b5a7 Mon Sep 17 00:00:00 2001 From: David Gow Date: Wed, 10 May 2023 13:38:31 +0800 Subject: Documentation: kunit: Warn that exit functions run even if init fails KUnit's exit functions will run even if the corresponding init function fails. It's easy, when writing an exit function, to assume the init function succeeded, and (for example) access uninitialised memory or dereference NULL pointers. Note that this case exists and should be handled in the documentation. Suggested-by: Benjamin Berg Link: https://lore.kernel.org/linux-kselftest/a39af0400abedb2e9b31d84c37551cecc3eed0e1.camel@sipsolutions.net/ Reviewed-by: Sadiya Kazi Signed-off-by: David Gow Signed-off-by: Shuah Khan --- Documentation/dev-tools/kunit/usage.rst | 12 ++++++++++-- include/kunit/test.h | 3 +++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Documentation/dev-tools/kunit/usage.rst b/Documentation/dev-tools/kunit/usage.rst index 9f720f1317d3..46957d1cbcbb 100644 --- a/Documentation/dev-tools/kunit/usage.rst +++ b/Documentation/dev-tools/kunit/usage.rst @@ -166,7 +166,12 @@ many similar tests. In order to reduce duplication in these closely related tests, most unit testing frameworks (including KUnit) provide the concept of a *test suite*. A test suite is a collection of test cases for a unit of code with optional setup and teardown functions that run before/after the whole -suite and/or every test case. For example: +suite and/or every test case. + +.. note:: + A test case will only run if it is associated with a test suite. + +For example: .. code-block:: c @@ -196,7 +201,10 @@ after everything else. ``kunit_test_suite(example_test_suite)`` registers the test suite with the KUnit test framework. .. note:: - A test case will only run if it is associated with a test suite. + The ``exit`` and ``suite_exit`` functions will run even if ``init`` or + ``suite_init`` fail. Make sure that they can handle any inconsistent + state which may result from ``init`` or ``suite_init`` encountering errors + or exiting early. ``kunit_test_suite(...)`` is a macro which tells the linker to put the specified test suite in a special linker section so that it can be run by KUnit diff --git a/include/kunit/test.h b/include/kunit/test.h index 57b309c6ca27..3028a1a3fcad 100644 --- a/include/kunit/test.h +++ b/include/kunit/test.h @@ -168,6 +168,9 @@ static inline char *kunit_status_to_ok_not_ok(enum kunit_status status) * test case, similar to the notion of a *test fixture* or a *test class* * in other unit testing frameworks like JUnit or Googletest. * + * Note that @exit and @suite_exit will run even if @init or @suite_init + * fail: make sure they can handle any inconsistent state which may result. + * * Every &struct kunit_case must be associated with a kunit_suite for KUnit * to run it. */ -- cgit v1.2.3 From a5ce66ad292b681ffe245e1c0e8840484da76784 Mon Sep 17 00:00:00 2001 From: David Gow Date: Wed, 10 May 2023 13:38:32 +0800 Subject: kunit: example: Provide example exit functions Add an example .exit and .suite_exit function to the KUnit example suite. Given exit functions are a bit more subtle than init functions (due to running in a different kthread, and running even after tests or test init functions fail), providing an easy place to experiment with them is useful. Reviewed-by: Rae Moar Signed-off-by: David Gow Signed-off-by: Shuah Khan --- lib/kunit/kunit-example-test.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/lib/kunit/kunit-example-test.c b/lib/kunit/kunit-example-test.c index cd8b7e51d02b..24315c882b31 100644 --- a/lib/kunit/kunit-example-test.c +++ b/lib/kunit/kunit-example-test.c @@ -41,6 +41,16 @@ static int example_test_init(struct kunit *test) return 0; } +/* + * This is run once after each test case, see the comment on + * example_test_suite for more information. + */ +static void example_test_exit(struct kunit *test) +{ + kunit_info(test, "cleaning up\n"); +} + + /* * This is run once before all test cases in the suite. * See the comment on example_test_suite for more information. @@ -52,6 +62,16 @@ static int example_test_init_suite(struct kunit_suite *suite) return 0; } +/* + * This is run once after all test cases in the suite. + * See the comment on example_test_suite for more information. + */ +static void example_test_exit_suite(struct kunit_suite *suite) +{ + kunit_info(suite, "exiting suite\n"); +} + + /* * This test should always be skipped. */ @@ -211,7 +231,9 @@ static struct kunit_case example_test_cases[] = { static struct kunit_suite example_test_suite = { .name = "example", .init = example_test_init, + .exit = example_test_exit, .suite_init = example_test_init_suite, + .suite_exit = example_test_exit_suite, .test_cases = example_test_cases, }; -- cgit v1.2.3 From b9dce8a1ed3efe0f5c0957f4605140f204226a0f Mon Sep 17 00:00:00 2001 From: David Gow Date: Thu, 25 May 2023 12:21:28 +0800 Subject: kunit: Add kunit_add_action() to defer a call until test exit Many uses of the KUnit resource system are intended to simply defer calling a function until the test exits (be it due to success or failure). The existing kunit_alloc_resource() function is often used for this, but was awkward to use (requiring passing NULL init functions, etc), and returned a resource without incrementing its reference count, which -- while okay for this use-case -- could cause problems in others. Instead, introduce a simple kunit_add_action() API: a simple function (returning nothing, accepting a single void* argument) can be scheduled to be called when the test exits. Deferred actions are called in the opposite order to that which they were registered. This mimics the devres API, devm_add_action(), and also provides kunit_remove_action(), to cancel a deferred action, and kunit_release_action() to trigger one early. This is implemented as a resource under the hood, so the ordering between resource cleanup and deferred functions is maintained. Reviewed-by: Benjamin Berg Reviewed-by: Maxime Ripard Tested-by: Maxime Ripard Signed-off-by: David Gow Signed-off-by: Shuah Khan --- include/kunit/resource.h | 92 ++++++++++++++++++++++++++++++++++++++++++++ lib/kunit/kunit-test.c | 88 +++++++++++++++++++++++++++++++++++++++++- lib/kunit/resource.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 278 insertions(+), 1 deletion(-) diff --git a/include/kunit/resource.h b/include/kunit/resource.h index c0d88b318e90..b64eb783b1bc 100644 --- a/include/kunit/resource.h +++ b/include/kunit/resource.h @@ -387,4 +387,96 @@ static inline int kunit_destroy_named_resource(struct kunit *test, */ void kunit_remove_resource(struct kunit *test, struct kunit_resource *res); +/* A 'deferred action' function to be used with kunit_add_action. */ +typedef void (kunit_action_t)(void *); + +/** + * kunit_add_action() - Call a function when the test ends. + * @test: Test case to associate the action with. + * @func: The function to run on test exit + * @ctx: Data passed into @func + * + * Defer the execution of a function until the test exits, either normally or + * due to a failure. @ctx is passed as additional context. All functions + * registered with kunit_add_action() will execute in the opposite order to that + * they were registered in. + * + * This is useful for cleaning up allocated memory and resources, as these + * functions are called even if the test aborts early due to, e.g., a failed + * assertion. + * + * See also: devm_add_action() for the devres equivalent. + * + * Returns: + * 0 on success, an error if the action could not be deferred. + */ +int kunit_add_action(struct kunit *test, kunit_action_t *action, void *ctx); + +/** + * kunit_add_action_or_reset() - Call a function when the test ends. + * @test: Test case to associate the action with. + * @func: The function to run on test exit + * @ctx: Data passed into @func + * + * Defer the execution of a function until the test exits, either normally or + * due to a failure. @ctx is passed as additional context. All functions + * registered with kunit_add_action() will execute in the opposite order to that + * they were registered in. + * + * This is useful for cleaning up allocated memory and resources, as these + * functions are called even if the test aborts early due to, e.g., a failed + * assertion. + * + * If the action cannot be created (e.g., due to the system being out of memory), + * then action(ctx) will be called immediately, and an error will be returned. + * + * See also: devm_add_action_or_reset() for the devres equivalent. + * + * Returns: + * 0 on success, an error if the action could not be deferred. + */ +int kunit_add_action_or_reset(struct kunit *test, kunit_action_t *action, + void *ctx); + +/** + * kunit_remove_action() - Cancel a matching deferred action. + * @test: Test case the action is associated with. + * @func: The deferred function to cancel. + * @ctx: The context passed to the deferred function to trigger. + * + * Prevent an action deferred via kunit_add_action() from executing when the + * test terminates. + * + * If the function/context pair was deferred multiple times, only the most + * recent one will be cancelled. + * + * See also: devm_remove_action() for the devres equivalent. + */ +void kunit_remove_action(struct kunit *test, + kunit_action_t *action, + void *ctx); + +/** + * kunit_release_action() - Run a matching action call immediately. + * @test: Test case the action is associated with. + * @func: The deferred function to trigger. + * @ctx: The context passed to the deferred function to trigger. + * + * Execute a function deferred via kunit_add_action()) immediately, rather than + * when the test ends. + * + * If the function/context pair was deferred multiple times, it will only be + * executed once here. The most recent deferral will no longer execute when + * the test ends. + * + * kunit_release_action(test, func, ctx); + * is equivalent to + * func(ctx); + * kunit_remove_action(test, func, ctx); + * + * See also: devm_release_action() for the devres equivalent. + */ +void kunit_release_action(struct kunit *test, + kunit_action_t *action, + void *ctx); #endif /* _KUNIT_RESOURCE_H */ diff --git a/lib/kunit/kunit-test.c b/lib/kunit/kunit-test.c index 42e44caa1bdd..83d8e90ca7a2 100644 --- a/lib/kunit/kunit-test.c +++ b/lib/kunit/kunit-test.c @@ -112,7 +112,7 @@ struct kunit_test_resource_context { struct kunit test; bool is_resource_initialized; int allocate_order[2]; - int free_order[2]; + int free_order[4]; }; static int fake_resource_init(struct kunit_resource *res, void *context) @@ -403,6 +403,88 @@ static void kunit_resource_test_named(struct kunit *test) KUNIT_EXPECT_TRUE(test, list_empty(&test->resources)); } +static void increment_int(void *ctx) +{ + int *i = (int *)ctx; + (*i)++; +} + +static void kunit_resource_test_action(struct kunit *test) +{ + int num_actions = 0; + + kunit_add_action(test, increment_int, &num_actions); + KUNIT_EXPECT_EQ(test, num_actions, 0); + kunit_cleanup(test); + KUNIT_EXPECT_EQ(test, num_actions, 1); + + /* Once we've cleaned up, the action queue is empty. */ + kunit_cleanup(test); + KUNIT_EXPECT_EQ(test, num_actions, 1); + + /* Check the same function can be deferred multiple times. */ + kunit_add_action(test, increment_int, &num_actions); + kunit_add_action(test, increment_int, &num_actions); + kunit_cleanup(test); + KUNIT_EXPECT_EQ(test, num_actions, 3); +} +static void kunit_resource_test_remove_action(struct kunit *test) +{ + int num_actions = 0; + + kunit_add_action(test, increment_int, &num_actions); + KUNIT_EXPECT_EQ(test, num_actions, 0); + + kunit_remove_action(test, increment_int, &num_actions); + kunit_cleanup(test); + KUNIT_EXPECT_EQ(test, num_actions, 0); +} +static void kunit_resource_test_release_action(struct kunit *test) +{ + int num_actions = 0; + + kunit_add_action(test, increment_int, &num_actions); + KUNIT_EXPECT_EQ(test, num_actions, 0); + /* Runs immediately on trigger. */ + kunit_release_action(test, increment_int, &num_actions); + KUNIT_EXPECT_EQ(test, num_actions, 1); + + /* Doesn't run again on test exit. */ + kunit_cleanup(test); + KUNIT_EXPECT_EQ(test, num_actions, 1); +} +static void action_order_1(void *ctx) +{ + struct kunit_test_resource_context *res_ctx = (struct kunit_test_resource_context *)ctx; + + KUNIT_RESOURCE_TEST_MARK_ORDER(res_ctx, free_order, 1); + kunit_log(KERN_INFO, current->kunit_test, "action_order_1"); +} +static void action_order_2(void *ctx) +{ + struct kunit_test_resource_context *res_ctx = (struct kunit_test_resource_context *)ctx; + + KUNIT_RESOURCE_TEST_MARK_ORDER(res_ctx, free_order, 2); + kunit_log(KERN_INFO, current->kunit_test, "action_order_2"); +} +static void kunit_resource_test_action_ordering(struct kunit *test) +{ + struct kunit_test_resource_context *ctx = test->priv; + + kunit_add_action(test, action_order_1, ctx); + kunit_add_action(test, action_order_2, ctx); + kunit_add_action(test, action_order_1, ctx); + kunit_add_action(test, action_order_2, ctx); + kunit_remove_action(test, action_order_1, ctx); + kunit_release_action(test, action_order_2, ctx); + kunit_cleanup(test); + + /* [2 is triggered] [2], [(1 is cancelled)] [1] */ + KUNIT_EXPECT_EQ(test, ctx->free_order[0], 2); + KUNIT_EXPECT_EQ(test, ctx->free_order[1], 2); + KUNIT_EXPECT_EQ(test, ctx->free_order[2], 1); +} + static int kunit_resource_test_init(struct kunit *test) { struct kunit_test_resource_context *ctx = @@ -434,6 +516,10 @@ static struct kunit_case kunit_resource_test_cases[] = { KUNIT_CASE(kunit_resource_test_proper_free_ordering), KUNIT_CASE(kunit_resource_test_static), KUNIT_CASE(kunit_resource_test_named), + KUNIT_CASE(kunit_resource_test_action), + KUNIT_CASE(kunit_resource_test_remove_action), + KUNIT_CASE(kunit_resource_test_release_action), + KUNIT_CASE(kunit_resource_test_action_ordering), {} }; diff --git a/lib/kunit/resource.c b/lib/kunit/resource.c index c414df922f34..f0209252b179 100644 --- a/lib/kunit/resource.c +++ b/lib/kunit/resource.c @@ -77,3 +77,102 @@ int kunit_destroy_resource(struct kunit *test, kunit_resource_match_t match, return 0; } EXPORT_SYMBOL_GPL(kunit_destroy_resource); + +struct kunit_action_ctx { + struct kunit_resource res; + kunit_action_t *func; + void *ctx; +}; + +static void __kunit_action_free(struct kunit_resource *res) +{ + struct kunit_action_ctx *action_ctx = container_of(res, struct kunit_action_ctx, res); + + action_ctx->func(action_ctx->ctx); +} + + +int kunit_add_action(struct kunit *test, void (*action)(void *), void *ctx) +{ + struct kunit_action_ctx *action_ctx; + + KUNIT_ASSERT_NOT_NULL_MSG(test, action, "Tried to action a NULL function!"); + + action_ctx = kzalloc(sizeof(*action_ctx), GFP_KERNEL); + if (!action_ctx) + return -ENOMEM; + + action_ctx->func = action; + action_ctx->ctx = ctx; + + action_ctx->res.should_kfree = true; + /* As init is NULL, this cannot fail. */ + __kunit_add_resource(test, NULL, __kunit_action_free, &action_ctx->res, action_ctx); + + return 0; +} +EXPORT_SYMBOL_GPL(kunit_add_action); + +int kunit_add_action_or_reset(struct kunit *test, void (*action)(void *), + void *ctx) +{ + int res = kunit_add_action(test, action, ctx); + + if (res) + action(ctx); + return res; +} +EXPORT_SYMBOL_GPL(kunit_add_action_or_reset); + +static bool __kunit_action_match(struct kunit *test, + struct kunit_resource *res, void *match_data) +{ + struct kunit_action_ctx *match_ctx = (struct kunit_action_ctx *)match_data; + struct kunit_action_ctx *res_ctx = container_of(res, struct kunit_action_ctx, res); + + /* Make sure this is a free function. */ + if (res->free != __kunit_action_free) + return false; + + /* Both the function and context data should match. */ + return (match_ctx->func == res_ctx->func) && (match_ctx->ctx == res_ctx->ctx); +} + +void kunit_remove_action(struct kunit *test, + kunit_action_t *action, + void *ctx) +{ + struct kunit_action_ctx match_ctx; + struct kunit_resource *res; + + match_ctx.func = action; + match_ctx.ctx = ctx; + + res = kunit_find_resource(test, __kunit_action_match, &match_ctx); + if (res) { + /* Remove the free function so we don't run the action. */ + res->free = NULL; + kunit_remove_resource(test, res); + kunit_put_resource(res); + } +} +EXPORT_SYMBOL_GPL(kunit_remove_action); + +void kunit_release_action(struct kunit *test, + kunit_action_t *action, + void *ctx) +{ + struct kunit_action_ctx match_ctx; + struct kunit_resource *res; + + match_ctx.func = action; + match_ctx.ctx = ctx; + + res = kunit_find_resource(test, __kunit_action_match, &match_ctx); + if (res) { + kunit_remove_resource(test, res); + /* We have to put() this here, else free won't be called. */ + kunit_put_resource(res); + } +} +EXPORT_SYMBOL_GPL(kunit_release_action); -- cgit v1.2.3 From 00e63f8afcfc6bf93d75141c51d35e8a40e86363 Mon Sep 17 00:00:00 2001 From: David Gow Date: Thu, 25 May 2023 12:21:29 +0800 Subject: kunit: executor_test: Use kunit_add_action() Now we have the kunit_add_action() function, we can use it to implement kfree_at_end() and free_subsuite_at_end() without the need for extra helper functions. Reviewed-by: Benjamin Berg Reviewed-by: Maxime Ripard Tested-by: Maxime Ripard Signed-off-by: David Gow Signed-off-by: Shuah Khan --- lib/kunit/executor_test.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/lib/kunit/executor_test.c b/lib/kunit/executor_test.c index 0cea31c27b23..ce6749af374d 100644 --- a/lib/kunit/executor_test.c +++ b/lib/kunit/executor_test.c @@ -125,11 +125,6 @@ kunit_test_suites(&executor_test_suite); /* Test helpers */ -static void kfree_res_free(struct kunit_resource *res) -{ - kfree(res->data); -} - /* Use the resource API to register a call to kfree(to_free). * Since we never actually use the resource, it's safe to use on const data. */ @@ -138,8 +133,10 @@ static void kfree_at_end(struct kunit *test, const void *to_free) /* kfree() handles NULL already, but avoid allocating a no-op cleanup. */ if (IS_ERR_OR_NULL(to_free)) return; - kunit_alloc_resource(test, NULL, kfree_res_free, GFP_KERNEL, - (void *)to_free); + + kunit_add_action(test, + (kunit_action_t *)kfree, + (void *)to_free); } static struct kunit_suite *alloc_fake_suite(struct kunit *test, -- cgit v1.2.3 From 57e3cded99e9c840bc5310878d0a7f4e7768a296 Mon Sep 17 00:00:00 2001 From: David Gow Date: Thu, 25 May 2023 12:21:30 +0800 Subject: kunit: kmalloc_array: Use kunit_add_action() The kunit_add_action() function is much simpler and cleaner to use that the full KUnit resource API for simple things like the kunit_kmalloc_array() functionality. Replacing it allows us to get rid of a number of helper functions, and leaves us with no uses of kunit_alloc_resource(), which has some usability problems and is going to have its behaviour modified in an upcoming patch. Note that we need to use kunit_defer_trigger_all() to implement kunit_kfree(). Reviewed-by: Benjamin Berg Reviewed-by: Maxime Ripard Tested-by: Maxime Ripard Signed-off-by: David Gow Signed-off-by: Shuah Khan --- include/kunit/test.h | 10 ++++++++-- lib/kunit/test.c | 48 +++++++++--------------------------------------- 2 files changed, 17 insertions(+), 41 deletions(-) diff --git a/include/kunit/test.h b/include/kunit/test.h index 3028a1a3fcad..2f23d6efa505 100644 --- a/include/kunit/test.h +++ b/include/kunit/test.h @@ -324,8 +324,11 @@ enum kunit_status kunit_suite_has_succeeded(struct kunit_suite *suite); * @gfp: flags passed to underlying kmalloc(). * * Just like `kmalloc_array(...)`, except the allocation is managed by the test case - * and is automatically cleaned up after the test case concludes. See &struct - * kunit_resource for more information. + * and is automatically cleaned up after the test case concludes. See kunit_add_action() + * for more information. + * + * Note that some internal context data is also allocated with GFP_KERNEL, + * regardless of the gfp passed in. */ void *kunit_kmalloc_array(struct kunit *test, size_t n, size_t size, gfp_t gfp); @@ -336,6 +339,9 @@ void *kunit_kmalloc_array(struct kunit *test, size_t n, size_t size, gfp_t gfp); * @gfp: flags passed to underlying kmalloc(). * * See kmalloc() and kunit_kmalloc_array() for more information. + * + * Note that some internal context data is also allocated with GFP_KERNEL, + * regardless of the gfp passed in. */ static inline void *kunit_kmalloc(struct kunit *test, size_t size, gfp_t gfp) { diff --git a/lib/kunit/test.c b/lib/kunit/test.c index f5e4ceffd282..d3fb93a23ccc 100644 --- a/lib/kunit/test.c +++ b/lib/kunit/test.c @@ -752,58 +752,28 @@ static struct notifier_block kunit_mod_nb = { }; #endif -struct kunit_kmalloc_array_params { - size_t n; - size_t size; - gfp_t gfp; -}; - -static int kunit_kmalloc_array_init(struct kunit_resource *res, void *context) +void *kunit_kmalloc_array(struct kunit *test, size_t n, size_t size, gfp_t gfp) { - struct kunit_kmalloc_array_params *params = context; + void *data; - res->data = kmalloc_array(params->n, params->size, params->gfp); - if (!res->data) - return -ENOMEM; + data = kmalloc_array(n, size, gfp); - return 0; -} + if (!data) + return NULL; -static void kunit_kmalloc_array_free(struct kunit_resource *res) -{ - kfree(res->data); -} + if (kunit_add_action_or_reset(test, (kunit_action_t *)kfree, data) != 0) + return NULL; -void *kunit_kmalloc_array(struct kunit *test, size_t n, size_t size, gfp_t gfp) -{ - struct kunit_kmalloc_array_params params = { - .size = size, - .n = n, - .gfp = gfp - }; - - return kunit_alloc_resource(test, - kunit_kmalloc_array_init, - kunit_kmalloc_array_free, - gfp, - ¶ms); + return data; } EXPORT_SYMBOL_GPL(kunit_kmalloc_array); -static inline bool kunit_kfree_match(struct kunit *test, - struct kunit_resource *res, void *match_data) -{ - /* Only match resources allocated with kunit_kmalloc() and friends. */ - return res->free == kunit_kmalloc_array_free && res->data == match_data; -} - void kunit_kfree(struct kunit *test, const void *ptr) { if (!ptr) return; - if (kunit_destroy_resource(test, kunit_kfree_match, (void *)ptr)) - KUNIT_FAIL(test, "kunit_kfree: %px already freed or not allocated by kunit", ptr); + kunit_release_action(test, (kunit_action_t *)kfree, (void *)ptr); } EXPORT_SYMBOL_GPL(kunit_kfree); -- cgit v1.2.3 From c7853b55116e644e1fd4f0e5d8ea7c5dc89d71b8 Mon Sep 17 00:00:00 2001 From: David Gow Date: Thu, 25 May 2023 12:21:31 +0800 Subject: Documentation: kunit: Add usage notes for kunit_add_action() Add some basic documentation for kunit_add_action() and related deferred action functions. Reviewed-by: Rae Moar Signed-off-by: David Gow Signed-off-by: Shuah Khan --- Documentation/dev-tools/kunit/usage.rst | 51 +++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/Documentation/dev-tools/kunit/usage.rst b/Documentation/dev-tools/kunit/usage.rst index 46957d1cbcbb..c27e1646ecd9 100644 --- a/Documentation/dev-tools/kunit/usage.rst +++ b/Documentation/dev-tools/kunit/usage.rst @@ -615,6 +615,57 @@ For example: KUNIT_ASSERT_STREQ(test, buffer, ""); } +Registering Cleanup Actions +--------------------------- + +If you need to perform some cleanup beyond simple use of ``kunit_kzalloc``, +you can register a custom "deferred action", which is a cleanup function +run when the test exits (whether cleanly, or via a failed assertion). + +Actions are simple functions with no return value, and a single ``void*`` +context argument, and fulfill the same role as "cleanup" functions in Python +and Go tests, "defer" statements in languages which support them, and +(in some cases) destructors in RAII languages. + +These are very useful for unregistering things from global lists, closing +files or other resources, or freeing resources. + +For example: + +.. code-block:: C + + static void cleanup_device(void *ctx) + { + struct device *dev = (struct device *)ctx; + + device_unregister(dev); + } + + void example_device_test(struct kunit *test) + { + struct my_device dev; + + device_register(&dev); + + kunit_add_action(test, &cleanup_device, &dev); + } + +Note that, for functions like device_unregister which only accept a single +pointer-sized argument, it's possible to directly cast that function to +a ``kunit_action_t`` rather than writing a wrapper function, for example: + +.. code-block:: C + + kunit_add_action(test, (kunit_action_t *)&device_unregister, &dev); + +``kunit_add_action`` can fail if, for example, the system is out of memory. +You can use ``kunit_add_action_or_reset`` instead which runs the action +immediately if it cannot be deferred. + +If you need more control over when the cleanup function is called, you +can trigger it early using ``kunit_release_action``, or cancel it entirely +with ``kunit_remove_action``. + Testing Static Functions ------------------------ -- cgit v1.2.3 From d273b72846d636a7a9072587b5c53e7c0aeb791b Mon Sep 17 00:00:00 2001 From: Michal Wajdeczko Date: Wed, 17 May 2023 13:18:14 +0200 Subject: kunit/test: Add example test showing parameterized testing Use of parameterized testing is documented [1] but such use case is not present in demo kunit test. Add small subtest for that. [1] https://kernel.org/doc/html/latest/dev-tools/kunit/usage.html#parameterized-testing Signed-off-by: Michal Wajdeczko Cc: David Gow Reviewed-by: David Gow Signed-off-by: Shuah Khan --- lib/kunit/kunit-example-test.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/lib/kunit/kunit-example-test.c b/lib/kunit/kunit-example-test.c index 24315c882b31..b69b689ea850 100644 --- a/lib/kunit/kunit-example-test.c +++ b/lib/kunit/kunit-example-test.c @@ -187,6 +187,39 @@ static void example_static_stub_test(struct kunit *test) KUNIT_EXPECT_EQ(test, add_one(1), 2); } +static const struct example_param { + int value; +} example_params_array[] = { + { .value = 2, }, + { .value = 1, }, + { .value = 0, }, +}; + +static void example_param_get_desc(const struct example_param *p, char *desc) +{ + snprintf(desc, KUNIT_PARAM_DESC_SIZE, "example value %d", p->value); +} + +KUNIT_ARRAY_PARAM(example, example_params_array, example_param_get_desc); + +/* + * This test shows the use of params. + */ +static void example_params_test(struct kunit *test) +{ + const struct example_param *param = test->param_value; + + /* By design, param pointer will not be NULL */ + KUNIT_ASSERT_NOT_NULL(test, param); + + /* Test can be skipped on unsupported param values */ + if (!param->value) + kunit_skip(test, "unsupported param value"); + + /* You can use param values for parameterized testing */ + KUNIT_EXPECT_EQ(test, param->value % param->value, 0); +} + /* * Here we make a list of all the test cases we want to add to the test suite * below. @@ -203,6 +236,7 @@ static struct kunit_case example_test_cases[] = { KUNIT_CASE(example_mark_skipped_test), KUNIT_CASE(example_all_expect_macros_test), KUNIT_CASE(example_static_stub_test), + KUNIT_CASE_PARAM(example_params_test, example_gen_params), {} }; -- cgit v1.2.3 From b08f75b9bb0196a626a804e76970733f0a05de94 Mon Sep 17 00:00:00 2001 From: Michal Wajdeczko Date: Wed, 17 May 2023 13:18:15 +0200 Subject: kunit: Fix reporting of the skipped parameterized tests Logs from the parameterized tests that were skipped don't include SKIP directive thus they are displayed as PASSED. Fix that. Signed-off-by: Michal Wajdeczko Cc: David Gow Reviewed-by: Rae Moar Reviewed-by: David Gow Signed-off-by: Shuah Khan --- lib/kunit/test.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/kunit/test.c b/lib/kunit/test.c index d3fb93a23ccc..b09047907a47 100644 --- a/lib/kunit/test.c +++ b/lib/kunit/test.c @@ -627,9 +627,11 @@ int kunit_run_tests(struct kunit_suite *suite) kunit_log(KERN_INFO, &test, KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT - "%s %d %s", + "%s %d %s%s%s", kunit_status_to_ok_not_ok(test.status), - test.param_index + 1, param_desc); + test.param_index + 1, param_desc, + test.status == KUNIT_SKIPPED ? " # SKIP " : "", + test.status == KUNIT_SKIPPED ? test.status_comment : ""); /* Get next param. */ param_desc[0] = '\0'; -- cgit v1.2.3 From b1eaa8b2a55c9d5d22f5d2929f4d9973d6392241 Mon Sep 17 00:00:00 2001 From: Michal Wajdeczko Date: Wed, 17 May 2023 13:18:16 +0200 Subject: kunit: Update kunit_print_ok_not_ok function There is no need use opaque test_or_suite pointer and is_test flag as we don't use anything from the suite struct. Always expect test pointer and use NULL as indication that provided results are from the suite so we can treat them differently. Since results could be from nested tests, like parameterized tests, add explicit level parameter to properly indent output messages and thus allow to reuse this function from other places. While around, remove small code duplication near skip directive. Signed-off-by: Michal Wajdeczko Cc: David Gow Cc: Rae Moar Reviewed-by: David Gow Signed-off-by: Shuah Khan --- include/kunit/test.h | 1 + lib/kunit/test.c | 45 ++++++++++++++++++++++++++++----------------- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/include/kunit/test.h b/include/kunit/test.h index 2f23d6efa505..8718bd21e61e 100644 --- a/include/kunit/test.h +++ b/include/kunit/test.h @@ -47,6 +47,7 @@ struct kunit; * sub-subtest. See the "Subtests" section in * https://node-tap.org/tap-protocol/ */ +#define KUNIT_INDENT_LEN 4 #define KUNIT_SUBTEST_INDENT " " #define KUNIT_SUBSUBTEST_INDENT " " diff --git a/lib/kunit/test.c b/lib/kunit/test.c index b09047907a47..f0ae303da353 100644 --- a/lib/kunit/test.c +++ b/lib/kunit/test.c @@ -185,16 +185,28 @@ static void kunit_print_suite_start(struct kunit_suite *suite) kunit_suite_num_test_cases(suite)); } -static void kunit_print_ok_not_ok(void *test_or_suite, - bool is_test, +/* Currently supported test levels */ +enum { + KUNIT_LEVEL_SUITE = 0, + KUNIT_LEVEL_CASE, + KUNIT_LEVEL_CASE_PARAM, +}; + +static void kunit_print_ok_not_ok(struct kunit *test, + unsigned int test_level, enum kunit_status status, size_t test_number, const char *description, const char *directive) { - struct kunit_suite *suite = is_test ? NULL : test_or_suite; - struct kunit *test = is_test ? test_or_suite : NULL; const char *directive_header = (status == KUNIT_SKIPPED) ? " # SKIP " : ""; + const char *directive_body = (status == KUNIT_SKIPPED) ? directive : ""; + + /* + * When test is NULL assume that results are from the suite + * and today suite results are expected at level 0 only. + */ + WARN(!test && test_level, "suite test level can't be %u!\n", test_level); /* * We do not log the test suite results as doing so would @@ -203,17 +215,18 @@ static void kunit_print_ok_not_ok(void *test_or_suite, * separately seq_printf() the suite results for the debugfs * representation. */ - if (suite) + if (!test) pr_info("%s %zd %s%s%s\n", kunit_status_to_ok_not_ok(status), test_number, description, directive_header, - (status == KUNIT_SKIPPED) ? directive : ""); + directive_body); else kunit_log(KERN_INFO, test, - KUNIT_SUBTEST_INDENT "%s %zd %s%s%s", + "%*s%s %zd %s%s%s", + KUNIT_INDENT_LEN * test_level, "", kunit_status_to_ok_not_ok(status), test_number, description, directive_header, - (status == KUNIT_SKIPPED) ? directive : ""); + directive_body); } enum kunit_status kunit_suite_has_succeeded(struct kunit_suite *suite) @@ -239,7 +252,7 @@ static size_t kunit_suite_counter = 1; static void kunit_print_suite_end(struct kunit_suite *suite) { - kunit_print_ok_not_ok((void *)suite, false, + kunit_print_ok_not_ok(NULL, KUNIT_LEVEL_SUITE, kunit_suite_has_succeeded(suite), kunit_suite_counter++, suite->name, @@ -625,13 +638,11 @@ int kunit_run_tests(struct kunit_suite *suite) "param-%d", test.param_index); } - kunit_log(KERN_INFO, &test, - KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT - "%s %d %s%s%s", - kunit_status_to_ok_not_ok(test.status), - test.param_index + 1, param_desc, - test.status == KUNIT_SKIPPED ? " # SKIP " : "", - test.status == KUNIT_SKIPPED ? test.status_comment : ""); + kunit_print_ok_not_ok(&test, KUNIT_LEVEL_CASE_PARAM, + test.status, + test.param_index + 1, + param_desc, + test.status_comment); /* Get next param. */ param_desc[0] = '\0'; @@ -645,7 +656,7 @@ int kunit_run_tests(struct kunit_suite *suite) kunit_print_test_stats(&test, param_stats); - kunit_print_ok_not_ok(&test, true, test_case->status, + kunit_print_ok_not_ok(&test, KUNIT_LEVEL_CASE, test_case->status, kunit_test_case_num(suite, test_case), test_case->name, test.status_comment); -- cgit v1.2.3 From 6543960cd3bdb9fabdd0567ab02f4d1168ee96d9 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Tue, 30 May 2023 19:52:48 +0900 Subject: Documentation: Kunit: add MODULE_LICENSE to sample code The sample code has Kconfig for tristate configuration. In the case, it could be friendly to developers that the code has MODULE_LICENSE, since the missing MODULE_LICENSE brings error to modpost when the code is built as loadable kernel module. Signed-off-by: Takashi Sakamoto Reviewed-by: David Gow Signed-off-by: Shuah Khan --- Documentation/dev-tools/kunit/start.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/dev-tools/kunit/start.rst b/Documentation/dev-tools/kunit/start.rst index 9619a0440930..a98235326bab 100644 --- a/Documentation/dev-tools/kunit/start.rst +++ b/Documentation/dev-tools/kunit/start.rst @@ -250,6 +250,8 @@ Now we are ready to write the test cases. }; kunit_test_suite(misc_example_test_suite); + MODULE_LICENSE("GPL"); + 2. Add the following lines to ``drivers/misc/Kconfig``: .. code-block:: kconfig -- cgit v1.2.3 From c042030aa15e9265504a034243a8cae062e900a1 Mon Sep 17 00:00:00 2001 From: David Gow Date: Tue, 30 May 2023 15:55:57 +0800 Subject: kunit: Fix obsolete name in documentation headers (func->action) The kunit_add_action() and related functions named the kunit_action_t parameter 'func' in early drafts, which was later renamed to 'action' However, the doc comments were not properly updated. Fix these to avoid confusion and 'make htmldocs' warnings. Fixes: b9dce8a1ed3e ("kunit: Add kunit_add_action() to defer a call until test exit") Reported-by: Stephen Rothwell Closes: https://lore.kernel.org/lkml/20230530151840.16a56460@canb.auug.org.au/ Signed-off-by: David Gow Reviewed-by: Maxime Ripard Signed-off-by: Shuah Khan --- include/kunit/resource.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/kunit/resource.h b/include/kunit/resource.h index b64eb783b1bc..c7383e90f5c9 100644 --- a/include/kunit/resource.h +++ b/include/kunit/resource.h @@ -393,7 +393,7 @@ typedef void (kunit_action_t)(void *); /** * kunit_add_action() - Call a function when the test ends. * @test: Test case to associate the action with. - * @func: The function to run on test exit + * @action: The function to run on test exit * @ctx: Data passed into @func * * Defer the execution of a function until the test exits, either normally or @@ -415,7 +415,7 @@ int kunit_add_action(struct kunit *test, kunit_action_t *action, void *ctx); /** * kunit_add_action_or_reset() - Call a function when the test ends. * @test: Test case to associate the action with. - * @func: The function to run on test exit + * @action: The function to run on test exit * @ctx: Data passed into @func * * Defer the execution of a function until the test exits, either normally or @@ -441,7 +441,7 @@ int kunit_add_action_or_reset(struct kunit *test, kunit_action_t *action, /** * kunit_remove_action() - Cancel a matching deferred action. * @test: Test case the action is associated with. - * @func: The deferred function to cancel. + * @action: The deferred function to cancel. * @ctx: The context passed to the deferred function to trigger. * * Prevent an action deferred via kunit_add_action() from executing when the @@ -459,7 +459,7 @@ void kunit_remove_action(struct kunit *test, /** * kunit_release_action() - Run a matching action call immediately. * @test: Test case the action is associated with. - * @func: The deferred function to trigger. + * @action: The deferred function to trigger. * @ctx: The context passed to the deferred function to trigger. * * Execute a function deferred via kunit_add_action()) immediately, rather than -- cgit v1.2.3 From 260755184cbdb267a046e7ffd397c1d2ba09bb5e Mon Sep 17 00:00:00 2001 From: David Gow Date: Wed, 31 May 2023 13:21:57 +0800 Subject: kunit: Move kunit_abort() call out of kunit_do_failed_assertion() KUnit aborts the current thread when an assertion fails. Currently, this is done conditionally as part of the kunit_do_failed_assertion() function, but this hides the kunit_abort() call from the compiler (particularly if it's in another module). This, in turn, can lead to both suboptimal code generation (the compiler can't know if kunit_do_failed_assertion() will return), and to static analysis tools like smatch giving false positives. Moving the kunit_abort() call into the macro should give the compiler and tools a better chance at understanding what's going on. Doing so requires exporting kunit_abort(), though it's recommended to continue to use assertions in lieu of aborting directly. In addition, kunit_abort() and kunit_do_failed_assertion() are renamed to make it clear they they're intended for internal KUnit use, to: __kunit_do_failed_assertion() and __kunit_abort() Suggested-by: Dan Carpenter Signed-off-by: David Gow Reviewed-by: Miguel Ojeda Reviewed-by: Daniel Latypov Signed-off-by: Shuah Khan --- include/kunit/test.h | 20 ++++++++++++-------- lib/kunit/test.c | 10 ++++------ 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/include/kunit/test.h b/include/kunit/test.h index 8718bd21e61e..23120d50499e 100644 --- a/include/kunit/test.h +++ b/include/kunit/test.h @@ -482,7 +482,9 @@ void __printf(2, 3) kunit_log_append(char *log, const char *fmt, ...); */ #define KUNIT_SUCCEED(test) do {} while (0) -void kunit_do_failed_assertion(struct kunit *test, +void __noreturn __kunit_abort(struct kunit *test); + +void __kunit_do_failed_assertion(struct kunit *test, const struct kunit_loc *loc, enum kunit_assert_type type, const struct kunit_assert *assert, @@ -492,13 +494,15 @@ void kunit_do_failed_assertion(struct kunit *test, #define _KUNIT_FAILED(test, assert_type, assert_class, assert_format, INITIALIZER, fmt, ...) do { \ static const struct kunit_loc __loc = KUNIT_CURRENT_LOC; \ const struct assert_class __assertion = INITIALIZER; \ - kunit_do_failed_assertion(test, \ - &__loc, \ - assert_type, \ - &__assertion.assert, \ - assert_format, \ - fmt, \ - ##__VA_ARGS__); \ + __kunit_do_failed_assertion(test, \ + &__loc, \ + assert_type, \ + &__assertion.assert, \ + assert_format, \ + fmt, \ + ##__VA_ARGS__); \ + if (assert_type == KUNIT_ASSERTION) \ + __kunit_abort(test); \ } while (0) diff --git a/lib/kunit/test.c b/lib/kunit/test.c index f0ae303da353..84e4666555c9 100644 --- a/lib/kunit/test.c +++ b/lib/kunit/test.c @@ -323,7 +323,7 @@ static void kunit_fail(struct kunit *test, const struct kunit_loc *loc, string_stream_destroy(stream); } -static void __noreturn kunit_abort(struct kunit *test) +void __noreturn __kunit_abort(struct kunit *test) { kunit_try_catch_throw(&test->try_catch); /* Does not return. */ @@ -335,8 +335,9 @@ static void __noreturn kunit_abort(struct kunit *test) */ WARN_ONCE(true, "Throw could not abort from test!\n"); } +EXPORT_SYMBOL_GPL(__kunit_abort); -void kunit_do_failed_assertion(struct kunit *test, +void __kunit_do_failed_assertion(struct kunit *test, const struct kunit_loc *loc, enum kunit_assert_type type, const struct kunit_assert *assert, @@ -353,11 +354,8 @@ void kunit_do_failed_assertion(struct kunit *test, kunit_fail(test, loc, type, assert, assert_format, &message); va_end(args); - - if (type == KUNIT_ASSERTION) - kunit_abort(test); } -EXPORT_SYMBOL_GPL(kunit_do_failed_assertion); +EXPORT_SYMBOL_GPL(__kunit_do_failed_assertion); void kunit_init_test(struct kunit *test, const char *name, char *log) { -- cgit v1.2.3 From fefdb43943c1a0d87e1b43ae4d03e5f9a1d058f4 Mon Sep 17 00:00:00 2001 From: David Gow Date: Fri, 2 Jun 2023 11:53:59 +0800 Subject: Documentation: kunit: Rename references to kunit_abort() The kunit_abort() function has been renamed __kunit_abort(), update the references to it in the documentation. Suggested-by: Daniel Latypov Signed-off-by: David Gow Reviewed-by: Daniel Latypov Signed-off-by: Shuah Khan --- Documentation/dev-tools/kunit/architecture.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/dev-tools/kunit/architecture.rst b/Documentation/dev-tools/kunit/architecture.rst index e95ab05342bb..f335f883f8f6 100644 --- a/Documentation/dev-tools/kunit/architecture.rst +++ b/Documentation/dev-tools/kunit/architecture.rst @@ -119,9 +119,9 @@ All expectations/assertions are formatted as: terminated immediately. - Assertions call the function: - ``void __noreturn kunit_abort(struct kunit *)``. + ``void __noreturn __kunit_abort(struct kunit *)``. - - ``kunit_abort`` calls the function: + - ``__kunit_abort`` calls the function: ``void __noreturn kunit_try_catch_throw(struct kunit_try_catch *try_catch)``. - ``kunit_try_catch_throw`` calls the function: -- cgit v1.2.3 From 2e66833579ed759d7b7da1a8f07eb727ec6e80db Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Tue, 13 Jun 2023 18:30:15 +0000 Subject: MAINTAINERS: Add source tree entry for kunit Patches for kunit are managed in kunit and kunit-fixes branches of linux-kselftest tree before merged into the mainline. However, the MAINTAINERS section for kunit is not having the entries for the branches. Add the entries. Signed-off-by: SeongJae Park Reviewed-by: David Gow Signed-off-by: Shuah Khan --- MAINTAINERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 7e0b87d5aa2e..218489d0039e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11311,6 +11311,8 @@ L: linux-kselftest@vger.kernel.org L: kunit-dev@googlegroups.com S: Maintained W: https://google.github.io/kunit-docs/third_party/kernel/docs/ +T: git git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest.git kunit +T: git git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest.git kunit-fixes F: Documentation/dev-tools/kunit/ F: include/kunit/ F: lib/kunit/ -- cgit v1.2.3