Chapter 02.05 — Orchestration#

Test → TestSuite → TestCampaign → TestCycle: how each level slots into the next, who owns parallelization, who owns retries, who decides skipping, and where the pre-execution invariants live.

Plan#

#FileSubject
0101-test.mdThe Test[Driver] class — spawn, pre/post fragments, skip.
0202-test-executor.mdTestExecutor — execution of one attempt, order: setup → watchers.start → chain → watchers.stop → teardown.
0303-test-flow-retries.mdTestFlow — retry loop (1 + max_retries), linear backoff, setup-failure handling.
0404-test-suite.mdTestSuite — parallelization with ThreadPoolExecutor, ID filtering, guardrails.
0505-saturation.mdWorker saturation: random cloning [COPY N]. Why and how.
0606-test-campaign.mdTestCampaign — sequence of suites, campaign_has_failed.
0707-test-cycle-modes.mdTestCycle — smoke + main, fail-fast vs wait-for-all modes, has_test_cycle_failed.
0808-filter-tests-by-ids.mdfilter_tests_by_ids — --only/--exclude, mutex, ignores unknown IDs.

Diagram#

            ┌────────────────────────────────────────────────┐
            │                  TestCycle                     │
            │                                                │
            │  ┌──────────────────────────────────────────┐  │
            │  │ smoke_tests_campaigns                    │  │ ◄── first, gate
            │  │  └─ TestCampaign                         │  │     mode: fail-fast |
            │  │      └─ TestSuite                        │  │           wait-for-all
            │  │          └─ Test                         │  │
            │  └──────────────────────────────────────────┘  │
            │                                                │
            │  ┌──────────────────────────────────────────┐  │
            │  │ campaigns (main)                         │  │ ◄── skipped if a
            │  │  └─ TestCampaign                         │  │     smoke fails
            │  │      └─ TestSuite                        │  │
            │  │          └─ Test                         │  │
            │  └──────────────────────────────────────────┘  │
            └────────────────────────────────────────────────┘

Who does what?#

LevelSingle responsibilityConcurrencyOut of scope
TestMetadata + spawn(driver, logger)noneexecution
TestExecutorOne attemptnoneretries, driver acquisition
TestFlowRetry loopnoneparallelization, aggregation
TestSuiteParallelization + saturation + ID filteringN threadsinter-suite sequence
TestCampaignSequence of suitessuite-levelsmoke vs main
TestCycleSmoke + main + modecampaign-levelbootstrap / post-exec plugins

The strict separation is deliberate. TestExecutor knows nothing about retries. TestFlow knows nothing about concurrency. TestSuite knows nothing about campaign-level aggregation. One axis of responsibility per class.

Table of pre-execution invariants#

WhenInvariantSource
TestSuite constructionvalidate_test_runners_ids(tests).execute()oc_test_suite.py#_guards_on_invoke
Before TestSuite.run execution (single mode)validate_test_runners_names(tests).execute() (also post-saturation)oc_test_suite.py#_guards_with_mounted_tests
Before TestSuite.run execution (parallel mode)validate_test_runners_names(tests).execute()id.
Before TestSuite.run executionvalidate_workers_amount(max_workers).execute()id.
TestCampaign constructionvalidate_test_suites_names(suites).execute()oc_test_campaign.py
TestCycle constructionchain_validations(validate_test_cycle_name, validate_campaigns_names, validate_test_suites_names, validate_test_runners_names).execute()oc_test_cycle.py

All these invariants ride on validate(...) and raise an AggregateInvariantViolationError.