1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3# Copyright (C) 2025 Huawei Device Co., Ltd. 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16""" 17运行环境: python 3.10+ 18测试方法: 19方法1、使用pytest框架执行 20请见测试用例根目录test\scripts下readme.md中的执行稳定性用例小节 21方法2、直接执行 22进入test\scripts目录,执行python .\testModule\stability_utils.py 23 24测试范围 25hdc shell 26hdc file send/recv 27hdc fport 文件传输 28多session场景测试(hdc tmode + hdc tconn) 29 30注意: 311、当前仅支持一台设备的压力测试 322、如果异常退出,请执行 hdc -t [usb_connect_key] tmode port close,关闭网络接口通道 33执行 hdc list targets查看是否还有网络连接,如果有请执行hdc tconn 127.0.0.1:port -remove断开 34执行hdc fport ls查看是否还有端口转发的规则,如果有请执行hdc fport rm tcp:xxxxx tcp:9999删除转发 35""" 36 37import subprocess 38import os 39import logging 40import logging.config 41import time 42from datetime import datetime 43from enum import Enum 44import threading 45import uuid 46import hashlib 47import queue 48from utils import server_loop 49from utils import client_get_file 50from utils import get_local_md5 51 52STABILITY_TEST_VERSION = "v1.0.0" 53 54TIME_REPORT_FORMAT = '%Y-%m-%d %H:%M:%S' 55TIME_FILE_FORMAT = '%Y%m%d_%H%M%S' 56TEST_RESULT = False 57 58WORK_DIR = os.getcwd() 59 60# 消息队列中使用到的key名称 61# 报告公共信息key名称 62REPORT_PUBLIC_INFO_NAME = "public_info" 63# 报告公共信息中的错误信息key名称 64TASK_ERROR_KEY_NAME = "task_error" 65 66# 资源文件相对路径 67RESOURCE_RELATIVE_PATH = "resource" 68# 报告文件相对路径 69REPORTS_RELATIVE_PATH = "reports" 70# 缓存查询到的设备sn 71DEVICE_LIST = [] 72# 记录当前启动的session类信息 73SESSION_LIST = [] 74# 释放资源命令列表 75RELEASE_TCONN_CMD_LIST = [] 76RELEASE_FPORT_CMD_LIST = [] 77 78RESOURCE_PATH = os.path.join(WORK_DIR, RESOURCE_RELATIVE_PATH) 79 80EXIT_EVENT = threading.Event() 81 82TEST_THREAD_MSG_QUEUE = queue.Queue() 83 84 85def get_time_str(fmt=TIME_REPORT_FORMAT, need_ms=False): 86 now_time = datetime.now() 87 if need_ms is False: 88 return now_time.strftime(fmt) 89 else: 90 return f"{now_time.strftime(fmt)}.{now_time.microsecond // 1000:03d}" 91 92 93TEST_FILE_TIME_STR = get_time_str(TIME_FILE_FORMAT) 94LOG_FILE_NAME = os.path.join(REPORTS_RELATIVE_PATH, f"hdc_stability_test_{TEST_FILE_TIME_STR}.log") 95REPORT_FILE_NAME = os.path.join(REPORTS_RELATIVE_PATH, f"hdc_stability_test_report_{TEST_FILE_TIME_STR}.html") 96logger = None 97 98# 配置日志记录 99logging_config_info = { 100 'version': 1, 101 'formatters': { 102 'format1': { 103 'format': '[%(asctime)s %(name)s %(funcName)s %(lineno)d %(levelname)s][%(process)d][%(thread)d]' 104 '[%(threadName)s]%(message)s' 105 }, 106 }, 107 'handlers': { 108 'console_handle': { 109 'level': 'DEBUG', 110 'formatter': 'format1', 111 'class': 'logging.StreamHandler', 112 'stream': 'ext://sys.stdout', 113 }, 114 'file_handle': { 115 'level': 'DEBUG', 116 'formatter': 'format1', 117 'class': 'logging.FileHandler', 118 'filename': LOG_FILE_NAME, 119 }, 120 }, 121 'loggers': { 122 '': { 123 'handlers': ['console_handle', 'file_handle'], 124 'level': 'DEBUG', 125 }, 126 }, 127} 128 129 130class TestType(Enum): 131 SHELL = 1 132 FILE_SEND = 2 133 FILE_RECV = 3 134 FPORT_TRANS_FILE = 4 135 136 137# 当前测试任务并行运行的数量 138TASK_COUNT_DEFAULT = 5 139# 循环测试次数 140LOOP_COUNT_DEFAULT = 20 141# 每个循环结束的等待时间,单位秒 142LOOP_DELAY_S_DEFAULT = 0.01 143 144# 进行多session测试时,启动多个端口转发到设备侧的tmode监听的端口,电脑端开启的端口列表 145# 参考端口配置:12345, 12346, 12347, 12348, 12349,需要几个session,可以复制几个放入下面的MUTIL_SESSION_TEST_PC_PORT_LIST中 146MUTIL_SESSION_TEST_PC_PORT_LIST = [] 147# 多session测试, 通过tmode命令,设备端切换tcp模式,监听的端口号 148DEVICE_LISTEN_PORT = 9999 149 150# usb session 测试项配置 151""" 152配置项包括如下: 153 { 154 "test_type": TestType.SHELL, 155 "loop_count": LOOP_COUNT_DEFAULT, 156 "loop_delay_s": LOOP_DELAY_S_DEFAULT, 157 "task_count": TASK_COUNT_DEFAULT, 158 "cmd": "shell ls", 159 "expected_results": "data", 160 }, 161 { 162 "test_type": TestType.FILE_SEND, 163 "loop_count": LOOP_COUNT_DEFAULT, 164 "loop_delay_s": LOOP_DELAY_S_DEFAULT, 165 "task_count": TASK_COUNT_DEFAULT, 166 "cmd": "file send", 167 "local": "medium", 168 "remote": "/data/medium", 169 "expected_results": "FileTransfer finish", 170 }, 171 { 172 "test_type": TestType.FILE_RECV, 173 "loop_count": LOOP_COUNT_DEFAULT, 174 "loop_delay_s": LOOP_DELAY_S_DEFAULT, 175 "task_count": TASK_COUNT_DEFAULT, 176 "cmd": "file recv", 177 "original_file": "medium", 178 "local": "medium", 179 "remote": "/data/medium", 180 "expected_results": "FileTransfer finish", 181 }, 182 { 183 "test_type": TestType.FPORT_TRANS_FILE, 184 "port_info": [ 185 { 186 "client_connect_port": 22345, 187 "daemon_transmit_port": 11081, 188 "server_listen_port": 18000, 189 }, 190 { 191 "client_connect_port": 22346, 192 "daemon_transmit_port": 11082, 193 "server_listen_port": 18001, 194 }, 195 ], 196 "loop_count": LOOP_COUNT_DEFAULT, 197 "loop_delay_s": LOOP_DELAY_S_DEFAULT, 198 "original_file": "medium", 199 }, 200""" 201USB_SESSION_TEST_CONFIG = [ 202 { 203 "test_type": TestType.SHELL, 204 "loop_count": LOOP_COUNT_DEFAULT, 205 "loop_delay_s": LOOP_DELAY_S_DEFAULT, 206 "task_count": TASK_COUNT_DEFAULT, 207 "cmd": "shell ls", 208 "expected_results": "data", 209 }, 210 { 211 "test_type": TestType.FILE_SEND, 212 "loop_count": LOOP_COUNT_DEFAULT, 213 "loop_delay_s": LOOP_DELAY_S_DEFAULT, 214 "task_count": TASK_COUNT_DEFAULT, 215 "cmd": "file send", 216 "local": "medium", 217 "remote": "/data/medium", 218 "expected_results": "FileTransfer finish", 219 }, 220 { 221 "test_type": TestType.FILE_RECV, 222 "loop_count": LOOP_COUNT_DEFAULT, 223 "loop_delay_s": LOOP_DELAY_S_DEFAULT, 224 "task_count": TASK_COUNT_DEFAULT, 225 "cmd": "file recv", 226 "original_file": "medium", 227 "local": "medium", 228 "remote": "/data/medium", 229 "expected_results": "FileTransfer finish", 230 }, 231] 232 233# tcp session 测试项配置 234""" 235配置项包括如下: 236 { 237 "test_type": TestType.SHELL, 238 "loop_count": LOOP_COUNT_DEFAULT, 239 "loop_delay_s": LOOP_DELAY_S_DEFAULT, 240 "task_count": TASK_COUNT_DEFAULT, 241 "cmd": "shell ls", 242 "expected_results": "data", 243 }, 244 { 245 "test_type": TestType.FILE_SEND, 246 "loop_count": LOOP_COUNT_DEFAULT, 247 "loop_delay_s": LOOP_DELAY_S_DEFAULT, 248 "task_count": TASK_COUNT_DEFAULT, 249 "cmd": "file send", 250 "local": "medium", 251 "remote": "/data/medium", 252 "expected_results": "FileTransfer finish", 253 }, 254 { 255 "test_type": TestType.FILE_RECV, 256 "loop_count": LOOP_COUNT_DEFAULT, 257 "loop_delay_s": LOOP_DELAY_S_DEFAULT, 258 "task_count": TASK_COUNT_DEFAULT, 259 "cmd": "file recv", 260 "original_file": "medium", 261 "local": "medium", 262 "remote": "/data/medium", 263 "expected_results": "FileTransfer finish", 264 }, 265""" 266TCP_SESSION_TEST_CONFIG = [ 267 { 268 "test_type": TestType.SHELL, 269 "loop_count": LOOP_COUNT_DEFAULT, 270 "loop_delay_s": LOOP_DELAY_S_DEFAULT, 271 "task_count": TASK_COUNT_DEFAULT, 272 "cmd": "shell ls", 273 "expected_results": "data", 274 }, 275 { 276 "test_type": TestType.FILE_SEND, 277 "loop_count": LOOP_COUNT_DEFAULT, 278 "loop_delay_s": LOOP_DELAY_S_DEFAULT, 279 "task_count": TASK_COUNT_DEFAULT, 280 "cmd": "file send", 281 "local": "medium", 282 "remote": "/data/medium", 283 "expected_results": "FileTransfer finish", 284 }, 285 { 286 "test_type": TestType.FILE_RECV, 287 "loop_count": LOOP_COUNT_DEFAULT, 288 "loop_delay_s": LOOP_DELAY_S_DEFAULT, 289 "task_count": TASK_COUNT_DEFAULT, 290 "cmd": "file recv", 291 "original_file": "medium", 292 "local": "medium", 293 "remote": "/data/medium", 294 "expected_results": "FileTransfer finish", 295 }, 296] 297 298HTML_HEAD = """ 299<head> 300<meta charset="UTF-8"> 301<title>HDC稳定性测试报告</title> 302<style> 303 body { font-family: Arial, sans-serif; margin: 0; padding: 20px; } 304 table { border-collapse: collapse; width: 100%; } 305 th { background-color: #f0f0f0; } 306 th, td { border: 1px solid #ddd; padding: 6px; text-align: left; } 307 tr:nth-child(even) { background-color: #f9f9f9; } 308 .report { 309 max-width: 1200px; 310 margin: 0 auto; 311 background-color: #fafafa; 312 padding: 20px; 313 border-radius: 5px; 314 box-shadow: 0 0 10px rgba(0, 0, 0, 0.09); 315 color: #333; 316 } 317 .summary { 318 margin-bottom: 10px; 319 padding: 20px; 320 background-color: #fff; 321 border-radius: 5px; 322 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.05); 323 } 324 .summary-header { margin-bottom: 10px; padding-bottom: 5px; } 325 .summary-row { 326 display: flex; 327 justify-content: space-between; 328 margin-bottom: 2px; 329 flex-wrap: wrap; 330 } 331 .summary-item { 332 flex: 1; 333 min-width: 300px; 334 margin: 4px; 335 padding: 7px; 336 background-color: #f2f2f2; 337 border-radius: 4px; 338 } 339 .summary-item-key { 340 font-weight: bold; 341 margin-bottom: 3px; 342 display: block; 343 } 344 .summary-item-value { color: #444; } 345 .detail { 346 padding: 20px; 347 background-color: #fff; 348 border-radius: 5px; 349 box-shadow: 0 2px 2px rgba(0, 0, 0, 0.05); 350 } 351 .detail-header { 352 margin-bottom: 10px; 353 border-bottom: 1px solid #eee; 354 padding-bottom: 10px; 355 } 356 .pass { color: green; } 357 .fail { color: red; } 358</style> 359</head> 360""" 361 362 363def init_logger(): 364 logging.config.dictConfig(logging_config_info) 365 366 367def put_msg(queue_obj, info): 368 """ 369 给队列中填入测试报告信息,用于汇总报告和生成结果 370 """ 371 queue_obj.put(info) 372 373 374def run_cmd_block(command, timeout=600): 375 logger.info(f"cmd: {command}") 376 # 启动子进程 377 process = subprocess.Popen(command.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) 378 379 # 用于存储子进程的输出 380 output = "" 381 error = "" 382 383 try: 384 # 读取子进程的输出 385 output, error = process.communicate(timeout=timeout) 386 except subprocess.TimeoutExpired: 387 logger.info(f"run cmd:{command} timeout") 388 process.terminate() 389 process.kill() 390 output, error = process.communicate(timeout=timeout) 391 return output, error 392 393 394def run_cmd_and_check_output(command, want_str, timeout=600): 395 """ 396 阻塞的方式运行命令,然后判断标准输出的返回值中是否有预期的字符串 397 存在预期的字符串返回 True 398 不存在预期的字符串返回 False 399 """ 400 output, error = run_cmd_block(command, timeout) 401 if want_str in output: 402 return True, (output, error) 403 else: 404 logger.error(f"can not get expect str:{want_str} cmd:{command} output:{output} error:{error}") 405 return False, (output, error) 406 407 408def process_shell_test(connect_key, config, thread_name): 409 """ 410 config格式 411 { 412 "test_type": TestType.SHELL, 413 "loop_count": LOOP_COUNT_DEFAULT, 414 "loop_delay_s": 0.01, 415 "task_count": TASK_COUNT_DEFAULT, 416 "cmd": "shell ls", 417 "expected_results": "data", 418 } 419 """ 420 logger.info(f"start process_shell_test thread {thread_name}") 421 test_count = config["loop_count"] 422 delay_s = config["loop_delay_s"] 423 cmd = f"hdc -t {connect_key} {config['cmd']}" 424 expected_results = config["expected_results"] 425 report = {"test_type": config["test_type"], "test_name": thread_name, "loop_count": test_count} 426 start_time = time.perf_counter() 427 for count in range(test_count): 428 logger.info(f"{thread_name} {count}") 429 is_ok, info = run_cmd_and_check_output(cmd, expected_results) 430 if is_ok: 431 logger.info(f"{thread_name} {count} success result:{info}") 432 else: 433 logger.error(f"{thread_name} loop_count:{count+1} run:{cmd} can not get expect result:{expected_results}," 434 f"result:{info}") 435 error_time = get_time_str(need_ms=True) 436 msg = f"<span style='color: #0000ff;'>[{error_time}]</span> {thread_name} loop_count:{count+1} run:{cmd}" \ 437 f" can not get expect result:{expected_results}, result:{info}" 438 report["error_msg"] = f"[{error_time}] result:{info}" 439 TEST_THREAD_MSG_QUEUE.put({TASK_ERROR_KEY_NAME: {"name": thread_name, "error_msg": msg}}) 440 EXIT_EVENT.set() 441 logger.info(f"{thread_name} {count} set exit event") 442 report["finished_test_count"] = count 443 break 444 if EXIT_EVENT.is_set(): 445 logger.info(f"{thread_name} {count} exit event is set") 446 report["error_msg"] = "event is set, normal exit" 447 report["finished_test_count"] = count + 1 448 break 449 time.sleep(delay_s) 450 if "finished_test_count" not in report: 451 report["finished_test_count"] = test_count 452 if report["finished_test_count"] < test_count: 453 report["passed"] = False 454 else: 455 report["passed"] = True 456 elapsed_time = time.perf_counter() - start_time 457 report["cost_time"] = elapsed_time 458 TEST_THREAD_MSG_QUEUE.put({thread_name: report}) 459 logger.info(f"stop process_shell_test thread {thread_name}") 460 461 462def process_file_send_test(connect_key, config, thread_name): 463 """ 464 config格式 465 { 466 "test_type": TestType.FILE_SEND, 467 "loop_count": LOOP_COUNT_DEFAULT, 468 "loop_delay_s": 0.01, 469 "task_count": TASK_COUNT_DEFAULT, 470 "cmd": "file send", 471 "local": "medium", 472 "remote": "/data/medium", 473 "expected_results": "FileTransfer finish", 474 } 475 """ 476 logger.info(f"start process_file_send_test thread {thread_name}") 477 test_count = config["loop_count"] 478 delay_s = config["loop_delay_s"] 479 expected_results = config["expected_results"] 480 local_file = os.path.join(RESOURCE_PATH, config['local']) 481 remote_file = f"{config['remote']}_{uuid.uuid4()}" 482 cmd = f"hdc -t {connect_key} {config['cmd']} {local_file} {remote_file}" 483 report = {"test_type": config["test_type"], "test_name": thread_name, "loop_count": test_count} 484 start_time = time.perf_counter() 485 for count in range(test_count): 486 logger.info(f"{thread_name} {count}") 487 is_ok, info = run_cmd_and_check_output(cmd, expected_results) 488 if is_ok: 489 logger.info(f"{thread_name} {count} success result:{info}") 490 else: 491 logger.error(f"{thread_name} loop_count:{count+1} run:{cmd} can not get expect result:{expected_results}," 492 f"result:{info}") 493 error_time = get_time_str(need_ms=True) 494 msg = f"<span style='color: #0000ff;'>[{error_time}]</span> {thread_name} loop_count:{count+1} run:{cmd}" \ 495 f" can not get expect result:{expected_results}, result:{info}" 496 report["error_msg"] = f"[{error_time}] result:{info}" 497 TEST_THREAD_MSG_QUEUE.put({TASK_ERROR_KEY_NAME: {"name": thread_name, "error_msg": msg}}) 498 EXIT_EVENT.set() 499 logger.info(f"{thread_name} {count} set exit event") 500 report["finished_test_count"] = count 501 break 502 503 run_cmd_block(f"hdc -t {connect_key} shell rm {remote_file}") 504 if EXIT_EVENT.is_set(): 505 logger.info(f"{thread_name} {count} exit event is set") 506 report["error_msg"] = "event is set, normal exit" 507 report["finished_test_count"] = count + 1 508 break 509 time.sleep(delay_s) 510 if "finished_test_count" not in report: 511 report["finished_test_count"] = test_count 512 if report["finished_test_count"] < test_count: 513 report["passed"] = False 514 else: 515 report["passed"] = True 516 elapsed_time = time.perf_counter() - start_time 517 report["cost_time"] = elapsed_time 518 TEST_THREAD_MSG_QUEUE.put({thread_name: report}) 519 logger.info(f"stop process_file_send_test thread {thread_name}") 520 521 522def process_file_recv_test(connect_key, config, thread_name): 523 """ 524 config格式 525 { 526 "test_type": TestType.FILE_RECV, 527 "loop_count": LOOP_COUNT_DEFAULT, 528 "loop_delay_s": 0.01, 529 "task_count": TASK_COUNT_DEFAULT, 530 "cmd": "file recv", 531 "original_file": "medium", 532 "local": "medium", 533 "remote": "/data/medium", 534 "expected_results": "FileTransfer finish", 535 } 536 """ 537 logger.info(f"start process_file_recv_test thread {thread_name}") 538 test_count = config["loop_count"] 539 delay_s = config["loop_delay_s"] 540 expected_results = config["expected_results"] 541 original_file = os.path.join(RESOURCE_PATH, config['original_file']) 542 name_id = uuid.uuid4() 543 local_file = os.path.join(RESOURCE_PATH, f"{config['local']}_{name_id}") 544 remote_file = f"{config['remote']}_{name_id}" 545 cmd = f"hdc -t {connect_key} {config['cmd']} {remote_file} {local_file}" 546 report = {"test_type": config["test_type"], "test_name": thread_name, "loop_count": test_count} 547 start_time = time.perf_counter() 548 run_cmd_block(f"hdc -t {connect_key} file send {original_file} {remote_file}") 549 for count in range(test_count): 550 logger.info(f"{thread_name} {count}") 551 is_ok, info = run_cmd_and_check_output(cmd, expected_results) 552 if is_ok: 553 logger.info(f"{thread_name} {count} success result:{info}") 554 else: 555 logger.error(f"{thread_name} loop_count:{count+1} run:{cmd} can not get expect result:{expected_results}," 556 f"result:{info}") 557 error_time = get_time_str(need_ms=True) 558 msg = f"<span style='color: #0000ff;'>[{error_time}]</span> {thread_name} loop_count:{count+1} run:{cmd}" \ 559 f" can not get expect result:{expected_results}, result:{info}" 560 report["error_msg"] = f"[{error_time}] result:{info}" 561 TEST_THREAD_MSG_QUEUE.put({TASK_ERROR_KEY_NAME: {"name": thread_name, "error_msg": msg}}) 562 EXIT_EVENT.set() 563 logger.info(f"{thread_name} {count} set exit event") 564 report["finished_test_count"] = count 565 break 566 os.remove(local_file) 567 logger.debug(f"{connect_key} remove {local_file} finished") 568 if EXIT_EVENT.is_set(): 569 logger.info(f"{thread_name} {count} exit event is set") 570 report["error_msg"] = "event is set, normal exit" 571 report["finished_test_count"] = count + 1 572 break 573 time.sleep(delay_s) 574 if "finished_test_count" not in report: 575 report["finished_test_count"] = test_count 576 if report["finished_test_count"] < test_count: 577 report["passed"] = False 578 else: 579 report["passed"] = True 580 run_cmd_block(f"hdc -t {connect_key} shell rm {remote_file}") 581 elapsed_time = time.perf_counter() - start_time 582 report["cost_time"] = elapsed_time 583 TEST_THREAD_MSG_QUEUE.put({thread_name: report}) 584 logger.info(f"stop process_file_recv_test thread {thread_name}") 585 586 587def create_fport_trans_test_env(info): 588 # 构建传输通道 589 thread_name = info.get("thread_name") 590 connect_key = info.get("connect_key") 591 fport_arg = info.get("fport_arg") 592 result, _ = run_cmd_block(f"hdc -t {connect_key} fport {fport_arg}") 593 logger.info(f"{thread_name} fport result:{result}") 594 rport_arg = info.get("rport_arg") 595 result, _ = run_cmd_block(f"hdc -t {connect_key} rport {rport_arg}") 596 logger.info(f"{thread_name} rport result:{result}") 597 598 599def do_fport_trans_file_test_once(client_connect_port, server_listen_port, file_name, file_save_name): 600 """ 601 进行一次数据传输测试 602 """ 603 logger.info(f"do_fport_trans_file_test_once start, file_save_name:{file_save_name}") 604 server_thread = threading.Thread(target=server_loop, args=(server_listen_port,)) 605 server_thread.start() 606 607 client_get_file(client_connect_port, file_name, file_save_name) 608 server_thread.join() 609 610 ori_file_md5 = get_local_md5(os.path.join(RESOURCE_PATH, file_name)) 611 new_file = os.path.join(RESOURCE_PATH, file_save_name) 612 new_file_md5 = 0 613 if os.path.exists(new_file): 614 new_file_md5 = get_local_md5(new_file) 615 os.remove(new_file) 616 logger.info(f"ori_file_md5:{ori_file_md5}, new_file_md5:{new_file_md5}") 617 618 if not ori_file_md5 == new_file_md5: 619 logger.error(f"check file md5 failed, file_save_name:{file_save_name}, ori_file_md5:{ori_file_md5}," 620 f"new_file_md5:{new_file_md5}") 621 return False 622 logger.info(f"do_fport_trans_file_test_once exit, file_save_name:{file_save_name}") 623 return True 624 625 626def process_fport_trans_file_test(connect_key, config, thread_name, task_index): 627 """ 628 task_index对应当前测试的port_info,从0开始计数。 629 config格式 630 { 631 "test_type": TestType.FPORT_TRANS_FILE, 632 "port_info": [ 633 { 634 "client_connect_port": 22345, 635 "daemon_transmit_port": 11081, 636 "server_listen_port": 18000, 637 }, 638 { 639 "client_connect_port": 22346, 640 "daemon_transmit_port": 11082, 641 "server_listen_port": 18001, 642 }, 643 ], 644 645 "loop_count": LOOP_COUNT_DEFAULT, 646 "loop_delay_s": 0.01, 647 "original_file": "medium", 648 } 649 """ 650 logger.info(f"start process_fport_trans_file_test thread {thread_name}") 651 test_count = config["loop_count"] 652 delay_s = config["loop_delay_s"] 653 port_info = config["port_info"] 654 client_connect_port = port_info[task_index]["client_connect_port"] 655 daemon_transmit_port = port_info[task_index]["daemon_transmit_port"] 656 server_listen_port = port_info[task_index]["server_listen_port"] 657 fport_arg = f"tcp:{client_connect_port} tcp:{daemon_transmit_port}" 658 rport_arg = f"tcp:{daemon_transmit_port} tcp:{server_listen_port}" 659 660 report = {"test_type": config["test_type"], "test_name": thread_name, "loop_count": test_count} 661 start_time = time.perf_counter() 662 663 # 构建传输通道 664 create_fport_trans_test_env({"thread_name": thread_name, "connect_key": connect_key, 665 "fport_arg": fport_arg, "rport_arg": rport_arg}) 666 667 # transmit file start 668 file_name = config["original_file"] 669 file_save_name = f"{file_name}_recv_fport_{uuid.uuid4()}" 670 for count in range(test_count): 671 logger.info(f"{thread_name} {count}") 672 if do_fport_trans_file_test_once(client_connect_port, server_listen_port, file_name, file_save_name) is False: 673 error_time = get_time_str(need_ms=True) 674 msg = f"<span style='color: #0000ff;'>[{error_time}]</span> {thread_name} loop_count:{count+1}" \ 675 f" check file md5 failed" 676 report["error_msg"] = f"[{error_time}] check file md5 failed" 677 TEST_THREAD_MSG_QUEUE.put({TASK_ERROR_KEY_NAME: {"name": thread_name, "error_msg": msg}}) 678 EXIT_EVENT.set() 679 logger.info(f"{thread_name} {count} set exit event") 680 report["finished_test_count"] = count 681 break 682 if EXIT_EVENT.is_set(): 683 logger.info(f"{thread_name} {count} exit event is set") 684 report["error_msg"] = "event is set, normal exit" 685 report["finished_test_count"] = count + 1 686 break 687 time.sleep(delay_s) 688 689 # 关闭fport通道 690 run_cmd_block(f"hdc -t {connect_key} fport rm {fport_arg}") 691 run_cmd_block(f"hdc -t {connect_key} fport rm {rport_arg}") 692 if "finished_test_count" not in report: 693 report["finished_test_count"] = test_count 694 if report["finished_test_count"] < test_count: 695 report["passed"] = False 696 else: 697 report["passed"] = True 698 elapsed_time = time.perf_counter() - start_time 699 report["cost_time"] = elapsed_time 700 TEST_THREAD_MSG_QUEUE.put({thread_name: report}) 701 logger.info(f"stop process_fport_trans_file_test thread {thread_name}") 702 703 704def get_test_result(fail_num): 705 """ 706 返回错误结果及错误结果显示类型信息 707 """ 708 global TEST_RESULT 709 if fail_num > 0: 710 test_result = "失败" 711 TEST_RESULT = False 712 test_result_class = "fail" 713 else: 714 test_result = "通过" 715 TEST_RESULT = True 716 test_result_class = "pass" 717 return test_result, test_result_class 718 719 720def get_error_msg_html(public_info): 721 """ 722 获取html格式的错误信息 723 """ 724 error_msg_list = public_info.get(TASK_ERROR_KEY_NAME) 725 error_msg_html = '' 726 if error_msg_list is not None: 727 error_msg = '\n'.join(error_msg_list) 728 error_msg_html = f""" 729 <div class="summary-row"> 730 <div class="summary-item"> 731 <span class="summary-item-key">错误信息</span> 732 <span class="summary-item-value fail">{error_msg}</span> 733 </div> 734 </div> 735 """ 736 return error_msg_html 737 738 739def gen_report_public_info(public_info): 740 """ 741 生成html格式的报告公共信息部分 742 """ 743 start_time = public_info.get('start_time') 744 stop_time = public_info.get('stop_time') 745 pass_num = public_info.get('pass_num') 746 fail_num = public_info.get('fail_num') 747 test_task_num = pass_num + fail_num 748 test_result, test_result_class = get_test_result(fail_num) 749 750 # 错误信息 751 error_msg_html = get_error_msg_html(public_info) 752 753 public_html_temp = f""" 754 <div class="summary-row"> 755 <div class="summary-item"> 756 <span class="summary-item-key">用例版本号</span> 757 <span class="summary-item-value">{STABILITY_TEST_VERSION}</span> 758 </div> 759 <div class="summary-item"> 760 <span class="summary-item-key">开始时间</span> 761 <span class="summary-item-value">{start_time}</span> 762 </div> 763 <div class="summary-item"> 764 <span class="summary-item-key">结束时间</span> 765 <span class="summary-item-value">{stop_time}</span> 766 </div> 767 </div> 768 <div class="summary-row"> 769 <div class="summary-item"> 770 <span class="summary-item-key">测试任务数</span> 771 <span class="summary-item-value">{test_task_num}</span> 772 </div> 773 <div class="summary-item"> 774 <span class="summary-item-key">失败任务数</span> 775 <span class="summary-item-value fail">{fail_num}</span> 776 </div> 777 <div class="summary-item"> 778 <span class="summary-item-key">成功任务数</span> 779 <span class="summary-item-value pass">{pass_num}</span> 780 </div> 781 </div> 782 <div class="summary-row"> 783 <div class="summary-item"> 784 <span class="summary-item-key">测试结果</span> 785 <span class="summary-item-value {test_result_class}">{test_result}</span> 786 </div> 787 </div> 788 {error_msg_html} 789 """ 790 return public_html_temp 791 792 793def gen_report_detail_info(pass_list, fail_list): 794 """ 795 生成html格式的报告详细信息部分 796 pass_list fail_list 格式:[("测试名称", {"key": value, "key": value, "key": value, "key": value ...}), ... ] 797 """ 798 # 生成表格每一行记录 799 detail_rows_list = [] 800 for one_test_key, one_test_value in fail_list + pass_list: 801 if one_test_value.get("passed") is True: 802 result_class = "pass" 803 result_text = "通过" 804 else: 805 result_class = "fail" 806 result_text = "失败" 807 finished_test_count = one_test_value.get("finished_test_count") 808 loop_count = one_test_value.get("loop_count") 809 completion_rate = 100.0 * finished_test_count / loop_count 810 cost_time = one_test_value.get("cost_time") 811 if finished_test_count > 0: 812 cost_time_per_test = f"{cost_time / finished_test_count:.3f}" 813 else: 814 cost_time_per_test = "NA" 815 one_row = f""" 816 <tr> 817 <td>{one_test_key}</td> 818 <td class="{result_class}">{result_text}</td> 819 <td>{completion_rate:.1f}</td> 820 <td>{finished_test_count}</td> 821 <td>{loop_count}</td> 822 <td>{cost_time_per_test}</td> 823 <td>{cost_time:.3f}</td> 824 <td>{one_test_value.get("error_msg", "NA")}</td> 825 </tr> 826 """ 827 detail_rows_list.append(one_row) 828 detail_rows = ''.join(detail_rows_list) 829 830 # 合成表格 831 detail_table_temp = f""" 832 <table> 833 <tr> 834 <th>任务名称</th> 835 <th>测试结果</th> 836 <th>完成率</th> 837 <th>已完成次数</th> 838 <th>总次数</th> 839 <th>平均每轮耗时(秒)</th> 840 <th>总耗时(秒)</th> 841 <th>错误信息</th> 842 </tr> 843 {detail_rows} 844 </table> 845 """ 846 return detail_table_temp 847 848 849def gen_report_info(public_info, detail_info): 850 """ 851 生成html格式的报告 852 """ 853 html_temp = f""" 854 <!DOCTYPE html> 855 <html lang="zh-CN"> 856 {HTML_HEAD} 857 <body> 858 <div class="report"> 859 <section class="summary"> 860 <h1 style="text-align: center;">HDC稳定性测试报告</h1> 861 <h2 class="summary-header">概要</h2> 862 {public_info} 863 </section> 864 <section class="detail"> 865 <h2 class="detail-header">详情</h2> 866 {detail_info} 867 </section> 868 </div> 869 </body> 870 </html> 871 """ 872 return html_temp 873 874 875def save_report(report_info, save_file_name): 876 """ 877 生成测试报告 878 """ 879 # 字典中获取报告公共信息 880 public_info = report_info.get(REPORT_PUBLIC_INFO_NAME) 881 # 字典中获取详细测试项目的信息 882 detail_test_info = {key: value for key, value in report_info.items() if key != REPORT_PUBLIC_INFO_NAME} 883 884 # 分类测试结果 885 pass_list = [] 886 fail_list = [] 887 for one_test_key, one_test_value in detail_test_info.items(): 888 if one_test_value.get("passed") is True: 889 pass_list.append((one_test_key, one_test_value)) 890 else: 891 fail_list.append((one_test_key, one_test_value)) 892 893 # 排序成功和失败的任务列表 894 pass_list = sorted(pass_list) 895 fail_list = sorted(fail_list) 896 public_info["pass_num"] = len(pass_list) 897 public_info["fail_num"] = len(fail_list) 898 899 # 生成报告公共信息 900 public_info_html = gen_report_public_info(public_info) 901 902 # 生成报告详细信息 903 detail_info_html = gen_report_detail_info(pass_list, fail_list) 904 905 # 合成报告 906 report_info_html = gen_report_info(public_info_html, detail_info_html) 907 908 # 写入文件 909 with open(save_file_name, "w", encoding="utf-8") as f: 910 f.write(report_info_html) 911 912 913def add_error_msg_to_public_info(err_msg, report_info): 914 """ 915 将一条错误信息追加到public_info里面的task_error字段中 916 err_msg的结构为{"name": "", "error_msg": ""} 917 """ 918 msg = err_msg.get("error_msg") 919 if REPORT_PUBLIC_INFO_NAME in report_info: 920 public_info = report_info.get(REPORT_PUBLIC_INFO_NAME) 921 if TASK_ERROR_KEY_NAME in public_info: 922 public_info[TASK_ERROR_KEY_NAME].append(msg) 923 else: 924 public_info[TASK_ERROR_KEY_NAME] = [msg, ] 925 else: 926 report_info[REPORT_PUBLIC_INFO_NAME] = {TASK_ERROR_KEY_NAME: [msg, ]} 927 928 929def add_to_report_info(one_info, report_info): 930 """ 931 将一条信息添加到报告信息集合中, 932 不存在则新增,存在则添加或者覆盖以前的值 933 one_info格式为 {key: {key1: value ...}, key: {key1: value ...}...} 934 当前的key包括如下几类: 935 1、任务名称:[sn]_filerecv_0 或者 [ip:port]_filerecv_0 936 2、公共信息:REPORT_PUBLIC_INFO_NAME = "public_info" 937 3、测试任务报告的错误信息:TASK_ERROR_KEY_NAME = "task_error" 938 最终追加到public_info里面的task_error字段中 939 """ 940 for report_section_add, section_dict_add in one_info.items(): 941 if report_section_add == TASK_ERROR_KEY_NAME: 942 # 追加error msg到public_info里面的task_error字段中 943 add_error_msg_to_public_info(section_dict_add, report_info) 944 continue 945 if report_section_add in report_info: 946 # 报告数据中已经存在待添加的字段,则获取报告数据中的该段落,给段落中增加或者覆盖已有字段 947 report_section_dict = report_info.get(report_section_add) 948 report_section_dict.update(section_dict_add) 949 else: 950 report_info[report_section_add] = section_dict_add 951 952 953def process_msg(msg_queue, thread_name): 954 """ 955 消息中心,用于接收所有测试线程的消息,保存到如下结构的字典report_info中,用于生成报告: 956 { 957 "public_info": {"key": value, "key": value, "key": value, "key": value ...} 958 "thread_name1": {"key": value, "key": value, "key": value, "key": value ...} 959 "thread_name2": {"key": value, "key": value, "key": value, "key": value ...} 960 } 961 public_info为报告开头的公共信息 962 thread_namex为各个测试项目的测试信息 963 通过msg_queue,传递过来的消息,格式为 {key: {key1: value ...}, key: {key1: value ...}...}的格式, 964 key表示上面report_info的key,不存在则新增,存在则添加或者覆盖以前的值 965 """ 966 logger.info(f"start process_msg thread {thread_name}") 967 report_info = {} 968 while True: 969 one_info = msg_queue.get() 970 if one_info is None: # 报告接收完毕,退出 971 logger.info(f"{thread_name} get None from queue") 972 break 973 add_to_report_info(one_info, report_info) 974 logger.info(f"start save report to {REPORT_FILE_NAME}, report len:{len(report_info)}") 975 save_report(report_info, REPORT_FILE_NAME) 976 logger.info(f"stop process_msg thread {thread_name}") 977 978 979class Session(object): 980 """ 981 session class 982 一个session连接对应一个session class 983 包含了所有在当前连接下面的测试任务 984 """ 985 # session类型,usb or tcp 986 session_type = "" 987 # 设备连接标识 988 connect_key = "" 989 test_config = [] 990 thread_list = [] 991 992 def __init__(self, connect_key): 993 self.connect_key = connect_key 994 995 def set_test_config(self, config): 996 self.test_config = config 997 998 def start_test(self): 999 if len(self.test_config) == 0: 1000 logger.error(f"start test test_config is empty, do not test, type:{self.session_type} {self.connect_key}") 1001 return True 1002 for one_config in self.test_config: 1003 if self.start_one_test(one_config) is False: 1004 logger.error(f"start one test failed, type:{self.session_type} {self.connect_key} config:{one_config}") 1005 return False 1006 return True 1007 1008 def start_one_test(self, config): 1009 if config["test_type"] == TestType.SHELL: 1010 if self.start_shell_test(config) is False: 1011 logger.error(f"start_shell_test failed, type:{self.session_type} {self.connect_key} config:{config}") 1012 return False 1013 if config["test_type"] == TestType.FILE_SEND: 1014 if self.start_file_send_test(config) is False: 1015 logger.error(f"start_file_send_test failed, type:{self.session_type} \ 1016 {self.connect_key} config:{config}") 1017 return False 1018 if config["test_type"] == TestType.FILE_RECV: 1019 if self.start_file_recv_test(config) is False: 1020 logger.error(f"start_file_recv_test failed, type:{self.session_type} \ 1021 {self.connect_key} config:{config}") 1022 return False 1023 if config["test_type"] == TestType.FPORT_TRANS_FILE: 1024 if self.start_fport_trans_file_test(config) is False: 1025 logger.error(f"start_fport_trans_file_test failed, type:{self.session_type} \ 1026 {self.connect_key} config:{config}") 1027 return False 1028 return True 1029 1030 def start_shell_test(self, config): 1031 task_count = config["task_count"] 1032 test_name = f"{self.connect_key}_shell" 1033 for task_index in range(task_count): 1034 thread = threading.Thread(target=process_shell_test, name=f"{test_name}_{task_index}", 1035 args=(self.connect_key, config, f"{test_name}_{task_index}")) 1036 thread.start() 1037 self.thread_list.append(thread) 1038 1039 def start_file_send_test(self, config): 1040 task_count = config["task_count"] 1041 test_name = f"{self.connect_key}_filesend" 1042 for task_index in range(task_count): 1043 thread = threading.Thread(target=process_file_send_test, name=f"{test_name}_{task_index}", 1044 args=(self.connect_key, config, f"{test_name}_{task_index}")) 1045 thread.start() 1046 self.thread_list.append(thread) 1047 1048 def start_file_recv_test(self, config): 1049 task_count = config["task_count"] 1050 test_name = f"{self.connect_key}_filerecv" 1051 for task_index in range(task_count): 1052 thread = threading.Thread(target=process_file_recv_test, name=f"{test_name}_{task_index}", 1053 args=(self.connect_key, config, f"{test_name}_{task_index}")) 1054 thread.start() 1055 self.thread_list.append(thread) 1056 1057 def start_fport_trans_file_test(self, config): 1058 task_count = len(config["port_info"]) 1059 test_name = f"{self.connect_key}_fporttrans" 1060 for task_index in range(task_count): 1061 thread = threading.Thread(target=process_fport_trans_file_test, name=f"{test_name}_{task_index}", 1062 args=(self.connect_key, config, f"{test_name}_{task_index}", task_index)) 1063 thread.start() 1064 self.thread_list.append(thread) 1065 1066 1067class UsbSession(Session): 1068 """ 1069 usb连接对应的session类 1070 """ 1071 session_type = "usb" 1072 1073 1074class TcpSession(Session): 1075 """ 1076 tcp连接对应的session类 1077 """ 1078 session_type = "tcp" 1079 1080 def start_tcp_connect(self): 1081 result, _ = run_cmd_block(f"hdc tconn {self.connect_key}") 1082 logger.info(f"start_tcp_connect {self.connect_key} result:{result}") 1083 RELEASE_TCONN_CMD_LIST.append(f"hdc tconn {self.connect_key} -remove") 1084 return True 1085 1086 1087def start_server(): 1088 cmd = "hdc start" 1089 result, _ = run_cmd_block(cmd) 1090 return result 1091 1092 1093def get_dev_list(): 1094 try: 1095 result, _ = run_cmd_block("hdc list targets") 1096 result = result.split() 1097 except (OSError, IndexError): 1098 result = ["failed to detect device"] 1099 return False, result 1100 targets = result 1101 if len(targets) == 0: 1102 logger.error(f"get device, devices list is empty") 1103 return False, [] 1104 if "[Empty]" in targets[0]: 1105 logger.error(f"get device, no devices found, devices:{targets}") 1106 return False, targets 1107 return True, targets 1108 1109 1110def check_device_online(connect_key): 1111 """ 1112 确认设备是否在线 1113 """ 1114 result, devices = get_dev_list() 1115 if result is False: 1116 logger.error(f"get device failed, devices:{devices}") 1117 return False 1118 if connect_key in devices: 1119 return True 1120 return False 1121 1122 1123def init_test_env(): 1124 """ 1125 初始化测试环境 1126 1、使用tmode port和fport转发,构建多session tcp测试场景 1127 """ 1128 logger.info(f"init env") 1129 start_server() 1130 result, devices = get_dev_list() 1131 if result is False: 1132 logger.error(f"get device failed, devices:{devices}") 1133 return False 1134 device_sn = devices[0] 1135 if len(devices) > 1: 1136 # 存在多个设备连接,获取不是ip:port的设备作为待测试的设备 1137 logger.info(f"Multiple devices are connected, devices:{devices}") 1138 for dev in devices: 1139 if ':' not in dev: 1140 device_sn = dev 1141 # 关闭设备侧监听端口 1142 result, _ = run_cmd_block(f"hdc -t {device_sn} tmode port close") 1143 logger.info(f"close tmode port finished") 1144 time.sleep(10) 1145 1146 logger.info(f"get device:{device_sn}") 1147 1148 # 开启设备侧监听端口 1149 result, _ = run_cmd_block(f"hdc -t {device_sn} tmode port {DEVICE_LISTEN_PORT}") 1150 logger.info(f"run tmode port {DEVICE_LISTEN_PORT}, result:{result}") 1151 time.sleep(10) 1152 logger.info(f"start check device:{device_sn}") 1153 if check_device_online(device_sn) is False: 1154 logger.error(f"device {device_sn} in not online") 1155 return False 1156 logger.info(f"init_test_env finished") 1157 return True 1158 1159 1160def start_usb_session_test(connect_key): 1161 """ 1162 开始一个usb session的测试 1163 """ 1164 logger.info(f"start_usb_session_test connect_key:{connect_key}") 1165 session = UsbSession(connect_key) 1166 session.set_test_config(USB_SESSION_TEST_CONFIG) 1167 if session.start_test() is False: 1168 logger.error(f"session.start_test failed, connect_key:{connect_key}") 1169 return False 1170 SESSION_LIST.append(session) 1171 logger.info(f"start_usb_session_test connect_key:{connect_key} finished") 1172 return True 1173 1174 1175def start_local_tcp_session_test(connect_key, port_list): 1176 """ 1177 1、遍历传入的端口号,创建fport端口转发到设备端的DEVICE_LISTEN_PORT, 1178 2、使用tconn连接后进行测试 1179 """ 1180 logger.info(f"start_local_tcp_session_test port_list:{port_list}") 1181 if len(TCP_SESSION_TEST_CONFIG) == 0 or len(port_list) == 0: 1182 logger.info(f"port_list:{port_list} TCP_SESSION_TEST_CONFIG:{TCP_SESSION_TEST_CONFIG}") 1183 logger.info(f"port_list or TCP_SESSION_TEST_CONFIG is empty, no need do local tcp session test, exit") 1184 return True 1185 for port in port_list: 1186 # 构建fport通道 1187 logger.info(f"create fport local:{port} device:{DEVICE_LISTEN_PORT}") 1188 result, _ = run_cmd_block(f"hdc -t {connect_key} fport tcp:{port} tcp:{DEVICE_LISTEN_PORT}") 1189 logger.info(f"create fport local:{port} device:{DEVICE_LISTEN_PORT} result:{result}") 1190 RELEASE_FPORT_CMD_LIST.append(f"hdc -t {connect_key} fport rm tcp:{port} tcp:{DEVICE_LISTEN_PORT}") 1191 tcp_key = f"127.0.0.1:{port}" 1192 logger.info(f"start TcpSession connect_key:{tcp_key}") 1193 session = TcpSession(tcp_key) 1194 session.set_test_config(TCP_SESSION_TEST_CONFIG) 1195 session.start_tcp_connect() 1196 if session.start_test() is False: 1197 logger.error(f"session.start_test failed, connect_key:{tcp_key}") 1198 return False 1199 SESSION_LIST.append(session) 1200 logger.info(f"start tcp test connect_key:{tcp_key} finished") 1201 1202 logger.info(f"start_local_tcp_session_test finished") 1203 return True 1204 1205 1206def start_test(msg_queue): 1207 """ 1208 启动测试 1209 1、启动usb session场景的测试 1210 2、启动通过 tmode+fport模拟的tcp session场景的测试 1211 """ 1212 logger.info(f"start test") 1213 result, devices = get_dev_list() 1214 if result is False: 1215 logger.error(f"get device failed, devices:{devices}") 1216 return False 1217 device_sn = devices[0] 1218 DEVICE_LIST.append(device_sn) 1219 if start_usb_session_test(device_sn) is False: 1220 logger.error(f"start_usb_session_test failed, devices:{devices}") 1221 return False 1222 if start_local_tcp_session_test(device_sn, MUTIL_SESSION_TEST_PC_PORT_LIST) is False: 1223 logger.error(f"start_tcp_session_test failed, devices:{devices}") 1224 return False 1225 return True 1226 1227 1228def release_resource(): 1229 """ 1230 释放相关资源 1231 1、断开tconn连接 1232 2、fport转发 1233 3、tmode port 1234 """ 1235 logger.info(f"enter release_resource") 1236 for cmd in RELEASE_TCONN_CMD_LIST: 1237 result, _ = run_cmd_block(cmd) 1238 logger.info(f"release tconn cmd:{cmd} result:{result}") 1239 for cmd in RELEASE_FPORT_CMD_LIST: 1240 result, _ = run_cmd_block(cmd) 1241 logger.info(f"release fport cmd:{cmd} result:{result}") 1242 result, _ = run_cmd_block(f"hdc -t {DEVICE_LIST[0]} tmode port close") 1243 logger.info(f"tmode port close, device:{DEVICE_LIST[0]} result:{result}") 1244 1245 1246def run_stability_test(): 1247 global logger 1248 logger = logging.getLogger(__name__) 1249 logger.info(f"main resource_path:{RESOURCE_PATH}") 1250 start_time = get_time_str() 1251 if init_test_env() is False: 1252 logger.error(f"init_test_env failed") 1253 return False 1254 1255 # 启动消息收集进程 1256 msg_thread = threading.Thread(target=process_msg, name="msg_process", args=(TEST_THREAD_MSG_QUEUE, "msg_process")) 1257 msg_thread.start() 1258 1259 put_msg(TEST_THREAD_MSG_QUEUE, {"public_info": {"start_time": start_time}}) 1260 logger.info(f"start run test thread") 1261 if start_test(TEST_THREAD_MSG_QUEUE) is False: 1262 logger.error(f"start_test failed") 1263 stop_time = get_time_str() 1264 put_msg(TEST_THREAD_MSG_QUEUE, {"public_info": {"stop_time": stop_time}}) 1265 TEST_THREAD_MSG_QUEUE.put(None) 1266 msg_thread.join() 1267 return False 1268 1269 # wait all thread exit 1270 logger.info(f"wait all test thread exit") 1271 for session in SESSION_LIST: 1272 for thread in session.thread_list: 1273 thread.join() 1274 1275 stop_time = get_time_str() 1276 put_msg(TEST_THREAD_MSG_QUEUE, {"public_info": {"stop_time": stop_time}}) 1277 1278 release_resource() 1279 1280 # 传递 None 参数,报告进程收到后退出 1281 logger.info(f"main put None to TEST_THREAD_MSG_QUEUE") 1282 TEST_THREAD_MSG_QUEUE.put(None) 1283 msg_thread.join() 1284 1285 logger.info(f"exit main, test result:{TEST_RESULT}") 1286 return TEST_RESULT 1287 1288 1289if __name__ == "__main__": 1290 init_logger() 1291 run_stability_test() 1292