blob: c6d0493949cec18360a165b30b4b5e2d2b353047 [file] [log] [blame]
// Copyright 2022 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 <fuchsia/logger/cpp/fidl.h>
#include <fuchsia/sdk/examples/fidl/cpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/async/default.h>
#include <lib/async/dispatcher.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 <zircon/status.h>
#include <memory>
#include <gtest/gtest.h>
constexpr static char kGreetingService[] = "greeting_service";
// Create a test fixture to manage the async loop required by Realm Builder.
class RealmBuilderTest : public ::testing::Test {
protected:
RealmBuilderTest() : loop_(&kAsyncLoopConfigAttachToCurrentThread) {}
async_dispatcher_t* dispatcher() { return loop_.dispatcher(); }
void RunLoop() {
loop_.Run();
loop_.ResetQuit();
}
void QuitLoop() { loop_.Quit(); }
fit::closure QuitLoopClosure() {
return [this] { loop_.Quit(); };
}
private:
async::Loop loop_;
};
// This first test constructs a realm with a single component and tests that the
// component correctly implements the
// fuchsia.sdk.examples.fidl.GreetingService protocol.
TEST_F(RealmBuilderTest, RoutesFromComponent) {
// Component URL of the component to test.
static constexpr char kGreetingServiceServerUrl[] = "#meta/greeting_service.cm";
// Use Realm::Builder factory method to initiate builder object.
auto realm_builder = component_testing::RealmBuilder::Create();
// Add the Greeting Service component as child of the Realm.
realm_builder.AddChild(kGreetingService, kGreetingServiceServerUrl);
// Route the fuchsia.logger.LogSink protocol from the test to the
// Greeting Service component.
realm_builder.AddRoute(component_testing::Route{
.capabilities = {component_testing::Protocol{fuchsia::logger::LogSink::Name_}},
.source = component_testing::ParentRef(),
.targets = {component_testing::ChildRef{kGreetingService}}});
// Route the fuchsia.sdk.examples.fidl.GreetingService protocol from
// the Greeting Service component to this component.
realm_builder.AddRoute(
component_testing::Route{.capabilities = {component_testing::Protocol{
fuchsia::sdk::examples::fidl::GreetingService::Name_}},
.source = component_testing::ChildRef{kGreetingService},
.targets = {component_testing::ParentRef()}});
// Build the realm once the topology has been determined.
auto realm = realm_builder.Build(dispatcher());
// Since we routed the fuchsia.sdk.examples.fidl.GreetingService
// protocol to this component, we should be able to connect to it through
// the exposed directory of the created Realm.
auto greeting_service = realm.ConnectSync<fuchsia::sdk::examples::fidl::GreetingService>();
auto greeting = fuchsia::sdk::examples::fidl::Greeting::New();
static constexpr char kGreeting[] = "Hello World!";
greeting->text = kGreeting;
fuchsia::sdk::examples::fidl::GreetingPtr reply = nullptr;
ASSERT_EQ(greeting_service->Greet(std::move(greeting), &reply), ZX_OK);
ASSERT_NE(reply, nullptr);
EXPECT_EQ(reply->text, kGreeting);
}
// This test is the same as the one above, except that the implementation
// of fuchsia.sdk.examples.fidl.GreetingService comes from a
// legacy (CMX) component.
TEST_F(RealmBuilderTest, RoutesFromLegacyComponent) {
static constexpr char kGreetingServiceServerLegacyUrl[] =
"fuchsia-pkg://fuchsia.com/greeting_service#meta/greeting_service.cmx";
auto realm_builder = component_testing::RealmBuilder::Create();
realm_builder.AddLegacyChild(kGreetingService, kGreetingServiceServerLegacyUrl);
realm_builder.AddRoute(component_testing::Route{
.capabilities = {component_testing::Protocol{fuchsia::logger::LogSink::Name_}},
.source = component_testing::ParentRef(),
.targets = {component_testing::ChildRef{kGreetingService}}});
realm_builder.AddRoute(
component_testing::Route{.capabilities = {component_testing::Protocol{
fuchsia::sdk::examples::fidl::GreetingService::Name_}},
.source = component_testing::ChildRef{kGreetingService},
.targets = {component_testing::ParentRef()}});
auto realm = realm_builder.Build(dispatcher());
auto greeting_service = realm.ConnectSync<fuchsia::sdk::examples::fidl::GreetingService>();
auto greeting = fuchsia::sdk::examples::fidl::Greeting::New();
static constexpr char kGreeting[] = "Hello World!";
greeting->text = kGreeting;
fuchsia::sdk::examples::fidl::GreetingPtr reply = nullptr;
ASSERT_EQ(greeting_service->Greet(std::move(greeting), &reply), ZX_OK);
ASSERT_NE(reply, nullptr);
EXPECT_EQ(reply->text, kGreeting);
}
// The next test mocks a component in the Realm instead of using a prebuilt one.
// When component_manager issues a Start request for this component, the
// `Start` method will be called.
class MockGreetingServiceServer : public component_testing::LocalComponent,
public fuchsia::sdk::examples::fidl::GreetingService {
public:
MockGreetingServiceServer(async_dispatcher_t* dispatcher)
: dispatcher_(dispatcher), called_(false) {}
void Greet(std::unique_ptr<fuchsia::sdk::examples::fidl::Greeting> value,
GreetCallback callback) override {
called_ = true;
callback(std::move(value));
}
void Start(std::unique_ptr<component_testing::LocalComponentHandles> mock_handles) override {
mock_handles_ = std::move(mock_handles);
ASSERT_EQ(mock_handles_->outgoing()->AddPublicService(bindings_.GetHandler(this, dispatcher_)),
ZX_OK);
}
bool WasCalled() const { return called_; }
private:
async_dispatcher_t* dispatcher_;
fidl::BindingSet<fuchsia::sdk::examples::fidl::GreetingService> bindings_;
bool called_;
std::unique_ptr<component_testing::LocalComponentHandles> mock_handles_;
};
// This test differs from the other two in that instead of using a prebuilt
// component added to this test's package, it mocks an implementation inside
// a local C++ class via the overridden `Start` method.
TEST_F(RealmBuilderTest, RoutesFromMockComponent) {
auto realm_builder = component_testing::RealmBuilder::Create();
MockGreetingServiceServer mock_server(dispatcher());
realm_builder.AddLocalChild(kGreetingService, &mock_server);
realm_builder.AddRoute(
component_testing::Route{.capabilities = {component_testing::Protocol{
fuchsia::sdk::examples::fidl::GreetingService::Name_}},
.source = component_testing::ChildRef{kGreetingService},
.targets = {component_testing::ParentRef()}});
auto realm = realm_builder.Build(dispatcher());
fuchsia::sdk::examples::fidl::GreetingServicePtr greeting_service;
ASSERT_EQ(realm.Connect(greeting_service.NewRequest()), ZX_OK);
auto greeting = fuchsia::sdk::examples::fidl::Greeting::New();
greeting->text = "hello";
greeting_service->Greet(std::move(greeting),
[&](fuchsia::sdk::examples::fidl::GreetingPtr response) {
// Use EXPECT here so the loop can still quit if the test fails
EXPECT_EQ(response->text, "hello");
QuitLoop();
});
// Wait for async callback to complete
RunLoop();
EXPECT_TRUE(mock_server.WasCalled());
}