1#!/usr/bin/env python3 2# coding=utf-8 3 4# 5# Copyright (c) 2020-2021 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 copy 20import re 21import time 22import datetime 23from queue import Queue 24 25from _core.interface import IParser 26from _core.plugin import Plugin 27from _core.constants import ParserType 28from _core.executor.listener import StateRecorder 29from _core.executor.listener import TestDescription 30from _core.interface import LifeCycle 31from _core.report.suite_reporter import ResultCode 32from _core.logger import platform_logger 33from _core.report.encrypt import check_pub_key_exist 34 35__all__ = ["CppTestListParserLite", "CTestParser", "OpenSourceParser", 36 "CppTestParserLite", "ShellHandler"] 37 38_INFORMATIONAL_START = "[----------]" 39_TEST_START_RUN_TAG = "[==========] Running" 40_TEST_RUN_TAG = "[==========]" 41_CPP_TEST_DRYRUN_TAG = "Running main() " 42_TEST_START_TAG = "[ RUN ]" 43_TEST_OK_TAG = "[ OK ]" 44_TEST_SKIPPED_TAG = "[ SKIPPED ]" 45_TEST_FAILED_TAG = "[ FAILED ]" 46_ALT_OK_TAG = "[ OK ]" 47_TIMEOUT_TAG = "[ TIMEOUT ]" 48 49_CTEST_START_TEST_RUN_TAG = "Framework inited." 50_CTEST_END_TEST_RUN_TAG = "Framework finished." 51_CTEST_SUITE_TEST_RUN_TAG = "Start to run test suite:" 52_CTEST_SUITE_TIME_RUN_TAG = "Run test suite " 53_CTEST_SETUP_TAG = "setup" 54_CTEST_RUN_TAG = "-----------------------" 55 56_TEST_PASSED_LOWER = "pass" 57 58_COMPILE_PASSED = "compile PASSED" 59_COMPILE_PARA = r"(.* compile .*)" 60 61_PRODUCT_PARA = r"(.*The .* is .*)" 62_PRODUCT_PARA_START = r"To Obtain Product Params Start" 63_PRODUCT_PARA_END = r"To Obtain Product Params End" 64 65_START_JSUNIT_RUN_MARKER = "[start] start run suites" 66_START_JSUNIT_SUITE_RUN_MARKER = "[suite start]" 67_START_JSUNIT_SUITE_END_MARKER = "[suite end]" 68_END_JSUNIT_RUN_MARKER = "[end] run suites end" 69_PASS_JSUNIT_MARKER = "[%s]" % "pass" 70_FAIL_JSUNIT_MARKER = "[fail]" 71_ACE_LOG_MARKER = "[Console Info]" 72 73LOG = platform_logger("ParserLite") 74 75 76@Plugin(type=Plugin.PARSER, id=ParserType.cpp_test_lite) 77class CppTestParserLite(IParser): 78 def __init__(self): 79 self.state_machine = StateRecorder() 80 self.suite_name = "" 81 self.listeners = [] 82 self.product_info = {} 83 self.is_params = False 84 85 def get_suite_name(self): 86 return self.suite_name 87 88 def get_listeners(self): 89 return self.listeners 90 91 def __process__(self, lines): 92 if not self.state_machine.suites_is_started(): 93 self.state_machine.trace_logs.extend(lines) 94 for line in lines: 95 if not check_pub_key_exist(): 96 LOG.debug(line) 97 self.parse(line) 98 99 def __done__(self): 100 suite_result = self.state_machine.suite() 101 suite_result.is_completed = True 102 for listener in self.get_listeners(): 103 suite = copy.copy(suite_result) 104 listener.__ended__(LifeCycle.TestSuite, suite, is_clear=True) 105 self.state_machine.running_test_index = 0 106 107 suites_result = self.state_machine.get_suites() 108 if not suites_result.suites_name: 109 return 110 for listener in self.get_listeners(): 111 suites = copy.copy(suites_result) 112 listener.__ended__(LifeCycle.TestSuites, test_result=suites, 113 suites_name=suites.suites_name, 114 product_info=suites.product_info) 115 self.state_machine.current_suites = None 116 117 @staticmethod 118 def _is_test_run(line): 119 return True if _TEST_RUN_TAG in line else False 120 121 @staticmethod 122 def _is_test_start_run(line): 123 return True if _TEST_START_RUN_TAG in line else False 124 125 @staticmethod 126 def _is_informational_start(line): 127 return True if _INFORMATIONAL_START in line else False 128 129 @staticmethod 130 def _is_test_start(line): 131 return True if _TEST_START_TAG in line else False 132 133 def _process_informational_line(self, line): 134 pattern = r"(.*) (\(\d+ ms total\))" 135 message = line[len(_INFORMATIONAL_START):].strip() 136 if re.match(pattern, line.strip()): 137 self.handle_suite_ended_tag(message) 138 elif re.match(r'(\d+) test[s]? from (.*)', message): 139 self.handle_suite_started_tag(message) 140 141 def _process_test_run_line(self, line): 142 if not self.state_machine.suites_is_running(): 143 return 144 message = line[len(_TEST_RUN_TAG):].strip() 145 self.handle_suites_ended_tag(message) 146 147 def parse(self, line): 148 if _PRODUCT_PARA_START in line: 149 self.is_params = True 150 elif _PRODUCT_PARA_END in line: 151 self.is_params = False 152 if re.match(_PRODUCT_PARA, line) and self.is_params: 153 handle_product_info(line, self.product_info) 154 155 if self.state_machine.suites_is_started() or self._is_test_run(line): 156 if self._is_test_start_run(line): 157 self.handle_suites_started_tag(line) 158 elif self._is_informational_start(line): 159 self._process_informational_line(line) 160 elif self._is_test_run(line): 161 self._process_test_run_line(line) 162 elif self._is_test_start(line): 163 message = line[line.index(_TEST_START_TAG) + 164 len(_TEST_START_TAG):].strip() 165 self.handle_test_started_tag(message) 166 else: 167 self.process_test(line) 168 169 def process_test(self, line): 170 if _TEST_SKIPPED_TAG in line: 171 message = line[line.index(_TEST_SKIPPED_TAG) + len( 172 _TEST_SKIPPED_TAG):].strip() 173 if not self.state_machine.test_is_running(): 174 LOG.error( 175 "Found {} without {} before, wrong GTest log format". 176 format(line, _TEST_START_TAG), error_no="00405") 177 return 178 self.handle_test_ended_tag(message, ResultCode.SKIPPED) 179 elif _TEST_OK_TAG in line: 180 message = line[line.index(_TEST_OK_TAG) + len( 181 _TEST_OK_TAG):].strip() 182 if not self.state_machine.test_is_running(): 183 LOG.error( 184 "Found {} without {} before, wrong GTest log format". 185 format(line, _TEST_START_TAG), error_no="00405") 186 return 187 self.handle_test_ended_tag(message, ResultCode.PASSED) 188 elif _ALT_OK_TAG in line: 189 message = line[line.index(_ALT_OK_TAG) + len( 190 _ALT_OK_TAG):].strip() 191 self.fake_run_marker(message) 192 self.handle_test_ended_tag(message, ResultCode.PASSED) 193 elif _TEST_FAILED_TAG in line: 194 message = line[line.index(_TEST_FAILED_TAG) + len( 195 _TEST_FAILED_TAG):].strip() 196 if not self.state_machine.suite_is_running(): 197 return 198 if not self.state_machine.test_is_running(): 199 self.fake_run_marker(message) 200 self.handle_test_ended_tag(message, ResultCode.FAILED) 201 elif _TIMEOUT_TAG in line: 202 message = line[line.index(_TIMEOUT_TAG) + len( 203 _TIMEOUT_TAG):].strip() 204 self.fake_run_marker(message) 205 self.handle_test_ended_tag(message, ResultCode.FAILED) 206 elif self.state_machine.test_is_running(): 207 self.append_test_output(line) 208 209 def handle_test_started_tag(self, message): 210 test_class, test_name, _ = self.parse_test_description(message) 211 test_result = self.state_machine.test(reset=True) 212 test_result.test_class = test_class 213 test_result.test_name = test_name 214 for listener in self.get_listeners(): 215 test_result = copy.copy(test_result) 216 listener.__started__(LifeCycle.TestCase, test_result) 217 218 @classmethod 219 def parse_test_description(cls, message): 220 run_time = 0 221 matcher = re.match(r'(.*) \((\d+) ms\)(.*)', message) 222 if matcher: 223 test_class, test_name = matcher.group(1).rsplit(".", 1) 224 run_time = int(matcher.group(2)) 225 else: 226 test_class, test_name = message.rsplit(".", 1) 227 return test_class.split(" ")[-1], test_name.split(" ")[0], run_time 228 229 def handle_test_ended_tag(self, message, test_status): 230 test_class, test_name, run_time = self.parse_test_description( 231 message) 232 test_result = self.state_machine.test() 233 test_result.run_time = int(run_time) 234 test_result.code = test_status.value 235 if not test_result.is_running(): 236 LOG.error( 237 "Test has no start tag when trying to end test: %s", message, 238 error_no="00405") 239 return 240 found_unexpected_test = False 241 if test_result.test_class != test_class: 242 LOG.error( 243 "Expected class: {} but got:{} ".format(test_result.test_class, 244 test_class), 245 error_no="00405") 246 found_unexpected_test = True 247 if test_result.test_name != test_name: 248 LOG.error( 249 "Expected test: {} but got: {}".format(test_result.test_name, 250 test_name), 251 error_no="00405") 252 found_unexpected_test = True 253 test_result.current = self.state_machine.running_test_index + 1 254 self.state_machine.test().is_completed = True 255 if found_unexpected_test: 256 test_result.code = ResultCode.FAILED.value 257 258 for listener in self.get_listeners(): 259 result = copy.copy(test_result) 260 listener.__ended__(LifeCycle.TestCase, result) 261 self.state_machine.running_test_index += 1 262 263 def fake_run_marker(self, message): 264 fake_marker = re.compile(" +").split(message) 265 self.handle_test_started_tag(fake_marker) 266 267 def handle_suites_started_tag(self, message): 268 self.state_machine.get_suites(reset=True) 269 matcher = re.match(r'.* Running (\d+) test[s]? from .*', message) 270 expected_test_num = int(matcher.group(1)) if matcher else -1 271 if expected_test_num >= 0: 272 test_suites = self.state_machine.get_suites() 273 test_suites.suites_name = self.get_suite_name() 274 test_suites.test_num = expected_test_num 275 test_suites.product_info = self.product_info 276 for listener in self.get_listeners(): 277 suite_report = copy.copy(test_suites) 278 listener.__started__(LifeCycle.TestSuites, suite_report) 279 280 def handle_suite_started_tag(self, message): 281 self.state_machine.suite(reset=True) 282 matcher = re.match(r'(\d+) test[s]? from (.*)', message) 283 expected_test_num = int(matcher.group(1)) if matcher else -1 284 if expected_test_num >= 0: 285 test_suite = self.state_machine.suite() 286 test_suite.suite_name = matcher.group(2) 287 test_suite.test_num = expected_test_num 288 for listener in self.get_listeners(): 289 suite_report = copy.copy(test_suite) 290 listener.__started__(LifeCycle.TestSuite, suite_report) 291 292 def handle_suite_ended_tag(self, message): 293 suite_result = self.state_machine.suite() 294 matcher = re.match(r'.*\((\d+) ms total\)', message) 295 if matcher: 296 suite_result.run_time = int(matcher.group(1)) 297 suite_result.is_completed = True 298 for listener in self.get_listeners(): 299 suite = copy.copy(suite_result) 300 listener.__ended__(LifeCycle.TestSuite, suite, is_clear=True) 301 self.state_machine.running_test_index = 0 302 303 def handle_suites_ended_tag(self, message): 304 suites = self.state_machine.get_suites() 305 matcher = re.match(r'.*\((\d+) ms total\)', message) 306 if matcher: 307 suites.run_time = int(matcher.group(1)) 308 suites.is_completed = True 309 for listener in self.get_listeners(): 310 copy_suites = copy.copy(suites) 311 listener.__ended__(LifeCycle.TestSuites, test_result=copy_suites, 312 suites_name=suites.suites_name, 313 product_info=suites.product_info) 314 315 def append_test_output(self, message): 316 if self.state_machine.test().stacktrace: 317 self.state_machine.test().stacktrace = "{}\r\n".format( 318 self.state_machine.test().stacktrace) 319 self.state_machine.test().stacktrace = "{}{}".format( 320 self.state_machine.test().stacktrace, message) 321 322 @staticmethod 323 def handle_test_run_failed(error_msg): 324 if not error_msg: 325 error_msg = "Unknown error" 326 if not check_pub_key_exist(): 327 LOG.debug("Error msg:%s" % error_msg) 328 329 def mark_test_as_failed(self, test): 330 if not self.state_machine.current_suite and not test.class_name: 331 return 332 suites_result = self.state_machine.get_suites(reset=True) 333 suites_result.suites_name = self.get_suite_name() 334 335 suite_name = self.state_machine.current_suite.suite_name if \ 336 self.state_machine.current_suite else None 337 suite_result = self.state_machine.suite(reset=True) 338 test_result = self.state_machine.test(reset=True) 339 suite_result.suite_name = suite_name or test.class_name 340 suite_result.suite_num = 1 341 test_result.test_class = test.class_name 342 test_result.test_name = test.test_name 343 test_result.stacktrace = "error_msg: Unknown error" 344 test_result.num_tests = 1 345 test_result.run_time = 0 346 test_result.code = ResultCode.FAILED.value 347 for listener in self.get_listeners(): 348 suite_report = copy.copy(suites_result) 349 listener.__started__(LifeCycle.TestSuites, suite_report) 350 for listener in self.get_listeners(): 351 suite_report = copy.copy(suite_result) 352 listener.__started__(LifeCycle.TestSuite, suite_report) 353 for listener in self.get_listeners(): 354 test_result = copy.copy(test_result) 355 listener.__started__(LifeCycle.TestCase, test_result) 356 for listener in self.get_listeners(): 357 test_result = copy.copy(test_result) 358 listener.__ended__(LifeCycle.TestCase, test_result) 359 for listener in self.get_listeners(): 360 suite_report = copy.copy(suite_result) 361 listener.__ended__(LifeCycle.TestSuite, suite_report, 362 is_clear=True) 363 self.__done__() 364 365 366@Plugin(type=Plugin.PARSER, id=ParserType.cpp_test_list_lite) 367class CppTestListParserLite(IParser): 368 def __init__(self): 369 self.last_test_class_name = None 370 self.state_machine = StateRecorder() 371 self.listeners = [] 372 self.tests = [] 373 self.suites_name = "" 374 self.class_result = None 375 self.method_result = None 376 377 def __process__(self, lines): 378 for line in lines: 379 if not check_pub_key_exist(): 380 LOG.debug(line) 381 self.parse(line) 382 383 def get_suite_name(self): 384 return self.suites_name 385 386 def get_listeners(self): 387 return self.listeners 388 389 def __done__(self): 390 if self.state_machine.is_started(): 391 self.handle_suite_ended_tag() 392 suites_result = self.state_machine.get_suites() 393 if not suites_result.suites_name: 394 return 395 for listener in self.get_listeners(): 396 suites = copy.copy(suites_result) 397 listener.__ended__(LifeCycle.TestSuites, test_result=suites, 398 suites_name=suites.suites_name) 399 self.state_machine.current_suites = None 400 401 def _is_class(self, line): 402 self.class_result = re.compile('^([a-zA-Z]+.*)\\.$').match(line) 403 return self.class_result 404 405 def _is_method(self, line): 406 self.method_result = re.compile( 407 '\\s+([a-zA-Z_]+[\\S]*)(.*)?(\\s+.*)?$').match(line) 408 return self.method_result 409 410 def _process_class_line(self, line): 411 del line 412 if not self.state_machine.suites_is_started(): 413 self.handle_suites_started_tag() 414 self.last_test_class_name = self.class_result.group(1) 415 if self.state_machine.is_started(): 416 self.handle_suite_ended_tag() 417 self.handle_suite_started_tag(self.class_result.group(1)) 418 419 def _process_method_line(self, line): 420 if not self.last_test_class_name: 421 LOG.error( 422 "Parsed new test case name %s but no test class" 423 " name has been set" % line, error_no="00405") 424 else: 425 test = TestDescription(self.last_test_class_name, 426 self.method_result.group(1)) 427 self.tests.append(test) 428 self.handle_test_tag(self.last_test_class_name, 429 self.method_result.group(1)) 430 431 @staticmethod 432 def _is_cpp_test_dryrun(line): 433 return True if line.find(_CPP_TEST_DRYRUN_TAG) != -1 else False 434 435 def parse(self, line): 436 if self.state_machine.suites_is_started() or self._is_cpp_test_dryrun( 437 line): 438 if self._is_cpp_test_dryrun(line): 439 self.handle_suites_started_tag() 440 elif self._is_class(line): 441 self._process_class_line(line) 442 elif self._is_method(line): 443 self._process_method_line(line) 444 else: 445 if not check_pub_key_exist(): 446 LOG.debug("Line ignored: %s" % line) 447 448 def handle_test_tag(self, test_class, test_name): 449 test_result = self.state_machine.test(reset=True) 450 test_result.test_class = test_class 451 test_result.test_name = test_name 452 for listener in self.get_listeners(): 453 test_result = copy.copy(test_result) 454 listener.__started__(LifeCycle.TestCase, test_result) 455 self.state_machine.test().is_completed = True 456 test_result.code = ResultCode.SKIPPED.value 457 for listener in self.get_listeners(): 458 result = copy.copy(test_result) 459 listener.__ended__(LifeCycle.TestCase, result) 460 self.state_machine.running_test_index += 1 461 test_suites = self.state_machine.get_suites() 462 test_suite = self.state_machine.suite() 463 test_suites.test_num += 1 464 test_suite.test_num += 1 465 466 def handle_suites_started_tag(self): 467 self.state_machine.get_suites(reset=True) 468 test_suites = self.state_machine.get_suites() 469 test_suites.suites_name = self.get_suite_name() 470 test_suites.test_num = 0 471 for listener in self.get_listeners(): 472 suite_report = copy.copy(test_suites) 473 listener.__started__(LifeCycle.TestSuites, suite_report) 474 475 def handle_suite_started_tag(self, class_name): 476 self.state_machine.suite(reset=True) 477 test_suite = self.state_machine.suite() 478 test_suite.suite_name = class_name 479 test_suite.test_num = 0 480 for listener in self.get_listeners(): 481 test_suite_copy = copy.copy(test_suite) 482 listener.__started__(LifeCycle.TestSuite, test_suite_copy) 483 484 def handle_suite_ended_tag(self): 485 suite_result = self.state_machine.suite() 486 suite_result.is_completed = True 487 for listener in self.get_listeners(): 488 suite = copy.copy(suite_result) 489 listener.__ended__(LifeCycle.TestSuite, suite) 490 491 def handle_suites_ended_tag(self): 492 suites = self.state_machine.get_suites() 493 suites.is_completed = True 494 for listener in self.get_listeners(): 495 copy_suites = copy.copy(suites) 496 listener.__ended__(LifeCycle.TestSuites, test_result=copy_suites, 497 suites_name=suites.suites_name) 498 499 def mark_test_as_failed(self, test): 500 if not self.state_machine.current_suite and not test.class_name: 501 return 502 suite_name = self.state_machine.current_suite.suite_name if \ 503 self.state_machine.current_suite else None 504 suite_result = self.state_machine.suite(reset=True) 505 test_result = self.state_machine.test(reset=True) 506 suite_result.suite_name = suite_name or test.class_name 507 suite_result.suite_num = 1 508 test_result.test_class = test.class_name 509 test_result.test_name = test.test_name 510 test_result.stacktrace = "error_msg: Unknown error" 511 test_result.num_tests = 1 512 test_result.run_time = 0 513 test_result.code = ResultCode.FAILED.value 514 for listener in self.get_listeners(): 515 suite_report = copy.copy(suite_result) 516 listener.__started__(LifeCycle.TestSuite, suite_report) 517 for listener in self.get_listeners(): 518 test_result = copy.copy(test_result) 519 listener.__started__(LifeCycle.TestCase, test_result) 520 for listener in self.get_listeners(): 521 test_result = copy.copy(test_result) 522 listener.__ended__(LifeCycle.TestCase, test_result) 523 self.__done__() 524 525 526@Plugin(type=Plugin.PARSER, id=ParserType.ctest_lite) 527class CTestParser(IParser): 528 last_line = "" 529 pattern = r"(\d{4}-\d{1,2}-\d{1,2}\s\d{1,2}:\d{1,2}:\d{1,2}\.\d{3}) " 530 531 def __init__(self): 532 self.state_machine = StateRecorder() 533 self.suites_name = "" 534 self.listeners = [] 535 self.product_info = {} 536 self.is_params = False 537 self.result_lines = [] 538 539 def get_suite_name(self): 540 return self.suites_name 541 542 def get_listeners(self): 543 return self.listeners 544 545 def __process__(self, lines): 546 if not self.state_machine.suites_is_started(): 547 self.state_machine.trace_logs.extend(lines) 548 for line in lines: 549 self.parse(line) 550 551 def __done__(self): 552 suites = self.state_machine.get_suites() 553 suites.is_completed = True 554 555 for listener in self.get_listeners(): 556 listener.__ended__(LifeCycle.TestSuites, test_result=suites, 557 suites_name=suites.suites_name, 558 product_info=suites.product_info) 559 self.state_machine.current_suites = None 560 561 @staticmethod 562 def _is_ctest_start_test_run(line): 563 return True if line.endswith(_CTEST_START_TEST_RUN_TAG) else False 564 565 @staticmethod 566 def _is_ctest_end_test_run(line): 567 return True if line.endswith(_CTEST_END_TEST_RUN_TAG) else False 568 569 @staticmethod 570 def _is_ctest_run(line): 571 return re.match(r".*(\d+ Tests \d+ Failures \d+ Ignored).*", line) 572 573 def _is_ctest_suite_test_run(self, line): 574 return re.match("{}{}".format(self.pattern, _CTEST_SUITE_TEST_RUN_TAG), 575 line) 576 577 def is_ctest_suite_time_run(self, line): 578 return re.match("{}{}".format(self.pattern, _CTEST_SUITE_TIME_RUN_TAG), 579 line) 580 581 def _process_ctest_suite_test_run_line(self, line): 582 _, message_index = re.match( 583 "{}{}".format(self.pattern, _CTEST_SUITE_TEST_RUN_TAG), 584 line).span() 585 self.handle_suite_started_tag(line[message_index:].strip()) 586 587 @staticmethod 588 def _is_execute_result_line(line): 589 return re.match( 590 r"(.*" + "\\.c:" + "\\d+:.*:(PASS|FAIL|OK|IGNORE"")\\.*)", 591 line.strip()) 592 593 @staticmethod 594 def _is_result_line(line): 595 return line.find("PASS") != -1 or line.find("FAIL") != -1 or line.find( 596 "IGNORE") != -1 597 598 def parse(self, line): 599 self._parse_product_info(line) 600 601 if self.state_machine.suites_is_started() or \ 602 self._is_ctest_start_test_run(line): 603 try: 604 test_matcher = re.match(r".*(\d+ Tests).+", line) 605 other_matcher = \ 606 re.match(r".*(\d+ Failures \d+ Ignored).*",line) 607 if (test_matcher or other_matcher) and \ 608 not self._is_ctest_run(line): 609 if test_matcher: 610 self.result_lines.append(test_matcher.group(1)) 611 if other_matcher: 612 self.result_lines.append(other_matcher.group(1)) 613 line = " ".join(self.result_lines) 614 self.result_lines.clear() 615 616 if self._is_ctest_start_test_run(line): 617 self.handle_suites_started_tag() 618 elif self._is_ctest_end_test_run(line): 619 self.process_suites_ended_tag() 620 elif self._is_ctest_run(line): 621 self.handle_suite_ended_tag(line) 622 elif self._is_ctest_suite_test_run(line) and \ 623 not self.state_machine.suite_is_running(): 624 self._process_ctest_suite_test_run_line(line) 625 elif self.is_ctest_suite_time_run(line) and \ 626 not self.state_machine.suite_is_running(): 627 self.handle_suite_started_tag(line) 628 elif self._is_result_line(line) and \ 629 self.state_machine.suite_is_running(): 630 if line.find(":") != -1 and line.count( 631 ":") >= 3 and self._is_execute_result_line(line): 632 self.handle_one_test_tag(line.strip(), False) 633 else: 634 self.handle_one_test_tag(line.strip(), True) 635 except AttributeError: 636 LOG.error("parsing log: %s failed" % (line.strip()), 637 error_no="00405") 638 self.last_line = line 639 640 def _parse_product_info(self, line): 641 if _PRODUCT_PARA_START in line: 642 self.is_params = True 643 elif _PRODUCT_PARA_END in line: 644 self.is_params = False 645 if self.is_params and re.match(_PRODUCT_PARA, line): 646 handle_product_info(line, self.product_info) 647 648 def parse_error_test_description(self, message): 649 end_time = re.match(self.pattern, message).group().strip() 650 start_time = re.match(self.pattern, 651 self.last_line.strip()).group().strip() 652 start_timestamp = int(time.mktime( 653 time.strptime(start_time, "%Y-%m-%d %H:%M:%S.%f"))) * 1000 + int( 654 start_time.split(".")[-1]) 655 end_timestamp = int(time.mktime( 656 time.strptime(end_time, "%Y-%m-%d %H:%M:%S.%f"))) * 1000 + int( 657 end_time.split(".")[-1]) 658 run_time = end_timestamp - start_timestamp 659 status_dict = {"PASS": ResultCode.PASSED, "FAIL": ResultCode.FAILED, 660 "IGNORE": ResultCode.SKIPPED} 661 status = "" 662 if message.find("PASS") != -1: 663 status = "PASS" 664 elif message.find("FAIL") != -1: 665 status = "FAIL" 666 elif message.find("IGNORE") != -1: 667 status = "IGNORE" 668 status = status_dict.get(status) 669 return "", "", status, run_time 670 671 def parse_test_description(self, message): 672 673 test_class = message.split(".c:")[0].split(" ")[-1].split("/")[-1] 674 message_index = message.index(".c:") 675 end_time = re.match(self.pattern, message).group().strip() 676 start_time = re.match(self.pattern, 677 self.last_line.strip()).group().strip() 678 start_timestamp = int(time.mktime( 679 time.strptime(start_time, "%Y-%m-%d %H:%M:%S.%f"))) * 1000 + int( 680 start_time.split(".")[-1]) 681 end_timestamp = int(time.mktime( 682 time.strptime(end_time, "%Y-%m-%d %H:%M:%S.%f"))) * 1000 + int( 683 end_time.split(".")[-1]) 684 run_time = end_timestamp - start_timestamp 685 message_list = message[message_index + 3:].split(":") 686 test_name, status = message_list[1].strip(), message_list[2].strip() 687 status_dict = {"PASS": ResultCode.PASSED, "FAIL": ResultCode.FAILED, 688 "IGNORE": ResultCode.SKIPPED} 689 status = status_dict.get(status) 690 return test_class, test_name, status, run_time 691 692 def handle_one_test_tag(self, message, is_error): 693 if is_error: 694 test_class, test_name, status, run_time = \ 695 self.parse_error_test_description(message) 696 else: 697 test_class, test_name, status, run_time = \ 698 self.parse_test_description(message) 699 test_result = self.state_machine.test(reset=True) 700 test_result.test_class = test_class 701 test_result.test_name = test_name 702 test_result.run_time = run_time 703 self.state_machine.running_test_index += 1 704 test_result.current = self.state_machine.running_test_index 705 test_result.code = status.value 706 self.state_machine.suite().run_time += run_time 707 for listener in self.get_listeners(): 708 test_result = copy.copy(test_result) 709 listener.__started__(LifeCycle.TestCase, test_result) 710 711 test_suite = self.state_machine.suite() 712 test_suites = self.state_machine.get_suites() 713 714 found_unexpected_test = False 715 716 if found_unexpected_test or ResultCode.FAILED == status: 717 if "FAIL:" in message and not message.endswith("FAIL:"): 718 test_result.stacktrace = message[ 719 message.rindex("FAIL:") + len( 720 "FAIL:"):] 721 for listener in self.get_listeners(): 722 result = copy.copy(test_result) 723 listener.__failed__(LifeCycle.TestCase, result) 724 elif ResultCode.SKIPPED == status: 725 for listener in self.get_listeners(): 726 result = copy.copy(test_result) 727 listener.__failed__(LifeCycle.TestCase, result) 728 729 self.state_machine.test().is_completed = True 730 test_suite.test_num += 1 731 test_suites.test_num += 1 732 733 for listener in self.get_listeners(): 734 result = copy.copy(test_result) 735 listener.__ended__(LifeCycle.TestCase, result) 736 737 def handle_suites_started_tag(self): 738 self.state_machine.get_suites(reset=True) 739 test_suites = self.state_machine.get_suites() 740 test_suites.suites_name = self.suites_name 741 test_suites.product_info = self.product_info 742 test_suites.test_num = 0 743 for listener in self.get_listeners(): 744 suite_report = copy.copy(test_suites) 745 listener.__started__(LifeCycle.TestSuites, suite_report) 746 747 def handle_suite_started_tag(self, message): 748 if re.match("{}{}".format(self.pattern, _CTEST_SUITE_TIME_RUN_TAG), 749 message.strip()): 750 message = self.state_machine.suite().suite_name 751 self.state_machine.suite(reset=True) 752 test_suite = self.state_machine.suite() 753 test_suite.suite_name = message 754 test_suite.test_num = 0 755 for listener in self.get_listeners(): 756 suite_report = copy.copy(test_suite) 757 listener.__started__(LifeCycle.TestSuite, suite_report) 758 759 def handle_suite_ended_tag(self, line): 760 suite_result = self.state_machine.suite() 761 suites = self.state_machine.get_suites() 762 suite_result.run_time = suite_result.run_time 763 suites.run_time += suite_result.run_time 764 suite_result.is_completed = True 765 766 for listener in self.get_listeners(): 767 suite = copy.copy(suite_result) 768 listener.__ended__(LifeCycle.TestSuite, suite, is_clear=True) 769 self.state_machine.running_test_index = 0 770 771 def process_suites_ended_tag(self): 772 suites = self.state_machine.get_suites() 773 suites.is_completed = True 774 775 for listener in self.get_listeners(): 776 listener.__ended__(LifeCycle.TestSuites, test_result=suites, 777 suites_name=suites.suites_name, 778 product_info=suites.product_info) 779 780 def append_test_output(self, message): 781 if self.state_machine.test().stacktrace: 782 self.state_machine.test().stacktrace = "{}\r\n".format( 783 self.state_machine.test().stacktrace) 784 self.state_machine.test().stacktrace = "{}{}".format( 785 self.state_machine.test().stacktrace, message) 786 787 788@Plugin(type=Plugin.PARSER, id=ParserType.open_source_test) 789class OpenSourceParser(IParser): 790 def __init__(self): 791 self.state_machine = StateRecorder() 792 self.suite_name = "" 793 self.test_name = "" 794 self.test_num = 1 795 self.listeners = [] 796 self.output = "" 797 self.lines = [] 798 self.start_time = None 799 800 def get_suite_name(self): 801 return self.suite_name 802 803 def get_listeners(self): 804 return self.listeners 805 806 def __process__(self, lines): 807 if not self.start_time: 808 self.start_time = datetime.datetime.now() 809 self.lines.extend(lines) 810 811 def __done__(self): 812 if not self.state_machine.suites_is_started(): 813 self.state_machine.trace_logs.extend(self.lines) 814 self.handle_suite_started_tag(self.test_num) 815 816 test_result = self.state_machine.test(reset=True, 817 test_index=self.test_name) 818 test_result.run_time = 0 819 test_result.test_class = self.suite_name 820 test_result.test_name = self.test_name 821 test_result.test_num = 1 822 test_result.current = 1 823 for listener in self.get_listeners(): 824 result = copy.copy(test_result) 825 listener.__started__(LifeCycle.TestCase, result) 826 for line in self.lines: 827 self.output = "{}{}".format(self.output, line) 828 if _TEST_PASSED_LOWER in line.lower(): 829 test_result.code = ResultCode.PASSED.value 830 if self.start_time: 831 end_time = datetime.datetime.now() 832 run_time = (end_time - self.start_time).total_seconds() 833 test_result.run_time = int(run_time * 1000) 834 for listener in self.get_listeners(): 835 result = copy.copy(test_result) 836 listener.__ended__(LifeCycle.TestCase, result) 837 break 838 else: 839 test_result.code = ResultCode.FAILED.value 840 test_result.stacktrace = "\\n".join(self.lines) 841 if self.start_time: 842 end_time = datetime.datetime.now() 843 run_time = (end_time - self.start_time).total_seconds() 844 test_result.run_time = int(run_time * 1000) 845 for listener in self.get_listeners(): 846 result = copy.copy(test_result) 847 listener.__ended__(LifeCycle.TestCase, result) 848 849 self.state_machine.test().is_completed = True 850 self.handle_suite_ended_tag() 851 852 def handle_suite_started_tag(self, test_num): 853 test_suite = self.state_machine.suite() 854 if test_num >= 0: 855 test_suite.suite_name = self.suite_name 856 test_suite.test_num = test_num 857 for listener in self.get_listeners(): 858 suite_report = copy.copy(test_suite) 859 listener.__started__(LifeCycle.TestSuite, suite_report) 860 861 def handle_suite_ended_tag(self): 862 suite_result = self.state_machine.suite() 863 for listener in self.get_listeners(): 864 suite = copy.copy(suite_result) 865 listener.__ended__(LifeCycle.TestSuite, suite, 866 suite_report=True) 867 868 869@Plugin(type=Plugin.PARSER, id=ParserType.build_only_test) 870class BuildOnlyParser(IParser): 871 def __init__(self): 872 self.state_machine = StateRecorder() 873 self.suite_name = "" 874 self.test_name = "" 875 self.test_num = 0 876 self.listeners = [] 877 self.output = "" 878 879 def get_suite_name(self): 880 return self.suite_name 881 882 def get_listeners(self): 883 return self.listeners 884 885 def __process__(self, lines): 886 if not self.state_machine.suites_is_started(): 887 self.state_machine.trace_logs.extend(lines) 888 self.handle_suite_started_tag(self.test_num) 889 890 self.state_machine.running_test_index = \ 891 self.state_machine.running_test_index + 1 892 893 for line in lines: 894 if re.match(_COMPILE_PARA, line): 895 self.test_name = str(line).split('compile')[0].strip() 896 test_result = self.state_machine.test(reset=True) 897 test_result.run_time = 0 898 test_result.test_class = self.suite_name 899 test_result.test_name = self.test_name 900 for listener in self.get_listeners(): 901 result = copy.copy(test_result) 902 listener.__started__(LifeCycle.TestCase, result) 903 if _COMPILE_PASSED in line: 904 test_result.code = ResultCode.PASSED.value 905 for listener in self.get_listeners(): 906 result = copy.copy(test_result) 907 listener.__ended__(LifeCycle.TestCase, result) 908 else: 909 test_result.code = ResultCode.FAILED.value 910 for listener in self.get_listeners(): 911 result = copy.copy(test_result) 912 listener.__failed__(LifeCycle.TestCase, result) 913 self.state_machine.test().is_completed = True 914 915 def __done__(self): 916 self.handle_suite_ended_tag() 917 918 def handle_suite_started_tag(self, test_num): 919 test_suite = self.state_machine.suite() 920 if test_num >= 0: 921 test_suite.suite_name = self.suite_name 922 test_suite.test_num = test_num 923 for listener in self.get_listeners(): 924 suite_report = copy.copy(test_suite) 925 listener.__started__(LifeCycle.TestSuite, suite_report) 926 927 def handle_suite_ended_tag(self): 928 suite_result = self.state_machine.suite() 929 for listener in self.get_listeners(): 930 suite = copy.copy(suite_result) 931 listener.__ended__(LifeCycle.TestSuite, suite, 932 suite_report=True) 933 934 935@Plugin(type=Plugin.PARSER, id=ParserType.jsuit_test_lite) 936class JSUnitParserLite(IParser): 937 last_line = "" 938 pattern = r"(\d{1,2}-\d{1,2}\s\d{1,2}:\d{1,2}:\d{1,2}\.\d{3}) " 939 940 def __init__(self): 941 self.state_machine = StateRecorder() 942 self.suites_name = "" 943 self.listeners = [] 944 945 def get_listeners(self): 946 return self.listeners 947 948 def __process__(self, lines): 949 if not self.state_machine.suites_is_started(): 950 self.state_machine.trace_logs.extend(lines) 951 for line in lines: 952 self.parse(line) 953 954 def __done__(self): 955 pass 956 957 def parse(self, line): 958 if (self.state_machine.suites_is_started() or 959 line.find(_START_JSUNIT_RUN_MARKER) != -1) and \ 960 line.find(_ACE_LOG_MARKER) != -1: 961 if line.find(_START_JSUNIT_RUN_MARKER) != -1: 962 self.handle_suites_started_tag() 963 elif line.endswith(_END_JSUNIT_RUN_MARKER): 964 self.handle_suites_ended_tag() 965 elif line.find(_START_JSUNIT_SUITE_RUN_MARKER) != -1: 966 self.handle_suite_started_tag(line.strip()) 967 elif line.endswith(_START_JSUNIT_SUITE_END_MARKER): 968 self.handle_suite_ended_tag() 969 elif _PASS_JSUNIT_MARKER in line or _FAIL_JSUNIT_MARKER \ 970 in line: 971 self.handle_one_test_tag(line.strip()) 972 self.last_line = line 973 974 def parse_test_description(self, message): 975 pattern = r"\[(pass|fail)\]" 976 year = time.strftime("%Y") 977 filter_message = message.split("[Console Info]")[1].strip() 978 end_time = "%s-%s" % \ 979 (year, re.match(self.pattern, message).group().strip()) 980 start_time = "%s-%s" % \ 981 (year, re.match(self.pattern, 982 self.last_line.strip()).group().strip()) 983 start_timestamp = int(time.mktime( 984 time.strptime(start_time, "%Y-%m-%d %H:%M:%S.%f"))) * 1000 + int( 985 start_time.split(".")[-1]) 986 end_timestamp = int(time.mktime( 987 time.strptime(end_time, "%Y-%m-%d %H:%M:%S.%f"))) * 1000 + int( 988 end_time.split(".")[-1]) 989 run_time = end_timestamp - start_timestamp 990 _, status_end_index = re.match(pattern, filter_message).span() 991 status = filter_message[:status_end_index] 992 test_name = filter_message[status_end_index:] 993 status_dict = {"pass": ResultCode.PASSED, "fail": ResultCode.FAILED, 994 "ignore": ResultCode.SKIPPED} 995 status = status_dict.get(status[1:-1]) 996 return test_name, status, run_time 997 998 def handle_suites_started_tag(self): 999 self.state_machine.get_suites(reset=True) 1000 test_suites = self.state_machine.get_suites() 1001 test_suites.suites_name = self.suites_name 1002 test_suites.test_num = 0 1003 for listener in self.get_listeners(): 1004 suite_report = copy.copy(test_suites) 1005 listener.__started__(LifeCycle.TestSuites, suite_report) 1006 1007 def handle_suites_ended_tag(self): 1008 suites = self.state_machine.get_suites() 1009 suites.is_completed = True 1010 1011 for listener in self.get_listeners(): 1012 listener.__ended__(LifeCycle.TestSuites, test_result=suites, 1013 suites_name=suites.suites_name) 1014 1015 def handle_one_test_tag(self, message): 1016 test_name, status, run_time = \ 1017 self.parse_test_description(message) 1018 test_result = self.state_machine.test(reset=True) 1019 test_suite = self.state_machine.suite() 1020 test_result.test_class = test_suite.suite_name 1021 test_result.test_name = test_name 1022 test_result.run_time = run_time 1023 test_result.code = status.value 1024 test_result.current = self.state_machine.running_test_index + 1 1025 self.state_machine.suite().run_time += run_time 1026 for listener in self.get_listeners(): 1027 test_result = copy.copy(test_result) 1028 listener.__started__(LifeCycle.TestCase, test_result) 1029 1030 test_suites = self.state_machine.get_suites() 1031 found_unexpected_test = False 1032 1033 if found_unexpected_test or ResultCode.FAILED == status: 1034 for listener in self.get_listeners(): 1035 result = copy.copy(test_result) 1036 listener.__failed__(LifeCycle.TestCase, result) 1037 elif ResultCode.SKIPPED == status: 1038 for listener in self.get_listeners(): 1039 result = copy.copy(test_result) 1040 listener.__skipped__(LifeCycle.TestCase, result) 1041 1042 self.state_machine.test().is_completed = True 1043 test_suite.test_num += 1 1044 test_suites.test_num += 1 1045 for listener in self.get_listeners(): 1046 result = copy.copy(test_result) 1047 listener.__ended__(LifeCycle.TestCase, result) 1048 self.state_machine.running_test_index += 1 1049 1050 def fake_run_marker(self, message): 1051 fake_marker = re.compile(" +").split(message) 1052 self.processTestStartedTag(fake_marker) 1053 1054 def handle_suite_started_tag(self, message): 1055 self.state_machine.suite(reset=True) 1056 test_suite = self.state_machine.suite() 1057 if re.match(r".*\[suite start\].*", message): 1058 _, index = re.match(r".*\[suite start\]", message).span() 1059 test_suite.suite_name = message[index:] 1060 test_suite.test_num = 0 1061 for listener in self.get_listeners(): 1062 suite_report = copy.copy(test_suite) 1063 listener.__started__(LifeCycle.TestSuite, suite_report) 1064 1065 def handle_suite_ended_tag(self): 1066 suite_result = self.state_machine.suite() 1067 suites = self.state_machine.get_suites() 1068 suite_result.run_time = suite_result.run_time 1069 suites.run_time += suite_result.run_time 1070 suite_result.is_completed = True 1071 1072 for listener in self.get_listeners(): 1073 suite = copy.copy(suite_result) 1074 listener.__ended__(LifeCycle.TestSuite, suite, is_clear=True) 1075 1076 def append_test_output(self, message): 1077 if self.state_machine.test().stacktrace: 1078 self.state_machine.test().stacktrace = \ 1079 "%s\r\n" % self.state_machine.test().stacktrace 1080 self.state_machine.test().stacktrace = \ 1081 ''.join((self.state_machine.test().stacktrace, message)) 1082 1083 1084class ShellHandler: 1085 def __init__(self, parsers): 1086 self.parsers = [] 1087 self.unfinished_line = "" 1088 self.output_queue = Queue() 1089 for parser in parsers: 1090 if isinstance(parser, IParser): 1091 self.parsers.append(parser) 1092 else: 1093 raise TypeError( 1094 "Parser {} must implement IOutputParser interface.".format( 1095 parser, )) 1096 1097 def _process_output(self, output, end_mark="\n"): 1098 content = output 1099 if self.unfinished_line: 1100 content = "".join((self.unfinished_line, content)) 1101 self.unfinished_line = "" 1102 lines = content.split(end_mark) 1103 if content.endswith(end_mark): 1104 # get rid of the tail element of this list contains empty str 1105 return lines[:-1] 1106 else: 1107 self.unfinished_line = lines[-1] 1108 # not return the tail element of this list contains unfinished str, 1109 # so we set position -1 1110 return lines 1111 1112 def __read__(self, output): 1113 lines = self._process_output(output) 1114 for line in lines: 1115 for parser in self.parsers: 1116 try: 1117 parser.__process__([line]) 1118 except (ValueError, TypeError, SyntaxError, AttributeError) \ 1119 as error: 1120 LOG.debug("Parse %s line error: %s" % (line, error)) 1121 1122 def __error__(self, message): 1123 if message: 1124 for parser in self.parsers: 1125 parser.__process__([message]) 1126 1127 def __done__(self, result_code="", message=""): 1128 msg_fmt = "" 1129 if message: 1130 msg_fmt = ", message is {}".format(message) 1131 for parser in self.parsers: 1132 parser.__process__([message]) 1133 if not check_pub_key_exist(): 1134 LOG.debug("result code is: {}{}".format(result_code, msg_fmt)) 1135 for parser in self.parsers: 1136 parser.__done__() 1137 1138 1139def handle_product_info(message, product_info): 1140 message = message[message.index("The"):] 1141 items = message[len("The "):].split(" is ") 1142 product_info.setdefault(items[0].strip(), 1143 items[1].strip().strip("[").strip("]")) 1144