• 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 = [" a0c0d0", " a03d00"]
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            line = str(line).strip().rstrip("\r")
100            LOG.debug(line)
101            self.parse(line)
102
103    def __done__(self):
104        suite_result = self.state_machine.get_suites()
105        if not suite_result.suites_name:
106            return
107        for listener in self.get_listeners():
108            suites = copy.copy(suite_result)
109            listener.__ended__(LifeCycle.TestSuites, test_result=suites,
110                               suites_name=suites.suites_name,
111                               product_info=suites.product_info)
112        self.state_machine.current_suites = None
113
114    def parse(self, line):
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            if line.endswith("\r") or line.endswith("\n"):
371                line = str(line).replace("\r", "").replace("\n", "")
372            line = line.rstrip()
373            line = str(line).strip().rstrip("\r")
374            self.result_data = "{}{}\n".format(self.result_data, line)
375            self.parse(line)
376
377    def __done__(self):
378        LOG.debug("CppTestListParser data:")
379        LOG.debug(self.result_data)
380        self.result_data = ""
381
382    def parse(self, line):
383        class_matcher = re.match('^([a-zA-Z]+.*)\\.$', line)
384        method_matcher = re.match('\\s+([a-zA-Z_]+[\\S]*)(.*)?(\\s+.*)?$',
385                                  line)
386        if class_matcher:
387            self.last_test_class_name = class_matcher.group(1)
388            if self.last_test_class_name not in self.suites:
389                self.suites.setdefault(self.last_test_class_name, [])
390        elif method_matcher:
391            if not self.last_test_class_name:
392                LOG.error("Parsed new test case name %s but no test class name"
393                          " has been set" % line)
394            else:
395                test_name = method_matcher.group(1)
396                if test_name not in self.suites.get(self.last_test_class_name, []):
397                    test = TestDescription(self.last_test_class_name,
398                                           test_name)
399                    self.tests.append(test)
400                    self.suites.get(self.last_test_class_name, []).append(test_name)
401                else:
402                    LOG.debug("[{}.{}] has already collect it, skip it.".format(
403                        self.last_test_class_name, test_name))
404        else:
405            if not check_pub_key_exist():
406                LOG.debug("Line ignored: %s" % line)
407
408
409class StatusCodes(Enum):
410    FAILURE = -2
411    START = 1
412    ERROR = -1
413    SUCCESS = 0
414    IN_PROGRESS = 2
415    IGNORE = -3
416    BLOCKED = 3
417
418
419class Prefixes(Enum):
420    STATUS = "INSTRUMENTATION_STATUS: "
421    STATUS_CODE = "INSTRUMENTATION_STATUS_CODE: "
422    STATUS_FAILED = "INSTRUMENTATION_FAILED: "
423    CODE = "INSTRUMENTATION_CODE: "
424    RESULT = "INSTRUMENTATION_RESULT: "
425    TIME_REPORT = "Time: "
426
427
428@Plugin(type=Plugin.PARSER, id=CommonParserType.junit)
429class JunitParser(IParser):
430    def __init__(self):
431        self.state_machine = StateRecorder()
432        self.suite_name = ""
433        self.listeners = []
434        self.current_key = None
435        self.current_value = None
436        self.start_time = get_cst_time()
437        self.test_time = 0
438        self.test_run_finished = False
439
440    def get_suite_name(self):
441        return self.suite_name
442
443    def get_listeners(self):
444        return self.listeners
445
446    def __process__(self, lines):
447        for line in lines:
448            if not check_pub_key_exist():
449                LOG.debug(line)
450            self.parse(line)
451
452    def __done__(self):
453        suite_result = self.state_machine.suite()
454        suite_result.run_time = self.test_time
455        suite_result.is_completed = True
456        for listener in self.get_listeners():
457            suite = copy.copy(suite_result)
458            listener.__ended__(LifeCycle.TestSuite, suite,
459                               suite_report=True)
460        self.state_machine.current_suite = None
461
462    def parse(self, line):
463        if line.startswith(Prefixes.STATUS_CODE.value):
464            self.submit_current_key_value()
465            self.parse_status_code(line)
466        elif line.startswith(Prefixes.STATUS.value):
467            self.submit_current_key_value()
468            self.parse_key(line, len(Prefixes.STATUS.value))
469        elif line.startswith(Prefixes.RESULT.value):
470            self.test_run_finished = True
471        elif line.startswith(Prefixes.STATUS_FAILED.value) or \
472                line.startswith(Prefixes.CODE.value):
473            self.submit_current_key_value()
474            self.test_run_finished = True
475        elif line.startswith(Prefixes.TIME_REPORT.value):
476            self.parse_time(line)
477        else:
478            if self.current_key == "stack" and self.current_value:
479                self.current_value = self.current_value + r"\r\n"
480                self.current_value = self.current_value + line
481            elif line:
482                pass
483
484    def parse_key(self, line, key_start_pos):
485        key_value = line[key_start_pos:].split("=", 1)
486        if len(key_value) == 2:
487            self.current_key = key_value[0]
488            self.current_value = key_value[1]
489
490    def parse_time(self, line):
491        message = line[len(Prefixes.TIME_REPORT.value):]
492        self.test_time = float(message.replace(",", "")) * 1000
493
494    @staticmethod
495    def check_legality(name):
496        if not name or name == "null":
497            return False
498        return True
499
500    def parse_status_code(self, line):
501        value = line[len(Prefixes.STATUS_CODE.value):]
502        test_info = self.state_machine.test()
503        test_info.code = int(value)
504        if test_info.code != StatusCodes.IN_PROGRESS:
505            if self.check_legality(test_info.test_class) and \
506                    self.check_legality(test_info.test_name):
507                self.report_result(test_info)
508                self.clear_current_test_info()
509
510    def clear_current_test_info(self):
511        self.state_machine.current_test = None
512
513    def submit_current_key_value(self):
514        if self.current_key and self.current_value:
515            status_value = self.current_value
516            test_info = self.state_machine.test()
517            if self.current_key == "class":
518                test_info.test_class = status_value
519            elif self.current_key == "test":
520                test_info.test_name = status_value
521            elif self.current_key == "numtests":
522                test_info.num_tests = int(status_value)
523            elif self.current_key == "Error":
524                self.handle_test_run_failed(status_value)
525            elif self.current_key == "stack":
526                test_info.stacktrace = status_value
527            elif self.current_key == "stream":
528                pass
529            self.current_key = None
530            self.current_value = None
531
532    def report_result(self, test_info):
533        if not test_info.test_name or not test_info.test_class:
534            LOG.info("Invalid instrumentation status bundle")
535            return
536        test_info.is_completed = True
537        self.report_test_run_started(test_info)
538        if test_info.code == StatusCodes.START.value:
539            self.start_time = get_cst_time()
540            for listener in self.get_listeners():
541                result = copy.copy(test_info)
542                listener.__started__(LifeCycle.TestCase, result)
543        elif test_info.code == StatusCodes.FAILURE.value:
544            self.state_machine.running_test_index += 1
545            test_info.current = self.state_machine.running_test_index
546            end_time = get_cst_time()
547            run_time = (end_time - self.start_time).total_seconds()
548            test_info.run_time = int(run_time * 1000)
549            for listener in self.get_listeners():
550                result = copy.copy(test_info)
551                result.code = ResultCode.FAILED.value
552                listener.__ended__(LifeCycle.TestCase, result)
553        elif test_info.code == StatusCodes.ERROR.value:
554            self.state_machine.running_test_index += 1
555            test_info.current = self.state_machine.running_test_index
556            end_time = get_cst_time()
557            run_time = (end_time - self.start_time).total_seconds()
558            test_info.run_time = int(run_time * 1000)
559            for listener in self.get_listeners():
560                result = copy.copy(test_info)
561                result.code = ResultCode.FAILED.value
562                listener.__ended__(LifeCycle.TestCase, result)
563        elif test_info.code == StatusCodes.SUCCESS.value:
564            self.state_machine.running_test_index += 1
565            test_info.current = self.state_machine.running_test_index
566            end_time = get_cst_time()
567            run_time = (end_time - self.start_time).total_seconds()
568            test_info.run_time = int(run_time * 1000)
569            for listener in self.get_listeners():
570                result = copy.copy(test_info)
571                result.code = ResultCode.PASSED.value
572                listener.__ended__(LifeCycle.TestCase, result)
573        elif test_info.code == StatusCodes.IGNORE.value:
574            end_time = get_cst_time()
575            run_time = (end_time - self.start_time).total_seconds()
576            test_info.run_time = int(run_time * 1000)
577            for listener in self.get_listeners():
578                result = copy.copy(test_info)
579                result.code = ResultCode.SKIPPED.value
580                listener.__skipped__(LifeCycle.TestCase, result)
581        elif test_info.code == StatusCodes.BLOCKED.value:
582            test_info.current = self.state_machine.running_test_index
583            end_time = get_cst_time()
584            run_time = (end_time - self.start_time).total_seconds()
585            test_info.run_time = int(run_time * 1000)
586            for listener in self.get_listeners():
587                result = copy.copy(test_info)
588                result.code = ResultCode.BLOCKED.value
589                listener.__ended__(LifeCycle.TestCase, result)
590
591        self.output_stack_trace(test_info)
592
593    @classmethod
594    def output_stack_trace(cls, test_info):
595        if check_pub_key_exist():
596            return
597        if test_info.stacktrace:
598            stack_lines = test_info.stacktrace.split(r"\r\n")
599            LOG.error("Stacktrace information is:")
600            for line in stack_lines:
601                line.strip()
602                if line:
603                    LOG.error(line)
604
605    def report_test_run_started(self, test_result):
606        test_suite = self.state_machine.suite()
607        if not self.state_machine.suite().is_started:
608            if not test_suite.test_num or not test_suite.suite_name:
609                test_suite.suite_name = self.get_suite_name()
610                test_suite.test_num = test_result.num_tests
611                for listener in self.get_listeners():
612                    suite_report = copy.copy(test_suite)
613                    listener.__started__(LifeCycle.TestSuite, suite_report)
614
615    @staticmethod
616    def handle_test_run_failed(error_msg):
617        if not error_msg:
618            error_msg = "Unknown error"
619        if not check_pub_key_exist():
620            LOG.debug("Error msg:%s" % error_msg)
621
622    def mark_test_as_failed(self, test):
623        test_info = self.state_machine.test()
624        if test_info:
625            test_info.test_class = test.class_name
626            test_info.test_name = test.test_name
627            test_info.code = StatusCodes.START.value
628            self.report_result(test_info)
629            test_info.code = StatusCodes.FAILURE.value
630            self.report_result(test_info)
631            self.__done__()
632
633    def mark_test_as_blocked(self, test):
634        test_info = self.state_machine.test()
635        if test_info:
636            test_info.test_class = test.class_name
637            test_info.test_name = test.test_name
638            test_info.num_tests = 1
639            test_info.run_time = 0
640            test_info.code = StatusCodes.START.value
641            self.report_result(test_info)
642            test_info.code = StatusCodes.BLOCKED.value
643            self.report_result(test_info)
644            self.__done__()
645
646
647@Plugin(type=Plugin.PARSER, id=CommonParserType.jsunit)
648class JSUnitParser(IParser):
649    last_line = ""
650    pattern = r"(\d{1,2}-\d{1,2}\s\d{1,2}:\d{1,2}:\d{1,2}\.\d{3}) "
651
652    def __init__(self):
653        self.state_machine = StateRecorder()
654        self.suites_name = ""
655        self.listeners = []
656        self.expect_tests_dict = dict()
657        self.marked_suite_set = set()
658        self.exclude_list = list()
659        self.ace_log_marker = ""
660
661    def get_listeners(self):
662        return self.listeners
663
664    def __process__(self, lines):
665        if not self.state_machine.suites_is_started():
666            self.state_machine.trace_logs.extend(lines)
667        for line in lines:
668            self.parse(line)
669
670    def __done__(self):
671        pass
672
673    def parse(self, line):
674        if (self.state_machine.suites_is_started() or line.find(
675                _START_JSUNIT_RUN_MARKER) != -1) and \
676                self._is_match_marker(line):
677            if line.find(_START_JSUNIT_RUN_MARKER) != -1:
678                self.handle_suites_started_tag()
679            elif line.endswith(_END_JSUNIT_RUN_MARKER):
680                self.handle_suites_ended_tag()
681            elif line.find(_START_JSUNIT_SUITE_RUN_MARKER) != -1:
682                self.handle_suite_started_tag(line.strip())
683            elif line.endswith(_START_JSUNIT_SUITE_END_MARKER):
684                self.handle_suite_ended_tag()
685            elif _PASS_JSUNIT_MARKER in line or _FAIL_JSUNIT_MARKER \
686                    in line or _ERROR_JSUNIT_MARKER in line:
687                self.handle_one_test_tag(line.strip())
688            self.last_line = line
689
690    def parse_test_description(self, message):
691        pattern = r".*\[(pass|fail|error)\]"
692        year = time.strftime("%Y")
693        filter_message = message
694        end_time = "%s-%s" % \
695                   (year, re.match(self.pattern, message).group().strip())
696        start_time = "%s-%s" % \
697                     (year, re.match(self.pattern,
698                                     self.last_line.strip()).group().strip())
699        start_timestamp = int(time.mktime(
700            time.strptime(start_time, "%Y-%m-%d %H:%M:%S.%f"))) * 1000 + int(
701            start_time.split(".")[-1])
702        end_timestamp = int(time.mktime(
703            time.strptime(end_time, "%Y-%m-%d %H:%M:%S.%f"))) * 1000 + int(
704            end_time.split(".")[-1])
705        run_time = end_timestamp - start_timestamp
706        match = re.match(pattern, filter_message)
707        _, status_end_index = match.span()
708        if " ;" in filter_message:
709            test_name = filter_message[status_end_index:
710                                       str(filter_message).find(" ;")]
711        else:
712            test_name = filter_message[status_end_index:]
713        status_dict = {"pass": ResultCode.PASSED, "fail": ResultCode.FAILED,
714                       "ignore": ResultCode.SKIPPED,
715                       "error": ResultCode.FAILED}
716        status = status_dict.get(match.group(1))
717        return test_name.strip(), status, run_time
718
719    def handle_suites_started_tag(self):
720        self.state_machine.get_suites(reset=True)
721        test_suites = self.state_machine.get_suites()
722        test_suites.suites_name = self.suites_name
723        test_suites.test_num = 0
724        for listener in self.get_listeners():
725            suite_report = copy.copy(test_suites)
726            listener.__started__(LifeCycle.TestSuites, suite_report)
727
728    def handle_suites_ended_tag(self):
729        self._mark_all_test_case()
730        suites = self.state_machine.get_suites()
731        suites.is_completed = True
732
733        for listener in self.get_listeners():
734            listener.__ended__(LifeCycle.TestSuites, test_result=suites,
735                               suites_name=suites.suites_name)
736
737    def handle_one_test_tag(self, message):
738        test_name, status, run_time = \
739            self.parse_test_description(message)
740        test_suite = self.state_machine.suite()
741        if self.exclude_list:
742            qualified_name = "{}#{}".format(test_suite.suite_name, test_name)
743            if qualified_name in self.exclude_list:
744                LOG.debug("{} will be discard!".format(qualified_name))
745                test_suite.test_num -= 1
746                return
747        test_result = self.state_machine.test(reset=True)
748        test_result.test_class = test_suite.suite_name
749        test_result.test_name = test_name
750        test_result.run_time = run_time
751        test_result.code = status.value
752        test_result.current = self.state_machine.running_test_index + 1
753        self.state_machine.suite().run_time += run_time
754        for listener in self.get_listeners():
755            test_result = copy.copy(test_result)
756            listener.__started__(LifeCycle.TestCase, test_result)
757
758        test_suites = self.state_machine.get_suites()
759        found_unexpected_test = False
760
761        if found_unexpected_test or ResultCode.FAILED == status:
762            for listener in self.get_listeners():
763                result = copy.copy(test_result)
764                listener.__failed__(LifeCycle.TestCase, result)
765        elif ResultCode.SKIPPED == status:
766            for listener in self.get_listeners():
767                result = copy.copy(test_result)
768                listener.__skipped__(LifeCycle.TestCase, result)
769
770        self.state_machine.test().is_completed = True
771        if not hasattr(test_suite, "total_cases"):
772            test_suite.test_num += 1
773        test_suites.test_num += 1
774        for listener in self.get_listeners():
775            result = copy.copy(test_result)
776            listener.__ended__(LifeCycle.TestCase, result)
777        self.state_machine.running_test_index += 1
778
779    def fake_run_marker(self, message):
780        fake_marker = re.compile(" +").split(message)
781        self.processTestStartedTag(fake_marker)
782
783    def handle_suite_started_tag(self, message):
784        self.state_machine.suite(reset=True)
785        self.state_machine.running_test_index = 0
786        test_suite = self.state_machine.suite()
787        if "total cases:" in message:
788            m_result = re.match(r".*\[suite start](.+), total cases: (\d+)",
789                                message)
790            if m_result:
791                expect_test_num = m_result.group(2)
792                test_suite.suite_name = m_result.group(1)
793                test_suite.test_num = int(expect_test_num)
794                setattr(test_suite, "total_cases", True)
795
796        else:
797            if re.match(r".*\[suite start].*", message):
798                _, index = re.match(r".*\[suite start]", message).span()
799                if message[index:]:
800                    test_suite.suite_name = message[index:]
801                else:
802                    test_suite.suite_name = self.suite_name
803                test_suite.test_num = 0
804        for listener in self.get_listeners():
805            suite_report = copy.copy(test_suite)
806            listener.__started__(LifeCycle.TestSuite, suite_report)
807
808    def handle_suite_ended_tag(self):
809        suite_result = self.state_machine.suite()
810        suites = self.state_machine.get_suites()
811        suite_result.run_time = suite_result.run_time
812        suites.run_time += suite_result.run_time
813        suite_result.is_completed = True
814        self._mark_test_case(suite_result, self.get_listeners())
815        for listener in self.get_listeners():
816            suite = copy.copy(suite_result)
817            listener.__ended__(LifeCycle.TestSuite, suite, is_clear=True)
818
819    def append_test_output(self, message):
820        if self.state_machine.test().stacktrace:
821            self.state_machine.test().stacktrace = \
822                "%s\r\n" % self.state_machine.test().stacktrace
823        self.state_machine.test().stacktrace = \
824            ''.join((self.state_machine.test().stacktrace, message))
825
826    def _mark_test_case(self, suite, listeners):
827        if not self.expect_tests_dict:
828            return
829        tests_list = []
830        for listener in listeners:
831            if listener.__class__.__name__ == "ReportListener":
832                tests_list.extend(listener.tests.values())
833                break
834        test_name_list = []
835        for item_test in tests_list:
836            test_name_list.append(item_test.test_name)
837        self.marked_suite_set.add(suite.suite_name)
838        test_in_cur = self.expect_tests_dict.get(suite.suite_name, [])
839        for test in test_in_cur:
840            if "{}#{}".format(suite.suite_name, test.test_name) \
841                    in self.exclude_list:
842                suite.test_num -= 1
843                continue
844            if test.test_name not in test_name_list:
845                self._mock_test_case_life_cycle(listeners, test)
846
847    def _mock_test_case_life_cycle(self, listeners, test):
848        test_result = self.state_machine.test(reset=True)
849        test_result.test_class = test.class_name
850        test_result.test_name = test.test_name
851        test_result.stacktrace = "error_msg: mark blocked"
852        test_result.num_tests = 1
853        test_result.run_time = 0
854        test_result.current = self.state_machine.running_test_index + 1
855        test_result.code = ResultCode.BLOCKED.value
856        test_result = copy.copy(test_result)
857        for listener in listeners:
858            listener.__started__(LifeCycle.TestCase, test_result)
859        test_result = copy.copy(test_result)
860        for listener in listeners:
861            listener.__ended__(LifeCycle.TestCase, test_result)
862        self.state_machine.running_test_index += 1
863
864    def _mark_all_test_case(self):
865        if not self.expect_tests_dict:
866            return
867        all_suite_set = set(self.expect_tests_dict.keys())
868        un_suite_set = all_suite_set.difference(self.marked_suite_set)
869        for un_suite_name in un_suite_set:
870            test_list = self.expect_tests_dict.get(un_suite_name, [])
871
872            self.state_machine.suite(reset=True)
873            self.state_machine.running_test_index = 0
874            test_suite = self.state_machine.suite()
875            test_suite.suite_name = un_suite_name
876            test_suite.test_num = len(test_list)
877            for listener in self.get_listeners():
878                suite_report = copy.copy(test_suite)
879                listener.__started__(LifeCycle.TestSuite, suite_report)
880
881            for test in test_list:
882                if "{}#{}".format(test_suite.suite_name, test.test_name) \
883                        in self.exclude_list:
884                    test_suite.test_num -= 1
885                    continue
886                self._mock_test_case_life_cycle(self.get_listeners(), test)
887
888            test_suite.is_completed = True
889            for listener in self.get_listeners():
890                suite = copy.copy(test_suite)
891                listener.__ended__(LifeCycle.TestSuite, suite, is_clear=True)
892
893    def _is_match_marker(self, line):
894        if self.ace_log_marker:
895            return line.lower().find(self.ace_log_marker) != -1
896        else:
897            for mark_str in _ACE_LOG_MARKER:
898                if line.lower().find(mark_str) != -1:
899                    self.ace_log_marker = mark_str
900                    return True
901            return False
902
903
904@Plugin(type=Plugin.PARSER, id=CommonParserType.oh_kernel_test)
905class OHKernelTestParser(IParser):
906
907    def __init__(self):
908        self.state_machine = StateRecorder()
909        self.suites_name = ""
910        self.listeners = []
911
912    def get_listeners(self):
913        return self.listeners
914
915    def __process__(self, lines):
916        if not self.state_machine.suites_is_started():
917            self.state_machine.trace_logs.extend(lines)
918        for line in lines:
919            self.parse(line)
920
921    def __done__(self):
922        pass
923
924    def parse(self, line):
925        line = re.sub('\x1b.*?m', '', line)
926        if self.state_machine.suites_is_started() or RUNTEST_TEST in line:
927            if RUNTEST_TEST in line:
928                self.handle_suites_started_tag(line)
929            elif START_TO_TEST in line:
930                self.handle_suite_start_tag(line)
931            elif FINISHED_TO_TEST in line:
932                self.handle_suite_end_tag(line)
933            elif line.endswith(PASS_DOT) or line.endswith(FAIL_DOT):
934                self.handle_one_test_case_tag(line)
935            elif line.endswith(ERROR_EXCLAMATION) \
936                    or line.endswith(TIMEOUT_EXCLAMATION):
937                self.handle_test_case_error(line)
938            elif TIMEOUT_TESTCASES in line:
939                self.handle_suites_ended_tag(line)
940
941    def handle_suites_started_tag(self, line):
942        self.state_machine.get_suites(reset=True)
943        test_suites = self.state_machine.get_suites()
944        test_suites.suites_name = self.suites_name
945        test_suites.test_num = 0
946        for listener in self.get_listeners():
947            suite_report = copy.copy(test_suites)
948            listener.__started__(LifeCycle.TestSuites, suite_report)
949
950    def handle_suites_ended_tag(self, line):
951        suites = self.state_machine.get_suites()
952        suites.is_completed = True
953
954        for listener in self.get_listeners():
955            listener.__ended__(LifeCycle.TestSuites, test_result=suites,
956                               suites_name=suites.suites_name)
957
958    def handle_suite_start_tag(self, line):
959        pattern = "^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}" \
960                  " Start to test (.+)$"
961        matcher = re.match(pattern, line)
962        if matcher and matcher.group(1):
963            self.state_machine.suite(reset=True)
964            test_suite = self.state_machine.suite()
965            test_suite.suite_name = matcher.group(1)
966            for listener in self.get_listeners():
967                suite_report = copy.copy(test_suite)
968                listener.__started__(LifeCycle.TestSuite, suite_report)
969
970    def handle_suite_end_tag(self, line):
971        pattern = "^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}" \
972                  " Finished to test (.+)$"
973        matcher = re.match(pattern, line)
974        if matcher and matcher.group(1):
975            suite_result = self.state_machine.suite()
976            suites = self.state_machine.get_suites()
977            suite_result.run_time = suite_result.run_time
978            suites.run_time += suite_result.run_time
979            suite_result.is_completed = True
980
981            for listener in self.get_listeners():
982                suite = copy.copy(suite_result)
983                listener.__ended__(LifeCycle.TestSuite, suite, is_clear=True)
984
985    def handle_one_test_case_tag(self, line):
986        pattern = "^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} (.+) " \
987                  "(PASS)\\.$"
988        matcher = re.match(pattern, line)
989        if not (matcher and matcher.group(1) and matcher.group(2)):
990            return
991        test_result = self.state_machine.test(reset=True)
992        test_suite = self.state_machine.suite()
993        test_result.test_class = test_suite.suite_name
994        test_result.test_name = matcher.group(1)
995        test_result.current = self.state_machine.running_test_index + 1
996        for listener in self.get_listeners():
997            test_result = copy.copy(test_result)
998            listener.__started__(LifeCycle.TestCase, test_result)
999
1000        test_suites = self.state_machine.get_suites()
1001        if PASS_DOT in line:
1002            test_result.code = ResultCode.PASSED.value
1003        elif FAIL_DOT in line:
1004            test_result.code = ResultCode.FAILED.value
1005            for listener in self.get_listeners():
1006                result = copy.copy(test_result)
1007                listener.__failed__(LifeCycle.TestCase, result)
1008        self.state_machine.test().is_completed = True
1009        test_suite.test_num += 1
1010        test_suites.test_num += 1
1011        for listener in self.get_listeners():
1012            result = copy.copy(test_result)
1013            listener.__ended__(LifeCycle.TestCase, result)
1014        self.state_machine.running_test_index += 1
1015
1016    def handle_test_case_error(self, line):
1017        pattern = "^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} (.+) " \
1018                  "(ERROR!!!|TIMEOUT!)$"
1019        matcher = re.match(pattern, line)
1020        if not (matcher and matcher.group(1) and matcher.group(2)):
1021            return
1022        test_result = self.state_machine.test(reset=True)
1023        test_suite = self.state_machine.suite()
1024        test_result.test_class = test_suite.suite_name
1025        test_result.test_name = matcher.group(1)
1026        test_result.current = self.state_machine.running_test_index + 1
1027        for listener in self.get_listeners():
1028            test_result = copy.copy(test_result)
1029            listener.__started__(LifeCycle.TestCase, test_result)
1030
1031        test_suites = self.state_machine.get_suites()
1032        if ERROR_EXCLAMATION in line:
1033            test_result.code = ResultCode.FAILED.value
1034        elif TIMEOUT_EXCLAMATION in line:
1035            test_result.code = ResultCode.BLOCKED.value
1036
1037        for listener in self.get_listeners():
1038            result = copy.copy(test_result)
1039            listener.__failed__(LifeCycle.TestCase, result)
1040        self.state_machine.test().is_completed = True
1041        test_suite.test_num += 1
1042        test_suites.test_num += 1
1043        for listener in self.get_listeners():
1044            result = copy.copy(test_result)
1045            listener.__ended__(LifeCycle.TestCase, result)
1046        self.state_machine.running_test_index += 1
1047
1048
1049class OHJSUnitPrefixes(Enum):
1050    SUM = "OHOS_REPORT_SUM: "
1051    STATUS = "OHOS_REPORT_STATUS: "
1052    STATUS_CODE = "OHOS_REPORT_STATUS_CODE: "
1053    RESULT = "OHOS_REPORT_RESULT: "
1054    CODE = "OHOS_REPORT_CODE: "
1055    TEST_FINISHED_RESULT_MSG = "TestFinished-ResultMsg: "
1056
1057
1058class OHJSUnitItemConstants(Enum):
1059    CLASS = "class"
1060    TEST = "test"
1061    NUM_TESTS = "numtests"
1062    STACK = "stack"
1063    STREAM = "stream"
1064    SUITE_CONSUMING = "suiteconsuming"
1065    CONSUMING = "consuming"
1066    APP_DIED = "App died"
1067
1068
1069@Plugin(type=Plugin.PARSER, id=CommonParserType.oh_jsunit)
1070class OHJSUnitTestParser(IParser):
1071
1072    def __init__(self):
1073        self.state_machine = StateRecorder()
1074        self.suites_name = ""
1075        self.listeners = []
1076        self.current_key = None
1077        self.current_value = None
1078        self.start_time = get_cst_time()
1079        self.suite_start_time = get_cst_time()
1080        self.test_time = 0
1081        self.test_run_finished = False
1082        self.cur_sum = -1
1083        self.runner = None
1084
1085    def get_suite_name(self):
1086        return self.suites_name
1087
1088    def get_listeners(self):
1089        return self.listeners
1090
1091    def __process__(self, lines):
1092        for line in lines:
1093            line = str(line).strip().rstrip("\r")
1094            LOG.debug(line)
1095            self.parse(line)
1096
1097    def parse(self, line):
1098        if not str(line).strip():
1099            return
1100        if line.startswith(OHJSUnitPrefixes.SUM.value):
1101            self.handle_sum_line(line)
1102        elif line.startswith(OHJSUnitPrefixes.STATUS.value):
1103            self.handle_status_line(line)
1104        elif line.startswith(OHJSUnitPrefixes.STATUS_CODE.value):
1105            self.submit_current_key_value()
1106            self.parse_status_code(line)
1107        elif line.startswith(OHJSUnitPrefixes.TEST_FINISHED_RESULT_MSG.value):
1108            self._handle_result_msg(line)
1109
1110    def handle_sum_line(self, line):
1111        value = line[len(OHJSUnitPrefixes.SUM.value):].split("=", 1)[0]
1112        self.cur_sum = int(value)
1113
1114    def handle_status_line(self, line):
1115        self.parse_key(line, len(OHJSUnitPrefixes.STATUS.value))
1116        if self.cur_sum > 0 and \
1117                self.current_key == OHJSUnitItemConstants.CLASS.value:
1118            if self.current_value not in self.runner.suite_recorder.keys():
1119                current_suite = self.state_machine.suite(reset=True)
1120                current_suite.test_num = self.cur_sum
1121                current_suite.suite_name = self.current_value
1122                self.runner.suite_recorder.update({
1123                    self.current_value:
1124                        [len(self.runner.suite_recorder.keys()),
1125                         current_suite]})
1126            else:
1127                current_suite = self.runner.suite_recorder.get(
1128                    self.current_value)[1]
1129                self.state_machine.current_suite = current_suite
1130            self.cur_sum = -1
1131            self.current_key = None
1132            self.current_value = None
1133            self.state_machine.running_test_index = 0
1134            self.suite_start_time = get_cst_time()
1135            for listener in self.get_listeners():
1136                suite = copy.copy(current_suite)
1137                listener.__started__(LifeCycle.TestSuite, suite)
1138
1139        else:
1140            if self.current_key == OHJSUnitItemConstants.SUITE_CONSUMING.value:
1141                self.test_time = int(self.current_value)
1142                self.handle_suite_end()
1143            elif self.current_key == OHJSUnitItemConstants.CONSUMING.value:
1144                self.test_time = int(self.current_value)
1145                self.handle_case_end()
1146            else:
1147                self.submit_current_key_value()
1148                self.parse_key(line, len(OHJSUnitPrefixes.STATUS.value))
1149
1150    def submit_current_key_value(self):
1151        if self.current_key and self.current_value:
1152            status_value = self.current_value
1153            test_info = self.state_machine.test()
1154            if self.current_key == OHJSUnitItemConstants.CLASS.value:
1155                test_info.test_class = status_value
1156            elif self.current_key == OHJSUnitItemConstants.TEST.value:
1157                test_info.test_name = status_value
1158            elif self.current_key == OHJSUnitItemConstants.NUM_TESTS.value:
1159                test_info.num_tests = int(status_value)
1160            elif self.current_key == OHJSUnitItemConstants.STREAM.value:
1161                test_info.stacktrace = status_value
1162            self.current_key = None
1163            self.current_value = None
1164
1165    def parse_key(self, line, key_start_pos):
1166        key_value = line[key_start_pos:].split("=", 1)
1167        if len(key_value) == 2:
1168            self.current_key = key_value[0]
1169            self.current_value = key_value[1]
1170
1171    def parse_status_code(self, line):
1172        value = line[len(OHJSUnitPrefixes.STATUS_CODE.value):]
1173        test_info = self.state_machine.test()
1174        test_info.code = int(value)
1175        if test_info.code != StatusCodes.IN_PROGRESS:
1176            if self.check_legality(test_info.test_class) and \
1177                    self.check_legality(test_info.test_name):
1178                self.report_result(test_info)
1179
1180    def clear_current_test_info(self):
1181        self.state_machine.current_test = None
1182
1183    def report_result(self, test_info):
1184        if not test_info.test_name or not test_info.test_class:
1185            LOG.info("Invalid instrumentation status bundle")
1186            return
1187        if test_info.code == StatusCodes.START.value:
1188            self.start_time = get_cst_time()
1189            for listener in self.get_listeners():
1190                result = copy.copy(test_info)
1191                listener.__started__(LifeCycle.TestCase, result)
1192            return
1193        if test_info.code == StatusCodes.FAILURE.value:
1194            self.state_machine.running_test_index += 1
1195            test_info.current = self.state_machine.running_test_index
1196            test_info.code = ResultCode.FAILED.value
1197            test_info.run_time = get_delta_time_ms(self.start_time)
1198        elif test_info.code == StatusCodes.ERROR.value:
1199            self.state_machine.running_test_index += 1
1200            test_info.current = self.state_machine.running_test_index
1201            test_info.code = ResultCode.FAILED.value
1202            test_info.run_time = get_delta_time_ms(self.start_time)
1203        elif test_info.code == StatusCodes.SUCCESS.value:
1204            self.state_machine.running_test_index += 1
1205            test_info.current = self.state_machine.running_test_index
1206            test_info.code = ResultCode.PASSED.value
1207            test_info.run_time = get_delta_time_ms(self.start_time)
1208
1209    @classmethod
1210    def output_stack_trace(cls, test_info):
1211        if check_pub_key_exist():
1212            return
1213        if test_info.stacktrace:
1214            stack_lines = test_info.stacktrace.split(r"\r\n")
1215            LOG.error("Stacktrace information is:")
1216            for line in stack_lines:
1217                line.strip()
1218                if line:
1219                    LOG.error(line)
1220
1221    @staticmethod
1222    def check_legality(name):
1223        if not name or name == "null":
1224            return False
1225        return True
1226
1227    def __done__(self):
1228        pass
1229
1230    def handle_case_end(self):
1231        test_info = self.state_machine.test()
1232        if not test_info.test_name or not test_info.test_class:
1233            LOG.warning("Test case name or class is invalid. test_name: {}, test_class: {}"
1234                        .format(test_info.test_name, test_info.test_class))
1235            return
1236        if test_info.run_time == 0 or test_info.run_time < self.test_time:
1237            test_info.run_time = self.test_time
1238        for listener in self.get_listeners():
1239            result = copy.copy(test_info)
1240            result.code = test_info.code
1241            listener.__ended__(LifeCycle.TestCase, result)
1242            if listener.__class__.__name__ == "ReportListener" \
1243                    and self.runner.retry_times > 1:
1244                index = list(listener.tests.keys())[-1]
1245                listener.tests.pop(index)
1246        test_info.is_completed = True
1247        self.clear_current_test_info()
1248
1249    def handle_suite_end(self):
1250        suite_result = self.state_machine.suite()
1251        suite_result.run_time = get_delta_time_ms(self.suite_start_time)
1252        if suite_result.run_time == 0:
1253            suite_result.run_time = self.test_time
1254        suite_result.is_completed = True
1255        for listener in self.get_listeners():
1256            suite = copy.copy(suite_result)
1257            listener.__ended__(LifeCycle.TestSuite, suite, is_clear=True)
1258
1259    def handle_suites_end(self):
1260        suite_result = self.state_machine.suite()
1261        if not suite_result.is_completed:
1262            self.handle_suite_end()
1263        for listener in self.get_listeners():
1264            if listener.__class__.__name__ == "ReportListener":
1265                self._cal_result(listener)
1266            suite = copy.copy(suite_result)
1267            listener.__ended__(LifeCycle.TestSuites, suite,
1268                               suites_name=self.suites_name)
1269        self.state_machine.current_suite = None
1270
1271    def _cal_result(self, report_listener):
1272        result_len = len(report_listener.result)
1273        suites_len = len(report_listener.suites)
1274        if result_len > suites_len:
1275            diff_result_tuple_list = report_listener.result[suites_len:]
1276            report_listener.result = report_listener.result[:suites_len]
1277            for diff_result_tuple in diff_result_tuple_list:
1278                suite, case_result_list = diff_result_tuple
1279                pos = self.runner.suite_recorder.get(suite.suite_name)[0]
1280                report_listener.result[pos][1].extend(case_result_list)
1281        self._handle_lacking_one_testcase(report_listener)
1282        self._handle_lacking_whole_suite(report_listener)
1283
1284    def _handle_lacking_one_testcase(self, report_listener):
1285        for suite in report_listener.suites.values():
1286            test_des_list = self.runner.expect_tests_dict.get(
1287                suite.suite_name, [])
1288            pos = self.runner.suite_recorder.get(suite.suite_name)[0]
1289            if len(test_des_list) == len(report_listener.result[pos][1]):
1290                continue
1291            interval = len(test_des_list) - len(report_listener.result[pos][1])
1292            if len(test_des_list) > 0:
1293                LOG.info("{} tests in {} had missed.".format(
1294                    interval, suite.suite_name))
1295            else:
1296                LOG.info("The count of tests in '{}' is incorrect! {} test "
1297                         "form dry run and {} tests have run."
1298                         "".format(suite.suite_name, len(test_des_list),
1299                                   len(report_listener.result[pos][1])))
1300            for test_des in test_des_list:
1301                is_contain = False
1302                for case in report_listener.result[pos][1]:
1303                    if case.test_name == test_des.test_name:
1304                        is_contain = True
1305                        break
1306                if not is_contain:
1307                    test_result = self.state_machine.test(reset=True)
1308                    test_result.test_class = test_des.class_name
1309                    test_result.test_name = test_des.test_name
1310                    test_result.stacktrace = "error_msg:mark blocked"
1311                    test_result.num_tests = 1
1312                    test_result.run_time = 0
1313                    test_result.current = \
1314                        self.state_machine.running_test_index + 1
1315                    test_result.code = ResultCode.BLOCKED.value
1316                    report_listener.result[pos][1].append(test_result)
1317                    LOG.debug("Add {}#{}".format(test_des.class_name,
1318                                                 test_des.test_name))
1319
1320    def _handle_lacking_whole_suite(self, report_listener):
1321        all_suite_set = set(self.runner.expect_tests_dict.keys())
1322        un_suite_set = set()
1323        if len(all_suite_set) > len(report_listener.suites):
1324            suite_name_set = set()
1325            for suite in report_listener.suites.values():
1326                suite_name_set.add(suite.suite_name)
1327            un_suite_set = all_suite_set.difference(suite_name_set)
1328            if un_suite_set:
1329                LOG.info("{} suites have missed.".format(len(un_suite_set)))
1330        for name in un_suite_set:
1331            self.state_machine.running_test_index = 0
1332            test_des_list = self.runner.expect_tests_dict.get(
1333                name, [])
1334            current_suite = self.state_machine.suite(reset=True)
1335            current_suite.test_num = len(test_des_list)
1336            current_suite.suite_name = name
1337            for listener in self.get_listeners():
1338                suite = copy.copy(current_suite)
1339                listener.__started__(LifeCycle.TestSuite, suite)
1340
1341            for test in test_des_list:
1342                test_result = self.state_machine.test(reset=True)
1343                test_result.test_class = test.class_name
1344                test_result.test_name = test.test_name
1345                test_result.stacktrace = "error_msg:mark blocked"
1346                test_result.num_tests = 1
1347                test_result.run_time = 0
1348                test_result.current = self.state_machine.running_test_index + 1
1349                test_result.code = ResultCode.BLOCKED.value
1350                test_result = copy.copy(test_result)
1351                for listener in self.get_listeners():
1352                    listener.__started__(LifeCycle.TestCase, test_result)
1353                test_result = copy.copy(test_result)
1354                for listener in self.get_listeners():
1355                    listener.__ended__(LifeCycle.TestCase, test_result)
1356                self.state_machine.running_test_index += 1
1357            current_suite.run_time = self.test_time
1358            current_suite.is_completed = True
1359            for listener in self.get_listeners():
1360                suite = copy.copy(current_suite)
1361                listener.__ended__(LifeCycle.TestSuite, suite, is_clear=True)
1362
1363    def notify_task_finished(self):
1364        self.handle_suites_end()
1365
1366    def _handle_result_msg(self, line):
1367        if OHJSUnitItemConstants.APP_DIED.value in line:
1368            test_result = self.state_machine.test()
1369            suite = self.state_machine.suite()
1370            if not test_result.is_completed:
1371                if self.check_legality(test_result.test_class) and \
1372                        self.check_legality(test_result.test_name):
1373                    self.report_result(test_result)
1374                    self.clear_current_test_info()
1375            if not suite.is_completed:
1376                self.handle_suite_end()
1377
1378
1379@Plugin(type=Plugin.PARSER, id=CommonParserType.oh_jsunit_list)
1380class OHJSUnitTestListParser(IParser):
1381
1382    def __init__(self):
1383        self.tests = []
1384        self.json_str = ""
1385        self.tests_dict = dict()
1386        self.result_data = ""
1387
1388    def __process__(self, lines):
1389        for line in lines:
1390            line = str(line).strip().rstrip("\r")
1391            self.result_data = "{}{}".format(self.result_data, line)
1392            self.parse(line)
1393
1394    def __done__(self):
1395        LOG.debug("OHJSUnitTestListParser data:")
1396        LOG.debug(self.result_data)
1397        self.result_data = ""
1398
1399    def parse(self, line):
1400        if "{" in line or "}" in line:
1401            self.json_str = "%s%s" % (self.json_str, line)
1402            return
1403        if "dry run finished" in line:
1404            suite_dict_list = json.loads(self.json_str).get("suites", [])
1405            for suite_dict in suite_dict_list:
1406                for class_name, test_name_dict_list in suite_dict.items():
1407                    self.tests_dict.update({class_name.strip(): []})
1408                    for test_name_dict in test_name_dict_list:
1409                        for test_name in test_name_dict.values():
1410                            test = TestDescription(class_name.strip(),
1411                                                   test_name.strip())
1412                            self.tests_dict.get(
1413                                class_name.strip()).append(test)
1414                            self.tests.append(test)
1415
1416
1417@Plugin(type=Plugin.PARSER, id=CommonParserType.oh_rust)
1418class OHRustTestParser(IParser):
1419
1420    def __init__(self):
1421        self.test_pattern = "test (?:tests::)?(.+) ... (ok|FAILED)"
1422        self.stout_pattern = "---- tests::(.+) stdout ----"
1423        self.running_pattern = "running (\\d+) test|tests"
1424        self.test_result_pattern = "test result: (ok|FAILED)\\..+finished in (.+)s"
1425        self.suite_name = ""
1426        self.result_list = list()
1427        self.stdout_list = list()
1428        self.failures_stdout = list()
1429        self.cur_fail_case = ""
1430        self.state_machine = StateRecorder()
1431        self.listeners = []
1432
1433    def get_listeners(self):
1434        return self.listeners
1435
1436    def __process__(self, lines):
1437        for line in lines:
1438            LOG.debug(line)
1439            self.parse(line)
1440
1441    def __done__(self):
1442        self.handle_suite_end()
1443
1444    def parse(self, line):
1445        if line.startswith("running"):
1446            matcher = re.match(self.running_pattern, line)
1447            if not (matcher and matcher.group(1)):
1448                return
1449            self.handle_suite_start(matcher)
1450        elif line.startswith("test result:"):
1451            matcher = re.match(self.test_result_pattern, line)
1452            if not (matcher and matcher.group(2)):
1453                return
1454            self.handle_case_lifecycle(matcher)
1455
1456        elif "..." in line:
1457            matcher = re.match(self.test_pattern, line)
1458            if not (matcher and matcher.group(1) and matcher.group(2)):
1459                return
1460            self.collect_case(matcher)
1461        elif line.startswith("---- tests::"):
1462            matcher = re.match(self.stout_pattern, line)
1463            if not (matcher and matcher.group(1)):
1464                return
1465            self.cur_fail_case = matcher.group(1)
1466        else:
1467            if self.cur_fail_case:
1468                self.handle_stdout(line)
1469
1470    def handle_case_lifecycle(self, matcher):
1471        cost_time = matcher.group(2)
1472        for test_result in self.result_list:
1473            if test_result.code == ResultCode.FAILED.value:
1474                if self.stdout_list and \
1475                        self.stdout_list[0][0] == test_result.test_name:
1476                    test_result.stacktrace = self.stdout_list[0][1]
1477                    self.stdout_list.pop(0)
1478            test_result.current = self.state_machine.running_test_index + 1
1479            for listener in self.get_listeners():
1480                test_result = copy.copy(test_result)
1481                listener.__started__(LifeCycle.TestCase, test_result)
1482            for listener in self.get_listeners():
1483                result = copy.copy(test_result)
1484                listener.__ended__(LifeCycle.TestCase, result)
1485        test_suite = self.state_machine.suite()
1486        test_suite.run_time = float(cost_time) * 1000
1487
1488    def handle_stdout(self, line):
1489        if line.strip():
1490            self.failures_stdout.append(line.strip())
1491        else:
1492            self.stdout_list.append((self.cur_fail_case,
1493                                     " ".join(self.failures_stdout)))
1494            self.cur_fail_case = ""
1495            self.failures_stdout.clear()
1496
1497    def collect_case(self, matcher):
1498        test_result = self.state_machine.test(reset=True)
1499        test_result.test_class = self.suite_name
1500        test_result.test_name = matcher.group(1)
1501        test_result.code = ResultCode.PASSED.value if \
1502            matcher.group(2) == "ok" else ResultCode.FAILED.value
1503        self.result_list.append(test_result)
1504
1505    def handle_suite_start(self, matcher):
1506        self.state_machine.suite(reset=True)
1507        test_suite = self.state_machine.suite()
1508        test_suite.suite_name = self.suite_name
1509        test_suite.test_num = int(matcher.group(1))
1510        for listener in self.get_listeners():
1511            suite_report = copy.copy(test_suite)
1512            listener.__started__(LifeCycle.TestSuite, suite_report)
1513
1514    def handle_suite_end(self):
1515        suite_result = self.state_machine.suite()
1516        suite_result.run_time += suite_result.run_time
1517        suite_result.is_completed = True
1518        for listener in self.get_listeners():
1519            suite = copy.copy(suite_result)
1520            listener.__ended__(LifeCycle.TestSuite, suite, suite_report=True)
1521
1522
1523@Plugin(type=Plugin.PARSER, id=CommonParserType.oh_yara)
1524class OHYaraTestParser(IParser):
1525    last_line = ""
1526    pattern = r"(\d{1,2}-\d{1,2}\s\d{1,2}:\d{1,2}:\d{1,2}\.\d{3}) "
1527
1528    def __init__(self):
1529        self.state_machine = StateRecorder()
1530        self.suites_name = ""
1531        self.vul_items = None
1532        self.listeners = []
1533
1534    def get_listeners(self):
1535        return self.listeners
1536
1537    def __process__(self, lines):
1538        self.parse(lines)
1539
1540    def __done__(self):
1541        pass
1542
1543    def parse(self, lines):
1544        for line in lines:
1545            if line:
1546                self.handle_suites_started_tag()
1547                self.handle_suite_started_tag()
1548                self.handle_one_test_tag(line)
1549                self.handle_suite_ended_tag()
1550                self.handle_suites_ended_tag()
1551
1552    def handle_suites_started_tag(self):
1553        self.state_machine.get_suites(reset=True)
1554        test_suites = self.state_machine.get_suites()
1555        test_suites.suites_name = self.suites_name
1556        test_suites.test_num = len(self.vul_items)
1557        for listener in self.get_listeners():
1558            suite_report = copy.copy(test_suites)
1559            listener.__started__(LifeCycle.TestSuites, suite_report)
1560
1561    def handle_suites_ended_tag(self):
1562        suites = self.state_machine.get_suites()
1563        suites.is_completed = True
1564        for listener in self.get_listeners():
1565            listener.__ended__(LifeCycle.TestSuites, test_result=suites,
1566                               suites_name=suites.suites_name)
1567
1568    def handle_one_test_tag(self, message):
1569        status_dict = {"pass": ResultCode.PASSED, "fail": ResultCode.FAILED,
1570                       "block": ResultCode.BLOCKED}
1571        message = message.strip().split("|")
1572        test_name = message[0]
1573        status = status_dict.get(message[3])
1574        trace = message[6] if message[3] else ""
1575        run_time = 0
1576        test_suite = self.state_machine.suite()
1577        test_result = self.state_machine.test(reset=True)
1578        test_result.test_class = test_suite.suite_name
1579        test_result.test_name = test_name
1580        test_result.run_time = run_time
1581        test_result.code = status.value
1582        test_result.stacktrace = trace
1583        test_result.current = self.state_machine.running_test_index + 1
1584        self.state_machine.suite().run_time += run_time
1585        for listener in self.get_listeners():
1586            test_result = copy.copy(test_result)
1587            listener.__started__(LifeCycle.TestCase, test_result)
1588
1589        test_suites = self.state_machine.get_suites()
1590        self.state_machine.test().is_completed = True
1591        test_suites.test_num += 1
1592        for listener in self.get_listeners():
1593            result = copy.copy(test_result)
1594            listener.__ended__(LifeCycle.TestCase, result)
1595        self.state_machine.running_test_index += 1
1596
1597    def handle_suite_started_tag(self):
1598        self.state_machine.suite(reset=True)
1599        self.state_machine.running_test_index = 0
1600        test_suite = self.state_machine.suite()
1601        test_suite.suite_name = self.suites_name
1602        test_suite.test_num = 1
1603        for listener in self.get_listeners():
1604            suite_report = copy.copy(test_suite)
1605            listener.__started__(LifeCycle.TestSuite, suite_report)
1606
1607    def handle_suite_ended_tag(self):
1608        suite_result = self.state_machine.suite()
1609        suites = self.state_machine.get_suites()
1610        suite_result.run_time = suite_result.run_time
1611        suites.run_time += suite_result.run_time
1612        suite_result.is_completed = True
1613        for listener in self.get_listeners():
1614            suite = copy.copy(suite_result)
1615            listener.__ended__(LifeCycle.TestSuite, suite, is_clear=True)