[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <20260119085229-a89b3a97-a283-4417-ac09-13240c39c9fc@linutronix.de>
Date: Mon, 19 Jan 2026 08:57:04 +0100
From: Thomas Weißschuh <thomas.weissschuh@...utronix.de>
To: David Gow <davidgow@...gle.com>
Cc: Brendan Higgins <brendan.higgins@...ux.dev>,
Rae Moar <raemoar63@...il.com>, Shuah Khan <skhan@...uxfoundation.org>,
linux-kselftest@...r.kernel.org, workflows@...r.kernel.org, linux-kernel@...r.kernel.org,
kunit-dev@...glegroups.com
Subject: Re: [PATCH] kunit: tool: Add (primitive) support for outputting
JUnit XML
On Mon, Jan 19, 2026 at 03:34:24PM +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.
>
> Signed-off-by: David Gow <davidgow@...gle.com>
> ---
> Documentation/dev-tools/kunit/run_wrapper.rst | 3 ++
> tools/testing/kunit/kunit.py | 25 +++++++++++-
> tools/testing/kunit/kunit_junit.py | 36 +++++++++++++++++
> tools/testing/kunit/kunit_tool_test.py | 40 +++++++++++++++++--
> 4 files changed, 100 insertions(+), 4 deletions(-)
> create mode 100644 tools/testing/kunit/kunit_junit.py
(...)
> diff --git a/tools/testing/kunit/kunit.py b/tools/testing/kunit/kunit.py
> index e3d82a038f93..0698d27c3629 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
>
> @@ -261,6 +263,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))
> +
> if test.status != kunit_parser.TestStatus.SUCCESS:
> return KunitResult(KunitStatus.TEST_FAILURE, parse_time), test
>
(...)
> diff --git a/tools/testing/kunit/kunit_junit.py b/tools/testing/kunit/kunit_junit.py
> new file mode 100644
> index 000000000000..58d482e0c793
> --- /dev/null
> +++ b/tools/testing/kunit/kunit_junit.py
> @@ -0,0 +1,36 @@
> +# SPDX-License-Identifier: GPL-2.0
> +#
> +# Generates JSON from KUnit results according to
> +# KernelCI spec: https://github.com/kernelci/kernelci-doc/wiki/Test-API
> +#
> +# Copyright (C) 2025, Google LLC.
> +# Author: David Gow <davidgow@...gle.com>
> +
> +
> +from kunit_parser import Test, TestStatus
> +
> +def escape_xml_string(string : str) -> str:
> + return string.replace("&", "&").replace("\"", """).replace("'", "'").replace("<", "<").replace(">", ">")
> +
> +def get_test_suite(test: Test) -> str:
> + xml_output = '<testsuite name="' + escape_xml_string(test.name) + '" tests="' + str(test.counts.total()) + '" failures="' + str(test.counts.failed) + '" skipped="' +str(test.counts.skipped) + '">\n'
> +
> + for subtest in test.subtests:
> + if subtest.subtests:
> + xml_output += get_test_suite(subtest)
> + continue
> + xml_output += '<testcase name="' + escape_xml_string(subtest.name) + '" >\n'
> + if subtest.status == TestStatus.FAILURE:
> + xml_output += '<failure>Test Failed</failure>\n'
> + xml_output += '<system-out><![CDATA[' + "\n".join(subtest.log) + ']]></system-out>\n'
> + xml_output += '</testcase>\n'
> +
> + xml_output += '</testsuite>\n\n'
> +
> + return xml_output
> +
> +def get_junit_result(test: Test) -> str:
> + xml_output = '<?xml version="1.0" encoding="UTF-8" ?>\n\n'
> +
> + xml_output += get_test_suite(test)
> + return xml_output
Did you look into the Python stdlib XML serializer?
https://docs.python.org/3/library/xml.sax.utils.html#xml.sax.saxutils.XMLGenerator
https://docs.python.org/3/library/xml.sax.handler.html#contenthandler-objects
With that there should be no need to mess around with low-level XML syntax.
(...)
Thomas
Powered by blists - more mailing lists