blob: 569eb518c6ccb01591aa90d5fc0a574138556582 [file] [log] [blame]
// Copyright 2016 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 <utility>
#include "base/bind.h"
#include "base/callback.h"
#include "base/message_loop/message_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/synchronization/lock.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/platform_thread.h"
#include "mojo/public/cpp/bindings/associated_binding.h"
#include "mojo/public/cpp/bindings/associated_interface_ptr.h"
#include "mojo/public/cpp/bindings/associated_interface_ptr_info.h"
#include "mojo/public/cpp/bindings/associated_interface_request.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "mojo/public/interfaces/bindings/tests/test_associated_interfaces.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace mojo {
namespace test {
namespace {
class TestTaskRunner : public base::SingleThreadTaskRunner {
public:
TestTaskRunner()
: thread_id_(base::PlatformThread::CurrentRef()),
quit_called_(false),
task_ready_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
base::WaitableEvent::InitialState::NOT_SIGNALED) {}
bool PostNonNestableDelayedTask(const tracked_objects::Location& from_here,
base::OnceClosure task,
base::TimeDelta delay) override {
NOTREACHED();
return false;
}
bool PostDelayedTask(const tracked_objects::Location& from_here,
base::OnceClosure task,
base::TimeDelta delay) override {
{
base::AutoLock locker(lock_);
tasks_.push(std::move(task));
}
task_ready_.Signal();
return true;
}
bool RunsTasksOnCurrentThread() const override {
return base::PlatformThread::CurrentRef() == thread_id_;
}
// Only quits when Quit() is called.
void Run() {
DCHECK(RunsTasksOnCurrentThread());
quit_called_ = false;
while (true) {
{
base::AutoLock locker(lock_);
while (!tasks_.empty()) {
auto task = std::move(tasks_.front());
tasks_.pop();
{
base::AutoUnlock unlocker(lock_);
std::move(task).Run();
if (quit_called_)
return;
}
}
}
task_ready_.Wait();
}
}
void Quit() {
DCHECK(RunsTasksOnCurrentThread());
quit_called_ = true;
}
// Waits until one task is ready and runs it.
void RunOneTask() {
DCHECK(RunsTasksOnCurrentThread());
while (true) {
{
base::AutoLock locker(lock_);
if (!tasks_.empty()) {
auto task = std::move(tasks_.front());
tasks_.pop();
{
base::AutoUnlock unlocker(lock_);
std::move(task).Run();
return;
}
}
}
task_ready_.Wait();
}
}
private:
~TestTaskRunner() override {}
const base::PlatformThreadRef thread_id_;
bool quit_called_;
base::WaitableEvent task_ready_;
// Protect |tasks_|.
base::Lock lock_;
std::queue<base::OnceClosure> tasks_;
DISALLOW_COPY_AND_ASSIGN(TestTaskRunner);
};
template <typename BindingType, typename RequestType>
class IntegerSenderImpl : public IntegerSender {
public:
IntegerSenderImpl(RequestType request,
scoped_refptr<base::SingleThreadTaskRunner> runner)
: binding_(this, std::move(request), std::move(runner)) {}
~IntegerSenderImpl() override {}
using EchoHandler = base::Callback<void(int32_t, const EchoCallback&)>;
void set_echo_handler(const EchoHandler& handler) { echo_handler_ = handler; }
void Echo(int32_t value, const EchoCallback& callback) override {
if (echo_handler_.is_null())
callback.Run(value);
else
echo_handler_.Run(value, callback);
}
void Send(int32_t value) override { NOTREACHED(); }
BindingType* binding() { return &binding_; }
private:
BindingType binding_;
EchoHandler echo_handler_;
};
class IntegerSenderConnectionImpl : public IntegerSenderConnection {
public:
using SenderType = IntegerSenderImpl<AssociatedBinding<IntegerSender>,
IntegerSenderAssociatedRequest>;
explicit IntegerSenderConnectionImpl(
IntegerSenderConnectionRequest request,
scoped_refptr<base::SingleThreadTaskRunner> runner,
scoped_refptr<base::SingleThreadTaskRunner> sender_runner)
: binding_(this, std::move(request), std::move(runner)),
sender_runner_(std::move(sender_runner)) {}
~IntegerSenderConnectionImpl() override {}
void set_get_sender_notification(const base::Closure& notification) {
get_sender_notification_ = notification;
}
void GetSender(IntegerSenderAssociatedRequest sender) override {
sender_impl_.reset(new SenderType(std::move(sender), sender_runner_));
get_sender_notification_.Run();
}
void AsyncGetSender(const AsyncGetSenderCallback& callback) override {
NOTREACHED();
}
Binding<IntegerSenderConnection>* binding() { return &binding_; }
SenderType* sender_impl() { return sender_impl_.get(); }
private:
Binding<IntegerSenderConnection> binding_;
std::unique_ptr<SenderType> sender_impl_;
scoped_refptr<base::SingleThreadTaskRunner> sender_runner_;
base::Closure get_sender_notification_;
};
class BindTaskRunnerTest : public testing::Test {
protected:
void SetUp() override {
binding_task_runner_ = scoped_refptr<TestTaskRunner>(new TestTaskRunner);
ptr_task_runner_ = scoped_refptr<TestTaskRunner>(new TestTaskRunner);
auto request = MakeRequest(&ptr_, ptr_task_runner_);
impl_.reset(new ImplType(std::move(request), binding_task_runner_));
}
base::MessageLoop loop_;
scoped_refptr<TestTaskRunner> binding_task_runner_;
scoped_refptr<TestTaskRunner> ptr_task_runner_;
IntegerSenderPtr ptr_;
using ImplType =
IntegerSenderImpl<Binding<IntegerSender>, IntegerSenderRequest>;
std::unique_ptr<ImplType> impl_;
};
class AssociatedBindTaskRunnerTest : public testing::Test {
protected:
void SetUp() override {
connection_binding_task_runner_ =
scoped_refptr<TestTaskRunner>(new TestTaskRunner);
connection_ptr_task_runner_ =
scoped_refptr<TestTaskRunner>(new TestTaskRunner);
sender_binding_task_runner_ =
scoped_refptr<TestTaskRunner>(new TestTaskRunner);
sender_ptr_task_runner_ = scoped_refptr<TestTaskRunner>(new TestTaskRunner);
auto connection_request =
MakeRequest(&connection_ptr_, connection_ptr_task_runner_);
connection_impl_.reset(new IntegerSenderConnectionImpl(
std::move(connection_request), connection_binding_task_runner_,
sender_binding_task_runner_));
connection_impl_->set_get_sender_notification(
base::Bind(&AssociatedBindTaskRunnerTest::QuitTaskRunner,
base::Unretained(this)));
connection_ptr_->GetSender(
MakeRequest(&sender_ptr_, sender_ptr_task_runner_));
connection_binding_task_runner_->Run();
}
void QuitTaskRunner() {
connection_binding_task_runner_->Quit();
}
base::MessageLoop loop_;
scoped_refptr<TestTaskRunner> connection_binding_task_runner_;
scoped_refptr<TestTaskRunner> connection_ptr_task_runner_;
scoped_refptr<TestTaskRunner> sender_binding_task_runner_;
scoped_refptr<TestTaskRunner> sender_ptr_task_runner_;
IntegerSenderConnectionPtr connection_ptr_;
std::unique_ptr<IntegerSenderConnectionImpl> connection_impl_;
IntegerSenderAssociatedPtr sender_ptr_;
};
void DoSetFlagAndQuitTaskRunner(bool* flag,
scoped_refptr<TestTaskRunner> task_runner) {
*flag = true;
if (task_runner)
task_runner->Quit();
}
void DoExpectValueSetFlagAndQuitTaskRunner(
int32_t expected_value,
bool* flag,
scoped_refptr<TestTaskRunner> task_runner,
int32_t value) {
EXPECT_EQ(expected_value, value);
DoSetFlagAndQuitTaskRunner(flag, task_runner);
}
void DoExpectValueSetFlagForwardValueAndQuitTaskRunner(
int32_t expected_value,
bool* flag,
scoped_refptr<TestTaskRunner> task_runner,
int32_t value,
const IntegerSender::EchoCallback& callback) {
EXPECT_EQ(expected_value, value);
*flag = true;
callback.Run(value);
task_runner->Quit();
}
base::Closure SetFlagAndQuitTaskRunner(
bool* flag,
scoped_refptr<TestTaskRunner> task_runner) {
return base::Bind(&DoSetFlagAndQuitTaskRunner, flag, task_runner);
}
base::Callback<void(int32_t)> ExpectValueSetFlagAndQuitTaskRunner(
int32_t expected_value,
bool* flag,
scoped_refptr<TestTaskRunner> task_runner) {
return base::Bind(&DoExpectValueSetFlagAndQuitTaskRunner, expected_value,
flag, task_runner);
}
TEST_F(BindTaskRunnerTest, MethodCall) {
bool echo_called = false;
impl_->set_echo_handler(
base::Bind(&DoExpectValueSetFlagForwardValueAndQuitTaskRunner,
1024, &echo_called, binding_task_runner_));
bool echo_replied = false;
ptr_->Echo(1024, ExpectValueSetFlagAndQuitTaskRunner(1024, &echo_replied,
ptr_task_runner_));
binding_task_runner_->Run();
EXPECT_TRUE(echo_called);
ptr_task_runner_->Run();
EXPECT_TRUE(echo_replied);
}
TEST_F(BindTaskRunnerTest, BindingConnectionError) {
bool connection_error_called = false;
impl_->binding()->set_connection_error_handler(
SetFlagAndQuitTaskRunner(&connection_error_called, binding_task_runner_));
ptr_.reset();
binding_task_runner_->Run();
EXPECT_TRUE(connection_error_called);
}
TEST_F(BindTaskRunnerTest, PtrConnectionError) {
bool connection_error_called = false;
ptr_.set_connection_error_handler(
SetFlagAndQuitTaskRunner(&connection_error_called, ptr_task_runner_));
impl_->binding()->Close();
ptr_task_runner_->Run();
EXPECT_TRUE(connection_error_called);
}
void ExpectValueSetFlagAndForward(int32_t expected_value,
bool* flag,
int32_t value,
const IntegerSender::EchoCallback& callback) {
EXPECT_EQ(expected_value, value);
*flag = true;
callback.Run(value);
}
TEST_F(AssociatedBindTaskRunnerTest, MethodCall) {
bool echo_called = false;
connection_impl_->sender_impl()->set_echo_handler(
base::Bind(&ExpectValueSetFlagAndForward, 1024, &echo_called));
bool echo_replied = false;
sender_ptr_->Echo(
1024, ExpectValueSetFlagAndQuitTaskRunner(1024, &echo_replied, nullptr));
// The Echo request first arrives at the master endpoint's task runner, and
// then is forwarded to the associated endpoint's task runner.
connection_binding_task_runner_->RunOneTask();
sender_binding_task_runner_->RunOneTask();
EXPECT_TRUE(echo_called);
// Similarly, the Echo response arrives at the master endpoint's task runner
// and then is forwarded to the associated endpoint's task runner.
connection_ptr_task_runner_->RunOneTask();
sender_ptr_task_runner_->RunOneTask();
EXPECT_TRUE(echo_replied);
}
TEST_F(AssociatedBindTaskRunnerTest, BindingConnectionError) {
bool sender_impl_error = false;
connection_impl_->sender_impl()->binding()->set_connection_error_handler(
SetFlagAndQuitTaskRunner(&sender_impl_error,
sender_binding_task_runner_));
bool connection_impl_error = false;
connection_impl_->binding()->set_connection_error_handler(
SetFlagAndQuitTaskRunner(&connection_impl_error,
connection_binding_task_runner_));
bool sender_ptr_error = false;
sender_ptr_.set_connection_error_handler(
SetFlagAndQuitTaskRunner(&sender_ptr_error, sender_ptr_task_runner_));
connection_ptr_.reset();
sender_ptr_task_runner_->Run();
EXPECT_TRUE(sender_ptr_error);
connection_binding_task_runner_->Run();
EXPECT_TRUE(connection_impl_error);
sender_binding_task_runner_->Run();
EXPECT_TRUE(sender_impl_error);
}
TEST_F(AssociatedBindTaskRunnerTest, PtrConnectionError) {
bool sender_impl_error = false;
connection_impl_->sender_impl()->binding()->set_connection_error_handler(
SetFlagAndQuitTaskRunner(&sender_impl_error,
sender_binding_task_runner_));
bool connection_ptr_error = false;
connection_ptr_.set_connection_error_handler(
SetFlagAndQuitTaskRunner(&connection_ptr_error,
connection_ptr_task_runner_));
bool sender_ptr_error = false;
sender_ptr_.set_connection_error_handler(
SetFlagAndQuitTaskRunner(&sender_ptr_error, sender_ptr_task_runner_));
connection_impl_->binding()->Close();
sender_binding_task_runner_->Run();
EXPECT_TRUE(sender_impl_error);
connection_ptr_task_runner_->Run();
EXPECT_TRUE(connection_ptr_error);
sender_ptr_task_runner_->Run();
EXPECT_TRUE(sender_ptr_error);
}
} // namespace
} // namespace test
} // namespace mojo