blob: 0cec1557d3be17bfd48170b70cbadcadb0e04a1b [file] [log] [blame]
// Copyright 2018 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 "garnet/bin/appmgr/namespace.h"
#include <map>
#include <string>
#include <utility>
#include <vector>
#include <fbl/ref_ptr.h>
#include <fs/pseudo-dir.h>
#include <fs/synchronous-vfs.h>
#include <fuchsia/sys/cpp/fidl.h>
#include <lib/fdio/util.h>
#include <lib/zx/channel.h>
#include <zircon/status.h>
#include <zircon/errors.h>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "lib/component/cpp/service_provider_impl.h"
#include "lib/component/cpp/testing/test_util.h"
#include "lib/svc/cpp/services.h"
#include "lib/gtest/real_loop_fixture.h"
using fuchsia::sys::ServiceList;
using fuchsia::sys::ServiceListPtr;
using fuchsia::sys::ServiceProvider;
using fuchsia::sys::ServiceProviderPtr;
namespace component {
namespace {
class NamespaceTest : public ::gtest::RealLoopFixture {
protected:
fxl::RefPtr<Namespace> MakeNamespace(
ServiceListPtr additional_services,
fxl::RefPtr<Namespace> parent = nullptr) {
return fxl::MakeRefCounted<Namespace>(
parent, nullptr, std::move(additional_services), nullptr);
}
zx_status_t ConnectToService(zx_handle_t svc_dir, const std::string& name) {
zx::channel h1, h2;
zx_status_t r = zx::channel::create(0, &h1, &h2);
if (r != ZX_OK) return r;
fdio_service_connect_at(svc_dir, name.c_str(), h2.release());
return ZX_OK;
}
};
class NamespaceHostDirectoryTest : public NamespaceTest {
protected:
NamespaceHostDirectoryTest()
: vfs_(dispatcher()),
directory_(fbl::AdoptRef(new fs::PseudoDir())) {}
zx::channel OpenAsDirectory() {
return testing::OpenAsDirectory(&vfs_, directory_);
}
zx_status_t AddService(const std::string& name) {
auto cb = [this, name](zx::channel channel) {
++connection_ctr_[name];
return ZX_OK;
};
return directory_->AddEntry(name, fbl::AdoptRef(new fs::Service(cb)));
}
fs::SynchronousVfs vfs_;
fbl::RefPtr<fs::PseudoDir> directory_;
std::map<std::string, int> connection_ctr_;
};
class NamespaceProviderTest : public NamespaceTest {
protected:
fxl::RefPtr<Namespace> MakeNamespace(ServiceListPtr additional_services) {
return fxl::MakeRefCounted<Namespace>(
nullptr, nullptr, std::move(additional_services), nullptr);
}
void BindServiceProvider(fidl::InterfaceRequest<ServiceProvider> request) {
provider_.AddBinding(std::move(request));
}
zx_status_t AddService(const std::string& name) {
auto cb = [this, name](zx::channel channel) {
++connection_ctr_[name];
};
provider_.AddServiceForName(cb, name);
return ZX_OK;
}
ServiceProviderImpl provider_;
std::map<std::string, int> connection_ctr_;
};
std::pair<std::string, int> StringIntPair(const std::string& s, int i) {
return {s, i};
}
TEST_F(NamespaceHostDirectoryTest, AdditionalServices) {
static constexpr char kService1[] = "fuchsia.test.TestService1";
static constexpr char kService2[] = "fuchsia.test.TestService2";
ServiceListPtr service_list(new ServiceList);
service_list->names.push_back(kService1);
service_list->names.push_back(kService2);
AddService(kService1);
AddService(kService2);
ServiceProviderPtr service_provider;
service_list->host_directory = OpenAsDirectory();
auto ns = MakeNamespace(std::move(service_list));
zx::channel svc_dir = ns->OpenServicesAsDirectory();
EXPECT_EQ(ZX_OK, ConnectToService(svc_dir.get(), kService1));
EXPECT_EQ(ZX_OK, ConnectToService(svc_dir.get(), kService2));
EXPECT_EQ(ZX_OK, ConnectToService(svc_dir.get(), kService2));
// fdio_service_connect_at does not return an error if connection failed.
EXPECT_EQ(ZX_OK, ConnectToService(svc_dir.get(), "fuchsia.test.NotExists"));
RunLoopUntilIdle();
std::vector<std::pair<std::string, int>> connection_ctr_vec;
for (const auto& e : connection_ctr_) {
connection_ctr_vec.push_back(e);
}
EXPECT_THAT(connection_ctr_vec, ::testing::ElementsAre(
StringIntPair(kService1, 1), StringIntPair(kService2, 2)));
}
TEST_F(NamespaceHostDirectoryTest, AdditionalServices_InheritParent) {
static constexpr char kService1[] = "fuchsia.test.TestService1";
static constexpr char kService2[] = "fuchsia.test.TestService2";
ServiceListPtr parent_service_list(new ServiceList);
parent_service_list->names.push_back(kService1);
ServiceListPtr service_list(new ServiceList);
service_list->names.push_back(kService2);
AddService(kService1);
AddService(kService2);
parent_service_list->host_directory = OpenAsDirectory();
service_list->host_directory = OpenAsDirectory();
auto parent_ns = MakeNamespace(std::move(parent_service_list));
auto ns = MakeNamespace(std::move(service_list), parent_ns);
zx::channel svc_dir = ns->OpenServicesAsDirectory();
EXPECT_EQ(ZX_OK, ConnectToService(svc_dir.get(), kService1));
EXPECT_EQ(ZX_OK, ConnectToService(svc_dir.get(), kService2));
// fdio_service_connect_at does not return an error if connection failed.
EXPECT_EQ(ZX_OK, ConnectToService(svc_dir.get(), "fuchsia.test.NotExists"));
RunLoopUntilIdle();
std::vector<std::pair<std::string, int>> connection_ctr_vec;
for (const auto& e : connection_ctr_) {
connection_ctr_vec.push_back(e);
}
EXPECT_THAT(connection_ctr_vec, ::testing::ElementsAre(
StringIntPair(kService1, 1), StringIntPair(kService2, 1)));
}
TEST_F(NamespaceProviderTest, AdditionalServices) {
static constexpr char kService1[] = "fuchsia.test.TestService1";
static constexpr char kService2[] = "fuchsia.test.TestService2";
ServiceListPtr service_list(new ServiceList);
ServiceProviderPtr service_provider;
BindServiceProvider(service_provider.NewRequest());
service_list->names.push_back(kService1);
service_list->names.push_back(kService2);
EXPECT_EQ(ZX_OK, AddService(kService1));
EXPECT_EQ(ZX_OK, AddService(kService2));
service_list->provider = std::move(service_provider);
auto ns = MakeNamespace(std::move(service_list));
zx::channel svc_dir = ns->OpenServicesAsDirectory();
EXPECT_EQ(ZX_OK, ConnectToService(svc_dir.get(), kService1));
EXPECT_EQ(ZX_OK, ConnectToService(svc_dir.get(), kService2));
EXPECT_EQ(ZX_OK, ConnectToService(svc_dir.get(), kService2));
// fdio_service_connect_at does not return an error if connection failed.
EXPECT_EQ(ZX_OK, ConnectToService(svc_dir.get(), "fuchsia.test.NotExists"));
RunLoopUntilIdle();
std::vector<std::pair<std::string, int>> connection_ctr_vec;
for (const auto& e : connection_ctr_) {
connection_ctr_vec.push_back(e);
}
EXPECT_THAT(connection_ctr_vec, ::testing::ElementsAre(
StringIntPair(kService1, 1), StringIntPair(kService2, 2)));
}
} // namespace
} // namespace component