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 xml.dom import minidom 21from xml.etree import ElementTree 22 23from xdevice import get_cst_time, FilePermission 24from devicetest.core.constants import RunResult 25from devicetest.core.exception import DeviceTestError 26from devicetest.error import ErrorMessage 27from devicetest.log.logger import DeviceTestLog as Log 28 29 30class ReportConstants: 31 time_format = "%Y-%m-%d %H:%M:%S" 32 report_xml = "report.xml" 33 34 35class ReportHandler: 36 37 def __init__(self, report_path): 38 self.report_path = report_path 39 self.test_results = [] 40 41 def generate_test_report(self, test_runner, _test_results=None, report_type="normal"): 42 if os.path.exists(self.report_path): 43 root = ElementTree.parse(self.report_path).getroot() 44 return self.report_path, ElementTree.tostring(root).decode() 45 try: 46 Log.info("start generate test report.") 47 test_results = _test_results or test_runner.test_results 48 49 start_time = test_runner.start_time 50 51 testsuites = ElementTree.Element('testsuites') 52 53 test_name = test_runner.configs.get("test_name") 54 if test_name is not None: 55 testsuites.set("name", test_name) 56 else: 57 testsuites.set("name", ReportConstants.report_xml) 58 59 if report_type == "xts": 60 tests_total, tests_error = self.report_xts_type(testsuites, test_results) 61 else: 62 tests_total, tests_error = self.report_normal_type(testsuites, test_results, test_name) 63 64 testsuites.set("tests", str(tests_total)) 65 testsuites.set("failures", str(tests_error)) 66 testsuites.set("disabled", '') 67 testsuites.set("errors", "") 68 testsuites.set("starttime", self.get_strftime(start_time)) 69 testsuites.set("endtime", self.get_now_strftime()) 70 testsuites.set("report_version", "1.0") 71 72 os.makedirs(os.path.dirname(self.report_path), exist_ok=True) 73 xml_content = ElementTree.tostring(testsuites).decode() 74 xml_pretty = minidom.parseString(xml_content).toprettyxml(indent=" ") 75 76 result_fd = os.open(self.report_path, os.O_CREAT | os.O_WRONLY | os.O_TRUNC, FilePermission.mode_644) 77 with os.fdopen(result_fd, mode="w", encoding="utf-8") as result_file: 78 result_file.write(xml_pretty) 79 return self.report_path, xml_content 80 81 except Exception as error: 82 err_msg = ErrorMessage.Common.Code_0201003 83 Log.error(err_msg, is_traceback=True) 84 raise DeviceTestError(err_msg) from error 85 86 finally: 87 Log.info("exit generate test report.") 88 89 def get_strftime(self, stamp_time): 90 return stamp_time.strftime(ReportConstants.time_format) 91 92 def get_now_strftime(self): 93 return get_cst_time().strftime(ReportConstants.time_format) 94 95 def report_normal_type(self, testsuites, test_results, test_name): 96 tests_total = 0 97 tests_error = 0 98 for result_info in test_results: 99 100 tests_total += 1 101 case_error = 0 102 case_result = "true" 103 result = result_info.get('result') 104 if result != RunResult.PASSED: 105 tests_error += 1 106 case_error += 1 107 case_result = "false" 108 case_name = result_info.get('case_name') 109 case_start_time = result_info.get('start_time').timestamp() 110 case_end_time = result_info.get('end_time').timestamp() 111 error = result_info.get('error') 112 report = result_info.get("report", "") 113 case_time = case_end_time - case_start_time 114 115 testcase = ElementTree.Element('testcase') 116 testcase.set("name", case_name) 117 testcase.set("status", 'run') 118 testcase.set("classname", case_name) 119 testcase.set("level", "") 120 testcase.set("result", case_result) 121 testcase.set("result_kind", result) 122 testcase.set("message", error) 123 testcase.set("report", report) 124 # 用例测试结果的拓展内容 125 result_content = result_info.get('result_content') 126 if result_content: 127 testcase.set("result_content", f"<![CDATA[{result_content}]]>") 128 129 testsuite = ElementTree.Element('testsuite') 130 testsuite.set("modulename", test_name) 131 testsuite.set("name", case_name) 132 testsuite.set("tests", str(1)) 133 testsuite.set("failures", str(case_error)) 134 testsuite.set("disabled", '0') 135 testsuite.set("time", "{:.2f}".format(case_time)) 136 testsuite.set("result", case_result) 137 testsuite.set("result_kind", result) 138 testsuite.set("report", report) 139 140 testsuite.append(testcase) 141 testsuites.append(testsuite) 142 return tests_total, tests_error 143 144 def report_xts_type(self, testsuites, test_results): 145 tests_total = 0 146 tests_error = 0 147 test_suites = {} 148 for result_info in test_results: 149 150 tests_total += 1 151 case_error = 0 152 case_result = "true" 153 result = result_info.get('result') 154 if result != RunResult.PASSED: 155 tests_error += 1 156 case_error += 1 157 case_result = "false" 158 case_info = result_info.get('case_name').split("#") 159 case_name = case_info[1] 160 module_name = case_info[0] 161 case_start_time = result_info.get('start_time').timestamp() 162 case_end_time = result_info.get('end_time').timestamp() 163 error = result_info.get('error') 164 report = result_info.get("report", "") 165 case_time = case_end_time - case_start_time 166 167 testcase = ElementTree.Element('testcase') 168 testcase.set("name", case_name) 169 testcase.set("status", 'run') 170 testcase.set("classname", module_name) 171 testcase.set("level", "") 172 testcase.set("result", case_result) 173 testcase.set("result_kind", result) 174 testcase.set("message", error) 175 testcase.set("report", report) 176 177 testsuite = ElementTree.Element('testsuite') 178 testsuite.set("modulename", module_name) 179 testsuite.set("name", module_name) 180 testsuite.set("tests", str(1)) 181 testsuite.set("disabled", '0') 182 testsuite.set("time", "{:.2f}".format(case_time)) 183 testsuite.set("report", report) 184 if module_name not in test_suites: 185 test_suites[module_name] = {"test_suite": testsuite, "tests": 0, "failures": 0} 186 testsuites.append(testsuite) 187 test_suites[module_name]["test_suite"].append(testcase) 188 test_suites[module_name]["tests"] += 1 189 tests = test_suites[module_name]["tests"] 190 if case_result == "false": 191 test_suites[module_name]["failures"] += 1 192 failures = test_suites[module_name]["failures"] 193 test_suites[module_name]["test_suite"].set("tests", str(tests)) 194 test_suites[module_name]["test_suite"].set("failures", str(failures)) 195 return tests_total, tests_error 196