using std::vector;
using testing::_;
using testing::AnyNumber;
+using testing::AtLeast;
using testing::ContainerEq;
using testing::Contains;
using testing::DoAll;
return true;
}
- MOCK_METHOD4(RecordIncomingPacket,
- void(QuicByteCount, QuicPacketSequenceNumber, QuicTime, bool));
+ MOCK_METHOD3(RecordIncomingPacket,
+ void(QuicByteCount, QuicPacketSequenceNumber, QuicTime));
private:
QuicCongestionFeedbackFrame* feedback_;
TestPacketWriter()
: last_packet_size_(0),
write_blocked_(false),
- block_next_write_(false),
+ block_on_next_write_(false),
is_write_blocked_data_buffered_(false),
is_server_(true),
final_bytes_of_last_packet_(0),
virtual WriteResult WritePacket(
const char* buffer, size_t buf_len,
const IPAddressNumber& self_address,
- const IPEndPoint& peer_address,
- QuicBlockedWriterInterface* blocked_writer) OVERRIDE {
+ const IPEndPoint& peer_address) OVERRIDE {
QuicEncryptedPacket packet(buffer, buf_len);
++packets_write_attempts_;
visitor_.Reset();
framer.set_visitor(&visitor_);
EXPECT_TRUE(framer.ProcessPacket(packet));
- if (block_next_write_) {
+ if (block_on_next_write_) {
write_blocked_ = true;
- block_next_write_ = false;
+ block_on_next_write_ = false;
}
if (IsWriteBlocked()) {
return WriteResult(WRITE_STATUS_BLOCKED, -1);
virtual void SetWritable() OVERRIDE { write_blocked_ = false; }
- void BlockNextWrite() { block_next_write_ = true; }
+ void BlockOnNextWrite() { block_on_next_write_ = true; }
// Resets the visitor's state by clearing out the headers and frames.
void Reset() {
FramerVisitorCapturingFrames visitor_;
size_t last_packet_size_;
bool write_blocked_;
- bool block_next_write_;
+ bool block_on_next_write_;
bool is_write_blocked_data_buffered_;
bool is_server_;
uint32 final_bytes_of_last_packet_;
return SendStreamDataWithString(kStreamId5, "food2", 0, !kFin, NULL);
}
+ // Ensures the connection can write stream data before writing.
+ QuicConsumedData EnsureWritableAndSendStreamData5() {
+ EXPECT_TRUE(CanWriteStreamData());
+ return SendStreamData5();
+ }
+
// The crypto stream has special semantics so that it is not blocked by a
// congestion window limitation, and also so that it gets put into a separate
// packet (so that it is easier to reason about a crypto frame not being
}
void set_version(QuicVersion version) {
- framer_.set_version(version);
+ QuicConnectionPeer::GetFramer(this)->set_version(version);
}
void set_is_server(bool is_server) {
*send_algorithm_, TimeUntilSend(_, _, _, _)).WillRepeatedly(Return(
QuicTime::Delta::Zero()));
EXPECT_CALL(*receive_algorithm_,
- RecordIncomingPacket(_, _, _, _)).Times(AnyNumber());
+ RecordIncomingPacket(_, _, _)).Times(AnyNumber());
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
.Times(AnyNumber());
EXPECT_CALL(*send_algorithm_, RetransmissionDelay()).WillRepeatedly(
}
size_t ProcessFecProtectedPacket(QuicPacketSequenceNumber number,
- bool expect_revival, bool entropy_flag) {
+ bool expect_revival, bool entropy_flag) {
if (expect_revival) {
EXPECT_CALL(visitor_, OnStreamFrames(_)).WillOnce(Return(accept_packet_));
}
QuicConnectionPeer::GetConnectionClosePacket(&connection_) == NULL);
}
+ void BlockOnNextWrite() {
+ writer_->BlockOnNextWrite();
+ EXPECT_CALL(visitor_, OnWriteBlocked()).Times(AtLeast(1));
+ }
+
QuicGuid guid_;
QuicFramer framer_;
QuicPacketCreator creator_;
// This should be fine.
creator_.set_sequence_number(1);
QuicAckFrame frame2 = InitAckFrame(0, 1);
- // The scheduler will not process out of order acks.
- EXPECT_CALL(visitor_, OnCanWrite()).Times(0);
+ // The scheduler will not process out of order acks, but all packet processing
+ // causes the connection to try to write.
+ EXPECT_CALL(visitor_, OnCanWrite()).Times(1);
ProcessAckPacket(&frame2);
// Now claim it's one, but set the ordering so it was sent "after" the first
TEST_F(QuicConnectionTest, BasicSending) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
-// EXPECT_CALL(*send_algorithm_, UpdateRtt(_));
-// EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _)).Times(6);
QuicPacketSequenceNumber last_packet;
SendStreamDataToPeer(1, "foo", 0, !kFin, &last_packet); // Packet 1
EXPECT_EQ(1u, last_packet);
connection_.options()->max_packets_per_fec_group = 2;
EXPECT_EQ(0u, connection_.NumQueuedPackets());
- writer_->BlockNextWrite();
+ BlockOnNextWrite();
const string payload(payload_length, 'a');
connection_.SendStreamDataWithString(1, payload, 0, !kFin, NULL);
EXPECT_FALSE(creator_.ShouldSendFec(true));
// Try to send two stream frames in 1 packet by using writev.
EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, NOT_RETRANSMISSION, _));
- writer_->BlockNextWrite();
+ BlockOnNextWrite();
char data[] = "ABCD";
IOVector data_iov;
data_iov.AppendNoCoalesce(data, 2);
NackPacket(2, &nack_two);
// The first nack should trigger a fast retransmission, but we'll be
// write blocked, so the packet will be queued.
- writer_->BlockNextWrite();
+ BlockOnNextWrite();
EXPECT_CALL(*send_algorithm_, UpdateRtt(_));
EXPECT_CALL(*send_algorithm_, OnPacketAcked(_, _));
-
ProcessAckPacket(&nack_two);
EXPECT_EQ(1u, connection_.NumQueuedPackets());
}
// Block the congestion window and ensure they're queued.
- writer_->BlockNextWrite();
+ BlockOnNextWrite();
clock_.AdvanceTime(DefaultRetransmissionTime());
// Only one packet should be retransmitted.
EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
}
TEST_F(QuicConnectionTest, WriteBlockedThenSent) {
- writer_->BlockNextWrite();
+ BlockOnNextWrite();
writer_->set_is_write_blocked_data_buffered(true);
-
connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL);
EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
TEST_F(QuicConnectionTest, WriteBlockedAckedThenSent) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
- writer_->BlockNextWrite();
-
+ BlockOnNextWrite();
writer_->set_is_write_blocked_data_buffered(true);
connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL);
EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
connection_.SendStreamDataWithString(3, "foo", 0, !kFin, NULL);
EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
- writer_->BlockNextWrite();
+ BlockOnNextWrite();
writer_->set_is_write_blocked_data_buffered(true);
-
// Simulate the retransmission alarm firing.
EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(_));
clock_.AdvanceTime(DefaultRetransmissionTime());
ProcessAckPacket(&ack);
connection_.OnPacketSent(WriteResult(WRITE_STATUS_OK, 0));
- EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
+ // The retransmission alarm should not be set because there are
+ // no unacked packets.
+ EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
}
TEST_F(QuicConnectionTest, ResumptionAlarmWhenWriteBlocked) {
// Block the connection.
- writer_->BlockNextWrite();
+ BlockOnNextWrite();
connection_.SendStreamDataWithString(3, "foo", 0, !kFin, NULL);
EXPECT_EQ(1u, writer_->packets_write_attempts());
EXPECT_TRUE(writer_->IsWriteBlocked());
// Don't send missing packet 2.
ProcessFecProtectedPacket(3, false, !kEntropyFlag);
ProcessFecPacket(4, 1, true, kEntropyFlag, NULL);
- // Entropy flag should be true, so entropy should not be 0.
- EXPECT_NE(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 2));
+ // Ensure QUIC no longer revives entropy for lost packets.
+ EXPECT_EQ(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 2));
+ EXPECT_NE(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 4));
}
TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPacket) {
ProcessFecProtectedPacket(3, false, kEntropyFlag);
ProcessFecProtectedPacket(4, false, kEntropyFlag);
ProcessFecProtectedPacket(5, true, !kEntropyFlag);
- // Entropy flag should be true, so entropy should be 0.
- EXPECT_NE(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 2));
+ // Ensure entropy is not revived for the missing packet.
+ EXPECT_EQ(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 2));
+ EXPECT_NE(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 3));
}
TEST_F(QuicConnectionTest, RTO) {
}
TEST_F(QuicConnectionTest, SetRTOAfterWritingToSocket) {
- writer_->BlockNextWrite();
+ BlockOnNextWrite();
connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL);
// Make sure that RTO is not started when the packet is queued.
EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
TEST_F(QuicConnectionTest, TestQueued) {
EXPECT_EQ(0u, connection_.NumQueuedPackets());
- writer_->BlockNextWrite();
+ BlockOnNextWrite();
connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL);
EXPECT_EQ(1u, connection_.NumQueuedPackets());
TEST_F(QuicConnectionTest, UpdateQuicCongestionFeedbackFrame) {
SendAckPacketToPeer();
- EXPECT_CALL(*receive_algorithm_, RecordIncomingPacket(_, _, _, _));
+ EXPECT_CALL(*receive_algorithm_, RecordIncomingPacket(_, _, _));
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessPacket(1);
}
SendAckPacketToPeer();
// Process an FEC packet, and revive the missing data packet
// but only contact the receive_algorithm once.
- EXPECT_CALL(*receive_algorithm_, RecordIncomingPacket(_, _, _, _));
+ EXPECT_CALL(*receive_algorithm_, RecordIncomingPacket(_, _, _));
ProcessFecPacket(2, 1, true, !kEntropyFlag, NULL);
}
TEST_F(QuicConnectionTest, SendSchedulerEAGAIN) {
QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag);
- writer_->BlockNextWrite();
+ BlockOnNextWrite();
EXPECT_CALL(*send_algorithm_,
TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce(
testing::Return(QuicTime::Delta::Zero()));
EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
}
+TEST_F(QuicConnectionTest, BundleAckWithDataOnIncomingAck) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ connection_.SendStreamDataWithString(kStreamId3, "foo", 0, !kFin, NULL);
+ connection_.SendStreamDataWithString(kStreamId3, "foo", 3, !kFin, NULL);
+ // Ack the second packet, which will retransmit the first packet.
+ QuicAckFrame ack = InitAckFrame(2, 0);
+ NackPacket(1, &ack);
+ EXPECT_CALL(*send_algorithm_, UpdateRtt(_));
+ EXPECT_CALL(*send_algorithm_, OnPacketAcked(2, _)).Times(1);
+ EXPECT_CALL(*send_algorithm_, OnPacketLost(1, _)).Times(1);
+ EXPECT_CALL(*send_algorithm_, OnPacketAbandoned(1, _)).Times(1);
+ ProcessAckPacket(&ack);
+ EXPECT_EQ(1u, writer_->frame_count());
+ EXPECT_EQ(1u, writer_->stream_frames()->size());
+ writer_->Reset();
+
+ // Now ack the retransmission, which will both raise the high water mark
+ // and see if there is more data to send.
+ ack = InitAckFrame(3, 0);
+ NackPacket(1, &ack);
+ EXPECT_CALL(*send_algorithm_, UpdateRtt(_));
+ EXPECT_CALL(*send_algorithm_, OnPacketAcked(3, _)).Times(1);
+ ProcessAckPacket(&ack);
+ // TODO(ianswett): This extra ack should not be necessary, because the above
+ // ack raises the high water mark.
+ ProcessAckPacket(&ack);
+
+ // Check that ack alarm is set, but no packet is sent.
+ EXPECT_EQ(0u, writer_->frame_count());
+ EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+ writer_->Reset();
+
+ // Send the same ack, but send both data and an ack together.
+ ack = InitAckFrame(3, 0);
+ NackPacket(1, &ack);
+ EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(DoAll(
+ IgnoreResult(InvokeWithoutArgs(
+ &connection_,
+ &TestConnection::EnsureWritableAndSendStreamData5)),
+ Return(true)));
+ ProcessAckPacket(&ack);
+
+ // Check that ack is bundled with outgoing data and the delayed ack
+ // alarm is reset.
+ EXPECT_EQ(2u, writer_->frame_count());
+ EXPECT_TRUE(writer_->ack());
+ EXPECT_EQ(1u, writer_->stream_frames()->size());
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+}
+
TEST_F(QuicConnectionTest, NoAckForClose) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
ProcessPacket(1);
EXPECT_EQ(146u, outgoing_ack()->received_info.entropy_hash);
}
+TEST_F(QuicConnectionTest, ReceivedEntropyHashCalculationHalfFEC) {
+ // FEC packets should not change the entropy hash calculation.
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).WillRepeatedly(Return(true));
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ ProcessDataPacket(1, 1, kEntropyFlag);
+ ProcessFecPacket(4, 1, false, kEntropyFlag, NULL);
+ ProcessDataPacket(3, 3, !kEntropyFlag);
+ ProcessFecPacket(7, 3, false, kEntropyFlag, NULL);
+ EXPECT_EQ(146u, outgoing_ack()->received_info.entropy_hash);
+}
+
TEST_F(QuicConnectionTest, UpdateEntropyForReceivedPackets) {
EXPECT_CALL(visitor_, OnStreamFrames(_)).WillRepeatedly(Return(true));
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
framer_.set_version(QuicVersionMax());
connection_.set_is_server(true);
- writer_->BlockNextWrite();
+ BlockOnNextWrite();
connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted);
EXPECT_EQ(0u, writer_->last_packet_size());
EXPECT_TRUE(connection_.HasQueuedData());
framer_.set_version(QuicVersionMax());
connection_.set_is_server(true);
- writer_->BlockNextWrite();
+ BlockOnNextWrite();
writer_->set_is_write_blocked_data_buffered(true);
connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted);
EXPECT_EQ(0u, writer_->last_packet_size());
}
TEST_F(QuicConnectionTest, ConnectionCloseGettingWriteBlocked) {
- writer_->BlockNextWrite();
+ BlockOnNextWrite();
TriggerConnectionClose();
EXPECT_EQ(1u, writer_->packets_write_attempts());
EXPECT_TRUE(writer_->IsWriteBlocked());
}
TEST_F(QuicConnectionTest, ConnectionCloseWhenWriteBlocked) {
- writer_->BlockNextWrite();
+ BlockOnNextWrite();
connection_.SendStreamDataWithString(1, "foo", 0, !kFin, NULL);
EXPECT_EQ(1u, connection_.NumQueuedPackets());
EXPECT_EQ(1u, writer_->packets_write_attempts());