new file mode 100644
@@ -0,0 +1,77 @@
+# resulttool - report test results in JUnit XML format
+#
+# Copyright (c) 2024, Siemens AG.
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+
+import os
+import re
+import xml.etree.ElementTree as ET
+import resulttool.resultutils as resultutils
+
+def junit(args, logger):
+ testresults = resultutils.load_resultsdata(args.json_file, configmap=resultutils.store_map)
+
+ total_time = 0
+ skipped = 0
+ failures = 0
+ errors = 0
+
+ for tests in testresults.values():
+ results = tests[next(reversed(tests))].get("result", {})
+
+ for result_id, result in results.items():
+ # filter out ptestresult.rawlogs and ptestresult.sections
+ if re.search(r'\.test_', result_id):
+ total_time += result.get("duration", 0)
+
+ if result['status'] == "FAILED":
+ failures += 1
+ elif result['status'] == "ERROR":
+ errors += 1
+ elif result['status'] == "SKIPPED":
+ skipped += 1
+
+ testsuites_node = ET.Element("testsuites")
+ testsuites_node.set("time", "%s" % total_time)
+ testsuite_node = ET.SubElement(testsuites_node, "testsuite")
+ testsuite_node.set("name", "Testimage")
+ testsuite_node.set("time", "%s" % total_time)
+ testsuite_node.set("tests", "%s" % len(results))
+ testsuite_node.set("failures", "%s" % failures)
+ testsuite_node.set("errors", "%s" % errors)
+ testsuite_node.set("skipped", "%s" % skipped)
+
+ for result_id, result in results.items():
+ if re.search(r'\.test_', result_id):
+ testcase_node = ET.SubElement(testsuite_node, "testcase", {
+ "name": result_id,
+ "classname": "Testimage",
+ "time": str(result['duration'])
+ })
+ if result['status'] == "SKIPPED":
+ ET.SubElement(testcase_node, "skipped", message=result['log'])
+ elif result['status'] == "FAILED":
+ ET.SubElement(testcase_node, "failure", message=result['log'])
+ elif result['status'] == "ERROR":
+ ET.SubElement(testcase_node, "error", message=result['log'])
+
+ tree = ET.ElementTree(testsuites_node)
+
+ if args.junit_xml_path is None:
+ args.junit_xml_path = os.environ['BUILDDIR'] + '/tmp/log/oeqa/junit.xml'
+ tree.write(args.junit_xml_path, encoding='UTF-8', xml_declaration=True)
+
+ logger.info('Saved JUnit XML report as %s' % args.junit_xml_path)
+
+def register_commands(subparsers):
+ """Register subcommands from this plugin"""
+ parser_build = subparsers.add_parser('junit', help='create test report in JUnit XML format',
+ description='generate unit test report in JUnit XML format based on the latest test results in the testresults.json.',
+ group='analysis')
+ parser_build.set_defaults(func=junit)
+ parser_build.add_argument('json_file',
+ help='json file should point to the testresults.json')
+ parser_build.add_argument('-j', '--junit_xml_path',
+ help='junit xml path allows setting the path of the generated test report. The default location is <build_dir>/tmp/log/oeqa/junit.xml')
@@ -15,6 +15,9 @@
# To report test report, execute the below
# $ resulttool report <source_dir>
#
+# To create a unit test report in JUnit XML format, execute the below
+# $ resulttool junit <json_file>
+#
# To perform regression file analysis, execute the below
# $ resulttool regression-file <base_result_file> <target_result_file>
#
@@ -43,6 +46,7 @@ import resulttool.regression
import resulttool.report
import resulttool.manualexecution
import resulttool.log
+import resulttool.junit
logger = scriptutils.logger_create('resulttool')
def main():
@@ -61,6 +65,7 @@ def main():
resulttool.regression.register_commands(subparsers)
resulttool.report.register_commands(subparsers)
resulttool.log.register_commands(subparsers)
+ resulttool.junit.register_commands(subparsers)
args = parser.parse_args()
if args.debug:
This adds the functionality to convert the results of the testresults.json file to a unit test report in JUnit XML format. The unit test report can be used in the CI/CD pipeline to display the test results. To use the resulttool scripts, first source oe environment, then run the entry point script to look for help. $ resulttool To generate the unit test report, execute the below $ resulttool junit <json_file> By default the unit test report is stored as <build_dir>/tmp/log/oeqa/junit.xml. Signed-off-by: Clara Kowalsky <clara.kowalsky@siemens.com> --- scripts/lib/resulttool/junit.py | 77 +++++++++++++++++++++++++++++++++ scripts/resulttool | 5 +++ 2 files changed, 82 insertions(+) create mode 100644 scripts/lib/resulttool/junit.py