• 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 os
20import re
21import time
22import json
23import stat
24
25from xdevice import ConfigConst
26from xdevice import ParamError
27from xdevice import IDriver
28from xdevice import platform_logger
29from xdevice import Plugin
30from xdevice import get_plugin
31from xdevice import JsonParser
32from xdevice import ShellHandler
33from xdevice import driver_output_method
34from xdevice import TestDescription
35from xdevice import get_device_log_file
36from xdevice import check_result_report
37from xdevice import get_kit_instances
38from xdevice import get_config_value
39from xdevice import do_module_kit_setup
40from xdevice import do_module_kit_teardown
41from xdevice import DeviceTestType
42from xdevice import CommonParserType
43from xdevice import FilePermission
44from xdevice import CollectingTestListener
45from xdevice import ShellCommandUnresponsiveException
46from xdevice import gtest_para_parse
47
48from ohos.environment.dmlib import process_command_ret
49from ohos.parser.parser import _ACE_LOG_MARKER
50
51__all__ = ["CppTestDriver", "DexTestDriver", "HapTestDriver",
52           "JSUnitTestDriver", "JUnitTestDriver", "RemoteTestRunner",
53           "RemoteDexRunner"]
54LOG = platform_logger("Drivers")
55DEFAULT_TEST_PATH = "/%s/%s/" % ("data", "test")
56ON_DEVICE_TEST_DIR_LOCATION = "/%s/%s/%s/" % ("data", "local", "tmp")
57
58FAILED_RUN_TEST_ATTEMPTS = 3
59TIME_OUT = 900 * 1000
60
61
62def get_xml_output(config, json_config):
63    xml_output = config.testargs.get("xml-output")
64    if not xml_output:
65        if get_config_value('xml-output', json_config.get_driver(), False):
66            xml_output = get_config_value('xml-output',
67                                          json_config.get_driver(), False)
68        else:
69            xml_output = "false"
70    else:
71        xml_output = xml_output[0]
72    xml_output = str(xml_output).lower()
73    return xml_output
74
75
76def get_result_savepath(testsuit_path, result_rootpath):
77    findkey = "%stests%s" % (os.sep, os.sep)
78    filedir, _ = os.path.split(testsuit_path)
79    pos = filedir.find(findkey)
80    if -1 != pos:
81        subpath = filedir[pos + len(findkey):]
82        pos1 = subpath.find(os.sep)
83        if -1 != pos1:
84            subpath = subpath[pos1 + len(os.sep):]
85            result_path = os.path.join(result_rootpath, "result", subpath)
86        else:
87            result_path = os.path.join(result_rootpath, "result")
88    else:
89        result_path = os.path.join(result_rootpath, "result")
90
91    if not os.path.exists(result_path):
92        os.makedirs(result_path)
93
94    LOG.info("Result save path = %s" % result_path)
95    return result_path
96
97
98# all testsuit common Unavailable test result xml
99def _create_empty_result_file(filepath, filename, error_message):
100    error_message = str(error_message)
101    error_message = error_message.replace("\"", """)
102    error_message = error_message.replace("<", "&lt;")
103    error_message = error_message.replace(">", "&gt;")
104    error_message = error_message.replace("&", "&amp;")
105    if filename.endswith(".hap"):
106        filename = filename.split(".")[0]
107    if not os.path.exists(filepath):
108        file_open = os.open(filepath, os.O_WRONLY | os.O_CREAT | os.O_APPEND,
109                            FilePermission.mode_755)
110        with os.fdopen(file_open, "w") as file_desc:
111            time_stamp = time.strftime("%Y-%m-%d %H:%M:%S",
112                                       time.localtime())
113            file_desc.write('<?xml version="1.0" encoding="UTF-8"?>\n')
114            file_desc.write('<testsuites tests="0" failures="0" '
115                            'disabled="0" errors="0" timestamp="%s" '
116                            'time="0" name="AllTests">\n' % time_stamp)
117            file_desc.write(
118                '  <testsuite name="%s" tests="0" failures="0" '
119                'disabled="0" errors="0" time="0.0" '
120                'unavailable="1" message="%s">\n' %
121                (filename, error_message))
122            file_desc.write('  </testsuite>\n')
123            file_desc.write('</testsuites>\n')
124            file_desc.flush()
125    return
126
127
128class ResultManager(object):
129    def __init__(self, testsuit_path, result_rootpath, device,
130                 device_testpath):
131        self.testsuite_path = testsuit_path
132        self.result_rootpath = result_rootpath
133        self.device = device
134        self.device_testpath = device_testpath
135        self.testsuite_name = os.path.basename(self.testsuite_path)
136        self.is_coverage = False
137
138    def set_is_coverage(self, is_coverage):
139        self.is_coverage = is_coverage
140
141    def get_test_results(self, error_message=""):
142        # Get test result files
143        filepath = self.obtain_test_result_file()
144        if not os.path.exists(filepath):
145            _create_empty_result_file(filepath, self.testsuite_name,
146                                      error_message)
147
148        # Get coverage data files
149        if self.is_coverage:
150            self.obtain_coverage_data()
151
152        return filepath
153
154    def obtain_test_result_file(self):
155        result_savepath = get_result_savepath(self.testsuite_path,
156                                              self.result_rootpath)
157        if self.testsuite_path.endswith('.hap'):
158            filepath = os.path.join(result_savepath, "%s.xml" % str(
159                self.testsuite_name).split(".")[0])
160
161            remote_result_name = ""
162            if self.device.is_file_exist(os.path.join(self.device_testpath,
163                                                      "testcase_result.xml")):
164                remote_result_name = "testcase_result.xml"
165            elif self.device.is_file_exist(os.path.join(self.device_testpath,
166                                                        "report.xml")):
167                remote_result_name = "report.xml"
168
169            if remote_result_name:
170                self.device.pull_file(
171                    os.path.join(self.device_testpath, remote_result_name),
172                    filepath)
173            else:
174                LOG.error("%s no report file", self.device_testpath)
175
176        else:
177            filepath = os.path.join(result_savepath, "%s.xml" %
178                                    self.testsuite_name)
179            remote_result_file = os.path.join(self.device_testpath,
180                                              "%s.xml" % self.testsuite_name)
181
182            if self.device.is_file_exist(remote_result_file):
183                self.device.pull_file(remote_result_file, result_savepath)
184            else:
185                LOG.error("%s not exists", remote_result_file)
186        return filepath
187
188    def is_exist_target_in_device(self, path, target):
189        command = "ls -l %s | grep %s" % (path, target)
190
191        check_result = False
192        stdout_info = self.device.execute_shell_command(command)
193        if stdout_info != "" and stdout_info.find(target) != -1:
194            check_result = True
195        return check_result
196
197    def obtain_coverage_data(self):
198        java_cov_path = os.path.abspath(
199            os.path.join(self.result_rootpath, "..", "coverage/data/exec"))
200        dst_target_name = "%s.exec" % self.testsuite_name
201        src_target_name = "jacoco.exec"
202        if self.is_exist_target_in_device(self.device_testpath,
203                                          src_target_name):
204            if not os.path.exists(java_cov_path):
205                os.makedirs(java_cov_path)
206            self.device.pull_file(
207                os.path.join(self.device_testpath, src_target_name),
208                os.path.join(java_cov_path, dst_target_name))
209
210        cxx_cov_path = os.path.abspath(
211            os.path.join(self.result_rootpath, "..", "coverage/data/cxx",
212                         self.testsuite_name))
213        target_name = "obj"
214        if self.is_exist_target_in_device(self.device_testpath, target_name):
215            if not os.path.exists(cxx_cov_path):
216                os.makedirs(cxx_cov_path)
217            src_file = os.path.join(self.device_testpath, target_name)
218            self.device.pull_file(src_file, cxx_cov_path)
219
220
221@Plugin(type=Plugin.DRIVER, id=DeviceTestType.cpp_test)
222class CppTestDriver(IDriver):
223    """
224    CppTestDriver is a Test that runs a native test package on given harmony
225    device.
226    """
227
228    def __init__(self):
229        self.result = ""
230        self.error_message = ""
231        self.config = None
232        self.rerun = True
233        self.rerun_all = True
234        self.runner = None
235        # log
236        self.device_log = None
237        self.hilog = None
238        self.log_proc = None
239        self.hilog_proc = None
240
241    def __check_environment__(self, device_options):
242        pass
243
244    def __check_config__(self, config):
245        pass
246
247    def __execute__(self, request):
248        try:
249            LOG.debug("Start execute xdevice extension CppTest")
250
251            self.config = request.config
252            self.config.device = request.config.environment.devices[0]
253
254            config_file = request.root.source.config_file
255            self.result = "%s.xml" % \
256                          os.path.join(request.config.report_path,
257                                       "result", request.root.source.test_name)
258
259            self.device_log = get_device_log_file(
260                request.config.report_path,
261                request.config.device.__get_serial__(),
262                "device_log",
263                module_name=request.get_module_name())
264
265            self.hilog = get_device_log_file(
266                request.config.report_path,
267                request.config.device.__get_serial__(),
268                "device_hilog",
269                module_name=request.get_module_name())
270
271            device_log_open = os.open(self.device_log, os.O_WRONLY | os.O_CREAT |
272                                      os.O_APPEND, FilePermission.mode_755)
273            hilog_open = os.open(self.hilog, os.O_WRONLY | os.O_CREAT | os.O_APPEND,
274                                 FilePermission.mode_755)
275            self.config.device.device_log_collector.add_log_address(self.device_log, self.hilog)
276            with os.fdopen(device_log_open, "a") as log_file_pipe, \
277                    os.fdopen(hilog_open, "a") as hilog_file_pipe:
278                self.log_proc, self.hilog_proc = self.config.device.device_log_collector.\
279                    start_catch_device_log(log_file_pipe, hilog_file_pipe)
280                self._run_cpp_test(config_file, listeners=request.listeners,
281                                   request=request)
282                log_file_pipe.flush()
283                hilog_file_pipe.flush()
284
285        except Exception as exception:
286            self.error_message = exception
287            if not getattr(exception, "error_no", ""):
288                setattr(exception, "error_no", "03404")
289            LOG.exception(self.error_message, exc_info=False, error_no="03404")
290            raise exception
291
292        finally:
293            self.config.device.device_log_collector.remove_log_address(self.device_log, self.hilog)
294            self.config.device.device_log_collector.stop_catch_device_log(self.log_proc)
295            self.config.device.device_log_collector.stop_catch_device_log(self.hilog_proc)
296            self.result = check_result_report(
297                request.config.report_path, self.result, self.error_message)
298
299    def _run_cpp_test(self, config_file, listeners=None, request=None):
300        try:
301            if not os.path.exists(config_file):
302                LOG.error("Error: Test cases don't exit %s." % config_file,
303                          error_no="00102")
304                raise ParamError(
305                    "Error: Test cases don't exit %s." % config_file,
306                    error_no="00102")
307
308            json_config = JsonParser(config_file)
309            kits = get_kit_instances(json_config, self.config.resource_path,
310                                     self.config.testcases_path)
311
312            for listener in listeners:
313                listener.device_sn = self.config.device.device_sn
314
315            self._get_driver_config(json_config)
316            do_module_kit_setup(request, kits)
317            self.runner = RemoteCppTestRunner(self.config)
318            self.runner.suite_name = request.root.source.test_name
319
320            if hasattr(self.config, "history_report_path") and \
321                    self.config.testargs.get("test"):
322                self._do_test_retry(listeners, self.config.testargs)
323            else:
324                gtest_para_parse(self.config.testargs, self.runner, request)
325                self._do_test_run(listeners)
326
327        finally:
328            do_module_kit_teardown(request)
329
330    def _do_test_retry(self, listener, testargs):
331        for test in testargs.get("test"):
332            test_item = test.split("#")
333            if len(test_item) != 2:
334                continue
335            self.runner.add_instrumentation_arg(
336                "gtest_filter", "%s.%s" % (test_item[0], test_item[1]))
337            self.runner.run(listener)
338
339    def _do_test_run(self, listener):
340        test_to_run = self._collect_test_to_run()
341        LOG.info("Collected test count is: %s" % (len(test_to_run)
342                 if test_to_run else 0))
343        if not test_to_run:
344            self.runner.run(listener)
345        else:
346            self._run_with_rerun(listener, test_to_run)
347
348    def _collect_test_to_run(self):
349        if self.rerun:
350            self.runner.add_instrumentation_arg("gtest_list_tests", True)
351            self.runner.add_instrumentation_arg("gtest_color", "no")
352            run_results = self.runner.dry_run()
353            self.runner.remove_instrumentation_arg("gtest_list_tests")
354            return run_results
355        return None
356
357    def _run_tests(self, listener):
358        test_tracker = CollectingTestListener()
359        listener_copy = listener.copy()
360        listener_copy.append(test_tracker)
361        self.runner.run(listener_copy)
362        test_run = test_tracker.get_current_run_results()
363        return test_run
364
365    def _run_with_rerun(self, listener, expected_tests):
366        LOG.debug("Ready to run with rerun, expect run: %s"
367                  % len(expected_tests))
368        test_run = self._run_tests(listener)
369        LOG.debug("Run with rerun, has run: %s" % len(test_run)
370                  if test_run else 0)
371        if len(test_run) < len(expected_tests):
372            expected_tests = TestDescription.remove_test(expected_tests,
373                                                         test_run)
374            if not expected_tests:
375                LOG.debug("No tests to re-run, all tests executed at least "
376                          "once.")
377            if self.rerun_all:
378                self._rerun_all(expected_tests, listener)
379            else:
380                self._rerun_serially(expected_tests, listener)
381
382    def _rerun_all(self, expected_tests, listener):
383        tests = []
384        for test in expected_tests:
385            tests.append("%s.%s" % (test.class_name, test.test_name))
386        self.runner.add_instrumentation_arg("gtest_filter", ":".join(tests))
387        LOG.debug("Ready to rerun file, expect run: %s" % len(expected_tests))
388        test_run = self._run_tests(listener)
389        LOG.debug("Rerun file, has run: %s" % len(test_run))
390        if len(test_run) < len(expected_tests):
391            expected_tests = TestDescription.remove_test(expected_tests,
392                                                         test_run)
393            if not expected_tests:
394                LOG.debug("Rerun textFile success")
395            self._rerun_serially(expected_tests, listener)
396
397    def _rerun_serially(self, expected_tests, listener):
398        LOG.debug("Rerun serially, expected run: %s" % len(expected_tests))
399        for test in expected_tests:
400            self.runner.add_instrumentation_arg(
401                "gtest_filter", "%s.%s" % (test.class_name, test.test_name))
402            self.runner.rerun(listener, test)
403            self.runner.remove_instrumentation_arg("gtest_filter")
404
405    def _get_driver_config(self, json_config):
406        target_test_path = get_config_value('native-test-device-path',
407                                            json_config.get_driver(), False)
408        if target_test_path:
409            self.config.target_test_path = target_test_path
410        else:
411            self.config.target_test_path = DEFAULT_TEST_PATH
412
413        self.config.module_name = get_config_value(
414            'module-name', json_config.get_driver(), False)
415
416        timeout_config = get_config_value('native-test-timeout',
417                                          json_config.get_driver(), False)
418        if timeout_config:
419            self.config.timeout = int(timeout_config)
420        else:
421            self.config.timeout = TIME_OUT
422
423        rerun = get_config_value('rerun', json_config.get_driver(), False)
424        if isinstance(rerun, bool):
425            self.rerun = rerun
426        elif str(rerun).lower() == "false":
427            self.rerun = False
428        else:
429            self.rerun = True
430
431    def __result__(self):
432        return self.result if os.path.exists(self.result) else ""
433
434
435class RemoteCppTestRunner:
436    def __init__(self, config):
437        self.arg_list = {}
438        self.suite_name = None
439        self.config = config
440        self.rerun_attempt = FAILED_RUN_TEST_ATTEMPTS
441
442    def dry_run(self):
443        parsers = get_plugin(Plugin.PARSER, CommonParserType.cpptest_list)
444        if parsers:
445            parsers = parsers[:1]
446        parser_instances = []
447        for parser in parsers:
448            parser_instance = parser.__class__()
449            parser_instances.append(parser_instance)
450        handler = ShellHandler(parser_instances)
451        handler.add_process_method(driver_output_method)
452
453        command = "cd %s; chmod +x *; ./%s %s" \
454                  % (self.config.target_test_path, self.config.module_name,
455                     self.get_args_command())
456
457        self.config.device.execute_shell_command(
458            command, timeout=self.config.timeout, receiver=handler, retry=0)
459        return parser_instances[0].tests
460
461    def run(self, listener):
462        handler = self._get_shell_handler(listener)
463        bin_path = "{}/{}".format(self.config.target_test_path,
464                                  self.config.module_name)
465        command = "cd %s; chmod +x *; hilog -d %s; ./%s %s" \
466                  % (self.config.target_test_path,
467                     bin_path,
468                     self.config.module_name,
469                     self.get_args_command())
470
471        self.config.device.execute_shell_command(
472            command, timeout=self.config.timeout, receiver=handler, retry=0)
473
474    def rerun(self, listener, test):
475        if self.rerun_attempt:
476            test_tracker = CollectingTestListener()
477            listener_copy = listener.copy()
478            listener_copy.append(test_tracker)
479            handler = self._get_shell_handler(listener_copy)
480            try:
481                bin_path = "{}/{}".format(self.config.target_test_path,
482                                          self.config.module_name)
483                command = "cd %s; chmod +x *; hilog -d %s; ./%s %s" \
484                          % (self.config.target_test_path,
485                             bin_path,
486                             self.config.module_name,
487                             self.get_args_command())
488
489                self.config.device.execute_shell_command(
490                    command, timeout=self.config.timeout, receiver=handler,
491                    retry=0)
492
493            except ShellCommandUnresponsiveException as _:
494                LOG.debug("Exception: ShellCommandUnresponsiveException")
495            finally:
496                if not len(test_tracker.get_current_run_results()):
497                    LOG.debug("No test case is obtained finally")
498                    self.rerun_attempt -= 1
499                    handler.parsers[0].mark_test_as_blocked(test)
500        else:
501            LOG.debug("Not execute and mark as blocked finally")
502            handler = self._get_shell_handler(listener)
503            handler.parsers[0].mark_test_as_blocked(test)
504
505    def add_instrumentation_arg(self, name, value):
506        if not name or not value:
507            return
508        self.arg_list[name] = value
509
510    def remove_instrumentation_arg(self, name):
511        if not name:
512            return
513        if name in self.arg_list:
514            del self.arg_list[name]
515
516    def get_args_command(self):
517        args_commands = ""
518        for key, value in self.arg_list.items():
519            if key == "gtest_list_tests":
520                args_commands = "%s --%s" % (args_commands, key)
521            else:
522                args_commands = "%s --%s=%s" % (args_commands, key, value)
523        return args_commands
524
525    def _get_shell_handler(self, listener):
526        parsers = get_plugin(Plugin.PARSER, CommonParserType.cpptest)
527        if parsers:
528            parsers = parsers[:1]
529        parser_instances = []
530        for parser in parsers:
531            parser_instance = parser.__class__()
532            parser_instance.suite_name = self.suite_name
533            parser_instance.listeners = listener
534            parser_instances.append(parser_instance)
535        handler = ShellHandler(parser_instances)
536        handler.add_process_method(driver_output_method)
537        return handler
538
539
540@Plugin(type=Plugin.DRIVER, id=DeviceTestType.jsunit_test)
541class JSUnitTestDriver(IDriver):
542    """
543    JSUnitTestDriver is a Test that runs a native test package on given device.
544    """
545
546    def __init__(self):
547        self.xml_output = "false"
548        self.timeout = 30 * 1000
549        self.start_time = None
550        self.result = ""
551        self.error_message = ""
552        self.kits = []
553        self.config = None
554        # log
555        self.device_log = None
556        self.hilog = None
557        self.log_proc = None
558        self.hilog_proc = None
559        self.ace_log_marker = ""
560
561    def __check_environment__(self, device_options):
562        pass
563
564    def __check_config__(self, config):
565        pass
566
567    def __execute__(self, request):
568
569        device = request.config.environment.devices[0]
570        exe_out = device.execute_shell_command(
571            "param get const.product.software.version")
572        LOG.debug("Software version is {}".format(exe_out))
573        self.run_js_outer(request)
574
575    def generate_console_output(self, request, timeout):
576        LOG.info("prepare to read device log, may wait some time")
577        message_list = list()
578        label_list, suite_info, is_suites_end = self.read_device_log_timeout(
579            self.hilog, message_list, timeout)
580        if not is_suites_end and self.ace_log_marker:
581            message_list.append(self.ace_log_marker + "  [end] run suites end\n")
582            LOG.warning("there is no suites end")
583        if len(label_list[0]) > 0 and sum(label_list[0]) != 0:
584            # the problem happened! when the sum of label list is not zero
585            self._insert_suite_end(label_list, message_list)
586
587        result_message = "".join(message_list)
588        message_list.clear()
589        expect_tests_dict = self._parse_suite_info(suite_info)
590        self._analyse_tests(request, result_message, expect_tests_dict)
591
592    def _insert_suite_end(self, label_list, message_list):
593        for i in range(len(label_list[0])):
594            if label_list[0][i] != 1:  # skipp
595                continue
596            # check the start label, then peek next position
597            if i + 1 == len(label_list[0]):  # next position at the tail
598                message_list.insert(-1, self.ace_log_marker + " [suite end]\n")
599                LOG.warning("there is no suite end")
600                continue
601            if label_list[0][i + 1] != 1:  # 0 present the end label
602                continue
603            message_list.insert(label_list[1][i + 1],
604                                self.ace_log_marker + "  [suite end]\n")
605            LOG.warning("there is no suite end")
606            for j in range(i + 1, len(label_list[1])):
607                label_list[1][j] += 1  # move the index to next
608
609    def _analyse_tests(self, request, result_message, expect_tests_dict):
610        exclude_list = self._make_exclude_list_file(request)
611        exclude_list.extend(self._get_retry_skip_list(expect_tests_dict))
612        listener_copy = request.listeners.copy()
613        parsers = get_plugin(
614            Plugin.PARSER, CommonParserType.jsunit)
615        if parsers:
616            parsers = parsers[:1]
617        for listener in listener_copy:
618            listener.device_sn = self.config.device.device_sn
619        parser_instances = []
620        for parser in parsers:
621            parser_instance = parser.__class__()
622            parser_instance.suites_name = request.get_module_name()
623            parser_instance.listeners = listener_copy
624            parser_instances.append(parser_instance)
625        handler = ShellHandler(parser_instances)
626        handler.parsers[0].expect_tests_dict = expect_tests_dict
627        handler.parsers[0].exclude_list = exclude_list
628        process_command_ret(result_message, handler)
629
630    def _get_retry_skip_list(self, expect_tests_dict):
631        # get already pass case
632        skip_list = []
633        if hasattr(self.config, "history_report_path") and \
634                self.config.testargs.get("test"):
635            for class_name in expect_tests_dict.keys():
636                for test_desc in expect_tests_dict.get(class_name, list()):
637                    test = "{}#{}".format(test_desc.class_name, test_desc.test_name)
638                    if test not in self.config.testargs.get("test"):
639                        skip_list.append(test)
640        LOG.debug("Retry skip list: {}, total skip case: {}".
641                  format(skip_list, len(skip_list)))
642        return skip_list
643
644    @classmethod
645    def _parse_suite_info(cls, suite_info):
646        tests_dict = dict()
647        test_count = 0
648        if suite_info:
649            json_str = "".join(suite_info)
650            LOG.debug("Suites info: %s" % json_str)
651            try:
652                suite_dict_list = json.loads(json_str).get("suites", [])
653                for suite_dict in suite_dict_list:
654                    for class_name, test_name_dict_list in suite_dict.items():
655                        tests_dict.update({class_name.strip(): []})
656                        for test_name_dict in test_name_dict_list:
657                            for test_name in test_name_dict.values():
658                                test = TestDescription(class_name.strip(),
659                                                       test_name.strip())
660                                tests_dict.get(class_name.strip()).append(test)
661                                test_count += 1
662            except json.decoder.JSONDecodeError as json_error:
663                LOG.warning("Suites info is invalid: %s" % json_error)
664        LOG.debug("Collect suite count is %s, test count is %s" %
665                  (len(tests_dict), test_count))
666        return tests_dict
667
668    def read_device_log_timeout(self, device_log_file,
669                                message_list, timeout):
670        LOG.info("The timeout is {} seconds".format(timeout))
671        pattern = "^\\d{2}-\\d{2}\\s+\\d{2}:\\d{2}:\\d{2}\\.\\d{3}\\s+(\\d+)"
672        while time.time() - self.start_time <= timeout:
673            with open(device_log_file, "r", encoding='utf-8',
674                      errors='ignore') as file_read_pipe:
675                pid = ""
676                message_list.clear()
677                label_list = [[], []]  # [-1, 1 ..] [line1, line2 ..]
678                suite_info = []
679                while True:
680                    try:
681                        line = file_read_pipe.readline()
682                    except UnicodeError as error:
683                        LOG.warning("While read log file: %s" % error)
684                    if not line:
685                        time.sleep(5)  # wait for log write to file
686                        break
687                    if self._is_match_marker(line):
688                        if "[suites info]" in line:
689                            _, pos = re.match(".+\\[suites info]", line).span()
690                            suite_info.append(line[pos:].strip())
691
692                        if "[start] start run suites" in line:  # 发现了任务开始标签
693                            pid, is_update = \
694                                self._init_suites_start(line, pattern, pid)
695                            if is_update:
696                                message_list.clear()
697                                label_list[0].clear()
698                                label_list[1].clear()
699                        if not pid or pid not in line:
700                            continue
701                        message_list.append(line)
702                        if "[suite end]" in line:
703                            label_list[0].append(-1)
704                            label_list[1].append(len(message_list) - 1)
705                        if "[suite start]" in line:
706                            label_list[0].append(1)
707                            label_list[1].append(len(message_list) - 1)
708                        if "[end] run suites end" in line:
709                            LOG.info("Find the end mark then analysis result")
710                            LOG.debug("current JSApp pid= %s" % pid)
711                            return label_list, suite_info, True
712        else:
713            LOG.error("Hjsunit run timeout {}s reached".format(timeout))
714            LOG.debug("current JSApp pid= %s" % pid)
715            return label_list, suite_info, False
716
717    @classmethod
718    def _init_suites_start(cls, line, pattern, pid):
719        matcher = re.match(pattern, line.strip())
720        if matcher and matcher.group(1):
721            pid = matcher.group(1)
722            return pid, True
723        return pid, False
724
725    def run_js_outer(self, request):
726        try:
727            LOG.debug("Start execute xdevice extension JSUnit Test")
728            LOG.debug("Outer version about Community")
729            self.result = os.path.join(
730                request.config.report_path, "result",
731                '.'.join((request.get_module_name(), "xml")))
732            self.config = request.config
733            self.config.device = request.config.environment.devices[0]
734
735            config_file = request.root.source.config_file
736            suite_file = request.root.source.source_file
737
738            if not suite_file:
739                raise ParamError(
740                    "test source '%s' not exists" %
741                    request.root.source.source_string, error_no="00110")
742
743            LOG.debug("Test case file path: %s" % suite_file)
744            # avoid hilog service stuck issue
745            self.config.device.connector_command("shell stop_service hilogd",
746                                           timeout=30 * 1000)
747            self.config.device.connector_command("shell start_service hilogd",
748                                           timeout=30 * 1000)
749            time.sleep(10)
750
751            self.config.device.set_device_report_path(request.config.report_path)
752            self.config.device.connector_command("shell hilog -r", timeout=30 * 1000)
753            self._run_jsunit_outer(config_file, request)
754        except Exception as exception:
755            self.error_message = exception
756            if not getattr(exception, "error_no", ""):
757                setattr(exception, "error_no", "03409")
758            LOG.exception(self.error_message, exc_info=False, error_no="03409")
759            raise exception
760        finally:
761            serial = "{}_{}".format(str(self.config.device.__get_serial__()), time.time_ns())
762            log_tar_file_name = "{}".format(str(serial).replace(":", "_"))
763            if hasattr(self.config, ConfigConst.device_log) and \
764                    self.config.device_log.get(ConfigConst.tag_enable) == ConfigConst.device_log_on:
765                self.config.device.device_log_collector.start_get_crash_log(log_tar_file_name,
766                                                                            module_name=request.get_module_name())
767            self.config.device.device_log_collector.remove_log_address(self.device_log, self.hilog)
768            self.config.device.device_log_collector.stop_catch_device_log(self.log_proc)
769            self.config.device.device_log_collector.stop_catch_device_log(self.hilog_proc)
770            do_module_kit_teardown(request)
771            self.result = check_result_report(
772                request.config.report_path, self.result, self.error_message)
773
774    def _run_jsunit_outer(self, config_file, request):
775        if not os.path.exists(config_file):
776            LOG.error("Error: Test cases don't exist %s." % config_file)
777            raise ParamError(
778                "Error: Test cases don't exist %s." % config_file,
779                error_no="00102")
780
781        json_config = JsonParser(config_file)
782        self.kits = get_kit_instances(json_config,
783                                      self.config.resource_path,
784                                      self.config.testcases_path)
785
786        package, ability_name = self._get_driver_config_outer(json_config)
787        self.config.device.connector_command("target mount")
788        do_module_kit_setup(request, self.kits)
789
790        self.hilog = get_device_log_file(
791            request.config.report_path,
792            request.config.device.__get_serial__(),
793            "device_hilog",
794            module_name=request.get_module_name())
795
796        hilog_open = os.open(self.hilog, os.O_WRONLY | os.O_CREAT | os.O_APPEND,
797                             0o755)
798        self.config.device.device_log_collector.add_log_address(self.device_log, self.hilog)
799        with os.fdopen(hilog_open, "a") as hilog_file_pipe:
800            if hasattr(self.config, ConfigConst.device_log) and \
801                    self.config.device_log.get(ConfigConst.tag_enable) == ConfigConst.device_log_on:
802                self.config.device.device_log_collector.clear_crash_log()
803            self.log_proc, self.hilog_proc = self.config.device.device_log_collector. \
804                start_catch_device_log(hilog_file_pipe=hilog_file_pipe)
805
806        # execute test case
807        command = "shell aa start -d 123 -a %s -b %s" \
808                  % (ability_name, package)
809        result_value = self.config.device.connector_command(command)
810        if result_value and "start ability successfully" in \
811                str(result_value).lower():
812            setattr(self, "start_success", True)
813            LOG.info("execute %s's testcase success. result value=%s"
814                     % (package, result_value))
815        else:
816            LOG.info("execute %s's testcase failed. result value=%s"
817                     % (package, result_value))
818            raise RuntimeError("hjsunit test run error happened!")
819
820        self.start_time = time.time()
821        timeout_config = get_config_value('test-timeout',
822                                          json_config.get_driver(),
823                                          False, 60000)
824        timeout = int(timeout_config) / 1000
825        self.generate_console_output(request, timeout)
826
827    def _jsunit_clear_outer(self):
828        self.config.device.execute_shell_command(
829            "rm -r /%s/%s/%s/%s" % ("data", "local", "tmp", "ajur"))
830
831    def _get_driver_config_outer(self, json_config):
832        package = get_config_value('package', json_config.get_driver(), False)
833        default_ability = "{}.MainAbility".format(package)
834        ability_name = get_config_value('abilityName', json_config.
835                                        get_driver(), False, default_ability)
836        self.xml_output = get_xml_output(self.config, json_config)
837        timeout_config = get_config_value('native-test-timeout',
838                                          json_config.get_driver(), False)
839        if timeout_config:
840            self.timeout = int(timeout_config)
841
842        if not package:
843            raise ParamError("Can't find package in config file.",
844                             error_no="03201")
845        return package, ability_name
846
847    def _make_exclude_list_file(self, request):
848        filter_list = []
849        if "all-test-file-exclude-filter" in self.config.testargs:
850            json_file_list = self.config.testargs.get(
851                "all-test-file-exclude-filter")
852            self.config.testargs.pop("all-test-file-exclude-filter")
853            if not json_file_list:
854                LOG.debug("all-test-file-exclude-filter value is empty!")
855            else:
856                if not os.path.isfile(json_file_list[0]):
857                    LOG.warning(
858                        "[{}] is not a valid file".format(json_file_list[0]))
859                    return []
860                file_open = os.open(json_file_list[0], os.O_RDONLY,
861                                    stat.S_IWUSR | stat.S_IRUSR)
862                with os.fdopen(file_open, "r") as file_handler:
863                    json_data = json.load(file_handler)
864                exclude_list = json_data.get(
865                    DeviceTestType.jsunit_test, [])
866
867                for exclude in exclude_list:
868                    if request.get_module_name() not in exclude:
869                        continue
870                    filter_list.extend(exclude.get(request.get_module_name()))
871        return filter_list
872
873    def _is_match_marker(self, line):
874        if self.ace_log_marker:
875            return line.lower().find(self.ace_log_marker) != -1
876        else:
877            for mark_str in _ACE_LOG_MARKER:
878                if line.lower().find(mark_str) != -1:
879                    self.ace_log_marker = mark_str
880                    return True
881            return False
882
883    def __result__(self):
884        return self.result if os.path.exists(self.result) else ""
885
886
887@Plugin(type=Plugin.DRIVER, id=DeviceTestType.ltp_posix_test)
888class LTPPosixTestDriver(IDriver):
889    def __init__(self):
890        self.timeout = 80 * 1000
891        self.start_time = None
892        self.result = ""
893        self.error_message = ""
894        self.kits = []
895        self.config = None
896        self.handler = None
897        # log
898        self.hilog = None
899        self.log_proc = None
900
901    def __check_environment__(self, device_options):
902        pass
903
904    def __check_config__(self, config):
905        pass
906
907    def __execute__(self, request):
908        try:
909            LOG.debug("Start execute xdevice extension LTP Posix Test")
910            self.result = os.path.join(
911                request.config.report_path, "result",
912                '.'.join((request.get_module_name(), "xml")))
913            self.config = request.config
914            self.config.device = request.config.environment.devices[0]
915
916            config_file = request.root.source.config_file
917            suite_file = request.root.source.source_file
918
919            if not suite_file:
920                raise ParamError(
921                    "test source '%s' not exists" %
922                    request.root.source.source_string, error_no="00110")
923
924            LOG.debug("Test case file path: %s" % suite_file)
925            # avoid hilog service stuck issue
926            self.config.device.connector_command("shell stop_service hilogd",
927                                           timeout=30 * 1000)
928            self.config.device.connector_command("shell start_service hilogd",
929                                           timeout=30 * 1000)
930            time.sleep(10)
931
932            self.config.device.connector_command("shell hilog -r", timeout=30 * 1000)
933            self._run_posix(config_file, request)
934        except Exception as exception:
935            self.error_message = exception
936            if not getattr(exception, "error_no", ""):
937                setattr(exception, "error_no", "03409")
938            LOG.exception(self.error_message, exc_info=True, error_no="03409")
939            raise exception
940        finally:
941            self.config.device.device_log_collector.remove_log_address(None, self.hilog)
942            self.config.device.device_log_collector.stop_catch_device_log(self.log_proc)
943            self.result = check_result_report(
944                request.config.report_path, self.result, self.error_message)
945
946    def _run_posix(self, config_file, request):
947        try:
948            if not os.path.exists(config_file):
949                LOG.error("Error: Test cases don't exist %s." % config_file)
950                raise ParamError(
951                    "Error: Test cases don't exist %s." % config_file,
952                    error_no="00102")
953
954            json_config = JsonParser(config_file)
955            self.kits = get_kit_instances(json_config,
956                                          self.config.resource_path,
957                                          self.config.testcases_path)
958            self.config.device.connector_command("target mount")
959            test_list = None
960            dst = None
961            for kit in self.kits:
962                test_list, dst = kit.__setup__(request.config.device,
963                                               request=request)
964            # apply execute right
965            self.config.device.connector_command("shell chmod -R 777 {}".format(dst))
966
967            self.hilog = get_device_log_file(
968                request.config.report_path,
969                request.config.device.__get_serial__(),
970                "device_hilog",
971                module_name=request.get_module_name())
972
973            hilog_open = os.open(self.hilog, os.O_WRONLY | os.O_CREAT | os.O_APPEND,
974                                 0o755)
975            self.config.device.device_log_collector.add_log_address(None, self.hilog)
976            with os.fdopen(hilog_open, "a") as hilog_file_pipe:
977                _, self.log_proc = self.config.device.device_log_collector.\
978                    start_catch_device_log(hilog_file_pipe=hilog_file_pipe)
979                if hasattr(self.config, "history_report_path") and \
980                        self.config.testargs.get("test"):
981                    self._do_test_retry(request, self.config.testargs)
982                else:
983                    self._do_test_run(request, test_list)
984        finally:
985            do_module_kit_teardown(request)
986
987    def _do_test_retry(self, request, testargs):
988        un_pass_list = []
989        for test in testargs.get("test"):
990            test_item = test.split("#")
991            if len(test_item) != 2:
992                continue
993            un_pass_list.append(test_item[1])
994        LOG.debug("LTP Posix un pass list: [{}]".format(un_pass_list))
995        self._do_test_run(request, un_pass_list)
996
997    def _do_test_run(self, request, test_list):
998        for test_bin in test_list:
999            if not test_bin.endswith(".run-test"):
1000                continue
1001            listeners = request.listeners
1002            for listener in listeners:
1003                listener.device_sn = self.config.device.device_sn
1004            parsers = get_plugin(Plugin.PARSER,
1005                                 "OpenSourceTest")
1006            parser_instances = []
1007            for parser in parsers:
1008                parser_instance = parser.__class__()
1009                parser_instance.suite_name = request.root.source. \
1010                    test_name
1011                parser_instance.test_name = test_bin.replace("./", "")
1012                parser_instance.listeners = listeners
1013                parser_instances.append(parser_instance)
1014            self.handler = ShellHandler(parser_instances)
1015            self.handler.add_process_method(_ltp_output_method)
1016            result_message = self.config.device.connector_command(
1017                "shell {}".format(test_bin))
1018            LOG.info("get result from command {}".
1019                     format(result_message))
1020            process_command_ret(result_message, self.handler)
1021
1022    def __result__(self):
1023        return self.result if os.path.exists(self.result) else ""
1024
1025
1026def _lock_screen(device):
1027    device.execute_shell_command("svc power stayon false")
1028    time.sleep(1)
1029
1030
1031def _sleep_according_to_result(result):
1032    if result:
1033        time.sleep(1)
1034
1035
1036def _ltp_output_method(handler, output, end_mark="\n"):
1037    content = output
1038    if handler.unfinished_line:
1039        content = "".join((handler.unfinished_line, content))
1040        handler.unfinished_line = ""
1041    lines = content.split(end_mark)
1042    if content.endswith(end_mark):
1043        # get rid of the tail element of this list contains empty str
1044        return lines[:-1]
1045    else:
1046        handler.unfinished_line = lines[-1]
1047        # not return the tail element of this list contains unfinished str,
1048        # so we set position -1
1049        return lines
1050