blob: a53942f0a2b3c4fa63799286ec873c085c5f7580 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <algorithm>
#include <vector>
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "net/quic/quic_fec_group.h"
#include "testing/gmock/include/gmock/gmock.h"
using ::testing::_;
using base::StringPiece;
namespace net {
namespace {
const char* kData[] = {
"abc12345678",
"987defg",
"ghi12345",
"987jlkmno",
"mno4567890",
"789pqrstuvw",
};
} // namespace
class QuicFecGroupTest : public ::testing::Test {
protected:
void RunTest(size_t num_packets, size_t lost_packet, bool out_of_order) {
size_t max_len = strlen(kData[0]);
scoped_array<char>redundancy(new char[max_len]);
for (size_t i = 0; i < max_len; i++) {
// Initialize to the first packet.
redundancy[i] = kData[0][i];
// XOR in the remaining packets.
for (size_t packet = 1; packet < num_packets; packet++) {
uint8 byte = i > strlen(kData[packet]) ? 0x00 : kData[packet][i];
redundancy[i] = redundancy[i] ^ byte;
}
}
QuicFecGroup group;
// If we're out of order, send the FEC packet in the position of the
// lost packet. Otherwise send all (non-missing) packets, then FEC.
if (out_of_order) {
// Update the FEC state for each non-lost packet.
for (size_t packet = 0; packet < num_packets; packet++) {
if (packet == lost_packet) {
ASSERT_FALSE(group.IsFinished());
QuicFecData fec;
fec.min_protected_packet_sequence_number = 0;
fec.redundancy = StringPiece(redundancy.get(), strlen(kData[0]));
ASSERT_TRUE(group.UpdateFec(num_packets, fec));
} else {
QuicPacketHeader header;
header.packet_sequence_number = packet;
ASSERT_TRUE(group.Update(header, kData[packet]));
}
ASSERT_TRUE(group.CanRevive() == (packet == num_packets - 1));
}
} else {
// Update the FEC state for each non-lost packet.
for (size_t packet = 0; packet < num_packets; packet++) {
if (packet == lost_packet) {
continue;
}
QuicPacketHeader header;
header.packet_sequence_number = packet;
ASSERT_TRUE(group.Update(header, kData[packet]));
ASSERT_FALSE(group.CanRevive());
}
ASSERT_FALSE(group.IsFinished());
// Attempt to revive the missing packet.
QuicFecData fec;
fec.min_protected_packet_sequence_number = 0;
fec.redundancy = StringPiece(redundancy.get(), strlen(kData[0]));
ASSERT_TRUE(group.UpdateFec(num_packets, fec));
}
QuicPacketHeader header;
char recovered[kMaxPacketSize];
ASSERT_TRUE(group.CanRevive());
size_t len = group.Revive(&header, recovered, arraysize(recovered));
ASSERT_NE(0u, len)
<< "Failed to revive packet " << lost_packet << " out of "
<< num_packets;
EXPECT_EQ(lost_packet, header.packet_sequence_number)
<< "Failed to revive packet " << lost_packet << " out of "
<< num_packets;
ASSERT_GE(len, strlen(kData[lost_packet])) << "Incorrect length";
for (size_t i = 0; i < strlen(kData[lost_packet]); i++) {
EXPECT_EQ(kData[lost_packet][i], recovered[i]);
}
ASSERT_TRUE(group.IsFinished());
}
};
TEST_F(QuicFecGroupTest, UpdateAndRevive) {
RunTest(2, 0, false);
RunTest(2, 1, false);
RunTest(3, 0, false);
RunTest(3, 1, false);
RunTest(3, 2, false);
}
TEST_F(QuicFecGroupTest, UpdateAndReviveOutOfOrder) {
RunTest(2, 0, true);
RunTest(2, 1, true);
RunTest(3, 0, true);
RunTest(3, 1, true);
RunTest(3, 2, true);
}
TEST_F(QuicFecGroupTest, UpdateFecIfReceivedPacketIsNotCovered) {
char data1[] = "abc123";
char redundancy[arraysize(data1)];
for (size_t i = 0; i < arraysize(data1); i++) {
redundancy[i] = data1[i];
}
QuicFecGroup group;
QuicPacketHeader header;
header.packet_sequence_number = 3;
group.Update(header, data1);
QuicFecData fec;
fec.min_protected_packet_sequence_number = 1;
fec.redundancy = redundancy;
ASSERT_FALSE(group.UpdateFec(2, fec));
}
TEST_F(QuicFecGroupTest, ProtectsPacketsBefore) {
QuicPacketHeader header;
header.packet_sequence_number = 3;
QuicFecGroup group;
ASSERT_TRUE(group.Update(header, kData[0]));
EXPECT_FALSE(group.ProtectsPacketsBefore(1));
EXPECT_FALSE(group.ProtectsPacketsBefore(2));
EXPECT_FALSE(group.ProtectsPacketsBefore(3));
EXPECT_TRUE(group.ProtectsPacketsBefore(4));
EXPECT_TRUE(group.ProtectsPacketsBefore(5));
EXPECT_TRUE(group.ProtectsPacketsBefore(50));
}
TEST_F(QuicFecGroupTest, ProtectsPacketsBeforeWithSeveralPackets) {
QuicPacketHeader header;
header.packet_sequence_number = 3;
QuicFecGroup group;
ASSERT_TRUE(group.Update(header, kData[0]));
header.packet_sequence_number = 7;
ASSERT_TRUE(group.Update(header, kData[0]));
header.packet_sequence_number = 5;
ASSERT_TRUE(group.Update(header, kData[0]));
EXPECT_FALSE(group.ProtectsPacketsBefore(1));
EXPECT_FALSE(group.ProtectsPacketsBefore(2));
EXPECT_FALSE(group.ProtectsPacketsBefore(3));
EXPECT_TRUE(group.ProtectsPacketsBefore(4));
EXPECT_TRUE(group.ProtectsPacketsBefore(5));
EXPECT_TRUE(group.ProtectsPacketsBefore(6));
EXPECT_TRUE(group.ProtectsPacketsBefore(7));
EXPECT_TRUE(group.ProtectsPacketsBefore(8));
EXPECT_TRUE(group.ProtectsPacketsBefore(9));
EXPECT_TRUE(group.ProtectsPacketsBefore(50));
}
TEST_F(QuicFecGroupTest, ProtectsPacketsBeforeWithFecData) {
QuicFecData fec;
fec.min_protected_packet_sequence_number = 2;
fec.redundancy = kData[0];
QuicFecGroup group;
ASSERT_TRUE(group.UpdateFec(3, fec));
EXPECT_FALSE(group.ProtectsPacketsBefore(1));
EXPECT_FALSE(group.ProtectsPacketsBefore(2));
EXPECT_TRUE(group.ProtectsPacketsBefore(3));
EXPECT_TRUE(group.ProtectsPacketsBefore(4));
EXPECT_TRUE(group.ProtectsPacketsBefore(5));
EXPECT_TRUE(group.ProtectsPacketsBefore(50));
}
} // namespace net