blob: a31d9a9184d34b2edfa0105ebf039409654f8700 [file] [log] [blame]
// 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/location.h"
#include "base/message_loop/message_loop.h"
#include "base/message_loop/message_loop_current.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/synchronization/lock.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread_restrictions.h"
#include "net/test/test_with_scoped_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace net {
namespace {
class SerialWorkerTest : public TestWithScopedTaskEnvironment {
public:
// The class under test
class TestSerialWorker : public SerialWorker {
public:
explicit TestSerialWorker(SerialWorkerTest* t) : test_(t) {}
void DoWork() override {
ASSERT_TRUE(test_);
test_->OnWork();
}
void OnWorkFinished() override {
ASSERT_TRUE(test_);
test_->OnWorkFinished();
}
private:
~TestSerialWorker() override = default;
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");
{
base::ScopedAllowBaseSyncPrimitivesForTesting
scoped_allow_base_sync_primitives;
work_allowed_.Wait();
}
// Calling from TaskScheduler, 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_->task_runner()->BelongsToCurrentThread());
EXPECT_EQ(output_value_, input_value_);
BreakNow("OnWorkFinished");
}
protected:
void BreakCallback(const std::string& breakpoint) {
breakpoint_ = breakpoint;
run_loop_->Quit();
}
void BreakNow(const std::string& b) {
message_loop_->task_runner()->PostTask(
FROM_HERE, base::Bind(&SerialWorkerTest::BreakCallback,
base::Unretained(this), b));
}
void RunUntilBreak(const std::string& b) {
base::RunLoop run_loop;
ASSERT_FALSE(run_loop_);
run_loop_ = &run_loop;
run_loop_->Run();
run_loop_ = nullptr;
ASSERT_EQ(breakpoint_, b);
}
SerialWorkerTest()
: input_value_(0),
output_value_(-1),
work_allowed_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
base::WaitableEvent::InitialState::NOT_SIGNALED),
work_called_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
base::WaitableEvent::InitialState::NOT_SIGNALED),
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
void SetUp() override {
message_loop_ = base::MessageLoopCurrent::Get();
worker_ = new TestSerialWorker(this);
}
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.
base::MessageLoop* message_loop_;
// WatcherDelegate under test.
scoped_refptr<TestSerialWorker> worker_;
std::string breakpoint_;
base::RunLoop* run_loop_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(SerialWorkerTest);
};
TEST_F(SerialWorkerTest, ExecuteAndSerializeReads) {
for (int i = 0; i < 3; ++i) {
++input_value_;
worker_->WorkNow();
WaitForWork();
RunUntilBreak("OnWorkFinished");
EXPECT_TRUE(message_loop_->IsIdleForTesting());
}
// 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.
EXPECT_TRUE(message_loop_->IsIdleForTesting());
}
} // namespace
} // namespace net