• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_gso_batch_writer.h"
6 
7 #include <sys/socket.h>
8 
9 #include <cstdint>
10 #include <limits>
11 #include <memory>
12 #include <utility>
13 
14 #include "quiche/quic/platform/api/quic_ip_address.h"
15 #include "quiche/quic/platform/api/quic_test.h"
16 #include "quiche/quic/test_tools/quic_mock_syscall_wrapper.h"
17 
18 using testing::_;
19 using testing::Invoke;
20 using testing::StrictMock;
21 
22 namespace quic {
23 namespace test {
24 namespace {
25 
PacketLength(const msghdr * msg)26 size_t PacketLength(const msghdr* msg) {
27   size_t length = 0;
28   for (size_t i = 0; i < msg->msg_iovlen; ++i) {
29     length += msg->msg_iov[i].iov_len;
30   }
31   return length;
32 }
33 
MillisToNanos(uint64_t milliseconds)34 uint64_t MillisToNanos(uint64_t milliseconds) { return milliseconds * 1000000; }
35 
36 class QUICHE_EXPORT TestQuicGsoBatchWriter : public QuicGsoBatchWriter {
37  public:
38   using QuicGsoBatchWriter::batch_buffer;
39   using QuicGsoBatchWriter::buffered_writes;
40   using QuicGsoBatchWriter::CanBatch;
41   using QuicGsoBatchWriter::CanBatchResult;
42   using QuicGsoBatchWriter::GetReleaseTime;
43   using QuicGsoBatchWriter::MaxSegments;
44   using QuicGsoBatchWriter::QuicGsoBatchWriter;
45   using QuicGsoBatchWriter::ReleaseTime;
46 
47   static std::unique_ptr<TestQuicGsoBatchWriter>
NewInstanceWithReleaseTimeSupport()48   NewInstanceWithReleaseTimeSupport() {
49     return std::unique_ptr<TestQuicGsoBatchWriter>(new TestQuicGsoBatchWriter(
50         std::make_unique<QuicBatchWriterBuffer>(),
51         /*fd=*/-1, CLOCK_MONOTONIC, ReleaseTimeForceEnabler()));
52   }
53 
NowInNanosForReleaseTime() const54   uint64_t NowInNanosForReleaseTime() const override {
55     return MillisToNanos(forced_release_time_ms_);
56   }
57 
ForceReleaseTimeMs(uint64_t forced_release_time_ms)58   void ForceReleaseTimeMs(uint64_t forced_release_time_ms) {
59     forced_release_time_ms_ = forced_release_time_ms;
60   }
61 
62  private:
63   uint64_t forced_release_time_ms_ = 1;
64 };
65 
66 // TestBufferedWrite is a copy-constructible BufferedWrite.
67 struct QUICHE_EXPORT TestBufferedWrite : public BufferedWrite {
68   using BufferedWrite::BufferedWrite;
TestBufferedWritequic::test::__anon2f819bd10111::TestBufferedWrite69   TestBufferedWrite(const TestBufferedWrite& other)
70       : BufferedWrite(other.buffer, other.buf_len, other.self_address,
71                       other.peer_address,
72                       other.options ? other.options->Clone()
73                                     : std::unique_ptr<PerPacketOptions>(),
74                       QuicPacketWriterParams(), other.release_time) {}
75 };
76 
77 // Pointed to by all instances of |BatchCriteriaTestData|. Content not used.
78 static char unused_packet_buffer[kMaxOutgoingPacketSize];
79 
80 struct QUICHE_EXPORT BatchCriteriaTestData {
BatchCriteriaTestDataquic::test::__anon2f819bd10111::BatchCriteriaTestData81   BatchCriteriaTestData(size_t buf_len, const QuicIpAddress& self_address,
82                         const QuicSocketAddress& peer_address,
83                         uint64_t release_time, bool can_batch, bool must_flush)
84       : buffered_write(unused_packet_buffer, buf_len, self_address,
85                        peer_address, std::unique_ptr<PerPacketOptions>(),
86                        QuicPacketWriterParams(), release_time),
87         can_batch(can_batch),
88         must_flush(must_flush) {}
89 
90   TestBufferedWrite buffered_write;
91   // Expected value of CanBatchResult.can_batch when batching |buffered_write|.
92   bool can_batch;
93   // Expected value of CanBatchResult.must_flush when batching |buffered_write|.
94   bool must_flush;
95 };
96 
BatchCriteriaTestData_SizeDecrease()97 std::vector<BatchCriteriaTestData> BatchCriteriaTestData_SizeDecrease() {
98   const QuicIpAddress self_addr;
99   const QuicSocketAddress peer_addr;
100   std::vector<BatchCriteriaTestData> test_data_table = {
101       // clang-format off
102   // buf_len   self_addr   peer_addr   t_rel   can_batch       must_flush
103     {1350,     self_addr,  peer_addr,  0,      true,           false},
104     {1350,     self_addr,  peer_addr,  0,      true,           false},
105     {1350,     self_addr,  peer_addr,  0,      true,           false},
106     {39,       self_addr,  peer_addr,  0,      true,           true},
107     {39,       self_addr,  peer_addr,  0,      false,          true},
108     {1350,     self_addr,  peer_addr,  0,      false,          true},
109       // clang-format on
110   };
111   return test_data_table;
112 }
113 
BatchCriteriaTestData_SizeIncrease()114 std::vector<BatchCriteriaTestData> BatchCriteriaTestData_SizeIncrease() {
115   const QuicIpAddress self_addr;
116   const QuicSocketAddress peer_addr;
117   std::vector<BatchCriteriaTestData> test_data_table = {
118       // clang-format off
119   // buf_len   self_addr   peer_addr   t_rel   can_batch       must_flush
120     {1350,     self_addr,  peer_addr,  0,      true,           false},
121     {1350,     self_addr,  peer_addr,  0,      true,           false},
122     {1350,     self_addr,  peer_addr,  0,      true,           false},
123     {1351,     self_addr,  peer_addr,  0,      false,          true},
124       // clang-format on
125   };
126   return test_data_table;
127 }
128 
BatchCriteriaTestData_AddressChange()129 std::vector<BatchCriteriaTestData> BatchCriteriaTestData_AddressChange() {
130   const QuicIpAddress self_addr1 = QuicIpAddress::Loopback4();
131   const QuicIpAddress self_addr2 = QuicIpAddress::Loopback6();
132   const QuicSocketAddress peer_addr1(self_addr1, 666);
133   const QuicSocketAddress peer_addr2(self_addr1, 777);
134   const QuicSocketAddress peer_addr3(self_addr2, 666);
135   const QuicSocketAddress peer_addr4(self_addr2, 777);
136   std::vector<BatchCriteriaTestData> test_data_table = {
137       // clang-format off
138   // buf_len   self_addr   peer_addr    t_rel  can_batch       must_flush
139     {1350,     self_addr1, peer_addr1,  0,     true,           false},
140     {1350,     self_addr1, peer_addr1,  0,     true,           false},
141     {1350,     self_addr1, peer_addr1,  0,     true,           false},
142     {1350,     self_addr2, peer_addr1,  0,     false,          true},
143     {1350,     self_addr1, peer_addr2,  0,     false,          true},
144     {1350,     self_addr1, peer_addr3,  0,     false,          true},
145     {1350,     self_addr1, peer_addr4,  0,     false,          true},
146     {1350,     self_addr1, peer_addr4,  0,     false,          true},
147       // clang-format on
148   };
149   return test_data_table;
150 }
151 
BatchCriteriaTestData_ReleaseTime1()152 std::vector<BatchCriteriaTestData> BatchCriteriaTestData_ReleaseTime1() {
153   const QuicIpAddress self_addr;
154   const QuicSocketAddress peer_addr;
155   std::vector<BatchCriteriaTestData> test_data_table = {
156       // clang-format off
157   // buf_len   self_addr   peer_addr   t_rel   can_batch       must_flush
158     {1350,     self_addr,  peer_addr,  5,      true,           false},
159     {1350,     self_addr,  peer_addr,  5,      true,           false},
160     {1350,     self_addr,  peer_addr,  5,      true,           false},
161     {1350,     self_addr,  peer_addr,  9,      false,          true},
162       // clang-format on
163   };
164   return test_data_table;
165 }
166 
BatchCriteriaTestData_ReleaseTime2()167 std::vector<BatchCriteriaTestData> BatchCriteriaTestData_ReleaseTime2() {
168   const QuicIpAddress self_addr;
169   const QuicSocketAddress peer_addr;
170   std::vector<BatchCriteriaTestData> test_data_table = {
171       // clang-format off
172   // buf_len   self_addr   peer_addr   t_rel   can_batch       must_flush
173     {1350,     self_addr,  peer_addr,  0,      true,           false},
174     {1350,     self_addr,  peer_addr,  0,      true,           false},
175     {1350,     self_addr,  peer_addr,  0,      true,           false},
176     {1350,     self_addr,  peer_addr,  9,      false,          true},
177       // clang-format on
178   };
179   return test_data_table;
180 }
181 
BatchCriteriaTestData_MaxSegments(size_t gso_size)182 std::vector<BatchCriteriaTestData> BatchCriteriaTestData_MaxSegments(
183     size_t gso_size) {
184   const QuicIpAddress self_addr;
185   const QuicSocketAddress peer_addr;
186   std::vector<BatchCriteriaTestData> test_data_table;
187   size_t max_segments = TestQuicGsoBatchWriter::MaxSegments(gso_size);
188   for (size_t i = 0; i < max_segments; ++i) {
189     bool is_last_in_batch = (i + 1 == max_segments);
190     test_data_table.push_back({gso_size, self_addr, peer_addr,
191                                /*release_time=*/0, true, is_last_in_batch});
192   }
193   test_data_table.push_back(
194       {gso_size, self_addr, peer_addr, /*release_time=*/0, false, true});
195   return test_data_table;
196 }
197 
198 class QuicGsoBatchWriterTest : public QuicTest {
199  protected:
WritePacket(QuicGsoBatchWriter * writer,size_t packet_size)200   WriteResult WritePacket(QuicGsoBatchWriter* writer, size_t packet_size) {
201     return writer->WritePacket(&packet_buffer_[0], packet_size, self_address_,
202                                peer_address_, nullptr,
203                                QuicPacketWriterParams());
204   }
205 
WritePacketWithParams(QuicGsoBatchWriter * writer,QuicPacketWriterParams & params)206   WriteResult WritePacketWithParams(QuicGsoBatchWriter* writer,
207                                     QuicPacketWriterParams& params) {
208     return writer->WritePacket(&packet_buffer_[0], 1350, self_address_,
209                                peer_address_, nullptr, params);
210   }
211 
212   QuicIpAddress self_address_ = QuicIpAddress::Any4();
213   QuicSocketAddress peer_address_{QuicIpAddress::Any4(), 443};
214   char packet_buffer_[1500];
215   StrictMock<MockQuicSyscallWrapper> mock_syscalls_;
216   ScopedGlobalSyscallWrapperOverride syscall_override_{&mock_syscalls_};
217 };
218 
TEST_F(QuicGsoBatchWriterTest,BatchCriteria)219 TEST_F(QuicGsoBatchWriterTest, BatchCriteria) {
220   std::unique_ptr<TestQuicGsoBatchWriter> writer;
221 
222   std::vector<std::vector<BatchCriteriaTestData>> test_data_tables;
223   test_data_tables.emplace_back(BatchCriteriaTestData_SizeDecrease());
224   test_data_tables.emplace_back(BatchCriteriaTestData_SizeIncrease());
225   test_data_tables.emplace_back(BatchCriteriaTestData_AddressChange());
226   test_data_tables.emplace_back(BatchCriteriaTestData_ReleaseTime1());
227   test_data_tables.emplace_back(BatchCriteriaTestData_ReleaseTime2());
228   test_data_tables.emplace_back(BatchCriteriaTestData_MaxSegments(1));
229   test_data_tables.emplace_back(BatchCriteriaTestData_MaxSegments(2));
230   test_data_tables.emplace_back(BatchCriteriaTestData_MaxSegments(1350));
231 
232   for (size_t i = 0; i < test_data_tables.size(); ++i) {
233     writer = TestQuicGsoBatchWriter::NewInstanceWithReleaseTimeSupport();
234 
235     const auto& test_data_table = test_data_tables[i];
236     for (size_t j = 0; j < test_data_table.size(); ++j) {
237       const BatchCriteriaTestData& test_data = test_data_table[j];
238       SCOPED_TRACE(testing::Message() << "i=" << i << ", j=" << j);
239       QuicPacketWriterParams params;
240       params.release_time_delay = QuicTime::Delta::FromMicroseconds(
241           test_data.buffered_write.release_time);
242       TestQuicGsoBatchWriter::CanBatchResult result = writer->CanBatch(
243           test_data.buffered_write.buffer, test_data.buffered_write.buf_len,
244           test_data.buffered_write.self_address,
245           test_data.buffered_write.peer_address, nullptr, params,
246           test_data.buffered_write.release_time);
247 
248       ASSERT_EQ(test_data.can_batch, result.can_batch);
249       ASSERT_EQ(test_data.must_flush, result.must_flush);
250 
251       if (result.can_batch) {
252         ASSERT_TRUE(writer->batch_buffer()
253                         .PushBufferedWrite(
254                             test_data.buffered_write.buffer,
255                             test_data.buffered_write.buf_len,
256                             test_data.buffered_write.self_address,
257                             test_data.buffered_write.peer_address, nullptr,
258                             params, test_data.buffered_write.release_time)
259                         .succeeded);
260       }
261     }
262   }
263 }
264 
TEST_F(QuicGsoBatchWriterTest,WriteSuccess)265 TEST_F(QuicGsoBatchWriterTest, WriteSuccess) {
266   TestQuicGsoBatchWriter writer(/*fd=*/-1);
267 
268   ASSERT_EQ(WriteResult(WRITE_STATUS_OK, 0), WritePacket(&writer, 1000));
269 
270   EXPECT_CALL(mock_syscalls_, Sendmsg(_, _, _))
271       .WillOnce(Invoke([](int /*sockfd*/, const msghdr* msg, int /*flags*/) {
272         EXPECT_EQ(1100u, PacketLength(msg));
273         return 1100;
274       }));
275   ASSERT_EQ(WriteResult(WRITE_STATUS_OK, 1100), WritePacket(&writer, 100));
276   ASSERT_EQ(0u, writer.batch_buffer().SizeInUse());
277   ASSERT_EQ(0u, writer.buffered_writes().size());
278 }
279 
TEST_F(QuicGsoBatchWriterTest,WriteBlockDataNotBuffered)280 TEST_F(QuicGsoBatchWriterTest, WriteBlockDataNotBuffered) {
281   TestQuicGsoBatchWriter writer(/*fd=*/-1);
282 
283   ASSERT_EQ(WriteResult(WRITE_STATUS_OK, 0), WritePacket(&writer, 100));
284   ASSERT_EQ(WriteResult(WRITE_STATUS_OK, 0), WritePacket(&writer, 100));
285 
286   EXPECT_CALL(mock_syscalls_, Sendmsg(_, _, _))
287       .WillOnce(Invoke([](int /*sockfd*/, const msghdr* msg, int /*flags*/) {
288         EXPECT_EQ(200u, PacketLength(msg));
289         errno = EWOULDBLOCK;
290         return -1;
291       }));
292   ASSERT_EQ(WriteResult(WRITE_STATUS_BLOCKED, EWOULDBLOCK),
293             WritePacket(&writer, 150));
294   ASSERT_EQ(200u, writer.batch_buffer().SizeInUse());
295   ASSERT_EQ(2u, writer.buffered_writes().size());
296 }
297 
TEST_F(QuicGsoBatchWriterTest,WriteBlockDataBuffered)298 TEST_F(QuicGsoBatchWriterTest, WriteBlockDataBuffered) {
299   TestQuicGsoBatchWriter writer(/*fd=*/-1);
300 
301   ASSERT_EQ(WriteResult(WRITE_STATUS_OK, 0), WritePacket(&writer, 100));
302   ASSERT_EQ(WriteResult(WRITE_STATUS_OK, 0), WritePacket(&writer, 100));
303 
304   EXPECT_CALL(mock_syscalls_, Sendmsg(_, _, _))
305       .WillOnce(Invoke([](int /*sockfd*/, const msghdr* msg, int /*flags*/) {
306         EXPECT_EQ(250u, PacketLength(msg));
307         errno = EWOULDBLOCK;
308         return -1;
309       }));
310   ASSERT_EQ(WriteResult(WRITE_STATUS_BLOCKED_DATA_BUFFERED, EWOULDBLOCK),
311             WritePacket(&writer, 50));
312 
313   EXPECT_TRUE(writer.IsWriteBlocked());
314 
315   ASSERT_EQ(250u, writer.batch_buffer().SizeInUse());
316   ASSERT_EQ(3u, writer.buffered_writes().size());
317 }
318 
TEST_F(QuicGsoBatchWriterTest,WriteErrorWithoutDataBuffered)319 TEST_F(QuicGsoBatchWriterTest, WriteErrorWithoutDataBuffered) {
320   TestQuicGsoBatchWriter writer(/*fd=*/-1);
321 
322   ASSERT_EQ(WriteResult(WRITE_STATUS_OK, 0), WritePacket(&writer, 100));
323   ASSERT_EQ(WriteResult(WRITE_STATUS_OK, 0), WritePacket(&writer, 100));
324 
325   EXPECT_CALL(mock_syscalls_, Sendmsg(_, _, _))
326       .WillOnce(Invoke([](int /*sockfd*/, const msghdr* msg, int /*flags*/) {
327         EXPECT_EQ(200u, PacketLength(msg));
328         errno = EPERM;
329         return -1;
330       }));
331   WriteResult error_result = WritePacket(&writer, 150);
332   ASSERT_EQ(WriteResult(WRITE_STATUS_ERROR, EPERM), error_result);
333 
334   ASSERT_EQ(3u, error_result.dropped_packets);
335   ASSERT_EQ(0u, writer.batch_buffer().SizeInUse());
336   ASSERT_EQ(0u, writer.buffered_writes().size());
337 }
338 
TEST_F(QuicGsoBatchWriterTest,WriteErrorAfterDataBuffered)339 TEST_F(QuicGsoBatchWriterTest, WriteErrorAfterDataBuffered) {
340   TestQuicGsoBatchWriter writer(/*fd=*/-1);
341 
342   ASSERT_EQ(WriteResult(WRITE_STATUS_OK, 0), WritePacket(&writer, 100));
343   ASSERT_EQ(WriteResult(WRITE_STATUS_OK, 0), WritePacket(&writer, 100));
344 
345   EXPECT_CALL(mock_syscalls_, Sendmsg(_, _, _))
346       .WillOnce(Invoke([](int /*sockfd*/, const msghdr* msg, int /*flags*/) {
347         EXPECT_EQ(250u, PacketLength(msg));
348         errno = EPERM;
349         return -1;
350       }));
351   WriteResult error_result = WritePacket(&writer, 50);
352   ASSERT_EQ(WriteResult(WRITE_STATUS_ERROR, EPERM), error_result);
353 
354   ASSERT_EQ(3u, error_result.dropped_packets);
355   ASSERT_EQ(0u, writer.batch_buffer().SizeInUse());
356   ASSERT_EQ(0u, writer.buffered_writes().size());
357 }
358 
TEST_F(QuicGsoBatchWriterTest,FlushError)359 TEST_F(QuicGsoBatchWriterTest, FlushError) {
360   TestQuicGsoBatchWriter writer(/*fd=*/-1);
361 
362   ASSERT_EQ(WriteResult(WRITE_STATUS_OK, 0), WritePacket(&writer, 100));
363   ASSERT_EQ(WriteResult(WRITE_STATUS_OK, 0), WritePacket(&writer, 100));
364 
365   EXPECT_CALL(mock_syscalls_, Sendmsg(_, _, _))
366       .WillOnce(Invoke([](int /*sockfd*/, const msghdr* msg, int /*flags*/) {
367         EXPECT_EQ(200u, PacketLength(msg));
368         errno = EINVAL;
369         return -1;
370       }));
371   WriteResult error_result = writer.Flush();
372   ASSERT_EQ(WriteResult(WRITE_STATUS_ERROR, EINVAL), error_result);
373 
374   ASSERT_EQ(2u, error_result.dropped_packets);
375   ASSERT_EQ(0u, writer.batch_buffer().SizeInUse());
376   ASSERT_EQ(0u, writer.buffered_writes().size());
377 }
378 
TEST_F(QuicGsoBatchWriterTest,ReleaseTime)379 TEST_F(QuicGsoBatchWriterTest, ReleaseTime) {
380   const WriteResult write_buffered(WRITE_STATUS_OK, 0);
381 
382   auto writer = TestQuicGsoBatchWriter::NewInstanceWithReleaseTimeSupport();
383 
384   QuicPacketWriterParams params;
385   EXPECT_TRUE(params.release_time_delay.IsZero());
386   EXPECT_FALSE(params.allow_burst);
387   EXPECT_EQ(MillisToNanos(1),
388             writer->GetReleaseTime(params).actual_release_time);
389 
390   // The 1st packet has no delay.
391   WriteResult result = WritePacketWithParams(writer.get(), params);
392   ASSERT_EQ(write_buffered, result);
393   EXPECT_EQ(MillisToNanos(1), writer->buffered_writes().back().release_time);
394   EXPECT_EQ(result.send_time_offset, QuicTime::Delta::Zero());
395 
396   // The 2nd packet has some delay, but allows burst.
397   params.release_time_delay = QuicTime::Delta::FromMilliseconds(3);
398   params.allow_burst = true;
399   result = WritePacketWithParams(writer.get(), params);
400   ASSERT_EQ(write_buffered, result);
401   EXPECT_EQ(MillisToNanos(1), writer->buffered_writes().back().release_time);
402   EXPECT_EQ(result.send_time_offset, QuicTime::Delta::FromMilliseconds(-3));
403 
404   // The 3rd packet has more delay and does not allow burst.
405   // The first 2 packets are flushed due to different release time.
406   EXPECT_CALL(mock_syscalls_, Sendmsg(_, _, _))
407       .WillOnce(Invoke([](int /*sockfd*/, const msghdr* msg, int /*flags*/) {
408         EXPECT_EQ(2700u, PacketLength(msg));
409         errno = 0;
410         return 0;
411       }));
412   params.release_time_delay = QuicTime::Delta::FromMilliseconds(5);
413   params.allow_burst = false;
414   result = WritePacketWithParams(writer.get(), params);
415   ASSERT_EQ(WriteResult(WRITE_STATUS_OK, 2700), result);
416   EXPECT_EQ(MillisToNanos(6), writer->buffered_writes().back().release_time);
417   EXPECT_EQ(result.send_time_offset, QuicTime::Delta::Zero());
418 
419   // The 4th packet has same delay, but allows burst.
420   params.allow_burst = true;
421   result = WritePacketWithParams(writer.get(), params);
422   ASSERT_EQ(write_buffered, result);
423   EXPECT_EQ(MillisToNanos(6), writer->buffered_writes().back().release_time);
424   EXPECT_EQ(result.send_time_offset, QuicTime::Delta::Zero());
425 
426   // The 5th packet has same delay, allows burst, but is shorter.
427   // Packets 3,4 and 5 are flushed.
428   EXPECT_CALL(mock_syscalls_, Sendmsg(_, _, _))
429       .WillOnce(Invoke([](int /*sockfd*/, const msghdr* msg, int /*flags*/) {
430         EXPECT_EQ(3000u, PacketLength(msg));
431         errno = 0;
432         return 0;
433       }));
434   params.allow_burst = true;
435   EXPECT_EQ(MillisToNanos(6),
436             writer->GetReleaseTime(params).actual_release_time);
437   ASSERT_EQ(WriteResult(WRITE_STATUS_OK, 3000),
438             writer->WritePacket(&packet_buffer_[0], 300, self_address_,
439                                 peer_address_, nullptr, params));
440   EXPECT_TRUE(writer->buffered_writes().empty());
441 
442   // Pretend 1ms has elapsed and the 6th packet has 1ms less delay. In other
443   // words, the release time should still be the same as packets 3-5.
444   writer->ForceReleaseTimeMs(2);
445   params.release_time_delay = QuicTime::Delta::FromMilliseconds(4);
446   result = WritePacketWithParams(writer.get(), params);
447   ASSERT_EQ(write_buffered, result);
448   EXPECT_EQ(MillisToNanos(6), writer->buffered_writes().back().release_time);
449   EXPECT_EQ(result.send_time_offset, QuicTime::Delta::Zero());
450 }
451 
TEST_F(QuicGsoBatchWriterTest,EcnCodepoint)452 TEST_F(QuicGsoBatchWriterTest, EcnCodepoint) {
453   const WriteResult write_buffered(WRITE_STATUS_OK, 0);
454 
455   auto writer = TestQuicGsoBatchWriter::NewInstanceWithReleaseTimeSupport();
456 
457   QuicPacketWriterParams params;
458   EXPECT_TRUE(params.release_time_delay.IsZero());
459   EXPECT_FALSE(params.allow_burst);
460   params.ecn_codepoint = ECN_ECT0;
461 
462   // The 1st packet has no delay.
463   WriteResult result = WritePacketWithParams(writer.get(), params);
464   ASSERT_EQ(write_buffered, result);
465   EXPECT_EQ(MillisToNanos(1), writer->buffered_writes().back().release_time);
466   EXPECT_EQ(result.send_time_offset, QuicTime::Delta::Zero());
467 
468   // The 2nd packet should be buffered.
469   params.allow_burst = true;
470   result = WritePacketWithParams(writer.get(), params);
471   ASSERT_EQ(write_buffered, result);
472 
473   // The 3rd packet changes the ECN codepoint.
474   // The first 2 packets are flushed due to different codepoint.
475   params.ecn_codepoint = ECN_ECT1;
476   EXPECT_CALL(mock_syscalls_, Sendmsg(_, _, _))
477       .WillOnce(Invoke([](int /*sockfd*/, const msghdr* msg, int /*flags*/) {
478         const int kEct0 = 0x02;
479         EXPECT_EQ(2700u, PacketLength(msg));
480         msghdr mutable_msg;
481         memcpy(&mutable_msg, msg, sizeof(*msg));
482         for (struct cmsghdr* cmsg = CMSG_FIRSTHDR(&mutable_msg); cmsg != NULL;
483              cmsg = CMSG_NXTHDR(&mutable_msg, cmsg)) {
484           if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TOS) {
485             EXPECT_EQ(*reinterpret_cast<int*> CMSG_DATA(cmsg), kEct0);
486             break;
487           }
488         }
489         errno = 0;
490         return 0;
491       }));
492   result = WritePacketWithParams(writer.get(), params);
493   ASSERT_EQ(WriteResult(WRITE_STATUS_OK, 2700), result);
494 }
495 
TEST_F(QuicGsoBatchWriterTest,EcnCodepointIPv6)496 TEST_F(QuicGsoBatchWriterTest, EcnCodepointIPv6) {
497   const WriteResult write_buffered(WRITE_STATUS_OK, 0);
498 
499   self_address_ = QuicIpAddress::Any6();
500   peer_address_ = QuicSocketAddress(QuicIpAddress::Any6(), 443);
501   auto writer = TestQuicGsoBatchWriter::NewInstanceWithReleaseTimeSupport();
502 
503   QuicPacketWriterParams params;
504   EXPECT_TRUE(params.release_time_delay.IsZero());
505   EXPECT_FALSE(params.allow_burst);
506   params.ecn_codepoint = ECN_ECT0;
507 
508   // The 1st packet has no delay.
509   WriteResult result = WritePacketWithParams(writer.get(), params);
510   ASSERT_EQ(write_buffered, result);
511   EXPECT_EQ(MillisToNanos(1), writer->buffered_writes().back().release_time);
512   EXPECT_EQ(result.send_time_offset, QuicTime::Delta::Zero());
513 
514   // The 2nd packet should be buffered.
515   params.allow_burst = true;
516   result = WritePacketWithParams(writer.get(), params);
517   ASSERT_EQ(write_buffered, result);
518 
519   // The 3rd packet changes the ECN codepoint.
520   // The first 2 packets are flushed due to different codepoint.
521   params.ecn_codepoint = ECN_ECT1;
522   EXPECT_CALL(mock_syscalls_, Sendmsg(_, _, _))
523       .WillOnce(Invoke([](int /*sockfd*/, const msghdr* msg, int /*flags*/) {
524         const int kEct0 = 0x02;
525         EXPECT_EQ(2700u, PacketLength(msg));
526         msghdr mutable_msg;
527         memcpy(&mutable_msg, msg, sizeof(*msg));
528         for (struct cmsghdr* cmsg = CMSG_FIRSTHDR(&mutable_msg); cmsg != NULL;
529              cmsg = CMSG_NXTHDR(&mutable_msg, cmsg)) {
530           if (cmsg->cmsg_level == IPPROTO_IPV6 &&
531               cmsg->cmsg_type == IPV6_TCLASS) {
532             EXPECT_EQ(*reinterpret_cast<int*> CMSG_DATA(cmsg), kEct0);
533             break;
534           }
535         }
536         errno = 0;
537         return 0;
538       }));
539   result = WritePacketWithParams(writer.get(), params);
540   ASSERT_EQ(WriteResult(WRITE_STATUS_OK, 2700), result);
541 }
542 
543 }  // namespace
544 }  // namespace test
545 }  // namespace quic
546