Skip to content

test(quic): expand branch coverage for quic_server.cpp#1058

Merged
kcenon merged 2 commits into
developfrom
test/issue-1052-quic-server-coverage
Apr 26, 2026
Merged

test(quic): expand branch coverage for quic_server.cpp#1058
kcenon merged 2 commits into
developfrom
test/issue-1052-quic-server-coverage

Conversation

@kcenon

@kcenon kcenon commented Apr 26, 2026

Copy link
Copy Markdown
Owner

What

Summary

Adds hermetic branch-coverage tests for src/experimental/quic_server.cpp to raise
the file from the 2026-04-26 baseline (line 43.7%, branch 17.5%) toward the
acceptance bar (line >= 70%, branch >= 60%) defined in epic #953. Tests operate
purely on the public API of messaging_quic_server and on quic_server_config,
covering field round-trips, default-state queries, error-path early-returns,
callback registration (legacy + interface adapters), concurrency invariants,
multi-instance independence, and destructor cleanup. No live QUIC peer or TLS
handshake is required.

Change Type

  • Test

Why

Related Issues

Motivation

src/experimental/quic_server.cpp sits among the lowest-coverage files in the
network_system source tree per the 2026-04-26 measurement on develop @ 05c1b7bb
(line 43.7%, branch 17.5%). The uncovered region concentrates in error and
boundary paths exactly the surfaces that regress during the ongoing Result<T>
migration and API stabilization. Bringing this file over the 70% line / 60%
branch bar contributes directly to the #953 acceptance criteria (80% line / 70%
branch ecosystem-wide). This PR is sub-issue 4 of 5 in the sequential batch
under #953 and follows the pattern established by #1054 (http2_client),
#1055 (grpc client), #1056 (http2_server), and #1057 (quic_socket).

Where

Files Changed

  • tests/unit/quic_server_branch_test.cpp (new, ~720 lines)
  • tests/CMakeLists.txt (registers network_quic_server_branch_test)
  • CHANGELOG.md (Unreleased -> Added entry)
  • docs/CHANGELOG.md (Unreleased -> Added entry, detailed)

How

Implementation Highlights

The test file groups cases by surface:

  • QuicServerConfigRoundTrip (12 tests): full-field round-trips for
    quic_server_config including empty / 1024-character / binary-byte cert/key
    paths, optional ca_cert_file present (16-character / 2048-character) and
    absent, require_client_cert toggling, ALPN vector with empty / single /
    four-with-empty-string / 32-entry growth and clear()-shrink, six numeric
    settings at zero and std::numeric_limits max boundaries, enable_retry
    toggle, retry_key vector growth, and full-config copy round-trip.
  • QuicServerConstruction (4 tests): empty / 512-character / binary-byte server
    IDs, 16-iteration short-lived loops, server_id() reference stability.
  • QuicServerDefaults (5 tests): never-started invariants for is_running,
    session_count, connection_count, sessions, and get_session for
    unknown / empty / 2048-character ids.
  • QuicServerStop (3 tests): rejection branch when never started, 5-iteration
    repeated rejection idempotency, plus i_quic_server::stop() interface
    delegation.
  • QuicServerDisconnect (6 tests): disconnect_session() not_found branch
    for unknown / empty / long / binary-byte ids with varied error codes (0, 1,
    42, max-uint64); disconnect_all() empty-map no-op.
  • QuicServerBroadcast / QuicServerMulticast (10 tests): empty-map ok()
    return for empty / small / 64 KiB payloads, repeated invocation, empty
    session_ids, many unknown ids (3, 64), and empty-string id.
  • QuicServerLegacyCallbacks (7 tests): default-constructed std::function,
    triple replacement, and shared_ptr capture for all five legacy callback
    types (connection / disconnection / receive / stream_receive / error).
  • QuicServerInterfaceCallbacks (12 tests): interface (i_quic_server)
    callback adapters for all five callbacks with populated lambdas, empty
    std::function, and (for connection) triple replacement; combined-
    registration test setting all five together.
  • QuicServerConcurrency (5 tests): concurrent polling under shared_ptr
    lifetime with 4 threads x 100-200 iterations of is_running, session_count,
    connection_count, server_id, sessions, plus a writer/reader pair on
    set_error_callback / is_running.
  • QuicServerMultiInstance (2 tests): 8 servers with independent default
    state, broadcast-on-one-does-not-affect-another sibling.
  • QuicServerDestructor (3 tests): never-started, after failed-stop, and with
    all five legacy callbacks registered.

Total: 69 hermetic test cases in 11 test suites.

Honest scope statement

