Coverage Report

Created: 2026-10-24 08:16

/work/testing/support/doubles/network_universe_test.cc
Line
Count
Source
1
#include "network_universe.hh"
2
3
#include <gtest/gtest.h>
4
5
#include <cstddef>
6
7
#include "fake_sockets.hh"
8
9
namespace tox::test {
10
namespace {
11
12
    class NetworkUniverseTest : public ::testing::Test {
13
    public:
14
        ~NetworkUniverseTest() override;
15
16
    protected:
17
        NetworkUniverse universe;
18
        FakeUdpSocket s1{universe};
19
        FakeUdpSocket s2{universe};
20
    };
21
22
9
    NetworkUniverseTest::~NetworkUniverseTest() = default;
23
24
    TEST_F(NetworkUniverseTest, LatencySimulation)
25
1
    {
26
1
        universe.set_latency(100);
27
28
1
        IP_Port s2_addr;
29
1
        ip_init(&s2_addr.ip, false);
30
1
        s2_addr.ip.ip.v4.uint32 = net_htonl(0x7F000001);
31
1
        s2_addr.port = net_htons(9004);
32
1
        s2.bind(&s2_addr);
33
34
1
        uint8_t data[] = "Ping";
35
1
        s1.sendto(data, 4, &s2_addr);
36
37
        // Time 0: packet sent but delivery time is 100
38
1
        universe.process_events(0);
39
1
        IP_Port from;
40
1
        uint8_t buf[10];
41
1
        ASSERT_EQ(s2.recvfrom(buf, 10, &from), -1);
42
43
        // Time 50: still not delivered
44
1
        universe.process_events(50);
45
1
        ASSERT_EQ(s2.recvfrom(buf, 10, &from), -1);
46
47
        // Time 100: delivered
48
1
        universe.process_events(100);
49
1
        ASSERT_EQ(s2.recvfrom(buf, 10, &from), 4);
50
1
    }
51
52
    TEST_F(NetworkUniverseTest, RoutesBasedOnIpAndPort)
53
1
    {
54
1
        IP ip1{}, ip2{};
55
1
        ip_init(&ip1, false);
56
1
        ip1.ip.v4.uint32 = net_htonl(0x01010101);
57
1
        ip_init(&ip2, false);
58
1
        ip2.ip.v4.uint32 = net_htonl(0x02020202);
59
60
1
        uint16_t port = 33445;
61
62
1
        FakeUdpSocket sock1{universe};
63
1
        FakeUdpSocket sock2{universe};
64
65
1
        sock1.set_ip(ip1);
66
1
        sock2.set_ip(ip2);
67
68
1
        IP_Port addr1{ip1, net_htons(port)};
69
1
        IP_Port addr2{ip2, net_htons(port)};
70
71
1
        ASSERT_EQ(sock1.bind(&addr1), 0);
72
1
        ASSERT_EQ(sock2.bind(&addr2), 0);
73
74
1
        const char *msg1 = "To IP 1";
75
1
        const char *msg2 = "To IP 2";
76
77
1
        FakeUdpSocket sender{universe};
78
1
        sender.sendto(reinterpret_cast<const uint8_t *>(msg1), strlen(msg1), &addr1);
79
1
        sender.sendto(reinterpret_cast<const uint8_t *>(msg2), strlen(msg2), &addr2);
80
81
1
        universe.process_events(0);
82
83
1
        uint8_t buf[100];
84
1
        IP_Port from;
85
86
1
        int len1 = sock1.recvfrom(buf, sizeof(buf), &from);
87
1
        ASSERT_GT(len1, 0);
88
1
        EXPECT_EQ(std::string(reinterpret_cast<char *>(buf), static_cast<std::size_t>(len1)), msg1);
89
90
1
        int len2 = sock2.recvfrom(buf, sizeof(buf), &from);
91
1
        ASSERT_GT(len2, 0);
92
1
        EXPECT_EQ(std::string(reinterpret_cast<char *>(buf), static_cast<std::size_t>(len2)), msg2);
93
1
    }
94
95
    TEST_F(NetworkUniverseTest, FindFreePortIsIpSpecific)
96
1
    {
97
1
        IP ip1{}, ip2{};
98
1
        ip_init(&ip1, false);
99
1
        ip1.ip.v4.uint32 = net_htonl(0x01010101);
100
1
        ip_init(&ip2, false);
101
1
        ip2.ip.v4.uint32 = net_htonl(0x02020202);
102
103
1
        FakeUdpSocket sock1{universe};
104
1
        sock1.set_ip(ip1);
105
106
1
        uint16_t port = 33445;
107
1
        IP_Port addr1{ip1, net_htons(port)};
108
1
        ASSERT_EQ(sock1.bind(&addr1), 0);
109
110
        // Port 33445 should be busy for ip1, but free for ip2
111
1
        EXPECT_EQ(universe.find_free_port(ip1, port), port + 1);
112
1
        EXPECT_EQ(universe.find_free_port(ip2, port), port);
113
1
    }
114
115
    TEST_F(NetworkUniverseTest, IpPortKeyEqualityRobustness)
116
1
    {
117
1
        IP ip1{}, ip2{};
118
1
        ip_init(&ip1, false);
119
1
        ip1.ip.v4.uint32 = net_htonl(0x7F000001);
120
1
        ip_init(&ip2, false);
121
1
        ip2.ip.v4.uint32 = net_htonl(0x7F000001);
122
123
1
        uint16_t port = 12345;
124
125
        // Force different garbage in the union padding for IPv4
126
        // The union is 16 bytes. IP4 is 4 bytes. Trailing 12 bytes are unused.
127
1
        memset(ip1.ip.v6.uint8 + 4, 0x11, 12);
128
1
        memset(ip2.ip.v6.uint8 + 4, 0x22, 12);
129
130
1
        NetworkUniverse::IP_Port_Key key1{ip1, port};
131
1
        NetworkUniverse::IP_Port_Key key2{ip2, port};
132
133
        // They should be considered equal (neither is less than the other)
134
1
        EXPECT_FALSE(key1 < key2);
135
1
        EXPECT_FALSE(key2 < key1);
136
137
        // Now try with different IPv4 but same garbage
138
1
        ip2.ip.v4.uint32 = net_htonl(0x7F000002);
139
1
        memset(ip2.ip.v6.uint8 + 4, 0x11, 12);  // same garbage as ip1
140
1
        NetworkUniverse::IP_Port_Key key3{ip2, port};
141
142
1
        EXPECT_TRUE(key1 < key3 || key3 < key1);
143
1
    }
144
145
    TEST_F(NetworkUniverseTest, IPv4v6Distinction)
146
1
    {
147
1
        IP ip1{}, ip2{};
148
1
        ip_init(&ip1, false);  // IPv4
149
1
        ip1.ip.v4.uint32 = net_htonl(0x01020304);
150
1
        ip_init(&ip2, true);  // IPv6
151
        // Set IPv6 bytes to match IPv4 bytes at the beginning
152
1
        memset(ip2.ip.v6.uint8, 0, 16);
153
1
        ip2.ip.v6.uint32[0] = net_htonl(0x01020304);
154
155
1
        uint16_t port = 12345;
156
157
1
        NetworkUniverse::IP_Port_Key key1{ip1, port};
158
1
        NetworkUniverse::IP_Port_Key key2{ip2, port};
159
160
        // Different families must be different even if underlying bytes happen to match
161
1
        EXPECT_TRUE(key1 < key2 || key2 < key1);
162
1
    }
163
164
    TEST_F(NetworkUniverseTest, ManyNodes)
165
1
    {
166
1
        const int num_nodes = 5000;
167
1
        struct NodeInfo {
168
1
            std::unique_ptr<FakeUdpSocket> sock;
169
1
            IP_Port addr;
170
1
        };
171
1
        std::vector<NodeInfo> nodes;
172
1
        nodes.reserve(num_nodes);
173
174
5.00k
        for (int i = 0; i < num_nodes; ++i) {
175
5.00k
            auto sock = std::make_unique<FakeUdpSocket>(universe);
176
5.00k
            IP ip{};
177
5.00k
            ip_init(&ip, false);
178
5.00k
            ip.ip.v4.uint32 = net_htonl(0x0A000000 + i);  // 10.0.x.y
179
5.00k
            sock->set_ip(ip);
180
181
5.00k
            IP_Port addr{ip, net_htons(33445)};
182
5.00k
            ASSERT_EQ(sock->bind(&addr), 0);
183
5.00k
            nodes.push_back({std::move(sock), addr});
184
5.00k
        }
185
186
1
        const int num_messages = 100;
187
        // Send messages from first num_messages to last num_messages nodes
188
101
        for (int i = 0; i < num_messages; ++i) {
189
100
            const char *msg = "Stress test";
190
100
            nodes[i].sock->sendto(reinterpret_cast<const uint8_t *>(msg), strlen(msg),
191
100
                &nodes[num_nodes - 1 - i].addr);
192
100
        }
193
194
1
        universe.process_events(0);
195
196
101
        for (int i = 0; i < num_messages; ++i) {
197
100
            uint8_t buf[100];
198
100
            IP_Port from;
199
100
            int len = nodes[num_nodes - 1 - i].sock->recvfrom(buf, sizeof(buf), &from);
200
100
            ASSERT_GT(len, 0);
201
100
            EXPECT_EQ(std::string(reinterpret_cast<char *>(buf), static_cast<std::size_t>(len)),
202
100
                "Stress test");
203
100
            EXPECT_TRUE(ip_equal(&from.ip, &nodes[i].addr.ip));
204
100
        }
205
1
    }
206
207
    TEST_F(NetworkUniverseTest, IpPadding)
208
1
    {
209
1
        IP ip1{};
210
1
        ip_init(&ip1, false);
211
1
        ip1.ip.v4.uint32 = net_htonl(0x7F000001);
212
213
1
        FakeUdpSocket sock{universe};
214
1
        sock.set_ip(ip1);
215
1
        IP_Port bind_addr{ip1, net_htons(12345)};
216
1
        ASSERT_EQ(sock.bind(&bind_addr), 0);
217
218
        // Create an address with garbage in the padding
219
1
        IP_Port target_addr;
220
1
        memset(&target_addr, 0xAA, sizeof(target_addr));
221
1
        ip_init(&target_addr.ip, false);
222
1
        target_addr.ip.ip.v4.uint32 = net_htonl(0x7F000001);
223
1
        target_addr.port = net_htons(12345);
224
225
1
        FakeUdpSocket sender{universe};
226
1
        const char *msg = "Padding test";
227
1
        sender.sendto(reinterpret_cast<const uint8_t *>(msg), strlen(msg), &target_addr);
228
229
1
        universe.process_events(0);
230
231
1
        uint8_t buf[100];
232
1
        IP_Port from;
233
1
        int len = sock.recvfrom(buf, sizeof(buf), &from);
234
235
        // If this fails, it means NetworkUniverse is not robust against padding garbage
236
1
        ASSERT_GT(len, 0);
237
1
        EXPECT_EQ(std::string(reinterpret_cast<char *>(buf), static_cast<std::size_t>(len)),
238
1
            "Padding test");
239
1
    }
240
241
    TEST_F(NetworkUniverseTest, TcpRoutingSpecificity)
242
1
    {
243
1
        IP ip1{};
244
1
        ip_init(&ip1, false);
245
1
        ip1.ip.v4.uint32 = net_htonl(0x0A000001);  // 10.0.0.1
246
247
1
        uint16_t port = 12345;
248
1
        IP_Port local_addr{ip1, net_htons(port)};
249
250
1
        FakeTcpSocket listen_sock(universe);
251
1
        listen_sock.set_ip(ip1);
252
1
        listen_sock.bind(&local_addr);
253
1
        listen_sock.listen(5);
254
255
1
        IP remote_ip{};
256
1
        ip_init(&remote_ip, false);
257
1
        remote_ip.ip.v4.uint32 = net_htonl(0x0A000002);  // 10.0.0.2
258
1
        IP_Port remote_addr{remote_ip, net_htons(33445)};
259
260
1
        auto established_sock = FakeTcpSocket::create_connected(universe, remote_addr, port);
261
1
        established_sock->set_ip(ip1);
262
1
        universe.bind_tcp(ip1, port, established_sock.get());
263
264
        // Send a data packet from remote to local
265
1
        Packet p{};
266
1
        p.from = remote_addr;
267
1
        p.to = local_addr;
268
1
        p.is_tcp = true;
269
1
        p.tcp_flags = 0x10;  // ACK (Data)
270
1
        const char *data = "Specific";
271
1
        p.data.assign(data, data + strlen(data));
272
273
1
        universe.send_packet(p);
274
1
        universe.process_events(0);
275
276
        // established_sock should have received it
277
1
        EXPECT_EQ(established_sock->recv_buffer_size(), strlen(data));
278
279
        // listen_sock should NOT have received it (it doesn't have a buffer, but it shouldn't have
280
        // been called) We can't easily check listen_sock wasn't called without mocks or checking
281
        // logs, but we can check that it didn't create a new pending connection.
282
1
        EXPECT_FALSE(listen_sock.is_readable());
283
1
    }
284
285
    TEST_F(NetworkUniverseTest, PacketOrdering)
286
1
    {
287
1
        IP ip1{}, ip2{};
288
1
        ip_init(&ip1, false);
289
1
        ip1.ip.v4.uint32 = net_htonl(0x0A000001);
290
1
        ip_init(&ip2, false);
291
1
        ip2.ip.v4.uint32 = net_htonl(0x0A000002);
292
293
1
        uint16_t port = 33445;
294
1
        IP_Port addr1{ip1, net_htons(port)};
295
1
        IP_Port addr2{ip2, net_htons(port)};
296
297
1
        FakeUdpSocket sock1{universe};
298
1
        sock1.set_ip(ip1);
299
1
        sock1.bind(&addr1);
300
301
1
        FakeUdpSocket sock2{universe};
302
1
        sock2.set_ip(ip2);
303
1
        sock2.bind(&addr2);
304
305
        // Send 10 packets with the same delivery time (global latency = 0)
306
11
        for (int i = 0; i < 10; ++i) {
307
10
            uint8_t data = static_cast<uint8_t>(i);
308
10
            sock1.sendto(&data, 1, &addr2);
309
10
        }
310
311
1
        universe.process_events(0);
312
313
        // They should be received in the exact order they were sent
314
11
        for (int i = 0; i < 10; ++i) {
315
10
            uint8_t buf[1];
316
10
            IP_Port from;
317
10
            ASSERT_EQ(sock2.recvfrom(buf, 1, &from), 1);
318
20
            EXPECT_EQ(buf[0], i) << "Packet " << i << " was delivered out of order";
319
10
        }
320
1
    }
321
322
}  // namespace
323
}  // namespace tox::test