Launcher script extension

It is common that a user test environment needs to do a couple of things like:

  • prepare the environment before the test execution,

  • execute additional actions after the test execution,

  • offer configurable features.

To do so, the user test environment may define its own launcher script, as illustrated by the demo/run-demo.py file.

Command line argument extension

About configurable features, configuration files come as a straight forward solution. Nevertheless, it is sometimes faster in use to provide command line options to the test launcher script also. To do so, our ‘demo/run-demo.py’ first overloads the ScenarioArgs class:

class DemoArgs(scenario.ScenarioArgs):
    def __init__(self):
        scenario.ScenarioArgs.__init__(self)

        self.setdescription("Demo test launcher.")

        self.welcome_message = "Hello you!"
        self.bye_message = "Bye!"
        self.addarg("Name", "welcome_message", str).define(
            "--welcome", metavar="NAME",
            action="store", type=str,
            help="User name.",
        )

        self.show_config_db = False
        self.addarg("Show configuration database", "show_config_db", bool).define(
            "--show-configs",
            action="store_true",
            help="Show the configuration values with their origin, then stop.",
        )

The Args._checkargs() method may be overloaded in order to check additional constraints, after the arguments have been parsed, and the Args attributes have been updated:

  • Start or finish with calling the mother class’s ScenarioArgs._checkargs() method.

  • This method is expected to return True or False whether an error has been detected or not.

    def _checkargs(self, args):
        if not super()._checkargs(args):
            return False

        if not self.welcome_message:
            scenario.logging.error(f"Wrong name {self.welcome_message!r}")
            return False
        if not self.welcome_message.startswith("Hello"):
            _name = self.welcome_message
            self.welcome_message = f"Hello {_name}!"
            self.bye_message = f"Bye {_name}!"

        return True

Then, in the main part, prior to calling the ScenarioRunner.main() method:

    # Command line arguments.
    scenario.Args.setinstance(DemoArgs())
    if not scenario.Args.getinstance().parse(sys.argv[1:]):
        sys.exit(int(scenario.Args.getinstance().error_code))

At this point, the user test environment can use the extra arguments added with the DemoArgs class, but regular arguments as well.

    # --show-configs option.
    if DemoArgs.getinstance().show_config_db:
        scenario.conf.show(logging.INFO)
        sys.exit(int(scenario.ErrorCode.SUCCESS))

    # Welcome message.
    scenario.logging.info(DemoArgs.getinstance().welcome_message)

    # File logging: use the first scenario file name to determine the output log file name.
    _outpath = DemoArgs.getinstance().scenario_paths[0].with_suffix(".log")
    scenario.conf.set("scenario.log_file", _outpath)
    scenario.logging.info(f"Test log saved in '{_outpath}'")

Using the --help option displays both:

  • the usual ScenarioArgs options,

  • and the extra options added by the DemoArgs class.

$ ./demo/run-demo.py --help
usage: run-demo.py [-h] [--config-file CONFIG_PATH] [--config-value KEY VALUE]
                   [--debug-class DEBUG_CLASS] [--doc-only]
                   [--issue-level-error ISSUE_LEVEL]
                   [--issue-level-ignored ISSUE_LEVEL]
                   [--json-report JSON_REPORT_PATH]
                   [--extra-info ATTRIBUTE_NAME] [--welcome NAME]
                   [--show-configs]
                   SCENARIO_PATH [SCENARIO_PATH ...]

Demo test launcher.

positional arguments:
  SCENARIO_PATH         Scenario script(s) to execute.

optional arguments:
  -h, --help            Show this help message and exit.
  --config-file CONFIG_PATH
                        Input configuration file path. This option may be
                        called several times.
  --config-value KEY VALUE
                        Single configuration value. This option may be called
                        several times.
  --debug-class DEBUG_CLASS
                        Activate debugging for the given class.
  --doc-only            Generate documentation without executing the test(s).
  --issue-level-error ISSUE_LEVEL
                        Define the issue level from and above which known
                        issues should be considered as errors. None by
                        default, i.e. all known issues are considered as
                        warnings.
  --issue-level-ignored ISSUE_LEVEL
                        Define the issue level from and under which known
                        issues should be ignored. None by default, i.e. no
                        known issue ignored by default.
  --json-report JSON_REPORT_PATH
                        Save the report in the given JSON output file path.
                        Single scenario only.
  --extra-info ATTRIBUTE_NAME
                        Scenario attribute to display for extra info when
                        displaying results. Applicable when executing several
                        tests. This option may be called several times to
                        display more info.
  --welcome NAME        User name.
  --show-configs        Show the configuration values with their origin, then
                        stop.

Pre & post-operations

As introduced above, extending the launcher script gives you the opportunity to add pre-operations, as soon as the command line arguments have been parsed, and post-operations after the test execution.

Our demo/run-demo.py script gives examples of pre & post-operations:

  • a welcome message displayed before the test is executed:

    # Welcome message.
    scenario.logging.info(DemoArgs.getinstance().welcome_message)
  • a bye message displayed just before the command line ends:

    # Bye message.
    scenario.logging.info(DemoArgs.getinstance().bye_message)
  • optional display of the configuration database:

    # --show-configs option.
    if DemoArgs.getinstance().show_config_db:
        scenario.conf.show(logging.INFO)
        sys.exit(int(scenario.ErrorCode.SUCCESS))
    # File logging: use the first scenario file name to determine the output log file name.
    _outpath = DemoArgs.getinstance().scenario_paths[0].with_suffix(".log")
    scenario.conf.set("scenario.log_file", _outpath)
    scenario.logging.info(f"Test log saved in '{_outpath}'")

Base launcher execution

The call to the ScenarioRunner.main() method will not analyze command line arguments twice, and use the values given by our DemoArgs instance already set.

    # Scenario execution.
    _res = scenario.runner.main()

Return code

Eventually, convert the enum value returned by ScenarioRunner.main() into a simple integer value, so that the error can be handled in the shell that launched the command line.

    # Error code.
    sys.exit(int(_res))

Campaign launcher script extension

Extending the campaign launcher script works the same, except that:

Setting the main path (optional)

Another thing that a launcher script may do is to set the main path (see Path.setmainpath()).

A main path shall be set for the current test projet. This way, all paths displayed during the tests may be nicely displayed as pretty path from this main path, whatever the current working directory (see Path.prettypath).

    # Main path.
    scenario.Path.setmainpath(scenario.Path(__file__).parents[1])

Tip

For display purpose, it is advised to set the main path after the program arguments have been analyzed.