blob: 1c00b47c1c79c705611018c78519a0878ab84412 [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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020016#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
17#include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h"
18#include "rtc_base/checks.h"
19#include "test/gmock.h"
20#include "test/gtest.h"
21#include "voice_engine/transport_feedback_packet_loss_tracker.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,
97 rtc::Optional<float> expected_plr,
98 rtc::Optional<float> expected_rplr) {
eladalonedd6eea2017-05-25 00:15:35 -070099 // TODO(eladalon): Comparing the rtc::Optional<float> directly would have
elad.alon3f9b12d2017-03-15 07:38:13 -0700100 // given concise code, but less readable error messages. If we modify
101 // the way rtc::Optional is printed, we can get rid of this.
102 rtc::Optional<float> plr = tracker.GetPacketLossRate();
103 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
elad.alon3f9b12d2017-03-15 07:38:13 -0700108 rtc::Optional<float> rplr = tracker.GetRecoverablePacketLossRate();
109 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 // Convenience function for when both are valid, and explicitly stating
116 // the rtc::Optional<float> constructor is just cumbersome.
117 void ValidatePacketLossStatistics(
118 const TransportFeedbackPacketLossTracker& tracker,
119 float expected_plr,
120 float expected_rplr) {
121 ValidatePacketLossStatistics(tracker, rtc::Optional<float>(expected_plr),
122 rtc::Optional<float>(expected_rplr));
elad.alond83b9672017-02-01 08:36:09 -0800123 }
124
elad.alon3f9b12d2017-03-15 07:38:13 -0700125 uint16_t base_{GetParam()};
minyue435ddf92017-01-23 08:07:05 -0800126
elad.alon3f9b12d2017-03-15 07:38:13 -0700127 private:
128 int64_t time_ms_{0};
elad.alond83b9672017-02-01 08:36:09 -0800129
elad.alon3f9b12d2017-03-15 07:38:13 -0700130 RTC_DISALLOW_COPY_AND_ASSIGN(TransportFeedbackPacketLossTrackerTest);
131};
elad.alond83b9672017-02-01 08:36:09 -0800132
minyue435ddf92017-01-23 08:07:05 -0800133} // namespace
134
135// Sanity check on an empty window.
elad.alon3f9b12d2017-03-15 07:38:13 -0700136TEST_P(TransportFeedbackPacketLossTrackerTest, EmptyWindow) {
elad.alon6a64bd92017-03-21 05:58:04 -0700137 TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 5, 5);
minyue435ddf92017-01-23 08:07:05 -0800138
elad.alond83b9672017-02-01 08:36:09 -0800139 // PLR and RPLR reported as unknown before reception of first feedback.
140 ValidatePacketLossStatistics(tracker,
141 rtc::Optional<float>(),
142 rtc::Optional<float>());
minyue435ddf92017-01-23 08:07:05 -0800143}
144
elad.alon7af93572017-03-03 10:51:35 -0800145// A feedback received for an empty window has no effect.
elad.alon02455b22017-03-03 11:11:06 -0800146TEST_P(TransportFeedbackPacketLossTrackerTest, EmptyWindowFeedback) {
elad.alon6a64bd92017-03-21 05:58:04 -0700147 TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 3, 2);
elad.alon7af93572017-03-03 10:51:35 -0800148
elad.alon02455b22017-03-03 11:11:06 -0800149 // Feedback doesn't correspond to any packets - ignored.
150 AddTransportFeedbackAndValidate(&tracker, base_, {true, false, true});
151 ValidatePacketLossStatistics(tracker,
152 rtc::Optional<float>(),
153 rtc::Optional<float>());
elad.alon7af93572017-03-03 10:51:35 -0800154
elad.alon02455b22017-03-03 11:11:06 -0800155 // After the packets are transmitted, acking them would have an effect.
elad.alon6a64bd92017-03-21 05:58:04 -0700156 SendPackets(&tracker, base_, 3, kDefaultSendIntervalMs);
elad.alon02455b22017-03-03 11:11:06 -0800157 AddTransportFeedbackAndValidate(&tracker, base_, {true, false, true});
158 ValidatePacketLossStatistics(tracker, 1.0f / 3.0f, 0.5f);
elad.alon7af93572017-03-03 10:51:35 -0800159}
160
minyue435ddf92017-01-23 08:07:05 -0800161// Sanity check on partially filled window.
elad.alon02455b22017-03-03 11:11:06 -0800162TEST_P(TransportFeedbackPacketLossTrackerTest, PartiallyFilledWindow) {
elad.alon6a64bd92017-03-21 05:58:04 -0700163 TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 5, 4);
minyue435ddf92017-01-23 08:07:05 -0800164
elad.alon02455b22017-03-03 11:11:06 -0800165 // PLR unknown before minimum window size reached.
166 // RPLR unknown before minimum pairs reached.
167 // Expected window contents: [] -> [1001].
elad.alon6a64bd92017-03-21 05:58:04 -0700168 SendPackets(&tracker, base_, 3, kDefaultSendIntervalMs);
elad.alon02455b22017-03-03 11:11:06 -0800169 AddTransportFeedbackAndValidate(&tracker, base_, {true, false, false, true});
170 ValidatePacketLossStatistics(tracker,
171 rtc::Optional<float>(),
172 rtc::Optional<float>());
minyue435ddf92017-01-23 08:07:05 -0800173}
174
elad.alond83b9672017-02-01 08:36:09 -0800175// Sanity check on minimum filled window - PLR known, RPLR unknown.
elad.alon02455b22017-03-03 11:11:06 -0800176TEST_P(TransportFeedbackPacketLossTrackerTest, PlrMinimumFilledWindow) {
elad.alon6a64bd92017-03-21 05:58:04 -0700177 TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 5, 5);
minyue435ddf92017-01-23 08:07:05 -0800178
elad.alon02455b22017-03-03 11:11:06 -0800179 // PLR correctly calculated after minimum window size reached.
180 // RPLR not necessarily known at that time (not if min-pairs not reached).
181 // Expected window contents: [] -> [10011].
elad.alon6a64bd92017-03-21 05:58:04 -0700182 SendPackets(&tracker, base_, 5, kDefaultSendIntervalMs);
elad.alon02455b22017-03-03 11:11:06 -0800183 AddTransportFeedbackAndValidate(&tracker, base_,
184 {true, false, false, true, true});
185 ValidatePacketLossStatistics(tracker,
186 rtc::Optional<float>(2.0f / 5.0f),
187 rtc::Optional<float>());
minyue435ddf92017-01-23 08:07:05 -0800188}
189
elad.alond83b9672017-02-01 08:36:09 -0800190// Sanity check on minimum filled window - PLR unknown, RPLR known.
elad.alon02455b22017-03-03 11:11:06 -0800191TEST_P(TransportFeedbackPacketLossTrackerTest, RplrMinimumFilledWindow) {
elad.alon6a64bd92017-03-21 05:58:04 -0700192 TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 6, 4);
elad.alond83b9672017-02-01 08:36:09 -0800193
elad.alon02455b22017-03-03 11:11:06 -0800194 // RPLR correctly calculated after minimum pairs reached.
195 // PLR not necessarily known at that time (not if min window not reached).
196 // Expected window contents: [] -> [10011].
elad.alon6a64bd92017-03-21 05:58:04 -0700197 SendPackets(&tracker, base_, 5, kDefaultSendIntervalMs);
elad.alon02455b22017-03-03 11:11:06 -0800198 AddTransportFeedbackAndValidate(&tracker, base_,
199 {true, false, false, true, true});
200 ValidatePacketLossStatistics(tracker,
201 rtc::Optional<float>(),
202 rtc::Optional<float>(1.0f / 4.0f));
elad.alond83b9672017-02-01 08:36:09 -0800203}
204
elad.alon3f9b12d2017-03-15 07:38:13 -0700205// If packets are sent close enough together that the clock reading for both
206// is the same, that's handled properly.
207TEST_P(TransportFeedbackPacketLossTrackerTest, SameSentTime) {
elad.alon6a64bd92017-03-21 05:58:04 -0700208 TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 3, 2);
elad.alon3f9b12d2017-03-15 07:38:13 -0700209
210 // Expected window contents: [] -> [101].
211 SendPackets(&tracker, base_, 3, 0); // Note: time interval = 0ms.
212 AddTransportFeedbackAndValidate(&tracker, base_, {true, false, true});
213
214 ValidatePacketLossStatistics(tracker, 1.0f / 3.0f, 0.5f);
215}
216
elad.alond83b9672017-02-01 08:36:09 -0800217// Additional reports update PLR and RPLR.
elad.alon02455b22017-03-03 11:11:06 -0800218TEST_P(TransportFeedbackPacketLossTrackerTest, ExtendWindow) {
elad.alon6a64bd92017-03-21 05:58:04 -0700219 TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 5, 5);
minyue435ddf92017-01-23 08:07:05 -0800220
elad.alon6a64bd92017-03-21 05:58:04 -0700221 SendPackets(&tracker, base_, 25, kDefaultSendIntervalMs);
elad.alon7af93572017-03-03 10:51:35 -0800222
elad.alon02455b22017-03-03 11:11:06 -0800223 // Expected window contents: [] -> [10011].
224 AddTransportFeedbackAndValidate(&tracker, base_,
225 {true, false, false, true, true});
226 ValidatePacketLossStatistics(tracker,
227 rtc::Optional<float>(2.0f / 5.0f),
228 rtc::Optional<float>());
minyue435ddf92017-01-23 08:07:05 -0800229
elad.alon02455b22017-03-03 11:11:06 -0800230 // Expected window contents: [10011] -> [1001110101].
231 AddTransportFeedbackAndValidate(&tracker, base_ + 5,
232 {true, false, true, false, true});
233 ValidatePacketLossStatistics(tracker, 4.0f / 10.0f, 3.0f / 9.0f);
minyue435ddf92017-01-23 08:07:05 -0800234
elad.alon02455b22017-03-03 11:11:06 -0800235 // Expected window contents: [1001110101] -> [1001110101-GAP-10001].
236 AddTransportFeedbackAndValidate(&tracker, base_ + 20,
237 {true, false, false, false, true});
238 ValidatePacketLossStatistics(tracker, 7.0f / 15.0f, 4.0f / 13.0f);
minyue435ddf92017-01-23 08:07:05 -0800239}
240
elad.alon3f9b12d2017-03-15 07:38:13 -0700241// Correct calculation with different packet lengths.
242TEST_P(TransportFeedbackPacketLossTrackerTest, DifferentSentIntervals) {
elad.alon6a64bd92017-03-21 05:58:04 -0700243 TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 5, 4);
elad.alon3f9b12d2017-03-15 07:38:13 -0700244
elad.alon3f9b12d2017-03-15 07:38:13 -0700245 int64_t frames[] = {20, 60, 120, 20, 60};
246 for (size_t i = 0; i < sizeof(frames) / sizeof(frames[0]); i++) {
elad.alon6a64bd92017-03-21 05:58:04 -0700247 SendPackets(&tracker, {static_cast<uint16_t>(base_ + i)}, frames[i]);
elad.alon3f9b12d2017-03-15 07:38:13 -0700248 }
249
250 // Expected window contents: [] -> [10011].
251 AddTransportFeedbackAndValidate(&tracker, base_,
252 {true, false, false, true, true});
253 ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 1.0f / 4.0f);
254}
255
256// The window retains information up to sent times that exceed the the max
257// window size. The oldest packets get shifted out of window to make room
258// for the newer ones.
259TEST_P(TransportFeedbackPacketLossTrackerTest, MaxWindowSize) {
elad.alon6a64bd92017-03-21 05:58:04 -0700260 TransportFeedbackPacketLossTracker tracker(4 * kDefaultSendIntervalMs, 5, 1);
elad.alon3f9b12d2017-03-15 07:38:13 -0700261
elad.alon6a64bd92017-03-21 05:58:04 -0700262 SendPackets(&tracker, base_, 6, kDefaultSendIntervalMs, true);
elad.alon3f9b12d2017-03-15 07:38:13 -0700263
elad.alon6a64bd92017-03-21 05:58:04 -0700264 // Up to the maximum time-span retained (first + 4 * kDefaultSendIntervalMs).
elad.alon3f9b12d2017-03-15 07:38:13 -0700265 // Expected window contents: [] -> [01001].
266 AddTransportFeedbackAndValidate(&tracker, base_,
267 {false, true, false, false, true});
268 ValidatePacketLossStatistics(tracker, 3.0f / 5.0f, 2.0f / 4.0f);
269
270 // After the maximum time-span, older entries are discarded to accommodate
271 // newer ones.
272 // Expected window contents: [01001] -> [10011].
273 AddTransportFeedbackAndValidate(&tracker, base_ + 5, {true});
274 ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 1.0f / 4.0f);
275}
276
277// All packets received.
elad.alon02455b22017-03-03 11:11:06 -0800278TEST_P(TransportFeedbackPacketLossTrackerTest, AllReceived) {
elad.alon6a64bd92017-03-21 05:58:04 -0700279 TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 5, 4);
minyue435ddf92017-01-23 08:07:05 -0800280
elad.alon02455b22017-03-03 11:11:06 -0800281 // Expected window contents: [] -> [11111].
elad.alon6a64bd92017-03-21 05:58:04 -0700282 SendPackets(&tracker, base_, 5, kDefaultSendIntervalMs);
elad.alon02455b22017-03-03 11:11:06 -0800283 AddTransportFeedbackAndValidate(&tracker, base_,
284 {true, true, true, true, true});
285 ValidatePacketLossStatistics(tracker, 0.0f, 0.0f);
minyue435ddf92017-01-23 08:07:05 -0800286}
287
elad.alon3f9b12d2017-03-15 07:38:13 -0700288// All packets lost.
elad.alon02455b22017-03-03 11:11:06 -0800289TEST_P(TransportFeedbackPacketLossTrackerTest, AllLost) {
elad.alon6a64bd92017-03-21 05:58:04 -0700290 TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 5, 4);
elad.alon7af93572017-03-03 10:51:35 -0800291
elad.alon3f9b12d2017-03-15 07:38:13 -0700292 // Note: The last packet in the feedback does not belong to the stream.
293 // It's only there because we're not allowed to end a feedback with a loss.
elad.alon02455b22017-03-03 11:11:06 -0800294 // Expected window contents: [] -> [00000].
elad.alon6a64bd92017-03-21 05:58:04 -0700295 SendPackets(&tracker, base_, 5, kDefaultSendIntervalMs);
elad.alon02455b22017-03-03 11:11:06 -0800296 AddTransportFeedbackAndValidate(&tracker, base_,
297 {false, false, false, false, false, true});
298 ValidatePacketLossStatistics(tracker, 1.0f, 0.0f);
elad.alon7af93572017-03-03 10:51:35 -0800299}
300
minyue435ddf92017-01-23 08:07:05 -0800301// Repeated reports are ignored.
elad.alon02455b22017-03-03 11:11:06 -0800302TEST_P(TransportFeedbackPacketLossTrackerTest, ReportRepetition) {
elad.alon6a64bd92017-03-21 05:58:04 -0700303 TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 5, 4);
minyue435ddf92017-01-23 08:07:05 -0800304
elad.alon6a64bd92017-03-21 05:58:04 -0700305 SendPackets(&tracker, base_, 5, kDefaultSendIntervalMs);
elad.alon7af93572017-03-03 10:51:35 -0800306
elad.alon02455b22017-03-03 11:11:06 -0800307 // Expected window contents: [] -> [10011].
308 AddTransportFeedbackAndValidate(&tracker, base_,
309 {true, false, false, true, true});
310 ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 1.0f / 4.0f);
minyue435ddf92017-01-23 08:07:05 -0800311
elad.alon02455b22017-03-03 11:11:06 -0800312 // Repeat entire previous feedback
313 // Expected window contents: [10011] -> [10011].
314 AddTransportFeedbackAndValidate(&tracker, base_,
315 {true, false, false, true, true});
316 ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 1.0f / 4.0f);
minyue435ddf92017-01-23 08:07:05 -0800317}
318
319// Report overlap.
elad.alon02455b22017-03-03 11:11:06 -0800320TEST_P(TransportFeedbackPacketLossTrackerTest, ReportOverlap) {
elad.alon6a64bd92017-03-21 05:58:04 -0700321 TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 5, 1);
minyue435ddf92017-01-23 08:07:05 -0800322
elad.alon6a64bd92017-03-21 05:58:04 -0700323 SendPackets(&tracker, base_, 15, kDefaultSendIntervalMs);
elad.alon7af93572017-03-03 10:51:35 -0800324
elad.alon02455b22017-03-03 11:11:06 -0800325 // Expected window contents: [] -> [10011].
326 AddTransportFeedbackAndValidate(&tracker, base_,
327 {true, false, false, true, true});
328 ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 1.0f / 4.0f);
minyue435ddf92017-01-23 08:07:05 -0800329
elad.alon02455b22017-03-03 11:11:06 -0800330 // Expected window contents: [10011] -> [1001101].
331 AddTransportFeedbackAndValidate(&tracker, base_ + 3,
332 {true, true, false, true});
333 ValidatePacketLossStatistics(tracker, 3.0f / 7.0f, 2.0f / 6.0f);
minyue435ddf92017-01-23 08:07:05 -0800334}
335
336// Report conflict.
elad.alon02455b22017-03-03 11:11:06 -0800337TEST_P(TransportFeedbackPacketLossTrackerTest, ReportConflict) {
elad.alon6a64bd92017-03-21 05:58:04 -0700338 TransportFeedbackPacketLossTracker tracker(kDefaultMaxWindowSizeMs, 5, 4);
minyue435ddf92017-01-23 08:07:05 -0800339
elad.alon3f9b12d2017-03-15 07:38:13 -0700340 SendPackets(&tracker, base_, 15, 10);
elad.alon7af93572017-03-03 10:51:35 -0800341
elad.alon02455b22017-03-03 11:11:06 -0800342 // Expected window contents: [] -> [01001].
343 AddTransportFeedbackAndValidate(&tracker, base_,
344 {false, true, false, false, true});
345 ValidatePacketLossStatistics(tracker, 3.0f / 5.0f, 2.0f / 4.0f);
minyue435ddf92017-01-23 08:07:05 -0800346
elad.alon02455b22017-03-03 11:11:06 -0800347 // Expected window contents: [01001] -> [11101].
348 // While false->true will be applied, true -> false will be ignored.
349 AddTransportFeedbackAndValidate(&tracker, base_, {true, false, true});
350 ValidatePacketLossStatistics(tracker, 1.0f / 5.0f, 1.0f / 4.0f);
minyue435ddf92017-01-23 08:07:05 -0800351}
352
353// Skipped packets treated as unknown (not lost).
elad.alon02455b22017-03-03 11:11:06 -0800354TEST_P(TransportFeedbackPacketLossTrackerTest, SkippedPackets) {
elad.alon6a64bd92017-03-21 05:58:04 -0700355 TransportFeedbackPacketLossTracker tracker(200 * kDefaultSendIntervalMs, 5,
356 1);
minyue435ddf92017-01-23 08:07:05 -0800357
elad.alon6a64bd92017-03-21 05:58:04 -0700358 SendPackets(&tracker, base_, 200, kDefaultSendIntervalMs);
elad.alon7af93572017-03-03 10:51:35 -0800359
elad.alon02455b22017-03-03 11:11:06 -0800360 // Expected window contents: [] -> [10011].
361 AddTransportFeedbackAndValidate(&tracker, base_,
362 {true, false, false, true, true});
363 ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 1.0f / 4.0f);
minyue435ddf92017-01-23 08:07:05 -0800364
elad.alon02455b22017-03-03 11:11:06 -0800365 // Expected window contents: [10011] -> [10011-GAP-101].
366 AddTransportFeedbackAndValidate(&tracker, base_ + 100, {true, false, true});
367 ValidatePacketLossStatistics(tracker, 3.0f / 8.0f, 2.0f / 6.0f);
minyue435ddf92017-01-23 08:07:05 -0800368}
369
elad.alon3f9b12d2017-03-15 07:38:13 -0700370// Moving a window, if it excludes some old acked messages, can leave
371// in-window unacked messages intact, and ready to be used later.
372TEST_P(TransportFeedbackPacketLossTrackerTest, MovedWindowRetainsRelevantInfo) {
373 constexpr int64_t max_window_size_ms = 100;
374 TransportFeedbackPacketLossTracker tracker(max_window_size_ms, 5, 1);
minyue435ddf92017-01-23 08:07:05 -0800375
elad.alon3f9b12d2017-03-15 07:38:13 -0700376 // Note: All messages in this test are sent 1ms apart from each other.
377 // Therefore, the delta in sequence numbers equals the timestamps delta.
378 SendPackets(&tracker, base_, 4 * max_window_size_ms, 1);
elad.alon7af93572017-03-03 10:51:35 -0800379
elad.alon02455b22017-03-03 11:11:06 -0800380 // Expected window contents: [] -> [10101].
381 AddTransportFeedbackAndValidate(&tracker, base_,
382 {true, false, true, false, true});
383 ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 2.0f / 4.0f);
minyue435ddf92017-01-23 08:07:05 -0800384
elad.alon3f9b12d2017-03-15 07:38:13 -0700385 // Expected window contents: [10101] -> [100011].
386 const int64_t moved_oldest_acked = base_ + 2 * max_window_size_ms;
387 const std::vector<bool> feedback = {true, false, false, false, true, true};
388 AddTransportFeedbackAndValidate(&tracker, moved_oldest_acked, feedback);
389 ValidatePacketLossStatistics(tracker, 3.0f / 6.0f, 1.0f / 5.0f);
minyue435ddf92017-01-23 08:07:05 -0800390
elad.alon3f9b12d2017-03-15 07:38:13 -0700391 // Having acked |feedback.size()| starting with |moved_oldest_acked|, the
392 // newest of the acked ones is now:
393 const int64_t moved_newest_acked = moved_oldest_acked + feedback.size() - 1;
394
395 // Messages that *are* more than the span-limit away from the newest
396 // acked message *are* too old. Acking them would have no effect.
397 AddTransportFeedbackAndValidate(
398 &tracker, moved_newest_acked - max_window_size_ms - 1, {true});
399 ValidatePacketLossStatistics(tracker, 3.0f / 6.0f, 1.0f / 5.0f);
400
401 // Messages that are *not* more than the span-limit away from the newest
402 // acked message are *not* too old. Acking them would have an effect.
403 AddTransportFeedbackAndValidate(
404 &tracker, moved_newest_acked - max_window_size_ms, {true});
405 ValidatePacketLossStatistics(tracker, 3.0f / 7.0f, 1.0f / 5.0f);
minyue435ddf92017-01-23 08:07:05 -0800406}
407
elad.alon3f9b12d2017-03-15 07:38:13 -0700408// Inserting feedback into the middle of a window works correctly - can
elad.alon7af93572017-03-03 10:51:35 -0800409// complete two pairs.
elad.alon02455b22017-03-03 11:11:06 -0800410TEST_P(TransportFeedbackPacketLossTrackerTest, InsertionCompletesTwoPairs) {
elad.alon6a64bd92017-03-21 05:58:04 -0700411 TransportFeedbackPacketLossTracker tracker(150 * kDefaultSendIntervalMs, 5,
412 1);
elad.alond83b9672017-02-01 08:36:09 -0800413
elad.alon6a64bd92017-03-21 05:58:04 -0700414 SendPackets(&tracker, base_, 15, kDefaultSendIntervalMs);
elad.alon7af93572017-03-03 10:51:35 -0800415
elad.alon02455b22017-03-03 11:11:06 -0800416 // Expected window contents: [] -> [10111].
417 AddTransportFeedbackAndValidate(&tracker, base_,
418 {true, false, true, true, true});
419 ValidatePacketLossStatistics(tracker, 1.0f / 5.0f, 1.0f / 4.0f);
elad.alond83b9672017-02-01 08:36:09 -0800420
elad.alon02455b22017-03-03 11:11:06 -0800421 // Expected window contents: [10111] -> [10111-GAP-10101].
422 AddTransportFeedbackAndValidate(&tracker, base_ + 7,
423 {true, false, true, false, true});
424 ValidatePacketLossStatistics(tracker, 3.0f / 10.0f, 3.0f / 8.0f);
elad.alond83b9672017-02-01 08:36:09 -0800425
elad.alon02455b22017-03-03 11:11:06 -0800426 // Insert in between, closing the gap completely.
elad.alon3f9b12d2017-03-15 07:38:13 -0700427 // Expected window contents: [10111-GAP-10101] -> [101110110101].
elad.alon02455b22017-03-03 11:11:06 -0800428 AddTransportFeedbackAndValidate(&tracker, base_ + 5, {false, true});
429 ValidatePacketLossStatistics(tracker, 4.0f / 12.0f, 4.0f / 11.0f);
elad.alond83b9672017-02-01 08:36:09 -0800430}
431
elad.alon7af93572017-03-03 10:51:35 -0800432// Sequence number gaps are not gaps in reception. However, gaps in reception
433// are still possible, if a packet which WAS sent on the stream is not acked.
elad.alon02455b22017-03-03 11:11:06 -0800434TEST_P(TransportFeedbackPacketLossTrackerTest, SanityGapsInSequenceNumbers) {
elad.alon6a64bd92017-03-21 05:58:04 -0700435 TransportFeedbackPacketLossTracker tracker(50 * kDefaultSendIntervalMs, 5, 1);
minyue435ddf92017-01-23 08:07:05 -0800436
elad.alon3f9b12d2017-03-15 07:38:13 -0700437 SendPackets(&tracker,
438 {static_cast<uint16_t>(base_),
439 static_cast<uint16_t>(base_ + 2),
440 static_cast<uint16_t>(base_ + 4),
441 static_cast<uint16_t>(base_ + 6),
442 static_cast<uint16_t>(base_ + 8)},
elad.alon6a64bd92017-03-21 05:58:04 -0700443 kDefaultSendIntervalMs);
minyue435ddf92017-01-23 08:07:05 -0800444
elad.alon02455b22017-03-03 11:11:06 -0800445 // Gaps in sequence numbers not considered as gaps in window, because only
446 // those sequence numbers which were associated with the stream count.
447 // Expected window contents: [] -> [11011].
448 AddTransportFeedbackAndValidate(
elad.alon3f9b12d2017-03-15 07:38:13 -0700449 // Note: Left packets belong to this stream, right ones ignored.
elad.alon02455b22017-03-03 11:11:06 -0800450 &tracker, base_, {true, false,
451 true, false,
452 false, false,
453 true, false,
454 true, true});
455 ValidatePacketLossStatistics(tracker, 1.0f / 5.0f, 1.0f / 4.0f);
minyue435ddf92017-01-23 08:07:05 -0800456
elad.alon02455b22017-03-03 11:11:06 -0800457 // Create gap by sending [base + 10] but not acking it.
458 // Note: Acks for [base + 11] and [base + 13] ignored (other stream).
459 // Expected window contents: [11011] -> [11011-GAP-01].
elad.alon3f9b12d2017-03-15 07:38:13 -0700460 SendPackets(&tracker,
461 {static_cast<uint16_t>(base_ + 10),
462 static_cast<uint16_t>(base_ + 12),
463 static_cast<uint16_t>(base_ + 14)},
elad.alon6a64bd92017-03-21 05:58:04 -0700464 kDefaultSendIntervalMs);
elad.alon02455b22017-03-03 11:11:06 -0800465 AddTransportFeedbackAndValidate(&tracker, base_ + 11,
466 {false, false, false, true, true});
467 ValidatePacketLossStatistics(tracker, 2.0f / 7.0f, 2.0f / 5.0f);
minyue435ddf92017-01-23 08:07:05 -0800468}
469
elad.alon3f9b12d2017-03-15 07:38:13 -0700470// The window cannot span more than 0x8000 in sequence numbers, regardless
471// of time stamps and ack/unacked status.
472TEST_P(TransportFeedbackPacketLossTrackerTest, MaxUnackedPackets) {
473 TransportFeedbackPacketLossTracker tracker(0x10000, 4, 1);
minyue435ddf92017-01-23 08:07:05 -0800474
elad.alon3f9b12d2017-03-15 07:38:13 -0700475 SendPackets(&tracker, base_, 0x2000, 1, false);
476
477 // Expected window contents: [] -> [10011].
elad.alon02455b22017-03-03 11:11:06 -0800478 AddTransportFeedbackAndValidate(&tracker, base_,
elad.alon3f9b12d2017-03-15 07:38:13 -0700479 {true, false, false, true, true});
480 ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 1.0f / 4.0f);
minyue435ddf92017-01-23 08:07:05 -0800481
elad.alon3f9b12d2017-03-15 07:38:13 -0700482 // Sending more unacked packets, up to 0x7fff from the base, does not
483 // move the window or discard any information.
484 SendPackets(&tracker, static_cast<uint16_t>(base_ + 0x8000 - 0x2000), 0x2000,
485 1, false);
486 ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 1.0f / 4.0f);
487
488 // Sending more unacked packets, up to 0x7fff from the base, does not
489 // move the window or discard any information.
490 // Expected window contents: [10011] -> [0011].
491 SendPackets(&tracker, static_cast<uint16_t>(base_ + 0x8000), 1, 1);
492 ValidatePacketLossStatistics(tracker, 2.0f / 4.0f, 1.0f / 3.0f);
minyue435ddf92017-01-23 08:07:05 -0800493}
494
elad.alon3f9b12d2017-03-15 07:38:13 -0700495// The window holds acked packets up until the difference in timestamps between
496// the oldest and newest reaches the configured maximum. Once this maximum
497// is exceeded, old packets are shifted out of window until the maximum is
498// once again observed.
499TEST_P(TransportFeedbackPacketLossTrackerTest, TimeDifferenceMaximumObserved) {
500 constexpr int64_t max_window_size_ms = 500;
501 TransportFeedbackPacketLossTracker tracker(max_window_size_ms, 3, 1);
502
503 // Note: All messages in this test are sent 1ms apart from each other.
504 // Therefore, the delta in sequence numbers equals the timestamps delta.
minyue435ddf92017-01-23 08:07:05 -0800505
elad.alon02455b22017-03-03 11:11:06 -0800506 // Baseline - window has acked messages.
elad.alon3f9b12d2017-03-15 07:38:13 -0700507 // Expected window contents: [] -> [01101].
508 const std::vector<bool> feedback = {false, true, true, false, true};
509 SendPackets(&tracker, base_, feedback.size(), 1);
510 AddTransportFeedbackAndValidate(&tracker, base_, feedback);
elad.alon02455b22017-03-03 11:11:06 -0800511 ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 2.0f / 4.0f);
minyue435ddf92017-01-23 08:07:05 -0800512
elad.alon3f9b12d2017-03-15 07:38:13 -0700513 // Test - window base not moved.
514 // Expected window contents: [01101] -> [011011].
515 AdvanceClock(max_window_size_ms - feedback.size());
516 SendPackets(&tracker, static_cast<uint16_t>(base_ + feedback.size()), 1, 1);
517 AddTransportFeedbackAndValidate(
518 &tracker, static_cast<uint16_t>(base_ + feedback.size()), {true});
elad.alon02455b22017-03-03 11:11:06 -0800519 ValidatePacketLossStatistics(tracker, 2.0f / 6.0f, 2.0f / 5.0f);
minyue435ddf92017-01-23 08:07:05 -0800520
elad.alon3f9b12d2017-03-15 07:38:13 -0700521 // Another packet, sent 1ms later, would already be too late. The window will
522 // be moved, but only after the ACK is received.
523 const uint16_t new_packet_seq_num =
524 static_cast<uint16_t>(base_ + feedback.size() + 1);
525 SendPackets(&tracker, {new_packet_seq_num}, 1);
526 ValidatePacketLossStatistics(tracker, 2.0f / 6.0f, 2.0f / 5.0f);
527 // Expected window contents: [011011] -> [110111].
528 AddTransportFeedbackAndValidate(&tracker, new_packet_seq_num, {true});
529 ValidatePacketLossStatistics(tracker, 1.0f / 6.0f, 1.0f / 5.0f);
530}
elad.alon7af93572017-03-03 10:51:35 -0800531
elad.alon3f9b12d2017-03-15 07:38:13 -0700532TEST_P(TransportFeedbackPacketLossTrackerTest, RepeatedSeqNumResetsWindow) {
elad.alon6a64bd92017-03-21 05:58:04 -0700533 TransportFeedbackPacketLossTracker tracker(50 * kDefaultSendIntervalMs, 2, 1);
elad.alon3f9b12d2017-03-15 07:38:13 -0700534
535 // Baseline - window has acked messages.
536 // Expected window contents: [] -> [01101].
elad.alon6a64bd92017-03-21 05:58:04 -0700537 SendPackets(&tracker, base_, 5, kDefaultSendIntervalMs);
elad.alon3f9b12d2017-03-15 07:38:13 -0700538 AddTransportFeedbackAndValidate(&tracker, base_,
539 {false, true, true, false, true});
540 ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 2.0f / 4.0f);
541
542 // A reset occurs.
elad.alon6a64bd92017-03-21 05:58:04 -0700543 SendPackets(&tracker, {static_cast<uint16_t>(base_ + 2)},
544 kDefaultSendIntervalMs);
elad.alon02455b22017-03-03 11:11:06 -0800545 ValidatePacketLossStatistics(tracker,
546 rtc::Optional<float>(),
547 rtc::Optional<float>());
minyue435ddf92017-01-23 08:07:05 -0800548}
549
elad.alon3f9b12d2017-03-15 07:38:13 -0700550// The window is reset by the sending of a packet which is 0x8000 or more
551// away from the newest packet acked/unacked packet.
552TEST_P(TransportFeedbackPacketLossTrackerTest,
553 SendAfterLongSuspensionResetsWindow) {
elad.alon6a64bd92017-03-21 05:58:04 -0700554 TransportFeedbackPacketLossTracker tracker(50 * kDefaultSendIntervalMs, 2, 1);
elad.alon3f9b12d2017-03-15 07:38:13 -0700555
556 // Baseline - window has acked messages.
557 // Expected window contents: [] -> [01101].
elad.alon6a64bd92017-03-21 05:58:04 -0700558 SendPackets(&tracker, base_, 5, kDefaultSendIntervalMs);
elad.alon3f9b12d2017-03-15 07:38:13 -0700559 AddTransportFeedbackAndValidate(&tracker, base_,
560 {false, true, true, false, true});
561 ValidatePacketLossStatistics(tracker, 2.0f / 5.0f, 2.0f / 4.0f);
562
563 // A reset occurs.
elad.alon6a64bd92017-03-21 05:58:04 -0700564 SendPackets(&tracker, {static_cast<uint16_t>(base_ + 5 + 0x8000)},
565 kDefaultSendIntervalMs);
elad.alon3f9b12d2017-03-15 07:38:13 -0700566 ValidatePacketLossStatistics(tracker,
567 rtc::Optional<float>(),
568 rtc::Optional<float>());
569}
570
571#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
572TEST(TransportFeedbackPacketLossTrackerTest, InvalidConfigMaxWindowSize) {
573 EXPECT_DEATH(TransportFeedbackPacketLossTracker tracker(0, 20, 10), "");
574}
575
576TEST(TransportFeedbackPacketLossTrackerTest, InvalidConfigPlrMinAcked) {
577 EXPECT_DEATH(TransportFeedbackPacketLossTracker tracker(5000, 0, 10), "");
578}
579
580TEST(TransportFeedbackPacketLossTrackerTest, InvalidConfigRplrMinPairs) {
581 EXPECT_DEATH(TransportFeedbackPacketLossTracker tracker(5000, 20, 0), "");
582}
583
584TEST(TransportFeedbackPacketLossTrackerTest, TimeCantFlowBackwards) {
585 TransportFeedbackPacketLossTracker tracker(5000, 2, 1);
586 tracker.OnPacketAdded(100, 0);
587 tracker.OnPacketAdded(101, 2);
588 EXPECT_DEATH(tracker.OnPacketAdded(102, 1), "");
589}
590#endif
591
elad.alon02455b22017-03-03 11:11:06 -0800592// All tests are run multiple times with various baseline sequence number,
593// to weed out potential bugs with wrap-around handling.
594constexpr uint16_t kBases[] = {0x0000, 0x3456, 0xc032, 0xfffe};
595
596INSTANTIATE_TEST_CASE_P(_,
597 TransportFeedbackPacketLossTrackerTest,
598 testing::ValuesIn(kBases));
599
minyue435ddf92017-01-23 08:07:05 -0800600} // namespace webrtc