blob: 0f54f8317924c8825f41a8ae70c419e4e2284728 [file] [log] [blame]
// Copyright 2018 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 <memory>
#include <fuchsia/ui/scenic/cpp/fidl.h>
#include <lib/async/cpp/time.h>
#include <lib/zx/eventpair.h>
#include "garnet/lib/ui/input/tests/util.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "lib/fxl/logging.h"
#include "lib/gtest/test_loop_fixture.h"
#include "lib/ui/input/cpp/formatting.h"
#include "lib/ui/scenic/cpp/resources.h"
#include "lib/ui/scenic/cpp/session.h"
// This test exercises the event delivery logic for Views created with an
// ImportNode (v1), as opposed to a View resource (v2).
//
// The client has its root node attached to an ImportNode, which in turn is
// attached to an EntityNode in ViewManager. Here, we merely simulate the
// structure of such a graph; we rely on the invariant that in the ViewManager
// world, each client's View terminates in an ImportNode before transitioning to
// a ViewManager node.
//
//
// The geometry is contrained to a 7x7 display and layer, with one 5x5 rectangle
// that sits at an offset, like so:
//
// - - - - - - -
// - - - - - - -
// - - x 1 1 1 U
// - - 1 1 1 M 1 x - client's view origin
// - - 1 1 D 1 1 D - add and down events
// - - 1 1 1 1 1 M - move event
// - - 1 1 1 1 1 U - up and remove events
//
// To create this test setup, we perform translation of the View itself (i.e.,
// (2,2)), in addition to aligning (translating) the Shape to its View.
//
// The touch events have the following correspondence of coordinates:
//
// Event Mark Device View
// ADD D (4,4) (2,2)
// DOWN D (4,4) (2,2)
// MOVE M (5,3) (3,1)
// UP U (6,2) (4,0)
// REMOVE U (6,2) (4,0)
//
// N.B. This test is carefully constructed to avoid Vulkan functionality.
namespace lib_ui_input_tests {
using ScenicEvent = fuchsia::ui::scenic::Event;
using escher::impl::CommandBufferSequencer;
using fuchsia::ui::input::InputEvent;
using fuchsia::ui::input::PointerEvent;
using fuchsia::ui::input::PointerEventPhase;
using fuchsia::ui::input::PointerEventType;
using fuchsia::ui::scenic::SessionListener;
using scenic_impl::Scenic;
using scenic_impl::gfx::Display;
using scenic_impl::gfx::DisplayManager;
using scenic_impl::gfx::test::GfxSystemForTest;
using scenic_impl::input::InputSystem;
using scenic_impl::test::ScenicTest;
// Class fixture for TEST_F. Sets up a 7x7 "display" for GfxSystem.
class ImportNodeTest : public InputSystemTest {
protected:
uint32_t test_display_width_px() const override { return 7; }
uint32_t test_display_height_px() const override { return 7; }
};
TEST_F(ImportNodeTest, ImportNodeEventDelivery) {
SessionWrapper presenter(scenic());
zx::eventpair import_token, export_token;
CreateTokenPair(&import_token, &export_token);
// Tie the test's dispatcher clock to the system (real) clock.
RunLoopUntil(zx::clock::get_monotonic());
// "Presenter" sets up a scene with one view.
uint32_t compositor_id = 0;
presenter.RunNow(
[this, &compositor_id, export_token = std::move(export_token)](
scenic::Session* session, scenic::EntityNode* root_node) mutable {
// Minimal scene.
scenic::Compositor compositor(session);
compositor_id = compositor.id();
scenic::Scene scene(session);
scenic::Camera camera(scene);
scenic::Renderer renderer(session);
renderer.SetCamera(camera);
scenic::Layer layer(session);
layer.SetSize(test_display_width_px(), test_display_height_px());
layer.SetRenderer(renderer);
scenic::LayerStack layer_stack(session);
layer_stack.AddLayer(layer);
compositor.SetLayerStack(layer_stack);
// Add local root node to the scene. Add a per-view translation node;
// export that node so that the client can hang their content from it.
scene.AddChild(*root_node);
scenic::EntityNode translate_child_view(session);
translate_child_view.SetTranslation(2, 2, 0);
translate_child_view.SetTag(1); // Emulate ViewManager's usage.
root_node->AddChild(translate_child_view);
translate_child_view.Export(std::move(export_token));
RequestToPresent(session);
});
// Client sets up its content.
SessionWrapper client(scenic());
client.RunNow(
[this, import_token = std::move(import_token)](
scenic::Session* session, scenic::EntityNode* root_node) mutable {
// Connect our root node to the presenter's root node.
// We've "imported" the presenter's root node in our session.
scenic::ImportNode import(session);
import.Bind(std::move(import_token));
import.AddChild(*root_node);
scenic::ShapeNode shape(session);
shape.SetTranslation(2, 2, 0); // Center the shape within the View.
root_node->AddPart(shape);
scenic::Rectangle rec(session, 5, 5); // Simple; no real GPU work.
shape.SetShape(rec);
scenic::Material material(session);
shape.SetMaterial(material);
RequestToPresent(session);
#if 0
FXL_LOG(INFO) << DumpScenes(); // Handy debugging.
#endif
});
// Scene is now set up, send in the input.
presenter.RunNow([this, compositor_id](scenic::Session* session,
scenic::EntityNode* root_node) {
PointerCommandGenerator pointer(compositor_id, /*device id*/ 1,
/*pointer id*/ 1, PointerEventType::TOUCH);
// A touch sequence that starts at the (4,4) location of the 7x7 display.
// The sequence ends 2x2 diagonally away (north-east) from the touch down.
session->Enqueue(pointer.Add(4, 4));
session->Enqueue(pointer.Down(4, 4));
session->Enqueue(pointer.Move(5, 3));
session->Enqueue(pointer.Up(6, 2));
session->Enqueue(pointer.Remove(6, 2));
RunLoopUntilIdle();
});
// Verify client's intake of input events.
client.ExamineEvents([this](const std::vector<InputEvent>& events) {
EXPECT_EQ(events.size(), 6u) << "Should receive exactly 6 input events.";
// ADD
EXPECT_TRUE(events[0].is_pointer());
EXPECT_TRUE(
PointerMatches(events[0].pointer(), 1u, PointerEventPhase::ADD, 2, 2));
// FOCUS
EXPECT_TRUE(events[1].is_focus());
EXPECT_TRUE(events[1].focus().focused);
// DOWN
EXPECT_TRUE(events[2].is_pointer());
EXPECT_TRUE(
PointerMatches(events[2].pointer(), 1u, PointerEventPhase::DOWN, 2, 2));
// MOVE
EXPECT_TRUE(events[3].is_pointer());
EXPECT_TRUE(
PointerMatches(events[3].pointer(), 1u, PointerEventPhase::MOVE, 3, 1));
// UP
EXPECT_TRUE(events[4].is_pointer());
EXPECT_TRUE(
PointerMatches(events[4].pointer(), 1u, PointerEventPhase::UP, 4, 0));
// REMOVE
EXPECT_TRUE(events[5].is_pointer());
EXPECT_TRUE(PointerMatches(events[5].pointer(), 1u,
PointerEventPhase::REMOVE, 4, 0));
});
}
} // namespace lib_ui_input_tests