Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 | // SPDX-License-Identifier: GPL-2.0 #include <linux/reboot.h> #include <kunit/test.h> #include <linux/glob.h> #include <linux/moduleparam.h> /* * These symbols point to the .kunit_test_suites section and are defined in * include/asm-generic/vmlinux.lds.h, and consequently must be extern. */ extern struct kunit_suite * const __kunit_suites_start[]; extern struct kunit_suite * const __kunit_suites_end[]; #if IS_BUILTIN(CONFIG_KUNIT) static char *filter_glob_param; static char *action_param; module_param_named(filter_glob, filter_glob_param, charp, 0); MODULE_PARM_DESC(filter_glob, "Filter which KUnit test suites/tests run at boot-time, e.g. list* or list*.*del_test"); module_param_named(action, action_param, charp, 0); MODULE_PARM_DESC(action, "Changes KUnit executor behavior, valid values are:\n" "<none>: run the tests like normal\n" "'list' to list test names instead of running them.\n"); /* glob_match() needs NULL terminated strings, so we need a copy of filter_glob_param. */ struct kunit_test_filter { char *suite_glob; char *test_glob; }; /* Split "suite_glob.test_glob" into two. Assumes filter_glob is not empty. */ static void kunit_parse_filter_glob(struct kunit_test_filter *parsed, const char *filter_glob) { const int len = strlen(filter_glob); const char *period = strchr(filter_glob, '.'); if (!period) { parsed->suite_glob = kzalloc(len + 1, GFP_KERNEL); parsed->test_glob = NULL; strcpy(parsed->suite_glob, filter_glob); return; } parsed->suite_glob = kzalloc(period - filter_glob + 1, GFP_KERNEL); parsed->test_glob = kzalloc(len - (period - filter_glob) + 1, GFP_KERNEL); strncpy(parsed->suite_glob, filter_glob, period - filter_glob); strncpy(parsed->test_glob, period + 1, len - (period - filter_glob)); } /* Create a copy of suite with only tests that match test_glob. */ static struct kunit_suite * kunit_filter_tests(const struct kunit_suite *const suite, const char *test_glob) { int n = 0; struct kunit_case *filtered, *test_case; struct kunit_suite *copy; kunit_suite_for_each_test_case(suite, test_case) { if (!test_glob || glob_match(test_glob, test_case->name)) ++n; } if (n == 0) return NULL; copy = kmemdup(suite, sizeof(*copy), GFP_KERNEL); if (!copy) return ERR_PTR(-ENOMEM); filtered = kcalloc(n + 1, sizeof(*filtered), GFP_KERNEL); if (!filtered) { kfree(copy); return ERR_PTR(-ENOMEM); } n = 0; kunit_suite_for_each_test_case(suite, test_case) { if (!test_glob || glob_match(test_glob, test_case->name)) filtered[n++] = *test_case; } copy->test_cases = filtered; return copy; } static char *kunit_shutdown; core_param(kunit_shutdown, kunit_shutdown, charp, 0644); /* Stores an array of suites, end points one past the end */ struct suite_set { struct kunit_suite * const *start; struct kunit_suite * const *end; }; static void kunit_free_suite_set(struct suite_set suite_set) { struct kunit_suite * const *suites; for (suites = suite_set.start; suites < suite_set.end; suites++) kfree(*suites); kfree(suite_set.start); } static struct suite_set kunit_filter_suites(const struct suite_set *suite_set, const char *filter_glob, int *err) { int i; struct kunit_suite **copy, *filtered_suite; struct suite_set filtered; struct kunit_test_filter filter; const size_t max = suite_set->end - suite_set->start; copy = kmalloc_array(max, sizeof(*filtered.start), GFP_KERNEL); filtered.start = copy; if (!copy) { /* won't be able to run anything, return an empty set */ filtered.end = copy; return filtered; } kunit_parse_filter_glob(&filter, filter_glob); for (i = 0; &suite_set->start[i] != suite_set->end; i++) { if (!glob_match(filter.suite_glob, suite_set->start[i]->name)) continue; filtered_suite = kunit_filter_tests(suite_set->start[i], filter.test_glob); if (IS_ERR(filtered_suite)) { *err = PTR_ERR(filtered_suite); return filtered; } if (!filtered_suite) continue; *copy++ = filtered_suite; } filtered.end = copy; kfree(filter.suite_glob); kfree(filter.test_glob); return filtered; } static void kunit_handle_shutdown(void) { if (!kunit_shutdown) return; if (!strcmp(kunit_shutdown, "poweroff")) kernel_power_off(); else if (!strcmp(kunit_shutdown, "halt")) kernel_halt(); else if (!strcmp(kunit_shutdown, "reboot")) kernel_restart(NULL); } static void kunit_exec_run_tests(struct suite_set *suite_set) { size_t num_suites = suite_set->end - suite_set->start; pr_info("KTAP version 1\n"); pr_info("1..%zu\n", num_suites); __kunit_test_suites_init(suite_set->start, num_suites); } static void kunit_exec_list_tests(struct suite_set *suite_set) { struct kunit_suite * const *suites; struct kunit_case *test_case; /* Hack: print a ktap header so kunit.py can find the start of KUnit output. */ pr_info("KTAP version 1\n"); for (suites = suite_set->start; suites < suite_set->end; suites++) kunit_suite_for_each_test_case((*suites), test_case) { pr_info("%s.%s\n", (*suites)->name, test_case->name); } } int kunit_run_all_tests(void) { struct suite_set suite_set = {__kunit_suites_start, __kunit_suites_end}; int err = 0; if (!kunit_enabled()) { pr_info("kunit: disabled\n"); goto out; } if (filter_glob_param) { suite_set = kunit_filter_suites(&suite_set, filter_glob_param, &err); if (err) { pr_err("kunit executor: error filtering suites: %d\n", err); goto out; } } if (!action_param) kunit_exec_run_tests(&suite_set); else if (strcmp(action_param, "list") == 0) kunit_exec_list_tests(&suite_set); else pr_err("kunit executor: unknown action '%s'\n", action_param); if (filter_glob_param) { /* a copy was made of each suite */ kunit_free_suite_set(suite_set); } out: kunit_handle_shutdown(); return err; } #if IS_BUILTIN(CONFIG_KUNIT_TEST) #include "executor_test.c" #endif #endif /* IS_BUILTIN(CONFIG_KUNIT) */ |