1 // Copyright (c) 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "quiche/quic/core/quic_interval_deque.h"
6
7 #include <cstdint>
8 #include <ostream>
9
10 #include "quiche/quic/core/quic_interval.h"
11 #include "quiche/quic/platform/api/quic_expect_bug.h"
12 #include "quiche/quic/platform/api/quic_test.h"
13 #include "quiche/quic/test_tools/quic_interval_deque_peer.h"
14 #include "quiche/quic/test_tools/quic_test_utils.h"
15
16 namespace quic {
17 namespace test {
18 namespace {
19
20 const int32_t kSize = 100;
21 const std::size_t kIntervalStep = 10;
22
23 } // namespace
24
25 struct TestIntervalItem {
26 int32_t val;
27 std::size_t interval_start, interval_end;
intervalquic::test::TestIntervalItem28 QuicInterval<std::size_t> interval() const {
29 return QuicInterval<std::size_t>(interval_start, interval_end);
30 }
TestIntervalItemquic::test::TestIntervalItem31 TestIntervalItem(int32_t val, std::size_t interval_start,
32 std::size_t interval_end)
33 : val(val), interval_start(interval_start), interval_end(interval_end) {}
34 };
35
36 using QID = QuicIntervalDeque<TestIntervalItem>;
37
38 class QuicIntervalDequeTest : public QuicTest {
39 public:
QuicIntervalDequeTest()40 QuicIntervalDequeTest() {
41 // Add items with intervals of |kIntervalStep| size.
42 for (int32_t i = 0; i < kSize; ++i) {
43 const std::size_t interval_begin = kIntervalStep * i;
44 const std::size_t interval_end = interval_begin + kIntervalStep;
45 qid_.PushBack(TestIntervalItem(i, interval_begin, interval_end));
46 }
47 }
48
49 QID qid_;
50 };
51
52 // The goal of this test is to show insertion/push_back, iteration, and and
53 // deletion/pop_front from the container.
TEST_F(QuicIntervalDequeTest,InsertRemoveSize)54 TEST_F(QuicIntervalDequeTest, InsertRemoveSize) {
55 QID qid;
56
57 EXPECT_EQ(qid.Size(), std::size_t(0));
58 qid.PushBack(TestIntervalItem(0, 0, 10));
59 EXPECT_EQ(qid.Size(), std::size_t(1));
60 qid.PushBack(TestIntervalItem(1, 10, 20));
61 EXPECT_EQ(qid.Size(), std::size_t(2));
62 qid.PushBack(TestIntervalItem(2, 20, 30));
63 EXPECT_EQ(qid.Size(), std::size_t(3));
64 qid.PushBack(TestIntervalItem(3, 30, 40));
65 EXPECT_EQ(qid.Size(), std::size_t(4));
66
67 // Advance the index all the way...
68 int32_t i = 0;
69 for (auto it = qid.DataAt(0); it != qid.DataEnd(); ++it, ++i) {
70 const int32_t index = QuicIntervalDequePeer::GetCachedIndex(&qid);
71 EXPECT_EQ(index, i);
72 EXPECT_EQ(it->val, i);
73 }
74 const int32_t index = QuicIntervalDequePeer::GetCachedIndex(&qid);
75 EXPECT_EQ(index, -1);
76
77 qid.PopFront();
78 EXPECT_EQ(qid.Size(), std::size_t(3));
79 qid.PopFront();
80 EXPECT_EQ(qid.Size(), std::size_t(2));
81 qid.PopFront();
82 EXPECT_EQ(qid.Size(), std::size_t(1));
83 qid.PopFront();
84 EXPECT_EQ(qid.Size(), std::size_t(0));
85
86 EXPECT_QUIC_BUG(qid.PopFront(), "Trying to pop from an empty container.");
87 }
88
89 // The goal of this test is to push data into the container at specific
90 // intervals and show how the |DataAt| method can move the |cached_index| as the
91 // iterator moves through the data.
TEST_F(QuicIntervalDequeTest,InsertIterateWhole)92 TEST_F(QuicIntervalDequeTest, InsertIterateWhole) {
93 // The write index should point to the beginning of the container.
94 const int32_t cached_index = QuicIntervalDequePeer::GetCachedIndex(&qid_);
95 EXPECT_EQ(cached_index, 0);
96
97 auto it = qid_.DataBegin();
98 auto end = qid_.DataEnd();
99 for (int32_t i = 0; i < kSize; ++i, ++it) {
100 EXPECT_EQ(it->val, i);
101 const std::size_t current_iteraval_begin = i * kIntervalStep;
102 // The |DataAt| method should find the correct interval.
103 auto lookup = qid_.DataAt(current_iteraval_begin);
104 EXPECT_EQ(i, lookup->val);
105 // Make sure the index hasn't changed just from using |DataAt|
106 const int32_t index_before = QuicIntervalDequePeer::GetCachedIndex(&qid_);
107 EXPECT_EQ(index_before, i);
108 // This increment should move the index forward.
109 lookup++;
110 // Check that the index has changed.
111 const int32_t index_after = QuicIntervalDequePeer::GetCachedIndex(&qid_);
112 const int32_t after_i = (i + 1) == kSize ? -1 : (i + 1);
113 EXPECT_EQ(index_after, after_i);
114 EXPECT_NE(it, end);
115 }
116 }
117
118 // The goal of this test is to push data into the container at specific
119 // intervals and show how the |DataAt| method can move the |cached_index| using
120 // the off-by-one logic.
TEST_F(QuicIntervalDequeTest,OffByOne)121 TEST_F(QuicIntervalDequeTest, OffByOne) {
122 // The write index should point to the beginning of the container.
123 const int32_t cached_index = QuicIntervalDequePeer::GetCachedIndex(&qid_);
124 EXPECT_EQ(cached_index, 0);
125
126 auto it = qid_.DataBegin();
127 auto end = qid_.DataEnd();
128 for (int32_t i = 0; i < kSize - 1; ++i, ++it) {
129 EXPECT_EQ(it->val, i);
130 const int32_t off_by_one_i = i + 1;
131 const std::size_t current_iteraval_begin = off_by_one_i * kIntervalStep;
132 // Make sure the index has changed just from using |DataAt|
133 const int32_t index_before = QuicIntervalDequePeer::GetCachedIndex(&qid_);
134 EXPECT_EQ(index_before, i);
135 // The |DataAt| method should find the correct interval.
136 auto lookup = qid_.DataAt(current_iteraval_begin);
137 EXPECT_EQ(off_by_one_i, lookup->val);
138 // Check that the index has changed.
139 const int32_t index_after = QuicIntervalDequePeer::GetCachedIndex(&qid_);
140 const int32_t after_i = off_by_one_i == kSize ? -1 : off_by_one_i;
141 EXPECT_EQ(index_after, after_i);
142 EXPECT_NE(it, end);
143 }
144 }
145
146 // The goal of this test is to push data into the container at specific
147 // intervals and show modify the structure with a live iterator.
TEST_F(QuicIntervalDequeTest,IteratorInvalidation)148 TEST_F(QuicIntervalDequeTest, IteratorInvalidation) {
149 // The write index should point to the beginning of the container.
150 const int32_t cached_index = QuicIntervalDequePeer::GetCachedIndex(&qid_);
151 EXPECT_EQ(cached_index, 0);
152
153 const std::size_t iteraval_begin = (kSize - 1) * kIntervalStep;
154 auto lookup = qid_.DataAt(iteraval_begin);
155 EXPECT_EQ((*lookup).val, (kSize - 1));
156 qid_.PopFront();
157 EXPECT_QUIC_BUG(lookup++, "Iterator out of bounds.");
158 auto lookup_end = qid_.DataAt(iteraval_begin + kIntervalStep);
159 EXPECT_EQ(lookup_end, qid_.DataEnd());
160 }
161
162 // The goal of this test is the same as |InsertIterateWhole| but to
163 // skip certain intervals and show the |cached_index| is updated properly.
TEST_F(QuicIntervalDequeTest,InsertIterateSkip)164 TEST_F(QuicIntervalDequeTest, InsertIterateSkip) {
165 // The write index should point to the beginning of the container.
166 const int32_t cached_index = QuicIntervalDequePeer::GetCachedIndex(&qid_);
167 EXPECT_EQ(cached_index, 0);
168
169 const std::size_t step = 4;
170 for (int32_t i = 0; i < kSize; i += 4) {
171 if (i != 0) {
172 const int32_t before_i = (i - (step - 1));
173 EXPECT_EQ(QuicIntervalDequePeer::GetCachedIndex(&qid_), before_i);
174 }
175 const std::size_t current_iteraval_begin = i * kIntervalStep;
176 // The |DataAt| method should find the correct interval.
177 auto lookup = qid_.DataAt(current_iteraval_begin);
178 EXPECT_EQ(i, lookup->val);
179 // Make sure the index _has_ changed just from using |DataAt| since we're
180 // skipping data.
181 const int32_t index_before = QuicIntervalDequePeer::GetCachedIndex(&qid_);
182 EXPECT_EQ(index_before, i);
183 // This increment should move the index forward.
184 lookup++;
185 // Check that the index has changed.
186 const int32_t index_after = QuicIntervalDequePeer::GetCachedIndex(&qid_);
187 const int32_t after_i = (i + 1) == kSize ? -1 : (i + 1);
188 EXPECT_EQ(index_after, after_i);
189 }
190 }
191
192 // The goal of this test is the same as |InsertIterateWhole| but it has
193 // |PopFront| calls interleaved to show the |cached_index| updates correctly.
TEST_F(QuicIntervalDequeTest,InsertDeleteIterate)194 TEST_F(QuicIntervalDequeTest, InsertDeleteIterate) {
195 // The write index should point to the beginning of the container.
196 const int32_t index = QuicIntervalDequePeer::GetCachedIndex(&qid_);
197 EXPECT_EQ(index, 0);
198
199 std::size_t limit = 0;
200 for (int32_t i = 0; limit < qid_.Size(); ++i, ++limit) {
201 // Always point to the beginning of the container.
202 auto it = qid_.DataBegin();
203 EXPECT_EQ(it->val, i);
204
205 // Get an iterator.
206 const std::size_t current_iteraval_begin = i * kIntervalStep;
207 auto lookup = qid_.DataAt(current_iteraval_begin);
208 const int32_t index_before = QuicIntervalDequePeer::GetCachedIndex(&qid_);
209 // The index should always point to 0.
210 EXPECT_EQ(index_before, 0);
211 // This iterator increment should effect the index.
212 lookup++;
213 const int32_t index_after = QuicIntervalDequePeer::GetCachedIndex(&qid_);
214 EXPECT_EQ(index_after, 1);
215 // Decrement the |temp_size| and pop from the front.
216 qid_.PopFront();
217 // Show the index has been updated to point to 0 again (from 1).
218 const int32_t index_after_pop =
219 QuicIntervalDequePeer::GetCachedIndex(&qid_);
220 EXPECT_EQ(index_after_pop, 0);
221 }
222 }
223
224 // The goal of this test is to move the index to the end and then add more data
225 // to show it can be reset to a valid index.
TEST_F(QuicIntervalDequeTest,InsertIterateInsert)226 TEST_F(QuicIntervalDequeTest, InsertIterateInsert) {
227 // The write index should point to the beginning of the container.
228 const int32_t index = QuicIntervalDequePeer::GetCachedIndex(&qid_);
229 EXPECT_EQ(index, 0);
230
231 int32_t iterated_elements = 0;
232 for (int32_t i = 0; i < kSize; ++i, ++iterated_elements) {
233 // Get an iterator.
234 const std::size_t current_iteraval_begin = i * kIntervalStep;
235 auto lookup = qid_.DataAt(current_iteraval_begin);
236 const int32_t index_before = QuicIntervalDequePeer::GetCachedIndex(&qid_);
237 // The index should always point to i.
238 EXPECT_EQ(index_before, i);
239 // This iterator increment should effect the index.
240 lookup++;
241 // Show the index has been updated to point to i + 1 or -1 if at the end.
242 const int32_t index_after = QuicIntervalDequePeer::GetCachedIndex(&qid_);
243 const int32_t after_i = (i + 1) == kSize ? -1 : (i + 1);
244 EXPECT_EQ(index_after, after_i);
245 }
246 const int32_t invalid_index = QuicIntervalDequePeer::GetCachedIndex(&qid_);
247 EXPECT_EQ(invalid_index, -1);
248
249 // Add more data to the container, making the index valid.
250 const std::size_t offset = qid_.Size();
251 for (int32_t i = 0; i < kSize; ++i) {
252 const std::size_t interval_begin = offset + (kIntervalStep * i);
253 const std::size_t interval_end = offset + interval_begin + kIntervalStep;
254 qid_.PushBack(TestIntervalItem(i + offset, interval_begin, interval_end));
255 const int32_t index_current = QuicIntervalDequePeer::GetCachedIndex(&qid_);
256 // Index should now be valid and equal to the size of the container before
257 // adding more items to it.
258 EXPECT_EQ(index_current, iterated_elements);
259 }
260 // Show the index is still valid and hasn't changed since the first iteration
261 // of the loop.
262 const int32_t index_after_add = QuicIntervalDequePeer::GetCachedIndex(&qid_);
263 EXPECT_EQ(index_after_add, iterated_elements);
264
265 // Iterate over all the data in the container and eventually reset the index
266 // as we did before.
267 for (int32_t i = 0; i < kSize; ++i, ++iterated_elements) {
268 const std::size_t interval_begin = offset + (kIntervalStep * i);
269 const int32_t index_current = QuicIntervalDequePeer::GetCachedIndex(&qid_);
270 EXPECT_EQ(index_current, iterated_elements);
271 auto lookup = qid_.DataAt(interval_begin);
272 const int32_t expected_value = i + offset;
273 EXPECT_EQ(lookup->val, expected_value);
274 lookup++;
275 const int32_t after_inc =
276 (iterated_elements + 1) == (kSize * 2) ? -1 : (iterated_elements + 1);
277 const int32_t after_index = QuicIntervalDequePeer::GetCachedIndex(&qid_);
278 EXPECT_EQ(after_index, after_inc);
279 }
280 // Show the index is now invalid.
281 const int32_t invalid_index_again =
282 QuicIntervalDequePeer::GetCachedIndex(&qid_);
283 EXPECT_EQ(invalid_index_again, -1);
284 }
285
286 // The goal of this test is to push data into the container at specific
287 // intervals and show how the |DataAt| can iterate over already scanned data.
TEST_F(QuicIntervalDequeTest,RescanData)288 TEST_F(QuicIntervalDequeTest, RescanData) {
289 // The write index should point to the beginning of the container.
290 const int32_t index = QuicIntervalDequePeer::GetCachedIndex(&qid_);
291 EXPECT_EQ(index, 0);
292
293 auto it = qid_.DataBegin();
294 auto end = qid_.DataEnd();
295 for (int32_t i = 0; i < kSize - 1; ++i, ++it) {
296 EXPECT_EQ(it->val, i);
297 const std::size_t current_iteraval_begin = i * kIntervalStep;
298 // The |DataAt| method should find the correct interval.
299 auto lookup = qid_.DataAt(current_iteraval_begin);
300 EXPECT_EQ(i, lookup->val);
301 // Make sure the index has changed just from using |DataAt|
302 const int32_t cached_index_before =
303 QuicIntervalDequePeer::GetCachedIndex(&qid_);
304 EXPECT_EQ(cached_index_before, i);
305 // Ensure the real index has changed just from using |DataAt| and the
306 // off-by-one logic
307 const int32_t index_before = QuicIntervalDequePeer::GetCachedIndex(&qid_);
308 const int32_t before_i = i;
309 EXPECT_EQ(index_before, before_i);
310 // This increment should move the cached index forward.
311 lookup++;
312 // Check that the cached index has moved foward.
313 const int32_t cached_index_after =
314 QuicIntervalDequePeer::GetCachedIndex(&qid_);
315 const int32_t after_i = (i + 1);
316 EXPECT_EQ(cached_index_after, after_i);
317 EXPECT_NE(it, end);
318 }
319
320 // Iterate over items which have been consumed before.
321 int32_t expected_index = static_cast<int32_t>(kSize - 1);
322 for (int32_t i = 0; i < kSize - 1; ++i) {
323 const std::size_t current_iteraval_begin = i * kIntervalStep;
324 // The |DataAt| method should find the correct interval.
325 auto lookup = qid_.DataAt(current_iteraval_begin);
326 EXPECT_EQ(i, lookup->val);
327 // This increment shouldn't move the index forward as the index is currently
328 // ahead.
329 lookup++;
330 // Check that the index hasn't moved foward.
331 const int32_t index_after = QuicIntervalDequePeer::GetCachedIndex(&qid_);
332 EXPECT_EQ(index_after, expected_index);
333 EXPECT_NE(it, end);
334 }
335 }
336
337 // The goal of this test is to show that popping from an empty container is a
338 // bug.
TEST_F(QuicIntervalDequeTest,PopEmpty)339 TEST_F(QuicIntervalDequeTest, PopEmpty) {
340 QID qid;
341 EXPECT_TRUE(qid.Empty());
342 EXPECT_QUIC_BUG(qid.PopFront(), "Trying to pop from an empty container.");
343 }
344
345 // The goal of this test is to show that adding a zero-sized interval is a bug.
TEST_F(QuicIntervalDequeTest,ZeroSizedInterval)346 TEST_F(QuicIntervalDequeTest, ZeroSizedInterval) {
347 QID qid;
348 EXPECT_QUIC_BUG(qid.PushBack(TestIntervalItem(0, 0, 0)),
349 "Trying to save empty interval to .");
350 }
351
352 // The goal of this test is to show that an iterator to an empty container
353 // returns |DataEnd|.
TEST_F(QuicIntervalDequeTest,IteratorEmpty)354 TEST_F(QuicIntervalDequeTest, IteratorEmpty) {
355 QID qid;
356 auto it = qid.DataAt(0);
357 EXPECT_EQ(it, qid.DataEnd());
358 }
359
360 } // namespace test
361 } // namespace quic
362