Skip to content

test(quic): expand quic_server.cpp coverage to 70%/50%#1135

Merged
kcenon merged 1 commit into
developfrom
test/issue-1123-quic-server-coverage-70
May 9, 2026
Merged

test(quic): expand quic_server.cpp coverage to 70%/50%#1135
kcenon merged 1 commit into
developfrom
test/issue-1123-quic-server-coverage-70

Conversation

@kcenon

@kcenon kcenon commented May 9, 2026

Copy link
Copy Markdown
Owner

What

Expands unit test coverage for src/experimental/quic_server.cpp from current 43.7% line / 17.5% branch toward >=70% line / >=50% branch by driving the previously private dispatch helpers of messaging_quic_server through the existing quic_server_probe friend (gated by NETWORK_ENABLE_TEST_INJECTION).

Change Type

  • Test
  • Feature
  • Bugfix
  • Refactor
  • Documentation

Affected Components

  • tests/support/quic_server_probe.[h|cpp] — extends the existing probe with forwarders for generate_session_id, on_session_close, cleanup_dead_sessions, start_receive, start_cleanup_timer, and the five invoke_*_callback dispatchers
  • tests/unit/quic_server_dispatcher_branch_test.cpp (new) — 34 TEST_F cases driving the previously unreachable branches
  • tests/CMakeLists.txt — wires the new network_quic_server_dispatcher_branch_test target

Why

Part of #953. v1.0 readiness gate requires >=80% line coverage ecosystem-wide (#964) — applies even to experimental modules included in default build. quic_server.cpp is 378 LOC; its private dispatchers (session lookup / creation, callback invocation, periodic cleanup, async-receive guard) cannot be reached hermetically from the public API: doing so requires a live UDP peer that completes a TLS-1.3 handshake.

Mirrors the strategy of #1122 (quic_socket dispatcher) and #1121 (http2_server dispatcher) which already proved the friend-probe pattern under coverage instrumentation.

Where

  • Source under test: src/experimental/quic_server.cpp (378 LOC)
  • New test: tests/unit/quic_server_dispatcher_branch_test.cpp (744 LOC, 34 TEST_F)
  • Friend probe extension: tests/support/quic_server_probe.[h|cpp]
  • Friend gate: NETWORK_ENABLE_TEST_INJECTION macro defined PUBLIC on network_system when BUILD_TESTS=ON (cmake/network_system_targets.cmake)

How

Coverage Targets per Private Surface

  • generate_session_id: counter increment, server_id prefix preservation, cross-instance independence, concurrent invocation uniqueness
  • on_session_close: empty-map silent no-op branch, unknown-id silent no-op branch (find()==end short-circuit), populated-callback no-op when id is unknown
  • cleanup_dead_sessions: empty-map fall-through (no log line emitted)
  • start_receive: not-running early-return guard
  • start_cleanup_timer: not-running / null-timer early-return guard
  • invoke_*_callback dispatchers: empty-function branch and populated-function branch for all five callback types
  • handle_packet: empty data, zero-filled buffer (parse error), random bytes (parse error), repeated invalid packets, varied source endpoints
  • Running-state integration: dispatch helpers fired on start_server(0) lifecycle
  • max_connections=0 config round-trip lifecycle

Acceptance Criteria

  • New 34 tests pass locally on macOS Debug
  • Existing tests continue to pass without regression (quic_server_probe_test: 2, quic_server_branch_test: 78, experimental_quic_server_test: 14)
  • CI green on Ubuntu/macOS/Windows matrix
  • Coverage workflow verifies >=70% line / >=50% branch on experimental/quic_server.cpp

Testing Done

[==========] 34 tests from 9 test suites ran. (1 ms total)
[  PASSED  ] 34 tests.

Pre-existing test failures observed in network_quic_socket_branch_test (4 QuicSocketHermeticTransportTest cases) are unrelated to this PR — confirmed by reproducing them on develop HEAD before applying these changes.

Pre-existing build errors in examples/{tcp,udp}_echo*.cpp are unrelated to this PR — also confirmed on develop HEAD.

Closes #1123

Drives the previously private dispatch helpers of messaging_quic_server
through the existing quic_server_probe friend (gated by
NETWORK_ENABLE_TEST_INJECTION) so the previously unreachable branches in
src/experimental/quic_server.cpp can be exercised under coverage
instrumentation.

Probe additions (tests/support/quic_server_probe.[h|cpp]):
- generate_session_id forwarder
- on_session_close forwarder
- cleanup_dead_sessions forwarder
- start_receive forwarder (not-running guard)
- start_cleanup_timer forwarder (not-running guard)
- invoke_*_callback dispatcher forwarders for connection / disconnection
  / receive / stream_receive / error

New test file tests/unit/quic_server_dispatcher_branch_test.cpp adds 34
tests covering:
- generate_session_id: counter increment, server_id prefix preservation,
  cross-instance independence, concurrent invocation uniqueness
- on_session_close: empty-map silent no-op, unknown-id silent no-op,
  populated-callback no-op when id is unknown
- cleanup_dead_sessions: empty-map fall-through (not-running and running
  lifecycle states)
- start_receive / start_cleanup_timer: not-running early-return guards
- invoke_*_callback: empty-function branch and populated-function branch
  for all five callback types (legacy + interface adapters)
