Skip to content

Commit 783955f

Browse files
committed
Merge tag 'linux-kselftest-kunit-5.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest
Pull KUnit updates from Shuah Khan - support for filtering test suites using glob from Daniel Latypov. "kunit_filter.glob" command line option is passed to the UML kernel, which currently only supports filtering by suite name. This support allows running different subsets of tests, e.g. $ ./tools/testing/kunit/kunit.py build $ ./tools/testing/kunit/kunit.py exec 'list*' $ ./tools/testing/kunit/kunit.py exec 'kunit*' - several fixes and cleanups also from Daniel Latypov. * tag 'linux-kselftest-kunit-5.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest: kunit: tool: fix unintentional statefulness in run_kernel() kunit: tool: add support for filtering suites by glob kunit: add kunit.filter_glob cmdline option to filter suites kunit: don't show `1 == 1` in failed assertion messages kunit: make kunit_tool accept optional path to .kunitconfig fragment Documentation: kunit: add tips.rst for small examples KUnit: Docs: make start.rst example Kconfig follow style.rst kunit: tool: simplify kconfig is_subset_of() logic minor: kunit: tool: fix unit test so it can run from non-root dir kunit: tool: use `with open()` in unit test kunit: tool: stop using bare asserts in unit test kunit: tool: fix unit test cleanup handling
2 parents 8021509 + 7af2914 commit 783955f

File tree

10 files changed

+390
-132
lines changed

10 files changed

+390
-132
lines changed

Documentation/dev-tools/kunit/index.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ KUnit - Unit Testing for the Linux Kernel
1313
api/index
1414
style
1515
faq
16+
tips
1617

1718
What is KUnit?
1819
==============
@@ -88,6 +89,7 @@ How do I use it?
8889
================
8990

9091
* :doc:`start` - for new users of KUnit
92+
* :doc:`tips` - for short examples of best practices
9193
* :doc:`usage` - for a more detailed explanation of KUnit features
9294
* :doc:`api/index` - for the list of KUnit APIs used for testing
9395
* :doc:`kunit-tool` - for more information on the kunit_tool helper script

Documentation/dev-tools/kunit/start.rst

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,8 +196,9 @@ Now add the following to ``drivers/misc/Kconfig``:
196196
.. code-block:: kconfig
197197
198198
config MISC_EXAMPLE_TEST
199-
bool "Test for my example"
199+
tristate "Test for my example" if !KUNIT_ALL_TESTS
200200
depends on MISC_EXAMPLE && KUNIT=y
201+
default KUNIT_ALL_TESTS
201202
202203
and the following to ``drivers/misc/Makefile``:
203204

@@ -233,5 +234,7 @@ Congrats! You just wrote your first KUnit test!
233234

