|  | // Copyright 2013 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.h" | 
|  |  | 
|  | #include <stddef.h> | 
|  |  | 
|  | #include <memory> | 
|  | #include <string> | 
|  |  | 
|  | #include "base/bind.h" | 
|  | #include "base/bind_helpers.h" | 
|  | #include "base/callback_forward.h" | 
|  | #include "base/macros.h" | 
|  | #include "base/sequence_token.h" | 
|  | #include "base/single_thread_task_runner.h" | 
|  | #include "base/test/gtest_util.h" | 
|  | #include "base/threading/simple_thread.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | namespace base { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // Runs a callback on another thread. | 
|  | class RunCallbackThread : public SimpleThread { | 
|  | public: | 
|  | explicit RunCallbackThread(const Closure& callback) | 
|  | : SimpleThread("RunCallbackThread"), callback_(callback) { | 
|  | Start(); | 
|  | Join(); | 
|  | } | 
|  |  | 
|  | private: | 
|  | // SimpleThread: | 
|  | void Run() override { callback_.Run(); } | 
|  |  | 
|  | const Closure callback_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(RunCallbackThread); | 
|  | }; | 
|  |  | 
|  | void ExpectCalledOnValidSequence(SequenceCheckerImpl* sequence_checker) { | 
|  | ASSERT_TRUE(sequence_checker); | 
|  |  | 
|  | // This should bind |sequence_checker| to the current sequence if it wasn't | 
|  | // already bound to a sequence. | 
|  | EXPECT_TRUE(sequence_checker->CalledOnValidSequence()); | 
|  |  | 
|  | // Since |sequence_checker| is now bound to the current sequence, another call | 
|  | // to CalledOnValidSequence() should return true. | 
|  | EXPECT_TRUE(sequence_checker->CalledOnValidSequence()); | 
|  | } | 
|  |  | 
|  | void ExpectCalledOnValidSequenceWithSequenceToken( | 
|  | SequenceCheckerImpl* sequence_checker, | 
|  | SequenceToken sequence_token) { | 
|  | ScopedSetSequenceTokenForCurrentThread | 
|  | scoped_set_sequence_token_for_current_thread(sequence_token); | 
|  | ExpectCalledOnValidSequence(sequence_checker); | 
|  | } | 
|  |  | 
|  | void ExpectNotCalledOnValidSequence(SequenceCheckerImpl* sequence_checker) { | 
|  | ASSERT_TRUE(sequence_checker); | 
|  | EXPECT_FALSE(sequence_checker->CalledOnValidSequence()); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | TEST(SequenceCheckerTest, CallsAllowedOnSameThreadNoSequenceToken) { | 
|  | SequenceCheckerImpl sequence_checker; | 
|  | EXPECT_TRUE(sequence_checker.CalledOnValidSequence()); | 
|  | } | 
|  |  | 
|  | TEST(SequenceCheckerTest, CallsAllowedOnSameThreadSameSequenceToken) { | 
|  | ScopedSetSequenceTokenForCurrentThread | 
|  | scoped_set_sequence_token_for_current_thread(SequenceToken::Create()); | 
|  | SequenceCheckerImpl sequence_checker; | 
|  | EXPECT_TRUE(sequence_checker.CalledOnValidSequence()); | 
|  | } | 
|  |  | 
|  | TEST(SequenceCheckerTest, CallsDisallowedOnDifferentThreadsNoSequenceToken) { | 
|  | SequenceCheckerImpl sequence_checker; | 
|  | RunCallbackThread thread( | 
|  | Bind(&ExpectNotCalledOnValidSequence, Unretained(&sequence_checker))); | 
|  | } | 
|  |  | 
|  | TEST(SequenceCheckerTest, CallsAllowedOnDifferentThreadsSameSequenceToken) { | 
|  | const SequenceToken sequence_token(SequenceToken::Create()); | 
|  |  | 
|  | ScopedSetSequenceTokenForCurrentThread | 
|  | scoped_set_sequence_token_for_current_thread(sequence_token); | 
|  | SequenceCheckerImpl sequence_checker; | 
|  | EXPECT_TRUE(sequence_checker.CalledOnValidSequence()); | 
|  |  | 
|  | RunCallbackThread thread(Bind(&ExpectCalledOnValidSequenceWithSequenceToken, | 
|  | Unretained(&sequence_checker), sequence_token)); | 
|  | } | 
|  |  | 
|  | TEST(SequenceCheckerTest, CallsDisallowedOnSameThreadDifferentSequenceToken) { | 
|  | std::unique_ptr<SequenceCheckerImpl> sequence_checker; | 
|  |  | 
|  | { | 
|  | ScopedSetSequenceTokenForCurrentThread | 
|  | scoped_set_sequence_token_for_current_thread(SequenceToken::Create()); | 
|  | sequence_checker.reset(new SequenceCheckerImpl); | 
|  | } | 
|  |  | 
|  | { | 
|  | // Different SequenceToken. | 
|  | ScopedSetSequenceTokenForCurrentThread | 
|  | scoped_set_sequence_token_for_current_thread(SequenceToken::Create()); | 
|  | EXPECT_FALSE(sequence_checker->CalledOnValidSequence()); | 
|  | } | 
|  |  | 
|  | // No SequenceToken. | 
|  | EXPECT_FALSE(sequence_checker->CalledOnValidSequence()); | 
|  | } | 
|  |  | 
|  | TEST(SequenceCheckerTest, DetachFromSequence) { | 
|  | std::unique_ptr<SequenceCheckerImpl> sequence_checker; | 
|  |  | 
|  | { | 
|  | ScopedSetSequenceTokenForCurrentThread | 
|  | scoped_set_sequence_token_for_current_thread(SequenceToken::Create()); | 
|  | sequence_checker.reset(new SequenceCheckerImpl); | 
|  | } | 
|  |  | 
|  | sequence_checker->DetachFromSequence(); | 
|  |  | 
|  | { | 
|  | // Verify that CalledOnValidSequence() returns true when called with | 
|  | // a different sequence token after a call to DetachFromSequence(). | 
|  | ScopedSetSequenceTokenForCurrentThread | 
|  | scoped_set_sequence_token_for_current_thread(SequenceToken::Create()); | 
|  | EXPECT_TRUE(sequence_checker->CalledOnValidSequence()); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST(SequenceCheckerTest, DetachFromSequenceNoSequenceToken) { | 
|  | SequenceCheckerImpl sequence_checker; | 
|  | sequence_checker.DetachFromSequence(); | 
|  |  | 
|  | // Verify that CalledOnValidSequence() returns true when called on a | 
|  | // different thread after a call to DetachFromSequence(). | 
|  | RunCallbackThread thread( | 
|  | Bind(&ExpectCalledOnValidSequence, Unretained(&sequence_checker))); | 
|  |  | 
|  | EXPECT_FALSE(sequence_checker.CalledOnValidSequence()); | 
|  | } | 
|  |  | 
|  | TEST(SequenceCheckerMacroTest, Macros) { | 
|  | auto scope = std::make_unique<ScopedSetSequenceTokenForCurrentThread>( | 
|  | SequenceToken::Create()); | 
|  | SEQUENCE_CHECKER(my_sequence_checker); | 
|  |  | 
|  | // Don't expect a DCHECK death when a SequenceChecker is used on the right | 
|  | // sequence. | 
|  | DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker) << "Error message."; | 
|  |  | 
|  | scope.reset(); | 
|  |  | 
|  | #if DCHECK_IS_ON() | 
|  | // Expect DCHECK death when used on a different sequence. | 
|  | EXPECT_DCHECK_DEATH({ | 
|  | DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker) << "Error message."; | 
|  | }); | 
|  | #else | 
|  | // Happily no-ops on non-dcheck builds. | 
|  | DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker) << "Error message."; | 
|  | #endif | 
|  |  | 
|  | DETACH_FROM_SEQUENCE(my_sequence_checker); | 
|  |  | 
|  | // Don't expect a DCHECK death when a SequenceChecker is used for the first | 
|  | // time after having been detached. | 
|  | DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker) << "Error message."; | 
|  | } | 
|  |  | 
|  | }  // namespace base |