1 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
2 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
3 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
4 // option. This file may not be copied, modified, or distributed
5 // except according to those terms.
6 
7 // Congestion control
8 #![deny(clippy::pedantic)]
9 
10 use crate::cc::new_reno::NewReno;
11 use crate::cc::{ClassicCongestionControl, CongestionControl, CWND_INITIAL, MAX_DATAGRAM_SIZE};
12 use crate::packet::PacketType;
13 use crate::tracking::SentPacket;
14 use std::time::Duration;
15 use test_fixture::now;
16 
17 const PTO: Duration = Duration::from_millis(100);
18 const RTT: Duration = Duration::from_millis(98);
19 
cwnd_is_default(cc: &ClassicCongestionControl<NewReno>)20 fn cwnd_is_default(cc: &ClassicCongestionControl<NewReno>) {
21     assert_eq!(cc.cwnd(), CWND_INITIAL);
22     assert_eq!(cc.ssthresh(), usize::MAX);
23 }
24 
cwnd_is_halved(cc: &ClassicCongestionControl<NewReno>)25 fn cwnd_is_halved(cc: &ClassicCongestionControl<NewReno>) {
26     assert_eq!(cc.cwnd(), CWND_INITIAL / 2);
27     assert_eq!(cc.ssthresh(), CWND_INITIAL / 2);
28 }
29 
30 #[test]
issue_876()31 fn issue_876() {
32     let mut cc = ClassicCongestionControl::new(NewReno::default());
33     let time_now = now();
34     let time_before = time_now - Duration::from_millis(100);
35     let time_after = time_now + Duration::from_millis(150);
36 
37     let sent_packets = &[
38         SentPacket::new(
39             PacketType::Short,
40             1,                     // pn
41             time_before,           // time sent
42             true,                  // ack eliciting
43             Vec::new(),            // tokens
44             MAX_DATAGRAM_SIZE - 1, // size
45         ),
46         SentPacket::new(
47             PacketType::Short,
48             2,                     // pn
49             time_before,           // time sent
50             true,                  // ack eliciting
51             Vec::new(),            // tokens
52             MAX_DATAGRAM_SIZE - 2, // size
53         ),
54         SentPacket::new(
55             PacketType::Short,
56             3,                 // pn
57             time_before,       // time sent
58             true,              // ack eliciting
59             Vec::new(),        // tokens
60             MAX_DATAGRAM_SIZE, // size
61         ),
62         SentPacket::new(
63             PacketType::Short,
64             4,                 // pn
65             time_before,       // time sent
66             true,              // ack eliciting
67             Vec::new(),        // tokens
68             MAX_DATAGRAM_SIZE, // size
69         ),
70         SentPacket::new(
71             PacketType::Short,
72             5,                 // pn
73             time_before,       // time sent
74             true,              // ack eliciting
75             Vec::new(),        // tokens
76             MAX_DATAGRAM_SIZE, // size
77         ),
78         SentPacket::new(
79             PacketType::Short,
80             6,                 // pn
81             time_before,       // time sent
82             true,              // ack eliciting
83             Vec::new(),        // tokens
84             MAX_DATAGRAM_SIZE, // size
85         ),
86         SentPacket::new(
87             PacketType::Short,
88             7,                     // pn
89             time_after,            // time sent
90             true,                  // ack eliciting
91             Vec::new(),            // tokens
92             MAX_DATAGRAM_SIZE - 3, // size
93         ),
94     ];
95 
96     // Send some more packets so that the cc is not app-limited.
97     for p in &sent_packets[..6] {
98         cc.on_packet_sent(p);
99     }
100     assert_eq!(cc.acked_bytes(), 0);
101     cwnd_is_default(&cc);
102     assert_eq!(cc.bytes_in_flight(), 6 * MAX_DATAGRAM_SIZE - 3);
103 
104     cc.on_packets_lost(Some(time_now), None, PTO, &sent_packets[0..1]);
105 
106     // We are now in recovery
107     assert!(cc.recovery_packet());
108     assert_eq!(cc.acked_bytes(), 0);
109     cwnd_is_halved(&cc);
110     assert_eq!(cc.bytes_in_flight(), 5 * MAX_DATAGRAM_SIZE - 2);
111 
112     // Send a packet after recovery starts
113     cc.on_packet_sent(&sent_packets[6]);
114     assert!(!cc.recovery_packet());
115     cwnd_is_halved(&cc);
116     assert_eq!(cc.acked_bytes(), 0);
117     assert_eq!(cc.bytes_in_flight(), 6 * MAX_DATAGRAM_SIZE - 5);
118 
119     // and ack it. cwnd increases slightly
120     cc.on_packets_acked(&sent_packets[6..], RTT, time_now);
121     assert_eq!(cc.acked_bytes(), sent_packets[6].size);
122     cwnd_is_halved(&cc);
123     assert_eq!(cc.bytes_in_flight(), 5 * MAX_DATAGRAM_SIZE - 2);
124 
125     // Packet from before is lost. Should not hurt cwnd.
126     cc.on_packets_lost(Some(time_now), None, PTO, &sent_packets[1..2]);
127     assert!(!cc.recovery_packet());
128     assert_eq!(cc.acked_bytes(), sent_packets[6].size);
129     cwnd_is_halved(&cc);
130     assert_eq!(cc.bytes_in_flight(), 4 * MAX_DATAGRAM_SIZE);
131 }
132