Home | History | Annotate | Download | only in base
      1 /*
      2  *  Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
      3  *
      4  *  Use of this source code is governed by a BSD-style license
      5  *  that can be found in the LICENSE file in the root of the source
      6  *  tree. An additional intellectual property rights grant can be found
      7  *  in the file PATENTS.  All contributing project authors may
      8  *  be found in the AUTHORS file in the root of the source tree.
      9  */
     10 
     11 // Borrowed from Chromium's src/base/threading/thread_checker_unittest.cc.
     12 
     13 #include "testing/gtest/include/gtest/gtest.h"
     14 #include "webrtc/base/checks.h"
     15 #include "webrtc/base/thread.h"
     16 #include "webrtc/base/thread_checker.h"
     17 #include "webrtc/base/scoped_ptr.h"
     18 
     19 // Duplicated from base/threading/thread_checker.h so that we can be
     20 // good citizens there and undef the macro.
     21 #if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
     22 #define ENABLE_THREAD_CHECKER 1
     23 #else
     24 #define ENABLE_THREAD_CHECKER 0
     25 #endif
     26 
     27 namespace rtc {
     28 
     29 namespace {
     30 
     31 // Simple class to exercise the basics of ThreadChecker.
     32 // Both the destructor and DoStuff should verify that they were
     33 // called on the same thread as the constructor.
     34 class ThreadCheckerClass : public ThreadChecker {
     35  public:
     36   ThreadCheckerClass() {}
     37 
     38   // Verifies that it was called on the same thread as the constructor.
     39   void DoStuff() { RTC_DCHECK(CalledOnValidThread()); }
     40 
     41   void DetachFromThread() {
     42     ThreadChecker::DetachFromThread();
     43   }
     44 
     45   static void MethodOnDifferentThreadImpl();
     46   static void DetachThenCallFromDifferentThreadImpl();
     47 
     48  private:
     49   RTC_DISALLOW_COPY_AND_ASSIGN(ThreadCheckerClass);
     50 };
     51 
     52 // Calls ThreadCheckerClass::DoStuff on another thread.
     53 class CallDoStuffOnThread : public Thread {
     54  public:
     55   explicit CallDoStuffOnThread(ThreadCheckerClass* thread_checker_class)
     56       : Thread(),
     57         thread_checker_class_(thread_checker_class) {
     58     SetName("call_do_stuff_on_thread", NULL);
     59   }
     60 
     61   void Run() override { thread_checker_class_->DoStuff(); }
     62 
     63   // New method. Needed since Thread::Join is protected, and it is called by
     64   // the TEST.
     65   void Join() {
     66     Thread::Join();
     67   }
     68 
     69  private:
     70   ThreadCheckerClass* thread_checker_class_;
     71 
     72   RTC_DISALLOW_COPY_AND_ASSIGN(CallDoStuffOnThread);
     73 };
     74 
     75 // Deletes ThreadCheckerClass on a different thread.
     76 class DeleteThreadCheckerClassOnThread : public Thread {
     77  public:
     78   explicit DeleteThreadCheckerClassOnThread(
     79       ThreadCheckerClass* thread_checker_class)
     80       : Thread(),
     81         thread_checker_class_(thread_checker_class) {
     82     SetName("delete_thread_checker_class_on_thread", NULL);
     83   }
     84 
     85   void Run() override { thread_checker_class_.reset(); }
     86 
     87   // New method. Needed since Thread::Join is protected, and it is called by
     88   // the TEST.
     89   void Join() {
     90     Thread::Join();
     91   }
     92 
     93  private:
     94   scoped_ptr<ThreadCheckerClass> thread_checker_class_;
     95 
     96   RTC_DISALLOW_COPY_AND_ASSIGN(DeleteThreadCheckerClassOnThread);
     97 };
     98 
     99 }  // namespace
    100 
    101 TEST(ThreadCheckerTest, CallsAllowedOnSameThread) {
    102   scoped_ptr<ThreadCheckerClass> thread_checker_class(
    103       new ThreadCheckerClass);
    104 
    105   // Verify that DoStuff doesn't assert.
    106   thread_checker_class->DoStuff();
    107 
    108   // Verify that the destructor doesn't assert.
    109   thread_checker_class.reset();
    110 }
    111 
    112 TEST(ThreadCheckerTest, DestructorAllowedOnDifferentThread) {
    113   scoped_ptr<ThreadCheckerClass> thread_checker_class(
    114       new ThreadCheckerClass);
    115 
    116   // Verify that the destructor doesn't assert
    117   // when called on a different thread.
    118   DeleteThreadCheckerClassOnThread delete_on_thread(
    119       thread_checker_class.release());
    120 
    121   delete_on_thread.Start();
    122   delete_on_thread.Join();
    123 }
    124 
    125 TEST(ThreadCheckerTest, DetachFromThread) {
    126   scoped_ptr<ThreadCheckerClass> thread_checker_class(
    127       new ThreadCheckerClass);
    128 
    129   // Verify that DoStuff doesn't assert when called on a different thread after
    130   // a call to DetachFromThread.
    131   thread_checker_class->DetachFromThread();
    132   CallDoStuffOnThread call_on_thread(thread_checker_class.get());
    133 
    134   call_on_thread.Start();
    135   call_on_thread.Join();
    136 }
    137 
    138 #if GTEST_HAS_DEATH_TEST || !ENABLE_THREAD_CHECKER
    139 
    140 void ThreadCheckerClass::MethodOnDifferentThreadImpl() {
    141   scoped_ptr<ThreadCheckerClass> thread_checker_class(
    142       new ThreadCheckerClass);
    143 
    144   // DoStuff should assert in debug builds only when called on a
    145   // different thread.
    146   CallDoStuffOnThread call_on_thread(thread_checker_class.get());
    147 
    148   call_on_thread.Start();
    149   call_on_thread.Join();
    150 }
    151 
    152 #if ENABLE_THREAD_CHECKER
    153 TEST(ThreadCheckerDeathTest, MethodNotAllowedOnDifferentThreadInDebug) {
    154   ASSERT_DEATH({
    155       ThreadCheckerClass::MethodOnDifferentThreadImpl();
    156     }, "");
    157 }
    158 #else
    159 TEST(ThreadCheckerTest, MethodAllowedOnDifferentThreadInRelease) {
    160   ThreadCheckerClass::MethodOnDifferentThreadImpl();
    161 }
    162 #endif  // ENABLE_THREAD_CHECKER
    163 
    164 void ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl() {
    165   scoped_ptr<ThreadCheckerClass> thread_checker_class(
    166       new ThreadCheckerClass);
    167 
    168   // DoStuff doesn't assert when called on a different thread
    169   // after a call to DetachFromThread.
    170   thread_checker_class->DetachFromThread();
    171   CallDoStuffOnThread call_on_thread(thread_checker_class.get());
    172 
    173   call_on_thread.Start();
    174   call_on_thread.Join();
    175 
    176   // DoStuff should assert in debug builds only after moving to
    177   // another thread.
    178   thread_checker_class->DoStuff();
    179 }
    180 
    181 #if ENABLE_THREAD_CHECKER
    182 TEST(ThreadCheckerDeathTest, DetachFromThreadInDebug) {
    183   ASSERT_DEATH({
    184     ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl();
    185     }, "");
    186 }
    187 #else
    188 TEST(ThreadCheckerTest, DetachFromThreadInRelease) {
    189   ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl();
    190 }
    191 #endif  // ENABLE_THREAD_CHECKER
    192 
    193 #endif  // GTEST_HAS_DEATH_TEST || !ENABLE_THREAD_CHECKER
    194 
    195 // Just in case we ever get lumped together with other compilation units.
    196 #undef ENABLE_THREAD_CHECKER
    197 
    198 }  // namespace rtc
    199