blob: 8db2ac3cd690ea4819f486fac366e4eda0f61fcb [file] [log] [blame]
// Copyright 2023 The Fuchsia 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 <fidl/fidl.service.test/cpp/fidl.h>
#include <fidl/fidl.service.test/cpp/wire_test_base.h>
#include <lib/async/dispatcher.h>
#include <lib/component/incoming/cpp/service_member_watcher.h>
#include <lib/component/tests/echo_server.h>
#include <lib/fidl/cpp/binding_set.h>
#include <lib/fidl/cpp/string.h>
#include <lib/fit/function.h>
#include <lib/sys/component/cpp/testing/realm_builder.h>
#include <lib/sys/component/cpp/testing/realm_builder_types.h>
#include <lib/syslog/cpp/macros.h>
#include <zircon/status.h>
#include <memory>
#include <gtest/gtest.h>
#include "src/lib/testing/loop_fixture/real_loop_fixture.h"
namespace {
using namespace component_testing;
class LocalEchoServer : public LocalCppComponent {
public:
static constexpr const char kAlternateServiceInstance[] = "alternate";
explicit LocalEchoServer(async_dispatcher_t* dispatcher, unsigned int* called)
: dispatcher_(dispatcher), called_(called) {}
void OnStart() override {
servers_.emplace("default", dispatcher_, called_);
ASSERT_EQ(outgoing()
->AddUnmanagedProtocol<fidl_service_test::Echo>(servers_.back().CreateHandler())
.status_value(),
ZX_OK);
{
servers_.emplace("default", dispatcher_, called_);
auto result = outgoing()->AddService<fidl_service_test::EchoService>(
fidl_service_test::EchoService::InstanceHandler({
.foo = servers_.back().CreateHandler(),
.bar = servers_.back().CreateHandler(),
}));
ASSERT_EQ(result.status_value(), ZX_OK);
}
// Add another service instance. This simulates how an aggregated service would present
// multiple service instances to a client.
{
servers_.emplace(kAlternateServiceInstance, dispatcher_, called_);
auto result = outgoing()->AddService<fidl_service_test::EchoService>(
fidl_service_test::EchoService::InstanceHandler({
.foo = servers_.back().CreateHandler(),
.bar = servers_.back().CreateHandler(),
}),
kAlternateServiceInstance);
ASSERT_EQ(result.status_value(), ZX_OK);
}
}
private:
async_dispatcher_t* dispatcher_;
unsigned int* called_;
std::queue<EchoCommon> servers_;
};
class IncomingTest : public gtest::RealLoopFixture {};
TEST_F(IncomingTest, ConnectsToProtocolInNamespace) {
auto realm_builder = RealmBuilder::Create();
realm_builder.AddChild("echo_client", "#meta/echo_client.cm",
ChildOptions{.startup_mode = StartupMode::EAGER});
unsigned int called = 0;
realm_builder.AddLocalChild("echo_server", [dispatcher = dispatcher(), called_ptr = &called]() {
return std::make_unique<LocalEchoServer>(dispatcher, called_ptr);
});
realm_builder.AddRoute(Route{
.capabilities = {Protocol{fidl::DiscoverableProtocolName<fidl_service_test::Echo>}},
.source = ChildRef{"echo_server"},
.targets = {ChildRef{"echo_client"}},
});
auto realm = realm_builder.Build(dispatcher());
auto cleanup = fit::defer([&]() {
bool complete = false;
realm.Teardown([&](fit::result<fuchsia::component::Error> result) { complete = true; });
RunLoopUntil([&]() { return complete; });
});
RunLoopUntil([&called]() { return called > 0; });
}
TEST_F(IncomingTest, ConnectsToServiceInNamespace) {
auto realm_builder = RealmBuilder::Create();
realm_builder.AddChild("echo_client", "#meta/echo_service_client.cm",
ChildOptions{.startup_mode = StartupMode::EAGER});
unsigned int called = 0;
realm_builder.AddLocalChild("echo_server", [dispatcher = dispatcher(), called_ptr = &called]() {
return std::make_unique<LocalEchoServer>(dispatcher, called_ptr);
});
realm_builder.AddRoute(Route{
.capabilities = {Service{fidl_service_test::EchoService::Name}},
.source = ChildRef{"echo_server"},
.targets = {ChildRef{"echo_client"}},
});
auto realm = realm_builder.Build(dispatcher());
auto cleanup = fit::defer([&]() {
bool complete = false;
realm.Teardown([&](fit::result<fuchsia::component::Error> result) { complete = true; });
RunLoopUntil([&]() { return complete; });
});
RunLoopUntil([&called]() { return called > 0; });
}
TEST_F(IncomingTest, ConnectsToAggregatedServiceInNamespace) {
auto realm_builder = RealmBuilder::Create();
realm_builder.AddChild("echo_client", "#meta/echo_service_watcher_client.cm",
ChildOptions{.startup_mode = StartupMode::EAGER});
unsigned int called = 0;
realm_builder.AddLocalChild("echo_server", [dispatcher = dispatcher(), called_ptr = &called]() {
return std::make_unique<LocalEchoServer>(dispatcher, called_ptr);
});
realm_builder.AddRoute(Route{
.capabilities = {Service{fidl_service_test::EchoService::Name}},
.source = ChildRef{"echo_server"},
.targets = {ChildRef{"echo_client"}},
});
auto realm = realm_builder.Build(dispatcher());
auto cleanup = fit::defer([&]() {
bool complete = false;
realm.Teardown([&](fit::result<fuchsia::component::Error> result) { complete = true; });
RunLoopUntil([&]() { return complete; });
});
// Here we expect 4 calls to the servers:
// The client component runs a watcher with the default constructor:
// sees instance "default" and calls EchoString (1)
// sees instance "alternate" and calls EchoString (2)
// Then the client gets the idle callback and stops.
// The the client does runs again with a manually set service root, making 2 more calls.
RunLoopUntil([&called]() { return called >= 4; });
}
} // namespace