blob: 6d523e38473ddf2df10876bd08248c9229c0501c [file] [log] [blame]
// Copyright 2020 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 <fcntl.h>
#include <fuchsia/sys/internal/cpp/fidl.h>
#include <fuchsia/sys/internal/cpp/fidl_test_base.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/sys/cpp/testing/fake_component.h>
#include <unistd.h>
#include <gtest/gtest.h>
#include "lib/sys/cpp/testing/component_context_provider.h"
#include "src/lib/files/directory.h"
#include "src/lib/files/scoped_temp_dir.h"
#include "src/lib/fxl/strings/substitute.h"
#include "src/lib/testing/loop_fixture/test_loop_fixture.h"
#include "src/sys/appmgr/component_controller_impl.h"
#include "src/sys/appmgr/component_id_index.h"
#include "src/sys/appmgr/realm.h"
namespace component {
namespace {
// Listener is not discovearble, and needs an explicit name.
const char kListenerName[] = "fuchsia::sys::internal::ComponentEventListener";
const char kEmptyComponentIdIndex[] =
R"({ "appmgr_restrict_isolated_persistent_storage": false, "instances": [] })";
class FakeListener : public fuchsia::sys::internal::testing::ComponentEventListener_TestBase {
public:
FakeListener() { component_.AddPublicService(bindings_.GetHandler(this), kListenerName); }
void NotImplemented_(const std::string& name) override { FAIL() << "not implemented: " << name; }
private:
sys::testing::FakeComponent component_;
fidl::BindingSet<fuchsia::sys::internal::ComponentEventListener> bindings_;
};
class ComponentEventProviderTest : public ::gtest::TestLoopFixture {
protected:
void SetUp() {
TestLoopFixture::SetUp();
fake_listener_service_ = std::make_unique<FakeListener>();
context_provider_ = std::make_unique<sys::testing::ComponentContextProvider>(dispatcher());
}
// Borrowed from realm_unittest.cc, consider deduping if things get overly copied around.
std::unique_ptr<Realm> CreateTestRealm(fbl::unique_fd dirfd) {
files::CreateDirectoryAt(dirfd.get(), "scheme_map");
auto environment_services = sys::ServiceDirectory::CreateFromNamespace();
fuchsia::sys::ServiceListPtr root_realm_services(new fuchsia::sys::ServiceList);
RealmArgs realm_args = RealmArgs::MakeWithAdditionalServices(
nullptr, "test", "/data", "/data/cache", "/tmp", std::move(environment_services),
std::move(root_realm_services), fuchsia::sys::EnvironmentOptions{}, std::move(dirfd),
ComponentIdIndex::CreateFromIndexContents(kEmptyComponentIdIndex).take_value());
return Realm::Create(std::move(realm_args));
}
files::ScopedTempDir tmp_dir_;
std::unique_ptr<fuchsia::sys::internal::ComponentEventListener> fake_listener_service_;
std::unique_ptr<sys::testing::ComponentContextProvider> context_provider_;
};
TEST_F(ComponentEventProviderTest, NotificationAfterShutdownDoesNotCrash) {
std::string dir;
ASSERT_TRUE(tmp_dir_.NewTempDir(&dir));
fbl::unique_fd dirfd(open(dir.c_str(), O_RDONLY));
std::unique_ptr<Realm> realm = CreateTestRealm(std::move(dirfd));
fuchsia::sys::internal::ComponentEventListenerPtr client;
context_provider_->ConnectToPublicService(client.NewRequest(dispatcher()), kListenerName);
{
ComponentEventProviderImpl event_provider(realm->weak_ptr(), dispatcher());
event_provider.SetListener(std::move(client));
// Let event_provider go out of scope on purpose while still having a listener.
}
// Drain events to force the listener callback to fire and try to send notifications to the
// expired event_provider.
RunLoopUntilIdle();
}
} // namespace
} // namespace component