blob: 084e080ad36512aca17a1c1ff37c01ac64d4a39e [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/logging.h"
#include "base/macros.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/threading/thread.h"
#include "mojo/public/cpp/bindings/associated_binding.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "mojo/public/interfaces/bindings/tests/test_sync_methods.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace mojo {
namespace test {
namespace {
template <typename... Args>
struct LambdaBinder {
using CallbackType = base::Callback<void(Args...)>;
template <typename Func>
static void RunLambda(Func func, Args... args) {
func(std::move(args)...);
}
template <typename Func>
static CallbackType BindLambda(Func func) {
return base::Bind(&LambdaBinder::RunLambda<Func>, func);
}
};
class TestSyncCommonImpl {
public:
TestSyncCommonImpl() {}
using PingHandler = base::Callback<void(const base::Callback<void()>&)>;
using PingBinder = LambdaBinder<const base::Callback<void()>&>;
template <typename Func>
void set_ping_handler(Func handler) {
ping_handler_ = PingBinder::BindLambda(handler);
}
using EchoHandler =
base::Callback<void(int32_t, const base::Callback<void(int32_t)>&)>;
using EchoBinder =
LambdaBinder<int32_t, const base::Callback<void(int32_t)>&>;
template <typename Func>
void set_echo_handler(Func handler) {
echo_handler_ = EchoBinder::BindLambda(handler);
}
using AsyncEchoHandler =
base::Callback<void(int32_t, const base::Callback<void(int32_t)>&)>;
using AsyncEchoBinder =
LambdaBinder<int32_t, const base::Callback<void(int32_t)>&>;
template <typename Func>
void set_async_echo_handler(Func handler) {
async_echo_handler_ = AsyncEchoBinder::BindLambda(handler);
}
using SendInterfaceHandler = base::Callback<void(TestSyncAssociatedPtrInfo)>;
using SendInterfaceBinder = LambdaBinder<TestSyncAssociatedPtrInfo>;
template <typename Func>
void set_send_interface_handler(Func handler) {
send_interface_handler_ = SendInterfaceBinder::BindLambda(handler);
}
using SendRequestHandler = base::Callback<void(TestSyncAssociatedRequest)>;
using SendRequestBinder = LambdaBinder<TestSyncAssociatedRequest>;
template <typename Func>
void set_send_request_handler(Func handler) {
send_request_handler_ = SendRequestBinder::BindLambda(handler);
}
void PingImpl(const base::Callback<void()>& callback) {
if (ping_handler_.is_null()) {
callback.Run();
return;
}
ping_handler_.Run(callback);
}
void EchoImpl(int32_t value, const base::Callback<void(int32_t)>& callback) {
if (echo_handler_.is_null()) {
callback.Run(value);
return;
}
echo_handler_.Run(value, callback);
}
void AsyncEchoImpl(int32_t value,
const base::Callback<void(int32_t)>& callback) {
if (async_echo_handler_.is_null()) {
callback.Run(value);
return;
}
async_echo_handler_.Run(value, callback);
}
void SendInterfaceImpl(TestSyncAssociatedPtrInfo ptr) {
send_interface_handler_.Run(std::move(ptr));
}
void SendRequestImpl(TestSyncAssociatedRequest request) {
send_request_handler_.Run(std::move(request));
}
private:
PingHandler ping_handler_;
EchoHandler echo_handler_;
AsyncEchoHandler async_echo_handler_;
SendInterfaceHandler send_interface_handler_;
SendRequestHandler send_request_handler_;
DISALLOW_COPY_AND_ASSIGN(TestSyncCommonImpl);
};
class TestSyncImpl : public TestSync, public TestSyncCommonImpl {
public:
explicit TestSyncImpl(TestSyncRequest request)
: binding_(this, std::move(request)) {}
// TestSync implementation:
void Ping(const PingCallback& callback) override { PingImpl(callback); }
void Echo(int32_t value, const EchoCallback& callback) override {
EchoImpl(value, callback);
}
void AsyncEcho(int32_t value, const AsyncEchoCallback& callback) override {
AsyncEchoImpl(value, callback);
}
Binding<TestSync>* binding() { return &binding_; }
private:
Binding<TestSync> binding_;
DISALLOW_COPY_AND_ASSIGN(TestSyncImpl);
};
class TestSyncMasterImpl : public TestSyncMaster, public TestSyncCommonImpl {
public:
explicit TestSyncMasterImpl(TestSyncMasterRequest request)
: binding_(this, std::move(request)) {}
// TestSyncMaster implementation:
void Ping(const PingCallback& callback) override { PingImpl(callback); }
void Echo(int32_t value, const EchoCallback& callback) override {
EchoImpl(value, callback);
}
void AsyncEcho(int32_t value, const AsyncEchoCallback& callback) override {
AsyncEchoImpl(value, callback);
}
void SendInterface(TestSyncAssociatedPtrInfo ptr) override {
SendInterfaceImpl(std::move(ptr));
}
void SendRequest(TestSyncAssociatedRequest request) override {
SendRequestImpl(std::move(request));
}
Binding<TestSyncMaster>* binding() { return &binding_; }
private:
Binding<TestSyncMaster> binding_;
DISALLOW_COPY_AND_ASSIGN(TestSyncMasterImpl);
};
class TestSyncAssociatedImpl : public TestSync, public TestSyncCommonImpl {
public:
explicit TestSyncAssociatedImpl(TestSyncAssociatedRequest request)
: binding_(this, std::move(request)) {}
// TestSync implementation:
void Ping(const PingCallback& callback) override { PingImpl(callback); }
void Echo(int32_t value, const EchoCallback& callback) override {
EchoImpl(value, callback);
}
void AsyncEcho(int32_t value, const AsyncEchoCallback& callback) override {
AsyncEchoImpl(value, callback);
}
AssociatedBinding<TestSync>* binding() { return &binding_; }
private:
AssociatedBinding<TestSync> binding_;
DISALLOW_COPY_AND_ASSIGN(TestSyncAssociatedImpl);
};
template <typename Interface>
struct ImplTraits;
template <>
struct ImplTraits<TestSync> {
using Type = TestSyncImpl;
};
template <>
struct ImplTraits<TestSyncMaster> {
using Type = TestSyncMasterImpl;
};
template <typename Interface>
using ImplTypeFor = typename ImplTraits<Interface>::Type;
// A wrapper for either an InterfacePtr or scoped_refptr<ThreadSafeInterfacePtr>
// that exposes the InterfacePtr interface.
template <typename Interface>
class PtrWrapper {
public:
explicit PtrWrapper(InterfacePtr<Interface> ptr) : ptr_(std::move(ptr)) {}
explicit PtrWrapper(
scoped_refptr<ThreadSafeInterfacePtr<Interface>> thread_safe_ptr)
: thread_safe_ptr_(thread_safe_ptr) {}
PtrWrapper(PtrWrapper&& other) = default;
Interface* operator->() {
return thread_safe_ptr_ ? thread_safe_ptr_->get() : ptr_.get();
}
void set_connection_error_handler(const base::Closure& error_handler) {
DCHECK(!thread_safe_ptr_);
ptr_.set_connection_error_handler(error_handler);
}
void reset() {
ptr_ = nullptr;
thread_safe_ptr_ = nullptr;
}
private:
InterfacePtr<Interface> ptr_;
scoped_refptr<ThreadSafeInterfacePtr<Interface>> thread_safe_ptr_;
DISALLOW_COPY_AND_ASSIGN(PtrWrapper);
};
// The type parameter for SyncMethodCommonTests for varying the Interface and
// whether to use InterfacePtr or ThreadSafeInterfacePtr.
template <typename InterfaceT, bool use_thread_safe_ptr>
struct TestParams {
using Interface = InterfaceT;
static const bool kIsThreadSafeInterfacePtrTest = use_thread_safe_ptr;
static PtrWrapper<InterfaceT> Wrap(InterfacePtr<Interface> ptr) {
if (kIsThreadSafeInterfacePtrTest) {
return PtrWrapper<Interface>(
ThreadSafeInterfacePtr<Interface>::Create(std::move(ptr)));
} else {
return PtrWrapper<Interface>(std::move(ptr));
}
}
};
template <typename Interface>
class TestSyncServiceThread {
public:
TestSyncServiceThread()
: thread_("TestSyncServiceThread"), ping_called_(false) {
thread_.Start();
}
void SetUp(InterfaceRequest<Interface> request) {
CHECK(thread_.task_runner()->BelongsToCurrentThread());
impl_.reset(new ImplTypeFor<Interface>(std::move(request)));
impl_->set_ping_handler(
[this](const typename Interface::PingCallback& callback) {
{
base::AutoLock locker(lock_);
ping_called_ = true;
}
callback.Run();
});
}
void TearDown() {
CHECK(thread_.task_runner()->BelongsToCurrentThread());
impl_.reset();
}
base::Thread* thread() { return &thread_; }
bool ping_called() const {
base::AutoLock locker(lock_);
return ping_called_;
}
private:
base::Thread thread_;
std::unique_ptr<ImplTypeFor<Interface>> impl_;
mutable base::Lock lock_;
bool ping_called_;
DISALLOW_COPY_AND_ASSIGN(TestSyncServiceThread);
};
class SyncMethodTest : public testing::Test {
public:
SyncMethodTest() {}
~SyncMethodTest() override { base::RunLoop().RunUntilIdle(); }
protected:
base::MessageLoop loop_;
};
template <typename T>
class SyncMethodCommonTest : public SyncMethodTest {
public:
SyncMethodCommonTest() {}
~SyncMethodCommonTest() override {}
};
class SyncMethodAssociatedTest : public SyncMethodTest {
public:
SyncMethodAssociatedTest() {}
~SyncMethodAssociatedTest() override {}
protected:
void SetUp() override {
master_impl_.reset(new TestSyncMasterImpl(MakeRequest(&master_ptr_)));
asso_request_ = MakeRequest(&asso_ptr_info_);
opposite_asso_request_ = MakeRequest(&opposite_asso_ptr_info_);
master_impl_->set_send_interface_handler(
[this](TestSyncAssociatedPtrInfo ptr) {
opposite_asso_ptr_info_ = std::move(ptr);
});
base::RunLoop run_loop;
master_impl_->set_send_request_handler(
[this, &run_loop](TestSyncAssociatedRequest request) {
asso_request_ = std::move(request);
run_loop.Quit();
});
master_ptr_->SendInterface(std::move(opposite_asso_ptr_info_));
master_ptr_->SendRequest(std::move(asso_request_));
run_loop.Run();
}
void TearDown() override {
asso_ptr_info_ = TestSyncAssociatedPtrInfo();
asso_request_ = TestSyncAssociatedRequest();
opposite_asso_ptr_info_ = TestSyncAssociatedPtrInfo();
opposite_asso_request_ = TestSyncAssociatedRequest();
master_ptr_ = nullptr;
master_impl_.reset();
}
InterfacePtr<TestSyncMaster> master_ptr_;
std::unique_ptr<TestSyncMasterImpl> master_impl_;
// An associated interface whose binding lives at the |master_impl_| side.
TestSyncAssociatedPtrInfo asso_ptr_info_;
TestSyncAssociatedRequest asso_request_;
// An associated interface whose binding lives at the |master_ptr_| side.
TestSyncAssociatedPtrInfo opposite_asso_ptr_info_;
TestSyncAssociatedRequest opposite_asso_request_;
};
void SetFlagAndRunClosure(bool* flag, const base::Closure& closure) {
*flag = true;
closure.Run();
}
void ExpectValueAndRunClosure(int32_t expected_value,
const base::Closure& closure,
int32_t value) {
EXPECT_EQ(expected_value, value);
closure.Run();
}
template <typename Func>
void CallAsyncEchoCallback(Func func, int32_t value) {
func(value);
}
template <typename Func>
TestSync::AsyncEchoCallback BindAsyncEchoCallback(Func func) {
return base::Bind(&CallAsyncEchoCallback<Func>, func);
}
// TestSync (without associated interfaces) and TestSyncMaster (with associated
// interfaces) exercise MultiplexRouter with different configurations.
// Each test is run once with an InterfacePtr and once with a
// ThreadSafeInterfacePtr to ensure that they behave the same with respect to
// sync calls.
using InterfaceTypes = testing::Types<TestParams<TestSync, true>,
TestParams<TestSync, false>,
TestParams<TestSyncMaster, true>,
TestParams<TestSyncMaster, false>>;
TYPED_TEST_CASE(SyncMethodCommonTest, InterfaceTypes);
TYPED_TEST(SyncMethodCommonTest, CallSyncMethodAsynchronously) {
using Interface = typename TypeParam::Interface;
InterfacePtr<Interface> interface_ptr;
ImplTypeFor<Interface> impl(MakeRequest(&interface_ptr));
auto ptr = TypeParam::Wrap(std::move(interface_ptr));
base::RunLoop run_loop;
ptr->Echo(123, base::Bind(&ExpectValueAndRunClosure, 123,
run_loop.QuitClosure()));
run_loop.Run();
}
TYPED_TEST(SyncMethodCommonTest, BasicSyncCalls) {
using Interface = typename TypeParam::Interface;
InterfacePtr<Interface> interface_ptr;
InterfaceRequest<Interface> request = MakeRequest(&interface_ptr);
auto ptr = TypeParam::Wrap(std::move(interface_ptr));
TestSyncServiceThread<Interface> service_thread;
service_thread.thread()->task_runner()->PostTask(
FROM_HERE,
base::Bind(&TestSyncServiceThread<Interface>::SetUp,
base::Unretained(&service_thread), base::Passed(&request)));
ASSERT_TRUE(ptr->Ping());
ASSERT_TRUE(service_thread.ping_called());
int32_t output_value = -1;
ASSERT_TRUE(ptr->Echo(42, &output_value));
ASSERT_EQ(42, output_value);
base::RunLoop run_loop;
service_thread.thread()->task_runner()->PostTaskAndReply(
FROM_HERE,
base::Bind(&TestSyncServiceThread<Interface>::TearDown,
base::Unretained(&service_thread)),
run_loop.QuitClosure());
run_loop.Run();
}
TYPED_TEST(SyncMethodCommonTest, ReenteredBySyncMethodBinding) {
// Test that an interface pointer waiting for a sync call response can be
// reentered by a binding serving sync methods on the same thread.
using Interface = typename TypeParam::Interface;
InterfacePtr<Interface> interface_ptr;
// The binding lives on the same thread as the interface pointer.
ImplTypeFor<Interface> impl(MakeRequest(&interface_ptr));
auto ptr = TypeParam::Wrap(std::move(interface_ptr));
int32_t output_value = -1;
ASSERT_TRUE(ptr->Echo(42, &output_value));
EXPECT_EQ(42, output_value);
}
TYPED_TEST(SyncMethodCommonTest, InterfacePtrDestroyedDuringSyncCall) {
// Test that it won't result in crash or hang if an interface pointer is
// destroyed while it is waiting for a sync call response.
using Interface = typename TypeParam::Interface;
InterfacePtr<Interface> interface_ptr;
ImplTypeFor<Interface> impl(MakeRequest(&interface_ptr));
auto ptr = TypeParam::Wrap(std::move(interface_ptr));
impl.set_ping_handler([&ptr](const TestSync::PingCallback& callback) {
ptr.reset();
callback.Run();
});
ASSERT_FALSE(ptr->Ping());
}
TYPED_TEST(SyncMethodCommonTest, BindingDestroyedDuringSyncCall) {
// Test that it won't result in crash or hang if a binding is
// closed (and therefore the message pipe handle is closed) while the
// corresponding interface pointer is waiting for a sync call response.
using Interface = typename TypeParam::Interface;
InterfacePtr<Interface> interface_ptr;
ImplTypeFor<Interface> impl(MakeRequest(&interface_ptr));
auto ptr = TypeParam::Wrap(std::move(interface_ptr));
impl.set_ping_handler([&impl](const TestSync::PingCallback& callback) {
impl.binding()->Close();
callback.Run();
});
ASSERT_FALSE(ptr->Ping());
}
TYPED_TEST(SyncMethodCommonTest, NestedSyncCallsWithInOrderResponses) {
// Test that we can call a sync method on an interface ptr, while there is
// already a sync call ongoing. The responses arrive in order.
using Interface = typename TypeParam::Interface;
InterfacePtr<Interface> interface_ptr;
ImplTypeFor<Interface> impl(MakeRequest(&interface_ptr));
auto ptr = TypeParam::Wrap(std::move(interface_ptr));
// The same variable is used to store the output of the two sync calls, in
// order to test that responses are handled in the correct order.
int32_t result_value = -1;
bool first_call = true;
impl.set_echo_handler([&first_call, &ptr, &result_value](
int32_t value, const TestSync::EchoCallback& callback) {
if (first_call) {
first_call = false;
ASSERT_TRUE(ptr->Echo(456, &result_value));
EXPECT_EQ(456, result_value);
}
callback.Run(value);
});
ASSERT_TRUE(ptr->Echo(123, &result_value));
EXPECT_EQ(123, result_value);
}
TYPED_TEST(SyncMethodCommonTest, NestedSyncCallsWithOutOfOrderResponses) {
// Test that we can call a sync method on an interface ptr, while there is
// already a sync call ongoing. The responses arrive out of order.
using Interface = typename TypeParam::Interface;
InterfacePtr<Interface> interface_ptr;
ImplTypeFor<Interface> impl(MakeRequest(&interface_ptr));
auto ptr = TypeParam::Wrap(std::move(interface_ptr));
// The same variable is used to store the output of the two sync calls, in
// order to test that responses are handled in the correct order.
int32_t result_value = -1;
bool first_call = true;
impl.set_echo_handler([&first_call, &ptr, &result_value](
int32_t value, const TestSync::EchoCallback& callback) {
callback.Run(value);
if (first_call) {
first_call = false;
ASSERT_TRUE(ptr->Echo(456, &result_value));
EXPECT_EQ(456, result_value);
}
});
ASSERT_TRUE(ptr->Echo(123, &result_value));
EXPECT_EQ(123, result_value);
}
TYPED_TEST(SyncMethodCommonTest, AsyncResponseQueuedDuringSyncCall) {
// Test that while an interface pointer is waiting for the response to a sync
// call, async responses are queued until the sync call completes.
using Interface = typename TypeParam::Interface;
InterfacePtr<Interface> interface_ptr;
ImplTypeFor<Interface> impl(MakeRequest(&interface_ptr));
auto ptr = TypeParam::Wrap(std::move(interface_ptr));
int32_t async_echo_request_value = -1;
TestSync::AsyncEchoCallback async_echo_request_callback;
base::RunLoop run_loop1;
impl.set_async_echo_handler(
[&async_echo_request_value, &async_echo_request_callback, &run_loop1](
int32_t value, const TestSync::AsyncEchoCallback& callback) {
async_echo_request_value = value;
async_echo_request_callback = callback;
run_loop1.Quit();
});
bool async_echo_response_dispatched = false;
base::RunLoop run_loop2;
ptr->AsyncEcho(
123,
BindAsyncEchoCallback(
[&async_echo_response_dispatched, &run_loop2](int32_t result) {
async_echo_response_dispatched = true;
EXPECT_EQ(123, result);
run_loop2.Quit();
}));
// Run until the AsyncEcho request reaches the service side.
run_loop1.Run();
impl.set_echo_handler(
[&async_echo_request_value, &async_echo_request_callback](
int32_t value, const TestSync::EchoCallback& callback) {
// Send back the async response first.
EXPECT_FALSE(async_echo_request_callback.is_null());
async_echo_request_callback.Run(async_echo_request_value);
callback.Run(value);
});
int32_t result_value = -1;
ASSERT_TRUE(ptr->Echo(456, &result_value));
EXPECT_EQ(456, result_value);
// Although the AsyncEcho response arrives before the Echo response, it should
// be queued and not yet dispatched.
EXPECT_FALSE(async_echo_response_dispatched);
// Run until the AsyncEcho response is dispatched.
run_loop2.Run();
EXPECT_TRUE(async_echo_response_dispatched);
}
TYPED_TEST(SyncMethodCommonTest, AsyncRequestQueuedDuringSyncCall) {
// Test that while an interface pointer is waiting for the response to a sync
// call, async requests for a binding running on the same thread are queued
// until the sync call completes.
using Interface = typename TypeParam::Interface;
InterfacePtr<Interface> interface_ptr;
ImplTypeFor<Interface> impl(MakeRequest(&interface_ptr));
auto ptr = TypeParam::Wrap(std::move(interface_ptr));
bool async_echo_request_dispatched = false;
impl.set_async_echo_handler([&async_echo_request_dispatched](
int32_t value, const TestSync::AsyncEchoCallback& callback) {
async_echo_request_dispatched = true;
callback.Run(value);
});
bool async_echo_response_dispatched = false;
base::RunLoop run_loop;
ptr->AsyncEcho(
123,
BindAsyncEchoCallback(
[&async_echo_response_dispatched, &run_loop](int32_t result) {
async_echo_response_dispatched = true;
EXPECT_EQ(123, result);
run_loop.Quit();
}));
impl.set_echo_handler([&async_echo_request_dispatched](
int32_t value, const TestSync::EchoCallback& callback) {
// Although the AsyncEcho request is sent before the Echo request, it
// shouldn't be dispatched yet at this point, because there is an ongoing
// sync call on the same thread.
EXPECT_FALSE(async_echo_request_dispatched);
callback.Run(value);
});
int32_t result_value = -1;
ASSERT_TRUE(ptr->Echo(456, &result_value));
EXPECT_EQ(456, result_value);
// Although the AsyncEcho request is sent before the Echo request, it
// shouldn't be dispatched yet.
EXPECT_FALSE(async_echo_request_dispatched);
// Run until the AsyncEcho response is dispatched.
run_loop.Run();
EXPECT_TRUE(async_echo_response_dispatched);
}
TYPED_TEST(SyncMethodCommonTest,
QueuedMessagesProcessedBeforeErrorNotification) {
// Test that while an interface pointer is waiting for the response to a sync
// call, async responses are queued. If the message pipe is disconnected
// before the queued messages are processed, the connection error
// notification is delayed until all the queued messages are processed.
// ThreadSafeInterfacePtr doesn't guarantee that messages are delivered before
// error notifications, so skip it for this test.
if (TypeParam::kIsThreadSafeInterfacePtrTest)
return;
using Interface = typename TypeParam::Interface;
InterfacePtr<Interface> ptr;
ImplTypeFor<Interface> impl(MakeRequest(&ptr));
int32_t async_echo_request_value = -1;
TestSync::AsyncEchoCallback async_echo_request_callback;
base::RunLoop run_loop1;
impl.set_async_echo_handler(
[&async_echo_request_value, &async_echo_request_callback, &run_loop1](
int32_t value, const TestSync::AsyncEchoCallback& callback) {
async_echo_request_value = value;
async_echo_request_callback = callback;
run_loop1.Quit();
});
bool async_echo_response_dispatched = false;
bool connection_error_dispatched = false;
base::RunLoop run_loop2;
ptr->AsyncEcho(
123,
BindAsyncEchoCallback(
[&async_echo_response_dispatched, &connection_error_dispatched, &ptr,
&run_loop2](int32_t result) {
async_echo_response_dispatched = true;
// At this point, error notification should not be dispatched
// yet.
EXPECT_FALSE(connection_error_dispatched);
EXPECT_FALSE(ptr.encountered_error());
EXPECT_EQ(123, result);
run_loop2.Quit();
}));
// Run until the AsyncEcho request reaches the service side.
run_loop1.Run();
impl.set_echo_handler(
[&impl, &async_echo_request_value, &async_echo_request_callback](
int32_t value, const TestSync::EchoCallback& callback) {
// Send back the async response first.
EXPECT_FALSE(async_echo_request_callback.is_null());
async_echo_request_callback.Run(async_echo_request_value);
impl.binding()->Close();
});
base::RunLoop run_loop3;
ptr.set_connection_error_handler(
base::Bind(&SetFlagAndRunClosure, &connection_error_dispatched,
run_loop3.QuitClosure()));
int32_t result_value = -1;
ASSERT_FALSE(ptr->Echo(456, &result_value));
EXPECT_EQ(-1, result_value);
ASSERT_FALSE(connection_error_dispatched);
EXPECT_FALSE(ptr.encountered_error());
// Although the AsyncEcho response arrives before the Echo response, it should
// be queued and not yet dispatched.
EXPECT_FALSE(async_echo_response_dispatched);
// Run until the AsyncEcho response is dispatched.
run_loop2.Run();
EXPECT_TRUE(async_echo_response_dispatched);
// Run until the error notification is dispatched.
run_loop3.Run();
ASSERT_TRUE(connection_error_dispatched);
EXPECT_TRUE(ptr.encountered_error());
}
TYPED_TEST(SyncMethodCommonTest, InvalidMessageDuringSyncCall) {
// Test that while an interface pointer is waiting for the response to a sync
// call, an invalid incoming message will disconnect the message pipe, cause
// the sync call to return false, and run the connection error handler
// asynchronously.
using Interface = typename TypeParam::Interface;
MessagePipe pipe;
InterfacePtr<Interface> interface_ptr;
interface_ptr.Bind(InterfacePtrInfo<Interface>(std::move(pipe.handle0), 0u));
auto ptr = TypeParam::Wrap(std::move(interface_ptr));
MessagePipeHandle raw_binding_handle = pipe.handle1.get();
ImplTypeFor<Interface> impl(MakeRequest<Interface>(std::move(pipe.handle1)));
impl.set_echo_handler([&raw_binding_handle](
int32_t value, const TestSync::EchoCallback& callback) {
// Write a 1-byte message, which is considered invalid.
char invalid_message = 0;
MojoResult result =
WriteMessageRaw(raw_binding_handle, &invalid_message, 1u, nullptr, 0u,
MOJO_WRITE_MESSAGE_FLAG_NONE);
ASSERT_EQ(MOJO_RESULT_OK, result);
callback.Run(value);
});
bool connection_error_dispatched = false;
base::RunLoop run_loop;
// ThreadSafeInterfacePtr doesn't support setting connection error handlers.
if (!TypeParam::kIsThreadSafeInterfacePtrTest) {
ptr.set_connection_error_handler(base::Bind(&SetFlagAndRunClosure,
&connection_error_dispatched,
run_loop.QuitClosure()));
}
int32_t result_value = -1;
ASSERT_FALSE(ptr->Echo(456, &result_value));
EXPECT_EQ(-1, result_value);
ASSERT_FALSE(connection_error_dispatched);
if (!TypeParam::kIsThreadSafeInterfacePtrTest) {
run_loop.Run();
ASSERT_TRUE(connection_error_dispatched);
}
}
TEST_F(SyncMethodAssociatedTest, ReenteredBySyncMethodAssoBindingOfSameRouter) {
// Test that an interface pointer waiting for a sync call response can be
// reentered by an associated binding serving sync methods on the same thread.
// The associated binding belongs to the same MultiplexRouter as the waiting
// interface pointer.
TestSyncAssociatedImpl opposite_asso_impl(std::move(opposite_asso_request_));
TestSyncAssociatedPtr opposite_asso_ptr;
opposite_asso_ptr.Bind(std::move(opposite_asso_ptr_info_));
master_impl_->set_echo_handler([&opposite_asso_ptr](
int32_t value, const TestSyncMaster::EchoCallback& callback) {
int32_t result_value = -1;
ASSERT_TRUE(opposite_asso_ptr->Echo(123, &result_value));
EXPECT_EQ(123, result_value);
callback.Run(value);
});
int32_t result_value = -1;
ASSERT_TRUE(master_ptr_->Echo(456, &result_value));
EXPECT_EQ(456, result_value);
}
TEST_F(SyncMethodAssociatedTest,
ReenteredBySyncMethodAssoBindingOfDifferentRouter) {
// Test that an interface pointer waiting for a sync call response can be
// reentered by an associated binding serving sync methods on the same thread.
// The associated binding belongs to a different MultiplexRouter as the
// waiting interface pointer.
TestSyncAssociatedImpl asso_impl(std::move(asso_request_));
TestSyncAssociatedPtr asso_ptr;
asso_ptr.Bind(std::move(asso_ptr_info_));
master_impl_->set_echo_handler(
[&asso_ptr](int32_t value, const TestSyncMaster::EchoCallback& callback) {
int32_t result_value = -1;
ASSERT_TRUE(asso_ptr->Echo(123, &result_value));
EXPECT_EQ(123, result_value);
callback.Run(value);
});
int32_t result_value = -1;
ASSERT_TRUE(master_ptr_->Echo(456, &result_value));
EXPECT_EQ(456, result_value);
}
// TODO(yzshen): Add more tests related to associated interfaces.
} // namespace
} // namespace test
} // namespace mojo