Skip to content

Commit a127b15

Browse files
dlatypovshuahkh
authored andcommitted
kunit: tool: allow filtering test cases via glob
Commit 1d71307 ("kunit: add unit test for filtering suites by names") introduced the ability to filter which suites we run via glob. This change extends it so we can also filter individual test cases inside of suites as well. This is quite useful when, e.g. * trying to run just the tests cases you've just added or are working on * trying to debug issues with test hermeticity Examples: $ ./tools/testing/kunit/kunit.py run --kunitconfig=lib/kunit '*exec*.parse*' ... ============================================================ ======== [PASSED] kunit_executor_test ======== [PASSED] parse_filter_test ============================================================ Testing complete. 1 tests run. 0 failed. 0 crashed. $ ./tools/testing/kunit/kunit.py run --kunitconfig=lib/kunit '*.no_matching_tests' ... [ERROR] no tests run! Signed-off-by: Daniel Latypov <dlatypov@google.com> Reviewed-by: David Gow <davidgow@google.com> Reviewed-by: Brendan Higgins <brendanhiggins@google.com> Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
1 parent b7cbaef commit a127b15

File tree

4 files changed

+203
-29
lines changed

4 files changed

+203
-29
lines changed

Documentation/dev-tools/kunit/running_tips.rst

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ It can be handy to create a bash function like:
2525
Running a subset of tests
2626
-------------------------
2727

28-
``kunit.py run`` accepts an optional glob argument to filter tests. Currently
29-
this only matches against suite names, but this may change in the future.
28+
``kunit.py run`` accepts an optional glob argument to filter tests. The format
29+
is ``"<suite_glob>[.test_glob]"``.
3030

3131
Say that we wanted to run the sysctl tests, we could do so via:
3232

@@ -35,6 +35,13 @@ Say that we wanted to run the sysctl tests, we could do so via:
3535
$ echo -e 'CONFIG_KUNIT=y\nCONFIG_KUNIT_ALL_TESTS=y' > .kunit/.kunitconfig
3636
$ ./tools/testing/kunit/kunit.py run 'sysctl*'
3737
38+
We can filter down to just the "write" tests via:
39+
40+
.. code-block:: bash
41+
42+
$ echo -e 'CONFIG_KUNIT=y\nCONFIG_KUNIT_ALL_TESTS=y' > .kunit/.kunitconfig
43+
$ ./tools/testing/kunit/kunit.py run 'sysctl*.*write*'
44+
3845
We're paying the cost of building more tests than we need this way, but it's
3946
easier than fiddling with ``.kunitconfig`` files or commenting out
4047
``kunit_suite``'s.

lib/kunit/executor.c

Lines changed: 96 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,80 @@ extern struct kunit_suite * const * const __kunit_suites_end[];
1717
static char *filter_glob_param;
1818
module_param_named(filter_glob, filter_glob_param, charp, 0);
1919
MODULE_PARM_DESC(filter_glob,
20-
"Filter which KUnit test suites run at boot-time, e.g. list*");
20+
"Filter which KUnit test suites/tests run at boot-time, e.g. list* or list*.*del_test");
21+
22+
/* glob_match() needs NULL terminated strings, so we need a copy of filter_glob_param. */
23+
struct kunit_test_filter {
24+
char *suite_glob;
25+
char *test_glob;
26+
};
27+
28+
/* Split "suite_glob.test_glob" into two. Assumes filter_glob is not empty. */
29+
static void kunit_parse_filter_glob(struct kunit_test_filter *parsed,
30+
const char *filter_glob)
31+
{
32+
const int len = strlen(filter_glob);
33+
const char *period = strchr(filter_glob, '.');
34+
35+
if (!period) {
36+
parsed->suite_glob = kmalloc(len, GFP_KERNEL);
37+
parsed->test_glob = NULL;
38+
strcpy(parsed->suite_glob, filter_glob);
39+
return;
40+
}
41+
42+
parsed->suite_glob = kzalloc(period - filter_glob + 1, GFP_KERNEL);
43+
parsed->test_glob = kzalloc(len - (period - filter_glob) + 1, GFP_KERNEL);
44+
45+
strncpy(parsed->suite_glob, filter_glob, period - filter_glob);
46+
strncpy(parsed->test_glob, period + 1, len - (period - filter_glob));
47+
}
48+
49+
/* Create a copy of suite with only tests that match test_glob. */
50+
static struct kunit_suite *
51+
kunit_filter_tests(struct kunit_suite *const suite, const char *test_glob)
52+
{
53+
int n = 0;
54+
struct kunit_case *filtered, *test_case;
55+
struct kunit_suite *copy;
56+
57+
kunit_suite_for_each_test_case(suite, test_case) {
58+
if (!test_glob || glob_match(test_glob, test_case->name))
59+
++n;
60+
}
61+
62+
if (n == 0)
63+
return NULL;
64+
65+
/* Use memcpy to workaround copy->name being const. */
66+
copy = kmalloc(sizeof(*copy), GFP_KERNEL);
67+
memcpy(copy, suite, sizeof(*copy));
68+
69+
filtered = kcalloc(n + 1, sizeof(*filtered), GFP_KERNEL);
70+
71+
n = 0;
72+
kunit_suite_for_each_test_case(suite, test_case) {
73+
if (!test_glob || glob_match(test_glob, test_case->name))
74+
filtered[n++] = *test_case;
75+
}
76+
77+
copy->test_cases = filtered;
78+
return copy;
79+
}
2180

