diff options
author | kaiwang@chromium.org <kaiwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-03 07:21:03 +0000 |
---|---|---|
committer | kaiwang@chromium.org <kaiwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-03 07:21:03 +0000 |
commit | 62867b07596aec5ca3b7d20c5e6d6024706ba0f1 (patch) | |
tree | 9e31feae30f9d34788384a9d15edd031dd471024 /chrome/common/cancelable_task_tracker_unittest.cc | |
parent | fa07151133b425cd817be30d9bc88a4ca11f5a32 (diff) | |
download | chromium_src-62867b07596aec5ca3b7d20c5e6d6024706ba0f1.zip chromium_src-62867b07596aec5ca3b7d20c5e6d6024706ba0f1.tar.gz chromium_src-62867b07596aec5ca3b7d20c5e6d6024706ba0f1.tar.bz2 |
Cancelable task posting with TaskRunner. This to replace chrome/browser/common/cancelable_request.*
Convert TopSitesBackend::GetMostVisitedThumbnails as a sample.
BUG=155883
Review URL: https://chromiumcodereview.appspot.com/11198024
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@165849 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/common/cancelable_task_tracker_unittest.cc')
-rw-r--r-- | chrome/common/cancelable_task_tracker_unittest.cc | 328 |
1 files changed, 328 insertions, 0 deletions
diff --git a/chrome/common/cancelable_task_tracker_unittest.cc b/chrome/common/cancelable_task_tracker_unittest.cc new file mode 100644 index 0000000..60315bc --- /dev/null +++ b/chrome/common/cancelable_task_tracker_unittest.cc @@ -0,0 +1,328 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/cancelable_task_tracker.h" + +#include "base/basictypes.h" +#include "base/bind.h" +#include "base/callback.h" +#include "base/memory/scoped_ptr.h" +#include "base/synchronization/waitable_event.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 { + public: + explicit WaitableEventScoper(WaitableEvent* event) : event_(event) {} + ~WaitableEventScoper() { + if (event_) + event_->Signal(); + } + 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 void TearDown() { + UnblockTaskThread(); + + // Create tracker on client thread. + WaitableEvent tracker_destroyed(true, false); + client_thread_runner_->PostTask( + FROM_HERE, + Bind(&CancelableTaskTrackerTest::DestroyTrackerOnClientThread, + Unretained(this), &tracker_destroyed)); + tracker_destroyed.Wait(); + + client_thread_->Stop(); + task_thread_->Stop(); + } + + void RunOnClientAndWait( + void (*func)(CancelableTaskTrackerTest*, WaitableEvent*)) { + WaitableEvent event(true, false); + client_thread_runner_->PostTask(FROM_HERE, + Bind(func, Unretained(this), &event)); + event.Wait(); + } + + public: + // Client thread posts tasks and runs replies. + scoped_refptr<TaskRunner> client_thread_runner_; + + // Task thread runs tasks. + scoped_refptr<TaskRunner> task_thread_runner_; + + // |tracker_| can only live on client thread. + scoped_ptr<CancelableTaskTracker> tracker_; + + CancelableTaskTracker::TaskId task_id_; + + void UnblockTaskThread() { + task_thread_start_event_.Signal(); + } + + ////////////////////////////////////////////////////////////////////////////// + // Testing data and related functions + int test_data_; // Defaults to 0. + + Closure IncreaseTestDataAndSignalClosure(WaitableEvent* event) { + return Bind(&CancelableTaskTrackerTest::IncreaseDataAndSignal, + &test_data_, event); + } + + Closure DecreaseTestDataClosure(WaitableEvent* event) { + return Bind(&CancelableTaskTrackerTest::DecreaseData, + Owned(new WaitableEventScoper(event)), &test_data_); + } + + private: + void CreateTrackerOnClientThread(WaitableEvent* event) { + tracker_.reset(new CancelableTaskTracker()); + event->Signal(); + } + + void DestroyTrackerOnClientThread(WaitableEvent* event) { + tracker_.reset(); + event->Signal(); + } + + static void IncreaseDataAndSignal(int* data, WaitableEvent* event) { + (*data)++; + if (event) + event->Signal(); + } + + static void DecreaseData(WaitableEventScoper* event_scoper, int* data) { + (*data) -= 2; + } + + scoped_ptr<Thread> client_thread_; + scoped_ptr<Thread> task_thread_; + + WaitableEvent task_thread_start_event_; +}; + +#if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) && GTEST_HAS_DEATH_TEST +typedef CancelableTaskTrackerTest CancelableTaskTrackerDeathTest; + +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)), + ""); +} + +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_); + + // Canceling a non-existed task is noop. + test->tracker_->TryCancel(test->task_id_ + 1); + + test->UnblockTaskThread(); +} + +TEST_F(CancelableTaskTrackerDeathTest, CancelOnDifferentThread) { + // The default style "fast" does not support multi-threaded tests. + ::testing::FLAGS_gtest_death_test_style = "threadsafe"; + + // Post a task and we'll try canceling it on a different thread. + RunOnClientAndWait(&CancelOnDifferentThread_Test); + + // Canceling on the wrong thread. + EXPECT_DEATH(tracker_->TryCancel(task_id_), ""); + + // Even canceling a non-existant task will crash. + EXPECT_DEATH(tracker_->TryCancel(task_id_ + 1), ""); +} + +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(); +} + +TEST_F(CancelableTaskTrackerDeathTest, TrackerCancelAllOnDifferentThread) { + // The default style "fast" does not support multi-threaded tests. + ::testing::FLAGS_gtest_death_test_style = "threadsafe"; + + // |tracker_| can only live on client thread. + EXPECT_DEATH(tracker_.reset(), ""); + + RunOnClientAndWait(&TrackerCancelAllOnDifferentThread_Test); + + EXPECT_DEATH(tracker_->TryCancelAll(), ""); + EXPECT_DEATH(tracker_.reset(), ""); +} + +#endif // (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) && + // GTEST_HAS_DEATH_TEST + +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_); + + test->tracker_->TryCancel(test->task_id_); + test->UnblockTaskThread(); +} + +TEST_F(CancelableTaskTrackerTest, Canceled) { + RunOnClientAndWait(&Canceled_Test); + EXPECT_EQ(0, test_data_); +} + +void SignalAndWaitThenIncrease(WaitableEvent* start_event, + WaitableEvent* continue_event, + int* data) { + start_event->Signal(); + continue_event->Wait(); + (*data)++; +} + +void CancelWhileTaskRunning_Test(CancelableTaskTrackerTest* test, + WaitableEvent* event) { + WaitableEvent task_start_event(true, false); + WaitableEvent* task_continue_event = new WaitableEvent(true, false); + + 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_); + + test->UnblockTaskThread(); + task_start_event.Wait(); + + // Now task is running. Let's try to cancel. + test->tracker_->TryCancel(test->task_id_); + + // Let task continue. + task_continue_event->Signal(); +} + +TEST_F(CancelableTaskTrackerTest, CancelWhileTaskRunning) { + RunOnClientAndWait(&CancelWhileTaskRunning_Test); + + // Task will continue running but reply will be canceled. + EXPECT_EQ(1, test_data_); +} + +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_); + + test->UnblockTaskThread(); +} + +TEST_F(CancelableTaskTrackerTest, NotCanceled) { + RunOnClientAndWait(&NotCanceled_Test); + EXPECT_EQ(-1, test_data_); +} + +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_); + + test->tracker_.reset(); + test->UnblockTaskThread(); +} + +TEST_F(CancelableTaskTrackerTest, TrackerDestructed) { + RunOnClientAndWait(&TrackerDestructed_Test); + EXPECT_EQ(0, test_data_); +} + +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_); + + test->UnblockTaskThread(); + + task_done_event.Wait(); + + // 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(); +} + +TEST_F(CancelableTaskTrackerTest, TrackerDestructedAfterTask) { + RunOnClientAndWait(&TrackerDestructedAfterTask_Test); + EXPECT_EQ(1, test_data_); +} + +} // namespace |