blob: 0ec09da7be13a5f89f96deccfbc99860079c50a1 [file] [log] [blame]
// 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/agent_runner/agent_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/component/cpp/service_provider_impl.h>
#include <lib/component/cpp/testing/fake_launcher.h>
#include <lib/fidl/cpp/binding.h>
#include <lib/fxl/files/scoped_temp_dir.h>
#include <lib/fxl/macros.h>
#include "gtest/gtest.h"
#include "peridot/bin/sessionmgr/entity_provider_runner/entity_provider_runner.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 AgentRunnerTest : public TestWithLedger {
public:
AgentRunnerTest() = 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>(nullptr);
// 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_; }
private:
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(AgentRunnerTest);
};
class MyDummyAgent : fuchsia::modular::Agent,
public fuchsia::sys::ComponentController,
public testing::MockBase {
public:
MyDummyAgent(zx::channel directory_request,
fidl::InterfaceRequest<fuchsia::sys::ComponentController> ctrl)
: vfs_(async_get_default_dispatcher()),
outgoing_directory_(fbl::AdoptRef(new fs::PseudoDir())),
controller_(this, std::move(ctrl)),
agent_binding_(this) {
outgoing_directory_->AddEntry(
fuchsia::modular::Agent::Name_,
fbl::AdoptRef(new fs::Service([this](zx::channel channel) {
agent_binding_.Bind(std::move(channel));
return ZX_OK;
})));
vfs_.ServeDirectory(outgoing_directory_, std::move(directory_request));
}
void KillApplication() { controller_.Unbind(); }
size_t GetCallCount(const std::string func) { return counts.count(func); }
private:
// |ComponentController|
void Kill() override { ++counts["Kill"]; }
// |ComponentController|
void Detach() override { ++counts["Detach"]; }
// |fuchsia::modular::Agent|
void Connect(
std::string /*requestor_url*/,
fidl::InterfaceRequest<fuchsia::sys::ServiceProvider> /*services*/)
override {
++counts["Connect"];
}
// |fuchsia::modular::Agent|
void RunTask(std::string /*task_id*/,
RunTaskCallback /*callback*/) override {
++counts["RunTask"];
}
private:
fs::SynchronousVfs vfs_;
fbl::RefPtr<fs::PseudoDir> outgoing_directory_;
fidl::Binding<fuchsia::sys::ComponentController> controller_;
fidl::Binding<fuchsia::modular::Agent> agent_binding_;
FXL_DISALLOW_COPY_AND_ASSIGN(MyDummyAgent);
};
// Test that connecting to an agent will start it up.
// Then there should be an fuchsia::modular::Agent.Connect().
TEST_F(AgentRunnerTest, ConnectToAgent) {
int agent_launch_count = 0;
std::unique_ptr<MyDummyAgent> dummy_agent;
constexpr char kMyAgentUrl[] = "file:///my_agent";
launcher()->RegisterComponent(
kMyAgentUrl,
[&dummy_agent, &agent_launch_count](
fuchsia::sys::LaunchInfo launch_info,
fidl::InterfaceRequest<fuchsia::sys::ComponentController> ctrl) {
dummy_agent = std::make_unique<MyDummyAgent>(
std::move(launch_info.directory_request), std::move(ctrl));
++agent_launch_count;
});
fuchsia::sys::ServiceProviderPtr incoming_services;
fuchsia::modular::AgentControllerPtr agent_controller;
agent_runner()->ConnectToAgent("requestor_url", kMyAgentUrl,
incoming_services.NewRequest(),
agent_controller.NewRequest());
RunLoopWithTimeoutOrUntil([&dummy_agent] {
return dummy_agent && dummy_agent->GetCallCount("Connect") > 0;
});
EXPECT_EQ(1, agent_launch_count);
dummy_agent->ExpectCalledOnce("Connect");
dummy_agent->ExpectNoOtherCalls();
// Connecting to the same agent again shouldn't launch a new instance and
// shouldn't re-initialize the existing instance of the agent application,
// but should call |Connect()|.
fuchsia::modular::AgentControllerPtr agent_controller2;
fuchsia::sys::ServiceProviderPtr incoming_services2;
agent_runner()->ConnectToAgent("requestor_url2", kMyAgentUrl,
incoming_services2.NewRequest(),
agent_controller2.NewRequest());
RunLoopWithTimeoutOrUntil([&dummy_agent] {
return dummy_agent && dummy_agent->GetCallCount("Connect");
});
EXPECT_EQ(1, agent_launch_count);
dummy_agent->ExpectCalledOnce("Connect");
dummy_agent->ExpectNoOtherCalls();
}
// Test that if an agent application dies, it is removed from agent runner
// (which means outstanding AgentControllers are closed).
TEST_F(AgentRunnerTest, AgentController) {
std::unique_ptr<MyDummyAgent> 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<MyDummyAgent>(
std::move(launch_info.directory_request), std::move(ctrl));
});
fuchsia::sys::ServiceProviderPtr incoming_services;
fuchsia::modular::AgentControllerPtr agent_controller;
agent_runner()->ConnectToAgent("requestor_url", kMyAgentUrl,
incoming_services.NewRequest(),
agent_controller.NewRequest());
RunLoopWithTimeoutOrUntil([&dummy_agent] { return !!dummy_agent; });
dummy_agent->KillApplication();
// fuchsia::modular::Agent application died, so check that
// fuchsia::modular::AgentController dies here.
agent_controller.set_error_handler(
[&agent_controller](zx_status_t status) { agent_controller.Unbind(); });
RunLoopWithTimeoutOrUntil(
[&agent_controller] { return !agent_controller.is_bound(); });
EXPECT_FALSE(agent_controller.is_bound());
}
} // namespace
} // namespace testing
} // namespace modular