The impl-level packet-driven paths in messaging_quic_server remain reachable
only with a UDP transport fixture and a TLS-1.3 client. Specifically the
following private surfaces stay uncovered without a live peer:

  • do_start_impl() success path past asio::ip::udp::socket bind; the
    bind_failed branches for address_in_use / access_denied are reachable
    only by binding to a privileged or in-use port and would not be hermetic.
  • do_stop_impl() (only reachable after a successful start; the success path
    starts an io_context loop that runs asynchronously and cannot be cleanly
    stopped from a unit test process).
  • start_receive() async UDP receive completion.
  • handle_packet() header parsing, dcid extraction, and session dispatch.
  • find_or_create_session() (session lookup, create, connection-limit-reached
    branch, UDP socket connect, quic_socket::accept, session callback wiring,
    metrics reporting, monitor recording).
  • generate_session_id() (only invoked inside find_or_create_session).
  • on_session_close() (peer-driven close).
  • start_cleanup_timer() and cleanup_dead_sessions() (periodic timer).
  • All five invoke_*_callback() helpers (only invoked from packet-driven
    paths).
  • The success branches of broadcast(), multicast(), disconnect_session(),
    and disconnect_all() under a populated session map.

Driving these would require either a live QUIC client that completes a TLS
handshake (with on-disk PEM cert/key) or a friend-declared test injection
point inside messaging_quic_server. Both are out of scope for this PR
following the precedent established by #1054 / #1055 / #1056 / #1057.

Testing Done

  • Tests added: 69 hermetic test cases across 11 test suites
  • Local build: skipped cmake/ctest not installed in sandbox
  • Will rely on CI (Ubuntu GCC/Clang, macOS, Windows MSVC, ASAN/TSAN/UBSAN)

Breaking Changes

None test-only additions. No production code changed.

kcenon added 2 commits April 26, 2026 18:20
Add tests/unit/quic_server_branch_test.cpp complementing
experimental_quic_server_test.cpp (Issue #989). Covers public-API
surfaces of messaging_quic_server reachable without a live QUIC peer:

- quic_server_config full-field round-trips: empty/1024-char/binary-byte
  cert/key paths, optional ca_cert_file present and absent,
  require_client_cert toggling, ALPN protocol vector growth (empty,
  single, four-with-empty-string, 32-entry), all six numeric settings
  at zero and std::numeric_limits boundaries, enable_retry toggle,
  retry_key vector growth (empty, 16, 256 bytes), full-config copy
  round-trip preserving every field
- Construction with empty / 512-char / binary-byte server IDs,
  16-iteration short-lived loops, server_id() reference stability
- Default-state queries on a never-started server: is_running,
  session_count, connection_count, sessions, get_session
- stop_server() rejection branch when never started, repeated rejection
  idempotency, i_quic_server::stop() interface delegation
- disconnect_session() not_found branch for unknown/empty/long/binary
  ids and varied error codes
- disconnect_all() empty-map no-op for varied error codes
- broadcast() and multicast() empty-map ok-return for empty/small/64KiB
  payloads, repeated invocation, empty session_ids, many unknown ids
- Legacy callback registration with empty std::function / triple
  replacement / shared_ptr capture for all five callback types
- Interface (i_quic_server) callback adapters for all five callbacks
  with null callbacks, populated lambdas, triple replacement
- Concurrent polling of is_running / session_count / server_id /
  sessions and concurrent legacy callback replacement under shared_ptr
- Multi-instance independent default state across 8 servers
- Destructor on never-started, after failed-stop, with callbacks
  registered

The impl-level packet-driven paths (do_start_impl, do_stop_impl,
start_receive, handle_packet, find_or_create_session, on_session_close,
start_cleanup_timer, cleanup_dead_sessions, all five invoke_*_callback
helpers, populated-map broadcast/multicast/disconnect_session/
disconnect_all branches) require a live QUIC client that completes a
TLS handshake or a friend-declared injection point and remain
uncovered.

Register network_quic_server_branch_test in tests/CMakeLists.txt.
Update CHANGELOG.md and docs/CHANGELOG.md.

Closes #1052
Part of #953
@github-actions

Copy link
Copy Markdown
Contributor

Coverage Report

Metric Value
Line Coverage 66.5%
Branch Coverage 32.9%
Target 80% lines / 70% branches
Coverage Details

Full HTML report is available as a build artifact.

@kcenon kcenon merged commit 2b3c25f into develop Apr 26, 2026
9 checks passed
@kcenon kcenon deleted the test/issue-1052-quic-server-coverage branch April 26, 2026 13:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant