1 /*
2  * Copyright (c) Facebook, Inc. and its affiliates.
3  *
4  * This source code is licensed under the MIT license found in the
5  * LICENSE file in the root directory of this source tree.
6  *
7  */
8 
9 #include <folly/portability/GTest.h>
10 #include <quic/common/test/TestUtils.h>
11 #include <quic/congestion_control/QuicCubic.h>
12 
13 using namespace testing;
14 
15 namespace quic {
16 namespace test {
17 
18 class CubicRecoveryTest : public Test {};
19 
TEST_F(CubicRecoveryTest,LossBurst)20 TEST_F(CubicRecoveryTest, LossBurst) {
21   QuicConnectionStateBase conn(QuicNodeType::Client);
22   Cubic cubic(conn);
23   uint64_t totalSent = 0;
24   auto packet0 = makeTestingWritePacket(0, 1000, 1000 + totalSent);
25   // Send and loss immediately
26   cubic.onPacketSent(packet0);
27   totalSent += 1000;
28   CongestionController::LossEvent loss;
29   loss.addLostPacket(packet0);
30   cubic.onPacketAckOrLoss(folly::none, std::move(loss));
31   EXPECT_EQ(CubicStates::FastRecovery, cubic.state());
32   auto cwndAfterLoss = cubic.getCongestionWindow();
33 
34   // Then lose a few more:
35   CongestionController::LossEvent loss2;
36   for (size_t i = 1; i < 5; i++) {
37     auto packet = makeTestingWritePacket(i, 1000, 1000 + totalSent);
38     cubic.onPacketSent(packet);
39     totalSent += 1000;
40     conn.lossState.largestSent = i;
41     loss2.addLostPacket(packet);
42   }
43   cubic.onPacketAckOrLoss(folly::none, std::move(loss2));
44   // Still in recovery:
45   EXPECT_EQ(CubicStates::FastRecovery, cubic.state());
46   // Cwnd should be reduced.
47   EXPECT_GT(cwndAfterLoss, cubic.getCongestionWindow());
48 }
49 
TEST_F(CubicRecoveryTest,LossBeforeRecovery)50 TEST_F(CubicRecoveryTest, LossBeforeRecovery) {
51   QuicConnectionStateBase conn(QuicNodeType::Client);
52   Cubic cubic(conn);
53   uint64_t totalSent = 0;
54 
55   // Send/ack one packet.
56   auto packet = makeTestingWritePacket(0, 1000, 1000 + totalSent);
57   cubic.onPacketSent(packet);
58   totalSent += 1000;
59   cubic.onPacketAckOrLoss(
60       makeAck(0, 1000, Clock::now(), packet.metadata.time), folly::none);
61   EXPECT_EQ(CubicStates::Hystart, cubic.state());
62 
63   // Send three packets, lose second immediately.
64   auto packet1 = makeTestingWritePacket(1, 1000, 1000 + totalSent);
65   totalSent += 1000;
66   auto packet2 = makeTestingWritePacket(2, 1000, 1000 + totalSent);
67   totalSent += 1000;
68   auto packet3 = makeTestingWritePacket(3, 1000, 1000 + totalSent);
69   totalSent += 1000;
70   cubic.onPacketSent(packet1);
71   cubic.onPacketSent(packet2);
72   cubic.onPacketSent(packet3);
73   conn.lossState.largestSent = 3;
74   CongestionController::LossEvent loss2;
75   loss2.addLostPacket(packet2);
76   cubic.onPacketAckOrLoss(folly::none, std::move(loss2));
77 
78   // Should now be in recovery. Send packet4, receive acks for 3 and 4 which
79   // should exit recovery with a certain cwnd.
80   EXPECT_EQ(CubicStates::FastRecovery, cubic.state());
81   auto packet4 = makeTestingWritePacket(4, 1000, 1000 + totalSent);
82   cubic.onPacketSent(packet4);
83   totalSent += 1000;
84   conn.lossState.largestSent = 4;
85   cubic.onPacketAckOrLoss(
86       makeAck(3, 1000, Clock::now(), packet3.metadata.time), folly::none);
87   cubic.onPacketAckOrLoss(
88       makeAck(4, 1000, Clock::now(), packet4.metadata.time), folly::none);
89   auto cwndAfterRecovery = cubic.getCongestionWindow();
90   EXPECT_EQ(CubicStates::Steady, cubic.state());
91 
92   // Now lose packet1, which should be ignored.
93   CongestionController::LossEvent loss1;
94   loss1.addLostPacket(packet1);
95   cubic.onPacketAckOrLoss(folly::none, std::move(loss1));
96   EXPECT_EQ(CubicStates::Steady, cubic.state());
97   EXPECT_EQ(cwndAfterRecovery, cubic.getCongestionWindow());
98 }
99 
TEST_F(CubicRecoveryTest,LossAfterRecovery)100 TEST_F(CubicRecoveryTest, LossAfterRecovery) {
101   QuicConnectionStateBase conn(QuicNodeType::Client);
102   Cubic cubic(conn);
103 
104   // Send/ack one packet.
105   auto packet = makeTestingWritePacket(0, 1000, 1000);
106   cubic.onPacketSent(packet);
107   cubic.onPacketAckOrLoss(
108       makeAck(0, 1000, Clock::now(), packet.metadata.time), folly::none);
109   // Lose one packet.
110   auto packet1 = makeTestingWritePacket(1, 1000, 2000);
111   cubic.onPacketSent(packet1);
112   conn.lossState.largestSent = 1;
113   CongestionController::LossEvent loss1;
114   loss1.addLostPacket(packet1);
115   cubic.onPacketAckOrLoss(folly::none, std::move(loss1));
116   EXPECT_EQ(CubicStates::FastRecovery, cubic.state());
117   auto cwndAfterLoss = cubic.getCongestionWindow();
118 
119   // Lose another packet, cwnd should go down.
120   auto packet2 = makeTestingWritePacket(2, 1000, 3000);
121   cubic.onPacketSent(packet1);
122   conn.lossState.largestSent = 2;
123   CongestionController::LossEvent loss2;
124   loss2.addLostPacket(packet2);
125   cubic.onPacketAckOrLoss(folly::none, std::move(loss2));
126   EXPECT_EQ(CubicStates::FastRecovery, cubic.state());
127   EXPECT_TRUE(cwndAfterLoss > cubic.getCongestionWindow());
128 }
129 
TEST_F(CubicRecoveryTest,AckNotLargestNotChangeCwnd)130 TEST_F(CubicRecoveryTest, AckNotLargestNotChangeCwnd) {
131   QuicConnectionStateBase conn(QuicNodeType::Client);
132   Cubic cubic(conn);
133   auto packet1 = makeTestingWritePacket(0, 1000, 1000);
134   auto packet2 = makeTestingWritePacket(1, 1000, 2000);
135   auto packet3 = makeTestingWritePacket(2, 1000, 3000);
136   auto packet4 = makeTestingWritePacket(3, 1000, 4000);
137   auto packet5 = makeTestingWritePacket(4, 1000, 5000);
138 
139   CongestionController::LossEvent loss;
140   cubic.onPacketSent(packet1);
141   cubic.onPacketSent(packet2);
142   cubic.onPacketSent(packet3);
143   cubic.onPacketSent(packet4);
144   cubic.onPacketSent(packet5);
145   conn.lossState.largestSent = 4;
146 
147   // packet5 is lost:
148   loss.addLostPacket(packet5);
149   cubic.onPacketAckOrLoss(folly::none, std::move(loss));
150   EXPECT_EQ(CubicStates::FastRecovery, cubic.state());
151   auto cwndAfterLoss = cubic.getWritableBytes() + 4000; // 4k are in flight
152 
153   // the the rest are acked:
154   cubic.onPacketAckOrLoss(
155       makeAck(0, 1000, Clock::now(), packet1.metadata.time), folly::none);
156   cubic.onPacketAckOrLoss(
157       makeAck(1, 1000, Clock::now(), packet2.metadata.time), folly::none);
158   cubic.onPacketAckOrLoss(
159       makeAck(2, 1000, Clock::now(), packet3.metadata.time), folly::none);
160   cubic.onPacketAckOrLoss(
161       makeAck(3, 1000, Clock::now(), packet4.metadata.time), folly::none);
162 
163   // Still in recovery:
164   EXPECT_EQ(CubicStates::FastRecovery, cubic.state());
165 
166   // Cwnd never changed during the whole time, and inflight is 0 at this point:
167   EXPECT_EQ(cwndAfterLoss, cubic.getWritableBytes());
168 }
169 } // namespace test
170 } // namespace quic
171