| 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..4c76fdd825e28ed35263bb44773f04648de01a34 100644
|
| --- a/chrome/common/cancelable_task_tracker_unittest.cc
|
| +++ b/chrome/common/cancelable_task_tracker_unittest.cc
|
| @@ -4,393 +4,435 @@
|
|
|
| #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|.
|
| + 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_;
|
| + CancelableTaskTracker task_tracker_;
|
|
|
| - void UnblockTaskThread() {
|
| - task_thread_start_event_.Signal();
|
| - }
|
| + private:
|
| + // Needed by CancelableTaskTracker methods.
|
| + MessageLoop message_loop_;
|
| +};
|
|
|
| - //////////////////////////////////////////////////////////////////////////////
|
| - // 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();
|
|
|
| -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());
|
|
|
| -TEST_F(CancelableTaskTrackerDeathTest, CancelOnDifferentThread) {
|
| - // The default style "fast" does not support multi-threaded tests.
|
| - ::testing::FLAGS_gtest_death_test_style = "threadsafe";
|
| + task_tracker_.TryCancel(task_id);
|
|
|
| - // Post a task and we'll try canceling it on a different thread.
|
| - RunOnClientAndWait(&CancelOnDifferentThread_Test);
|
| + fake_task_runner->RunUntilIdle();
|
| +}
|
|
|
| - // Canceling on the wrong thread.
|
| - EXPECT_DEATH(tracker_->TryCancel(task_id_), "");
|
| +// 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());
|
|
|
| - // Even canceling a non-existant task will crash.
|
| - EXPECT_DEATH(tracker_->TryCancel(task_id_ + 1), "");
|
| -}
|
| + 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);
|
|
|
| -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();
|
| + task_tracker_.TryCancel(task_id);
|
| +
|
| + fake_task_runner->RunUntilIdle();
|
| }
|
|
|
| -TEST_F(CancelableTaskTrackerDeathTest, TrackerCancelAllOnDifferentThread) {
|
| - // 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 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());
|
|
|
| - // |tracker_| can only live on client thread.
|
| - EXPECT_DEATH(tracker_.reset(), "");
|
| + 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);
|
|
|
| - RunOnClientAndWait(&TrackerCancelAllOnDifferentThread_Test);
|
| + fake_task_runner->RunUntilIdle();
|
|
|
| - EXPECT_DEATH(tracker_->TryCancelAll(), "");
|
| - EXPECT_DEATH(tracker_.reset(), "");
|
| + task_tracker_.TryCancel(task_id);
|
| }
|
|
|
| -#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();
|
| }
|
|
|
| -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
|
| +
|
| +// 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
|
|
|