The parallel item runs branches concurrently with sync:all or sync:any policy and optional per-branch wait_for synchronization. Each branch runs in its own daemon thread and produces a clean per-item entry in the SQLite report; the live output is prefixed [<branch_name>] so concurrent branches stay readable. Supporting changes: - StdoutProxy (lib/stdout_redirect.py): thread-aware sys.stdout/stderr with per-thread capture buffers and per-branch live-output prefix. Adds writeln() for Python 3.14 unittest compatibility. - TestItemContainer: shared base extracted from Group/Cycle for the sequential children execution pattern. - TestItemSleep: interruptible loop polling _is_stopped so sync:any can cancel slow branches quickly. - TestReport: thread-safe SQLite (check_same_thread=False + lock). Also drops the unused -m/--terminal mode and its module. Validation: 11 scenarios in test/validation/items/parallel covering sync:all/any, no_fail, wait_for + timeout, conditions, multi-branch, nested parallel, parallel inside loop, real branch failure. Documentation: new parallel_test_item.rst added to the manual. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
97 lines
3.6 KiB
ReStructuredText
97 lines
3.6 KiB
ReStructuredText
.. _sec_parallel_item:
|
|
|
|
**parallel** test item
|
|
============================================================
|
|
|
|
This element is of the following form:
|
|
|
|
.. code-block:: yaml
|
|
:caption: ``parallel`` test item usage example
|
|
|
|
- parallel:
|
|
name: My parallel block
|
|
sync: all
|
|
branches:
|
|
- name: Branch A
|
|
steps:
|
|
- py_func:
|
|
name: Long operation
|
|
file: long_op.py
|
|
func_name: do_work
|
|
- name: Branch B
|
|
wait_for:
|
|
condition: <| "$(ready_flag)" == "True" |>
|
|
timeout: 30
|
|
steps:
|
|
- let:
|
|
name: Mark done
|
|
values:
|
|
- branch_b_done: true
|
|
|
|
The ``parallel`` element runs several sequences of items concurrently. Each
|
|
inner sequence is called a *branch* and runs in its own thread. The parent
|
|
test item waits for branches to finish according to the ``sync`` policy.
|
|
|
|
Attributes
|
|
--------------------
|
|
|
|
* ``branches``: required. A list of branches to execute concurrently. Each
|
|
branch has a ``name`` and a ``steps`` list (same structure as a ``group``
|
|
item). It can also declare a ``wait_for`` precondition (see below).
|
|
* ``sync``: optional, defaults to ``all``.
|
|
|
|
* ``all``: the parallel item completes when *every* branch has finished.
|
|
The result is ``PASS`` if no branch returned ``FAIL`` (skipped or
|
|
disabled branches are ignored, like in ``group``); otherwise ``FAIL``.
|
|
* ``any``: the parallel item completes as soon as the *first* branch
|
|
finishes. The remaining branches are stopped (their next test items
|
|
are not executed). The result is ``PASS`` if at least one branch
|
|
succeeded.
|
|
|
|
* ``no_fail``: optional. When ``true``, a ``FAIL`` result is forced to
|
|
``PASS`` for the parallel item itself (same semantics as for any test
|
|
item). Branches keep their own result.
|
|
|
|
Branch attributes
|
|
--------------------
|
|
|
|
Each entry of ``branches`` is a dict with the following attributes:
|
|
|
|
* ``name``: required. The branch name. Used in reports and as a prefix
|
|
in the live log output (each line printed by the branch is prefixed
|
|
with ``[<name>] `` so concurrent branches stay readable).
|
|
* ``steps``: required. The list of test items executed sequentially
|
|
inside the branch.
|
|
* ``wait_for``: optional. Forces the branch to wait until a condition is
|
|
met before running its steps. If the timeout elapses, the branch
|
|
returns ``FAIL`` (the steps are not run). Sub-attributes:
|
|
|
|
* ``condition``: a testium expression evaluated repeatedly (every
|
|
100 ms) until it returns ``True``.
|
|
* ``timeout``: maximum wait, in seconds. Defaults to 30.
|
|
|
|
Reporting
|
|
--------------------
|
|
|
|
Each branch produces its own row in the SQLite report (with type
|
|
``Parallel branch``), in addition to the parent ``Parallel`` row. The
|
|
``log`` column of each row contains only the output emitted from that
|
|
branch's thread, so logs are never mixed between concurrent branches.
|
|
|
|
In the live (terminal / GUI) output, lines emitted from a branch are
|
|
prefixed with ``[<branch name>] ``. The prefix is not stored in the
|
|
SQLite log column.
|
|
|
|
Notes
|
|
--------------------
|
|
|
|
* A ``sleep`` item inside a branch is interruptible: if another
|
|
``sync: any`` branch wins the race, slow ``sleep`` items are aborted
|
|
within ~50 ms.
|
|
* A ``py_func`` or ``console`` item inside a branch is **not**
|
|
interruptible: a ``sync: any`` stop will only take effect after the
|
|
current item returns. The branch will then skip its remaining steps.
|
|
* When a user disables a branch in the GUI tree, the branch returns
|
|
``SKIP`` instantly without affecting the others (it does *not* win a
|
|
``sync: any`` race).
|