blob: fde886d6272ae58b22036f2a64a87f3181512281 [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 "sdk/lib/virtualization/scenic_wayland_dispatcher.h"
#include <fuchsia/virtualization/cpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/gtest/test_loop_fixture.h>
#include <lib/sys/cpp/testing/component_context_provider.h>
#include <lib/sys/cpp/testing/fake_component.h>
#include <lib/sys/cpp/testing/fake_launcher.h>
namespace guest {
static constexpr const char* kWaylandDispatcherUrl =
"fuchsia-pkg://fuchsia.com/wayland_bridge#meta/wayland_bridge.cmx";
class FakeDispatcher : public fuchsia::virtualization::WaylandDispatcher,
public fuchsia::wayland::ViewProducer {
public:
FakeDispatcher() {
component_.AddPublicService(bindings_.GetHandler(this));
component_.AddPublicService(view_producer_bindings_.GetHandler(this));
}
// Register to be launched with a fake URL
void Register(sys::testing::FakeLauncher& fake_launcher) {
component_.Register(kWaylandDispatcherUrl, fake_launcher);
}
size_t BindingCount() const { return bindings_.size(); }
size_t ConnectionCount() const { return connections_.size(); }
// Simulates the bridge dying by clearing all state and closing any bindings.
void Terminate() {
bindings_.CloseAll();
view_producer_bindings_.CloseAll();
connections_.clear();
}
void SendOnNewView() {
for (auto& binding : view_producer_bindings_.bindings()) {
zx::channel c1, c2;
zx::channel::create(0, &c1, &c2);
binding->events().OnNewView(
fidl::InterfaceHandle<fuchsia::ui::app::ViewProvider>(std::move(c1)));
new_view_channels_.push_back(std::move(c2));
}
}
private:
// |fuchsia::virtualization::WaylandDispatcher|
void OnNewConnection(zx::channel channel) { connections_.push_back(std::move(channel)); }
sys::testing::FakeComponent component_;
fidl::BindingSet<fuchsia::virtualization::WaylandDispatcher> bindings_;
fidl::BindingSet<fuchsia::wayland::ViewProducer> view_producer_bindings_;
std::vector<zx::channel> connections_;
std::vector<zx::channel> new_view_channels_;
};
class ScenicWaylandDispatcherTest : public gtest::TestLoopFixture {
public:
void SetUp() override {
TestLoopFixture::SetUp();
dispatcher_.reset(new ScenicWaylandDispatcher(
provider_.context(), fit::bind_member(this, &ScenicWaylandDispatcherTest::OnNewView)));
provider_.service_directory_provider()->AddService(fake_launcher_.GetHandler());
fake_dispatcher_impl_.reset(new FakeDispatcher());
fake_dispatcher_impl_->Register(fake_launcher_);
}
void TearDown() override { TestLoopFixture::TearDown(); }
protected:
ScenicWaylandDispatcher* dispatcher() const { return dispatcher_.get(); }
FakeDispatcher* remote_dispatcher() const { return fake_dispatcher_impl_.get(); }
std::vector<fidl::InterfaceHandle<fuchsia::ui::app::ViewProvider>>* views() { return &views_; }
private:
void OnNewView(fidl::InterfaceHandle<fuchsia::ui::app::ViewProvider> view) {
views_.push_back(std::move(view));
}
sys::testing::FakeLauncher fake_launcher_;
std::unique_ptr<FakeDispatcher> fake_dispatcher_impl_;
sys::testing::ComponentContextProvider provider_;
std::unique_ptr<ScenicWaylandDispatcher> dispatcher_;
std::vector<fidl::InterfaceHandle<fuchsia::ui::app::ViewProvider>> views_;
};
// The |ScenicWaylandDispatcher| will simply spawn a new bridge process for each
// connection and reuse that process for subsequent connections.
//
// Test that multiple connections are sent to the same bridge component.
TEST_F(ScenicWaylandDispatcherTest, LaunchBridgeOnce) {
zx::channel c1, c2;
zx::channel::create(0, &c1, &c2);
dispatcher()->OnNewConnection(std::move(c1));
RunLoopUntilIdle();
ASSERT_EQ(1u, remote_dispatcher()->BindingCount());
ASSERT_EQ(1u, remote_dispatcher()->ConnectionCount());
dispatcher()->OnNewConnection(std::move(c2));
RunLoopUntilIdle();
ASSERT_EQ(1u, remote_dispatcher()->BindingCount());
ASSERT_EQ(2u, remote_dispatcher()->ConnectionCount());
}
// When the remote wayland_bridge component dies, we restart it on the next
// connection request.
TEST_F(ScenicWaylandDispatcherTest, RelaunchBridgeWhenLost) {
zx::channel c1, c2;
zx::channel::create(0, &c1, &c2);
dispatcher()->OnNewConnection(std::move(c1));
RunLoopUntilIdle();
ASSERT_EQ(1u, remote_dispatcher()->BindingCount());
ASSERT_EQ(1u, remote_dispatcher()->ConnectionCount());
remote_dispatcher()->Terminate();
RunLoopUntilIdle();
ASSERT_EQ(0u, remote_dispatcher()->BindingCount());
ASSERT_EQ(0u, remote_dispatcher()->ConnectionCount());
dispatcher()->OnNewConnection(std::move(c2));
RunLoopUntilIdle();
ASSERT_EQ(1u, remote_dispatcher()->BindingCount());
ASSERT_EQ(1u, remote_dispatcher()->ConnectionCount());
}
// Verify we can correctly receive new ViewProviders from the remote bridge
// process.
TEST_F(ScenicWaylandDispatcherTest, ReceiveNewViewEvents) {
zx::channel c1, c2;
zx::channel::create(0, &c1, &c2);
dispatcher()->OnNewConnection(std::move(c1));
RunLoopUntilIdle();
ASSERT_EQ(1u, remote_dispatcher()->BindingCount());
ASSERT_EQ(1u, remote_dispatcher()->ConnectionCount());
ASSERT_EQ(0u, views()->size());
remote_dispatcher()->SendOnNewView();
RunLoopUntilIdle();
ASSERT_EQ(1u, views()->size());
}
}; // namespace guest