| 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,379 @@
|
| +// 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 <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/synchronization/waitable_event.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:
|
| + // Keeps track of a task's run status. I.e. whether it has run to completion
|
| + // and whether its predecessor had run to completion by the time it started
|
| + // running.
|
| + class TaskStatus {
|
| + public:
|
| + TaskStatus();
|
| + TaskStatus(const TaskStatus& ts);
|
| + TaskStatus& operator=(const TaskStatus& ts);
|
| + bool operator==(const TaskStatus& ts) const;
|
| +
|
| + // Claims ownership of this task status for a task.
|
| + bool Claim();
|
| + // Whether the owning task has started running yet.
|
| + bool Started() const;
|
| + // Whether the owning task has run to completion yet.
|
| + bool Completed() const;
|
| + // Whether the previous task had started by the time this one did.
|
| + bool PrevStartedBefore() const;
|
| + // Whether the previous task (the one which owns the preceding position in
|
| + // the task status vector) had finished by the time this one started.
|
| + bool PrevCompletedBefore() const;
|
| + // Declares that the owning task has started running.
|
| + void SetStarted();
|
| + // Declares that the owning task has run to completion.
|
| + void SetCompleted();
|
| + // Declares that the previous task had started by the time the owning task
|
| + // started.
|
| + void SetPrevStartedBefore(bool b);
|
| + // Declares that the previous task had run to completion by the time the
|
| + // owning task started.
|
| + void SetPrevCompletedBefore(bool b);
|
| +
|
| + private:
|
| + // An output operator is required to make a failed gtest assertion print
|
| + // human-readable information.
|
| + friend std::ostream& operator<<(std::ostream&, const TaskStatus&);
|
| +
|
| + mutable Lock lock_;
|
| + // Used to prevent task_i from checking task_i-1.Started() before task_i-1
|
| + // has had a chance to signal that it has started. There would otherwise
|
| + // be a race between the top of the task function and the line where it
|
| + // calls SetStarted().
|
| + mutable WaitableEvent start_event_;
|
| + // Whether this status slot has been claimed by a task yet.
|
| + bool claimed_;
|
| + bool started_;
|
| + bool completed_;
|
| + bool prev_started_;
|
| + bool prev_completed_;
|
| + };
|
| +
|
| + typedef std::vector<TaskStatus> TaskStatuses;
|
| +
|
| + SequencedTaskTracker();
|
| +
|
| + // Pre-allocates memory for the non-nestable task statuses.
|
| + void SetNonNestableTaskCount(std::size_t task_count);
|
| +
|
| + // Pre-allocates memory for the nestable task statuses.
|
| + void SetNestableTaskCount(std::size_t task_count);
|
| +
|
| + const TaskStatuses& GetNonNestableTaskStatuses() const;
|
| + const TaskStatuses& GetNestableTaskStatuses() const;
|
| +
|
| + // A fast, non-nestable task.
|
| + void FastNonNestableTask(int base_status_i);
|
| +
|
| + // A fast, nestable task.
|
| + void FastNestableTask(int parent_task_status_slot);
|
| +
|
| + // A slow, non-nestable task (sleeps for 1 second).
|
| + void SlowNonNestableTask(int base_status_i);
|
| +
|
| + // A slow, nestable task (sleeps for 1 second).
|
| + void SlowNestableTask(int base_status_i);
|
| +
|
| + // Posts a fast, non-nestable task from a slow, non-nestable one.
|
| + void PostFastNonNestableFromSlowNonNestable(
|
| + scoped_refptr<SequencedTaskRunner> task_runner,
|
| + const int base_status_i,
|
| + const int child_count);
|
| +
|
| + // Posts a fast, nestable task from a slow, non-nestable one.
|
| + void PostFastNestableFromSlowNonNestable(
|
| + scoped_refptr<SequencedTaskRunner> task_runner,
|
| + const int base_status_i,
|
| + const int child_count);
|
| +
|
| + private:
|
| + friend class RefCountedThreadSafe<SequencedTaskTracker>;
|
| +
|
| + ~SequencedTaskTracker();
|
| +
|
| + // Finds the lowest-numbered non-nestable task status object, beginning at
|
| + // position |search_from|, which has not yet been claimed.
|
| + int ClaimNonNestableTaskStatus(const int search_from);
|
| +
|
| + // Finds the lowest-numbered nestable task status object, beginning at
|
| + // position |search_from|, which has not yet been claimed.
|
| + int ClaimNestableTaskStatus(const int search_from);
|
| +
|
| + TaskStatuses nestable_statuses_;
|
| + TaskStatuses non_nestable_statuses_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(SequencedTaskTracker);
|
| +};
|
| +
|
| +// An output operator is required to make a failed gtest assertion print
|
| +// human-readable information.
|
| +std::ostream& operator<<(std::ostream& os,
|
| + const SequencedTaskTracker::TaskStatus& ts);
|
| +
|
| +} // namespace internal
|
| +
|
| +template <typename TaskRunnerTestDelegate>
|
| +class SequencedTaskRunnerTest : public testing::Test {
|
| + protected:
|
| + SequencedTaskRunnerTest()
|
| + : task_tracker_(new internal::SequencedTaskTracker()) {
|
| + good_task_status_prototype_.SetStarted();
|
| + good_task_status_prototype_.SetPrevStartedBefore(true);
|
| + good_task_status_prototype_.SetPrevCompletedBefore(true);
|
| + good_task_status_prototype_.SetCompleted();
|
| + }
|
| +
|
| + const scoped_refptr<internal::SequencedTaskTracker> task_tracker_;
|
| + TaskRunnerTestDelegate delegate_;
|
| + internal::SequencedTaskTracker::TaskStatus good_task_status_prototype_;
|
| +};
|
| +
|
| +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->task_tracker_->SetNonNestableTaskCount(task_count);
|
| + internal::SequencedTaskTracker::TaskStatuses expected_statuses(
|
| + task_count,
|
| + this->good_task_status_prototype_);
|
| +
|
| + 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::SlowNonNestableTask,
|
| + this->task_tracker_, i);
|
| + } else {
|
| + task = Bind(&internal::SequencedTaskTracker::FastNonNestableTask,
|
| + this->task_tracker_, i);
|
| + }
|
| + task_runner->PostNonNestableTask(FROM_HERE, task);
|
| + }
|
| +
|
| + this->delegate_.StopTaskRunner();
|
| +
|
| + EXPECT_EQ(expected_statuses,
|
| + this->task_tracker_->GetNonNestableTaskStatuses());
|
| +}
|
| +
|
| +// 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, SequentialNestable) {
|
| + const int task_count = 1000;
|
| +
|
| + this->task_tracker_->SetNestableTaskCount(task_count);
|
| + internal::SequencedTaskTracker::TaskStatuses expected_statuses(
|
| + task_count,
|
| + this->good_task_status_prototype_);
|
| +
|
| + 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::SlowNestableTask,
|
| + this->task_tracker_, i);
|
| + } else {
|
| + task = Bind(&internal::SequencedTaskTracker::FastNestableTask,
|
| + this->task_tracker_, i);
|
| + }
|
| + task_runner->PostTask(FROM_HERE, task);
|
| + }
|
| +
|
| + this->delegate_.StopTaskRunner();
|
| +
|
| + EXPECT_EQ(expected_statuses,
|
| + this->task_tracker_->GetNestableTaskStatuses());
|
| +}
|
| +
|
| +// 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) {
|
| + if (!this->delegate_.TaskRunnerHandlesNonZeroDelays()) {
|
| + DLOG(INFO) << "This SequencedTaskRunner doesn't handle "
|
| + "non-zero delays; skipping";
|
| + return;
|
| + }
|
| +
|
| + const int task_count = 20;
|
| + const int delay_increment_ms = 50;
|
| +
|
| + this->task_tracker_->SetNonNestableTaskCount(task_count);
|
| + internal::SequencedTaskTracker::TaskStatuses expected_statuses(
|
| + task_count,
|
| + this->good_task_status_prototype_);
|
| +
|
| + this->delegate_.StartTaskRunner();
|
| + scoped_refptr<SequencedTaskRunner> task_runner =
|
| + this->delegate_.GetTaskRunner();
|
| +
|
| + for (int i = 0; i < task_count; ++i) {
|
| + Closure task = Bind(&internal::SequencedTaskTracker::FastNonNestableTask,
|
| + this->task_tracker_, i);
|
| + task_runner->PostNonNestableDelayedTask(
|
| + FROM_HERE,
|
| + task,
|
| + TimeDelta::FromMilliseconds(delay_increment_ms * i));
|
| + }
|
| +
|
| + this->delegate_.StopTaskRunner();
|
| +
|
| + EXPECT_EQ(expected_statuses,
|
| + this->task_tracker_->GetNonNestableTaskStatuses());
|
| +}
|
| +
|
| +// 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;
|
| + const int task_count = parent_count * (children_per_parent + 1);
|
| +
|
| + this->task_tracker_->SetNonNestableTaskCount(task_count);
|
| + internal::SequencedTaskTracker::TaskStatuses expected_statuses(
|
| + task_count,
|
| + this->good_task_status_prototype_);
|
| +
|
| + this->delegate_.StartTaskRunner();
|
| + scoped_refptr<SequencedTaskRunner> task_runner =
|
| + this->delegate_.GetTaskRunner();
|
| +
|
| + const int end = parent_count;
|
| + for (int i = 0; i < end; ++i) {
|
| + Closure task = Bind(
|
| + &internal::SequencedTaskTracker::PostFastNonNestableFromSlowNonNestable,
|
| + this->task_tracker_,
|
| + task_runner,
|
| + i,
|
| + children_per_parent);
|
| + task_runner->PostNonNestableTask(FROM_HERE, task);
|
| + }
|
| +
|
| + this->delegate_.StopTaskRunner();
|
| +
|
| + EXPECT_EQ(expected_statuses,
|
| + this->task_tracker_->GetNonNestableTaskStatuses());
|
| +}
|
| +
|
| +// This test posts a number of fast, nestable tasks from within each of a
|
| +// number of slow, non-nestable tasks and checks that the non-nestable ones
|
| +// all run in the sequence they were posted in and that there is no execution
|
| +// overlap whatsoever.
|
| +TYPED_TEST_P(SequencedTaskRunnerTest,
|
| + NestablePostFromNonNestableTask) {
|
| + const int parent_count = 500;
|
| + const int children_per_parent = 2;
|
| +
|
| + this->task_tracker_->SetNonNestableTaskCount(parent_count);
|
| + internal::SequencedTaskTracker::TaskStatuses expected_non_nestable_statuses(
|
| + parent_count,
|
| + this->good_task_status_prototype_);
|
| + this->task_tracker_->SetNestableTaskCount(children_per_parent * parent_count);
|
| + internal::SequencedTaskTracker::TaskStatuses expected_nestable_statuses(
|
| + children_per_parent * parent_count,
|
| + this->good_task_status_prototype_);
|
| +
|
| + this->delegate_.StartTaskRunner();
|
| + scoped_refptr<SequencedTaskRunner> task_runner =
|
| + this->delegate_.GetTaskRunner();
|
| +
|
| + const int end = parent_count;
|
| + for (int i = 0; i < end; ++i) {
|
| + Closure task = Bind(
|
| + &internal::SequencedTaskTracker::PostFastNestableFromSlowNonNestable,
|
| + this->task_tracker_,
|
| + task_runner,
|
| + i,
|
| + children_per_parent);
|
| + task_runner->PostNonNestableTask(FROM_HERE, task);
|
| + }
|
| +
|
| + this->delegate_.StopTaskRunner();
|
| +
|
| + EXPECT_EQ(expected_non_nestable_statuses,
|
| + this->task_tracker_->GetNonNestableTaskStatuses());
|
| + EXPECT_EQ(expected_nestable_statuses,
|
| + this->task_tracker_->GetNestableTaskStatuses());
|
| +}
|
| +
|
| +// 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,
|
| + SequentialNestable,
|
| + SequentialDelayedNonNestable,
|
| + NonNestablePostFromNonNestableTask,
|
| + NestablePostFromNonNestableTask);
|
| +
|
| +} // 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
|
|
|
|
|