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