1 // Copyright (c) 2012 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.
5 #include "net/quic/quic_fec_group.h"
10 #include "base/basictypes.h"
11 #include "base/logging.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "testing/gmock/include/gmock/gmock.h"
16 using base::StringPiece
;
23 // kData[] and kEntropyFlag[] are indexed by packet numbers, which
24 // start at 1, so their first elements are dummy.
25 const char* kData
[] = {
27 // kData[1] must be at least as long as every element of kData[], because
28 // it is used to calculate kDataMaxLen.
36 // The maximum length of an element of kData.
37 const size_t kDataMaxLen
= strlen(kData
[1]);
38 // A suitable test data string, whose length is kDataMaxLen.
39 const char* kDataSingle
= kData
[1];
41 const bool kEntropyFlag
[] = {
53 class QuicFecGroupTest
: public ::testing::Test
{
55 void RunTest(size_t num_packets
, size_t lost_packet
, bool out_of_order
) {
56 // kData[] and kEntropyFlag[] are indexed by packet numbers, which
58 DCHECK_GE(arraysize(kData
), num_packets
);
59 scoped_ptr
<char[]> redundancy(new char[kDataMaxLen
]);
60 for (size_t i
= 0; i
< kDataMaxLen
; i
++) {
63 // XOR in the packets.
64 for (size_t packet
= 1; packet
<= num_packets
; ++packet
) {
65 for (size_t i
= 0; i
< kDataMaxLen
; i
++) {
66 uint8 byte
= i
> strlen(kData
[packet
]) ? 0x00 : kData
[packet
][i
];
67 redundancy
[i
] = redundancy
[i
] ^ byte
;
73 // If we're out of order, send the FEC packet in the position of the
74 // lost packet. Otherwise send all (non-missing) packets, then FEC.
76 // Update the FEC state for each non-lost packet.
77 for (size_t packet
= 1; packet
<= num_packets
; packet
++) {
78 if (packet
== lost_packet
) {
79 ASSERT_FALSE(group
.IsFinished());
82 fec
.redundancy
= StringPiece(redundancy
.get(), kDataMaxLen
);
84 group
.UpdateFec(ENCRYPTION_FORWARD_SECURE
, num_packets
+ 1, fec
));
86 QuicPacketHeader header
;
87 header
.packet_packet_number
= packet
;
88 header
.entropy_flag
= kEntropyFlag
[packet
];
89 ASSERT_TRUE(group
.Update(ENCRYPTION_FORWARD_SECURE
, header
,
92 ASSERT_TRUE(group
.CanRevive() == (packet
== num_packets
));
95 // Update the FEC state for each non-lost packet.
96 for (size_t packet
= 1; packet
<= num_packets
; packet
++) {
97 if (packet
== lost_packet
) {
101 QuicPacketHeader header
;
102 header
.packet_packet_number
= packet
;
103 header
.entropy_flag
= kEntropyFlag
[packet
];
104 ASSERT_TRUE(group
.Update(ENCRYPTION_FORWARD_SECURE
, header
,
106 ASSERT_FALSE(group
.CanRevive());
109 ASSERT_FALSE(group
.IsFinished());
110 // Attempt to revive the missing packet.
113 fec
.redundancy
= StringPiece(redundancy
.get(), kDataMaxLen
);
116 group
.UpdateFec(ENCRYPTION_FORWARD_SECURE
, num_packets
+ 1, fec
));
118 QuicPacketHeader header
;
119 char recovered
[kMaxPacketSize
];
120 ASSERT_TRUE(group
.CanRevive());
121 size_t len
= group
.Revive(&header
, recovered
, arraysize(recovered
));
123 << "Failed to revive packet " << lost_packet
<< " out of "
125 EXPECT_EQ(lost_packet
, header
.packet_packet_number
)
126 << "Failed to revive packet " << lost_packet
<< " out of "
128 // Revived packets have an unknown entropy.
129 EXPECT_FALSE(header
.entropy_flag
);
130 ASSERT_GE(len
, strlen(kData
[lost_packet
])) << "Incorrect length";
131 for (size_t i
= 0; i
< strlen(kData
[lost_packet
]); i
++) {
132 EXPECT_EQ(kData
[lost_packet
][i
], recovered
[i
]);
134 ASSERT_TRUE(group
.IsFinished());
138 TEST_F(QuicFecGroupTest
, UpdateAndRevive
) {
139 RunTest(2, 1, false);
140 RunTest(2, 2, false);
142 RunTest(3, 1, false);
143 RunTest(3, 2, false);
144 RunTest(3, 3, false);
147 TEST_F(QuicFecGroupTest
, UpdateAndReviveOutOfOrder
) {
156 TEST_F(QuicFecGroupTest
, UpdateFecIfReceivedPacketIsNotCovered
) {
157 char data1
[] = "abc123";
158 char redundancy
[arraysize(data1
)];
159 for (size_t i
= 0; i
< arraysize(data1
); i
++) {
160 redundancy
[i
] = data1
[i
];
165 QuicPacketHeader header
;
166 header
.packet_packet_number
= 3;
167 group
.Update(ENCRYPTION_FORWARD_SECURE
, header
, data1
);
171 fec
.redundancy
= redundancy
;
173 header
.packet_packet_number
= 2;
174 ASSERT_FALSE(group
.UpdateFec(ENCRYPTION_FORWARD_SECURE
, 2, fec
));
177 TEST_F(QuicFecGroupTest
, ProtectsPacketsBefore
) {
178 QuicPacketHeader header
;
179 header
.packet_packet_number
= 3;
182 ASSERT_TRUE(group
.Update(ENCRYPTION_FORWARD_SECURE
, header
, kDataSingle
));
184 EXPECT_FALSE(group
.ProtectsPacketsBefore(1));
185 EXPECT_FALSE(group
.ProtectsPacketsBefore(2));
186 EXPECT_FALSE(group
.ProtectsPacketsBefore(3));
187 EXPECT_TRUE(group
.ProtectsPacketsBefore(4));
188 EXPECT_TRUE(group
.ProtectsPacketsBefore(5));
189 EXPECT_TRUE(group
.ProtectsPacketsBefore(50));
192 TEST_F(QuicFecGroupTest
, ProtectsPacketsBeforeWithSeveralPackets
) {
193 QuicPacketHeader header
;
194 header
.packet_packet_number
= 3;
197 ASSERT_TRUE(group
.Update(ENCRYPTION_FORWARD_SECURE
, header
, kDataSingle
));
199 header
.packet_packet_number
= 7;
200 ASSERT_TRUE(group
.Update(ENCRYPTION_FORWARD_SECURE
, header
, kDataSingle
));
202 header
.packet_packet_number
= 5;
203 ASSERT_TRUE(group
.Update(ENCRYPTION_FORWARD_SECURE
, header
, kDataSingle
));
205 EXPECT_FALSE(group
.ProtectsPacketsBefore(1));
206 EXPECT_FALSE(group
.ProtectsPacketsBefore(2));
207 EXPECT_FALSE(group
.ProtectsPacketsBefore(3));
208 EXPECT_TRUE(group
.ProtectsPacketsBefore(4));
209 EXPECT_TRUE(group
.ProtectsPacketsBefore(5));
210 EXPECT_TRUE(group
.ProtectsPacketsBefore(6));
211 EXPECT_TRUE(group
.ProtectsPacketsBefore(7));
212 EXPECT_TRUE(group
.ProtectsPacketsBefore(8));
213 EXPECT_TRUE(group
.ProtectsPacketsBefore(9));
214 EXPECT_TRUE(group
.ProtectsPacketsBefore(50));
217 TEST_F(QuicFecGroupTest
, ProtectsPacketsBeforeWithFecData
) {
220 fec
.redundancy
= kDataSingle
;
223 ASSERT_TRUE(group
.UpdateFec(ENCRYPTION_FORWARD_SECURE
, 3, fec
));
225 EXPECT_FALSE(group
.ProtectsPacketsBefore(1));
226 EXPECT_FALSE(group
.ProtectsPacketsBefore(2));
227 EXPECT_TRUE(group
.ProtectsPacketsBefore(3));
228 EXPECT_TRUE(group
.ProtectsPacketsBefore(4));
229 EXPECT_TRUE(group
.ProtectsPacketsBefore(5));
230 EXPECT_TRUE(group
.ProtectsPacketsBefore(50));
233 TEST_F(QuicFecGroupTest
, EffectiveEncryptionLevel
) {
235 EXPECT_EQ(NUM_ENCRYPTION_LEVELS
, group
.effective_encryption_level());
237 QuicPacketHeader header
;
238 header
.packet_packet_number
= 5;
239 ASSERT_TRUE(group
.Update(ENCRYPTION_INITIAL
, header
, kDataSingle
));
240 EXPECT_EQ(ENCRYPTION_INITIAL
, group
.effective_encryption_level());
244 fec
.redundancy
= kDataSingle
;
245 ASSERT_TRUE(group
.UpdateFec(ENCRYPTION_FORWARD_SECURE
, 7, fec
));
246 EXPECT_EQ(ENCRYPTION_INITIAL
, group
.effective_encryption_level());
248 header
.packet_packet_number
= 3;
249 ASSERT_TRUE(group
.Update(ENCRYPTION_NONE
, header
, kDataSingle
));
250 EXPECT_EQ(ENCRYPTION_NONE
, group
.effective_encryption_level());
253 // Test the code assuming it is going to be operating in 128-bit chunks (which
254 // is something that can happen if it is compiled with full vectorization).
255 const QuicByteCount kWordSize
= 128 / 8;
257 // A buffer which stores the data with the specified offset with respect to word
258 // alignment boundary.
259 class MisalignedBuffer
{
261 MisalignedBuffer(const string
& original
, size_t offset
);
263 char* buffer() { return buffer_
; }
264 size_t size() { return size_
; }
266 StringPiece
AsStringPiece() { return StringPiece(buffer_
, size_
); }
272 scoped_ptr
<char[]> allocation_
;
275 MisalignedBuffer::MisalignedBuffer(const string
& original
, size_t offset
) {
276 CHECK_LT(offset
, kWordSize
);
277 size_
= original
.size();
279 // Allocate aligned buffer two words larger than needed.
280 const size_t aligned_buffer_size
= size_
+ 2 * kWordSize
;
281 allocation_
.reset(new char[aligned_buffer_size
]);
282 char* aligned_buffer
=
284 (kWordSize
- reinterpret_cast<uintptr_t>(allocation_
.get()) % kWordSize
);
285 CHECK_EQ(0u, reinterpret_cast<uintptr_t>(aligned_buffer
) % kWordSize
);
287 buffer_
= aligned_buffer
+ offset
;
288 CHECK_EQ(offset
, reinterpret_cast<uintptr_t>(buffer_
) % kWordSize
);
289 memcpy(buffer_
, original
.data(), size_
);
292 // Checks whether XorBuffers works correctly with buffers aligned in various
294 TEST(XorBuffersTest
, XorBuffers
) {
295 const string longer_data
=
296 "Having to care about memory alignment can be incredibly frustrating.";
297 const string shorter_data
= "strict aliasing";
299 // Compute the reference XOR using simpler slow way.
300 string output_reference
;
301 for (size_t i
= 0; i
< longer_data
.size(); i
++) {
302 char shorter_byte
= i
< shorter_data
.size() ? shorter_data
[i
] : 0;
303 output_reference
.push_back(longer_data
[i
] ^ shorter_byte
);
306 // Check whether XorBuffers works correctly for all possible misalignments.
307 for (size_t offset_shorter
= 0; offset_shorter
< kWordSize
;
309 for (size_t offset_longer
= 0; offset_longer
< kWordSize
; offset_longer
++) {
310 // Prepare the misaligned buffer.
311 MisalignedBuffer
longer(longer_data
, offset_longer
);
312 MisalignedBuffer
shorter(shorter_data
, offset_shorter
);
314 // XOR the buffers and compare the result with the reference.
315 QuicFecGroup::XorBuffers(shorter
.buffer(), shorter
.size(),
317 EXPECT_EQ(output_reference
, longer
.AsStringPiece());