[PATCH v3 5/9] KVM: selftests: Run tests concurrently in KVM selftests runner

Vipin Sharma
2025-09-30T16:36:57Z

Add a command line argument, --jobs, to specify how many tests can
execute concurrently. Set default to 1.

Example:
python3 runner --test-dirs tests -j 10

Signed-off-by: Vipin Sharma <vipinsh@google.com>
---
.../testing/selftests/kvm/runner/__main__.py | 6 ++++
.../selftests/kvm/runner/test_runner.py | 28 +++++++++++++------
2 files changed, 26 insertions(+), 8 deletions(-)

diff --git a/tools/testing/selftests/kvm/runner/__main__.py b/tools/testing/selftests/kvm/runner/__main__.py
index b27a41e86271..b98f72c9f7ee 100644
--- a/tools/testing/selftests/kvm/runner/__main__.py
+++ b/tools/testing/selftests/kvm/runner/__main__.py
@@ -54,6 +54,12 @@ def cli():
default=False,
help="Appends timestamp to the output directory.")

+ parser.add_argument("-j",
+ "--jobs",
+ default=1,
+ type=int,
+ help="Maximum number of tests that can be run concurrently. (Default: 1)")
+
return parser.parse_args()

diff --git a/tools/testing/selftests/kvm/runner/test_runner.py b/tools/testing/selftests/kvm/runner/test_runner.py
index b9101f0e0432..92eec18fe5c6 100644
--- a/tools/testing/selftests/kvm/runner/test_runner.py
+++ b/tools/testing/selftests/kvm/runner/test_runner.py
@@ -4,6 +4,8 @@
# Author: vipinsh@google.com (Vipin Sharma)

import logging
+import concurrent.futures
+
from selftest import Selftest
from selftest import SelftestStatus

@@ -14,11 +16,16 @@ class TestRunner:
def __init__(self, testcases, args):
self.tests = []
self.output_dir = args.output
+ self.jobs = args.jobs

for testcase in testcases:
self.tests.append(Selftest(testcase, args.path, args.timeout,
args.output))

+ def _run_test(self, test):
+ test.run()
+ return test
+
def _log_result(self, test_result):
logger.info("*** stdout ***\n" + test_result.stdout)
logger.info("*** stderr ***\n" + test_result.stderr)
@@ -28,12 +35,17 @@ class TestRunner:
def start(self):
ret = 0

- for test in self.tests:
- test.run()
- self._log_result(test)
-
- if (test.status not in [SelftestStatus.PASSED,
- SelftestStatus.NO_RUN,
- SelftestStatus.SKIPPED]):
- ret = 1
+ with concurrent.futures.ProcessPoolExecutor(max_workers=self.jobs) as executor:
+ all_futures = []
+ for test in self.tests:
+ future = executor.submit(self._run_test, test)
+ all_futures.append(future)
+
+ for future in concurrent.futures.as_completed(all_futures):
+ test_result = future.result()
+ self._log_result(test_result)
+ if (test_result.status not in [SelftestStatus.PASSED,
+ SelftestStatus.NO_RUN,
+ SelftestStatus.SKIPPED]):
+ ret = 1
return ret
--
2.51.0.618.g983fd99d29-goog