1 // SPDX-License-Identifier: GPL-2.0 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 __kunit_suites_start[]; 13 extern struct kunit_suite * 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 /* glob_match() needs NULL terminated strings, so we need a copy of filter_glob_param. */ 30 struct kunit_test_filter { 31 char *suite_glob; 32 char *test_glob; 33 }; 34 35 /* Split "suite_glob.test_glob" into two. Assumes filter_glob is not empty. */ 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 /* Create a copy of suite with only tests that match test_glob. */ 57 static struct kunit_suite * 58 kunit_filter_tests(const 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 copy = kmemdup(suite, sizeof(*copy), GFP_KERNEL); 73 if (!copy) 74 return ERR_PTR(-ENOMEM); 75 76 filtered = kcalloc(n + 1, sizeof(*filtered), GFP_KERNEL); 77 if (!filtered) { 78 kfree(copy); 79 return ERR_PTR(-ENOMEM); 80 } 81 82 n = 0; 83 kunit_suite_for_each_test_case(suite, test_case) { 84 if (!test_glob || glob_match(test_glob, test_case->name)) 85 filtered[n++] = *test_case; 86 } 87 88 copy->test_cases = filtered; 89 return copy; 90 } 91 92 static char *kunit_shutdown; 93 core_param(kunit_shutdown, kunit_shutdown, charp, 0644); 94 95 /* Stores an array of suites, end points one past the end */ 96 struct suite_set { 97 struct kunit_suite * const *start; 98 struct kunit_suite * const *end; 99 }; 100 101 static void kunit_free_suite_set(struct suite_set suite_set) 102 { 103 struct kunit_suite * const *suites; 104 105 for (suites = suite_set.start; suites < suite_set.end; suites++) 106 kfree(*suites); 107 kfree(suite_set.start); 108 } 109 110 static struct suite_set kunit_filter_suites(const struct suite_set *suite_set, 111 const char *filter_glob, 112 int *err) 113 { 114 int i; 115 struct kunit_suite **copy, *filtered_suite; 116 struct suite_set filtered; 117 struct kunit_test_filter filter; 118 119 const size_t max = suite_set->end - suite_set->start; 120 121 copy = kmalloc_array(max, sizeof(*filtered.start), GFP_KERNEL); 122 filtered.start = copy; 123 if (!copy) { /* won't be able to run anything, return an empty set */ 124 filtered.end = copy; 125 return filtered; 126 } 127 128 kunit_parse_filter_glob(&filter, filter_glob); 129 130 for (i = 0; &suite_set->start[i] != suite_set->end; i++) { 131 if (!glob_match(filter.suite_glob, suite_set->start[i]->name)) 132 continue; 133 134 filtered_suite = kunit_filter_tests(suite_set->start[i], filter.test_glob); 135 if (IS_ERR(filtered_suite)) { 136 *err = PTR_ERR(filtered_suite); 137 return filtered; 138 } 139 if (!filtered_suite) 140 continue; 141 142 *copy++ = filtered_suite; 143 } 144 filtered.end = copy; 145 146 kfree(filter.suite_glob); 147 kfree(filter.test_glob); 148 return filtered; 149 } 150 151 static void kunit_handle_shutdown(void) 152 { 153 if (!kunit_shutdown) 154 return; 155 156 if (!strcmp(kunit_shutdown, "poweroff")) 157 kernel_power_off(); 158 else if (!strcmp(kunit_shutdown, "halt")) 159 kernel_halt(); 160 else if (!strcmp(kunit_shutdown, "reboot")) 161 kernel_restart(NULL); 162 163 } 164 165 static void kunit_exec_run_tests(struct suite_set *suite_set) 166 { 167 size_t num_suites = suite_set->end - suite_set->start; 168 169 pr_info("TAP version 14\n"); 170 pr_info("1..%zu\n", num_suites); 171 172 __kunit_test_suites_init(suite_set->start, num_suites); 173 } 174 175 static void kunit_exec_list_tests(struct suite_set *suite_set) 176 { 177 struct kunit_suite * const *suites; 178 struct kunit_case *test_case; 179 180 /* Hack: print a tap header so kunit.py can find the start of KUnit output. */ 181 pr_info("TAP version 14\n"); 182 183 for (suites = suite_set->start; suites < suite_set->end; suites++) 184 kunit_suite_for_each_test_case((*suites), test_case) { 185 pr_info("%s.%s\n", (*suites)->name, test_case->name); 186 } 187 } 188 189 int kunit_run_all_tests(void) 190 { 191 struct suite_set suite_set = {__kunit_suites_start, __kunit_suites_end}; 192 int err = 0; 193 if (!kunit_enabled()) { 194 pr_info("kunit: disabled\n"); 195 goto out; 196 } 197 198 if (filter_glob_param) { 199 suite_set = kunit_filter_suites(&suite_set, filter_glob_param, &err); 200 if (err) { 201 pr_err("kunit executor: error filtering suites: %d\n", err); 202 goto out; 203 } 204 } 205 206 if (!action_param) 207 kunit_exec_run_tests(&suite_set); 208 else if (strcmp(action_param, "list") == 0) 209 kunit_exec_list_tests(&suite_set); 210 else 211 pr_err("kunit executor: unknown action '%s'\n", action_param); 212 213 if (filter_glob_param) { /* a copy was made of each suite */ 214 kunit_free_suite_set(suite_set); 215 } 216 217 out: 218 kunit_handle_shutdown(); 219 return err; 220 } 221 222 #if IS_BUILTIN(CONFIG_KUNIT_TEST) 223 #include "executor_test.c" 224 #endif 225 226 #endif /* IS_BUILTIN(CONFIG_KUNIT) */ 227