// 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 #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 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 fake_sequenced_task_runner1( new FakeTaskRunner()); const scoped_refptr 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 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