Chromium Code Reviews| Index: base/sequence_checker_unittest.cc |
| diff --git a/base/sequence_checker_unittest.cc b/base/sequence_checker_unittest.cc |
| index 4fc3027cf820f9563cd25e2c916fe0f65277c2c6..0213e25fbfb4cc2e869c288df8a463f590c7a3c1 100644 |
| --- a/base/sequence_checker_unittest.cc |
| +++ b/base/sequence_checker_unittest.cc |
| @@ -1,29 +1,336 @@ |
| -// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| +// 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 <cstddef> |
| - |
| -#include "base/compiler_specific.h" |
| +#include "base/basictypes.h" |
| +#include "base/bind.h" |
| +#include "base/bind_helpers.h" |
| +#include "base/location.h" |
| +#include "base/logging.h" |
| #include "base/memory/ref_counted.h" |
| -#include "base/test/null_task_runner.h" |
| +#include "base/memory/scoped_ptr.h" |
| +#include "base/message_loop.h" |
| +#include "base/sequence_checker.h" |
| +#include "base/test/sequenced_worker_pool_owner.h" |
| +#include "base/threading/thread.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| +// Duplicated from base/sequence_checker.h so that we can be good citizens |
| +// there and undef the macro. |
| +#if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) |
| +#define ENABLE_SEQUENCE_CHECKER 1 |
| +#else |
| +#define ENABLE_SEQUENCE_CHECKER 0 |
| +#endif |
| + |
| namespace base { |
| namespace { |
| -// Call various methods of SequenceChecker to make sure nothing blows |
| -// up in either debug or release mode. |
| -TEST(SequenceCheckerTest, Basic) { |
| - SequenceChecker sequence_checker(new NullTaskRunner()); |
| - sequence_checker.CalledOnValidSequence(); |
| - sequence_checker.ChangeSequence(NULL); |
| - sequence_checker.CalledOnValidSequence(); |
| +const size_t kNumWorkerThreads = 3; |
| + |
| +// Simple class to exercise the basics of SequenceChecker. |
| +// DoStuff should verify that it's called on a valid sequenced thread. |
| +// SequenceCheckedObject can be destroyed on any thread (like WeakPtr). |
| +class SequenceCheckedObject { |
| + public: |
| + SequenceCheckedObject() {} |
| + ~SequenceCheckedObject() {} |
| + |
| + // Verifies that it was called on the same thread as the constructor. |
| + void DoStuff() { |
| + DCHECK(sequence_checker_.CalledOnValidSequencedThread()); |
| + } |
| + |
| + void DetachFromSequence() { |
| + sequence_checker_.DetachFromSequence(); |
| + } |
| + |
| + private: |
| + SequenceChecker sequence_checker_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(SequenceCheckedObject); |
| +}; |
| + |
| +void DeleteTask(scoped_ptr<SequenceCheckedObject> sequence_checked_object) { |
| + sequence_checked_object.reset(); |
| } |
| +class SequenceCheckerTest : public testing::Test { |
| + public: |
| + SequenceCheckerTest() : other_thread_("sequence_checker_test_other_thread") {} |
| + |
| + virtual ~SequenceCheckerTest() {} |
| + |
| + virtual void SetUp() OVERRIDE { |
| + other_thread_.Start(); |
| + ResetPool(); |
| + } |
| + |
| + virtual void TearDown() OVERRIDE { |
| + pool()->Shutdown(); |
|
akalin
2013/07/12 19:41:50
perhaps stop other_thread_ manually here
tommycli
2013/07/12 22:00:06
Done.
|
| + } |
| + |
| + protected: |
| + base::Thread* other_thread() { return &other_thread_; } |
| + |
| + const scoped_refptr<SequencedWorkerPool>& pool() { |
| + return pool_owner_->pool(); |
| + } |
| + |
| + void PostDoStuffToWorkerPool(SequenceCheckedObject* sequence_checked_object, |
| + const std::string& token_name) { |
| + pool()->PostNamedSequencedWorkerTask( |
| + token_name, |
| + FROM_HERE, |
| + base::Bind(&SequenceCheckedObject::DoStuff, |
| + base::Unretained(sequence_checked_object))); |
| + } |
| + |
| + void PostDoStuffToOtherThread( |
| + SequenceCheckedObject* sequence_checked_object) { |
| + other_thread()->message_loop()->PostTask( |
| + FROM_HERE, |
| + base::Bind(&SequenceCheckedObject::DoStuff, |
| + base::Unretained(sequence_checked_object))); |
| + } |
| + |
| + void PostDeleteToOtherThread( |
|
akalin
2013/07/12 19:41:50
you can remove DeleteTask and instead use MessageL
tommycli
2013/07/12 22:00:06
Done.
|
| + scoped_ptr<SequenceCheckedObject> sequence_checked_object) { |
| + other_thread()->message_loop()->PostTask( |
| + FROM_HERE, |
| + base::Bind(&DeleteTask, base::Passed(sequence_checked_object.Pass()))); |
| + } |
| + |
| + // Destroys the SequencedWorkerPool instance, blocking until it is fully shut |
| + // down, and creates a new instance. |
| + void ResetPool() { |
| + pool_owner_.reset(new SequencedWorkerPoolOwner(kNumWorkerThreads, "test")); |
| + } |
| + |
| + void MethodOnDifferentThread(); |
| + void DetachThenCallFromDifferentThread(); |
| + void DifferentSequenceTokensDeathTest(); |
| + void WorkerPoolAndSimpleThreadDeathTest(); |
| + void TwoDifferentWorkerPoolsDeathTest(); |
| + |
| + private: |
| + MessageLoop message_loop_; // Needed by SequencedWorkerPool to function. |
| + base::Thread other_thread_; |
| + scoped_ptr<SequencedWorkerPoolOwner> pool_owner_; |
| +}; |
| + |
| } // namespace |
| +TEST_F(SequenceCheckerTest, CallsAllowedOnSameThread) { |
| + scoped_ptr<SequenceCheckedObject> sequence_checked_object( |
| + new SequenceCheckedObject); |
| + |
| + // Verify that DoStuff doesn't assert. |
| + sequence_checked_object->DoStuff(); |
| + |
| + // Verify that the destructor doesn't assert. |
| + sequence_checked_object.reset(); |
| +} |
| + |
| +TEST_F(SequenceCheckerTest, DestructorAllowedOnDifferentThread) { |
| + scoped_ptr<SequenceCheckedObject> sequence_checked_object( |
| + new SequenceCheckedObject); |
| + |
| + // Verify the destructor doesn't assert when called on a different thread. |
| + PostDeleteToOtherThread(sequence_checked_object.Pass()); |
| + other_thread()->Stop(); |
| +} |
| + |
| +TEST_F(SequenceCheckerTest, DetachFromSequence) { |
| + scoped_ptr<SequenceCheckedObject> sequence_checked_object( |
| + new SequenceCheckedObject); |
| + |
| + // Verify that DoStuff doesn't assert when called on a different thread after |
| + // a call to DetachFromSequence. |
| + sequence_checked_object->DetachFromSequence(); |
| + |
| + PostDoStuffToOtherThread(sequence_checked_object.get()); |
| + other_thread()->Stop(); |
| +} |
| + |
| +#if GTEST_HAS_DEATH_TEST || !ENABLE_SEQUENCE_CHECKER |
| + |
| +void SequenceCheckerTest::MethodOnDifferentThread() { |
| + scoped_ptr<SequenceCheckedObject> sequence_checked_object( |
| + new SequenceCheckedObject); |
| + |
| + // DoStuff should assert in debug builds only when called on a |
| + // different thread. |
| + PostDoStuffToOtherThread(sequence_checked_object.get()); |
| + other_thread()->Stop(); |
| +} |
| + |
| +#if ENABLE_SEQUENCE_CHECKER |
| +TEST_F(SequenceCheckerTest, MethodNotAllowedOnDifferentThreadInDebug) { |
| + ASSERT_DEATH({ |
| + MethodOnDifferentThread(); |
| + }, ""); |
| +} |
| +#else |
| +TEST_F(SequenceCheckerTest, MethodAllowedOnDifferentThreadInRelease) { |
| + MethodOnDifferentThread(); |
| +} |
| +#endif // ENABLE_SEQUENCE_CHECKER |
| + |
| +void SequenceCheckerTest::DetachThenCallFromDifferentThread() { |
| + scoped_ptr<SequenceCheckedObject> sequence_checked_object( |
| + new SequenceCheckedObject); |
| + |
| + // DoStuff doesn't assert when called on a different thread |
| + // after a call to DetachFromSequence. |
| + sequence_checked_object->DetachFromSequence(); |
| + PostDoStuffToOtherThread(sequence_checked_object.get()); |
| + other_thread()->Stop(); |
| + |
| + // DoStuff should assert in debug builds only after moving to |
| + // another thread. |
| + sequence_checked_object->DoStuff(); |
| +} |
| + |
| +#if ENABLE_SEQUENCE_CHECKER |
| +TEST_F(SequenceCheckerTest, DetachFromSequenceInDebug) { |
| + ASSERT_DEATH({ |
| + DetachThenCallFromDifferentThread(); |
| + }, ""); |
| +} |
| +#else |
| +TEST_F(SequenceCheckerTest, DetachFromThreadInRelease) { |
| + DetachThenCallFromDifferentThread(); |
| +} |
| +#endif // ENABLE_SEQUENCE_CHECKER |
| + |
| +#endif // GTEST_HAS_DEATH_TEST || !ENABLE_SEQUENCE_CHECKER |
| + |
| +TEST_F(SequenceCheckerTest, SameSequenceTokenValid) { |
|
akalin
2013/07/12 19:41:50
can you lump all the death tests at the bottom and
tommycli
2013/07/12 22:00:06
Done.
|
| + scoped_ptr<SequenceCheckedObject> sequence_checked_object( |
| + new SequenceCheckedObject); |
| + |
| + sequence_checked_object->DetachFromSequence(); |
| + PostDoStuffToWorkerPool(sequence_checked_object.get(), "A"); |
| + PostDoStuffToWorkerPool(sequence_checked_object.get(), "A"); |
| + PostDoStuffToWorkerPool(sequence_checked_object.get(), "A"); |
| + PostDoStuffToWorkerPool(sequence_checked_object.get(), "A"); |
| + pool()->FlushForTesting(); |
| + |
| + PostDeleteToOtherThread(sequence_checked_object.Pass()); |
| + other_thread()->Stop(); |
| +} |
| + |
| +TEST_F(SequenceCheckerTest, DetachSequenceTokenValid) { |
| + scoped_ptr<SequenceCheckedObject> sequence_checked_object( |
| + new SequenceCheckedObject); |
| + |
| + sequence_checked_object->DetachFromSequence(); |
| + PostDoStuffToWorkerPool(sequence_checked_object.get(), "A"); |
| + PostDoStuffToWorkerPool(sequence_checked_object.get(), "A"); |
| + pool()->FlushForTesting(); |
| + |
| + sequence_checked_object->DetachFromSequence(); |
| + PostDoStuffToWorkerPool(sequence_checked_object.get(), "B"); |
| + PostDoStuffToWorkerPool(sequence_checked_object.get(), "B"); |
| + pool()->FlushForTesting(); |
| + |
| + PostDeleteToOtherThread(sequence_checked_object.Pass()); |
| + other_thread()->Stop(); |
| +} |
| + |
| +#if GTEST_HAS_DEATH_TEST || !ENABLE_SEQUENCE_CHECKER |
| + |
| +void SequenceCheckerTest::DifferentSequenceTokensDeathTest() { |
| + scoped_ptr<SequenceCheckedObject> sequence_checked_object( |
| + new SequenceCheckedObject); |
| + |
| + sequence_checked_object->DetachFromSequence(); |
| + PostDoStuffToWorkerPool(sequence_checked_object.get(), "A"); |
| + PostDoStuffToWorkerPool(sequence_checked_object.get(), "A"); |
| + PostDoStuffToWorkerPool(sequence_checked_object.get(), "B"); |
| + PostDoStuffToWorkerPool(sequence_checked_object.get(), "B"); |
| + pool()->FlushForTesting(); |
| + |
| + PostDeleteToOtherThread(sequence_checked_object.Pass()); |
| + other_thread()->Stop(); |
| +} |
| + |
| +#if ENABLE_SEQUENCE_CHECKER |
| +TEST_F(SequenceCheckerTest, DifferentSequenceTokensDeathTestInDebug) { |
| + ASSERT_DEATH({ |
| + DifferentSequenceTokensDeathTest(); |
| + }, ""); |
| +} |
| +#else |
| +TEST_F(SequenceCheckerTest, |
| + DifferentSequenceTokensDeathTestInRelease) { |
| + DifferentSequenceTokensDeathTest(); |
| +} |
| +#endif // ENABLE_SEQUENCE_CHECKER |
| + |
| +void SequenceCheckerTest::WorkerPoolAndSimpleThreadDeathTest() { |
| + scoped_ptr<SequenceCheckedObject> sequence_checked_object( |
| + new SequenceCheckedObject); |
| + |
| + sequence_checked_object->DetachFromSequence(); |
| + PostDoStuffToWorkerPool(sequence_checked_object.get(), "A"); |
| + PostDoStuffToWorkerPool(sequence_checked_object.get(), "A"); |
| + pool()->FlushForTesting(); |
| + |
| + PostDoStuffToOtherThread(sequence_checked_object.get()); |
| + other_thread()->Stop(); |
| +} |
| + |
| +#if ENABLE_SEQUENCE_CHECKER |
| +TEST_F(SequenceCheckerTest, WorkerPoolAndSimpleThreadDeathTestInDebug) { |
| + ASSERT_DEATH({ |
| + WorkerPoolAndSimpleThreadDeathTest(); |
| + }, ""); |
| +} |
| +#else |
| +TEST_F(SequenceCheckerTest, |
| + WorkerPoolAndSimpleThreadDeathTestInRelease) { |
| + WorkerPoolAndSimpleThreadDeathTest(); |
| +} |
| +#endif // ENABLE_SEQUENCE_CHECKER |
| + |
| +void SequenceCheckerTest::TwoDifferentWorkerPoolsDeathTest() { |
| + scoped_ptr<SequenceCheckedObject> sequence_checked_object( |
| + new SequenceCheckedObject); |
| + |
| + sequence_checked_object->DetachFromSequence(); |
| + PostDoStuffToWorkerPool(sequence_checked_object.get(), "A"); |
| + PostDoStuffToWorkerPool(sequence_checked_object.get(), "A"); |
| + pool()->FlushForTesting(); |
| + |
| + SequencedWorkerPoolOwner second_pool_owner(kNumWorkerThreads, "test2"); |
| + second_pool_owner.pool()->PostNamedSequencedWorkerTask( |
| + "A", |
| + FROM_HERE, |
| + base::Bind(&SequenceCheckedObject::DoStuff, |
| + base::Unretained(sequence_checked_object.get()))); |
| + second_pool_owner.pool()->FlushForTesting(); |
| + second_pool_owner.pool()->Shutdown(); |
| +} |
| + |
| +#if ENABLE_SEQUENCE_CHECKER |
| +TEST_F(SequenceCheckerTest, TwoDifferentWorkerPoolsDeathTestInDebug) { |
| + ASSERT_DEATH({ |
| + TwoDifferentWorkerPoolsDeathTest(); |
| + }, ""); |
| +} |
| +#else |
| +TEST_F(SequenceCheckerTest, |
| + TwoDifferentWorkerPoolsDeathTestInRelease) { |
| + TwoDifferentWorkerPoolsDeathTest(); |
| +} |
| +#endif // ENABLE_SEQUENCE_CHECKER |
| + |
| +#endif // GTEST_HAS_DEATH_TEST || !ENABLE_SEQUENCE_CHECKER |
| + |
| +// Just in case we ever get lumped together with other compilation units. |
| +#undef ENABLE_SEQUENCE_CHECKER |
| + |
| } // namespace base |