test(quic): expand quic_socket coverage with mock_udp_peer (Part of #1065)#1079
Merged
Merged
Conversation
Add 8 new TEST_F cases under the existing QuicSocketHermeticTransportTest fixture in tests/unit/quic_socket_branch_test.cpp. They use make_loopback_udp_pair + mock_udp_peer to exercise paths that PR #1071 left uncovered because that PR only validated idle/disconnected state. New paths driven: - connect() success path past TLS init / derive_initial_secrets / start_handshake / queue_crypto_data / send_pending_packets / send_packet, with the loopback peer capturing the long-header Initial datagram and verifying the 0xC0+ first byte and a parsed src connection id. - connect() state transition from idle to handshake_start/handshake. - close() at the initial encryption level building a CONNECTION_CLOSE frame and dispatching it as a second datagram on the same socket. - do_receive() lambda handing a real datagram to handle_packet, with the silent-drop branches taken when packet_parser::parse_header fails (1-byte input) or when get_read_keys reports keys-not-yet- derived (long-header Initial stub on a fresh client). - stop_receive() correctness under an active peer-side sender. - Distinct connection-id generation across two independent client sockets, asserted via SCID extraction from the captured Initial packets. - connect() with an explicit non-empty SNI string still emitting a long-header Initial. No new test_support files; no CMake changes (the file is already linked to network::test_support at tests/CMakeLists.txt:4962). The acceptance criteria of issue #1065 (>= 80% line / >= 70% branch on src/internal/quic_socket.cpp) remain unreachable until Phase 2C of #1074 lands mock_quic_peer_loop with TLS 1.3 key derivation, so this PR ships incremental progress only. Part of #1065 Part of #953
Contributor
Coverage Report
Coverage DetailsFull HTML report is available as a build artifact. |
This was referenced Apr 28, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds 8 new
TEST_Fcases totests/unit/quic_socket_branch_test.cpp(+299 LOC, no new test files) under the existingQuicSocketHermeticTransportTestfixture. They compose the existingmake_loopback_udp_pair+mock_udp_peerinfrastructure (introduced in #1060) to drivequic_socketpaths that were left uncovered by the PR #1071 batch, which only exercised idle/disconnected-state validation.Newly reached paths in
src/internal/quic_socket.cppconnect()success path past TLS init, initial-secret derivation, ClientHello generation,queue_crypto_data,send_pending_packets, andsend_packetat the initial encryption level (lines 135-211).connect()state transitionidle -> handshake_start -> handshakeobserved viastate().close()from non-handshake-complete state: builds aCONNECTION_CLOSEframe and dispatches it as a second datagram on the same socket (lines 251-298).do_receive()lambda forwarding a real loopback datagram tohandle_packet(), including:packet_parser::parse_headerfails on a 1-byte input.get_read_keys()reports keys-not-yet-derived on a long-header Initial stub against a fresh client.stop_receive()correctness under an active peer-side sender (theis_receiving_short-circuit in the receive completion lambda).connect()with an explicit non-empty SNI string still emits a long-header Initial packet.Why
Part of #1065andPart of #953(epic: 40% -> 80% coverage).src/internal/quic_socket.cppline coverage stood at 43.7% line / 21.3% branch as of the 2026-04-26 lcov run (workflow 24947193873). PR #1071 added 35TEST_Fcases (583 LOC) covering validation guards, state-machine guards onsend_stream_data/create_stream/close_stream, receive-loop idempotency, multi-instance state isolation, move semantics, and callback edge cases — but every one of those tests left the socket inidlestate and never put a datagram on the loopback wire. As a result, large blocks of the file (the connect/close packet emit chain, the receive-completion lambda,handle_packet's parse and key-availability branches) were not driven by any unit test.Because
make_loopback_udp_pair(fromhermetic_transport_fixture.h) andmock_udp_peer(frommock_udp_peer.h) already ship as part ofnetwork::test_support(linked attests/CMakeLists.txt:4962), this PR can drive those paths without introducing any new test infrastructure. The HEADERS+DATA-style server replies and theconnected_callbackcompletion path remain gated on Phase 2C of #1074 (mock_quic_peer_loopwith TLS 1.3 key derivation), so this PR usesPart of #1065, notCloses.Where
tests/unit/quic_socket_branch_test.cppTEST_Fcases appended underQuicSocketHermeticTransportTest, plus<array>and<span>includes added explicitly (previously transitively included viamock_udp_peer.h).No changes under
src/, no new test files, no CMake change, no new test_support files.How
Each new
TEST_F:make_loopback_udp_pair(io())from the inheritedhermetic_transport_fixtureworker thread.mock_udp_peer, takes its endpoint, and uses the other half to construct aquic_socket(client or server role).connect-driven tests: callsclient->connect(peer_endpoint, ...), thenpeer.receive(4096, 200ms)to capture the emitted Initial datagram.do_receive-driven tests: registers anerror_callbackthat increments an atomic counter, callsstart_receive(), sends bytes from the peer side, sleeps 50ms, callsstop_receive(), and asserts the counter remained at zero (silent-drop branches do not invokeerror_callback).stop_receive-correctness: armsstart_receive()then immediatelystop_receive()before the peer send, asserting the datagram never reacheshandle_packet.stop_receive()andshared_ptrdestruction.Tests reaching the connect-side paths assert the captured first byte has the long-header high+fixed bits (
>= 0xC0) per RFC 9000 Section 17.2, which is stronger than just "something was sent" — it confirms the emitted bytes are shaped like a QUIC long-header packet rather than arbitrary garbage.The
MultipleConcurrentConnectsHaveDistinctSourceConnectionIdstest parses the long-header SCID by walking the byte layout (byte0 + version[4] + dcid_len[1] + dcid + scid_len[1] + scid) and asserts the two extracted SCIDs differ, validating thatgenerate_connection_id()produces independent IDs per fresh socket.Coverage Scope (Honest Assessment)
Local C++ toolchain (cmake/g++/ninja) is not available in this dev environment; the
coverage.ymlworkflow is the authoritative verification surface (PR #1071 / #1075 / #1076 / #1077 precedent). Expected outcome:src/internal/quic_socket.cppline coverage strictly increases above the 43.7% baseline. Branch coverage above the 21.3% baseline.process_crypto_framepast key derivation,process_stream_frame,process_handshake_done_frame,connected_callbackinvocation, full successful disconnect path) remain uncovered — Phase 2C of infra(test): protocol-aware loopback peers + error injection (Phase 2 of #1060) #1074 will unblock these by shippingmock_quic_peer_loopwith TLS 1.3 key derivation.Part of, notCloses); the issue's>= 80% line / >= 70% branchacceptance criteria becomes reachable only after Phase 2C lands and a final follow-up test PR drives the success paths.Part of #1065
Part of #953