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#include "webrtc/test/testsupport/gtest_disable.h"
19
20// Duplicated from base/threading/thread_checker.h so that we can be
21// good citizens there and undef the macro.
22#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
23#define ENABLE_THREAD_CHECKER 1
24#else
25#define ENABLE_THREAD_CHECKER 0
26#endif
27
28namespace rtc {
29
30namespace {
31
32// Simple class to exercise the basics of ThreadChecker.
33// Both the destructor and DoStuff should verify that they were
34// called on the same thread as the constructor.
35class ThreadCheckerClass : public ThreadChecker {
36 public:
37  ThreadCheckerClass() {}
38
39  // Verifies that it was called on the same thread as the constructor.
40  void DoStuff() {
41    DCHECK(CalledOnValidThread());
42  }
43
44  void DetachFromThread() {
45    ThreadChecker::DetachFromThread();
46  }
47
48  static void MethodOnDifferentThreadImpl();
49  static void DetachThenCallFromDifferentThreadImpl();
50
51 private:
52  DISALLOW_COPY_AND_ASSIGN(ThreadCheckerClass);
53};
54
55// Calls ThreadCheckerClass::DoStuff on another thread.
56class CallDoStuffOnThread : public Thread {
57 public:
58  explicit CallDoStuffOnThread(ThreadCheckerClass* thread_checker_class)
59      : Thread(),
60        thread_checker_class_(thread_checker_class) {
61    SetName("call_do_stuff_on_thread", NULL);
62  }
63
64  virtual void Run() OVERRIDE {
65    thread_checker_class_->DoStuff();
66  }
67
68  // New method. Needed since Thread::Join is protected, and it is called by
69  // the TEST.
70  void Join() {
71    Thread::Join();
72  }
73
74 private:
75  ThreadCheckerClass* thread_checker_class_;
76
77  DISALLOW_COPY_AND_ASSIGN(CallDoStuffOnThread);
78};
79
80// Deletes ThreadCheckerClass on a different thread.
81class DeleteThreadCheckerClassOnThread : public Thread {
82 public:
83  explicit DeleteThreadCheckerClassOnThread(
84      ThreadCheckerClass* thread_checker_class)
85      : Thread(),
86        thread_checker_class_(thread_checker_class) {
87    SetName("delete_thread_checker_class_on_thread", NULL);
88  }
89
90  virtual void Run() OVERRIDE {
91    thread_checker_class_.reset();
92  }
93
94  // New method. Needed since Thread::Join is protected, and it is called by
95  // the TEST.
96  void Join() {
97    Thread::Join();
98  }
99
100 private:
101  scoped_ptr<ThreadCheckerClass> thread_checker_class_;
102
103  DISALLOW_COPY_AND_ASSIGN(DeleteThreadCheckerClassOnThread);
104};
105
106}  // namespace
107
108TEST(ThreadCheckerTest, DISABLED_ON_MAC(CallsAllowedOnSameThread)) {
109  scoped_ptr<ThreadCheckerClass> thread_checker_class(
110      new ThreadCheckerClass);
111
112  // Verify that DoStuff doesn't assert.
113  thread_checker_class->DoStuff();
114
115  // Verify that the destructor doesn't assert.
116  thread_checker_class.reset();
117}
118
119TEST(ThreadCheckerTest, DISABLED_ON_MAC(DestructorAllowedOnDifferentThread)) {
120  scoped_ptr<ThreadCheckerClass> thread_checker_class(
121      new ThreadCheckerClass);
122
123  // Verify that the destructor doesn't assert
124  // when called on a different thread.
125  DeleteThreadCheckerClassOnThread delete_on_thread(
126      thread_checker_class.release());
127
128  delete_on_thread.Start();
129  delete_on_thread.Join();
130}
131
132TEST(ThreadCheckerTest, DISABLED_ON_MAC(DetachFromThread)) {
133  scoped_ptr<ThreadCheckerClass> thread_checker_class(
134      new ThreadCheckerClass);
135
136  // Verify that DoStuff doesn't assert when called on a different thread after
137  // a call to DetachFromThread.
138  thread_checker_class->DetachFromThread();
139  CallDoStuffOnThread call_on_thread(thread_checker_class.get());
140
141  call_on_thread.Start();
142  call_on_thread.Join();
143}
144
145#if GTEST_HAS_DEATH_TEST || !ENABLE_THREAD_CHECKER
146
147void ThreadCheckerClass::MethodOnDifferentThreadImpl() {
148  scoped_ptr<ThreadCheckerClass> thread_checker_class(
149      new ThreadCheckerClass);
150
151  // DoStuff should assert in debug builds only when called on a
152  // different thread.
153  CallDoStuffOnThread call_on_thread(thread_checker_class.get());
154
155  call_on_thread.Start();
156  call_on_thread.Join();
157}
158
159#if ENABLE_THREAD_CHECKER
160TEST(ThreadCheckerDeathTest,
161     DISABLED_MethodNotAllowedOnDifferentThreadInDebug) {
162  ASSERT_DEATH({
163      ThreadCheckerClass::MethodOnDifferentThreadImpl();
164    }, "");
165}
166#else
167TEST(ThreadCheckerTest, MethodAllowedOnDifferentThreadInRelease) {
168  ThreadCheckerClass::MethodOnDifferentThreadImpl();
169}
170#endif  // ENABLE_THREAD_CHECKER
171
172void ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl() {
173  scoped_ptr<ThreadCheckerClass> thread_checker_class(
174      new ThreadCheckerClass);
175
176  // DoStuff doesn't assert when called on a different thread
177  // after a call to DetachFromThread.
178  thread_checker_class->DetachFromThread();
179  CallDoStuffOnThread call_on_thread(thread_checker_class.get());
180
181  call_on_thread.Start();
182  call_on_thread.Join();
183
184  // DoStuff should assert in debug builds only after moving to
185  // another thread.
186  thread_checker_class->DoStuff();
187}
188
189#if ENABLE_THREAD_CHECKER
190TEST(ThreadCheckerDeathTest, DetachFromThreadInDebug) {
191  ASSERT_DEATH({
192    ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl();
193    }, "");
194}
195#else
196TEST(ThreadCheckerTest, DetachFromThreadInRelease) {
197  ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl();
198}
199#endif  // ENABLE_THREAD_CHECKER
200
201#endif  // GTEST_HAS_DEATH_TEST || !ENABLE_THREAD_CHECKER
202
203// Just in case we ever get lumped together with other compilation units.
204#undef ENABLE_THREAD_CHECKER
205
206}  // namespace rtc
207