| // Copyright 2017 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 "peridot/bin/sessionmgr/entity_provider_runner/entity_provider_runner.h" |
| |
| #include <memory> |
| |
| #include <fs/service.h> |
| #include <fs/synchronous-vfs.h> |
| #include <fuchsia/modular/cpp/fidl.h> |
| #include <fuchsia/sys/cpp/fidl.h> |
| #include <lib/agent/cpp/agent_impl.h> |
| #include <lib/component/cpp/connect.h> |
| #include <lib/component/cpp/service_provider_impl.h> |
| #include <lib/component/cpp/testing/fake_launcher.h> |
| #include <lib/fidl/cpp/binding.h> |
| #include <lib/fsl/vmo/strings.h> |
| #include <src/lib/fxl/macros.h> |
| #include "src/lib/files/scoped_temp_dir.h" |
| |
| #include "gtest/gtest.h" |
| #include "peridot/bin/sessionmgr/agent_runner/agent_runner.h" |
| #include "peridot/bin/sessionmgr/entity_provider_runner/entity_provider_launcher.h" |
| #include "peridot/bin/sessionmgr/message_queue/message_queue_manager.h" |
| #include "peridot/lib/fidl/array_to_string.h" |
| #include "peridot/lib/ledger_client/page_id.h" |
| #include "peridot/lib/testing/fake_agent_runner_storage.h" |
| #include "peridot/lib/testing/mock_base.h" |
| #include "peridot/lib/testing/test_with_ledger.h" |
| |
| namespace modular { |
| namespace testing { |
| namespace { |
| |
| using ::component::testing::FakeLauncher; |
| |
| class EntityProviderRunnerTest : public TestWithLedger, EntityProviderLauncher { |
| public: |
| EntityProviderRunnerTest() = default; |
| |
| void SetUp() override { |
| TestWithLedger::SetUp(); |
| |
| mqm_ = std::make_unique<MessageQueueManager>( |
| ledger_client(), MakePageId("0123456789123456"), mq_data_dir_.path()); |
| entity_provider_runner_ = std::make_unique<EntityProviderRunner>( |
| static_cast<EntityProviderLauncher*>(this)); |
| // The |fuchsia::modular::UserIntelligenceProvider| below must be nullptr in |
| // order for agent creation to be synchronous, which these tests assume. |
| agent_runner_ = std::make_unique<AgentRunner>( |
| &launcher_, mqm_.get(), ledger_repository(), &agent_runner_storage_, |
| token_manager_.get(), nullptr, entity_provider_runner_.get()); |
| } |
| |
| void TearDown() override { |
| agent_runner_.reset(); |
| entity_provider_runner_.reset(); |
| mqm_.reset(); |
| |
| TestWithLedger::TearDown(); |
| } |
| |
| MessageQueueManager* message_queue_manager() { return mqm_.get(); } |
| |
| protected: |
| AgentRunner* agent_runner() { return agent_runner_.get(); } |
| FakeLauncher* launcher() { return &launcher_; } |
| EntityProviderRunner* entity_provider_runner() { |
| return entity_provider_runner_.get(); |
| } |
| |
| private: |
| // TODO(vardhan): A test probably shouldn't be implementing this.. |
| // |EntityProviderLauncher| |
| void ConnectToEntityProvider( |
| const std::string& agent_url, |
| fidl::InterfaceRequest<fuchsia::modular::EntityProvider> |
| entity_provider_request, |
| fidl::InterfaceRequest<fuchsia::modular::AgentController> |
| agent_controller_request) override { |
| agent_runner_->ConnectToEntityProvider(agent_url, |
| std::move(entity_provider_request), |
| std::move(agent_controller_request)); |
| } |
| |
| void ConnectToStoryEntityProvider( |
| const std::string& story_id, |
| fidl::InterfaceRequest<fuchsia::modular::EntityProvider> |
| entity_provider_request) override { |
| FXL_NOTIMPLEMENTED(); |
| } |
| |
| FakeLauncher launcher_; |
| |
| files::ScopedTempDir mq_data_dir_; |
| std::unique_ptr<MessageQueueManager> mqm_; |
| FakeAgentRunnerStorage agent_runner_storage_; |
| std::unique_ptr<EntityProviderRunner> entity_provider_runner_; |
| std::unique_ptr<AgentRunner> agent_runner_; |
| |
| fuchsia::auth::TokenManagerPtr token_manager_; |
| |
| FXL_DISALLOW_COPY_AND_ASSIGN(EntityProviderRunnerTest); |
| }; |
| |
| class MyEntityProvider : AgentImpl::Delegate, |
| fuchsia::modular::EntityProvider, |
| public fuchsia::sys::ComponentController, |
| public testing::MockBase { |
| public: |
| MyEntityProvider( |
| fuchsia::sys::LaunchInfo launch_info, |
| fidl::InterfaceRequest<fuchsia::sys::ComponentController> ctrl) |
| : vfs_(async_get_default_dispatcher()), |
| outgoing_directory_(fbl::AdoptRef(new fs::PseudoDir())), |
| controller_(this, std::move(ctrl)), |
| entity_provider_binding_(this), |
| launch_info_(std::move(launch_info)) { |
| outgoing_directory_->AddEntry( |
| fuchsia::modular::EntityProvider::Name_, |
| fbl::AdoptRef(new fs::Service([this](zx::channel channel) { |
| entity_provider_binding_.Bind(std::move(channel)); |
| return ZX_OK; |
| }))); |
| vfs_.ServeDirectory(outgoing_directory_, |
| std::move(launch_info_.directory_request)); |
| agent_impl_ = std::make_unique<AgentImpl>( |
| outgoing_directory_, static_cast<AgentImpl::Delegate*>(this)); |
| |
| // Get |agent_context_| and |entity_resolver_| from incoming namespace. |
| FXL_CHECK(launch_info_.additional_services); |
| FXL_CHECK(launch_info_.additional_services->provider.is_valid()); |
| auto additional_services = |
| launch_info_.additional_services->provider.Bind(); |
| component::ConnectToService(additional_services.get(), |
| agent_context_.NewRequest()); |
| fuchsia::modular::ComponentContextPtr component_context; |
| agent_context_->GetComponentContext(component_context.NewRequest()); |
| component_context->GetEntityResolver(entity_resolver_.NewRequest()); |
| } |
| |
| size_t GetCallCount(const std::string func) { return counts.count(func); } |
| fuchsia::modular::EntityResolver* entity_resolver() { |
| return entity_resolver_.get(); |
| } |
| fuchsia::modular::AgentContext* agent_context() { |
| return agent_context_.get(); |
| } |
| |
| private: |
| // |ComponentController| |
| void Kill() override { ++counts["Kill"]; } |
| // |ComponentController| |
| void Detach() override { ++counts["Detach"]; } |
| |
| // |AgentImpl::Delegate| |
| void Connect(fidl::InterfaceRequest<fuchsia::sys::ServiceProvider> |
| outgoing_services) override { |
| ++counts["Connect"]; |
| } |
| // |AgentImpl::Delegate| |
| void RunTask(const fidl::StringPtr& task_id, |
| fit::function<void()> done) override { |
| ++counts["RunTask"]; |
| done(); |
| } |
| |
| // |fuchsia::modular::EntityProvider| |
| void GetTypes(std::string cookie, GetTypesCallback callback) override { |
| std::vector<std::string> types; |
| types.push_back("MyType"); |
| callback(std::move(types)); |
| } |
| |
| // |fuchsia::modular::EntityProvider| |
| void GetData(std::string cookie, std::string type, |
| GetDataCallback callback) override { |
| fsl::SizedVmo vmo; |
| FXL_CHECK(fsl::VmoFromString(type + ":MyData", &vmo)); |
| auto vmo_ptr = |
| std::make_unique<fuchsia::mem::Buffer>(std::move(vmo).ToTransport()); |
| |
| callback(std::move(vmo_ptr)); |
| } |
| |
| // |fuchsia::modular::EntityProvider| |
| void WriteData(std::string cookie, std::string type, |
| fuchsia::mem::Buffer data, |
| WriteDataCallback callback) override { |
| // TODO(MI4-1301) |
| callback(fuchsia::modular::EntityWriteStatus::READ_ONLY); |
| } |
| |
| // |fuchsia::modular::EntityProvider| |
| void Watch( |
| std::string cookie, std::string type, |
| fidl::InterfaceHandle<fuchsia::modular::EntityWatcher> watcher) override { |
| // TODO(MI4-1301) |
| FXL_NOTIMPLEMENTED(); |
| } |
| |
| private: |
| fs::SynchronousVfs vfs_; |
| fbl::RefPtr<fs::PseudoDir> outgoing_directory_; |
| fuchsia::modular::AgentContextPtr agent_context_; |
| std::unique_ptr<AgentImpl> agent_impl_; |
| fuchsia::modular::EntityResolverPtr entity_resolver_; |
| fidl::Binding<fuchsia::sys::ComponentController> controller_; |
| fidl::Binding<fuchsia::modular::EntityProvider> entity_provider_binding_; |
| fuchsia::sys::LaunchInfo launch_info_; |
| |
| FXL_DISALLOW_COPY_AND_ASSIGN(MyEntityProvider); |
| }; |
| |
| TEST_F(EntityProviderRunnerTest, Basic) { |
| std::unique_ptr<MyEntityProvider> dummy_agent; |
| constexpr char kMyAgentUrl[] = "file:///my_agent"; |
| launcher()->RegisterComponent( |
| kMyAgentUrl, |
| [&dummy_agent]( |
| fuchsia::sys::LaunchInfo launch_info, |
| fidl::InterfaceRequest<fuchsia::sys::ComponentController> ctrl) { |
| dummy_agent = std::make_unique<MyEntityProvider>(std::move(launch_info), |
| std::move(ctrl)); |
| }); |
| |
| // 1. Start up the entity provider agent. |
| fuchsia::sys::ServiceProviderPtr incoming_services; |
| fuchsia::modular::AgentControllerPtr agent_controller; |
| agent_runner()->ConnectToAgent("dummy_requestor_url", kMyAgentUrl, |
| incoming_services.NewRequest(), |
| agent_controller.NewRequest()); |
| |
| RunLoopWithTimeoutOrUntil([&dummy_agent] { |
| return dummy_agent.get() != nullptr && |
| dummy_agent->GetCallCount("Connect") == 1; |
| }); |
| dummy_agent->ExpectCalledOnce("Connect"); |
| |
| // 2. Make an entity reference on behalf of this agent. |
| // The framework should use |kMyAgentUrl| as the agent to associate new |
| // references. |
| fuchsia::modular::EntityReferenceFactoryPtr factory; |
| dummy_agent->agent_context()->GetEntityReferenceFactory(factory.NewRequest()); |
| fidl::StringPtr entity_ref; |
| factory->CreateReference("my_cookie", [&entity_ref](fidl::StringPtr retval) { |
| entity_ref = retval; |
| }); |
| |
| RunLoopWithTimeoutOrUntil([&entity_ref] { return !entity_ref.is_null(); }); |
| EXPECT_FALSE(entity_ref.is_null()); |
| |
| // 3. Resolve the reference into an |fuchsia::modular::Entity|, make calls to |
| // GetTypes and |
| // GetData, which should route into our |MyEntityProvider|. |
| fuchsia::modular::EntityPtr entity; |
| dummy_agent->entity_resolver()->ResolveEntity(entity_ref, |
| entity.NewRequest()); |
| |
| std::map<std::string, uint32_t> counts; |
| entity->GetTypes([&counts](const std::vector<std::string>& types) { |
| EXPECT_EQ(1u, types.size()); |
| EXPECT_EQ("MyType", types.at(0)); |
| counts["GetTypes"]++; |
| }); |
| entity->GetData("MyType", |
| [&counts](std::unique_ptr<fuchsia::mem::Buffer> data) { |
| std::string data_string; |
| FXL_CHECK(fsl::StringFromVmo(*data, &data_string)); |
| EXPECT_EQ("MyType:MyData", data_string); |
| counts["GetData"]++; |
| }); |
| RunLoopWithTimeoutOrUntil( |
| [&counts] { return counts["GetTypes"] == 1 && counts["GetData"] == 1; }); |
| EXPECT_EQ(1u, counts["GetTypes"]); |
| EXPECT_EQ(1u, counts["GetData"]); |
| } |
| |
| TEST_F(EntityProviderRunnerTest, DataEntity) { |
| std::map<std::string, std::string> data; |
| data["type1"] = "data1"; |
| |
| auto entity_ref = entity_provider_runner()->CreateReferenceFromData(data); |
| |
| fuchsia::modular::EntityResolverPtr entity_resolver; |
| entity_provider_runner()->ConnectEntityResolver(entity_resolver.NewRequest()); |
| fuchsia::modular::EntityPtr entity; |
| entity_resolver->ResolveEntity(entity_ref, entity.NewRequest()); |
| |
| fidl::VectorPtr<std::string> output_types; |
| entity->GetTypes([&output_types](std::vector<std::string> result) { |
| output_types.reset(std::move(result)); |
| }); |
| RunLoopWithTimeoutOrUntil( |
| [&output_types] { return !output_types.is_null(); }); |
| |
| EXPECT_EQ(data.size(), output_types->size()); |
| EXPECT_EQ("type1", output_types->at(0)); |
| |
| fidl::StringPtr output_data; |
| entity->GetData("type1", |
| [&output_data](std::unique_ptr<fuchsia::mem::Buffer> result) { |
| std::string data_string; |
| FXL_CHECK(fsl::StringFromVmo(*result, &data_string)); |
| output_data = data_string; |
| }); |
| RunLoopWithTimeoutOrUntil([&output_data] { return !output_data.is_null(); }); |
| EXPECT_EQ("data1", output_data); |
| } |
| |
| } // namespace |
| } // namespace testing |
| } // namespace modular |