Commit 071092e3 authored by Connor Lange's avatar Connor Lange Committed by Commit Bot

[Fuchsia] Update cast_runner to handle multiple agents.

Update cast_runner to delay connections for bindings and rewrite rules
until after the AppConfig is returned. This AppConfig will now contain
the agent_url, which will be used to contact the correct agent for
bindings, etc.

Bug: b/141573972
Test: cast_runner_integration_tests, Launch application

Change-Id: Ida6966fba6039fc2f9ea9d7c136711d13a0a5a2a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1895401Reviewed-by: default avatarWez <wez@chromium.org>
Commit-Queue: Connor Lange <connorl@chromium.org>
Auto-Submit: Connor Lange <connorl@chromium.org>
Cr-Commit-Position: refs/heads/master@{#722625}
parent 5e294641
......@@ -20,17 +20,30 @@ FakeComponentContext::FakeComponentContext(
sys::OutgoingDirectory* outgoing_directory,
base::StringPiece component_url)
: binding_(outgoing_directory, this),
// Publishing the Agent to |outgoing_directory| is not necessary, but
// also shouldn't do any harm.
agent_impl_(outgoing_directory,
std::move(create_component_state_callback)),
component_url_(component_url.as_string()) {}
component_url_(component_url.as_string()),
outgoing_directory_(outgoing_directory),
default_agent_impl_(outgoing_directory,
std::move(create_component_state_callback)) {}
void FakeComponentContext::RegisterCreateComponentStateCallback(
base::StringPiece agent_url,
AgentImpl::CreateComponentStateCallback create_component_state_callback) {
agent_impl_map_.insert(std::make_pair(
agent_url,
std::make_unique<AgentImpl>(outgoing_directory_,
std::move(create_component_state_callback))));
}
void FakeComponentContext::ConnectToAgent(
std::string agent_url,
fidl::InterfaceRequest<::fuchsia::sys::ServiceProvider> services,
fidl::InterfaceRequest<fuchsia::modular::AgentController> controller) {
agent_impl_.Connect(component_url_, std::move(services));
auto it = agent_impl_map_.find(agent_url);
if (it == agent_impl_map_.end()) {
default_agent_impl_.Connect(component_url_, std::move(services));
} else {
it->second->Connect(component_url_, std::move(services));
}
}
void FakeComponentContext::ConnectToAgentService(
......
......@@ -7,6 +7,7 @@
#include <fuchsia/base/agent_impl.h>
#include <fuchsia/modular/cpp/fidl_test_base.h>
#include <map>
#include <memory>
#include <string>
#include <utility>
......@@ -31,6 +32,10 @@ class FakeComponentContext
base::StringPiece component_url);
~FakeComponentContext() override;
void RegisterCreateComponentStateCallback(
base::StringPiece agent_url,
AgentImpl::CreateComponentStateCallback callback);
// fuchsia::modular::ComponentContext_TestBase implementation.
void ConnectToAgent(
std::string agent_url,
......@@ -44,10 +49,13 @@ class FakeComponentContext
private:
base::fuchsia::ScopedServiceBinding<fuchsia::modular::ComponentContext>
binding_;
AgentImpl agent_impl_;
const std::string component_url_;
sys::OutgoingDirectory* const outgoing_directory_;
fuchsia::sys::ServiceProviderPtr agent_services_;
std::map<base::StringPiece, std::unique_ptr<AgentImpl>> agent_impl_map_;
AgentImpl default_agent_impl_;
DISALLOW_COPY_AND_ASSIGN(FakeComponentContext);
};
......
......@@ -101,38 +101,9 @@ void CastRunner::StartComponent(
pending_component->startup_context->component_context()->svc().get());
pending_component->controller_request = std::move(controller_request);
// Get binding details from the Agent.
fidl::InterfaceHandle<chromium::cast::ApiBindings> api_bindings_client;
pending_component->agent_manager->ConnectToAgentService(
kAgentComponentUrl, api_bindings_client.NewRequest());
pending_component->api_bindings_client = std::make_unique<ApiBindingsClient>(
std::move(api_bindings_client),
base::BindOnce(&CastRunner::MaybeStartComponent, base::Unretained(this),
base::Unretained(pending_component.get())),
base::BindOnce(&CastRunner::CancelComponentLaunch, base::Unretained(this),
base::Unretained(pending_component.get())));
// Get UrlRequestRewriteRulesProvider from the Agent.
fidl::InterfaceHandle<chromium::cast::UrlRequestRewriteRulesProvider>
url_request_rules_provider;
pending_component->agent_manager->ConnectToAgentService(
kAgentComponentUrl, url_request_rules_provider.NewRequest());
pending_component->rewrite_rules_provider = url_request_rules_provider.Bind();
pending_component->rewrite_rules_provider.set_error_handler(
[this, pending_component = pending_component.get()](zx_status_t status) {
ZX_LOG(ERROR, status) << "UrlRequestRewriteRulesProvider disconnected.";
CancelComponentLaunch(pending_component);
});
pending_component->rewrite_rules_provider->GetUrlRequestRewriteRules(
[this, pending_component = pending_component.get()](
std::vector<fuchsia::web::UrlRequestRewriteRule> rewrite_rules) {
pending_component->rewrite_rules =
base::Optional<std::vector<fuchsia::web::UrlRequestRewriteRule>>(
std::move(rewrite_rules));
MaybeStartComponent(pending_component);
});
// Request the configuration for the specified application.
// Request the configuration for this application from the app_config_manager.
// This will return the configuration for the application, as well as the
// agent that should handle this application.
pending_component->agent_manager->ConnectToAgentService(
kAgentComponentUrl, pending_component->app_config_manager.NewRequest());
pending_component->app_config_manager.set_error_handler(
......@@ -199,9 +170,43 @@ void CastRunner::GetConfigCallback(
return;
}
if (!app_config.has_agent_url()) {
pending_components_.erase(it);
DLOG(WARNING) << "No agent has been associated with this app.";
return;
}
pending_component->app_config = std::move(app_config);
MaybeStartComponent(pending_component);
// Request binding details from the Agent.
fidl::InterfaceHandle<chromium::cast::ApiBindings> api_bindings_client;
pending_component->agent_manager->ConnectToAgentService(
pending_component->app_config.agent_url(),
api_bindings_client.NewRequest());
pending_component->api_bindings_client = std::make_unique<ApiBindingsClient>(
std::move(api_bindings_client),
base::BindOnce(&CastRunner::MaybeStartComponent, base::Unretained(this),
base::Unretained(pending_component)),
base::BindOnce(&CastRunner::CancelComponentLaunch, base::Unretained(this),
base::Unretained(pending_component)));
// Request UrlRequestRewriteRulesProvider from the Agent.
pending_component->agent_manager->ConnectToAgentService(
kAgentComponentUrl,
pending_component->rewrite_rules_provider.NewRequest());
pending_component->rewrite_rules_provider.set_error_handler(
[this, pending_component = pending_component](zx_status_t status) {
ZX_LOG(ERROR, status) << "UrlRequestRewriteRulesProvider disconnected.";
CancelComponentLaunch(pending_component);
});
pending_component->rewrite_rules_provider->GetUrlRequestRewriteRules(
[this, pending_component = pending_component](
std::vector<fuchsia::web::UrlRequestRewriteRule> rewrite_rules) {
pending_component->rewrite_rules =
base::Optional<std::vector<fuchsia::web::UrlRequestRewriteRule>>(
std::move(rewrite_rules));
MaybeStartComponent(pending_component);
});
}
void CastRunner::MaybeStartComponent(
......
......@@ -46,6 +46,9 @@ namespace {
const char kTestServerRoot[] =
FILE_PATH_LITERAL("fuchsia/runners/cast/testdata");
const char kDummyAgentUrl[] =
"fuchsia-pkg://fuchsia.com/dummy_agent#meta/dummy_agent.cmx";
void ComponentErrorHandler(zx_status_t status) {
ZX_LOG(ERROR, status) << "Component launch failed";
ADD_FAILURE();
......@@ -136,6 +139,10 @@ class FakeComponentState : public cr_fuchsia::AgentImpl::ComponentStateBase {
void Disconnect() { DisconnectClientsAndTeardown(); }
bool api_bindings_has_clients() {
return bindings_manager_binding_.has_clients();
}
protected:
const base::fuchsia::ScopedServiceBinding<
chromium::cast::ApplicationConfigManager>
......@@ -199,18 +206,7 @@ class CastRunnerIntegrationTest : public testing::Test {
}
fuchsia::sys::ComponentControllerPtr StartCastComponent(
base::StringPiece component_url,
bool start_component_context) {
DCHECK(!component_state_);
if (start_component_context) {
// Create a FakeComponentContext and publish it into component_services_.
component_context_ = std::make_unique<cr_fuchsia::FakeComponentContext>(
base::BindRepeating(&CastRunnerIntegrationTest::OnComponentConnect,
base::Unretained(this)),
&component_services_, component_url);
}
base::StringPiece component_url) {
// Configure the Runner, including a service directory channel to publish
// services to.
fidl::InterfaceHandle<fuchsia::io::Directory> directory;
......@@ -261,6 +257,14 @@ class CastRunnerIntegrationTest : public testing::Test {
return component_state;
}
std::unique_ptr<cr_fuchsia::FakeComponentContext> CreateComponentContext(
const base::StringPiece& component_url) {
return std::make_unique<cr_fuchsia::FakeComponentContext>(
base::BindRepeating(&CastRunnerIntegrationTest::OnComponentConnect,
base::Unretained(this)),
&component_services_, component_url);
}
const base::RunLoop::ScopedRunTimeoutForTest run_timeout_;
base::test::SingleThreadTaskEnvironment task_environment_{
base::test::SingleThreadTaskEnvironment::MainThreadType::IO};
......@@ -293,9 +297,14 @@ TEST_F(CastRunnerIntegrationTest, BasicRequest) {
app_config_manager_.AddAppMapping(kBlankAppId,
test_server_.GetURL(kBlankAppPath), false);
auto component_url = base::StringPrintf("cast:%s", kBlankAppId);
// Create a FakeComponentContext and publish it into component_services_.
component_context_ = CreateComponentContext(component_url);
// Launch the test-app component.
fuchsia::sys::ComponentControllerPtr component_controller =
StartCastComponent(base::StringPrintf("cast:%s", kBlankAppId), true);
StartCastComponent(component_url);
component_controller.set_error_handler(&ComponentErrorHandler);
// Access the NavigationController from the WebComponent. The test will hang
......@@ -337,6 +346,8 @@ TEST_F(CastRunnerIntegrationTest, BasicRequest) {
TEST_F(CastRunnerIntegrationTest, ApiBindings) {
const char kBlankAppId[] = "00000000";
const char kBlankAppPath[] = "/echo.html";
auto component_url = base::StringPrintf("cast:%s", kBlankAppId);
app_config_manager_.AddAppMapping(kBlankAppId,
test_server_.GetURL(kBlankAppPath), false);
......@@ -348,9 +359,12 @@ TEST_F(CastRunnerIntegrationTest, ApiBindings) {
binding_list.emplace_back(std::move(echo_binding));
api_bindings_.set_bindings(std::move(binding_list));
// Create a FakeComponentContext and publish it into component_services_.
component_context_ = CreateComponentContext(component_url);
// Launch the test-app component.
fuchsia::sys::ComponentControllerPtr component_controller =
StartCastComponent(base::StringPrintf("cast:%s", kBlankAppId), true);
StartCastComponent(component_url);
component_controller.set_error_handler(&ComponentErrorHandler);
fuchsia::web::MessagePortPtr port =
......@@ -374,12 +388,18 @@ TEST_F(CastRunnerIntegrationTest, ApiBindings) {
EXPECT_TRUE(
cr_fuchsia::StringFromMemBuffer(response->data(), &response_string));
EXPECT_EQ("ack ping", response_string);
EXPECT_TRUE(component_state_->api_bindings_has_clients());
}
TEST_F(CastRunnerIntegrationTest, IncorrectCastAppId) {
const char kIncorrectComponentUrl[] = "cast:99999999";
// Create a FakeComponentContext and publish it into component_services_.
component_context_ = CreateComponentContext(kIncorrectComponentUrl);
// Launch the a component with an invalid Cast app Id.
fuchsia::sys::ComponentControllerPtr component_controller =
StartCastComponent("cast:99999999", true);
StartCastComponent(kIncorrectComponentUrl);
component_controller.set_error_handler(&ComponentErrorHandler);
// Run the loop until the ComponentController is dropped, or a WebComponent is
......@@ -401,11 +421,16 @@ TEST_F(CastRunnerIntegrationTest, UrlRequestRewriteRulesProvider) {
const char kEchoAppId[] = "00000000";
const char kEchoAppPath[] = "/echoheader?Test";
const GURL echo_app_url = test_server_.GetURL(kEchoAppPath);
auto component_url = base::StringPrintf("cast:%s", kEchoAppId);
app_config_manager_.AddAppMapping(kEchoAppId, echo_app_url, false);
// Create a FakeComponentContext and publish it into component_services_.
component_context_ = CreateComponentContext(component_url);
// Launch the test-app component.
fuchsia::sys::ComponentControllerPtr component_controller =
StartCastComponent(base::StringPrintf("cast:%s", kEchoAppId), true);
StartCastComponent(component_url);
component_controller.set_error_handler(&ComponentErrorHandler);
WebComponent* web_component = nullptr;
......@@ -439,12 +464,16 @@ TEST_F(CastRunnerIntegrationTest, UrlRequestRewriteRulesProvider) {
TEST_F(CastRunnerIntegrationTest, ApplicationControllerBound) {
const char kCastChannelAppId[] = "00000001";
const char kCastChannelAppPath[] = "/defaultresponse";
auto component_url = base::StringPrintf("cast:%s", kCastChannelAppId);
app_config_manager_.AddAppMapping(
kCastChannelAppId, test_server_.GetURL(kCastChannelAppPath), false);
// Create a FakeComponentContext and publish it into component_services_.
component_context_ = CreateComponentContext(component_url);
fuchsia::sys::ComponentControllerPtr component_controller =
StartCastComponent(base::StringPrintf("cast:%s", kCastChannelAppId),
true);
StartCastComponent(component_url);
// Spin the message loop to handle creation of the component state.
base::RunLoop().RunUntilIdle();
......@@ -457,12 +486,16 @@ TEST_F(CastRunnerIntegrationTest, RemoteDebugging) {
const char kBlankAppId[] = "00000000";
const char kBlankAppPath[] = "/defaultresponse";
const GURL kBlankAppUrl = test_server_.GetURL(kBlankAppPath);
auto component_url = base::StringPrintf("cast:%s", kBlankAppId);
app_config_manager_.AddAppMapping(kBlankAppId, kBlankAppUrl, true);
// Create a FakeComponentContext and publish it into component_services_.
component_context_ = CreateComponentContext(component_url);
// Launch the test-app component.
fuchsia::sys::ComponentControllerPtr component_controller =
StartCastComponent(base::StringPrintf("cast:%s", kBlankAppId), true);
StartCastComponent(component_url);
component_controller.set_error_handler(&ComponentErrorHandler);
// Get the remote debugging port from the Context.
......@@ -495,6 +528,7 @@ TEST_F(CastRunnerIntegrationTest, RemoteDebugging) {
TEST_F(CastRunnerIntegrationTest, IsolatedContext) {
const char kBlankAppId[] = "00000000";
const GURL kContentDirectoryUrl("fuchsia-dir://testdata/echo.html");
auto component_url = base::StringPrintf("cast:%s", kBlankAppId);
EXPECT_EQ(cast_runner_->GetChildCastRunnerCountForTest(), 0u);
......@@ -509,9 +543,12 @@ TEST_F(CastRunnerIntegrationTest, IsolatedContext) {
app_config_manager_.AddAppMappingWithContentDirectories(
kBlankAppId, kContentDirectoryUrl, std::move(providers));
// Create a FakeComponentContext and publish it into component_services_.
component_context_ = CreateComponentContext(component_url);
// Launch the test-app component.
fuchsia::sys::ComponentControllerPtr component_controller =
StartCastComponent(base::StringPrintf("cast:%s", kBlankAppId), true);
StartCastComponent(component_url);
component_controller.set_error_handler(&ComponentErrorHandler);
// Navigate to the page and verify that we read it.
......@@ -554,7 +591,7 @@ TEST_F(CastRunnerIntegrationTest, NoCastAgent) {
// Launch the test-app component.
fuchsia::sys::ComponentControllerPtr component_controller =
StartCastComponent(base::StringPrintf("cast:%s", kEchoAppId), false);
StartCastComponent(base::StringPrintf("cast:%s", kEchoAppId));
base::RunLoop run_loop;
component_controller.set_error_handler([&run_loop](zx_status_t error) {
......@@ -569,11 +606,15 @@ TEST_F(CastRunnerIntegrationTest, DisconnectedCastAgent) {
const char kEchoAppId[] = "00000000";
const char kEchoAppPath[] = "/echoheader?Test";
const GURL echo_app_url = test_server_.GetURL(kEchoAppPath);
auto component_url = base::StringPrintf("cast:%s", kEchoAppId);
app_config_manager_.AddAppMapping(kEchoAppId, echo_app_url, false);
component_context_ = CreateComponentContext(component_url);
// Launch the test-app component.
fuchsia::sys::ComponentControllerPtr component_controller =
StartCastComponent(base::StringPrintf("cast:%s", kEchoAppId), true);
StartCastComponent(component_url);
// Access the NavigationController from the WebComponent. The test will hang
// here if no WebComponent was created.
......@@ -604,4 +645,52 @@ TEST_F(CastRunnerIntegrationTest, DisconnectedCastAgent) {
run_loop.Run();
}
// Test that the ApiBindings are received from the secondary DummyAgent. This
// validates that the |agent_url| retrieved from AppConfigManager is the one
// used to retrieve the bindings.
TEST_F(CastRunnerIntegrationTest, ApplicationConfigAgentUrl) {
const char kBlankAppId[] = "00000000";
const char kBlankAppPath[] = "/echo.html";
auto component_url = base::StringPrintf("cast:%s", kBlankAppId);
TestApiBindings dummy_agent_api_bindings;
// Indicate that this app is to get bindings from a secondary agent.
app_config_manager_.AddAppMappingWithAgent(
kBlankAppId, test_server_.GetURL(kBlankAppPath), false, kDummyAgentUrl);
// Instantiate the bindings that are returned in the multi-agent scenario. The
// bindings returned for the single-agent scenario are not initialized.
std::vector<chromium::cast::ApiBinding> binding_list;
chromium::cast::ApiBinding echo_binding;
echo_binding.set_before_load_script(cr_fuchsia::MemBufferFromString(
"window.echo = cast.__platform__.PortConnector.bind('dummyService');",
"test"));
binding_list.emplace_back(std::move(echo_binding));
// Assign the bindings to the multi-agent binding.
dummy_agent_api_bindings.set_bindings(std::move(binding_list));
component_context_ = CreateComponentContext(component_url);
EXPECT_NE(component_context_, nullptr);
component_context_->RegisterCreateComponentStateCallback(
kDummyAgentUrl,
base::BindLambdaForTesting(
[&](base::StringPiece component_url)
-> std::unique_ptr<cr_fuchsia::AgentImpl::ComponentStateBase> {
return std::make_unique<FakeComponentState>(
component_url, &app_config_manager_, &dummy_agent_api_bindings,
&url_request_rewrite_rules_provider_);
}));
// Launch the test-app component.
fuchsia::sys::ComponentControllerPtr component_controller =
StartCastComponent(component_url);
component_controller.set_error_handler(&ComponentErrorHandler);
base::RunLoop().RunUntilIdle();
// Validate that the correct bindings were requested.
EXPECT_FALSE(component_state_->api_bindings_has_clients());
}
} // namespace castrunner
......@@ -9,6 +9,11 @@
#include "base/logging.h"
namespace {
const char kAgentComponentUrl[] =
"fuchsia-pkg://fuchsia.com/cast_agent#meta/cast_agent.cmx";
} // namespace
FakeApplicationConfigManager::FakeApplicationConfigManager() = default;
FakeApplicationConfigManager::~FakeApplicationConfigManager() = default;
......@@ -28,11 +33,20 @@ void FakeApplicationConfigManager::GetConfig(std::string id,
void FakeApplicationConfigManager::AddAppMapping(const std::string& id,
const GURL& url,
bool enable_remote_debugging) {
AddAppMappingWithAgent(id, url, enable_remote_debugging, kAgentComponentUrl);
}
void FakeApplicationConfigManager::AddAppMappingWithAgent(
const std::string& id,
const GURL& url,
bool enable_remote_debugging,
const std::string& agent_url) {
chromium::cast::ApplicationConfig app_config;
app_config.set_id(id);
app_config.set_display_name("Dummy test app");
app_config.set_web_url(url.spec());
app_config.set_enable_remote_debugging(enable_remote_debugging);
app_config.set_agent_url(agent_url);
id_to_config_[id] = std::move(app_config);
}
......@@ -44,6 +58,7 @@ void FakeApplicationConfigManager::AddAppMappingWithContentDirectories(
app_config.set_id(id);
app_config.set_display_name("Dummy test app");
app_config.set_web_url(url.spec());
app_config.set_agent_url(kAgentComponentUrl);
if (!directories.empty()) {
app_config.set_content_directories_for_isolated_application(
std::move(directories));
......
......@@ -27,6 +27,13 @@ class FakeApplicationConfigManager
const GURL& url,
bool enable_remote_debugging);
// Associates a Cast application |id| with a url and an agent that handles
// its bindings, to be served from the EmbeddedTestServer.
void AddAppMappingWithAgent(const std::string& id,
const GURL& url,
bool enable_remote_debugging,
const std::string& agent_url);
// Associates a Cast application |id| with a url and a set of content
// directories, to be served from the EmbeddedTestServer.
void AddAppMappingWithContentDirectories(
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment