Skip to content

Commit 4a59a35

Browse files
committed
Extend unit tests
1 parent a5a5cfd commit 4a59a35

6 files changed

Lines changed: 193 additions & 9 deletions

‎test/ip_address_tests.cpp‎

Lines changed: 53 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <cppcoro/net/ip_address.hpp>
77

88
#include "doctest/cppcoro_doctest.h"
9+
#include <string_view>
910

1011
TEST_SUITE_BEGIN("ip_address");
1112

@@ -31,15 +32,58 @@ TEST_CASE("to_string")
3132

3233
TEST_CASE("from_string")
3334
{
34-
CHECK(ip_address::from_string("") == std::nullopt);
35-
CHECK(ip_address::from_string("foo") == std::nullopt);
36-
CHECK(ip_address::from_string(" 192.168.0.1") == std::nullopt);
37-
CHECK(ip_address::from_string("192.168.0.1asdf") == std::nullopt);
38-
39-
CHECK(ip_address::from_string("192.168.0.1") == ipv4_address(192, 168, 0, 1));
40-
CHECK(ip_address::from_string("::192.168.0.1") == ipv6_address(0, 0, 0, 0, 0, 0, 0xc0a8, 0x1));
41-
CHECK(ip_address::from_string("aabb:ccdd:11:2233:102:304:506:708") ==
42-
ipv6_address{ 0xAABBCCDD00112233, 0x0102030405060708 });
35+
CHECK(ip_address::from_string("") == std::nullopt);
36+
CHECK(ip_address::from_string("foo") == std::nullopt);
37+
CHECK(ip_address::from_string(" 192.168.0.1") == std::nullopt);
38+
CHECK(ip_address::from_string("192.168.0.1asdf") == std::nullopt);
39+
40+
CHECK(ip_address::from_string("192.168.0.1") == ipv4_address(192, 168, 0, 1));
41+
CHECK(ip_address::from_string("::192.168.0.1") == ipv6_address(0, 0, 0, 0, 0, 0, 0xc0a8, 0x1));
42+
CHECK(ip_address::from_string("aabb:ccdd:11:2233:102:304:506:708") ==
43+
ipv6_address{ 0xAABBCCDD00112233, 0x0102030405060708 });
44+
}
45+
46+
TEST_CASE("round-trip and ordering")
47+
{
48+
// Round-trip: to_string(from_string(s)) yields canonical string, and parsing back preserves value
49+
auto check_round_trip = [](std::string_view s) {
50+
auto p = ip_address::from_string(s);
51+
REQUIRE(p.has_value());
52+
auto canon = p->to_string();
53+
auto p2 = ip_address::from_string(canon);
54+
REQUIRE(p2.has_value());
55+
CHECK(*p == *p2);
56+
// Canonical string should be stable
57+
CHECK(p2->to_string() == canon);
58+
};
59+
60+
// IPv4
61+
check_round_trip("0.0.0.0");
62+
check_round_trip("255.255.255.255");
63+
64+
// IPv6 (mixed case input should parse and normalise to lower-case hex without leading zeros)
65+
check_round_trip("::");
66+
check_round_trip("::1");
67+
check_round_trip("FFFF:0000:0000:0000:0000:0000:0000:0001");
68+
check_round_trip("::192.168.0.1");
69+
70+
// Ordering: IPv4 sorts less than IPv6
71+
ip_address v4 = ipv4_address{127, 0, 0, 1};
72+
ip_address v6 = ipv6_address{0, 0, 0, 0, 0, 0, 0, 1}; // ::1
73+
CHECK(v4 < v6);
74+
75+
// Ordering within same family
76+
CHECK(ipv4_address{1, 1, 1, 1} < ipv4_address{1, 1, 1, 2});
77+
CHECK(ipv6_address{0, 0, 0, 0, 0, 0, 0, 1} < ipv6_address{0, 0, 0, 0, 0, 0, 0, 2});
78+
79+
// Reject trailing/leading whitespace
80+
CHECK(ip_address::from_string("192.168.0.1 ") == std::nullopt);
81+
CHECK(ip_address::from_string("\t::1") == std::nullopt);
82+
83+
// Reject malformed IPv6
84+
CHECK(ip_address::from_string("::::") == std::nullopt);
85+
CHECK(ip_address::from_string("gggg::1") == std::nullopt);
86+
CHECK(ip_address::from_string("12345::1") == std::nullopt);
4387
}
4488

