1 // Copyright (c) 2012 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.
8 #include "base/compiler_specific.h"
9 #include "base/logging.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/memory/scoped_vector.h"
12 #include "net/base/prioritized_dispatcher.h"
13 #include "net/base/request_priority.h"
14 #include "testing/gtest/include/gtest/gtest.h"
20 // We rely on the priority enum values being sequential having starting at 0,
21 // and increasing for higher priorities.
22 static_assert(MINIMUM_PRIORITY
== 0u && MINIMUM_PRIORITY
== IDLE
&&
23 IDLE
< LOWEST
&& LOWEST
< HIGHEST
&&
24 HIGHEST
<= MAXIMUM_PRIORITY
,
25 "priority indexes incompatible");
27 class PrioritizedDispatcherTest
: public testing::Test
{
29 typedef PrioritizedDispatcher::Priority Priority
;
30 // A job that appends |tag| to |log| when started and '.' when finished.
31 // This is intended to confirm the execution order of a sequence of jobs added
32 // to the dispatcher. Note that finishing order of jobs does not matter.
33 class TestJob
: public PrioritizedDispatcher::Job
{
35 TestJob(PrioritizedDispatcher
* dispatcher
,
39 : dispatcher_(dispatcher
),
45 bool running() const {
49 const PrioritizedDispatcher::Handle
handle() const {
53 void Add(bool at_head
) {
54 CHECK(handle_
.is_null());
56 size_t num_queued
= dispatcher_
->num_queued_jobs();
57 size_t num_running
= dispatcher_
->num_running_jobs();
60 handle_
= dispatcher_
->Add(this, priority_
);
62 handle_
= dispatcher_
->AddAtHead(this, priority_
);
65 if (handle_
.is_null()) {
66 EXPECT_EQ(num_queued
, dispatcher_
->num_queued_jobs());
67 EXPECT_TRUE(running_
);
68 EXPECT_EQ(num_running
+ 1, dispatcher_
->num_running_jobs());
70 EXPECT_FALSE(running_
);
71 EXPECT_EQ(priority_
, handle_
.priority());
72 EXPECT_EQ(tag_
, reinterpret_cast<TestJob
*>(handle_
.value())->tag_
);
73 EXPECT_EQ(num_running
, dispatcher_
->num_running_jobs());
77 void ChangePriority(Priority priority
) {
78 CHECK(!handle_
.is_null());
80 size_t num_queued
= dispatcher_
->num_queued_jobs();
81 size_t num_running
= dispatcher_
->num_running_jobs();
83 handle_
= dispatcher_
->ChangePriority(handle_
, priority
);
85 if (handle_
.is_null()) {
86 EXPECT_TRUE(running_
);
87 EXPECT_EQ(num_queued
- 1, dispatcher_
->num_queued_jobs());
88 EXPECT_EQ(num_running
+ 1, dispatcher_
->num_running_jobs());
90 EXPECT_FALSE(running_
);
91 EXPECT_EQ(priority
, handle_
.priority());
92 EXPECT_EQ(tag_
, reinterpret_cast<TestJob
*>(handle_
.value())->tag_
);
93 EXPECT_EQ(num_queued
, dispatcher_
->num_queued_jobs());
94 EXPECT_EQ(num_running
, dispatcher_
->num_running_jobs());
99 CHECK(!handle_
.is_null());
101 size_t num_queued
= dispatcher_
->num_queued_jobs();
103 dispatcher_
->Cancel(handle_
);
105 EXPECT_EQ(num_queued
- 1, dispatcher_
->num_queued_jobs());
106 handle_
= PrioritizedDispatcher::Handle();
112 log_
->append(1u, '.');
114 dispatcher_
->OnJobFinished();
117 // PriorityDispatch::Job interface
118 void Start() override
{
119 EXPECT_FALSE(running_
);
120 handle_
= PrioritizedDispatcher::Handle();
122 log_
->append(1u, tag_
);
126 PrioritizedDispatcher
* dispatcher_
;
131 PrioritizedDispatcher::Handle handle_
;
138 void Prepare(const PrioritizedDispatcher::Limits
& limits
) {
139 dispatcher_
.reset(new PrioritizedDispatcher(limits
));
142 TestJob
* AddJob(char data
, Priority priority
) {
143 TestJob
* job
= new TestJob(dispatcher_
.get(), data
, priority
, &log_
);
144 jobs_
.push_back(job
);
149 TestJob
* AddJobAtHead(char data
, Priority priority
) {
150 TestJob
* job
= new TestJob(dispatcher_
.get(), data
, priority
, &log_
);
151 jobs_
.push_back(job
);
156 void Expect(std::string log
) {
157 EXPECT_EQ(0u, dispatcher_
->num_queued_jobs());
158 EXPECT_EQ(0u, dispatcher_
->num_running_jobs());
159 EXPECT_EQ(log
, log_
);
164 scoped_ptr
<PrioritizedDispatcher
> dispatcher_
;
165 ScopedVector
<TestJob
> jobs_
;
168 TEST_F(PrioritizedDispatcherTest
, GetLimits
) {
169 // Set non-trivial initial limits.
170 PrioritizedDispatcher::Limits
original_limits(NUM_PRIORITIES
, 5);
171 original_limits
.reserved_slots
[HIGHEST
] = 1;
172 original_limits
.reserved_slots
[LOW
] = 2;
173 Prepare(original_limits
);
175 // Get current limits, make sure the original limits are returned.
176 PrioritizedDispatcher::Limits retrieved_limits
= dispatcher_
->GetLimits();
177 ASSERT_EQ(original_limits
.total_jobs
, retrieved_limits
.total_jobs
);
178 ASSERT_EQ(NUM_PRIORITIES
, retrieved_limits
.reserved_slots
.size());
179 for (size_t priority
= MINIMUM_PRIORITY
; priority
<= MAXIMUM_PRIORITY
;
181 EXPECT_EQ(original_limits
.reserved_slots
[priority
],
182 retrieved_limits
.reserved_slots
[priority
]);
186 PrioritizedDispatcher::Limits
new_limits(NUM_PRIORITIES
, 6);
187 new_limits
.reserved_slots
[MEDIUM
] = 3;
188 new_limits
.reserved_slots
[LOWEST
] = 1;
191 // Get current limits, make sure the new limits are returned.
192 retrieved_limits
= dispatcher_
->GetLimits();
193 ASSERT_EQ(new_limits
.total_jobs
, retrieved_limits
.total_jobs
);
194 ASSERT_EQ(NUM_PRIORITIES
, retrieved_limits
.reserved_slots
.size());
195 for (size_t priority
= MINIMUM_PRIORITY
; priority
<= MAXIMUM_PRIORITY
;
197 EXPECT_EQ(new_limits
.reserved_slots
[priority
],
198 retrieved_limits
.reserved_slots
[priority
]);
202 TEST_F(PrioritizedDispatcherTest
, AddAFIFO
) {
203 // Allow only one running job.
204 PrioritizedDispatcher::Limits
limits(NUM_PRIORITIES
, 1);
207 TestJob
* job_a
= AddJob('a', IDLE
);
208 TestJob
* job_b
= AddJob('b', IDLE
);
209 TestJob
* job_c
= AddJob('c', IDLE
);
210 TestJob
* job_d
= AddJob('d', IDLE
);
212 ASSERT_TRUE(job_a
->running());
214 ASSERT_TRUE(job_b
->running());
216 ASSERT_TRUE(job_c
->running());
218 ASSERT_TRUE(job_d
->running());
224 TEST_F(PrioritizedDispatcherTest
, AddPriority
) {
225 PrioritizedDispatcher::Limits
limits(NUM_PRIORITIES
, 1);
228 TestJob
* job_a
= AddJob('a', IDLE
);
229 TestJob
* job_b
= AddJob('b', MEDIUM
);
230 TestJob
* job_c
= AddJob('c', HIGHEST
);
231 TestJob
* job_d
= AddJob('d', HIGHEST
);
232 TestJob
* job_e
= AddJob('e', MEDIUM
);
234 ASSERT_TRUE(job_a
->running());
236 ASSERT_TRUE(job_c
->running());
238 ASSERT_TRUE(job_d
->running());
240 ASSERT_TRUE(job_b
->running());
242 ASSERT_TRUE(job_e
->running());
245 Expect("a.c.d.b.e.");
248 TEST_F(PrioritizedDispatcherTest
, AddAtHead
) {
249 PrioritizedDispatcher::Limits
limits(NUM_PRIORITIES
, 1);
252 TestJob
* job_a
= AddJob('a', MEDIUM
);
253 TestJob
* job_b
= AddJobAtHead('b', MEDIUM
);
254 TestJob
* job_c
= AddJobAtHead('c', HIGHEST
);
255 TestJob
* job_d
= AddJobAtHead('d', HIGHEST
);
256 TestJob
* job_e
= AddJobAtHead('e', MEDIUM
);
257 TestJob
* job_f
= AddJob('f', MEDIUM
);
259 ASSERT_TRUE(job_a
->running());
261 ASSERT_TRUE(job_d
->running());
263 ASSERT_TRUE(job_c
->running());
265 ASSERT_TRUE(job_e
->running());
267 ASSERT_TRUE(job_b
->running());
269 ASSERT_TRUE(job_f
->running());
272 Expect("a.d.c.e.b.f.");
275 TEST_F(PrioritizedDispatcherTest
, EnforceLimits
) {
276 // Reserve 2 for HIGHEST and 1 for LOW or higher.
277 // This leaves 2 for LOWEST or lower.
278 PrioritizedDispatcher::Limits
limits(NUM_PRIORITIES
, 5);
279 limits
.reserved_slots
[HIGHEST
] = 2;
280 limits
.reserved_slots
[LOW
] = 1;
283 TestJob
* job_a
= AddJob('a', IDLE
); // Uses unreserved slot.
284 TestJob
* job_b
= AddJob('b', IDLE
); // Uses unreserved slot.
285 TestJob
* job_c
= AddJob('c', LOWEST
); // Must wait.
286 TestJob
* job_d
= AddJob('d', LOW
); // Uses reserved slot.
287 TestJob
* job_e
= AddJob('e', MEDIUM
); // Must wait.
288 TestJob
* job_f
= AddJob('f', HIGHEST
); // Uses reserved slot.
289 TestJob
* job_g
= AddJob('g', HIGHEST
); // Uses reserved slot.
290 TestJob
* job_h
= AddJob('h', HIGHEST
); // Must wait.
292 EXPECT_EQ(5u, dispatcher_
->num_running_jobs());
293 EXPECT_EQ(3u, dispatcher_
->num_queued_jobs());
295 ASSERT_TRUE(job_a
->running());
296 ASSERT_TRUE(job_b
->running());
297 ASSERT_TRUE(job_d
->running());
298 ASSERT_TRUE(job_f
->running());
299 ASSERT_TRUE(job_g
->running());
300 // a, b, d, f, g are running. Finish them in any order.
301 job_b
->Finish(); // Releases h.
304 job_g
->Finish(); // Releases e.
306 ASSERT_TRUE(job_e
->running());
307 ASSERT_TRUE(job_h
->running());
309 job_e
->Finish(); // Releases c.
310 ASSERT_TRUE(job_c
->running());
314 Expect("abdfg.h...e..c..");
317 TEST_F(PrioritizedDispatcherTest
, ChangePriority
) {
318 PrioritizedDispatcher::Limits
limits(NUM_PRIORITIES
, 2);
319 // Reserve one slot only for HIGHEST priority requests.
320 limits
.reserved_slots
[HIGHEST
] = 1;
323 TestJob
* job_a
= AddJob('a', IDLE
);
324 TestJob
* job_b
= AddJob('b', LOW
);
325 TestJob
* job_c
= AddJob('c', MEDIUM
);
326 TestJob
* job_d
= AddJob('d', MEDIUM
);
327 TestJob
* job_e
= AddJob('e', IDLE
);
329 ASSERT_FALSE(job_b
->running());
330 ASSERT_FALSE(job_c
->running());
331 job_b
->ChangePriority(MEDIUM
);
332 job_c
->ChangePriority(LOW
);
334 ASSERT_TRUE(job_a
->running());
336 ASSERT_TRUE(job_d
->running());
339 EXPECT_FALSE(job_e
->running());
340 // Increasing |job_e|'s priority to HIGHEST should result in it being
341 // started immediately.
342 job_e
->ChangePriority(HIGHEST
);
343 ASSERT_TRUE(job_e
->running());
346 ASSERT_TRUE(job_b
->running());
348 ASSERT_TRUE(job_c
->running());
351 Expect("a.d.be..c.");
354 TEST_F(PrioritizedDispatcherTest
, Cancel
) {
355 PrioritizedDispatcher::Limits
limits(NUM_PRIORITIES
, 1);
358 TestJob
* job_a
= AddJob('a', IDLE
);
359 TestJob
* job_b
= AddJob('b', IDLE
);
360 TestJob
* job_c
= AddJob('c', IDLE
);
361 TestJob
* job_d
= AddJob('d', IDLE
);
362 TestJob
* job_e
= AddJob('e', IDLE
);
364 ASSERT_FALSE(job_b
->running());
365 ASSERT_FALSE(job_d
->running());
369 ASSERT_TRUE(job_a
->running());
371 ASSERT_TRUE(job_c
->running());
373 ASSERT_TRUE(job_e
->running());
379 TEST_F(PrioritizedDispatcherTest
, Evict
) {
380 PrioritizedDispatcher::Limits
limits(NUM_PRIORITIES
, 1);
383 TestJob
* job_a
= AddJob('a', IDLE
);
384 TestJob
* job_b
= AddJob('b', LOW
);
385 TestJob
* job_c
= AddJob('c', HIGHEST
);
386 TestJob
* job_d
= AddJob('d', LOW
);
387 TestJob
* job_e
= AddJob('e', HIGHEST
);
389 EXPECT_EQ(job_b
, dispatcher_
->EvictOldestLowest());
390 EXPECT_EQ(job_d
, dispatcher_
->EvictOldestLowest());
392 ASSERT_TRUE(job_a
->running());
394 ASSERT_TRUE(job_c
->running());
396 ASSERT_TRUE(job_e
->running());
402 TEST_F(PrioritizedDispatcherTest
, EvictFromEmpty
) {
403 PrioritizedDispatcher::Limits
limits(NUM_PRIORITIES
, 1);
405 EXPECT_TRUE(dispatcher_
->EvictOldestLowest() == NULL
);
408 TEST_F(PrioritizedDispatcherTest
, AddWhileZeroLimits
) {
409 PrioritizedDispatcher::Limits
limits(NUM_PRIORITIES
, 2);
412 dispatcher_
->SetLimitsToZero();
413 TestJob
* job_a
= AddJob('a', LOW
);
414 TestJob
* job_b
= AddJob('b', MEDIUM
);
415 TestJob
* job_c
= AddJobAtHead('c', MEDIUM
);
417 EXPECT_EQ(0u, dispatcher_
->num_running_jobs());
418 EXPECT_EQ(3u, dispatcher_
->num_queued_jobs());
420 dispatcher_
->SetLimits(limits
);
421 EXPECT_EQ(2u, dispatcher_
->num_running_jobs());
422 EXPECT_EQ(1u, dispatcher_
->num_queued_jobs());
424 ASSERT_TRUE(job_b
->running());
427 ASSERT_TRUE(job_c
->running());
430 ASSERT_TRUE(job_a
->running());
436 TEST_F(PrioritizedDispatcherTest
, ReduceLimitsWhileJobQueued
) {
437 PrioritizedDispatcher::Limits
initial_limits(NUM_PRIORITIES
, 2);
438 Prepare(initial_limits
);
440 TestJob
* job_a
= AddJob('a', MEDIUM
);
441 TestJob
* job_b
= AddJob('b', MEDIUM
);
442 TestJob
* job_c
= AddJob('c', MEDIUM
);
443 TestJob
* job_d
= AddJob('d', MEDIUM
);
444 TestJob
* job_e
= AddJob('e', MEDIUM
);
446 EXPECT_EQ(2u, dispatcher_
->num_running_jobs());
447 EXPECT_EQ(3u, dispatcher_
->num_queued_jobs());
449 // Reduce limits to just allow one job at a time. Running jobs should not
451 dispatcher_
->SetLimits(PrioritizedDispatcher::Limits(NUM_PRIORITIES
, 1));
453 EXPECT_EQ(2u, dispatcher_
->num_running_jobs());
454 EXPECT_EQ(3u, dispatcher_
->num_queued_jobs());
456 // Finishing a job should not result in another job starting.
457 ASSERT_TRUE(job_a
->running());
459 EXPECT_EQ(1u, dispatcher_
->num_running_jobs());
460 EXPECT_EQ(3u, dispatcher_
->num_queued_jobs());
462 ASSERT_TRUE(job_b
->running());
464 EXPECT_EQ(1u, dispatcher_
->num_running_jobs());
465 EXPECT_EQ(2u, dispatcher_
->num_queued_jobs());
467 // Increasing the limits again should let c start.
468 dispatcher_
->SetLimits(initial_limits
);
470 ASSERT_TRUE(job_c
->running());
472 ASSERT_TRUE(job_d
->running());
474 ASSERT_TRUE(job_e
->running());
477 Expect("ab..cd.e..");
480 TEST_F(PrioritizedDispatcherTest
, ZeroLimitsThenCancel
) {
481 PrioritizedDispatcher::Limits
limits(NUM_PRIORITIES
, 1);
484 TestJob
* job_a
= AddJob('a', IDLE
);
485 TestJob
* job_b
= AddJob('b', IDLE
);
486 TestJob
* job_c
= AddJob('c', IDLE
);
487 dispatcher_
->SetLimitsToZero();
489 ASSERT_TRUE(job_a
->running());
490 EXPECT_FALSE(job_b
->running());
491 EXPECT_FALSE(job_c
->running());
494 EXPECT_FALSE(job_b
->running());
495 EXPECT_FALSE(job_c
->running());
497 // Cancelling b shouldn't start job c.
499 EXPECT_FALSE(job_c
->running());
501 // Restoring the limits should start c.
502 dispatcher_
->SetLimits(limits
);
503 ASSERT_TRUE(job_c
->running());
509 TEST_F(PrioritizedDispatcherTest
, ZeroLimitsThenIncreasePriority
) {
510 PrioritizedDispatcher::Limits
limits(NUM_PRIORITIES
, 2);
511 limits
.reserved_slots
[HIGHEST
] = 1;
514 TestJob
* job_a
= AddJob('a', IDLE
);
515 TestJob
* job_b
= AddJob('b', IDLE
);
516 EXPECT_TRUE(job_a
->running());
517 EXPECT_FALSE(job_b
->running());
518 dispatcher_
->SetLimitsToZero();
520 job_b
->ChangePriority(HIGHEST
);
521 EXPECT_FALSE(job_b
->running());
523 EXPECT_FALSE(job_b
->running());
529 #if GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
530 TEST_F(PrioritizedDispatcherTest
, CancelNull
) {
531 PrioritizedDispatcher::Limits
limits(NUM_PRIORITIES
, 1);
533 EXPECT_DEBUG_DEATH(dispatcher_
->Cancel(PrioritizedDispatcher::Handle()), "");
536 TEST_F(PrioritizedDispatcherTest
, CancelMissing
) {
537 PrioritizedDispatcher::Limits
limits(NUM_PRIORITIES
, 1);
540 TestJob
* job_b
= AddJob('b', IDLE
);
541 PrioritizedDispatcher::Handle handle
= job_b
->handle();
542 ASSERT_FALSE(handle
.is_null());
543 dispatcher_
->Cancel(handle
);
544 EXPECT_DEBUG_DEATH(dispatcher_
->Cancel(handle
), "");
546 #endif // GTEST_HAS_DEATH_TEST && !defined(NDEBUG)