blob: 5c3494fa7c73133a153bd3ef470fdfde9e61137f [file] [log] [blame]
minyue435ddf92017-01-23 08:07:05 -08001/*
2 * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
Jonas Olssona4d87372019-07-05 19:08:33 +020011#include "audio/transport_feedback_packet_loss_tracker.h"
12
minyue435ddf92017-01-23 08:07:05 -080013#include <limits>
14#include <memory>
elad.alon7af93572017-03-03 10:51:35 -080015#include <numeric>
minyue435ddf92017-01-23 08:07:05 -080016#include <vector>
17
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020018#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
19#include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h"
20#include "rtc_base/checks.h"
21#include "test/gmock.h"
22#include "test/gtest.h"
minyue435ddf92017-01-23 08:07:05 -080023
minyue435ddf92017-01-23 08:07:05 -080024namespace webrtc {
25
26namespace {
27
elad.alon6a64bd92017-03-21 05:58:04 -070028constexpr int64_t kDefaultSendIntervalMs = 10;
29constexpr int64_t kDefaultMaxWindowSizeMs = 500 * kDefaultSendIntervalMs;
30
elad.alon02455b22017-03-03 11:11:06 -080031class TransportFeedbackPacketLossTrackerTest
32 : public ::testing::TestWithParam<uint16_t> {
33 public:
34 TransportFeedbackPacketLossTrackerTest() = default;
35 virtual ~TransportFeedbackPacketLossTrackerTest() = default;
36
37 protected:
elad.alon3f9b12d2017-03-15 07:38:13 -070038 void SendPackets(TransportFeedbackPacketLossTracker* tracker,
39 const std::vector<uint16_t>& sequence_numbers,
40 int64_t send_time_interval_ms,
41 bool validate_all = true) {
42 RTC_CHECK_GE(send_time_interval_ms, 0);
43 for (uint16_t sequence_number : sequence_numbers) {
44 tracker->OnPacketAdded(sequence_number, time_ms_);
45 if (validate_all) {
46 tracker->Validate();
47 }
48 time_ms_ += send_time_interval_ms;
49 }
elad.alon02455b22017-03-03 11:11:06 -080050
elad.alon3f9b12d2017-03-15 07:38:13 -070051 // We've either validated after each packet, or, for making sure the UT
52 // doesn't run too long, we might validate only at the end of the range.
53 if (!validate_all) {
elad.alon7af93572017-03-03 10:51:35 -080054 tracker->Validate();
55 }
56 }
57
elad.alon3f9b12d2017-03-15 07:38:13 -070058 void SendPackets(TransportFeedbackPacketLossTracker* tracker,
59 uint16_t first_seq_num,
60 size_t num_of_packets,
61 int64_t send_time_interval_ms,
62 bool validate_all = true) {
63 RTC_CHECK_GE(send_time_interval_ms, 0);
64 std::vector<uint16_t> sequence_numbers(num_of_packets);
65 std::iota(sequence_numbers.begin(), sequence_numbers.end(), first_seq_num);
66 SendPackets(tracker, sequence_numbers, send_time_interval_ms, validate_all);
67 }
68
69 void AdvanceClock(int64_t time_delta_ms) {
70 RTC_CHECK_GT(time_delta_ms, 0);
71 time_ms_ += time_delta_ms;
72 }
73
74 void AddTransportFeedbackAndValidate(
75 TransportFeedbackPacketLossTracker* tracker,
76 uint16_t base_sequence_num,
77 const std::vector<bool>& reception_status_vec) {
elad.alon92e448d2017-03-21 07:31:35 -070078 // Any positive integer signals reception. kNotReceived signals loss.
79 // Other values are just illegal.
80 constexpr int64_t kArrivalTimeMs = 1234;
81
82 std::vector<PacketFeedback> packet_feedback_vector;
83 uint16_t seq_num = base_sequence_num;
84 for (bool received : reception_status_vec) {
85 packet_feedback_vector.emplace_back(PacketFeedback(
86 received ? kArrivalTimeMs : PacketFeedback::kNotReceived, seq_num));
87 ++seq_num;
elad.alon3f9b12d2017-03-15 07:38:13 -070088 }
89
elad.alond12a8e12017-03-23 11:04:48 -070090 tracker->OnPacketFeedbackVector(packet_feedback_vector);
elad.alon7af93572017-03-03 10:51:35 -080091 tracker->Validate();
92 }
elad.alon7af93572017-03-03 10:51:35 -080093
elad.alon3f9b12d2017-03-15 07:38:13 -070094 // Checks that validty is as expected. If valid, checks also that
95 // value is as expected.
96 void ValidatePacketLossStatistics(
97 const TransportFeedbackPacketLossTracker& tracker,
Danil Chapovalovb9b146c2018-06-15 12:28:07 +020098 absl::optional<float> expected_plr,
99 absl::optional<float> expected_rplr) {
100 // TODO(eladalon): Comparing the absl::optional<float> directly would have
elad.alon3f9b12d2017-03-15 07:38:13 -0700101 // given concise code, but less readable error messages. If we modify
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200102 // the way absl::optional is printed, we can get rid of this.
103 absl::optional<float> plr = tracker.GetPacketLossRate();
elad.alon3f9b12d2017-03-15 07:38:13 -0700104 EXPECT_EQ(static_cast<bool>(expected_plr), static_cast<bool>(plr));
105 if (expected_plr && plr) {
106 EXPECT_EQ(*expected_plr, *plr);
107 }
elad.alon7af93572017-03-03 10:51:35 -0800108
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200109 absl::optional<float> rplr = tracker.GetRecoverablePacketLossRate();
elad.alon3f9b12d2017-03-15 07:38:13 -0700110 EXPECT_EQ(static_cast<bool>(expected_rplr), static_cast<bool>(rplr));
111 if (expected_rplr && rplr) {
112 EXPECT_EQ(*expected_rplr, *rplr);
113 }
minyue435ddf92017-01-23 08:07:05 -0800114 }
elad.alond83b9672017-02-01 08:36:09 -0800115
elad.alon3f9b12d2017-03-15 07:38:13 -0700116 uint16_t base_{GetParam()};
minyue435ddf92017-01-23 08:07:05 -0800117
elad.alon3f9b12d2017-03-15 07:38:13 -0700118 private:
119 int64_t time_ms_{0};
elad.alon3f9b12d2017-03-15 07:38:13 -0700120};
elad.alond83b9672017-02-01 08:36:09 -0800121
minyue435ddf92017-01-23 08:07:05 -0800122} // namespace
123
124// Sanity check on an empty window.
elad.alon3f9b12d2017-03-15 07:38:13 -0700125TEST_P(TransportFeedbackPacketLossTrackerTest, EmptyWindow) {
elad.alon6a64bd92017-03-21 05:58:04 -0700126 TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 5, 5);
minyue435ddf92017-01-23 08:07:05 -0800127
elad.alond83b9672017-02-01 08:36:09 -0800128 // PLR and RPLR reported as unknown before reception of first feedback.
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200129 ValidatePacketLossStatistics(tracker, absl::nullopt, absl::nullopt);
minyue435ddf92017-01-23 08:07:05 -0800130}
131
elad.alon7af93572017-03-03 10:51:35 -0800132// A feedback received for an empty window has no effect.
elad.alon02455b22017-03-03 11:11:06 -0800133TEST_P(TransportFeedbackPacketLossTrackerTest, EmptyWindowFeedback) {
elad.alon6a64bd92017-03-21 05:58:04 -0700134 TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 3, 2);
elad.alon7af93572017-03-03 10:51:35 -0800135
elad.alon02455b22017-03-03 11:11:06 -0800136 // Feedback doesn't correspond to any packets - ignored.
137 AddTransportFeedbackAndValidate(&tracker, base_, {true, false, true});
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200138 ValidatePacketLossStatistics(tracker, absl::nullopt, absl::nullopt);
elad.alon7af93572017-03-03 10:51:35 -0800139
elad.alon02455b22017-03-03 11:11:06 -0800140 // After the packets are transmitted, acking them would have an effect.
elad.alon6a64bd92017-03-21 05:58:04 -0700141 SendPackets(&tracker, base_, 3, kDefaultSendIntervalMs);
elad.alon02455b22017-03-03 11:11:06 -0800142 AddTransportFeedbackAndValidate(&tracker, base_, {true, false, true});
143 ValidatePacketLossStatistics(tracker, 1.0f / 3.0f, 0.5f);
elad.alon7af93572017-03-03 10:51:35 -0800144}
145
minyue435ddf92017-01-23 08:07:05 -0800146// Sanity check on partially filled window.
elad.alon02455b22017-03-03 11:11:06 -0800147TEST_P(TransportFeedbackPacketLossTrackerTest, PartiallyFilledWindow) {
elad.alon6a64bd92017-03-21 05:58:04 -0700148 TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 5, 4);
minyue435ddf92017-01-23 08:07:05 -0800149
elad.alon02455b22017-03-03 11:11:06 -0800150 // PLR unknown before minimum window size reached.
151 // RPLR unknown before minimum pairs reached.
152 // Expected window contents: [] -> [1001].
elad.alon6a64bd92017-03-21 05:58:04 -0700153 SendPackets(&tracker, base_, 3, kDefaultSendIntervalMs);
elad.alon02455b22017-03-03 11:11:06 -0800154 AddTransportFeedbackAndValidate(&tracker, base_, {true, false, false, true});
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200155 ValidatePacketLossStatistics(tracker, absl::nullopt, absl::nullopt);
minyue435ddf92017-01-23 08:07:05 -0800156}
157
elad.alond83b9672017-02-01 08:36:09 -0800158// Sanity check on minimum filled window - PLR known, RPLR unknown.
elad.alon02455b22017-03-03 11:11:06 -0800159TEST_P(TransportFeedbackPacketLossTrackerTest, PlrMinimumFilledWindow) {
elad.alon6a64bd92017-03-21 05:58:04 -0700160 TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 5, 5);
minyue435ddf92017-01-23 08:07:05 -0800161
elad.alon02455b22017-03-03 11:11:06 -0800162 // PLR correctly calculated after minimum window size reached.
163 // RPLR not necessarily known at that time (not if min-pairs not reached).
164 // Expected window contents: [] -> [10011].
elad.alon6a64bd92017-03-21 05:58:04 -0700165 SendPackets(&tracker, base_, 5, kDefaultSendIntervalMs);
elad.alon02455b22017-03-03 11:11:06 -0800166 AddTransportFeedbackAndValidate(&tracker, base_,
167 {true, false, false, true, true});
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200168 ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, absl::nullopt);
minyue435ddf92017-01-23 08:07:05 -0800169}
170
elad.alond83b9672017-02-01 08:36:09 -0800171// Sanity check on minimum filled window - PLR unknown, RPLR known.
elad.alon02455b22017-03-03 11:11:06 -0800172TEST_P(TransportFeedbackPacketLossTrackerTest, RplrMinimumFilledWindow) {
elad.alon6a64bd92017-03-21 05:58:04 -0700173 TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 6, 4);
elad.alond83b9672017-02-01 08:36:09 -0800174
elad.alon02455b22017-03-03 11:11:06 -0800175 // RPLR correctly calculated after minimum pairs reached.
176 // PLR not necessarily known at that time (not if min window not reached).
177 // Expected window contents: [] -> [10011].
elad.alon6a64bd92017-03-21 05:58:04 -0700178 SendPackets(&tracker, base_, 5, kDefaultSendIntervalMs);
elad.alon02455b22017-03-03 11:11:06 -0800179 AddTransportFeedbackAndValidate(&tracker, base_,
180 {true, false, false, true, true});
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200181 ValidatePacketLossStatistics(tracker, absl::nullopt, 1.0f / 4.0f);
elad.alond83b9672017-02-01 08:36:09 -0800182}
183
elad.alon3f9b12d2017-03-15 07:38:13 -0700184// If packets are sent close enough together that the clock reading for both
185// is the same, that's handled properly.
186TEST_P(TransportFeedbackPacketLossTrackerTest, SameSentTime) {
elad.alon6a64bd92017-03-21 05:58:04 -0700187 TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 3, 2);
elad.alon3f9b12d2017-03-15 07:38:13 -0700188
189 // Expected window contents: [] -> [101].
190 SendPackets(&tracker, base_, 3, 0); // Note: time interval = 0ms.
191 AddTransportFeedbackAndValidate(&tracker, base_, {true, false, true});
192
193 ValidatePacketLossStatistics(tracker, 1.0f / 3.0f, 0.5f);
194}
195
elad.alond83b9672017-02-01 08:36:09 -0800196// Additional reports update PLR and RPLR.
elad.alon02455b22017-03-03 11:11:06 -0800197TEST_P(TransportFeedbackPacketLossTrackerTest, ExtendWindow) {
elad.alon6a64bd92017-03-21 05:58:04 -0700198 TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 5, 5);
minyue435ddf92017-01-23 08:07:05 -0800199
elad.alon6a64bd92017-03-21 05:58:04 -0700200 SendPackets(&tracker, base_, 25, kDefaultSendIntervalMs);
elad.alon7af93572017-03-03 10:51:35 -0800201
elad.alon02455b22017-03-03 11:11:06 -0800202 // Expected window contents: [] -> [10011].
203 AddTransportFeedbackAndValidate(&tracker, base_,
204 {true, false, false, true, true});
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200205 ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, absl::nullopt);
minyue435ddf92017-01-23 08:07:05 -0800206
elad.alon02455b22017-03-03 11:11:06 -0800207 // Expected window contents: [10011] -> [1001110101].
208 AddTransportFeedbackAndValidate(&tracker, base_ + 5,
209 {true, false, true, false, true});
210 ValidatePacketLossStatistics(tracker, 4.0f / 10.0f, 3.0f / 9.0f);
minyue435ddf92017-01-23 08:07:05 -0800211
elad.alon02455b22017-03-03 11:11:06 -0800212 // Expected window contents: [1001110101] -> [1001110101-GAP-10001].
213 AddTransportFeedbackAndValidate(&tracker, base_ + 20,
214 {true, false, false, false, true});
215 ValidatePacketLossStatistics(tracker, 7.0f / 15.0f, 4.0f / 13.0f);
minyue435ddf92017-01-23 08:07:05 -0800216}
217
elad.alon3f9b12d2017-03-15 07:38:13 -0700218// Correct calculation with different packet lengths.
219TEST_P(TransportFeedbackPacketLossTrackerTest, DifferentSentIntervals) {
elad.alon6a64bd92017-03-21 05:58:04 -0700220 TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 5, 4);
elad.alon3f9b12d2017-03-15 07:38:13 -0700221
elad.alon3f9b12d2017-03-15 07:38:13 -0700222 int64_t frames[] = {20, 60, 120, 20, 60};
223 for (size_t i = 0; i < sizeof(frames) / sizeof(frames[0]); i++) {
elad.alon6a64bd92017-03-21 05:58:04 -0700224 SendPackets(&tracker, {static_cast<uint16_t>(base_ + i)}, frames[i]);
elad.alon3f9b12d2017-03-15 07:38:13 -0700225 }
226
227 // Expected window contents: [] -> [10011].
228 AddTransportFeedbackAndValidate(&tracker, base_,
229 {true, false, false, true, true});
230 ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 1.0f / 4.0f);
231}
232
233// The window retains information up to sent times that exceed the the max
234// window size. The oldest packets get shifted out of window to make room
235// for the newer ones.
236TEST_P(TransportFeedbackPacketLossTrackerTest, MaxWindowSize) {
elad.alon6a64bd92017-03-21 05:58:04 -0700237 TransportFeedbackPacketLossTracker tracker(4 * kDefaultSendIntervalMs, 5, 1);
elad.alon3f9b12d2017-03-15 07:38:13 -0700238
elad.alon6a64bd92017-03-21 05:58:04 -0700239 SendPackets(&tracker, base_, 6, kDefaultSendIntervalMs, true);
elad.alon3f9b12d2017-03-15 07:38:13 -0700240
elad.alon6a64bd92017-03-21 05:58:04 -0700241 // Up to the maximum time-span retained (first + 4 * kDefaultSendIntervalMs).
elad.alon3f9b12d2017-03-15 07:38:13 -0700242 // Expected window contents: [] -> [01001].
243 AddTransportFeedbackAndValidate(&tracker, base_,
244 {false, true, false, false, true});
245 ValidatePacketLossStatistics(tracker, 3.0f / 5.0f, 2.0f / 4.0f);
246
247 // After the maximum time-span, older entries are discarded to accommodate
248 // newer ones.
249 // Expected window contents: [01001] -> [10011].
250 AddTransportFeedbackAndValidate(&tracker, base_ + 5, {true});
251 ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 1.0f / 4.0f);
252}
253
254// All packets received.
elad.alon02455b22017-03-03 11:11:06 -0800255TEST_P(TransportFeedbackPacketLossTrackerTest, AllReceived) {
elad.alon6a64bd92017-03-21 05:58:04 -0700256 TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 5, 4);
minyue435ddf92017-01-23 08:07:05 -0800257
elad.alon02455b22017-03-03 11:11:06 -0800258 // Expected window contents: [] -> [11111].
elad.alon6a64bd92017-03-21 05:58:04 -0700259 SendPackets(&tracker, base_, 5, kDefaultSendIntervalMs);
elad.alon02455b22017-03-03 11:11:06 -0800260 AddTransportFeedbackAndValidate(&tracker, base_,
261 {true, true, true, true, true});
262 ValidatePacketLossStatistics(tracker, 0.0f, 0.0f);
minyue435ddf92017-01-23 08:07:05 -0800263}
264
elad.alon3f9b12d2017-03-15 07:38:13 -0700265// All packets lost.
elad.alon02455b22017-03-03 11:11:06 -0800266TEST_P(TransportFeedbackPacketLossTrackerTest, AllLost) {
elad.alon6a64bd92017-03-21 05:58:04 -0700267 TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 5, 4);
elad.alon7af93572017-03-03 10:51:35 -0800268
elad.alon3f9b12d2017-03-15 07:38:13 -0700269 // Note: The last packet in the feedback does not belong to the stream.
270 // It's only there because we're not allowed to end a feedback with a loss.
elad.alon02455b22017-03-03 11:11:06 -0800271 // Expected window contents: [] -> [00000].
elad.alon6a64bd92017-03-21 05:58:04 -0700272 SendPackets(&tracker, base_, 5, kDefaultSendIntervalMs);
elad.alon02455b22017-03-03 11:11:06 -0800273 AddTransportFeedbackAndValidate(&tracker, base_,
274 {false, false, false, false, false, true});
275 ValidatePacketLossStatistics(tracker, 1.0f, 0.0f);
elad.alon7af93572017-03-03 10:51:35 -0800276}
277
minyue435ddf92017-01-23 08:07:05 -0800278// Repeated reports are ignored.
elad.alon02455b22017-03-03 11:11:06 -0800279TEST_P(TransportFeedbackPacketLossTrackerTest, ReportRepetition) {
elad.alon6a64bd92017-03-21 05:58:04 -0700280 TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 5, 4);
minyue435ddf92017-01-23 08:07:05 -0800281
elad.alon6a64bd92017-03-21 05:58:04 -0700282 SendPackets(&tracker, base_, 5, kDefaultSendIntervalMs);
elad.alon7af93572017-03-03 10:51:35 -0800283
elad.alon02455b22017-03-03 11:11:06 -0800284 // Expected window contents: [] -> [10011].
285 AddTransportFeedbackAndValidate(&tracker, base_,
286 {true, false, false, true, true});
287 ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 1.0f / 4.0f);
minyue435ddf92017-01-23 08:07:05 -0800288
elad.alon02455b22017-03-03 11:11:06 -0800289 // Repeat entire previous feedback
290 // Expected window contents: [10011] -> [10011].
291 AddTransportFeedbackAndValidate(&tracker, base_,
292 {true, false, false, true, true});
293 ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 1.0f / 4.0f);
minyue435ddf92017-01-23 08:07:05 -0800294}
295
296// Report overlap.
elad.alon02455b22017-03-03 11:11:06 -0800297TEST_P(TransportFeedbackPacketLossTrackerTest, ReportOverlap) {
elad.alon6a64bd92017-03-21 05:58:04 -0700298 TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 5, 1);
minyue435ddf92017-01-23 08:07:05 -0800299
elad.alon6a64bd92017-03-21 05:58:04 -0700300 SendPackets(&tracker, base_, 15, kDefaultSendIntervalMs);
elad.alon7af93572017-03-03 10:51:35 -0800301
elad.alon02455b22017-03-03 11:11:06 -0800302 // Expected window contents: [] -> [10011].
303 AddTransportFeedbackAndValidate(&tracker, base_,
304 {true, false, false, true, true});
305 ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 1.0f / 4.0f);
minyue435ddf92017-01-23 08:07:05 -0800306
elad.alon02455b22017-03-03 11:11:06 -0800307 // Expected window contents: [10011] -> [1001101].
308 AddTransportFeedbackAndValidate(&tracker, base_ + 3,
309 {true, true, false, true});
310 ValidatePacketLossStatistics(tracker, 3.0f / 7.0f, 2.0f / 6.0f);
minyue435ddf92017-01-23 08:07:05 -0800311}
312
313// Report conflict.
elad.alon02455b22017-03-03 11:11:06 -0800314TEST_P(TransportFeedbackPacketLossTrackerTest, ReportConflict) {
elad.alon6a64bd92017-03-21 05:58:04 -0700315 TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 5, 4);
minyue435ddf92017-01-23 08:07:05 -0800316
elad.alon3f9b12d2017-03-15 07:38:13 -0700317 SendPackets(&tracker, base_, 15, 10);
elad.alon7af93572017-03-03 10:51:35 -0800318
elad.alon02455b22017-03-03 11:11:06 -0800319 // Expected window contents: [] -> [01001].
320 AddTransportFeedbackAndValidate(&tracker, base_,
321 {false, true, false, false, true});
322 ValidatePacketLossStatistics(tracker, 3.0f / 5.0f, 2.0f / 4.0f);
minyue435ddf92017-01-23 08:07:05 -0800323
elad.alon02455b22017-03-03 11:11:06 -0800324 // Expected window contents: [01001] -> [11101].
325 // While false->true will be applied, true -> false will be ignored.
326 AddTransportFeedbackAndValidate(&tracker, base_, {true, false, true});
327 ValidatePacketLossStatistics(tracker, 1.0f / 5.0f, 1.0f / 4.0f);
minyue435ddf92017-01-23 08:07:05 -0800328}
329
330// Skipped packets treated as unknown (not lost).
elad.alon02455b22017-03-03 11:11:06 -0800331TEST_P(TransportFeedbackPacketLossTrackerTest, SkippedPackets) {
elad.alon6a64bd92017-03-21 05:58:04 -0700332 TransportFeedbackPacketLossTracker tracker(200 * kDefaultSendIntervalMs, 5,
333 1);
minyue435ddf92017-01-23 08:07:05 -0800334
elad.alon6a64bd92017-03-21 05:58:04 -0700335 SendPackets(&tracker, base_, 200, kDefaultSendIntervalMs);
elad.alon7af93572017-03-03 10:51:35 -0800336
elad.alon02455b22017-03-03 11:11:06 -0800337 // Expected window contents: [] -> [10011].
338 AddTransportFeedbackAndValidate(&tracker, base_,
339 {true, false, false, true, true});
340 ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 1.0f / 4.0f);
minyue435ddf92017-01-23 08:07:05 -0800341
elad.alon02455b22017-03-03 11:11:06 -0800342 // Expected window contents: [10011] -> [10011-GAP-101].
343 AddTransportFeedbackAndValidate(&tracker, base_ + 100, {true, false, true});
344 ValidatePacketLossStatistics(tracker, 3.0f / 8.0f, 2.0f / 6.0f);
minyue435ddf92017-01-23 08:07:05 -0800345}
346
elad.alon3f9b12d2017-03-15 07:38:13 -0700347// Moving a window, if it excludes some old acked messages, can leave
348// in-window unacked messages intact, and ready to be used later.
349TEST_P(TransportFeedbackPacketLossTrackerTest, MovedWindowRetainsRelevantInfo) {
350 constexpr int64_t max_window_size_ms = 100;
351 TransportFeedbackPacketLossTracker tracker(max_window_size_ms, 5, 1);
minyue435ddf92017-01-23 08:07:05 -0800352
elad.alon3f9b12d2017-03-15 07:38:13 -0700353 // Note: All messages in this test are sent 1ms apart from each other.
354 // Therefore, the delta in sequence numbers equals the timestamps delta.
355 SendPackets(&tracker, base_, 4 * max_window_size_ms, 1);
elad.alon7af93572017-03-03 10:51:35 -0800356
elad.alon02455b22017-03-03 11:11:06 -0800357 // Expected window contents: [] -> [10101].
358 AddTransportFeedbackAndValidate(&tracker, base_,
359 {true, false, true, false, true});
360 ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 2.0f / 4.0f);
minyue435ddf92017-01-23 08:07:05 -0800361
elad.alon3f9b12d2017-03-15 07:38:13 -0700362 // Expected window contents: [10101] -> [100011].
363 const int64_t moved_oldest_acked = base_ + 2 * max_window_size_ms;
364 const std::vector<bool> feedback = {true, false, false, false, true, true};
365 AddTransportFeedbackAndValidate(&tracker, moved_oldest_acked, feedback);
366 ValidatePacketLossStatistics(tracker, 3.0f / 6.0f, 1.0f / 5.0f);
minyue435ddf92017-01-23 08:07:05 -0800367
elad.alon3f9b12d2017-03-15 07:38:13 -0700368 // Having acked |feedback.size()| starting with |moved_oldest_acked|, the
369 // newest of the acked ones is now:
370 const int64_t moved_newest_acked = moved_oldest_acked + feedback.size() - 1;
371
372 // Messages that *are* more than the span-limit away from the newest
373 // acked message *are* too old. Acking them would have no effect.
374 AddTransportFeedbackAndValidate(
375 &tracker, moved_newest_acked - max_window_size_ms - 1, {true});
376 ValidatePacketLossStatistics(tracker, 3.0f / 6.0f, 1.0f / 5.0f);
377
378 // Messages that are *not* more than the span-limit away from the newest
379 // acked message are *not* too old. Acking them would have an effect.
380 AddTransportFeedbackAndValidate(
381 &tracker, moved_newest_acked - max_window_size_ms, {true});
382 ValidatePacketLossStatistics(tracker, 3.0f / 7.0f, 1.0f / 5.0f);
minyue435ddf92017-01-23 08:07:05 -0800383}
384
elad.alon3f9b12d2017-03-15 07:38:13 -0700385// Inserting feedback into the middle of a window works correctly - can
elad.alon7af93572017-03-03 10:51:35 -0800386// complete two pairs.
elad.alon02455b22017-03-03 11:11:06 -0800387TEST_P(TransportFeedbackPacketLossTrackerTest, InsertionCompletesTwoPairs) {
elad.alon6a64bd92017-03-21 05:58:04 -0700388 TransportFeedbackPacketLossTracker tracker(150 * kDefaultSendIntervalMs, 5,
389 1);
elad.alond83b9672017-02-01 08:36:09 -0800390
elad.alon6a64bd92017-03-21 05:58:04 -0700391 SendPackets(&tracker, base_, 15, kDefaultSendIntervalMs);
elad.alon7af93572017-03-03 10:51:35 -0800392
elad.alon02455b22017-03-03 11:11:06 -0800393 // Expected window contents: [] -> [10111].
394 AddTransportFeedbackAndValidate(&tracker, base_,
395 {true, false, true, true, true});
396 ValidatePacketLossStatistics(tracker, 1.0f / 5.0f, 1.0f / 4.0f);
elad.alond83b9672017-02-01 08:36:09 -0800397
elad.alon02455b22017-03-03 11:11:06 -0800398 // Expected window contents: [10111] -> [10111-GAP-10101].
399 AddTransportFeedbackAndValidate(&tracker, base_ + 7,
400 {true, false, true, false, true});
401 ValidatePacketLossStatistics(tracker, 3.0f / 10.0f, 3.0f / 8.0f);
elad.alond83b9672017-02-01 08:36:09 -0800402
elad.alon02455b22017-03-03 11:11:06 -0800403 // Insert in between, closing the gap completely.
elad.alon3f9b12d2017-03-15 07:38:13 -0700404 // Expected window contents: [10111-GAP-10101] -> [101110110101].
elad.alon02455b22017-03-03 11:11:06 -0800405 AddTransportFeedbackAndValidate(&tracker, base_ + 5, {false, true});
406 ValidatePacketLossStatistics(tracker, 4.0f / 12.0f, 4.0f / 11.0f);
elad.alond83b9672017-02-01 08:36:09 -0800407}
408
elad.alon7af93572017-03-03 10:51:35 -0800409// Sequence number gaps are not gaps in reception. However, gaps in reception
410// are still possible, if a packet which WAS sent on the stream is not acked.
elad.alon02455b22017-03-03 11:11:06 -0800411TEST_P(TransportFeedbackPacketLossTrackerTest, SanityGapsInSequenceNumbers) {
elad.alon6a64bd92017-03-21 05:58:04 -0700412 TransportFeedbackPacketLossTracker tracker(50 * kDefaultSendIntervalMs, 5, 1);
minyue435ddf92017-01-23 08:07:05 -0800413
Yves Gerey665174f2018-06-19 15:03:05 +0200414 SendPackets(
415 &tracker,
416 {static_cast<uint16_t>(base_), static_cast<uint16_t>(base_ + 2),
417 static_cast<uint16_t>(base_ + 4), static_cast<uint16_t>(base_ + 6),
418 static_cast<uint16_t>(base_ + 8)},
419 kDefaultSendIntervalMs);
minyue435ddf92017-01-23 08:07:05 -0800420
elad.alon02455b22017-03-03 11:11:06 -0800421 // Gaps in sequence numbers not considered as gaps in window, because only
422 // those sequence numbers which were associated with the stream count.
423 // Expected window contents: [] -> [11011].
424 AddTransportFeedbackAndValidate(
elad.alon3f9b12d2017-03-15 07:38:13 -0700425 // Note: Left packets belong to this stream, right ones ignored.
Yves Gerey665174f2018-06-19 15:03:05 +0200426 &tracker, base_,
427 {true, false, true, false, false, false, true, false, true, true});
elad.alon02455b22017-03-03 11:11:06 -0800428 ValidatePacketLossStatistics(tracker, 1.0f / 5.0f, 1.0f / 4.0f);
minyue435ddf92017-01-23 08:07:05 -0800429
elad.alon02455b22017-03-03 11:11:06 -0800430 // Create gap by sending [base + 10] but not acking it.
431 // Note: Acks for [base + 11] and [base + 13] ignored (other stream).
432 // Expected window contents: [11011] -> [11011-GAP-01].
Yves Gerey665174f2018-06-19 15:03:05 +0200433 SendPackets(
434 &tracker,
435 {static_cast<uint16_t>(base_ + 10), static_cast<uint16_t>(base_ + 12),
436 static_cast<uint16_t>(base_ + 14)},
437 kDefaultSendIntervalMs);
elad.alon02455b22017-03-03 11:11:06 -0800438 AddTransportFeedbackAndValidate(&tracker, base_ + 11,
439 {false, false, false, true, true});
440 ValidatePacketLossStatistics(tracker, 2.0f / 7.0f, 2.0f / 5.0f);
minyue435ddf92017-01-23 08:07:05 -0800441}
442
elad.alon3f9b12d2017-03-15 07:38:13 -0700443// The window cannot span more than 0x8000 in sequence numbers, regardless
444// of time stamps and ack/unacked status.
445TEST_P(TransportFeedbackPacketLossTrackerTest, MaxUnackedPackets) {
446 TransportFeedbackPacketLossTracker tracker(0x10000, 4, 1);
minyue435ddf92017-01-23 08:07:05 -0800447
elad.alon3f9b12d2017-03-15 07:38:13 -0700448 SendPackets(&tracker, base_, 0x2000, 1, false);
449
450 // Expected window contents: [] -> [10011].
elad.alon02455b22017-03-03 11:11:06 -0800451 AddTransportFeedbackAndValidate(&tracker, base_,
elad.alon3f9b12d2017-03-15 07:38:13 -0700452 {true, false, false, true, true});
453 ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 1.0f / 4.0f);
minyue435ddf92017-01-23 08:07:05 -0800454
elad.alon3f9b12d2017-03-15 07:38:13 -0700455 // Sending more unacked packets, up to 0x7fff from the base, does not
456 // move the window or discard any information.
457 SendPackets(&tracker, static_cast<uint16_t>(base_ + 0x8000 - 0x2000), 0x2000,
458 1, false);
459 ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 1.0f / 4.0f);
460
461 // Sending more unacked packets, up to 0x7fff from the base, does not
462 // move the window or discard any information.
463 // Expected window contents: [10011] -> [0011].
464 SendPackets(&tracker, static_cast<uint16_t>(base_ + 0x8000), 1, 1);
465 ValidatePacketLossStatistics(tracker, 2.0f / 4.0f, 1.0f / 3.0f);
minyue435ddf92017-01-23 08:07:05 -0800466}
467
elad.alon3f9b12d2017-03-15 07:38:13 -0700468// The window holds acked packets up until the difference in timestamps between
469// the oldest and newest reaches the configured maximum. Once this maximum
470// is exceeded, old packets are shifted out of window until the maximum is
471// once again observed.
472TEST_P(TransportFeedbackPacketLossTrackerTest, TimeDifferenceMaximumObserved) {
473 constexpr int64_t max_window_size_ms = 500;
474 TransportFeedbackPacketLossTracker tracker(max_window_size_ms, 3, 1);
475
476 // Note: All messages in this test are sent 1ms apart from each other.
477 // Therefore, the delta in sequence numbers equals the timestamps delta.
minyue435ddf92017-01-23 08:07:05 -0800478
elad.alon02455b22017-03-03 11:11:06 -0800479 // Baseline - window has acked messages.
elad.alon3f9b12d2017-03-15 07:38:13 -0700480 // Expected window contents: [] -> [01101].
481 const std::vector<bool> feedback = {false, true, true, false, true};
482 SendPackets(&tracker, base_, feedback.size(), 1);
483 AddTransportFeedbackAndValidate(&tracker, base_, feedback);
elad.alon02455b22017-03-03 11:11:06 -0800484 ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 2.0f / 4.0f);
minyue435ddf92017-01-23 08:07:05 -0800485
elad.alon3f9b12d2017-03-15 07:38:13 -0700486 // Test - window base not moved.
487 // Expected window contents: [01101] -> [011011].
488 AdvanceClock(max_window_size_ms - feedback.size());
489 SendPackets(&tracker, static_cast<uint16_t>(base_ + feedback.size()), 1, 1);
490 AddTransportFeedbackAndValidate(
491 &tracker, static_cast<uint16_t>(base_ + feedback.size()), {true});
elad.alon02455b22017-03-03 11:11:06 -0800492 ValidatePacketLossStatistics(tracker, 2.0f / 6.0f, 2.0f / 5.0f);
minyue435ddf92017-01-23 08:07:05 -0800493
elad.alon3f9b12d2017-03-15 07:38:13 -0700494 // Another packet, sent 1ms later, would already be too late. The window will
495 // be moved, but only after the ACK is received.
496 const uint16_t new_packet_seq_num =
497 static_cast<uint16_t>(base_ + feedback.size() + 1);
498 SendPackets(&tracker, {new_packet_seq_num}, 1);
499 ValidatePacketLossStatistics(tracker, 2.0f / 6.0f, 2.0f / 5.0f);
500 // Expected window contents: [011011] -> [110111].
501 AddTransportFeedbackAndValidate(&tracker, new_packet_seq_num, {true});
502 ValidatePacketLossStatistics(tracker, 1.0f / 6.0f, 1.0f / 5.0f);
503}
elad.alon7af93572017-03-03 10:51:35 -0800504
elad.alon3f9b12d2017-03-15 07:38:13 -0700505TEST_P(TransportFeedbackPacketLossTrackerTest, RepeatedSeqNumResetsWindow) {
elad.alon6a64bd92017-03-21 05:58:04 -0700506 TransportFeedbackPacketLossTracker tracker(50 * kDefaultSendIntervalMs, 2, 1);
elad.alon3f9b12d2017-03-15 07:38:13 -0700507
508 // Baseline - window has acked messages.
509 // Expected window contents: [] -> [01101].
elad.alon6a64bd92017-03-21 05:58:04 -0700510 SendPackets(&tracker, base_, 5, kDefaultSendIntervalMs);
elad.alon3f9b12d2017-03-15 07:38:13 -0700511 AddTransportFeedbackAndValidate(&tracker, base_,
512 {false, true, true, false, true});
513 ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 2.0f / 4.0f);
514
515 // A reset occurs.
elad.alon6a64bd92017-03-21 05:58:04 -0700516 SendPackets(&tracker, {static_cast<uint16_t>(base_ + 2)},
517 kDefaultSendIntervalMs);
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200518 ValidatePacketLossStatistics(tracker, absl::nullopt, absl::nullopt);
minyue435ddf92017-01-23 08:07:05 -0800519}
520
elad.alon3f9b12d2017-03-15 07:38:13 -0700521// The window is reset by the sending of a packet which is 0x8000 or more
522// away from the newest packet acked/unacked packet.
523TEST_P(TransportFeedbackPacketLossTrackerTest,
524 SendAfterLongSuspensionResetsWindow) {
elad.alon6a64bd92017-03-21 05:58:04 -0700525 TransportFeedbackPacketLossTracker tracker(50 * kDefaultSendIntervalMs, 2, 1);
elad.alon3f9b12d2017-03-15 07:38:13 -0700526
527 // Baseline - window has acked messages.
528 // Expected window contents: [] -> [01101].
elad.alon6a64bd92017-03-21 05:58:04 -0700529 SendPackets(&tracker, base_, 5, kDefaultSendIntervalMs);
elad.alon3f9b12d2017-03-15 07:38:13 -0700530 AddTransportFeedbackAndValidate(&tracker, base_,
531 {false, true, true, false, true});
532 ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 2.0f / 4.0f);
533
534 // A reset occurs.
elad.alon6a64bd92017-03-21 05:58:04 -0700535 SendPackets(&tracker, {static_cast<uint16_t>(base_ + 5 + 0x8000)},
536 kDefaultSendIntervalMs);
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200537 ValidatePacketLossStatistics(tracker, absl::nullopt, absl::nullopt);
elad.alon3f9b12d2017-03-15 07:38:13 -0700538}
539
540#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
541TEST(TransportFeedbackPacketLossTrackerTest, InvalidConfigMaxWindowSize) {
542 EXPECT_DEATH(TransportFeedbackPacketLossTracker tracker(0, 20, 10), "");
543}
544
545TEST(TransportFeedbackPacketLossTrackerTest, InvalidConfigPlrMinAcked) {
546 EXPECT_DEATH(TransportFeedbackPacketLossTracker tracker(5000, 0, 10), "");
547}
548
549TEST(TransportFeedbackPacketLossTrackerTest, InvalidConfigRplrMinPairs) {
550 EXPECT_DEATH(TransportFeedbackPacketLossTracker tracker(5000, 20, 0), "");
551}
552
553TEST(TransportFeedbackPacketLossTrackerTest, TimeCantFlowBackwards) {
554 TransportFeedbackPacketLossTracker tracker(5000, 2, 1);
555 tracker.OnPacketAdded(100, 0);
556 tracker.OnPacketAdded(101, 2);
557 EXPECT_DEATH(tracker.OnPacketAdded(102, 1), "");
558}
559#endif
560
elad.alon02455b22017-03-03 11:11:06 -0800561// All tests are run multiple times with various baseline sequence number,
562// to weed out potential bugs with wrap-around handling.
563constexpr uint16_t kBases[] = {0x0000, 0x3456, 0xc032, 0xfffe};
564
Mirko Bonadeic84f6612019-01-31 12:20:57 +0100565INSTANTIATE_TEST_SUITE_P(_,
566 TransportFeedbackPacketLossTrackerTest,
Mirko Bonadei6a489f22019-04-09 15:11:12 +0200567 ::testing::ValuesIn(kBases));
elad.alon02455b22017-03-03 11:11:06 -0800568
minyue435ddf92017-01-23 08:07:05 -0800569} // namespace webrtc