root/sync/internal_api/public/base/cancelation_signal_unittest.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. was_started_
  2. RunAsync
  3. Run
  4. OnSignalReceived
  5. WasStarted
  6. blocking_task_
  7. StartBlockingTaskAsync
  8. StartBlockingTaskAndWaitForItToStart
  9. CancelBlocking
  10. VerifyTaskNotStarted
  11. OnSignalReceived
  12. TEST
  13. TEST_F
  14. TEST_F

// 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/platform_thread.h"
#include "base/threading/thread.h"
#include "base/time/time.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_start_signal,
                base::WaitableEvent* task_done_signal);

  // Blocks until canceled.  Signals |task_done_signal| when finished (either
  // via early cancel or cancel after start).  Signals |task_start_signal| if
  // and when the task starts successfully (which will not happen if the task
  // was cancelled early).
  void Run(base::WaitableEvent* task_start_signal,
           base::WaitableEvent* task_done_signal);

  // Implementation of CancelationObserver.
  // Wakes up the thread blocked in Run().
  virtual void OnSignalReceived() 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() {
  if (was_started_) {
    cancel_signal_->UnregisterHandler(this);
  }
}

void BlockingTask::RunAsync(base::WaitableEvent* task_start_signal,
                            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_start_signal),
                 base::Unretained(task_done_signal)));
}

void BlockingTask::Run(
    base::WaitableEvent* task_start_signal,
    base::WaitableEvent* task_done_signal) {
  if (cancel_signal_->TryRegisterHandler(this)) {
    DCHECK(!event_.IsSignaled());
    was_started_ = true;
    task_start_signal->Signal();
    event_.Wait();
  }
  task_done_signal->Signal();
}

void BlockingTask::OnSignalReceived() {
  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.  Does not wait for the
  // task to start.
  void StartBlockingTaskAsync();

  // Starts the blocking task on a background thread.  Does not return until
  // the task has been started.
  void StartBlockingTaskAndWaitForItToStart();

  // Cancels the blocking task.
  void CancelBlocking();

  // 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_start_event_;
  base::WaitableEvent task_done_event_;
  BlockingTask blocking_task_;
};

CancelationSignalTest::CancelationSignalTest()
  : task_start_event_(false, false),
    task_done_event_(false, false),
    blocking_task_(&signal_) {}

CancelationSignalTest::~CancelationSignalTest() {}

void CancelationSignalTest::StartBlockingTaskAsync() {
  blocking_task_.RunAsync(&task_start_event_, &task_done_event_);
}

void CancelationSignalTest::StartBlockingTaskAndWaitForItToStart() {
  blocking_task_.RunAsync(&task_start_event_, &task_done_event_);
  task_start_event_.Wait();
}

void CancelationSignalTest::CancelBlocking() {
  signal_.Signal();
}

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 OnSignalReceived() OVERRIDE { }
};

TEST(CancelationSignalTest_SingleThread, CheckFlags) {
  FakeCancelationObserver observer;
  CancelationSignal signal;

  EXPECT_FALSE(signal.IsSignalled());
  signal.Signal();
  EXPECT_TRUE(signal.IsSignalled());
  EXPECT_FALSE(signal.TryRegisterHandler(&observer));
}

// Send the cancelation signal before the task is started.  This will ensure
// that the task will never be "started" (ie. TryRegisterHandler() will fail,
// so it will never start blocking on its main WaitableEvent).
TEST_F(CancelationSignalTest, CancelEarly) {
  CancelBlocking();
  StartBlockingTaskAsync();
  EXPECT_TRUE(VerifyTaskNotStarted());
}

// Send the cancelation signal after the task has started running.  This tests
// the non-early exit code path, where the task is stopped while it is in
// progress.
TEST_F(CancelationSignalTest, Cancel) {
  StartBlockingTaskAndWaitForItToStart();

  // Wait for the task to finish and let verify it has been started.
  CancelBlocking();
  EXPECT_FALSE(VerifyTaskNotStarted());
}

}  // namespace syncer

/* [<][>][^][v][top][bottom][index][help] */