blob: 585e0d6a2bf75d718a79cdef3c051cc77ed80d26 [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/fuchsia.component.decl/cpp/fidl.h>
#include <fidl/fuchsia.component.decl/cpp/hlcpp_conversion.h>
#include <fidl/fuchsia.component/cpp/fidl.h>
#include <fidl/fuchsia.diagnostics/cpp/fidl.h>
#include <fuchsia/diagnostics/cpp/fidl.h>
#include <lib/component/incoming/cpp/protocol.h>
#include <lib/diagnostics/reader/cpp/archive_reader.h>
#include <lib/sys/component/cpp/testing/realm_builder.h>
#include <lib/syslog/cpp/macros.h>
#include <string>
#include <rapidjson/document.h>
#include <rapidjson/pointer.h>
#include "src/lib/testing/loop_fixture/real_loop_fixture.h"
using diagnostics::reader::InspectData;
constexpr char kChildUrl[] = "#meta/config_example.cm";
constexpr char kCollectionName[] = "realm_api_collection";
class IntegrationTest : public gtest::RealLoopFixture {
protected:
InspectData GetInspect(const std::string& child_name) {
std::string child_moniker = std::string(kCollectionName) + ":" + child_name;
return GetInspect(child_name, child_moniker);
}
InspectData GetInspect(const std::string& child_name, const std::string& child_moniker) {
std::string selector =
diagnostics::reader::SanitizeMonikerForSelectors(child_moniker) + ":root";
diagnostics::reader::ArchiveReader reader(dispatcher(), {selector});
fpromise::result<std::vector<InspectData>, std::string> result;
async::Executor executor(dispatcher());
executor.schedule_task(
reader.SnapshotInspectUntilPresent({child_moniker})
.then([&](fpromise::result<std::vector<InspectData>, std::string>& rest) {
result = std::move(rest);
}));
RunLoopUntil([&] { return result.is_ok() || result.is_error(); });
EXPECT_EQ(result.is_error(), false) << "Error was " << result.error();
EXPECT_EQ(result.value().size(), 1ul) << "Expected only one component";
return std::move(result.value()[0]);
}
};
TEST_F(IntegrationTest, ParentValuesObserved) {
std::string child_name = "dynamic_child_realm_api_parent_values";
std::string expected_greeting = "parent component";
zx::result client_end = component::Connect<fuchsia_component::Realm>();
ZX_ASSERT(client_end.is_ok());
fidl::Client realm(std::move(*client_end), dispatcher());
ZX_ASSERT(realm.is_valid());
// [START create_child]
fuchsia_component_decl::Child child_decl;
child_decl.name(child_name);
child_decl.url(kChildUrl);
child_decl.startup(fuchsia_component_decl::StartupMode::kLazy);
fuchsia_component_decl::ConfigOverride greeting_override;
greeting_override.key("greeting");
greeting_override.value(fuchsia_component_decl::ConfigValue::WithSingle(
fuchsia_component_decl::ConfigSingleValue::WithString(expected_greeting)));
child_decl.config_overrides({{greeting_override}});
fuchsia_component_decl::CollectionRef collection;
collection.name(kCollectionName);
fuchsia_component::CreateChildArgs child_args;
realm->CreateChild({collection, child_decl, std::move(child_args)})
.ThenExactlyOnce([this](fidl::Result<fuchsia_component::Realm::CreateChild>& result) {
if (!result.is_ok()) {
FX_LOGS(ERROR) << "CreateChild failed: " << result.error_value();
ZX_PANIC("%s", result.error_value().FormatDescription().c_str());
}
QuitLoop();
});
RunLoop();
// [END create_child]
fuchsia_component_decl::ChildRef child_ref;
child_ref.collection(kCollectionName);
child_ref.name(child_name);
auto exposed_endpoints = fidl::Endpoints<fuchsia_io::Directory>::Create();
auto exposed_client = std::move(exposed_endpoints.client);
realm->OpenExposedDir({child_ref, std::move(exposed_endpoints.server)})
.ThenExactlyOnce([this](fidl::Result<fuchsia_component::Realm::OpenExposedDir>& result) {
if (!result.is_ok()) {
FX_LOGS(ERROR) << "OpenExposedDir failed: " << result.error_value();
ZX_PANIC("%s", result.error_value().FormatDescription().c_str());
}
QuitLoop();
});
RunLoop();
zx::result bind_result = component::ConnectAt<fuchsia_component::Binder>(exposed_client);
if (!bind_result.is_ok()) {
FX_LOGS(ERROR) << "Opening fuchsia.component.Binder failed: " << bind_result.error_value();
}
auto data = GetInspect(child_name);
EXPECT_EQ(expected_greeting, data.GetByPath({"root", "config", "greeting"}).GetString());
}
// This test ensures that the test above is passing because parent overrides work rather than
// lucking into the same value as the packaged config.
TEST_F(IntegrationTest, DefaultValuesObserved) {
std::string child_name = "dynamic_child_realm_api_default_values";
std::string expected_greeting = "World!";
zx::result client_end = component::Connect<fuchsia_component::Realm>();
ZX_ASSERT(client_end.is_ok());
fidl::Client realm(std::move(*client_end), dispatcher());
ZX_ASSERT(realm.is_valid());
fuchsia_component_decl::Child child_decl;
child_decl.name(child_name);
child_decl.url(kChildUrl);
child_decl.startup(fuchsia_component_decl::StartupMode::kLazy);
fuchsia_component_decl::CollectionRef collection;
collection.name(kCollectionName);
fuchsia_component::CreateChildArgs child_args;
realm->CreateChild({collection, child_decl, std::move(child_args)})
.ThenExactlyOnce([this](fidl::Result<fuchsia_component::Realm::CreateChild>& result) {
if (!result.is_ok()) {
FX_LOGS(ERROR) << "CreateChild failed: " << result.error_value();
ZX_PANIC("%s", result.error_value().FormatDescription().c_str());
}
QuitLoop();
});
RunLoop();
fuchsia_component_decl::ChildRef child_ref;
child_ref.collection(kCollectionName);
child_ref.name(child_name);
auto exposed_endpoints = fidl::Endpoints<fuchsia_io::Directory>::Create();
auto exposed_client = std::move(exposed_endpoints.client);
realm->OpenExposedDir({child_ref, std::move(exposed_endpoints.server)})
.ThenExactlyOnce([this](fidl::Result<fuchsia_component::Realm::OpenExposedDir>& result) {
if (!result.is_ok()) {
FX_LOGS(ERROR) << "OpenExposedDir failed: " << result.error_value();
ZX_PANIC("%s", result.error_value().FormatDescription().c_str());
}
QuitLoop();
});
RunLoop();
zx::result bind_result = component::ConnectAt<fuchsia_component::Binder>(exposed_client);
if (!bind_result.is_ok()) {
FX_LOGS(ERROR) << "Opening fuchsia.component.Binder failed: " << bind_result.error_value();
}
auto data = GetInspect(child_name);
EXPECT_EQ(expected_greeting, data.GetByPath({"root", "config", "greeting"}).GetString());
}
TEST_F(IntegrationTest, ConfigCppRealmBuilderParentOverride) {
std::vector<fuchsia_component_decl::ConfigOverride> child_overrides;
child_overrides.emplace_back();
child_overrides.back().key("greeting");
child_overrides.back().value(fuchsia_component_decl::ConfigValue::WithSingle(
fuchsia_component_decl::ConfigSingleValue::WithString("parent component")));
auto realm_builder = component_testing::RealmBuilder::Create();
auto options =
component_testing::ChildOptions{.startup_mode = component_testing::StartupMode::EAGER,
.config_overrides = fidl::NaturalToHLCPP(child_overrides)};
auto child_name = "config_example_override_mutable_by_parent";
realm_builder.AddChild(child_name, kChildUrl, options);
realm_builder.AddRoute(component_testing::Route{
.capabilities = {component_testing::Protocol{"fuchsia.logger.LogSink"}},
.source = component_testing::ParentRef(),
.targets = {component_testing::ChildRef{child_name}}});
auto realm = realm_builder.Build();
auto moniker = "realm_builder:" + realm.component().GetChildName() + "/" + child_name;
auto data = GetInspect(child_name, moniker);
EXPECT_EQ(rapidjson::Value("parent component"), data.GetByPath({"root", "config", "greeting"}));
}
// TODO(https://fxbug.dev/42053123): Include a subpackaged child component in the examples
// and tests, including the Rust version of this suite.