#ifndef BASE_TEST_SEQUENCED_TASK_RUNNER_TEST_TEMPLATE_H_
#define BASE_TEST_SEQUENCED_TASK_RUNNER_TEST_TEMPLATE_H_
#include <cstddef>
#include <iosfwd>
#include <vector>
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/memory/ref_counted.h"
#include "base/synchronization/condition_variable.h"
#include "base/synchronization/lock.h"
#include "base/task/sequenced_task_runner.h"
#include "base/threading/platform_thread.h"
#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
namespace internal {
struct TaskEvent {
enum Type { POST, START, END };
TaskEvent(int i, Type type);
int i;
Type type;
};
class SequencedTaskTracker : public RefCountedThreadSafe<SequencedTaskTracker> {
public:
SequencedTaskTracker();
SequencedTaskTracker(const SequencedTaskTracker&) = delete;
SequencedTaskTracker& operator=(const SequencedTaskTracker&) = delete;
void PostWrappedNonNestableTask(SequencedTaskRunner* task_runner,
OnceClosure task);
void PostWrappedNestableTask(SequencedTaskRunner* task_runner,
OnceClosure task);
void PostWrappedDelayedNonNestableTask(SequencedTaskRunner* task_runner,
OnceClosure task,
TimeDelta delay);
void PostNonNestableTasks(SequencedTaskRunner* task_runner, int task_count);
const std::vector<TaskEvent>& GetTaskEvents() const;
void WaitForCompletedTasks(int count);
private:
friend class RefCountedThreadSafe<SequencedTaskTracker>;
~SequencedTaskTracker();
void RunTask(OnceClosure task, int task_i);
void TaskPosted(int i);
void TaskStarted(int i);
void TaskEnded(int i);
Lock lock_;
std::vector<TaskEvent> events_;
int next_post_i_;
int task_end_count_;
ConditionVariable task_end_cv_;
};
void PrintTo(const TaskEvent& event, std::ostream* os);
::testing::AssertionResult CheckNonNestableInvariants(
const std::vector<TaskEvent>& events,
int task_count);
}
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_SUITE_P(SequencedTaskRunnerTest);
TYPED_TEST_P(SequencedTaskRunnerTest, SequentialNonNestable) {
const int kTaskCount = 1000;
this->delegate_.StartTaskRunner();
const scoped_refptr<SequencedTaskRunner> task_runner =
this->delegate_.GetTaskRunner();
this->task_tracker_->PostWrappedNonNestableTask(
task_runner.get(), BindOnce(&PlatformThread::Sleep, Seconds(1)));
for (int i = 1; i < kTaskCount; ++i) {
this->task_tracker_->PostWrappedNonNestableTask(task_runner.get(),
OnceClosure());
}
this->delegate_.StopTaskRunner();
EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
kTaskCount));
}
TYPED_TEST_P(SequencedTaskRunnerTest, SequentialNestable) {
const int kTaskCount = 1000;
this->delegate_.StartTaskRunner();
const scoped_refptr<SequencedTaskRunner> task_runner =
this->delegate_.GetTaskRunner();
this->task_tracker_->PostWrappedNestableTask(
task_runner.get(), BindOnce(&PlatformThread::Sleep, Seconds(1)));
for (int i = 1; i < kTaskCount; ++i) {
this->task_tracker_->PostWrappedNestableTask(task_runner.get(),
OnceClosure());
}
this->delegate_.StopTaskRunner();
EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
kTaskCount));
}
TYPED_TEST_P(SequencedTaskRunnerTest, SequentialDelayedNonNestable) {
const int kTaskCount = 20;
const int kDelayIncrementMs = 50;
this->delegate_.StartTaskRunner();
const scoped_refptr<SequencedTaskRunner> task_runner =
this->delegate_.GetTaskRunner();
for (int i = 0; i < kTaskCount; ++i) {
this->task_tracker_->PostWrappedDelayedNonNestableTask(
task_runner.get(), OnceClosure(), Milliseconds(kDelayIncrementMs * i));
}
this->task_tracker_->WaitForCompletedTasks(kTaskCount);
this->delegate_.StopTaskRunner();
EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
kTaskCount));
}
TYPED_TEST_P(SequencedTaskRunnerTest, NonNestablePostFromNonNestableTask) {
const int kParentCount = 10;
const int kChildrenPerParent = 10;
this->delegate_.StartTaskRunner();
const scoped_refptr<SequencedTaskRunner> task_runner =
this->delegate_.GetTaskRunner();
for (int i = 0; i < kParentCount; ++i) {
auto task = BindOnce(&internal::SequencedTaskTracker::PostNonNestableTasks,
this->task_tracker_, RetainedRef(task_runner),
kChildrenPerParent);
this->task_tracker_->PostWrappedNonNestableTask(task_runner.get(),
std::move(task));
}
this->delegate_.StopTaskRunner();
EXPECT_TRUE(CheckNonNestableInvariants(
this->task_tracker_->GetTaskEvents(),
kParentCount * (kChildrenPerParent + 1)));
}
TYPED_TEST_P(SequencedTaskRunnerTest, DelayedTasksSameDelay) {
const int kTaskCount = 2;
const TimeDelta kDelay = Milliseconds(100);
this->delegate_.StartTaskRunner();
const scoped_refptr<SequencedTaskRunner> task_runner =
this->delegate_.GetTaskRunner();
this->task_tracker_->PostWrappedDelayedNonNestableTask(task_runner.get(),
OnceClosure(), kDelay);
this->task_tracker_->PostWrappedDelayedNonNestableTask(task_runner.get(),
OnceClosure(), kDelay);
this->task_tracker_->WaitForCompletedTasks(kTaskCount);
this->delegate_.StopTaskRunner();
EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
kTaskCount));
}
TYPED_TEST_P(SequencedTaskRunnerTest, DelayedTaskAfterLongTask) {
const int kTaskCount = 2;
this->delegate_.StartTaskRunner();
const scoped_refptr<SequencedTaskRunner> task_runner =
this->delegate_.GetTaskRunner();
this->task_tracker_->PostWrappedNonNestableTask(
task_runner.get(),
base::BindOnce(&PlatformThread::Sleep, Milliseconds(50)));
this->task_tracker_->PostWrappedDelayedNonNestableTask(
task_runner.get(), OnceClosure(), Milliseconds(10));
this->task_tracker_->WaitForCompletedTasks(kTaskCount);
this->delegate_.StopTaskRunner();
EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
kTaskCount));
}
TYPED_TEST_P(SequencedTaskRunnerTest, DelayedTaskAfterManyLongTasks) {
const int kTaskCount = 11;
this->delegate_.StartTaskRunner();
const scoped_refptr<SequencedTaskRunner> task_runner =
this->delegate_.GetTaskRunner();
for (int i = 0; i < kTaskCount - 1; i++) {
this->task_tracker_->PostWrappedNonNestableTask(
task_runner.get(),
base::BindOnce(&PlatformThread::Sleep, Milliseconds(50)));
}
this->task_tracker_->PostWrappedDelayedNonNestableTask(
task_runner.get(), OnceClosure(), Milliseconds(10));
this->task_tracker_->WaitForCompletedTasks(kTaskCount);
this->delegate_.StopTaskRunner();
EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(),
kTaskCount));
}
REGISTER_TYPED_TEST_SUITE_P(SequencedTaskRunnerTest,
SequentialNonNestable,
SequentialNestable,
SequentialDelayedNonNestable,
NonNestablePostFromNonNestableTask,
DelayedTasksSameDelay,
DelayedTaskAfterLongTask,
DelayedTaskAfterManyLongTasks);
}
#endif