From: Simon Glass <simon.glass@canonical.com>
The ut command shows test output but does not provide a machine-readable
indication of whether each individual test passed or failed. External
tools must rely on heuristics like scanning for failure patterns in the
output.
Add a -E flag that emits an explicit result line after each test:
Result: PASS: test_name: file.c
Result: FAIL: test_name: file.c
This allows tools to reliably determine per-test pass/fail status
without fragile pattern matching. The flag is optional to maintain
backward compatibility with existing scripts.
Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
---
doc/usage/cmd/ut.rst | 7 ++++++-
include/test/test.h | 2 ++
test/cmd_ut.c | 8 +++++++-
test/test-main.c | 9 +++++++++
4 files changed, 24 insertions(+), 2 deletions(-)
@@ -11,7 +11,7 @@ Synopsis
::
- ut [-fmr<runs>] [-R] [-I<n>:<one_test>] [<suite> | all [<test>]] [<args>...]
+ ut [-Efmr<runs>] [-R] [-I<n>:<one_test>] [<suite> | all [<test>]] [<args>...]
ut [-s] info
Description
@@ -26,6 +26,11 @@ suite
test
Speciifes a particular test to run, within a suite, or all suites
+-E
+ Emit a result line after each test, in the format
+ `Result: PASS|FAIL|SKIP: <test_name>: <file>`. This is useful for
+ automated parsing of test results.
+
-f, -m
Force running of manual tests. Manual tests have the `_norun` suffix and
are normally skipped because they require external setup (e.g., creating
@@ -97,6 +97,7 @@ struct ut_arg {
* @arg_count: Number of parsed arguments
* @arg_error: Set if ut_str/int/bool() detects a type mismatch
* @keep_record: Preserve console recording when ut_fail() is called
+ * @emit_result: Emit result line after each test completes
* @priv: Private data for tests to use as needed
*/
struct unit_test_state {
@@ -128,6 +129,7 @@ struct unit_test_state {
int arg_count;
bool arg_error;
bool keep_record;
+ bool emit_result;
char priv[UT_PRIV_SIZE];
};
@@ -255,6 +255,7 @@ static int do_ut(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
bool show_suites = false;
bool force_run = false;
bool keep_record = false;
+ bool emit_result = false;
int runs_per_text = 1;
struct suite *ste;
char *name;
@@ -269,6 +270,9 @@ static int do_ut(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
for (str++; *str; str++) {
switch (*str) {
+ case 'E':
+ emit_result = true;
+ break;
case 'r':
runs_per_text = dectoul(str + 1, NULL);
goto next_arg;
@@ -299,6 +303,7 @@ next_arg:
ut_init_state(&uts);
uts.keep_record = keep_record;
+ uts.emit_result = emit_result;
name = argv[0];
select_name = cmd_arg1(argc, argv);
@@ -344,7 +349,8 @@ next_arg:
}
U_BOOT_LONGHELP(ut,
- "[-fmrs] [-R] [-I<n>:<one_test>] <suite> [<test> [<args>...]] - run unit tests\n"
+ "[-Efmrs] [-R] [-I<n>:<one_test>] <suite> [<test> [<args>...]] - run unit tests\n"
+ " -E Emit result line after each test\n"
" -r<runs> Number of times to run each test\n"
" -f/-m Force 'manual' tests to run as well\n"
" -I Test to run after <n> other tests have run\n"
@@ -624,6 +624,7 @@ static int ut_run_test(struct unit_test_state *uts, struct unit_test *test,
{
const char *fname = strrchr(test->file, '/') + 1;
const char *note = "";
+ int old_fail_count;
int ret;
if ((test->flags & UTF_DM) && !uts->of_live)
@@ -639,6 +640,7 @@ static int ut_run_test(struct unit_test_state *uts, struct unit_test *test,
if (ret)
return ret;
+ old_fail_count = uts->cur.fail_count;
uts->arg_error = false;
ret = test->func(uts);
if (ret == -EAGAIN)
@@ -650,6 +652,13 @@ static int ut_run_test(struct unit_test_state *uts, struct unit_test *test,
ut_set_state(NULL);
+ if (uts->emit_result) {
+ bool passed = uts->cur.fail_count == old_fail_count;
+
+ printf("Result: %s: %s: %s%s\n", passed ? "PASS" : "FAIL",
+ test_name, fname, note);
+ }
+
return 0;
}