2281
static char *kunit_shutdown;
2382
core_param(kunit_shutdown, kunit_shutdown, charp, 0644);
2483

2584
static struct kunit_suite * const *
2685
kunit_filter_subsuite(struct kunit_suite * const * const subsuite,
27-
const char *filter_glob)
86+
struct kunit_test_filter *filter)
2887
{
2988
int i, n = 0;
30-
struct kunit_suite **filtered;
89+
struct kunit_suite **filtered, *filtered_suite;
3190

3291
n = 0;
33-
for (i = 0; subsuite[i] != NULL; ++i) {
34-
if (glob_match(filter_glob, subsuite[i]->name))
92+
for (i = 0; subsuite[i]; ++i) {
93+
if (glob_match(filter->suite_glob, subsuite[i]->name))
3594
++n;
3695
}
3796

@@ -44,8 +103,11 @@ kunit_filter_subsuite(struct kunit_suite * const * const subsuite,
44103

45104
n = 0;
46105
for (i = 0; subsuite[i] != NULL; ++i) {
47-
if (glob_match(filter_glob, subsuite[i]->name))
48-
filtered[n++] = subsuite[i];
106+
if (!glob_match(filter->suite_glob, subsuite[i]->name))
107+
continue;
108+
filtered_suite = kunit_filter_tests(subsuite[i], filter->test_glob);
109+
if (filtered_suite)
110+
filtered[n++] = filtered_suite;
49111
}
50112
filtered[n] = NULL;
51113

@@ -57,12 +119,32 @@ struct suite_set {
57119
struct kunit_suite * const * const *end;
58120
};
59121

122+
static void kunit_free_subsuite(struct kunit_suite * const *subsuite)
123+
{
124+
unsigned int i;
125+
126+
for (i = 0; subsuite[i]; i++)
127+
kfree(subsuite[i]);
128+
129+
kfree(subsuite);
130+
}
131+
132+
static void kunit_free_suite_set(struct suite_set suite_set)
133+
{
134+
struct kunit_suite * const * const *suites;
135+
136+
for (suites = suite_set.start; suites < suite_set.end; suites++)
137+
kunit_free_subsuite(*suites);
138+
kfree(suite_set.start);
139+
}
140+
60141
static struct suite_set kunit_filter_suites(const struct suite_set *suite_set,
61142
const char *filter_glob)
62143
{
63144
int i;
64145
struct kunit_suite * const **copy, * const *filtered_subsuite;
65146
struct suite_set filtered;
147+
struct kunit_test_filter filter;
66148

67149
const size_t max = suite_set->end - suite_set->start;
68150

@@ -73,12 +155,17 @@ static struct suite_set kunit_filter_suites(const struct suite_set *suite_set,
73155
return filtered;
74156
}
75157

158+
kunit_parse_filter_glob(&filter, filter_glob);
159+
76160
for (i = 0; i < max; ++i) {
77-
filtered_subsuite = kunit_filter_subsuite(suite_set->start[i], filter_glob);
161+
filtered_subsuite = kunit_filter_subsuite(suite_set->start[i], &filter);
78162
if (filtered_subsuite)
79163
*copy++ = filtered_subsuite;
80164
}
81165
filtered.end = copy;
166+
167+
kfree(filter.suite_glob);
168+
kfree(filter.test_glob);
82169
return filtered;
83170
}
84171

@@ -126,9 +213,7 @@ int kunit_run_all_tests(void)
126213
__kunit_test_suites_init(*suites);
127214

128215
if (filter_glob_param) { /* a copy was made of each array */
129-
for (suites = suite_set.start; suites < suite_set.end; suites++)
130-
kfree(*suites);
131-
kfree(suite_set.start);
216+
kunit_free_suite_set(suite_set);
132217
}
133218

134219
kunit_handle_shutdown();

lib/kunit/executor_test.c

Lines changed: 96 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,38 +9,103 @@
99
#include <kunit/test.h>
1010

1111
static void kfree_at_end(struct kunit *test, const void *to_free);
12+
static void free_subsuite_at_end(struct kunit *test,
13+
struct kunit_suite *const *to_free);
1214
static struct kunit_suite *alloc_fake_suite(struct kunit *test,
13-
const char *suite_name);
15+
const char *suite_name,
16+
struct kunit_case *test_cases);
17+
18+
static void dummy_test(struct kunit *test) {}
19+
20+
static struct kunit_case dummy_test_cases[] = {
21+
/* .run_case is not important, just needs to be non-NULL */
22+
{ .name = "test1", .run_case = dummy_test },
23+
{ .name = "test2", .run_case = dummy_test },
24+
{},
25+
};
26+
27+
static void parse_filter_test(struct kunit *test)
28+
{
29+
struct kunit_test_filter filter = {NULL, NULL};
30+
31+
kunit_parse_filter_glob(&filter, "suite");
32+
KUNIT_EXPECT_STREQ(test, filter.suite_glob, "suite");
33+
KUNIT_EXPECT_FALSE(test, filter.test_glob);
34+
kfree(filter.suite_glob);
35+
kfree(filter.test_glob);
36+
37+
kunit_parse_filter_glob(&filter, "suite.test");
38+
KUNIT_EXPECT_STREQ(test, filter.suite_glob, "suite");
39+
KUNIT_EXPECT_STREQ(test, filter.test_glob, "test");
40+
kfree(filter.suite_glob);
41+
kfree(filter.test_glob);
42+
}
1443

1544
static void filter_subsuite_test(struct kunit *test)
1645
{
1746
struct kunit_suite *subsuite[3] = {NULL, NULL, NULL};
1847
struct kunit_suite * const *filtered;
48+
struct kunit_test_filter filter = {
49+
.suite_glob = "suite2",
50+
.test_glob = NULL,
51+
};
1952

20-
subsuite[0] = alloc_fake_suite(test, "suite1");
21-
subsuite[1] = alloc_fake_suite(test, "suite2");
53+
subsuite[0] = alloc_fake_suite(test, "suite1", dummy_test_cases);
54+
subsuite[1] = alloc_fake_suite(test, "suite2", dummy_test_cases);
2255

2356
/* Want: suite1, suite2, NULL -> suite2, NULL */
24-
filtered = kunit_filter_subsuite(subsuite, "suite2*");
57+
filtered = kunit_filter_subsuite(subsuite, &filter);
2558
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filtered);
26-
kfree_at_end(test, filtered);
59+
free_subsuite_at_end(test, filtered);
2760

61+
/* Validate we just have suite2 */
2862
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filtered[0]);
2963
KUNIT_EXPECT_STREQ(test, (const char *)filtered[0]->name, "suite2");
64+
KUNIT_EXPECT_FALSE(test, filtered[1]);
65+
}
66+
67+
static void filter_subsuite_test_glob_test(struct kunit *test)
68+
{
69+
struct kunit_suite *subsuite[3] = {NULL, NULL, NULL};
70+
struct kunit_suite * const *filtered;
71+
struct kunit_test_filter filter = {
72+
.suite_glob = "suite2",
73+
.test_glob = "test2",
74+
};
75+
76+
subsuite[0] = alloc_fake_suite(test, "suite1", dummy_test_cases);
77+
subsuite[1] = alloc_fake_suite(test, "suite2", dummy_test_cases);
3078

79+
/* Want: suite1, suite2, NULL -> suite2 (just test1), NULL */
80+
filtered = kunit_filter_subsuite(subsuite, &filter);
81+
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filtered);
82+
free_subsuite_at_end(test, filtered);
83+
84+
/* Validate we just have suite2 */
85+
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filtered[0]);
86+
KUNIT_EXPECT_STREQ(test, (const char *)filtered[0]->name, "suite2");
3187
KUNIT_EXPECT_FALSE(test, filtered[1]);
88+
89+
/* Now validate we just have test2 */
90+
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filtered[0]->test_cases);
91+
KUNIT_EXPECT_STREQ(test, (const char *)filtered[0]->test_cases[0].name, "test2");
92+
KUNIT_EXPECT_FALSE(test, filtered[0]->test_cases[1].name);
3293
}
3394

