/src/u-boot/test/cmd_ut.c
Line | Count | Source |
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 | | */ |
113 | | static bool has_tests(struct suite *ste) |
114 | 0 | { |
115 | 0 | int n_ents = ste->end - ste->start; |
116 | |
|
117 | 0 | return n_ents; |
118 | 0 | } |
119 | | |
120 | | /** run_suite() - Run a suite of tests */ |
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 | 0 | { |
125 | 0 | int n_ents = ste->end - ste->start; |
126 | 0 | char prefix[30]; |
127 | 0 | int ret; |
128 | | |
129 | | /* use a standard prefix */ |
130 | 0 | snprintf(prefix, sizeof(prefix), "%s_test_", ste->name); |
131 | |
|
132 | 0 | ret = ut_run_list(uts, ste->name, prefix, ste->start, n_ents, |
133 | 0 | select_name, runs_per_test, force_run, test_insert); |
134 | |
|
135 | 0 | return ret; |
136 | 0 | } |
137 | | |
138 | | static void show_stats(struct unit_test_state *uts) |
139 | 0 | { |
140 | 0 | if (uts->run_count < 2) |
141 | 0 | return; |
142 | | |
143 | 0 | ut_report(&uts->total, uts->run_count); |
144 | 0 | if (CONFIG_IS_ENABLED(UNIT_TEST_DURATION) && |
145 | 0 | uts->total.test_count && uts->worst) { |
146 | 0 | ulong avg = uts->total.duration_ms / uts->total.test_count; |
147 | |
|
148 | 0 | printf("Average test time: %ld ms, worst case '%s' took %d ms\n", |
149 | 0 | avg, uts->worst->name, uts->worst_ms); |
150 | 0 | } |
151 | 0 | } |
152 | | |
153 | | static void update_stats(struct unit_test_state *uts, const struct suite *ste) |
154 | 0 | { |
155 | 0 | if (CONFIG_IS_ENABLED(UNIT_TEST_DURATION) && uts->cur.test_count) { |
156 | 0 | ulong avg; |
157 | |
|
158 | 0 | avg = uts->cur.duration_ms ? |
159 | 0 | uts->cur.duration_ms / |
160 | 0 | uts->cur.test_count : 0; |
161 | 0 | if (avg > uts->worst_ms) { |
162 | 0 | uts->worst_ms = avg; |
163 | 0 | uts->worst = ste; |
164 | 0 | } |
165 | 0 | } |
166 | 0 | } |
167 | | |
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 | 0 | { |
171 | 0 | int i; |
172 | 0 | int retval; |
173 | 0 | int any_fail = 0; |
174 | |
|
175 | 0 | for (i = 0; i < ARRAY_SIZE(suites); i++) { |
176 | 0 | struct suite *ste = &suites[i]; |
177 | |
|
178 | 0 | if (has_tests(ste)) { |
179 | 0 | printf("----Running %s tests----\n", ste->name); |
180 | 0 | retval = run_suite(uts, ste, select_name, runs_per_test, |
181 | 0 | force_run, test_insert); |
182 | 0 | if (!any_fail) |
183 | 0 | any_fail = retval; |
184 | 0 | update_stats(uts, ste); |
185 | 0 | } |
186 | 0 | } |
187 | |
|
188 | 0 | return any_fail; |
189 | 0 | } |
190 | | |
191 | | static int do_ut_info(bool show_suites) |
192 | 0 | { |
193 | 0 | int suite_count, i; |
194 | |
|
195 | 0 | for (suite_count = 0, i = 0; i < ARRAY_SIZE(suites); i++) { |
196 | 0 | struct suite *ste = &suites[i]; |
197 | |
|
198 | 0 | if (has_tests(ste)) |
199 | 0 | suite_count++; |
200 | 0 | } |
201 | |
|
202 | 0 | printf("Test suites: %d\n", suite_count); |
203 | 0 | printf("Total tests: %d\n", (int)UNIT_TEST_ALL_COUNT()); |
204 | |
|
205 | 0 | if (show_suites) { |
206 | 0 | int i, total; |
207 | |
|
208 | 0 | puts("\nTests Suite Purpose"); |
209 | 0 | puts("\n----- ------------ -------------------------\n"); |
210 | 0 | for (i = 0, total = 0; i < ARRAY_SIZE(suites); i++) { |
211 | 0 | struct suite *ste = &suites[i]; |
212 | 0 | long n_ent = ste->end - ste->start; |
213 | |
|
214 | 0 | if (n_ent) { |
215 | 0 | printf("%5ld %-13.13s %s\n", n_ent, ste->name, |
216 | 0 | ste->help); |
217 | 0 | total += n_ent; |
218 | 0 | } |
219 | 0 | } |
220 | 0 | puts("----- ------------ -------------------------\n"); |
221 | 0 | printf("%5d %-13.13s\n", total, "Total"); |
222 | |
|
223 | 0 | if (UNIT_TEST_ALL_COUNT() != total) |
224 | 0 | puts("Error: Suite test-count does not match total\n"); |
225 | 0 | } |
226 | |
|
227 | 0 | return 0; |
228 | 0 | } |
229 | | |
230 | | static struct suite *find_suite(const char *name) |
231 | 0 | { |
232 | 0 | struct suite *ste; |
233 | 0 | int i; |
234 | |
|
235 | 0 | for (i = 0, ste = suites; i < ARRAY_SIZE(suites); i++, ste++) { |
236 | 0 | if (!strcmp(ste->name, name)) |
237 | 0 | return ste; |
238 | 0 | } |
239 | | |
240 | 0 | return NULL; |
241 | 0 | } |
242 | | |
243 | | static int do_ut(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) |
244 | 0 | { |
245 | 0 | const char *test_insert = NULL, *select_name; |
246 | 0 | struct unit_test_state uts; |
247 | 0 | bool show_suites = false; |
248 | 0 | bool force_run = false; |
249 | 0 | int runs_per_text = 1; |
250 | 0 | struct suite *ste; |
251 | 0 | char *name; |
252 | 0 | int ret; |
253 | | |
254 | | /* drop initial "ut" arg */ |
255 | 0 | argc--; |
256 | 0 | argv++; |
257 | |
|
258 | 0 | while (argc > 0 && *argv[0] == '-') { |
259 | 0 | const char *str = argv[0]; |
260 | |
|
261 | 0 | switch (str[1]) { |
262 | 0 | case 'r': |
263 | 0 | runs_per_text = dectoul(str + 2, NULL); |
264 | 0 | break; |
265 | 0 | case 'f': |
266 | 0 | force_run = true; |
267 | 0 | break; |
268 | 0 | case 'I': |
269 | 0 | test_insert = str + 2; |
270 | 0 | if (!strchr(test_insert, ':')) |
271 | 0 | return CMD_RET_USAGE; |
272 | 0 | break; |
273 | 0 | case 's': |
274 | 0 | show_suites = true; |
275 | 0 | break; |
276 | 0 | } |
277 | 0 | argv++; |
278 | 0 | argc--; |
279 | 0 | } |
280 | | |
281 | 0 | if (argc < 1) |
282 | 0 | return CMD_RET_USAGE; |
283 | | |
284 | 0 | ut_init_state(&uts); |
285 | 0 | name = argv[0]; |
286 | 0 | select_name = cmd_arg1(argc, argv); |
287 | 0 | if (!strcmp(name, "all")) { |
288 | 0 | ret = do_ut_all(&uts, select_name, runs_per_text, force_run, |
289 | 0 | test_insert); |
290 | 0 | } else if (!strcmp(name, "info")) { |
291 | 0 | ret = do_ut_info(show_suites); |
292 | 0 | } else { |
293 | 0 | int any_fail = 0; |
294 | 0 | const char *p; |
295 | |
|
296 | 0 | for (; p = strsep(&name, ","), p; name = NULL) { |
297 | 0 | ste = find_suite(p); |
298 | 0 | if (!ste) { |
299 | 0 | printf("Suite '%s' not found\n", p); |
300 | 0 | return CMD_RET_FAILURE; |
301 | 0 | } else if (!has_tests(ste)) { |
302 | | /* perhaps a Kconfig option needs to be set? */ |
303 | 0 | printf("Suite '%s' is not enabled\n", p); |
304 | 0 | return CMD_RET_FAILURE; |
305 | 0 | } |
306 | | |
307 | 0 | ret = run_suite(&uts, ste, select_name, runs_per_text, |
308 | 0 | force_run, test_insert); |
309 | 0 | if (!any_fail) |
310 | 0 | any_fail = ret; |
311 | 0 | update_stats(&uts, ste); |
312 | 0 | } |
313 | 0 | ret = any_fail; |
314 | 0 | } |
315 | 0 | show_stats(&uts); |
316 | 0 | if (ret) |
317 | 0 | return ret; |
318 | 0 | ut_uninit_state(&uts); |
319 | |
|
320 | 0 | return 0; |
321 | 0 | } |
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 | | ); |