blob: 39a9c75d52074e0d416da4d1bde2c17866a61b5f [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 "mojo/public/cpp/application/service_provider_impl.h"
#include <utility>
#include "gtest/gtest.h"
#include "mojo/public/cpp/application/connect.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
#include "mojo/public/cpp/system/macros.h"
#include "mojo/public/cpp/utility/run_loop.h"
#include "mojo/public/interfaces/application/service_provider.mojom.h"
#include "mojo/public/interfaces/bindings/tests/ping_service.mojom.h"
namespace mojo {
namespace {
class ServiceProviderImplTest : public testing::Test {
public:
ServiceProviderImplTest() {}
~ServiceProviderImplTest() override { loop_.RunUntilIdle(); }
RunLoop& loop() { return loop_; }
protected:
void QuitLoop(bool ok) {
EXPECT_TRUE(ok);
loop_.Quit();
}
private:
RunLoop loop_;
MOJO_DISALLOW_COPY_AND_ASSIGN(ServiceProviderImplTest);
};
class PingServiceImpl : public test::PingService {
public:
PingServiceImpl(InterfaceRequest<test::PingService> ping_service_request)
: strong_binding_(this, std::move(ping_service_request)) {}
~PingServiceImpl() override {}
// |test::PingService|:
void Ping(const PingCallback& callback) override { callback.Run(); }
private:
StrongBinding<test::PingService> strong_binding_;
MOJO_DISALLOW_COPY_AND_ASSIGN(PingServiceImpl);
};
TEST_F(ServiceProviderImplTest, Basic) {
const char kRemoteUrl[] = "https://example.com/remote.mojo";
const char kConnectionUrl[] = "https://example.com/me.mojo";
const char kPing1[] = "Ping1";
const char kPing2[] = "Ping2";
const char kPing3[] = "Ping3";
ServiceProviderPtr sp;
ServiceProviderImpl impl(ConnectionContext(ConnectionContext::Type::INCOMING,
kRemoteUrl, kConnectionUrl),
GetProxy(&sp));
EXPECT_EQ(ConnectionContext::Type::INCOMING, impl.connection_context().type);
EXPECT_EQ(kRemoteUrl, impl.connection_context().remote_url);
EXPECT_EQ(kConnectionUrl, impl.connection_context().connection_url);
impl.AddService<test::PingService>(
[&kRemoteUrl, &kConnectionUrl](
const ConnectionContext& connection_context,
InterfaceRequest<test::PingService> ping_service_request) {
EXPECT_EQ(ConnectionContext::Type::INCOMING, connection_context.type);
EXPECT_EQ(kRemoteUrl, connection_context.remote_url);
EXPECT_EQ(kConnectionUrl, connection_context.connection_url);
new PingServiceImpl(std::move(ping_service_request));
},
kPing1);
impl.AddService<test::PingService>(
[&kRemoteUrl, &kConnectionUrl](
const ConnectionContext& connection_context,
InterfaceRequest<test::PingService> ping_service_request) {
EXPECT_EQ(ConnectionContext::Type::INCOMING, connection_context.type);
EXPECT_EQ(kRemoteUrl, connection_context.remote_url);
EXPECT_EQ(kConnectionUrl, connection_context.connection_url);
new PingServiceImpl(std::move(ping_service_request));
},
kPing2);
{
test::PingServicePtr ping1;
ConnectToService(sp.get(), GetProxy(&ping1), kPing1);
ping1.set_connection_error_handler([this] { QuitLoop(false); });
ping1->Ping([this] { QuitLoop(true); });
loop().Run();
}
loop().RunUntilIdle(); // Run stuff caused by destructors.
{
test::PingServicePtr ping2;
ConnectToService(sp.get(), GetProxy(&ping2), kPing2);
ping2.set_connection_error_handler([this] { QuitLoop(false); });
ping2->Ping([this] { QuitLoop(true); });
loop().Run();
}
loop().RunUntilIdle(); // Run stuff caused by destructors.
// "Ping3" isn't actually registered!
{
test::PingServicePtr ping3;
ConnectToService(sp.get(), GetProxy(&ping3), kPing3);
ping3.set_connection_error_handler([this] { QuitLoop(true); });
ping3->Ping([this] { QuitLoop(false); });
loop().Run();
}
loop().RunUntilIdle(); // Run stuff caused by destructors.
impl.RemoveService<test::PingService>(kPing2);
// "Ping2" should no longer work.
{
test::PingServicePtr ping2;
ConnectToService(sp.get(), GetProxy(&ping2), kPing2);
ping2.set_connection_error_handler([this] { QuitLoop(true); });
ping2->Ping([this] { QuitLoop(false); });
loop().Run();
}
loop().RunUntilIdle(); // Run stuff caused by destructors.
// But "Ping1" should still work.
{
test::PingServicePtr ping1;
ConnectToService(sp.get(), GetProxy(&ping1), kPing1);
ping1.set_connection_error_handler([this] { QuitLoop(false); });
ping1->Ping([this] { QuitLoop(true); });
loop().Run();
}
loop().RunUntilIdle(); // Run stuff caused by destructors.
impl.RemoveServiceForName(kPing1);
// "Ping1" should no longer work.
{
test::PingServicePtr ping1;
ConnectToService(sp.get(), GetProxy(&ping1), kPing1);
ping1.set_connection_error_handler([this] { QuitLoop(true); });
ping1->Ping([this] { QuitLoop(false); });
loop().Run();
}
loop().RunUntilIdle(); // Run stuff caused by destructors.
sp.reset();
loop().RunUntilIdle();
}
TEST_F(ServiceProviderImplTest, CloseAndRebind) {
const char kRemoteUrl1[] = "https://example.com/remote1.mojo";
const char kRemoteUrl2[] = "https://example.com/remote2.mojo";
const char kConnectionUrl[] = "https://example.com/me.mojo";
const char kPing[] = "Ping";
ServiceProviderPtr sp1;
ServiceProviderImpl impl(ConnectionContext(ConnectionContext::Type::INCOMING,
kRemoteUrl1, kConnectionUrl),
GetProxy(&sp1));
EXPECT_EQ(ConnectionContext::Type::INCOMING, impl.connection_context().type);
EXPECT_EQ(kRemoteUrl1, impl.connection_context().remote_url);
EXPECT_EQ(kConnectionUrl, impl.connection_context().connection_url);
impl.AddService<test::PingService>(
[&kRemoteUrl1, &kRemoteUrl2, &kConnectionUrl](
const ConnectionContext& connection_context,
InterfaceRequest<test::PingService> ping_service_request) {
EXPECT_EQ(ConnectionContext::Type::INCOMING, connection_context.type);
EXPECT_TRUE(connection_context.remote_url == kRemoteUrl1 ||
connection_context.remote_url == kRemoteUrl2);
EXPECT_EQ(kConnectionUrl, connection_context.connection_url);
new PingServiceImpl(std::move(ping_service_request));
},
kPing);
{
test::PingServicePtr ping;
ConnectToService(sp1.get(), GetProxy(&ping), kPing);
ping.set_connection_error_handler([this] { QuitLoop(false); });
ping->Ping([this] { QuitLoop(true); });
loop().Run();
}
loop().RunUntilIdle(); // Run stuff caused by destructors.
impl.Close();
EXPECT_EQ(ConnectionContext::Type::UNKNOWN, impl.connection_context().type);
EXPECT_EQ(std::string(), impl.connection_context().remote_url);
EXPECT_EQ(std::string(), impl.connection_context().connection_url);
sp1.reset();
loop().RunUntilIdle();
ServiceProviderPtr sp2;
impl.Bind(ConnectionContext(ConnectionContext::Type::INCOMING, kRemoteUrl2,
kConnectionUrl),
GetProxy(&sp2));
EXPECT_EQ(ConnectionContext::Type::INCOMING, impl.connection_context().type);
EXPECT_EQ(kRemoteUrl2, impl.connection_context().remote_url);
EXPECT_EQ(kConnectionUrl, impl.connection_context().connection_url);
{
test::PingServicePtr ping;
ConnectToService(sp2.get(), GetProxy(&ping), kPing);
ping.set_connection_error_handler([this] { QuitLoop(false); });
ping->Ping([this] { QuitLoop(true); });
loop().Run();
}
loop().RunUntilIdle(); // Run stuff caused by destructors.
// Can close multiple times.
impl.Close();
impl.Close();
sp2.reset();
loop().RunUntilIdle();
}
TEST_F(ServiceProviderImplTest, Bind) {
const char kRemoteUrl[] = "https://example.com/remote.mojo";
const char kConnectionUrl[] = "https://example.com/me.mojo";
const char kPing[] = "Ping";
ServiceProviderPtr sp;
ServiceProviderImpl impl;
EXPECT_EQ(ConnectionContext::Type::UNKNOWN, impl.connection_context().type);
EXPECT_EQ(std::string(), impl.connection_context().remote_url);
EXPECT_EQ(std::string(), impl.connection_context().connection_url);
impl.Bind(ConnectionContext(ConnectionContext::Type::INCOMING, kRemoteUrl,
kConnectionUrl),
GetProxy(&sp));
impl.AddService<test::PingService>(
[&kRemoteUrl, &kConnectionUrl](
const ConnectionContext& connection_context,
InterfaceRequest<test::PingService> request) {
EXPECT_EQ(ConnectionContext::Type::INCOMING, connection_context.type);
EXPECT_EQ(kRemoteUrl, connection_context.remote_url);
EXPECT_EQ(kConnectionUrl, connection_context.connection_url);
new PingServiceImpl(std::move(request));
},
kPing);
{
test::PingServicePtr ping;
ConnectToService(sp.get(), GetProxy(&ping), kPing);
ping.set_connection_error_handler([this] { QuitLoop(false); });
ping->Ping([this] { QuitLoop(true); });
loop().Run();
}
loop().RunUntilIdle(); // Run stuff caused by destructors.
sp.reset();
loop().RunUntilIdle();
}
class FauxServiceProvider : public ServiceProvider {
public:
explicit FauxServiceProvider(
std::function<void(const std::string& service_name)>
on_connect_to_service)
: on_connect_to_service_(on_connect_to_service) {}
~FauxServiceProvider() override {}
// |ServiceProvider|:
void ConnectToService(const String& service_name,
ScopedMessagePipeHandle client_handle) override {
on_connect_to_service_(service_name.get());
}
private:
std::function<void(const std::string& service_name)> on_connect_to_service_;
MOJO_DISALLOW_COPY_AND_ASSIGN(FauxServiceProvider);
};
TEST_F(ServiceProviderImplTest, FallbackServiceProvider) {
const char kWhatever[] = "Whatever";
ServiceProviderPtr sp;
ServiceProviderImpl impl(ConnectionContext(ConnectionContext::Type::INCOMING,
"https://example.com/remote.mojo",
"https://example.com/me.mojo"),
GetProxy(&sp));
{
test::PingServicePtr ping;
ConnectToService(sp.get(), GetProxy(&ping), kWhatever);
ping.set_connection_error_handler([this] { QuitLoop(true); });
ping->Ping([this] { QuitLoop(false); });
loop().Run();
}
loop().RunUntilIdle(); // Run stuff caused by destructors.
bool was_run = false;
FauxServiceProvider fallback_sp(
[this, &kWhatever, &was_run](const std::string& service_name) {
EXPECT_EQ(kWhatever, service_name);
was_run = true;
});
impl.set_fallback_service_provider(&fallback_sp);
{
test::PingServicePtr ping;
ConnectToService(sp.get(), GetProxy(&ping), kWhatever);
ping.set_connection_error_handler([this] { QuitLoop(true); });
EXPECT_FALSE(was_run);
ping->Ping([this] { QuitLoop(false); });
loop().Run();
EXPECT_TRUE(was_run);
}
loop().RunUntilIdle(); // Run stuff caused by destructors.
// Clear the fallback.
impl.set_fallback_service_provider(nullptr);
was_run = false;
{
test::PingServicePtr ping;
ConnectToService(sp.get(), GetProxy(&ping), kWhatever);
ping.set_connection_error_handler([this] { QuitLoop(true); });
ping->Ping([this] { QuitLoop(false); });
loop().Run();
}
loop().RunUntilIdle(); // Run stuff caused by destructors.
sp.reset();
loop().RunUntilIdle();
EXPECT_FALSE(was_run);
}
TEST_F(ServiceProviderImplTest, ConstructRequestNotPending) {
ServiceProviderImpl impl(ConnectionContext(ConnectionContext::Type::INCOMING,
"https://example.com/remote.mojo",
"https://example.com/me.mojo"),
InterfaceRequest<ServiceProvider>());
EXPECT_EQ(ConnectionContext::Type::UNKNOWN, impl.connection_context().type);
EXPECT_EQ(std::string(), impl.connection_context().remote_url);
EXPECT_EQ(std::string(), impl.connection_context().connection_url);
}
// TODO(vtl): Explicitly test |AddServiceForName()|?
} // namespace
} // namespace mojo