Chromium Code Reviews| Index: chrome/common/cancelable_task_tracker_unittest.cc |
| diff --git a/chrome/common/cancelable_task_tracker_unittest.cc b/chrome/common/cancelable_task_tracker_unittest.cc |
| index c6a7a8583e0cc370deab7bcfd6160087a670045b..0b3cb3179cb25ae3bc648da9efe1565780290c8f 100644 |
| --- a/chrome/common/cancelable_task_tracker_unittest.cc |
| +++ b/chrome/common/cancelable_task_tracker_unittest.cc |
| @@ -4,393 +4,442 @@ |
| #include "chrome/common/cancelable_task_tracker.h" |
| -#include "base/basictypes.h" |
| +#include <cstddef> |
| +#include <deque> |
| + |
| #include "base/bind.h" |
| -#include "base/callback.h" |
| -#include "base/memory/scoped_ptr.h" |
| -#include "base/synchronization/waitable_event.h" |
| +#include "base/bind_helpers.h" |
| +#include "base/compiler_specific.h" |
| +#include "base/location.h" |
| +#include "base/logging.h" |
| +#include "base/memory/ref_counted.h" |
| +#include "base/memory/weak_ptr.h" |
| +#include "base/message_loop.h" |
| +#include "base/run_loop.h" |
| +#include "base/task_runner.h" |
| #include "base/threading/thread.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| -using base::Bind; |
| -using base::Closure; |
| -using base::Owned; |
| -using base::TaskRunner; |
| -using base::Thread; |
| -using base::Unretained; |
| -using base::WaitableEvent; |
| - |
| namespace { |
| -class WaitableEventScoper { |
| +// Test TaskRunner implementation that simply stores posted tasks in a |
| +// queue. |
| +// |
| +// TOOD(akalin): Pull this out into its own file once something else |
| +// needs it. |
| +class FakeNonThreadSafeTaskRunner : public base::TaskRunner { |
| public: |
| - explicit WaitableEventScoper(WaitableEvent* event) : event_(event) {} |
| - ~WaitableEventScoper() { |
| - if (event_) |
| - event_->Signal(); |
| + // base::TaskRunner implementation. |
| + // 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.
|
| + virtual bool PostDelayedTask(const tracked_objects::Location& from_here, |
| + const base::Closure& task, |
| + base::TimeDelta delay) OVERRIDE { |
| + tasks_.push_back(task); |
| + return true; |
| } |
| - private: |
| - WaitableEvent* event_; |
| - DISALLOW_COPY_AND_ASSIGN(WaitableEventScoper); |
| -}; |
| -class CancelableTaskTrackerTest : public testing::Test { |
| - protected: |
| - CancelableTaskTrackerTest() |
| - : task_id_(CancelableTaskTracker::kBadTaskId), |
| - test_data_(0), |
| - task_thread_start_event_(true, false) {} |
| - |
| - virtual void SetUp() { |
| - task_thread_.reset(new Thread("task thread")); |
| - client_thread_.reset(new Thread("client thread")); |
| - task_thread_->Start(); |
| - client_thread_->Start(); |
| - |
| - task_thread_runner_ = task_thread_->message_loop_proxy(); |
| - client_thread_runner_ = client_thread_->message_loop_proxy(); |
| - |
| - // Create tracker on client thread. |
| - WaitableEvent tracker_created(true, false); |
| - client_thread_runner_->PostTask( |
| - FROM_HERE, |
| - Bind(&CancelableTaskTrackerTest::CreateTrackerOnClientThread, |
| - Unretained(this), &tracker_created)); |
| - tracker_created.Wait(); |
| - |
| - // Block server thread so we can prepare the test. |
| - task_thread_runner_->PostTask( |
| - FROM_HERE, |
| - Bind(&WaitableEvent::Wait, Unretained(&task_thread_start_event_))); |
| + virtual bool RunsTasksOnCurrentThread() const OVERRIDE { |
| + return true; |
| } |
| - virtual void TearDown() { |
| - UnblockTaskThread(); |
| - |
| - // Destroy tracker on client thread. |
| - WaitableEvent tracker_destroyed(true, false); |
| - client_thread_runner_->PostTask( |
| - FROM_HERE, |
| - Bind(&CancelableTaskTrackerTest::DestroyTrackerOnClientThread, |
| - Unretained(this), &tracker_destroyed)); |
| - |
| - // This will also wait for any pending tasks on client thread. |
| - tracker_destroyed.Wait(); |
| - |
| - client_thread_->Stop(); |
| - task_thread_->Stop(); |
| + size_t GetPendingTaskCount() const { |
| + return tasks_.size(); |
| } |
| - void RunOnClientAndWait( |
| - void (*func)(CancelableTaskTrackerTest*, WaitableEvent*)) { |
| - WaitableEvent event(true, false); |
| - client_thread_runner_->PostTask(FROM_HERE, |
| - Bind(func, Unretained(this), &event)); |
| - event.Wait(); |
| + void RunUntilIdle() { |
| + // Use a while loop since a task may post more tasks. |
| + while (!tasks_.empty()) { |
| + base::Closure task = tasks_.front(); |
| + tasks_.pop_front(); |
| + task.Run(); |
| + } |
| } |
| - public: |
| - // Client thread posts tasks and runs replies. |
| - scoped_refptr<TaskRunner> client_thread_runner_; |
| + protected: |
| + virtual ~FakeNonThreadSafeTaskRunner() {} |
| - // Task thread runs tasks. |
| - scoped_refptr<TaskRunner> task_thread_runner_; |
| + private: |
| + std::deque<base::Closure> tasks_; |
| +}; |
| - // |tracker_| can only live on client thread. |
| - scoped_ptr<CancelableTaskTracker> tracker_; |
| +class CancelableTaskTrackerTest : public testing::Test { |
| + protected: |
| + virtual ~CancelableTaskTrackerTest() { |
| + base::RunLoop run_loop; |
| + run_loop.RunUntilIdle(); |
| + } |
| - CancelableTaskTracker::TaskId task_id_; |
| + private: |
| + // 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
|
| + MessageLoop message_loop_; |
| - void UnblockTaskThread() { |
| - task_thread_start_event_.Signal(); |
| - } |
| + protected: |
| + CancelableTaskTracker task_tracker_; |
| +}; |
| - ////////////////////////////////////////////////////////////////////////////// |
| - // Testing data and related functions |
| - int test_data_; // Defaults to 0. |
| +void AddFailureAt(const tracked_objects::Location& location) { |
| + ADD_FAILURE_AT(location.file_name(), location.line_number()); |
| +} |
| - Closure IncreaseTestDataAndSignalClosure(WaitableEvent* event) { |
| - return Bind(&CancelableTaskTrackerTest::IncreaseDataAndSignal, |
| - &test_data_, event); |
| - } |
| +// Returns a closure that fails if run. |
| +base::Closure MakeExpectedNotRunClosure( |
| + const tracked_objects::Location& location) { |
| + return base::Bind(&AddFailureAt, location); |
| +} |
| - Closure IncreaseTestDataIfNotCanceledAndSignalClosure( |
| - const CancelableTaskTracker::IsCanceledCallback& is_canceled_cb, |
| - WaitableEvent* event) { |
| - return Bind(&CancelableTaskTrackerTest::IncreaseDataIfNotCanceledAndSignal, |
| - &test_data_, is_canceled_cb, event); |
| +// A helper class for MakeExpectedRunClosure() that fails if it is |
| +// destroyed without Run() having been called. This class may be used |
| +// from multiple threads as long as Run() is called at most once |
| +// before destruction. |
| +class RunChecker { |
| + public: |
| + explicit RunChecker(const tracked_objects::Location& location) |
| + : location_(location), |
| + called_(false) {} |
| + |
| + ~RunChecker() { |
| + if (!called_) { |
| + ADD_FAILURE_AT(location_.file_name(), location_.line_number()); |
| + } |
| } |
| - Closure DecreaseTestDataClosure(WaitableEvent* event) { |
| - return Bind(&CancelableTaskTrackerTest::DecreaseData, |
| - Owned(new WaitableEventScoper(event)), &test_data_); |
| + void Run() { |
| + called_ = true; |
| } |
| private: |
| - void CreateTrackerOnClientThread(WaitableEvent* event) { |
| - tracker_.reset(new CancelableTaskTracker()); |
| - event->Signal(); |
| - } |
| - |
| - void DestroyTrackerOnClientThread(WaitableEvent* event) { |
| - tracker_.reset(); |
| - event->Signal(); |
| - } |
| + tracked_objects::Location location_; |
| + bool called_; |
| +}; |
| - static void IncreaseDataAndSignal(int* data, WaitableEvent* event) { |
| - (*data)++; |
| - if (event) |
| - event->Signal(); |
| - } |
| +// Returns a closure that fails on destruction if it hasn't been run. |
| +base::Closure MakeExpectedRunClosure( |
| + const tracked_objects::Location& location) { |
| + return base::Bind(&RunChecker::Run, base::Owned(new RunChecker(location))); |
| +} |
| - static void IncreaseDataIfNotCanceledAndSignal( |
| - int* data, |
| - const CancelableTaskTracker::IsCanceledCallback& is_canceled_cb, |
| - WaitableEvent* event) { |
| - if (!is_canceled_cb.Run()) |
| - (*data)++; |
| - if (event) |
| - event->Signal(); |
| - } |
| +// With the task tracker, post a task, a task with a reply, and get a |
| +// new task id without canceling any of them. The tasks and the reply |
| +// should run and the "is canceled" callback should return false. |
| +TEST_F(CancelableTaskTrackerTest, NoCancel) { |
| + base::Thread worker_thread("worker thread"); |
| + ASSERT_TRUE(worker_thread.Start()); |
| - static void DecreaseData(WaitableEventScoper* event_scoper, int* data) { |
| - (*data) -= 2; |
| - } |
| + ignore_result( |
| + task_tracker_.PostTask( |
| + worker_thread.message_loop_proxy(), |
| + FROM_HERE, |
| + MakeExpectedRunClosure(FROM_HERE))); |
| - scoped_ptr<Thread> client_thread_; |
| - scoped_ptr<Thread> task_thread_; |
| + ignore_result( |
| + task_tracker_.PostTaskAndReply( |
| + worker_thread.message_loop_proxy(), |
| + FROM_HERE, |
| + MakeExpectedRunClosure(FROM_HERE), |
| + MakeExpectedRunClosure(FROM_HERE))); |
| - WaitableEvent task_thread_start_event_; |
| -}; |
| + CancelableTaskTracker::IsCanceledCallback is_canceled; |
| + ignore_result(task_tracker_.NewTrackedTaskId(&is_canceled)); |
| -#if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) && GTEST_HAS_DEATH_TEST |
| + worker_thread.Stop(); |
| -typedef CancelableTaskTrackerTest CancelableTaskTrackerDeathTest; |
| + base::RunLoop run_loop; |
| + 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
|
| -TEST_F(CancelableTaskTrackerDeathTest, PostFromDifferentThread) { |
| - // The default style "fast" does not support multi-threaded tests. |
| - ::testing::FLAGS_gtest_death_test_style = "threadsafe"; |
| - |
| - EXPECT_DEATH( |
| - tracker_->PostTask(task_thread_runner_, |
| - FROM_HERE, |
| - DecreaseTestDataClosure(NULL)), |
| - ""); |
| + EXPECT_FALSE(is_canceled.Run()); |
| } |
| -void CancelOnDifferentThread_Test(CancelableTaskTrackerTest* test, |
| - WaitableEvent* event) { |
| - test->task_id_ = test->tracker_->PostTask( |
| - test->task_thread_runner_, |
| - FROM_HERE, |
| - test->DecreaseTestDataClosure(event)); |
| - EXPECT_NE(CancelableTaskTracker::kBadTaskId, test->task_id_); |
| +// Post a task with the task tracker but cancel it before running the |
| +// task runner. The task should not run. |
| +TEST_F(CancelableTaskTrackerTest, CancelPostedTask) { |
| + scoped_refptr<FakeNonThreadSafeTaskRunner> fake_task_runner( |
| + new FakeNonThreadSafeTaskRunner()); |
| - // Canceling a non-existed task is noop. |
| - test->tracker_->TryCancel(test->task_id_ + 1); |
| + CancelableTaskTracker::TaskId task_id = |
| + task_tracker_.PostTask( |
| + fake_task_runner.get(), |
| + FROM_HERE, |
| + MakeExpectedNotRunClosure(FROM_HERE)); |
| + EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id); |
| - test->UnblockTaskThread(); |
| + EXPECT_EQ(1U, fake_task_runner->GetPendingTaskCount()); |
| + |
| + task_tracker_.TryCancel(task_id); |
| + |
| + fake_task_runner->RunUntilIdle(); |
| } |
| -TEST_F(CancelableTaskTrackerDeathTest, CancelOnDifferentThread) { |
| - // The default style "fast" does not support multi-threaded tests. |
| - ::testing::FLAGS_gtest_death_test_style = "threadsafe"; |
| +// Post a task with reply with the task tracker and cancel it before |
| +// running the task runner. Neither the task nor the reply should |
| +// run. |
| +TEST_F(CancelableTaskTrackerTest, CancelPostedTaskAndReply) { |
| + scoped_refptr<FakeNonThreadSafeTaskRunner> fake_task_runner( |
| + new FakeNonThreadSafeTaskRunner()); |
| - // Post a task and we'll try canceling it on a different thread. |
| - RunOnClientAndWait(&CancelOnDifferentThread_Test); |
| + CancelableTaskTracker::TaskId task_id = |
| + task_tracker_.PostTaskAndReply( |
| + fake_task_runner.get(), |
| + FROM_HERE, |
| + MakeExpectedNotRunClosure(FROM_HERE), |
| + MakeExpectedNotRunClosure(FROM_HERE)); |
| + EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id); |
| - // Canceling on the wrong thread. |
| - EXPECT_DEATH(tracker_->TryCancel(task_id_), ""); |
| + task_tracker_.TryCancel(task_id); |
| - // Even canceling a non-existant task will crash. |
| - EXPECT_DEATH(tracker_->TryCancel(task_id_ + 1), ""); |
| + fake_task_runner->RunUntilIdle(); |
| } |
| -void TrackerCancelAllOnDifferentThread_Test( |
| - CancelableTaskTrackerTest* test, WaitableEvent* event) { |
| - test->task_id_ = test->tracker_->PostTask( |
| - test->task_thread_runner_, |
| - FROM_HERE, |
| - test->DecreaseTestDataClosure(event)); |
| - EXPECT_NE(CancelableTaskTracker::kBadTaskId, test->task_id_); |
| - test->UnblockTaskThread(); |
| -} |
| +// Post a task with reply with the task tracker and cancel it after |
| +// running the task runner but before running the current message |
| +// loop. The task should run but the reply should not. |
| +TEST_F(CancelableTaskTrackerTest, CancelReply) { |
| + scoped_refptr<FakeNonThreadSafeTaskRunner> fake_task_runner( |
| + new FakeNonThreadSafeTaskRunner()); |
| -TEST_F(CancelableTaskTrackerDeathTest, TrackerCancelAllOnDifferentThread) { |
| - // The default style "fast" does not support multi-threaded tests. |
| - ::testing::FLAGS_gtest_death_test_style = "threadsafe"; |
| + CancelableTaskTracker::TaskId task_id = |
| + task_tracker_.PostTaskAndReply( |
| + fake_task_runner.get(), |
| + FROM_HERE, |
| + MakeExpectedRunClosure(FROM_HERE), |
| + MakeExpectedNotRunClosure(FROM_HERE)); |
| + EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id); |
| - // |tracker_| can only live on client thread. |
| - EXPECT_DEATH(tracker_.reset(), ""); |
| + fake_task_runner->RunUntilIdle(); |
| - RunOnClientAndWait(&TrackerCancelAllOnDifferentThread_Test); |
| + task_tracker_.TryCancel(task_id); |
| - EXPECT_DEATH(tracker_->TryCancelAll(), ""); |
| - EXPECT_DEATH(tracker_.reset(), ""); |
| + base::RunLoop run_loop; |
| + run_loop.RunUntilIdle(); |
|
kaiwang
2012/11/22 02:05:54
same here
akalin
2012/11/22 07:00:26
Done.
|
| } |
| -#endif // (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) && |
| - // GTEST_HAS_DEATH_TEST |
| +// Post a task with reply with the task tracker on a worker thread and |
| +// cancel it before running the current message loop. The task should |
| +// run but the reply should not. |
| +TEST_F(CancelableTaskTrackerTest, CancelReplyDifferentThread) { |
| + base::Thread worker_thread("worker thread"); |
| + ASSERT_TRUE(worker_thread.Start()); |
| -void Canceled_Test(CancelableTaskTrackerTest* test, WaitableEvent* event) { |
| - test->task_id_ = test->tracker_->PostTask( |
| - test->task_thread_runner_, |
| - FROM_HERE, |
| - test->DecreaseTestDataClosure(event)); |
| - EXPECT_NE(CancelableTaskTracker::kBadTaskId, test->task_id_); |
| + CancelableTaskTracker::TaskId task_id = |
| + task_tracker_.PostTaskAndReply( |
| + worker_thread.message_loop_proxy(), |
| + FROM_HERE, |
| + base::Bind(&base::DoNothing), |
| + MakeExpectedNotRunClosure(FROM_HERE)); |
| + EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id); |
| - test->tracker_->TryCancel(test->task_id_); |
| - test->UnblockTaskThread(); |
| -} |
| + task_tracker_.TryCancel(task_id); |
| -TEST_F(CancelableTaskTrackerTest, Canceled) { |
| - RunOnClientAndWait(&Canceled_Test); |
| - EXPECT_EQ(0, test_data_); |
| + worker_thread.Stop(); |
| + |
| + base::RunLoop run_loop; |
| + run_loop.RunUntilIdle(); |
| } |
| -void SignalAndWaitThenIncrease(WaitableEvent* start_event, |
| - WaitableEvent* continue_event, |
| - int* data) { |
| - start_event->Signal(); |
| - continue_event->Wait(); |
| - (*data)++; |
| +void ExpectIsCanceled( |
| + const CancelableTaskTracker::IsCanceledCallback& is_canceled, |
| + bool expected_is_canceled) { |
| + EXPECT_EQ(expected_is_canceled, is_canceled.Run()); |
| } |
| -void CancelWhileTaskRunning_Test(CancelableTaskTrackerTest* test, |
| - WaitableEvent* event) { |
| - WaitableEvent task_start_event(true, false); |
| - WaitableEvent* task_continue_event = new WaitableEvent(true, false); |
| +// Create a new task ID and check its status on a separate thread |
| +// before and after canceling. The is-canceled callback should be |
| +// thread-safe (i.e., nothing should blow up). |
| +TEST_F(CancelableTaskTrackerTest, NewTrackedTaskIdDifferentThread) { |
| + CancelableTaskTracker::IsCanceledCallback is_canceled; |
| + CancelableTaskTracker::TaskId task_id = |
| + task_tracker_.NewTrackedTaskId(&is_canceled); |
| - test->task_id_ = test->tracker_->PostTaskAndReply( |
| - test->task_thread_runner_, |
| - FROM_HERE, |
| - Bind(&SignalAndWaitThenIncrease, |
| - &task_start_event, Owned(task_continue_event), &test->test_data_), |
| - test->DecreaseTestDataClosure(event)); |
| - EXPECT_NE(CancelableTaskTracker::kBadTaskId, test->task_id_); |
| + EXPECT_FALSE(is_canceled.Run()); |
| - test->UnblockTaskThread(); |
| - task_start_event.Wait(); |
| + base::Thread other_thread("other thread"); |
| + ASSERT_TRUE(other_thread.Start()); |
| + other_thread.message_loop_proxy()->PostTask( |
| + FROM_HERE, |
| + base::Bind(&ExpectIsCanceled, is_canceled, false)); |
| + other_thread.Stop(); |
| - // Now task is running. Let's try to cancel. |
| - test->tracker_->TryCancel(test->task_id_); |
| + task_tracker_.TryCancel(task_id); |
| - // Let task continue. |
| - task_continue_event->Signal(); |
| + ASSERT_TRUE(other_thread.Start()); |
| + other_thread.message_loop_proxy()->PostTask( |
| + FROM_HERE, |
| + base::Bind(&ExpectIsCanceled, is_canceled, true)); |
| + other_thread.Stop(); |
| } |
| -TEST_F(CancelableTaskTrackerTest, CancelWhileTaskRunning) { |
| - RunOnClientAndWait(&CancelWhileTaskRunning_Test); |
| +// With the task tracker, post a task, a task with a reply, get a new |
| +// task id, and then cancel all of them. None of the tasks nor the |
| +// reply should run and the "is canceled" callback should return |
| +// true. |
| +TEST_F(CancelableTaskTrackerTest, CancelAll) { |
| + scoped_refptr<FakeNonThreadSafeTaskRunner> fake_task_runner( |
| + new FakeNonThreadSafeTaskRunner()); |
| - // Task will continue running but reply will be canceled. |
| - EXPECT_EQ(1, test_data_); |
| -} |
| + ignore_result( |
| + task_tracker_.PostTask( |
| + fake_task_runner, |
| + FROM_HERE, |
| + MakeExpectedNotRunClosure(FROM_HERE))); |
| -void NotCanceled_Test(CancelableTaskTrackerTest* test, WaitableEvent* event) { |
| - test->task_id_ = test->tracker_->PostTaskAndReply( |
| - test->task_thread_runner_, |
| - FROM_HERE, |
| - test->IncreaseTestDataAndSignalClosure(NULL), |
| - test->DecreaseTestDataClosure(event)); |
| - EXPECT_NE(CancelableTaskTracker::kBadTaskId, test->task_id_); |
| + ignore_result( |
| + task_tracker_.PostTaskAndReply( |
| + fake_task_runner, |
| + FROM_HERE, |
| + MakeExpectedNotRunClosure(FROM_HERE), |
| + MakeExpectedNotRunClosure(FROM_HERE))); |
| - test->UnblockTaskThread(); |
| -} |
| + CancelableTaskTracker::IsCanceledCallback is_canceled; |
| + ignore_result(task_tracker_.NewTrackedTaskId(&is_canceled)); |
| -TEST_F(CancelableTaskTrackerTest, NotCanceled) { |
| - RunOnClientAndWait(&NotCanceled_Test); |
| - EXPECT_EQ(-1, test_data_); |
| -} |
| + task_tracker_.TryCancelAll(); |
| -void TrackerDestructed_Test(CancelableTaskTrackerTest* test, |
| - WaitableEvent* event) { |
| - test->task_id_ = test->tracker_->PostTaskAndReply( |
| - test->task_thread_runner_, |
| - FROM_HERE, |
| - test->IncreaseTestDataAndSignalClosure(NULL), |
| - test->DecreaseTestDataClosure(event)); |
| - EXPECT_NE(CancelableTaskTracker::kBadTaskId, test->task_id_); |
| + fake_task_runner->RunUntilIdle(); |
| - test->tracker_.reset(); |
| - test->UnblockTaskThread(); |
| -} |
| + base::RunLoop run_loop; |
| + run_loop.RunUntilIdle(); |
| -TEST_F(CancelableTaskTrackerTest, TrackerDestructed) { |
| - RunOnClientAndWait(&TrackerDestructed_Test); |
| - EXPECT_EQ(0, test_data_); |
| + EXPECT_TRUE(is_canceled.Run()); |
| } |
| -void TrackerDestructedAfterTask_Test(CancelableTaskTrackerTest* test, |
| - WaitableEvent* event) { |
| - WaitableEvent task_done_event(true, false); |
| - test->task_id_ = test->tracker_->PostTaskAndReply( |
| - test->task_thread_runner_, |
| - FROM_HERE, |
| - test->IncreaseTestDataAndSignalClosure(&task_done_event), |
| - test->DecreaseTestDataClosure(event)); |
| - ASSERT_NE(CancelableTaskTracker::kBadTaskId, test->task_id_); |
| +// With the task tracker, post a task, a task with a reply, get a new |
| +// task id, and then cancel all of them. None of the tasks nor the |
| +// reply should run and the "is canceled" callback should return |
| +// true. |
| +TEST_F(CancelableTaskTrackerTest, DestructionCancelsAll) { |
| + scoped_refptr<FakeNonThreadSafeTaskRunner> fake_task_runner( |
| + new FakeNonThreadSafeTaskRunner()); |
| + |
| + CancelableTaskTracker::IsCanceledCallback is_canceled; |
| + |
| + { |
| + // Create another task tracker with a smaller scope. |
| + CancelableTaskTracker task_tracker; |
| + |
| + ignore_result( |
| + task_tracker.PostTask( |
| + fake_task_runner, |
| + FROM_HERE, |
| + MakeExpectedNotRunClosure(FROM_HERE))); |
| + |
| + ignore_result( |
| + task_tracker.PostTaskAndReply( |
| + fake_task_runner, |
| + FROM_HERE, |
| + MakeExpectedNotRunClosure(FROM_HERE), |
| + MakeExpectedNotRunClosure(FROM_HERE))); |
| + |
| + ignore_result(task_tracker_.NewTrackedTaskId(&is_canceled)); |
| + } |
| - test->UnblockTaskThread(); |
| + fake_task_runner->RunUntilIdle(); |
| - task_done_event.Wait(); |
| + base::RunLoop run_loop; |
| + run_loop.RunUntilIdle(); |
| - // At this point, task is already finished on task thread but reply has not |
| - // started yet (because this function is still running on client thread). |
| - // Now delete the tracker to cancel reply. |
| - test->tracker_.reset(); |
| + EXPECT_FALSE(is_canceled.Run()); |
| } |
| -TEST_F(CancelableTaskTrackerTest, TrackerDestructedAfterTask) { |
| - RunOnClientAndWait(&TrackerDestructedAfterTask_Test); |
| - EXPECT_EQ(1, test_data_); |
| -} |
| +// The death tests below make sure that calling task tracker member |
| +// functions from a thread different from its owner thread DCHECKs in |
| +// debug mode. |
| -void CheckTrackedTaskIdOnSameThread_Test(CancelableTaskTrackerTest* test, |
| - WaitableEvent* event) { |
| - CancelableTaskTracker::IsCanceledCallback is_canceled_cb; |
| - test->task_id_ = test->tracker_->NewTrackedTaskId(&is_canceled_cb); |
| - ASSERT_NE(CancelableTaskTracker::kBadTaskId, test->task_id_); |
| +class CancelableTaskTrackerDeathTest : public CancelableTaskTrackerTest { |
| + protected: |
| + CancelableTaskTrackerDeathTest() { |
| + // The default style "fast" does not support multi-threaded tests. |
| + ::testing::FLAGS_gtest_death_test_style = "threadsafe"; |
| + } |
| - EXPECT_FALSE(is_canceled_cb.Run()); |
| + virtual ~CancelableTaskTrackerDeathTest() {} |
| +}; |
| - test->tracker_->TryCancel(test->task_id_); |
| - EXPECT_TRUE(is_canceled_cb.Run()); |
| +// Duplicated from base/threading/thread_checker.h so that we can be |
| +// good citizens there and undef the macro. |
| +#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON) |
| +#define ENABLE_THREAD_CHECKER 1 |
| +#else |
| +#define ENABLE_THREAD_CHECKER 0 |
| +#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
|
| + |
| +// Runs |fn| with |task_tracker|, expecting it to crash in debug mode. |
| +void MaybeRunDeadlyTaskTrackerMemberFunction( |
| + CancelableTaskTracker* task_tracker, |
| + const base::Callback<void(CancelableTaskTracker*)>& fn) { |
| + // CancelableTask uses DCHECKs with its ThreadChecker (itself only |
| + // enabled in debug mode). |
| +#if ENABLE_THREAD_CHECKER |
| + EXPECT_DEATH_IF_SUPPORTED(fn.Run(task_tracker), ""); |
| +#endif |
| +} |
| - test->task_id_ = test->tracker_->NewTrackedTaskId(&is_canceled_cb); |
| - EXPECT_FALSE(is_canceled_cb.Run()); |
| +void PostDoNothingTask(CancelableTaskTracker* task_tracker) { |
| + ignore_result( |
| + task_tracker->PostTask( |
| + scoped_refptr<FakeNonThreadSafeTaskRunner>( |
| + new FakeNonThreadSafeTaskRunner()), |
| + FROM_HERE, base::Bind(&base::DoNothing))); |
| +} |
| - // Destroy tracker will cancel all tasks. |
| - test->tracker_.reset(); |
| - EXPECT_TRUE(is_canceled_cb.Run()); |
| +TEST_F(CancelableTaskTrackerDeathTest, PostFromDifferentThread) { |
| + base::Thread bad_thread("bad thread"); |
| + ASSERT_TRUE(bad_thread.Start()); |
| - event->Signal(); |
| + bad_thread.message_loop_proxy()->PostTask( |
| + FROM_HERE, |
| + base::Bind(&MaybeRunDeadlyTaskTrackerMemberFunction, |
| + base::Unretained(&task_tracker_), |
| + base::Bind(&PostDoNothingTask))); |
| } |
| -TEST_F(CancelableTaskTrackerTest, CheckTrackedTaskIdOnSameThread) { |
| - RunOnClientAndWait(&CheckTrackedTaskIdOnSameThread_Test); |
| +void TryCancel(CancelableTaskTracker::TaskId task_id, |
| + CancelableTaskTracker* task_tracker) { |
| + task_tracker->TryCancel(task_id); |
| } |
| -void CheckTrackedTaskIdOnDifferentThread_Test(CancelableTaskTrackerTest* test, |
| - WaitableEvent* event) { |
| - CancelableTaskTracker::IsCanceledCallback is_canceled_cb; |
| - test->task_id_ = test->tracker_->NewTrackedTaskId(&is_canceled_cb); |
| - ASSERT_NE(CancelableTaskTracker::kBadTaskId, test->task_id_); |
| +TEST_F(CancelableTaskTrackerDeathTest, CancelOnDifferentThread) { |
| + scoped_refptr<FakeNonThreadSafeTaskRunner> fake_task_runner( |
| + new FakeNonThreadSafeTaskRunner()); |
| + |
| + base::Thread bad_thread("bad thread"); |
| + ASSERT_TRUE(bad_thread.Start()); |
| - // Post task to task thread. |
| - test->task_thread_runner_->PostTask( |
| + CancelableTaskTracker::TaskId task_id = |
| + task_tracker_.PostTask( |
| + fake_task_runner.get(), |
| + FROM_HERE, |
| + base::Bind(&base::DoNothing)); |
| + EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id); |
| + |
| + bad_thread.message_loop_proxy()->PostTask( |
| FROM_HERE, |
| - test->IncreaseTestDataIfNotCanceledAndSignalClosure(is_canceled_cb, |
| - event)); |
| - is_canceled_cb.Reset(); // So the one in task thread runner is the last ref, |
| - // and will be destroyed on task thread. |
| + base::Bind(&MaybeRunDeadlyTaskTrackerMemberFunction, |
| + base::Unretained(&task_tracker_), |
| + base::Bind(&TryCancel, task_id))); |
| - test->tracker_->TryCancel(test->task_id_); |
| - test->UnblockTaskThread(); |
| + fake_task_runner->RunUntilIdle(); |
| } |
| -TEST_F(CancelableTaskTrackerTest, CheckTrackedTaskIdOnDifferentThread) { |
| - RunOnClientAndWait(&CheckTrackedTaskIdOnDifferentThread_Test); |
| - EXPECT_EQ(0, test_data_); |
| +TEST_F(CancelableTaskTrackerDeathTest, CancelAllOnDifferentThread) { |
| + scoped_refptr<FakeNonThreadSafeTaskRunner> fake_task_runner( |
| + new FakeNonThreadSafeTaskRunner()); |
| + |
| + base::Thread bad_thread("bad thread"); |
| + ASSERT_TRUE(bad_thread.Start()); |
| + |
| + CancelableTaskTracker::TaskId task_id = |
| + task_tracker_.PostTask( |
| + fake_task_runner.get(), |
| + FROM_HERE, |
| + base::Bind(&base::DoNothing)); |
| + EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id); |
| + |
| + bad_thread.message_loop_proxy()->PostTask( |
| + FROM_HERE, |
| + base::Bind(&MaybeRunDeadlyTaskTrackerMemberFunction, |
| + base::Unretained(&task_tracker_), |
| + base::Bind(&CancelableTaskTracker::TryCancelAll))); |
| + |
| + fake_task_runner->RunUntilIdle(); |
| } |
| } // namespace |