Adds support to KUnit for failure expectation for specific tools. Uses named KUnit resources to keep track of whether or not a failure expectation has been set by the user. - Adds a generic KUNIT_EXPECT_TOOL_FAIL macro that can expect failure from any supported tool that uses the same identifying name - Adds kunit_fail_from_tool which is used to flag failures for specific tools. Requires "kunit: suppport failure from dynamic analysis tools": https://lore.kernel.org/linux-kselftest/20200813205722.1384108-1-urielguajardojr@xxxxxxxxx/ Signed-off-by: Uriel Guajardo <urielguajardo@xxxxxxxxxx> --- include/kunit/test-bug.h | 53 ++++++++++++++++++++++++++++++++++++++++ include/kunit/test.h | 5 ++++ lib/kunit/test.c | 46 +++++++++++++++++++++++++++++----- 3 files changed, 98 insertions(+), 6 deletions(-) diff --git a/include/kunit/test-bug.h b/include/kunit/test-bug.h index 283c19ec328f..383198f70cb5 100644 --- a/include/kunit/test-bug.h +++ b/include/kunit/test-bug.h @@ -9,16 +9,69 @@ #ifndef _KUNIT_TEST_BUG_H #define _KUNIT_TEST_BUG_H +/** + * struct kunit_expectation - represents expectations in KUnit, specifically + * used to keep track of failure expectations from analysis tools + */ +struct kunit_expectation { + bool expected; + bool found; +}; + #if IS_ENABLED(CONFIG_KUNIT) extern void kunit_fail_current_test(void); +/** + * kunit_fail_from_tool() - Fails the currently running KUnit tests under the + * given tool name. + * + * Note: Uses a named KUnit resource to track state. Do not use the name + * KUNIT_TOOL_{tool} for KUnit resources outside of here. + */ +#define kunit_fail_from_tool(tool) \ + if (current->kunit_test) \ + kunit_tool_fail(current->kunit_test, #tool,\ + "KUNIT_TOOL_" #tool) + +/** + * kunit_tool_expectation() - Returns the kunit_expectation for the given + * tool. If it cannot find the expectation, it creates an expectation and + * returns it. + * + * Note: Uses a named KUnit resource to track state. Do not use the name + * KUNIT_TOOL_{tool} for KUnit resources outside of here. + */ +#define kunit_tool_expectation(test, tool) \ + kunit_find_expectation(test, "KUNIT_TOOL_" #tool) + + +/** + * KUNIT_EXPECT_TOOL_FAIL() - Fails the currently running KUnit test if the + * condition does not cause an error within the given tool. + * + * Note: 'tool' must be consistent with the name specified in + * kunit_fail_from_tool(). If the tool fails KUnit using another name, KUnit + * will treat it as a separate tool. + */ +#define KUNIT_EXPECT_TOOL_FAIL(test, condition, tool) do { \ + struct kunit_expectation *data = kunit_tool_expectation(test, tool);\ + data->expected = true; \ + data->found = false; \ + condition; \ + KUNIT_EXPECT_EQ(test, data->expected, data->found); \ + data->expected = false; \ + data->found = false; \ +} while (0) + #else static inline void kunit_fail_current_test(void) { } +#define kunit_fail_from_tool(tool) do { } while (0) + #endif #endif /* _KUNIT_TEST_BUG_H */ diff --git a/include/kunit/test.h b/include/kunit/test.h index 81bf43a1abda..3da8e17ee32b 100644 --- a/include/kunit/test.h +++ b/include/kunit/test.h @@ -511,6 +511,11 @@ static inline int kunit_destroy_named_resource(struct kunit *test, */ void kunit_remove_resource(struct kunit *test, struct kunit_resource *res); +void kunit_tool_fail(struct kunit *test, char *tool_name, char *resource_name); + +struct kunit_expectation *kunit_find_expectation(struct kunit *test, + char *resource_name); + /** * kunit_kmalloc() - Like kmalloc() except the allocation is *test managed*. * @test: The test context object. diff --git a/lib/kunit/test.c b/lib/kunit/test.c index d8189d827368..458d1ad2daf2 100644 --- a/lib/kunit/test.c +++ b/lib/kunit/test.c @@ -22,6 +22,45 @@ void kunit_fail_current_test(void) kunit_set_failure(current->kunit_test); } +static void kunit_data_free(struct kunit_resource *res) +{ + kfree(res->data); +} + +struct kunit_expectation *kunit_find_expectation(struct kunit *test, + char *resource_name) +{ + struct kunit_resource *resource; + struct kunit_expectation *expectation; + + struct kunit_resource *existing = kunit_find_named_resource( + test, resource_name); + if (!existing) { + expectation = kzalloc(sizeof(*expectation), GFP_KERNEL); + resource = kunit_alloc_and_get_resource(test, NULL, + kunit_data_free, GFP_KERNEL, + expectation); + resource->name = resource_name; + kunit_put_resource(resource); + return expectation; + } + kunit_put_resource(existing); + return existing->data; +} +EXPORT_SYMBOL_GPL(kunit_find_expectation); + +void kunit_tool_fail(struct kunit *test, char *tool_name, char *resource_name) +{ + struct kunit_expectation *data = kunit_find_expectation(test, + resource_name); + if (!data->expected) { + kunit_warn(test, "Dynamic analysis tool failure from %s", + tool_name); + return kunit_fail_current_test(); + } + data->found = true; +} + static void kunit_print_tap_version(void) { static bool kunit_has_printed_tap_version; @@ -538,11 +577,6 @@ static int kunit_kmalloc_init(struct kunit_resource *res, void *context) return 0; } -static void kunit_kmalloc_free(struct kunit_resource *res) -{ - kfree(res->data); -} - void *kunit_kmalloc(struct kunit *test, size_t size, gfp_t gfp) { struct kunit_kmalloc_params params = { @@ -552,7 +586,7 @@ void *kunit_kmalloc(struct kunit *test, size_t size, gfp_t gfp) return kunit_alloc_resource(test, kunit_kmalloc_init, - kunit_kmalloc_free, + kunit_data_free, gfp, ¶ms); } -- 2.28.0.297.g1956fa8f8d-goog