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