234235
Next Steps
235236
==========
236-
* Check out the :doc:`usage` page for a more
237+
* Check out the :doc:`tips` page for tips on
238+
writing idiomatic KUnit tests.
239+
* Optional: see the :doc:`usage` page for a more
237240
in-depth explanation of KUnit.
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
.. SPDX-License-Identifier: GPL-2.0
2+
3+
============================
4+
Tips For Writing KUnit Tests
5+
============================
6+
7+
Exiting early on failed expectations
8+
------------------------------------
9+
10+
``KUNIT_EXPECT_EQ`` and friends will mark the test as failed and continue
11+
execution. In some cases, it's unsafe to continue and you can use the
12+
``KUNIT_ASSERT`` variant to exit on failure.
13+
14+
.. code-block:: c
15+
16+
void example_test_user_alloc_function(struct kunit *test)
17+
{
18+
void *object = alloc_some_object_for_me();
19+
20+
/* Make sure we got a valid pointer back. */
21+
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, object);
22+
do_something_with_object(object);
23+
}
24+
25+
Allocating memory
26+
-----------------
27+
28+
Where you would use ``kzalloc``, you should prefer ``kunit_kzalloc`` instead.
29+
KUnit will ensure the memory is freed once the test completes.
30+
31+
This is particularly useful since it lets you use the ``KUNIT_ASSERT_EQ``
32+
macros to exit early from a test without having to worry about remembering to
33+
call ``kfree``.
34+
35+
Example:
36+
37+
.. code-block:: c
38+
39+
void example_test_allocation(struct kunit *test)
40+
{
41+
char *buffer = kunit_kzalloc(test, 16, GFP_KERNEL);
42+
/* Ensure allocation succeeded. */
43+
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buffer);
44+
45+
KUNIT_ASSERT_STREQ(test, buffer, "");
46+
}
47+
48+
49+
Testing static functions
50+
------------------------
51+
52+
If you don't want to expose functions or variables just for testing, one option
53+
is to conditionally ``#include`` the test file at the end of your .c file, e.g.
54+
55+
.. code-block:: c
56+
57+
/* In my_file.c */
58+
59+
static int do_interesting_thing();
60+
61+
#ifdef CONFIG_MY_KUNIT_TEST
62+
#include "my_kunit_test.c"
63+
#endif
64+
65+
Injecting test-only code
66+
------------------------
67+
68+
Similarly to the above, it can be useful to add test-specific logic.
69+
70+
.. code-block:: c
71+
72+
/* In my_file.h */
73+
74+
#ifdef CONFIG_MY_KUNIT_TEST
75+
/* Defined in my_kunit_test.c */
76+
void test_only_hook(void);
77+
#else
78+
void test_only_hook(void) { }
79+
#endif
80+
81+
TODO([email protected]): add an example of using ``current->kunit_test`` in
82+
such a hook when it's not only updated for ``CONFIG_KASAN=y``.
83+
84+
Customizing error messages
85+
--------------------------
86+
87+
Each of the ``KUNIT_EXPECT`` and ``KUNIT_ASSERT`` macros have a ``_MSG`` variant.
88+
These take a format string and arguments to provide additional context to the automatically generated error messages.
89+
90+
.. code-block:: c
91+
92+
char some_str[41];
93+
generate_sha1_hex_string(some_str);
94+
95+
/* Before. Not easy to tell why the test failed. */
96+
KUNIT_EXPECT_EQ(test, strlen(some_str), 40);
97+
98+
/* After. Now we see the offending string. */
99+
KUNIT_EXPECT_EQ_MSG(test, strlen(some_str), 40, "some_str='%s'", some_str);
100+
101+
Alternatively, one can take full control over the error message by using ``KUNIT_FAIL()``, e.g.
102+
103+
.. code-block:: c
104+
105+
/* Before */
106+
KUNIT_EXPECT_EQ(test, some_setup_function(), 0);
107+
108+
/* After: full control over the failure message. */
109+
if (some_setup_function())
110+
KUNIT_FAIL(test, "Failed to setup thing for testing");
111+
112+
Next Steps
113+
==========
114+
* Optional: see the :doc:`usage` page for a more
115+
in-depth explanation of KUnit.

lib/kunit/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
menuconfig KUNIT
66
tristate "KUnit - Enable support for unit tests"
7+
select GLOB if KUNIT=y
78
help
89
Enables support for kernel unit tests (KUnit), a lightweight unit
910
testing and mocking framework for the Linux kernel. These tests are

lib/kunit/assert.c

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,29 @@ void kunit_ptr_not_err_assert_format(const struct kunit_assert *assert,
8585
}
8686
EXPORT_SYMBOL_GPL(kunit_ptr_not_err_assert_format);
8787

88+
/* Checks if `text` is a literal representing `value`, e.g. "5" and 5 */
89+
static bool is_literal(struct kunit *test, const char *text, long long value,
90+
gfp_t gfp)
91+
{
92+
char *buffer;
93+
int len;
94+
bool ret;
95+
96+
len = snprintf(NULL, 0, "%lld", value);
97+
if (strlen(text) != len)
98+
return false;
99+
100+
buffer = kunit_kmalloc(test, len+1, gfp);
101+
if (!buffer)
102+
return false;
103+
104+
snprintf(buffer, len+1, "%lld", value);
105+
ret = strncmp(buffer, text, len) == 0;
106+
107+
kunit_kfree(test, buffer);
108+
return ret;
109+
}
110+
88111
void kunit_binary_assert_format(const struct kunit_assert *assert,
89112
struct string_stream *stream)
90113
{
@@ -97,12 +120,16 @@ void kunit_binary_assert_format(const struct kunit_assert *assert,
97120
binary_assert->left_text,
98121
binary_assert->operation,
99122
binary_assert->right_text);
100-
string_stream_add(stream, KUNIT_SUBSUBTEST_INDENT "%s == %lld\n",
101-
binary_assert->left_text,
102-
binary_assert->left_value);
103-
string_stream_add(stream, KUNIT_SUBSUBTEST_INDENT "%s == %lld",
104-
binary_assert->right_text,
105-
binary_assert->right_value);
123+
if (!is_literal(stream->test, binary_assert->left_text,
124+
binary_assert->left_value, stream->gfp))
125+
string_stream_add(stream, KUNIT_SUBSUBTEST_INDENT "%s == %lld\n",
126+
binary_assert->left_text,
127+
binary_assert->left_value);
128+
if (!is_literal(stream->test, binary_assert->right_text,
129+
binary_assert->right_value, stream->gfp))
130+
string_stream_add(stream, KUNIT_SUBSUBTEST_INDENT "%s == %lld",
131+
binary_assert->right_text,
132+
binary_assert->right_value);
106133
kunit_assert_print_msg(assert, stream);
107134
}
108135
EXPORT_SYMBOL_GPL(kunit_binary_assert_format);

