oc_chat
Loading...
Searching...
No Matches
oc_chat.testing.runner Namespace Reference

Classes

class  StandInTimeoutError
class  RunResult
class  SessionOutcome
class  _SignalState
class  _Collector

Functions

str _outcome_for (str nodeid, dict rec, set[str] timed_out)
SessionOutcome run_session (*, Sequence[str] pytest_args, Path root, int|None timeout, bool dry_run, _SignalState|None sig_state=None, bool install_signal_handlers=True, str|None violation_file=None, str violation_scope="")
dict _build_report (*, str run_id, str status, int exit_code, str|None workspace_root, str|None workspace_mode, list[str] packages, str|None profile, str|None marker, int|None seed, list[str] nodeids, int|str|None workers_requested, int|None workers_actual, str|None dist_mode, int collected, list[dict] tests, list[dict] violations, list[dict] errors, float duration_seconds, dict|None scheduling=None)
dict[str, str] _features_for (str|None dist_mode)
dict serial_scheduling (str|None reason)
RunResult _pre_execution_result (*, str run_id, float t0, list[dict] errors, StandInConfig|None cfg, WalkResult|None walk, str failed_step)
RunResult run_standin (list[str] argv, Path|None cwd=None)
RunResult _run (list[str] argv, Path base_cwd, str run_id, float t0)
list[dict] _tests_from_units (Sequence[UnitOutcome] outcomes)
dict _parallel_scheduling (*, Path manifest_path, Path root, WorkerPlan plan, Sequence[UnitOutcome] outcomes, Sequence[str] missing)

Variables

str STANDIN_ACTIVE_ENV = "OC_CHAT_STANDIN_ACTIVE"
dict _PROFILE_EXPRESSIONS
int _LONGREPR_CAP = 2000

Detailed Description

The §20 stand-in orchestrator: serial in-process execution (Plan 184).

Purpose
-------
Drive one stand-in invocation end to end: argv parse → ADR-T-12
discovery → §10 undeclared-root scan → profile/marker/seed composition
→ serial IN-PROCESS ``pytest.main`` with a collector plugin → §20.2
process-level CWD/env restore checks → §11 report assembly → §4 exit
mapping. In-process execution is the pinned decision: only it lets the
post-run ``os.environ``/CWD diff observe anything.

Responsibilities
----------------
- ``run_standin`` — the runner API; returns report + exit code + the
  API-level execution-order witness (the report's ``tests`` array is
  nodeid-sorted by contract, so order lives here).
- ``_Collector`` — the pytest plugin: collection accounting, static
  scan before execution, dry-run/violation short-circuit, per-test
  watchdog, outcome aggregation, collection-error capture.
- ``_SignalState`` — the ADR-T-08 subset for a serial in-process run.

Signal-handling subset (ADR-T-08, stated per plan T3)
-----------------------------------------------------
First ``SIGINT``/``SIGTERM`` (plus ``SIGBREAK`` on Windows): there are
no worker processes to TERM and nothing to wait ten seconds for, so the
stand-in unwinds the in-process pytest session, writes the partial
report with ``status: "orchestrator_interrupted"`` and the
``ORCHESTRATOR_INTERRUPTED`` signal entry, and exits 3. A second signal
hard-exits 130 with no report (the report-less second-signal wording
carries a known erratum upstream, B_04 P2 — out of scope here).
Handlers install only when running on the main thread.

Per-test-timeout deviation (ADR-T-07/§16.1, stated per plan T3)
---------------------------------------------------------------
The spec pins ``pytest-timeout`` as the serial stand-in's mechanism,
but its only Windows-viable method (``thread``) terminates the ENTIRE
process on expiry — for an in-process stand-in that would kill the
orchestrator and make the §20 surviving-report contract (timeout
outcome + duration + exit 1) unsatisfiable. The stand-in therefore
implements the ADR's intent with a watchdog timer that soft-interrupts
the hung test via ``PyThreadState_SetAsyncExc`` (the run continues;
the nodeid reports ``outcome: "timeout"`` with its elapsed duration).
``pytest-timeout`` remains the installed, pinned dependency and the
v1.0 in-worker backstop. Residual: a test blocked inside a C call is
interrupted only when that call returns — the same class of residual
pytest-timeout's signal method carries.

Diagnostics
-----------
Domain: OC_TESTING
Levels:
    L2 — lifecycle events
    L3 — semantic details
    L4 — deep tracing

Contracts
---------
- The restore checks consume :data:`TRACKED_ENV_PREFIXES` from its
  single export (§20.2) — object identity, no copy.
- The runner restores its own CWD and the recursion-guard variable
  before diffing, so only TEST leaks are visible.
- ``report["exit_code"]`` always equals the returned exit code.

Dependencies
------------
- pytest (in-process engine)
- oc_diagnostics (structured debug logging via _dbg)
- oc_chat.testing.cli (argv contract)
- oc_chat.testing.package_walk (discovery + root scan)
- oc_chat.testing.static_scan (static AST guard)
- oc_chat.testing.report (report constants + exit mapping)
- oc_chat.testing.env_inventory (tracked prefixes)
- oc_chat.testing.scheduler (the parallel path - worker plan, unit schedule, bounded child pool and crash reconciliation)
- _pytest.mark.expression (lazy; Expression - validates the composed marker expression before the run)
- pytest_randomly (not imported; the seed-pinning plugin loaded with -p randomly when --seed is given, disabled with -p no:randomly otherwise)

Function Documentation

◆ _build_report()

