test(http2): stabilize coverage and expand dispatcher branches (#1106)#1109
Merged
kcenon merged 2 commits intoMay 6, 2026
Merged
Conversation
…_peer Adds an optional post_handshake_frames constructor argument that lets callers supply pre-serialized HTTP/2 frame byte buffers for the mock peer to write immediately after the SETTINGS-ACK and before the per-mode request/response handling. Each entry is written verbatim, bypassing the frame_injector, so tests can construct precisely-formed PING, GOAWAY, WINDOW_UPDATE, RST_STREAM or arbitrary unknown-type frames whose wire format frame::parse will accept and route through the client's process_frame dispatcher. This unlocks dispatcher-branch coverage paths in http2_client.cpp that the request-error path (echo_one + injector) cannot reach because those branches require a server-originated frame on the connected state, not a corrupted response to a client-initiated request. Default empty value preserves existing Phase 2A / 2A.2 / 2E.R2 behavior for all current call sites.
Phase 2E.R3 follow-up to PR #1108. The Round 2 work landed 6 new TEST_F cases on the http2_client.cpp branch test, but the post-merge coverage workflow showed line/branch coverage on http2_client.cpp unchanged at 18.8% / 9.9% (target 60% / 40%). Root cause: 2 positive-path TEST_F (slow_write handshake, echo_one + truncate-at-9 response) fail under coverage instrumentation because lcov/gcov slow the SETTINGS handshake past the 2-3 second wait budget. The other 4 TEST_F pass but only re-cover early-connect lines that existing happy-path tests already hit, so they contribute zero net coverage. handle_ping_frame, handle_goaway_frame, handle_window_update_frame, handle_rst_stream_frame, and process_frame default branch remain unreachable. Two changes: 1. Guard the two coverage-flaky tests with #ifndef NETWORK_COVERAGE_BUILD. tests/CMakeLists.txt defines NETWORK_COVERAGE_BUILD on the branch test target only when ENABLE_COVERAGE is on, so Debug/Release matrix builds keep the slow_write partial-read accumulator and truncate-on- connected request-error branches under test, while the coverage workflow no longer carries a measured-failing-test signal. 2. Add 7 dispatcher TEST_F cases that use the new mock_h2_server_peer post_handshake_frames hook to write server-originated frames after the SETTINGS handshake: - ServerPingFrameDrivesHandlePingAndKeepsConnectionAlive - ServerPingAckFrameIsAbsorbedSilently - ServerGoawayFrameFlipsConnectionStateToDisconnected - ServerWindowUpdateOnConnectionStreamExpandsWindow - ServerWindowUpdateOnUnknownStreamIsSilentlyIgnored - ServerRstStreamOnUnknownStreamIsSilentlyIgnored - ServerUnknownFrameTypeIsHandledWithoutCrashing Each frame is serialized via the production frame class (ping_frame, goaway_frame, window_update_frame, rst_stream_frame) so frame::parse accepts the wire format and routes the frame to the intended handler. The unknown-type case constructs raw bytes with type=0xFF to drive either process_frame's default branch or the run_io read-error path; both outcomes are RFC-compliant. Local Debug build: existing pre-merge hermetic instability on macOS Apple Silicon affects the new tests the same way it affects the unmodified ConnectCompletesSettingsExchangeWithMockPeer baseline (documented in PR #1108 body). CI Linux + GitHub macOS runners are the authoritative verification surface, as before. Closes #1106
Contributor
Coverage Report
Coverage DetailsFull HTML report is available as a build artifact. |
This was referenced May 6, 2026
Closed
kcenon
added a commit
that referenced
this pull request
May 8, 2026
…ixture (#1113) Scale every hermetic-fixture wait_for budget by NETWORK_COVERAGE_TIMEOUT_MULTIPLIER so Http2ClientHermeticTransportTest dispatcher cases can complete the SETTINGS handshake under -fprofile-arcs -ftest-coverage instrumentation. PR #1111 diagnosis confirmed all 7 Round-3 dispatcher TEST_F (and ~30 other fixture-based tests) failed at wait_for(settings_exchanged(), 3s): 5.1-5.4s observed under coverage vs 100-300ms in Debug/Release, producing 0pp delta on src/protocols/http2/http2_client.cpp for three rounds (PR #1108, PR #1109). Changes: - tests/support/hermetic_transport_fixture.h: define NETWORK_COVERAGE_TIMEOUT_MULTIPLIER (default 1), apply to wait_for default timeout argument. - tests/support/hermetic_transport_fixture.cpp: scale make_loopback_tcp_pair accept-handler deadline by the multiplier. - tests/support/CMakeLists.txt: define NETWORK_COVERAGE_TIMEOUT_MULTIPLIER=5 on network_test_support PUBLIC when ENABLE_COVERAGE is ON, so all consuming test executables inherit it at the point of header inclusion. No src/ changes. No new TEST_F. Multiplier defaults to 1 outside coverage builds; Debug/Release matrix observable behaviour is unchanged. Closes #1112
This was referenced May 8, 2026
kcenon
added a commit
that referenced
this pull request
May 8, 2026
Records the Round 6 friend-injected process_frame pivot for #1115/#1116 under [Unreleased] in both root CHANGELOG.md and docs/CHANGELOG.md (SSOT). Notes the load-bearing 15.175s -> 0ms wall-time collapse, the 7 Server* TEST_F scope vs the 14 connect-state TEST_F that remain on the multiplier scaffold, the production-header byte-identity guarantee under BUILD_TESTS=OFF, and the deferred post-merge coverage gate (>=+20pp line / >=+10pp branch vs PR #1109 baseline) on src/internal/protocols/http2/http2_client.cpp.
kcenon
added a commit
that referenced
this pull request
May 8, 2026
Records the Round 6 friend-injected process_frame pivot for #1115/#1116 under [Unreleased] in both root CHANGELOG.md and docs/CHANGELOG.md (SSOT). Notes the load-bearing 15.175s -> 0ms wall-time collapse, the 7 Server* TEST_F scope vs the 14 connect-state TEST_F that remain on the multiplier scaffold, the production-header byte-identity guarantee under BUILD_TESTS=OFF, and the deferred post-merge coverage gate (>=+20pp line / >=+10pp branch vs PR #1109 baseline) on src/internal/protocols/http2/http2_client.cpp.
kcenon
added a commit
that referenced
this pull request
May 8, 2026
Closes #1115 Pivot http2_client dispatcher branch tests to friend-injected process_frame to bypass the SETTINGS-handshake wait_for path. PR #1114 (Round 5 multiplier) was structurally bounded by the ctest 300 s timeout under coverage build; direct injection collapses the 15.175 s wall-clock to 0 ms. - Add http2_client_test_access friend class gated by NETWORK_ENABLE_TEST_INJECTION - Refactor 7 Server* dispatcher TEST_F to call process_frame directly - Production header byte-identical when BUILD_TESTS=OFF - 14 connect-state TEST_F genuinely require connection state, retained on multiplier scaffold Post-merge verification: Coverage Analysis workflow on develop measures delta vs PR #1109 baseline (LH/LF=108/576, BRH/BRF=97/979). Acceptance gate per #1115 is >=+20pp line / >=+10pp branch on http2_client.cpp. Part of #953 coverage expansion.
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
Summary
Round 3 follow-up to PR #1108 (Round 2 of issue #1106). Two changes:
Guard 2 coverage-flaky tests in
tests/unit/http2_client_branch_test.cpp(SlowWriteServerSettingsStillCompletesHandshake,EchoOneTruncateAtNineDropsResponsePayloadFailingGet) with#ifndef NETWORK_COVERAGE_BUILD.tests/CMakeLists.txtdefines that macro on the branch-test target only whenENABLE_COVERAGE=ON, so Debug/Release matrix runs continue to exercise the slow_write partial-read accumulator and truncate-on-connected request-error branches, while the coverage workflow no longer surfaces them as measured-failing tests.Add 7 dispatcher TEST_F cases that drive previously-unreachable handlers in
process_frameby writing server-originated frames after the SETTINGS handshake. Uses a newpost_handshake_frameshook onmock_h2_server_peer(Phase 2E.R3 substrate, first commit) that bypasses the injector so productionframe_class::serialize()output reaches the client unmodified.Change Type
src/changes)Affected Components
tests/support/mock_h2_server_peer.htests/support/mock_h2_server_peer.cpptests/CMakeLists.txtNETWORK_COVERAGE_BUILDguard for branch-test targettests/unit/http2_client_branch_test.cppWhy
After PR #1108 merged, the post-merge coverage run 25449908024 at
b4692fedshowedhttp2_client.cppline/branch coverage unchanged at 18.8% / 9.9% (target 60% / 40%). Issue #1106 was therefore left OPEN with a recommendation comment proposing exactly the two changes shipped here:Why these specific dispatcher tests
The 4 negative-assertion tests added in PR #1108 (drop / truncate / malform-type / malform-streamid) all assert
EXPECT_FALSE(is_connected())and exercise only early-connect lines that pre-existing happy-path tests already hit. Net coverage contribution: zero. The 2 positive-assertion tests fail under coverage instrumentation, also contributing zero.The TEST_F batch added here advances the client past the connected state via the unmodified happy-path SETTINGS handshake (which works under coverage instrumentation) and then has the peer write one precisely-formed server-originated frame. Each frame routes through
process_frameto a specific handler:ServerPingFrameDrivesHandlePingAndKeepsConnectionAlivehandle_ping_framenon-ACK path +send_framereplyServerPingAckFrameIsAbsorbedSilentlyhandle_ping_frameis_ack early-returnServerGoawayFrameFlipsConnectionStateToDisconnectedhandle_goaway_frame+is_connected()conjunctionServerWindowUpdateOnConnectionStreamExpandsWindowhandle_window_update_frameconnection-level branchServerWindowUpdateOnUnknownStreamIsSilentlyIgnoredServerRstStreamOnUnknownStreamIsSilentlyIgnoredServerUnknownFrameTypeIsHandledWithoutCrashingprocess_framedefault branch ORrun_ioread-error pathWhere
src/is not touched. No public-API change. Production frame classes (ping_frame,goaway_frame,window_update_frame,rst_stream_frame) are reused frominternal/protocols/http2/frame.hso wire format always matches whatframe::parseexpects.Phase 2E.R3 (Issue #1106).How
Substrate change (commit 1)
mock_h2_server_peergains a fourth constructor argument:Each entry is written verbatim immediately after
settings_exchanged_flips true and before the per-mode branch. The injector is bypassed (this hook is for tests that need exact wire format, not fault injection). All existing call sites compile unchanged because the new parameter defaults to empty.Test additions (commit 2)
Each new TEST_F follows the same pattern: serialize a single server-originated frame via the production class, construct the peer with that frame in
post_handshake_frames, complete the SETTINGS handshake viamake_connected_client, wait briefly forrun_ioto consume the frame, and assert the resulting state.Local verification
network_test_supportlibrarymock_h2_server_peer.cpprecompiled,+24 / -4)network_http2_client_branch_testtargetConnectCompletesSettingsExchangeWithMockPeerbaseline test (peer.settings_exchanged()does not flip true within the wait budget on this machine). This is the same pre-existing local-environment hermetic instability documented in PR #1108's body. CI Linux + GitHub macOS runners are the authoritative verification surface.Test plan for reviewers
Acceptance follow-up (#1106)
Coverage targets (line >= 60%, branch >= 40% on
http2_client.cpp) are measured by the post-merge coverage workflow on develop. This PR's CI verifies test correctness; the coverage delta will appear on the next develop coverage run after merge. Whether one PR can close the gap entirely or whether a Round 4 is needed will be determined from that measurement, following the Round 1 / Round 2 convention of keeping #1106 open until coverage is verified.Breaking changes
None. The
mock_h2_server_peerconstructor change is additive with a default value.Rollback plan
Revert this PR. Production code is not touched. The substrate change is independent of any caller, so no other tests need adjusting.
Related
Checklist
tests/unit/http2_client_branch_test.cppstylesrc/changes