 Chromium Code Reviews
 Chromium Code Reviews Issue 11417077:
  Rewrite CancelableTaskTracker unit tests  (Closed) 
  Base URL: svn://svn.chromium.org/chrome/trunk/src
    
  
    Issue 11417077:
  Rewrite CancelableTaskTracker unit tests  (Closed) 
  Base URL: svn://svn.chromium.org/chrome/trunk/src| OLD | NEW | 
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be | 
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. | 
| 4 | 4 | 
| 5 #include "chrome/common/cancelable_task_tracker.h" | 5 #include "chrome/common/cancelable_task_tracker.h" | 
| 6 | 6 | 
| 7 #include "base/basictypes.h" | 7 #include <cstddef> | 
| 8 #include <deque> | |
| 9 | |
| 8 #include "base/bind.h" | 10 #include "base/bind.h" | 
| 9 #include "base/callback.h" | 11 #include "base/bind_helpers.h" | 
| 10 #include "base/memory/scoped_ptr.h" | 12 #include "base/compiler_specific.h" | 
| 11 #include "base/synchronization/waitable_event.h" | 13 #include "base/location.h" | 
| 14 #include "base/logging.h" | |
| 15 #include "base/memory/ref_counted.h" | |
| 16 #include "base/memory/weak_ptr.h" | |
| 17 #include "base/message_loop.h" | |
| 18 #include "base/run_loop.h" | |
| 19 #include "base/task_runner.h" | |
| 12 #include "base/threading/thread.h" | 20 #include "base/threading/thread.h" | 
| 13 #include "testing/gtest/include/gtest/gtest.h" | 21 #include "testing/gtest/include/gtest/gtest.h" | 
| 14 | 22 | 
| 15 using base::Bind; | |
| 16 using base::Closure; | |
| 17 using base::Owned; | |
| 18 using base::TaskRunner; | |
| 19 using base::Thread; | |
| 20 using base::Unretained; | |
| 21 using base::WaitableEvent; | |
| 22 | |
| 23 namespace { | 23 namespace { | 
| 24 | 24 | 
| 25 class WaitableEventScoper { | 25 // Test TaskRunner implementation that simply stores posted tasks in a | 
| 26 // queue. | |
| 27 // | |
| 28 // TOOD(akalin): Pull this out into its own file once something else | |
| 29 // needs it. | |
| 30 class FakeNonThreadSafeTaskRunner : public base::TaskRunner { | |
| 26 public: | 31 public: | 
| 27 explicit WaitableEventScoper(WaitableEvent* event) : event_(event) {} | 32 // base::TaskRunner implementation. | 
| 28 ~WaitableEventScoper() { | 33 // Stores posted tasks in a FIFO, ignoring |delay|.p | 
| 
kaiwang
2012/11/22 02:05:54
Remove trailing p
 
akalin
2012/11/22 07:00:26
Done.
 | |
| 29 if (event_) | 34 virtual bool PostDelayedTask(const tracked_objects::Location& from_here, | 
| 30 event_->Signal(); | 35 const base::Closure& task, | 
| 31 } | 36 base::TimeDelta delay) OVERRIDE { | 
| 37 tasks_.push_back(task); | |
| 38 return true; | |
| 39 } | |
| 40 | |
| 41 virtual bool RunsTasksOnCurrentThread() const OVERRIDE { | |
| 42 return true; | |
| 43 } | |
| 44 | |
| 45 size_t GetPendingTaskCount() const { | |
| 46 return tasks_.size(); | |
| 47 } | |
| 48 | |
| 49 void RunUntilIdle() { | |
| 50 // Use a while loop since a task may post more tasks. | |
| 51 while (!tasks_.empty()) { | |
| 52 base::Closure task = tasks_.front(); | |
| 53 tasks_.pop_front(); | |
| 54 task.Run(); | |
| 55 } | |
| 56 } | |
| 57 | |
| 58 protected: | |
| 59 virtual ~FakeNonThreadSafeTaskRunner() {} | |
| 60 | |
| 32 private: | 61 private: | 
| 33 WaitableEvent* event_; | 62 std::deque<base::Closure> tasks_; | 
| 34 DISALLOW_COPY_AND_ASSIGN(WaitableEventScoper); | |
| 35 }; | 63 }; | 
| 36 | 64 | 
| 37 class CancelableTaskTrackerTest : public testing::Test { | 65 class CancelableTaskTrackerTest : public testing::Test { | 
| 38 protected: | 66 protected: | 
| 39 CancelableTaskTrackerTest() | 67 virtual ~CancelableTaskTrackerTest() { | 
| 40 : task_id_(CancelableTaskTracker::kBadTaskId), | 68 base::RunLoop run_loop; | 
| 41 test_data_(0), | 69 run_loop.RunUntilIdle(); | 
| 42 task_thread_start_event_(true, false) {} | 70 } | 
| 43 | 71 | 
| 44 virtual void SetUp() { | 72 private: | 
| 45 task_thread_.reset(new Thread("task thread")); | 73 // Needs to be initialized before |task_tracker_|. | 
| 
kaiwang
2012/11/22 02:05:54
Why?
 
akalin
2012/11/22 07:00:26
Oops, looks like that's wrong.  Only the methods n
 | |
| 46 client_thread_.reset(new Thread("client thread")); | 74 MessageLoop message_loop_; | 
| 47 task_thread_->Start(); | 75 | 
| 48 client_thread_->Start(); | 76 protected: | 
| 49 | 77 CancelableTaskTracker task_tracker_; | 
| 50 task_thread_runner_ = task_thread_->message_loop_proxy(); | 78 }; | 
| 51 client_thread_runner_ = client_thread_->message_loop_proxy(); | 79 | 
| 52 | 80 void AddFailureAt(const tracked_objects::Location& location) { | 
| 53 // Create tracker on client thread. | 81 ADD_FAILURE_AT(location.file_name(), location.line_number()); | 
| 54 WaitableEvent tracker_created(true, false); | 82 } | 
| 55 client_thread_runner_->PostTask( | 83 | 
| 56 FROM_HERE, | 84 // Returns a closure that fails if run. | 
| 57 Bind(&CancelableTaskTrackerTest::CreateTrackerOnClientThread, | 85 base::Closure MakeExpectedNotRunClosure( | 
| 58 Unretained(this), &tracker_created)); | 86 const tracked_objects::Location& location) { | 
| 59 tracker_created.Wait(); | 87 return base::Bind(&AddFailureAt, location); | 
| 60 | 88 } | 
| 61 // Block server thread so we can prepare the test. | 89 | 
| 62 task_thread_runner_->PostTask( | 90 // A helper class for MakeExpectedRunClosure() that fails if it is | 
| 63 FROM_HERE, | 91 // destroyed without Run() having been called. This class may be used | 
| 64 Bind(&WaitableEvent::Wait, Unretained(&task_thread_start_event_))); | 92 // from multiple threads as long as Run() is called at most once | 
| 65 } | 93 // before destruction. | 
| 66 | 94 class RunChecker { | 
| 67 virtual void TearDown() { | |
| 68 UnblockTaskThread(); | |
| 69 | |
| 70 // Destroy tracker on client thread. | |
| 71 WaitableEvent tracker_destroyed(true, false); | |
| 72 client_thread_runner_->PostTask( | |
| 73 FROM_HERE, | |
| 74 Bind(&CancelableTaskTrackerTest::DestroyTrackerOnClientThread, | |
| 75 Unretained(this), &tracker_destroyed)); | |
| 76 | |
| 77 // This will also wait for any pending tasks on client thread. | |
| 78 tracker_destroyed.Wait(); | |
| 79 | |
| 80 client_thread_->Stop(); | |
| 81 task_thread_->Stop(); | |
| 82 } | |
| 83 | |
| 84 void RunOnClientAndWait( | |
| 85 void (*func)(CancelableTaskTrackerTest*, WaitableEvent*)) { | |
| 86 WaitableEvent event(true, false); | |
| 87 client_thread_runner_->PostTask(FROM_HERE, | |
| 88 Bind(func, Unretained(this), &event)); | |
| 89 event.Wait(); | |
| 90 } | |
| 91 | |
| 92 public: | 95 public: | 
| 93 // Client thread posts tasks and runs replies. | 96 explicit RunChecker(const tracked_objects::Location& location) | 
| 94 scoped_refptr<TaskRunner> client_thread_runner_; | 97 : location_(location), | 
| 95 | 98 called_(false) {} | 
| 96 // Task thread runs tasks. | 99 | 
| 97 scoped_refptr<TaskRunner> task_thread_runner_; | 100 ~RunChecker() { | 
| 98 | 101 if (!called_) { | 
| 99 // |tracker_| can only live on client thread. | 102 ADD_FAILURE_AT(location_.file_name(), location_.line_number()); | 
| 100 scoped_ptr<CancelableTaskTracker> tracker_; | 103 } | 
| 101 | 104 } | 
| 102 CancelableTaskTracker::TaskId task_id_; | 105 | 
| 103 | 106 void Run() { | 
| 104 void UnblockTaskThread() { | 107 called_ = true; | 
| 105 task_thread_start_event_.Signal(); | |
| 106 } | |
| 107 | |
| 108 ////////////////////////////////////////////////////////////////////////////// | |
| 109 // Testing data and related functions | |
| 110 int test_data_; // Defaults to 0. | |
| 111 | |
| 112 Closure IncreaseTestDataAndSignalClosure(WaitableEvent* event) { | |
| 113 return Bind(&CancelableTaskTrackerTest::IncreaseDataAndSignal, | |
| 114 &test_data_, event); | |
| 115 } | |
| 116 | |
| 117 Closure IncreaseTestDataIfNotCanceledAndSignalClosure( | |
| 118 const CancelableTaskTracker::IsCanceledCallback& is_canceled_cb, | |
| 119 WaitableEvent* event) { | |
| 120 return Bind(&CancelableTaskTrackerTest::IncreaseDataIfNotCanceledAndSignal, | |
| 121 &test_data_, is_canceled_cb, event); | |
| 122 } | |
| 123 | |
| 124 Closure DecreaseTestDataClosure(WaitableEvent* event) { | |
| 125 return Bind(&CancelableTaskTrackerTest::DecreaseData, | |
| 126 Owned(new WaitableEventScoper(event)), &test_data_); | |
| 127 } | 108 } | 
| 128 | 109 | 
| 129 private: | 110 private: | 
| 130 void CreateTrackerOnClientThread(WaitableEvent* event) { | 111 tracked_objects::Location location_; | 
| 131 tracker_.reset(new CancelableTaskTracker()); | 112 bool called_; | 
| 132 event->Signal(); | |
| 133 } | |
| 134 | |
| 135 void DestroyTrackerOnClientThread(WaitableEvent* event) { | |
| 136 tracker_.reset(); | |
| 137 event->Signal(); | |
| 138 } | |
| 139 | |
| 140 static void IncreaseDataAndSignal(int* data, WaitableEvent* event) { | |
| 141 (*data)++; | |
| 142 if (event) | |
| 143 event->Signal(); | |
| 144 } | |
| 145 | |
| 146 static void IncreaseDataIfNotCanceledAndSignal( | |
| 147 int* data, | |
| 148 const CancelableTaskTracker::IsCanceledCallback& is_canceled_cb, | |
| 149 WaitableEvent* event) { | |
| 150 if (!is_canceled_cb.Run()) | |
| 151 (*data)++; | |
| 152 if (event) | |
| 153 event->Signal(); | |
| 154 } | |
| 155 | |
| 156 static void DecreaseData(WaitableEventScoper* event_scoper, int* data) { | |
| 157 (*data) -= 2; | |
| 158 } | |
| 159 | |
| 160 scoped_ptr<Thread> client_thread_; | |
| 161 scoped_ptr<Thread> task_thread_; | |
| 162 | |
| 163 WaitableEvent task_thread_start_event_; | |
| 164 }; | 113 }; | 
| 165 | 114 | 
| 166 #if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) && GTEST_HAS_DEATH_TEST | 115 // Returns a closure that fails on destruction if it hasn't been run. | 
| 167 | 116 base::Closure MakeExpectedRunClosure( | 
| 168 typedef CancelableTaskTrackerTest CancelableTaskTrackerDeathTest; | 117 const tracked_objects::Location& location) { | 
| 118 return base::Bind(&RunChecker::Run, base::Owned(new RunChecker(location))); | |
| 119 } | |
| 120 | |
| 121 // With the task tracker, post a task, a task with a reply, and get a | |
| 122 // new task id without canceling any of them. The tasks and the reply | |
| 123 // should run and the "is canceled" callback should return false. | |
| 124 TEST_F(CancelableTaskTrackerTest, NoCancel) { | |
| 125 base::Thread worker_thread("worker thread"); | |
| 126 ASSERT_TRUE(worker_thread.Start()); | |
| 127 | |
| 128 ignore_result( | |
| 129 task_tracker_.PostTask( | |
| 130 worker_thread.message_loop_proxy(), | |
| 131 FROM_HERE, | |
| 132 MakeExpectedRunClosure(FROM_HERE))); | |
| 133 | |
| 134 ignore_result( | |
| 135 task_tracker_.PostTaskAndReply( | |
| 136 worker_thread.message_loop_proxy(), | |
| 137 FROM_HERE, | |
| 138 MakeExpectedRunClosure(FROM_HERE), | |
| 139 MakeExpectedRunClosure(FROM_HERE))); | |
| 140 | |
| 141 CancelableTaskTracker::IsCanceledCallback is_canceled; | |
| 142 ignore_result(task_tracker_.NewTrackedTaskId(&is_canceled)); | |
| 143 | |
| 144 worker_thread.Stop(); | |
| 145 | |
| 146 base::RunLoop run_loop; | |
| 147 run_loop.RunUntilIdle(); | |
| 
kaiwang
2012/11/22 02:05:54
already have this in Test destructor, necessary to
 
akalin
2012/11/22 07:00:26
This one is necessary (since the EXPECT below depe
 | |
| 148 | |
| 149 EXPECT_FALSE(is_canceled.Run()); | |
| 150 } | |
| 151 | |
| 152 // Post a task with the task tracker but cancel it before running the | |
| 153 // task runner. The task should not run. | |
| 154 TEST_F(CancelableTaskTrackerTest, CancelPostedTask) { | |
| 155 scoped_refptr<FakeNonThreadSafeTaskRunner> fake_task_runner( | |
| 156 new FakeNonThreadSafeTaskRunner()); | |
| 157 | |
| 158 CancelableTaskTracker::TaskId task_id = | |
| 159 task_tracker_.PostTask( | |
| 160 fake_task_runner.get(), | |
| 161 FROM_HERE, | |
| 162 MakeExpectedNotRunClosure(FROM_HERE)); | |
| 163 EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id); | |
| 164 | |
| 165 EXPECT_EQ(1U, fake_task_runner->GetPendingTaskCount()); | |
| 166 | |
| 167 task_tracker_.TryCancel(task_id); | |
| 168 | |
| 169 fake_task_runner->RunUntilIdle(); | |
| 170 } | |
| 171 | |
| 172 // Post a task with reply with the task tracker and cancel it before | |
| 173 // running the task runner. Neither the task nor the reply should | |
| 174 // run. | |
| 175 TEST_F(CancelableTaskTrackerTest, CancelPostedTaskAndReply) { | |
| 176 scoped_refptr<FakeNonThreadSafeTaskRunner> fake_task_runner( | |
| 177 new FakeNonThreadSafeTaskRunner()); | |
| 178 | |
| 179 CancelableTaskTracker::TaskId task_id = | |
| 180 task_tracker_.PostTaskAndReply( | |
| 181 fake_task_runner.get(), | |
| 182 FROM_HERE, | |
| 183 MakeExpectedNotRunClosure(FROM_HERE), | |
| 184 MakeExpectedNotRunClosure(FROM_HERE)); | |
| 185 EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id); | |
| 186 | |
| 187 task_tracker_.TryCancel(task_id); | |
| 188 | |
| 189 fake_task_runner->RunUntilIdle(); | |
| 190 } | |
| 191 | |
| 192 // Post a task with reply with the task tracker and cancel it after | |
| 193 // running the task runner but before running the current message | |
| 194 // loop. The task should run but the reply should not. | |
| 195 TEST_F(CancelableTaskTrackerTest, CancelReply) { | |
| 196 scoped_refptr<FakeNonThreadSafeTaskRunner> fake_task_runner( | |
| 197 new FakeNonThreadSafeTaskRunner()); | |
| 198 | |
| 199 CancelableTaskTracker::TaskId task_id = | |
| 200 task_tracker_.PostTaskAndReply( | |
| 201 fake_task_runner.get(), | |
| 202 FROM_HERE, | |
| 203 MakeExpectedRunClosure(FROM_HERE), | |
| 204 MakeExpectedNotRunClosure(FROM_HERE)); | |
| 205 EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id); | |
| 206 | |
| 207 fake_task_runner->RunUntilIdle(); | |
| 208 | |
| 209 task_tracker_.TryCancel(task_id); | |
| 210 | |
| 211 base::RunLoop run_loop; | |
| 212 run_loop.RunUntilIdle(); | |
| 
kaiwang
2012/11/22 02:05:54
same here
 
akalin
2012/11/22 07:00:26
Done.
 | |
| 213 } | |
| 214 | |
| 215 // Post a task with reply with the task tracker on a worker thread and | |
| 216 // cancel it before running the current message loop. The task should | |
| 217 // run but the reply should not. | |
| 218 TEST_F(CancelableTaskTrackerTest, CancelReplyDifferentThread) { | |
| 219 base::Thread worker_thread("worker thread"); | |
| 220 ASSERT_TRUE(worker_thread.Start()); | |
| 221 | |
| 222 CancelableTaskTracker::TaskId task_id = | |
| 223 task_tracker_.PostTaskAndReply( | |
| 224 worker_thread.message_loop_proxy(), | |
| 225 FROM_HERE, | |
| 226 base::Bind(&base::DoNothing), | |
| 227 MakeExpectedNotRunClosure(FROM_HERE)); | |
| 228 EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id); | |
| 229 | |
| 230 task_tracker_.TryCancel(task_id); | |
| 231 | |
| 232 worker_thread.Stop(); | |
| 233 | |
| 234 base::RunLoop run_loop; | |
| 235 run_loop.RunUntilIdle(); | |
| 236 } | |
| 237 | |
| 238 void ExpectIsCanceled( | |
| 239 const CancelableTaskTracker::IsCanceledCallback& is_canceled, | |
| 240 bool expected_is_canceled) { | |
| 241 EXPECT_EQ(expected_is_canceled, is_canceled.Run()); | |
| 242 } | |
| 243 | |
| 244 // Create a new task ID and check its status on a separate thread | |
| 245 // before and after canceling. The is-canceled callback should be | |
| 246 // thread-safe (i.e., nothing should blow up). | |
| 247 TEST_F(CancelableTaskTrackerTest, NewTrackedTaskIdDifferentThread) { | |
| 248 CancelableTaskTracker::IsCanceledCallback is_canceled; | |
| 249 CancelableTaskTracker::TaskId task_id = | |
| 250 task_tracker_.NewTrackedTaskId(&is_canceled); | |
| 251 | |
| 252 EXPECT_FALSE(is_canceled.Run()); | |
| 253 | |
| 254 base::Thread other_thread("other thread"); | |
| 255 ASSERT_TRUE(other_thread.Start()); | |
| 256 other_thread.message_loop_proxy()->PostTask( | |
| 257 FROM_HERE, | |
| 258 base::Bind(&ExpectIsCanceled, is_canceled, false)); | |
| 259 other_thread.Stop(); | |
| 260 | |
| 261 task_tracker_.TryCancel(task_id); | |
| 262 | |
| 263 ASSERT_TRUE(other_thread.Start()); | |
| 264 other_thread.message_loop_proxy()->PostTask( | |
| 265 FROM_HERE, | |
| 266 base::Bind(&ExpectIsCanceled, is_canceled, true)); | |
| 267 other_thread.Stop(); | |
| 268 } | |
| 269 | |
| 270 // With the task tracker, post a task, a task with a reply, get a new | |
| 271 // task id, and then cancel all of them. None of the tasks nor the | |
| 272 // reply should run and the "is canceled" callback should return | |
| 273 // true. | |
| 274 TEST_F(CancelableTaskTrackerTest, CancelAll) { | |
| 275 scoped_refptr<FakeNonThreadSafeTaskRunner> fake_task_runner( | |
| 276 new FakeNonThreadSafeTaskRunner()); | |
| 277 | |
| 278 ignore_result( | |
| 279 task_tracker_.PostTask( | |
| 280 fake_task_runner, | |
| 281 FROM_HERE, | |
| 282 MakeExpectedNotRunClosure(FROM_HERE))); | |
| 283 | |
| 284 ignore_result( | |
| 285 task_tracker_.PostTaskAndReply( | |
| 286 fake_task_runner, | |
| 287 FROM_HERE, | |
| 288 MakeExpectedNotRunClosure(FROM_HERE), | |
| 289 MakeExpectedNotRunClosure(FROM_HERE))); | |
| 290 | |
| 291 CancelableTaskTracker::IsCanceledCallback is_canceled; | |
| 292 ignore_result(task_tracker_.NewTrackedTaskId(&is_canceled)); | |
| 293 | |
| 294 task_tracker_.TryCancelAll(); | |
| 295 | |
| 296 fake_task_runner->RunUntilIdle(); | |
| 297 | |
| 298 base::RunLoop run_loop; | |
| 299 run_loop.RunUntilIdle(); | |
| 300 | |
| 301 EXPECT_TRUE(is_canceled.Run()); | |
| 302 } | |
| 303 | |
| 304 // With the task tracker, post a task, a task with a reply, get a new | |
| 305 // task id, and then cancel all of them. None of the tasks nor the | |
| 306 // reply should run and the "is canceled" callback should return | |
| 307 // true. | |
| 308 TEST_F(CancelableTaskTrackerTest, DestructionCancelsAll) { | |
| 309 scoped_refptr<FakeNonThreadSafeTaskRunner> fake_task_runner( | |
| 310 new FakeNonThreadSafeTaskRunner()); | |
| 311 | |
| 312 CancelableTaskTracker::IsCanceledCallback is_canceled; | |
| 313 | |
| 314 { | |
| 315 // Create another task tracker with a smaller scope. | |
| 316 CancelableTaskTracker task_tracker; | |
| 317 | |
| 318 ignore_result( | |
| 319 task_tracker.PostTask( | |
| 320 fake_task_runner, | |
| 321 FROM_HERE, | |
| 322 MakeExpectedNotRunClosure(FROM_HERE))); | |
| 323 | |
| 324 ignore_result( | |
| 325 task_tracker.PostTaskAndReply( | |
| 326 fake_task_runner, | |
| 327 FROM_HERE, | |
| 328 MakeExpectedNotRunClosure(FROM_HERE), | |
| 329 MakeExpectedNotRunClosure(FROM_HERE))); | |
| 330 | |
| 331 ignore_result(task_tracker_.NewTrackedTaskId(&is_canceled)); | |
| 332 } | |
| 333 | |
| 334 fake_task_runner->RunUntilIdle(); | |
| 335 | |
| 336 base::RunLoop run_loop; | |
| 337 run_loop.RunUntilIdle(); | |
| 338 | |
| 339 EXPECT_FALSE(is_canceled.Run()); | |
| 340 } | |
| 341 | |
| 342 // The death tests below make sure that calling task tracker member | |
| 343 // functions from a thread different from its owner thread DCHECKs in | |
| 344 // debug mode. | |
| 345 | |
| 346 class CancelableTaskTrackerDeathTest : public CancelableTaskTrackerTest { | |
| 347 protected: | |
| 348 CancelableTaskTrackerDeathTest() { | |
| 349 // The default style "fast" does not support multi-threaded tests. | |
| 350 ::testing::FLAGS_gtest_death_test_style = "threadsafe"; | |
| 351 } | |
| 352 | |
| 353 virtual ~CancelableTaskTrackerDeathTest() {} | |
| 354 }; | |
| 355 | |
| 356 // Duplicated from base/threading/thread_checker.h so that we can be | |
| 357 // good citizens there and undef the macro. | |
| 358 #if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON) | |
| 359 #define ENABLE_THREAD_CHECKER 1 | |
| 360 #else | |
| 361 #define ENABLE_THREAD_CHECKER 0 | |
| 362 #endif | |
| 
kaiwang
2012/11/22 02:05:54
I suggest to directly use ENABLE_THREAD_CHECKER an
 
