blob: 4e601b074236e883ade2c940bed98a3e8d032d09 [file] [log] [blame]
// 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 "base/sequence_checker_impl.h"
#include <cstddef>
#include "base/bind.h"
#include "base/compiler_specific.h"
#include "base/location.h"
#include "base/memory/ref_counted.h"
#include "base/message_loop.h"
#include "base/sequenced_task_runner.h"
#include "base/threading/thread.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
namespace {
// Implementation of SequencedTaskRunner that lets us control what
// RunsTasksOnCurrentThread() returns.
class FakeTaskRunner : public SequencedTaskRunner {
public:
FakeTaskRunner() : runs_tasks_on_current_thread_(false) {}
void SetRunsTasksOnCurrentThread(bool runs_tasks_on_current_thread) {
runs_tasks_on_current_thread_ = runs_tasks_on_current_thread;
}
// SequencedTaskRunner implementation.
virtual bool PostDelayedTask(const tracked_objects::Location& from_here,
const Closure& task,
TimeDelta delay) OVERRIDE {
ADD_FAILURE();
return false;
}
virtual bool PostNonNestableDelayedTask(
const tracked_objects::Location& from_here,
const Closure& task,
TimeDelta delay) OVERRIDE {
ADD_FAILURE();
return false;
}
virtual bool RunsTasksOnCurrentThread() const OVERRIDE {
return runs_tasks_on_current_thread_;
}
protected:
virtual ~FakeTaskRunner() {}
private:
bool runs_tasks_on_current_thread_;
};
class SequenceCheckerImplTest : public ::testing::Test {
};
// Create a SequenceCheckerImpl with a SequencedTaskRunner and make
// sure that CalledOnValidSequence() returns what that SequencedTaskRunner
// returns for RunsTasksOnCurrentThread().
TEST_F(SequenceCheckerImplTest, CalledOnValidSequenceNonNull) {
const scoped_refptr<FakeTaskRunner> fake_sequenced_task_runner(
new FakeTaskRunner());
const SequenceCheckerImpl sequence_checker_impl(fake_sequenced_task_runner);
EXPECT_FALSE(sequence_checker_impl.CalledOnValidSequence());
fake_sequenced_task_runner->SetRunsTasksOnCurrentThread(true);
EXPECT_TRUE(sequence_checker_impl.CalledOnValidSequence());
fake_sequenced_task_runner->SetRunsTasksOnCurrentThread(false);
EXPECT_FALSE(sequence_checker_impl.CalledOnValidSequence());
}
void ExpectCalledOnValidSequence(
const tracked_objects::Location& location,
const SequenceCheckerImpl* sequence_checker_impl,
bool expected_value) {
EXPECT_EQ(expected_value, sequence_checker_impl->CalledOnValidSequence())
<< location.ToString();
}
// Create a SequenceCheckerImpl with no SequencedTaskRunner and make
// sure that CalledOnValidSequence() behaves like
// ThreadChecker::CalledOnValidThread().
TEST_F(SequenceCheckerImplTest, CalledOnValidSequenceNull) {
const SequenceCheckerImpl sequence_checker_impl(NULL);
EXPECT_TRUE(sequence_checker_impl.CalledOnValidSequence());
{
Thread thread("thread 1");
ASSERT_TRUE(thread.Start());
thread.message_loop()->PostTask(
FROM_HERE, Bind(&ExpectCalledOnValidSequence,
FROM_HERE,
Unretained(&sequence_checker_impl),
false));
}
EXPECT_TRUE(sequence_checker_impl.CalledOnValidSequence());
}
// Create a SequenceCheckerImpl with a SequencedTaskRunner and switch
// it to another one. CalledOnValidSequence() should return what its
// underlying SequencedTaskRunner returns for
// RunsTasksOnCurrentThread().
TEST_F(SequenceCheckerImplTest, ChangeSequenceNonNull) {
const scoped_refptr<FakeTaskRunner> fake_sequenced_task_runner1(
new FakeTaskRunner());
const scoped_refptr<FakeTaskRunner> fake_sequenced_task_runner2(
new FakeTaskRunner());
SequenceCheckerImpl sequence_checker_impl(fake_sequenced_task_runner1);
EXPECT_FALSE(sequence_checker_impl.CalledOnValidSequence());
fake_sequenced_task_runner2->SetRunsTasksOnCurrentThread(true);
EXPECT_FALSE(sequence_checker_impl.CalledOnValidSequence());
sequence_checker_impl.ChangeSequence(fake_sequenced_task_runner2);
EXPECT_TRUE(sequence_checker_impl.CalledOnValidSequence());
sequence_checker_impl.ChangeSequence(fake_sequenced_task_runner1);
EXPECT_FALSE(sequence_checker_impl.CalledOnValidSequence());
}
// Create a SequenceCheckerImpl with a SequencedTaskRunner and switch
// it to a NULL one. CalledOnValidSequence() should then behave like
// ThreadChecker::CalledOnValidThread().
TEST_F(SequenceCheckerImplTest, ChangeSequenceNull) {
const scoped_refptr<FakeTaskRunner> fake_sequenced_task_runner(
new FakeTaskRunner());
SequenceCheckerImpl sequence_checker_impl(fake_sequenced_task_runner);
EXPECT_FALSE(sequence_checker_impl.CalledOnValidSequence());
sequence_checker_impl.ChangeSequence(NULL);
// Binds to current thread.
EXPECT_TRUE(sequence_checker_impl.CalledOnValidSequence());
{
Thread thread("thread 1");
ASSERT_TRUE(thread.Start());
thread.message_loop()->PostTask(
FROM_HERE, Bind(&ExpectCalledOnValidSequence,
FROM_HERE,
Unretained(&sequence_checker_impl),
false));
}
EXPECT_TRUE(sequence_checker_impl.CalledOnValidSequence());
sequence_checker_impl.ChangeSequence(NULL);
// Binds to worker thread.
{
Thread thread("thread 2");
ASSERT_TRUE(thread.Start());
thread.message_loop()->PostTask(
FROM_HERE, Bind(&ExpectCalledOnValidSequence,
FROM_HERE,
Unretained(&sequence_checker_impl),
true));
}
EXPECT_FALSE(sequence_checker_impl.CalledOnValidSequence());
}
// Create a SequenceCheckerImpl with the current thread's task runner
// and switch it to other task runners. CalledOnValidSequence() should
// return true only when it's on the correct thread.
TEST_F(SequenceCheckerImplTest, MultipleThreads) {
MessageLoop loop;
SequenceCheckerImpl sequence_checker_impl(loop.message_loop_proxy());
EXPECT_TRUE(sequence_checker_impl.CalledOnValidSequence());
{
Thread thread("thread 1");
ASSERT_TRUE(thread.Start());
thread.message_loop()->PostTask(
FROM_HERE, Bind(&ExpectCalledOnValidSequence,
FROM_HERE,
Unretained(&sequence_checker_impl),
false));
thread.message_loop()->PostTask(
FROM_HERE, Bind(&SequenceCheckerImpl::ChangeSequence,
Unretained(&sequence_checker_impl),
thread.message_loop_proxy()));
thread.message_loop()->PostTask(
FROM_HERE, Bind(&ExpectCalledOnValidSequence,
FROM_HERE,
Unretained(&sequence_checker_impl),
true));
}
EXPECT_FALSE(sequence_checker_impl.CalledOnValidSequence());
sequence_checker_impl.ChangeSequence(loop.message_loop_proxy());
EXPECT_TRUE(sequence_checker_impl.CalledOnValidSequence());
{
Thread thread("thread 2");
ASSERT_TRUE(thread.Start());
thread.message_loop()->PostTask(
FROM_HERE, Bind(&ExpectCalledOnValidSequence,
FROM_HERE,
Unretained(&sequence_checker_impl),
false));
}
EXPECT_TRUE(sequence_checker_impl.CalledOnValidSequence());
}
} // namespace
} // namespace base