4589
TEST_SUITE_END();

‎test/ip_endpoint_tests.cpp‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,5 +52,35 @@ TEST_CASE("from_string" * doctest::skip{ isMsvc15_5X86Optimised })
5252
443 });
5353
}
5454

55+
TEST_CASE("round-trip and ordering" * doctest::skip{ isMsvc15_5X86Optimised })
56+
{
57+
// Round-trip
58+
auto check_round_trip = [](const char* s) {
59+
auto p = ip_endpoint::from_string(s);
60+
REQUIRE(p.has_value());
61+
auto canon = p->to_string();
62+
auto p2 = ip_endpoint::from_string(canon);
63+
REQUIRE(p2.has_value());
64+
CHECK(*p == *p2);
65+
CHECK(p2->to_string() == canon);
66+
};
67+
68+
check_round_trip("192.168.2.254:80");
69+
check_round_trip("[2001:db8:85a3::8a2e:370:7334]:22");
70+
71+
// Invalid ports/whitespace
72+
CHECK(ip_endpoint::from_string("192.168.1.1:65536") == std::nullopt);
73+
CHECK(ip_endpoint::from_string("[::1]:65536") == std::nullopt);
74+
CHECK(ip_endpoint::from_string(" 192.168.1.1:80") == std::nullopt);
75+
CHECK(ip_endpoint::from_string("[::1]:80 ") == std::nullopt);
76+
77+
// Ordering: IPv4 < IPv6, and within family compare address/port
78+
ip_endpoint v4a = ipv4_endpoint{ ipv4_address{127,0,0,1}, 1 };
79+
ip_endpoint v4b = ipv4_endpoint{ ipv4_address{127,0,0,1}, 2 };
80+
ip_endpoint v6 = ipv6_endpoint{ ipv6_address{0,0,0,0,0,0,0,1}, 1 };
81+
CHECK(v4a < v6);
82+
CHECK(v4a < v4b);
83+
}
84+
5585
TEST_SUITE_END();
5686

‎test/ipv4_address_tests.cpp‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,4 +87,31 @@ TEST_CASE("from_string")
8787
CHECK(ipv4_address::from_string("0.0.0.0") == ipv4_address(0, 0, 0, 0));
8888
CHECK(ipv4_address::from_string("1.2.3.4") == ipv4_address(1, 2, 3, 4));
8989
}
90+
91+
TEST_CASE("round-trip and ordering")
92+
{
93+
// Round-trip canonicalisation via to_string()/from_string()
94+
auto check_round_trip = [](const char* s) {
95+
auto p = ipv4_address::from_string(s);
96+
REQUIRE(p.has_value());
97+
auto canon = p->to_string();
98+
auto p2 = ipv4_address::from_string(canon);
99+
REQUIRE(p2.has_value());
100+
CHECK(*p == *p2);
101+
CHECK(p2->to_string() == canon); // stable
102+
};
103+
104+
check_round_trip("0.0.0.0");
105+
check_round_trip("255.255.255.255");
106+
check_round_trip("192.168.0.1");
107+
108+
// Single-integer form canonicals to dotted-decimal
109+
auto p = ipv4_address::from_string("1");
110+
REQUIRE(p.has_value());
111+
CHECK(p->to_string() == std::string("0.0.0.1"));
112+
113+
// Ordering within IPv4 space
114+
CHECK(ipv4_address{1,2,3,4} < ipv4_address{1,2,3,5});
115+
CHECK(ipv4_address{10,0,0,1} < ipv4_address{10,0,0,2});
116+
}
90117
TEST_SUITE_END();

‎test/ipv4_endpoint_tests.cpp‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,32 @@ TEST_CASE("from_string")
3030
ipv4_endpoint{ ipv4_address{ 192, 168, 2, 254 }, 80 });
3131
}
3232

