blob: cc9065109c55f6e0e14122ee2203b38a5bfb81eb [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
11#include <limits>
12#include <memory>
elad.alon7af93572017-03-03 10:51:35 -080013#include <numeric>
minyue435ddf92017-01-23 08:07:05 -080014#include <vector>
15
Fredrik Solenberga8b7c7f2018-01-17 11:18:31 +010016#include "audio/transport_feedback_packet_loss_tracker.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020017#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
18#include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h"
19#include "rtc_base/checks.h"
20#include "test/gmock.h"
21#include "test/gtest.h"
minyue435ddf92017-01-23 08:07:05 -080022
minyue435ddf92017-01-23 08:07:05 -080023namespace webrtc {
24
25namespace {
26
elad.alon6a64bd92017-03-21 05:58:04 -070027constexpr int64_t kDefaultSendIntervalMs = 10;
28constexpr int64_t kDefaultMaxWindowSizeMs = 500 * kDefaultSendIntervalMs;
29
elad.alon02455b22017-03-03 11:11:06 -080030class TransportFeedbackPacketLossTrackerTest
31 : public ::testing::TestWithParam<uint16_t> {
32 public:
33 TransportFeedbackPacketLossTrackerTest() = default;
34 virtual ~TransportFeedbackPacketLossTrackerTest() = default;
35
36 protected:
elad.alon3f9b12d2017-03-15 07:38:13 -070037 void SendPackets(TransportFeedbackPacketLossTracker* tracker,
38 const std::vector<uint16_t>& sequence_numbers,
39 int64_t send_time_interval_ms,
40 bool validate_all = true) {
41 RTC_CHECK_GE(send_time_interval_ms, 0);
42 for (uint16_t sequence_number : sequence_numbers) {
43 tracker->OnPacketAdded(sequence_number, time_ms_);
44 if (validate_all) {
45 tracker->Validate();
46 }
47 time_ms_ += send_time_interval_ms;
48 }
elad.alon02455b22017-03-03 11:11:06 -080049
elad.alon3f9b12d2017-03-15 07:38:13 -070050 // We've either validated after each packet, or, for making sure the UT
51 // doesn't run too long, we might validate only at the end of the range.
52 if (!validate_all) {
elad.alon7af93572017-03-03 10:51:35 -080053 tracker->Validate();
54 }
55 }
56
elad.alon3f9b12d2017-03-15 07:38:13 -070057 void SendPackets(TransportFeedbackPacketLossTracker* tracker,
58 uint16_t first_seq_num,
59 size_t num_of_packets,
60 int64_t send_time_interval_ms,
61 bool validate_all = true) {
62 RTC_CHECK_GE(send_time_interval_ms, 0);
63 std::vector<uint16_t> sequence_numbers(num_of_packets);
64 std::iota(sequence_numbers.begin(), sequence_numbers.end(), first_seq_num);
65 SendPackets(tracker, sequence_numbers, send_time_interval_ms, validate_all);
66 }
67
68 void AdvanceClock(int64_t time_delta_ms) {
69 RTC_CHECK_GT(time_delta_ms, 0);
70 time_ms_ += time_delta_ms;
71 }
72
73 void AddTransportFeedbackAndValidate(
74 TransportFeedbackPacketLossTracker* tracker,
75 uint16_t base_sequence_num,
76 const std::vector<bool>& reception_status_vec) {
elad.alon92e448d2017-03-21 07:31:35 -070077 // Any positive integer signals reception. kNotReceived signals loss.
78 // Other values are just illegal.
79 constexpr int64_t kArrivalTimeMs = 1234;
80
81 std::vector<PacketFeedback> packet_feedback_vector;
82 uint16_t seq_num = base_sequence_num;
83 for (bool received : reception_status_vec) {
84 packet_feedback_vector.emplace_back(PacketFeedback(
85 received ? kArrivalTimeMs : PacketFeedback::kNotReceived, seq_num));
86 ++seq_num;
elad.alon3f9b12d2017-03-15 07:38:13 -070087 }
88
elad.alond12a8e12017-03-23 11:04:48 -070089 tracker->OnPacketFeedbackVector(packet_feedback_vector);
elad.alon7af93572017-03-03 10:51:35 -080090 tracker->Validate();
91 }
elad.alon7af93572017-03-03 10:51:35 -080092
elad.alon3f9b12d2017-03-15 07:38:13 -070093 // Checks that validty is as expected. If valid, checks also that
94 // value is as expected.
95 void ValidatePacketLossStatistics(
96 const TransportFeedbackPacketLossTracker& tracker,
Danil Chapovalovb9b146c2018-06-15 12:28:07 +020097 absl::optional<float> expected_plr,
98 absl::optional<float> expected_rplr) {
99 // TODO(eladalon): Comparing the absl::optional<float> directly would have
elad.alon3f9b12d2017-03-15 07:38:13 -0700100 // given concise code, but less readable error messages. If we modify
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200101 // the way absl::optional is printed, we can get rid of this.
102 absl::optional<float> plr = tracker.GetPacketLossRate();
elad.alon3f9b12d2017-03-15 07:38:13 -0700103 EXPECT_EQ(static_cast<bool>(expected_plr), static_cast<bool>(plr));
104 if (expected_plr && plr) {
105 EXPECT_EQ(*expected_plr, *plr);
106 }
elad.alon7af93572017-03-03 10:51:35 -0800107
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200108 absl::optional<float> rplr = tracker.GetRecoverablePacketLossRate();
elad.alon3f9b12d2017-03-15 07:38:13 -0700109 EXPECT_EQ(static_cast<bool>(expected_rplr), static_cast<bool>(rplr));
110 if (expected_rplr && rplr) {
111 EXPECT_EQ(*expected_rplr, *rplr);
112 }
minyue435ddf92017-01-23 08:07:05 -0800113 }
elad.alond83b9672017-02-01 08:36:09 -0800114
elad.alon3f9b12d2017-03-15 07:38:13 -0700115 uint16_t base_{GetParam()};
minyue435ddf92017-01-23 08:07:05 -0800116
elad.alon3f9b12d2017-03-15 07:38:13 -0700117 private:
118 int64_t time_ms_{0};
elad.alon3f9b12d2017-03-15 07:38:13 -0700119};
elad.alond83b9672017-02-01 08:36:09 -0800120
minyue435ddf92017-01-23 08:07:05 -0800121} // namespace
122
123// Sanity check on an empty window.
elad.alon3f9b12d2017-03-15 07:38:13 -0700124TEST_P(TransportFeedbackPacketLossTrackerTest, EmptyWindow) {
elad.alon6a64bd92017-03-21 05:58:04 -0700125 TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 5, 5);
minyue435ddf92017-01-23 08:07:05 -0800126
elad.alond83b9672017-02-01 08:36:09 -0800127 // PLR and RPLR reported as unknown before reception of first feedback.
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200128 ValidatePacketLossStatistics(tracker, absl::nullopt, absl::nullopt);
minyue435ddf92017-01-23 08:07:05 -0800129}
130
elad.alon7af93572017-03-03 10:51:35 -0800131// A feedback received for an empty window has no effect.
elad.alon02455b22017-03-03 11:11:06 -0800132TEST_P(TransportFeedbackPacketLossTrackerTest, EmptyWindowFeedback) {
elad.alon6a64bd92017-03-21 05:58:04 -0700133 TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 3, 2);
elad.alon7af93572017-03-03 10:51:35 -0800134
elad.alon02455b22017-03-03 11:11:06 -0800135 // Feedback doesn't correspond to any packets - ignored.
136 AddTransportFeedbackAndValidate(&tracker, base_, {true, false, true});
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200137 ValidatePacketLossStatistics(tracker, absl::nullopt, absl::nullopt);
elad.alon7af93572017-03-03 10:51:35 -0800138
elad.alon02455b22017-03-03 11:11:06 -0800139 // After the packets are transmitted, acking them would have an effect.
elad.alon6a64bd92017-03-21 05:58:04 -0700140 SendPackets(&tracker, base_, 3, kDefaultSendIntervalMs);
elad.alon02455b22017-03-03 11:11:06 -0800141 AddTransportFeedbackAndValidate(&tracker, base_, {true, false, true});
142 ValidatePacketLossStatistics(tracker, 1.0f / 3.0f, 0.5f);
elad.alon7af93572017-03-03 10:51:35 -0800143}
144
minyue435ddf92017-01-23 08:07:05 -0800145// Sanity check on partially filled window.
elad.alon02455b22017-03-03 11:11:06 -0800146TEST_P(TransportFeedbackPacketLossTrackerTest, PartiallyFilledWindow) {
elad.alon6a64bd92017-03-21 05:58:04 -0700147 TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 5, 4);
minyue435ddf92017-01-23 08:07:05 -0800148
elad.alon02455b22017-03-03 11:11:06 -0800149 // PLR unknown before minimum window size reached.
150 // RPLR unknown before minimum pairs reached.
151 // Expected window contents: [] -> [1001].
elad.alon6a64bd92017-03-21 05:58:04 -0700152 SendPackets(&tracker, base_, 3, kDefaultSendIntervalMs);
elad.alon02455b22017-03-03 11:11:06 -0800153 AddTransportFeedbackAndValidate(&tracker, base_, {true, false, false, true});
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200154 ValidatePacketLossStatistics(tracker, absl::nullopt, absl::nullopt);
minyue435ddf92017-01-23 08:07:05 -0800155}
156
elad.alond83b9672017-02-01 08:36:09 -0800157// Sanity check on minimum filled window - PLR known, RPLR unknown.
elad.alon02455b22017-03-03 11:11:06 -0800158TEST_P(TransportFeedbackPacketLossTrackerTest, PlrMinimumFilledWindow) {
elad.alon6a64bd92017-03-21 05:58:04 -0700159 TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 5, 5);
minyue435ddf92017-01-23 08:07:05 -0800160
elad.alon02455b22017-03-03 11:11:06 -0800161 // PLR correctly calculated after minimum window size reached.
162 // RPLR not necessarily known at that time (not if min-pairs not reached).
163 // Expected window contents: [] -> [10011].
elad.alon6a64bd92017-03-21 05:58:04 -0700164 SendPackets(&tracker, base_, 5, kDefaultSendIntervalMs);
elad.alon02455b22017-03-03 11:11:06 -0800165 AddTransportFeedbackAndValidate(&tracker, base_,
166 {true, false, false, true, true});
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200167 ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, absl::nullopt);
minyue435ddf92017-01-23 08:07:05 -0800168}
169
elad.alond83b9672017-02-01 08:36:09 -0800170// Sanity check on minimum filled window - PLR unknown, RPLR known.
elad.alon02455b22017-03-03 11:11:06 -0800171TEST_P(TransportFeedbackPacketLossTrackerTest, RplrMinimumFilledWindow) {
elad.alon6a64bd92017-03-21 05:58:04 -0700172 TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 6, 4);
elad.alond83b9672017-02-01 08:36:09 -0800173
elad.alon02455b22017-03-03 11:11:06 -0800174 // RPLR correctly calculated after minimum pairs reached.
175 // PLR not necessarily known at that time (not if min window not reached).
176 // Expected window contents: [] -> [10011].
elad.alon6a64bd92017-03-21 05:58:04 -0700177 SendPackets(&tracker, base_, 5, kDefaultSendIntervalMs);
elad.alon02455b22017-03-03 11:11:06 -0800178 AddTransportFeedbackAndValidate(&tracker, base_,
179 {true, false, false, true, true});
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200180 ValidatePacketLossStatistics(tracker, absl::nullopt, 1.0f / 4.0f);
elad.alond83b9672017-02-01 08:36:09 -0800181}
182
elad.alon3f9b12d2017-03-15 07:38:13 -0700183// If packets are sent close enough together that the clock reading for both
184// is the same, that's handled properly.
185TEST_P(TransportFeedbackPacketLossTrackerTest, SameSentTime) {
elad.alon6a64bd92017-03-21 05:58:04 -0700186 TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 3, 2);
elad.alon3f9b12d2017-03-15 07:38:13 -0700187
188 // Expected window contents: [] -> [101].
189 SendPackets(&tracker, base_, 3, 0); // Note: time interval = 0ms.
190 AddTransportFeedbackAndValidate(&tracker, base_, {true, false, true});
191
192 ValidatePacketLossStatistics(tracker, 1.0f / 3.0f, 0.5f);
193}
194
elad.alond83b9672017-02-01 08:36:09 -0800195// Additional reports update PLR and RPLR.
elad.alon02455b22017-03-03 11:11:06 -0800196TEST_P(TransportFeedbackPacketLossTrackerTest, ExtendWindow) {
elad.alon6a64bd92017-03-21 05:58:04 -0700197 TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 5, 5);
minyue435ddf92017-01-23 08:07:05 -0800198
elad.alon6a64bd92017-03-21 05:58:04 -0700199 SendPackets(&tracker, base_, 25, kDefaultSendIntervalMs);
elad.alon7af93572017-03-03 10:51:35 -0800200
elad.alon02455b22017-03-03 11:11:06 -0800201 // Expected window contents: [] -> [10011].
202 AddTransportFeedbackAndValidate(&tracker, base_,
203 {true, false, false, true, true});
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200204 ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, absl::nullopt);
minyue435ddf92017-01-23 08:07:05 -0800205
elad.alon02455b22017-03-03 11:11:06 -0800206 // Expected window contents: [10011] -> [1001110101].
207 AddTransportFeedbackAndValidate(&tracker, base_ + 5,
208 {true, false, true, false, true});
209 ValidatePacketLossStatistics(tracker, 4.0f / 10.0f, 3.0f / 9.0f);
minyue435ddf92017-01-23 08:07:05 -0800210
elad.alon02455b22017-03-03 11:11:06 -0800211 // Expected window contents: [1001110101] -> [1001110101-GAP-10001].
212 AddTransportFeedbackAndValidate(&tracker, base_ + 20,
213 {true, false, false, false, true});
214 ValidatePacketLossStatistics(tracker, 7.0f / 15.0f, 4.0f / 13.0f);
minyue435ddf92017-01-23 08:07:05 -0800215}
216
elad.alon3f9b12d2017-03-15 07:38:13 -0700217// Correct calculation with different packet lengths.
218TEST_P(TransportFeedbackPacketLossTrackerTest, DifferentSentIntervals) {
elad.alon6a64bd92017-03-21 05:58:04 -0700219 TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 5, 4);
elad.alon3f9b12d2017-03-15 07:38:13 -0700220
elad.alon3f9b12d2017-03-15 07:38:13 -0700221 int64_t frames[] = {20, 60, 120, 20, 60};
222 for (size_t i = 0; i < sizeof(frames) / sizeof(frames[0]); i++) {
elad.alon6a64bd92017-03-21 05:58:04 -0700223 SendPackets(&tracker, {static_cast<uint16_t>(base_ + i)}, frames[i]);
elad.alon3f9b12d2017-03-15 07:38:13 -0700224 }
225
226 // Expected window contents: [] -> [10011].
227 AddTransportFeedbackAndValidate(&tracker, base_,
228 {true, false, false, true, true});
229 ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 1.0f / 4.0f);
230}
231
232// The window retains information up to sent times that exceed the the max
233// window size. The oldest packets get shifted out of window to make room
234// for the newer ones.
235TEST_P(TransportFeedbackPacketLossTrackerTest, MaxWindowSize) {
elad.alon6a64bd92017-03-21 05:58:04 -0700236 TransportFeedbackPacketLossTracker tracker(4 * kDefaultSendIntervalMs, 5, 1);
elad.alon3f9b12d2017-03-15 07:38:13 -0700237
elad.alon6a64bd92017-03-21 05:58:04 -0700238 SendPackets(&tracker, base_, 6, kDefaultSendIntervalMs, true);
elad.alon3f9b12d2017-03-15 07:38:13 -0700239
elad.alon6a64bd92017-03-21 05:58:04 -0700240 // Up to the maximum time-span retained (first + 4 * kDefaultSendIntervalMs).
elad.alon3f9b12d2017-03-15 07:38:13 -0700241 // Expected window contents: [] -> [01001].
242 AddTransportFeedbackAndValidate(&tracker, base_,
243 {false, true, false, false, true});
244 ValidatePacketLossStatistics(tracker, 3.0f / 5.0f, 2.0f / 4.0f);
245
246 // After the maximum time-span, older entries are discarded to accommodate
247 // newer ones.
248 // Expected window contents: [01001] -> [10011].
249 AddTransportFeedbackAndValidate(&tracker, base_ + 5, {true});
250 ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 1.0f / 4.0f);
251}
252
253// All packets received.
elad.alon02455b22017-03-03 11:11:06 -0800254TEST_P(TransportFeedbackPacketLossTrackerTest, AllReceived) {
elad.alon6a64bd92017-03-21 05:58:04 -0700255 TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 5, 4);
minyue435ddf92017-01-23 08:07:05 -0800256
elad.alon02455b22017-03-03 11:11:06 -0800257 // Expected window contents: [] -> [11111].
elad.alon6a64bd92017-03-21 05:58:04 -0700258 SendPackets(&tracker, base_, 5, kDefaultSendIntervalMs);
elad.alon02455b22017-03-03 11:11:06 -0800259 AddTransportFeedbackAndValidate(&tracker, base_,
260 {true, true, true, true, true});
261 ValidatePacketLossStatistics(tracker, 0.0f, 0.0f);
minyue435ddf92017-01-23 08:07:05 -0800262}
263
elad.alon3f9b12d2017-03-15 07:38:13 -0700264// All packets lost.
elad.alon02455b22017-03-03 11:11:06 -0800265TEST_P(TransportFeedbackPacketLossTrackerTest, AllLost) {
elad.alon6a64bd92017-03-21 05:58:04 -0700266 TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 5, 4);
elad.alon7af93572017-03-03 10:51:35 -0800267
elad.alon3f9b12d2017-03-15 07:38:13 -0700268 // Note: The last packet in the feedback does not belong to the stream.
269 // It's only there because we're not allowed to end a feedback with a loss.
elad.alon02455b22017-03-03 11:11:06 -0800270 // Expected window contents: [] -> [00000].
elad.alon6a64bd92017-03-21 05:58:04 -0700271 SendPackets(&tracker, base_, 5, kDefaultSendIntervalMs);
elad.alon02455b22017-03-03 11:11:06 -0800272 AddTransportFeedbackAndValidate(&tracker, base_,
273 {false, false, false, false, false, true});
274 ValidatePacketLossStatistics(tracker, 1.0f, 0.0f);
elad.alon7af93572017-03-03 10:51:35 -0800275}
276
minyue435ddf92017-01-23 08:07:05 -0800277// Repeated reports are ignored.
elad.alon02455b22017-03-03 11:11:06 -0800278TEST_P(TransportFeedbackPacketLossTrackerTest, ReportRepetition) {
elad.alon6a64bd92017-03-21 05:58:04 -0700279 TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 5, 4);
minyue435ddf92017-01-23 08:07:05 -0800280
elad.alon6a64bd92017-03-21 05:58:04 -0700281 SendPackets(&tracker, base_, 5, kDefaultSendIntervalMs);
elad.alon7af93572017-03-03 10:51:35 -0800282
elad.alon02455b22017-03-03 11:11:06 -0800283 // Expected window contents: [] -> [10011].
284 AddTransportFeedbackAndValidate(&tracker, base_,
285 {true, false, false, true, true});
286 ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 1.0f / 4.0f);
minyue435ddf92017-01-23 08:07:05 -0800287
elad.alon02455b22017-03-03 11:11:06 -0800288 // Repeat entire previous feedback
289 // Expected window contents: [10011] -> [10011].
290 AddTransportFeedbackAndValidate(&tracker, base_,
291 {true, false, false, true, true});
292 ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 1.0f / 4.0f);
minyue435ddf92017-01-23 08:07:05 -0800293}
294
295// Report overlap.
elad.alon02455b22017-03-03 11:11:06 -0800296TEST_P(TransportFeedbackPacketLossTrackerTest, ReportOverlap) {
elad.alon6a64bd92017-03-21 05:58:04 -0700297 TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 5, 1);
minyue435ddf92017-01-23 08:07:05 -0800298
elad.alon6a64bd92017-03-21 05:58:04 -0700299 SendPackets(&tracker, base_, 15, kDefaultSendIntervalMs);
elad.alon7af93572017-03-03 10:51:35 -0800300
elad.alon02455b22017-03-03 11:11:06 -0800301 // Expected window contents: [] -> [10011].
302 AddTransportFeedbackAndValidate(&tracker, base_,
303 {true, false, false, true, true});
304 ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 1.0f / 4.0f);
minyue435ddf92017-01-23 08:07:05 -0800305
elad.alon02455b22017-03-03 11:11:06 -0800306 // Expected window contents: [10011] -> [1001101].
307 AddTransportFeedbackAndValidate(&tracker, base_ + 3,
308 {true, true, false, true});
309 ValidatePacketLossStatistics(tracker, 3.0f / 7.0f, 2.0f / 6.0f);
minyue435ddf92017-01-23 08:07:05 -0800310}
311
312// Report conflict.
elad.alon02455b22017-03-03 11:11:06 -0800313TEST_P(TransportFeedbackPacketLossTrackerTest, ReportConflict) {
elad.alon6a64bd92017-03-21 05:58:04 -0700314 TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 5, 4);
minyue435ddf92017-01-23 08:07:05 -0800315
elad.alon3f9b12d2017-03-15 07:38:13 -0700316 SendPackets(&tracker, base_, 15, 10);
elad.alon7af93572017-03-03 10:51:35 -0800317
elad.alon02455b22017-03-03 11:11:06 -0800318 // Expected window contents: [] -> [01001].
319 AddTransportFeedbackAndValidate(&tracker, base_,
320 {false, true, false, false, true});
321 ValidatePacketLossStatistics(tracker, 3.0f / 5.0f, 2.0f / 4.0f);
minyue435ddf92017-01-23 08:07:05 -0800322
elad.alon02455b22017-03-03 11:11:06 -0800323 // Expected window contents: [01001] -> [11101].
324 // While false->true will be applied, true -> false will be ignored.
325 AddTransportFeedbackAndValidate(&tracker, base_, {true, false, true});
326 ValidatePacketLossStatistics(tracker, 1.0f / 5.0f, 1.0f / 4.0f);
minyue435ddf92017-01-23 08:07:05 -0800327}
328
329// Skipped packets treated as unknown (not lost).
elad.alon02455b22017-03-03 11:11:06 -0800330TEST_P(TransportFeedbackPacketLossTrackerTest, SkippedPackets) {
elad.alon6a64bd92017-03-21 05:58:04 -0700331 TransportFeedbackPacketLossTracker tracker(200 * kDefaultSendIntervalMs, 5,
332 1);
minyue435ddf92017-01-23 08:07:05 -0800333
elad.alon6a64bd92017-03-21 05:58:04 -0700334 SendPackets(&tracker, base_, 200, kDefaultSendIntervalMs);
elad.alon7af93572017-03-03 10:51:35 -0800335
elad.alon02455b22017-03-03 11:11:06 -0800336 // Expected window contents: [] -> [10011].
337 AddTransportFeedbackAndValidate(&tracker, base_,
338 {true, false, false, true, true});
339 ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 1.0f / 4.0f);
minyue435ddf92017-01-23 08:07:05 -0800340
elad.alon02455b22017-03-03 11:11:06 -0800341 // Expected window contents: [10011] -> [10011-GAP-101].
342 AddTransportFeedbackAndValidate(&tracker, base_ + 100, {true, false, true});
343 ValidatePacketLossStatistics(tracker, 3.0f / 8.0f, 2.0f / 6.0f);
minyue435ddf92017-01-23 08:07:05 -0800344}
345
elad.alon3f9b12d2017-03-15 07:38:13 -0700346// Moving a window, if it excludes some old acked messages, can leave
347// in-window unacked messages intact, and ready to be used later.
348TEST_P(TransportFeedbackPacketLossTrackerTest, MovedWindowRetainsRelevantInfo) {
349 constexpr int64_t max_window_size_ms = 100;
350 TransportFeedbackPacketLossTracker tracker(max_window_size_ms, 5, 1);
minyue435ddf92017-01-23 08:07:05 -0800351
elad.alon3f9b12d2017-03-15 07:38:13 -0700352 // Note: All messages in this test are sent 1ms apart from each other.
353 // Therefore, the delta in sequence numbers equals the timestamps delta.
354 SendPackets(&tracker, base_, 4 * max_window_size_ms, 1);
elad.alon7af93572017-03-03 10:51:35 -0800355
elad.alon02455b22017-03-03 11:11:06 -0800356 // Expected window contents: [] -> [10101].
357 AddTransportFeedbackAndValidate(&tracker, base_,
358 {true, false, true, false, true});
359 ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 2.0f / 4.0f);
minyue435ddf92017-01-23 08:07:05 -0800360
elad.alon3f9b12d2017-03-15 07:38:13 -0700361 // Expected window contents: [10101] -> [100011].
362 const int64_t moved_oldest_acked = base_ + 2 * max_window_size_ms;
363 const std::vector<bool> feedback = {true, false, false, false, true, true};
364 AddTransportFeedbackAndValidate(&tracker, moved_oldest_acked, feedback);
365 ValidatePacketLossStatistics(tracker, 3.0f / 6.0f, 1.0f / 5.0f);
minyue435ddf92017-01-23 08:07:05 -0800366
elad.alon3f9b12d2017-03-15 07:38:13 -0700367 // Having acked |feedback.size()| starting with |moved_oldest_acked|, the
368 // newest of the acked ones is now:
369 const int64_t moved_newest_acked = moved_oldest_acked + feedback.size() - 1;
370
371 // Messages that *are* more than the span-limit away from the newest
372 // acked message *are* too old. Acking them would have no effect.
373 AddTransportFeedbackAndValidate(
374 &tracker, moved_newest_acked - max_window_size_ms - 1, {true});
375 ValidatePacketLossStatistics(tracker, 3.0f / 6.0f, 1.0f / 5.0f);
376
377 // Messages that are *not* more than the span-limit away from the newest
378 // acked message are *not* too old. Acking them would have an effect.
379 AddTransportFeedbackAndValidate(
380 &tracker, moved_newest_acked - max_window_size_ms, {true});
381 ValidatePacketLossStatistics(tracker, 3.0f / 7.0f, 1.0f / 5.0f);
minyue435ddf92017-01-23 08:07:05 -0800382}
383
elad.alon3f9b12d2017-03-15 07:38:13 -0700384// Inserting feedback into the middle of a window works correctly - can
elad.alon7af93572017-03-03 10:51:35 -0800385// complete two pairs.
elad.alon02455b22017-03-03 11:11:06 -0800386TEST_P(TransportFeedbackPacketLossTrackerTest, InsertionCompletesTwoPairs) {
elad.alon6a64bd92017-03-21 05:58:04 -0700387 TransportFeedbackPacketLossTracker tracker(150 * kDefaultSendIntervalMs, 5,
388 1);
elad.alond83b9672017-02-01 08:36:09 -0800389
elad.alon6a64bd92017-03-21 05:58:04 -0700390 SendPackets(&tracker, base_, 15, kDefaultSendIntervalMs);
elad.alon7af93572017-03-03 10:51:35 -0800391
elad.alon02455b22017-03-03 11:11:06 -0800392 // Expected window contents: [] -> [10111].
393 AddTransportFeedbackAndValidate(&tracker, base_,
394 {true, false, true, true, true});
395 ValidatePacketLossStatistics(tracker, 1.0f / 5.0f, 1.0f / 4.0f);
elad.alond83b9672017-02-01 08:36:09 -0800396
elad.alon02455b22017-03-03 11:11:06 -0800397 // Expected window contents: [10111] -> [10111-GAP-10101].
398 AddTransportFeedbackAndValidate(&tracker, base_ + 7,
399 {true, false, true, false, true});
400 ValidatePacketLossStatistics(tracker, 3.0f / 10.0f, 3.0f / 8.0f);
elad.alond83b9672017-02-01 08:36:09 -0800401
elad.alon02455b22017-03-03 11:11:06 -0800402 // Insert in between, closing the gap completely.
elad.alon3f9b12d2017-03-15 07:38:13 -0700403 // Expected window contents: [10111-GAP-10101] -> [101110110101].
elad.alon02455b22017-03-03 11:11:06 -0800404 AddTransportFeedbackAndValidate(&tracker, base_ + 5, {false, true});
405 ValidatePacketLossStatistics(tracker, 4.0f / 12.0f, 4.0f / 11.0f);
elad.alond83b9672017-02-01 08:36:09 -0800406}
407
elad.alon7af93572017-03-03 10:51:35 -0800408// Sequence number gaps are not gaps in reception. However, gaps in reception
409// are still possible, if a packet which WAS sent on the stream is not acked.
elad.alon02455b22017-03-03 11:11:06 -0800410TEST_P(TransportFeedbackPacketLossTrackerTest, SanityGapsInSequenceNumbers) {
elad.alon6a64bd92017-03-21 05:58:04 -0700411 TransportFeedbackPacketLossTracker tracker(50 * kDefaultSendIntervalMs, 5, 1);
minyue435ddf92017-01-23 08:07:05 -0800412
Yves Gerey665174f2018-06-19 15:03:05 +0200413 SendPackets(
414 &tracker,
415 {static_cast<uint16_t>(base_), static_cast<uint16_t>(base_ + 2),
416 static_cast<uint16_t>(base_ + 4), static_cast<uint16_t>(base_ + 6),
417 static_cast<uint16_t>(base_ + 8)},
418 kDefaultSendIntervalMs);
minyue435ddf92017-01-23 08:07:05 -0800419
elad.alon02455b22017-03-03 11:11:06 -0800420 // Gaps in sequence numbers not considered as gaps in window, because only
421 // those sequence numbers which were associated with the stream count.
422 // Expected window contents: [] -> [11011].
423 AddTransportFeedbackAndValidate(
elad.alon3f9b12d2017-03-15 07:38:13 -0700424 // Note: Left packets belong to this stream, right ones ignored.
Yves Gerey665174f2018-06-19 15:03:05 +0200425 &tracker, base_,
426 {true, false, true, false, false, false, true, false, true, true});
elad.alon02455b22017-03-03 11:11:06 -0800427 ValidatePacketLossStatistics(tracker, 1.0f / 5.0f, 1.0f / 4.0f);
minyue435ddf92017-01-23 08:07:05 -0800428
elad.alon02455b22017-03-03 11:11:06 -0800429 // Create gap by sending [base + 10] but not acking it.
430 // Note: Acks for [base + 11] and [base + 13] ignored (other stream).
431 // Expected window contents: [11011] -> [11011-GAP-01].
Yves Gerey665174f2018-06-19 15:03:05 +0200432 SendPackets(
433 &tracker,
434 {static_cast<uint16_t>(base_ + 10), static_cast<uint16_t>(base_ + 12),
435 static_cast<uint16_t>(base_ + 14)},
436 kDefaultSendIntervalMs);
elad.alon02455b22017-03-03 11:11:06 -0800437 AddTransportFeedbackAndValidate(&tracker, base_ + 11,
438 {false, false, false, true, true});
439 ValidatePacketLossStatistics(tracker, 2.0f / 7.0f, 2.0f / 5.0f);
minyue435ddf92017-01-23 08:07:05 -0800440}
441
elad.alon3f9b12d2017-03-15 07:38:13 -0700442// The window cannot span more than 0x8000 in sequence numbers, regardless
443// of time stamps and ack/unacked status.
444TEST_P(TransportFeedbackPacketLossTrackerTest, MaxUnackedPackets) {
445 TransportFeedbackPacketLossTracker tracker(0x10000, 4, 1);
minyue435ddf92017-01-23 08:07:05 -0800446
elad.alon3f9b12d2017-03-15 07:38:13 -0700447 SendPackets(&tracker, base_, 0x2000, 1, false);
448
449 // Expected window contents: [] -> [10011].
elad.alon02455b22017-03-03 11:11:06 -0800450 AddTransportFeedbackAndValidate(&tracker, base_,
elad.alon3f9b12d2017-03-15 07:38:13 -0700451 {true, false, false, true, true});
452 ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 1.0f / 4.0f);
minyue435ddf92017-01-23 08:07:05 -0800453
elad.alon3f9b12d2017-03-15 07:38:13 -0700454 // Sending more unacked packets, up to 0x7fff from the base, does not
455 // move the window or discard any information.
456 SendPackets(&tracker, static_cast<uint16_t>(base_ + 0x8000 - 0x2000), 0x2000,
457 1, false);
458 ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 1.0f / 4.0f);
459
460 // Sending more unacked packets, up to 0x7fff from the base, does not
461 // move the window or discard any information.
462 // Expected window contents: [10011] -> [0011].
463 SendPackets(&tracker, static_cast<uint16_t>(base_ + 0x8000), 1, 1);
464 ValidatePacketLossStatistics(tracker, 2.0f / 4.0f, 1.0f / 3.0f);
minyue435ddf92017-01-23 08:07:05 -0800465}
466
elad.alon3f9b12d2017-03-15 07:38:13 -0700467// The window holds acked packets up until the difference in timestamps between
468// the oldest and newest reaches the configured maximum. Once this maximum
469// is exceeded, old packets are shifted out of window until the maximum is
470// once again observed.
471TEST_P(TransportFeedbackPacketLossTrackerTest, TimeDifferenceMaximumObserved) {
472 constexpr int64_t max_window_size_ms = 500;
473 TransportFeedbackPacketLossTracker tracker(max_window_size_ms, 3, 1);
474
475 // Note: All messages in this test are sent 1ms apart from each other.
476 // Therefore, the delta in sequence numbers equals the timestamps delta.
minyue435ddf92017-01-23 08:07:05 -0800477
elad.alon02455b22017-03-03 11:11:06 -0800478 // Baseline - window has acked messages.
elad.alon3f9b12d2017-03-15 07:38:13 -0700479 // Expected window contents: [] -> [01101].
480 const std::vector<bool> feedback = {false, true, true, false, true};
481 SendPackets(&tracker, base_, feedback.size(), 1);
482 AddTransportFeedbackAndValidate(&tracker, base_, feedback);
elad.alon02455b22017-03-03 11:11:06 -0800483 ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 2.0f / 4.0f);
minyue435ddf92017-01-23 08:07:05 -0800484
elad.alon3f9b12d2017-03-15 07:38:13 -0700485 // Test - window base not moved.
486 // Expected window contents: [01101] -> [011011].
487 AdvanceClock(max_window_size_ms - feedback.size());
488 SendPackets(&tracker, static_cast<uint16_t>(base_ + feedback.size()), 1, 1);
489 AddTransportFeedbackAndValidate(
490 &tracker, static_cast<uint16_t>(base_ + feedback.size()), {true});
elad.alon02455b22017-03-03 11:11:06 -0800491 ValidatePacketLossStatistics(tracker, 2.0f / 6.0f, 2.0f / 5.0f);
minyue435ddf92017-01-23 08:07:05 -0800492
elad.alon3f9b12d2017-03-15 07:38:13 -0700493 // Another packet, sent 1ms later, would already be too late. The window will
494 // be moved, but only after the ACK is received.
495 const uint16_t new_packet_seq_num =
496 static_cast<uint16_t>(base_ + feedback.size() + 1);
497 SendPackets(&tracker, {new_packet_seq_num}, 1);
498 ValidatePacketLossStatistics(tracker, 2.0f / 6.0f, 2.0f / 5.0f);
499 // Expected window contents: [011011] -> [110111].
500 AddTransportFeedbackAndValidate(&tracker, new_packet_seq_num, {true});
501 ValidatePacketLossStatistics(tracker, 1.0f / 6.0f, 1.0f / 5.0f);
502}
elad.alon7af93572017-03-03 10:51:35 -0800503
elad.alon3f9b12d2017-03-15 07:38:13 -0700504TEST_P(TransportFeedbackPacketLossTrackerTest, RepeatedSeqNumResetsWindow) {
elad.alon6a64bd92017-03-21 05:58:04 -0700505 TransportFeedbackPacketLossTracker tracker(50 * kDefaultSendIntervalMs, 2, 1);
elad.alon3f9b12d2017-03-15 07:38:13 -0700506
507 // Baseline - window has acked messages.
508 // Expected window contents: [] -> [01101].
elad.alon6a64bd92017-03-21 05:58:04 -0700509 SendPackets(&tracker, base_, 5, kDefaultSendIntervalMs);
elad.alon3f9b12d2017-03-15 07:38:13 -0700510 AddTransportFeedbackAndValidate(&tracker, base_,
511 {false, true, true, false, true});
512 ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 2.0f / 4.0f);
513
514 // A reset occurs.
elad.alon6a64bd92017-03-21 05:58:04 -0700515 SendPackets(&tracker, {static_cast<uint16_t>(base_ + 2)},
516 kDefaultSendIntervalMs);
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200517 ValidatePacketLossStatistics(tracker, absl::nullopt, absl::nullopt);
minyue435ddf92017-01-23 08:07:05 -0800518}
519
elad.alon3f9b12d2017-03-15 07:38:13 -0700520// The window is reset by the sending of a packet which is 0x8000 or more
521// away from the newest packet acked/unacked packet.
522TEST_P(TransportFeedbackPacketLossTrackerTest,
523 SendAfterLongSuspensionResetsWindow) {
elad.alon6a64bd92017-03-21 05:58:04 -0700524 TransportFeedbackPacketLossTracker tracker(50 * kDefaultSendIntervalMs, 2, 1);
elad.alon3f9b12d2017-03-15 07:38:13 -0700525
526 // Baseline - window has acked messages.
527 // Expected window contents: [] -> [01101].
elad.alon6a64bd92017-03-21 05:58:04 -0700528 SendPackets(&tracker, base_, 5, kDefaultSendIntervalMs);
elad.alon3f9b12d2017-03-15 07:38:13 -0700529 AddTransportFeedbackAndValidate(&tracker, base_,
530 {false, true, true, false, true});
531 ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 2.0f / 4.0f);
532
533 // A reset occurs.
elad.alon6a64bd92017-03-21 05:58:04 -0700534 SendPackets(&tracker, {static_cast<uint16_t>(base_ + 5 + 0x8000)},
535 kDefaultSendIntervalMs);
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200536 ValidatePacketLossStatistics(tracker, absl::nullopt, absl::nullopt);
elad.alon3f9b12d2017-03-15 07:38:13 -0700537}
538
539#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
540TEST(TransportFeedbackPacketLossTrackerTest, InvalidConfigMaxWindowSize) {
541 EXPECT_DEATH(TransportFeedbackPacketLossTracker tracker(0, 20, 10), "");
542}
543
544TEST(TransportFeedbackPacketLossTrackerTest, InvalidConfigPlrMinAcked) {
545 EXPECT_DEATH(TransportFeedbackPacketLossTracker tracker(5000, 0, 10), "");
546}
547
548TEST(TransportFeedbackPacketLossTrackerTest, InvalidConfigRplrMinPairs) {
549 EXPECT_DEATH(TransportFeedbackPacketLossTracker tracker(5000, 20, 0), "");
550}
551
552TEST(TransportFeedbackPacketLossTrackerTest, TimeCantFlowBackwards) {
553 TransportFeedbackPacketLossTracker tracker(5000, 2, 1);
554 tracker.OnPacketAdded(100, 0);
555 tracker.OnPacketAdded(101, 2);
556 EXPECT_DEATH(tracker.OnPacketAdded(102, 1), "");
557}
558#endif
559
elad.alon02455b22017-03-03 11:11:06 -0800560// All tests are run multiple times with various baseline sequence number,
561// to weed out potential bugs with wrap-around handling.
562constexpr uint16_t kBases[] = {0x0000, 0x3456, 0xc032, 0xfffe};
563
Mirko Bonadeic84f6612019-01-31 12:20:57 +0100564INSTANTIATE_TEST_SUITE_P(_,
565 TransportFeedbackPacketLossTrackerTest,
566 testing::ValuesIn(kBases));
elad.alon02455b22017-03-03 11:11:06 -0800567
minyue435ddf92017-01-23 08:07:05 -0800568} // namespace webrtc