|  | // 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 "net/dns/serial_worker.h" | 
|  |  | 
|  | #include "base/bind.h" | 
|  | #include "base/message_loop.h" | 
|  | #include "base/synchronization/lock.h" | 
|  | #include "base/synchronization/waitable_event.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | namespace net { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | class SerialWorkerTest : public testing::Test { | 
|  | public: | 
|  | // The class under test | 
|  | class TestSerialWorker : public SerialWorker { | 
|  | public: | 
|  | explicit TestSerialWorker(SerialWorkerTest* t) | 
|  | : test_(t) {} | 
|  | virtual void DoWork() override { | 
|  | ASSERT_TRUE(test_); | 
|  | test_->OnWork(); | 
|  | } | 
|  | virtual void OnWorkFinished() override { | 
|  | ASSERT_TRUE(test_); | 
|  | test_->OnWorkFinished(); | 
|  | } | 
|  | private: | 
|  | virtual ~TestSerialWorker() {} | 
|  | SerialWorkerTest* test_; | 
|  | }; | 
|  |  | 
|  | // Mocks | 
|  |  | 
|  | void OnWork() { | 
|  | { // Check that OnWork is executed serially. | 
|  | base::AutoLock lock(work_lock_); | 
|  | EXPECT_FALSE(work_running_) << "DoRead is not called serially!"; | 
|  | work_running_ = true; | 
|  | } | 
|  | BreakNow("OnWork"); | 
|  | work_allowed_.Wait(); | 
|  | // Calling from WorkerPool, but protected by work_allowed_/work_called_. | 
|  | output_value_ = input_value_; | 
|  |  | 
|  | { // This lock might be destroyed after work_called_ is signalled. | 
|  | base::AutoLock lock(work_lock_); | 
|  | work_running_ = false; | 
|  | } | 
|  | work_called_.Signal(); | 
|  | } | 
|  |  | 
|  | void OnWorkFinished() { | 
|  | EXPECT_TRUE(message_loop_ == MessageLoop::current()); | 
|  | EXPECT_EQ(output_value_, input_value_); | 
|  | BreakNow("OnWorkFinished"); | 
|  | } | 
|  |  | 
|  | protected: | 
|  | void BreakCallback(std::string breakpoint) { | 
|  | breakpoint_ = breakpoint; | 
|  | MessageLoop::current()->QuitNow(); | 
|  | } | 
|  |  | 
|  | void BreakNow(std::string b) { | 
|  | message_loop_->PostTask(FROM_HERE, | 
|  | base::Bind(&SerialWorkerTest::BreakCallback, | 
|  | base::Unretained(this), b)); | 
|  | } | 
|  |  | 
|  | void RunUntilBreak(std::string b) { | 
|  | message_loop_->Run(); | 
|  | ASSERT_EQ(breakpoint_, b); | 
|  | } | 
|  |  | 
|  | SerialWorkerTest() | 
|  | : input_value_(0), | 
|  | output_value_(-1), | 
|  | work_allowed_(false, false), | 
|  | work_called_(false, false), | 
|  | work_running_(false) { | 
|  | } | 
|  |  | 
|  | // Helpers for tests. | 
|  |  | 
|  | // Lets OnWork run and waits for it to complete. Can only return if OnWork is | 
|  | // executed on a concurrent thread. | 
|  | void WaitForWork() { | 
|  | RunUntilBreak("OnWork"); | 
|  | work_allowed_.Signal(); | 
|  | work_called_.Wait(); | 
|  | } | 
|  |  | 
|  | // test::Test methods | 
|  | virtual void SetUp() override { | 
|  | message_loop_ = MessageLoop::current(); | 
|  | worker_ = new TestSerialWorker(this); | 
|  | } | 
|  |  | 
|  | virtual void TearDown() override { | 
|  | // Cancel the worker to catch if it makes a late DoWork call. | 
|  | worker_->Cancel(); | 
|  | // Check if OnWork is stalled. | 
|  | EXPECT_FALSE(work_running_) << "OnWork should be done by TearDown"; | 
|  | // Release it for cleanliness. | 
|  | if (work_running_) { | 
|  | WaitForWork(); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Input value read on WorkerPool. | 
|  | int input_value_; | 
|  | // Output value written on WorkerPool. | 
|  | int output_value_; | 
|  |  | 
|  | // read is called on WorkerPool so we need to synchronize with it. | 
|  | base::WaitableEvent work_allowed_; | 
|  | base::WaitableEvent work_called_; | 
|  |  | 
|  | // Protected by read_lock_. Used to verify that read calls are serialized. | 
|  | bool work_running_; | 
|  | base::Lock work_lock_; | 
|  |  | 
|  | // Loop for this thread. | 
|  | MessageLoop* message_loop_; | 
|  |  | 
|  | // WatcherDelegate under test. | 
|  | scoped_refptr<TestSerialWorker> worker_; | 
|  |  | 
|  | std::string breakpoint_; | 
|  | }; | 
|  |  | 
|  | TEST_F(SerialWorkerTest, ExecuteAndSerializeReads) { | 
|  | for (int i = 0; i < 3; ++i) { | 
|  | ++input_value_; | 
|  | worker_->WorkNow(); | 
|  | WaitForWork(); | 
|  | RunUntilBreak("OnWorkFinished"); | 
|  |  | 
|  | message_loop_->AssertIdle(); | 
|  | } | 
|  |  | 
|  | // Schedule two calls. OnWork checks if it is called serially. | 
|  | ++input_value_; | 
|  | worker_->WorkNow(); | 
|  | // read is blocked, so this will have to induce re-work | 
|  | worker_->WorkNow(); | 
|  | WaitForWork(); | 
|  | WaitForWork(); | 
|  | RunUntilBreak("OnWorkFinished"); | 
|  |  | 
|  | // No more tasks should remain. | 
|  | message_loop_->AssertIdle(); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | }  // namespace net | 
|  |  |