Skip to content

Multi-process testing

Many Qt systems are more than one process: a main GUI plus workers, a shell hosting plugin sandboxes, or several cooperating tools. veriCue handles this by embedding one Runtime per process and driving them all from a single test through MultiVeriCueClient.

Fleets run over TCP

MultiVeriCueClient addresses each process by (host, port) - it has no local-endpoint form in either the Python or the C# client. So a fleet is the one common case where TCP on loopback is the right answer even for a single-host run. A single process you drive on its own can still use local IPC via connect_local().

1. Embed a Runtime in each process

Each process gets its own VeriCueServer on its own port:

cpp
// main GUI process
vericue::VeriCueServer server(&mainWindow);
server.start(4242);
cpp
// worker process
vericue::VeriCueServer server(&workerWindow);
server.start(4243);

Fixed ports are fine for a stable test rig. For CI, prefer start(0) (OS-assigned) and have each process print its port - printf("VERICUE_PORT=%d\n", server.serverPort()) - so the harness can collect the endpoints.

If your processes set auth tokens, each process can have its own.

2. Drive them with MultiVeriCueClient

MultiVeriCueClient (Python) manages one VeriCueClient per process and exposes them by name:

python
from vericue import MultiVeriCueClient

async with MultiVeriCueClient({
    "main":   ("127.0.0.1", 4242),
    "worker": ("127.0.0.1", 4243),
}) as fleet:
    # Drive the GUI...
    await fleet["main"].mouse_click("MainWindow/startWorkerButton")

    # ...then assert on the worker process it spawned.
    await fleet["worker"].wait_for_property(
        "WorkerWindow/statusLabel", "text", "Ready"
    )

connect_all() runs in parallel and the async with block guarantees all connections close on exit - including when an assertion fails.

Per-process tokens

An endpoint is (host, port) or (host, port, token):

python
fleet = MultiVeriCueClient({
    "main":   ("127.0.0.1", 4242, os.environ["MAIN_TOKEN"]),
    "worker": ("127.0.0.1", 4243, os.environ["WORKER_TOKEN"]),
})

3. Bulk helpers

python
fleet.names()                    # ["main", "worker"]
"worker" in fleet                # True
for name, client in fleet: ...   # iterate name -> client pairs

# Same raw request to every process; returns {name: response},
# with an Exception object as the value for processes that failed.
results = await fleet.broadcast("ping")

# Arbitrary parallel map across all processes:
windows = await fleet.gather(lambda c: c.list_windows())
# -> {"main": [...], "worker": [...]}

broadcast never raises for a single failing process - check the values

  • which makes it useful for health sweeps across a fleet.

A realistic flow: GUI spawns a worker

The subtlety in multi-process tests is that the worker process (and its veriCue port) usually doesn't exist until the GUI spawns it. Connect in two stages:

python
import asyncio
from vericue import VeriCueClient
from vericue.errors import ConnectionError as VeriCueConnectionError

async def test_gui_spawns_worker():
    # Stage 1: the GUI is already running.
    async with VeriCueClient() as gui:
        await gui.connect("127.0.0.1", 4242)
        await gui.mouse_click("MainWindow/startWorkerButton")

        # Stage 2: poll until the worker's veriCue port accepts connections.
        worker = VeriCueClient()
        for _ in range(50):                       # ~5 s budget
            try:
                await worker.connect("127.0.0.1", 4243)
                break
            except VeriCueConnectionError:      # not a builtin OSError
                await asyncio.sleep(0.1)
        else:
            raise AssertionError("worker never opened its veriCue port")

        try:
            await worker.wait_for_property(
                "WorkerWindow/statusLabel", "text", "Ready", timeout=10000
            )
        finally:
            await worker.disconnect()

Concurrent automation sessions

Each process runs its own server, so licensing counts per process. With an organization key file, every server enforces its own session limit against the clients connected to it. With floating licensing, each process checks out one lease from the shared pool at startup - a GUI + two workers holds three leases while running. See Floating licences for sizing guidance.

Released under a commercial licence. Privacy · Terms