3495
static void filter_subsuite_to_empty_test(struct kunit *test)
3596
{
3697
struct kunit_suite *subsuite[3] = {NULL, NULL, NULL};
3798
struct kunit_suite * const *filtered;
99+
struct kunit_test_filter filter = {
100+
.suite_glob = "not_found",
101+
.test_glob = NULL,
102+
};
38103

39-
subsuite[0] = alloc_fake_suite(test, "suite1");
40-
subsuite[1] = alloc_fake_suite(test, "suite2");
104+
subsuite[0] = alloc_fake_suite(test, "suite1", dummy_test_cases);
105+
subsuite[1] = alloc_fake_suite(test, "suite2", dummy_test_cases);
41106

42-
filtered = kunit_filter_subsuite(subsuite, "not_found");
43-
kfree_at_end(test, filtered); /* just in case */
107+
filtered = kunit_filter_subsuite(subsuite, &filter);
108+
free_subsuite_at_end(test, filtered); /* just in case */
44109

45110
KUNIT_EXPECT_FALSE_MSG(test, filtered,
46111
"should be NULL to indicate no match");
@@ -52,7 +117,7 @@ static void kfree_subsuites_at_end(struct kunit *test, struct suite_set *suite_s
52117

53118
kfree_at_end(test, suite_set->start);
54119
for (suites = suite_set->start; suites < suite_set->end; suites++)
55-
kfree_at_end(test, *suites);
120+
free_subsuite_at_end(test, *suites);
56121
}
57122

58123
static void filter_suites_test(struct kunit *test)
@@ -74,8 +139,8 @@ static void filter_suites_test(struct kunit *test)
74139
struct suite_set filtered = {.start = NULL, .end = NULL};
75140

76141
/* Emulate two files, each having one suite */
77-
subsuites[0][0] = alloc_fake_suite(test, "suite0");
78-
subsuites[1][0] = alloc_fake_suite(test, "suite1");
142+
subsuites[0][0] = alloc_fake_suite(test, "suite0", dummy_test_cases);
143+
subsuites[1][0] = alloc_fake_suite(test, "suite1", dummy_test_cases);
79144

80145
/* Filter out suite1 */
81146
filtered = kunit_filter_suites(&suite_set, "suite0");
@@ -88,7 +153,9 @@ static void filter_suites_test(struct kunit *test)
88153
}
89154

