blob: 0ca148573da876a576eb088d7c828ee86512e804 [file] [log] [blame]
// 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/decl/cpp/fidl.h>
#include <fuchsia/io/cpp/fidl.h>
#include <lib/async/default.h>
#include <lib/fdio/directory.h>
#include <lib/fdio/io.h>
#include <lib/fit/defer.h>
#include <lib/sys/component/cpp/testing/internal/errors.h>
#include <lib/sys/component/cpp/testing/internal/realm.h>
#include <lib/sys/component/cpp/testing/scoped_child.h>
#include <lib/sys/cpp/component_context.h>
#include <lib/sys/cpp/service_directory.h>
#include <zircon/assert.h>
#include <zircon/status.h>
#include <zircon/types.h>
#include <memory>
#include <optional>
#include <random>
namespace component_testing {
namespace {
std::size_t random_unsigned() {
std::random_device random_device;
std::mt19937 generator(random_device());
std::uniform_int_distribution<std::size_t> distribution;
return distribution(generator);
}
} // namespace
ScopedChild ScopedChild::New(fuchsia::component::RealmSyncPtr realm_proxy, std::string collection,
std::string url) {
std::string name = "auto-" + std::to_string(random_unsigned());
return New(std::move(realm_proxy), std::move(collection), std::move(name), std::move(url));
}
ScopedChild ScopedChild::New(fuchsia::component::RealmSyncPtr realm_proxy, std::string collection,
std::string name, std::string url) {
#if __Fuchsia_API_level__ >= 14
fuchsia::component::ControllerSyncPtr controller_proxy;
internal::CreateChild(realm_proxy.get(), controller_proxy.NewRequest(), collection, name,
std::move(url));
#else
internal::CreateChild(realm_proxy.get(), collection, name, std::move(url));
#endif
fuchsia::component::decl::ChildRef child_ref{.name = std::move(name),
.collection = std::move(collection)};
fuchsia::io::DirectorySyncPtr exposed_dir;
exposed_dir.Bind(internal::OpenExposedDir(realm_proxy.get(), child_ref));
return ScopedChild(sys::ServiceDirectory::CreateFromNamespace(), std::move(child_ref),
std::move(exposed_dir)
#if __Fuchsia_API_level__ >= 14
,
std::move(controller_proxy)
#endif
);
}
ScopedChild ScopedChild::New(std::string collection, std::string name, std::string url,
std::shared_ptr<sys::ServiceDirectory> svc) {
fuchsia::component::RealmSyncPtr realm_proxy;
svc->Connect(realm_proxy.NewRequest());
#if __Fuchsia_API_level__ >= 14
fuchsia::component::ControllerSyncPtr controller_proxy;
internal::CreateChild(realm_proxy.get(), controller_proxy.NewRequest(), collection, name,
std::move(url));
#else
internal::CreateChild(realm_proxy.get(), collection, name, std::move(url));
#endif
fuchsia::component::decl::ChildRef child_ref{.name = std::move(name),
.collection = std::move(collection)};
fuchsia::io::DirectorySyncPtr exposed_dir;
exposed_dir.Bind(internal::OpenExposedDir(realm_proxy.get(), child_ref));
return ScopedChild(std::move(svc), std::move(child_ref), std::move(exposed_dir)
#if __Fuchsia_API_level__ >= 14
,
std::move(controller_proxy)
#endif
);
}
ScopedChild ScopedChild::New(std::string collection, std::string url,
std::shared_ptr<sys::ServiceDirectory> svc) {
std::string name = "auto-" + std::to_string(random_unsigned());
return New(std::move(collection), std::move(name), std::move(url), std::move(svc));
}
ScopedChild::ScopedChild(std::shared_ptr<sys::ServiceDirectory> svc,
fuchsia::component::decl::ChildRef child_ref,
fuchsia::io::DirectorySyncPtr exposed_dir
#if __Fuchsia_API_level__ >= 14
,
fuchsia::component::ControllerSyncPtr controller_proxy
#endif
)
: svc_(std::move(svc)),
child_ref_(std::move(child_ref)),
exposed_dir_(std::move(exposed_dir))
#if __Fuchsia_API_level__ >= 14
,
controller_proxy_(std::move(controller_proxy))
#endif
{
}
ScopedChild::~ScopedChild() {
if (svc_ == nullptr) {
return;
}
fuchsia::component::RealmSyncPtr sync_realm_proxy;
ZX_COMPONENT_ASSERT_STATUS_OK("sys::ServiceDirectory/Connect",
svc_->Connect(sync_realm_proxy.NewRequest()));
internal::DestroyChild(sync_realm_proxy.get(), child_ref_);
}
void ScopedChild::Teardown(async_dispatcher_t* dispatcher, TeardownCallback callback) {
ZX_ASSERT(callback != nullptr);
if (svc_ == nullptr) {
callback(fit::ok());
return;
}
fuchsia::component::RealmPtr async_realm_proxy;
async_realm_proxy.set_error_handler(
[](zx_status_t status) { ZX_PANIC("%s", zx_status_get_string(status)); });
ZX_COMPONENT_ASSERT_STATUS_OK("sys::ServiceDirectory/Connect",
svc_->Connect(async_realm_proxy.NewRequest(dispatcher)));
internal::DestroyChild(
async_realm_proxy.get(), child_ref_,
[callback = std::move(callback),
// Stash a panic-on-drop callback into the closure; this asserts that callers do not shut
// down the dispatcher before the async call completes.
forbid_drop = fit::deferred_callback(
[]() { ZX_PANIC("async callback dropped without being called"); }),
// We have to move the proxy into the callback so that the channel
// connection doesn't prematurely close.
_proxy = std::move(async_realm_proxy)](
fuchsia::component::Realm_DestroyChild_Result result) mutable {
forbid_drop.cancel();
switch (result.Which()) {
case fuchsia::component::Realm_DestroyChild_Result::kResponse:
callback(fit::ok());
break;
case fuchsia::component::Realm_DestroyChild_Result::kErr:
callback(fit::error(result.err()));
break;
case fuchsia::component::Realm_DestroyChild_Result::Invalid:
ZX_PANIC("Realm/DestroyChild returned invalid response");
}
});
// Prevent the destructor from calling DestroyChild again.
svc_ = nullptr;
}
zx_status_t ScopedChild::Connect(const std::string& interface_name, zx::channel request) const {
return fdio_service_connect_at(exposed_dir_.unowned_channel()->get(), interface_name.c_str(),
request.release());
}
std::string ScopedChild::GetChildName() const { return child_ref_.name; }
const fuchsia::io::DirectorySyncPtr& ScopedChild::exposed() const { return exposed_dir_; }
#if __Fuchsia_API_level__ >= 14
ExecutionController ScopedChild::Start(fuchsia::component::StartChildArgs start_args) const {
fuchsia::component::Controller_Start_Result result;
fuchsia::component::ExecutionControllerPtr execution_controller_proxy;
ZX_COMPONENT_ASSERT_STATUS_AND_RESULT_OK(
"Controller/Start",
controller_proxy_->Start(std::move(start_args), execution_controller_proxy.NewRequest(),
&result),
result);
return ExecutionController(std::move(execution_controller_proxy));
}
#endif
} // namespace component_testing