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