1// Copyright (c) 2011 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/logging.h"
7#include "base/memory/scoped_ptr.h"
8#include "base/threading/thread_checker.h"
9#include "base/threading/simple_thread.h"
10#include "testing/gtest/include/gtest/gtest.h"
11
12namespace base {
13
14// Simple class to exercise the basics of ThreadChecker.
15// Both the destructor and DoStuff should verify that they were
16// called on the same thread as the constructor.
17class ThreadCheckerClass : public ThreadChecker {
18 public:
19  ThreadCheckerClass() {}
20
21  // Verifies that it was called on the same thread as the constructor.
22  void DoStuff() {
23    CHECK(CalledOnValidThread());
24  }
25
26  void DetachFromThread() {
27    ThreadChecker::DetachFromThread();
28  }
29
30  static void MethodOnDifferentThreadImpl();
31  static void DetachThenCallFromDifferentThreadImpl();
32
33 private:
34  DISALLOW_COPY_AND_ASSIGN(ThreadCheckerClass);
35};
36
37// Calls ThreadCheckerClass::DoStuff on another thread.
38class CallDoStuffOnThread : public base::SimpleThread {
39 public:
40  CallDoStuffOnThread(ThreadCheckerClass* thread_checker_class)
41      : SimpleThread("call_do_stuff_on_thread"),
42        thread_checker_class_(thread_checker_class) {
43  }
44
45  virtual void Run() {
46    thread_checker_class_->DoStuff();
47  }
48
49 private:
50  ThreadCheckerClass* thread_checker_class_;
51
52  DISALLOW_COPY_AND_ASSIGN(CallDoStuffOnThread);
53};
54
55// Deletes ThreadCheckerClass on a different thread.
56class DeleteThreadCheckerClassOnThread : public base::SimpleThread {
57 public:
58  DeleteThreadCheckerClassOnThread(ThreadCheckerClass* thread_checker_class)
59      : SimpleThread("delete_thread_checker_class_on_thread"),
60        thread_checker_class_(thread_checker_class) {
61  }
62
63  virtual void Run() {
64    thread_checker_class_.reset();
65  }
66
67 private:
68  scoped_ptr<ThreadCheckerClass> thread_checker_class_;
69
70  DISALLOW_COPY_AND_ASSIGN(DeleteThreadCheckerClassOnThread);
71};
72
73TEST(ThreadCheckerTest, CallsAllowedOnSameThread) {
74  scoped_ptr<ThreadCheckerClass> thread_checker_class(
75      new ThreadCheckerClass);
76
77  // Verify that DoStuff doesn't assert.
78  thread_checker_class->DoStuff();
79
80  // Verify that the destructor doesn't assert.
81  thread_checker_class.reset();
82}
83
84TEST(ThreadCheckerTest, DestructorAllowedOnDifferentThread) {
85  scoped_ptr<ThreadCheckerClass> thread_checker_class(
86      new ThreadCheckerClass);
87
88  // Verify that the destructor doesn't assert
89  // when called on a different thread.
90  DeleteThreadCheckerClassOnThread delete_on_thread(
91      thread_checker_class.release());
92
93  delete_on_thread.Start();
94  delete_on_thread.Join();
95}
96
97TEST(ThreadCheckerTest, DetachFromThread) {
98  scoped_ptr<ThreadCheckerClass> thread_checker_class(
99      new ThreadCheckerClass);
100
101  // Verify that DoStuff doesn't assert when called on a different thread after
102  // a call to DetachFromThread.
103  thread_checker_class->DetachFromThread();
104  CallDoStuffOnThread call_on_thread(thread_checker_class.get());
105
106  call_on_thread.Start();
107  call_on_thread.Join();
108}
109
110#if GTEST_HAS_DEATH_TEST || NDEBUG
111
112void ThreadCheckerClass::MethodOnDifferentThreadImpl() {
113  scoped_ptr<ThreadCheckerClass> thread_checker_class(
114      new ThreadCheckerClass);
115
116  // DoStuff should assert in debug builds only when called on a
117  // different thread.
118  CallDoStuffOnThread call_on_thread(thread_checker_class.get());
119
120  call_on_thread.Start();
121  call_on_thread.Join();
122}
123
124#ifndef NDEBUG
125TEST(ThreadCheckerDeathTest, MethodNotAllowedOnDifferentThreadInDebug) {
126  ASSERT_DEBUG_DEATH({
127      ThreadCheckerClass::MethodOnDifferentThreadImpl();
128    }, "");
129}
130#else
131TEST(ThreadCheckerTest, MethodAllowedOnDifferentThreadInRelease) {
132  ThreadCheckerClass::MethodOnDifferentThreadImpl();
133}
134#endif  // NDEBUG
135
136void ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl() {
137  scoped_ptr<ThreadCheckerClass> thread_checker_class(
138      new ThreadCheckerClass);
139
140  // DoStuff doesn't assert when called on a different thread
141  // after a call to DetachFromThread.
142  thread_checker_class->DetachFromThread();
143  CallDoStuffOnThread call_on_thread(thread_checker_class.get());
144
145  call_on_thread.Start();
146  call_on_thread.Join();
147
148  // DoStuff should assert in debug builds only after moving to
149  // another thread.
150  thread_checker_class->DoStuff();
151}
152
153#ifndef NDEBUG
154TEST(ThreadCheckerDeathTest, DetachFromThreadInDebug) {
155  ASSERT_DEBUG_DEATH({
156    ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl();
157    }, "");
158}
159#else
160TEST(ThreadCheckerTest, DetachFromThreadInRelease) {
161  ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl();
162}
163#endif  // NDEBUG
164
165#endif  // GTEST_HAS_DEATH_TEST || NDEBUG
166
167}  // namespace base
168