lib/kunit/executor.c

Lines changed: 84 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// SPDX-License-Identifier: GPL-2.0
22

33
#include <kunit/test.h>
4+
#include <linux/glob.h>
5+
#include <linux/moduleparam.h>
46

57
/*
68
* 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[];
1113

1214
#if IS_BUILTIN(CONFIG_KUNIT)
1315

14-
static void kunit_print_tap_header(void)
16+
static char *filter_glob;
17+
module_param(filter_glob, charp, 0);
18+
MODULE_PARM_DESC(filter_glob,
19+
"Filter which KUnit test suites run at boot-time, e.g. list*");
20+
21+
static struct kunit_suite * const *
22+
kunit_filter_subsuite(struct kunit_suite * const * const subsuite)
23+
{
24+
int i, n = 0;
25+
struct kunit_suite **filtered;
26+
27+
n = 0;
28+
for (i = 0; subsuite[i] != NULL; ++i) {
29+
if (glob_match(filter_glob, subsuite[i]->name))
30+
++n;
31+
}
32+
33+
if (n == 0)
34+
return NULL;
35+
36+
filtered = kmalloc_array(n + 1, sizeof(*filtered), GFP_KERNEL);
37+
if (!filtered)
38+
return NULL;
39+
40+
n = 0;
41+
for (i = 0; subsuite[i] != NULL; ++i) {
42+
if (glob_match(filter_glob, subsuite[i]->name))
43+
filtered[n++] = subsuite[i];
44+
}
45+
filtered[n] = NULL;
46+
47+
return filtered;
48+
}
49+
50+
struct suite_set {
51+
struct kunit_suite * const * const *start;
52+
struct kunit_suite * const * const *end;
53+
};
54+
55+
static struct suite_set kunit_filter_suites(void)
56+
{
57+
int i;
58+
struct kunit_suite * const **copy, * const *filtered_subsuite;
59+
struct suite_set filtered;
60+
61+
const size_t max = __kunit_suites_end - __kunit_suites_start;
62+
63+
if (!filter_glob) {
64+
filtered.start = __kunit_suites_start;
65+
filtered.end = __kunit_suites_end;
66+
return filtered;
67+
}
68+
69+
copy = kmalloc_array(max, sizeof(*filtered.start), GFP_KERNEL);
70+
filtered.start = copy;
71+
if (!copy) { /* won't be able to run anything, return an empty set */
72+
filtered.end = copy;
73+
return filtered;
74+
}
75+
76+
for (i = 0; i < max; ++i) {
77+
filtered_subsuite = kunit_filter_subsuite(__kunit_suites_start[i]);
78+
if (filtered_subsuite)
79+
*copy++ = filtered_subsuite;
80+
}
81+
filtered.end = copy;
82+
return filtered;
83+
}
84+
85+
static void kunit_print_tap_header(struct suite_set *suite_set)
1586
{
1687
struct kunit_suite * const * const *suites, * const *subsuite;
1788
int num_of_suites = 0;
1889

19-
for (suites = __kunit_suites_start;
20-
suites < __kunit_suites_end;
21-
suites++)
90+
for (suites = suite_set->start; suites < suite_set->end; suites++)
2291
for (subsuite = *suites; *subsuite != NULL; subsuite++)
2392
num_of_suites++;
2493

@@ -30,12 +99,18 @@ int kunit_run_all_tests(void)
3099
{
31100
struct kunit_suite * const * const *suites;
32101

33-
kunit_print_tap_header();
102+
struct suite_set suite_set = kunit_filter_suites();
103+
104+
kunit_print_tap_header(&suite_set);
105+
106+
for (suites = suite_set.start; suites < suite_set.end; suites++)
107+
__kunit_test_suites_init(*suites);
34108

35-
for (suites = __kunit_suites_start;
36-
suites < __kunit_suites_end;
37-
suites++)
38-
__kunit_test_suites_init(*suites);
109+
if (filter_glob) { /* a copy was made of each array */
110+
for (suites = suite_set.start; suites < suite_set.end; suites++)
111+
kfree(*suites);
112+
kfree(suite_set.start);
113+
}
39114

40115
return 0;
41116
}

0 commit comments

Comments
 (0)