• 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 argparse
20import sys
21import signal
22import platform
23from dataclasses import dataclass
24from core.constants import ToolCommandType
25from core.exception import ParamError
26from xdevice import platform_logger
27from xdevice import EnvironmentManager
28from xdevice._core.utils import SplicingAction
29from core.command.run import Run
30from core.command.gen import Gen
31from core.command.display import display_help_info
32from core.command.display import display_show_info
33from core.command.display import display_version_info
34from core.command.display import show_wizard_mode
35from core.config.config_manager import UserConfigManager
36from core.utils import is_lite_product
37
38try:
39    if platform.system() != 'Windows':
40        import readline
41except ModuleNotFoundError:
42    print("ModuleNotFoundError: readline module is not exist.")
43except ImportError:
44    print("ImportError: libreadline.so is not exist.")
45
46__all__ = ["Console", "ConfigConst"]
47LOG = platform_logger("Console")
48
49##############################################################################
50##############################################################################
51
52
53class Console(object):
54    """
55    Class representing an console for executing test.
56    Main xDevice console providing user with the interface to interact
57    """
58    __instance = None
59    wizard_dic = {}
60
61    def __new__(cls, *args, **kwargs):
62        if cls.__instance is None:
63            cls.__instance = super(Console, cls).__new__(cls, *args, **kwargs)
64        return cls.__instance
65
66    def __init__(self):
67        pass
68
69    def handler_ctrl_c(self, signalnum, frame):
70        pass
71
72    def handler_ctrl_z(self, signalnum, frame):
73        pass
74
75    def console(self, args):
76        """
77        Main xDevice console providing user with the interface to interact
78        """
79        EnvironmentManager()
80        if args is None or len(args) < 2:
81            self.wizard_dic = show_wizard_mode()
82            print(self.wizard_dic)
83            if self._build_version(self.wizard_dic["productform"]):
84                self._console()
85            else:
86                LOG.error("Build version failed, exit test framework.")
87        else:
88            self.command_parser(" ".join(args[1:]))
89
90    # 命令执行总入口
91    def _console(self):
92        if platform.system() != 'Windows':
93            signal.signal(signal.SIGTSTP, self.handler_ctrl_z)  # ctrl+x linux
94        signal.signal(signal.SIGINT, self.handler_ctrl_c)  # ctrl+c
95
96        while True:
97            try:
98                # 获取用户命令输入
99                usr_input = input(">>> ")
100                if usr_input == "":
101                    continue
102                # 用户输入命令解析
103                self.command_parser(usr_input)
104            except SystemExit:
105                LOG.info("Program exit normally!")
106                return
107            except (IOError, EOFError, KeyboardInterrupt) as error:
108                LOG.exception("Input Error: %s" % error)
109
110    @staticmethod
111    def _parse_combination_param(combination_value):
112        # sample: size:xxx1;exclude-annotation:xxx
113        parse_result = {}
114        key_value_pairs = str(combination_value).split(";")
115        for key_value_pair in key_value_pairs:
116            key, value = key_value_pair.split(":", 1)
117            if not value:
118                raise ParamError("'%s' no value" % key)
119            value_list = str(value).split(",")
120            exist_list = parse_result.get(key, [])
121            exist_list.extend(value_list)
122            parse_result[key] = exist_list
123        return parse_result
124
125    @classmethod
126    def _params_post_processing(self, options):
127        # params post-processing
128        if options.testargs:
129            test_args = self._parse_combination_param(options.testargs)
130            setattr(options, ConfigConst.testargs, test_args)
131
132    # 参数解析方法
133    @classmethod
134    def argument_parser(cls, para_list):
135        """
136        argument parser
137        """
138        options = None
139        unparsed = []
140        valid_param = True
141        parser = None
142
143        try:
144            # argparse是一个Python模块:命令行选项、参数和子命令解析器
145            # 使用argparse的第一步:创建一个ArgumentParser对象,ArgumentParser对象包含将命令行解析成Python数据类型所需的全部信息
146            parser = argparse.ArgumentParser(description="Specify test para.")
147            parser.add_argument("action", type=str.lower,
148                                help="Specify action")
149            # Developer test general test parameters
150            parser.add_argument("-p", "--productform",
151                                action="store",
152                                type=str.lower,
153                                dest="productform",
154                                default="phone",
155                                help="Specified product form"
156                                )
157            parser.add_argument("-t", "--testtype",
158                                nargs='*',
159                                type=str.upper,
160                                dest="testtype",
161                                default=["UT"],
162                                help="Specify test type(UT,MST,ST,PERF,ALL)"
163                                )
164            parser.add_argument("-ss", "--subsystem",
165                                nargs='*',
166                                dest="subsystem",
167                                default=[],
168                                help="Specify test subsystem"
169                                )
170            parser.add_argument("--retry",
171                                action="store_true",
172                                dest="retry",
173                                default=False,
174                                help="Specify retry command"
175                                )
176            parser.add_argument("--dryrun",
177                                action="store_true",
178                                dest="dry_run",
179                                help="show retry test case list")
180            parser.add_argument("--repeat",
181                                type=int,
182                                dest="repeat",
183                                default=0,
184                                help="Specify number of times that a test is executed"
185                                )
186            parser.add_argument("-hl", "--historylist",
187                                action='store_true',
188                                dest="historylist",
189                                default=False,
190                                help="Show last 10 excuted commands except -hl,-rh,-retry"
191                                )
192            parser.add_argument("-rh", "--runhistory",
193                                type=int,
194                                dest="runhistory",
195                                default=0,
196                                help="Run history command by history command id"
197                                )
198            parser.add_argument("-tp", "--testpart",
199                                nargs='*',
200                                dest="testpart",
201                                default=[],
202                                help="Specify test testpart"
203                                )
204            parser.add_argument("-tm", "--testmodule",
205                                action="store",
206                                type=str,
207                                dest="testmodule",
208                                default="",
209                                help="Specified test module"
210                                )
211            parser.add_argument("-ts", "--testsuit",
212                                action="store",
213                                type=str,
214                                dest="testsuit",
215                                default="",
216                                help="Specify test suit"
217                                )
218            parser.add_argument("-ta", "--testargs",
219                                action=SplicingAction,
220                                type=str,
221                                nargs='+',
222                                dest=ConfigConst.testargs,
223                                default={},
224                                help="Specify test arguments"
225                                )
226            parser.add_argument("-tc", "--testcase",
227                                action="store",
228                                type=str,
229                                dest="testcase",
230                                default="",
231                                help="Specify test case"
232                                )
233            parser.add_argument("-tl", "--testlevel",
234                                action="store",
235                                type=str,
236                                dest="testlevel",
237                                default="",
238                                help="Specify test level"
239                                )
240
241            # Developer test extended test parameters
242            parser.add_argument("-cov", "--coverage",
243                                action="store_true",
244                                dest="coverage",
245                                default=False,
246                                help="Specify coverage"
247                                )
248            parser.add_argument("-tf", "--testfile",
249                                action="store",
250                                type=str,
251                                dest="testfile",
252                                default="",
253                                help="Specify test suites list file"
254                                )
255            parser.add_argument("-res", "--resource",
256                                action="store",
257                                type=str,
258                                dest="resource",
259                                default="",
260                                help="Specify test resource"
261                                )
262            parser.add_argument("-dp", "--dirpath",
263                                action="store",
264                                type=str,
265                                dest="dirpath",
266                                default="",
267                                help="Specify fuzz test dirpath"
268                                )
269            parser.add_argument("-fn", "--fuzzername",
270                                action="store",
271                                type=str,
272                                dest="fuzzername",
273                                default="",
274                                help="Specify fuzzer name"
275                                )
276
277            # 解析部分命令行参数,会返回一个由两个条目构成的元组,其中包含带成员的命名空间(options)和剩余参数字符串的列表(unparsed)
278            cls._params_pre_processing(para_list)
279            (options, unparsed) = parser.parse_known_args(para_list)
280            cls._params_post_processing(options)
281
282            # Set default value
283            options.target_os_name = "OHOS"
284            options.build_variant = "release"
285            options.device_sn = ""
286            options.config = ""
287            options.reportpath = ""
288            options.exectype = "device"
289            options.testdriver = ""
290
291        except SystemExit:
292            valid_param = False
293            parser.print_help()
294            LOG.warning("Parameter parsing systemexit exception.")
295
296        return options, unparsed, valid_param
297
298    def command_parser(self, args):
299        try:
300            # 将用户输入的指令按空格拆分成字符串数组
301            para_list = args.split()
302            options, _, valid_param = self.argument_parser(para_list)
303            if options is None or not valid_param:
304                LOG.warning("options is None.")
305                return
306
307            # 根据命令行的命令选择不同的方法执行
308            command = options.action
309            if command == "":
310                LOG.warning("action is empty.")
311                return
312
313            if "productform" in self.wizard_dic.keys():
314                productform = self.wizard_dic["productform"]
315                options.productform = productform
316            else:
317                productform = options.productform
318
319            if command.startswith(ToolCommandType.TOOLCMD_KEY_HELP):
320                self._process_command_help(para_list)
321            elif command.startswith(ToolCommandType.TOOLCMD_KEY_SHOW):
322                self._process_command_show(para_list, productform)
323            elif command.startswith(ToolCommandType.TOOLCMD_KEY_GEN):
324                self._process_command_gen(command, options)
325            elif command.startswith(ToolCommandType.TOOLCMD_KEY_RUN):
326                # 保存原始控制命令
327                options.current_raw_cmd = args
328                self._process_command_run(command, options)
329            elif command.startswith(ToolCommandType.TOOLCMD_KEY_QUIT):
330                self._process_command_quit(command)
331            elif command.startswith(ToolCommandType.TOOLCMD_KEY_LIST):
332                self._process_command_device(command)
333            elif command.startswith(ToolCommandType.TOOLCMD_KEY_VERSION):
334                self._process_command_version(command)
335            else:
336                print("The %s command is not supported." % command)
337        except (AttributeError, IOError, IndexError, ImportError, NameError,
338                RuntimeError, SystemError, TypeError, ValueError,
339                UnicodeError) as exception:
340            LOG.exception(exception, exc_info=False)
341
342    @classmethod
343    def _params_pre_processing(cls, para_list):
344        if len(para_list) <= 1 or (
345                len(para_list) > 1 and "-" in str(para_list[1])):
346            para_list.insert(1, "empty")
347        for index, param in enumerate(para_list):
348            if param == "--retry":
349                if index + 1 == len(para_list):
350                    para_list.append("retry_previous_command")
351                elif "-" in str(para_list[index + 1]):
352                    para_list.insert(index + 1, "retry_previous_command")
353            elif param == "-->":
354                para_list[index] = "!%s" % param
355
356    @staticmethod
357    def _parse_combination_param(combination_value):
358        # sample: size:xxx1;exclude-annotation:xxx
359        parse_result = {}
360        key_value_pairs = str(combination_value).split(";")
361        for key_value_pair in key_value_pairs:
362            key, value = key_value_pair.split(":", 1)
363            if not value:
364                raise ParamError("'%s' no value" % key)
365            value_list = str(value).split(",")
366            exist_list = parse_result.get(key, [])
367            exist_list.extend(value_list)
368            parse_result[key] = exist_list
369        return parse_result
370
371
372    @classmethod
373    def _process_command_version(cls, para_list):
374        display_version_info(para_list)
375        return
376
377
378    @classmethod
379    def _process_command_help(cls, para_list):
380        if para_list[0] == ToolCommandType.TOOLCMD_KEY_HELP:
381            display_help_info(para_list)
382        else:
383            LOG.error("Wrong help command.")
384        return
385
386    @classmethod
387    def _process_command_show(cls, para_list, productform="phone"):
388        if para_list[0] == ToolCommandType.TOOLCMD_KEY_SHOW:
389            display_show_info(para_list, productform)
390        else:
391            LOG.error("Wrong show command.")
392        return
393
394    @classmethod
395    def _process_command_gen(cls, command, options):
396        if command == ToolCommandType.TOOLCMD_KEY_GEN:
397            Gen().process_command_gen(options)
398        else:
399            LOG.error("Wrong gen command.")
400        return
401
402    @classmethod
403    def _process_command_run(cls, command, options):
404        if command == ToolCommandType.TOOLCMD_KEY_RUN:
405            Run().process_command_run(command, options)
406        else:
407            LOG.error("Wrong run command.")
408        return
409
410    @classmethod
411    def _process_command_device(cls, command):
412        if command == ToolCommandType.TOOLCMD_KEY_LIST:
413            env_manager = EnvironmentManager()
414            env_manager.list_devices()
415        else:
416            LOG.error("Wrong list command.")
417        return
418
419    @classmethod
420    def _process_command_quit(cls, command):
421        if command == ToolCommandType.TOOLCMD_KEY_QUIT:
422            env_manager = EnvironmentManager()
423            env_manager.env_stop()
424            sys.exit(0)
425        else:
426            LOG.error("Wrong exit command.")
427        return
428
429    @classmethod
430    def _build_version(cls, product_form):
431        is_build_version = UserConfigManager().get_user_config_flag(
432            "build", "version")
433
434        project_root_path = sys.source_code_root_path
435        if project_root_path == "":
436            return True
437
438        build_result = True
439        if is_lite_product(product_form, sys.source_code_root_path):
440            if not is_build_version:
441                return True
442            from core.build.build_lite_manager import BuildLiteManager
443            build_lite_manager = BuildLiteManager(project_root_path)
444            build_result = build_lite_manager.build_version(product_form)
445        else:
446            from core.build.build_manager import BuildManager
447            build_manager = BuildManager()
448            if is_build_version:
449                build_result = build_manager.build_version(project_root_path,
450                                                           product_form)
451        return build_result
452
453
454@dataclass
455class ConfigConst(object):
456    action = "action"
457    task = "task"
458    testlist = "testlist"
459    testfile = "testfile"
460    testcase = "testcase"
461    testdict = "testdict"
462    device_sn = "device_sn"
463    report_path = "report_path"
464    resource_path = "resource_path"
465    testcases_path = "testcases_path"
466    testargs = "testargs"
467    pass_through = "pass_through"
468    test_environment = "test_environment"
469    exectype = "exectype"
470    testtype = "testtype"
471    testdriver = "testdriver"
472    retry = "retry"
473    session = "session"
474    dry_run = "dry_run"
475    reboot_per_module = "reboot_per_module"
476    check_device = "check_device"
477    configfile = "config"
478    repeat = "repeat"
479    subsystems = "subsystems"
480    parts = "parts"
481
482    # Runtime Constant
483    history_report_path = "history_report_path"
484    product_info = "product_info"
485    task_state = "task_state"
486    recover_state = "recover_state"
487    need_kit_setup = "need_kit_setup"
488    task_kits = "task_kits"
489    module_kits = "module_kits"
490    spt = "spt"
491    version = "version"
492    component_mapper = "_component_mapper"
493    component_base_kit = "component_base_kit"
494    support_component = "support_component"
495
496
497##############################################################################
498##############################################################################
499