• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# coding=utf-8
3
4#
5# Copyright (c) 2022 Huawei Device Co., Ltd.
6# Licensed under the Apache License, Version 2.0 (the "License");
7# you may not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10#     http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS,
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15# See the License for the specific language governing permissions and
16# limitations under the License.
17#
18
19import copy
20import re
21import threading
22import time
23import json
24from enum import Enum
25
26from xdevice import LifeCycle
27from xdevice import IParser
28from xdevice import platform_logger
29from xdevice import Plugin
30from xdevice import check_pub_key_exist
31from xdevice import StateRecorder
32from xdevice import TestDescription
33from xdevice import ResultCode
34from xdevice import CommonParserType
35from xdevice import get_cst_time
36from xdevice import get_delta_time_ms
37
38__all__ = ["CppTestParser", "CppTestListParser", "JunitParser", "JSUnitParser",
39           "OHKernelTestParser", "OHJSUnitTestParser",
40           "OHJSUnitTestListParser", "_ACE_LOG_MARKER", "OHRustTestParser"]
41
42_INFORMATIONAL_MARKER = "[----------]"
43_START_TEST_RUN_MARKER = "[==========] Running"
44_TEST_RUN_MARKER = "[==========]"
45_GTEST_DRYRUN_MARKER = "Running main() "
46_START_TEST_MARKER = "[ RUN      ]"
47_OK_TEST_MARKER = "[       OK ]"
48_SKIPPED_TEST_MARKER = "[  SKIPPED ]"
49_FAILED_TEST_MARKER = "[  FAILED  ]"
50_ALT_OK_MARKER = "[    OK    ]"
51_TIMEOUT_MARKER = "[ TIMEOUT  ]"
52
53_START_JSUNIT_RUN_MARKER = "[start] start run suites"
54_START_JSUNIT_SUITE_RUN_MARKER = "[suite start]"
55_START_JSUNIT_SUITE_END_MARKER = "[suite end]"
56_END_JSUNIT_RUN_MARKER = "[end] run suites end"
57_PASS_JSUNIT_MARKER = "[pass]"
58_FAIL_JSUNIT_MARKER = "[fail]"
59_ERROR_JSUNIT_MARKER = "[error]"
60_ACE_LOG_MARKER = "jsapp"
61
62"""
63OpenHarmony Kernel Test
64"""
65RUNTEST_TEST = "runtest test"
66START_TO_TEST = "Start to test"
67FINISHED_TO_TEST = "Finished to test"
68TIMEOUT_TESTCASES = "Timeout testcases"
69FAIL_DOT = "FAIL."
70PASS_DOT = "PASS."
71ERROR_EXCLAMATION = "ERROR!!!"
72TIMEOUT_EXCLAMATION = "TIMEOUT!"
73
74
75LOG = platform_logger("Parser")
76
77
78@Plugin(type=Plugin.PARSER, id=CommonParserType.cpptest)
79class CppTestParser(IParser):
80    def __init__(self):
81        self.state_machine = StateRecorder()
82        self.suite_name = ""
83        self.listeners = []
84        self.product_info = {}
85        self.is_params = False
86        self.start_time = get_cst_time()
87        self.suite_start_time = get_cst_time()
88
89    def get_suite_name(self):
90        return self.suite_name
91
92    def get_listeners(self):
93        return self.listeners
94
95    def __process__(self, lines):
96        if not self.state_machine.suites_is_started():
97            self.state_machine.trace_logs.extend(lines)
98        for line in lines:
99            LOG.debug(line)
100            self.parse(line)
101
102    def __done__(self):
103        suite_result = self.state_machine.get_suites()
104        if not suite_result.suites_name:
105            return
106        for listener in self.get_listeners():
107            suites = copy.copy(suite_result)
108            listener.__ended__(LifeCycle.TestSuites, test_result=suites,
109                               suites_name=suites.suites_name,
110                               product_info=suites.product_info)
111        self.state_machine.current_suites = None
112
113    def parse(self, line):
114
115        if self.state_machine.suites_is_started() or line.startswith(
116                _TEST_RUN_MARKER):
117            if line.startswith(_START_TEST_RUN_MARKER):
118                message = line[len(_TEST_RUN_MARKER):].strip()
119                self.handle_suites_started_tag(message)
120            elif line.startswith(_INFORMATIONAL_MARKER):
121                pattern = r"(.*) (\(\d+ ms total\))"
122                message = line[len(_INFORMATIONAL_MARKER):].strip()
123                if re.match(pattern, line.strip()):
124                    self.handle_suite_ended_tag(message)
125                elif re.match(r'(\d+) test[s]? from (.*)', message):
126                    self.handle_suite_started_tag(message)
127            elif line.startswith(_TEST_RUN_MARKER):
128                if not self.state_machine.suites_is_running():
129                    return
130                message = line[len(_TEST_RUN_MARKER):].strip()
131                self.handle_suites_ended_tag(message)
132            elif line.startswith(_START_TEST_MARKER):
133                # Individual test started
134                message = line[len(_START_TEST_MARKER):].strip()
135                self.handle_test_started_tag(message)
136            else:
137                self.process_test(line)
138
139    def process_test(self, line):
140        if _SKIPPED_TEST_MARKER in line:
141            message = line[line.index(_SKIPPED_TEST_MARKER) + len(
142                _SKIPPED_TEST_MARKER):].strip()
143            if not self.state_machine.test_is_running():
144                LOG.error(
145                    "Found {} without {} before, wrong GTest log format".
146                    format(line, _START_TEST_MARKER))
147                return
148            self.handle_test_ended_tag(message, ResultCode.SKIPPED)
149        elif _OK_TEST_MARKER in line:
150            message = line[line.index(_OK_TEST_MARKER) + len(
151                _OK_TEST_MARKER):].strip()
152            if not self.state_machine.test_is_running():
153                LOG.error(
154                    "Found {} without {} before, wrong GTest log format".
155                    format(line, _START_TEST_MARKER))
156                return
157            self.handle_test_ended_tag(message, ResultCode.PASSED)
158        elif _ALT_OK_MARKER in line:
159            message = line[line.index(_ALT_OK_MARKER) + len(
160                _ALT_OK_MARKER):].strip()
161            self.fake_run_marker(message)
162            self.handle_test_ended_tag(message, ResultCode.PASSED)
163        elif _FAILED_TEST_MARKER in line:
164            message = line[line.index(_FAILED_TEST_MARKER) + len(
165                _FAILED_TEST_MARKER):].strip()
166            if not self.state_machine.suite_is_running():
167                return
168            if not self.state_machine.test_is_running():
169                self.fake_run_marker(message)
170            self.handle_test_ended_tag(message, ResultCode.FAILED)
171        elif _TIMEOUT_MARKER in line:
172            message = line[line.index(_TIMEOUT_MARKER) + len(
173                _TIMEOUT_MARKER):].strip()
174            self.fake_run_marker(message)
175            self.handle_test_ended_tag(message, ResultCode.FAILED)
176        elif self.state_machine.test_is_running():
177            self.append_test_output(line)
178
179    def handle_test_suite_failed(self, error_msg):
180        error_msg = "Unknown error" if error_msg is None else error_msg
181        LOG.info("Test run failed: {}".format(error_msg))
182        if self.state_machine.test_is_running():
183            self.state_machine.test().is_completed = True
184            for listener in self.get_listeners():
185                test_result = copy.copy(self.currentTestResult)
186                listener.__failed__(LifeCycle.TestCase, test_result)
187                listener.__ended__(LifeCycle.TestCase, test_result)
188        self.state_machine.suite().stacktrace = error_msg
189        self.state_machine.suite().is_completed = True
190        for listener in self.get_listeners():
191            suite_result = copy.copy(self.currentSuiteResult)
192            listener.__failed__(LifeCycle.TestSuite, suite_result)
193            listener.__ended__(LifeCycle.TestSuite, suite_result)
194
195    def handle_test_started_tag(self, message):
196        test_class, test_name, _ = self.parse_test_description(
197            message)
198        test_result = self.state_machine.test(reset=True)
199        test_result.test_class = test_class
200        test_result.test_name = test_name
201        self.start_time = get_cst_time()
202        for listener in self.get_listeners():
203            test_result = copy.copy(test_result)
204            listener.__started__(LifeCycle.TestCase, test_result)
205
206    @classmethod
207    def parse_test_description(cls, message):
208        run_time = 0
209        matcher = re.match(r'(.*) \((\d+) ms\)', message)
210        if matcher:
211            test_class, test_name = matcher.group(1).rsplit(".", 1)
212            run_time = int(matcher.group(2))
213        else:
214            test_class, test_name = message.rsplit(".", 1)
215        return test_class, test_name, run_time
216
217    def handle_test_ended_tag(self, message, test_status):
218        test_class, test_name, run_time = self.parse_test_description(
219            message)
220        test_result = self.state_machine.test()
221        test_result.run_time = get_delta_time_ms(self.start_time)
222        if test_result.run_time == 0 or test_result.run_time < run_time:
223            test_result.run_time = run_time
224        test_result.code = test_status.value
225        test_result.current = self.state_machine.running_test_index + 1
226        if not test_result.is_running():
227            LOG.error(
228                "Test has no start tag when trying to end test: %s", message)
229            return
230        found_unexpected_test = False
231        if test_result.test_class != test_class:
232            LOG.error(
233                "Expected class: {} but got:{} ".format(test_result.test_class,
234                                                        test_class))
235            found_unexpected_test = True
236        if test_result.test_name != test_name:
237            LOG.error(
238                "Expected test: {} but got: {}".format(test_result.test_name,
239                                                       test_name))
240            found_unexpected_test = True
241
242        if found_unexpected_test or ResultCode.FAILED == test_status:
243            for listener in self.get_listeners():
244                result = copy.copy(test_result)
245                listener.__failed__(LifeCycle.TestCase, result)
246        elif ResultCode.SKIPPED == test_status:
247            for listener in self.get_listeners():
248                result = copy.copy(test_result)
249                listener.__skipped__(LifeCycle.TestCase, result)
250
251        self.state_machine.test().is_completed = True
252        for listener in self.get_listeners():
253            result = copy.copy(test_result)
254            listener.__ended__(LifeCycle.TestCase, result)
255        self.state_machine.running_test_index += 1
256
257    def fake_run_marker(self, message):
258        fake_marker = re.compile(" +").split(message)
259        self.handle_test_started_tag(fake_marker)
260
261    def handle_suites_started_tag(self, message):
262        self.state_machine.get_suites(reset=True)
263        matcher = re.match(r'Running (\d+) test[s]? from .*', message)
264        expected_test_num = int(matcher.group(1)) if matcher else -1
265        if expected_test_num >= 0:
266            test_suites = self.state_machine.get_suites()
267            test_suites.suites_name = self.get_suite_name()
268            test_suites.test_num = expected_test_num
269            test_suites.product_info = self.product_info
270            for listener in self.get_listeners():
271                suite_report = copy.copy(test_suites)
272                listener.__started__(LifeCycle.TestSuites, suite_report)
273
274    def handle_suite_started_tag(self, message):
275        self.state_machine.suite(reset=True)
276        matcher = re.match(r'(\d+) test[s]? from (.*)', message)
277        expected_test_num = int(matcher.group(1)) if matcher else -1
278        if expected_test_num >= 0:
279            test_suite = self.state_machine.suite()
280            test_suite.suite_name = matcher.group(2)
281            test_suite.test_num = expected_test_num
282            self.suite_start_time = get_cst_time()
283            for listener in self.get_listeners():
284                suite_report = copy.copy(test_suite)
285                listener.__started__(LifeCycle.TestSuite, suite_report)
286
287    def handle_suite_ended_tag(self, message):
288        self.state_machine.running_test_index = 0
289        suite_result = self.state_machine.suite()
290        suite_result.run_time = get_delta_time_ms(self.suite_start_time)
291        matcher = re.match(r'.*\((\d+) ms total\)', message)
292        if matcher and suite_result.run_time == 0:
293            suite_result.run_time = int(matcher.group(1))
294        suite_result.is_completed = True
295        for listener in self.get_listeners():
296            suite = copy.copy(suite_result)
297            listener.__ended__(LifeCycle.TestSuite, suite, is_clear=True)
298
299    def handle_suites_ended_tag(self, message):
300        suites = self.state_machine.get_suites()
301        matcher = re.match(r'.*\((\d+) ms total\)', message)
302        if matcher:
303            suites.run_time = int(matcher.group(1))
304        suites.is_completed = True
305        for listener in self.get_listeners():
306            copy_suites = copy.copy(suites)
307            listener.__ended__(LifeCycle.TestSuites, test_result=copy_suites,
308                               suites_name=suites.suites_name,
309                               product_info=suites.product_info,
310                               suite_report=True)
311
312    def append_test_output(self, message):
313        if self.state_machine.test().stacktrace:
314            self.state_machine.test().stacktrace += "\r\n"
315        self.state_machine.test().stacktrace += message
316
317    @staticmethod
318    def handle_test_run_failed(error_msg):
319        if not error_msg:
320            error_msg = "Unknown error"
321        if not check_pub_key_exist():
322            LOG.debug("Error msg:%s" % error_msg)
323
324    def mark_test_as_blocked(self, test):
325        if not self.state_machine.current_suite and not test.class_name:
326            return
327        suites_result = self.state_machine.get_suites(reset=True)
328        suites_result.suites_name = self.get_suite_name()
329        suite_name = self.state_machine.current_suite.suite_name if \
330            self.state_machine.current_suite else None
331        suite_result = self.state_machine.suite(reset=True)
332        test_result = self.state_machine.test(reset=True)
333        suite_result.suite_name = suite_name or test.class_name
334        suite_result.suite_num = 1
335        test_result.test_class = test.class_name
336        test_result.test_name = test.test_name
337        test_result.stacktrace = "error_msg: run crashed"
338        test_result.num_tests = 1
339        test_result.run_time = 0
340        test_result.code = ResultCode.BLOCKED.value
341        for listener in self.get_listeners():
342            suite_report = copy.copy(suites_result)
343            listener.__started__(LifeCycle.TestSuites, suite_report)
344        for listener in self.get_listeners():
345            suite_report = copy.copy(suite_result)
346            listener.__started__(LifeCycle.TestSuite, suite_report)
347        for listener in self.get_listeners():
348            test_result = copy.copy(test_result)
349            listener.__started__(LifeCycle.TestCase, test_result)
350        for listener in self.get_listeners():
351            test_result = copy.copy(test_result)
352            listener.__ended__(LifeCycle.TestCase, test_result)
353        for listener in self.get_listeners():
354            suite_report = copy.copy(suite_result)
355            listener.__ended__(LifeCycle.TestSuite, suite_report,
356                               is_clear=True)
357        self.__done__()
358
359
360@Plugin(type=Plugin.PARSER, id=CommonParserType.cpptest_list)
361class CppTestListParser(IParser):
362    def __init__(self):
363        self.last_test_class_name = None
364        self.tests = []
365        self.result_data = ""
366        self.suites = dict()
367
368    def __process__(self, lines):
369        for line in lines:
370            self.result_data = "{}{}\n".format(self.result_data, line)
371            self.parse(line)
372
373    def __done__(self):
374        LOG.debug("CppTestListParser data:")
375        LOG.debug(self.result_data)
376        self.result_data = ""
377
378    def parse(self, line):
379        class_matcher = re.match('^([a-zA-Z]+.*)\\.$', line)
380        method_matcher = re.match('\\s+([a-zA-Z_]+[\\S]*)(.*)?(\\s+.*)?$',
381                                  line)
382        if class_matcher:
383            self.last_test_class_name = class_matcher.group(1)
384            if self.last_test_class_name not in self.suites:
385                self.suites.setdefault(self.last_test_class_name, [])
386        elif method_matcher:
387            if not self.last_test_class_name:
388                LOG.error("Parsed new test case name %s but no test class name"
389                          " has been set" % line)
390            else:
391                test_name = method_matcher.group(1)
392                if test_name not in self.suites.get(self.last_test_class_name, []):
393                    test = TestDescription(self.last_test_class_name,
394                                           test_name)
395                    self.tests.append(test)
396                    self.suites.get(self.last_test_class_name, []).append(test_name)
397                else:
398                    LOG.debug("[{}.{}] has already collect it, skip it.".format(
399                        self.last_test_class_name, test_name))
400        else:
401            if not check_pub_key_exist():
402                LOG.debug("Line ignored: %s" % line)
403
404
405class StatusCodes(Enum):
406    FAILURE = -2
407    START = 1
408    ERROR = -1
409    SUCCESS = 0
410    IN_PROGRESS = 2
411    IGNORE = -3
412    BLOCKED = 3
413
414
415class Prefixes(Enum):
416    STATUS = "INSTRUMENTATION_STATUS: "
417    STATUS_CODE = "INSTRUMENTATION_STATUS_CODE: "
418    STATUS_FAILED = "INSTRUMENTATION_FAILED: "
419    CODE = "INSTRUMENTATION_CODE: "
420    RESULT = "INSTRUMENTATION_RESULT: "
421    TIME_REPORT = "Time: "
422
423
424@Plugin(type=Plugin.PARSER, id=CommonParserType.junit)
425class JunitParser(IParser):
426    def __init__(self):
427        self.state_machine = StateRecorder()
428        self.suite_name = ""
429        self.listeners = []
430        self.current_key = None
431        self.current_value = None
432        self.start_time = get_cst_time()
433        self.test_time = 0
434        self.test_run_finished = False
435
436    def get_suite_name(self):
437        return self.suite_name
438
439    def get_listeners(self):
440        return self.listeners
441
442    def __process__(self, lines):
443        for line in lines:
444            if not check_pub_key_exist():
445                LOG.debug(line)
446            self.parse(line)
447
448    def __done__(self):
449        suite_result = self.state_machine.suite()
450        suite_result.run_time = self.test_time
451        suite_result.is_completed = True
452        for listener in self.get_listeners():
453            suite = copy.copy(suite_result)
454            listener.__ended__(LifeCycle.TestSuite, suite,
455                               suite_report=True)
456        self.state_machine.current_suite = None
457
458    def parse(self, line):
459        if line.startswith(Prefixes.STATUS_CODE.value):
460            self.submit_current_key_value()
461            self.parse_status_code(line)
462        elif line.startswith(Prefixes.STATUS.value):
463            self.submit_current_key_value()
464            self.parse_key(line, len(Prefixes.STATUS.value))
465        elif line.startswith(Prefixes.RESULT.value):
466            self.test_run_finished = True
467        elif line.startswith(Prefixes.STATUS_FAILED.value) or \
468                line.startswith(Prefixes.CODE.value):
469            self.submit_current_key_value()
470            self.test_run_finished = True
471        elif line.startswith(Prefixes.TIME_REPORT.value):
472            self.parse_time(line)
473        else:
474            if self.current_key == "stack" and self.current_value:
475                self.current_value = self.current_value + r"\r\n"
476                self.current_value = self.current_value + line
477            elif line:
478                pass
479
480    def parse_key(self, line, key_start_pos):
481        key_value = line[key_start_pos:].split("=", 1)
482        if len(key_value) == 2:
483            self.current_key = key_value[0]
484            self.current_value = key_value[1]
485
486    def parse_time(self, line):
487        message = line[len(Prefixes.TIME_REPORT.value):]
488        self.test_time = float(message.replace(",", "")) * 1000
489
490    @staticmethod
491    def check_legality(name):
492        if not name or name == "null":
493            return False
494        return True
495
496    def parse_status_code(self, line):
497        value = line[len(Prefixes.STATUS_CODE.value):]
498        test_info = self.state_machine.test()
499        test_info.code = int(value)
500        if test_info.code != StatusCodes.IN_PROGRESS:
501            if self.check_legality(test_info.test_class) and \
502                    self.check_legality(test_info.test_name):
503                self.report_result(test_info)
504                self.clear_current_test_info()
505
506    def clear_current_test_info(self):
507        self.state_machine.current_test = None
508
509    def submit_current_key_value(self):
510        if self.current_key and self.current_value:
511            status_value = self.current_value
512            test_info = self.state_machine.test()
513            if self.current_key == "class":
514                test_info.test_class = status_value
515            elif self.current_key == "test":
516                test_info.test_name = status_value
517            elif self.current_key == "numtests":
518                test_info.num_tests = int(status_value)
519            elif self.current_key == "Error":
520                self.handle_test_run_failed(status_value)
521            elif self.current_key == "stack":
522                test_info.stacktrace = status_value
523            elif self.current_key == "stream":
524                pass
525            self.current_key = None
526            self.current_value = None
527
528    def report_result(self, test_info):
529        if not test_info.test_name or not test_info.test_class:
530            LOG.info("Invalid instrumentation status bundle")
531            return
532        test_info.is_completed = True
533        self.report_test_run_started(test_info)
534        if test_info.code == StatusCodes.START.value:
535            self.start_time = get_cst_time()
536            for listener in self.get_listeners():
537                result = copy.copy(test_info)
538                listener.__started__(LifeCycle.TestCase, result)
539        elif test_info.code == StatusCodes.FAILURE.value:
540            self.state_machine.running_test_index += 1
541            test_info.current = self.state_machine.running_test_index
542            end_time = get_cst_time()
543            run_time = (end_time - self.start_time).total_seconds()
544            test_info.run_time = int(run_time * 1000)
545            for listener in self.get_listeners():
546                result = copy.copy(test_info)
547                result.code = ResultCode.FAILED.value
548                listener.__ended__(LifeCycle.TestCase, result)
549        elif test_info.code == StatusCodes.ERROR.value:
550            self.state_machine.running_test_index += 1
551            test_info.current = self.state_machine.running_test_index
552            end_time = get_cst_time()
553            run_time = (end_time - self.start_time).total_seconds()
554            test_info.run_time = int(run_time * 1000)
555            for listener in self.get_listeners():
556                result = copy.copy(test_info)
557                result.code = ResultCode.FAILED.value
558                listener.__ended__(LifeCycle.TestCase, result)
559        elif test_info.code == StatusCodes.SUCCESS.value:
560            self.state_machine.running_test_index += 1
561            test_info.current = self.state_machine.running_test_index
562            end_time = get_cst_time()
563            run_time = (end_time - self.start_time).total_seconds()
564            test_info.run_time = int(run_time * 1000)
565            for listener in self.get_listeners():
566                result = copy.copy(test_info)
567                result.code = ResultCode.PASSED.value
568                listener.__ended__(LifeCycle.TestCase, result)
569        elif test_info.code == StatusCodes.IGNORE.value:
570            end_time = get_cst_time()
571            run_time = (end_time - self.start_time).total_seconds()
572            test_info.run_time = int(run_time * 1000)
573            for listener in self.get_listeners():
574                result = copy.copy(test_info)
575                result.code = ResultCode.SKIPPED.value
576                listener.__skipped__(LifeCycle.TestCase, result)
577        elif test_info.code == StatusCodes.BLOCKED.value:
578            test_info.current = self.state_machine.running_test_index
579            end_time = get_cst_time()
580            run_time = (end_time - self.start_time).total_seconds()
581            test_info.run_time = int(run_time * 1000)
582            for listener in self.get_listeners():
583                result = copy.copy(test_info)
584                result.code = ResultCode.BLOCKED.value
585                listener.__ended__(LifeCycle.TestCase, result)
586
587        self.output_stack_trace(test_info)
588
589    @classmethod
590    def output_stack_trace(cls, test_info):
591        if check_pub_key_exist():
592            return
593        if test_info.stacktrace:
594            stack_lines = test_info.stacktrace.split(r"\r\n")
595            LOG.error("Stacktrace information is:")
596            for line in stack_lines:
597                line.strip()
598                if line:
599                    LOG.error(line)
600
601    def report_test_run_started(self, test_result):
602        test_suite = self.state_machine.suite()
603        if not self.state_machine.suite().is_started:
604            if not test_suite.test_num or not test_suite.suite_name:
605                test_suite.suite_name = self.get_suite_name()
606                test_suite.test_num = test_result.num_tests
607                for listener in self.get_listeners():
608                    suite_report = copy.copy(test_suite)
609                    listener.__started__(LifeCycle.TestSuite, suite_report)
610
611    @staticmethod
612    def handle_test_run_failed(error_msg):
613        if not error_msg:
614            error_msg = "Unknown error"
615        if not check_pub_key_exist():
616            LOG.debug("Error msg:%s" % error_msg)
617
618    def mark_test_as_failed(self, test):
619        test_info = self.state_machine.test()
620        if test_info:
621            test_info.test_class = test.class_name
622            test_info.test_name = test.test_name
623            test_info.code = StatusCodes.START.value
624            self.report_result(test_info)
625            test_info.code = StatusCodes.FAILURE.value
626            self.report_result(test_info)
627            self.__done__()
628
629    def mark_test_as_blocked(self, test):
630        test_info = self.state_machine.test()
631        if test_info:
632            test_info.test_class = test.class_name
633            test_info.test_name = test.test_name
634            test_info.num_tests = 1
635            test_info.run_time = 0
636            test_info.code = StatusCodes.START.value
637            self.report_result(test_info)
638            test_info.code = StatusCodes.BLOCKED.value
639            self.report_result(test_info)
640            self.__done__()
641
642
643@Plugin(type=Plugin.PARSER, id=CommonParserType.jsunit)
644class JSUnitParser(IParser):
645    last_line = ""
646    pattern = r"(\d{1,2}-\d{1,2}\s\d{1,2}:\d{1,2}:\d{1,2}\.\d{3}) "
647
648    def __init__(self):
649        self.state_machine = StateRecorder()
650        self.suites_name = ""
651        self.listeners = []
652        self.expect_tests_dict = dict()
653        self.marked_suite_set = set()
654        self.exclude_list = list()
655
656    def get_listeners(self):
657        return self.listeners
658
659    def __process__(self, lines):
660        if not self.state_machine.suites_is_started():
661            self.state_machine.trace_logs.extend(lines)
662        for line in lines:
663            self.parse(line)
664
665    def __done__(self):
666        pass
667
668    def parse(self, line):
669        if (self.state_machine.suites_is_started() or line.find(
670                _START_JSUNIT_RUN_MARKER) != -1) and \
671                line.lower().find(_ACE_LOG_MARKER) != -1:
672            if line.find(_START_JSUNIT_RUN_MARKER) != -1:
673                self.handle_suites_started_tag()
674            elif line.endswith(_END_JSUNIT_RUN_MARKER):
675                self.handle_suites_ended_tag()
676            elif line.find(_START_JSUNIT_SUITE_RUN_MARKER) != -1:
677                self.handle_suite_started_tag(line.strip())
678            elif line.endswith(_START_JSUNIT_SUITE_END_MARKER):
679                self.handle_suite_ended_tag()
680            elif _PASS_JSUNIT_MARKER in line or _FAIL_JSUNIT_MARKER \
681                    in line or _ERROR_JSUNIT_MARKER in line:
682                self.handle_one_test_tag(line.strip())
683            self.last_line = line
684
685    def parse_test_description(self, message):
686        pattern = r".*\[(pass|fail|error)\]"
687        year = time.strftime("%Y")
688        match_list = ["app Log:", "JSApp:", "JsApp:", "JSAPP:"]
689        filter_message = ""
690        for keyword in match_list:
691            if keyword in message:
692                filter_message = \
693                    message.split(r"{0}".format(keyword))[1].strip()
694                break
695        end_time = "%s-%s" % \
696                   (year, re.match(self.pattern, message).group().strip())
697        start_time = "%s-%s" % \
698                     (year, re.match(self.pattern,
699                                     self.last_line.strip()).group().strip())
700        start_timestamp = int(time.mktime(
701            time.strptime(start_time, "%Y-%m-%d %H:%M:%S.%f"))) * 1000 + int(
702            start_time.split(".")[-1])
703        end_timestamp = int(time.mktime(
704            time.strptime(end_time, "%Y-%m-%d %H:%M:%S.%f"))) * 1000 + int(
705            end_time.split(".")[-1])
706        run_time = end_timestamp - start_timestamp
707        match = re.match(pattern, filter_message)
708        _, status_end_index = match.span()
709        if " ;" in filter_message:
710            test_name = filter_message[status_end_index:
711                                       str(filter_message).find(" ;")]
712        else:
713            test_name = filter_message[status_end_index:]
714        status_dict = {"pass": ResultCode.PASSED, "fail": ResultCode.FAILED,
715                       "ignore": ResultCode.SKIPPED,
716                       "error": ResultCode.FAILED}
717        status = status_dict.get(match.group(1))
718        return test_name.strip(), status, run_time
719
720    def handle_suites_started_tag(self):
721        self.state_machine.get_suites(reset=True)
722        test_suites = self.state_machine.get_suites()
723        test_suites.suites_name = self.suites_name
724        test_suites.test_num = 0
725        for listener in self.get_listeners():
726            suite_report = copy.copy(test_suites)
727            listener.__started__(LifeCycle.TestSuites, suite_report)
728
729    def handle_suites_ended_tag(self):
730        self._mark_all_test_case()
731        suites = self.state_machine.get_suites()
732        suites.is_completed = True
733
734        for listener in self.get_listeners():
735            listener.__ended__(LifeCycle.TestSuites, test_result=suites,
736                               suites_name=suites.suites_name)
737
738    def handle_one_test_tag(self, message):
739        test_name, status, run_time = \
740            self.parse_test_description(message)
741        test_suite = self.state_machine.suite()
742        if self.exclude_list:
743            qualified_name = "{}#{}".format(test_suite.suite_name, test_name)
744            if qualified_name in self.exclude_list:
745                LOG.debug("{} will be discard!".format(qualified_name))
746                test_suite.test_num -= 1
747                return
748        test_result = self.state_machine.test(reset=True)
749        test_result.test_class = test_suite.suite_name
750        test_result.test_name = test_name
751        test_result.run_time = run_time
752        test_result.code = status.value
753        test_result.current = self.state_machine.running_test_index + 1
754        self.state_machine.suite().run_time += run_time
755        for listener in self.get_listeners():
756            test_result = copy.copy(test_result)
757            listener.__started__(LifeCycle.TestCase, test_result)
758
759        test_suites = self.state_machine.get_suites()
760        found_unexpected_test = False
761
762        if found_unexpected_test or ResultCode.FAILED == status:
763            for listener in self.get_listeners():
764                result = copy.copy(test_result)
765                listener.__failed__(LifeCycle.TestCase, result)
766        elif ResultCode.SKIPPED == status:
767            for listener in self.get_listeners():
768                result = copy.copy(test_result)
769                listener.__skipped__(LifeCycle.TestCase, result)
770
771        self.state_machine.test().is_completed = True
772        if not hasattr(test_suite, "total_cases"):
773            test_suite.test_num += 1
774        test_suites.test_num += 1
775        for listener in self.get_listeners():
776            result = copy.copy(test_result)
777            listener.__ended__(LifeCycle.TestCase, result)
778        self.state_machine.running_test_index += 1
779
780    def fake_run_marker(self, message):
781        fake_marker = re.compile(" +").split(message)
782        self.processTestStartedTag(fake_marker)
783
784    def handle_suite_started_tag(self, message):
785        self.state_machine.suite(reset=True)
786        self.state_machine.running_test_index = 0
787        test_suite = self.state_machine.suite()
788        if "total cases:" in message:
789            m_result = re.match(r".*\[suite start](.+), total cases: (\d+)",
790                                message)
791            if m_result:
792                expect_test_num = m_result.group(2)
793                test_suite.suite_name = m_result.group(1)
794                test_suite.test_num = int(expect_test_num)
795                setattr(test_suite, "total_cases", True)
796
797        else:
798            if re.match(r".*\[suite start].*", message):
799                _, index = re.match(r".*\[suite start]", message).span()
800                if message[index:]:
801                    test_suite.suite_name = message[index:]
802                else:
803                    test_suite.suite_name = self.suite_name
804                test_suite.test_num = 0
805        for listener in self.get_listeners():
806            suite_report = copy.copy(test_suite)
807            listener.__started__(LifeCycle.TestSuite, suite_report)
808
809    def handle_suite_ended_tag(self):
810        suite_result = self.state_machine.suite()
811        suites = self.state_machine.get_suites()
812        suite_result.run_time = suite_result.run_time
813        suites.run_time += suite_result.run_time
814        suite_result.is_completed = True
815        self._mark_test_case(suite_result, self.get_listeners())
816        for listener in self.get_listeners():
817            suite = copy.copy(suite_result)
818            listener.__ended__(LifeCycle.TestSuite, suite, is_clear=True)
819
820    def append_test_output(self, message):
821        if self.state_machine.test().stacktrace:
822            self.state_machine.test().stacktrace = \
823                "%s\r\n" % self.state_machine.test().stacktrace
824        self.state_machine.test().stacktrace = \
825            ''.join((self.state_machine.test().stacktrace, message))
826
827    def _mark_test_case(self, suite, listeners):
828        if not self.expect_tests_dict:
829            return
830        tests_list = []
831        for listener in listeners:
832            if listener.__class__.__name__ == "ReportListener":
833                tests_list.extend(listener.tests.values())
834                break
835        test_name_list = []
836        for item_test in tests_list:
837            test_name_list.append(item_test.test_name)
838        self.marked_suite_set.add(suite.suite_name)
839        test_in_cur = self.expect_tests_dict.get(suite.suite_name, [])
840        for test in test_in_cur:
841            if "{}#{}".format(suite.suite_name, test.test_name) \
842                    in self.exclude_list:
843                suite.test_num -= 1
844                continue
845            if test.test_name not in test_name_list:
846                self._mock_test_case_life_cycle(listeners, test)
847
848    def _mock_test_case_life_cycle(self, listeners, test):
849        test_result = self.state_machine.test(reset=True)
850        test_result.test_class = test.class_name
851        test_result.test_name = test.test_name
852        test_result.stacktrace = "error_msg: mark blocked"
853        test_result.num_tests = 1
854        test_result.run_time = 0
855        test_result.current = self.state_machine.running_test_index + 1
856        test_result.code = ResultCode.BLOCKED.value
857        test_result = copy.copy(test_result)
858        for listener in listeners:
859            listener.__started__(LifeCycle.TestCase, test_result)
860        test_result = copy.copy(test_result)
861        for listener in listeners:
862            listener.__ended__(LifeCycle.TestCase, test_result)
863        self.state_machine.running_test_index += 1
864
865    def _mark_all_test_case(self):
866        if not self.expect_tests_dict:
867            return
868        all_suite_set = set(self.expect_tests_dict.keys())
869        un_suite_set = all_suite_set.difference(self.marked_suite_set)
870        for un_suite_name in un_suite_set:
871            test_list = self.expect_tests_dict.get(un_suite_name, [])
872
873            self.state_machine.suite(reset=True)
874            self.state_machine.running_test_index = 0
875            test_suite = self.state_machine.suite()
876            test_suite.suite_name = un_suite_name
877            test_suite.test_num = len(test_list)
878            for listener in self.get_listeners():
879                suite_report = copy.copy(test_suite)
880                listener.__started__(LifeCycle.TestSuite, suite_report)
881
882            for test in test_list:
883                if "{}#{}".format(test_suite.suite_name, test.test_name) \
884                        in self.exclude_list:
885                    test_suite.test_num -= 1
886                    continue
887                self._mock_test_case_life_cycle(self.get_listeners(), test)
888
889            test_suite.is_completed = True
890            for listener in self.get_listeners():
891                suite = copy.copy(test_suite)
892                listener.__ended__(LifeCycle.TestSuite, suite, is_clear=True)
893
894
895@Plugin(type=Plugin.PARSER, id=CommonParserType.oh_kernel_test)
896class OHKernelTestParser(IParser):
897
898    def __init__(self):
899        self.state_machine = StateRecorder()
900        self.suites_name = ""
901        self.listeners = []
902
903    def get_listeners(self):
904        return self.listeners
905
906    def __process__(self, lines):
907        if not self.state_machine.suites_is_started():
908            self.state_machine.trace_logs.extend(lines)
909        for line in lines:
910            self.parse(line)
911
912    def __done__(self):
913        pass
914
915    def parse(self, line):
916        line = re.sub('\x1b.*?m', '', line)
917        if self.state_machine.suites_is_started() or RUNTEST_TEST in line:
918            if RUNTEST_TEST in line:
919                self.handle_suites_started_tag(line)
920            elif START_TO_TEST in line:
921                self.handle_suite_start_tag(line)
922            elif FINISHED_TO_TEST in line:
923                self.handle_suite_end_tag(line)
924            elif line.endswith(PASS_DOT) or line.endswith(FAIL_DOT):
925                self.handle_one_test_case_tag(line)
926            elif line.endswith(ERROR_EXCLAMATION) \
927                    or line.endswith(TIMEOUT_EXCLAMATION):
928                self.handle_test_case_error(line)
929            elif TIMEOUT_TESTCASES in line:
930                self.handle_suites_ended_tag(line)
931
932    def handle_suites_started_tag(self, line):
933        self.state_machine.get_suites(reset=True)
934        test_suites = self.state_machine.get_suites()
935        test_suites.suites_name = self.suites_name
936        test_suites.test_num = 0
937        for listener in self.get_listeners():
938            suite_report = copy.copy(test_suites)
939            listener.__started__(LifeCycle.TestSuites, suite_report)
940
941    def handle_suites_ended_tag(self, line):
942        suites = self.state_machine.get_suites()
943        suites.is_completed = True
944
945        for listener in self.get_listeners():
946            listener.__ended__(LifeCycle.TestSuites, test_result=suites,
947                               suites_name=suites.suites_name)
948
949    def handle_suite_start_tag(self, line):
950        pattern = "^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}" \
951                  " Start to test (.+)$"
952        matcher = re.match(pattern, line)
953        if matcher and matcher.group(1):
954            self.state_machine.suite(reset=True)
955            test_suite = self.state_machine.suite()
956            test_suite.suite_name = matcher.group(1)
957            for listener in self.get_listeners():
958                suite_report = copy.copy(test_suite)
959                listener.__started__(LifeCycle.TestSuite, suite_report)
960
961    def handle_suite_end_tag(self, line):
962        pattern = "^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}" \
963                  " Finished to test (.+)$"
964        matcher = re.match(pattern, line)
965        if matcher and matcher.group(1):
966            suite_result = self.state_machine.suite()
967            suites = self.state_machine.get_suites()
968            suite_result.run_time = suite_result.run_time
969            suites.run_time += suite_result.run_time
970            suite_result.is_completed = True
971
972            for listener in self.get_listeners():
973                suite = copy.copy(suite_result)
974                listener.__ended__(LifeCycle.TestSuite, suite, is_clear=True)
975
976    def handle_one_test_case_tag(self, line):
977        pattern = "^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} (.+) " \
978                  "(PASS)\\.$"
979        matcher = re.match(pattern, line)
980        if not (matcher and matcher.group(1) and matcher.group(2)):
981            return
982        test_result = self.state_machine.test(reset=True)
983        test_suite = self.state_machine.suite()
984        test_result.test_class = test_suite.suite_name
985        test_result.test_name = matcher.group(1)
986        test_result.current = self.state_machine.running_test_index + 1
987        for listener in self.get_listeners():
988            test_result = copy.copy(test_result)
989            listener.__started__(LifeCycle.TestCase, test_result)
990
991        test_suites = self.state_machine.get_suites()
992        if PASS_DOT in line:
993            test_result.code = ResultCode.PASSED.value
994        elif FAIL_DOT in line:
995            test_result.code = ResultCode.FAILED.value
996            for listener in self.get_listeners():
997                result = copy.copy(test_result)
998                listener.__failed__(LifeCycle.TestCase, result)
999        self.state_machine.test().is_completed = True
1000        test_suite.test_num += 1
1001        test_suites.test_num += 1
1002        for listener in self.get_listeners():
1003            result = copy.copy(test_result)
1004            listener.__ended__(LifeCycle.TestCase, result)
1005        self.state_machine.running_test_index += 1
1006
1007    def handle_test_case_error(self, line):
1008        pattern = "^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} (.+) " \
1009                  "(ERROR!!!|TIMEOUT!)$"
1010        matcher = re.match(pattern, line)
1011        if not (matcher and matcher.group(1) and matcher.group(2)):
1012            return
1013        test_result = self.state_machine.test(reset=True)
1014        test_suite = self.state_machine.suite()
1015        test_result.test_class = test_suite.suite_name
1016        test_result.test_name = matcher.group(1)
1017        test_result.current = self.state_machine.running_test_index + 1
1018        for listener in self.get_listeners():
1019            test_result = copy.copy(test_result)
1020            listener.__started__(LifeCycle.TestCase, test_result)
1021
1022        test_suites = self.state_machine.get_suites()
1023        if ERROR_EXCLAMATION in line:
1024            test_result.code = ResultCode.FAILED.value
1025        elif TIMEOUT_EXCLAMATION in line:
1026            test_result.code = ResultCode.BLOCKED.value
1027
1028        for listener in self.get_listeners():
1029            result = copy.copy(test_result)
1030            listener.__failed__(LifeCycle.TestCase, result)
1031        self.state_machine.test().is_completed = True
1032        test_suite.test_num += 1
1033        test_suites.test_num += 1
1034        for listener in self.get_listeners():
1035            result = copy.copy(test_result)
1036            listener.__ended__(LifeCycle.TestCase, result)
1037        self.state_machine.running_test_index += 1
1038
1039
1040class OHJSUnitPrefixes(Enum):
1041    SUM = "OHOS_REPORT_SUM: "
1042    STATUS = "OHOS_REPORT_STATUS: "
1043    STATUS_CODE = "OHOS_REPORT_STATUS_CODE: "
1044    RESULT = "OHOS_REPORT_RESULT: "
1045    CODE = "OHOS_REPORT_CODE: "
1046    TEST_FINISHED_RESULT_MSG = "TestFinished-ResultMsg: "
1047
1048
1049class OHJSUnitItemConstants(Enum):
1050    CLASS = "class"
1051    TEST = "test"
1052    NUM_TESTS = "numtests"
1053    STACK = "stack"
1054    STREAM = "stream"
1055    SUITE_CONSUMING = "suiteconsuming"
1056    CONSUMING = "consuming"
1057    APP_DIED = "App died"
1058
1059
1060@Plugin(type=Plugin.PARSER, id=CommonParserType.oh_jsunit)
1061class OHJSUnitTestParser(IParser):
1062
1063    def __init__(self):
1064        self.state_machine = StateRecorder()
1065        self.suites_name = ""
1066        self.listeners = []
1067        self.current_key = None
1068        self.current_value = None
1069        self.start_time = get_cst_time()
1070        self.suite_start_time = get_cst_time()
1071        self.test_time = 0
1072        self.test_run_finished = False
1073        self.cur_sum = -1
1074        self.runner = None
1075
1076    def get_suite_name(self):
1077        return self.suites_name
1078
1079    def get_listeners(self):
1080        return self.listeners
1081
1082    def __process__(self, lines):
1083        for line in lines:
1084            LOG.debug(line)
1085            self.parse(line)
1086
1087    def parse(self, line):
1088        if not str(line).strip():
1089            return
1090        if line.startswith(OHJSUnitPrefixes.SUM.value):
1091            self.handle_sum_line(line)
1092        elif line.startswith(OHJSUnitPrefixes.STATUS.value):
1093            self.handle_status_line(line)
1094        elif line.startswith(OHJSUnitPrefixes.STATUS_CODE.value):
1095            self.submit_current_key_value()
1096            self.parse_status_code(line)
1097        elif line.startswith(OHJSUnitPrefixes.TEST_FINISHED_RESULT_MSG.value):
1098            self._handle_result_msg(line)
1099
1100    def handle_sum_line(self, line):
1101        value = line[len(OHJSUnitPrefixes.SUM.value):].split("=", 1)[0]
1102        self.cur_sum = int(value)
1103
1104    def handle_status_line(self, line):
1105        self.parse_key(line, len(OHJSUnitPrefixes.STATUS.value))
1106        if self.cur_sum > 0 and \
1107                self.current_key == OHJSUnitItemConstants.CLASS.value:
1108            if self.current_value not in self.runner.suite_recorder.keys():
1109                current_suite = self.state_machine.suite(reset=True)
1110                current_suite.test_num = self.cur_sum
1111                current_suite.suite_name = self.current_value
1112                self.runner.suite_recorder.update({
1113                    self.current_value:
1114                        [len(self.runner.suite_recorder.keys()),
1115                         current_suite]})
1116            else:
1117                current_suite = self.runner.suite_recorder.get(
1118                    self.current_value)[1]
1119                self.state_machine.current_suite = current_suite
1120            self.cur_sum = -1
1121            self.current_key = None
1122            self.current_value = None
1123            self.state_machine.running_test_index = 0
1124            self.suite_start_time = get_cst_time()
1125            for listener in self.get_listeners():
1126                suite = copy.copy(current_suite)
1127                listener.__started__(LifeCycle.TestSuite, suite)
1128
1129        else:
1130            if self.current_key == OHJSUnitItemConstants.SUITE_CONSUMING.value:
1131                self.test_time = int(self.current_value)
1132                self.handle_suite_end()
1133            elif self.current_key == OHJSUnitItemConstants.CONSUMING.value:
1134                self.test_time = int(self.current_value)
1135                self.handle_case_end()
1136            else:
1137                self.submit_current_key_value()
1138                self.parse_key(line, len(OHJSUnitPrefixes.STATUS.value))
1139
1140    def submit_current_key_value(self):
1141        if self.current_key and self.current_value:
1142            status_value = self.current_value
1143            test_info = self.state_machine.test()
1144            if self.current_key == OHJSUnitItemConstants.CLASS.value:
1145                test_info.test_class = status_value
1146            elif self.current_key == OHJSUnitItemConstants.TEST.value:
1147                test_info.test_name = status_value
1148            elif self.current_key == OHJSUnitItemConstants.NUM_TESTS.value:
1149                test_info.num_tests = int(status_value)
1150            elif self.current_key == OHJSUnitItemConstants.STREAM.value:
1151                test_info.stacktrace = status_value
1152            self.current_key = None
1153            self.current_value = None
1154
1155    def parse_key(self, line, key_start_pos):
1156        key_value = line[key_start_pos:].split("=", 1)
1157        if len(key_value) == 2:
1158            self.current_key = key_value[0]
1159            self.current_value = key_value[1]
1160
1161    def parse_status_code(self, line):
1162        value = line[len(OHJSUnitPrefixes.STATUS_CODE.value):]
1163        test_info = self.state_machine.test()
1164        test_info.code = int(value)
1165        if test_info.code != StatusCodes.IN_PROGRESS:
1166            if self.check_legality(test_info.test_class) and \
1167                    self.check_legality(test_info.test_name):
1168                self.report_result(test_info)
1169
1170    def clear_current_test_info(self):
1171        self.state_machine.current_test = None
1172
1173    def report_result(self, test_info):
1174        if not test_info.test_name or not test_info.test_class:
1175            LOG.info("Invalid instrumentation status bundle")
1176            return
1177        if test_info.code == StatusCodes.START.value:
1178            self.start_time = get_cst_time()
1179            for listener in self.get_listeners():
1180                result = copy.copy(test_info)
1181                listener.__started__(LifeCycle.TestCase, result)
1182            return
1183        if test_info.code == StatusCodes.FAILURE.value:
1184            self.state_machine.running_test_index += 1
1185            test_info.current = self.state_machine.running_test_index
1186            test_info.code = ResultCode.FAILED.value
1187            test_info.run_time = get_delta_time_ms(self.start_time)
1188        elif test_info.code == StatusCodes.ERROR.value:
1189            self.state_machine.running_test_index += 1
1190            test_info.current = self.state_machine.running_test_index
1191            test_info.code = ResultCode.FAILED.value
1192            test_info.run_time = get_delta_time_ms(self.start_time)
1193        elif test_info.code == StatusCodes.SUCCESS.value:
1194            self.state_machine.running_test_index += 1
1195            test_info.current = self.state_machine.running_test_index
1196            test_info.code = ResultCode.PASSED.value
1197            test_info.run_time = get_delta_time_ms(self.start_time)
1198
1199    @classmethod
1200    def output_stack_trace(cls, test_info):
1201        if check_pub_key_exist():
1202            return
1203        if test_info.stacktrace:
1204            stack_lines = test_info.stacktrace.split(r"\r\n")
1205            LOG.error("Stacktrace information is:")
1206            for line in stack_lines:
1207                line.strip()
1208                if line:
1209                    LOG.error(line)
1210
1211    @staticmethod
1212    def check_legality(name):
1213        if not name or name == "null":
1214            return False
1215        return True
1216
1217    def __done__(self):
1218        pass
1219
1220    def handle_case_end(self):
1221        test_info = self.state_machine.test()
1222        if test_info.run_time == 0 or test_info.run_time < self.test_time:
1223            test_info.run_time = self.test_time
1224        for listener in self.get_listeners():
1225            result = copy.copy(test_info)
1226            result.code = test_info.code
1227            listener.__ended__(LifeCycle.TestCase, result)
1228            if listener.__class__.__name__ == "ReportListener" \
1229                    and self.runner.retry_times > 1:
1230                index = list(listener.tests.keys())[-1]
1231                listener.tests.pop(index)
1232        test_info.is_completed = True
1233        self.clear_current_test_info()
1234
1235    def handle_suite_end(self):
1236        suite_result = self.state_machine.suite()
1237        suite_result.run_time = get_delta_time_ms(self.suite_start_time)
1238        if suite_result.run_time == 0:
1239            suite_result.run_time = self.test_time
1240        suite_result.is_completed = True
1241        for listener in self.get_listeners():
1242            suite = copy.copy(suite_result)
1243            listener.__ended__(LifeCycle.TestSuite, suite, is_clear=True)
1244
1245    def handle_suites_end(self):
1246        suite_result = self.state_machine.suite()
1247        if not suite_result.is_completed:
1248            self.handle_suite_end()
1249        for listener in self.get_listeners():
1250            if listener.__class__.__name__ == "ReportListener":
1251                self._cal_result(listener)
1252            suite = copy.copy(suite_result)
1253            listener.__ended__(LifeCycle.TestSuites, suite,
1254                               suites_name=self.suites_name)
1255        self.state_machine.current_suite = None
1256
1257    def _cal_result(self, report_listener):
1258        result_len = len(report_listener.result)
1259        suites_len = len(report_listener.suites)
1260        if result_len > suites_len:
1261            diff_result_tuple_list = report_listener.result[suites_len:]
1262            report_listener.result = report_listener.result[:suites_len]
1263            for diff_result_tuple in diff_result_tuple_list:
1264                suite, case_result_list = diff_result_tuple
1265                pos = self.runner.suite_recorder.get(suite.suite_name)[0]
1266                report_listener.result[pos][1].extend(case_result_list)
1267        self._handle_lacking_one_testcase(report_listener)
1268        self._handle_lacking_whole_suite(report_listener)
1269
1270    def _handle_lacking_one_testcase(self, report_listener):
1271        for suite in report_listener.suites.values():
1272            test_des_list = self.runner.expect_tests_dict.get(
1273                suite.suite_name, [])
1274            pos = self.runner.suite_recorder.get(suite.suite_name)[0]
1275            if len(test_des_list) == len(report_listener.result[pos][1]):
1276                continue
1277            interval = len(test_des_list) - len(report_listener.result[pos][1])
1278            if len(test_des_list) > 0:
1279                LOG.info("{} tests in {} had missed.".format(
1280                    interval, suite.suite_name))
1281            else:
1282                LOG.info("The count of tests in '{}' is incorrect! {} test "
1283                         "form dry run and {} tests have run."
1284                         "".format(suite.suite_name, len(test_des_list),
1285                                   len(report_listener.result[pos][1])))
1286            for test_des in test_des_list:
1287                is_contain = False
1288                for case in report_listener.result[pos][1]:
1289                    if case.test_name == test_des.test_name:
1290                        is_contain = True
1291                        break
1292                if not is_contain:
1293                    test_result = self.state_machine.test(reset=True)
1294                    test_result.test_class = test_des.class_name
1295                    test_result.test_name = test_des.test_name
1296                    test_result.stacktrace = "error_msg:mark blocked"
1297                    test_result.num_tests = 1
1298                    test_result.run_time = 0
1299                    test_result.current = \
1300                        self.state_machine.running_test_index + 1
1301                    test_result.code = ResultCode.BLOCKED.value
1302                    report_listener.result[pos][1].append(test_result)
1303                    LOG.debug("Add {}#{}".format(test_des.class_name,
1304                                                 test_des.test_name))
1305
1306    def _handle_lacking_whole_suite(self, report_listener):
1307        all_suite_set = set(self.runner.expect_tests_dict.keys())
1308        un_suite_set = set()
1309        if len(all_suite_set) > len(report_listener.suites):
1310            suite_name_set = set()
1311            for suite in report_listener.suites.values():
1312                suite_name_set.add(suite.suite_name)
1313            un_suite_set = all_suite_set.difference(suite_name_set)
1314            if un_suite_set:
1315                LOG.info("{} suites have missed.".format(len(un_suite_set)))
1316        for name in un_suite_set:
1317            self.state_machine.running_test_index = 0
1318            test_des_list = self.runner.expect_tests_dict.get(
1319                name, [])
1320            current_suite = self.state_machine.suite(reset=True)
1321            current_suite.test_num = len(test_des_list)
1322            current_suite.suite_name = name
1323            for listener in self.get_listeners():
1324                suite = copy.copy(current_suite)
1325                listener.__started__(LifeCycle.TestSuite, suite)
1326
1327            for test in test_des_list:
1328                test_result = self.state_machine.test(reset=True)
1329                test_result.test_class = test.class_name
1330                test_result.test_name = test.test_name
1331                test_result.stacktrace = "error_msg:mark blocked"
1332                test_result.num_tests = 1
1333                test_result.run_time = 0
1334                test_result.current = self.state_machine.running_test_index + 1
1335                test_result.code = ResultCode.BLOCKED.value
1336                test_result = copy.copy(test_result)
1337                for listener in self.get_listeners():
1338                    listener.__started__(LifeCycle.TestCase, test_result)
1339                test_result = copy.copy(test_result)
1340                for listener in self.get_listeners():
1341                    listener.__ended__(LifeCycle.TestCase, test_result)
1342                self.state_machine.running_test_index += 1
1343            current_suite.run_time = self.test_time
1344            current_suite.is_completed = True
1345            for listener in self.get_listeners():
1346                suite = copy.copy(current_suite)
1347                listener.__ended__(LifeCycle.TestSuite, suite, is_clear=True)
1348
1349    def notify_task_finished(self):
1350        self.handle_suites_end()
1351
1352    def _handle_result_msg(self, line):
1353        if OHJSUnitItemConstants.APP_DIED.value in line:
1354            test_result = self.state_machine.test()
1355            suite = self.state_machine.suite()
1356            if not test_result.is_completed:
1357                if self.check_legality(test_result.test_class) and \
1358                        self.check_legality(test_result.test_name):
1359                    self.report_result(test_result)
1360                    self.clear_current_test_info()
1361            if not suite.is_completed:
1362                self.handle_suite_end()
1363
1364
1365@Plugin(type=Plugin.PARSER, id=CommonParserType.oh_jsunit_list)
1366class OHJSUnitTestListParser(IParser):
1367
1368    def __init__(self):
1369        self.tests = []
1370        self.json_str = ""
1371        self.tests_dict = dict()
1372        self.result_data = ""
1373
1374    def __process__(self, lines):
1375        for line in lines:
1376            self.result_data = "{}{}".format(self.result_data, line)
1377            self.parse(line)
1378
1379    def __done__(self):
1380        LOG.debug("OHJSUnitTestListParser data:")
1381        LOG.debug(self.result_data)
1382        self.result_data = ""
1383
1384    def parse(self, line):
1385        if "{" in line or "}" in line:
1386            self.json_str = "%s%s" % (self.json_str, line)
1387            return
1388        if "dry run finished" in line:
1389            suite_dict_list = json.loads(self.json_str).get("suites", [])
1390            for suite_dict in suite_dict_list:
1391                for class_name, test_name_dict_list in suite_dict.items():
1392                    self.tests_dict.update({class_name.strip(): []})
1393                    for test_name_dict in test_name_dict_list:
1394                        for test_name in test_name_dict.values():
1395                            test = TestDescription(class_name.strip(),
1396                                                   test_name.strip())
1397                            self.tests_dict.get(
1398                                class_name.strip()).append(test)
1399                            self.tests.append(test)
1400
1401
1402@Plugin(type=Plugin.PARSER, id=CommonParserType.oh_rust)
1403class OHRustTestParser(IParser):
1404
1405    def __init__(self):
1406        self.test_pattern = "test (?:tests::)?(.+) ... (ok|FAILED)"
1407        self.stout_pattern = "---- tests::(.+) stdout ----"
1408        self.running_pattern = "running (\\d+) test|tests"
1409        self.test_result_pattern = "test result: (ok|FAILED)\\..+finished in (.+)s"
1410        self.suite_name = ""
1411        self.result_list = list()
1412        self.stdout_list = list()
1413        self.failures_stdout = list()
1414        self.cur_fail_case = ""
1415        self.state_machine = StateRecorder()
1416        self.listeners = []
1417
1418    def get_listeners(self):
1419        return self.listeners
1420
1421    def __process__(self, lines):
1422        for line in lines:
1423            LOG.debug(line)
1424            self.parse(line)
1425
1426    def __done__(self):
1427        self.handle_suite_end()
1428
1429    def parse(self, line):
1430        if line.startswith("running"):
1431            matcher = re.match(self.running_pattern, line)
1432            if not (matcher and matcher.group(1)):
1433                return
1434            self.handle_suite_start(matcher)
1435        elif line.startswith("test result:"):
1436            matcher = re.match(self.test_result_pattern, line)
1437            if not (matcher and matcher.group(2)):
1438                return
1439            self.handle_case_lifecycle(matcher)
1440
1441        elif "..." in line:
1442            matcher = re.match(self.test_pattern, line)
1443            if not (matcher and matcher.group(1) and matcher.group(2)):
1444                return
1445            self.collect_case(matcher)
1446        elif line.startswith("---- tests::"):
1447            matcher = re.match(self.stout_pattern, line)
1448            if not (matcher and matcher.group(1)):
1449                return
1450            self.cur_fail_case = matcher.group(1)
1451        else:
1452            if self.cur_fail_case:
1453                self.handle_stdout(line)
1454
1455    def handle_case_lifecycle(self, matcher):
1456        cost_time = matcher.group(2)
1457        for test_result in self.result_list:
1458            if test_result.code == ResultCode.FAILED.value:
1459                if self.stdout_list and \
1460                        self.stdout_list[0][0] == test_result.test_name:
1461                    test_result.stacktrace = self.stdout_list[0][1]
1462                    self.stdout_list.pop(0)
1463            test_result.current = self.state_machine.running_test_index + 1
1464            for listener in self.get_listeners():
1465                test_result = copy.copy(test_result)
1466                listener.__started__(LifeCycle.TestCase, test_result)
1467            for listener in self.get_listeners():
1468                result = copy.copy(test_result)
1469                listener.__ended__(LifeCycle.TestCase, result)
1470        test_suite = self.state_machine.suite()
1471        test_suite.run_time = float(cost_time) * 1000
1472
1473    def handle_stdout(self, line):
1474        if line.strip():
1475            self.failures_stdout.append(line.strip())
1476        else:
1477            self.stdout_list.append((self.cur_fail_case,
1478                                     " ".join(self.failures_stdout)))
1479            self.cur_fail_case = ""
1480            self.failures_stdout.clear()
1481
1482    def collect_case(self, matcher):
1483        test_result = self.state_machine.test(reset=True)
1484        test_result.test_class = self.suite_name
1485        test_result.test_name = matcher.group(1)
1486        test_result.code = ResultCode.PASSED.value if \
1487            matcher.group(2) == "ok" else ResultCode.FAILED.value
1488        self.result_list.append(test_result)
1489
1490    def handle_suite_start(self, matcher):
1491        self.state_machine.suite(reset=True)
1492        test_suite = self.state_machine.suite()
1493        test_suite.suite_name = self.suite_name
1494        test_suite.test_num = int(matcher.group(1))
1495        for listener in self.get_listeners():
1496            suite_report = copy.copy(test_suite)
1497            listener.__started__(LifeCycle.TestSuite, suite_report)
1498
1499    def handle_suite_end(self):
1500        suite_result = self.state_machine.suite()
1501        suite_result.run_time += suite_result.run_time
1502        suite_result.is_completed = True
1503        for listener in self.get_listeners():
1504            suite = copy.copy(suite_result)
1505            listener.__ended__(LifeCycle.TestSuite, suite, suite_report=True)
1506
1507
1508@Plugin(type=Plugin.PARSER, id=CommonParserType.oh_yara)
1509class OHYaraTestParser(IParser):
1510    last_line = ""
1511    pattern = r"(\d{1,2}-\d{1,2}\s\d{1,2}:\d{1,2}:\d{1,2}\.\d{3}) "
1512
1513    def __init__(self):
1514        self.state_machine = StateRecorder()
1515        self.suites_name = ""
1516        self.vul_items = None
1517        self.listeners = []
1518
1519    def get_listeners(self):
1520        return self.listeners
1521
1522    def __process__(self, lines):
1523        self.parse(lines)
1524
1525    def __done__(self):
1526        pass
1527
1528    def parse(self, lines):
1529        for line in lines:
1530            if line:
1531                self.handle_suites_started_tag()
1532                self.handle_suite_started_tag()
1533                self.handle_one_test_tag(line)
1534                self.handle_suite_ended_tag()
1535                self.handle_suites_ended_tag()
1536
1537    def handle_suites_started_tag(self):
1538        self.state_machine.get_suites(reset=True)
1539        test_suites = self.state_machine.get_suites()
1540        test_suites.suites_name = self.suites_name
1541        test_suites.test_num = len(self.vul_items)
1542        for listener in self.get_listeners():
1543            suite_report = copy.copy(test_suites)
1544            listener.__started__(LifeCycle.TestSuites, suite_report)
1545
1546    def handle_suites_ended_tag(self):
1547        suites = self.state_machine.get_suites()
1548        suites.is_completed = True
1549        for listener in self.get_listeners():
1550            listener.__ended__(LifeCycle.TestSuites, test_result=suites,
1551                               suites_name=suites.suites_name)
1552
1553    def handle_one_test_tag(self, message):
1554        status_dict = {"pass": ResultCode.PASSED, "fail": ResultCode.FAILED,
1555                       "block": ResultCode.BLOCKED}
1556        message = message.strip().split("|")
1557        test_name = message[0]
1558        status = status_dict.get(message[3])
1559        trace = message[6] if message[3] else ""
1560        run_time = 0
1561        test_suite = self.state_machine.suite()
1562        test_result = self.state_machine.test(reset=True)
1563        test_result.test_class = test_suite.suite_name
1564        test_result.test_name = test_name
1565        test_result.run_time = run_time
1566        test_result.code = status.value
1567        test_result.stacktrace = trace
1568        test_result.current = self.state_machine.running_test_index + 1
1569        self.state_machine.suite().run_time += run_time
1570        for listener in self.get_listeners():
1571            test_result = copy.copy(test_result)
1572            listener.__started__(LifeCycle.TestCase, test_result)
1573
1574        test_suites = self.state_machine.get_suites()
1575        self.state_machine.test().is_completed = True
1576        test_suites.test_num += 1
1577        for listener in self.get_listeners():
1578            result = copy.copy(test_result)
1579            listener.__ended__(LifeCycle.TestCase, result)
1580        self.state_machine.running_test_index += 1
1581
1582    def handle_suite_started_tag(self):
1583        self.state_machine.suite(reset=True)
1584        self.state_machine.running_test_index = 0
1585        test_suite = self.state_machine.suite()
1586        test_suite.suite_name = self.suites_name
1587        test_suite.test_num = 1
1588        for listener in self.get_listeners():
1589            suite_report = copy.copy(test_suite)
1590            listener.__started__(LifeCycle.TestSuite, suite_report)
1591
1592    def handle_suite_ended_tag(self):
1593        suite_result = self.state_machine.suite()
1594        suites = self.state_machine.get_suites()
1595        suite_result.run_time = suite_result.run_time
1596        suites.run_time += suite_result.run_time
1597        suite_result.is_completed = True
1598        for listener in self.get_listeners():
1599            suite = copy.copy(suite_result)
1600            listener.__ended__(LifeCycle.TestSuite, suite, is_clear=True)