• 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("-pg", "--pullgcda",
249                                action="store_true",
250                                dest="pullgcda",
251                                default=False,
252                                help="Only pull gcda file."
253                                )
254            parser.add_argument("-hlg", "--hidelog",
255                                action="store_true",
256                                dest="hidelog",
257                                default=False,
258                                help="Not show task log in console."
259                                )
260            parser.add_argument("-tf", "--testfile",
261                                action="store",
262                                type=str,
263                                dest="testfile",
264                                default="",
265                                help="Specify test suites list file"
266                                )
267            parser.add_argument("-res", "--resource",
268                                action="store",
269                                type=str,
270                                dest="resource",
271                                default="",
272                                help="Specify test resource"
273                                )
274            parser.add_argument("-dp", "--dirpath",
275                                action="store",
276                                type=str,
277                                dest="dirpath",
278                                default="",
279                                help="Specify fuzz test dirpath"
280                                )
281            parser.add_argument("-fn", "--fuzzername",
282                                action="store",
283                                type=str,
284                                dest="fuzzername",
285                                default="",
286                                help="Specify fuzzer name"
287                                )
288            parser.add_argument("-ra", "--random",
289                                action="store",
290                                type=str,
291                                dest="random",
292                                default="",
293                                help="Specify random name",
294                                choices=["random"]
295                                )
296            parser.add_argument("-pd", "--partdeps",
297                                action="store",
298                                type=str,
299                                dest="partdeps",
300                                default="",
301                                help="Specify part deps",
302                                choices=["partdeps"]
303                                )
304
305            # 解析部分命令行参数,会返回一个由两个条目构成的元组,其中包含带成员的命名空间(options)和剩余参数字符串的列表(unparsed)
306            cls._params_pre_processing(para_list)
307            (options, unparsed) = parser.parse_known_args(para_list)
308            cls._params_post_processing(options)
309
310            # Set default value
311            options.target_os_name = "OHOS"
312            options.build_variant = "release"
313            options.device_sn = ""
314            options.config = ""
315            options.reportpath = ""
316            options.exectype = "device"
317            options.testdriver = ""
318
319        except SystemExit:
320            valid_param = False
321            parser.print_help()
322            LOG.warning("Parameter parsing systemexit exception.")
323
324        return options, unparsed, valid_param
325
326    def command_parser(self, args):
327        try:
328            # 将用户输入的指令按空格拆分成字符串数组
329            para_list = args.split()
330            options, _, valid_param = self.argument_parser(para_list)
331            if options is None or not valid_param:
332                LOG.warning("options is None.")
333                return
334
335            # 根据命令行的命令选择不同的方法执行
336            command = options.action
337            if command == "":
338                LOG.warning("action is empty.")
339                return
340
341            if "productform" in self.wizard_dic.keys():
342                productform = self.wizard_dic["productform"]
343                options.productform = productform
344            else:
345                productform = options.productform
346
347            if command.startswith(ToolCommandType.TOOLCMD_KEY_HELP):
348                self._process_command_help(para_list)
349            elif command.startswith(ToolCommandType.TOOLCMD_KEY_SHOW):
350                self._process_command_show(para_list, productform)
351            elif command.startswith(ToolCommandType.TOOLCMD_KEY_GEN):
352                self._process_command_gen(command, options)
353            elif command.startswith(ToolCommandType.TOOLCMD_KEY_RUN):
354                # 保存原始控制命令
355                options.current_raw_cmd = args
356                self._process_command_run(command, options)
357            elif command.startswith(ToolCommandType.TOOLCMD_KEY_QUIT):
358                self._process_command_quit(command)
359            elif command.startswith(ToolCommandType.TOOLCMD_KEY_LIST):
360                self._process_command_device(command)
361            elif command.startswith(ToolCommandType.TOOLCMD_KEY_VERSION):
362                self._process_command_version(command)
363            else:
364                print("The %s command is not supported." % command)
365        except (AttributeError, IOError, IndexError, ImportError, NameError,
366                RuntimeError, SystemError, TypeError, ValueError) as exception:
367            LOG.exception(exception, exc_info=False)
368
369    @classmethod
370    def _params_pre_processing(cls, para_list):
371        if len(para_list) <= 1 or (
372                len(para_list) > 1 and "-" in str(para_list[1])):
373            para_list.insert(1, "empty")
374        for index, param in enumerate(para_list):
375            if param == "--retry":
376                if index + 1 == len(para_list):
377                    para_list.append("retry_previous_command")
378                elif "-" in str(para_list[index + 1]):
379                    para_list.insert(index + 1, "retry_previous_command")
380            elif param == "-->":
381                para_list[index] = "!%s" % param
382
383    @staticmethod
384    def _parse_combination_param(combination_value):
385        # sample: size:xxx1;exclude-annotation:xxx
386        parse_result = {}
387        key_value_pairs = str(combination_value).split(";")
388        for key_value_pair in key_value_pairs:
389            key, value = key_value_pair.split(":", 1)
390            if not value:
391                raise ParamError("'%s' no value" % key)
392            value_list = str(value).split(",")
393            exist_list = parse_result.get(key, [])
394            exist_list.extend(value_list)
395            parse_result[key] = exist_list
396        return parse_result
397
398
399    @classmethod
400    def _process_command_version(cls, para_list):
401        display_version_info(para_list)
402        return
403
404
405    @classmethod
406    def _process_command_help(cls, para_list):
407        if para_list[0] == ToolCommandType.TOOLCMD_KEY_HELP:
408            display_help_info(para_list)
409        else:
410            LOG.error("Wrong help command.")
411        return
412
413    @classmethod
414    def _process_command_show(cls, para_list, productform="phone"):
415        if para_list[0] == ToolCommandType.TOOLCMD_KEY_SHOW:
416            display_show_info(para_list, productform)
417        else:
418            LOG.error("Wrong show command.")
419        return
420
421    @classmethod
422    def _process_command_gen(cls, command, options):
423        if command == ToolCommandType.TOOLCMD_KEY_GEN:
424            Gen().process_command_gen(options)
425        else:
426            LOG.error("Wrong gen command.")
427        return
428
429    @classmethod
430    def _process_command_run(cls, command, options):
431        if command == ToolCommandType.TOOLCMD_KEY_RUN:
432            Run().process_command_run(command, options)
433        else:
434            LOG.error("Wrong run command.")
435        return
436
437    @classmethod
438    def _process_command_device(cls, command):
439        if command == ToolCommandType.TOOLCMD_KEY_LIST:
440            env_manager = EnvironmentManager()
441            env_manager.list_devices()
442        else:
443            LOG.error("Wrong list command.")
444        return
445
446    @classmethod
447    def _process_command_quit(cls, command):
448        if command == ToolCommandType.TOOLCMD_KEY_QUIT:
449            env_manager = EnvironmentManager()
450            env_manager.env_stop()
451            sys.exit(0)
452        else:
453            LOG.error("Wrong exit command.")
454        return
455
456    @classmethod
457    def _build_version(cls, product_form):
458        is_build_version = UserConfigManager().get_user_config_flag(
459            "build", "version")
460
461        project_root_path = sys.source_code_root_path
462        if project_root_path == "":
463            return True
464
465        build_result = True
466        if is_lite_product(product_form, sys.source_code_root_path):
467            if not is_build_version:
468                return True
469            from core.build.build_lite_manager import BuildLiteManager
470            build_lite_manager = BuildLiteManager(project_root_path)
471            build_result = build_lite_manager.build_version(product_form)
472        else:
473            from core.build.build_manager import BuildManager
474            build_manager = BuildManager()
475            if is_build_version:
476                build_result = build_manager.build_version(project_root_path,
477                                                           product_form)
478        return build_result
479
480
481@dataclass
482class ConfigConst(object):
483    action = "action"
484    task = "task"
485    testlist = "testlist"
486    testfile = "testfile"
487    testcase = "testcase"
488    testdict = "testdict"
489    device_sn = "device_sn"
490    report_path = "report_path"
491    resource_path = "resource_path"
492    testcases_path = "testcases_path"
493    testargs = "testargs"
494    pass_through = "pass_through"
495    test_environment = "test_environment"
496    exectype = "exectype"
497    testtype = "testtype"
498    testdriver = "testdriver"
499    retry = "retry"
500    session = "session"
501    dry_run = "dry_run"
502    reboot_per_module = "reboot_per_module"
503    check_device = "check_device"
504    configfile = "config"
505    repeat = "repeat"
506    subsystems = "subsystems"
507    parts = "parts"
508
509    # Runtime Constant
510    history_report_path = "history_report_path"
511    product_info = "product_info"
512    task_state = "task_state"
513    recover_state = "recover_state"
514    need_kit_setup = "need_kit_setup"
515    task_kits = "task_kits"
516    module_kits = "module_kits"
517    spt = "spt"
518    version = "version"
519    component_mapper = "_component_mapper"
520    component_base_kit = "component_base_kit"
521    support_component = "support_component"
522
523
524##############################################################################
525##############################################################################
526