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