Home | History | Annotate | Download | only in base
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "base/basictypes.h"
      6 #include "base/bind.h"
      7 #include "base/bind_helpers.h"
      8 #include "base/location.h"
      9 #include "base/logging.h"
     10 #include "base/memory/ref_counted.h"
     11 #include "base/memory/scoped_ptr.h"
     12 #include "base/message_loop/message_loop.h"
     13 #include "base/sequence_checker.h"
     14 #include "base/test/sequenced_worker_pool_owner.h"
     15 #include "base/threading/thread.h"
     16 #include "testing/gtest/include/gtest/gtest.h"
     17 
     18 // Duplicated from base/sequence_checker.h so that we can be good citizens
     19 // there and undef the macro.
     20 #if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON))
     21 #define ENABLE_SEQUENCE_CHECKER 1
     22 #else
     23 #define ENABLE_SEQUENCE_CHECKER 0
     24 #endif
     25 
     26 namespace base {
     27 
     28 namespace {
     29 
     30 const size_t kNumWorkerThreads = 3;
     31 
     32 // Simple class to exercise the basics of SequenceChecker.
     33 // DoStuff should verify that it's called on a valid sequenced thread.
     34 // SequenceCheckedObject can be destroyed on any thread (like WeakPtr).
     35 class SequenceCheckedObject {
     36  public:
     37   SequenceCheckedObject() {}
     38   ~SequenceCheckedObject() {}
     39 
     40   // Verifies that it was called on the same thread as the constructor.
     41   void DoStuff() {
     42     DCHECK(sequence_checker_.CalledOnValidSequencedThread());
     43   }
     44 
     45   void DetachFromSequence() {
     46     sequence_checker_.DetachFromSequence();
     47   }
     48 
     49  private:
     50   SequenceChecker sequence_checker_;
     51 
     52   DISALLOW_COPY_AND_ASSIGN(SequenceCheckedObject);
     53 };
     54 
     55 class SequenceCheckerTest : public testing::Test {
     56  public:
     57   SequenceCheckerTest() : other_thread_("sequence_checker_test_other_thread") {}
     58 
     59   virtual ~SequenceCheckerTest() {}
     60 
     61   virtual void SetUp() OVERRIDE {
     62     other_thread_.Start();
     63     ResetPool();
     64   }
     65 
     66   virtual void TearDown() OVERRIDE {
     67     other_thread_.Stop();
     68     pool()->Shutdown();
     69   }
     70 
     71  protected:
     72   base::Thread* other_thread() { return &other_thread_; }
     73 
     74   const scoped_refptr<SequencedWorkerPool>& pool() {
     75     return pool_owner_->pool();
     76   }
     77 
     78   void PostDoStuffToWorkerPool(SequenceCheckedObject* sequence_checked_object,
     79                                const std::string& token_name) {
     80     pool()->PostNamedSequencedWorkerTask(
     81         token_name,
     82         FROM_HERE,
     83         base::Bind(&SequenceCheckedObject::DoStuff,
     84                    base::Unretained(sequence_checked_object)));
     85   }
     86 
     87   void PostDoStuffToOtherThread(
     88       SequenceCheckedObject* sequence_checked_object) {
     89     other_thread()->message_loop()->PostTask(
     90         FROM_HERE,
     91         base::Bind(&SequenceCheckedObject::DoStuff,
     92                    base::Unretained(sequence_checked_object)));
     93   }
     94 
     95   void PostDeleteToOtherThread(
     96       scoped_ptr<SequenceCheckedObject> sequence_checked_object) {
     97     other_thread()->message_loop()->DeleteSoon(
     98         FROM_HERE,
     99         sequence_checked_object.release());
    100   }
    101 
    102   // Destroys the SequencedWorkerPool instance, blocking until it is fully shut
    103   // down, and creates a new instance.
    104   void ResetPool() {
    105     pool_owner_.reset(new SequencedWorkerPoolOwner(kNumWorkerThreads, "test"));
    106   }
    107 
    108   void MethodOnDifferentThreadDeathTest();
    109   void DetachThenCallFromDifferentThreadDeathTest();
    110   void DifferentSequenceTokensDeathTest();
    111   void WorkerPoolAndSimpleThreadDeathTest();
    112   void TwoDifferentWorkerPoolsDeathTest();
    113 
    114  private:
    115   MessageLoop message_loop_;  // Needed by SequencedWorkerPool to function.
    116   base::Thread other_thread_;
    117   scoped_ptr<SequencedWorkerPoolOwner> pool_owner_;
    118 };
    119 
    120 TEST_F(SequenceCheckerTest, CallsAllowedOnSameThread) {
    121   scoped_ptr<SequenceCheckedObject> sequence_checked_object(
    122       new SequenceCheckedObject);
    123 
    124   // Verify that DoStuff doesn't assert.
    125   sequence_checked_object->DoStuff();
    126 
    127   // Verify that the destructor doesn't assert.
    128   sequence_checked_object.reset();
    129 }
    130 
    131 TEST_F(SequenceCheckerTest, DestructorAllowedOnDifferentThread) {
    132   scoped_ptr<SequenceCheckedObject> sequence_checked_object(
    133       new SequenceCheckedObject);
    134 
    135   // Verify the destructor doesn't assert when called on a different thread.
    136   PostDeleteToOtherThread(sequence_checked_object.Pass());
    137   other_thread()->Stop();
    138 }
    139 
    140 TEST_F(SequenceCheckerTest, DetachFromSequence) {
    141   scoped_ptr<SequenceCheckedObject> sequence_checked_object(
    142       new SequenceCheckedObject);
    143 
    144   // Verify that DoStuff doesn't assert when called on a different thread after
    145   // a call to DetachFromSequence.
    146   sequence_checked_object->DetachFromSequence();
    147 
    148   PostDoStuffToOtherThread(sequence_checked_object.get());
    149   other_thread()->Stop();
    150 }
    151 
    152 TEST_F(SequenceCheckerTest, SameSequenceTokenValid) {
    153   scoped_ptr<SequenceCheckedObject> sequence_checked_object(
    154       new SequenceCheckedObject);
    155 
    156   sequence_checked_object->DetachFromSequence();
    157   PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
    158   PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
    159   PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
    160   PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
    161   pool()->FlushForTesting();
    162 
    163   PostDeleteToOtherThread(sequence_checked_object.Pass());
    164   other_thread()->Stop();
    165 }
    166 
    167 TEST_F(SequenceCheckerTest, DetachSequenceTokenValid) {
    168   scoped_ptr<SequenceCheckedObject> sequence_checked_object(
    169       new SequenceCheckedObject);
    170 
    171   sequence_checked_object->DetachFromSequence();
    172   PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
    173   PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
    174   pool()->FlushForTesting();
    175 
    176   sequence_checked_object->DetachFromSequence();
    177   PostDoStuffToWorkerPool(sequence_checked_object.get(), "B");
    178   PostDoStuffToWorkerPool(sequence_checked_object.get(), "B");
    179   pool()->FlushForTesting();
    180 
    181   PostDeleteToOtherThread(sequence_checked_object.Pass());
    182   other_thread()->Stop();
    183 }
    184 
    185 #if GTEST_HAS_DEATH_TEST || !ENABLE_SEQUENCE_CHECKER
    186 
    187 void SequenceCheckerTest::MethodOnDifferentThreadDeathTest() {
    188   scoped_ptr<SequenceCheckedObject> sequence_checked_object(
    189       new SequenceCheckedObject);
    190 
    191   // DoStuff should assert in debug builds only when called on a
    192   // different thread.
    193   PostDoStuffToOtherThread(sequence_checked_object.get());
    194   other_thread()->Stop();
    195 }
    196 
    197 #if ENABLE_SEQUENCE_CHECKER
    198 TEST_F(SequenceCheckerTest, MethodNotAllowedOnDifferentThreadDeathTestInDebug) {
    199   // The default style "fast" does not support multi-threaded tests.
    200   ::testing::FLAGS_gtest_death_test_style = "threadsafe";
    201   ASSERT_DEATH({
    202     MethodOnDifferentThreadDeathTest();
    203   }, "");
    204 }
    205 #else
    206 TEST_F(SequenceCheckerTest, MethodAllowedOnDifferentThreadDeathTestInRelease) {
    207   MethodOnDifferentThreadDeathTest();
    208 }
    209 #endif  // ENABLE_SEQUENCE_CHECKER
    210 
    211 void SequenceCheckerTest::DetachThenCallFromDifferentThreadDeathTest() {
    212   scoped_ptr<SequenceCheckedObject> sequence_checked_object(
    213       new SequenceCheckedObject);
    214 
    215   // DoStuff doesn't assert when called on a different thread
    216   // after a call to DetachFromSequence.
    217   sequence_checked_object->DetachFromSequence();
    218   PostDoStuffToOtherThread(sequence_checked_object.get());
    219   other_thread()->Stop();
    220 
    221   // DoStuff should assert in debug builds only after moving to
    222   // another thread.
    223   sequence_checked_object->DoStuff();
    224 }
    225 
    226 #if ENABLE_SEQUENCE_CHECKER
    227 TEST_F(SequenceCheckerTest, DetachFromSequenceDeathTestInDebug) {
    228   // The default style "fast" does not support multi-threaded tests.
    229   ::testing::FLAGS_gtest_death_test_style = "threadsafe";
    230   ASSERT_DEATH({
    231     DetachThenCallFromDifferentThreadDeathTest();
    232   }, "");
    233 }
    234 #else
    235 TEST_F(SequenceCheckerTest, DetachFromThreadDeathTestInRelease) {
    236   DetachThenCallFromDifferentThreadDeathTest();
    237 }
    238 #endif  // ENABLE_SEQUENCE_CHECKER
    239 
    240 void SequenceCheckerTest::DifferentSequenceTokensDeathTest() {
    241   scoped_ptr<SequenceCheckedObject> sequence_checked_object(
    242       new SequenceCheckedObject);
    243 
    244   sequence_checked_object->DetachFromSequence();
    245   PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
    246   PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
    247   PostDoStuffToWorkerPool(sequence_checked_object.get(), "B");
    248   PostDoStuffToWorkerPool(sequence_checked_object.get(), "B");
    249   pool()->FlushForTesting();
    250 
    251   PostDeleteToOtherThread(sequence_checked_object.Pass());
    252   other_thread()->Stop();
    253 }
    254 
    255 #if ENABLE_SEQUENCE_CHECKER
    256 TEST_F(SequenceCheckerTest, DifferentSequenceTokensDeathTestInDebug) {
    257   // The default style "fast" does not support multi-threaded tests.
    258   ::testing::FLAGS_gtest_death_test_style = "threadsafe";
    259   ASSERT_DEATH({
    260     DifferentSequenceTokensDeathTest();
    261   }, "");
    262 }
    263 #else
    264 TEST_F(SequenceCheckerTest,
    265      DifferentSequenceTokensDeathTestInRelease) {
    266   DifferentSequenceTokensDeathTest();
    267 }
    268 #endif  // ENABLE_SEQUENCE_CHECKER
    269 
    270 void SequenceCheckerTest::WorkerPoolAndSimpleThreadDeathTest() {
    271   scoped_ptr<SequenceCheckedObject> sequence_checked_object(
    272       new SequenceCheckedObject);
    273 
    274   sequence_checked_object->DetachFromSequence();
    275   PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
    276   PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
    277   pool()->FlushForTesting();
    278 
    279   PostDoStuffToOtherThread(sequence_checked_object.get());
    280   other_thread()->Stop();
    281 }
    282 
    283 #if ENABLE_SEQUENCE_CHECKER
    284 TEST_F(SequenceCheckerTest, WorkerPoolAndSimpleThreadDeathTestInDebug) {
    285   // The default style "fast" does not support multi-threaded tests.
    286   ::testing::FLAGS_gtest_death_test_style = "threadsafe";
    287   ASSERT_DEATH({
    288     WorkerPoolAndSimpleThreadDeathTest();
    289   }, "");
    290 }
    291 #else
    292 TEST_F(SequenceCheckerTest,
    293      WorkerPoolAndSimpleThreadDeathTestInRelease) {
    294   WorkerPoolAndSimpleThreadDeathTest();
    295 }
    296 #endif  // ENABLE_SEQUENCE_CHECKER
    297 
    298 void SequenceCheckerTest::TwoDifferentWorkerPoolsDeathTest() {
    299   scoped_ptr<SequenceCheckedObject> sequence_checked_object(
    300       new SequenceCheckedObject);
    301 
    302   sequence_checked_object->DetachFromSequence();
    303   PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
    304   PostDoStuffToWorkerPool(sequence_checked_object.get(), "A");
    305   pool()->FlushForTesting();
    306 
    307   SequencedWorkerPoolOwner second_pool_owner(kNumWorkerThreads, "test2");
    308   second_pool_owner.pool()->PostNamedSequencedWorkerTask(
    309       "A",
    310       FROM_HERE,
    311       base::Bind(&SequenceCheckedObject::DoStuff,
    312                  base::Unretained(sequence_checked_object.get())));
    313   second_pool_owner.pool()->FlushForTesting();
    314   second_pool_owner.pool()->Shutdown();
    315 }
    316 
    317 #if ENABLE_SEQUENCE_CHECKER
    318 TEST_F(SequenceCheckerTest, TwoDifferentWorkerPoolsDeathTestInDebug) {
    319   // The default style "fast" does not support multi-threaded tests.
    320   ::testing::FLAGS_gtest_death_test_style = "threadsafe";
    321   ASSERT_DEATH({
    322     TwoDifferentWorkerPoolsDeathTest();
    323   }, "");
    324 }
    325 #else
    326 TEST_F(SequenceCheckerTest,
    327      TwoDifferentWorkerPoolsDeathTestInRelease) {
    328   TwoDifferentWorkerPoolsDeathTest();
    329 }
    330 #endif  // ENABLE_SEQUENCE_CHECKER
    331 
    332 #endif  // GTEST_HAS_DEATH_TEST || !ENABLE_SEQUENCE_CHECKER
    333 
    334 }  // namespace
    335 
    336 }  // namespace base
    337 
    338 // Just in case we ever get lumped together with other compilation units.
    339 #undef ENABLE_SEQUENCE_CHECKER
    340