Skip to content

Commit 2f3aee5

Browse files
authored
test(quic): expand branch coverage for transport_params (#1036)
Add quic_transport_params_branch_test.cpp targeting the logical-branch gaps identified by the latest coverage.yml run on develop: - decode() per-case malformed-varint error returns for initial_max_stream_data_bidi_remote, initial_max_stream_data_uni, and initial_max_streams_uni (transport_params.cpp lines 409, 423, 451) — previously not individually exercised. - encode() emission of server-only retry_source_connection_id (transport_params.cpp lines 87-89) — existing tests set this field for validate() rejection but never round-tripped it through encode. Tests are hermetic (no network, filesystem, or sleeps) and reuse the make_param helper pattern from quic_transport_params_coverage_test. Refs #953 Closes #1024
1 parent 9fce777 commit 2f3aee5

2 files changed

Lines changed: 220 additions & 0 deletions

File tree

tests/CMakeLists.txt

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3510,6 +3510,39 @@ network_gtest_discover_tests(network_quic_transport_params_coverage_test
35103510
)
35113511
message(STATUS "QUIC transport params coverage unit tests enabled")
35123512

3513+
##################################################
3514+
# QUIC Transport Params Branch Coverage Tests (Issue #1024)
3515+
##################################################
3516+
3517+
add_executable(network_quic_transport_params_branch_test
3518+
unit/quic_transport_params_branch_test.cpp
3519+
)
3520+
3521+
target_link_libraries(network_quic_transport_params_branch_test PRIVATE
3522+
network_system
3523+
GTest::gtest
3524+
GTest::gtest_main
3525+
Threads::Threads
3526+
)
3527+
3528+
setup_asio_integration(network_quic_transport_params_branch_test)
3529+
3530+
if(COMMON_SYSTEM_INCLUDE_DIR)
3531+
target_include_directories(network_quic_transport_params_branch_test PRIVATE ${COMMON_SYSTEM_INCLUDE_DIR})
3532+
target_compile_definitions(network_quic_transport_params_branch_test PRIVATE WITH_COMMON_SYSTEM)
3533+
endif()
3534+
3535+
set_target_properties(network_quic_transport_params_branch_test PROPERTIES
3536+
CXX_STANDARD 20
3537+
CXX_STANDARD_REQUIRED ON
3538+
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
3539+
)
3540+
3541+
network_gtest_discover_tests(network_quic_transport_params_branch_test
3542+
DISCOVERY_TIMEOUT 60
3543+
)
3544+
message(STATUS "QUIC transport params branch coverage unit tests enabled (Issue #1024)")
3545+
35133546
##################################################
35143547
# QUIC Packet Unit Tests (Issue #954)
35153548
##################################################
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
/*****************************************************************************
2+
BSD 3-Clause License
3+
4+
Copyright (c) 2025, kcenon
5+
All rights reserved.
6+
*****************************************************************************/
7+
8+
// Branch-coverage gap tests for src/protocols/quic/transport_params.cpp.
9+
//
10+
// The existing quic_transport_params_test.cpp and
11+
// quic_transport_params_coverage_test.cpp suites bring line coverage for this
12+
// file above the issue #1024 target. The remaining uncovered logical branches
13+
// (per the latest coverage.yml run on develop) are concentrated in:
14+
//
15+
// * encode() server-only retry_source_connection_id emission path
16+
// (transport_params.cpp:87-89)
17+
// * decode() per-case malformed-varint error returns for parameters
18+
// not previously truncation-tested individually:
19+
// initial_max_stream_data_bidi_remote (line 409)
20+
// initial_max_stream_data_uni (line 423)
21+
// initial_max_streams_uni (line 451)
22+
//
23+
// These tests target those branches and add a small set of round-trip
24+
// assertions covering the server-only encode emission so the new path is
25+
// exercised end-to-end.
26+
//
27+
// Part of epic #953 (expand unit test coverage from 40% to 80%). Sub-issue
28+
// #1024 follow-up after #1015.
29+
30+
#include "internal/protocols/quic/transport_params.h"
31+
#include "internal/protocols/quic/varint.h"
32+
#include <gtest/gtest.h>
33+
34+
#include <array>
35+
#include <cstdint>
36+
#include <span>
37+
#include <vector>
38+
39+
namespace quic = kcenon::network::protocols::quic;
40+
41+
namespace {
42+
43+
auto make_param(uint64_t id, std::span<const uint8_t> value) -> std::vector<uint8_t>
44+
{
45+
auto id_bytes = quic::varint::encode(id);
46+
auto len_bytes = quic::varint::encode(value.size());
47+
std::vector<uint8_t> out;
48+
out.reserve(id_bytes.size() + len_bytes.size() + value.size());
49+
out.insert(out.end(), id_bytes.begin(), id_bytes.end());
50+
out.insert(out.end(), len_bytes.begin(), len_bytes.end());
51+
out.insert(out.end(), value.begin(), value.end());
52+
return out;
53+
}
54+
55+
auto as_span(const std::vector<uint8_t>& v) -> std::span<const uint8_t>
56+
{
57+
return std::span<const uint8_t>(v.data(), v.size());
58+
}
59+
60+
} // namespace
61+
62+
// ============================================================================
63+
// Per-case malformed-varint coverage for the three decode branches not
64+
// individually exercised by quic_transport_params_coverage_test.cpp.
65+
// ============================================================================
66+
67+
class TransportParamsDecodeMalformedVarintGapTest : public ::testing::Test
68+
{
69+
};
70+
71+
TEST_F(TransportParamsDecodeMalformedVarintGapTest,
72+
InitialMaxStreamDataBidiRemoteTruncatedVarint)
73+
{
74+
// Length says 1 byte; value is the prefix of a 2-byte varint (0x40)
75+
// without its trailing byte -> inner varint::decode() must fail and the
76+
// switch arm at line 409 must take its decode_error branch.
77+
std::vector<uint8_t> truncated{0x40};
78+
auto buf = make_param(
79+
quic::transport_param_id::initial_max_stream_data_bidi_remote,
80+
std::span<const uint8_t>(truncated.data(), truncated.size()));
81+
EXPECT_TRUE(quic::transport_parameters::decode(as_span(buf)).is_err());
82+
}
83+
84+
TEST_F(TransportParamsDecodeMalformedVarintGapTest,
85+
InitialMaxStreamDataUniTruncatedVarint)
86+
{
87+
// 4-byte varint prefix (0x80) with only 3 bytes delivered.
88+
std::vector<uint8_t> truncated{0x80, 0x00, 0x00};
89+
auto buf = make_param(
90+
quic::transport_param_id::initial_max_stream_data_uni,
91+
std::span<const uint8_t>(truncated.data(), truncated.size()));
92+
EXPECT_TRUE(quic::transport_parameters::decode(as_span(buf)).is_err());
93+
}
94+
95+
TEST_F(TransportParamsDecodeMalformedVarintGapTest,
96+
InitialMaxStreamsUniTruncatedVarint)
97+
{
98+
// 8-byte varint prefix (0xC0) with only 7 bytes of payload.
99+
std::vector<uint8_t> truncated{0xC0, 0, 0, 0, 0, 0, 0};
100+
auto buf = make_param(
101+
quic::transport_param_id::initial_max_streams_uni,
102+
std::span<const uint8_t>(truncated.data(), truncated.size()));
103+
EXPECT_TRUE(quic::transport_parameters::decode(as_span(buf)).is_err());
104+
}
105+
106+
// ============================================================================
107+
// encode() emission of server-only retry_source_connection_id (line 87).
108+
// The existing tests set this field for validate() rejection paths but never
109+
// invoke encode() with it set, so the optional<>::operator bool true branch
110+
// at line 87 and the inner append_parameter call at line 89 are unexercised.
111+
// ============================================================================
112+
113+
class TransportParamsEncodeRetrySourceTest : public ::testing::Test
114+
{
115+
};
116+
117+
TEST_F(TransportParamsEncodeRetrySourceTest, EmitsParameterWhenPresent)
118+
{
119+
quic::transport_parameters p;
120+
std::array<uint8_t, 8> cid_bytes{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88};
121+
p.retry_source_connection_id =
122+
quic::connection_id(std::span<const uint8_t>(cid_bytes.data(), cid_bytes.size()));
123+
124+
auto encoded = p.encode();
125+
126+
// Locate the parameter id byte. retry_source_connection_id == 0x10, fits
127+
// into a single varint byte.
128+
bool found = false;
129+
for (size_t i = 0; i + 1 + cid_bytes.size() < encoded.size(); ++i)
130+
{
131+
if (encoded[i] == quic::transport_param_id::retry_source_connection_id)
132+
{
133+
// Length prefix is a single byte equal to cid_bytes.size().
134+
if (encoded[i + 1] == cid_bytes.size())
135+
{
136+
found = true;
137+
for (size_t k = 0; k < cid_bytes.size(); ++k)
138+
{
139+
EXPECT_EQ(encoded[i + 2 + k], cid_bytes[k]);
140+
}
141+
break;
142+
}
143+
}
144+
}
145+
EXPECT_TRUE(found);
146+
}
147+
148+
TEST_F(TransportParamsEncodeRetrySourceTest, RoundTripsAsServerParameter)
149+
{
150+
quic::transport_parameters p;
151+
std::array<uint8_t, 4> cid_bytes{0xDE, 0xAD, 0xBE, 0xEF};
152+
p.retry_source_connection_id =
153+
quic::connection_id(std::span<const uint8_t>(cid_bytes.data(), cid_bytes.size()));
154+
155+
auto encoded = p.encode();
156+
auto decoded = quic::transport_parameters::decode(as_span(encoded));
157+
ASSERT_TRUE(decoded.is_ok());
158+
ASSERT_TRUE(decoded.value().retry_source_connection_id.has_value());
159+
EXPECT_EQ(decoded.value().retry_source_connection_id->length(), cid_bytes.size());
160+
auto data = decoded.value().retry_source_connection_id->data();
161+
for (size_t i = 0; i < cid_bytes.size(); ++i)
162+
{
163+
EXPECT_EQ(data[i], cid_bytes[i]);
164+
}
165+
}
166+
167+
TEST_F(TransportParamsEncodeRetrySourceTest, RetrySourceCidAtMaxLength)
168+
{
169+
// Max connection-id length is 20 bytes; encoded size is id (1) + len (1)
170+
// + 20 = 22 bytes for this single parameter.
171+
quic::transport_parameters p;
172+
std::array<uint8_t, 20> cid_bytes{};
173+
for (size_t i = 0; i < cid_bytes.size(); ++i)
174+
{
175+
cid_bytes[i] = static_cast<uint8_t>(0xA0 + i);
176+
}
177+
p.retry_source_connection_id =
178+
quic::connection_id(std::span<const uint8_t>(cid_bytes.data(), cid_bytes.size()));
179+
180+
auto encoded = p.encode();
181+
auto decoded = quic::transport_parameters::decode(as_span(encoded));
182+
ASSERT_TRUE(decoded.is_ok());
183+
ASSERT_TRUE(decoded.value().retry_source_connection_id.has_value());
184+
EXPECT_EQ(decoded.value().retry_source_connection_id->length(), 20u);
185+
// Server-side validate must accept retry_source_connection_id.
186+
EXPECT_TRUE(decoded.value().validate(true).is_ok());
187+
}

0 commit comments

Comments
 (0)