- handle_packet: empty data, zero-filled buffer, random bytes, repeated
  invalid packets, varied source endpoints
- Running-state integration: cleanup, start_receive, start_cleanup_timer,
  on_session_close, generate_session_id, handle_packet, all five
  invoke_*_callback dispatchers under start_server(0) lifecycle
- max_connections=0 config round-trip lifecycle

Mirrors the pattern from issue #1122 (quic_socket dispatcher) and #1121
(http2_server dispatcher). All new tests pass locally on macOS Debug.
Existing quic_server_probe_test (2), quic_server_branch_test (78), and
experimental_quic_server_test (14) continue to pass without regression.

Closes #1123
@github-actions

github-actions Bot commented May 9, 2026

Copy link
Copy Markdown
Contributor

Coverage Report

Metric Value
Line Coverage 70.5%
Branch Coverage 35.1%
Target 80% lines / 70% branches
Coverage Details

Full HTML report is available as a build artifact.

@kcenon kcenon merged commit 37512e3 into develop May 9, 2026
9 checks passed
@kcenon kcenon deleted the test/issue-1123-quic-server-coverage-70 branch May 9, 2026 22:50
kcenon added a commit that referenced this pull request May 21, 2026
* test(quic): expand connection.cpp branch coverage

Drives the private dispatch helpers of
src/protocols/quic/connection.cpp through a new test-only friend
class tests::support::quic_connection_test_access (gated by
NETWORK_ENABLE_TEST_INJECTION) so the std::visit arms of
handle_frame, plus process_frames / build_packet /
generate_ack_frame / handle_loss_detection_result /
generate_probe_packets / queue_frames_for_retransmission /
update_state, are reachable under -fprofile-arcs -ftest-coverage
without waiting on the TLS handshake (which exceeds wait_for(3s)
per PR #1111 diagnosis).

Coverage areas added:
- handle_frame std::visit arms: padding, ping, ack (with seeded
  sent_packets to exercise the erase loop), crypto, max_data,
  max_stream_data, max_streams (bidi + uni), connection_close
  (transport + application), handshake_done (client connected +
  server no-op), reset_stream / stop_sending unknown-stream arms,
  new_connection_id, retire_connection_id error propagation,
  data_blocked / stream_data_blocked / streams_blocked /
  path_challenge / path_response / new_token catch-all arm.
- process_frames empty + malformed payload dispatch.
- build_packet no-keys early-return for all encryption levels.
- generate_ack_frame nullopt vs populated arms (largest_received
  zero with ack_needed true exercises the OR-branch).
- get_pn_space switch arms for both const and non-const overloads
  including the initial / zero_rtt aliasing.
- update_state no-op when handshake not complete.
- close / close_application re-entry guards (already closing,
  draining, closed).
- enter_closing / enter_draining no-op arms.
- on_timeout drain-timeout, idle-timeout, no-timeout.
- next_timeout closed (nullopt) + draining (drain_deadline) arms.
- handle_loss_detection_result: none, pto_expired (asserts PING
  frame appended), packet_lost (asserts pending_frames grew),
  ecn::congestion_signal, ecn::ecn_failure.
- generate_probe_packets encryption-level cascade with and without
  pending crypto data.
- queue_frames_for_retransmission per-variant arms: padding / ack
  (not requeued), crypto (per level routed to matching queue),
  stream (skipped), ping / new_token / handshake_done (requeued),
  flow-control frames (requeued), reset_stream / stop_sending
  (requeued), new_connection_id / retire_connection_id (requeued),
  path_challenge / path_response (skipped), connection_close
  (skipped).
- start_handshake error guards (server-side rejection, non-idle
  state rejection) and init_server_handshake client-side rejection.

src/internal/protocols/quic/connection.h change is additive only:
a forward declaration of the probe and a friend declaration, both
gated by #if defined(NETWORK_ENABLE_TEST_INJECTION). Production
builds with BUILD_TESTS=OFF compile byte-identical.

Pattern mirrors PR #1116 (http2_client_test_access) and PR #1135
(quic_socket_test_access).

Closes #1145

* test(quic): extend connection branch coverage with derived keys

Round 7 follow-up to the initial PR commit. Local coverage delta
on the first round measured 62.6% -> 72.9% line / 34.4% -> 40.1%
branch from the run-26200588035 artifact, short of the 80%/70%
gate. The remaining gap is concentrated in build_packet's
post-keys body, which is unreachable until initial keys are
derived. Adding:

- ConnectionBuildPacketInitialKeysTest: derives initial secrets
  via the public crypto().derive_initial_secrets() API (does not
  require init_client/init_server) so build_packet can be driven
  past the keys-result.is_err() early-return. Covers empty payload
  short-circuit, pending CRYPTO assembly, ACK frame assembly,
  CONNECTION_CLOSE assembly, pending_frames emission, generate_packets
  pendng-crypto path, generate_packets close_sent path, and
  generate_packets after closed.

- generate_packets state-guard arms (idle, connected without data).

- next_timeout idle-deadline path with no loss-detector timeout.

- receive_packet draining and closed state arms (bytes/packets
  counters increment but no processing).

- on_timeout no-timeout idle arm.

All additions are header-include-only test cases routed through
the existing quic_connection_test_access probe; no further
src/ change required.
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