Skip to content

QuicX Interop Test Usage

This document describes how to run QuicX’s interop tests.

Status & result matrix lives in reports/interop_status.md.

Framework internals & official spec live in guide/interop_overview.md.


Interop testing is executed directly with the official quic-interop-runner.

The QuicX repo only provides the artifacts the official runner needs; the in-repo runner has been retired:

test/interop/
├── CMakeLists.txt # build rules for the interop binaries
├── interop_server.cpp # interop server source
├── interop_client.cpp # interop client source
├── Dockerfile # builds the quicx-interop image
└── run_endpoint.sh # in-container entrypoint (implements the runner's env-var protocol)

The official runner schedules implementations by image name, so build the image from the QuicX repo root first:

终端窗口
docker build -t quicx-interop:latest -f test/interop/Dockerfile .

Notes:

  • The image is based on the official martenseemann/quic-network-simulator-endpoint, with /run_endpoint.sh as the ENTRYPOINT, fully following the quic-network-simulator endpoint conventions
  • interop_server / interop_client are compiled from source inside the image; no pre-built local binaries are needed
  • After a C++ change, rebuild the image before rerunning (the runner picks up whatever local image carries the :latest tag)

To publish to a registry (e.g. GHCR):

终端窗口
docker tag quicx-interop:latest ghcr.io/<owner>/quicx-interop:latest
docker push ghcr.io/<owner>/quicx-interop:latest

终端窗口
git clone https://github.com/quic-interop/quic-interop-runner
cd quic-interop-runner
pip3 install -r requirements.txt
RequirementNotes
Docker + docker composeBrings up the client / server / sim containers
Python 3Runs the runner
Wireshark 4.5.0+The runner uses tshark on sim pcaps to decide results (resumption / zerortt / keyupdate …)
Linux hostRun sudo modprobe ip6table_filter before IPv6 test cases

Make sure implementations_quic.json contains the quicx entry (add it if missing):

"quicx": {
"image": "quicx-interop:latest",
"url": "https://github.com/caozhiyi/quicX",
"role": "both"
}

role: both means quicx participates in the matrix both as server and as client.


Run everything from the official runner’s repo root.

终端窗口
# Against a specific set of servers
python3 run.py -c quicx -s quiche,ngtcp2,quic-go
# Against every server
python3 run.py -c quicx
终端窗口
# Against a specific set of clients
python3 run.py -s quicx -c ngtcp2,picoquic,aioquic
# Against every client
python3 run.py -s quicx

4.3 Selecting scenarios / reproducing a single result

Section titled “4.3 Selecting scenarios / reproducing a single result”
终端窗口
# Only handshake, transfer and v2
python3 run.py -s quicx -c ngtcp2 -t handshake,transfer,v2
# Reproduce one failure with debug logs
python3 run.py -d -s quicx -c ngtcp2 -t v2
终端窗口
# Every server × every client × every scenario (very slow; use sparingly)
python3 run.py
FlagMeaning
-s LISTserver implementations (comma-separated)
-c LISTclient implementations (comma-separated)
-t LISTtest cases (comma-separated)
-ddebug logging
-j FILEwrite the result matrix as JSON
-mwrite the result matrix as Markdown
-l DIRlog directory (default logs/)
-fsave downloaded files when a test fails, for diffing
-p PROTOCOLquic (default) / webtransport

See python3 run.py --help for the full list.


The official runner currently defines 22 conformance scenarios plus 2 measurement scenarios. QuicX declares its supported set via the allow-list in run_endpoint.sh; unsupported scenarios exit with code 127 per the official convention and are recorded as UNSUPPORTED.

ScenarioDescriptionQuicX
handshakeBasic handshake, small download✅
transferLarge-file transfer✅
retryServer forces stateless retry✅
resumption1-RTT session resumption (two connections)✅
zerortt0-RTT early data✅
http3HTTP/3 interaction✅
chacha20Forces ChaCha20-Poly1305✅
keyupdateClient triggers key update✅
v2QUIC v2 (RFC 9369), version 0x6b3343cf✅
rebind-portClient NAT port rebinding✅
rebind-addrClient NAT address rebinding✅
connectionmigrationActive client-driven connection migration✅
ecnECN marking and echo✅
longrttHigh-RTT path❌
multiplexingMany concurrent streams❌
blackholeTransient network blackhole❌
amplificationlimitAmplification limit❌
handshakeloss / transferlossLoss during handshake / transfer❌
handshakecorruption / transfercorruptionCorrupted packets during handshake / transfer❌
ipv6IPv6 connectivity❌
goodput / crosstrafficThroughput / cross-traffic measurement❌

The run_endpoint.sh allow-list additionally contains multiconnect / versionnegotiation — scenarios from the retired in-repo runner that do not exist in the official matrix; they are kept for compatibility only. Per-scenario pass rates and failure root causes live in reports/interop_status.md.


After each run, logs are saved under logs/ in the runner directory (override with -l):

logs/
└── <server>_<client>/ # e.g. quicx_ngtcp2
└── <testcase>/ # e.g. v2
├── output.txt # runner console output (incl. failure reason)
├── server/ # server-side logs (stdout/stderr, qlog)
├── client/ # client-side logs (stdout/stderr, qlog)
└── sim/ # pcaps recorded by the simulator

Triage flow:

  1. Check output.txt for the failure reason (timeout / file mismatch / exit 127)
  2. Check QuicX’s logs under client/ or server/ to see whether the failure is at the handshake or the transfer stage
  3. Load qlog files into qvis for visualization
  4. Decrypt sim/ pcaps in Wireshark using the SSLKEYLOGFILE (NSS Key Log format)