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