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 "net/third_party/quiche/src/quic/core/batch_writer/quic_batch_writer_buffer.h"
6 #include <memory>
7 #include <string>
8 
9 #include "net/third_party/quiche/src/quic/core/quic_constants.h"
10 #include "net/third_party/quiche/src/quic/platform/api/quic_ip_address.h"
11 #include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
12 #include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
13 
14 namespace quic {
15 namespace test {
16 namespace {
17 
18 class QUIC_EXPORT_PRIVATE TestQuicBatchWriterBuffer
19     : 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 * options)51   void CheckBufferedWriteContent(int buffered_write_index,
52                                  char buffer_content,
53                                  size_t buf_len,
54                                  const QuicIpAddress& self_addr,
55                                  const QuicSocketAddress& peer_addr,
56                                  const PerPacketOptions* options) {
57     const BufferedWrite& buffered_write =
58         batch_buffer_->buffered_writes()[buffered_write_index];
59     EXPECT_EQ(buf_len, buffered_write.buf_len);
60     for (size_t i = 0; i < buf_len; ++i) {
61       EXPECT_EQ(buffer_content, buffered_write.buffer[i]);
62       if (buffer_content != buffered_write.buffer[i]) {
63         break;
64       }
65     }
66     EXPECT_EQ(self_addr, buffered_write.self_address);
67     EXPECT_EQ(peer_addr, buffered_write.peer_address);
68     if (options == nullptr) {
69       EXPECT_EQ(nullptr, buffered_write.options);
70     } else {
71       EXPECT_EQ(options->release_time_delay,
72                 buffered_write.options->release_time_delay);
73     }
74   }
75 
76  protected:
77   std::unique_ptr<TestQuicBatchWriterBuffer> batch_buffer_;
78   QuicIpAddress self_addr_;
79   QuicSocketAddress peer_addr_;
80   uint64_t release_time_ = 0;
81   char packet_buffer_[kMaxOutgoingPacketSize];
82 };
83 
84 class BufferSizeSequence {
85  public:
BufferSizeSequence(std::vector<std::pair<std::vector<size_t>,size_t>> stages)86   explicit BufferSizeSequence(
87       std::vector<std::pair<std::vector<size_t>, size_t>> stages)
88       : stages_(std::move(stages)),
89         total_buf_len_(0),
90         stage_index_(0),
91         sequence_index_(0) {}
92 
Next()93   size_t Next() {
94     const std::vector<size_t>& seq = stages_[stage_index_].first;
95     size_t buf_len = seq[sequence_index_++ % seq.size()];
96     total_buf_len_ += buf_len;
97     if (stages_[stage_index_].second <= total_buf_len_) {
98       stage_index_ = std::min(stage_index_ + 1, stages_.size() - 1);
99     }
100     return buf_len;
101   }
102 
103  private:
104   const std::vector<std::pair<std::vector<size_t>, size_t>> stages_;
105   size_t total_buf_len_;
106   size_t stage_index_;
107   size_t sequence_index_;
108 };
109 
110 // Test in-place pushes. A in-place push is a push with a buffer address that is
111 // equal to the result of GetNextWriteLocation().
TEST_F(QuicBatchWriterBufferTest,InPlacePushes)112 TEST_F(QuicBatchWriterBufferTest, InPlacePushes) {
113   std::vector<BufferSizeSequence> buffer_size_sequences = {
114       // Push large writes until the buffer is near full, then switch to 1-byte
115       // writes. This covers the edge cases when detecting insufficient buffer.
116       BufferSizeSequence({{{1350}, kBatchBufferSize - 3000}, {{1}, 1e6}}),
117       // A sequence that looks real.
118       BufferSizeSequence({{{1, 39, 97, 150, 1350, 1350, 1350, 1350}, 1e6}}),
119   };
120 
121   for (auto& buffer_size_sequence : buffer_size_sequences) {
122     SwitchToNewBuffer();
123     int64_t num_push_failures = 0;
124 
125     while (batch_buffer_->SizeInUse() < kBatchBufferSize) {
126       size_t buf_len = buffer_size_sequence.Next();
127       const bool has_enough_space =
128           (kBatchBufferSize - batch_buffer_->SizeInUse() >=
129            kMaxOutgoingPacketSize);
130 
131       char* buffer = batch_buffer_->GetNextWriteLocation();
132 
133       if (has_enough_space) {
134         EXPECT_EQ(batch_buffer_->buffer_ + batch_buffer_->SizeInUse(), buffer);
135       } else {
136         EXPECT_EQ(nullptr, buffer);
137       }
138 
139       SCOPED_TRACE(testing::Message()
140                    << "Before Push: buf_len=" << buf_len
141                    << ", has_enough_space=" << has_enough_space
142                    << ", batch_buffer=" << batch_buffer_->DebugString());
143 
144       auto push_result = batch_buffer_->PushBufferedWrite(
145           buffer, buf_len, self_addr_, peer_addr_, nullptr, release_time_);
146       if (!push_result.succeeded) {
147         ++num_push_failures;
148       }
149       EXPECT_EQ(has_enough_space, push_result.succeeded);
150       EXPECT_FALSE(push_result.buffer_copied);
151       if (!has_enough_space) {
152         break;
153       }
154     }
155     // Expect one and only one failure from the final push operation.
156     EXPECT_EQ(1, num_push_failures);
157   }
158 }
159 
160 // Test some in-place pushes mixed with pushes with external buffers.
TEST_F(QuicBatchWriterBufferTest,MixedPushes)161 TEST_F(QuicBatchWriterBufferTest, MixedPushes) {
162   // First, a in-place push.
163   char* buffer = batch_buffer_->GetNextWriteLocation();
164   auto push_result = batch_buffer_->PushBufferedWrite(
165       FillPacketBuffer('A', buffer), kDefaultMaxPacketSize, self_addr_,
166       peer_addr_, nullptr, release_time_);
167   EXPECT_TRUE(push_result.succeeded);
168   EXPECT_FALSE(push_result.buffer_copied);
169   CheckBufferedWriteContent(0, 'A', kDefaultMaxPacketSize, self_addr_,
170                             peer_addr_, nullptr);
171 
172   // Then a push with external buffer.
173   push_result = batch_buffer_->PushBufferedWrite(
174       FillPacketBuffer('B'), kDefaultMaxPacketSize, self_addr_, peer_addr_,
175       nullptr, release_time_);
176   EXPECT_TRUE(push_result.succeeded);
177   EXPECT_TRUE(push_result.buffer_copied);
178   CheckBufferedWriteContent(1, 'B', kDefaultMaxPacketSize, self_addr_,
179                             peer_addr_, nullptr);
180 
181   // Then another in-place push.
182   buffer = batch_buffer_->GetNextWriteLocation();
183   push_result = batch_buffer_->PushBufferedWrite(
184       FillPacketBuffer('C', buffer), kDefaultMaxPacketSize, self_addr_,
185       peer_addr_, nullptr, release_time_);
186   EXPECT_TRUE(push_result.succeeded);
187   EXPECT_FALSE(push_result.buffer_copied);
188   CheckBufferedWriteContent(2, 'C', kDefaultMaxPacketSize, self_addr_,
189                             peer_addr_, nullptr);
190 
191   // Then another push with external buffer.
192   push_result = batch_buffer_->PushBufferedWrite(
193       FillPacketBuffer('D'), kDefaultMaxPacketSize, self_addr_, peer_addr_,
194       nullptr, release_time_);
195   EXPECT_TRUE(push_result.succeeded);
196   EXPECT_TRUE(push_result.buffer_copied);
197   CheckBufferedWriteContent(3, 'D', kDefaultMaxPacketSize, self_addr_,
198                             peer_addr_, nullptr);
199 }
200 
TEST_F(QuicBatchWriterBufferTest,PopAll)201 TEST_F(QuicBatchWriterBufferTest, PopAll) {
202   const int kNumBufferedWrites = 10;
203   for (int i = 0; i < kNumBufferedWrites; ++i) {
204     EXPECT_TRUE(batch_buffer_
205                     ->PushBufferedWrite(packet_buffer_, kDefaultMaxPacketSize,
206                                         self_addr_, peer_addr_, nullptr,
207                                         release_time_)
208                     .succeeded);
209   }
210   EXPECT_EQ(kNumBufferedWrites,
211             static_cast<int>(batch_buffer_->buffered_writes().size()));
212 
213   auto pop_result = batch_buffer_->PopBufferedWrite(kNumBufferedWrites);
214   EXPECT_EQ(0u, batch_buffer_->buffered_writes().size());
215   EXPECT_EQ(kNumBufferedWrites, pop_result.num_buffers_popped);
216   EXPECT_FALSE(pop_result.moved_remaining_buffers);
217 }
218 
TEST_F(QuicBatchWriterBufferTest,PopPartial)219 TEST_F(QuicBatchWriterBufferTest, PopPartial) {
220   const int kNumBufferedWrites = 10;
221   for (int i = 0; i < kNumBufferedWrites; ++i) {
222     EXPECT_TRUE(batch_buffer_
223                     ->PushBufferedWrite(FillPacketBuffer('A' + i),
224                                         kDefaultMaxPacketSize - i, self_addr_,
225                                         peer_addr_, nullptr, release_time_)
226                     .succeeded);
227   }
228 
229   for (size_t i = 0;
230        i < kNumBufferedWrites && !batch_buffer_->buffered_writes().empty();
231        ++i) {
232     const size_t size_before_pop = batch_buffer_->buffered_writes().size();
233     const size_t expect_size_after_pop =
234         size_before_pop < i ? 0 : size_before_pop - i;
235     batch_buffer_->PopBufferedWrite(i);
236     ASSERT_EQ(expect_size_after_pop, batch_buffer_->buffered_writes().size());
237     const char first_write_content =
238         'A' + kNumBufferedWrites - expect_size_after_pop;
239     const size_t first_write_len =
240         kDefaultMaxPacketSize - kNumBufferedWrites + expect_size_after_pop;
241     for (size_t j = 0; j < expect_size_after_pop; ++j) {
242       CheckBufferedWriteContent(j, first_write_content + j, first_write_len - j,
243                                 self_addr_, peer_addr_, nullptr);
244     }
245   }
246 }
247 
TEST_F(QuicBatchWriterBufferTest,InPlacePushWithPops)248 TEST_F(QuicBatchWriterBufferTest, InPlacePushWithPops) {
249   // First, a in-place push.
250   char* buffer = batch_buffer_->GetNextWriteLocation();
251   const size_t first_packet_len = 2;
252   auto push_result = batch_buffer_->PushBufferedWrite(
253       FillPacketBuffer('A', buffer, first_packet_len), first_packet_len,
254       self_addr_, peer_addr_, nullptr, 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);
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, 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);
278 }
279 
280 }  // namespace
281 }  // namespace test
282 }  // namespace quic
283