xref: /dpdk/dts/framework/settings.py (revision b935bdc3da26ab86ec775dfad3aa63a1a61f5667)
1995fb337SOwen Hilyard# SPDX-License-Identifier: BSD-3-Clause
2995fb337SOwen Hilyard# Copyright(c) 2010-2021 Intel Corporation
378534506SJuraj Linkeš# Copyright(c) 2022-2023 PANTHEON.tech s.r.o.
4995fb337SOwen Hilyard# Copyright(c) 2022 University of New Hampshire
57f86be25SLuca Vizzarro# Copyright(c) 2024 Arm Limited
6995fb337SOwen Hilyard
76ef07151SJuraj Linkeš"""Environment variables and command line arguments parsing.
86ef07151SJuraj Linkeš
96ef07151SJuraj LinkešThis is a simple module utilizing the built-in argparse module to parse command line arguments,
106ef07151SJuraj Linkešaugment them with values from environment variables and make them available across the framework.
116ef07151SJuraj Linkeš
126ef07151SJuraj LinkešThe command line value takes precedence, followed by the environment variable value,
136ef07151SJuraj Linkešfollowed by the default value defined in this module.
146ef07151SJuraj Linkeš
156ef07151SJuraj LinkešThe command line arguments along with the supported environment variables are:
166ef07151SJuraj Linkeš
176ef07151SJuraj Linkeš.. option:: --config-file
186ef07151SJuraj Linkeš.. envvar:: DTS_CFG_FILE
196ef07151SJuraj Linkeš
206ef07151SJuraj Linkeš    The path to the YAML test run configuration file.
216ef07151SJuraj Linkeš
226ef07151SJuraj Linkeš.. option:: --output-dir, --output
236ef07151SJuraj Linkeš.. envvar:: DTS_OUTPUT_DIR
246ef07151SJuraj Linkeš
256ef07151SJuraj Linkeš    The directory where DTS logs and results are saved.
266ef07151SJuraj Linkeš
276ef07151SJuraj Linkeš.. option:: --compile-timeout
286ef07151SJuraj Linkeš.. envvar:: DTS_COMPILE_TIMEOUT
296ef07151SJuraj Linkeš
306ef07151SJuraj Linkeš    The timeout for compiling DPDK.
316ef07151SJuraj Linkeš
326ef07151SJuraj Linkeš.. option:: -t, --timeout
336ef07151SJuraj Linkeš.. envvar:: DTS_TIMEOUT
346ef07151SJuraj Linkeš
356ef07151SJuraj Linkeš    The timeout for all DTS operation except for compiling DPDK.
366ef07151SJuraj Linkeš
376ef07151SJuraj Linkeš.. option:: -v, --verbose
386ef07151SJuraj Linkeš.. envvar:: DTS_VERBOSE
396ef07151SJuraj Linkeš
406ef07151SJuraj Linkeš    Set to any value to enable logging everything to the console.
416ef07151SJuraj Linkeš
42f9957667STomáš Ďurovec.. option:: --dpdk-tree
43f9957667STomáš Ďurovec.. envvar:: DTS_DPDK_TREE
446ef07151SJuraj Linkeš
45187a9447STomáš Ďurovec    The path to the DPDK source tree directory to test. Cannot be used in conjunction with
46187a9447STomáš Ďurovec    --tarball.
476ef07151SJuraj Linkeš
48a23f2245SLuca Vizzarro.. option:: --tarball, --snapshot
496ef07151SJuraj Linkeš.. envvar:: DTS_DPDK_TARBALL
506ef07151SJuraj Linkeš
51f9957667STomáš Ďurovec    The path to the DPDK source tarball to test. DPDK must be contained in a folder with the same
52187a9447STomáš Ďurovec    name as the tarball file. Cannot be used in conjunction with --dpdk-tree.
53f9957667STomáš Ďurovec
54f9957667STomáš Ďurovec.. option:: --remote-source
55f9957667STomáš Ďurovec.. envvar:: DTS_REMOTE_SOURCE
56f9957667STomáš Ďurovec
57f9957667STomáš Ďurovec    Set this option if either the DPDK source tree or tarball to be used are located on the SUT
58f9957667STomáš Ďurovec    node. Can only be used with --dpdk-tree or --tarball.
59f9957667STomáš Ďurovec
60f9957667STomáš Ďurovec.. option:: --precompiled-build-dir
61f9957667STomáš Ďurovec.. envvar:: DTS_PRECOMPILED_BUILD_DIR
62f9957667STomáš Ďurovec
63*b935bdc3SLuca Vizzarro    Define the subdirectory under the DPDK tree root directory or tarball where the pre-compiled
64*b935bdc3SLuca Vizzarro    binaries are located.
656ef07151SJuraj Linkeš
664a4678c7SJuraj Linkeš.. option:: --test-suite
674a4678c7SJuraj Linkeš.. envvar:: DTS_TEST_SUITES
686ef07151SJuraj Linkeš
694a4678c7SJuraj Linkeš        A test suite with test cases which may be specified multiple times.
704a4678c7SJuraj Linkeš        In the environment variable, the suites are joined with a comma.
716ef07151SJuraj Linkeš
726ef07151SJuraj Linkeš.. option:: --re-run, --re_run
736ef07151SJuraj Linkeš.. envvar:: DTS_RERUN
746ef07151SJuraj Linkeš
756ef07151SJuraj Linkeš    Re-run each test case this many times in case of a failure.
766ef07151SJuraj Linkeš
77cfe40bacSLuca Vizzarro.. option:: --random-seed
78cfe40bacSLuca Vizzarro.. envvar:: DTS_RANDOM_SEED
79cfe40bacSLuca Vizzarro
80cfe40bacSLuca Vizzarro    The seed to use with the pseudo-random generator. If not specified, the configuration value is
81cfe40bacSLuca Vizzarro    used instead. If that's also not specified, a random seed is generated.
82cfe40bacSLuca Vizzarro
836ef07151SJuraj LinkešThe module provides one key module-level variable:
846ef07151SJuraj Linkeš
856ef07151SJuraj LinkešAttributes:
866ef07151SJuraj Linkeš    SETTINGS: The module level variable storing framework-wide DTS settings.
876ef07151SJuraj Linkeš
886ef07151SJuraj LinkešTypical usage example::
896ef07151SJuraj Linkeš
906ef07151SJuraj Linkeš  from framework.settings import SETTINGS
916ef07151SJuraj Linkeš  foo = SETTINGS.foo
926ef07151SJuraj Linkeš"""
936ef07151SJuraj Linkeš
94995fb337SOwen Hilyardimport argparse
95995fb337SOwen Hilyardimport os
967f86be25SLuca Vizzarroimport sys
977f86be25SLuca Vizzarrofrom argparse import Action, ArgumentDefaultsHelpFormatter, _get_action_name
98840b1e01SJuraj Linkešfrom dataclasses import dataclass, field
99680d8a24SJuraj Linkešfrom pathlib import Path
1007f86be25SLuca Vizzarrofrom typing import Callable
101995fb337SOwen Hilyard
102*b935bdc3SLuca Vizzarrofrom pydantic import ValidationError
103*b935bdc3SLuca Vizzarro
104*b935bdc3SLuca Vizzarrofrom .config import (
105*b935bdc3SLuca Vizzarro    DPDKLocation,
106*b935bdc3SLuca Vizzarro    LocalDPDKTarballLocation,
107*b935bdc3SLuca Vizzarro    LocalDPDKTreeLocation,
108*b935bdc3SLuca Vizzarro    RemoteDPDKTarballLocation,
109*b935bdc3SLuca Vizzarro    RemoteDPDKTreeLocation,
110*b935bdc3SLuca Vizzarro    TestSuiteConfig,
111*b935bdc3SLuca Vizzarro)
112680d8a24SJuraj Linkeš
113995fb337SOwen Hilyard
114840b1e01SJuraj Linkeš@dataclass(slots=True)
115840b1e01SJuraj Linkešclass Settings:
1166ef07151SJuraj Linkeš    """Default framework-wide user settings.
1176ef07151SJuraj Linkeš
1186ef07151SJuraj Linkeš    The defaults may be modified at the start of the run.
1196ef07151SJuraj Linkeš    """
1206ef07151SJuraj Linkeš
1216ef07151SJuraj Linkeš    #:
122840b1e01SJuraj Linkeš    config_file_path: Path = Path(__file__).parent.parent.joinpath("conf.yaml")
1236ef07151SJuraj Linkeš    #:
124840b1e01SJuraj Linkeš    output_dir: str = "output"
1256ef07151SJuraj Linkeš    #:
126840b1e01SJuraj Linkeš    timeout: float = 15
1276ef07151SJuraj Linkeš    #:
128840b1e01SJuraj Linkeš    verbose: bool = False
1296ef07151SJuraj Linkeš    #:
130f9957667STomáš Ďurovec    dpdk_location: DPDKLocation | None = None
1316ef07151SJuraj Linkeš    #:
132*b935bdc3SLuca Vizzarro    precompiled_build_dir: str | None = None
133*b935bdc3SLuca Vizzarro    #:
134840b1e01SJuraj Linkeš    compile_timeout: float = 1200
1356ef07151SJuraj Linkeš    #:
1364a4678c7SJuraj Linkeš    test_suites: list[TestSuiteConfig] = field(default_factory=list)
1376ef07151SJuraj Linkeš    #:
138840b1e01SJuraj Linkeš    re_run: int = 0
139cfe40bacSLuca Vizzarro    #:
140cfe40bacSLuca Vizzarro    random_seed: int | None = None
141840b1e01SJuraj Linkeš
142840b1e01SJuraj Linkeš
143840b1e01SJuraj LinkešSETTINGS: Settings = Settings()
144995fb337SOwen Hilyard
145995fb337SOwen Hilyard
1467f86be25SLuca Vizzarro#: Attribute name representing the env variable name to augment :class:`~argparse.Action` with.
1477f86be25SLuca Vizzarro_ENV_VAR_NAME_ATTR = "env_var_name"
1487f86be25SLuca Vizzarro#: Attribute name representing the value origin to augment :class:`~argparse.Action` with.
1497f86be25SLuca Vizzarro_IS_FROM_ENV_ATTR = "is_from_env"
1507f86be25SLuca Vizzarro
1517f86be25SLuca Vizzarro#: The prefix to be added to all of the environment variables.
1527f86be25SLuca Vizzarro_ENV_PREFIX = "DTS_"
1537f86be25SLuca Vizzarro
1547f86be25SLuca Vizzarro
1557f86be25SLuca Vizzarrodef _make_env_var_name(action: Action, env_var_name: str | None) -> str:
1567f86be25SLuca Vizzarro    """Make and assign an environment variable name to the given action."""
1577f86be25SLuca Vizzarro    env_var_name = f"{_ENV_PREFIX}{env_var_name or action.dest.upper()}"
1587f86be25SLuca Vizzarro    setattr(action, _ENV_VAR_NAME_ATTR, env_var_name)
1597f86be25SLuca Vizzarro    return env_var_name
1607f86be25SLuca Vizzarro
1617f86be25SLuca Vizzarro
1627f86be25SLuca Vizzarrodef _get_env_var_name(action: Action) -> str | None:
1637f86be25SLuca Vizzarro    """Get the environment variable name of the given action."""
1647f86be25SLuca Vizzarro    return getattr(action, _ENV_VAR_NAME_ATTR, None)
1657f86be25SLuca Vizzarro
1667f86be25SLuca Vizzarro
1677f86be25SLuca Vizzarrodef _set_is_from_env(action: Action) -> None:
1687f86be25SLuca Vizzarro    """Make the environment the given action's value origin."""
1697f86be25SLuca Vizzarro    setattr(action, _IS_FROM_ENV_ATTR, True)
1707f86be25SLuca Vizzarro
1717f86be25SLuca Vizzarro
1727f86be25SLuca Vizzarrodef _is_from_env(action: Action) -> bool:
1737f86be25SLuca Vizzarro    """Check if the given action's value originated from the environment."""
1747f86be25SLuca Vizzarro    return getattr(action, _IS_FROM_ENV_ATTR, False)
1757f86be25SLuca Vizzarro
1767f86be25SLuca Vizzarro
1777f86be25SLuca Vizzarrodef _is_action_in_args(action: Action) -> bool:
1787f86be25SLuca Vizzarro    """Check if the action is invoked in the command line arguments."""
1797f86be25SLuca Vizzarro    for option in action.option_strings:
1807f86be25SLuca Vizzarro        if option in sys.argv:
1817f86be25SLuca Vizzarro            return True
1827f86be25SLuca Vizzarro    return False
1837f86be25SLuca Vizzarro
1847f86be25SLuca Vizzarro
1857f86be25SLuca Vizzarrodef _add_env_var_to_action(
1867f86be25SLuca Vizzarro    action: Action,
1877f86be25SLuca Vizzarro    env_var_name: str | None = None,
1887f86be25SLuca Vizzarro) -> None:
1897f86be25SLuca Vizzarro    """Add an argument with an environment variable to the parser."""
1907f86be25SLuca Vizzarro    env_var_name = _make_env_var_name(action, env_var_name)
1917f86be25SLuca Vizzarro
1927f86be25SLuca Vizzarro    if not _is_action_in_args(action):
1937f86be25SLuca Vizzarro        env_var_value = os.environ.get(env_var_name)
1947f86be25SLuca Vizzarro        if env_var_value is not None:
1957f86be25SLuca Vizzarro            _set_is_from_env(action)
1967f86be25SLuca Vizzarro            sys.argv[1:0] = [action.format_usage(), env_var_value]
1977f86be25SLuca Vizzarro
1987f86be25SLuca Vizzarro
1997f86be25SLuca Vizzarroclass _DTSArgumentParser(argparse.ArgumentParser):
2007f86be25SLuca Vizzarro    """ArgumentParser with a custom error message.
2017f86be25SLuca Vizzarro
2027f86be25SLuca Vizzarro    This custom version of ArgumentParser changes the error message to accurately reflect the origin
2037f86be25SLuca Vizzarro    of the value of its arguments. If it was supplied through the command line nothing changes, but
2047f86be25SLuca Vizzarro    if it was supplied as an environment variable this is correctly communicated.
2057f86be25SLuca Vizzarro    """
2067f86be25SLuca Vizzarro
2077f86be25SLuca Vizzarro    def find_action(
2087f86be25SLuca Vizzarro        self, action_dest: str, filter_fn: Callable[[Action], bool] | None = None
2097f86be25SLuca Vizzarro    ) -> Action | None:
2107f86be25SLuca Vizzarro        """Find and return an action by its destination variable name.
2117f86be25SLuca Vizzarro
2127f86be25SLuca Vizzarro        Arguments:
2137f86be25SLuca Vizzarro            action_dest: the destination variable name of the action to find.
2147f86be25SLuca Vizzarro            filter_fn: if an action is found it is passed to this filter function, which must
2157f86be25SLuca Vizzarro                return a boolean value.
2167f86be25SLuca Vizzarro        """
2177f86be25SLuca Vizzarro        it = (action for action in self._actions if action.dest == action_dest)
2187f86be25SLuca Vizzarro        action = next(it, None)
2197f86be25SLuca Vizzarro
2207f86be25SLuca Vizzarro        if action and filter_fn:
2217f86be25SLuca Vizzarro            return action if filter_fn(action) else None
2227f86be25SLuca Vizzarro
2237f86be25SLuca Vizzarro        return action
2247f86be25SLuca Vizzarro
2257f86be25SLuca Vizzarro    def error(self, message):
2267f86be25SLuca Vizzarro        """Augments :meth:`~argparse.ArgumentParser.error` with environment variable awareness."""
2277f86be25SLuca Vizzarro        for action in self._actions:
2287f86be25SLuca Vizzarro            if _is_from_env(action):
2297f86be25SLuca Vizzarro                action_name = _get_action_name(action)
2307f86be25SLuca Vizzarro                env_var_name = _get_env_var_name(action)
2317f86be25SLuca Vizzarro                env_var_value = os.environ.get(env_var_name)
2327f86be25SLuca Vizzarro
2337f86be25SLuca Vizzarro                message = message.replace(
2347f86be25SLuca Vizzarro                    f"argument {action_name}",
2357f86be25SLuca Vizzarro                    f"environment variable {env_var_name} (value: {env_var_value})",
2367f86be25SLuca Vizzarro                )
2377f86be25SLuca Vizzarro
2387f86be25SLuca Vizzarro        print(f"{self.prog}: error: {message}\n", file=sys.stderr)
2397f86be25SLuca Vizzarro        self.exit(2, "For help and usage, " "run the command with the --help flag.\n")
2407f86be25SLuca Vizzarro
2417f86be25SLuca Vizzarro
2427f86be25SLuca Vizzarroclass _EnvVarHelpFormatter(ArgumentDefaultsHelpFormatter):
2437f86be25SLuca Vizzarro    """Custom formatter to add environment variables to the help page."""
2447f86be25SLuca Vizzarro
2457f86be25SLuca Vizzarro    def _get_help_string(self, action):
2467f86be25SLuca Vizzarro        """Overrides :meth:`ArgumentDefaultsHelpFormatter._get_help_string`."""
2477f86be25SLuca Vizzarro        help = super()._get_help_string(action)
2487f86be25SLuca Vizzarro
2497f86be25SLuca Vizzarro        env_var_name = _get_env_var_name(action)
2507f86be25SLuca Vizzarro        if env_var_name is not None:
2517f86be25SLuca Vizzarro            help = f"[{env_var_name}] {help}"
2527f86be25SLuca Vizzarro
2537f86be25SLuca Vizzarro            env_var_value = os.environ.get(env_var_name)
2547f86be25SLuca Vizzarro            if env_var_value is not None:
2557f86be25SLuca Vizzarro                help = f"{help} (env value: {env_var_value})"
2567f86be25SLuca Vizzarro
2577f86be25SLuca Vizzarro        return help
2587f86be25SLuca Vizzarro
2597f86be25SLuca Vizzarro
260f9957667STomáš Ďurovecdef _required_with_one_of(parser: _DTSArgumentParser, action: Action, *required_dests: str) -> None:
261f9957667STomáš Ďurovec    """Verify that `action` is listed together with at least one of `required_dests`.
262f9957667STomáš Ďurovec
263f9957667STomáš Ďurovec    Verify that when `action` is among the command-line arguments or
264f9957667STomáš Ďurovec    environment variables, at least one of `required_dests` is also among
265f9957667STomáš Ďurovec    the command-line arguments or environment variables.
266f9957667STomáš Ďurovec
267f9957667STomáš Ďurovec    Args:
268f9957667STomáš Ďurovec        parser: The custom ArgumentParser object which contains `action`.
269f9957667STomáš Ďurovec        action: The action to be verified.
270f9957667STomáš Ďurovec        *required_dests: Destination variable names of the required arguments.
271f9957667STomáš Ďurovec
272f9957667STomáš Ďurovec    Raises:
273f9957667STomáš Ďurovec        argparse.ArgumentTypeError: When none of the required_dest are defined.
274f9957667STomáš Ďurovec
275f9957667STomáš Ďurovec    Example:
276f9957667STomáš Ďurovec        We have ``--option1`` and we only want it to be a passed alongside
277f9957667STomáš Ďurovec        either ``--option2`` or ``--option3`` (meaning if ``--option1`` is
278f9957667STomáš Ďurovec        passed without either ``--option2`` or ``--option3``, that's an error).
279f9957667STomáš Ďurovec
280f9957667STomáš Ďurovec        parser = _DTSArgumentParser()
281f9957667STomáš Ďurovec        option1_arg = parser.add_argument('--option1', dest='option1', action='store_true')
282f9957667STomáš Ďurovec        option2_arg = parser.add_argument('--option2', dest='option2', action='store_true')
283f9957667STomáš Ďurovec        option2_arg = parser.add_argument('--option3', dest='option3', action='store_true')
284f9957667STomáš Ďurovec
285f9957667STomáš Ďurovec        _required_with_one_of(parser, option1_arg, 'option2', 'option3')
286f9957667STomáš Ďurovec    """
287f9957667STomáš Ďurovec    if _is_action_in_args(action):
288f9957667STomáš Ďurovec        for required_dest in required_dests:
289f9957667STomáš Ďurovec            required_action = parser.find_action(required_dest)
290f9957667STomáš Ďurovec            if required_action is None:
291f9957667STomáš Ďurovec                continue
292f9957667STomáš Ďurovec
293f9957667STomáš Ďurovec            if _is_action_in_args(required_action):
294f9957667STomáš Ďurovec                return None
295f9957667STomáš Ďurovec
296f9957667STomáš Ďurovec        raise argparse.ArgumentTypeError(
297f9957667STomáš Ďurovec            f"The '{action.dest}' is required at least with one of '{', '.join(required_dests)}'."
298f9957667STomáš Ďurovec        )
299f9957667STomáš Ďurovec
300f9957667STomáš Ďurovec
3017f86be25SLuca Vizzarrodef _get_parser() -> _DTSArgumentParser:
3024a4678c7SJuraj Linkeš    """Create the argument parser for DTS.
3034a4678c7SJuraj Linkeš
3044a4678c7SJuraj Linkeš    Command line options take precedence over environment variables, which in turn take precedence
3054a4678c7SJuraj Linkeš    over default values.
3064a4678c7SJuraj Linkeš
3074a4678c7SJuraj Linkeš    Returns:
3087f86be25SLuca Vizzarro        _DTSArgumentParser: The configured argument parser with defined options.
3094a4678c7SJuraj Linkeš    """
3107f86be25SLuca Vizzarro    parser = _DTSArgumentParser(
311517b4b26SJuraj Linkeš        description="Run DPDK test suites. All options may be specified with the environment "
312517b4b26SJuraj Linkeš        "variables provided in brackets. Command line arguments have higher priority.",
3137f86be25SLuca Vizzarro        formatter_class=_EnvVarHelpFormatter,
3147f86be25SLuca Vizzarro        allow_abbrev=False,
31578534506SJuraj Linkeš    )
316995fb337SOwen Hilyard
3177f86be25SLuca Vizzarro    action = parser.add_argument(
318995fb337SOwen Hilyard        "--config-file",
3197f86be25SLuca Vizzarro        default=SETTINGS.config_file_path,
320840b1e01SJuraj Linkeš        type=Path,
321ecaff610STomáš Ďurovec        help="The configuration file that describes the test cases, SUTs and DPDK build configs.",
3227f86be25SLuca Vizzarro        metavar="FILE_PATH",
3237f86be25SLuca Vizzarro        dest="config_file_path",
324995fb337SOwen Hilyard    )
3257f86be25SLuca Vizzarro    _add_env_var_to_action(action, "CFG_FILE")
326995fb337SOwen Hilyard
3277f86be25SLuca Vizzarro    action = parser.add_argument(
328179d7059SJuraj Linkeš        "--output-dir",
329179d7059SJuraj Linkeš        "--output",
3307f86be25SLuca Vizzarro        default=SETTINGS.output_dir,
3317f86be25SLuca Vizzarro        help="Output directory where DTS logs and results are saved.",
3327f86be25SLuca Vizzarro        metavar="DIR_PATH",
333179d7059SJuraj Linkeš    )
3347f86be25SLuca Vizzarro    _add_env_var_to_action(action)
335179d7059SJuraj Linkeš
3367f86be25SLuca Vizzarro    action = parser.add_argument(
33734dfd265SJuraj Linkeš        "-t",
33834dfd265SJuraj Linkeš        "--timeout",
3397f86be25SLuca Vizzarro        default=SETTINGS.timeout,
340680d8a24SJuraj Linkeš        type=float,
3417f86be25SLuca Vizzarro        help="The default timeout for all DTS operations except for compiling DPDK.",
3427f86be25SLuca Vizzarro        metavar="SECONDS",
34334dfd265SJuraj Linkeš    )
3447f86be25SLuca Vizzarro    _add_env_var_to_action(action)
34534dfd265SJuraj Linkeš
3467f86be25SLuca Vizzarro    action = parser.add_argument(
347179d7059SJuraj Linkeš        "-v",
348179d7059SJuraj Linkeš        "--verbose",
3494a4678c7SJuraj Linkeš        action="store_true",
3507f86be25SLuca Vizzarro        default=SETTINGS.verbose,
3517f86be25SLuca Vizzarro        help="Specify to enable verbose output, logging all messages to the console.",
352179d7059SJuraj Linkeš    )
3537f86be25SLuca Vizzarro    _add_env_var_to_action(action)
354179d7059SJuraj Linkeš
355f9957667STomáš Ďurovec    dpdk_build = parser.add_argument_group(
356f9957667STomáš Ďurovec        "DPDK Build Options",
357f9957667STomáš Ďurovec        description="Arguments in this group (and subgroup) will be applied to a "
358f9957667STomáš Ďurovec        "DPDKLocation when the DPDK tree, tarball or revision will be provided, "
359f9957667STomáš Ďurovec        "other arguments like remote source and build dir are optional. A DPDKLocation "
360f9957667STomáš Ďurovec        "from settings are used instead of from config if construct successful.",
361680d8a24SJuraj Linkeš    )
362680d8a24SJuraj Linkeš
363f9957667STomáš Ďurovec    dpdk_source = dpdk_build.add_mutually_exclusive_group()
364f9957667STomáš Ďurovec    action = dpdk_source.add_argument(
365f9957667STomáš Ďurovec        "--dpdk-tree",
366f9957667STomáš Ďurovec        help="The path to the DPDK source tree directory to test. Cannot be used in conjunction "
367187a9447STomáš Ďurovec        "with --tarball.",
368f9957667STomáš Ďurovec        metavar="DIR_PATH",
369f9957667STomáš Ďurovec        dest="dpdk_tree_path",
370f9957667STomáš Ďurovec    )
371f9957667STomáš Ďurovec    _add_env_var_to_action(action, "DPDK_TREE")
372a23f2245SLuca Vizzarro
373a23f2245SLuca Vizzarro    action = dpdk_source.add_argument(
374680d8a24SJuraj Linkeš        "--tarball",
375680d8a24SJuraj Linkeš        "--snapshot",
376f9957667STomáš Ďurovec        help="The path to the DPDK source tarball to test. DPDK must be contained in a folder with "
377187a9447STomáš Ďurovec        "the same name as the tarball file. Cannot be used in conjunction with --dpdk-tree.",
3787f86be25SLuca Vizzarro        metavar="FILE_PATH",
3797f86be25SLuca Vizzarro        dest="dpdk_tarball_path",
380680d8a24SJuraj Linkeš    )
3817f86be25SLuca Vizzarro    _add_env_var_to_action(action, "DPDK_TARBALL")
382680d8a24SJuraj Linkeš
383f9957667STomáš Ďurovec    action = dpdk_build.add_argument(
384f9957667STomáš Ďurovec        "--remote-source",
385f9957667STomáš Ďurovec        action="store_true",
386f9957667STomáš Ďurovec        default=False,
387f9957667STomáš Ďurovec        help="Set this option if either the DPDK source tree or tarball to be used are located on "
388f9957667STomáš Ďurovec        "the SUT node. Can only be used with --dpdk-tree or --tarball.",
389f9957667STomáš Ďurovec    )
390f9957667STomáš Ďurovec    _add_env_var_to_action(action)
391187a9447STomáš Ďurovec    _required_with_one_of(parser, action, "dpdk_tarball_path", "dpdk_tree_path")
392f9957667STomáš Ďurovec
393f9957667STomáš Ďurovec    action = dpdk_build.add_argument(
394f9957667STomáš Ďurovec        "--precompiled-build-dir",
395*b935bdc3SLuca Vizzarro        help="Define the subdirectory under the DPDK tree root directory or tarball where the "
396*b935bdc3SLuca Vizzarro        "pre-compiled binaries are located.",
397f9957667STomáš Ďurovec        metavar="DIR_NAME",
398f9957667STomáš Ďurovec    )
399f9957667STomáš Ďurovec    _add_env_var_to_action(action)
400f9957667STomáš Ďurovec
4017f86be25SLuca Vizzarro    action = parser.add_argument(
402680d8a24SJuraj Linkeš        "--compile-timeout",
4037f86be25SLuca Vizzarro        default=SETTINGS.compile_timeout,
404680d8a24SJuraj Linkeš        type=float,
4057f86be25SLuca Vizzarro        help="The timeout for compiling DPDK.",
4067f86be25SLuca Vizzarro        metavar="SECONDS",
407680d8a24SJuraj Linkeš    )
4087f86be25SLuca Vizzarro    _add_env_var_to_action(action)
409680d8a24SJuraj Linkeš
4107f86be25SLuca Vizzarro    action = parser.add_argument(
4114a4678c7SJuraj Linkeš        "--test-suite",
4124a4678c7SJuraj Linkeš        action="append",
4134a4678c7SJuraj Linkeš        nargs="+",
4144a4678c7SJuraj Linkeš        metavar=("TEST_SUITE", "TEST_CASES"),
4157f86be25SLuca Vizzarro        default=SETTINGS.test_suites,
4167f86be25SLuca Vizzarro        help="A list containing a test suite with test cases. "
4174a4678c7SJuraj Linkeš        "The first parameter is the test suite name, and the rest are test case names, "
4184a4678c7SJuraj Linkeš        "which are optional. May be specified multiple times. To specify multiple test suites in "
4194a4678c7SJuraj Linkeš        "the environment variable, join the lists with a comma. "
4204a4678c7SJuraj Linkeš        "Examples: "
4214a4678c7SJuraj Linkeš        "--test-suite suite case case --test-suite suite case ... | "
4224a4678c7SJuraj Linkeš        "DTS_TEST_SUITES='suite case case, suite case, ...' | "
4234a4678c7SJuraj Linkeš        "--test-suite suite --test-suite suite case ... | "
4244a4678c7SJuraj Linkeš        "DTS_TEST_SUITES='suite, suite case, ...'",
4257f86be25SLuca Vizzarro        dest="test_suites",
4266fc05ca7SJuraj Linkeš    )
4277f86be25SLuca Vizzarro    _add_env_var_to_action(action)
4286fc05ca7SJuraj Linkeš
4297f86be25SLuca Vizzarro    action = parser.add_argument(
4306fc05ca7SJuraj Linkeš        "--re-run",
4316fc05ca7SJuraj Linkeš        "--re_run",
4327f86be25SLuca Vizzarro        default=SETTINGS.re_run,
4336fc05ca7SJuraj Linkeš        type=int,
4347f86be25SLuca Vizzarro        help="Re-run each test case the specified number of times if a test failure occurs.",
4357f86be25SLuca Vizzarro        metavar="N_TIMES",
4366fc05ca7SJuraj Linkeš    )
4377f86be25SLuca Vizzarro    _add_env_var_to_action(action, "RERUN")
4386fc05ca7SJuraj Linkeš
439cfe40bacSLuca Vizzarro    action = parser.add_argument(
440cfe40bacSLuca Vizzarro        "--random-seed",
441cfe40bacSLuca Vizzarro        type=int,
442cfe40bacSLuca Vizzarro        help="The seed to use with the pseudo-random generator. If not specified, the configuration"
443cfe40bacSLuca Vizzarro        " value is used instead. If that's also not specified, a random seed is generated.",
444cfe40bacSLuca Vizzarro        metavar="NUMBER",
445cfe40bacSLuca Vizzarro    )
446cfe40bacSLuca Vizzarro    _add_env_var_to_action(action)
447cfe40bacSLuca Vizzarro
448995fb337SOwen Hilyard    return parser
449995fb337SOwen Hilyard
450995fb337SOwen Hilyard
451f9957667STomáš Ďurovecdef _process_dpdk_location(
452*b935bdc3SLuca Vizzarro    parser: _DTSArgumentParser,
453f9957667STomáš Ďurovec    dpdk_tree: str | None,
454f9957667STomáš Ďurovec    tarball: str | None,
455f9957667STomáš Ďurovec    remote: bool,
456*b935bdc3SLuca Vizzarro) -> DPDKLocation | None:
457f9957667STomáš Ďurovec    """Process and validate DPDK build arguments.
458f9957667STomáš Ďurovec
459f9957667STomáš Ďurovec    Ensures that either `dpdk_tree` or `tarball` is provided. Validate existence and format of
460f9957667STomáš Ďurovec    `dpdk_tree` or `tarball` on local filesystem, if `remote` is False. Constructs and returns
461*b935bdc3SLuca Vizzarro    any valid :class:`DPDKLocation` with the provided parameters if validation is successful.
462f9957667STomáš Ďurovec
463f9957667STomáš Ďurovec    Args:
464*b935bdc3SLuca Vizzarro        dpdk_tree: The path to the DPDK source tree directory.
465*b935bdc3SLuca Vizzarro        tarball: The path to the DPDK tarball.
466f9957667STomáš Ďurovec        remote: If :data:`True`, `dpdk_tree` or `tarball` is located on the SUT node, instead of the
467f9957667STomáš Ďurovec            execution host.
468f9957667STomáš Ďurovec
469f9957667STomáš Ďurovec    Returns:
470f9957667STomáš Ďurovec        A DPDK location if construction is successful, otherwise None.
471f9957667STomáš Ďurovec    """
472f9957667STomáš Ďurovec    if dpdk_tree:
473*b935bdc3SLuca Vizzarro        action = parser.find_action("dpdk_tree", _is_from_env)
474*b935bdc3SLuca Vizzarro
475*b935bdc3SLuca Vizzarro        try:
476*b935bdc3SLuca Vizzarro            if remote:
477*b935bdc3SLuca Vizzarro                return RemoteDPDKTreeLocation.model_validate({"dpdk_tree": dpdk_tree})
478*b935bdc3SLuca Vizzarro            else:
479*b935bdc3SLuca Vizzarro                return LocalDPDKTreeLocation.model_validate({"dpdk_tree": dpdk_tree})
480*b935bdc3SLuca Vizzarro        except ValidationError as e:
481*b935bdc3SLuca Vizzarro            print(
482*b935bdc3SLuca Vizzarro                "An error has occurred while validating the DPDK tree supplied in the "
483*b935bdc3SLuca Vizzarro                f"{'environment variable' if action else 'arguments'}:",
484*b935bdc3SLuca Vizzarro                file=sys.stderr,
485f9957667STomáš Ďurovec            )
486*b935bdc3SLuca Vizzarro            print(e, file=sys.stderr)
487*b935bdc3SLuca Vizzarro            sys.exit(1)
488f9957667STomáš Ďurovec
489f9957667STomáš Ďurovec    if tarball:
490*b935bdc3SLuca Vizzarro        action = parser.find_action("tarball", _is_from_env)
491f9957667STomáš Ďurovec
492*b935bdc3SLuca Vizzarro        try:
493*b935bdc3SLuca Vizzarro            if remote:
494*b935bdc3SLuca Vizzarro                return RemoteDPDKTarballLocation.model_validate({"tarball": tarball})
495*b935bdc3SLuca Vizzarro            else:
496*b935bdc3SLuca Vizzarro                return LocalDPDKTarballLocation.model_validate({"tarball": tarball})
497*b935bdc3SLuca Vizzarro        except ValidationError as e:
498*b935bdc3SLuca Vizzarro            print(
499*b935bdc3SLuca Vizzarro                "An error has occurred while validating the DPDK tarball supplied in the "
500*b935bdc3SLuca Vizzarro                f"{'environment variable' if action else 'arguments'}:",
501*b935bdc3SLuca Vizzarro                file=sys.stderr,
502f9957667STomáš Ďurovec            )
503*b935bdc3SLuca Vizzarro            print(e, file=sys.stderr)
504*b935bdc3SLuca Vizzarro            sys.exit(1)
505f9957667STomáš Ďurovec
506*b935bdc3SLuca Vizzarro    return None
507f9957667STomáš Ďurovec
508f9957667STomáš Ďurovec
5097f86be25SLuca Vizzarrodef _process_test_suites(
5107f86be25SLuca Vizzarro    parser: _DTSArgumentParser, args: list[list[str]]
5117f86be25SLuca Vizzarro) -> list[TestSuiteConfig]:
5124a4678c7SJuraj Linkeš    """Process the given argument to a list of :class:`TestSuiteConfig` to execute.
5134a4678c7SJuraj Linkeš
5144a4678c7SJuraj Linkeš    Args:
5154a4678c7SJuraj Linkeš        args: The arguments to process. The args is a string from an environment variable
5164a4678c7SJuraj Linkeš              or a list of from the user input containing tests suites with tests cases,
5174a4678c7SJuraj Linkeš              each of which is a list of [test_suite, test_case, test_case, ...].
5184a4678c7SJuraj Linkeš
5194a4678c7SJuraj Linkeš    Returns:
5204a4678c7SJuraj Linkeš        A list of test suite configurations to execute.
5214a4678c7SJuraj Linkeš    """
522*b935bdc3SLuca Vizzarro    action = parser.find_action("test_suites", _is_from_env)
523*b935bdc3SLuca Vizzarro    if action:
5247f86be25SLuca Vizzarro        # Environment variable in the form of "SUITE1 CASE1 CASE2, SUITE2 CASE1, SUITE3, ..."
5257f86be25SLuca Vizzarro        args = [suite_with_cases.split() for suite_with_cases in args[0][0].split(",")]
5264a4678c7SJuraj Linkeš
527*b935bdc3SLuca Vizzarro    try:
528*b935bdc3SLuca Vizzarro        return [
529*b935bdc3SLuca Vizzarro            TestSuiteConfig(test_suite=test_suite, test_cases=test_cases)
530*b935bdc3SLuca Vizzarro            for [test_suite, *test_cases] in args
531*b935bdc3SLuca Vizzarro        ]
532*b935bdc3SLuca Vizzarro    except ValidationError as e:
533*b935bdc3SLuca Vizzarro        print(
534*b935bdc3SLuca Vizzarro            "An error has occurred while validating the test suites supplied in the "
535*b935bdc3SLuca Vizzarro            f"{'environment variable' if action else 'arguments'}:",
536*b935bdc3SLuca Vizzarro            file=sys.stderr,
537*b935bdc3SLuca Vizzarro        )
538*b935bdc3SLuca Vizzarro        print(e, file=sys.stderr)
539*b935bdc3SLuca Vizzarro        sys.exit(1)
5404a4678c7SJuraj Linkeš
5414a4678c7SJuraj Linkeš
542840b1e01SJuraj Linkešdef get_settings() -> Settings:
5436ef07151SJuraj Linkeš    """Create new settings with inputs from the user.
5446ef07151SJuraj Linkeš
5456ef07151SJuraj Linkeš    The inputs are taken from the command line and from environment variables.
5464a4678c7SJuraj Linkeš
5474a4678c7SJuraj Linkeš    Returns:
5484a4678c7SJuraj Linkeš        The new settings object.
5496ef07151SJuraj Linkeš    """
5507f86be25SLuca Vizzarro    parser = _get_parser()
5517f86be25SLuca Vizzarro
5527f86be25SLuca Vizzarro    args = parser.parse_args()
5537f86be25SLuca Vizzarro
554f9957667STomáš Ďurovec    args.dpdk_location = _process_dpdk_location(
555*b935bdc3SLuca Vizzarro        parser, args.dpdk_tree_path, args.dpdk_tarball_path, args.remote_source
556f9957667STomáš Ďurovec    )
5577f86be25SLuca Vizzarro    args.test_suites = _process_test_suites(parser, args.test_suites)
5587f86be25SLuca Vizzarro
5597f86be25SLuca Vizzarro    kwargs = {k: v for k, v in vars(args).items() if hasattr(SETTINGS, k)}
5607f86be25SLuca Vizzarro    return Settings(**kwargs)
561