|  | // 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 <memory> | 
|  |  | 
|  | #include "base/bind.h" | 
|  | #include "base/bind_helpers.h" | 
|  | #include "base/macros.h" | 
|  | #include "base/memory/ref_counted.h" | 
|  | #include "base/sequence_token.h" | 
|  | #include "base/test/test_simple_task_runner.h" | 
|  | #include "base/threading/simple_thread.h" | 
|  | #include "base/threading/thread_checker_impl.h" | 
|  | #include "base/threading/thread_task_runner_handle.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | namespace base { | 
|  | namespace { | 
|  |  | 
|  | // A thread that runs a callback. | 
|  | class RunCallbackThread : public SimpleThread { | 
|  | public: | 
|  | explicit RunCallbackThread(const Closure& callback) | 
|  | : SimpleThread("RunCallbackThread"), callback_(callback) {} | 
|  |  | 
|  | private: | 
|  | // SimpleThread: | 
|  | void Run() override { callback_.Run(); } | 
|  |  | 
|  | const Closure callback_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(RunCallbackThread); | 
|  | }; | 
|  |  | 
|  | // Runs a callback on a new thread synchronously. | 
|  | void RunCallbackOnNewThreadSynchronously(const Closure& callback) { | 
|  | RunCallbackThread run_callback_thread(callback); | 
|  | run_callback_thread.Start(); | 
|  | run_callback_thread.Join(); | 
|  | } | 
|  |  | 
|  | void ExpectCalledOnValidThread(ThreadCheckerImpl* thread_checker) { | 
|  | ASSERT_TRUE(thread_checker); | 
|  |  | 
|  | // This should bind |thread_checker| to the current thread if it wasn't | 
|  | // already bound to a thread. | 
|  | EXPECT_TRUE(thread_checker->CalledOnValidThread()); | 
|  |  | 
|  | // Since |thread_checker| is now bound to the current thread, another call to | 
|  | // CalledOnValidThread() should return true. | 
|  | EXPECT_TRUE(thread_checker->CalledOnValidThread()); | 
|  | } | 
|  |  | 
|  | void ExpectNotCalledOnValidThread(ThreadCheckerImpl* thread_checker) { | 
|  | ASSERT_TRUE(thread_checker); | 
|  | EXPECT_FALSE(thread_checker->CalledOnValidThread()); | 
|  | } | 
|  |  | 
|  | void ExpectNotCalledOnValidThreadWithSequenceTokenAndThreadTaskRunnerHandle( | 
|  | ThreadCheckerImpl* thread_checker, | 
|  | SequenceToken sequence_token) { | 
|  | ThreadTaskRunnerHandle thread_task_runner_handle( | 
|  | make_scoped_refptr(new TestSimpleTaskRunner)); | 
|  | ScopedSetSequenceTokenForCurrentThread | 
|  | scoped_set_sequence_token_for_current_thread(sequence_token); | 
|  | ExpectNotCalledOnValidThread(thread_checker); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | TEST(ThreadCheckerTest, AllowedSameThreadNoSequenceToken) { | 
|  | ThreadCheckerImpl thread_checker; | 
|  | EXPECT_TRUE(thread_checker.CalledOnValidThread()); | 
|  | } | 
|  |  | 
|  | TEST(ThreadCheckerTest, | 
|  | AllowedSameThreadAndSequenceDifferentTasksWithThreadTaskRunnerHandle) { | 
|  | ThreadTaskRunnerHandle thread_task_runner_handle( | 
|  | make_scoped_refptr(new TestSimpleTaskRunner)); | 
|  |  | 
|  | std::unique_ptr<ThreadCheckerImpl> thread_checker; | 
|  | const SequenceToken sequence_token = SequenceToken::Create(); | 
|  |  | 
|  | { | 
|  | ScopedSetSequenceTokenForCurrentThread | 
|  | scoped_set_sequence_token_for_current_thread(sequence_token); | 
|  | thread_checker.reset(new ThreadCheckerImpl); | 
|  | } | 
|  |  | 
|  | { | 
|  | ScopedSetSequenceTokenForCurrentThread | 
|  | scoped_set_sequence_token_for_current_thread(sequence_token); | 
|  | EXPECT_TRUE(thread_checker->CalledOnValidThread()); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST(ThreadCheckerTest, | 
|  | AllowedSameThreadSequenceAndTaskNoThreadTaskRunnerHandle) { | 
|  | ScopedSetSequenceTokenForCurrentThread | 
|  | scoped_set_sequence_token_for_current_thread(SequenceToken::Create()); | 
|  | ThreadCheckerImpl thread_checker; | 
|  | EXPECT_TRUE(thread_checker.CalledOnValidThread()); | 
|  | } | 
|  |  | 
|  | TEST(ThreadCheckerTest, | 
|  | DisallowedSameThreadAndSequenceDifferentTasksNoThreadTaskRunnerHandle) { | 
|  | std::unique_ptr<ThreadCheckerImpl> thread_checker; | 
|  |  | 
|  | { | 
|  | ScopedSetSequenceTokenForCurrentThread | 
|  | scoped_set_sequence_token_for_current_thread(SequenceToken::Create()); | 
|  | thread_checker.reset(new ThreadCheckerImpl); | 
|  | } | 
|  |  | 
|  | { | 
|  | ScopedSetSequenceTokenForCurrentThread | 
|  | scoped_set_sequence_token_for_current_thread(SequenceToken::Create()); | 
|  | EXPECT_FALSE(thread_checker->CalledOnValidThread()); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST(ThreadCheckerTest, DisallowedDifferentThreadsNoSequenceToken) { | 
|  | ThreadCheckerImpl thread_checker; | 
|  | RunCallbackOnNewThreadSynchronously( | 
|  | Bind(&ExpectNotCalledOnValidThread, Unretained(&thread_checker))); | 
|  | } | 
|  |  | 
|  | TEST(ThreadCheckerTest, DisallowedDifferentThreadsSameSequence) { | 
|  | ThreadTaskRunnerHandle thread_task_runner_handle( | 
|  | make_scoped_refptr(new TestSimpleTaskRunner)); | 
|  | const SequenceToken sequence_token(SequenceToken::Create()); | 
|  |  | 
|  | ScopedSetSequenceTokenForCurrentThread | 
|  | scoped_set_sequence_token_for_current_thread(sequence_token); | 
|  | ThreadCheckerImpl thread_checker; | 
|  | EXPECT_TRUE(thread_checker.CalledOnValidThread()); | 
|  |  | 
|  | RunCallbackOnNewThreadSynchronously(Bind( | 
|  | &ExpectNotCalledOnValidThreadWithSequenceTokenAndThreadTaskRunnerHandle, | 
|  | Unretained(&thread_checker), sequence_token)); | 
|  | } | 
|  |  | 
|  | TEST(ThreadCheckerTest, DisallowedSameThreadDifferentSequence) { | 
|  | std::unique_ptr<ThreadCheckerImpl> thread_checker; | 
|  |  | 
|  | ThreadTaskRunnerHandle thread_task_runner_handle( | 
|  | make_scoped_refptr(new TestSimpleTaskRunner)); | 
|  |  | 
|  | { | 
|  | ScopedSetSequenceTokenForCurrentThread | 
|  | scoped_set_sequence_token_for_current_thread(SequenceToken::Create()); | 
|  | thread_checker.reset(new ThreadCheckerImpl); | 
|  | } | 
|  |  | 
|  | { | 
|  | // Different SequenceToken. | 
|  | ScopedSetSequenceTokenForCurrentThread | 
|  | scoped_set_sequence_token_for_current_thread(SequenceToken::Create()); | 
|  | EXPECT_FALSE(thread_checker->CalledOnValidThread()); | 
|  | } | 
|  |  | 
|  | // No SequenceToken. | 
|  | EXPECT_FALSE(thread_checker->CalledOnValidThread()); | 
|  | } | 
|  |  | 
|  | TEST(ThreadCheckerTest, DetachFromThread) { | 
|  | ThreadCheckerImpl thread_checker; | 
|  | thread_checker.DetachFromThread(); | 
|  |  | 
|  | // Verify that CalledOnValidThread() returns true when called on a different | 
|  | // thread after a call to DetachFromThread(). | 
|  | RunCallbackOnNewThreadSynchronously( | 
|  | Bind(&ExpectCalledOnValidThread, Unretained(&thread_checker))); | 
|  |  | 
|  | EXPECT_FALSE(thread_checker.CalledOnValidThread()); | 
|  | } | 
|  |  | 
|  | TEST(ThreadCheckerTest, DetachFromThreadWithSequenceToken) { | 
|  | ThreadTaskRunnerHandle thread_task_runner_handle( | 
|  | make_scoped_refptr(new TestSimpleTaskRunner)); | 
|  | ScopedSetSequenceTokenForCurrentThread | 
|  | scoped_set_sequence_token_for_current_thread(SequenceToken::Create()); | 
|  | ThreadCheckerImpl thread_checker; | 
|  | thread_checker.DetachFromThread(); | 
|  |  | 
|  | // Verify that CalledOnValidThread() returns true when called on a different | 
|  | // thread after a call to DetachFromThread(). | 
|  | RunCallbackOnNewThreadSynchronously( | 
|  | Bind(&ExpectCalledOnValidThread, Unretained(&thread_checker))); | 
|  |  | 
|  | EXPECT_FALSE(thread_checker.CalledOnValidThread()); | 
|  | } | 
|  |  | 
|  | }  // namespace base |