From 65af9b964d72d8d8e88f4f673d4d0e9467197373 Mon Sep 17 00:00:00 2001 From: Daniel Latypov Date: Fri, 5 Feb 2021 14:14:09 -0800 Subject: kunit: don't show `1 == 1` in failed assertion messages Currently, given something (fairly dystopian) like > KUNIT_EXPECT_EQ(test, 2 + 2, 5) KUnit will prints a failure message like this. > Expected 2 + 2 == 5, but > 2 + 2 == 4 > 5 == 5 With this patch, the output just becomes > Expected 2 + 2 == 5, but > 2 + 2 == 4 This patch is slightly hacky, but it's quite common* to compare an expression to a literal integer value, so this can make KUnit less chatty in many cases. (This patch also fixes variants like KUNIT_EXPECT_GT, LE, et al.). It also allocates an additional string briefly, but given this only happens on test failures, it doesn't seem too bad a tradeoff. Also, in most cases it'll realize the lengths are unequal and bail out before the allocation. We could save the result of the formatted string to avoid wasting this extra work, but it felt cleaner to leave it as-is. Edge case: for something silly and unrealistic like > KUNIT_EXPECT_EQ(test, 4, 5); It'll generate this message with a trailing "but" > Expected 4 == 5, but > It didn't feel worth adding a check up-front to see if both sides are literals to handle this better. *A quick grep suggests 100+ comparisons to an integer literal as the right hand side. Signed-off-by: Daniel Latypov Tested-by: David Gow Reviewed-by: Brendan Higgins Signed-off-by: Shuah Khan --- lib/kunit/assert.c | 39 +++++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/kunit/assert.c b/lib/kunit/assert.c index 33acdaa28a7d..e0ec7d6fed6f 100644 --- a/lib/kunit/assert.c +++ b/lib/kunit/assert.c @@ -85,6 +85,29 @@ void kunit_ptr_not_err_assert_format(const struct kunit_assert *assert, } EXPORT_SYMBOL_GPL(kunit_ptr_not_err_assert_format); +/* Checks if `text` is a literal representing `value`, e.g. "5" and 5 */ +static bool is_literal(struct kunit *test, const char *text, long long value, + gfp_t gfp) +{ + char *buffer; + int len; + bool ret; + + len = snprintf(NULL, 0, "%lld", value); + if (strlen(text) != len) + return false; + + buffer = kunit_kmalloc(test, len+1, gfp); + if (!buffer) + return false; + + snprintf(buffer, len+1, "%lld", value); + ret = strncmp(buffer, text, len) == 0; + + kunit_kfree(test, buffer); + return ret; +} + void kunit_binary_assert_format(const struct kunit_assert *assert, struct string_stream *stream) { @@ -97,12 +120,16 @@ void kunit_binary_assert_format(const struct kunit_assert *assert, binary_assert->left_text, binary_assert->operation, binary_assert->right_text); - string_stream_add(stream, KUNIT_SUBSUBTEST_INDENT "%s == %lld\n", - binary_assert->left_text, - binary_assert->left_value); - string_stream_add(stream, KUNIT_SUBSUBTEST_INDENT "%s == %lld", - binary_assert->right_text, - binary_assert->right_value); + if (!is_literal(stream->test, binary_assert->left_text, + binary_assert->left_value, stream->gfp)) + string_stream_add(stream, KUNIT_SUBSUBTEST_INDENT "%s == %lld\n", + binary_assert->left_text, + binary_assert->left_value); + if (!is_literal(stream->test, binary_assert->right_text, + binary_assert->right_value, stream->gfp)) + string_stream_add(stream, KUNIT_SUBSUBTEST_INDENT "%s == %lld", + binary_assert->right_text, + binary_assert->right_value); kunit_assert_print_msg(assert, stream); } EXPORT_SYMBOL_GPL(kunit_binary_assert_format); -- cgit v1.2.3 From 5d31f71efcb6bce56ca3ab92eed0c8f2dbcc6f9a Mon Sep 17 00:00:00 2001 From: Daniel Latypov Date: Fri, 5 Feb 2021 16:08:52 -0800 Subject: kunit: add kunit.filter_glob cmdline option to filter suites E.g. specifying this would run suites with "list" in their name. kunit.filter_glob=list* Note: the executor prints out a TAP header that includes the number of suites we intend to run. So unless we want to report empty results for filtered-out suites, we need to do the filtering here in the executor. It's also probably better in the executor since we most likely don't want any filtering to apply to tests built as modules. This code does add a CONFIG_GLOB=y dependency for CONFIG_KUNIT=y. But the code seems light enough that it shouldn't be an issue. For now, we only filter on suite names so we don't have to create copies of the suites themselves, just the array (of arrays) holding them. The name is rather generic since in the future, we could consider extending it to a syntax like: kunit.filter_glob=. E.g. to run all the del list tests kunit.filter_glob=list-kunit-test.*del* But at the moment, it's far easier to manually comment out test cases in test files as opposed to messing with sets of Kconfig entries to select specific suites. So even just doing this makes using kunit far less annoying. Signed-off-by: Daniel Latypov Reviewed-by: Brendan Higgins Signed-off-by: Shuah Khan --- lib/kunit/Kconfig | 1 + lib/kunit/executor.c | 93 +++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 85 insertions(+), 9 deletions(-) (limited to 'lib') diff --git a/lib/kunit/Kconfig b/lib/kunit/Kconfig index 00909e6a2443..0b5dfb001bac 100644 --- a/lib/kunit/Kconfig +++ b/lib/kunit/Kconfig @@ -4,6 +4,7 @@ menuconfig KUNIT tristate "KUnit - Enable support for unit tests" + select GLOB if KUNIT=y help Enables support for kernel unit tests (KUnit), a lightweight unit testing and mocking framework for the Linux kernel. These tests are diff --git a/lib/kunit/executor.c b/lib/kunit/executor.c index a95742a4ece7..15832ed44668 100644 --- a/lib/kunit/executor.c +++ b/lib/kunit/executor.c @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 #include +#include +#include /* * These symbols point to the .kunit_test_suites section and are defined in @@ -11,14 +13,81 @@ extern struct kunit_suite * const * const __kunit_suites_end[]; #if IS_BUILTIN(CONFIG_KUNIT) -static void kunit_print_tap_header(void) +static char *filter_glob; +module_param(filter_glob, charp, 0); +MODULE_PARM_DESC(filter_glob, + "Filter which KUnit test suites run at boot-time, e.g. list*"); + +static struct kunit_suite * const * +kunit_filter_subsuite(struct kunit_suite * const * const subsuite) +{ + int i, n = 0; + struct kunit_suite **filtered; + + n = 0; + for (i = 0; subsuite[i] != NULL; ++i) { + if (glob_match(filter_glob, subsuite[i]->name)) + ++n; + } + + if (n == 0) + return NULL; + + filtered = kmalloc_array(n + 1, sizeof(*filtered), GFP_KERNEL); + if (!filtered) + return NULL; + + n = 0; + for (i = 0; subsuite[i] != NULL; ++i) { + if (glob_match(filter_glob, subsuite[i]->name)) + filtered[n++] = subsuite[i]; + } + filtered[n] = NULL; + + return filtered; +} + +struct suite_set { + struct kunit_suite * const * const *start; + struct kunit_suite * const * const *end; +}; + +static struct suite_set kunit_filter_suites(void) +{ + int i; + struct kunit_suite * const **copy, * const *filtered_subsuite; + struct suite_set filtered; + + const size_t max = __kunit_suites_end - __kunit_suites_start; + + if (!filter_glob) { + filtered.start = __kunit_suites_start; + filtered.end = __kunit_suites_end; + return filtered; + } + + copy = kmalloc_array(max, sizeof(*filtered.start), GFP_KERNEL); + filtered.start = copy; + if (!copy) { /* won't be able to run anything, return an empty set */ + filtered.end = copy; + return filtered; + } + + for (i = 0; i < max; ++i) { + filtered_subsuite = kunit_filter_subsuite(__kunit_suites_start[i]); + if (filtered_subsuite) + *copy++ = filtered_subsuite; + } + filtered.end = copy; + return filtered; +} + +static void kunit_print_tap_header(struct suite_set *suite_set) { struct kunit_suite * const * const *suites, * const *subsuite; int num_of_suites = 0; - for (suites = __kunit_suites_start; - suites < __kunit_suites_end; - suites++) + for (suites = suite_set->start; suites < suite_set->end; suites++) for (subsuite = *suites; *subsuite != NULL; subsuite++) num_of_suites++; @@ -30,12 +99,18 @@ int kunit_run_all_tests(void) { struct kunit_suite * const * const *suites; - kunit_print_tap_header(); + struct suite_set suite_set = kunit_filter_suites(); + + kunit_print_tap_header(&suite_set); + + for (suites = suite_set.start; suites < suite_set.end; suites++) + __kunit_test_suites_init(*suites); - for (suites = __kunit_suites_start; - suites < __kunit_suites_end; - suites++) - __kunit_test_suites_init(*suites); + if (filter_glob) { /* a copy was made of each array */ + for (suites = suite_set.start; suites < suite_set.end; suites++) + kfree(*suites); + kfree(suite_set.start); + } return 0; } -- cgit v1.2.3