Chromium Code Reviews| Index: base/test/sequenced_task_runner_test_template.h |
| =================================================================== |
| --- base/test/sequenced_task_runner_test_template.h (revision 0) |
| +++ base/test/sequenced_task_runner_test_template.h (revision 0) |
| @@ -0,0 +1,254 @@ |
| +// 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. |
| + |
| +// This class defines tests that implementations of SequencedTaskRunner should |
| +// pass in order to be conformant. Here's how you use it to test your |
| +// implementation. |
| +// |
| +// See task_runner_test_template.h for a description of how to use the |
| +// constructs in this file; these work the same. |
| + |
| +#ifndef BASE_SEQUENCED_TASK_RUNNER_TEST_TEMPLATE_H_ |
| +#define BASE_SEQUENCED_TASK_RUNNER_TEST_TEMPLATE_H_ |
| +#pragma once |
| + |
| +#include <cstddef> |
| + |
| +#include <algorithm> |
| +#include <vector> |
| + |
| +#include "base/basictypes.h" |
| +#include "base/bind.h" |
| +#include "base/memory/ref_counted.h" |
| +#include "base/sequenced_task_runner.h" |
| +#include "base/synchronization/lock.h" |
| +#include "base/tracked_objects.h" |
| +#include "testing/gtest/include/gtest/gtest.h" |
| + |
| +namespace base { |
| + |
| +namespace internal { |
| + |
| +// Utility class used in the tests below. |
| +class SequencedTaskTracker : public RefCountedThreadSafe<SequencedTaskTracker> { |
| + public: |
| + SequencedTaskTracker(); |
| + |
| + int GetNextPostOrdinal(); |
| + |
| + void TaskPosted(int i); |
| + void TaskStarted(int i); |
| + void TaskEnded(int i); |
| + |
| + const std::vector<int>& GetPostOrder() const; |
| + const std::vector<int>& GetStartOrder() const; |
| + const std::vector<int>& GetEndOrder() const; |
| + |
| + void FastTask(int i); |
| + void SlowTask(int i); |
| + |
| + // Task which posts a fast, non-nestable task. |
| + void PostFastNonNestableFromSlowNonNestable( |
| + scoped_refptr<SequencedTaskRunner> task_runner, |
| + const int base_status_i, |
| + const int child_count); |
| + |
| + private: |
| + friend class RefCountedThreadSafe<SequencedTaskTracker>; |
| + |
| + ~SequencedTaskTracker(); |
| + |
| + public: |
| + std::vector<int> post_events_; |
| + mutable Lock post_lock_; |
| + |
| + private: |
| + std::vector<int> start_events_; |
| + mutable Lock start_lock_; |
| + |
| + std::vector<int> end_events_; |
| + mutable Lock end_lock_; |
| + |
| + int next_post_i_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(SequencedTaskTracker); |
| +}; |
| + |
| +} // namespace internal |
| + |
| +template <typename TaskRunnerTestDelegate> |
| +class SequencedTaskRunnerTest : public testing::Test { |
| + protected: |
| + SequencedTaskRunnerTest() |
| + : task_tracker_(new internal::SequencedTaskTracker()) {} |
| + |
| + const scoped_refptr<internal::SequencedTaskTracker> task_tracker_; |
| + TaskRunnerTestDelegate delegate_; |
| +}; |
| + |
| +TYPED_TEST_CASE_P(SequencedTaskRunnerTest); |
| + |
| +// This test posts N non-nestable tasks in sequence, and expects them to run |
| +// in FIFO order, with no part of any two tasks' execution overlapping. |
| +// |
| +// It checks that task0 was done by the time task1 started, and task2 only |
| +// started after task1 had executed to completion, etc. |
| +// |
| +// The first task it starts is a slow one which runs for a second, giving the |
| +// subsequent ones plenty of time to run if the implementation fails to |
| +// enforce the non-nestable property. |
| +TYPED_TEST_P(SequencedTaskRunnerTest, SequentialNonNestable) { |
| + const int task_count = 1000; |
| + |
| + this->delegate_.StartTaskRunner(); |
| + scoped_refptr<SequencedTaskRunner> task_runner = |
| + this->delegate_.GetTaskRunner(); |
| + |
| + for (int i = 0; i < task_count; ++i) { |
| + Closure task; |
| + if (i == 0) { |
| + task = Bind(&internal::SequencedTaskTracker::SlowTask, |
| + this->task_tracker_, i); |
| + } else { |
| + task = Bind(&internal::SequencedTaskTracker::FastTask, |
| + this->task_tracker_, i); |
| + } |
| + this->task_tracker_->TaskPosted(i); |
| + task_runner->PostNonNestableTask(FROM_HERE, task); |
| + } |
| + |
| + this->delegate_.StopTaskRunner(); |
| + |
| + EXPECT_EQ(this->task_tracker_->GetPostOrder(), |
| + this->task_tracker_->GetStartOrder()); |
| + EXPECT_EQ(this->task_tracker_->GetEndOrder(), |
| + this->task_tracker_->GetStartOrder()); |
| +} |
| + |
| +// This test posts N nestable tasks in sequence, and expects them to run in |
| +// FIFO order; overlapping execution is allowed. |
| +// |
| +// It checks only that task_i had started by the time task_i+1 started, for |
| +// increasing values of i. Thus it verifies the SequencedTaskRunner guarantee |
| +// that nested tasks are run in FIFO order. |
| +// |
| +// The first task it starts is a slow one which runs for a second, giving the |
| +// subsequent ones plenty of time to run in if the implementation fails to |
| +// enforce the FIFO property for nestable tasks. |
| +TYPED_TEST_P(SequencedTaskRunnerTest, FAILS_SequentialNestable) { |
| + const int task_count = 10; |
| + |
| + this->delegate_.StartTaskRunner(); |
| + scoped_refptr<SequencedTaskRunner> task_runner = |
| + this->delegate_.GetTaskRunner(); |
| + |
| + for (int i = 0; i < task_count; ++i) { |
| + Closure task; |
| + if (i == 0) { |
| + task = Bind(&internal::SequencedTaskTracker::SlowTask, |
| + this->task_tracker_, i); |
| + } else { |
| + task = Bind(&internal::SequencedTaskTracker::FastTask, |
| + this->task_tracker_, i); |
| + } |
| + this->task_tracker_->TaskPosted(i); |
| + task_runner->PostTask(FROM_HERE, task); |
| + } |
| + |
| + this->delegate_.StopTaskRunner(); |
| + |
| + const std::vector<int>& post_events = this->task_tracker_->GetPostOrder(); |
| + const std::vector<int>& end_events = this->task_tracker_->GetEndOrder(); |
| + |
| + EXPECT_EQ(post_events, this->task_tracker_->GetStartOrder()); |
| + |
| + // Check that all posted tasks finished. |
| + for (std::size_t i = 0; i < post_events.size(); ++i) { |
| + EXPECT_NE(std::find(end_events.begin(), end_events.end(), post_events[i]), |
| + end_events.end()); |
| + } |
| +} |
| + |
| +// This test posts non-nestable tasks in order of increasing delay, and checks |
| +// that that the tasks are run in FIFO order and that there is no execution |
| +// overlap whatsoever between any two tasks. |
| +// |
| +// If there are 10 tasks, task1 is posted first with a delay of zero, followed |
| +// by task2 with a delay of, say, 1, and so forth, until task10 is posted with |
| +// a delay of 10. It then checks that task2 started after task1, and only |
| +// after task1 had finished, and so forth. |
| +TYPED_TEST_P(SequencedTaskRunnerTest, SequentialDelayedNonNestable) { |
| + const int task_count = 20; |
| + const int delay_increment_ms = 50; |
| + |
| + this->delegate_.StartTaskRunner(); |
| + scoped_refptr<SequencedTaskRunner> task_runner = |
| + this->delegate_.GetTaskRunner(); |
| + |
| + for (int i = 0; i < task_count; ++i) { |
| + Closure task = Bind(&internal::SequencedTaskTracker::FastTask, |
| + this->task_tracker_, i); |
| + this->task_tracker_->TaskPosted(i); |
| + task_runner->PostNonNestableDelayedTask( |
| + FROM_HERE, |
| + task, |
| + TimeDelta::FromMilliseconds(delay_increment_ms * i)); |
| + } |
| + |
| + this->delegate_.StopTaskRunner(); |
| + |
| + EXPECT_EQ(this->task_tracker_->GetPostOrder(), |
| + this->task_tracker_->GetStartOrder()); |
| + EXPECT_EQ(this->task_tracker_->GetStartOrder(), |
| + this->task_tracker_->GetEndOrder()); |
| +} |
| + |
| +// This test posts a fast, non-nestable task from within each of a number of |
| +// slow, non-nestable tasks and checks that they all run in the sequence they |
| +// were posted in and that there is no execution overlap whatsoever. |
| +TYPED_TEST_P(SequencedTaskRunnerTest, NonNestablePostFromNonNestableTask) { |
| + const int parent_count = 1000; |
| + const int children_per_parent = 2; |
| + |
| + this->delegate_.StartTaskRunner(); |
| + scoped_refptr<SequencedTaskRunner> task_runner = |
| + this->delegate_.GetTaskRunner(); |
| + |
| + const int end = parent_count; |
| + for (int i = 0; i < end; ++i) { |
| + base::PlatformThread::YieldCurrentThread(); |
| + AutoLock post_lock(this->task_tracker_->post_lock_); |
| + const int post_i = this->task_tracker_->GetNextPostOrdinal(); |
| + Closure task = Bind( |
| + &internal::SequencedTaskTracker::PostFastNonNestableFromSlowNonNestable, |
| + this->task_tracker_, |
| + task_runner, |
| + post_i, |
| + children_per_parent); |
| + this->task_tracker_->post_events_.push_back(post_i); |
| + task_runner->PostNonNestableTask(FROM_HERE, task); |
|
Francois
2012/03/18 16:28:29
The loop body is messy, but I want to hear your co
|
| + } |
| + |
| + this->delegate_.StopTaskRunner(); |
| + |
| + EXPECT_EQ(this->task_tracker_->GetPostOrder(), |
| + this->task_tracker_->GetStartOrder()); |
| + EXPECT_EQ(this->task_tracker_->GetEndOrder(), |
| + this->task_tracker_->GetStartOrder()); |
| +} |
| + |
| +// TODO(francoisk777@gmail.com) Add a test, similiar to the above, which which |
| +// runs some tasked nestedly (which should be implemented in the test |
| +// delegate). Also add, to the the test delegate, a predicate which checks |
| +// whether the implementation supports nested tasks. |
| + |
| +REGISTER_TYPED_TEST_CASE_P(SequencedTaskRunnerTest, |
| + SequentialNonNestable, |
| + FAILS_SequentialNestable, |
| + SequentialDelayedNonNestable, |
| + NonNestablePostFromNonNestableTask); |
| + |
| +} // namespace base |
| + |
| +#endif // BASE_TASK_RUNNER_TEST_TEMPLATE_H_ |
| Property changes on: base/test/sequenced_task_runner_test_template.h |
| ___________________________________________________________________ |
| Added: svn:eol-style |
| + LF |