blob: 08a4ff692818ebe9e0e2e211220e731a92af633e [file] [log] [blame]
QUICHE team82dee2f2019-01-18 12:35:12 -05001// Copyright (c) 2015 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 "net/third_party/quiche/src/spdy/core/priority_write_scheduler.h"
6
7#include "testing/gtest/include/gtest/gtest.h"
8#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h"
9#include "net/third_party/quiche/src/spdy/core/spdy_test_utils.h"
danzh57252942019-04-17 08:26:12 -070010#include "net/third_party/quiche/src/spdy/platform/api/spdy_test_helpers.h"
QUICHE team82dee2f2019-01-18 12:35:12 -050011
12namespace spdy {
13namespace test {
14
15template <typename StreamIdType>
16class PriorityWriteSchedulerPeer {
17 public:
18 explicit PriorityWriteSchedulerPeer(
19 PriorityWriteScheduler<StreamIdType>* scheduler)
20 : scheduler_(scheduler) {}
21
22 size_t NumReadyStreams(SpdyPriority priority) const {
23 return scheduler_->priority_infos_[priority].ready_list.size();
24 }
25
26 private:
27 PriorityWriteScheduler<StreamIdType>* scheduler_;
28};
29
30namespace {
31
32class PriorityWriteSchedulerTest : public ::testing::Test {
33 public:
34 PriorityWriteSchedulerTest() : peer_(&scheduler_) {}
35
36 PriorityWriteScheduler<SpdyStreamId> scheduler_;
37 PriorityWriteSchedulerPeer<SpdyStreamId> peer_;
38};
39
40TEST_F(PriorityWriteSchedulerTest, RegisterUnregisterStreams) {
41 EXPECT_FALSE(scheduler_.HasReadyStreams());
42 EXPECT_FALSE(scheduler_.StreamRegistered(1));
43 scheduler_.RegisterStream(1, SpdyStreamPrecedence(1));
44 EXPECT_TRUE(scheduler_.StreamRegistered(1));
45
46 // Root stream counts as already registered.
47 EXPECT_SPDY_BUG(
48 scheduler_.RegisterStream(kHttp2RootStreamId, SpdyStreamPrecedence(1)),
49 "Stream 0 already registered");
50
51 // Try redundant registrations.
52 EXPECT_SPDY_BUG(scheduler_.RegisterStream(1, SpdyStreamPrecedence(1)),
53 "Stream 1 already registered");
54 EXPECT_SPDY_BUG(scheduler_.RegisterStream(1, SpdyStreamPrecedence(2)),
55 "Stream 1 already registered");
56
57 scheduler_.RegisterStream(2, SpdyStreamPrecedence(3));
58
59 // Verify registration != ready.
60 EXPECT_FALSE(scheduler_.HasReadyStreams());
61
62 scheduler_.UnregisterStream(1);
63 scheduler_.UnregisterStream(2);
64
65 // Try redundant unregistration.
66 EXPECT_SPDY_BUG(scheduler_.UnregisterStream(1), "Stream 1 not registered");
67 EXPECT_SPDY_BUG(scheduler_.UnregisterStream(2), "Stream 2 not registered");
68}
69
70TEST_F(PriorityWriteSchedulerTest, RegisterStreamWithHttp2StreamDependency) {
71 EXPECT_FALSE(scheduler_.HasReadyStreams());
72 EXPECT_FALSE(scheduler_.StreamRegistered(1));
73 scheduler_.RegisterStream(
74 1, SpdyStreamPrecedence(kHttp2RootStreamId, 123, false));
75 EXPECT_TRUE(scheduler_.StreamRegistered(1));
76 EXPECT_TRUE(scheduler_.GetStreamPrecedence(1).is_spdy3_priority());
77 EXPECT_EQ(3, scheduler_.GetStreamPrecedence(1).spdy3_priority());
78 EXPECT_FALSE(scheduler_.HasReadyStreams());
79
80 EXPECT_SPDY_BUG(scheduler_.RegisterStream(
81 1, SpdyStreamPrecedence(kHttp2RootStreamId, 256, false)),
82 "Stream 1 already registered");
83 EXPECT_TRUE(scheduler_.GetStreamPrecedence(1).is_spdy3_priority());
84 EXPECT_EQ(3, scheduler_.GetStreamPrecedence(1).spdy3_priority());
85
86 // Registering stream with a non-existent parent stream is permissible, per
87 // b/15676312, but parent stream will always be reset to 0.
88 scheduler_.RegisterStream(2, SpdyStreamPrecedence(3, 123, false));
89 EXPECT_TRUE(scheduler_.StreamRegistered(2));
90 EXPECT_FALSE(scheduler_.StreamRegistered(3));
91 EXPECT_EQ(kHttp2RootStreamId, scheduler_.GetStreamPrecedence(2).parent_id());
92}
93
94TEST_F(PriorityWriteSchedulerTest, GetStreamPrecedence) {
95 // Unknown streams tolerated due to b/15676312. However, return lowest
96 // priority.
97 EXPECT_EQ(kV3LowestPriority,
98 scheduler_.GetStreamPrecedence(1).spdy3_priority());
99
100 scheduler_.RegisterStream(1, SpdyStreamPrecedence(3));
101 EXPECT_TRUE(scheduler_.GetStreamPrecedence(1).is_spdy3_priority());
102 EXPECT_EQ(3, scheduler_.GetStreamPrecedence(1).spdy3_priority());
103
104 // Redundant registration shouldn't change stream priority.
105 EXPECT_SPDY_BUG(scheduler_.RegisterStream(1, SpdyStreamPrecedence(4)),
106 "Stream 1 already registered");
107 EXPECT_EQ(3, scheduler_.GetStreamPrecedence(1).spdy3_priority());
108
109 scheduler_.UpdateStreamPrecedence(1, SpdyStreamPrecedence(5));
110 EXPECT_EQ(5, scheduler_.GetStreamPrecedence(1).spdy3_priority());
111
112 // Toggling ready state shouldn't change stream priority.
113 scheduler_.MarkStreamReady(1, true);
114 EXPECT_EQ(5, scheduler_.GetStreamPrecedence(1).spdy3_priority());
115
116 // Test changing priority of ready stream.
117 EXPECT_EQ(1u, peer_.NumReadyStreams(5));
118 scheduler_.UpdateStreamPrecedence(1, SpdyStreamPrecedence(6));
119 EXPECT_EQ(6, scheduler_.GetStreamPrecedence(1).spdy3_priority());
120 EXPECT_EQ(0u, peer_.NumReadyStreams(5));
121 EXPECT_EQ(1u, peer_.NumReadyStreams(6));
122
123 EXPECT_EQ(1u, scheduler_.PopNextReadyStream());
124 EXPECT_EQ(6, scheduler_.GetStreamPrecedence(1).spdy3_priority());
125
126 scheduler_.UnregisterStream(1);
127 EXPECT_EQ(kV3LowestPriority,
128 scheduler_.GetStreamPrecedence(1).spdy3_priority());
129}
130
131TEST_F(PriorityWriteSchedulerTest, PopNextReadyStreamAndPrecedence) {
132 scheduler_.RegisterStream(1, SpdyStreamPrecedence(3));
133 scheduler_.MarkStreamReady(1, true);
134 EXPECT_EQ(std::make_tuple(1u, SpdyStreamPrecedence(3)),
135 scheduler_.PopNextReadyStreamAndPrecedence());
136 scheduler_.UnregisterStream(1);
137}
138
139TEST_F(PriorityWriteSchedulerTest, UpdateStreamPrecedence) {
140 // For the moment, updating stream precedence on a non-registered stream
141 // should have no effect. In the future, it will lazily cause the stream to
142 // be registered (b/15676312).
143 EXPECT_EQ(kV3LowestPriority,
144 scheduler_.GetStreamPrecedence(3).spdy3_priority());
145 EXPECT_FALSE(scheduler_.StreamRegistered(3));
146 scheduler_.UpdateStreamPrecedence(3, SpdyStreamPrecedence(1));
147 EXPECT_FALSE(scheduler_.StreamRegistered(3));
148 EXPECT_EQ(kV3LowestPriority,
149 scheduler_.GetStreamPrecedence(3).spdy3_priority());
150
151 scheduler_.RegisterStream(3, SpdyStreamPrecedence(1));
152 EXPECT_EQ(1, scheduler_.GetStreamPrecedence(3).spdy3_priority());
153 scheduler_.UpdateStreamPrecedence(3, SpdyStreamPrecedence(2));
154 EXPECT_EQ(2, scheduler_.GetStreamPrecedence(3).spdy3_priority());
155
156 // Updating priority of stream to current priority value is valid, but has no
157 // effect.
158 scheduler_.UpdateStreamPrecedence(3, SpdyStreamPrecedence(2));
159 EXPECT_EQ(2, scheduler_.GetStreamPrecedence(3).spdy3_priority());
160
161 // Even though stream 4 is marked ready after stream 5, it should be returned
162 // first by PopNextReadyStream() since it has higher priority.
163 scheduler_.RegisterStream(4, SpdyStreamPrecedence(1));
164 scheduler_.MarkStreamReady(3, false); // priority 2
165 EXPECT_TRUE(scheduler_.IsStreamReady(3));
166 scheduler_.MarkStreamReady(4, false); // priority 1
167 EXPECT_TRUE(scheduler_.IsStreamReady(4));
168 EXPECT_EQ(4u, scheduler_.PopNextReadyStream());
169 EXPECT_FALSE(scheduler_.IsStreamReady(4));
170 EXPECT_EQ(3u, scheduler_.PopNextReadyStream());
171 EXPECT_FALSE(scheduler_.IsStreamReady(3));
172
173 // Verify that lowering priority of stream 4 causes it to be returned later
174 // by PopNextReadyStream().
175 scheduler_.MarkStreamReady(3, false); // priority 2
176 scheduler_.MarkStreamReady(4, false); // priority 1
177 scheduler_.UpdateStreamPrecedence(4, SpdyStreamPrecedence(3));
178 EXPECT_EQ(3u, scheduler_.PopNextReadyStream());
179 EXPECT_EQ(4u, scheduler_.PopNextReadyStream());
180
181 scheduler_.UnregisterStream(3);
182}
183
184TEST_F(PriorityWriteSchedulerTest,
185 UpdateStreamPrecedenceWithHttp2StreamDependency) {
186 // Unknown streams tolerated due to b/15676312, but should have no effect.
187 scheduler_.UpdateStreamPrecedence(3, SpdyStreamPrecedence(0, 100, false));
188 EXPECT_FALSE(scheduler_.StreamRegistered(3));
189
190 scheduler_.RegisterStream(3, SpdyStreamPrecedence(3));
191 scheduler_.UpdateStreamPrecedence(3, SpdyStreamPrecedence(0, 100, false));
192 EXPECT_TRUE(scheduler_.GetStreamPrecedence(3).is_spdy3_priority());
193 EXPECT_EQ(4, scheduler_.GetStreamPrecedence(3).spdy3_priority());
194
195 scheduler_.UnregisterStream(3);
196 scheduler_.UpdateStreamPrecedence(3, SpdyStreamPrecedence(0, 100, false));
197 EXPECT_FALSE(scheduler_.StreamRegistered(3));
198}
199
200TEST_F(PriorityWriteSchedulerTest, MarkStreamReadyBack) {
201 EXPECT_FALSE(scheduler_.HasReadyStreams());
202 EXPECT_SPDY_BUG(scheduler_.MarkStreamReady(1, false),
203 "Stream 1 not registered");
204 EXPECT_FALSE(scheduler_.HasReadyStreams());
205 EXPECT_SPDY_BUG(EXPECT_EQ(0u, scheduler_.PopNextReadyStream()),
206 "No ready streams available");
207
208 // Add a bunch of ready streams to tail of per-priority lists.
209 // Expected order: (P2) 4, (P3) 1, 2, 3, (P5) 5.
210 scheduler_.RegisterStream(1, SpdyStreamPrecedence(3));
211 scheduler_.MarkStreamReady(1, false);
212 EXPECT_TRUE(scheduler_.HasReadyStreams());
213 scheduler_.RegisterStream(2, SpdyStreamPrecedence(3));
214 scheduler_.MarkStreamReady(2, false);
215 scheduler_.RegisterStream(3, SpdyStreamPrecedence(3));
216 scheduler_.MarkStreamReady(3, false);
217 scheduler_.RegisterStream(4, SpdyStreamPrecedence(2));
218 scheduler_.MarkStreamReady(4, false);
219 scheduler_.RegisterStream(5, SpdyStreamPrecedence(5));
220 scheduler_.MarkStreamReady(5, false);
221
222 EXPECT_EQ(4u, scheduler_.PopNextReadyStream());
223 EXPECT_EQ(1u, scheduler_.PopNextReadyStream());
224 EXPECT_EQ(2u, scheduler_.PopNextReadyStream());
225 EXPECT_EQ(3u, scheduler_.PopNextReadyStream());
226 EXPECT_EQ(5u, scheduler_.PopNextReadyStream());
227 EXPECT_SPDY_BUG(EXPECT_EQ(0u, scheduler_.PopNextReadyStream()),
228 "No ready streams available");
229}
230
231TEST_F(PriorityWriteSchedulerTest, MarkStreamReadyFront) {
232 EXPECT_FALSE(scheduler_.HasReadyStreams());
233 EXPECT_SPDY_BUG(scheduler_.MarkStreamReady(1, true),
234 "Stream 1 not registered");
235 EXPECT_FALSE(scheduler_.HasReadyStreams());
236 EXPECT_SPDY_BUG(EXPECT_EQ(0u, scheduler_.PopNextReadyStream()),
237 "No ready streams available");
238
239 // Add a bunch of ready streams to head of per-priority lists.
240 // Expected order: (P2) 4, (P3) 3, 2, 1, (P5) 5
241 scheduler_.RegisterStream(1, SpdyStreamPrecedence(3));
242 scheduler_.MarkStreamReady(1, true);
243 EXPECT_TRUE(scheduler_.HasReadyStreams());
244 scheduler_.RegisterStream(2, SpdyStreamPrecedence(3));
245 scheduler_.MarkStreamReady(2, true);
246 scheduler_.RegisterStream(3, SpdyStreamPrecedence(3));
247 scheduler_.MarkStreamReady(3, true);
248 scheduler_.RegisterStream(4, SpdyStreamPrecedence(2));
249 scheduler_.MarkStreamReady(4, true);
250 scheduler_.RegisterStream(5, SpdyStreamPrecedence(5));
251 scheduler_.MarkStreamReady(5, true);
252
253 EXPECT_EQ(4u, scheduler_.PopNextReadyStream());
254 EXPECT_EQ(3u, scheduler_.PopNextReadyStream());
255 EXPECT_EQ(2u, scheduler_.PopNextReadyStream());
256 EXPECT_EQ(1u, scheduler_.PopNextReadyStream());
257 EXPECT_EQ(5u, scheduler_.PopNextReadyStream());
258 EXPECT_SPDY_BUG(EXPECT_EQ(0u, scheduler_.PopNextReadyStream()),
259 "No ready streams available");
260}
261
262TEST_F(PriorityWriteSchedulerTest, MarkStreamReadyBackAndFront) {
263 scheduler_.RegisterStream(1, SpdyStreamPrecedence(4));
264 scheduler_.RegisterStream(2, SpdyStreamPrecedence(3));
265 scheduler_.RegisterStream(3, SpdyStreamPrecedence(3));
266 scheduler_.RegisterStream(4, SpdyStreamPrecedence(3));
267 scheduler_.RegisterStream(5, SpdyStreamPrecedence(4));
268 scheduler_.RegisterStream(6, SpdyStreamPrecedence(1));
269
270 // Add a bunch of ready streams to per-priority lists, with variety of adding
271 // at head and tail.
272 // Expected order: (P1) 6, (P3) 4, 2, 3, (P4) 1, 5
273 scheduler_.MarkStreamReady(1, true);
274 scheduler_.MarkStreamReady(2, true);
275 scheduler_.MarkStreamReady(3, false);
276 scheduler_.MarkStreamReady(4, true);
277 scheduler_.MarkStreamReady(5, false);
278 scheduler_.MarkStreamReady(6, true);
279
280 EXPECT_EQ(6u, scheduler_.PopNextReadyStream());
281 EXPECT_EQ(4u, scheduler_.PopNextReadyStream());
282 EXPECT_EQ(2u, scheduler_.PopNextReadyStream());
283 EXPECT_EQ(3u, scheduler_.PopNextReadyStream());
284 EXPECT_EQ(1u, scheduler_.PopNextReadyStream());
285 EXPECT_EQ(5u, scheduler_.PopNextReadyStream());
286 EXPECT_SPDY_BUG(EXPECT_EQ(0u, scheduler_.PopNextReadyStream()),
287 "No ready streams available");
288}
289
290TEST_F(PriorityWriteSchedulerTest, MarkStreamNotReady) {
291 // Verify ready state reflected in NumReadyStreams().
292 scheduler_.RegisterStream(1, SpdyStreamPrecedence(1));
293 EXPECT_EQ(0u, scheduler_.NumReadyStreams());
294 scheduler_.MarkStreamReady(1, false);
295 EXPECT_EQ(1u, scheduler_.NumReadyStreams());
296 scheduler_.MarkStreamNotReady(1);
297 EXPECT_EQ(0u, scheduler_.NumReadyStreams());
298
299 // Empty pop should fail.
300 EXPECT_SPDY_BUG(EXPECT_EQ(0u, scheduler_.PopNextReadyStream()),
301 "No ready streams available");
302
303 // Tolerate redundant marking of a stream as not ready.
304 scheduler_.MarkStreamNotReady(1);
305 EXPECT_EQ(0u, scheduler_.NumReadyStreams());
306
307 // Should only be able to mark registered streams.
308 EXPECT_SPDY_BUG(scheduler_.MarkStreamNotReady(3), "Stream 3 not registered");
309}
310
311TEST_F(PriorityWriteSchedulerTest, UnregisterRemovesStream) {
312 scheduler_.RegisterStream(3, SpdyStreamPrecedence(4));
313 scheduler_.MarkStreamReady(3, false);
314 EXPECT_EQ(1u, scheduler_.NumReadyStreams());
315
316 // Unregistering a stream should remove it from set of ready streams.
317 scheduler_.UnregisterStream(3);
318 EXPECT_EQ(0u, scheduler_.NumReadyStreams());
319 EXPECT_SPDY_BUG(EXPECT_EQ(0u, scheduler_.PopNextReadyStream()),
320 "No ready streams available");
321}
322
323TEST_F(PriorityWriteSchedulerTest, ShouldYield) {
324 scheduler_.RegisterStream(1, SpdyStreamPrecedence(1));
325 scheduler_.RegisterStream(4, SpdyStreamPrecedence(4));
326 scheduler_.RegisterStream(5, SpdyStreamPrecedence(4));
327 scheduler_.RegisterStream(7, SpdyStreamPrecedence(7));
328
329 // Make sure we don't yield when the list is empty.
330 EXPECT_FALSE(scheduler_.ShouldYield(1));
331
332 // Add a low priority stream.
333 scheduler_.MarkStreamReady(4, false);
334 // 4 should not yield to itself.
335 EXPECT_FALSE(scheduler_.ShouldYield(4));
336 // 7 should yield as 4 is blocked and a higher priority.
337 EXPECT_TRUE(scheduler_.ShouldYield(7));
338 // 5 should yield to 4 as they are the same priority.
339 EXPECT_TRUE(scheduler_.ShouldYield(5));
340 // 1 should not yield as 1 is higher priority.
341 EXPECT_FALSE(scheduler_.ShouldYield(1));
342
343 // Add a second stream in that priority class.
344 scheduler_.MarkStreamReady(5, false);
345 // 4 and 5 are both blocked, but 4 is at the front so should not yield.
346 EXPECT_FALSE(scheduler_.ShouldYield(4));
347 EXPECT_TRUE(scheduler_.ShouldYield(5));
348}
349
350TEST_F(PriorityWriteSchedulerTest, GetLatestEventWithPrecedence) {
351 EXPECT_SPDY_BUG(scheduler_.RecordStreamEventTime(3, 5),
352 "Stream 3 not registered");
353 EXPECT_SPDY_BUG(EXPECT_EQ(0, scheduler_.GetLatestEventWithPrecedence(4)),
354 "Stream 4 not registered");
355
356 for (int i = 1; i < 5; ++i) {
357 scheduler_.RegisterStream(i, SpdyStreamPrecedence(i));
358 }
359 for (int i = 1; i < 5; ++i) {
360 EXPECT_EQ(0, scheduler_.GetLatestEventWithPrecedence(i));
361 }
362 for (int i = 1; i < 5; ++i) {
363 scheduler_.RecordStreamEventTime(i, i * 100);
364 }
365 for (int i = 1; i < 5; ++i) {
366 EXPECT_EQ((i - 1) * 100, scheduler_.GetLatestEventWithPrecedence(i));
367 }
368}
369
370} // namespace
371} // namespace test
372} // namespace spdy