659 lines
20 KiB
C++
659 lines
20 KiB
C++
// Copyright 2015 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.
|
|
|
|
// Note: This file tests both binding.h (mojo::Binding) and strong_binding.h
|
|
// (mojo::StrongBinding).
|
|
|
|
#include "mojo/public/cpp/bindings/binding.h"
|
|
|
|
#include <stdint.h>
|
|
#include <utility>
|
|
|
|
#include "base/macros.h"
|
|
#include "base/memory/ptr_util.h"
|
|
#include "base/memory/weak_ptr.h"
|
|
#include "base/run_loop.h"
|
|
#include "mojo/core/embedder/embedder.h"
|
|
#include "mojo/public/cpp/bindings/strong_binding.h"
|
|
#include "mojo/public/cpp/bindings/tests/bindings_test_base.h"
|
|
#include "mojo/public/interfaces/bindings/tests/ping_service.mojom.h"
|
|
#include "mojo/public/interfaces/bindings/tests/sample_interfaces.mojom.h"
|
|
#include "mojo/public/interfaces/bindings/tests/sample_service.mojom.h"
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
|
|
|
namespace mojo {
|
|
namespace {
|
|
|
|
class ServiceImpl : public sample::Service {
|
|
public:
|
|
explicit ServiceImpl(bool* was_deleted = nullptr)
|
|
: was_deleted_(was_deleted) {}
|
|
~ServiceImpl() override {
|
|
if (was_deleted_)
|
|
*was_deleted_ = true;
|
|
}
|
|
|
|
private:
|
|
// sample::Service implementation
|
|
void Frobinate(sample::FooPtr foo,
|
|
BazOptions options,
|
|
sample::PortPtr port,
|
|
const FrobinateCallback& callback) override {
|
|
callback.Run(1);
|
|
}
|
|
void GetPort(InterfaceRequest<sample::Port> port) override {}
|
|
|
|
bool* const was_deleted_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(ServiceImpl);
|
|
};
|
|
|
|
template <typename... Args>
|
|
void DoSetFlagAndRunClosure(bool* flag,
|
|
const base::Closure& closure,
|
|
Args... args) {
|
|
*flag = true;
|
|
if (!closure.is_null())
|
|
closure.Run();
|
|
}
|
|
|
|
template <typename... Args>
|
|
base::Callback<void(Args...)> SetFlagAndRunClosure(
|
|
bool* flag,
|
|
const base::Closure& callback = base::Closure()) {
|
|
return base::Bind(&DoSetFlagAndRunClosure<Args...>, flag, callback);
|
|
}
|
|
|
|
// BindingTest -----------------------------------------------------------------
|
|
|
|
using BindingTest = BindingsTestBase;
|
|
|
|
TEST_P(BindingTest, Close) {
|
|
bool called = false;
|
|
sample::ServicePtr ptr;
|
|
auto request = MakeRequest(&ptr);
|
|
base::RunLoop run_loop;
|
|
ptr.set_connection_error_handler(
|
|
SetFlagAndRunClosure(&called, run_loop.QuitClosure()));
|
|
ServiceImpl impl;
|
|
Binding<sample::Service> binding(&impl, std::move(request));
|
|
|
|
binding.Close();
|
|
EXPECT_FALSE(called);
|
|
run_loop.Run();
|
|
EXPECT_TRUE(called);
|
|
}
|
|
|
|
// Tests that destroying a mojo::Binding closes the bound message pipe handle.
|
|
TEST_P(BindingTest, DestroyClosesMessagePipe) {
|
|
bool encountered_error = false;
|
|
ServiceImpl impl;
|
|
sample::ServicePtr ptr;
|
|
auto request = MakeRequest(&ptr);
|
|
base::RunLoop run_loop;
|
|
ptr.set_connection_error_handler(
|
|
SetFlagAndRunClosure(&encountered_error, run_loop.QuitClosure()));
|
|
bool called = false;
|
|
base::RunLoop run_loop2;
|
|
{
|
|
Binding<sample::Service> binding(&impl, std::move(request));
|
|
ptr->Frobinate(nullptr, sample::Service::BazOptions::REGULAR, nullptr,
|
|
SetFlagAndRunClosure<int32_t>(&called,
|
|
run_loop2.QuitClosure()));
|
|
run_loop2.Run();
|
|
EXPECT_TRUE(called);
|
|
EXPECT_FALSE(encountered_error);
|
|
}
|
|
// Now that the Binding is out of scope we should detect an error on the other
|
|
// end of the pipe.
|
|
run_loop.Run();
|
|
EXPECT_TRUE(encountered_error);
|
|
|
|
// And calls should fail.
|
|
called = false;
|
|
ptr->Frobinate(nullptr, sample::Service::BazOptions::REGULAR, nullptr,
|
|
SetFlagAndRunClosure<int32_t>(&called,
|
|
run_loop2.QuitClosure()));
|
|
base::RunLoop().RunUntilIdle();
|
|
EXPECT_FALSE(called);
|
|
}
|
|
|
|
// Tests that the binding's connection error handler gets called when the other
|
|
// end is closed.
|
|
TEST_P(BindingTest, ConnectionError) {
|
|
bool called = false;
|
|
{
|
|
ServiceImpl impl;
|
|
sample::ServicePtr ptr;
|
|
Binding<sample::Service> binding(&impl, MakeRequest(&ptr));
|
|
base::RunLoop run_loop;
|
|
binding.set_connection_error_handler(
|
|
SetFlagAndRunClosure(&called, run_loop.QuitClosure()));
|
|
ptr.reset();
|
|
EXPECT_FALSE(called);
|
|
run_loop.Run();
|
|
EXPECT_TRUE(called);
|
|
// We want to make sure that it isn't called again during destruction.
|
|
called = false;
|
|
}
|
|
EXPECT_FALSE(called);
|
|
}
|
|
|
|
// Tests that calling Close doesn't result in the connection error handler being
|
|
// called.
|
|
TEST_P(BindingTest, CloseDoesntCallConnectionErrorHandler) {
|
|
ServiceImpl impl;
|
|
sample::ServicePtr ptr;
|
|
Binding<sample::Service> binding(&impl, MakeRequest(&ptr));
|
|
bool called = false;
|
|
binding.set_connection_error_handler(SetFlagAndRunClosure(&called));
|
|
binding.Close();
|
|
base::RunLoop().RunUntilIdle();
|
|
EXPECT_FALSE(called);
|
|
|
|
// We can also close the other end, and the error handler still won't be
|
|
// called.
|
|
ptr.reset();
|
|
base::RunLoop().RunUntilIdle();
|
|
EXPECT_FALSE(called);
|
|
}
|
|
|
|
class ServiceImplWithBinding : public ServiceImpl {
|
|
public:
|
|
ServiceImplWithBinding(bool* was_deleted,
|
|
const base::Closure& closure,
|
|
InterfaceRequest<sample::Service> request)
|
|
: ServiceImpl(was_deleted),
|
|
binding_(this, std::move(request)),
|
|
closure_(closure) {
|
|
binding_.set_connection_error_handler(
|
|
base::Bind(&ServiceImplWithBinding::OnConnectionError,
|
|
base::Unretained(this)));
|
|
}
|
|
|
|
private:
|
|
~ServiceImplWithBinding() override{
|
|
closure_.Run();
|
|
}
|
|
|
|
void OnConnectionError() { delete this; }
|
|
|
|
Binding<sample::Service> binding_;
|
|
base::Closure closure_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(ServiceImplWithBinding);
|
|
};
|
|
|
|
// Tests that the binding may be deleted in the connection error handler.
|
|
TEST_P(BindingTest, SelfDeleteOnConnectionError) {
|
|
bool was_deleted = false;
|
|
sample::ServicePtr ptr;
|
|
// This should delete itself on connection error.
|
|
base::RunLoop run_loop;
|
|
new ServiceImplWithBinding(&was_deleted, run_loop.QuitClosure(),
|
|
MakeRequest(&ptr));
|
|
ptr.reset();
|
|
EXPECT_FALSE(was_deleted);
|
|
run_loop.Run();
|
|
EXPECT_TRUE(was_deleted);
|
|
}
|
|
|
|
// Tests that explicitly calling Unbind followed by rebinding works.
|
|
TEST_P(BindingTest, Unbind) {
|
|
ServiceImpl impl;
|
|
sample::ServicePtr ptr;
|
|
Binding<sample::Service> binding(&impl, MakeRequest(&ptr));
|
|
|
|
bool called = false;
|
|
base::RunLoop run_loop;
|
|
ptr->Frobinate(nullptr, sample::Service::BazOptions::REGULAR, nullptr,
|
|
SetFlagAndRunClosure<int32_t>(&called,
|
|
run_loop.QuitClosure()));
|
|
run_loop.Run();
|
|
EXPECT_TRUE(called);
|
|
|
|
called = false;
|
|
auto request = binding.Unbind();
|
|
EXPECT_FALSE(binding.is_bound());
|
|
// All calls should fail when not bound...
|
|
ptr->Frobinate(nullptr, sample::Service::BazOptions::REGULAR, nullptr,
|
|
SetFlagAndRunClosure<int32_t>(&called,
|
|
run_loop.QuitClosure()));
|
|
base::RunLoop().RunUntilIdle();
|
|
EXPECT_FALSE(called);
|
|
|
|
called = false;
|
|
binding.Bind(std::move(request));
|
|
EXPECT_TRUE(binding.is_bound());
|
|
// ...and should succeed again when the rebound.
|
|
base::RunLoop run_loop2;
|
|
ptr->Frobinate(nullptr, sample::Service::BazOptions::REGULAR, nullptr,
|
|
SetFlagAndRunClosure<int32_t>(&called,
|
|
run_loop2.QuitClosure()));
|
|
run_loop2.Run();
|
|
EXPECT_TRUE(called);
|
|
}
|
|
|
|
class IntegerAccessorImpl : public sample::IntegerAccessor {
|
|
public:
|
|
IntegerAccessorImpl() {}
|
|
~IntegerAccessorImpl() override {}
|
|
|
|
private:
|
|
// sample::IntegerAccessor implementation.
|
|
void GetInteger(const GetIntegerCallback& callback) override {
|
|
callback.Run(1, sample::Enum::VALUE);
|
|
}
|
|
void SetInteger(int64_t data, sample::Enum type) override {}
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(IntegerAccessorImpl);
|
|
};
|
|
|
|
TEST_P(BindingTest, PauseResume) {
|
|
bool called = false;
|
|
base::RunLoop run_loop;
|
|
sample::ServicePtr ptr;
|
|
auto request = MakeRequest(&ptr);
|
|
ServiceImpl impl;
|
|
Binding<sample::Service> binding(&impl, std::move(request));
|
|
binding.PauseIncomingMethodCallProcessing();
|
|
ptr->Frobinate(nullptr, sample::Service::BazOptions::REGULAR, nullptr,
|
|
SetFlagAndRunClosure<int32_t>(&called,
|
|
run_loop.QuitClosure()));
|
|
EXPECT_FALSE(called);
|
|
base::RunLoop().RunUntilIdle();
|
|
// Frobinate() should not be called as the binding is paused.
|
|
EXPECT_FALSE(called);
|
|
|
|
// Resume the binding, which should trigger processing.
|
|
binding.ResumeIncomingMethodCallProcessing();
|
|
run_loop.Run();
|
|
EXPECT_TRUE(called);
|
|
}
|
|
|
|
// Verifies the connection error handler is not run while a binding is paused.
|
|
TEST_P(BindingTest, ErrorHandleNotRunWhilePaused) {
|
|
bool called = false;
|
|
base::RunLoop run_loop;
|
|
sample::ServicePtr ptr;
|
|
auto request = MakeRequest(&ptr);
|
|
ServiceImpl impl;
|
|
Binding<sample::Service> binding(&impl, std::move(request));
|
|
binding.set_connection_error_handler(
|
|
SetFlagAndRunClosure(&called, run_loop.QuitClosure()));
|
|
binding.PauseIncomingMethodCallProcessing();
|
|
|
|
ptr.reset();
|
|
base::RunLoop().RunUntilIdle();
|
|
// The connection error handle should not be called as the binding is paused.
|
|
EXPECT_FALSE(called);
|
|
|
|
// Resume the binding, which should trigger the error handler.
|
|
binding.ResumeIncomingMethodCallProcessing();
|
|
run_loop.Run();
|
|
EXPECT_TRUE(called);
|
|
}
|
|
|
|
class PingServiceImpl : public test::PingService {
|
|
public:
|
|
PingServiceImpl() {}
|
|
~PingServiceImpl() override {}
|
|
|
|
// test::PingService:
|
|
void Ping(const PingCallback& callback) override {
|
|
if (!ping_handler_.is_null())
|
|
ping_handler_.Run();
|
|
callback.Run();
|
|
}
|
|
|
|
void set_ping_handler(const base::Closure& handler) {
|
|
ping_handler_ = handler;
|
|
}
|
|
|
|
private:
|
|
base::Closure ping_handler_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(PingServiceImpl);
|
|
};
|
|
|
|
class CallbackFilter : public MessageReceiver {
|
|
public:
|
|
explicit CallbackFilter(const base::Closure& callback)
|
|
: callback_(callback) {}
|
|
~CallbackFilter() override {}
|
|
|
|
static std::unique_ptr<CallbackFilter> Wrap(const base::Closure& callback) {
|
|
return std::make_unique<CallbackFilter>(callback);
|
|
}
|
|
|
|
// MessageReceiver:
|
|
bool Accept(Message* message) override {
|
|
callback_.Run();
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
const base::Closure callback_;
|
|
};
|
|
|
|
// Verifies that message filters are notified in the order they were added and
|
|
// are always notified before a message is dispatched.
|
|
TEST_P(BindingTest, MessageFilter) {
|
|
test::PingServicePtr ptr;
|
|
PingServiceImpl impl;
|
|
mojo::Binding<test::PingService> binding(&impl, MakeRequest(&ptr));
|
|
|
|
int status = 0;
|
|
auto handler_helper = [] (int* status, int expected_status, int new_status) {
|
|
EXPECT_EQ(expected_status, *status);
|
|
*status = new_status;
|
|
};
|
|
auto create_handler = [&] (int expected_status, int new_status) {
|
|
return base::Bind(handler_helper, &status, expected_status, new_status);
|
|
};
|
|
|
|
binding.AddFilter(CallbackFilter::Wrap(create_handler(0, 1)));
|
|
binding.AddFilter(CallbackFilter::Wrap(create_handler(1, 2)));
|
|
impl.set_ping_handler(create_handler(2, 3));
|
|
|
|
for (int i = 0; i < 10; ++i) {
|
|
status = 0;
|
|
base::RunLoop loop;
|
|
ptr->Ping(loop.QuitClosure());
|
|
loop.Run();
|
|
EXPECT_EQ(3, status);
|
|
}
|
|
}
|
|
|
|
void Fail() {
|
|
FAIL() << "Unexpected connection error";
|
|
}
|
|
|
|
TEST_P(BindingTest, FlushForTesting) {
|
|
bool called = false;
|
|
sample::ServicePtr ptr;
|
|
auto request = MakeRequest(&ptr);
|
|
ServiceImpl impl;
|
|
Binding<sample::Service> binding(&impl, std::move(request));
|
|
binding.set_connection_error_handler(base::Bind(&Fail));
|
|
|
|
ptr->Frobinate(nullptr, sample::Service::BazOptions::REGULAR, nullptr,
|
|
SetFlagAndRunClosure<int32_t>(&called));
|
|
EXPECT_FALSE(called);
|
|
// Because the flush is sent from the binding, it only guarantees that the
|
|
// request has been received, not the response. The second flush waits for the
|
|
// response to be received.
|
|
binding.FlushForTesting();
|
|
binding.FlushForTesting();
|
|
EXPECT_TRUE(called);
|
|
}
|
|
|
|
TEST_P(BindingTest, FlushForTestingWithClosedPeer) {
|
|
bool called = false;
|
|
sample::ServicePtr ptr;
|
|
auto request = MakeRequest(&ptr);
|
|
ServiceImpl impl;
|
|
Binding<sample::Service> binding(&impl, std::move(request));
|
|
binding.set_connection_error_handler(SetFlagAndRunClosure(&called));
|
|
ptr.reset();
|
|
|
|
EXPECT_FALSE(called);
|
|
binding.FlushForTesting();
|
|
EXPECT_TRUE(called);
|
|
binding.FlushForTesting();
|
|
}
|
|
|
|
TEST_P(BindingTest, ConnectionErrorWithReason) {
|
|
sample::ServicePtr ptr;
|
|
auto request = MakeRequest(&ptr);
|
|
ServiceImpl impl;
|
|
Binding<sample::Service> binding(&impl, std::move(request));
|
|
|
|
base::RunLoop run_loop;
|
|
binding.set_connection_error_with_reason_handler(base::Bind(
|
|
[](const base::Closure& quit_closure, uint32_t custom_reason,
|
|
const std::string& description) {
|
|
EXPECT_EQ(1234u, custom_reason);
|
|
EXPECT_EQ("hello", description);
|
|
quit_closure.Run();
|
|
},
|
|
run_loop.QuitClosure()));
|
|
|
|
ptr.ResetWithReason(1234u, "hello");
|
|
|
|
run_loop.Run();
|
|
}
|
|
|
|
template <typename T>
|
|
struct WeakPtrImplRefTraits {
|
|
using PointerType = base::WeakPtr<T>;
|
|
|
|
static bool IsNull(const base::WeakPtr<T>& ptr) { return !ptr; }
|
|
static T* GetRawPointer(base::WeakPtr<T>* ptr) { return ptr->get(); }
|
|
};
|
|
|
|
template <typename T>
|
|
using WeakBinding = Binding<T, WeakPtrImplRefTraits<T>>;
|
|
|
|
TEST_P(BindingTest, CustomImplPointerType) {
|
|
PingServiceImpl impl;
|
|
base::WeakPtrFactory<test::PingService> weak_factory(&impl);
|
|
|
|
test::PingServicePtr proxy;
|
|
WeakBinding<test::PingService> binding(weak_factory.GetWeakPtr(),
|
|
MakeRequest(&proxy));
|
|
|
|
{
|
|
// Ensure the binding is functioning.
|
|
base::RunLoop run_loop;
|
|
proxy->Ping(run_loop.QuitClosure());
|
|
run_loop.Run();
|
|
}
|
|
|
|
{
|
|
// Attempt to dispatch another message after the WeakPtr is invalidated.
|
|
base::Closure assert_not_reached = base::Bind([] { NOTREACHED(); });
|
|
impl.set_ping_handler(assert_not_reached);
|
|
proxy->Ping(assert_not_reached);
|
|
|
|
// The binding will close its end of the pipe which will trigger a
|
|
// connection error on |proxy|.
|
|
base::RunLoop run_loop;
|
|
proxy.set_connection_error_handler(run_loop.QuitClosure());
|
|
weak_factory.InvalidateWeakPtrs();
|
|
run_loop.Run();
|
|
}
|
|
}
|
|
|
|
TEST_P(BindingTest, ReportBadMessage) {
|
|
bool called = false;
|
|
test::PingServicePtr ptr;
|
|
auto request = MakeRequest(&ptr);
|
|
base::RunLoop run_loop;
|
|
ptr.set_connection_error_handler(
|
|
SetFlagAndRunClosure(&called, run_loop.QuitClosure()));
|
|
PingServiceImpl impl;
|
|
Binding<test::PingService> binding(&impl, std::move(request));
|
|
impl.set_ping_handler(base::Bind(
|
|
[](Binding<test::PingService>* binding) {
|
|
binding->ReportBadMessage("received bad message");
|
|
},
|
|
&binding));
|
|
|
|
std::string received_error;
|
|
core::SetDefaultProcessErrorCallback(
|
|
base::Bind([](std::string* out_error,
|
|
const std::string& error) { *out_error = error; },
|
|
&received_error));
|
|
|
|
ptr->Ping(base::Bind([] {}));
|
|
EXPECT_FALSE(called);
|
|
run_loop.Run();
|
|
EXPECT_TRUE(called);
|
|
EXPECT_EQ("received bad message", received_error);
|
|
|
|
core::SetDefaultProcessErrorCallback(mojo::core::ProcessErrorCallback());
|
|
}
|
|
|
|
TEST_P(BindingTest, GetBadMessageCallback) {
|
|
test::PingServicePtr ptr;
|
|
auto request = MakeRequest(&ptr);
|
|
base::RunLoop run_loop;
|
|
PingServiceImpl impl;
|
|
ReportBadMessageCallback bad_message_callback;
|
|
|
|
std::string received_error;
|
|
core::SetDefaultProcessErrorCallback(
|
|
base::Bind([](std::string* out_error,
|
|
const std::string& error) { *out_error = error; },
|
|
&received_error));
|
|
|
|
{
|
|
Binding<test::PingService> binding(&impl, std::move(request));
|
|
impl.set_ping_handler(base::Bind(
|
|
[](Binding<test::PingService>* binding,
|
|
ReportBadMessageCallback* out_callback) {
|
|
*out_callback = binding->GetBadMessageCallback();
|
|
},
|
|
&binding, &bad_message_callback));
|
|
ptr->Ping(run_loop.QuitClosure());
|
|
run_loop.Run();
|
|
EXPECT_TRUE(received_error.empty());
|
|
EXPECT_TRUE(bad_message_callback);
|
|
}
|
|
|
|
std::move(bad_message_callback).Run("delayed bad message");
|
|
EXPECT_EQ("delayed bad message", received_error);
|
|
|
|
core::SetDefaultProcessErrorCallback(mojo::core::ProcessErrorCallback());
|
|
}
|
|
|
|
// StrongBindingTest -----------------------------------------------------------
|
|
|
|
using StrongBindingTest = BindingsTestBase;
|
|
|
|
// Tests that destroying a mojo::StrongBinding closes the bound message pipe
|
|
// handle but does *not* destroy the implementation object.
|
|
TEST_P(StrongBindingTest, DestroyClosesMessagePipe) {
|
|
base::RunLoop run_loop;
|
|
bool encountered_error = false;
|
|
bool was_deleted = false;
|
|
sample::ServicePtr ptr;
|
|
auto request = MakeRequest(&ptr);
|
|
ptr.set_connection_error_handler(
|
|
SetFlagAndRunClosure(&encountered_error, run_loop.QuitClosure()));
|
|
bool called = false;
|
|
base::RunLoop run_loop2;
|
|
|
|
auto binding = MakeStrongBinding(std::make_unique<ServiceImpl>(&was_deleted),
|
|
std::move(request));
|
|
ptr->Frobinate(
|
|
nullptr, sample::Service::BazOptions::REGULAR, nullptr,
|
|
SetFlagAndRunClosure<int32_t>(&called, run_loop2.QuitClosure()));
|
|
run_loop2.Run();
|
|
EXPECT_TRUE(called);
|
|
EXPECT_FALSE(encountered_error);
|
|
binding->Close();
|
|
|
|
// Now that the StrongBinding is closed we should detect an error on the other
|
|
// end of the pipe.
|
|
run_loop.Run();
|
|
EXPECT_TRUE(encountered_error);
|
|
|
|
// Destroying the StrongBinding also destroys the impl.
|
|
ASSERT_TRUE(was_deleted);
|
|
}
|
|
|
|
// Tests the typical case, where the implementation object owns the
|
|
// StrongBinding (and should be destroyed on connection error).
|
|
TEST_P(StrongBindingTest, ConnectionErrorDestroysImpl) {
|
|
sample::ServicePtr ptr;
|
|
bool was_deleted = false;
|
|
// Will delete itself.
|
|
base::RunLoop run_loop;
|
|
new ServiceImplWithBinding(&was_deleted, run_loop.QuitClosure(),
|
|
MakeRequest(&ptr));
|
|
|
|
base::RunLoop().RunUntilIdle();
|
|
EXPECT_FALSE(was_deleted);
|
|
|
|
ptr.reset();
|
|
EXPECT_FALSE(was_deleted);
|
|
run_loop.Run();
|
|
EXPECT_TRUE(was_deleted);
|
|
}
|
|
|
|
TEST_P(StrongBindingTest, FlushForTesting) {
|
|
bool called = false;
|
|
bool was_deleted = false;
|
|
sample::ServicePtr ptr;
|
|
auto request = MakeRequest(&ptr);
|
|
auto binding = MakeStrongBinding(std::make_unique<ServiceImpl>(&was_deleted),
|
|
std::move(request));
|
|
binding->set_connection_error_handler(base::Bind(&Fail));
|
|
|
|
ptr->Frobinate(nullptr, sample::Service::BazOptions::REGULAR, nullptr,
|
|
SetFlagAndRunClosure<int32_t>(&called));
|
|
EXPECT_FALSE(called);
|
|
// Because the flush is sent from the binding, it only guarantees that the
|
|
// request has been received, not the response. The second flush waits for the
|
|
// response to be received.
|
|
ASSERT_TRUE(binding);
|
|
binding->FlushForTesting();
|
|
ASSERT_TRUE(binding);
|
|
binding->FlushForTesting();
|
|
EXPECT_TRUE(called);
|
|
EXPECT_FALSE(was_deleted);
|
|
ptr.reset();
|
|
ASSERT_TRUE(binding);
|
|
binding->set_connection_error_handler(base::Closure());
|
|
binding->FlushForTesting();
|
|
EXPECT_TRUE(was_deleted);
|
|
}
|
|
|
|
TEST_P(StrongBindingTest, FlushForTestingWithClosedPeer) {
|
|
bool called = false;
|
|
bool was_deleted = false;
|
|
sample::ServicePtr ptr;
|
|
auto request = MakeRequest(&ptr);
|
|
auto binding = MakeStrongBinding(std::make_unique<ServiceImpl>(&was_deleted),
|
|
std::move(request));
|
|
binding->set_connection_error_handler(SetFlagAndRunClosure(&called));
|
|
ptr.reset();
|
|
|
|
EXPECT_FALSE(called);
|
|
EXPECT_FALSE(was_deleted);
|
|
ASSERT_TRUE(binding);
|
|
binding->FlushForTesting();
|
|
EXPECT_TRUE(called);
|
|
EXPECT_TRUE(was_deleted);
|
|
ASSERT_FALSE(binding);
|
|
}
|
|
|
|
TEST_P(StrongBindingTest, ConnectionErrorWithReason) {
|
|
sample::ServicePtr ptr;
|
|
auto request = MakeRequest(&ptr);
|
|
auto binding =
|
|
MakeStrongBinding(std::make_unique<ServiceImpl>(), std::move(request));
|
|
base::RunLoop run_loop;
|
|
binding->set_connection_error_with_reason_handler(base::Bind(
|
|
[](const base::Closure& quit_closure, uint32_t custom_reason,
|
|
const std::string& description) {
|
|
EXPECT_EQ(5678u, custom_reason);
|
|
EXPECT_EQ("hello", description);
|
|
quit_closure.Run();
|
|
},
|
|
run_loop.QuitClosure()));
|
|
|
|
ptr.ResetWithReason(5678u, "hello");
|
|
|
|
run_loop.Run();
|
|
}
|
|
|
|
INSTANTIATE_MOJO_BINDINGS_TEST_CASE_P(BindingTest);
|
|
INSTANTIATE_MOJO_BINDINGS_TEST_CASE_P(StrongBindingTest);
|
|
|
|
} // namespace
|
|
} // mojo
|