summaryrefslogtreecommitdiff
path: root/chromium/net/base/prioritized_dispatcher_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/net/base/prioritized_dispatcher_unittest.cc')
-rw-r--r--chromium/net/base/prioritized_dispatcher_unittest.cc365
1 files changed, 365 insertions, 0 deletions
diff --git a/chromium/net/base/prioritized_dispatcher_unittest.cc b/chromium/net/base/prioritized_dispatcher_unittest.cc
new file mode 100644
index 00000000000..41a09c5cb92
--- /dev/null
+++ b/chromium/net/base/prioritized_dispatcher_unittest.cc
@@ -0,0 +1,365 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <ctype.h>
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "net/base/prioritized_dispatcher.h"
+#include "net/base/request_priority.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+// We rely on the priority enum values being sequential having starting at 0,
+// and increasing for higher priorities.
+COMPILE_ASSERT(MINIMUM_PRIORITY == 0u &&
+ MINIMUM_PRIORITY == IDLE &&
+ IDLE < LOWEST &&
+ LOWEST < HIGHEST &&
+ HIGHEST < NUM_PRIORITIES,
+ priority_indexes_incompatible);
+
+class PrioritizedDispatcherTest : public testing::Test {
+ public:
+ typedef PrioritizedDispatcher::Priority Priority;
+ // A job that appends |tag| to |log| when started and '.' when finished.
+ // This is intended to confirm the execution order of a sequence of jobs added
+ // to the dispatcher. Note that finishing order of jobs does not matter.
+ class TestJob : public PrioritizedDispatcher::Job {
+ public:
+ TestJob(PrioritizedDispatcher* dispatcher,
+ char tag,
+ Priority priority,
+ std::string* log)
+ : dispatcher_(dispatcher),
+ tag_(tag),
+ priority_(priority),
+ running_(false),
+ log_(log) {}
+
+ bool running() const {
+ return running_;
+ }
+
+ const PrioritizedDispatcher::Handle handle() const {
+ return handle_;
+ }
+
+ void Add() {
+ CHECK(handle_.is_null());
+ CHECK(!running_);
+ size_t num_queued = dispatcher_->num_queued_jobs();
+ size_t num_running = dispatcher_->num_running_jobs();
+
+ handle_ = dispatcher_->Add(this, priority_);
+
+ if (handle_.is_null()) {
+ EXPECT_EQ(num_queued, dispatcher_->num_queued_jobs());
+ EXPECT_TRUE(running_);
+ EXPECT_EQ(num_running + 1, dispatcher_->num_running_jobs());
+ } else {
+ EXPECT_FALSE(running_);
+ EXPECT_EQ(priority_, handle_.priority());
+ EXPECT_EQ(tag_, reinterpret_cast<TestJob*>(handle_.value())->tag_);
+ EXPECT_EQ(num_running, dispatcher_->num_running_jobs());
+ }
+ }
+
+ void ChangePriority(Priority priority) {
+ CHECK(!handle_.is_null());
+ CHECK(!running_);
+ size_t num_queued = dispatcher_->num_queued_jobs();
+ size_t num_running = dispatcher_->num_running_jobs();
+
+ handle_ = dispatcher_->ChangePriority(handle_, priority);
+
+ if (handle_.is_null()) {
+ EXPECT_TRUE(running_);
+ EXPECT_EQ(num_queued - 1, dispatcher_->num_queued_jobs());
+ EXPECT_EQ(num_running + 1, dispatcher_->num_running_jobs());
+ } else {
+ EXPECT_FALSE(running_);
+ EXPECT_EQ(priority, handle_.priority());
+ EXPECT_EQ(tag_, reinterpret_cast<TestJob*>(handle_.value())->tag_);
+ EXPECT_EQ(num_queued, dispatcher_->num_queued_jobs());
+ EXPECT_EQ(num_running, dispatcher_->num_running_jobs());
+ }
+ }
+
+ void Cancel() {
+ CHECK(!handle_.is_null());
+ CHECK(!running_);
+ size_t num_queued = dispatcher_->num_queued_jobs();
+
+ dispatcher_->Cancel(handle_);
+
+ EXPECT_EQ(num_queued - 1, dispatcher_->num_queued_jobs());
+ handle_ = PrioritizedDispatcher::Handle();
+ }
+
+ void Finish() {
+ CHECK(running_);
+ running_ = false;
+ log_->append(1u, '.');
+
+ dispatcher_->OnJobFinished();
+ }
+
+ // PriorityDispatch::Job interface
+ virtual void Start() OVERRIDE {
+ EXPECT_FALSE(running_);
+ handle_ = PrioritizedDispatcher::Handle();
+ running_ = true;
+ log_->append(1u, tag_);
+ }
+
+ private:
+ PrioritizedDispatcher* dispatcher_;
+
+ char tag_;
+ Priority priority_;
+
+ PrioritizedDispatcher::Handle handle_;
+ bool running_;
+
+ std::string* log_;
+ };
+
+ protected:
+ void Prepare(const PrioritizedDispatcher::Limits& limits) {
+ dispatcher_.reset(new PrioritizedDispatcher(limits));
+ }
+
+ TestJob* AddJob(char data, Priority priority) {
+ TestJob* job = new TestJob(dispatcher_.get(), data, priority, &log_);
+ jobs_.push_back(job);
+ job->Add();
+ return job;
+ }
+
+ void Expect(std::string log) {
+ EXPECT_EQ(0u, dispatcher_->num_queued_jobs());
+ EXPECT_EQ(0u, dispatcher_->num_running_jobs());
+ EXPECT_EQ(log, log_);
+ log_.clear();
+ }
+
+ std::string log_;
+ scoped_ptr<PrioritizedDispatcher> dispatcher_;
+ ScopedVector<TestJob> jobs_;
+};
+
+TEST_F(PrioritizedDispatcherTest, AddAFIFO) {
+ // Allow only one running job.
+ PrioritizedDispatcher::Limits limits(NUM_PRIORITIES, 1);
+ Prepare(limits);
+
+ TestJob* job_a = AddJob('a', IDLE);
+ TestJob* job_b = AddJob('b', IDLE);
+ TestJob* job_c = AddJob('c', IDLE);
+ TestJob* job_d = AddJob('d', IDLE);
+
+ ASSERT_TRUE(job_a->running());
+ job_a->Finish();
+ ASSERT_TRUE(job_b->running());
+ job_b->Finish();
+ ASSERT_TRUE(job_c->running());
+ job_c->Finish();
+ ASSERT_TRUE(job_d->running());
+ job_d->Finish();
+
+ Expect("a.b.c.d.");
+}
+
+TEST_F(PrioritizedDispatcherTest, AddPriority) {
+ PrioritizedDispatcher::Limits limits(NUM_PRIORITIES, 1);
+ Prepare(limits);
+
+ TestJob* job_a = AddJob('a', IDLE);
+ TestJob* job_b = AddJob('b', MEDIUM);
+ TestJob* job_c = AddJob('c', HIGHEST);
+ TestJob* job_d = AddJob('d', HIGHEST);
+ TestJob* job_e = AddJob('e', MEDIUM);
+
+ ASSERT_TRUE(job_a->running());
+ job_a->Finish();
+ ASSERT_TRUE(job_c->running());
+ job_c->Finish();
+ ASSERT_TRUE(job_d->running());
+ job_d->Finish();
+ ASSERT_TRUE(job_b->running());
+ job_b->Finish();
+ ASSERT_TRUE(job_e->running());
+ job_e->Finish();
+
+ Expect("a.c.d.b.e.");
+}
+
+TEST_F(PrioritizedDispatcherTest, EnforceLimits) {
+ // Reserve 2 for HIGHEST and 1 for LOW or higher.
+ // This leaves 2 for LOWEST or lower.
+ PrioritizedDispatcher::Limits limits(NUM_PRIORITIES, 5);
+ limits.reserved_slots[HIGHEST] = 2;
+ limits.reserved_slots[LOW] = 1;
+ Prepare(limits);
+
+ TestJob* job_a = AddJob('a', IDLE); // Uses unreserved slot.
+ TestJob* job_b = AddJob('b', IDLE); // Uses unreserved slot.
+ TestJob* job_c = AddJob('c', LOWEST); // Must wait.
+ TestJob* job_d = AddJob('d', LOW); // Uses reserved slot.
+ TestJob* job_e = AddJob('e', MEDIUM); // Must wait.
+ TestJob* job_f = AddJob('f', HIGHEST); // Uses reserved slot.
+ TestJob* job_g = AddJob('g', HIGHEST); // Uses reserved slot.
+ TestJob* job_h = AddJob('h', HIGHEST); // Must wait.
+
+ EXPECT_EQ(5u, dispatcher_->num_running_jobs());
+ EXPECT_EQ(3u, dispatcher_->num_queued_jobs());
+
+ ASSERT_TRUE(job_a->running());
+ ASSERT_TRUE(job_b->running());
+ ASSERT_TRUE(job_d->running());
+ ASSERT_TRUE(job_f->running());
+ ASSERT_TRUE(job_g->running());
+ // a, b, d, f, g are running. Finish them in any order.
+ job_b->Finish(); // Releases h.
+ job_f->Finish();
+ job_a->Finish();
+ job_g->Finish(); // Releases e.
+ job_d->Finish();
+ ASSERT_TRUE(job_e->running());
+ ASSERT_TRUE(job_h->running());
+ // h, e are running.
+ job_e->Finish(); // Releases c.
+ ASSERT_TRUE(job_c->running());
+ job_c->Finish();
+ job_h->Finish();
+
+ Expect("abdfg.h...e..c..");
+}
+
+TEST_F(PrioritizedDispatcherTest, ChangePriority) {
+ PrioritizedDispatcher::Limits limits(NUM_PRIORITIES, 1);
+ Prepare(limits);
+
+ TestJob* job_a = AddJob('a', IDLE);
+ TestJob* job_b = AddJob('b', MEDIUM);
+ TestJob* job_c = AddJob('c', HIGHEST);
+ TestJob* job_d = AddJob('d', HIGHEST);
+
+ ASSERT_FALSE(job_b->running());
+ ASSERT_FALSE(job_c->running());
+ job_b->ChangePriority(HIGHEST);
+ job_c->ChangePriority(MEDIUM);
+
+ ASSERT_TRUE(job_a->running());
+ job_a->Finish();
+ ASSERT_TRUE(job_d->running());
+ job_d->Finish();
+ ASSERT_TRUE(job_b->running());
+ job_b->Finish();
+ ASSERT_TRUE(job_c->running());
+ job_c->Finish();
+
+ Expect("a.d.b.c.");
+}
+
+TEST_F(PrioritizedDispatcherTest, Cancel) {
+ PrioritizedDispatcher::Limits limits(NUM_PRIORITIES, 1);
+ Prepare(limits);
+
+ TestJob* job_a = AddJob('a', IDLE);
+ TestJob* job_b = AddJob('b', IDLE);
+ TestJob* job_c = AddJob('c', IDLE);
+ TestJob* job_d = AddJob('d', IDLE);
+ TestJob* job_e = AddJob('e', IDLE);
+
+ ASSERT_FALSE(job_b->running());
+ ASSERT_FALSE(job_d->running());
+ job_b->Cancel();
+ job_d->Cancel();
+
+ ASSERT_TRUE(job_a->running());
+ job_a->Finish();
+ ASSERT_TRUE(job_c->running());
+ job_c->Finish();
+ ASSERT_TRUE(job_e->running());
+ job_e->Finish();
+
+ Expect("a.c.e.");
+}
+
+TEST_F(PrioritizedDispatcherTest, Evict) {
+ PrioritizedDispatcher::Limits limits(NUM_PRIORITIES, 1);
+ Prepare(limits);
+
+ TestJob* job_a = AddJob('a', IDLE);
+ TestJob* job_b = AddJob('b', LOW);
+ TestJob* job_c = AddJob('c', HIGHEST);
+ TestJob* job_d = AddJob('d', LOW);
+ TestJob* job_e = AddJob('e', HIGHEST);
+
+ EXPECT_EQ(job_b, dispatcher_->EvictOldestLowest());
+ EXPECT_EQ(job_d, dispatcher_->EvictOldestLowest());
+
+ ASSERT_TRUE(job_a->running());
+ job_a->Finish();
+ ASSERT_TRUE(job_c->running());
+ job_c->Finish();
+ ASSERT_TRUE(job_e->running());
+ job_e->Finish();
+
+ Expect("a.c.e.");
+}
+
+TEST_F(PrioritizedDispatcherTest, EvictFromEmpty) {
+ PrioritizedDispatcher::Limits limits(NUM_PRIORITIES, 1);
+ Prepare(limits);
+ EXPECT_TRUE(dispatcher_->EvictOldestLowest() == NULL);
+}
+
+#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
+TEST_F(PrioritizedDispatcherTest, CancelNull) {
+ PrioritizedDispatcher::Limits limits(NUM_PRIORITIES, 1);
+ Prepare(limits);
+ EXPECT_DEBUG_DEATH(dispatcher_->Cancel(PrioritizedDispatcher::Handle()), "");
+}
+
+TEST_F(PrioritizedDispatcherTest, CancelMissing) {
+ PrioritizedDispatcher::Limits limits(NUM_PRIORITIES, 1);
+ Prepare(limits);
+ AddJob('a', IDLE);
+ TestJob* job_b = AddJob('b', IDLE);
+ PrioritizedDispatcher::Handle handle = job_b->handle();
+ ASSERT_FALSE(handle.is_null());
+ dispatcher_->Cancel(handle);
+ EXPECT_DEBUG_DEATH(dispatcher_->Cancel(handle), "");
+}
+
+// TODO(szym): Fix the PriorityQueue::Pointer check to die here.
+// http://crbug.com/130846
+TEST_F(PrioritizedDispatcherTest, DISABLED_CancelIncompatible) {
+ PrioritizedDispatcher::Limits limits(NUM_PRIORITIES, 1);
+ Prepare(limits);
+ AddJob('a', IDLE);
+ TestJob* job_b = AddJob('b', IDLE);
+ PrioritizedDispatcher::Handle handle = job_b->handle();
+ ASSERT_FALSE(handle.is_null());
+
+ // New dispatcher.
+ Prepare(limits);
+ AddJob('a', IDLE);
+ AddJob('b', IDLE);
+ EXPECT_DEBUG_DEATH(dispatcher_->Cancel(handle), "");
+}
+#endif // GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
+
+} // namespace
+
+} // namespace net