| // 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), false, |
| 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 |