Hi David, On Sat, May 02, 2026 at 10:49:17AM +0800, David Gow wrote: > This is used by things like Jenkins and other CI systems, which can > pretty-print the test output and potentially provide test-level comparisons > between runs. > > The implementation here is pretty basic: it only provides the raw results, > split into tests and test suites, and doesn't provide any overall metadata. > However, CI systems like Jenkins can injest it and it is already useful.
"ingest"? > Signed-off-by: David Gow <[email protected]> > --- > > Finally got around to doing a new version of this. I'm running this > locally with Jenkins, and it's giving nice summaries of test results. > > Changes since v1: > https://lore.kernel.org/all/[email protected]/ > - Use python's provided XML quote escaping, rather than coding our own > (Thanks Thomas) What I tried to suggest was to do all of the XML generation through the Python module instead of manual string concatenation. The XML escaping would go away as a side effect. A pseudo-code example: out = xml.sax.saxutils.XMLGenerator(f, encoding='utf-8') out.startDocument() for subtest in test.subtests: out.startElement('testcase', {'name': 'subtest.name' }) if subtest.status == TestStatus.FAILURE: out.startElement('failure', {}) out.characters('Test failed') out.endElement('failure') out.endElement('testcase') How I used it recently: https://github.com/Linutronix/elbe/blob/master/website/ext/elbedocoverview.py#L133 > - Output proper <skipped> tags for skipped tests > - Report crashed tests as <error> > - Don't output <system-out> tags if there are no lines of log data > > --- > Documentation/dev-tools/kunit/run_wrapper.rst | 3 ++ > tools/testing/kunit/kunit.py | 25 ++++++++++- > tools/testing/kunit/kunit_junit.py | 43 +++++++++++++++++++ > tools/testing/kunit/kunit_tool_test.py | 38 ++++++++++++++-- > 4 files changed, 105 insertions(+), 4 deletions(-) > create mode 100644 tools/testing/kunit/kunit_junit.py > > diff --git a/Documentation/dev-tools/kunit/run_wrapper.rst > b/Documentation/dev-tools/kunit/run_wrapper.rst > index 770bb09a475a..cecc110a3399 100644 > --- a/Documentation/dev-tools/kunit/run_wrapper.rst > +++ b/Documentation/dev-tools/kunit/run_wrapper.rst > @@ -324,6 +324,9 @@ command line arguments: > - ``--json``: If set, stores the test results in a JSON format and prints to > `stdout` or > saves to a file if a filename is specified. > > +- ``--junit``: If set, stores the test results in JUnit XML format and > prints to `stdout` or > + saves to a file if a filename is specified. > + > - ``--filter``: Specifies filters on test attributes, for example, > ``speed!=slow``. > Multiple filters can be used by wrapping input in quotes and separating > filters > by commas. Example: ``--filter "speed>slow, module=example"``. > diff --git a/tools/testing/kunit/kunit.py b/tools/testing/kunit/kunit.py > index 742f5c555666..1a7ff594b791 100755 > --- a/tools/testing/kunit/kunit.py > +++ b/tools/testing/kunit/kunit.py > @@ -21,6 +21,7 @@ from enum import Enum, auto > from typing import Iterable, List, Optional, Sequence, Tuple > > import kunit_json > +import kunit_junit > import kunit_kernel > import kunit_parser > from kunit_printer import stdout, null_printer > @@ -49,6 +50,7 @@ class KunitBuildRequest(KunitConfigRequest): > class KunitParseRequest: > raw_output: Optional[str] > json: Optional[str] > + junit: Optional[str] > summary: bool > failed: bool > > @@ -268,6 +270,17 @@ def parse_tests(request: KunitParseRequest, metadata: > kunit_json.Metadata, input > stdout.print_with_timestamp("Test results stored in %s" > % > os.path.abspath(request.json)) > > + if request.junit: > + junit_str = kunit_junit.get_junit_result( > + test=test) Unnecessary linebreak? > + if request.junit == 'stdout': > + print(junit_str) > + else: > + with open(request.junit, 'w') as f: > + f.write(junit_str) > + stdout.print_with_timestamp("Test results stored in %s" > % > + os.path.abspath(request.junit)) f-string? > + > if test.status != kunit_parser.TestStatus.SUCCESS: > return KunitResult(KunitStatus.TEST_FAILURE, parse_time), test > > @@ -309,6 +322,7 @@ def run_tests(linux: kunit_kernel.LinuxSourceTree, > # So we hackily automatically rewrite --json => --json=stdout > pseudo_bool_flag_defaults = { > '--json': 'stdout', > + '--junit': 'stdout', > '--raw_output': 'kunit', > } (...) But all of it are just suggestions. Thomas

