chromium/fuchsia_web/common/test/test_realm_support.cc

// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "fuchsia_web/common/test/test_realm_support.h"

#include <lib/sys/component/cpp/testing/realm_builder.h>

#include <string>
#include <string_view>
#include <utility>

#include "base/check.h"
#include "base/command_line.h"
#include "base/ranges/algorithm.h"

using ::component_testing::ChildRef;
using ::component_testing::Directory;
using ::component_testing::ParentRef;
using ::component_testing::Protocol;
using ::component_testing::RealmBuilder;
using ::component_testing::Route;

namespace test {

namespace {

void AppendCommandLineArgumentsToProgram(
    fuchsia::component::decl::Program& program,
    const base::CommandLine& command_line) {
  // Find the "args" list in the program declaration.
  fuchsia::data::DictionaryEntry* args_entry = nullptr;
  auto* entries = program.mutable_info()->mutable_entries();
  for (auto& entry : *entries) {
    if (entry.key == "args") {
      DCHECK(entry.value->is_str_vec());
      args_entry = &entry;
      break;
    }
  }
  if (!args_entry) {
    // Create a new "args" list and insert it at the proper location in the
    // program's entries; entries' keys must be sorted as per
    // https://fuchsia.dev/reference/fidl/fuchsia.data?hl=en#Dictionary.
    auto lower_bound = base::ranges::lower_bound(
        *entries, "args", /*comp=*/{},
        [](const fuchsia::data::DictionaryEntry& entry) { return entry.key; });
    auto it = entries->emplace(lower_bound);
    it->key = "args";
    it->value = fuchsia::data::DictionaryValue::New();
    it->value->set_str_vec({});
    args_entry = &*it;
  }

  // Append all args following the program name in `command_line` to the
  // program's args.
  args_entry->value->str_vec().insert(args_entry->value->str_vec().end(),
                                      command_line.argv().begin() + 1,
                                      command_line.argv().end());
}

}  // namespace

void AppendCommandLineArguments(RealmBuilder& realm_builder,
                                std::string_view child_name,
                                const base::CommandLine& command_line) {
  const std::string child_name_str(child_name);
  auto child_component_decl = realm_builder.GetComponentDecl(child_name_str);
  AppendCommandLineArgumentsToProgram(*child_component_decl.mutable_program(),
                                      command_line);
  realm_builder.ReplaceComponentDecl(child_name_str,
                                     std::move(child_component_decl));
}

void AppendCommandLineArgumentsForRealm(
    ::component_testing::RealmBuilder& realm_builder,
    const base::CommandLine& command_line) {
  auto decl = realm_builder.GetRealmDecl();
  AppendCommandLineArgumentsToProgram(*decl.mutable_program(), command_line);
  realm_builder.ReplaceRealmDecl(std::move(decl));
}

void AddRouteFromParent(RealmBuilder& realm_builder,
                        std::string_view child_name,
                        std::string_view protocol_name) {
  ChildRef child_ref{std::string_view(child_name.data(), child_name.size())};
  realm_builder.AddRoute(Route{.capabilities = {Protocol{protocol_name}},
                               .source = ParentRef{},
                               .targets = {std::move(child_ref)}});
}

void AddSyslogRoutesFromParent(RealmBuilder& realm_builder,
                               std::string_view child_name) {
  AddRouteFromParent(realm_builder, child_name, "fuchsia.logger.LogSink");
}

void AddVulkanRoutesFromParent(RealmBuilder& realm_builder,
                               std::string_view child_name) {
  ChildRef child_ref{std::string_view(child_name.data(), child_name.size())};
  realm_builder.AddRoute(
      Route{.capabilities = {Protocol{"fuchsia.sysmem.Allocator"},
                             Protocol{"fuchsia.sysmem2.Allocator"},
                             Protocol{"fuchsia.tracing.provider.Registry"},
                             Protocol{"fuchsia.vulkan.loader.Loader"}},
            .source = ParentRef{},
            .targets = {std::move(child_ref)}});
}

void AddFontService(RealmBuilder& realm_builder, std::string_view child_name) {
  static constexpr char kFontsService[] = "isolated_fonts";
  static constexpr char kFontsUrl[] =
      "fuchsia-pkg://fuchsia.com/fonts_hermetic_for_test"
      "#meta/font_provider_hermetic_for_test.cm";
  realm_builder.AddChild(kFontsService, kFontsUrl);
  AddSyslogRoutesFromParent(realm_builder, kFontsService);
  ChildRef child_ref{std::string_view(child_name.data(), child_name.size())};
  realm_builder
      .AddRoute(Route{.capabilities =
                          {
                              Protocol{"fuchsia.tracing.provider.Registry"},
                          },
                      .source = ParentRef{},
                      .targets = {ChildRef{kFontsService}}})
      .AddRoute(Route{.capabilities = {Protocol{"fuchsia.fonts.Provider"}},
                      .source = ChildRef{kFontsService},
                      .targets = {std::move(child_ref)}});
}

void AddTestUiStack(RealmBuilder& realm_builder, std::string_view child_name) {
  static constexpr char kTestUiStackService[] = "test_ui_stack";
  static constexpr char kTestUiStackUrl[] =
      "fuchsia-pkg://fuchsia.com/flatland-scene-manager-test-ui-stack#meta/"
      "test-ui-stack.cm";
  realm_builder.AddChild(kTestUiStackService, kTestUiStackUrl);
  AddRouteFromParent(realm_builder, kTestUiStackService,
                     "fuchsia.scheduler.ProfileProvider");
  AddSyslogRoutesFromParent(realm_builder, kTestUiStackService);
  AddVulkanRoutesFromParent(realm_builder, kTestUiStackService);

  ChildRef child_ref{std::string_view(child_name.data(), child_name.size())};
  realm_builder
      .AddRoute(
          Route{.capabilities = {Protocol{"fuchsia.scheduler.ProfileProvider"}},
                .source = ParentRef(),
                .targets = {ChildRef{kTestUiStackService}}})
      .AddRoute(Route{.capabilities =
                          {
                              Protocol{"fuchsia.ui.composition.Allocator"},
                              Protocol{"fuchsia.ui.composition.Flatland"},
                          },
                      .source = ChildRef{kTestUiStackService},
                      .targets = {std::move(child_ref)}});
}

}  // namespace test