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 server per process and driving them all from a single test through MultiVeriCueClient.

1. Embed a server 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

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 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