Chromium Code Reviews| Index: sync/internal_api/public/base/cancelation_signal_unittest.cc |
| diff --git a/sync/internal_api/public/base/cancelation_signal_unittest.cc b/sync/internal_api/public/base/cancelation_signal_unittest.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..6ae558c0570f605eefaa642e3e0ce5c2ab877802 |
| --- /dev/null |
| +++ b/sync/internal_api/public/base/cancelation_signal_unittest.cc |
| @@ -0,0 +1,168 @@ |
| +// Copyright 2013 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 "sync/internal_api/public/base/cancelation_signal.h" |
| + |
| +#include "base/bind.h" |
| +#include "base/message_loop/message_loop.h" |
| +#include "base/synchronization/waitable_event.h" |
| +#include "base/threading/thread.h" |
| +#include "sync/internal_api/public/base/cancelation_observer.h" |
| +#include "testing/gtest/include/gtest/gtest.h" |
| + |
| +namespace syncer { |
| + |
| +class BlockingTask : public CancelationObserver { |
| + public: |
| + BlockingTask(CancelationSignal* cancel_signal); |
| + virtual ~BlockingTask(); |
| + |
| + // Starts the |exec_thread_| and uses it to execute DoRun(). |
| + void RunAsync(base::WaitableEvent* task_done_signal); |
| + |
| + // Blocks until canceled. Signals |task_done_signal| when finished. |
| + void Run(base::WaitableEvent* task_done_signal); |
| + |
| + // Implementation of CancelationObserver. |
| + // Wakes up the thread blocked in Run(). |
| + virtual void OnStopRequested() OVERRIDE; |
| + |
| + // Checks if we ever did successfully start waiting for |event_|. Be careful |
| + // with this. The flag itself is thread-unsafe, and the event that flips it |
| + // is racy. |
| + bool WasStarted(); |
| + |
| + private: |
| + base::WaitableEvent event_; |
| + base::Thread exec_thread_; |
| + CancelationSignal* cancel_signal_; |
| + bool was_started_; |
| +}; |
| + |
| +BlockingTask::BlockingTask(CancelationSignal* cancel_signal) |
| + : event_(true, false), |
| + exec_thread_("BlockingTaskBackgroundThread"), |
| + cancel_signal_(cancel_signal), |
| + was_started_(false) { } |
| + |
| +BlockingTask::~BlockingTask() {} |
| + |
| +void BlockingTask::RunAsync(base::WaitableEvent* task_done_signal) { |
| + exec_thread_.Start(); |
| + exec_thread_.message_loop()->PostTask( |
| + FROM_HERE, |
| + base::Bind(&BlockingTask::Run, |
| + base::Unretained(this), |
| + base::Unretained(task_done_signal))); |
| +} |
| + |
| +void BlockingTask::Run(base::WaitableEvent* task_done_signal) { |
| + if (cancel_signal_->TryRegisterHandler(this)) { |
| + DCHECK(!event_.IsSignaled()); |
| + event_.Wait(); |
| + } |
| + task_done_signal->Signal(); |
| +} |
| + |
| +void BlockingTask::OnStopRequested() { |
| + event_.Signal(); |
| +} |
| + |
| +bool BlockingTask::WasStarted() { |
| + return was_started_; |
| +} |
| + |
| +class CancelationSignalTest : public ::testing::Test { |
| + public: |
| + CancelationSignalTest(); |
| + virtual ~CancelationSignalTest(); |
| + |
| + // Starts the blocking task on a background thread. |
| + void StartBlockingTask(); |
| + |
| + // Cancels the blocking task. |
| + void RequestStop(); |
| + |
| + // Verifies that the background task is not running. This could be beacause |
| + // it was canceled early or because it was canceled after it was started. |
| + // |
| + // This method may block for a brief period of time while waiting for the |
| + // background thread to make progress. |
| + bool VerifyTaskDone(); |
| + |
| + // Verifies that the background task was canceled early. |
| + // |
| + // This method may block for a brief period of time while waiting for the |
| + // background thread to make progress. |
| + bool VerifyTaskNotStarted(); |
| + |
| + private: |
| + base::MessageLoop main_loop_; |
| + |
| + CancelationSignal signal_; |
| + base::WaitableEvent task_done_event_; |
| + BlockingTask blocking_task_; |
|
tim (not reviewing)
2013/09/09 18:46:34
I don't see anything ever call event_.Reset yet it
|
| +}; |
| + |
| +CancelationSignalTest::CancelationSignalTest() |
| + : task_done_event_(false, false), blocking_task_(&signal_) {} |
| + |
| +CancelationSignalTest::~CancelationSignalTest() {} |
| + |
| +void CancelationSignalTest::StartBlockingTask() { |
| + blocking_task_.RunAsync(&task_done_event_); |
| +} |
| + |
| +void CancelationSignalTest::RequestStop() { |
| + signal_.RequestStop(); |
| +} |
| + |
| +bool CancelationSignalTest::VerifyTaskDone() { |
| + // Wait until BlockingTask::Run() has finished. |
| + task_done_event_.Wait(); |
| + return true; |
| +} |
| + |
| +bool CancelationSignalTest::VerifyTaskNotStarted() { |
| + // Wait until BlockingTask::Run() has finished. |
| + task_done_event_.Wait(); |
| + |
| + // Verify the background thread never started blocking. |
| + return !blocking_task_.WasStarted(); |
| +} |
| + |
| +class FakeCancelationObserver : public CancelationObserver { |
| + virtual void OnStopRequested() OVERRIDE { } |
| +}; |
| + |
| +TEST(CancelationSignalTest_SingleThread, CheckFlags) { |
| + FakeCancelationObserver observer; |
| + CancelationSignal signal; |
| + |
| + EXPECT_FALSE(signal.IsStopRequested()); |
| + signal.RequestStop(); |
| + EXPECT_TRUE(signal.IsStopRequested()); |
| + EXPECT_FALSE(signal.TryRegisterHandler(&observer)); |
| +} |
| + |
| +// Send the cancelation signal before the task is started. This will ensure |
| +// that the task will never be attempted. |
| +TEST_F(CancelationSignalTest, CancelEarly) { |
| + RequestStop(); |
| + StartBlockingTask(); |
| + EXPECT_TRUE(VerifyTaskNotStarted()); |
| +} |
| + |
| +// Send the cancelation signal after the request to start the task has been |
| +// posted. This is racy. The signal to stop may arrive before the signal to |
| +// run the task. If that happens, we end up with another instance of the |
| +// CancelEarly test defined earlier. If the signal requesting a stop arrives |
| +// after the task has been started, it should end up stopping the task. |
| +TEST_F(CancelationSignalTest, Cancel) { |
| + StartBlockingTask(); |
| + RequestStop(); |
| + EXPECT_TRUE(VerifyTaskDone()); |
| +} |
| + |
| +} // namespace syncer |