akalin
2012/11/22 07:00:26
Can't; note that ENABLE_THREAD_CHECKER is #undef'e
 | |
| 363 | |
| 364 // Runs |fn| with |task_tracker|, expecting it to crash in debug mode. | |
| 365 void MaybeRunDeadlyTaskTrackerMemberFunction( | |
| 366 CancelableTaskTracker* task_tracker, | |
| 367 const base::Callback<void(CancelableTaskTracker*)>& fn) { | |
| 368 // CancelableTask uses DCHECKs with its ThreadChecker (itself only | |
| 369 // enabled in debug mode). | |
| 370 #if ENABLE_THREAD_CHECKER | |
| 371 EXPECT_DEATH_IF_SUPPORTED(fn.Run(task_tracker), ""); | |
| 372 #endif | |
| 373 } | |
| 374 | |
| 375 void PostDoNothingTask(CancelableTaskTracker* task_tracker) { | |
| 376 ignore_result( | |
| 377 task_tracker->PostTask( | |
| 378 scoped_refptr<FakeNonThreadSafeTaskRunner>( | |
| 379 new FakeNonThreadSafeTaskRunner()), | |
| 380 FROM_HERE, base::Bind(&base::DoNothing))); | |
| 381 } | |
| 169 | 382 | 
| 170 TEST_F(CancelableTaskTrackerDeathTest, PostFromDifferentThread) { | 383 TEST_F(CancelableTaskTrackerDeathTest, PostFromDifferentThread) { | 
| 171 // The default style "fast" does not support multi-threaded tests. | 384 base::Thread bad_thread("bad thread"); | 
| 172 ::testing::FLAGS_gtest_death_test_style = "threadsafe"; | 385 ASSERT_TRUE(bad_thread.Start()); | 
| 173 | 386 | 
| 174 EXPECT_DEATH( | 387 bad_thread.message_loop_proxy()->PostTask( | 
| 175 tracker_->PostTask(task_thread_runner_, | |
| 176 FROM_HERE, | |
| 177 DecreaseTestDataClosure(NULL)), | |
| 178 ""); | |
| 179 } | |
| 180 | |
| 181 void CancelOnDifferentThread_Test(CancelableTaskTrackerTest* test, | |
| 182 WaitableEvent* event) { | |
| 183 test->task_id_ = test->tracker_->PostTask( | |
| 184 test->task_thread_runner_, | |
| 185 FROM_HERE, | 388 FROM_HERE, | 
| 186 test->DecreaseTestDataClosure(event)); | 389 base::Bind(&MaybeRunDeadlyTaskTrackerMemberFunction, | 
| 187 EXPECT_NE(CancelableTaskTracker::kBadTaskId, test->task_id_); | 390 base::Unretained(&task_tracker_), | 
| 188 | 391 base::Bind(&PostDoNothingTask))); | 
| 189 // Canceling a non-existed task is noop. | 392 } | 
| 190 test->tracker_->TryCancel(test->task_id_ + 1); | 393 | 
| 191 | 394 void TryCancel(CancelableTaskTracker::TaskId task_id, | 
| 192 test->UnblockTaskThread(); | 395 CancelableTaskTracker* task_tracker) { | 
| 396 task_tracker->TryCancel(task_id); | |
| 193 } | 397 } | 
| 194 | 398 | 
| 195 TEST_F(CancelableTaskTrackerDeathTest, CancelOnDifferentThread) { | 399 TEST_F(CancelableTaskTrackerDeathTest, CancelOnDifferentThread) { | 
| 196 // The default style "fast" does not support multi-threaded tests. | 400 scoped_refptr<FakeNonThreadSafeTaskRunner> fake_task_runner( | 
| 197 ::testing::FLAGS_gtest_death_test_style = "threadsafe"; | 401 new FakeNonThreadSafeTaskRunner()); | 
| 198 | 402 | 
| 199 // Post a task and we'll try canceling it on a different thread. | 403 base::Thread bad_thread("bad thread"); | 
| 200 RunOnClientAndWait(&CancelOnDifferentThread_Test); | 404 ASSERT_TRUE(bad_thread.Start()); | 
| 201 | 405 | 
| 202 // Canceling on the wrong thread. | 406 CancelableTaskTracker::TaskId task_id = | 
| 203 EXPECT_DEATH(tracker_->TryCancel(task_id_), ""); | 407 task_tracker_.PostTask( | 
| 204 | 408 fake_task_runner.get(), | 
| 205 // Even canceling a non-existant task will crash. | 409 FROM_HERE, | 
| 206 EXPECT_DEATH(tracker_->TryCancel(task_id_ + 1), ""); | 410 base::Bind(&base::DoNothing)); | 
| 207 } | 411 EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id); | 
| 208 | 412 | 
| 209 void TrackerCancelAllOnDifferentThread_Test( | 413 bad_thread.message_loop_proxy()->PostTask( | 
| 210 CancelableTaskTrackerTest* test, WaitableEvent* event) { | |
| 211 test->task_id_ = test->tracker_->PostTask( | |
| 212 test->task_thread_runner_, | |
| 213 FROM_HERE, | 414 FROM_HERE, | 
| 214 test->DecreaseTestDataClosure(event)); | 415 base::Bind(&MaybeRunDeadlyTaskTrackerMemberFunction, | 
| 215 EXPECT_NE(CancelableTaskTracker::kBadTaskId, test->task_id_); | 416 base::Unretained(&task_tracker_), | 
| 216 test->UnblockTaskThread(); | 417 base::Bind(&TryCancel, task_id))); | 
| 217 } | 418 | 
| 218 | 419 fake_task_runner->RunUntilIdle(); | 
| 219 TEST_F(CancelableTaskTrackerDeathTest, TrackerCancelAllOnDifferentThread) { | 420 } | 
| 220 // The default style "fast" does not support multi-threaded tests. | 421 | 
| 221 ::testing::FLAGS_gtest_death_test_style = "threadsafe"; | 422 TEST_F(CancelableTaskTrackerDeathTest, CancelAllOnDifferentThread) { | 
| 222 | 423 scoped_refptr<FakeNonThreadSafeTaskRunner> fake_task_runner( | 
| 223 // |tracker_| can only live on client thread. | 424 new FakeNonThreadSafeTaskRunner()); | 
| 224 EXPECT_DEATH(tracker_.reset(), ""); | 425 | 
| 225 | 426 base::Thread bad_thread("bad thread"); | 
| 226 RunOnClientAndWait(&TrackerCancelAllOnDifferentThread_Test); | 427 ASSERT_TRUE(bad_thread.Start()); | 
| 227 | 428 | 
| 228 EXPECT_DEATH(tracker_->TryCancelAll(), ""); | 429 CancelableTaskTracker::TaskId task_id = | 
| 229 EXPECT_DEATH(tracker_.reset(), ""); | 430 task_tracker_.PostTask( | 
| 230 } | 431 fake_task_runner.get(), | 
| 231 | 432 FROM_HERE, | 
| 232 #endif // (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) && | 433 base::Bind(&base::DoNothing)); | 
| 233 // GTEST_HAS_DEATH_TEST | 434 EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id); | 
| 234 | 435 | 
| 235 void Canceled_Test(CancelableTaskTrackerTest* test, WaitableEvent* event) { | 436 bad_thread.message_loop_proxy()->PostTask( | 
| 236 test->task_id_ = test->tracker_->PostTask( | |
| 237 test->task_thread_runner_, | |
| 238 FROM_HERE, | 437 FROM_HERE, | 
| 239 test->DecreaseTestDataClosure(event)); | 438 base::Bind(&MaybeRunDeadlyTaskTrackerMemberFunction, | 
| 240 EXPECT_NE(CancelableTaskTracker::kBadTaskId, test->task_id_); | 439 base::Unretained(&task_tracker_), | 
| 241 | 440 base::Bind(&CancelableTaskTracker::TryCancelAll))); | 
| 242 test->tracker_->TryCancel(test->task_id_); | 441 | 
| 243 test->UnblockTaskThread(); | 442 fake_task_runner->RunUntilIdle(); | 
| 244 } | |
| 245 | |
| 246 TEST_F(CancelableTaskTrackerTest, Canceled) { | |
| 247 RunOnClientAndWait(&Canceled_Test); | |
| 248 EXPECT_EQ(0, test_data_); | |
| 249 } | |
| 250 | |
| 251 void SignalAndWaitThenIncrease(WaitableEvent* start_event, | |
| 252 WaitableEvent* continue_event, | |
| 253 int* data) { | |
| 254 start_event->Signal(); | |
| 255 continue_event->Wait(); | |
| 256 (*data)++; | |
| 257 } | |
| 258 | |
| 259 void CancelWhileTaskRunning_Test(CancelableTaskTrackerTest* test, | |
| 260 WaitableEvent* event) { | |
| 261 WaitableEvent task_start_event(true, false); | |
| 262 WaitableEvent* task_continue_event = new WaitableEvent(true, false); | |
| 263 | |
| 264 test->task_id_ = test->tracker_->PostTaskAndReply( | |
| 265 test->task_thread_runner_, | |
| 266 FROM_HERE, | |
| 267 Bind(&SignalAndWaitThenIncrease, | |
| 268 &task_start_event, Owned(task_continue_event), &test->test_data_), | |
| 269 test->DecreaseTestDataClosure(event)); | |
| 270 EXPECT_NE(CancelableTaskTracker::kBadTaskId, test->task_id_); | |
| 271 | |
| 272 test->UnblockTaskThread(); | |
| 273 task_start_event.Wait(); | |
| 274 | |
| 275 // Now task is running. Let's try to cancel. | |
| 276 test->tracker_->TryCancel(test->task_id_); | |
| 277 | |
| 278 // Let task continue. | |
| 279 task_continue_event->Signal(); | |
| 280 } | |
| 281 | |
| 282 TEST_F(CancelableTaskTrackerTest, CancelWhileTaskRunning) { | |
| 283 RunOnClientAndWait(&CancelWhileTaskRunning_Test); | |
| 284 | |
| 285 // Task will continue running but reply will be canceled. | |
| 286 EXPECT_EQ(1, test_data_); | |
| 287 } | |
| 288 | |
| 289 void NotCanceled_Test(CancelableTaskTrackerTest* test, WaitableEvent* event) { | |
| 290 test->task_id_ = test->tracker_->PostTaskAndReply( | |
| 291 test->task_thread_runner_, | |
| 292 FROM_HERE, | |
| 293 test->IncreaseTestDataAndSignalClosure(NULL), | |
| 294 test->DecreaseTestDataClosure(event)); | |
| 295 EXPECT_NE(CancelableTaskTracker::kBadTaskId, test->task_id_); | |
| 296 | |
| 297 test->UnblockTaskThread(); | |
| 298 } | |
| 299 | |
| 300 TEST_F(CancelableTaskTrackerTest, NotCanceled) { | |
| 301 RunOnClientAndWait(&NotCanceled_Test); | |
| 302 EXPECT_EQ(-1, test_data_); | |
| 303 } | |
| 304 | |
| 305 void TrackerDestructed_Test(CancelableTaskTrackerTest* test, | |
| 306 WaitableEvent* event) { | |
| 307 test->task_id_ = test->tracker_->PostTaskAndReply( | |
| 308 test->task_thread_runner_, | |
| 309 FROM_HERE, | |
| 310 test->IncreaseTestDataAndSignalClosure(NULL), | |
| 311 test->DecreaseTestDataClosure(event)); | |
| 312 EXPECT_NE(CancelableTaskTracker::kBadTaskId, test->task_id_); | |
| 313 | |
| 314 test->tracker_.reset(); | |
| 315 test->UnblockTaskThread(); | |
| 316 } | |
| 317 | |
| 318 TEST_F(CancelableTaskTrackerTest, TrackerDestructed) { | |
| 319 RunOnClientAndWait(&TrackerDestructed_Test); | |
| 320 EXPECT_EQ(0, test_data_); | |
| 321 } | |
| 322 | |
| 323 void TrackerDestructedAfterTask_Test(CancelableTaskTrackerTest* test, | |
| 324 WaitableEvent* event) { | |
| 325 WaitableEvent task_done_event(true, false); | |
| 326 test->task_id_ = test->tracker_->PostTaskAndReply( | |
| 327 test->task_thread_runner_, | |
| 328 FROM_HERE, | |
| 329 test->IncreaseTestDataAndSignalClosure(&task_done_event), | |
| 330 test->DecreaseTestDataClosure(event)); | |
| 331 ASSERT_NE(CancelableTaskTracker::kBadTaskId, test->task_id_); | |
| 332 | |
| 333 test->UnblockTaskThread(); | |
| 334 | |
| 335 task_done_event.Wait(); | |
| 336 | |
| 337 // At this point, task is already finished on task thread but reply has not | |
| 338 // started yet (because this function is still running on client thread). | |
| 339 // Now delete the tracker to cancel reply. | |
| 340 test->tracker_.reset(); | |
| 341 } | |
| 342 | |
| 343 TEST_F(CancelableTaskTrackerTest, TrackerDestructedAfterTask) { | |
| 344 RunOnClientAndWait(&TrackerDestructedAfterTask_Test); | |
| 345 EXPECT_EQ(1, test_data_); | |
| 346 } | |
| 347 | |
| 348 void CheckTrackedTaskIdOnSameThread_Test(CancelableTaskTrackerTest* test, | |
| 349 WaitableEvent* event) { | |
| 350 CancelableTaskTracker::IsCanceledCallback is_canceled_cb; | |
| 351 test->task_id_ = test->tracker_->NewTrackedTaskId(&is_canceled_cb); | |
| 352 ASSERT_NE(CancelableTaskTracker::kBadTaskId, test->task_id_); | |
| 353 | |
| 354 EXPECT_FALSE(is_canceled_cb.Run()); | |
| 355 | |
| 356 test->tracker_->TryCancel(test->task_id_); | |
| 357 EXPECT_TRUE(is_canceled_cb.Run()); | |
| 358 | |
| 359 test->task_id_ = test->tracker_->NewTrackedTaskId(&is_canceled_cb); | |
| 360 EXPECT_FALSE(is_canceled_cb.Run()); | |
| 361 | |
| 362 // Destroy tracker will cancel all tasks. | |
| 363 test->tracker_.reset(); | |
| 364 EXPECT_TRUE(is_canceled_cb.Run()); | |
| 365 | |
| 366 event->Signal(); | |
| 367 } | |
| 368 | |
| 369 TEST_F(CancelableTaskTrackerTest, CheckTrackedTaskIdOnSameThread) { | |
| 370 RunOnClientAndWait(&CheckTrackedTaskIdOnSameThread_Test); | |
| 371 } | |
| 372 | |
| 373 void CheckTrackedTaskIdOnDifferentThread_Test(CancelableTaskTrackerTest* test, | |
| 374 WaitableEvent* event) { | |
| 375 CancelableTaskTracker::IsCanceledCallback is_canceled_cb; | |
| 376 test->task_id_ = test->tracker_->NewTrackedTaskId(&is_canceled_cb); | |
| 377 ASSERT_NE(CancelableTaskTracker::kBadTaskId, test->task_id_); | |
| 378 | |
| 379 // Post task to task thread. | |
| 380 test->task_thread_runner_->PostTask( | |
| 381 FROM_HERE, | |
| 382 test->IncreaseTestDataIfNotCanceledAndSignalClosure(is_canceled_cb, | |
| 383 event)); | |
| 384 is_canceled_cb.Reset(); // So the one in task thread runner is the last ref, | |
| 385 // and will be destroyed on task thread. | |
| 386 | |
| 387 test->tracker_->TryCancel(test->task_id_); | |
| 388 test->UnblockTaskThread(); | |
| 389 } | |
| 390 | |
| 391 TEST_F(CancelableTaskTrackerTest, CheckTrackedTaskIdOnDifferentThread) { | |
| 392 RunOnClientAndWait(&CheckTrackedTaskIdOnDifferentThread_Test); | |
| 393 EXPECT_EQ(0, test_data_); | |
| 394 } | 443 } | 
| 395 | 444 | 
| 396 } // namespace | 445 } // namespace | 
| OLD | NEW |