blob: c3ee5c3bf83027071b01f61d91e18e0c180aac33 [file] [log] [blame] [edit]
// Copyright 2021 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/component/cpp/fidl.h>
#include <fuchsia/component/sandbox/cpp/fidl.h>
#include <fuchsia/element/cpp/fidl.h>
#include <fuchsia/element/test/cpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/fdio/directory.h>
#include <lib/fdio/namespace.h>
#include <lib/sys/cpp/component_context.h>
#include <atomic>
#include <string>
#include <zxtest/zxtest.h>
namespace {
// TODO(https://fxbug.dev/330610053): Once a C++ realm_proxy library is available, use
// that instead of this custom boilerplate
class InstalledNamespace {
public:
InstalledNamespace(std::string prefix, zx::channel realm_factory)
: prefix_(std::move(prefix)), realm_factory_(std::move(realm_factory)) {}
~InstalledNamespace() {
fdio_ns_t* ns;
EXPECT_EQ(fdio_ns_get_installed(&ns), ZX_OK);
EXPECT_EQ(fdio_ns_unbind(ns, prefix_.c_str()), ZX_OK);
}
InstalledNamespace(InstalledNamespace&&) = default;
InstalledNamespace& operator=(InstalledNamespace&&) = default;
zx_status_t Connect(const std::string& interface_name, zx::channel request) const {
const std::string path = prefix_ + "/" + interface_name;
return fdio_service_connect(path.c_str(), request.release());
}
static InstalledNamespace Create(sys::ComponentContext* context);
const std::string& prefix() const { return prefix_; }
private:
std::string prefix_;
/// This is not used, but it keeps the RealmFactory connection alive.
///
/// The RealmFactory server may use this connection to pin the lifetime of the realm created
/// for the test.
zx::channel realm_factory_;
};
std::atomic_int32_t namespace_ctr{1};
template <typename Interface>
InstalledNamespace ExtendNamespace(
sys::ComponentContext* context, fidl::InterfaceHandle<Interface> realm_factory,
fidl::InterfaceHandle<::fuchsia::component::sandbox::Dictionary> dictionary) {
std::string prefix = std::string("/dict-") + std::to_string(namespace_ctr++);
fuchsia::component::NamespaceSyncPtr namespace_proxy;
EXPECT_OK(context->svc()->Connect(namespace_proxy.NewRequest()));
std::vector<fuchsia::component::NamespaceInputEntry> entries;
entries.emplace_back(fuchsia::component::NamespaceInputEntry{
.path = prefix,
.dictionary = std::move(dictionary),
});
fuchsia::component::Namespace_Create_Result result;
EXPECT_OK(namespace_proxy->Create(std::move(entries), &result));
EXPECT_TRUE(!result.is_err());
std::vector<fuchsia::component::NamespaceEntry> namespace_entries =
std::move(result.response().entries);
EXPECT_EQ(namespace_entries.size(), 1);
auto& entry = namespace_entries[0];
EXPECT_TRUE(entry.has_path() && entry.has_directory());
EXPECT_EQ(entry.path(), prefix);
fdio_ns_t* ns;
EXPECT_OK(fdio_ns_get_installed(&ns));
zx_handle_t dir_handle = entry.mutable_directory()->TakeChannel().release();
EXPECT_OK(fdio_ns_bind(ns, prefix.c_str(), dir_handle));
return InstalledNamespace(std::move(prefix), realm_factory.TakeChannel());
}
constexpr auto kReferenceElementV2Url = "#meta/reference-element.cm";
class ElementManagerTest : public zxtest::Test {};
// Tests that proposing an element returns a successful result.
TEST_F(ElementManagerTest, ProposeElement) {
async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);
auto context = sys::ComponentContext::Create();
fuchsia::element::test::RealmFactorySyncPtr realm_factory;
EXPECT_EQ(ZX_OK, context->svc()->Connect(realm_factory.NewRequest()));
fuchsia::component::sandbox::DictionarySyncPtr dictionary;
fuchsia::element::test::RealmFactory_CreateRealm2_Result result;
fuchsia::element::test::RealmOptions options;
EXPECT_OK(realm_factory->CreateRealm2(std::move(options), dictionary.NewRequest(), &result));
auto test_ns = ExtendNamespace(context.get(), realm_factory.Unbind(), dictionary.Unbind());
// TODO(kjharland): Create a helper macro to shorten this to one line.
zx::channel local;
zx::channel remote;
ASSERT_OK(zx::channel::create(0, &local, &remote));
EXPECT_OK(test_ns.Connect("fuchsia.element.Manager", std::move(remote)));
fuchsia::element::ManagerPtr manager;
manager.Bind(std::move(local));
manager.set_error_handler([&](zx_status_t status) {
EXPECT_EQ(ZX_OK, status);
loop.Quit();
});
fuchsia::element::Spec spec;
spec.set_component_url(kReferenceElementV2Url);
bool is_proposed{false};
manager->ProposeElement(std::move(spec), /*controller=*/nullptr,
[&](fuchsia::element::Manager_ProposeElement_Result result) {
EXPECT_FALSE(result.is_err());
is_proposed = true;
loop.Quit();
});
loop.Run();
EXPECT_TRUE(is_proposed);
}
} // namespace