dict oc_chat.testing.runner._build_report ( * ,
str run_id,
str status,
int exit_code,
str | None workspace_root,
str | None workspace_mode,
list[str] packages,
str | None profile,
str | None marker,
int | None seed,
list[str] nodeids,
int | str | None workers_requested,
int | None workers_actual,
str | None dist_mode,
int collected,
list[dict] tests,
list[dict] violations,
list[dict] errors,
float duration_seconds,
dict | None scheduling = None )
protected
Assemble the §11/§20.3 report document (every field, always).

Definition at line 498 of file runner.py.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ _features_for()

dict[str, str] oc_chat.testing.runner._features_for ( str | None dist_mode)
protected
§20.3 ``supported_features`` for THIS run.

ADR-T-11's v0.3 amendment (readiness audit A-19) makes exactly one
value run-dependent: a serial stand-in never runs the ``auto``
algorithm and reports ``workers_auto: "deferred"``, while a
parallel-capable one reports ``"enforced"``. Plan 240 is what makes
the second case reachable; the pinned constant stays the serial
baseline, so a serial report is unchanged from plan 184's.

Definition at line 565 of file runner.py.

Here is the caller graph for this function:

◆ _outcome_for()

str oc_chat.testing.runner._outcome_for ( str nodeid,
dict rec,
set[str] timed_out )
protected

Definition at line 346 of file runner.py.

Here is the caller graph for this function:

◆ _parallel_scheduling()

dict oc_chat.testing.runner._parallel_scheduling ( * ,
Path manifest_path,
Path root,
WorkerPlan plan,
Sequence[UnitOutcome] outcomes,
Sequence[str] missing )
protected
The ``scheduling`` block of a run that distributed its groups.

Sorted by group name so the document is byte-stable regardless of
which child finished first (P3's determinism clause).

Definition at line 1162 of file runner.py.

Here is the caller graph for this function:

◆ _pre_execution_result()

RunResult oc_chat.testing.runner._pre_execution_result ( * ,
str run_id,
float t0,
list[dict] errors,
StandInConfig | None cfg,
WalkResult | None walk,
str failed_step )
protected
Build a §11 pre-execution failure report along the pinned
resolution chain argv → workspace → package → profile → seed →
workers: every leaf at-or-after *failed_step* is null.

Definition at line 600 of file runner.py.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ _run()

RunResult oc_chat.testing.runner._run ( list[str] argv,
Path base_cwd,
str run_id,
float t0 )
protected

Definition at line 681 of file runner.py.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ _tests_from_units()

list[dict] oc_chat.testing.runner._tests_from_units ( Sequence[UnitOutcome] outcomes)
protected
Reconcile every unit's records into one §11 ``tests`` array.

A crashed unit contributes EVERY nodeid it was given, reported
``failed`` with the spec-10 §8.4 crash summary: the child died
without reporting, so nothing it might have finished can be
believed, and a nodeid that vanished silently would be worse than
one reported failed.

Definition at line 1124 of file runner.py.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ run_session()

SessionOutcome oc_chat.testing.runner.run_session ( * ,
Sequence[str] pytest_args,
Path root,
int | None timeout,
bool dry_run,
_SignalState | None sig_state = None,
bool install_signal_handlers = True,
str | None violation_file = None,
str violation_scope = "" )
Run ONE guarded in-process pytest session (§20.2).

Everything the §20.2 process-level contract needs happens here and
only here: the recursion guard, the CWD move and its restoration,
the tracked-environment snapshot taken around the run, the static
gate, the per-test watchdog and the leak diff. The serial stand-in
calls it once; the scheduler calls it once per child, in that
child's own process, which is what makes the §20.2 findings
attributable to a group at all.

Parameters
----------
violation_file
    The ``file`` field of any runtime finding; defaults to *root*.
violation_scope
    Prefixed to a finding's message when non-empty, so a child's
    leak names the worker and group that produced it.

Definition at line 356 of file runner.py.

Here is the caller graph for this function:

◆ run_standin()

RunResult oc_chat.testing.runner.run_standin ( list[str] argv,
Path | None cwd = None )
Run one stand-in invocation; never raises for contract-covered
failures (internal surprises become a 9002 orchestrator error).

Definition at line 651 of file runner.py.

Here is the call graph for this function:

◆ serial_scheduling()

dict oc_chat.testing.runner.serial_scheduling ( str | None reason)
The ``scheduling`` block of a run that distributed nothing.

*reason* records WHY, so a run that could have parallelised and did
not is never silent about it: ``workers_1``, ``dry_run``,
``no_manifest``, ``single_worker``, ``single_unit``,
``classifier_unavailable`` or ``worker_cap_unusable``.

Definition at line 581 of file runner.py.

Here is the caller graph for this function:

Variable Documentation

◆ _LONGREPR_CAP

int oc_chat.testing.runner._LONGREPR_CAP = 2000
protected

Definition at line 148 of file runner.py.

◆ _PROFILE_EXPRESSIONS

dict oc_chat.testing.runner._PROFILE_EXPRESSIONS
protected
Initial value:
1= {
2 "strict": "not requires_network",
3 "smoke": "smoke and not slow and not requires_mcp and not requires_network",
4 # The §5 network-exclusion lift needs package config
5 # (allow_network=true) that no package declares yet; without it the
6 # exclusion can never lift, so the stand-in composes the same base
7 # exclusion as strict.
8 "full": "not requires_network",
9}

Definition at line 138 of file runner.py.

◆ STANDIN_ACTIVE_ENV

str oc_chat.testing.runner.STANDIN_ACTIVE_ENV = "OC_CHAT_STANDIN_ACTIVE"

Definition at line 136 of file runner.py.