1#!/usr/bin/env python3 2# coding: utf-8 3 4""" 5Copyright (c) 2023 Huawei Device Co., Ltd. 6Licensed under the Apache License, Version 2.0 (the "License"); 7you may not use this file except in compliance with the License. 8You may obtain a copy of the License at 9 10 http://www.apache.org/licenses/LICENSE-2.0 11 12Unless required by applicable law or agreed to in writing, software 13distributed under the License is distributed on an "AS IS" BASIS, 14WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15See the License for the specific language governing permissions and 16limitations under the License. 17 18Description: output test results 19""" 20 21import copy 22import logging 23import os 24import time 25import zipfile 26 27import pandas 28 29import options 30 31 32incremetal_compile_tests = ["no_change", 33 "add_oneline", 34 "add_file", 35 "add_nonexistent_file", 36 "delete_file", 37 "reverse_hap_mode", 38 "change_module_name" 39 ] 40 41other_tests = ["binary_consistency", 42 "break_continue_compile", 43 "compile_with_error", 44 "compile_with_exceed_length", 45 "ohos_test" 46 ] 47 48 49class TestResult: 50 def __init__(self): 51 self.passed = [] 52 self.failed = [] 53 self.time = 0.0 54 55 56def print_result(test_result, test_tasks): 57 logging.info("========================================") 58 logging.info("Test finished. The result is as following:") 59 logging.info("=====> Summary") 60 logging.info("Total test number: %s, took time: %.3f s", 61 len(test_tasks), test_result.time) 62 logging.info("Passed test number: %s", len(test_result.passed)) 63 logging.info("Failed test number: %s", len(test_result.failed)) 64 65 logging.info("=====> Detail Information") 66 logging.info("-----") 67 idx = 1 68 for task in test_tasks: 69 logging.info("task index: %d", idx) 70 idx = idx + 1 71 logging.info("task name: %s", task.name) 72 logging.info("task type: %s", task.type) 73 # print full compile result 74 logging.info("--full compilation result:") 75 logging.info("debug: %s, abc_size(byte) %s, time(s) %s, error message: %s", 76 task.full_compilation_info.debug_info.result, 77 task.full_compilation_info.debug_info.abc_size, 78 task.full_compilation_info.debug_info.time, 79 task.full_compilation_info.debug_info.error_message) 80 logging.info("release: %s, abc_size(byte) %s, time(s) %s, error message: %s", 81 task.full_compilation_info.release_info.result, 82 task.full_compilation_info.release_info.abc_size, 83 task.full_compilation_info.release_info.time, 84 task.full_compilation_info.debug_info.error_message) 85 86 # print incremental compile result 87 logging.info("--incremental compilation result:") 88 for inc_task in task.incre_compilation_info.values(): 89 logging.info("incre test: %s", inc_task.name) 90 logging.info("debug: %s, abc_size(byte) %s, time(s) %s, error message: %s", 91 inc_task.debug_info.result, 92 inc_task.debug_info.abc_size, 93 inc_task.debug_info.time, 94 inc_task.debug_info.error_message) 95 logging.info("release: %s, abc_size(byte) %s, time(s) %s, error message: %s", 96 inc_task.release_info.result, 97 inc_task.release_info.abc_size, 98 inc_task.release_info.time, 99 inc_task.release_info.error_message) 100 101 # print other tests result 102 for name, task_info in task.other_tests.items(): 103 logging.info("--test name: %s", name) 104 logging.info("result: %s, error message: %s", 105 task_info.result, 106 task_info.error_message) 107 108 logging.info("-----") 109 logging.info("========================================") 110 111 112def is_full_compilation_passed(task_info): 113 if not options.arguments.compile_mode in ['all', 'full']: 114 return True 115 116 passed_debug = True 117 passed_release = True 118 119 if options.arguments.hap_mode in ['all', 'release']: 120 passed_release = task_info.release_info.result == options.TaskResult.passed 121 if options.arguments.hap_mode == ['all', 'debug']: 122 passed_debug = task_info.debug_info.result == options.TaskResult.passed 123 124 return passed_debug and passed_release 125 126 127def is_incremental_compilation_passed(task_info): 128 if not options.arguments.compile_mode in ['all', 'incremental']: 129 return True 130 131 if len(task_info) == 0: 132 return False 133 134 passed_debug = True 135 passed_release = True 136 for inc_task in task_info.values(): 137 if options.arguments.hap_mode in ['all', 'release']: 138 passed_release = passed_release and inc_task.release_info.result == options.TaskResult.passed 139 if options.arguments.hap_mode == ['all', 'debug']: 140 passed_debug = passed_debug and inc_task.debug_info.result == options.TaskResult.passed 141 142 return passed_debug and passed_release 143 144 145def is_task_passed(task): 146 passed = is_full_compilation_passed(task.full_compilation_info) and \ 147 is_incremental_compilation_passed(task.incre_compilation_info) 148 149 for test in task.other_tests.values(): 150 passed = passed and (test.result == options.TaskResult.passed) 151 152 return passed 153 154 155def collect_result(test_result, test_tasks, start_time): 156 for task in test_tasks: 157 if not is_task_passed(task): 158 test_result.failed.append(task) 159 else: 160 test_result.passed.append(task) 161 162 end_time = time.time() 163 test_result.time = round(end_time - start_time, 3) 164 165 166def get_result_symbol(result_type): 167 if result_type == options.TaskResult.passed: 168 return '√' 169 elif result_type == options.TaskResult.failed: 170 return '×' 171 else: 172 return '-' 173 174 175def generate_summary_data(test_result, test_tasks): 176 # collect summary data 177 passed_task_name_list = [] 178 for task in test_result.passed: 179 passed_task_name_list.append(task.name) 180 failed_task_name_list = [] 181 for task in test_result.failed: 182 failed_task_name_list.append(task.name) 183 184 summary_data = { 185 'Total Test Number': len(test_tasks), 186 'Passed Test Number': len(test_result.passed), 187 'Failed Test Number': len(test_result.failed), 188 'Passed Tests': ','.join(passed_task_name_list), 189 'Failed Tests': ','.join(failed_task_name_list), 190 'Test Took Time(s)': test_result.time 191 } 192 193 return summary_data 194 195 196def generate_detail_data(test_tasks): 197 time_size_data = [] 198 result_data = [] 199 200 idx = 0 201 for task in test_tasks: 202 idx += 1 203 task_time_size_data = { 204 'Task Index': idx, 205 'Task Name': task.name 206 } 207 task_result_data = copy.deepcopy(task_time_size_data) 208 task_result_data['Task Type'] = ','.join(task.type) 209 210 full_compilation_debug, full_compilation_release = get_full_build_test_result(task, task_result_data, 211 task_time_size_data) 212 get_incremental_build_test_result(task, task_result_data, task_time_size_data) 213 214 get_other_test_result(task, task_result_data) 215 216 task_time_size_data['[Abc Size(byte)]\n[Debug]'] = full_compilation_debug.abc_size 217 task_time_size_data['[Abc Size(byte)]\n[Release]'] = full_compilation_release.abc_size 218 time_size_data.append(task_time_size_data) 219 result_data.append(task_result_data) 220 221 detail_data = { 222 'result_data': result_data, 223 'time_size_data': time_size_data 224 } 225 return detail_data 226 227 228def get_full_build_test_result(task, task_result_data, task_time_size_data): 229 full_compilation_debug = task.full_compilation_info.debug_info 230 full_compilation_release = task.full_compilation_info.release_info 231 task_time_size_data[ 232 '[Full Compilation]\n[Debug]\n[Compilation Time(s)]'] = full_compilation_debug.time 233 task_time_size_data[ 234 '[Full Compilation]\n[Release]\n[Compilation Time(s)]'] = full_compilation_release.time 235 task_result_data['[Debug]'] = get_result_symbol( 236 full_compilation_debug.result) 237 task_result_data['[Debug-runtime]'] = get_result_symbol( 238 full_compilation_debug.runtime_result) 239 task_result_data['[Release]'] = get_result_symbol( 240 full_compilation_release.result) 241 task_result_data['[Release-runtime]'] = get_result_symbol( 242 full_compilation_release.runtime_result) 243 return full_compilation_debug, full_compilation_release 244 245 246def get_incremental_build_test_result(task, task_result_data, task_time_size_data): 247 for test in incremetal_compile_tests: 248 debug_result = options.TaskResult.undefind 249 debug_runtime_result = options.TaskResult.undefind 250 release_result = options.TaskResult.undefind 251 release_runtime_result = options.TaskResult.undefind 252 if test in task.incre_compilation_info.keys(): 253 inc_task_info = task.incre_compilation_info[test] 254 debug_result = inc_task_info.debug_info.result 255 debug_runtime_result = inc_task_info.debug_info.runtime_result 256 release_result = inc_task_info.release_info.result 257 release_runtime_result = inc_task_info.release_info.runtime_result 258 task_result_data[f'[Debug]\n{test}'] = get_result_symbol( 259 debug_result) 260 task_result_data[f'[Debug-runtime]\n{test}'] = get_result_symbol( 261 debug_runtime_result) 262 task_result_data[f'[Release]\n{test}'] = get_result_symbol( 263 release_result) 264 task_result_data[f'[Release-runtime]\n{test}'] = get_result_symbol( 265 release_runtime_result) 266 267 if test == 'add_oneline': 268 debug_test_time = 0 269 release_test_time = 0 270 if test in task.incre_compilation_info.keys(): 271 inc_task_info = task.incre_compilation_info[test] 272 debug_test_time = inc_task_info.debug_info.time 273 release_test_time = inc_task_info.release_info.time 274 275 task_time_size_data[ 276 '[Incremental Compilation]\n[Debug]\n[Compilation Time(s)]'] = debug_test_time 277 task_time_size_data[ 278 '[Incremental Compilation]\n[Release]\n[Compilation Time(s)]'] = release_test_time 279 280 281def get_other_test_result(task, task_result_data): 282 for test in other_tests: 283 result = options.TaskResult.undefind 284 runtime_result = options.TaskResult.undefind 285 if test in task.other_tests.keys(): 286 task_info = task.other_tests[test] 287 result = task_info.result 288 runtime_result = task_info.runtime_result 289 task_result_data[f'{test}'] = get_result_symbol(result) 290 task_result_data[f'{test}-runtime'] = get_result_symbol(runtime_result) 291 292 293def rotate_data(df): 294 num_rows, num_cols = df.shape 295 rotated_df = pandas.DataFrame(columns=range(num_rows), index=range(num_cols)) 296 for i in range(num_rows): 297 for j in range(num_cols): 298 rotated_df.iloc[j, i] = df.iloc[i, j] 299 return rotated_df 300 301 302def get_merge_data(rotated_df): 303 data = rotated_df.iloc[3:, :].values.tolist() 304 merged_data = [] 305 for i in range(0, len(data) - 1, 2): 306 row = [value for sublist in zip(data[i], data[i + 1]) for value in sublist] 307 merged_data.append(row) 308 return merged_data 309 310 311def get_result_table_content(result_df_rotate): 312 merged_data = get_merge_data(result_df_rotate) 313 content = '<tr><th colspan="2" rowspan="2">Full Compilation</th><th>[Debug]</th>' + \ 314 ''.join( 315 [f'<th>{column}</th>' for column in merged_data[0]]) + '</tr>' 316 content += '<tr><th>[Release]</th>' + \ 317 ''.join( 318 [f'<th>{column}</th>' for column in merged_data[1]]) + '</tr>' 319 content += f'<tr><th rowspan={len(incremetal_compile_tests) * 2}>Incremental Compilation</th>' 320 start_index = 2 321 for index, item in enumerate(incremetal_compile_tests): 322 incre_debug_result = ''.join([f'<th>{column}</th>' for column in merged_data[start_index]]) 323 content = ''.join([content, f'<th rowspan="2">{item}</th><th>[Debug]</th>', incre_debug_result, '</tr>']) 324 incre_release_result = ''.join([f'<th>{column}</th>' for column in merged_data[start_index+1]]) 325 content = ''.join([content, '<tr><th>[Release]</th>', incre_release_result, '</tr>']) 326 start_index = start_index + 2 327 content += f'<tr><th colspan=2 rowspan={len(other_tests) * 2}>Other Tests</th>' 328 for index, item in enumerate(other_tests): 329 other_result = ''.join([f'<th>{column}</th>' for column in merged_data[start_index]]) 330 content = ''.join([content, f'<th>{item}</th>', other_result, '</tr>']) 331 start_index = start_index + 1 332 333 return content 334 335 336def generate_data_html(summary_data, detail_data): 337 # summary table 338 key_value_pairs = [ 339 f'<tr><td>{key}</td><td>{value}</td></tr>' for key, value in summary_data.items()] 340 summary_table_content = ''.join(key_value_pairs) 341 summary_table = f'<table id=sdk>{summary_table_content}</table>' 342 343 # time and size table 344 time_size_data = detail_data.get('time_size_data') 345 time_size_df = pandas.DataFrame(time_size_data) 346 347 time_size_table_header = '<tr>' + \ 348 ''.join( 349 [f'<th rowspan="2">{column}</th>' for column in time_size_df.columns[:2]]) 350 time_size_table_header += '<th colspan="2">Full Compilation Time(s)</th>' + \ 351 f'<th colspan="2">Incremental Compilation Time(s)</th>' + \ 352 f'<th colspan="2">Abc Size(byte)</th></tr>' 353 time_size_table_sub_header = '<tr>' + \ 354 f'<th>[Debug]</th><th>[Release]</th>' * 3 + '</tr>' 355 356 time_size_table_content = ''.join([ 357 '<tr>' + ''.join([f'<td>{value}</td>' for _, 358 value in row.items()]) + '</tr>' 359 for _, row in time_size_df.iterrows() 360 ]) 361 time_size_table = f'<table id=sdk> \ 362 {time_size_table_header}{time_size_table_sub_header}{time_size_table_content}</table>' 363 364 # result table 365 result_data = detail_data.get('result_data') 366 result_df = pandas.DataFrame(result_data) 367 result_df_rotate = rotate_data(result_df) 368 369 result_table_header = '<tr><th colspan="3">Task Index</th>' + \ 370 ''.join( 371 [f'<th colspan="2">{column}</th>' for column in result_df.iloc[:, 0].tolist()]) + '</tr>' 372 result_table_header += '<tr><th colspan="3">Task Name</th>' + \ 373 ''.join( 374 [f'<th colspan="2">{column}</th>' for column in result_df.iloc[:, 1].tolist()]) + '</tr>' 375 result_table_header += '<tr><th colspan="3">Task Type</th>' + \ 376 ''.join( 377 [f'<th colspan="2">{column}</th>' for column in result_df.iloc[:, 2].tolist()]) + '</tr>' 378 result_table_sub_header = f"<tr><th colspan=3>Build && Run </th>{'<th>[build]</th><th>[runtime]</th>' * result_df.shape[0]}</tr>" 379 result_table_content = get_result_table_content(result_df_rotate) 380 381 result_table = f'<table id=sdk> \ 382 {result_table_header}{result_table_sub_header}{result_table_content}</table>' 383 384 return summary_table, time_size_table, result_table 385 386 387def get_html_style(): 388 html_style = ''' 389 #sdk body { 390 font-family: Arial, sans-serif; 391 margin: 20px; 392 } 393 #sdk h2 { 394 color: #333; 395 } 396 #sdk { 397 border-collapse: collapse; 398 width: 100%; 399 margin-bottom: 20px; 400 } 401 #sdk th, #sdk td { 402 padding: 8px; 403 border: 1px solid #ddd; 404 } 405 #sdk th { 406 background-color: #f2f2f2; 407 font-weight: bold; 408 } 409 #sdk tr:nth-child(odd) { 410 background-color: #f9f9f9; 411 } 412 ''' 413 return html_style 414 415 416def generate_report_html(summary_data, detail_data): 417 [summary_table, time_size_table, result_table] = generate_data_html( 418 summary_data, detail_data) 419 html_style = get_html_style() 420 421 html_content = f''' 422 <html> 423 <head> 424 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> 425 <style> 426 {html_style} 427 </style> 428 </head> 429 <body> 430 <h2>SDK Test Results</h2> 431 <h3>Summary</h3> 432 {summary_table} 433 <h3>Detail Information</h3> 434 <h4>Test Result</h4> 435 {result_table} 436 <h4>Compilation Time And Abc Size</h4> 437 {time_size_table} 438 <p> 439 Notes:<br> 440 1. Incremental compilation time refers to add-one line incremental compile.<br> 441 2. For details compile output or error message during compile, please refer to attachment of log file.<br> 442 3. For sdk commit tags, please refer to attachment of manifest file(to be added). 443 </p> 444 </body> 445 </html> 446 ''' 447 448 daily_report_file = options.configs.get('output_html_file') 449 with open(daily_report_file, 'w', encoding='utf-8') as report: 450 report.write(html_content) 451 452 453def generate_log_file(): 454 logger = logging.getLogger() 455 if not hasattr(logger.handlers[0], 'baseFilename'): 456 return 457 log_file = logger.handlers[0].baseFilename 458 logger.handlers[0].close() 459 output_log_file = options.configs.get('log_file') 460 os.rename(log_file, output_log_file) 461 462 463def generate_result_reports(test_result, test_tasks): 464 summary_data = generate_summary_data(test_result, test_tasks) 465 detail_data = generate_detail_data(test_tasks) 466 generate_report_html(summary_data, detail_data) 467 generate_log_file() 468 469 470def process_test_result(test_tasks, start_time): 471 test_result = TestResult() 472 473 collect_result(test_result, test_tasks, start_time) 474 print_result(test_result, test_tasks) 475 generate_result_reports(test_result, test_tasks) 476 477