90155
static struct kunit_case executor_test_cases[] = {
156+
KUNIT_CASE(parse_filter_test),
91157
KUNIT_CASE(filter_subsuite_test),
158+
KUNIT_CASE(filter_subsuite_test_glob_test),
92159
KUNIT_CASE(filter_subsuite_to_empty_test),
93160
KUNIT_CASE(filter_suites_test),
94161
{}
@@ -120,14 +187,30 @@ static void kfree_at_end(struct kunit *test, const void *to_free)
120187
(void *)to_free);
121188
}
122189

190+
static void free_subsuite_res_free(struct kunit_resource *res)
191+
{
192+
kunit_free_subsuite(res->data);
193+
}
194+
195+
static void free_subsuite_at_end(struct kunit *test,
196+
struct kunit_suite *const *to_free)
197+
{
198+
if (IS_ERR_OR_NULL(to_free))
199+
return;
200+
kunit_alloc_resource(test, NULL, free_subsuite_res_free,
201+
GFP_KERNEL, (void *)to_free);
202+
}
203+
123204
static struct kunit_suite *alloc_fake_suite(struct kunit *test,
124-
const char *suite_name)
205+
const char *suite_name,
206+
struct kunit_case *test_cases)
125207
{
126208
struct kunit_suite *suite;
127209

128210
/* We normally never expect to allocate suites, hence the non-const cast. */
129211
suite = kunit_kzalloc(test, sizeof(*suite), GFP_KERNEL);
130212
strncpy((char *)suite->name, suite_name, sizeof(suite->name) - 1);
213+
suite->test_cases = test_cases;
131214

132215
return suite;
133216
}

tools/testing/kunit/kunit.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -263,9 +263,8 @@ def add_exec_opts(parser) -> None:
263263
default=300,
264264
metavar='timeout')
265265
parser.add_argument('filter_glob',
266-
help='maximum number of seconds to allow for all tests '
267-
'to run. This does not include time taken to build the '
268-
'tests.',
266+
help='Filter which KUnit test suites/tests run at '
267+
'boot-time, e.g. list* or list*.*del_test',
269268
type=str,
270269
nargs='?',
271270
default='',

0 commit comments

Comments
 (0)