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,299 @@ |
| +// 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/synchronization/lock.h" |
| +#include "base/sequenced_task_runner.h" |
| +#include "base/tracked_objects.h" |
| +#include "testing/gtest/include/gtest/gtest.h" |
| + |
| +namespace base { |
| + |
| +// Utility class used in the tests below. |
| +class SeqTaskTracker : public RefCountedThreadSafe<SeqTaskTracker> { |
|
akalin
2012/03/13 07:27:11
SeqTaskTracker -> SequencedTaskTracker (abbreviati
akalin
2012/03/13 07:27:11
put this class in the base::internal namespace
(y
Francois
2012/03/14 15:43:31
Done.
Francois
2012/03/14 15:43:31
Done.
|
| + 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 { |
|
akalin
2012/03/13 07:27:11
Hmm I think this class may be overkill. I was thi
akalin
2012/03/13 20:00:10
Actually a few corrections. WrapTask should *not*
Francois
2012/03/14 15:43:31
I like your model because it mirrors the task hier
|
| + 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 run to completion yet. |
| + bool Completed() 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 run to completion. |
| + void SetCompleted(); |
| + // Declares that the previous task had run to completion by the time the |
| + // owning task started. |
| + void SetPrevCompletedBefore(const bool b); |
| + |
| + private: |
| + mutable Lock lock_; |
| + // Whether this status slot has been claimed by a task yet. |
| + bool claimed_; |
| + bool completed_; |
| + // Whether the owning task has set the "completed" flag. |
| + bool completed_set_; |
| + bool prev_completed_; |
| + // Whether the owning task has set the "previous completed" flag. |
| + bool prev_completed_set_; |
| + }; |
| + |
| + typedef std::vector<TaskStatus> TaskStatuses; |
| + |
| + SeqTaskTracker(); |
| + |
| + // Pre-allocates memory for the non-nestable task statuses. |
| + void SetNonNestableTaskCount(const std::size_t task_count); |
|
akalin
2012/03/13 07:27:11
remove const (doesn't make sense for value paramet
Francois
2012/03/14 15:43:31
Done. But it does make sense to me :) If something
|
| + |
| + // Pre-allocates memory for the nestable task statuses. |
| + void SetNestableTaskCount(const 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); |
| + |
| + // Posts a fast, non-nestable task from a slow, non-nestable one. |
| + void PostFastNonNestableFromSlowNonNestable( |
| + scoped_refptr<SequencedTaskRunner> tr, |
|
akalin
2012/03/13 07:27:11
tr -> task_runner (abbreviations are discouraged)
Francois
2012/03/14 15:43:31
Done.
|
| + 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> tr, |
| + const int base_status_i, |
| + const int child_count); |
| + |
| + private: |
| + friend class RefCountedThreadSafe<SeqTaskTracker>; |
| + |
| + ~SeqTaskTracker(); |
| + |
| + // 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 = 0); |
|
akalin
2012/03/13 07:27:11
default arguments are prohibited by style guide
Francois
2012/03/14 15:43:31
Done.
|
| + |
| + // 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 = 0); |
|
akalin
2012/03/13 07:27:11
here too
Francois
2012/03/14 15:43:31
Done.
|
| + |
| + TaskStatuses nestable_statuses_; |
| + TaskStatuses non_nestable_statuses_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(SeqTaskTracker); |
| +}; |
| + |
| +std::ostream& operator<<(std::ostream& os, |
|
akalin
2012/03/13 07:27:11
a ToString() member function is preferred to overl
Francois
2012/03/14 15:43:31
OK, but I added this to customise the output of EX
|
| + const SeqTaskTracker::TaskStatus& ts); |
| + |
| +template <typename TaskRunnerTestDelegate> |
| +class SequencedTaskRunnerTest : public testing::Test { |
| + protected: |
| + SequencedTaskRunnerTest() : task_tracker_(new SeqTaskTracker()) { |
| + good_task_status_prototype_.SetPrevCompletedBefore(true); |
| + good_task_status_prototype_.SetCompleted(); |
| + } |
| + |
| + const scoped_refptr<SeqTaskTracker> task_tracker_; |
| + TaskRunnerTestDelegate delegate_; |
| + SeqTaskTracker::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); |
| + SeqTaskTracker::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(&SeqTaskTracker::SlowNonNestableTask, |
| + this->task_tracker_, i); |
| + } else { |
| + task = Bind(&SeqTaskTracker::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 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; |
|
akalin
2012/03/13 07:27:11
if you change the logs in PostDelayed...Task to us
Francois
2012/03/14 15:43:31
Done.
|
| + const int delay_increment_ms = 50; |
| + |
| + this->task_tracker_->SetNonNestableTaskCount(task_count); |
| + SeqTaskTracker::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(&SeqTaskTracker::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); |
| + SeqTaskTracker::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( |
| + &SeqTaskTracker::PostFastNonNestableFromSlowNonNestable, |
| + this->task_tracker_, |
| + task_runner, |
| + i, |
| + children_per_parent); |
| + DLOG(INFO) << "XXX Posted parent task " << i; |
| + 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); |
| + SeqTaskTracker::TaskStatuses expected_non_nestable_statuses( |
| + parent_count, |
| + this->good_task_status_prototype_); |
| + this->task_tracker_->SetNestableTaskCount(children_per_parent * parent_count); |
| + SeqTaskTracker::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( |
| + &SeqTaskTracker::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()); |
| +} |
| + |
|
akalin
2012/03/13 07:27:11
Add a TODO for a test similar to the above, but ha
Francois
2012/03/14 15:43:31
Done.
|
| +REGISTER_TYPED_TEST_CASE_P(SequencedTaskRunnerTest, |
| + SequentialNonNestable, |
| + SequentialDelayedNonNestable, |
| + NonNestablePostFromNonNestableTask, |
| + NestablePostFromNonNestableTask); |
| + |
| +} // namespace base |
| + |
| +#endif //#define BASE_TASK_RUNNER_TEST_TEMPLATE_H_ |
|
akalin
2012/03/13 07:27:11
no '#define' in comment
Francois
2012/03/14 15:43:31
Done.
|
| Property changes on: base/test/sequenced_task_runner_test_template.h |
| ___________________________________________________________________ |
| Added: svn:eol-style |
| + LF |