Files
linux/tools/testing/kunit/kunit_junit.py
David Gow e9e05c7275 kunit: tool: Add (primitive) support for outputting JUnit XML
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 ingest it and it is already useful.

Link: https://lore.kernel.org/r/20260606013827.240790-2-david@davidgow.net
Reviewed-by: Thomas Weißschuh <thomas.weissschuh@linutronix.de>
Signed-off-by: David Gow <david@davidgow.net>
Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
2026-06-07 19:50:16 -06:00

62 lines
2.2 KiB
Python

# SPDX-License-Identifier: GPL-2.0
#
# Generates JUnit XML files from KUnit test results
#
# Copyright (C) 2026, Google LLC and David Gow.
from xml.sax.saxutils import quoteattr, XMLGenerator
import xml.etree.ElementTree as ET
from kunit_parser import Test, TestStatus
from typing import Optional
# Get a string representing a tes suite (including subtests) in JUnit XML
def get_test_suite(test: Test, parent: Optional[ET.Element]) -> ET.Element:
suite_attrs = {
'name': test.name,
'tests': str(test.counts.total()),
'failures': str(test.counts.failed),
'skipped': str(test.counts.skipped),
'errors': str(test.counts.crashed + test.counts.errors),
}
if parent is not None:
test_suite_element = ET.SubElement(parent, 'testsuite', suite_attrs)
else:
test_suite_element = ET.Element('testsuite', suite_attrs)
for subtest in test.subtests:
if subtest.subtests:
get_test_suite(subtest, test_suite_element)
continue
test_case_element = ET.SubElement(test_suite_element, 'testcase', {'name': subtest.name})
if subtest.status == TestStatus.FAILURE:
ET.SubElement(test_case_element, 'failure', {}).text = 'Test Failed'
elif subtest.status == TestStatus.SKIPPED:
ET.SubElement(test_case_element, 'skipped', {}).text = subtest.skip_reason
elif subtest.status == TestStatus.TEST_CRASHED:
ET.SubElement(test_case_element, 'error', {}).text = 'Test Crashed'
if subtest.log:
ET.SubElement(test_case_element, 'system-out', {}).text = "\n".join(subtest.log)
return test_suite_element
# Get a string for an entire XML file for the test structure starting at test
def get_junit_result(test: Test) -> str:
root_element = get_test_suite(test, None)
ET.indent(root_element)
return ET.tostring(root_element, encoding="unicode", xml_declaration=True)
# Print a JUnit result to stdout.
def print_junit_result(test: Test) -> None:
root_element = get_test_suite(test, None)
ET.indent(root_element)
ET.dump(root_element)
# Write an entire XML file for the test structure starting at test
def write_junit_result(test: Test, filename: str) -> None:
root_element = get_test_suite(test, None)
ET.indent(root_element)
root_et = ET.ElementTree(root_element)
root_et.write(filename, encoding='utf-8', xml_declaration=True)