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 #include "quiche/quic/core/batch_writer/quic_batch_writer_buffer.h"
6
7 #include <memory>
8 #include <string>
9
10 #include "quiche/quic/core/quic_constants.h"
11 #include "quiche/quic/platform/api/quic_ip_address.h"
12 #include "quiche/quic/platform/api/quic_socket_address.h"
13 #include "quiche/quic/platform/api/quic_test.h"
14
15 namespace quic {
16 namespace test {
17 namespace {
18
19 class QUICHE_EXPORT TestQuicBatchWriterBuffer : public QuicBatchWriterBuffer {
20 public:
21 using QuicBatchWriterBuffer::buffer_;
22 using QuicBatchWriterBuffer::buffered_writes_;
23 };
24
25 static const size_t kBatchBufferSize = QuicBatchWriterBuffer::kBufferSize;
26
27 class QuicBatchWriterBufferTest : public QuicTest {
28 public:
QuicBatchWriterBufferTest()29 QuicBatchWriterBufferTest() { SwitchToNewBuffer(); }
30
SwitchToNewBuffer()31 void SwitchToNewBuffer() {
32 batch_buffer_ = std::make_unique<TestQuicBatchWriterBuffer>();
33 }
34
35 // Fill packet_buffer_ with kMaxOutgoingPacketSize bytes of |c|s.
FillPacketBuffer(char c)36 char* FillPacketBuffer(char c) {
37 return FillPacketBuffer(c, packet_buffer_, kMaxOutgoingPacketSize);
38 }
39
40 // Fill |packet_buffer| with kMaxOutgoingPacketSize bytes of |c|s.
FillPacketBuffer(char c,char * packet_buffer)41 char* FillPacketBuffer(char c, char* packet_buffer) {
42 return FillPacketBuffer(c, packet_buffer, kMaxOutgoingPacketSize);
43 }
44
45 // Fill |packet_buffer| with |buf_len| bytes of |c|s.
FillPacketBuffer(char c,char * packet_buffer,size_t buf_len)46 char* FillPacketBuffer(char c, char* packet_buffer, size_t buf_len) {
47 memset(packet_buffer, c, buf_len);
48 return packet_buffer;
49 }
50
CheckBufferedWriteContent(int buffered_write_index,char buffer_content,size_t buf_len,const QuicIpAddress & self_addr,const QuicSocketAddress & peer_addr,const PerPacketOptions *,const QuicPacketWriterParams & params)51 void CheckBufferedWriteContent(int buffered_write_index, char buffer_content,
52 size_t buf_len, const QuicIpAddress& self_addr,
53 const QuicSocketAddress& peer_addr,
54 const PerPacketOptions* /*options*/,
55 const QuicPacketWriterParams& params) {
56 const BufferedWrite& buffered_write =
57 batch_buffer_->buffered_writes()[buffered_write_index];
58 EXPECT_EQ(buf_len, buffered_write.buf_len);
59 for (size_t i = 0; i < buf_len; ++i) {
60 EXPECT_EQ(buffer_content, buffered_write.buffer[i]);
61 if (buffer_content != buffered_write.buffer[i]) {
62 break;
63 }
64 }
65 EXPECT_EQ(self_addr, buffered_write.self_address);
66 EXPECT_EQ(peer_addr, buffered_write.peer_address);
67 EXPECT_EQ(params.release_time_delay,
68 buffered_write.params.release_time_delay);
69 }
70
71 protected:
72 std::unique_ptr<TestQuicBatchWriterBuffer> batch_buffer_;
73 QuicIpAddress self_addr_;
74 QuicSocketAddress peer_addr_;
75 uint64_t release_time_ = 0;
76 char packet_buffer_[kMaxOutgoingPacketSize];
77 };
78
79 class BufferSizeSequence {
80 public:
BufferSizeSequence(std::vector<std::pair<std::vector<size_t>,size_t>> stages)81 explicit BufferSizeSequence(
82 std::vector<std::pair<std::vector<size_t>, size_t>> stages)
83 : stages_(std::move(stages)),
84 total_buf_len_(0),
85 stage_index_(0),
86 sequence_index_(0) {}
87
Next()88 size_t Next() {
89 const std::vector<size_t>& seq = stages_[stage_index_].first;
90 size_t buf_len = seq[sequence_index_++ % seq.size()];
91 total_buf_len_ += buf_len;
92 if (stages_[stage_index_].second <= total_buf_len_) {
93 stage_index_ = std::min(stage_index_ + 1, stages_.size() - 1);
94 }
95 return buf_len;
96 }
97
98 private:
99 const std::vector<std::pair<std::vector<size_t>, size_t>> stages_;
100 size_t total_buf_len_;
101 size_t stage_index_;
102 size_t sequence_index_;
103 };
104
105 // Test in-place pushes. A in-place push is a push with a buffer address that is
106 // equal to the result of GetNextWriteLocation().
TEST_F(QuicBatchWriterBufferTest,InPlacePushes)107 TEST_F(QuicBatchWriterBufferTest, InPlacePushes) {
108 std::vector<BufferSizeSequence> buffer_size_sequences = {
109 // Push large writes until the buffer is near full, then switch to 1-byte
110 // writes. This covers the edge cases when detecting insufficient buffer.
111 BufferSizeSequence({{{1350}, kBatchBufferSize - 3000}, {{1}, 1000000}}),
112 // A sequence that looks real.
113 BufferSizeSequence({{{1, 39, 97, 150, 1350, 1350, 1350, 1350}, 1000000}}),
114 };
115
116 for (auto& buffer_size_sequence : buffer_size_sequences) {
117 SwitchToNewBuffer();
118 int64_t num_push_failures = 0;
119
120 while (batch_buffer_->SizeInUse() < kBatchBufferSize) {
121 size_t buf_len = buffer_size_sequence.Next();
122 const bool has_enough_space =
123 (kBatchBufferSize - batch_buffer_->SizeInUse() >=
124 kMaxOutgoingPacketSize);
125
126 char* buffer = batch_buffer_->GetNextWriteLocation();
127
128 if (has_enough_space) {
129 EXPECT_EQ(batch_buffer_->buffer_ + batch_buffer_->SizeInUse(), buffer);
130 } else {
131 EXPECT_EQ(nullptr, buffer);
132 }
133
134 SCOPED_TRACE(testing::Message()
135 << "Before Push: buf_len=" << buf_len
136 << ", has_enough_space=" << has_enough_space
137 << ", batch_buffer=" << batch_buffer_->DebugString());
138
139 auto push_result = batch_buffer_->PushBufferedWrite(
140 buffer, buf_len, self_addr_, peer_addr_, nullptr,
141 QuicPacketWriterParams(), release_time_);
142 if (!push_result.succeeded) {
143 ++num_push_failures;
144 }
145 EXPECT_EQ(has_enough_space, push_result.succeeded);
146 EXPECT_FALSE(push_result.buffer_copied);
147 if (!has_enough_space) {
148 break;
149 }
150 }
151 // Expect one and only one failure from the final push operation.
152 EXPECT_EQ(1, num_push_failures);
153 }
154 }
155
156 // Test some in-place pushes mixed with pushes with external buffers.
TEST_F(QuicBatchWriterBufferTest,MixedPushes)157 TEST_F(QuicBatchWriterBufferTest, MixedPushes) {
158 // First, a in-place push.
159 char* buffer = batch_buffer_->GetNextWriteLocation();
160 QuicPacketWriterParams params;
161 auto push_result = batch_buffer_->PushBufferedWrite(
162 FillPacketBuffer('A', buffer), kDefaultMaxPacketSize, self_addr_,
163 peer_addr_, nullptr, params, release_time_);
164 EXPECT_TRUE(push_result.succeeded);
165 EXPECT_FALSE(push_result.buffer_copied);
166 CheckBufferedWriteContent(0, 'A', kDefaultMaxPacketSize, self_addr_,
167 peer_addr_, nullptr, params);
168
169 // Then a push with external buffer.
170 push_result = batch_buffer_->PushBufferedWrite(
171 FillPacketBuffer('B'), kDefaultMaxPacketSize, self_addr_, peer_addr_,
172 nullptr, params, release_time_);
173 EXPECT_TRUE(push_result.succeeded);
174 EXPECT_TRUE(push_result.buffer_copied);
175 CheckBufferedWriteContent(1, 'B', kDefaultMaxPacketSize, self_addr_,
176 peer_addr_, nullptr, params);
177
178 // Then another in-place push.
179 buffer = batch_buffer_->GetNextWriteLocation();
180 push_result = batch_buffer_->PushBufferedWrite(
181 FillPacketBuffer('C', buffer), kDefaultMaxPacketSize, self_addr_,
182 peer_addr_, nullptr, params, release_time_);
183 EXPECT_TRUE(push_result.succeeded);
184 EXPECT_FALSE(push_result.buffer_copied);
185 CheckBufferedWriteContent(2, 'C', kDefaultMaxPacketSize, self_addr_,
186 peer_addr_, nullptr, params);
187
188 // Then another push with external buffer.
189 push_result = batch_buffer_->PushBufferedWrite(
190 FillPacketBuffer('D'), kDefaultMaxPacketSize, self_addr_, peer_addr_,
191 nullptr, params, release_time_);
192 EXPECT_TRUE(push_result.succeeded);
193 EXPECT_TRUE(push_result.buffer_copied);
194 CheckBufferedWriteContent(3, 'D', kDefaultMaxPacketSize, self_addr_,
195 peer_addr_, nullptr, params);
196 }
197
TEST_F(QuicBatchWriterBufferTest,PopAll)198 TEST_F(QuicBatchWriterBufferTest, PopAll) {
199 const int kNumBufferedWrites = 10;
200 QuicPacketWriterParams params;
201 for (int i = 0; i < kNumBufferedWrites; ++i) {
202 EXPECT_TRUE(batch_buffer_
203 ->PushBufferedWrite(packet_buffer_, kDefaultMaxPacketSize,
204 self_addr_, peer_addr_, nullptr, params,
205 release_time_)
206 .succeeded);
207 }
208 EXPECT_EQ(kNumBufferedWrites,
209 static_cast<int>(batch_buffer_->buffered_writes().size()));
210
211 auto pop_result = batch_buffer_->PopBufferedWrite(kNumBufferedWrites);
212 EXPECT_EQ(0u, batch_buffer_->buffered_writes().size());
213 EXPECT_EQ(kNumBufferedWrites, pop_result.num_buffers_popped);
214 EXPECT_FALSE(pop_result.moved_remaining_buffers);
215 }
216
TEST_F(QuicBatchWriterBufferTest,PopPartial)217 TEST_F(QuicBatchWriterBufferTest, PopPartial) {
218 const int kNumBufferedWrites = 10;
219 QuicPacketWriterParams params;
220 for (int i = 0; i < kNumBufferedWrites; ++i) {
221 EXPECT_TRUE(batch_buffer_
222 ->PushBufferedWrite(
223 FillPacketBuffer('A' + i), kDefaultMaxPacketSize - i,
224 self_addr_, peer_addr_, nullptr, params, release_time_)
225 .succeeded);
226 }
227
228 for (size_t i = 0;
229 i < kNumBufferedWrites && !batch_buffer_->buffered_writes().empty();
230 ++i) {
231 const size_t size_before_pop = batch_buffer_->buffered_writes().size();
232 const size_t expect_size_after_pop =
233 size_before_pop < i ? 0 : size_before_pop - i;
234 batch_buffer_->PopBufferedWrite(i);
235 ASSERT_EQ(expect_size_after_pop, batch_buffer_->buffered_writes().size());
236 const char first_write_content =
237 'A' + kNumBufferedWrites - expect_size_after_pop;
238 const size_t first_write_len =
239 kDefaultMaxPacketSize - kNumBufferedWrites + expect_size_after_pop;
240 for (size_t j = 0; j < expect_size_after_pop; ++j) {
241 CheckBufferedWriteContent(j, first_write_content + j, first_write_len - j,
242 self_addr_, peer_addr_, nullptr, params);
243 }
244 }
245 }
246
TEST_F(QuicBatchWriterBufferTest,InPlacePushWithPops)247 TEST_F(QuicBatchWriterBufferTest, InPlacePushWithPops) {
248 // First, a in-place push.
249 char* buffer = batch_buffer_->GetNextWriteLocation();
250 const size_t first_packet_len = 2;
251 QuicPacketWriterParams params;
252 auto push_result = batch_buffer_->PushBufferedWrite(
253 FillPacketBuffer('A', buffer, first_packet_len), first_packet_len,
254 self_addr_, peer_addr_, nullptr, params, release_time_);
255 EXPECT_TRUE(push_result.succeeded);
256 EXPECT_FALSE(push_result.buffer_copied);
257 CheckBufferedWriteContent(0, 'A', first_packet_len, self_addr_, peer_addr_,
258 nullptr, params);
259
260 // Simulate the case where the writer wants to do another in-place push, but
261 // can't do so because it can't be batched with the first buffer.
262 buffer = batch_buffer_->GetNextWriteLocation();
263 const size_t second_packet_len = 1350;
264
265 // Flush the first buffer.
266 auto pop_result = batch_buffer_->PopBufferedWrite(1);
267 EXPECT_EQ(1, pop_result.num_buffers_popped);
268 EXPECT_FALSE(pop_result.moved_remaining_buffers);
269
270 // Now the second push.
271 push_result = batch_buffer_->PushBufferedWrite(
272 FillPacketBuffer('B', buffer, second_packet_len), second_packet_len,
273 self_addr_, peer_addr_, nullptr, params, release_time_);
274 EXPECT_TRUE(push_result.succeeded);
275 EXPECT_TRUE(push_result.buffer_copied);
276 CheckBufferedWriteContent(0, 'B', second_packet_len, self_addr_, peer_addr_,
277 nullptr, params);
278 }
279
TEST_F(QuicBatchWriterBufferTest,BatchID)280 TEST_F(QuicBatchWriterBufferTest, BatchID) {
281 const int kNumBufferedWrites = 10;
282 QuicPacketWriterParams params;
283 auto first_push_result = batch_buffer_->PushBufferedWrite(
284 packet_buffer_, kDefaultMaxPacketSize, self_addr_, peer_addr_, nullptr,
285 params, release_time_);
286 ASSERT_TRUE(first_push_result.succeeded);
287 ASSERT_NE(first_push_result.batch_id, 0);
288 for (int i = 1; i < kNumBufferedWrites; ++i) {
289 EXPECT_EQ(batch_buffer_
290 ->PushBufferedWrite(packet_buffer_, kDefaultMaxPacketSize,
291 self_addr_, peer_addr_, nullptr, params,
292 release_time_)
293 .batch_id,
294 first_push_result.batch_id);
295 }
296
297 batch_buffer_->PopBufferedWrite(kNumBufferedWrites);
298 EXPECT_TRUE(batch_buffer_->buffered_writes().empty());
299
300 EXPECT_NE(
301 batch_buffer_
302 ->PushBufferedWrite(packet_buffer_, kDefaultMaxPacketSize, self_addr_,
303 peer_addr_, nullptr, params, release_time_)
304 .batch_id,
305 first_push_result.batch_id);
306 }
307
308 } // namespace
309 } // namespace test
310 } // namespace quic
311