diff options
author | Clara Kowalsky <clara.kowalsky@siemens.com> | 2024-08-28 11:20:27 +0200 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2024-08-29 21:58:19 +0100 |
commit | 5aeabd3217da2a7ce9f789d5d224f6682b4555a1 (patch) | |
tree | c81f98c3e1a608ae71917e2f87fd640f70254a67 | |
parent | f4f9ebf6fb7da7605298c95187a70c118e0ab02e (diff) | |
download | poky-5aeabd3217da2a7ce9f789d5d224f6682b4555a1.tar.gz |
resulttool: Add support to create test report in JUnit XML format
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.
(From OE-Core rev: 3f9be03946243feaa09b908d7010899769091fe6)
Signed-off-by: Clara Kowalsky <clara.kowalsky@siemens.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
-rw-r--r-- | scripts/lib/resulttool/junit.py | 77 | ||||
-rwxr-xr-x | scripts/resulttool | 5 |
2 files changed, 82 insertions, 0 deletions
diff --git a/scripts/lib/resulttool/junit.py b/scripts/lib/resulttool/junit.py new file mode 100644 index 0000000000..c7a53dc550 --- /dev/null +++ b/scripts/lib/resulttool/junit.py | |||
@@ -0,0 +1,77 @@ | |||
1 | # resulttool - report test results in JUnit XML format | ||
2 | # | ||
3 | # Copyright (c) 2024, Siemens AG. | ||
4 | # | ||
5 | # SPDX-License-Identifier: GPL-2.0-only | ||
6 | # | ||
7 | |||
8 | import os | ||
9 | import re | ||
10 | import xml.etree.ElementTree as ET | ||
11 | import resulttool.resultutils as resultutils | ||
12 | |||
13 | def junit(args, logger): | ||
14 | testresults = resultutils.load_resultsdata(args.json_file, configmap=resultutils.store_map) | ||
15 | |||
16 | total_time = 0 | ||
17 | skipped = 0 | ||
18 | failures = 0 | ||
19 | errors = 0 | ||
20 | |||
21 | for tests in testresults.values(): | ||
22 | results = tests[next(reversed(tests))].get("result", {}) | ||
23 | |||
24 | for result_id, result in results.items(): | ||
25 | # filter out ptestresult.rawlogs and ptestresult.sections | ||
26 | if re.search(r'\.test_', result_id): | ||
27 | total_time += result.get("duration", 0) | ||
28 | |||
29 | if result['status'] == "FAILED": | ||
30 | failures += 1 | ||
31 | elif result['status'] == "ERROR": | ||
32 | errors += 1 | ||
33 | elif result['status'] == "SKIPPED": | ||
34 | skipped += 1 | ||
35 | |||
36 | testsuites_node = ET.Element("testsuites") | ||
37 | testsuites_node.set("time", "%s" % total_time) | ||
38 | testsuite_node = ET.SubElement(testsuites_node, "testsuite") | ||
39 | testsuite_node.set("name", "Testimage") | ||
40 | testsuite_node.set("time", "%s" % total_time) | ||
41 | testsuite_node.set("tests", "%s" % len(results)) | ||
42 | testsuite_node.set("failures", "%s" % failures) | ||
43 | testsuite_node.set("errors", "%s" % errors) | ||
44 | testsuite_node.set("skipped", "%s" % skipped) | ||
45 | |||
46 | for result_id, result in results.items(): | ||
47 | if re.search(r'\.test_', result_id): | ||
48 | testcase_node = ET.SubElement(testsuite_node, "testcase", { | ||
49 | "name": result_id, | ||
50 | "classname": "Testimage", | ||
51 | "time": str(result['duration']) | ||
52 | }) | ||
53 | if result['status'] == "SKIPPED": | ||
54 | ET.SubElement(testcase_node, "skipped", message=result['log']) | ||
55 | elif result['status'] == "FAILED": | ||
56 | ET.SubElement(testcase_node, "failure", message=result['log']) | ||
57 | elif result['status'] == "ERROR": | ||
58 | ET.SubElement(testcase_node, "error", message=result['log']) | ||
59 | |||
60 | tree = ET.ElementTree(testsuites_node) | ||
61 | |||
62 | if args.junit_xml_path is None: | ||
63 | args.junit_xml_path = os.environ['BUILDDIR'] + '/tmp/log/oeqa/junit.xml' | ||
64 | tree.write(args.junit_xml_path, encoding='UTF-8', xml_declaration=True) | ||
65 | |||
66 | logger.info('Saved JUnit XML report as %s' % args.junit_xml_path) | ||
67 | |||
68 | def register_commands(subparsers): | ||
69 | """Register subcommands from this plugin""" | ||
70 | parser_build = subparsers.add_parser('junit', help='create test report in JUnit XML format', | ||
71 | description='generate unit test report in JUnit XML format based on the latest test results in the testresults.json.', | ||
72 | group='analysis') | ||
73 | parser_build.set_defaults(func=junit) | ||
74 | parser_build.add_argument('json_file', | ||
75 | help='json file should point to the testresults.json') | ||
76 | parser_build.add_argument('-j', '--junit_xml_path', | ||
77 | help='junit xml path allows setting the path of the generated test report. The default location is <build_dir>/tmp/log/oeqa/junit.xml') | ||
diff --git a/scripts/resulttool b/scripts/resulttool index fc282bda6c..66a6af9959 100755 --- a/scripts/resulttool +++ b/scripts/resulttool | |||
@@ -15,6 +15,9 @@ | |||
15 | # To report test report, execute the below | 15 | # To report test report, execute the below |
16 | # $ resulttool report <source_dir> | 16 | # $ resulttool report <source_dir> |
17 | # | 17 | # |
18 | # To create a unit test report in JUnit XML format, execute the below | ||
19 | # $ resulttool junit <json_file> | ||
20 | # | ||
18 | # To perform regression file analysis, execute the below | 21 | # To perform regression file analysis, execute the below |
19 | # $ resulttool regression-file <base_result_file> <target_result_file> | 22 | # $ resulttool regression-file <base_result_file> <target_result_file> |
20 | # | 23 | # |
@@ -43,6 +46,7 @@ import resulttool.regression | |||
43 | import resulttool.report | 46 | import resulttool.report |
44 | import resulttool.manualexecution | 47 | import resulttool.manualexecution |
45 | import resulttool.log | 48 | import resulttool.log |
49 | import resulttool.junit | ||
46 | logger = scriptutils.logger_create('resulttool') | 50 | logger = scriptutils.logger_create('resulttool') |
47 | 51 | ||
48 | def main(): | 52 | def main(): |
@@ -61,6 +65,7 @@ def main(): | |||
61 | resulttool.regression.register_commands(subparsers) | 65 | resulttool.regression.register_commands(subparsers) |
62 | resulttool.report.register_commands(subparsers) | 66 | resulttool.report.register_commands(subparsers) |
63 | resulttool.log.register_commands(subparsers) | 67 | resulttool.log.register_commands(subparsers) |
68 | resulttool.junit.register_commands(subparsers) | ||
64 | 69 | ||
65 | args = parser.parse_args() | 70 | args = parser.parse_args() |
66 | if args.debug: | 71 | if args.debug: |