| // 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/threading/thread_checker.h" | 
 |  | 
 | #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/gtest_util.h" | 
 | #include "base/test/test_simple_task_runner.h" | 
 | #include "base/threading/simple_thread.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( | 
 |       MakeRefCounted<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( | 
 |       MakeRefCounted<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( | 
 |       MakeRefCounted<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( | 
 |       MakeRefCounted<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( | 
 |       MakeRefCounted<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 { | 
 |  | 
 | // This fixture is a helper for unit testing the thread checker macros as it is | 
 | // not possible to inline ExpectDeathOnOtherThread() and | 
 | // ExpectNoDeathOnOtherThreadAfterDetach() as lambdas since binding | 
 | // |Unretained(&my_sequence_checker)| wouldn't compile on non-dcheck builds | 
 | // where it won't be defined. | 
 | class ThreadCheckerMacroTest : public testing::Test { | 
 |  public: | 
 |   ThreadCheckerMacroTest() = default; | 
 |  | 
 |   void ExpectDeathOnOtherThread() { | 
 | #if DCHECK_IS_ON() | 
 |     EXPECT_DCHECK_DEATH({ DCHECK_CALLED_ON_VALID_THREAD(my_thread_checker_); }); | 
 | #else | 
 |     // Happily no-ops on non-dcheck builds. | 
 |     DCHECK_CALLED_ON_VALID_THREAD(my_thread_checker_); | 
 | #endif | 
 |   } | 
 |  | 
 |   void ExpectNoDeathOnOtherThreadAfterDetach() { | 
 |     DCHECK_CALLED_ON_VALID_THREAD(my_thread_checker_); | 
 |     DCHECK_CALLED_ON_VALID_THREAD(my_thread_checker_) | 
 |         << "Make sure it compiles when DCHECK is off"; | 
 |   } | 
 |  | 
 |  protected: | 
 |   THREAD_CHECKER(my_thread_checker_); | 
 |  | 
 |  private: | 
 |   DISALLOW_COPY_AND_ASSIGN(ThreadCheckerMacroTest); | 
 | }; | 
 |  | 
 | }  // namespace | 
 |  | 
 | TEST_F(ThreadCheckerMacroTest, Macros) { | 
 |   THREAD_CHECKER(my_thread_checker); | 
 |  | 
 |   RunCallbackOnNewThreadSynchronously(Bind( | 
 |       &ThreadCheckerMacroTest::ExpectDeathOnOtherThread, Unretained(this))); | 
 |  | 
 |   DETACH_FROM_THREAD(my_thread_checker_); | 
 |  | 
 |   RunCallbackOnNewThreadSynchronously( | 
 |       Bind(&ThreadCheckerMacroTest::ExpectNoDeathOnOtherThreadAfterDetach, | 
 |            Unretained(this))); | 
 | } | 
 |  | 
 | }  // namespace base |