1#!/usr/bin/env python3
2# coding=utf-8
3
4#
5# Copyright (c) 2022 Huawei Device Co., Ltd.
6# Licensed under the Apache License, Version 2.0 (the "License");
7# you may not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10#     http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS,
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15# See the License for the specific language governing permissions and
16# limitations under the License.
17#
18
19import os
20from ast import literal_eval
21from xml.dom import minidom
22from xml.etree import ElementTree
23
24from xdevice import get_cst_time, FilePermission
25from devicetest.core.constants import RunResult
26from devicetest.core.exception import DeviceTestError
27from devicetest.error import ErrorMessage
28from devicetest.log.logger import DeviceTestLog as Log
29
30
31class ReportConstants:
32    time_format = "%Y-%m-%d %H:%M:%S"
33    report_xml = "report.xml"
34
35
36class ReportHandler:
37
38    def __init__(self, report_path):
39        self.report_path = report_path
40        self.test_results = []
41
42    def generate_test_report(self, test_runner, _test_results=None, report_type="normal"):
43        if os.path.exists(self.report_path):
44            root = ElementTree.parse(self.report_path).getroot()
45            return self.report_path, ElementTree.tostring(root).decode()
46        try:
47            Log.info("start generate test report.")
48            test_results = _test_results or test_runner.test_results
49
50            start_time = test_runner.start_time
51
52            testsuites = ElementTree.Element('testsuites')
53
54            test_name = test_runner.configs.get("test_name")
55            if test_name is not None:
56                testsuites.set("name", test_name)
57            else:
58                testsuites.set("name", ReportConstants.report_xml)
59
60            if report_type == "xts":
61                tests_total, tests_error = self.report_xts_type(testsuites, test_results)
62            else:
63                tests_total, tests_error = self.report_normal_type(testsuites, test_results, test_name)
64
65            testsuites.set("tests", str(tests_total))
66            testsuites.set("failures", str(tests_error))
67            testsuites.set("disabled", '')
68            testsuites.set("errors", "")
69            testsuites.set("starttime", self.get_strftime(start_time))
70            testsuites.set("endtime", self.get_now_strftime())
71            testsuites.set("report_version", "1.0")
72
73            os.makedirs(os.path.dirname(self.report_path), exist_ok=True)
74            xml_content = ElementTree.tostring(testsuites).decode()
75            xml_pretty = minidom.parseString(xml_content).toprettyxml(indent="  ")
76
77            result_fd = os.open(self.report_path, os.O_CREAT | os.O_WRONLY | os.O_TRUNC, FilePermission.mode_644)
78            with os.fdopen(result_fd, mode="w", encoding="utf-8") as result_file:
79                result_file.write(xml_pretty)
80            return self.report_path, xml_content
81
82        except Exception as error:
83            err_msg = ErrorMessage.Common.Code_0201003
84            Log.error(err_msg, is_traceback=True)
85            raise DeviceTestError(err_msg) from error
86
87        finally:
88            Log.info("exit generate test report.")
89
90    def get_strftime(self, stamp_time):
91        return stamp_time.strftime(ReportConstants.time_format)
92
93    def get_now_strftime(self):
94        return get_cst_time().strftime(ReportConstants.time_format)
95
96    def report_normal_type(self, testsuites, test_results, test_name):
97        tests_total = 0
98        tests_error = 0
99        for result_info in test_results:
100
101            tests_total += 1
102            case_error = 0
103            case_result = "true"
104            result = result_info.get('result')
105            if result != RunResult.PASSED:
106                tests_error += 1
107                case_error += 1
108                case_result = "false"
109            case_name = result_info.get('case_name')
110            case_start_time = result_info.get('start_time').timestamp()
111            case_end_time = result_info.get('end_time').timestamp()
112            error = result_info.get('error')
113            report = result_info.get("report", "")
114            case_time = case_end_time - case_start_time
115
116            testcase = ElementTree.Element('testcase')
117            testcase.set("name", case_name)
118            testcase.set("status", 'run')
119            testcase.set("classname", case_name)
120            testcase.set("level", "")
121            testcase.set("result", case_result)
122            testcase.set("result_kind", result)
123            testcase.set("message", error)
124            testcase.set("report", report)
125            # 用例测试结果的拓展内容
126            result_content = result_info.get('result_content')
127            if result_content:
128                testcase.set("result_content", literal_eval(str(result_content)))
129
130            testsuite = ElementTree.Element('testsuite')
131            testsuite.set("modulename", test_name)
132            testsuite.set("name", case_name)
133            testsuite.set("tests", str(1))
134            testsuite.set("failures", str(case_error))
135            testsuite.set("disabled", '0')
136            testsuite.set("time", "{:.2f}".format(case_time))
137            testsuite.set("result", case_result)
138            testsuite.set("result_kind", result)
139            testsuite.set("report", report)
140
141            testsuite.append(testcase)
142            testsuites.append(testsuite)
143        return tests_total, tests_error
144
145    def report_xts_type(self, testsuites, test_results):
146        tests_total = 0
147        tests_error = 0
148        test_suites = {}
149        for result_info in test_results:
150
151            tests_total += 1
152            case_error = 0
153            case_result = "true"
154            result = result_info.get('result')
155            if result != RunResult.PASSED:
156                tests_error += 1
157                case_error += 1
158                case_result = "false"
159            case_info = result_info.get('case_name').split("#")
160            case_name = case_info[1]
161            module_name = case_info[0]
162            case_start_time = result_info.get('start_time').timestamp()
163            case_end_time = result_info.get('end_time').timestamp()
164            error = result_info.get('error')
165            report = result_info.get("report", "")
166            case_time = case_end_time - case_start_time
167
168            testcase = ElementTree.Element('testcase')
169            testcase.set("name", case_name)
170            testcase.set("status", 'run')
171            testcase.set("classname", module_name)
172            testcase.set("level", "")
173            testcase.set("result", case_result)
174            testcase.set("result_kind", result)
175            testcase.set("message", error)
176            testcase.set("report", report)
177
178            testsuite = ElementTree.Element('testsuite')
179            testsuite.set("modulename", module_name)
180            testsuite.set("name", module_name)
181            testsuite.set("tests", str(1))
182            testsuite.set("disabled", '0')
183            testsuite.set("time", "{:.2f}".format(case_time))
184            testsuite.set("report", report)
185            if module_name not in test_suites:
186                test_suites[module_name] = {"test_suite": testsuite, "tests": 0, "failures": 0}
187                testsuites.append(testsuite)
188            test_suites[module_name]["test_suite"].append(testcase)
189            test_suites[module_name]["tests"] += 1
190            tests = test_suites[module_name]["tests"]
191            if case_result == "false":
192                test_suites[module_name]["failures"] += 1
193            failures = test_suites[module_name]["failures"]
194            test_suites[module_name]["test_suite"].set("tests", str(tests))
195            test_suites[module_name]["test_suite"].set("failures", str(failures))
196        return tests_total, tests_error
197