suite.plugin¶
OttoPlugin — internal pytest plugin registered when otto invokes pytest.main().
Provides the pytest_runtest_makereport hook that attaches the per-phase
test report to each item (as item.rep_setup, item.rep_call,
item.rep_teardown). This makes pass/fail status available to fixtures
(including OttoSuite._test_lifecycle) during the teardown phase.
When sut_test_dirs is supplied, the pytest_ignore_collect hook
restricts collection to only those directories and their descendants,
ensuring that only tests defined in OTTO_SUT_DIRS repos are run.
Additional hooks:
pytest_runtest_protocolImplements stability testing (
--iterations/--duration). Repeats each test item within a single setup/teardown cycle, stopping when the iteration or time limit is reached.pytest_runtest_callImplements
@pytest.mark.retry(n)— retries the test body up to n times on failure, stopping on the first success.pytest_runtest_logreportIn stability mode, accumulates per-test pass/fail counts into the
StabilityCollectorattached to the plugin instance.
-
otto.suite.plugin.otto_cov_key : StashKey[bool] =
<_pytest.stash.StashKey object>¶ Stash key indicating that
--covwas passed tootto test. Fixtures can read this to decide whether to preserve.gcdafiles on remote hosts for post-run collection.
- class otto.suite.plugin.StabilityCollector¶
Bases:
objectAccumulates per-test pass/fail counts across multiple stability runs.
-
class otto.suite.plugin.OttoPlugin(sut_test_dirs=
None, stability_collector=None, cov=False, iterations=0, duration=0, monitor=False, monitor_interval=5.0, monitor_output=None, monitor_hosts=None)¶ Bases:
objectInternal pytest plugin used by
otto testto instrument test runs.Parameters¶
- sut_test_dirs :
Resolved test directories from all configured
OTTO_SUT_DIRSrepos (i.e. the union ofRepo.testsfor every repo). When provided, collection is restricted to these directories. Pass an empty list or omit to disable filtering.- stability_collector :
When running in stability mode, pass a
StabilityCollectorinstance here to accumulate pass/fail counts across repeated runs.
- pytest_configure(config)¶
Register the shared async timeout fixture and enforce auto asyncio mode.
OttoSuites always run with
asyncio_mode=autoso that async fixtures and test methods work without explicit@pytest.mark.asynciomarkers. This is distinct from otto’s own unit tests which useasyncio_mode=strict(set inpyproject.toml).- Return type:¶
None
- pytest_sessionstart(session)¶
Quiet down pytest’s terminal reporter output.
Two adjustments, both because otto streams its own Rich log output and pytest’s terse terminal chatter just collides with it. Done here rather than in
pytest_configurebecause the terminalreporter isn’t registered yet at configure time.showfspath = False: in non-verbose mode pytest writes the test file path with no trailing newline (write_fspath_result), expecting per-test progress letters to follow. otto suppresses those letters (seepytest_report_teststatus()), so the bare path would collide with the first log line. otto’s_otto_log_test_startfixture already logs each test start, making the header redundant.report_collect: the “collected N items” line has no granular suppression flag — only quiet mode (verbose < 0) hides it, which would strip other output too. Thepytest_collectionhook writes a bare, un-terminatedcollecting ...prefix thatreport_collectnormally rewrites in place intocollected N items\n; simply no-oping it would leave that prefix dangling. Instead override it to erase the line on the final call and park the cursor at column 0 for the next writer. Collection counts are tracked separately and stay intact.- Return type:¶
None
- pytest_ignore_collect(collection_path, config)¶
Ignore any path not under a configured SUT test directory.
Returns
True(ignore) for paths outside all SUT test dirs. ReturnsNone(collect normally) for paths inside a SUT test dir or for ancestor directories that need to be traversed to reach one. When no SUT test dirs are configured, all paths are collected normally.- Return type:¶
bool|None
- pytest_runtest_protocol(item, nextitem)¶
Repeat each test item when stability mode is active.
When
--iterationsor--duration(or both) are specified, each collected test is executed multiple times within a single pytest session. Class-scoped fixtures (setup_class/teardown_class) remain cached by pytest for the lifetime of the class and fire only once. Method-scoped fixtures fire on every iteration.Unlike calling
runtestprotocolin a loop (which tears down all fixtures including class-scoped ones after each call), this hook runs setup once, repeats the call phase N times, then runs teardown once. This keeps class-scoped resources (SSH connections, deployed artifacts, etc.) alive across iterations.Returns
Trueto signal that this hook handled the item, orNoneto fall through to default behaviour.- Return type:¶
bool|None
- pytest_runtest_call(item)¶
Implement
@pytest.mark.retry(n)— retry the test body up to n times.Stops on the first success. Re-raises the last exception if all attempts fail. Each failed attempt is logged at WARNING level.
- Return type:¶
None
- pytest_report_teststatus(report, config)¶
Suppress pytest’s per-test progress characters.
otto’s RichHandler streams log output to the console in real time, so pytest’s dot/
F/Ecolumn adds no information and races with log records when capture is disabled. Returning an empty short-letter keeps the category and verbose word intact (so failure summaries and the final pass/fail counts still render) while stopping the terminal reporter from writing anything per test.- Return type:¶
tuple[str,str,str] |None