| 1 | |
| 2 |
|
| 3 | #include <linux/reboot.h>
|
| 4 | #include <kunit/test.h>
|
| 5 | #include <linux/glob.h>
|
| 6 | #include <linux/moduleparam.h>
|
| 7 |
|
| 8 | |
| 9 | * These symbols point to the .kunit_test_suites section and are defined in
|
| 10 | * include/asm-generic/vmlinux.lds.h, and consequently must be extern.
|
| 11 |
|
| 12 | extern struct kunit_suite * const * const __kunit_suites_start[];
|
| 13 | extern struct kunit_suite * const * const __kunit_suites_end[];
|
| 14 |
|
| 15 | #if IS_BUILTIN(CONFIG_KUNIT)
|
| 16 |
|
| 17 | static char *filter_glob_param;
|
| 18 | static char *action_param;
|
| 19 |
|
| 20 | module_param_named(filter_glob, filter_glob_param, charp, 0);
|
| 21 | MODULE_PARM_DESC(filter_glob,
|
| 22 | "Filter which KUnit test suites/tests run at boot-time, e.g. list* or list*.*del_test");
|
| 23 | module_param_named(action, action_param, charp, 0);
|
| 24 | MODULE_PARM_DESC(action,
|
| 25 | "Changes KUnit executor behavior, valid values are:\n"
|
| 26 | "<none>: run the tests like normal\n"
|
| 27 | "'list' to list test names instead of running them.\n");
|
| 28 |
|
| 29 |
|
| 30 | struct kunit_test_filter {
|
| 31 | char *suite_glob;
|
| 32 | char *test_glob;
|
| 33 | };
|
| 34 |
|
| 35 |
|
| 36 | static void kunit_parse_filter_glob(struct kunit_test_filter *parsed,
|
| 37 | const char *filter_glob)
|
| 38 | {
|
| 39 | const int len = strlen(filter_glob);
|
| 40 | const char *period = strchr(filter_glob, '.');
|
| 41 |
|
| 42 | if (!period) {
|
| 43 | parsed->suite_glob = kzalloc(len + 1, GFP_KERNEL);
|
| 44 | parsed->test_glob = NULL;
|
| 45 | strcpy(parsed->suite_glob, filter_glob);
|
| 46 | return;
|
| 47 | }
|
| 48 |
|
| 49 | parsed->suite_glob = kzalloc(period - filter_glob + 1, GFP_KERNEL);
|
| 50 | parsed->test_glob = kzalloc(len - (period - filter_glob) + 1, GFP_KERNEL);
|
| 51 |
|
| 52 | strncpy(parsed->suite_glob, filter_glob, period - filter_glob);
|
| 53 | strncpy(parsed->test_glob, period + 1, len - (period - filter_glob));
|
| 54 | }
|
| 55 |
|
| 56 |
|
| 57 | static struct kunit_suite *
|
| 58 | kunit_filter_tests(struct kunit_suite *const suite, const char *test_glob)
|
| 59 | {
|
| 60 | int n = 0;
|
| 61 | struct kunit_case *filtered, *test_case;
|
| 62 | struct kunit_suite *copy;
|
| 63 |
|
| 64 | kunit_suite_for_each_test_case(suite, test_case) {
|
| 65 | if (!test_glob || glob_match(test_glob, test_case->name))
|
| 66 | ++n;
|
| 67 | }
|
| 68 |
|
| 69 | if (n == 0)
|
| 70 | return NULL;
|
| 71 |
|
| 72 |
|
| 73 | copy = kmalloc(sizeof(*copy), GFP_KERNEL);
|
| 74 | memcpy(copy, suite, sizeof(*copy));
|
| 75 |
|
| 76 | filtered = kcalloc(n + 1, sizeof(*filtered), GFP_KERNEL);
|
| 77 |
|
| 78 | n = 0;
|
| 79 | kunit_suite_for_each_test_case(suite, test_case) {
|
| 80 | if (!test_glob || glob_match(test_glob, test_case->name))
|
| 81 | filtered[n++] = *test_case;
|
| 82 | }
|
| 83 |
|
| 84 | copy->test_cases = filtered;
|
| 85 | return copy;
|
| 86 | }
|
| 87 |
|
| 88 | static char *kunit_shutdown;
|
| 89 | core_param(kunit_shutdown, kunit_shutdown, charp, 0644);
|
| 90 |
|
| 91 | static struct kunit_suite * const *
|
| 92 | kunit_filter_subsuite(struct kunit_suite * const * const subsuite,
|
| 93 | struct kunit_test_filter *filter)
|
| 94 | {
|
| 95 | int i, n = 0;
|
| 96 | struct kunit_suite **filtered, *filtered_suite;
|
| 97 |
|
| 98 | n = 0;
|
| 99 | for (i = 0; subsuite[i]; ++i) {
|
| 100 | if (glob_match(filter->suite_glob, subsuite[i]->name))
|
| 101 | ++n;
|
| 102 | }
|
| 103 |
|
| 104 | if (n == 0)
|
| 105 | return NULL;
|
| 106 |
|
| 107 | filtered = kmalloc_array(n + 1, sizeof(*filtered), GFP_KERNEL);
|
| 108 | if (!filtered)
|
| 109 | return NULL;
|
| 110 |
|
| 111 | n = 0;
|
| 112 | for (i = 0; subsuite[i] != NULL; ++i) {
|
| 113 | if (!glob_match(filter->suite_glob, subsuite[i]->name))
|
| 114 | continue;
|
| 115 | filtered_suite = kunit_filter_tests(subsuite[i], filter->test_glob);
|
| 116 | if (filtered_suite)
|
| 117 | filtered[n++] = filtered_suite;
|
| 118 | }
|
| 119 | filtered[n] = NULL;
|
| 120 |
|
| 121 | return filtered;
|
| 122 | }
|
| 123 |
|
| 124 | struct suite_set {
|
| 125 | struct kunit_suite * const * const *start;
|
| 126 | struct kunit_suite * const * const *end;
|
| 127 | };
|
| 128 |
|
| 129 | static void kunit_free_subsuite(struct kunit_suite * const *subsuite)
|
| 130 | {
|
| 131 | unsigned int i;
|
| 132 |
|
| 133 | for (i = 0; subsuite[i]; i++)
|
| 134 | kfree(subsuite[i]);
|
| 135 |
|
| 136 | kfree(subsuite);
|
| 137 | }
|
| 138 |
|
| 139 | static void kunit_free_suite_set(struct suite_set suite_set)
|
| 140 | {
|
| 141 | struct kunit_suite * const * const *suites;
|
| 142 |
|
| 143 | for (suites = suite_set.start; suites < suite_set.end; suites++)
|
| 144 | kunit_free_subsuite(*suites);
|
| 145 | kfree(suite_set.start);
|
| 146 | }
|
| 147 |
|
| 148 | static struct suite_set kunit_filter_suites(const struct suite_set *suite_set,
|
| 149 | const char *filter_glob)
|
| 150 | {
|
| 151 | int i;
|
| 152 | struct kunit_suite * const **copy, * const *filtered_subsuite;
|
| 153 | struct suite_set filtered;
|
| 154 | struct kunit_test_filter filter;
|
| 155 |
|
| 156 | const size_t max = suite_set->end - suite_set->start;
|
| 157 |
|
| 158 | copy = kmalloc_array(max, sizeof(*filtered.start), GFP_KERNEL);
|
| 159 | filtered.start = copy;
|
| 160 | if (!copy) {
|
| 161 | filtered.end = copy;
|
| 162 | return filtered;
|
| 163 | }
|
| 164 |
|
| 165 | kunit_parse_filter_glob(&filter, filter_glob);
|
| 166 |
|
| 167 | for (i = 0; i < max; ++i) {
|
| 168 | filtered_subsuite = kunit_filter_subsuite(suite_set->start[i], &filter);
|
| 169 | if (filtered_subsuite)
|
| 170 | *copy++ = filtered_subsuite;
|
| 171 | }
|
| 172 | filtered.end = copy;
|
| 173 |
|
| 174 | kfree(filter.suite_glob);
|
| 175 | kfree(filter.test_glob);
|
| 176 | return filtered;
|
| 177 | }
|
| 178 |
|
| 179 | static void kunit_handle_shutdown(void)
|
| 180 | {
|
| 181 | if (!kunit_shutdown)
|
| 182 | return;
|
| 183 |
|
| 184 | if (!strcmp(kunit_shutdown, "poweroff"))
|
| 185 | kernel_power_off();
|
| 186 | else if (!strcmp(kunit_shutdown, "halt"))
|
| 187 | kernel_halt();
|
| 188 | else if (!strcmp(kunit_shutdown, "reboot"))
|
| 189 | kernel_restart(NULL);
|
| 190 |
|
| 191 | }
|
| 192 |
|
| 193 | static void kunit_print_tap_header(struct suite_set *suite_set)
|
| 194 | {
|
| 195 | struct kunit_suite * const * const *suites, * const *subsuite;
|
| 196 | int num_of_suites = 0;
|
| 197 |
|
| 198 | for (suites = suite_set->start; suites < suite_set->end; suites++)
|
| 199 | for (subsuite = *suites; *subsuite != NULL; subsuite++)
|
| 200 | num_of_suites++;
|
| 201 |
|
| 202 | pr_info("TAP version 14\n");
|
| 203 | pr_info("1..%d\n", num_of_suites);
|
| 204 | }
|
| 205 |
|
| 206 | static void kunit_exec_run_tests(struct suite_set *suite_set)
|
| 207 | {
|
| 208 | struct kunit_suite * const * const *suites;
|
| 209 |
|
| 210 | kunit_print_tap_header(suite_set);
|
| 211 |
|
| 212 | for (suites = suite_set->start; suites < suite_set->end; suites++)
|
| 213 | __kunit_test_suites_init(*suites);
|
| 214 | }
|
| 215 |
|
| 216 | static void kunit_exec_list_tests(struct suite_set *suite_set)
|
| 217 | {
|
| 218 | unsigned int i;
|
| 219 | struct kunit_suite * const * const *suites;
|
| 220 | struct kunit_case *test_case;
|
| 221 |
|
| 222 |
|
| 223 | pr_info("TAP version 14\n");
|
| 224 |
|
| 225 | for (suites = suite_set->start; suites < suite_set->end; suites++)
|
| 226 | for (i = 0; (*suites)[i] != NULL; i++) {
|
| 227 | kunit_suite_for_each_test_case((*suites)[i], test_case) {
|
| 228 | pr_info("%s.%s\n", (*suites)[i]->name, test_case->name);
|
| 229 | }
|
| 230 | }
|
| 231 | }
|
| 232 |
|
| 233 | int kunit_run_all_tests(void)
|
| 234 | {
|
| 235 | struct suite_set suite_set = {
|
| 236 | .start = __kunit_suites_start,
|
| 237 | .end = __kunit_suites_end,
|
| 238 | };
|
| 239 |
|
| 240 | if (filter_glob_param)
|
| 241 | suite_set = kunit_filter_suites(&suite_set, filter_glob_param);
|
| 242 |
|
| 243 | if (!action_param)
|
| 244 | kunit_exec_run_tests(&suite_set);
|
| 245 | else if (strcmp(action_param, "list") == 0)
|
| 246 | kunit_exec_list_tests(&suite_set);
|
| 247 | else
|
| 248 | pr_err("kunit executor: unknown action '%s'\n", action_param);
|
| 249 |
|
| 250 | if (filter_glob_param) {
|
| 251 | kunit_free_suite_set(suite_set);
|
| 252 | }
|
| 253 |
|
| 254 | kunit_handle_shutdown();
|
| 255 |
|
| 256 | return 0;
|
| 257 | }
|
| 258 |
|
| 259 | #if IS_BUILTIN(CONFIG_KUNIT_TEST)
|
| 260 | #include "executor_test.c"
|
| 261 | #endif
|
| 262 |
|
| 263 | #endif
|