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