• 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 time
21import json
22import stat
23
24from xdevice import ConfigConst
25from xdevice import ParamError
26from xdevice import IDriver
27from xdevice import platform_logger
28from xdevice import Plugin
29from xdevice import get_plugin
30from xdevice import JsonParser
31from xdevice import ShellHandler
32from xdevice import TestDescription
33from xdevice import get_device_log_file
34from xdevice import check_result_report
35from xdevice import get_kit_instances
36from xdevice import get_config_value
37from xdevice import do_module_kit_setup
38from xdevice import do_module_kit_teardown
39from xdevice import DeviceTestType
40from xdevice import CommonParserType
41from xdevice import FilePermission
42
43from ohos.testkit.kit import oh_jsunit_para_parse
44from ohos.executor.listener import CollectingPassListener
45
46__all__ = ["OHJSUnitTestDriver", "OHKernelTestDriver"]
47
48TIME_OUT = 300 * 1000
49
50LOG = platform_logger("OpenHarmony")
51
52
53@Plugin(type=Plugin.DRIVER, id=DeviceTestType.oh_kernel_test)
54class OHKernelTestDriver(IDriver):
55    """
56        OpenHarmonyKernelTest
57    """
58    def __init__(self):
59        self.timeout = 30 * 1000
60        self.result = ""
61        self.error_message = ""
62        self.kits = []
63        self.config = None
64        self.runner = None
65        # log
66        self.device_log = None
67        self.hilog = None
68        self.log_proc = None
69        self.hilog_proc = None
70
71    def __check_environment__(self, device_options):
72        pass
73
74    def __check_config__(self, config):
75        pass
76
77    def __execute__(self, request):
78        try:
79            LOG.debug("Start to Execute OpenHarmony Kernel Test")
80
81            self.config = request.config
82            self.config.device = request.config.environment.devices[0]
83
84            config_file = request.root.source.config_file
85
86            self.result = "%s.xml" % \
87                          os.path.join(request.config.report_path,
88                                       "result", request.get_module_name())
89            self.device_log = get_device_log_file(
90                request.config.report_path,
91                request.config.device.__get_serial__(),
92                "device_log")
93
94            self.hilog = get_device_log_file(
95                request.config.report_path,
96                request.config.device.__get_serial__(),
97                "device_hilog")
98
99            device_log_open = os.open(self.device_log, os.O_WRONLY | os.O_CREAT |
100                                      os.O_APPEND, FilePermission.mode_755)
101            hilog_open = os.open(self.hilog, os.O_WRONLY | os.O_CREAT | os.O_APPEND,
102                                 FilePermission.mode_755)
103            self.config.device.device_log_collector.add_log_address(self.device_log, self.hilog)
104            with os.fdopen(device_log_open, "a") as log_file_pipe, \
105                    os.fdopen(hilog_open, "a") as hilog_file_pipe:
106                self.log_proc, self.hilog_proc = self.config.device.device_log_collector.\
107                    start_catch_device_log(log_file_pipe, hilog_file_pipe)
108                self._run_oh_kernel(config_file, request.listeners, request)
109                log_file_pipe.flush()
110                hilog_file_pipe.flush()
111        except Exception as exception:
112            self.error_message = exception
113            if not getattr(exception, "error_no", ""):
114                setattr(exception, "error_no", "03409")
115            LOG.exception(self.error_message, exc_info=False, error_no="03409")
116            raise exception
117        finally:
118            do_module_kit_teardown(request)
119            self.config.device.device_log_collector.remove_log_address(self.device_log, self.hilog)
120            self.config.device.device_log_collector.stop_catch_device_log(self.log_proc)
121            self.config.device.device_log_collector.stop_catch_device_log(self.hilog_proc)
122            self.result = check_result_report(
123                request.config.report_path, self.result, self.error_message)
124
125    def _run_oh_kernel(self, config_file, listeners=None, request=None):
126        try:
127            json_config = JsonParser(config_file)
128            kits = get_kit_instances(json_config, self.config.resource_path,
129                                     self.config.testcases_path)
130            self._get_driver_config(json_config)
131            do_module_kit_setup(request, kits)
132            self.runner = OHKernelTestRunner(self.config)
133            self.runner.suite_name = request.get_module_name()
134            self.runner.run(listeners)
135        finally:
136            do_module_kit_teardown(request)
137
138    def _get_driver_config(self, json_config):
139        target_test_path = get_config_value('native-test-device-path',
140                                            json_config.get_driver(), False)
141        test_suite_name = get_config_value('test-suite-name',
142                                           json_config.get_driver(), False)
143        test_suites_list = get_config_value('test-suites-list',
144                                            json_config.get_driver(), False)
145        timeout_limit = get_config_value('timeout-limit',
146                                         json_config.get_driver(), False)
147        conf_file = get_config_value('conf-file',
148                                     json_config.get_driver(), False)
149        self.config.arg_list = {}
150        if target_test_path:
151            self.config.target_test_path = target_test_path
152        if test_suite_name:
153            self.config.arg_list["test-suite-name"] = test_suite_name
154        if test_suites_list:
155            self.config.arg_list["test-suites-list"] = test_suites_list
156        if timeout_limit:
157            self.config.arg_list["timeout-limit"] = timeout_limit
158        if conf_file:
159            self.config.arg_list["conf-file"] = conf_file
160        timeout_config = get_config_value('shell-timeout',
161                                          json_config.get_driver(), False)
162        if timeout_config:
163            self.config.timeout = int(timeout_config)
164        else:
165            self.config.timeout = TIME_OUT
166
167    def __result__(self):
168        return self.result if os.path.exists(self.result) else ""
169
170
171class OHKernelTestRunner:
172    def __init__(self, config):
173        self.suite_name = None
174        self.config = config
175        self.arg_list = config.arg_list
176
177    def run(self, listeners):
178        handler = self._get_shell_handler(listeners)
179        # hdc shell cd /data/local/tmp/OH_kernel_test;
180        # sh runtest test -t OpenHarmony_RK3568_config
181        # -n OpenHarmony_RK3568_skiptest -l 60
182        command = "cd %s; chmod +x *; sh runtest test %s" % (
183            self.config.target_test_path, self.get_args_command())
184        self.config.device.execute_shell_command(
185            command, timeout=self.config.timeout, receiver=handler, retry=0)
186
187    def _get_shell_handler(self, listeners):
188        parsers = get_plugin(Plugin.PARSER, CommonParserType.oh_kernel_test)
189        if parsers:
190            parsers = parsers[:1]
191        parser_instances = []
192        for parser in parsers:
193            parser_instance = parser.__class__()
194            parser_instance.suites_name = self.suite_name
195            parser_instance.listeners = listeners
196            parser_instances.append(parser_instance)
197        handler = ShellHandler(parser_instances)
198        return handler
199
200    def get_args_command(self):
201        args_commands = ""
202        for key, value in self.arg_list.items():
203            if key == "test-suite-name" or key == "test-suites-list":
204                args_commands = "%s -t %s" % (args_commands, value)
205            elif key == "conf-file":
206                args_commands = "%s -n %s" % (args_commands, value)
207            elif key == "timeout-limit":
208                args_commands = "%s -l %s" % (args_commands, value)
209        return args_commands
210
211
212@Plugin(type=Plugin.DRIVER, id=DeviceTestType.oh_jsunit_test)
213class OHJSUnitTestDriver(IDriver):
214    """
215       OHJSUnitTestDriver is a Test that runs a native test package on
216       given device.
217    """
218
219    def __init__(self):
220        self.timeout = 80 * 1000
221        self.start_time = None
222        self.result = ""
223        self.error_message = ""
224        self.kits = []
225        self.config = None
226        self.runner = None
227        self.rerun = True
228        self.rerun_all = True
229        # log
230        self.device_log = None
231        self.hilog = None
232        self.log_proc = None
233        self.hilog_proc = None
234
235    def __check_environment__(self, device_options):
236        pass
237
238    def __check_config__(self, config):
239        pass
240
241    def __execute__(self, request):
242        try:
243            LOG.debug("Start execute OpenHarmony JSUnitTest")
244            self.result = os.path.join(
245                request.config.report_path, "result",
246                '.'.join((request.get_module_name(), "xml")))
247            self.config = request.config
248            self.config.device = request.config.environment.devices[0]
249
250            config_file = request.root.source.config_file
251            suite_file = request.root.source.source_file
252
253            if not suite_file:
254                raise ParamError(
255                    "test source '%s' not exists" %
256                    request.root.source.source_string, error_no="00110")
257            LOG.debug("Test case file path: %s" % suite_file)
258            self.config.device.set_device_report_path(request.config.report_path)
259            self.hilog = get_device_log_file(request.config.report_path,
260                                        request.config.device.__get_serial__() + "_" + request.
261                                        get_module_name(),
262                                        "device_hilog")
263
264            hilog_open = os.open(self.hilog, os.O_WRONLY | os.O_CREAT | os.O_APPEND,
265                                 0o755)
266            self.config.device.device_log_collector.add_log_address(self.device_log, self.hilog)
267            self.config.device.execute_shell_command(command="hilog -r")
268            with os.fdopen(hilog_open, "a") as hilog_file_pipe:
269                if hasattr(self.config, "device_log") \
270                        and self.config.device_log == ConfigConst.device_log_on \
271                        and hasattr(self.config.device, "clear_crash_log"):
272                    self.config.device.device_log_collector.clear_crash_log()
273                self.log_proc, self.hilog_proc = self.config.device.device_log_collector.\
274                    start_catch_device_log(hilog_file_pipe=hilog_file_pipe)
275                self._run_oh_jsunit(config_file, request)
276        except Exception as exception:
277            self.error_message = exception
278            if not getattr(exception, "error_no", ""):
279                setattr(exception, "error_no", "03409")
280            LOG.exception(self.error_message, exc_info=True, error_no="03409")
281            raise exception
282        finally:
283            try:
284                self._handle_logs(request)
285            finally:
286                self.result = check_result_report(
287                    request.config.report_path, self.result, self.error_message)
288
289    def __dry_run_execute__(self, request):
290        LOG.debug("Start dry run xdevice JSUnit Test")
291        self.config = request.config
292        self.config.device = request.config.environment.devices[0]
293        config_file = request.root.source.config_file
294        suite_file = request.root.source.source_file
295
296        if not suite_file:
297            raise ParamError(
298                "test source '%s' not exists" %
299                request.root.source.source_string, error_no="00110")
300        LOG.debug("Test case file path: %s" % suite_file)
301        self._dry_run_oh_jsunit(config_file, request)
302
303    def _dry_run_oh_jsunit(self, config_file, request):
304        try:
305            if not os.path.exists(config_file):
306                LOG.error("Error: Test cases don't exist %s." % config_file)
307                raise ParamError(
308                    "Error: Test cases don't exist %s." % config_file,
309                    error_no="00102")
310            json_config = JsonParser(config_file)
311            self.kits = get_kit_instances(json_config,
312                                          self.config.resource_path,
313                                          self.config.testcases_path)
314
315            self._get_driver_config(json_config)
316            self.config.device.connector_command("target mount")
317            do_module_kit_setup(request, self.kits)
318            self.runner = OHJSUnitTestRunner(self.config)
319            self.runner.suites_name = request.get_module_name()
320            # execute test case
321            self._get_runner_config(json_config)
322            oh_jsunit_para_parse(self.runner, self.config.testargs)
323
324            test_to_run = self._collect_test_to_run()
325            LOG.info("Collected suite count is: {}, test count is: {}".
326                     format(len(self.runner.expect_tests_dict.keys()),
327                            len(test_to_run) if test_to_run else 0))
328        finally:
329            do_module_kit_teardown(request)
330
331    def _run_oh_jsunit(self, config_file, request):
332        try:
333            if not os.path.exists(config_file):
334                LOG.error("Error: Test cases don't exist %s." % config_file)
335                raise ParamError(
336                    "Error: Test cases don't exist %s." % config_file,
337                    error_no="00102")
338            json_config = JsonParser(config_file)
339            self.kits = get_kit_instances(json_config,
340                                          self.config.resource_path,
341                                          self.config.testcases_path)
342
343            self._get_driver_config(json_config)
344            self.config.device.connector_command("target mount")
345            do_module_kit_setup(request, self.kits)
346            self.runner = OHJSUnitTestRunner(self.config)
347            self.runner.suites_name = request.get_module_name()
348            self._get_runner_config(json_config)
349            if hasattr(self.config, "history_report_path") and \
350                    self.config.testargs.get("test"):
351                self._do_test_retry(request.listeners, self.config.testargs)
352            else:
353                if self.rerun:
354                    self.runner.retry_times = self.runner.MAX_RETRY_TIMES
355                    # execute test case
356                self._make_exclude_list_file(request)
357                oh_jsunit_para_parse(self.runner, self.config.testargs)
358                self._do_test_run(listener=request.listeners)
359
360        finally:
361            do_module_kit_teardown(request)
362
363    def _get_driver_config(self, json_config):
364        package = get_config_value('package-name',
365                                   json_config.get_driver(), False)
366        module = get_config_value('module-name',
367                                  json_config.get_driver(), False)
368        bundle = get_config_value('bundle-name',
369                                  json_config. get_driver(), False)
370        is_rerun = get_config_value('rerun', json_config.get_driver(), False)
371
372        self.config.package_name = package
373        self.config.module_name = module
374        self.config.bundle_name = bundle
375        self.rerun = True if is_rerun == 'true' else False
376
377        if not package and not module:
378            raise ParamError("Neither package nor module is found"
379                             " in config file.", error_no="03201")
380        timeout_config = get_config_value("shell-timeout",
381                                          json_config.get_driver(), False)
382        if timeout_config:
383            self.config.timeout = int(timeout_config)
384        else:
385            self.config.timeout = TIME_OUT
386
387    def _get_runner_config(self, json_config):
388        test_timeout = get_config_value('test-timeout',
389                                        json_config.get_driver(), False)
390        if test_timeout:
391            self.runner.add_arg("wait_time", int(test_timeout))
392
393        testcase_timeout = get_config_value('testcase-timeout',
394                                            json_config.get_driver(), False)
395        if testcase_timeout:
396            self.runner.add_arg("timeout", int(testcase_timeout))
397
398    def _do_test_run(self, listener):
399        test_to_run = self._collect_test_to_run()
400        LOG.info("Collected suite count is: {}, test count is: {}".
401                 format(len(self.runner.expect_tests_dict.keys()),
402                        len(test_to_run) if test_to_run else 0))
403        if not test_to_run or not self.rerun:
404            self.runner.run(listener)
405            self.runner.notify_finished()
406        else:
407            self._run_with_rerun(listener, test_to_run)
408
409    def _collect_test_to_run(self):
410        run_results = self.runner.dry_run()
411        return run_results
412
413    def _run_tests(self, listener):
414        test_tracker = CollectingPassListener()
415        listener_copy = listener.copy()
416        listener_copy.append(test_tracker)
417        self.runner.run(listener_copy)
418        test_run = test_tracker.get_current_run_results()
419        return test_run
420
421    def _run_with_rerun(self, listener, expected_tests):
422        LOG.debug("Ready to run with rerun, expect run: %s"
423                  % len(expected_tests))
424        test_run = self._run_tests(listener)
425        self.runner.retry_times -= 1
426        LOG.debug("Run with rerun, has run: %s" % len(test_run)
427                  if test_run else 0)
428        if len(test_run) < len(expected_tests):
429            expected_tests = TestDescription.remove_test(expected_tests,
430                                                         test_run)
431            if not expected_tests:
432                LOG.debug("No tests to re-run twice,please check")
433                self.runner.notify_finished()
434            else:
435                self._rerun_twice(expected_tests, listener)
436        else:
437            LOG.debug("Rerun once success")
438            self.runner.notify_finished()
439
440    def _rerun_twice(self, expected_tests, listener):
441        tests = []
442        for test in expected_tests:
443            tests.append("%s#%s" % (test.class_name, test.test_name))
444        self.runner.add_arg("class", ",".join(tests))
445        LOG.debug("Ready to rerun twice, expect run: %s" % len(expected_tests))
446        test_run = self._run_tests(listener)
447        self.runner.retry_times -= 1
448        LOG.debug("Rerun twice, has run: %s" % len(test_run))
449        if len(test_run) < len(expected_tests):
450            expected_tests = TestDescription.remove_test(expected_tests,
451                                                         test_run)
452            if not expected_tests:
453                LOG.debug("No tests to re-run third,please check")
454                self.runner.notify_finished()
455            else:
456                self._rerun_third(expected_tests, listener)
457        else:
458            LOG.debug("Rerun twice success")
459            self.runner.notify_finished()
460
461    def _rerun_third(self, expected_tests, listener):
462        tests = []
463        for test in expected_tests:
464            tests.append("%s#%s" % (test.class_name, test.test_name))
465        self.runner.add_arg("class", ",".join(tests))
466        LOG.debug("Rerun to rerun third, expect run: %s" % len(expected_tests))
467        self._run_tests(listener)
468        LOG.debug("Rerun third success")
469        self.runner.notify_finished()
470
471    def _make_exclude_list_file(self, request):
472        if "all-test-file-exclude-filter" in self.config.testargs:
473            json_file_list = self.config.testargs.get(
474                "all-test-file-exclude-filter")
475            self.config.testargs.pop("all-test-file-exclude-filter")
476            if not json_file_list:
477                LOG.warning("all-test-file-exclude-filter value is empty!")
478            else:
479                if not os.path.isfile(json_file_list[0]):
480                    LOG.warning(
481                        "[{}] is not a valid file".format(json_file_list[0]))
482                    return
483                file_open = os.open(json_file_list[0], os.O_RDONLY,
484                                    stat.S_IWUSR | stat.S_IRUSR)
485                with os.fdopen(file_open, "r") as file_handler:
486                    json_data = json.load(file_handler)
487                exclude_list = json_data.get(
488                    DeviceTestType.oh_jsunit_test, [])
489                filter_list = []
490                for exclude in exclude_list:
491                    if request.get_module_name() not in exclude:
492                        continue
493                    filter_list.extend(exclude.get(request.get_module_name()))
494                if not isinstance(self.config.testargs, dict):
495                    return
496                if 'notClass' in self.config.testargs.keys():
497                    filter_list.extend(self.config.testargs.get('notClass', []))
498                self.config.testargs.update({"notClass": filter_list})
499
500    def _do_test_retry(self, listener, testargs):
501        tests_dict = dict()
502        case_list = list()
503        for test in testargs.get("test"):
504            test_item = test.split("#")
505            if len(test_item) != 2:
506                continue
507            case_list.append(test)
508            if test_item[0] not in tests_dict:
509                tests_dict.update({test_item[0] : []})
510            tests_dict.get(test_item[0]).append(
511                TestDescription(test_item[0], test_item[1]))
512        self.runner.add_arg("class", ",".join(case_list))
513        self.runner.expect_tests_dict = tests_dict
514        self.config.testargs.pop("test")
515        self.runner.run(listener)
516        self.runner.notify_finished()
517
518    def _handle_logs(self, request):
519        serial = "{}_{}".format(str(self.config.device.__get_serial__()), time.time_ns())
520        log_tar_file_name = "{}_{}".format(request.get_module_name(),
521                                           str(serial).replace(":", "_"))
522        if hasattr(self.config, "device_log") and \
523                self.config.device_log == ConfigConst.device_log_on \
524                and hasattr(self.config.device, "start_get_crash_log"):
525            self.config.device.device_log_collector. \
526                start_get_crash_log(log_tar_file_name)
527        self.config.device.device_log_collector. \
528            remove_log_address(self.device_log, self.hilog)
529        self.config.device.device_log_collector. \
530            stop_catch_device_log(self.log_proc)
531        self.config.device.device_log_collector. \
532            stop_catch_device_log(self.hilog_proc)
533
534    def __result__(self):
535        return self.result if os.path.exists(self.result) else ""
536
537
538class OHJSUnitTestRunner:
539    MAX_RETRY_TIMES = 3
540
541    def __init__(self, config):
542        self.arg_list = {}
543        self.suites_name = None
544        self.config = config
545        self.rerun_attemp = 3
546        self.suite_recorder = {}
547        self.finished = False
548        self.expect_tests_dict = dict()
549        self.finished_observer = None
550        self.retry_times = 1
551
552    def dry_run(self):
553        parsers = get_plugin(Plugin.PARSER, CommonParserType.oh_jsunit_list)
554        if parsers:
555            parsers = parsers[:1]
556        parser_instances = []
557        for parser in parsers:
558            parser_instance = parser.__class__()
559            parser_instances.append(parser_instance)
560        handler = ShellHandler(parser_instances)
561        command = self._get_dry_run_command()
562        self.config.device.execute_shell_command(
563            command, timeout=self.config.timeout, receiver=handler, retry=0)
564        self.expect_tests_dict = parser_instances[0].tests_dict
565        return parser_instances[0].tests
566
567    def run(self, listener):
568        handler = self._get_shell_handler(listener)
569        command = self._get_run_command()
570        self.config.device.execute_shell_command(
571            command, timeout=self.config.timeout, receiver=handler, retry=0)
572
573    def notify_finished(self):
574        if self.finished_observer:
575            self.finished_observer.notify_task_finished()
576        self.retry_times -= 1
577
578    def _get_shell_handler(self, listener):
579        parsers = get_plugin(Plugin.PARSER, CommonParserType.oh_jsunit)
580        if parsers:
581            parsers = parsers[:1]
582        parser_instances = []
583        for parser in parsers:
584            parser_instance = parser.__class__()
585            parser_instance.suites_name = self.suites_name
586            parser_instance.listeners = listener
587            parser_instance.runner = self
588            parser_instances.append(parser_instance)
589            self.finished_observer = parser_instance
590        handler = ShellHandler(parser_instances)
591        return handler
592
593    def add_arg(self, name, value):
594        if not name or not value:
595            return
596        self.arg_list[name] = value
597
598    def remove_arg(self, name):
599        if not name:
600            return
601        if name in self.arg_list:
602            del self.arg_list[name]
603
604    def get_args_command(self):
605        args_commands = ""
606        for key, value in self.arg_list.items():
607            if "wait_time" == key:
608                args_commands = "%s -w %s " % (args_commands, value)
609            else:
610                args_commands = "%s -s %s %s " % (args_commands, key, value)
611        return args_commands
612
613    def _get_run_command(self):
614        command = ""
615        if self.config.package_name:
616            # aa test -p ${packageName} -b ${bundleName}-s unittest OpenHarmonyTestRunner
617            command = "aa test -p %s -b %s -s unittest OpenHarmonyTestRunner" \
618                      " %s" % (self.config.package_name,
619                               self.config.bundle_name,
620                               self.get_args_command())
621        elif self.config.module_name:
622            #  aa test -m ${moduleName}  -b ${bundleName} -s unittest OpenHarmonyTestRunner
623            command = "aa test -m %s -b %s -s unittest OpenHarmonyTestRunner" \
624                      " %s" % (self.config.module_name,
625                               self.config.bundle_name,
626                               self.get_args_command())
627        return command
628
629    def _get_dry_run_command(self):
630        command = ""
631        if self.config.package_name:
632            command = "aa test -p %s -b %s -s unittest OpenHarmonyTestRunner" \
633                      " %s -s dryRun true" % (self.config.package_name,
634                                              self.config.bundle_name,
635                                              self.get_args_command())
636        elif self.config.module_name:
637            command = "aa test -m %s -b %s -s unittest OpenHarmonyTestRunner" \
638                      " %s -s dryRun true" % (self.config.module_name,
639                                              self.config.bundle_name,
640                                              self.get_args_command())
641
642        return command
643