• 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            serial = "{}_{}".format(str(self.config.device.__get_serial__()), time.time_ns())
284            log_tar_file_name = "{}_{}".format(request.get_module_name(),
285                                               str(serial).replace(":", "_"))
286            if hasattr(self.config, "device_log") and \
287                    self.config.device_log == ConfigConst.device_log_on \
288                    and hasattr(self.config.device, "start_get_crash_log"):
289                self.config.device.device_log_collector.start_get_crash_log(log_tar_file_name)
290            self.config.device.device_log_collector.remove_log_address(self.device_log, self.hilog)
291            self.config.device.device_log_collector.stop_catch_device_log(self.log_proc)
292            self.config.device.device_log_collector.stop_catch_device_log(self.hilog_proc)
293            self.result = check_result_report(
294                request.config.report_path, self.result, self.error_message)
295
296    def __dry_run_execute__(self, request):
297        LOG.debug("Start dry run xdevice JSUnit Test")
298        self.config = request.config
299        self.config.device = request.config.environment.devices[0]
300        config_file = request.root.source.config_file
301        suite_file = request.root.source.source_file
302
303        if not suite_file:
304            raise ParamError(
305                "test source '%s' not exists" %
306                request.root.source.source_string, error_no="00110")
307        LOG.debug("Test case file path: %s" % suite_file)
308        self._dry_run_oh_jsunit(config_file, request)
309
310    def _dry_run_oh_jsunit(self, config_file, request):
311        try:
312            if not os.path.exists(config_file):
313                LOG.error("Error: Test cases don't exist %s." % config_file)
314                raise ParamError(
315                    "Error: Test cases don't exist %s." % config_file,
316                    error_no="00102")
317            json_config = JsonParser(config_file)
318            self.kits = get_kit_instances(json_config,
319                                          self.config.resource_path,
320                                          self.config.testcases_path)
321
322            self._get_driver_config(json_config)
323            self.config.device.connector_command("target mount")
324            do_module_kit_setup(request, self.kits)
325            self.runner = OHJSUnitTestRunner(self.config)
326            self.runner.suites_name = request.get_module_name()
327            # execute test case
328            self._get_runner_config(json_config)
329            oh_jsunit_para_parse(self.runner, self.config.testargs)
330
331            test_to_run = self._collect_test_to_run()
332            LOG.info("Collected suite count is: {}, test count is: {}".
333                     format(len(self.runner.expect_tests_dict.keys()),
334                            len(test_to_run) if test_to_run else 0))
335        finally:
336            do_module_kit_teardown(request)
337
338    def _run_oh_jsunit(self, config_file, request):
339        try:
340            if not os.path.exists(config_file):
341                LOG.error("Error: Test cases don't exist %s." % config_file)
342                raise ParamError(
343                    "Error: Test cases don't exist %s." % config_file,
344                    error_no="00102")
345            json_config = JsonParser(config_file)
346            self.kits = get_kit_instances(json_config,
347                                          self.config.resource_path,
348                                          self.config.testcases_path)
349
350            self._get_driver_config(json_config)
351            self.config.device.connector_command("target mount")
352            do_module_kit_setup(request, self.kits)
353            self.runner = OHJSUnitTestRunner(self.config)
354            self.runner.suites_name = request.get_module_name()
355            self._get_runner_config(json_config)
356            if hasattr(self.config, "history_report_path") and \
357                    self.config.testargs.get("test"):
358                self._do_test_retry(request.listeners, self.config.testargs)
359            else:
360                if self.rerun:
361                    self.runner.retry_times = self.runner.MAX_RETRY_TIMES
362                    # execute test case
363                self._make_exclude_list_file(request)
364                oh_jsunit_para_parse(self.runner, self.config.testargs)
365                self._do_test_run(listener=request.listeners)
366
367        finally:
368            do_module_kit_teardown(request)
369
370    def _get_driver_config(self, json_config):
371        package = get_config_value('package-name',
372                                   json_config.get_driver(), False)
373        module = get_config_value('module-name',
374                                  json_config.get_driver(), False)
375        bundle = get_config_value('bundle-name',
376                                  json_config. get_driver(), False)
377        is_rerun = get_config_value('rerun', json_config.get_driver(), False)
378
379        self.config.package_name = package
380        self.config.module_name = module
381        self.config.bundle_name = bundle
382        self.rerun = True if is_rerun == 'true' else False
383
384        if not package and not module:
385            raise ParamError("Neither package nor module is found"
386                             " in config file.", error_no="03201")
387        timeout_config = get_config_value("shell-timeout",
388                                          json_config.get_driver(), False)
389        if timeout_config:
390            self.config.timeout = int(timeout_config)
391        else:
392            self.config.timeout = TIME_OUT
393
394    def _get_runner_config(self, json_config):
395        test_timeout = get_config_value('test-timeout',
396                                        json_config.get_driver(), False)
397        if test_timeout:
398            self.runner.add_arg("wait_time", int(test_timeout))
399
400        testcase_timeout = get_config_value('testcase-timeout',
401                                            json_config.get_driver(), False)
402        if testcase_timeout:
403            self.runner.add_arg("timeout", int(testcase_timeout))
404
405    def _do_test_run(self, listener):
406        test_to_run = self._collect_test_to_run()
407        LOG.info("Collected suite count is: {}, test count is: {}".
408                 format(len(self.runner.expect_tests_dict.keys()),
409                        len(test_to_run) if test_to_run else 0))
410        if not test_to_run or not self.rerun:
411            self.runner.run(listener)
412            self.runner.notify_finished()
413        else:
414            self._run_with_rerun(listener, test_to_run)
415
416    def _collect_test_to_run(self):
417        run_results = self.runner.dry_run()
418        return run_results
419
420    def _run_tests(self, listener):
421        test_tracker = CollectingPassListener()
422        listener_copy = listener.copy()
423        listener_copy.append(test_tracker)
424        self.runner.run(listener_copy)
425        test_run = test_tracker.get_current_run_results()
426        return test_run
427
428    def _run_with_rerun(self, listener, expected_tests):
429        LOG.debug("Ready to run with rerun, expect run: %s"
430                  % len(expected_tests))
431        test_run = self._run_tests(listener)
432        self.runner.retry_times -= 1
433        LOG.debug("Run with rerun, has run: %s" % len(test_run)
434                  if test_run else 0)
435        if len(test_run) < len(expected_tests):
436            expected_tests = TestDescription.remove_test(expected_tests,
437                                                         test_run)
438            if not expected_tests:
439                LOG.debug("No tests to re-run twice,please check")
440                self.runner.notify_finished()
441            else:
442                self._rerun_twice(expected_tests, listener)
443        else:
444            LOG.debug("Rerun once success")
445            self.runner.notify_finished()
446
447    def _rerun_twice(self, expected_tests, listener):
448        tests = []
449        for test in expected_tests:
450            tests.append("%s#%s" % (test.class_name, test.test_name))
451        self.runner.add_arg("class", ",".join(tests))
452        LOG.debug("Ready to rerun twice, expect run: %s" % len(expected_tests))
453        test_run = self._run_tests(listener)
454        self.runner.retry_times -= 1
455        LOG.debug("Rerun twice, has run: %s" % len(test_run))
456        if len(test_run) < len(expected_tests):
457            expected_tests = TestDescription.remove_test(expected_tests,
458                                                         test_run)
459            if not expected_tests:
460                LOG.debug("No tests to re-run third,please check")
461                self.runner.notify_finished()
462            else:
463                self._rerun_third(expected_tests, listener)
464        else:
465            LOG.debug("Rerun twice success")
466            self.runner.notify_finished()
467
468    def _rerun_third(self, expected_tests, listener):
469        tests = []
470        for test in expected_tests:
471            tests.append("%s#%s" % (test.class_name, test.test_name))
472        self.runner.add_arg("class", ",".join(tests))
473        LOG.debug("Rerun to rerun third, expect run: %s" % len(expected_tests))
474        self._run_tests(listener)
475        LOG.debug("Rerun third success")
476        self.runner.notify_finished()
477
478    def _make_exclude_list_file(self, request):
479        if "all-test-file-exclude-filter" in self.config.testargs:
480            json_file_list = self.config.testargs.get(
481                "all-test-file-exclude-filter")
482            self.config.testargs.pop("all-test-file-exclude-filter")
483            if not json_file_list:
484                LOG.warning("all-test-file-exclude-filter value is empty!")
485            else:
486                if not os.path.isfile(json_file_list[0]):
487                    LOG.warning(
488                        "[{}] is not a valid file".format(json_file_list[0]))
489                    return
490                file_open = os.open(json_file_list[0], os.O_RDONLY,
491                                    stat.S_IWUSR | stat.S_IRUSR)
492                with os.fdopen(file_open, "r") as file_handler:
493                    json_data = json.load(file_handler)
494                exclude_list = json_data.get(
495                    DeviceTestType.oh_jsunit_test, [])
496                filter_list = []
497                for exclude in exclude_list:
498                    if request.get_module_name() not in exclude:
499                        continue
500                    filter_list.extend(exclude.get(request.get_module_name()))
501                if not isinstance(self.config.testargs, dict):
502                    return
503                if 'notClass' in self.config.testargs.keys():
504                    filter_list.extend(self.config.testargs.get('notClass', []))
505                self.config.testargs.update({"notClass": filter_list})
506
507    def _do_test_retry(self, listener, testargs):
508        tests_dict = dict()
509        case_list = list()
510        for test in testargs.get("test"):
511            test_item = test.split("#")
512            if len(test_item) != 2:
513                continue
514            case_list.append(test)
515            if test_item[0] not in tests_dict:
516                tests_dict.update({test_item[0] : []})
517            tests_dict.get(test_item[0]).append(
518                TestDescription(test_item[0], test_item[1]))
519        self.runner.add_arg("class", ",".join(case_list))
520        self.runner.expect_tests_dict = tests_dict
521        self.config.testargs.pop("test")
522        self.runner.run(listener)
523        self.runner.notify_finished()
524
525    def __result__(self):
526        return self.result if os.path.exists(self.result) else ""
527
528
529class OHJSUnitTestRunner:
530    MAX_RETRY_TIMES = 3
531
532    def __init__(self, config):
533        self.arg_list = {}
534        self.suites_name = None
535        self.config = config
536        self.rerun_attemp = 3
537        self.suite_recorder = {}
538        self.finished = False
539        self.expect_tests_dict = dict()
540        self.finished_observer = None
541        self.retry_times = 1
542
543    def dry_run(self):
544        parsers = get_plugin(Plugin.PARSER, CommonParserType.oh_jsunit_list)
545        if parsers:
546            parsers = parsers[:1]
547        parser_instances = []
548        for parser in parsers:
549            parser_instance = parser.__class__()
550            parser_instances.append(parser_instance)
551        handler = ShellHandler(parser_instances)
552        command = self._get_dry_run_command()
553        self.config.device.execute_shell_command(
554            command, timeout=self.config.timeout, receiver=handler, retry=0)
555        self.expect_tests_dict = parser_instances[0].tests_dict
556        return parser_instances[0].tests
557
558    def run(self, listener):
559        handler = self._get_shell_handler(listener)
560        command = self._get_run_command()
561        self.config.device.execute_shell_command(
562            command, timeout=self.config.timeout, receiver=handler, retry=0)
563
564    def notify_finished(self):
565        if self.finished_observer:
566            self.finished_observer.notify_task_finished()
567        self.retry_times -= 1
568
569    def _get_shell_handler(self, listener):
570        parsers = get_plugin(Plugin.PARSER, CommonParserType.oh_jsunit)
571        if parsers:
572            parsers = parsers[:1]
573        parser_instances = []
574        for parser in parsers:
575            parser_instance = parser.__class__()
576            parser_instance.suites_name = self.suites_name
577            parser_instance.listeners = listener
578            parser_instance.runner = self
579            parser_instances.append(parser_instance)
580            self.finished_observer = parser_instance
581        handler = ShellHandler(parser_instances)
582        return handler
583
584    def add_arg(self, name, value):
585        if not name or not value:
586            return
587        self.arg_list[name] = value
588
589    def remove_arg(self, name):
590        if not name:
591            return
592        if name in self.arg_list:
593            del self.arg_list[name]
594
595    def get_args_command(self):
596        args_commands = ""
597        for key, value in self.arg_list.items():
598            if "wait_time" == key:
599                args_commands = "%s -w %s " % (args_commands, value)
600            else:
601                args_commands = "%s -s %s %s " % (args_commands, key, value)
602        return args_commands
603
604    def _get_run_command(self):
605        command = ""
606        if self.config.package_name:
607            # aa test -p ${packageName} -b ${bundleName}-s unittest OpenHarmonyTestRunner
608            command = "aa test -p %s -b %s -s unittest OpenHarmonyTestRunner" \
609                      " %s" % (self.config.package_name,
610                               self.config.bundle_name,
611                               self.get_args_command())
612        elif self.config.module_name:
613            #  aa test -m ${moduleName}  -b ${bundleName} -s unittest OpenHarmonyTestRunner
614            command = "aa test -m %s -b %s -s unittest OpenHarmonyTestRunner" \
615                      " %s" % (self.config.module_name,
616                               self.config.bundle_name,
617                               self.get_args_command())
618        return command
619
620    def _get_dry_run_command(self):
621        command = ""
622        if self.config.package_name:
623            command = "aa test -p %s -b %s -s unittest OpenHarmonyTestRunner" \
624                      " %s -s dryRun true" % (self.config.package_name,
625                                              self.config.bundle_name,
626                                              self.get_args_command())
627        elif self.config.module_name:
628            command = "aa test -m %s -b %s -s unittest OpenHarmonyTestRunner" \
629                      " %s -s dryRun true" % (self.config.module_name,
630                                              self.config.bundle_name,
631                                              self.get_args_command())
632
633        return command
634