blob: 6ddb6287f16cc64e1c2044f550e2e73298f260ac [file] [log] [blame]
// Copyright 2019 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 <fuchsia/modular/testing/cpp/fidl.h>
#include <fuchsia/ui/app/cpp/fidl.h>
#include <lib/modular/testing/cpp/fake_component.h>
#include <lib/syslog/cpp/macros.h>
#include <lib/ui/scenic/cpp/view_ref_pair.h>
#include <zircon/rights.h>
#include <zircon/syscalls/object.h>
#include <zircon/types.h>
#include <tuple>
#include <gmock/gmock.h>
#include "src/lib/fsl/vmo/strings.h"
#include "src/modular/lib/modular_test_harness/cpp/fake_module.h"
#include "src/modular/lib/modular_test_harness/cpp/fake_session_shell.h"
#include "src/modular/lib/modular_test_harness/cpp/fake_story_shell.h"
#include "src/modular/lib/modular_test_harness/cpp/test_harness_fixture.h"
using testing::ElementsAre;
namespace {
class ViewRefModule : public modular_testing::FakeModule, fuchsia::ui::app::ViewProvider {
public:
explicit ViewRefModule(modular_testing::FakeComponent::Args args)
: modular_testing::FakeModule(args) {}
fuchsia::ui::views::ViewRef& view_ref() { return view_ref_; }
bool has_created_view() { return has_created_view_; }
static std::unique_ptr<ViewRefModule> CreateWithDefaultOptions() {
return std::make_unique<ViewRefModule>(modular_testing::FakeComponent::Args{
.url = modular_testing::TestHarnessBuilder::GenerateFakeUrl(),
.sandbox_services = FakeModule::GetDefaultSandboxServices()});
}
private:
// |modular::testing::FakeComponent|
void OnCreate(fuchsia::sys::StartupInfo startup_info) override {
component_context()->outgoing()->AddPublicService<fuchsia::ui::app::ViewProvider>(
[this](fidl::InterfaceRequest<fuchsia::ui::app::ViewProvider> request) {
bindings_.AddBinding(this, std::move(request));
});
}
virtual void CreateView(
::zx::eventpair token,
::fidl::InterfaceRequest<::fuchsia::sys::ServiceProvider> incoming_services,
::fidl::InterfaceHandle<::fuchsia::sys::ServiceProvider> outgoing_services) override {}
virtual void CreateViewWithViewRef(::zx::eventpair token,
::fuchsia::ui::views::ViewRefControl view_ref_control,
::fuchsia::ui::views::ViewRef view_ref) override {
has_created_view_ = true;
view_ref_ = std::move(view_ref);
}
bool has_created_view_ = false;
fuchsia::ui::views::ViewRef view_ref_;
fidl::BindingSet<fuchsia::ui::app::ViewProvider> bindings_;
};
class StoryShellTest : public modular_testing::TestHarnessFixture {
protected:
StoryShellTest()
: session_shell_(modular_testing::FakeSessionShell::CreateWithDefaultOptions()),
story_shell_({.url = modular_testing::TestHarnessBuilder::GenerateFakeUrl(),
.sandbox_services = {"fuchsia.modular.StoryShellContext"}}) {}
void StartSession() { StartSessionWithInterceptedComponent(nullptr); }
void StartSessionWithInterceptedComponent(modular_testing::FakeComponent* component) {
modular_testing::TestHarnessBuilder builder;
builder.InterceptSessionShell(session_shell_->BuildInterceptOptions());
builder.InterceptStoryShell(story_shell_.BuildInterceptOptions());
if (component) {
builder.InterceptComponent(component->BuildInterceptOptions());
}
fake_module_url_ = modular_testing::TestHarnessBuilder::GenerateFakeUrl("module");
builder.InterceptComponent(
{.url = fake_module_url_,
.launch_handler =
[this](fuchsia::sys::StartupInfo startup_info,
fidl::InterfaceHandle<fuchsia::modular::testing::InterceptedComponent>
intercepted_component) {
intercepted_modules_.push_back(
{std::move(startup_info), std::move(intercepted_component)});
}});
builder.BuildAndRun(test_harness());
fuchsia::modular::testing::ModularService request;
request.set_puppet_master(puppet_master_.NewRequest());
test_harness()->ConnectToModularService(std::move(request));
// Wait for our session shell to start.
RunLoopUntil([this] { return session_shell_->is_running(); });
}
void AddModToStoryWithUrl(std::string story_name, std::string mod_name,
std::string parent_mod_name, std::string url) {
fuchsia::modular::StoryPuppetMasterPtr story_puppet_master;
puppet_master_->ControlStory(story_name, story_puppet_master.NewRequest());
fuchsia::modular::AddMod add_mod;
add_mod.mod_name_transitional = mod_name;
add_mod.intent.handler = url;
if (!parent_mod_name.empty()) {
add_mod.surface_parent_mod_name = {parent_mod_name};
}
std::vector<fuchsia::modular::StoryCommand> commands(1);
commands.at(0).set_add_mod(std::move(add_mod));
story_puppet_master->Enqueue(std::move(commands));
bool created = false;
story_puppet_master->Execute([&](fuchsia::modular::ExecuteResult result) { created = true; });
// Wait for the story to be created.
RunLoopUntil([&] { return created; });
}
void AddModToStory(std::string story_name, std::string mod_name,
std::string parent_mod_name = "") {
AddModToStoryWithUrl(story_name, mod_name, parent_mod_name, fake_module_url_);
}
void RestartStory(std::string story_name) {
fuchsia::modular::StoryControllerPtr story_controller;
session_shell_->story_provider()->GetController(story_name, story_controller.NewRequest());
bool restarted = false;
story_controller->Stop([&] {
story_controller->RequestStart();
restarted = true;
});
RunLoopUntil([&] { return restarted; });
}
fuchsia::modular::PuppetMasterPtr puppet_master_;
std::unique_ptr<modular_testing::FakeSessionShell> session_shell_;
modular_testing::FakeStoryShell story_shell_;
std::string fake_module_url_;
// Stories must have modules in them so the stories created above
// contain fake intercepted modules. This list holds onto them so that
// they can be successfully launched and don't die immediately.
std::vector<std::tuple<fuchsia::sys::StartupInfo,
fidl::InterfaceHandle<fuchsia::modular::testing::InterceptedComponent>>>
intercepted_modules_;
};
TEST_F(StoryShellTest, GetsModuleMetadata) {
StartSession();
std::vector<std::string> surface_ids_added;
story_shell_.set_on_add_surface([&](fuchsia::modular::ViewConnection view_connection,
fuchsia::modular::SurfaceInfo2 surface_info) {
surface_ids_added.push_back(view_connection.surface_id);
});
AddModToStory("story1", "mod1");
AddModToStory("story1", "mod2", {"mod1"} /* surface relation parent */);
// Wait for the story shell to be notified of the new modules.
RunLoopUntil([&] { return surface_ids_added.size() == 2; });
EXPECT_THAT(surface_ids_added, ElementsAre("mod1", "mod1:mod2"));
// Stop the story shell and restart it. Expect to see the same mods notified
// to the story shell in the same order.
surface_ids_added.clear();
RestartStory("story1");
RunLoopUntil([&] { return surface_ids_added.size() == 2; });
EXPECT_THAT(surface_ids_added, ElementsAre("mod1", "mod1:mod2"));
}
TEST_F(StoryShellTest, GetsViewRef) {
StartSession();
std::vector<bool> has_view_refs;
story_shell_.set_on_add_surface([&](fuchsia::modular::ViewConnection view_connection,
fuchsia::modular::SurfaceInfo2 surface_info) {
has_view_refs.push_back(surface_info.has_view_ref());
});
AddModToStory("story1", "mod1");
AddModToStory("story1", "mod2", {"mod1"} /* surface relation parent */);
// Wait for the story shell to be notified of the new modules.
RunLoopUntil([&] { return has_view_refs.size() == 2; });
EXPECT_THAT(has_view_refs, ElementsAre(true, true));
// Stop the story shell and restart it. Expect to see the same mods notified
// to the story shell in the same order.
has_view_refs.clear();
RestartStory("story1");
RunLoopUntil([&] { return has_view_refs.size() == 2; });
EXPECT_THAT(has_view_refs, ElementsAre(true, true));
}
TEST_F(StoryShellTest, GetsCorrectViewRef) {
auto view_ref_module = ViewRefModule::CreateWithDefaultOptions();
StartSessionWithInterceptedComponent(view_ref_module.get());
bool have_seen_view_ref = false;
fuchsia::ui::views::ViewRef seen_view_ref;
story_shell_.set_on_add_surface([&](fuchsia::modular::ViewConnection view_connection,
fuchsia::modular::SurfaceInfo2 surface_info) {
seen_view_ref = fidl::Clone(surface_info.view_ref());
have_seen_view_ref = true;
});
AddModToStoryWithUrl("story1", "mod1", "", view_ref_module->url());
// Wait for the story shell to be notified of the new modules.
RunLoopUntil([&] { return have_seen_view_ref; });
RunLoopUntil([&] { return view_ref_module->has_created_view(); });
// Get info about the |view_ref|.
zx_info_handle_basic_t seen_view_info;
zx_status_t seen_view_status =
zx_object_get_info(seen_view_ref.reference.get(), ZX_INFO_HANDLE_BASIC, &seen_view_info,
sizeof(seen_view_info), nullptr, nullptr);
zx_info_handle_basic_t mod_view_info;
zx_status_t mod_view_status =
zx_object_get_info(view_ref_module->view_ref().reference.get(), ZX_INFO_HANDLE_BASIC,
&mod_view_info, sizeof(mod_view_info), nullptr, nullptr);
EXPECT_EQ(mod_view_status, ZX_OK);
EXPECT_EQ(seen_view_status, ZX_OK);
EXPECT_EQ(mod_view_info.koid, seen_view_info.koid);
}
} // namespace