avocado/plugins/run.py
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# See LICENSE for more details.
#
# Copyright: Red Hat Inc. 2013-2014
# Author: Ruda Moura <rmoura@redhat.com>
"""
Base Test Runner Plugins.
"""
import argparse
import sys
from avocado.core import exit_codes, job, parser_common_args
from avocado.core.output import LOG_UI
from avocado.core.plugin_interfaces import CLICmd, Init
from avocado.core.settings import settings
from avocado.core.suite import TestSuite, TestSuiteError
class RunInit(Init):
name = "run"
description = "Initializes the run options"
def initialize(self):
help_msg = "Defines the order of iterating through test suite and test variants"
settings.register_option(
section="run",
key="execution_order",
choices=("tests-per-variant", "variants-per-test"),
default="variants-per-test",
help_msg=help_msg,
)
class Run(CLICmd):
"""
Implements the avocado 'run' subcommand
"""
name = "run"
description = "Runs one or more tests (native test, test alias, binary or script)"
@staticmethod
def _test_parameter(string):
param_name_value = string.split("=", 1)
if len(param_name_value) < 2:
msg = (
f'Invalid --test-parameter option: "{string}". '
f'Valid option must be a "NAME=VALUE" like expression'
)
raise argparse.ArgumentTypeError(msg)
return param_name_value
def configure(self, parser):
"""
Add the subparser for the run action.
:param parser: Main test runner parser.
"""
parser = super().configure(parser)
settings.add_argparser_to_option(
namespace="resolver.references",
nargs="*",
metavar="TEST_REFERENCE",
parser=parser,
positional_arg=True,
long_arg=None,
allow_multiple=True,
)
help_msg = (
"Parameter name and value to pass to all tests. This is "
"only applicable when not using a varianter plugin. "
"This option format must be given in the NAME=VALUE "
"format, and may be given any number of times, or per "
"parameter."
)
settings.register_option(
section="run",
key="test_parameters",
action="append",
default=[],
key_type=self._test_parameter,
metavar="NAME_VALUE",
help_msg=help_msg,
parser=parser,
long_arg="--test-parameter",
short_arg="-p",
)
settings.add_argparser_to_option(
namespace="run.suite_runner",
parser=parser,
long_arg="--suite-runner",
metavar="SUITE_RUNNER",
)
help_msg = "Instead of running the test only list them and log their params."
settings.register_option(
section="run.dry_run",
key="enabled",
default=False,
key_type=bool,
help_msg=help_msg,
parser=parser,
short_arg="-d",
long_arg="--dry-run",
)
help_msg = "Do not automatically clean up temporary directories used by dry-run"
settings.register_option(
section="run.dry_run",
key="no_cleanup",
help_msg=help_msg,
default=False,
key_type=bool,
parser=parser,
long_arg="--dry-run-no-cleanup",
)
help_msg = (
"Forces the use of a particular job ID. Used internally "
"when interacting with an avocado server. You should not "
"use this option unless you know exactly what you're "
"doing"
)
settings.register_option(
section="run",
key="unique_job_id",
default=None,
help_msg=help_msg,
parser=parser,
long_arg="--force-job-id",
metavar="UNIQUE_JOB_ID",
)
help_msg = "Forces to use of an alternate job results directory."
settings.register_option(
section="run",
key="results_dir",
default=None,
metavar="DIRECTORY",
help_msg=help_msg,
parser=parser,
long_arg="--job-results-dir",
)
help_msg = (
"Categorizes this within a directory with the same name, "
"by creating a link to the job result directory"
)
settings.register_option(
section="run",
key="job_category",
help_msg=help_msg,
parser=parser,
default=None,
metavar="CATEGORY",
long_arg="--job-category",
)
help_msg = (
"Path to the json file with list of dependencies needed for each test."
)
settings.register_option(
section="job.run",
key="dependency",
help_msg=help_msg,
parser=parser,
default=None,
metavar="FILE",
long_arg="--job-dependency",
)
settings.add_argparser_to_option(
namespace="job.run.timeout",
metavar="SECONDS",
parser=parser,
long_arg="--job-timeout",
)
help_msg = "Enable the job interruption on first failed test."
settings.register_option(
section="run",
key="failfast",
default=False,
key_type=bool,
action="store_true",
help_msg=help_msg,
parser=parser,
long_arg="--failfast",
)
help_msg = "Keep job temporary files (useful for avocado debugging)."
settings.register_option(
section="run",
key="keep_tmp",
default=False,
key_type=bool,
action="store_true",
help_msg=help_msg,
parser=parser,
long_arg="--keep-tmp",
)
help_msg = (
"Force the job execution, even if some of the test "
"references are not resolved to tests."
)
settings.register_option(
section="run",
key="ignore_missing_references",
default=False,
key_type=bool,
help_msg=help_msg,
parser=parser,
long_arg="--ignore-missing-references",
)
help_msg = "Disable sysinfo collection (like hardware details, profiles, etc)."
settings.add_argparser_to_option(
namespace="sysinfo.collect.enabled",
parser=parser,
action="store_false",
long_arg="--disable-sysinfo",
help_msg=help_msg,
)
settings.add_argparser_to_option(
"run.execution_order", parser=parser, long_arg="--execution-order"
)
parser.output = parser.add_argument_group("output and result format")
settings.add_argparser_to_option(
"job.run.store_logging_stream",
parser=parser.output,
long_arg="--store-logging-stream",
metavar="LOGGING_STREAM",
argparse_type=lambda x: set(x.split(",")),
)
help_msg = (
"Logs the possible data directories for each test. This "
"is helpful when writing new tests and not being sure "
'where to put data files. Look for "Test data '
'directories" in your test log'
)
settings.register_option(
section="run",
key="log_test_data_directories",
default=False,
key_type=bool,
help_msg=help_msg,
parser=parser,
long_arg="--log-test-data-directories",
)
parser_common_args.add_tag_filter_args(parser)
def run(self, config):
"""
Runs an Avocado Job with a TestSuite created from test references
:param config: Configuration received from command line parser and
possibly other sources.
:type config: dict
"""
unique_job_id = config.get("run.unique_job_id")
if unique_job_id is not None:
try:
int(unique_job_id, 16)
if len(unique_job_id) != 40:
raise ValueError
except ValueError:
LOG_UI.error("Unique Job ID needs to be a 40 digit hex number")
sys.exit(exit_codes.AVOCADO_FAIL)
try:
suite = TestSuite.from_config(config, name="")
if suite.size == 0:
msg = (
"Suite is empty. There is no tests to run. This usually "
"happens when you pass --ignore-missing-references and "
"there is no more references to process."
)
LOG_UI.warning(msg)
sys.exit(exit_codes.AVOCADO_FAIL)
except TestSuiteError as err:
LOG_UI.error(err)
sys.exit(exit_codes.AVOCADO_JOB_FAIL)
with job.Job(config, [suite]) as job_instance:
return job_instance.run()