33+
TEST_CASE("round-trip, boundary ports and invalid inputs")
34+
{
35+
// Round-trip canonicalisation
36+
auto check_round_trip = [](const char* s) {
37+
auto p = ipv4_endpoint::from_string(s);
38+
REQUIRE(p.has_value());
39+
auto canon = p->to_string();
40+
auto p2 = ipv4_endpoint::from_string(canon);
41+
REQUIRE(p2.has_value());
42+
CHECK(*p == *p2);
43+
CHECK(p2->to_string() == canon);
44+
};
45+
46+
check_round_trip("192.168.2.254:80");
47+
check_round_trip("0.0.0.0:0");
48+
check_round_trip("255.255.255.255:65535");
49+
50+
// Boundary ports
51+
CHECK(ipv4_endpoint{ ipv4_address{0,0,0,0}, 0 }.to_string() == std::string("0.0.0.0:0"));
52+
CHECK(ipv4_endpoint{ ipv4_address{255,255,255,255}, 65535 }.to_string() == std::string("255.255.255.255:65535"));
53+
54+
// Invalid: whitespace and out-of-range ports
55+
CHECK(ipv4_endpoint::from_string(" 192.168.0.1:80") == std::nullopt);
56+
CHECK(ipv4_endpoint::from_string("192.168.0.1:80 ") == std::nullopt);
57+
CHECK(ipv4_endpoint::from_string("192.168.0.1:65536") == std::nullopt);
58+
CHECK(ipv4_endpoint::from_string("192.168.0.1:-1") == std::nullopt);
59+
}
60+
3361
TEST_SUITE_END();

‎test/ipv6_address_tests.cpp‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,35 @@ TEST_CASE("from_string")
109109
ipv6_address(0x20010db885a308d3, 0x13198a2e03707348));
110110
}
111111

112+
TEST_CASE("round-trip and normalization")
113+
{
114+
auto check_round_trip = [](const char* s) {
115+
auto p = ipv6_address::from_string(s);
116+
REQUIRE(p.has_value());
117+
auto canon = p->to_string();
118+
auto p2 = ipv6_address::from_string(canon);
119+
REQUIRE(p2.has_value());
120+
CHECK(*p == *p2);
121+
CHECK(p2->to_string() == canon);
122+
};
123+
124+
// Basic
125+
check_round_trip("::");
126+
check_round_trip("::1");
127+
check_round_trip("FFFF:0000:0000:0000:0000:0000:0000:0001");
128+
129+
// Mixed case input should normalise to lower-case
130+
check_round_trip("AbCd:Ef01::");
131+
132+
// IPv4 interop
133+
check_round_trip("::ffff:192.168.0.1");
134+
135+
// Ordering stability already covered elsewhere; here just ensure canonical contraction is stable
136+
auto p = ipv6_address::from_string("0001:0010:0100:1000::");
137+
REQUIRE(p.has_value());
138+
CHECK(p->to_string() == std::string("1:10:100:1000::"));
139+
}
140+
112141
TEST_CASE("from_string IPv4 interop format")
113142
{
114143
CHECK(

‎test/ipv6_endpoint_tests.cpp‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,30 @@ TEST_CASE("from_string" * doctest::skip{ isMsvc15_5X86Optimised })
5252
ipv6_endpoint{ ipv6_address{ 0x20010db885a30000, 0x00008a2e03707334 }, 65535 });
5353
}
5454

55+
TEST_CASE("round-trip and invalid ports/brackets" * doctest::skip{ isMsvc15_5X86Optimised })
56+
{
57+
// Round-trip canonicalisation
58+
auto check_round_trip = [](const char* s) {
59+
auto p = ipv6_endpoint::from_string(s);
60+
REQUIRE(p.has_value());
61+
auto canon = p->to_string();
62+
auto p2 = ipv6_endpoint::from_string(canon);
63+
REQUIRE(p2.has_value());
64+
CHECK(*p == *p2);
65+
CHECK(p2->to_string() == canon);
66+
};
67+
68+
check_round_trip("[::1]:1");
69+
check_round_trip("[2001:db8:85a3::8a2e:370:7334]:22");
70+
71+
// Invalid port ranges and whitespace
72+
CHECK(ipv6_endpoint::from_string("[::1]:65536") == std::nullopt);
73+
CHECK(ipv6_endpoint::from_string(" [::1]:80") == std::nullopt);
74+
CHECK(ipv6_endpoint::from_string("[::1]:80 ") == std::nullopt);
75+
76+
// Malformed brackets
77+
CHECK(ipv6_endpoint::from_string("[[::1]]:80") == std::nullopt);
78+
CHECK(ipv6_endpoint::from_string("[::1:80") == std::nullopt);
79+
}
80+
5581
TEST_SUITE_END();

0 commit comments

Comments
 (0)