1 // Copyright (c) 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifndef QUICHE_QUIC_CORE_BATCH_WRITER_QUIC_BATCH_WRITER_TEST_H_
6 #define QUICHE_QUIC_CORE_BATCH_WRITER_QUIC_BATCH_WRITER_TEST_H_
7
8 #include <sys/socket.h>
9 #include <sys/types.h>
10
11 #include <cstddef>
12 #include <iostream>
13 #include <utility>
14
15 #include "absl/base/optimization.h"
16 #include "quiche/quic/core/batch_writer/quic_batch_writer_base.h"
17 #include "quiche/quic/core/quic_udp_socket.h"
18 #include "quiche/quic/platform/api/quic_test.h"
19
20 namespace quic {
21 namespace test {
22
IsAddressFamilySupported(int address_family)23 static bool IsAddressFamilySupported(int address_family) {
24 static auto check_function = [](int address_family) {
25 int fd = socket(address_family, SOCK_STREAM, 0);
26 if (fd < 0) {
27 QUIC_LOG(ERROR) << "address_family not supported: " << address_family
28 << ", error: " << strerror(errno);
29 EXPECT_EQ(EAFNOSUPPORT, errno);
30 return false;
31 }
32 close(fd);
33 return true;
34 };
35
36 if (address_family == AF_INET) {
37 static const bool ipv4_supported = check_function(AF_INET);
38 return ipv4_supported;
39 }
40
41 static const bool ipv6_supported = check_function(AF_INET6);
42 return ipv6_supported;
43 }
44
CreateSocket(int family,QuicSocketAddress * address,int * fd)45 static bool CreateSocket(int family, QuicSocketAddress* address, int* fd) {
46 if (family == AF_INET) {
47 *address = QuicSocketAddress(QuicIpAddress::Loopback4(), 0);
48 } else {
49 QUICHE_DCHECK_EQ(family, AF_INET6);
50 *address = QuicSocketAddress(QuicIpAddress::Loopback6(), 0);
51 }
52
53 QuicUdpSocketApi socket_api;
54 *fd = socket_api.Create(family,
55 /*receive_buffer_size=*/kDefaultSocketReceiveBuffer,
56 /*send_buffer_size=*/kDefaultSocketReceiveBuffer);
57 if (*fd < 0) {
58 QUIC_LOG(ERROR) << "CreateSocket() failed: " << strerror(errno);
59 return false;
60 }
61 socket_api.EnableDroppedPacketCount(*fd);
62
63 if (!socket_api.Bind(*fd, *address)) {
64 QUIC_LOG(ERROR) << "Bind failed: " << strerror(errno);
65 return false;
66 }
67
68 if (address->FromSocket(*fd) != 0) {
69 QUIC_LOG(ERROR) << "Unable to get self address. Error: "
70 << strerror(errno);
71 return false;
72 }
73 return true;
74 }
75
76 struct QuicUdpBatchWriterIOTestParams;
77 class QUICHE_EXPORT QuicUdpBatchWriterIOTestDelegate {
78 public:
~QuicUdpBatchWriterIOTestDelegate()79 virtual ~QuicUdpBatchWriterIOTestDelegate() {}
80
ShouldSkip(const QuicUdpBatchWriterIOTestParams &)81 virtual bool ShouldSkip(const QuicUdpBatchWriterIOTestParams& /*params*/) {
82 return false;
83 }
84
85 virtual void ResetWriter(int fd) = 0;
86
87 virtual QuicUdpBatchWriter* GetWriter() = 0;
88 };
89
90 struct QUICHE_EXPORT QuicUdpBatchWriterIOTestParams {
91 // Use shared_ptr because gtest makes copies of test params.
92 std::shared_ptr<QuicUdpBatchWriterIOTestDelegate> delegate;
93 int address_family;
94 int data_size;
95 int packet_size;
96
97 QUICHE_EXPORT friend std::ostream& operator<<(
98 std::ostream& os, const QuicUdpBatchWriterIOTestParams& p) {
99 os << "{ address_family: " << p.address_family
100 << " data_size: " << p.data_size << " packet_size: " << p.packet_size
101 << " }";
102 return os;
103 }
104 };
105
106 template <class QuicUdpBatchWriterIOTestDelegateT>
107 static std::vector<QuicUdpBatchWriterIOTestParams>
MakeQuicBatchWriterTestParams()108 MakeQuicBatchWriterTestParams() {
109 static_assert(std::is_base_of<QuicUdpBatchWriterIOTestDelegate,
110 QuicUdpBatchWriterIOTestDelegateT>::value,
111 "<QuicUdpBatchWriterIOTestDelegateT> needs to derive from "
112 "QuicUdpBatchWriterIOTestDelegate");
113
114 std::vector<QuicUdpBatchWriterIOTestParams> params;
115 for (int address_family : {AF_INET, AF_INET6}) {
116 for (int data_size : {1, 150, 1500, 15000, 64000, 512 * 1024}) {
117 for (int packet_size : {1, 50, 1350, 1452}) {
118 if (packet_size <= data_size && (data_size / packet_size < 2000)) {
119 params.push_back(
120 {std::make_unique<QuicUdpBatchWriterIOTestDelegateT>(),
121 address_family, data_size, packet_size});
122 }
123 }
124 }
125 }
126 return params;
127 }
128
129 // QuicUdpBatchWriterIOTest is a value parameterized test fixture that can be
130 // used by tests of derived classes of QuicUdpBatchWriter, to verify basic
131 // packet IO capabilities.
132 class QUICHE_EXPORT QuicUdpBatchWriterIOTest
133 : public QuicTestWithParam<QuicUdpBatchWriterIOTestParams> {
134 protected:
QuicUdpBatchWriterIOTest()135 QuicUdpBatchWriterIOTest()
136 : address_family_(GetParam().address_family),
137 data_size_(GetParam().data_size),
138 packet_size_(GetParam().packet_size),
139 self_socket_(-1),
140 peer_socket_(-1) {
141 QUIC_LOG(INFO) << "QuicUdpBatchWriterIOTestParams: " << GetParam();
142 EXPECT_TRUE(address_family_ == AF_INET || address_family_ == AF_INET6);
143 EXPECT_LE(packet_size_, data_size_);
144 EXPECT_LE(packet_size_, sizeof(packet_buffer_));
145 }
146
~QuicUdpBatchWriterIOTest()147 ~QuicUdpBatchWriterIOTest() override {
148 if (self_socket_ > 0) {
149 close(self_socket_);
150 }
151 if (peer_socket_ > 0) {
152 close(peer_socket_);
153 }
154 }
155
156 // Whether this test should be skipped. A test is passed if skipped.
157 // A test can be skipped when e.g. it exercises a kernel feature that is not
158 // available on the system.
ShouldSkip()159 bool ShouldSkip() {
160 if (!IsAddressFamilySupported(address_family_)) {
161 QUIC_LOG(WARNING)
162 << "Test skipped since address_family is not supported.";
163 return true;
164 }
165
166 return GetParam().delegate->ShouldSkip(GetParam());
167 }
168
169 // Initialize a test.
170 // To fail the test in Initialize, use ASSERT_xx macros.
Initialize()171 void Initialize() {
172 ASSERT_TRUE(CreateSocket(address_family_, &self_address_, &self_socket_));
173 ASSERT_TRUE(CreateSocket(address_family_, &peer_address_, &peer_socket_));
174
175 QUIC_DLOG(INFO) << "Self address: " << self_address_.ToString() << ", fd "
176 << self_socket_;
177 QUIC_DLOG(INFO) << "Peer address: " << peer_address_.ToString() << ", fd "
178 << peer_socket_;
179 GetParam().delegate->ResetWriter(self_socket_);
180 }
181
GetWriter()182 QuicUdpBatchWriter* GetWriter() { return GetParam().delegate->GetWriter(); }
183
ValidateWrite()184 void ValidateWrite() {
185 char this_packet_content = '\0';
186 int this_packet_size;
187 int num_writes = 0;
188 size_t bytes_flushed = 0;
189 WriteResult result;
190
191 for (size_t bytes_sent = 0; bytes_sent < data_size_;
192 bytes_sent += this_packet_size, ++this_packet_content) {
193 this_packet_size = std::min(packet_size_, data_size_ - bytes_sent);
194 memset(&packet_buffer_[0], this_packet_content, this_packet_size);
195
196 result = GetWriter()->WritePacket(&packet_buffer_[0], this_packet_size,
197 self_address_.host(), peer_address_,
198 nullptr, QuicPacketWriterParams());
199
200 ASSERT_EQ(WRITE_STATUS_OK, result.status) << strerror(result.error_code);
201 bytes_flushed += result.bytes_written;
202 ++num_writes;
203
204 QUIC_DVLOG(1) << "[write #" << num_writes
205 << "] this_packet_size: " << this_packet_size
206 << ", total_bytes_sent: " << bytes_sent + this_packet_size
207 << ", bytes_flushed: " << bytes_flushed
208 << ", pkt content:" << std::hex << int(this_packet_content);
209 }
210
211 result = GetWriter()->Flush();
212 ASSERT_EQ(WRITE_STATUS_OK, result.status) << strerror(result.error_code);
213 bytes_flushed += result.bytes_written;
214 ASSERT_EQ(data_size_, bytes_flushed);
215
216 QUIC_LOG(INFO) << "Sent " << data_size_ << " bytes in " << num_writes
217 << " writes.";
218 }
219
ValidateRead()220 void ValidateRead() {
221 char this_packet_content = '\0';
222 int this_packet_size;
223 int packets_received = 0;
224 for (size_t bytes_received = 0; bytes_received < data_size_;
225 bytes_received += this_packet_size, ++this_packet_content) {
226 this_packet_size = std::min(packet_size_, data_size_ - bytes_received);
227 SCOPED_TRACE(testing::Message()
228 << "Before ReadPacket: bytes_received=" << bytes_received
229 << ", this_packet_size=" << this_packet_size);
230
231 QuicUdpSocketApi::ReadPacketResult result;
232 result.packet_buffer = {&packet_buffer_[0], sizeof(packet_buffer_)};
233 result.control_buffer = {&control_buffer_[0], sizeof(control_buffer_)};
234 ASSERT_TRUE(QuicUdpSocketApi().WaitUntilReadable(
235 peer_socket_, QuicTime::Delta::FromSeconds(1)));
236 QuicUdpSocketApi().ReadPacket(
237 peer_socket_,
238 quic::QuicUdpPacketInfoBitMask({QuicUdpPacketInfoBit::V4_SELF_IP,
239 QuicUdpPacketInfoBit::V6_SELF_IP,
240 QuicUdpPacketInfoBit::PEER_ADDRESS}),
241 &result);
242 ASSERT_TRUE(result.ok);
243 ASSERT_TRUE(
244 result.packet_info.HasValue(QuicUdpPacketInfoBit::PEER_ADDRESS));
245 QuicSocketAddress read_peer_address = result.packet_info.peer_address();
246 QuicIpAddress read_self_address = read_peer_address.host().IsIPv6()
247 ? result.packet_info.self_v6_ip()
248 : result.packet_info.self_v4_ip();
249
250 EXPECT_EQ(read_self_address, peer_address_.host());
251 EXPECT_EQ(read_peer_address, self_address_);
252 for (int i = 0; i < this_packet_size; ++i) {
253 EXPECT_EQ(this_packet_content, packet_buffer_[i]);
254 }
255 packets_received += this_packet_size;
256 }
257
258 QUIC_LOG(INFO) << "Received " << data_size_ << " bytes in "
259 << packets_received << " packets.";
260 }
261
262 QuicSocketAddress self_address_;
263 QuicSocketAddress peer_address_;
264 ABSL_CACHELINE_ALIGNED char packet_buffer_[1500];
265 ABSL_CACHELINE_ALIGNED char
266 control_buffer_[kDefaultUdpPacketControlBufferSize];
267 int address_family_;
268 const size_t data_size_;
269 const size_t packet_size_;
270 int self_socket_;
271 int peer_socket_;
272 };
273
TEST_P(QuicUdpBatchWriterIOTest,WriteAndRead)274 TEST_P(QuicUdpBatchWriterIOTest, WriteAndRead) {
275 if (ShouldSkip()) {
276 return;
277 }
278
279 Initialize();
280
281 ValidateWrite();
282 ValidateRead();
283 }
284
285 } // namespace test
286 } // namespace quic
287
288 #endif // QUICHE_QUIC_CORE_BATCH_WRITER_QUIC_BATCH_WRITER_TEST_H_
289