test: Add parallel test execution support
Running the full test suite takes a long time. It would be useful to distribute tests across multiple sandbox instances to speed up testing. Add support for running tests in parallel across multiple sandbox instances. Each worker runs a subset of tests based on its worker ID. Add -P<n>:<w> option to the ut command where n is the total number of workers and w is this worker's ID (0 to n-1). Tests are distributed by index modulo number of workers. Series-to: u-boot Series-cc: heinrich Cover-letter: test: Various improvements to unit-test infrastructure This series adds several improvements to the unit-test infrastructure: - Move disk images to the persistent-data directory so they don't pollute the source tree - Add a way to keep pytest-created artefacts for faster iteration on C tests - Add a helper to simplify running manual unit tests from Python - Allow combined flags with the ut command (e.g. -Efm) - Add a -E flag to emit machine-readable result lines - Add a -P flag to distribute tests across parallel workers - Add -m as an alias for -f (force manual tests) These changes make it easier to run and debug tests, especially when iterating on C test code. END Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com>
This commit is contained in:
@@ -171,6 +171,34 @@ require a large amount of refactoring, e.g. with more use of pytest fixtures.
|
||||
The code-coverage tests are omitted since they cannot run in parallel due to a
|
||||
Python limitation.
|
||||
|
||||
Parallel C unit tests
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The ``ut`` command supports distributing tests across multiple sandbox
|
||||
instances using the ``-P`` flag. This is useful when running tests directly
|
||||
from the command line without pytest.
|
||||
|
||||
To run tests in parallel across 4 workers::
|
||||
|
||||
# Terminal 1
|
||||
/tmp/b/sandbox/u-boot -T -c "ut -P4:0 dm"
|
||||
|
||||
# Terminal 2
|
||||
/tmp/b/sandbox/u-boot -T -c "ut -P4:1 dm"
|
||||
|
||||
# Terminal 3
|
||||
/tmp/b/sandbox/u-boot -T -c "ut -P4:2 dm"
|
||||
|
||||
# Terminal 4
|
||||
/tmp/b/sandbox/u-boot -T -c "ut -P4:3 dm"
|
||||
|
||||
The format is ``-P<n>:<w>`` where ``n`` is the total number of workers and
|
||||
``w`` is this worker's ID (0 to n-1). Tests are distributed by index modulo
|
||||
the number of workers, so each worker runs a disjoint subset.
|
||||
|
||||
This can be combined with other flags, e.g. ``-EP4:0`` to emit result lines
|
||||
while running as worker 0 of 4.
|
||||
|
||||
|
||||
Testing under a debugger
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -11,7 +11,7 @@ Synopsis
|
||||
|
||||
::
|
||||
|
||||
ut [-Efmr<runs>] [-R] [-I<n>:<one_test>] [<suite> | all [<test>]] [<args>...]
|
||||
ut [-Efmr<runs>] [-R] [-I<n>:<one_test>] [-P<n>:<w>] [<suite> | all [<test>]] [<args>...]
|
||||
ut [-s] info
|
||||
|
||||
Description
|
||||
@@ -44,6 +44,12 @@ test
|
||||
causes another test to fail. If the one test fails, testing stops
|
||||
immediately.
|
||||
|
||||
-P <n>:<w>
|
||||
Run as worker `<w>` of `<n>` parallel workers. Tests are distributed by
|
||||
index modulo number of workers, so each worker runs a disjoint subset of
|
||||
tests. This allows running tests in parallel across multiple sandbox
|
||||
instances.
|
||||
|
||||
-R
|
||||
Preserve console recording on test failure. Normally when a test fails,
|
||||
console recording is disabled so error messages go directly to output.
|
||||
|
||||
@@ -89,6 +89,8 @@ struct ut_arg {
|
||||
* @of_other: Live tree for the other FDT
|
||||
* @runs_per_test: Number of times to run each test (typically 1)
|
||||
* @force_run: true to run tests marked with the UTF_MANUAL flag
|
||||
* @workers: Number of parallel workers, 0 if not sharding tests
|
||||
* @worker_id: ID of this worker (0 to workers-1)
|
||||
* @old_bloblist: stores the old gd->bloblist pointer
|
||||
* @soft_fail: continue execution of the test even after it fails
|
||||
* @expect_str: Temporary string used to hold expected string value
|
||||
@@ -121,6 +123,8 @@ struct unit_test_state {
|
||||
struct device_node *of_other;
|
||||
int runs_per_test;
|
||||
bool force_run;
|
||||
int workers;
|
||||
int worker_id;
|
||||
void *old_bloblist;
|
||||
bool soft_fail;
|
||||
char expect_str[1024];
|
||||
|
||||
@@ -257,6 +257,7 @@ static int do_ut(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
|
||||
bool keep_record = false;
|
||||
bool emit_result = false;
|
||||
int runs_per_text = 1;
|
||||
int workers = 0, worker_id = 0;
|
||||
struct suite *ste;
|
||||
char *name;
|
||||
int ret;
|
||||
@@ -285,6 +286,15 @@ static int do_ut(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
|
||||
if (!strchr(test_insert, ':'))
|
||||
return CMD_RET_USAGE;
|
||||
goto next_arg;
|
||||
case 'P': {
|
||||
const char *colon = strchr(str + 1, ':');
|
||||
|
||||
if (!colon)
|
||||
return CMD_RET_USAGE;
|
||||
workers = dectoul(str + 1, NULL);
|
||||
worker_id = dectoul(colon + 1, NULL);
|
||||
goto next_arg;
|
||||
}
|
||||
case 'R':
|
||||
keep_record = true;
|
||||
break;
|
||||
@@ -304,6 +314,8 @@ next_arg:
|
||||
ut_init_state(&uts);
|
||||
uts.keep_record = keep_record;
|
||||
uts.emit_result = emit_result;
|
||||
uts.workers = workers;
|
||||
uts.worker_id = worker_id;
|
||||
name = argv[0];
|
||||
select_name = cmd_arg1(argc, argv);
|
||||
|
||||
@@ -349,11 +361,13 @@ next_arg:
|
||||
}
|
||||
|
||||
U_BOOT_LONGHELP(ut,
|
||||
"[-Efmrs] [-R] [-I<n>:<one_test>] <suite> [<test> [<args>...]] - run unit tests\n"
|
||||
"[-Efmrs] [-R] [-I<n>:<one_test>] [-P<n>:<w>] <suite> [<test> [<args>...]]\n"
|
||||
" - 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"
|
||||
" -P<n>:<w> Run as worker <w> of <n> parallel workers\n"
|
||||
" -R Preserve console recording on test failure\n"
|
||||
" -s Show all suites with ut info\n"
|
||||
" <suite> Test suite to run (or comma-separated list)\n"
|
||||
|
||||
@@ -790,6 +790,10 @@ static int ut_run_tests(struct unit_test_state *uts, const char *prefix,
|
||||
!test_matches(prefix, test_name, select_name))
|
||||
continue;
|
||||
|
||||
/* Skip tests not assigned to this worker */
|
||||
if (uts->workers && upto % uts->workers != uts->worker_id)
|
||||
continue;
|
||||
|
||||
if (test->flags & UTF_MANUAL) {
|
||||
int len;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user