Index: base/sequence_checker_unittest.cc |
diff --git a/base/sequence_checker_unittest.cc b/base/sequence_checker_unittest.cc |
index 4fc3027cf820f9563cd25e2c916fe0f65277c2c6..7df76149260a9ffd2bf23807f4c48d02b4389f8e 100644 |
--- a/base/sequence_checker_unittest.cc |
+++ b/base/sequence_checker_unittest.cc |
@@ -1,29 +1,339 @@ |
-// 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/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); |
+}; |
+ |
+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 { |
+ other_thread_.Stop(); |
+ pool()->Shutdown(); |
+ } |
+ |
+ 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( |
+ scoped_ptr<SequenceCheckedObject> sequence_checked_object) { |
+ other_thread()->message_loop()->DeleteSoon( |
+ FROM_HERE, |
+ sequence_checked_object.release()); |
+ } |
+ |
+ // 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 MethodOnDifferentThreadDeathTest(); |
+ void DetachThenCallFromDifferentThreadDeathTest(); |
+ void DifferentSequenceTokensDeathTest(); |
+ void WorkerPoolAndSimpleThreadDeathTest(); |
+ void TwoDifferentWorkerPoolsDeathTest(); |
+ |
+ private: |
+ MessageLoop message_loop_; // Needed by SequencedWorkerPool to function. |
+ base::Thread other_thread_; |
+ scoped_ptr<SequencedWorkerPoolOwner> pool_owner_; |
+}; |
+ |
+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(); |
+} |
+ |
+TEST_F(SequenceCheckerTest, SameSequenceTokenValid) { |
+ 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::MethodOnDifferentThreadDeathTest() { |
+ 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, MethodNotAllowedOnDifferentThreadDeathTestInDebug) { |
+ // The default style "fast" does not support multi-threaded tests. |
+ ::testing::FLAGS_gtest_death_test_style = "threadsafe"; |
+ ASSERT_DEATH({ |
+ MethodOnDifferentThreadDeathTest(); |
+ }, ""); |
+} |
+#else |
+TEST_F(SequenceCheckerTest, MethodAllowedOnDifferentThreadDeathTestInRelease) { |
+ MethodOnDifferentThreadDeathTest(); |
+} |
+#endif // ENABLE_SEQUENCE_CHECKER |
+ |
+void SequenceCheckerTest::DetachThenCallFromDifferentThreadDeathTest() { |
+ 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, DetachFromSequenceDeathTestInDebug) { |
+ // The default style "fast" does not support multi-threaded tests. |
+ ::testing::FLAGS_gtest_death_test_style = "threadsafe"; |
+ ASSERT_DEATH({ |
+ DetachThenCallFromDifferentThreadDeathTest(); |
+ }, ""); |
+} |
+#else |
+TEST_F(SequenceCheckerTest, DetachFromThreadDeathTestInRelease) { |
+ DetachThenCallFromDifferentThreadDeathTest(); |
+} |
+#endif // 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) { |
+ // The default style "fast" does not support multi-threaded tests. |
+ ::testing::FLAGS_gtest_death_test_style = "threadsafe"; |
+ 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) { |
+ // The default style "fast" does not support multi-threaded tests. |
+ ::testing::FLAGS_gtest_death_test_style = "threadsafe"; |
+ 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) { |
+ // The default style "fast" does not support multi-threaded tests. |
+ ::testing::FLAGS_gtest_death_test_style = "threadsafe"; |
+ ASSERT_DEATH({ |
+ TwoDifferentWorkerPoolsDeathTest(); |
+ }, ""); |
+} |
+#else |
+TEST_F(SequenceCheckerTest, |
+ TwoDifferentWorkerPoolsDeathTestInRelease) { |
+ TwoDifferentWorkerPoolsDeathTest(); |
+} |
+#endif // ENABLE_SEQUENCE_CHECKER |
+ |
+#endif // GTEST_HAS_DEATH_TEST || !ENABLE_SEQUENCE_CHECKER |
+ |
} // namespace |
} // namespace base |
+ |
+// Just in case we ever get lumped together with other compilation units. |
+#undef ENABLE_SEQUENCE_CHECKER |