1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * (C) Copyright 2015
4 * Joe Hershberger, National Instruments, joe.hershberger@ni.com
5 */
6
7 #include <command.h>
8 #include <console.h>
9 #include <vsprintf.h>
10 #include <test/test.h>
11 #include <test/ut.h>
12
13 /**
14 * struct suite - A set of tests for a certain topic
15 *
16 * All tests end up in a single 'struct unit_test' linker-list array, in order
17 * of the suite they are in
18 *
19 * @name: Name of suite
20 * @start: First test in suite
21 * @end: End test in suite (points to the first test in the next suite)
22 * @help: Help-string to show for this suite
23 */
24 struct suite {
25 const char *name;
26 struct unit_test *start;
27 struct unit_test *end;
28 const char *help;
29 };
30
31 static int do_ut_all(struct unit_test_state *uts, const char *select_name,
32 int runs_per_test, bool force_run,
33 const char *test_insert);
34
35 static int do_ut_info(bool show_suites);
36
37 /* declare linker-list symbols for the start and end of a suite */
38 #define SUITE_DECL(_name) \
39 ll_start_decl(suite_start_ ## _name, struct unit_test, ut_ ## _name); \
40 ll_end_decl(suite_end_ ## _name, struct unit_test, ut_ ## _name)
41
42 /* declare a test suite which can be run directly without a subcommand */
43 #define SUITE(_name, _help) { \
44 #_name, \
45 suite_start_ ## _name, \
46 suite_end_ ## _name, \
47 _help, \
48 }
49
50 SUITE_DECL(addrmap);
51 SUITE_DECL(bdinfo);
52 SUITE_DECL(bloblist);
53 SUITE_DECL(bootm);
54 SUITE_DECL(bootstd);
55 SUITE_DECL(cmd);
56 SUITE_DECL(common);
57 SUITE_DECL(dm);
58 SUITE_DECL(env);
59 SUITE_DECL(exit);
60 SUITE_DECL(fdt);
61 SUITE_DECL(fdt_overlay);
62 SUITE_DECL(font);
63 SUITE_DECL(hush);
64 SUITE_DECL(lib);
65 SUITE_DECL(loadm);
66 SUITE_DECL(log);
67 SUITE_DECL(mbr);
68 SUITE_DECL(measurement);
69 SUITE_DECL(mem);
70 SUITE_DECL(optee);
71 SUITE_DECL(pci_mps);
72 SUITE_DECL(seama);
73 SUITE_DECL(setexpr);
74 SUITE_DECL(upl);
75
76 static struct suite suites[] = {
77 SUITE(addrmap, "very basic test of addrmap command"),
78 SUITE(bdinfo, "bdinfo (board info) command"),
79 SUITE(bloblist, "bloblist implementation"),
80 SUITE(bootm, "bootm command"),
81 SUITE(bootstd, "standard boot implementation"),
82 SUITE(cmd, "various commands"),
83 SUITE(common, "tests for common/ directory"),
84 SUITE(dm, "driver model"),
85 SUITE(env, "environment"),
86 SUITE(exit, "shell exit and variables"),
87 SUITE(fdt, "fdt command"),
88 SUITE(fdt_overlay, "device tree overlays"),
89 SUITE(font, "font command"),
90 SUITE(hush, "hush behaviour"),
91 SUITE(lib, "library functions"),
92 SUITE(loadm, "loadm command parameters and loading memory blob"),
93 SUITE(log, "logging functions"),
94 SUITE(mbr, "mbr command"),
95 SUITE(measurement, "TPM-based measured boot"),
96 SUITE(mem, "memory-related commands"),
97 SUITE(optee, "OP-TEE"),
98 SUITE(pci_mps, "PCI Express Maximum Payload Size"),
99 SUITE(seama, "seama command parameters loading and decoding"),
100 SUITE(setexpr, "setexpr command"),
101 SUITE(upl, "Universal payload support"),
102 };
103
104 /**
105 * has_tests() - Check if a suite has tests, i.e. is supported in this build
106 *
107 * If the suite is run using a command, we have to assume that tests may be
108 * present, since we have no visibility
109 *
110 * @ste: Suite to check
111 * Return: true if supported, false if not
112 */
has_tests(struct suite * ste)113 static bool has_tests(struct suite *ste)
114 {
115 int n_ents = ste->end - ste->start;
116
117 return n_ents;
118 }
119
120 /** run_suite() - Run a suite of tests */
run_suite(struct unit_test_state * uts,struct suite * ste,const char * select_name,int runs_per_test,bool force_run,const char * test_insert)121 static int run_suite(struct unit_test_state *uts, struct suite *ste,
122 const char *select_name, int runs_per_test, bool force_run,
123 const char *test_insert)
124 {
125 int n_ents = ste->end - ste->start;
126 char prefix[30];
127 int ret;
128
129 /* use a standard prefix */
130 snprintf(prefix, sizeof(prefix), "%s_test_", ste->name);
131
132 ret = ut_run_list(uts, ste->name, prefix, ste->start, n_ents,
133 select_name, runs_per_test, force_run, test_insert);
134
135 return ret;
136 }
137
show_stats(struct unit_test_state * uts)138 static void show_stats(struct unit_test_state *uts)
139 {
140 if (uts->run_count < 2)
141 return;
142
143 ut_report(&uts->total, uts->run_count);
144 if (CONFIG_IS_ENABLED(UNIT_TEST_DURATION) &&
145 uts->total.test_count && uts->worst) {
146 ulong avg = uts->total.duration_ms / uts->total.test_count;
147
148 printf("Average test time: %ld ms, worst case '%s' took %d ms\n",
149 avg, uts->worst->name, uts->worst_ms);
150 }
151 }
152
update_stats(struct unit_test_state * uts,const struct suite * ste)153 static void update_stats(struct unit_test_state *uts, const struct suite *ste)
154 {
155 if (CONFIG_IS_ENABLED(UNIT_TEST_DURATION) && uts->cur.test_count) {
156 ulong avg;
157
158 avg = uts->cur.duration_ms ?
159 uts->cur.duration_ms /
160 uts->cur.test_count : 0;
161 if (avg > uts->worst_ms) {
162 uts->worst_ms = avg;
163 uts->worst = ste;
164 }
165 }
166 }
167
do_ut_all(struct unit_test_state * uts,const char * select_name,int runs_per_test,bool force_run,const char * test_insert)168 static int do_ut_all(struct unit_test_state *uts, const char *select_name,
169 int runs_per_test, bool force_run, const char *test_insert)
170 {
171 int i;
172 int retval;
173 int any_fail = 0;
174
175 for (i = 0; i < ARRAY_SIZE(suites); i++) {
176 struct suite *ste = &suites[i];
177
178 if (has_tests(ste)) {
179 printf("----Running %s tests----\n", ste->name);
180 retval = run_suite(uts, ste, select_name, runs_per_test,
181 force_run, test_insert);
182 if (!any_fail)
183 any_fail = retval;
184 update_stats(uts, ste);
185 }
186 }
187
188 return any_fail;
189 }
190
do_ut_info(bool show_suites)191 static int do_ut_info(bool show_suites)
192 {
193 int suite_count, i;
194
195 for (suite_count = 0, i = 0; i < ARRAY_SIZE(suites); i++) {
196 struct suite *ste = &suites[i];
197
198 if (has_tests(ste))
199 suite_count++;
200 }
201
202 printf("Test suites: %d\n", suite_count);
203 printf("Total tests: %d\n", (int)UNIT_TEST_ALL_COUNT());
204
205 if (show_suites) {
206 int i, total;
207
208 puts("\nTests Suite Purpose");
209 puts("\n----- ------------ -------------------------\n");
210 for (i = 0, total = 0; i < ARRAY_SIZE(suites); i++) {
211 struct suite *ste = &suites[i];
212 long n_ent = ste->end - ste->start;
213
214 if (n_ent) {
215 printf("%5ld %-13.13s %s\n", n_ent, ste->name,
216 ste->help);
217 total += n_ent;
218 }
219 }
220 puts("----- ------------ -------------------------\n");
221 printf("%5d %-13.13s\n", total, "Total");
222
223 if (UNIT_TEST_ALL_COUNT() != total)
224 puts("Error: Suite test-count does not match total\n");
225 }
226
227 return 0;
228 }
229
find_suite(const char * name)230 static struct suite *find_suite(const char *name)
231 {
232 struct suite *ste;
233 int i;
234
235 for (i = 0, ste = suites; i < ARRAY_SIZE(suites); i++, ste++) {
236 if (!strcmp(ste->name, name))
237 return ste;
238 }
239
240 return NULL;
241 }
242
do_ut(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])243 static int do_ut(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
244 {
245 const char *test_insert = NULL, *select_name;
246 struct unit_test_state uts;
247 bool show_suites = false;
248 bool force_run = false;
249 int runs_per_text = 1;
250 struct suite *ste;
251 char *name;
252 int ret;
253
254 /* drop initial "ut" arg */
255 argc--;
256 argv++;
257
258 while (argc > 0 && *argv[0] == '-') {
259 const char *str = argv[0];
260
261 switch (str[1]) {
262 case 'r':
263 runs_per_text = dectoul(str + 2, NULL);
264 break;
265 case 'f':
266 force_run = true;
267 break;
268 case 'I':
269 test_insert = str + 2;
270 if (!strchr(test_insert, ':'))
271 return CMD_RET_USAGE;
272 break;
273 case 's':
274 show_suites = true;
275 break;
276 }
277 argv++;
278 argc--;
279 }
280
281 if (argc < 1)
282 return CMD_RET_USAGE;
283
284 ut_init_state(&uts);
285 name = argv[0];
286 select_name = cmd_arg1(argc, argv);
287 if (!strcmp(name, "all")) {
288 ret = do_ut_all(&uts, select_name, runs_per_text, force_run,
289 test_insert);
290 } else if (!strcmp(name, "info")) {
291 ret = do_ut_info(show_suites);
292 } else {
293 int any_fail = 0;
294 const char *p;
295
296 for (; p = strsep(&name, ","), p; name = NULL) {
297 ste = find_suite(p);
298 if (!ste) {
299 printf("Suite '%s' not found\n", p);
300 return CMD_RET_FAILURE;
301 } else if (!has_tests(ste)) {
302 /* perhaps a Kconfig option needs to be set? */
303 printf("Suite '%s' is not enabled\n", p);
304 return CMD_RET_FAILURE;
305 }
306
307 ret = run_suite(&uts, ste, select_name, runs_per_text,
308 force_run, test_insert);
309 if (!any_fail)
310 any_fail = ret;
311 update_stats(&uts, ste);
312 }
313 ret = any_fail;
314 }
315 show_stats(&uts);
316 if (ret)
317 return ret;
318 ut_uninit_state(&uts);
319
320 return 0;
321 }
322
323 U_BOOT_LONGHELP(ut,
324 "[-rs] [-f] [-I<n>:<one_test>][<suites>] - run unit tests\n"
325 " -r<runs> Number of times to run each test\n"
326 " -f Force 'manual' tests to run as well\n"
327 " -I Test to run after <n> other tests have run\n"
328 " -s Show all suites with ut info\n"
329 " <suites> Comma-separated list of suites to run\n"
330 "\n"
331 "Options for <suite>:\n"
332 "all - execute all enabled tests\n"
333 "info - show info about tests [and suites]"
334 );
335
336 U_BOOT_CMD(
337 ut, CONFIG_SYS_MAXARGS, 1, do_ut,
338 "unit tests", ut_help_text
339 );
340