1 | // SPDX-License-Identifier: GPL-2.0 |
2 | |
3 | #include <linux/reboot.h> |
4 | #include <kunit/test.h> |
5 | #include <kunit/attributes.h> |
6 | #include <linux/glob.h> |
7 | #include <linux/moduleparam.h> |
8 | |
9 | /* |
10 | * These symbols point to the .kunit_test_suites section and are defined in |
11 | * include/asm-generic/vmlinux.lds.h, and consequently must be extern. |
12 | */ |
13 | extern struct kunit_suite * const __kunit_suites_start[]; |
14 | extern struct kunit_suite * const __kunit_suites_end[]; |
15 | extern struct kunit_suite * const __kunit_init_suites_start[]; |
16 | extern struct kunit_suite * const __kunit_init_suites_end[]; |
17 | |
18 | static char *action_param; |
19 | |
20 | module_param_named(action, action_param, charp, 0400); |
21 | MODULE_PARM_DESC(action, |
22 | "Changes KUnit executor behavior, valid values are:\n" |
23 | "<none>: run the tests like normal\n" |
24 | "'list' to list test names instead of running them.\n" |
25 | "'list_attr' to list test names and attributes instead of running them.\n" ); |
26 | |
27 | const char *kunit_action(void) |
28 | { |
29 | return action_param; |
30 | } |
31 | |
32 | static char *filter_glob_param; |
33 | static char *filter_param; |
34 | static char *filter_action_param; |
35 | |
36 | module_param_named(filter_glob, filter_glob_param, charp, 0600); |
37 | MODULE_PARM_DESC(filter_glob, |
38 | "Filter which KUnit test suites/tests run at boot-time, e.g. list* or list*.*del_test" ); |
39 | module_param_named(filter, filter_param, charp, 0600); |
40 | MODULE_PARM_DESC(filter, |
41 | "Filter which KUnit test suites/tests run at boot-time using attributes, e.g. speed>slow" ); |
42 | module_param_named(filter_action, filter_action_param, charp, 0600); |
43 | MODULE_PARM_DESC(filter_action, |
44 | "Changes behavior of filtered tests using attributes, valid values are:\n" |
45 | "<none>: do not run filtered tests as normal\n" |
46 | "'skip': skip all filtered tests instead so tests will appear in output\n" ); |
47 | |
48 | const char *kunit_filter_glob(void) |
49 | { |
50 | return filter_glob_param; |
51 | } |
52 | |
53 | char *kunit_filter(void) |
54 | { |
55 | return filter_param; |
56 | } |
57 | |
58 | char *kunit_filter_action(void) |
59 | { |
60 | return filter_action_param; |
61 | } |
62 | |
63 | /* glob_match() needs NULL terminated strings, so we need a copy of filter_glob_param. */ |
64 | struct kunit_glob_filter { |
65 | char *suite_glob; |
66 | char *test_glob; |
67 | }; |
68 | |
69 | /* Split "suite_glob.test_glob" into two. Assumes filter_glob is not empty. */ |
70 | static int kunit_parse_glob_filter(struct kunit_glob_filter *parsed, |
71 | const char *filter_glob) |
72 | { |
73 | const int len = strlen(filter_glob); |
74 | const char *period = strchr(filter_glob, '.'); |
75 | |
76 | if (!period) { |
77 | parsed->suite_glob = kzalloc(size: len + 1, GFP_KERNEL); |
78 | if (!parsed->suite_glob) |
79 | return -ENOMEM; |
80 | |
81 | parsed->test_glob = NULL; |
82 | strcpy(p: parsed->suite_glob, q: filter_glob); |
83 | return 0; |
84 | } |
85 | |
86 | parsed->suite_glob = kzalloc(size: period - filter_glob + 1, GFP_KERNEL); |
87 | if (!parsed->suite_glob) |
88 | return -ENOMEM; |
89 | |
90 | parsed->test_glob = kzalloc(size: len - (period - filter_glob) + 1, GFP_KERNEL); |
91 | if (!parsed->test_glob) { |
92 | kfree(objp: parsed->suite_glob); |
93 | return -ENOMEM; |
94 | } |
95 | |
96 | strncpy(p: parsed->suite_glob, q: filter_glob, size: period - filter_glob); |
97 | strncpy(p: parsed->test_glob, q: period + 1, size: len - (period - filter_glob)); |
98 | |
99 | return 0; |
100 | } |
101 | |
102 | /* Create a copy of suite with only tests that match test_glob. */ |
103 | static struct kunit_suite * |
104 | kunit_filter_glob_tests(const struct kunit_suite *const suite, const char *test_glob) |
105 | { |
106 | int n = 0; |
107 | struct kunit_case *filtered, *test_case; |
108 | struct kunit_suite *copy; |
109 | |
110 | kunit_suite_for_each_test_case(suite, test_case) { |
111 | if (!test_glob || glob_match(pat: test_glob, str: test_case->name)) |
112 | ++n; |
113 | } |
114 | |
115 | if (n == 0) |
116 | return NULL; |
117 | |
118 | copy = kmemdup(p: suite, size: sizeof(*copy), GFP_KERNEL); |
119 | if (!copy) |
120 | return ERR_PTR(error: -ENOMEM); |
121 | |
122 | filtered = kcalloc(n: n + 1, size: sizeof(*filtered), GFP_KERNEL); |
123 | if (!filtered) { |
124 | kfree(objp: copy); |
125 | return ERR_PTR(error: -ENOMEM); |
126 | } |
127 | |
128 | n = 0; |
129 | kunit_suite_for_each_test_case(suite, test_case) { |
130 | if (!test_glob || glob_match(pat: test_glob, str: test_case->name)) |
131 | filtered[n++] = *test_case; |
132 | } |
133 | |
134 | copy->test_cases = filtered; |
135 | return copy; |
136 | } |
137 | |
138 | void kunit_free_suite_set(struct kunit_suite_set suite_set) |
139 | { |
140 | struct kunit_suite * const *suites; |
141 | |
142 | for (suites = suite_set.start; suites < suite_set.end; suites++) { |
143 | kfree(objp: (*suites)->test_cases); |
144 | kfree(objp: *suites); |
145 | } |
146 | kfree(objp: suite_set.start); |
147 | } |
148 | |
149 | /* |
150 | * Filter and reallocate test suites. Must return the filtered test suites set |
151 | * allocated at a valid virtual address or NULL in case of error. |
152 | */ |
153 | struct kunit_suite_set |
154 | kunit_filter_suites(const struct kunit_suite_set *suite_set, |
155 | const char *filter_glob, |
156 | char *filters, |
157 | char *filter_action, |
158 | int *err) |
159 | { |
160 | int i, j, k; |
161 | int filter_count = 0; |
162 | struct kunit_suite **copy, **copy_start, *filtered_suite, *new_filtered_suite; |
163 | struct kunit_suite_set filtered = {NULL, NULL}; |
164 | struct kunit_glob_filter parsed_glob; |
165 | struct kunit_attr_filter *parsed_filters = NULL; |
166 | struct kunit_suite * const *suites; |
167 | |
168 | const size_t max = suite_set->end - suite_set->start; |
169 | |
170 | copy = kcalloc(n: max, size: sizeof(*filtered.start), GFP_KERNEL); |
171 | if (!copy) { /* won't be able to run anything, return an empty set */ |
172 | return filtered; |
173 | } |
174 | copy_start = copy; |
175 | |
176 | if (filter_glob) { |
177 | *err = kunit_parse_glob_filter(parsed: &parsed_glob, filter_glob); |
178 | if (*err) |
179 | goto free_copy; |
180 | } |
181 | |
182 | /* Parse attribute filters */ |
183 | if (filters) { |
184 | filter_count = kunit_get_filter_count(input: filters); |
185 | parsed_filters = kcalloc(n: filter_count, size: sizeof(*parsed_filters), GFP_KERNEL); |
186 | if (!parsed_filters) { |
187 | *err = -ENOMEM; |
188 | goto free_parsed_glob; |
189 | } |
190 | for (j = 0; j < filter_count; j++) |
191 | parsed_filters[j] = kunit_next_attr_filter(filters: &filters, err); |
192 | if (*err) |
193 | goto free_parsed_filters; |
194 | } |
195 | |
196 | for (i = 0; &suite_set->start[i] != suite_set->end; i++) { |
197 | filtered_suite = suite_set->start[i]; |
198 | if (filter_glob) { |
199 | if (!glob_match(pat: parsed_glob.suite_glob, str: filtered_suite->name)) |
200 | continue; |
201 | filtered_suite = kunit_filter_glob_tests(suite: filtered_suite, |
202 | test_glob: parsed_glob.test_glob); |
203 | if (IS_ERR(ptr: filtered_suite)) { |
204 | *err = PTR_ERR(ptr: filtered_suite); |
205 | goto free_filtered_suite; |
206 | } |
207 | } |
208 | if (filter_count > 0 && parsed_filters != NULL) { |
209 | for (k = 0; k < filter_count; k++) { |
210 | new_filtered_suite = kunit_filter_attr_tests(suite: filtered_suite, |
211 | filter: parsed_filters[k], action: filter_action, err); |
212 | |
213 | /* Free previous copy of suite */ |
214 | if (k > 0 || filter_glob) { |
215 | kfree(objp: filtered_suite->test_cases); |
216 | kfree(objp: filtered_suite); |
217 | } |
218 | |
219 | filtered_suite = new_filtered_suite; |
220 | |
221 | if (*err) |
222 | goto free_filtered_suite; |
223 | |
224 | if (IS_ERR(ptr: filtered_suite)) { |
225 | *err = PTR_ERR(ptr: filtered_suite); |
226 | goto free_filtered_suite; |
227 | } |
228 | if (!filtered_suite) |
229 | break; |
230 | } |
231 | } |
232 | |
233 | if (!filtered_suite) |
234 | continue; |
235 | |
236 | *copy++ = filtered_suite; |
237 | } |
238 | filtered.start = copy_start; |
239 | filtered.end = copy; |
240 | |
241 | free_filtered_suite: |
242 | if (*err) { |
243 | for (suites = copy_start; suites < copy; suites++) { |
244 | kfree(objp: (*suites)->test_cases); |
245 | kfree(objp: *suites); |
246 | } |
247 | } |
248 | |
249 | free_parsed_filters: |
250 | if (filter_count) |
251 | kfree(objp: parsed_filters); |
252 | |
253 | free_parsed_glob: |
254 | if (filter_glob) { |
255 | kfree(objp: parsed_glob.suite_glob); |
256 | kfree(objp: parsed_glob.test_glob); |
257 | } |
258 | |
259 | free_copy: |
260 | if (*err) |
261 | kfree(objp: copy_start); |
262 | |
263 | return filtered; |
264 | } |
265 | |
266 | void kunit_exec_run_tests(struct kunit_suite_set *suite_set, bool builtin) |
267 | { |
268 | size_t num_suites = suite_set->end - suite_set->start; |
269 | |
270 | if (builtin || num_suites) { |
271 | pr_info("KTAP version 1\n" ); |
272 | pr_info("1..%zu\n" , num_suites); |
273 | } |
274 | |
275 | __kunit_test_suites_init(suites: suite_set->start, num_suites); |
276 | } |
277 | |
278 | void kunit_exec_list_tests(struct kunit_suite_set *suite_set, bool include_attr) |
279 | { |
280 | struct kunit_suite * const *suites; |
281 | struct kunit_case *test_case; |
282 | |
283 | /* Hack: print a ktap header so kunit.py can find the start of KUnit output. */ |
284 | pr_info("KTAP version 1\n" ); |
285 | |
286 | for (suites = suite_set->start; suites < suite_set->end; suites++) { |
287 | /* Print suite name and suite attributes */ |
288 | pr_info("%s\n" , (*suites)->name); |
289 | if (include_attr) |
290 | kunit_print_attr(test_or_suite: (void *)(*suites), is_test: false, test_level: 0); |
291 | |
292 | /* Print test case name and attributes in suite */ |
293 | kunit_suite_for_each_test_case((*suites), test_case) { |
294 | pr_info("%s.%s\n" , (*suites)->name, test_case->name); |
295 | if (include_attr) |
296 | kunit_print_attr(test_or_suite: (void *)test_case, is_test: true, test_level: 0); |
297 | } |
298 | } |
299 | } |
300 | |
301 | struct kunit_suite_set kunit_merge_suite_sets(struct kunit_suite_set init_suite_set, |
302 | struct kunit_suite_set suite_set) |
303 | { |
304 | struct kunit_suite_set total_suite_set = {NULL, NULL}; |
305 | struct kunit_suite **total_suite_start = NULL; |
306 | size_t init_num_suites, num_suites, suite_size; |
307 | int i = 0; |
308 | |
309 | init_num_suites = init_suite_set.end - init_suite_set.start; |
310 | num_suites = suite_set.end - suite_set.start; |
311 | suite_size = sizeof(suite_set.start); |
312 | |
313 | /* Allocate memory for array of all kunit suites */ |
314 | total_suite_start = kmalloc_array(n: init_num_suites + num_suites, size: suite_size, GFP_KERNEL); |
315 | if (!total_suite_start) |
316 | return total_suite_set; |
317 | |
318 | /* Append and mark init suites and then append all other kunit suites */ |
319 | memcpy(total_suite_start, init_suite_set.start, init_num_suites * suite_size); |
320 | for (i = 0; i < init_num_suites; i++) |
321 | total_suite_start[i]->is_init = true; |
322 | |
323 | memcpy(total_suite_start + init_num_suites, suite_set.start, num_suites * suite_size); |
324 | |
325 | /* Set kunit suite set start and end */ |
326 | total_suite_set.start = total_suite_start; |
327 | total_suite_set.end = total_suite_start + (init_num_suites + num_suites); |
328 | |
329 | return total_suite_set; |
330 | } |
331 | |
332 | #if IS_BUILTIN(CONFIG_KUNIT) |
333 | |
334 | static char *kunit_shutdown; |
335 | core_param(kunit_shutdown, kunit_shutdown, charp, 0644); |
336 | |
337 | static void kunit_handle_shutdown(void) |
338 | { |
339 | if (!kunit_shutdown) |
340 | return; |
341 | |
342 | if (!strcmp(kunit_shutdown, "poweroff" )) |
343 | kernel_power_off(); |
344 | else if (!strcmp(kunit_shutdown, "halt" )) |
345 | kernel_halt(); |
346 | else if (!strcmp(kunit_shutdown, "reboot" )) |
347 | kernel_restart(NULL); |
348 | |
349 | } |
350 | |
351 | int kunit_run_all_tests(void) |
352 | { |
353 | struct kunit_suite_set suite_set = {NULL, NULL}; |
354 | struct kunit_suite_set filtered_suite_set = {NULL, NULL}; |
355 | struct kunit_suite_set init_suite_set = { |
356 | __kunit_init_suites_start, __kunit_init_suites_end, |
357 | }; |
358 | struct kunit_suite_set normal_suite_set = { |
359 | __kunit_suites_start, __kunit_suites_end, |
360 | }; |
361 | size_t init_num_suites = init_suite_set.end - init_suite_set.start; |
362 | int err = 0; |
363 | |
364 | if (init_num_suites > 0) { |
365 | suite_set = kunit_merge_suite_sets(init_suite_set, suite_set: normal_suite_set); |
366 | if (!suite_set.start) |
367 | goto out; |
368 | } else |
369 | suite_set = normal_suite_set; |
370 | |
371 | if (!kunit_enabled()) { |
372 | pr_info("kunit: disabled\n" ); |
373 | goto free_out; |
374 | } |
375 | |
376 | if (filter_glob_param || filter_param) { |
377 | filtered_suite_set = kunit_filter_suites(suite_set: &suite_set, filter_glob: filter_glob_param, |
378 | filters: filter_param, filter_action: filter_action_param, err: &err); |
379 | |
380 | /* Free original suite set before using filtered suite set */ |
381 | if (init_num_suites > 0) |
382 | kfree(objp: suite_set.start); |
383 | suite_set = filtered_suite_set; |
384 | |
385 | if (err) { |
386 | pr_err("kunit executor: error filtering suites: %d\n" , err); |
387 | goto free_out; |
388 | } |
389 | } |
390 | |
391 | if (!action_param) |
392 | kunit_exec_run_tests(suite_set: &suite_set, builtin: true); |
393 | else if (strcmp(action_param, "list" ) == 0) |
394 | kunit_exec_list_tests(suite_set: &suite_set, include_attr: false); |
395 | else if (strcmp(action_param, "list_attr" ) == 0) |
396 | kunit_exec_list_tests(suite_set: &suite_set, include_attr: true); |
397 | else |
398 | pr_err("kunit executor: unknown action '%s'\n" , action_param); |
399 | |
400 | free_out: |
401 | if (filter_glob_param || filter_param) |
402 | kunit_free_suite_set(suite_set); |
403 | else if (init_num_suites > 0) |
404 | /* Don't use kunit_free_suite_set because suites aren't individually allocated */ |
405 | kfree(objp: suite_set.start); |
406 | |
407 | out: |
408 | kunit_handle_shutdown(); |
409 | return err; |
410 | } |
411 | |
412 | #if IS_BUILTIN(CONFIG_KUNIT_TEST) |
413 | #include "executor_test.c" |
414 | #endif |
415 | |
416 | #endif /* IS_BUILTIN(CONFIG_KUNIT) */ |
417 | |