mirror of
https://github.com/u-boot/u-boot.git
synced 2025-04-14 00:44:35 +00:00

Update documentation for how to write tests and the 'ut' command. Signed-off-by: Simon Glass <sjg@chromium.org>
339 lines
8.1 KiB
C
339 lines
8.1 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* (C) Copyright 2015
|
|
* Joe Hershberger, National Instruments, joe.hershberger@ni.com
|
|
*/
|
|
|
|
#include <command.h>
|
|
#include <console.h>
|
|
#include <vsprintf.h>
|
|
#include <test/test.h>
|
|
#include <test/ut.h>
|
|
|
|
/**
|
|
* struct suite - A set of tests for a certain topic
|
|
*
|
|
* All tests end up in a single 'struct unit_test' linker-list array, in order
|
|
* of the suite they are in
|
|
*
|
|
* @name: Name of suite
|
|
* @start: First test in suite
|
|
* @end: End test in suite (points to the first test in the next suite)
|
|
* @help: Help-string to show for this suite
|
|
*/
|
|
struct suite {
|
|
const char *name;
|
|
struct unit_test *start;
|
|
struct unit_test *end;
|
|
const char *help;
|
|
};
|
|
|
|
static int do_ut_all(struct unit_test_state *uts, const char *select_name,
|
|
int runs_per_test, bool force_run,
|
|
const char *test_insert);
|
|
|
|
static int do_ut_info(bool show_suites);
|
|
|
|
/* declare linker-list symbols for the start and end of a suite */
|
|
#define SUITE_DECL(_name) \
|
|
ll_start_decl(suite_start_ ## _name, struct unit_test, ut_ ## _name); \
|
|
ll_end_decl(suite_end_ ## _name, struct unit_test, ut_ ## _name)
|
|
|
|
/* declare a test suite which can be run directly without a subcommand */
|
|
#define SUITE(_name, _help) { \
|
|
#_name, \
|
|
suite_start_ ## _name, \
|
|
suite_end_ ## _name, \
|
|
_help, \
|
|
}
|
|
|
|
SUITE_DECL(addrmap);
|
|
SUITE_DECL(bdinfo);
|
|
SUITE_DECL(bloblist);
|
|
SUITE_DECL(bootm);
|
|
SUITE_DECL(bootstd);
|
|
SUITE_DECL(cmd);
|
|
SUITE_DECL(common);
|
|
SUITE_DECL(dm);
|
|
SUITE_DECL(env);
|
|
SUITE_DECL(exit);
|
|
SUITE_DECL(fdt);
|
|
SUITE_DECL(fdt_overlay);
|
|
SUITE_DECL(font);
|
|
SUITE_DECL(hush);
|
|
SUITE_DECL(lib);
|
|
SUITE_DECL(loadm);
|
|
SUITE_DECL(log);
|
|
SUITE_DECL(mbr);
|
|
SUITE_DECL(measurement);
|
|
SUITE_DECL(mem);
|
|
SUITE_DECL(optee);
|
|
SUITE_DECL(pci_mps);
|
|
SUITE_DECL(seama);
|
|
SUITE_DECL(setexpr);
|
|
SUITE_DECL(upl);
|
|
|
|
static struct suite suites[] = {
|
|
SUITE(addrmap, "very basic test of addrmap command"),
|
|
SUITE(bdinfo, "bdinfo (board info) command"),
|
|
SUITE(bloblist, "bloblist implementation"),
|
|
SUITE(bootm, "bootm command"),
|
|
SUITE(bootstd, "standard boot implementation"),
|
|
SUITE(cmd, "various commands"),
|
|
SUITE(common, "tests for common/ directory"),
|
|
SUITE(dm, "driver model"),
|
|
SUITE(env, "environment"),
|
|
SUITE(exit, "shell exit and variables"),
|
|
SUITE(fdt, "fdt command"),
|
|
SUITE(fdt_overlay, "device tree overlays"),
|
|
SUITE(font, "font command"),
|
|
SUITE(hush, "hush behaviour"),
|
|
SUITE(lib, "library functions"),
|
|
SUITE(loadm, "loadm command parameters and loading memory blob"),
|
|
SUITE(log, "logging functions"),
|
|
SUITE(mbr, "mbr command"),
|
|
SUITE(measurement, "TPM-based measured boot"),
|
|
SUITE(mem, "memory-related commands"),
|
|
SUITE(optee, "OP-TEE"),
|
|
SUITE(pci_mps, "PCI Express Maximum Payload Size"),
|
|
SUITE(seama, "seama command parameters loading and decoding"),
|
|
SUITE(setexpr, "setexpr command"),
|
|
SUITE(upl, "Universal payload support"),
|
|
};
|
|
|
|
/**
|
|
* has_tests() - Check if a suite has tests, i.e. is supported in this build
|
|
*
|
|
* If the suite is run using a command, we have to assume that tests may be
|
|
* present, since we have no visibility
|
|
*
|
|
* @ste: Suite to check
|
|
* Return: true if supported, false if not
|
|
*/
|
|
static bool has_tests(struct suite *ste)
|
|
{
|
|
int n_ents = ste->end - ste->start;
|
|
|
|
return n_ents;
|
|
}
|
|
|
|
/** run_suite() - Run a suite of tests */
|
|
static int 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)
|
|
{
|
|
int n_ents = ste->end - ste->start;
|
|
char prefix[30];
|
|
int ret;
|
|
|
|
/* use a standard prefix */
|
|
snprintf(prefix, sizeof(prefix), "%s_test_", ste->name);
|
|
|
|
ret = ut_run_list(uts, ste->name, prefix, ste->start, n_ents,
|
|
select_name, runs_per_test, force_run, test_insert);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void show_stats(struct unit_test_state *uts)
|
|
{
|
|
if (uts->run_count < 2)
|
|
return;
|
|
|
|
ut_report(&uts->total, uts->run_count);
|
|
if (CONFIG_IS_ENABLED(UNIT_TEST_DURATION) &&
|
|
uts->total.test_count && uts->worst) {
|
|
ulong avg = uts->total.duration_ms / uts->total.test_count;
|
|
|
|
printf("Average test time: %ld ms, worst case '%s' took %d ms\n",
|
|
avg, uts->worst->name, uts->worst_ms);
|
|
}
|
|
}
|
|
|
|
static void update_stats(struct unit_test_state *uts, const struct suite *ste)
|
|
{
|
|
if (CONFIG_IS_ENABLED(UNIT_TEST_DURATION) && uts->cur.test_count) {
|
|
ulong avg;
|
|
|
|
avg = uts->cur.duration_ms ?
|
|
uts->cur.duration_ms /
|
|
uts->cur.test_count : 0;
|
|
if (avg > uts->worst_ms) {
|
|
uts->worst_ms = avg;
|
|
uts->worst = ste;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int do_ut_all(struct unit_test_state *uts, const char *select_name,
|
|
int runs_per_test, bool force_run, const char *test_insert)
|
|
{
|
|
int i;
|
|
int retval;
|
|
int any_fail = 0;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(suites); i++) {
|
|
struct suite *ste = &suites[i];
|
|
|
|
if (has_tests(ste)) {
|
|
printf("----Running %s tests----\n", ste->name);
|
|
retval = run_suite(uts, ste, select_name, runs_per_test,
|
|
force_run, test_insert);
|
|
if (!any_fail)
|
|
any_fail = retval;
|
|
update_stats(uts, ste);
|
|
}
|
|
}
|
|
|
|
return any_fail;
|
|
}
|
|
|
|
static int do_ut_info(bool show_suites)
|
|
{
|
|
int suite_count, i;
|
|
|
|
for (suite_count = 0, i = 0; i < ARRAY_SIZE(suites); i++) {
|
|
struct suite *ste = &suites[i];
|
|
|
|
if (has_tests(ste))
|
|
suite_count++;
|
|
}
|
|
|
|
printf("Test suites: %d\n", suite_count);
|
|
printf("Total tests: %d\n", (int)UNIT_TEST_ALL_COUNT());
|
|
|
|
if (show_suites) {
|
|
int i, total;
|
|
|
|
puts("\nTests Suite Purpose");
|
|
puts("\n----- ------------ -------------------------\n");
|
|
for (i = 0, total = 0; i < ARRAY_SIZE(suites); i++) {
|
|
struct suite *ste = &suites[i];
|
|
long n_ent = ste->end - ste->start;
|
|
|
|
if (n_ent) {
|
|
printf("%5ld %-13.13s %s\n", n_ent, ste->name,
|
|
ste->help);
|
|
total += n_ent;
|
|
}
|
|
}
|
|
puts("----- ------------ -------------------------\n");
|
|
printf("%5d %-13.13s\n", total, "Total");
|
|
|
|
if (UNIT_TEST_ALL_COUNT() != total)
|
|
puts("Error: Suite test-count does not match total\n");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct suite *find_suite(const char *name)
|
|
{
|
|
struct suite *ste;
|
|
int i;
|
|
|
|
for (i = 0, ste = suites; i < ARRAY_SIZE(suites); i++, ste++) {
|
|
if (!strcmp(ste->name, name))
|
|
return ste;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int do_ut(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
|
|
{
|
|
const char *test_insert = NULL, *select_name;
|
|
struct unit_test_state uts;
|
|
bool show_suites = false;
|
|
bool force_run = false;
|
|
int runs_per_text = 1;
|
|
struct suite *ste;
|
|
char *name;
|
|
int ret;
|
|
|
|
/* drop initial "ut" arg */
|
|
argc--;
|
|
argv++;
|
|
|
|
while (argc > 0 && *argv[0] == '-') {
|
|
const char *str = argv[0];
|
|
|
|
switch (str[1]) {
|
|
case 'r':
|
|
runs_per_text = dectoul(str + 2, NULL);
|
|
break;
|
|
case 'f':
|
|
force_run = true;
|
|
break;
|
|
case 'I':
|
|
test_insert = str + 2;
|
|
if (!strchr(test_insert, ':'))
|
|
return CMD_RET_USAGE;
|
|
break;
|
|
case 's':
|
|
show_suites = true;
|
|
break;
|
|
}
|
|
argv++;
|
|
argc--;
|
|
}
|
|
|
|
if (argc < 1)
|
|
return CMD_RET_USAGE;
|
|
|
|
ut_init_state(&uts);
|
|
name = argv[0];
|
|
select_name = cmd_arg1(argc, argv);
|
|
if (!strcmp(name, "all")) {
|
|
ret = do_ut_all(&uts, select_name, runs_per_text, force_run,
|
|
test_insert);
|
|
} else if (!strcmp(name, "info")) {
|
|
ret = do_ut_info(show_suites);
|
|
} else {
|
|
int any_fail = 0;
|
|
const char *p;
|
|
|
|
for (; p = strsep(&name, ","), p; name = NULL) {
|
|
ste = find_suite(p);
|
|
if (!ste) {
|
|
printf("Suite '%s' not found\n", p);
|
|
return CMD_RET_FAILURE;
|
|
} else if (!has_tests(ste)) {
|
|
/* perhaps a Kconfig option needs to be set? */
|
|
printf("Suite '%s' is not enabled\n", p);
|
|
return CMD_RET_FAILURE;
|
|
}
|
|
|
|
ret = run_suite(&uts, ste, select_name, runs_per_text,
|
|
force_run, test_insert);
|
|
if (!any_fail)
|
|
any_fail = ret;
|
|
update_stats(&uts, ste);
|
|
}
|
|
ret = any_fail;
|
|
}
|
|
show_stats(&uts);
|
|
if (ret)
|
|
return ret;
|
|
ut_uninit_state(&uts);
|
|
|
|
return 0;
|
|
}
|
|
|
|
U_BOOT_LONGHELP(ut,
|
|
"[-rs] [-f] [-I<n>:<one_test>][<suites>] - run unit tests\n"
|
|
" -r<runs> Number of times to run each test\n"
|
|
" -f Force 'manual' tests to run as well\n"
|
|
" -I Test to run after <n> other tests have run\n"
|
|
" -s Show all suites with ut info\n"
|
|
" <suites> Comma-separated list of suites to run\n"
|
|
"\n"
|
|
"Options for <suite>:\n"
|
|
"all - execute all enabled tests\n"
|
|
"info - show info about tests [and suites]"
|
|
);
|
|
|
|
U_BOOT_CMD(
|
|
ut, CONFIG_SYS_MAXARGS, 1, do_ut,
|
|
"unit tests", ut_help_text
|
|
);
|