diff options
Diffstat (limited to 'chromium/net/base/prioritized_dispatcher_unittest.cc')
-rw-r--r-- | chromium/net/base/prioritized_dispatcher_unittest.cc | 365 |
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 |