| 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 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 |
|
| 64 | struct kunit_glob_filter {
|
| 65 | char *suite_glob;
|
| 66 | char *test_glob;
|
| 67 | };
|
| 68 |
|
| 69 |
|
| 70 | static int kunit_parse_glob_filter(struct kunit_glob_filter *parsed,
|
| 71 | const char *filter_glob)
|
| 72 | {
|
| 73 | const char *period = strchr(filter_glob, '.');
|
| 74 |
|
| 75 | if (!period) {
|
| 76 | parsed->suite_glob = kstrdup(filter_glob, GFP_KERNEL);
|
| 77 | if (!parsed->suite_glob)
|
| 78 | return -ENOMEM;
|
| 79 | parsed->test_glob = NULL;
|
| 80 | return 0;
|
| 81 | }
|
| 82 |
|
| 83 | parsed->suite_glob = kstrndup(filter_glob, period - filter_glob, GFP_KERNEL);
|
| 84 | if (!parsed->suite_glob)
|
| 85 | return -ENOMEM;
|
| 86 |
|
| 87 | parsed->test_glob = kstrdup(period + 1, GFP_KERNEL);
|
| 88 | if (!parsed->test_glob) {
|
| 89 | kfree(parsed->suite_glob);
|
| 90 | return -ENOMEM;
|
| 91 | }
|
| 92 |
|
| 93 | return 0;
|
| 94 | }
|
| 95 |
|
| 96 |
|
| 97 | static struct kunit_suite *
|
| 98 | kunit_filter_glob_tests(const struct kunit_suite *const suite, const char *test_glob)
|
| 99 | {
|
| 100 | int n = 0;
|
| 101 | struct kunit_case *filtered, *test_case;
|
| 102 | struct kunit_suite *copy;
|
| 103 |
|
| 104 | kunit_suite_for_each_test_case(suite, test_case) {
|
| 105 | if (!test_glob || glob_match(test_glob, test_case->name))
|
| 106 | ++n;
|
| 107 | }
|
| 108 |
|
| 109 | if (n == 0)
|
| 110 | return NULL;
|
| 111 |
|
| 112 | copy = kmemdup(suite, sizeof(*copy), GFP_KERNEL);
|
| 113 | if (!copy)
|
| 114 | return ERR_PTR(-ENOMEM);
|
| 115 |
|
| 116 | filtered = kcalloc(n + 1, sizeof(*filtered), GFP_KERNEL);
|
| 117 | if (!filtered) {
|
| 118 | kfree(copy);
|
| 119 | return ERR_PTR(-ENOMEM);
|
| 120 | }
|
| 121 |
|
| 122 | n = 0;
|
| 123 | kunit_suite_for_each_test_case(suite, test_case) {
|
| 124 | if (!test_glob || glob_match(test_glob, test_case->name))
|
| 125 | filtered[n++] = *test_case;
|
| 126 | }
|
| 127 |
|
| 128 | copy->test_cases = filtered;
|
| 129 | return copy;
|
| 130 | }
|
| 131 |
|
| 132 | void kunit_free_suite_set(struct kunit_suite_set suite_set)
|
| 133 | {
|
| 134 | struct kunit_suite * const *suites;
|
| 135 |
|
| 136 | for (suites = suite_set.start; suites < suite_set.end; suites++) {
|
| 137 | kfree((*suites)->test_cases);
|
| 138 | kfree(*suites);
|
| 139 | }
|
| 140 | kfree(suite_set.start);
|
| 141 | }
|
| 142 |
|
| 143 | |
| 144 | * Filter and reallocate test suites. Must return the filtered test suites set
|
| 145 | * allocated at a valid virtual address or NULL in case of error.
|
| 146 |
|
| 147 | struct kunit_suite_set
|
| 148 | kunit_filter_suites(const struct kunit_suite_set *suite_set,
|
| 149 | const char *filter_glob,
|
| 150 | char *filters,
|
| 151 | char *filter_action,
|
| 152 | int *err)
|
| 153 | {
|
| 154 | int i, j, k;
|
| 155 | int filter_count = 0;
|
| 156 | struct kunit_suite **copy, **copy_start, *filtered_suite, *new_filtered_suite;
|
| 157 | struct kunit_suite_set filtered = {NULL, NULL};
|
| 158 | struct kunit_glob_filter parsed_glob;
|
| 159 | struct kunit_attr_filter *parsed_filters = NULL;
|
| 160 | struct kunit_suite * const *suites;
|
| 161 |
|
| 162 | const size_t max = suite_set->end - suite_set->start;
|
| 163 |
|
| 164 | copy = kcalloc(max, sizeof(*filtered.start), GFP_KERNEL);
|
| 165 | if (!copy) {
|
| 166 | return filtered;
|
| 167 | }
|
| 168 | copy_start = copy;
|
| 169 |
|
| 170 | if (filter_glob) {
|
| 171 | *err = kunit_parse_glob_filter(&parsed_glob, filter_glob);
|
| 172 | if (*err)
|
| 173 | goto free_copy;
|
| 174 | }
|
| 175 |
|
| 176 |
|
| 177 | if (filters) {
|
| 178 | filter_count = kunit_get_filter_count(filters);
|
| 179 | parsed_filters = kcalloc(filter_count, sizeof(*parsed_filters), GFP_KERNEL);
|
| 180 | if (!parsed_filters) {
|
| 181 | *err = -ENOMEM;
|
| 182 | goto free_parsed_glob;
|
| 183 | }
|
| 184 | for (j = 0; j < filter_count; j++)
|
| 185 | parsed_filters[j] = kunit_next_attr_filter(&filters, err);
|
| 186 | if (*err)
|
| 187 | goto free_parsed_filters;
|
| 188 | }
|
| 189 |
|
| 190 | for (i = 0; &suite_set->start[i] != suite_set->end; i++) {
|
| 191 | filtered_suite = suite_set->start[i];
|
| 192 | if (filter_glob) {
|
| 193 | if (!glob_match(parsed_glob.suite_glob, filtered_suite->name))
|
| 194 | continue;
|
| 195 | filtered_suite = kunit_filter_glob_tests(filtered_suite,
|
| 196 | parsed_glob.test_glob);
|
| 197 | if (IS_ERR(filtered_suite)) {
|
| 198 | *err = PTR_ERR(filtered_suite);
|
| 199 | goto free_filtered_suite;
|
| 200 | }
|
| 201 | }
|
| 202 | if (filter_count > 0 && parsed_filters != NULL) {
|
| 203 | for (k = 0; k < filter_count; k++) {
|
| 204 | new_filtered_suite = kunit_filter_attr_tests(filtered_suite,
|
| 205 | parsed_filters[k], filter_action, err);
|
| 206 |
|
| 207 |
|
| 208 | if (k > 0 || filter_glob) {
|
| 209 | kfree(filtered_suite->test_cases);
|
| 210 | kfree(filtered_suite);
|
| 211 | }
|
| 212 |
|
| 213 | filtered_suite = new_filtered_suite;
|
| 214 |
|
| 215 | if (*err)
|
| 216 | goto free_filtered_suite;
|
| 217 |
|
| 218 | if (IS_ERR(filtered_suite)) {
|
| 219 | *err = PTR_ERR(filtered_suite);
|
| 220 | goto free_filtered_suite;
|
| 221 | }
|
| 222 | if (!filtered_suite)
|
| 223 | break;
|
| 224 | }
|
| 225 | }
|
| 226 |
|
| 227 | if (!filtered_suite)
|
| 228 | continue;
|
| 229 |
|
| 230 | *copy++ = filtered_suite;
|
| 231 | }
|
| 232 | filtered.start = copy_start;
|
| 233 | filtered.end = copy;
|
| 234 |
|
| 235 | free_filtered_suite:
|
| 236 | if (*err) {
|
| 237 | for (suites = copy_start; suites < copy; suites++) {
|
| 238 | kfree((*suites)->test_cases);
|
| 239 | kfree(*suites);
|
| 240 | }
|
| 241 | }
|
| 242 |
|
| 243 | free_parsed_filters:
|
| 244 | if (filter_count)
|
| 245 | kfree(parsed_filters);
|
| 246 |
|
| 247 | free_parsed_glob:
|
| 248 | if (filter_glob) {
|
| 249 | kfree(parsed_glob.suite_glob);
|
| 250 | kfree(parsed_glob.test_glob);
|
| 251 | }
|
| 252 |
|
| 253 | free_copy:
|
| 254 | if (*err)
|
| 255 | kfree(copy_start);
|
| 256 |
|
| 257 | return filtered;
|
| 258 | }
|
| 259 |
|
| 260 | void kunit_exec_run_tests(struct kunit_suite_set *suite_set, bool builtin)
|
| 261 | {
|
| 262 | size_t num_suites = suite_set->end - suite_set->start;
|
| 263 |
|
| 264 | if (builtin || num_suites) {
|
| 265 | pr_info("KTAP version 1\n");
|
| 266 | pr_info("1..%zu\n", num_suites);
|
| 267 | }
|
| 268 |
|
| 269 | __kunit_test_suites_init(suite_set->start, num_suites);
|
| 270 | }
|
| 271 |
|
| 272 | void kunit_exec_list_tests(struct kunit_suite_set *suite_set, bool include_attr)
|
| 273 | {
|
| 274 | struct kunit_suite * const *suites;
|
| 275 | struct kunit_case *test_case;
|
| 276 |
|
| 277 |
|
| 278 | pr_info("KTAP version 1\n");
|
| 279 |
|
| 280 | for (suites = suite_set->start; suites < suite_set->end; suites++) {
|
| 281 |
|
| 282 | pr_info("%s\n", (*suites)->name);
|
| 283 | if (include_attr)
|
| 284 | kunit_print_attr((void *)(*suites), false, 0);
|
| 285 |
|
| 286 |
|
| 287 | kunit_suite_for_each_test_case((*suites), test_case) {
|
| 288 | pr_info("%s.%s\n", (*suites)->name, test_case->name);
|
| 289 | if (include_attr)
|
| 290 | kunit_print_attr((void *)test_case, true, 0);
|
| 291 | }
|
| 292 | }
|
| 293 | }
|
| 294 |
|
| 295 | struct kunit_suite_set kunit_merge_suite_sets(struct kunit_suite_set init_suite_set,
|
| 296 | struct kunit_suite_set suite_set)
|
| 297 | {
|
| 298 | struct kunit_suite_set total_suite_set = {NULL, NULL};
|
| 299 | struct kunit_suite **total_suite_start = NULL;
|
| 300 | size_t init_num_suites, num_suites, suite_size;
|
| 301 | int i = 0;
|
| 302 |
|
| 303 | init_num_suites = init_suite_set.end - init_suite_set.start;
|
| 304 | num_suites = suite_set.end - suite_set.start;
|
| 305 | suite_size = sizeof(suite_set.start);
|
| 306 |
|
| 307 |
|
| 308 | total_suite_start = kmalloc_array(init_num_suites + num_suites, suite_size, GFP_KERNEL);
|
| 309 | if (!total_suite_start)
|
| 310 | return total_suite_set;
|
| 311 |
|
| 312 |
|
| 313 | memcpy(total_suite_start, init_suite_set.start, init_num_suites * suite_size);
|
| 314 | for (i = 0; i < init_num_suites; i++)
|
| 315 | total_suite_start[i]->is_init = true;
|
| 316 |
|
| 317 | memcpy(total_suite_start + init_num_suites, suite_set.start, num_suites * suite_size);
|
| 318 |
|
| 319 |
|
| 320 | total_suite_set.start = total_suite_start;
|
| 321 | total_suite_set.end = total_suite_start + (init_num_suites + num_suites);
|
| 322 |
|
| 323 | return total_suite_set;
|
| 324 | }
|
| 325 |
|
| 326 | #if IS_BUILTIN(CONFIG_KUNIT)
|
| 327 |
|
| 328 | static char *kunit_shutdown;
|
| 329 | core_param(kunit_shutdown, kunit_shutdown, charp, 0644);
|
| 330 |
|
| 331 | static void kunit_handle_shutdown(void)
|
| 332 | {
|
| 333 | if (!kunit_shutdown)
|
| 334 | return;
|
| 335 |
|
| 336 | if (!strcmp(kunit_shutdown, "poweroff"))
|
| 337 | kernel_power_off();
|
| 338 | else if (!strcmp(kunit_shutdown, "halt"))
|
| 339 | kernel_halt();
|
| 340 | else if (!strcmp(kunit_shutdown, "reboot"))
|
| 341 | kernel_restart(NULL);
|
| 342 |
|
| 343 | }
|
| 344 |
|
| 345 | int kunit_run_all_tests(void)
|
| 346 | {
|
| 347 | struct kunit_suite_set suite_set = {NULL, NULL};
|
| 348 | struct kunit_suite_set filtered_suite_set = {NULL, NULL};
|
| 349 | struct kunit_suite_set init_suite_set = {
|
| 350 | __kunit_init_suites_start, __kunit_init_suites_end,
|
| 351 | };
|
| 352 | struct kunit_suite_set normal_suite_set = {
|
| 353 | __kunit_suites_start, __kunit_suites_end,
|
| 354 | };
|
| 355 | size_t init_num_suites = init_suite_set.end - init_suite_set.start;
|
| 356 | int err = 0;
|
| 357 |
|
| 358 | if (init_num_suites > 0) {
|
| 359 | suite_set = kunit_merge_suite_sets(init_suite_set, normal_suite_set);
|
| 360 | if (!suite_set.start)
|
| 361 | goto out;
|
| 362 | } else
|
| 363 | suite_set = normal_suite_set;
|
| 364 |
|
| 365 | if (!kunit_enabled()) {
|
| 366 | pr_info("kunit: disabled\n");
|
| 367 | goto free_out;
|
| 368 | }
|
| 369 |
|
| 370 | if (filter_glob_param || filter_param) {
|
| 371 | filtered_suite_set = kunit_filter_suites(&suite_set, filter_glob_param,
|
| 372 | filter_param, filter_action_param, &err);
|
| 373 |
|
| 374 |
|
| 375 | if (init_num_suites > 0)
|
| 376 | kfree(suite_set.start);
|
| 377 | suite_set = filtered_suite_set;
|
| 378 |
|
| 379 | if (err) {
|
| 380 | pr_err("kunit executor: error filtering suites: %d\n", err);
|
| 381 | goto free_out;
|
| 382 | }
|
| 383 | }
|
| 384 |
|
| 385 | if (!action_param)
|
| 386 | kunit_exec_run_tests(&suite_set, true);
|
| 387 | else if (strcmp(action_param, "list") == 0)
|
| 388 | kunit_exec_list_tests(&suite_set, false);
|
| 389 | else if (strcmp(action_param, "list_attr") == 0)
|
| 390 | kunit_exec_list_tests(&suite_set, true);
|
| 391 | else
|
| 392 | pr_err("kunit executor: unknown action '%s'\n", action_param);
|
| 393 |
|
| 394 | free_out:
|
| 395 | if (filter_glob_param || filter_param)
|
| 396 | kunit_free_suite_set(suite_set);
|
| 397 | else if (init_num_suites > 0)
|
| 398 |
|
| 399 | kfree(suite_set.start);
|
| 400 |
|
| 401 | out:
|
| 402 | kunit_handle_shutdown();
|
| 403 | return err;
|
| 404 | }
|
| 405 |
|
| 406 | #if IS_BUILTIN(CONFIG_KUNIT_TEST)
|
| 407 | #include "executor_test.c"
|
| 408 | #endif
|
| 409 |
|
| 410 | #endif
|