blob: 75288693c1ae0773cb99f1c99b15534099fa648b [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 "net/quic/quic_connection_helper.h"
#include <vector>
#include "net/base/net_errors.h"
#include "net/quic/test_tools/mock_clock.h"
#include "net/quic/test_tools/quic_test_utils.h"
#include "net/quic/test_tools/test_task_runner.h"
#include "net/socket/socket_test_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::_;
namespace net {
class QuicConnectionPeer {
public:
static void SendAck(QuicConnection* connection) {
connection->SendAck();
}
static void SetScheduler(QuicConnection* connection,
QuicSendScheduler* scheduler) {
connection->scheduler_.reset(scheduler);
}
};
namespace test {
const char kData[] = "foo";
class TestConnection : public QuicConnection {
public:
TestConnection(QuicGuid guid,
IPEndPoint address,
QuicConnectionHelper* helper)
: QuicConnection(guid, address, helper) {
}
void SendAck() {
QuicConnectionPeer::SendAck(this);
}
void SetScheduler(QuicSendScheduler* scheduler) {
QuicConnectionPeer::SetScheduler(this, scheduler);
}
};
class QuicConnectionHelperTest : public ::testing::Test {
protected:
// Holds a packet to be written to the wire, and the IO mode that should
// be used by the mock socket when performing the write.
struct PacketToWrite {
PacketToWrite(IoMode mode, QuicEncryptedPacket* packet)
: mode(mode),
packet(packet) {
}
IoMode mode;
QuicEncryptedPacket* packet;
};
QuicConnectionHelperTest()
: guid_(2),
framer_(QuicDecrypter::Create(kNULL), QuicEncrypter::Create(kNULL)),
creator_(guid_, &framer_),
net_log_(BoundNetLog()),
frame_(1, false, 0, kData) {
Initialize();
}
~QuicConnectionHelperTest() {
for (size_t i = 0; i < writes_.size(); i++) {
delete writes_[i].packet;
}
}
// Adds a packet to the list of expected writes.
void AddWrite(IoMode mode, QuicEncryptedPacket* packet) {
writes_.push_back(PacketToWrite(mode, packet));
}
// Returns the packet to be written at position |pos|.
QuicEncryptedPacket* GetWrite(size_t pos) {
return writes_[pos].packet;
}
bool AtEof() {
return socket_data_->at_read_eof() && socket_data_->at_write_eof();
}
// Configures the test fixture to use the list of expected writes.
void Initialize() {
mock_writes_.reset(new MockWrite[writes_.size()]);
for (size_t i = 0; i < writes_.size(); i++) {
mock_writes_[i] = MockWrite(writes_[i].mode,
writes_[i].packet->data(),
writes_[i].packet->length());
};
socket_data_.reset(new StaticSocketDataProvider(NULL, 0, mock_writes_.get(),
writes_.size()));
MockUDPClientSocket* socket =
new MockUDPClientSocket(socket_data_.get(), net_log_.net_log());
socket->Connect(IPEndPoint());
runner_ = new TestTaskRunner(&clock_);
helper_.reset(new QuicConnectionHelper(runner_.get(), &clock_, socket));
scheduler_ = new testing::StrictMock<MockScheduler>();
EXPECT_CALL(*scheduler_, TimeUntilSend(_)).
WillRepeatedly(testing::Return(QuicTime::Delta()));
connection_.reset(new TestConnection(guid_, IPEndPoint(), helper_.get()));
connection_->set_visitor(&visitor_);
connection_->SetScheduler(scheduler_);
}
// Returns a newly created packet to send kData on stream 1.
QuicEncryptedPacket* ConstructDataPacket(
QuicPacketSequenceNumber sequence_number) {
InitializeHeader(sequence_number);
return ConstructPacket(header_, QuicFrame(&frame_));
}
// Returns a newly created packet to send ack data.
QuicEncryptedPacket* ConstructAckPacket(
QuicPacketSequenceNumber sequence_number) {
InitializeHeader(sequence_number);
QuicAckFrame ack(0, sequence_number);
return ConstructPacket(header_, QuicFrame(&ack));
}
// Returns a newly created packet to send congestion feedback data.
QuicEncryptedPacket* ConstructFeedbackPacket(
QuicPacketSequenceNumber sequence_number) {
InitializeHeader(sequence_number);
QuicCongestionFeedbackFrame frame;
frame.type = kTCP;
frame.tcp.accumulated_number_of_lost_packets = 0;
frame.tcp.receive_window = 16000;
return ConstructPacket(header_, QuicFrame(&frame));
}
// Returns a newly created packet to send a connection close frame.
QuicEncryptedPacket* ConstructClosePacket(
QuicPacketSequenceNumber sequence_number,
QuicPacketSequenceNumber least_waiting) {
InitializeHeader(sequence_number);
QuicFrames frames;
QuicAckFrame ack(0, least_waiting);
QuicConnectionCloseFrame close;
close.error_code = QUIC_CONNECTION_TIMED_OUT;
close.ack_frame = ack;
return ConstructPacket(header_, QuicFrame(&close));
}
testing::StrictMock<MockScheduler>* scheduler_;
scoped_refptr<TestTaskRunner> runner_;
scoped_ptr<QuicConnectionHelper> helper_;
scoped_array<MockWrite> mock_writes_;
MockClock clock_;
scoped_ptr<TestConnection> connection_;
testing::StrictMock<MockConnectionVisitor> visitor_;
private:
void InitializeHeader(QuicPacketSequenceNumber sequence_number) {
header_.guid = guid_;
header_.packet_sequence_number = sequence_number;
header_.flags = PACKET_FLAGS_NONE;
header_.fec_group = 0;
}
QuicEncryptedPacket* ConstructPacket(const QuicPacketHeader& header,
const QuicFrame& frame) {
QuicFrames frames;
frames.push_back(frame);
scoped_ptr<QuicPacket> packet(
framer_.ConstructFrameDataPacket(header_, frames));
return framer_.EncryptPacket(*packet);
}
QuicGuid guid_;
QuicFramer framer_;
QuicPacketCreator creator_;
QuicPacketHeader header_;
BoundNetLog net_log_;
QuicStreamFrame frame_;
scoped_ptr<StaticSocketDataProvider> socket_data_;
std::vector<PacketToWrite> writes_;
};
TEST_F(QuicConnectionHelperTest, GetClock) {
EXPECT_EQ(&clock_, helper_->GetClock());
}
TEST_F(QuicConnectionHelperTest, IsSendAlarmSet) {
EXPECT_FALSE(helper_->IsSendAlarmSet());
}
TEST_F(QuicConnectionHelperTest, SetSendAlarm) {
helper_->SetSendAlarm(QuicTime::Delta());
EXPECT_TRUE(helper_->IsSendAlarmSet());
}
TEST_F(QuicConnectionHelperTest, UnregisterSendAlarmIfRegistered) {
helper_->SetSendAlarm(QuicTime::Delta());
helper_->UnregisterSendAlarmIfRegistered() ;
EXPECT_FALSE(helper_->IsSendAlarmSet());
}
TEST_F(QuicConnectionHelperTest, TestResend) {
AddWrite(SYNCHRONOUS, ConstructDataPacket(1));
AddWrite(SYNCHRONOUS, ConstructDataPacket(2));
Initialize();
QuicTime::Delta kDefaultResendTime = QuicTime::Delta::FromMilliseconds(500);
QuicTime start = clock_.Now();
EXPECT_CALL(*scheduler_, SentPacket(1, _, false));
// Send a packet.
connection_->SendStreamData(1, kData, 0, false, NULL);
EXPECT_CALL(*scheduler_, SentPacket(2, _, true));
// Since no ack was received, the resend alarm will fire and resend it.
runner_->RunNextTask();
EXPECT_EQ(kDefaultResendTime, clock_.Now().Subtract(start));
EXPECT_TRUE(AtEof());
}
TEST_F(QuicConnectionHelperTest, InitialTimeout) {
AddWrite(SYNCHRONOUS, ConstructClosePacket(1, 0));
Initialize();
// Verify that a single task was posted.
EXPECT_EQ(1u, runner_->tasks()->size());
EXPECT_EQ(base::TimeDelta::FromMicroseconds(kDefaultTimeoutUs),
runner_->GetTask(0).delta);
EXPECT_CALL(*scheduler_, SentPacket(1, _, false));
// After we run the next task, we should close the connection.
EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, false));
runner_->RunNextTask();
EXPECT_EQ(QuicTime::FromMicroseconds(kDefaultTimeoutUs), clock_.Now());
EXPECT_FALSE(connection_->connected());
EXPECT_TRUE(AtEof());
}
TEST_F(QuicConnectionHelperTest, WritePacketToWire) {
AddWrite(SYNCHRONOUS, ConstructDataPacket(1));
Initialize();
int len = GetWrite(0)->length();
int error = 0;
EXPECT_EQ(len, helper_->WritePacketToWire(*GetWrite(0), &error));
EXPECT_EQ(0, error);
EXPECT_TRUE(AtEof());
}
TEST_F(QuicConnectionHelperTest, WritePacketToWireAsync) {
AddWrite(ASYNC, ConstructClosePacket(1, 0));
Initialize();
EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(testing::Return(true));
int error = 0;
EXPECT_EQ(ERR_IO_PENDING,
helper_->WritePacketToWire(*GetWrite(0), &error));
EXPECT_EQ(0, error);
MessageLoop::current()->RunUntilIdle();
EXPECT_TRUE(AtEof());
}
TEST_F(QuicConnectionHelperTest, TimeoutAfterSend) {
AddWrite(SYNCHRONOUS, ConstructAckPacket(1));
AddWrite(SYNCHRONOUS, ConstructFeedbackPacket(2));
AddWrite(SYNCHRONOUS, ConstructClosePacket(3, 1));
Initialize();
EXPECT_TRUE(connection_->connected());
EXPECT_EQ(0u, clock_.Now().ToMicroseconds());
// When we send a packet, the timeout will change to 5000 + kDefaultTimeout.
clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(5000));
EXPECT_EQ(5000u, clock_.Now().ToMicroseconds());
EXPECT_CALL(*scheduler_, SentPacket(1, _, false));
EXPECT_CALL(*scheduler_, SentPacket(2, _, false));
// Send an ack so we don't set the resend alarm.
connection_->SendAck();
// The original alarm will fire. We should not time out because we had a
// network event at t=5000. The alarm will reregister.
runner_->RunNextTask();
EXPECT_EQ(QuicTime::FromMicroseconds(kDefaultTimeoutUs), clock_.Now());
EXPECT_TRUE(connection_->connected());
// This time, we should time out.
EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, false));
EXPECT_CALL(*scheduler_, SentPacket(3, _, false));
runner_->RunNextTask();
EXPECT_EQ(kDefaultTimeoutUs + 5000, clock_.Now().ToMicroseconds());
EXPECT_FALSE(connection_->connected());
EXPECT_TRUE(AtEof());
}
TEST_F(QuicConnectionHelperTest, SendSchedulerDelayThenSend) {
AddWrite(SYNCHRONOUS, ConstructDataPacket(1));
Initialize();
// Test that if we send a packet with a delay, it ends up queued.
EXPECT_CALL(*scheduler_, TimeUntilSend(true)).WillOnce(testing::Return(
QuicTime::Delta::FromMicroseconds(1)));
connection_->SendStreamData(1, kData, 0, false, NULL);
EXPECT_CALL(*scheduler_, SentPacket(1, _, false));
EXPECT_EQ(1u, connection_->NumQueuedPackets());
// Advance the clock to fire the alarm, and configure the scheduler
// to permit the packet to be sent.
EXPECT_CALL(*scheduler_, TimeUntilSend(true)).WillOnce(testing::Return(
QuicTime::Delta()));
EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(testing::Return(true));
runner_->RunNextTask();
EXPECT_EQ(0u, connection_->NumQueuedPackets());
EXPECT_TRUE(AtEof());
}
} // namespace test
} // namespace net