• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# coding=utf-8
3
4#
5# Copyright (c) 2020-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 os
21import platform
22import signal
23import sys
24import threading
25import copy
26from collections import namedtuple
27
28from _core.config.config_manager import UserConfigManager
29from _core.constants import SchedulerType
30from _core.constants import ConfigConst
31from _core.constants import ReportConst
32from _core.constants import ModeType
33from _core.constants import ToolCommandType
34from _core.environment.manager_env import EnvironmentManager
35from _core.exception import ParamError
36from _core.exception import ExecuteTerminate
37from _core.executor.request import Task
38from _core.executor.scheduler import Scheduler
39from _core.logger import platform_logger
40from _core.plugin import Plugin
41from _core.plugin import get_plugin
42from _core.utils import SplicingAction
43from _core.utils import get_instance_name
44from _core.utils import is_python_satisfied
45from _core.report.result_reporter import ResultReporter
46
47__all__ = ["Console"]
48
49LOG = platform_logger("Console")
50try:
51    if platform.system() != 'Windows':
52        import readline
53except (ModuleNotFoundError, ImportError):  # pylint:disable=undefined-variable
54    LOG.warning("Readline module is not exist.")
55
56MAX_VISIBLE_LENGTH = 49
57MAX_RESERVED_LENGTH = 46
58Argument = namedtuple('Argument', 'options unparsed valid_param parser')
59
60
61class Console(object):
62    """
63    Class representing an console for executing test.
64    Main xDevice console providing user with the interface to interact
65    """
66    __instance = None
67
68    def __new__(cls, *args, **kwargs):
69        """
70        Singleton instance
71        """
72        if cls.__instance is None:
73            cls.__instance = super(Console, cls).__new__(cls, *args, **kwargs)
74        return cls.__instance
75
76    def __init__(self):
77        pass
78
79    @classmethod
80    def handler_terminate_signal(cls, signalnum, frame):
81        # ctrl+c
82        del signalnum, frame
83        if not Scheduler.is_execute:
84            return
85        LOG.info("Get terminate input")
86        terminate_thread = threading.Thread(
87            target=Scheduler.terminate_cmd_exec)
88        terminate_thread.setDaemon(True)
89        terminate_thread.start()
90
91    def console(self, args):
92        """
93        Main xDevice console providing user with the interface to interact
94        """
95        if not is_python_satisfied():
96            sys.exit(0)
97
98        if args is None or len(args) < 2:
99            # init environment manager
100            EnvironmentManager()
101            # Enter xDevice console
102            self._console()
103        else:
104            # init environment manager
105            EnvironmentManager()
106            # Enter xDevice command parser
107            self.command_parser(" ".join(args[1:]))
108
109    def _console(self):
110        # Enter xDevice console
111        signal.signal(signal.SIGINT, self.handler_terminate_signal)
112
113        while True:
114            try:
115                usr_input = input(">>> ")
116                if usr_input == "":
117                    continue
118
119                self.command_parser(usr_input)
120
121            except SystemExit as _:
122                LOG.info("Program exit normally!")
123                break
124            except ExecuteTerminate as _:
125                LOG.info("Execution terminated")
126            except (IOError, EOFError, KeyboardInterrupt) as error:
127                LOG.exception("Input Error: {}".format(error),
128                              exc_info=False)
129
130    def argument_parser(self, para_list):
131        """
132        Argument parser
133        """
134        options = None
135        unparsed = []
136        valid_param = True
137        parser = None
138
139        try:
140            parser = argparse.ArgumentParser(
141                description="Specify tests to run.")
142            group = parser.add_mutually_exclusive_group()
143            parser.add_argument("action",
144                                type=str.lower,
145                                help="Specify action")
146            parser.add_argument("task",
147                                type=str,
148                                default=None,
149                                help="Specify task name")
150            group.add_argument("-l", "--testlist",
151                               action=SplicingAction,
152                               type=str,
153                               nargs='+',
154                               dest=ConfigConst.testlist,
155                               default="",
156                               help="Specify test list"
157                               )
158            group.add_argument("-tf", "--testfile",
159                               action=SplicingAction,
160                               type=str,
161                               nargs='+',
162                               dest=ConfigConst.testfile,
163                               default="",
164                               help="Specify test list file"
165                               )
166            parser.add_argument("-tc", "--testcase",
167                                action="store",
168                                type=str,
169                                dest=ConfigConst.testcase,
170                                default="",
171                                help="Specify test case"
172                                )
173            parser.add_argument("-c", "--config",
174                                action=SplicingAction,
175                                type=str,
176                                nargs='+',
177                                dest=ConfigConst.configfile,
178                                default="",
179                                help="Specify config file path"
180                                )
181            parser.add_argument("-sn", "--device_sn",
182                                action="store",
183                                type=str,
184                                dest=ConfigConst.device_sn,
185                                default="",
186                                help="Specify device serial number"
187                                )
188            parser.add_argument("-rp", "--reportpath",
189                                action=SplicingAction,
190                                type=str,
191                                nargs='+',
192                                dest=ConfigConst.report_path,
193                                default="",
194                                help="Specify test report path"
195                                )
196            parser.add_argument("-respath", "--resourcepath",
197                                action=SplicingAction,
198                                type=str,
199                                nargs='+',
200                                dest=ConfigConst.resource_path,
201                                default="",
202                                help="Specify test resource path"
203                                )
204            parser.add_argument("-tcpath", "--testcasespath",
205                                action=SplicingAction,
206                                type=str,
207                                nargs='+',
208                                dest=ConfigConst.testcases_path,
209                                default="",
210                                help="Specify testcases path"
211                                )
212            parser.add_argument("-ta", "--testargs",
213                                action=SplicingAction,
214                                type=str,
215                                nargs='+',
216                                dest=ConfigConst.testargs,
217                                default={},
218                                help="Specify test arguments"
219                                )
220            parser.add_argument("-pt", "--passthrough",
221                                action="store_true",
222                                dest=ConfigConst.pass_through,
223                                help="Pass through test arguments"
224                                )
225            parser.add_argument("-env", "--environment",
226                                action=SplicingAction,
227                                type=str,
228                                nargs='+',
229                                dest=ConfigConst.test_environment,
230                                default="",
231                                help="Specify test environment"
232                                )
233            parser.add_argument("-e", "--exectype",
234                                action="store",
235                                type=str,
236                                dest=ConfigConst.exectype,
237                                default="device",
238                                help="Specify test execute type"
239                                )
240            parser.add_argument("-t", "--testtype",
241                                nargs='*',
242                                dest=ConfigConst.testtype,
243                                default=[],
244                                help="Specify test type" +
245                                     "(UT,MST,ST,PERF,SEC,RELI,DST,ALL)"
246                                )
247            parser.add_argument("-td", "--testdriver",
248                                action="store",
249                                type=str,
250                                dest=ConfigConst.testdriver,
251                                default="",
252                                help="Specify test driver id"
253                                )
254            parser.add_argument("-tl", "--testlevel",
255                                action="store",
256                                type=str,
257                                dest="testlevel",
258                                default="",
259                                help="Specify test level"
260                                )
261            parser.add_argument("-bv", "--build_variant",
262                                action="store",
263                                type=str,
264                                dest="build_variant",
265                                default="release",
266                                help="Specify build variant(release,debug)"
267                                )
268            parser.add_argument("-cov", "--coverage",
269                                action="store",
270                                type=str,
271                                dest="coverage",
272                                default="",
273                                help="Specify coverage"
274                                )
275            parser.add_argument("--retry",
276                                action="store",
277                                type=str,
278                                dest=ConfigConst.retry,
279                                default="",
280                                help="Specify retry command"
281                                )
282            parser.add_argument("--session",
283                                action=SplicingAction,
284                                type=str,
285                                nargs='+',
286                                dest=ConfigConst.session,
287                                help="retry task by session id")
288            parser.add_argument("--dryrun",
289                                action="store_true",
290                                dest=ConfigConst.dry_run,
291                                help="show retry test case list")
292            parser.add_argument("--reboot-per-module",
293                                action="store_true",
294                                dest=ConfigConst.reboot_per_module,
295                                help="reboot devices before executing each "
296                                     "module")
297            parser.add_argument("--check-device",
298                                action="store_true",
299                                dest=ConfigConst.check_device,
300                                help="check the test device meets the "
301                                     "requirements")
302            parser.add_argument("--repeat",
303                                type=int,
304                                default=0,
305                                dest=ConfigConst.repeat,
306                                help="number of times that a task is executed"
307                                     " repeatedly")
308            parser.add_argument("-le", "--local_execution_log_path",
309                                dest="local_execution_log_path",
310                                help="- The local execution log path.")
311            parser.add_argument("-s", "--subsystem",
312                                dest="subsystems",
313                                action="store",
314                                type=str,
315                                help="- Specify the list of subsystem")
316            parser.add_argument("-p", "--part",
317                                dest="parts",
318                                action="store",
319                                type=str,
320                                help="- Specify the list of part")
321            parser.add_argument("-kim", "--kits_in_module",
322                                dest=ConfigConst.kits_in_module,
323                                action=SplicingAction,
324                                type=str,
325                                nargs='+',
326                                default="",
327                                help="- kits that are used for specify module")
328            parser.add_argument("--kp", "--kits_params",
329                                dest=ConfigConst.kits_params,
330                                action=SplicingAction,
331                                type=str,
332                                nargs='+',
333                                default="",
334                                help="- the params of kits that related to module")
335            parser.add_argument("--auto_retry",
336                                dest=ConfigConst.auto_retry,
337                                type=int,
338                                default=0,
339                                help="- the count of auto retry"),
340            parser.add_argument("-module_config",
341                                action=SplicingAction,
342                                type=str,
343                                nargs='+',
344                                dest=ConfigConst.module_config,
345                                default="",
346                                help="Specify module config json path"
347                                )
348            self._params_pre_processing(para_list)
349            (options, unparsed) = parser.parse_known_args(para_list)
350            if unparsed:
351                LOG.warning("Unparsed input: %s", " ".join(unparsed))
352            self._params_post_processing(options)
353
354        except SystemExit as _:
355            valid_param = False
356            parser.print_help()
357            LOG.warning("Parameter parsing system exit exception.")
358        return Argument(options, unparsed, valid_param, parser)
359
360    @classmethod
361    def _params_pre_processing(cls, para_list):
362        if len(para_list) <= 1 or (
363                len(para_list) > 1 and "-" in str(para_list[1])):
364            para_list.insert(1, Task.EMPTY_TASK)
365        for index, param in enumerate(para_list):
366            if param == "--retry":
367                if index + 1 == len(para_list):
368                    para_list.append("retry_previous_command")
369                elif "-" in str(para_list[index + 1]):
370                    para_list.insert(index + 1, "retry_previous_command")
371            elif param == "-->":
372                para_list[index] = "!%s" % param
373
374    def _params_post_processing(self, options):
375        # params post-processing
376        if options.task == Task.EMPTY_TASK:
377            setattr(options, ConfigConst.task, "")
378        if options.testargs:
379            if not options.pass_through:
380                test_args = self._parse_combination_param(options.testargs)
381                setattr(options, ConfigConst.testargs, test_args)
382            else:
383                setattr(options, ConfigConst.testargs, {
384                    ConfigConst.pass_through: options.testargs})
385        if not options.resource_path:
386            resource_path = UserConfigManager(
387                config_file=options.config, env=options.test_environment).\
388                get_resource_path()
389            setattr(options, ConfigConst.resource_path, resource_path)
390        if not options.testcases_path:
391            testcases_path = UserConfigManager(
392                config_file=options.config, env=options.test_environment).\
393                get_testcases_dir()
394            setattr(options, ConfigConst.testcases_path, testcases_path)
395        device_log_dict = UserConfigManager(
396            config_file=options.config, env=options.test_environment). \
397            get_device_log_status()
398        setattr(options, ConfigConst.device_log, device_log_dict)
399        if options.subsystems:
400            subsystem_list = str(options.subsystems).split(";")
401            setattr(options, ConfigConst.subsystems, subsystem_list)
402        if options.parts:
403            part_list = str(options.parts).split(";")
404            setattr(options, ConfigConst.parts, part_list)
405
406    def command_parser(self, args):
407        try:
408            Scheduler.command_queue.append(args)
409            LOG.info("Input command: {}".format(args))
410            para_list = args.split()
411            argument = self.argument_parser(para_list)
412            options = argument.options
413            if options is None or not argument.valid_param:
414                LOG.warning("Options is None.")
415                return None
416            if options.action == ToolCommandType.toolcmd_key_run and \
417                    options.retry:
418                if hasattr(options, ConfigConst.auto_retry):
419                    setattr(options, ConfigConst.auto_retry, 0)
420                options = self._get_retry_options(options, argument.parser)
421                if options.dry_run:
422                    history_report_path = getattr(options,
423                                                  "history_report_path", "")
424                    self._list_retry_case(history_report_path)
425                    return
426            else:
427                from xdevice import SuiteReporter
428                SuiteReporter.clear_failed_case_list()
429                SuiteReporter.clear_report_result()
430
431            command = options.action
432            if command == "":
433                LOG.info("Command is empty.")
434                return
435
436            self._process_command(command, options, para_list, argument.parser)
437        except (ParamError, ValueError, TypeError, SyntaxError,
438                AttributeError) as exception:
439            error_no = getattr(exception, "error_no", "00000")
440            LOG.exception("%s: %s" % (get_instance_name(exception), exception),
441                          exc_info=False, error_no=error_no)
442            if Scheduler.upload_address:
443                Scheduler.upload_unavailable_result(str(exception.args))
444                Scheduler.upload_report_end()
445        finally:
446            if isinstance(Scheduler.command_queue[-1], str):
447                Scheduler.command_queue.pop()
448
449    def _process_command(self, command, options, para_list, parser):
450        if command.startswith(ToolCommandType.toolcmd_key_help):
451            self._process_command_help(parser, para_list)
452        elif command.startswith(ToolCommandType.toolcmd_key_show):
453            self._process_command_show(para_list)
454        elif command.startswith(ToolCommandType.toolcmd_key_run):
455            self._process_command_run(command, options)
456        elif command.startswith(ToolCommandType.toolcmd_key_quit):
457            self._process_command_quit(command)
458        elif command.startswith(ToolCommandType.toolcmd_key_list):
459            self._process_command_list(command, para_list)
460        elif command.startswith(ToolCommandType.toolcmd_key_tool):
461            self._process_command_tool(command, para_list, options)
462        else:
463            LOG.error("Unsupported command action", error_no="00100",
464                      action=command)
465
466    def _get_retry_options(self, options, parser):
467        input_options = copy.deepcopy(options)
468        # get history command, history report path
469        history_command, history_report_path = self._parse_retry_option(
470            options)
471        LOG.info("History command: %s", history_command)
472        if not os.path.exists(history_report_path) and \
473                Scheduler.mode != ModeType.decc:
474            raise ParamError(
475                "history report path %s not exists" % history_report_path)
476
477        # parse history command, set history report path
478        is_dry_run = True if options.dry_run else False
479
480        history_command = self._wash_history_command(history_command)
481
482        argument = self.argument_parser(history_command.split())
483        argument.options.dry_run = is_dry_run
484        setattr(argument.options, "history_report_path", history_report_path)
485        # modify history_command -rp param and -sn param
486        for option_tuple in self._get_to_be_replaced_option(parser):
487            history_command = self._replace_history_option(
488                history_command, (input_options, argument.options),
489                option_tuple)
490
491        # add history command to Scheduler.command_queue
492        LOG.info("Retry command: %s", history_command)
493        Scheduler.command_queue[-1] = history_command
494        return argument.options
495
496    @classmethod
497    def _process_command_help(cls, parser, para_list):
498        if para_list[0] == ToolCommandType.toolcmd_key_help:
499            if len(para_list) == 2:
500                cls.display_help_command_info(para_list[1])
501            else:
502                parser.print_help()
503        else:
504            LOG.error("Wrong help command. Use 'help' to print help")
505        return
506
507    @classmethod
508    def _process_command_show(cls, para_list):
509        if para_list[0] == ToolCommandType.toolcmd_key_show:
510            pass
511        else:
512            LOG.error("Wrong show command.")
513        return
514
515    @classmethod
516    def _process_command_run(cls, command, options):
517
518        scheduler = get_plugin(plugin_type=Plugin.SCHEDULER,
519                               plugin_id=SchedulerType.scheduler)[0]
520        if scheduler is None:
521            LOG.error("Can not find the scheduler plugin.")
522        else:
523            scheduler.exec_command(command, options)
524
525        return
526
527    def _process_command_list(self, command, para_list):
528        if command != ToolCommandType.toolcmd_key_list:
529            LOG.error("Wrong list command.")
530            return
531        if len(para_list) > 1:
532            if para_list[1] == "history":
533                self._list_history()
534            elif para_list[1] == "devices" or para_list[1] == Task.EMPTY_TASK:
535                env_manager = EnvironmentManager()
536                env_manager.list_devices()
537            else:
538                self._list_task_id(para_list[1])
539            return
540        # list devices
541        env_manager = EnvironmentManager()
542        env_manager.list_devices()
543        return
544
545    @classmethod
546    def _process_command_quit(cls, command):
547        if command == ToolCommandType.toolcmd_key_quit:
548            env_manager = EnvironmentManager()
549            env_manager.env_stop()
550            sys.exit(0)
551        else:
552            LOG.error("Wrong exit command. Use 'quit' to quit program")
553        return
554
555    def _process_command_tool(cls, command, para_list, options):
556        if not command.startswith(ToolCommandType.toolcmd_key_tool):
557            LOG.error("Wrong tool command.")
558            return
559        if len(para_list) > 2:
560            if para_list[1] == ConfigConst.renew_report:
561                if options.report_path:
562                    report_list = str(options.report_path).split(";")
563                    cls._renew_report(report_list)
564
565    @staticmethod
566    def _parse_combination_param(combination_value):
567        # sample: size:xxx1;exclude-annotation:xxx
568        parse_result = {}
569        key_value_pairs = str(combination_value).split(";")
570        for key_value_pair in key_value_pairs:
571            key, value = key_value_pair.split(":", 1)
572            if not value:
573                raise ParamError("'%s' no value" % key)
574            if ConfigConst.pass_through not in key:
575                value_list = str(value).split(",")
576                exist_list = parse_result.get(key, [])
577                exist_list.extend(value_list)
578                parse_result[key] = exist_list
579            else:
580                parse_result[key] = value
581        return parse_result
582
583    @classmethod
584    def _list_history(cls):
585        print("Command history:")
586        print("{0:<16}{1:<50}{2:<50}".format(
587            "TaskId", "Command", "ReportPath"))
588        for command_info in Scheduler.command_queue[:-1]:
589            command, report_path = command_info[1], command_info[2]
590            if len(command) > MAX_VISIBLE_LENGTH:
591                command = "%s..." % command[:MAX_RESERVED_LENGTH]
592            if len(report_path) > MAX_VISIBLE_LENGTH:
593                report_path = "%s..." % report_path[:MAX_RESERVED_LENGTH]
594            print("{0:<16}{1:<50}{2:<50}".format(
595                command_info[0], command, report_path))
596
597    @classmethod
598    def _list_task_id(cls, task_id):
599        print("List task:")
600        task_id, command, report_path = task_id, "", ""
601        for command_info in Scheduler.command_queue[:-1]:
602            if command_info[0] != task_id:
603                continue
604            task_id, command, report_path = command_info
605            break
606        print("{0:<16}{1:<100}".format("TaskId:", task_id))
607        print("{0:<16}{1:<100}".format("Command:", command))
608        print("{0:<16}{1:<100}".format("ReportPath:", report_path))
609
610    @classmethod
611    def _list_retry_case(cls, history_path):
612        params = ResultReporter.get_task_info_params(history_path)
613        if not params:
614            raise ParamError("no retry case exists")
615        session_id, command, report_path, failed_list = \
616            params[ReportConst.session_id], params[ReportConst.command], \
617            params[ReportConst.report_path], \
618            [(module, failed) for module, case_list in params[ReportConst.unsuccessful_params].items()
619             for failed in case_list]
620        if Scheduler.mode == ModeType.decc:
621            from xdevice import SuiteReporter
622            SuiteReporter.failed_case_list = failed_list
623            return
624
625        # draw tables in console
626        left, middle, right = 23, 49, 49
627        two_segments = "{0:-<%s}{1:-<%s}+" % (left, middle + right)
628        two_rows = "|{0:^%s}|{1:^%s}|" % (left - 1, middle + right - 1)
629
630        three_segments = "{0:-<%s}{1:-<%s}{2:-<%s}+" % (left, middle, right)
631        three_rows = "|{0:^%s}|{1:^%s}|{2:^%s}|" % \
632                     (left - 1, middle - 1, right - 1)
633        if len(session_id) > middle + right - 1:
634            session_id = "%s..." % session_id[:middle + right - 4]
635        if len(command) > middle + right - 1:
636            command = "%s..." % command[:middle + right - 4]
637        if len(report_path) > middle + right - 1:
638            report_path = "%s..." % report_path[:middle + right - 4]
639
640        print(two_segments.format("+", '+'))
641        print(two_rows.format("SessionId", session_id))
642        print(two_rows.format("Command", command))
643        print(two_rows.format("ReportPath", report_path))
644
645        print(three_segments.format("+", '+', '+'))
646        print(three_rows.format("Module", "Testsuite", "Testcase"))
647        print(three_segments.format("+", '+', '+'))
648        for module, failed in failed_list:
649            # all module is failed
650            if "#" not in failed:
651                class_name = "-"
652                test = "-"
653            # others, get failed cases info
654            else:
655                pos = failed.rfind("#")
656                class_name = failed[:pos]
657                test = failed[pos + 1:]
658            if len(module) > left - 1:
659                module = "%s..." % module[:left - 4]
660            if len(class_name) > middle - 1:
661                class_name = "%s..." % class_name[:middle - 4]
662            if len(test) > right - 1:
663                test = "%s..." % test[:right - 4]
664            print(three_rows.format(module, class_name, test))
665        print(three_segments.format("+", '+', '+'))
666
667    @classmethod
668    def _find_history_path(cls, session):
669        from xdevice import Variables
670        if os.path.isdir(session):
671            return session
672
673        target_path = os.path.join(
674            Variables.exec_dir, Variables.report_vars.report_dir, session)
675        if not os.path.isdir(target_path):
676            raise ParamError("session '%s' is invalid!" % session)
677
678        return target_path
679
680    def _parse_retry_option(self, options):
681        if Scheduler.mode == ModeType.decc:
682            if len(Scheduler.command_queue) < 2:
683                raise ParamError("no previous command executed")
684            _, history_command, history_report_path = \
685                Scheduler.command_queue[-2]
686            return history_command, history_report_path
687
688        # get history_command, history_report_path
689        if options.retry == "retry_previous_command":
690            from xdevice import Variables
691            history_path = os.path.join(Variables.temp_dir, "latest")
692            if options.session:
693                history_path = self._find_history_path(options.session)
694
695            params = ResultReporter.get_task_info_params(history_path)
696            if not params:
697                error_msg = "no previous command executed" if not \
698                    options.session else "'%s' has no command executed" % \
699                                         options.session
700                raise ParamError(error_msg)
701            history_command, history_report_path = params[ReportConst.command], params[ReportConst.report_path]
702        else:
703            history_command, history_report_path = "", ""
704            for command_tuple in Scheduler.command_queue[:-1]:
705                if command_tuple[0] != options.retry:
706                    continue
707                history_command, history_report_path = \
708                    command_tuple[1], command_tuple[2]
709                break
710            if not history_command:
711                raise ParamError("wrong task id input: %s" % options.retry)
712        return history_command, history_report_path
713
714    @classmethod
715    def display_help_command_info(cls, command):
716        if command == ToolCommandType.toolcmd_key_run:
717            print(RUN_INFORMATION)
718        elif command == ToolCommandType.toolcmd_key_list:
719            print(LIST_INFORMATION)
720        elif command == "empty":
721            print(GUIDE_INFORMATION)
722        else:
723            print("'%s' command no help information." % command)
724
725    @classmethod
726    def _replace_history_option(cls, history_command, options_tuple,
727                                target_option_tuple):
728        input_options, history_options = options_tuple
729        option_name, short_option_str, full_option_str = target_option_tuple
730        history_value = getattr(history_options, option_name, "")
731        input_value = getattr(input_options, option_name, "")
732        if history_value:
733            if input_value:
734                history_command = history_command.replace(history_value,
735                                                          input_value)
736                setattr(history_options, option_name, input_value)
737            else:
738                history_command = str(history_command).replace(
739                    history_value, "").replace(full_option_str, "").\
740                    replace(short_option_str, "")
741        else:
742            if input_value:
743                history_command = "{}{}".format(
744                    history_command, " %s %s" % (short_option_str,
745                                                 input_value))
746                setattr(history_options, option_name, input_value)
747
748        return history_command.strip()
749
750    @classmethod
751    def _get_to_be_replaced_option(cls, parser):
752        name_list = ["report_path", "device_sn"]
753        option_str_list = list()
754        action_list = getattr(parser, "_actions", [])
755        if action_list:
756            for action in action_list:
757                if action.dest not in name_list:
758                    continue
759                option_str_list.append((action.dest, action.option_strings[0],
760                                        action.option_strings[1]))
761        else:
762            option_str_list = [("report_path", "-rp", "--reportpath"),
763                               ("device_sn", "-sn", "--device_sn")]
764        return option_str_list
765
766    @classmethod
767    def _renew_report(cls, report_list):
768        from _core.report.__main__ import main_report
769        for report in report_list:
770            run_command = Scheduler.command_queue.pop()
771            Scheduler.command_queue.append(("", run_command, report))
772            sys.argv.insert(1, report)
773            main_report()
774            sys.argv.pop(1)
775
776    @classmethod
777    def _wash_history_command(cls, history_command):
778        # clear redundant content in history command. e.g. repeat,sn
779        if "--repeat" in history_command or "-sn" in history_command\
780                or "--auto_retry" in history_command:
781            split_list = list(history_command.split())
782            if "--repeat" in split_list:
783                pos = split_list.index("--repeat")
784                split_list = split_list[:pos] + split_list[pos + 2:]
785            if "-sn" in split_list:
786                pos = split_list.index("-sn")
787                split_list = split_list[:pos] + split_list[pos + 2:]
788            if "--auto_retry" in split_list:
789                pos = split_list.index("--auto_retry")
790                split_list = split_list[:pos] + split_list[pos + 2:]
791            return " ".join(split_list)
792        else:
793            return history_command
794
795
796RUN_INFORMATION = """run:
797    This command is used to execute the selected testcases.
798    It includes a series of processes such as use case compilation, \
799execution, and result collection.
800
801usage: run [-l TESTLIST [TESTLIST ...] | -tf TESTFILE
802           [TESTFILE ...]] [-tc TESTCASE] [-c CONFIG] [-sn DEVICE_SN]
803           [-rp REPORT_PATH [REPORT_PATH ...]]
804           [-respath RESOURCE_PATH [RESOURCE_PATH ...]]
805           [-tcpath TESTCASES_PATH [TESTCASES_PATH ...]]
806           [-ta TESTARGS [TESTARGS ...]] [-pt]
807           [-env TEST_ENVIRONMENT [TEST_ENVIRONMENT ...]]
808           [-e EXECTYPE] [-t [TESTTYPE [TESTTYPE ...]]]
809           [-td TESTDRIVER] [-tl TESTLEVEL] [-bv BUILD_VARIANT]
810           [-cov COVERAGE] [--retry RETRY] [--session SESSION]
811           [--dryrun] [--reboot-per-module] [--check-device]
812           [--repeat REPEAT]
813           action task
814
815Specify tests to run.
816
817positional arguments:
818  action                Specify action
819  task                  Specify task name,such as "ssts", "acts", "hits"
820
821optional arguments:
822    -h, --help            show this help message and exit
823    -l TESTLIST [TESTLIST ...], --testlist TESTLIST [TESTLIST ...]
824                        Specify test list
825    -tf TESTFILE [TESTFILE ...], --testfile TESTFILE [TESTFILE ...]
826                        Specify test list file
827    -tc TESTCASE, --testcase TESTCASE
828                        Specify test case
829    -c CONFIG, --config CONFIG
830                        Specify config file path
831    -sn DEVICE_SN, --device_sn DEVICE_SN
832                        Specify device serial number
833    -rp REPORT_PATH [REPORT_PATH ...], --reportpath REPORT_PATH [REPORT_PATH \
834...]
835                        Specify test report path
836    -respath RESOURCE_PATH [RESOURCE_PATH ...], --resourcepath RESOURCE_PATH \
837[RESOURCE_PATH ...]
838                        Specify test resource path
839    -tcpath TESTCASES_PATH [TESTCASES_PATH ...], --testcasespath \
840TESTCASES_PATH [TESTCASES_PATH ...]
841                        Specify testcases path
842    -ta TESTARGS [TESTARGS ...], --testargs TESTARGS [TESTARGS ...]
843                        Specify test arguments
844    -pt, --passthrough    Pass through test arguments
845    -env TEST_ENVIRONMENT [TEST_ENVIRONMENT ...], --environment \
846TEST_ENVIRONMENT [TEST_ENVIRONMENT ...]
847                        Specify test environment
848    -e EXECTYPE, --exectype EXECTYPE
849                        Specify test execute type
850    -t [TESTTYPE [TESTTYPE ...]], --testtype [TESTTYPE [TESTTYPE ...]]
851                        Specify test type(UT,MST,ST,PERF,SEC,RELI,DST,ALL)
852    -td TESTDRIVER, --testdriver TESTDRIVER
853                        Specify test driver id
854    -tl TESTLEVEL, --testlevel TESTLEVEL
855                        Specify test level
856    -bv BUILD_VARIANT, --build_variant BUILD_VARIANT
857                        Specify build variant(release,debug)
858    -cov COVERAGE, --coverage COVERAGE
859                        Specify coverage
860    --retry RETRY         Specify retry command
861    --session SESSION     retry task by session id
862    --dryrun              show retry test case list
863    --reboot-per-module   reboot devices before executing each module
864    --check-device        check the test device meets the requirements
865    --repeat REPEAT       number of times that a task is executed repeatedly
866
867Examples:
868    run -l <module name>;<module name>
869    run -tf test/resource/<test file name>.txt
870
871    run –l <module name> -sn <device serial number>;<device serial number>
872    run –l <module name> -respath <path of resource>
873    run –l <module name> -ta size:large
874    run –l <module name> –ta class:<package>#<class>#<method>
875    run –l <module name> -ta size:large -pt
876    run –l <module name> –env <the content string of user_config.xml>
877    run –l <module name> –e device
878    run –l <module name> –t ALL
879    run –l <module name> –td CppTest
880    run –l <module name> -tcpath resource/testcases
881
882    run ssts
883    run ssts –tc <python script name>;<python script name>
884    run ssts -sn <device serial number>;<device serial number>
885    run ssts -respath <path of resource>
886    ... ...
887
888    run acts
889    run acts –tc <python script name>;<python script name>
890    run acts -sn <device serial number>;<device serial number>
891    run acts -respath <path of resource>
892    ... ...
893
894    run hits
895    ... ...
896
897    run --retry
898    run --retry --session <report folder name>
899    run --retry --dryrun
900"""
901
902LIST_INFORMATION = "list:" + """
903    This command is used to display device list and task record.\n
904usage:
905    list
906    list history
907    list <id>
908
909Introduction:
910    list:         display device list
911    list history: display history record of a serial of tasks
912    list <id>:    display history record about task what contains specific id
913
914Examples:
915    list
916    list history
917    list 6e****90
918"""
919
920
921GUIDE_INFORMATION = """help:
922    use help to get  information.
923
924usage:
925    run:  Display a list of supported run command.
926    list: Display a list of supported device and task record.
927
928Examples:
929    help run
930    help list
931"""
932