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:
// main GUI process
vericue::VeriCueServer server(&mainWindow);
server.start(4242);// 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:
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):
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
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:
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.

