lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<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("&", "&amp;").replace("\"", "&quot;").replace("'", "&apos;").replace("<", "&lt;").replace(">", "&gt;")
> +
> +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

Powered by Openwall GNU/*/Linux Powered by OpenVZ