You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Pivot from the caller-side wait_for multiplier approach (Round 5: #1112 + PR #1113 + PR #1114) to a friend-injected process_frame mechanism so that Http2ClientHermeticTransportTest dispatcher TEST_F can exercise src/protocols/http2/http2_client.cpp branches without depending on the async SETTINGS-handshake gate. PR #1111 diagnosis named this as the architectural alternative ("Option C") to be used if the multiplier approach proved inadequate.
Change Type
Test infrastructure (no src/ behavioural change beyond a friend declaration; no new TEST_F shape, but existing dispatcher TEST_F refactored to call process_frame directly)
Affected Components
src/protocols/http2/http2_client.h — add friend class Http2ClientHermeticTransportTest; (or per-test friend declarations) gated by a test-only macro so production builds are unaffected
tests/support/hermetic_transport_fixture.{h,cpp} — likely no change; fixture remains for tests that genuinely need async transport
Possibly: tests/CMakeLists.txt — define a test-only macro (e.g. NETWORK_HTTP2_CLIENT_FRIEND_TESTS) when building test targets so the friend declaration only activates in test builds
Why
PR #1114 acceptance verification (Coverage Analysis run 25562347620, head 1efbc1d) measured 0pp delta on http2_client.cpp (LH/LF=108/576, BRH/BRF=97/979 — byte-identical with the PR #1109 baseline). Test execution evidence:
Test #5868's 15.175 s exactly equals wait_for(3s) × NETWORK_COVERAGE_TIMEOUT_MULTIPLIER(5) = 15s — confirming the PR #1114 multiplier was applied as designed. SETTINGS handshake under coverage instrumentation now exceeds 15 s (vs the 5.1–5.4 s observed in PR #1111 Round-3 diagnosis), and Test #5869 hitting the ctest 300 s timeout proves that simply increasing the multiplier (10×, 15×) is incompatible with reasonable ctest budgets — the multiplier-scaling approach is structurally bounded.
The dispatcher branches in http2_client.cpp cannot be reached via the async SETTINGS-handshake path under instrumentation. Bypassing that path entirely (Option C) is the remaining architectural avenue.
Add friend declaration for the hermetic test fixture, gated by #ifdef NETWORK_HTTP2_CLIENT_FRIEND_TESTS
tests/unit/http2_client_branch_test.cpp
Refactor 24 dispatcher TEST_F to call process_frame(...) (or chosen internal entry point) directly with constructed frames; the wait_for(SETTINGS-exchange) step is dropped
tests/CMakeLists.txt
Define NETWORK_HTTP2_CLIENT_FRIEND_TESTS=1 PRIVATE on the test binary that consumes the friend access
No other src/ behavioural changes. No new TEST_F. The 24 existing dispatcher TEST_F continue to assert the same observable contract; only their execution path changes.
How
Technical Approach
Choose the friend entry point. Inspect Http2Client for a single internal method that already drives the dispatcher (likely something like process_frame, handle_frame, or dispatch_frame). The chosen entry point should be reachable without first completing SETTINGS handshake — this is the whole point of Option C.
Add the friend declaration in src/protocols/http2/http2_client.h:
#ifdef NETWORK_HTTP2_CLIENT_FRIEND_TESTS
classHttp2ClientHermeticTransportTest; // forward-declare in test namespaceclassHttp2Client {
// ...friendclass ::testing::Test; // or a more specific friend class// ...
};
#endif
Verify the exact friend syntax against the test class's actual namespace.
Refactor the 24 dispatcher TEST_F in tests/unit/http2_client_branch_test.cpp. Before:
TEST_F(Http2ClientHermeticTransportTest, ServerPingFrameDrivesHandlePingAndKeepsConnectionAlive) {
auto client = make_connected_client(); // hangs at SETTINGS handshake under coverage
peer_->send_ping_frame();
ASSERT_TRUE(wait_for([&]{ return client->is_alive(); }, 3s));
}
After:
TEST_F(Http2ClientHermeticTransportTest, ServerPingFrameDrivesHandlePingAndKeepsConnectionAlive) {
auto client = make_uninitialized_client(); // skip handshake; client constructor only
Http2Frame frame = build_ping_frame();
client->process_frame(frame); // direct dispatcher call via friend accessEXPECT_TRUE(client->ping_handler_invoked()); // or appropriate assertion
}
Define the test-only macro in tests/CMakeLists.txt:
Do NOT remove the existing wait_for/multiplier scaffolding — it stays for tests that genuinely exercise async transport (e.g. ConnectAttemptsHandshakeAgainstLoopbackTlsPeer which already passes in 0.27 s). Only the dispatcher-branch TEST_F migrate to direct-call.
Acceptance Criteria
src/protocols/http2/http2_client.h defines a friend access point gated by NETWORK_HTTP2_CLIENT_FRIEND_TESTS; production builds (where the macro is undefined) are byte-identical (verified by ABI/hash check or simple diff of preprocessor output).
All 24 dispatcher TEST_F in tests/unit/http2_client_branch_test.cpp execute the dispatcher branch via direct process_frame (or chosen entry point) instead of wait_for(SETTINGS-exchange).
Debug and Release matrix CI on the PR pass (multiplier=1 path unchanged for the few remaining wait_for-based tests; friend access compiles cleanly).
Post-merge coverage workflow run on develop measures delta ≥ +20pp line AND ≥ +10pp branch on src/protocols/http2/http2_client.cpp versus the pre-merge sha. Reference baseline: 18.75% line / 9.91% branch (108/576, 97/979) from PR test(http2): stabilize coverage and expand dispatcher branches (#1106) #1109.
No dispatcher TEST_F hits the ctest 300 s timeout (vs Test #5869's timeout in run 25562347620).
No new TEST_F introduced; only execution-path refactor.
Test Plan for Reviewers
Inspect the friend-declaration diff and confirm production builds are unaffected (e.g. cmake --build in non-test mode does not see the friend).
Inspect the refactored TEST_F bodies — confirm they call the chosen dispatcher entry point directly and assert observable side-effects.
After merge, trigger the develop coverage workflow and check coverage_filtered.info for post-merge LH/LF/BRH/BRF on http2_client.cpp against the ≥+20pp / ≥+10pp gate.
Breaking Changes
None. friend declarations gated by a test-only macro do not affect production binary layout (some compilers emit different symbol info for friends, but no observable runtime/ABI change). The 24 TEST_F still assert the same contracts.
Rollback
git revert is sufficient. Test infrastructure + a single header friend line; no production-source semantics change.
Risks
Wrong entry point choice: if process_frame requires connection state that only the SETTINGS-handshake provides, calling it on an uninitialized client may segfault or hit asserts. Mitigation: read the dispatcher implementation carefully before choosing the entry point; if needed, add a setup_minimal_state_for_testing() helper exposed via friend.
Friend coupling: introducing friend class Http2ClientHermeticTransportTest couples production header to a test class name. Mitigation: use a single dedicated friend struct (e.g. struct Http2ClientTestAccess) in the production header, and have the test class delegate through that. This keeps the production header agnostic of test class names.
Coverage delta still 0pp: if the dispatcher branches truly cannot be reached without going through process_frame's prerequisites that themselves call into branch-counted paths, Option C also fails and the coverage gap is structural. In that case, the right move is to lower the gate or accept the gap with a written justification (separate decision).
What
Pivot from the caller-side
wait_formultiplier approach (Round 5: #1112 + PR #1113 + PR #1114) to a friend-injectedprocess_framemechanism so thatHttp2ClientHermeticTransportTestdispatcher TEST_F can exercisesrc/protocols/http2/http2_client.cppbranches without depending on the async SETTINGS-handshake gate. PR #1111 diagnosis named this as the architectural alternative ("Option C") to be used if the multiplier approach proved inadequate.Change Type
src/behavioural change beyond afrienddeclaration; no new TEST_F shape, but existing dispatcher TEST_F refactored to callprocess_framedirectly)Affected Components
src/protocols/http2/http2_client.h— addfriend class Http2ClientHermeticTransportTest;(or per-test friend declarations) gated by a test-only macro so production builds are unaffectedtests/unit/http2_client_branch_test.cpp— refactor dispatcher TEST_F (~24 cases at the lines edited by PR fix(test): apply NETWORK_COVERAGE_TIMEOUT_MULTIPLIER at explicit wait_for call sites (#1112) #1114) to callprocess_frame(or whichever internal entry point cleanly exposes dispatcher branches) directly with crafted frames, instead of waiting onwait_for(SETTINGS-exchange)tests/support/hermetic_transport_fixture.{h,cpp}— likely no change; fixture remains for tests that genuinely need async transporttests/CMakeLists.txt— define a test-only macro (e.g.NETWORK_HTTP2_CLIENT_FRIEND_TESTS) when building test targets so thefrienddeclaration only activates in test buildsWhy
PR #1114 acceptance verification (Coverage Analysis run 25562347620, head
1efbc1d) measured 0pp delta onhttp2_client.cpp(LH/LF=108/576, BRH/BRF=97/979 — byte-identical with the PR #1109 baseline). Test execution evidence:ConnectAttemptsHandshakeAgainstLoopbackTlsPeerConnectCompletesSettingsExchangeWithMockPeerSecondConnectReturnsAlreadyExistsAfterFirstSucceedsSendRequestTimesOutWhenPeerSendsNoHeadersTest #5868's 15.175 s exactly equals
wait_for(3s) × NETWORK_COVERAGE_TIMEOUT_MULTIPLIER(5) = 15s— confirming the PR #1114 multiplier was applied as designed. SETTINGS handshake under coverage instrumentation now exceeds 15 s (vs the 5.1–5.4 s observed in PR #1111 Round-3 diagnosis), and Test #5869 hitting the ctest 300 s timeout proves that simply increasing the multiplier (10×, 15×) is incompatible with reasonable ctest budgets — the multiplier-scaling approach is structurally bounded.The dispatcher branches in
http2_client.cppcannot be reached via the async SETTINGS-handshake path under instrumentation. Bypassing that path entirely (Option C) is the remaining architectural avenue.Related Issues
docs/coverage/http2_client_round-4-diagnosis.md)Where
src/protocols/http2/http2_client.hfrienddeclaration for the hermetic test fixture, gated by#ifdef NETWORK_HTTP2_CLIENT_FRIEND_TESTStests/unit/http2_client_branch_test.cppprocess_frame(...)(or chosen internal entry point) directly with constructed frames; thewait_for(SETTINGS-exchange)step is droppedtests/CMakeLists.txtNETWORK_HTTP2_CLIENT_FRIEND_TESTS=1PRIVATE on the test binary that consumes the friend accessNo other
src/behavioural changes. No new TEST_F. The 24 existing dispatcher TEST_F continue to assert the same observable contract; only their execution path changes.How
Technical Approach
Http2Clientfor a single internal method that already drives the dispatcher (likely something likeprocess_frame,handle_frame, ordispatch_frame). The chosen entry point should be reachable without first completing SETTINGS handshake — this is the whole point of Option C.src/protocols/http2/http2_client.h:tests/unit/http2_client_branch_test.cpp. Before:tests/CMakeLists.txt:wait_for/multiplier scaffolding — it stays for tests that genuinely exercise async transport (e.g.ConnectAttemptsHandshakeAgainstLoopbackTlsPeerwhich already passes in 0.27 s). Only the dispatcher-branch TEST_F migrate to direct-call.Acceptance Criteria
src/protocols/http2/http2_client.hdefines a friend access point gated byNETWORK_HTTP2_CLIENT_FRIEND_TESTS; production builds (where the macro is undefined) are byte-identical (verified by ABI/hash check or simple diff of preprocessor output).tests/unit/http2_client_branch_test.cppexecute the dispatcher branch via directprocess_frame(or chosen entry point) instead ofwait_for(SETTINGS-exchange).src/protocols/http2/http2_client.cppversus the pre-merge sha. Reference baseline: 18.75% line / 9.91% branch (108/576, 97/979) from PR test(http2): stabilize coverage and expand dispatcher branches (#1106) #1109.Test Plan for Reviewers
cmake --buildin non-test mode does not see the friend).coverage_filtered.infofor post-merge LH/LF/BRH/BRF onhttp2_client.cppagainst the ≥+20pp / ≥+10pp gate.Breaking Changes
None.
frienddeclarations gated by a test-only macro do not affect production binary layout (some compilers emit different symbol info for friends, but no observable runtime/ABI change). The 24 TEST_F still assert the same contracts.Rollback
git revertis sufficient. Test infrastructure + a single headerfriendline; no production-source semantics change.Risks
process_framerequires connection state that only the SETTINGS-handshake provides, calling it on an uninitialized client may segfault or hit asserts. Mitigation: read the dispatcher implementation carefully before choosing the entry point; if needed, add asetup_minimal_state_for_testing()helper exposed via friend.friend class Http2ClientHermeticTransportTestcouples production header to a test class name. Mitigation: use a single dedicated friend struct (e.g.struct Http2ClientTestAccess) in the production header, and have the test class delegate through that. This keeps the production header agnostic of test class names.process_frame's prerequisites that themselves call into branch-counted paths, Option C also fails and the coverage gap is structural. In that case, the right move is to lower the gate or accept the gap with a written justification (separate decision).