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