cli.test¶
Run a registered OttoSuite test suite.
Each test suite decorated with @register_suite() appears as a subcommand of
otto test. Suite-specific options (declared in the suite’s inner Options
dataclass) are automatically registered as Typer parameters with full type
enforcement and --help documentation.
Markers
integrationRequires live Vagrant VMs. Skip with
--markers "not integration".timeout(seconds)Fail the test if it runs longer than seconds.
retry(n)Retry a failing test up to n times before reporting failure.
Listing tests
--list-suites List test suites with run syntax and exit.
Options on ``otto test`` (before the suite name)
--markers / -m EXPRESSIONpytest
-mmarker expression applied after collection.--iterations / -i NRepeat each test N times within a single setup/teardown cycle (0 = disabled).
--duration / -d SECONDSRepeat tests for N seconds within a single setup/teardown cycle (0 = disabled).
--threshold FLOATMinimum per-test pass rate percentage required in stability mode (0-100, default: 100).
--results PATHWrite test results (JUnit XML) to PATH (default: auto-written to the log directory).
When both --iterations and --duration are specified, testing stops when
either limit is reached first.
--covFetch
.gcdafiles from remote hosts after the suite finishes and place them in acov/directory in the suite’s output directory.--cov-dir PATHWrite coverage data to
PATHinstead of the default<output_dir>/cov. Implies--cov. The directory is created if missing; if it already exists and is non-empty, the command aborts unless--overwrite-cov-diris also given.--overwrite-cov-dirClear the contents of the
--cov-dirdestination before the run so stale data from a previous invocation cannot be mixed with the new results.--cov-clean / --no-cov-cleanDelete
.gcdafiles on remote hosts before the test run. Enabled by default; use--no-cov-cleanto keep stale data.--cov-report / -rAfter coverage collection, render an HTML report. Implies
--cov. Default location:<output_dir>/cov_report.--cov-report-dir PATHWrite the HTML report to
PATHinstead of the default. Implies--cov-report(and therefore--cov). Empty/overwrite rules match--cov-dir: created if missing, aborts if non-empty unless--overwrite-cov-report-diris also given.--overwrite-cov-report-dirClear the contents of
--cov-report-dirbefore the report is rendered.--project-name STRTitle shown in the HTML report header (only used with
--cov-report).--monitorEnable host performance monitoring for the duration of the run. Samples every host (or those matched by
--monitor-hosts) on a fixed interval and emits per-test start/end events automatically. At the end of the run a JSON snapshot of all metrics and events is written to<output_dir>/monitor.json.--monitor-interval SECONDSSampling interval for
--monitor(default: 5).--monitor-output PATHOverride the destination for the captured monitor data. Format inferred from the suffix:
.json(default) writes a self-contained snapshot,.dbwrites a SQLite database loadable viaotto monitor --file.--monitor-hosts REGEXRestrict
--monitorto host IDs matching this regex (re.search).
Examples:
otto test --list-suites
otto test TestMyDevice --help
otto test TestMyDevice --device-type switch --firmware 2.1
otto test --iterations 50 --threshold 95 TestMyDevice
otto test --duration 300 --threshold 90 TestMyDevice
otto test --iterations 100 --duration 60 TestMyDevice
otto test --cov TestMyDevice
otto test --cov-dir /tmp/myrun TestMyDevice
otto test --cov-dir /tmp/myrun --overwrite-cov-dir TestMyDevice
otto test --cov --no-cov-clean TestMyDevice
otto test --cov --cov-report TestMyDevice
otto test -r --cov-report-dir /tmp/myreport TestMyDevice
-
class otto.cli.test.TestRunOptions(markers: str =
'', iterations: int =0, duration: int =0, threshold: float =100.0, results: str ='', cov: bool =False, cov_dir: Path | None =None, cov_clean: bool =True, cov_report: bool =False, cov_report_dir: Path | None =None, overwrite_cov_report_dir: bool =False, project_name: str ='Coverage Report', monitor: bool =False, monitor_interval: float =5.0, monitor_output: Path | None =None, monitor_hosts: str | None =None)¶ Bases:
objectShared
otto testrun options, set by the suite_app callback and read byrun_suite. Stored in Typerctx.meta(shared across the whole context chain by click’s design) rather thanctx.obj(whose parent->subcommand propagation broke under click 8.3).
- otto.cli.test.resolve_suite(suite: str, repos: list[Repo]) str¶
Expand a sut_dir-relative suite path to an absolute path for pytest.
- otto.cli.test.run_suite(suite_class: type, suite_file: str, opts_instance: object | None, ctx: Context) None¶
Execute a registered suite via pytest.main().
Runner options (
--markers,--iterations,--duration,--threshold,--results) and coverage options (--cov/--cov-clean) are read from theTestRunOptionstheotto testcallback stored inctx.meta[RUN_OPTIONS_KEY]. The context is passed in by the suite runner (Typer injects it), so this function never reaches into a global context stack.
- otto.cli.test.main(ctx: ~typer.models.Context, list_suites: ~typing.Annotated[bool, <typer.models.OptionInfo object at 0x7fcc40a83d90>] = False, markers: ~typing.Annotated[str, <typer.models.OptionInfo object at 0x7fcc40a81540>] = '', iterations: ~typing.Annotated[int, <typer.models.OptionInfo object at 0x7fcc40a815d0>] = 0, duration: ~typing.Annotated[int, <typer.models.OptionInfo object at 0x7fcc40a80520>] = 0, threshold: ~typing.Annotated[float, <typer.models.OptionInfo object at 0x7fcc40a80190>] = 100.0, results: ~typing.Annotated[str, <typer.models.OptionInfo object at 0x7fcc40a83220>] = '', cov: ~typing.Annotated[bool, <typer.models.OptionInfo object at 0x7fcc40a81630>] = False, cov_dir: ~types.Annotated[~pathlib.Path | None, <typer.models.OptionInfo object at 0x7fcc40a83040>] | None = None, overwrite_cov_dir: ~typing.Annotated[bool, <typer.models.OptionInfo object at 0x7fcc40a821d0>] = False, cov_clean: ~typing.Annotated[bool, <typer.models.OptionInfo object at 0x7fcc40a83d30>] = True, cov_report: ~typing.Annotated[bool, <typer.models.OptionInfo object at 0x7fcc40a80160>] = False, cov_report_dir: ~types.Annotated[~pathlib.Path | None, <typer.models.OptionInfo object at 0x7fcc40a83370>] | None = None, overwrite_cov_report_dir: ~typing.Annotated[bool, <typer.models.OptionInfo object at 0x7fcc40a831c0>] = False, project_name: ~typing.Annotated[str, <typer.models.OptionInfo object at 0x7fcc40a82920>] = 'Coverage Report', monitor: ~typing.Annotated[bool, <typer.models.OptionInfo object at 0x7fcc40a82f20>] = False, monitor_interval: ~typing.Annotated[float, <typer.models.OptionInfo object at 0x7fcc40a82fb0>] = 5.0, monitor_output: ~types.Annotated[~pathlib.Path | None, <typer.models.OptionInfo object at 0x7fcc40a83460>] | None = None, monitor_hosts: ~types.Annotated[str | None, <typer.models.OptionInfo object at 0x7fcc40a83a00>] | None = None) None¶