summaryrefslogtreecommitdiff
path: root/chromium/net/quic/congestion_control/tcp_cubic_sender_test.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/net/quic/congestion_control/tcp_cubic_sender_test.cc')
-rw-r--r--chromium/net/quic/congestion_control/tcp_cubic_sender_test.cc256
1 files changed, 256 insertions, 0 deletions
diff --git a/chromium/net/quic/congestion_control/tcp_cubic_sender_test.cc b/chromium/net/quic/congestion_control/tcp_cubic_sender_test.cc
new file mode 100644
index 00000000000..e9f88937426
--- /dev/null
+++ b/chromium/net/quic/congestion_control/tcp_cubic_sender_test.cc
@@ -0,0 +1,256 @@
+// 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 "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/quic/congestion_control/tcp_cubic_sender.h"
+#include "net/quic/congestion_control/tcp_receiver.h"
+#include "net/quic/test_tools/mock_clock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+const uint32 kDefaultWindowTCP = 10 * kMaxPacketSize;
+const QuicByteCount kNoNBytesInFlight = 0;
+
+class TcpCubicSenderPeer : public TcpCubicSender {
+ public:
+ // TODO(ianswett): Remove 10000 once b/10075719 is fixed.
+ TcpCubicSenderPeer(const QuicClock* clock, bool reno)
+ : TcpCubicSender(clock, reno, 10000) {
+ }
+ using TcpCubicSender::AvailableCongestionWindow;
+ using TcpCubicSender::CongestionWindow;
+ using TcpCubicSender::AckAccounting;
+};
+
+class TcpCubicSenderTest : public ::testing::Test {
+ protected:
+ TcpCubicSenderTest()
+ : rtt_(QuicTime::Delta::FromMilliseconds(60)),
+ one_ms_(QuicTime::Delta::FromMilliseconds(1)),
+ sender_(new TcpCubicSenderPeer(&clock_, true)),
+ receiver_(new TcpReceiver()),
+ sequence_number_(1),
+ acked_sequence_number_(0) {
+ }
+
+ void SendAvailableCongestionWindow() {
+ QuicByteCount bytes_to_send = sender_->AvailableCongestionWindow();
+ while (bytes_to_send > 0) {
+ QuicByteCount bytes_in_packet = std::min(kMaxPacketSize, bytes_to_send);
+ sender_->SentPacket(clock_.Now(), sequence_number_++, bytes_in_packet,
+ NOT_RETRANSMISSION);
+ bytes_to_send -= bytes_in_packet;
+ if (bytes_to_send > 0) {
+ EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
+ }
+ }
+ }
+ // Normal is that TCP acks every other segment.
+ void AckNPackets(int n) {
+ for (int i = 0; i < n; ++i) {
+ acked_sequence_number_++;
+ sender_->OnIncomingAck(acked_sequence_number_, kMaxPacketSize, rtt_);
+ }
+ clock_.AdvanceTime(one_ms_); // 1 millisecond.
+ }
+
+ const QuicTime::Delta rtt_;
+ const QuicTime::Delta one_ms_;
+ MockClock clock_;
+ SendAlgorithmInterface::SentPacketsMap not_used_;
+ scoped_ptr<TcpCubicSenderPeer> sender_;
+ scoped_ptr<TcpReceiver> receiver_;
+ QuicPacketSequenceNumber sequence_number_;
+ QuicPacketSequenceNumber acked_sequence_number_;
+};
+
+TEST_F(TcpCubicSenderTest, SimpleSender) {
+ QuicCongestionFeedbackFrame feedback;
+ // At startup make sure we are at the default.
+ EXPECT_EQ(kDefaultWindowTCP,
+ sender_->AvailableCongestionWindow());
+ // At startup make sure we can send.
+ EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
+ NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
+ // Get default QuicCongestionFeedbackFrame from receiver.
+ ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback));
+ sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(),
+ not_used_);
+ // Make sure we can send.
+ EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
+ NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
+ // And that window is un-affected.
+ EXPECT_EQ(kDefaultWindowTCP, sender_->AvailableCongestionWindow());
+
+ // A retransmitt should always retun 0.
+ EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
+ IS_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
+}
+
+TEST_F(TcpCubicSenderTest, ExponentialSlowStart) {
+ const int kNumberOfAck = 20;
+ QuicCongestionFeedbackFrame feedback;
+ // At startup make sure we can send.
+ EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
+ NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
+ // Get default QuicCongestionFeedbackFrame from receiver.
+ ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback));
+ sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(),
+ not_used_);
+ // Make sure we can send.
+ EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
+ NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
+
+ for (int n = 0; n < kNumberOfAck; ++n) {
+ // Send our full congestion window.
+ SendAvailableCongestionWindow();
+ AckNPackets(2);
+ }
+ QuicByteCount bytes_to_send = sender_->CongestionWindow();
+ EXPECT_EQ(kDefaultWindowTCP + kMaxPacketSize * 2 * kNumberOfAck,
+ bytes_to_send);
+}
+
+TEST_F(TcpCubicSenderTest, SlowStartAckTrain) {
+ // Make sure that we fall out of slow start when we send ACK train longer
+ // than half the RTT, in this test case 30ms, which is more than 30 calls to
+ // Ack2Packets in one round.
+ // Since we start at 10 packet first round will be 5 second round 10 etc
+ // Hence we should pass 30 at 65 = 5 + 10 + 20 + 30
+ const int kNumberOfAck = 65;
+ QuicCongestionFeedbackFrame feedback;
+ // At startup make sure we can send.
+ EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
+ NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
+ // Get default QuicCongestionFeedbackFrame from receiver.
+ ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback));
+ sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(),
+ not_used_);
+ // Make sure we can send.
+ EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
+ NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
+
+ for (int n = 0; n < kNumberOfAck; ++n) {
+ // Send our full congestion window.
+ SendAvailableCongestionWindow();
+ AckNPackets(2);
+ }
+ QuicByteCount expected_congestion_window =
+ kDefaultWindowTCP + (kMaxPacketSize * 2 * kNumberOfAck);
+ EXPECT_EQ(expected_congestion_window, sender_->CongestionWindow());
+ // We should now have fallen out of slow start.
+ SendAvailableCongestionWindow();
+ AckNPackets(2);
+ expected_congestion_window += kMaxPacketSize;
+ EXPECT_EQ(expected_congestion_window, sender_->CongestionWindow());
+
+ // Testing Reno phase.
+ // We should need 141(65*2+1+10) ACK:ed packets before increasing window by
+ // one.
+ for (int m = 0; m < 70; ++m) {
+ SendAvailableCongestionWindow();
+ AckNPackets(2);
+ EXPECT_EQ(expected_congestion_window, sender_->CongestionWindow());
+ }
+ SendAvailableCongestionWindow();
+ AckNPackets(2);
+ expected_congestion_window += kMaxPacketSize;
+ EXPECT_EQ(expected_congestion_window, sender_->CongestionWindow());
+}
+
+TEST_F(TcpCubicSenderTest, SlowStartPacketLoss) {
+ // Make sure that we fall out of slow start when we encounter a packet loss.
+ const int kNumberOfAck = 10;
+ QuicCongestionFeedbackFrame feedback;
+ // At startup make sure we can send.
+ EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
+ NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
+ // Get default QuicCongestionFeedbackFrame from receiver.
+ ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback));
+ sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(),
+ not_used_);
+ // Make sure we can send.
+ EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
+ NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero());
+
+ for (int i = 0; i < kNumberOfAck; ++i) {
+ // Send our full congestion window.
+ SendAvailableCongestionWindow();
+ AckNPackets(2);
+ }
+ SendAvailableCongestionWindow();
+ QuicByteCount expected_congestion_window = kDefaultWindowTCP +
+ (kMaxPacketSize * 2 * kNumberOfAck);
+ EXPECT_EQ(expected_congestion_window, sender_->CongestionWindow());
+
+ sender_->OnIncomingLoss(clock_.Now());
+
+ // Make sure that we should not send right now.
+ EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsInfinite());
+
+ // We should now have fallen out of slow start.
+ // We expect window to be cut in half.
+ expected_congestion_window /= 2;
+ EXPECT_EQ(expected_congestion_window, sender_->CongestionWindow());
+
+ // Testing Reno phase.
+ // We need to ack half of the pending packet before we can send agin.
+ int number_of_packets_in_window = expected_congestion_window / kMaxPacketSize;
+ AckNPackets(number_of_packets_in_window);
+ EXPECT_EQ(expected_congestion_window, sender_->CongestionWindow());
+ EXPECT_EQ(0u, sender_->AvailableCongestionWindow());
+
+ AckNPackets(1);
+ expected_congestion_window += kMaxPacketSize;
+ number_of_packets_in_window++;
+ EXPECT_EQ(expected_congestion_window, sender_->CongestionWindow());
+
+ // We should need number_of_packets_in_window ACK:ed packets before
+ // increasing window by one.
+ for (int k = 0; k < number_of_packets_in_window; ++k) {
+ SendAvailableCongestionWindow();
+ AckNPackets(1);
+ EXPECT_EQ(expected_congestion_window, sender_->CongestionWindow());
+ }
+ SendAvailableCongestionWindow();
+ AckNPackets(1);
+ expected_congestion_window += kMaxPacketSize;
+ EXPECT_EQ(expected_congestion_window, sender_->CongestionWindow());
+}
+
+TEST_F(TcpCubicSenderTest, RetransmissionDelay) {
+ const int64 kRttMs = 10;
+ const int64 kDeviationMs = 3;
+ EXPECT_EQ(QuicTime::Delta::Zero(), sender_->RetransmissionDelay());
+
+ sender_->AckAccounting(QuicTime::Delta::FromMilliseconds(kRttMs));
+
+ // Initial value is to set the median deviation to half of the initial
+ // rtt, the median in then multiplied by a factor of 4 and finaly the
+ // smoothed rtt is added which is the inital rtt.
+ QuicTime::Delta expected_delay =
+ QuicTime::Delta::FromMilliseconds(kRttMs + kRttMs / 2 * 4);
+ EXPECT_EQ(expected_delay, sender_->RetransmissionDelay());
+
+ for (int i = 0; i < 100; ++i) {
+ // Run to make sure that we converge.
+ sender_->AckAccounting(
+ QuicTime::Delta::FromMilliseconds(kRttMs + kDeviationMs));
+ sender_->AckAccounting(
+ QuicTime::Delta::FromMilliseconds(kRttMs - kDeviationMs));
+ }
+ expected_delay = QuicTime::Delta::FromMilliseconds(kRttMs + kDeviationMs * 4);
+
+ EXPECT_NEAR(kRttMs, sender_->SmoothedRtt().ToMilliseconds(), 1);
+ EXPECT_NEAR(expected_delay.ToMilliseconds(),
+ sender_->RetransmissionDelay().ToMilliseconds(),
+ 1);
+}
+} // namespace test
+} // namespace net