blob: 640c8c86fce7f877ad5abc0567abfc027b666370 [file] [log] [blame]
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <utility>
#include "build/build_config.h"
#include "mojo/public/cpp/base/time_mojom_traits.h"
#include "mojo/public/cpp/test_support/test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/events/event.h"
#include "ui/events/ipc/ui_events_param_traits_macros.h"
#include "ui/events/keycodes/dom/dom_code.h"
#include "ui/events/mojom/event.mojom.h"
#include "ui/events/mojom/event_mojom_traits.h"
#include "ui/events/test/keyboard_layout.h"
#include "ui/gfx/geometry/mojom/geometry_mojom_traits.h"
#include "ui/latency/mojom/latency_info_mojom_traits.h"
#if BUILDFLAG(IS_OZONE)
#include "ui/events/ozone/layout/scoped_keyboard_layout_engine.h" // nogncheck
#include "ui/events/ozone/layout/stub/stub_keyboard_layout_engine.h" // nogncheck
#endif
namespace ui {
namespace {
void ExpectTouchEventsEqual(const TouchEvent& expected,
const TouchEvent& actual) {
EXPECT_EQ(expected.may_cause_scrolling(), actual.may_cause_scrolling());
EXPECT_EQ(expected.hovering(), actual.hovering());
EXPECT_EQ(expected.pointer_details(), actual.pointer_details());
}
void ExpectLocatedEventsEqual(const LocatedEvent& expected,
const LocatedEvent& actual) {
EXPECT_EQ(expected.location_f(), actual.location_f());
EXPECT_EQ(expected.root_location_f(), actual.root_location_f());
}
void ExpectMouseEventsEqual(const MouseEvent& expected,
const MouseEvent& actual) {
EXPECT_EQ(expected.pointer_details(), actual.pointer_details());
EXPECT_EQ(expected.changed_button_flags(), actual.changed_button_flags());
}
void ExpectMouseWheelEventsEqual(const MouseWheelEvent& expected,
const MouseWheelEvent& actual) {
EXPECT_EQ(expected.offset(), actual.offset());
EXPECT_EQ(expected.tick_120ths(), actual.tick_120ths());
}
void ExpectKeyEventsEqual(const KeyEvent& expected, const KeyEvent& actual) {
EXPECT_EQ(expected.GetCharacter(), actual.GetCharacter());
EXPECT_EQ(expected.GetUnmodifiedText(), actual.GetUnmodifiedText());
EXPECT_EQ(expected.GetText(), actual.GetText());
EXPECT_EQ(expected.is_char(), actual.is_char());
EXPECT_EQ(expected.is_repeat(), actual.is_repeat());
EXPECT_EQ(expected.GetConflatedWindowsKeyCode(),
actual.GetConflatedWindowsKeyCode());
EXPECT_EQ(expected.code(), actual.code());
}
void ExpectEventsEqual(const Event& expected, const Event& actual) {
EXPECT_EQ(expected.type(), actual.type());
EXPECT_EQ(expected.time_stamp(), actual.time_stamp());
EXPECT_EQ(expected.flags(), actual.flags());
if (expected.IsLocatedEvent()) {
ASSERT_TRUE(actual.IsLocatedEvent());
ExpectLocatedEventsEqual(*expected.AsLocatedEvent(),
*actual.AsLocatedEvent());
}
if (expected.IsMouseEvent()) {
ASSERT_TRUE(actual.IsMouseEvent());
ExpectMouseEventsEqual(*expected.AsMouseEvent(), *actual.AsMouseEvent());
}
if (expected.IsMouseWheelEvent()) {
ASSERT_TRUE(actual.IsMouseWheelEvent());
ExpectMouseWheelEventsEqual(*expected.AsMouseWheelEvent(),
*actual.AsMouseWheelEvent());
}
if (expected.IsTouchEvent()) {
ASSERT_TRUE(actual.IsTouchEvent());
ExpectTouchEventsEqual(*expected.AsTouchEvent(), *actual.AsTouchEvent());
}
if (expected.IsKeyEvent()) {
ASSERT_TRUE(actual.IsKeyEvent());
ExpectKeyEventsEqual(*expected.AsKeyEvent(), *actual.AsKeyEvent());
}
}
} // namespace
TEST(StructTraitsTest, KeyEvent) {
ui::ScopedKeyboardLayout keyboard_layout(ui::KEYBOARD_LAYOUT_ENGLISH_US);
const KeyEvent kTestData[] = {
{ET_KEY_PRESSED, VKEY_RETURN, EF_CONTROL_DOWN},
{ET_KEY_PRESSED, VKEY_MENU, EF_ALT_DOWN},
{ET_KEY_RELEASED, VKEY_SHIFT, EF_SHIFT_DOWN},
{ET_KEY_RELEASED, VKEY_MENU, EF_ALT_DOWN},
{ET_KEY_PRESSED, VKEY_A, DomCode::US_A, EF_NONE},
{ET_KEY_PRESSED, VKEY_B, DomCode::US_B, EF_CONTROL_DOWN | EF_ALT_DOWN},
ui::KeyEvent::FromCharacter('\x12', VKEY_2, DomCode::NONE,
EF_CONTROL_DOWN),
ui::KeyEvent::FromCharacter('Z', VKEY_Z, DomCode::NONE, EF_CAPS_LOCK_ON),
ui::KeyEvent::FromCharacter('z', VKEY_Z, DomCode::NONE, EF_NONE),
{ET_KEY_PRESSED, VKEY_Z, EF_NONE,
base::TimeTicks() + base::Microseconds(101)},
ui::KeyEvent::FromCharacter('Z', VKEY_Z, DomCode::NONE, EF_NONE,
base::TimeTicks() + base::Microseconds(102)),
};
for (size_t i = 0; i < std::size(kTestData); i++) {
std::unique_ptr<Event> expected_copy = kTestData[i].Clone();
std::unique_ptr<Event> output;
ASSERT_TRUE(mojo::test::SerializeAndDeserialize<mojom::Event>(expected_copy,
output));
EXPECT_TRUE(output->IsKeyEvent());
const KeyEvent* output_key_event = output->AsKeyEvent();
ExpectEventsEqual(kTestData[i], *output_key_event);
}
}
TEST(StructTraitsTest, MouseEvent) {
const MouseEvent kTestData[] = {
{ET_MOUSE_PRESSED, gfx::Point(10, 10), gfx::Point(20, 30),
base::TimeTicks() + base::Microseconds(201), EF_NONE, 0,
PointerDetails(EventPointerType::kMouse, kPointerIdMouse)},
{ET_MOUSE_DRAGGED, gfx::Point(1, 5), gfx::Point(5, 1),
base::TimeTicks() + base::Microseconds(202), EF_LEFT_MOUSE_BUTTON,
EF_LEFT_MOUSE_BUTTON,
PointerDetails(EventPointerType::kMouse, kPointerIdMouse)},
{ET_MOUSE_RELEASED, gfx::Point(411, 130), gfx::Point(20, 30),
base::TimeTicks() + base::Microseconds(203),
EF_MIDDLE_MOUSE_BUTTON | EF_RIGHT_MOUSE_BUTTON, EF_RIGHT_MOUSE_BUTTON,
PointerDetails(EventPointerType::kMouse, kPointerIdMouse)},
{ET_MOUSE_MOVED, gfx::Point(0, 1), gfx::Point(2, 3),
base::TimeTicks() + base::Microseconds(204), EF_ALT_DOWN, 0,
PointerDetails(EventPointerType::kMouse, kPointerIdMouse)},
{ET_MOUSE_ENTERED, gfx::Point(6, 7), gfx::Point(8, 9),
base::TimeTicks() + base::Microseconds(205), EF_NONE, 0,
PointerDetails(EventPointerType::kMouse, kPointerIdMouse)},
{ET_MOUSE_EXITED, gfx::Point(10, 10), gfx::Point(20, 30),
base::TimeTicks() + base::Microseconds(206), EF_BACK_MOUSE_BUTTON, 0,
PointerDetails(EventPointerType::kMouse, kPointerIdMouse)},
{ET_MOUSE_CAPTURE_CHANGED, gfx::Point(99, 99), gfx::Point(99, 99),
base::TimeTicks() + base::Microseconds(207), EF_CONTROL_DOWN, 0,
PointerDetails(EventPointerType::kMouse, kPointerIdMouse)},
};
for (size_t i = 0; i < std::size(kTestData); i++) {
std::unique_ptr<Event> expected_copy = kTestData[i].Clone();
std::unique_ptr<Event> output;
ASSERT_TRUE(mojo::test::SerializeAndDeserialize<mojom::Event>(expected_copy,
output));
ASSERT_TRUE(output->IsMouseEvent());
ExpectEventsEqual(kTestData[i], *output);
}
}
TEST(StructTraitsTest, MouseWheelEvent) {
const MouseWheelEvent kTestData[] = {
{gfx::Vector2d(11, 15), gfx::PointF(3, 4), gfx::PointF(40, 30),
base::TimeTicks() + base::Microseconds(301), EF_LEFT_MOUSE_BUTTON,
EF_LEFT_MOUSE_BUTTON, gfx::Vector2d(1320, 1800)},
{gfx::Vector2d(-5, 3), gfx::PointF(40, 3), gfx::PointF(4, 0),
base::TimeTicks() + base::Microseconds(302),
EF_MIDDLE_MOUSE_BUTTON | EF_RIGHT_MOUSE_BUTTON,
EF_MIDDLE_MOUSE_BUTTON | EF_RIGHT_MOUSE_BUTTON,
gfx::Vector2d(-600, 360)},
{gfx::Vector2d(1, 0), gfx::PointF(3, 4), gfx::PointF(40, 30),
base::TimeTicks() + base::Microseconds(303), EF_NONE, EF_NONE,
gfx::Vector2d(120, -15)},
};
for (size_t i = 0; i < std::size(kTestData); i++) {
std::unique_ptr<Event> expected_copy = kTestData[i].Clone();
std::unique_ptr<Event> output;
ASSERT_TRUE(mojo::test::SerializeAndDeserialize<mojom::Event>(expected_copy,
output));
ASSERT_EQ(ET_MOUSEWHEEL, output->type());
const MouseWheelEvent* output_event = output->AsMouseWheelEvent();
// TODO(sky): make this use ExpectEventsEqual().
ExpectMouseWheelEventsEqual(kTestData[i], *output_event);
}
}
TEST(StructTraitsTest, FloatingPointLocations) {
// Create some events with non-integer locations.
const gfx::PointF location(11.1, 22.2);
const gfx::PointF root_location(33.3, 44.4);
const base::TimeTicks time_stamp = base::TimeTicks::Now();
MouseEvent mouse_event(ET_MOUSE_PRESSED, location, root_location, time_stamp,
EF_NONE, EF_NONE);
MouseWheelEvent wheel_event(gfx::Vector2d(1, 0), location, root_location,
time_stamp, EF_NONE, EF_NONE);
ScrollEvent scroll_event(ET_SCROLL, location, root_location, time_stamp,
EF_NONE, 1, 2, 3, 4, 5);
TouchEvent touch_event(ET_TOUCH_PRESSED, location, root_location, time_stamp,
{}, EF_NONE);
Event* test_data[] = {&mouse_event, &wheel_event, &scroll_event,
&touch_event};
// Serialize and deserialize does not round or truncate the locations.
for (Event* event : test_data) {
std::unique_ptr<Event> event_copy = event->Clone();
std::unique_ptr<Event> output;
ASSERT_TRUE(
mojo::test::SerializeAndDeserialize<mojom::Event>(event_copy, output));
EXPECT_EQ(location, output->AsLocatedEvent()->location_f());
EXPECT_EQ(root_location, output->AsLocatedEvent()->root_location_f());
}
}
TEST(StructTraitsTest, KeyEventPropertiesSerialized) {
ui::ScopedKeyboardLayout keyboard_layout(ui::KEYBOARD_LAYOUT_ENGLISH_US);
KeyEvent key_event(ET_KEY_PRESSED, VKEY_T, EF_NONE);
const std::string key = "key";
const std::vector<uint8_t> value(0xCD, 2);
KeyEvent::Properties properties;
properties[key] = value;
key_event.SetProperties(properties);
std::unique_ptr<Event> event_ptr = key_event.Clone();
std::unique_ptr<Event> deserialized;
ASSERT_TRUE(mojom::Event::Deserialize(mojom::Event::Serialize(&event_ptr),
&deserialized));
ASSERT_TRUE(deserialized->IsKeyEvent());
ASSERT_TRUE(deserialized->AsKeyEvent()->properties());
EXPECT_EQ(properties, *(deserialized->AsKeyEvent()->properties()));
}
TEST(StructTraitsTest, GestureEvent) {
GestureEventDetails pinch_begin_details(ET_GESTURE_PINCH_BEGIN);
pinch_begin_details.set_device_type(ui::GestureDeviceType::DEVICE_TOUCHPAD);
GestureEventDetails pinch_end_details(ET_GESTURE_PINCH_END);
pinch_end_details.set_device_type(ui::GestureDeviceType::DEVICE_TOUCHPAD);
GestureEventDetails pinch_update_details(ET_GESTURE_PINCH_UPDATE);
pinch_update_details.set_device_type(ui::GestureDeviceType::DEVICE_TOUCHPAD);
pinch_update_details.set_scale(1.23f);
GestureEventDetails swipe_top_left_details(ET_GESTURE_SWIPE, -1, -1);
swipe_top_left_details.set_device_type(
ui::GestureDeviceType::DEVICE_TOUCHPAD);
GestureEventDetails swipe_right_details(ET_GESTURE_SWIPE, 1, 0);
swipe_right_details.set_device_type(ui::GestureDeviceType::DEVICE_TOUCHPAD);
const GestureEvent kTestData[] = {
{10, 20, EF_NONE, base::TimeTicks() + base::Microseconds(401),
GestureEventDetails(ET_SCROLL_FLING_START)},
{10, 20, EF_NONE, base::TimeTicks() + base::Microseconds(401),
GestureEventDetails(ET_GESTURE_TAP)},
{10, 20, EF_NONE, base::TimeTicks() + base::Microseconds(401),
pinch_begin_details},
{10, 20, EF_NONE, base::TimeTicks() + base::Microseconds(401),
pinch_end_details},
{10, 20, EF_NONE, base::TimeTicks() + base::Microseconds(401),
pinch_update_details},
{10, 20, EF_NONE, base::TimeTicks() + base::Microseconds(401),
swipe_top_left_details},
{10, 20, EF_NONE, base::TimeTicks() + base::Microseconds(401),
swipe_right_details},
};
for (size_t i = 0; i < std::size(kTestData); i++) {
std::unique_ptr<Event> expected_copy = kTestData[i].Clone();
std::unique_ptr<Event> output;
ASSERT_TRUE(mojo::test::SerializeAndDeserialize<mojom::Event>(expected_copy,
output));
ASSERT_TRUE(output->IsGestureEvent());
const GestureEvent* output_ptr_event = output->AsGestureEvent();
ExpectEventsEqual(kTestData[i], *output);
EXPECT_EQ(kTestData[i].details(), output_ptr_event->details());
EXPECT_EQ(kTestData[i].unique_touch_event_id(),
output_ptr_event->unique_touch_event_id());
}
}
TEST(StructTraitsTest, ScrollEvent) {
const ScrollEvent kTestData[] = {
{ET_SCROLL, gfx::Point(10, 20),
base::TimeTicks() + base::Microseconds(501), EF_NONE, 1, 2, 3, 4, 5,
EventMomentumPhase::NONE, ScrollEventPhase::kNone},
{ET_SCROLL, gfx::Point(10, 20),
base::TimeTicks() + base::Microseconds(501), EF_NONE, 1, 2, 3, 4, 5,
EventMomentumPhase::NONE, ScrollEventPhase::kUpdate},
{ET_SCROLL, gfx::Point(10, 20),
base::TimeTicks() + base::Microseconds(501), EF_NONE, 1, 2, 3, 4, 5,
EventMomentumPhase::NONE, ScrollEventPhase::kBegan},
{ET_SCROLL, gfx::Point(10, 20),
base::TimeTicks() + base::Microseconds(501), EF_NONE, 1, 2, 3, 4, 5,
EventMomentumPhase::NONE, ScrollEventPhase::kEnd},
{ET_SCROLL, gfx::Point(10, 20),
base::TimeTicks() + base::Microseconds(501), EF_NONE, 1, 2, 3, 4, 5,
EventMomentumPhase::BEGAN, ScrollEventPhase::kNone},
{ET_SCROLL, gfx::Point(10, 20),
base::TimeTicks() + base::Microseconds(501), EF_NONE, 1, 2, 3, 4, 5,
EventMomentumPhase::INERTIAL_UPDATE, ScrollEventPhase::kNone},
{ET_SCROLL, gfx::Point(10, 20),
base::TimeTicks() + base::Microseconds(501), EF_NONE, 1, 2, 3, 4, 5,
EventMomentumPhase::END, ScrollEventPhase::kNone},
{ET_SCROLL_FLING_START, gfx::Point(10, 20),
base::TimeTicks() + base::Microseconds(502), EF_NONE, 1, 2, 3, 4, 5,
EventMomentumPhase::MAY_BEGIN, ScrollEventPhase::kNone},
{ET_SCROLL_FLING_CANCEL, gfx::Point(10, 20),
base::TimeTicks() + base::Microseconds(502), EF_NONE, 1, 2, 3, 4, 5,
EventMomentumPhase::END, ScrollEventPhase::kNone},
};
for (size_t i = 0; i < std::size(kTestData); i++) {
std::unique_ptr<Event> expected_copy = kTestData[i].Clone();
std::unique_ptr<Event> output;
ASSERT_TRUE(mojo::test::SerializeAndDeserialize<mojom::Event>(expected_copy,
output));
EXPECT_TRUE(output->IsScrollEvent());
const ScrollEvent* output_ptr_event = output->AsScrollEvent();
ExpectEventsEqual(kTestData[i], *output_ptr_event);
EXPECT_EQ(kTestData[i].x_offset(), output_ptr_event->x_offset());
EXPECT_EQ(kTestData[i].y_offset(), output_ptr_event->y_offset());
EXPECT_EQ(kTestData[i].x_offset_ordinal(),
output_ptr_event->x_offset_ordinal());
EXPECT_EQ(kTestData[i].y_offset_ordinal(),
output_ptr_event->y_offset_ordinal());
EXPECT_EQ(kTestData[i].finger_count(), output_ptr_event->finger_count());
EXPECT_EQ(kTestData[i].momentum_phase(),
output_ptr_event->momentum_phase());
}
}
TEST(StructTraitsTest, PointerDetails) {
const PointerDetails kTestData[] = {
{EventPointerType::kUnknown, 1, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f},
{EventPointerType::kMouse, 1, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f},
{EventPointerType::kPen, 11, 12.f, 13.f, 14.f, 15.f, 16.f, 17.f},
{EventPointerType::kTouch, 1, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f},
{EventPointerType::kEraser, 21, 22.f, 23.f, 24.f, 25.f, 26.f, 27.f},
};
for (size_t i = 0; i < std::size(kTestData); i++) {
// Set |offset| as the constructor used above does not modify it.
PointerDetails input(kTestData[i]);
input.offset.set_x(i);
input.offset.set_y(i + 1);
PointerDetails output;
ASSERT_TRUE(mojo::test::SerializeAndDeserialize<mojom::PointerDetails>(
input, output));
EXPECT_EQ(input, output);
}
}
TEST(StructTraitsTest, TouchEvent) {
const TouchEvent kTestData[] = {
{ET_TOUCH_RELEASED,
{1, 2},
base::TimeTicks::Now(),
{EventPointerType::kUnknown, 1, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f},
EF_SHIFT_DOWN},
{ET_TOUCH_PRESSED, {1, 2}, base::TimeTicks::Now(), {}, EF_CONTROL_DOWN},
{ET_TOUCH_MOVED, {1, 2}, base::TimeTicks::Now(), {}, EF_NONE},
{ET_TOUCH_CANCELLED, {1, 2}, base::TimeTicks::Now(), {}, EF_NONE},
};
for (size_t i = 0; i < std::size(kTestData); i++) {
std::unique_ptr<Event> expected_copy = kTestData[i].Clone();
std::unique_ptr<Event> output;
ASSERT_TRUE(mojo::test::SerializeAndDeserialize<mojom::Event>(expected_copy,
output));
ExpectEventsEqual(*expected_copy, *output);
}
// Serialize/Deserialize with fields that can not be set from constructor.
std::unique_ptr<TouchEvent> touch_event =
std::make_unique<TouchEvent>(ET_TOUCH_CANCELLED, gfx::Point(),
base::TimeTicks::Now(), PointerDetails());
touch_event->set_may_cause_scrolling(true);
touch_event->set_hovering(true);
std::unique_ptr<Event> expected = std::move(touch_event);
std::unique_ptr<Event> output;
ASSERT_TRUE(
mojo::test::SerializeAndDeserialize<mojom::Event>(expected, output));
ExpectEventsEqual(*expected, *output);
}
TEST(StructTraitsTest, UnserializedTouchEventFields) {
std::unique_ptr<TouchEvent> touch_event =
std::make_unique<TouchEvent>(ET_TOUCH_CANCELLED, gfx::Point(),
base::TimeTicks::Now(), PointerDetails());
std::unique_ptr<Event> expected = std::move(touch_event);
std::unique_ptr<Event> output;
ASSERT_TRUE(
mojo::test::SerializeAndDeserialize<mojom::Event>(expected, output));
ExpectEventsEqual(*expected, *output);
EXPECT_NE(expected->AsTouchEvent()->unique_event_id(),
output->AsTouchEvent()->unique_event_id());
}
#if BUILDFLAG(IS_OZONE)
// Test KeyboardLayoutEngine implementation that always returns 'x'.
class FixedKeyboardLayoutEngine : public StubKeyboardLayoutEngine {
public:
FixedKeyboardLayoutEngine() = default;
FixedKeyboardLayoutEngine(const FixedKeyboardLayoutEngine&) = delete;
FixedKeyboardLayoutEngine& operator=(const FixedKeyboardLayoutEngine&) =
delete;
~FixedKeyboardLayoutEngine() override = default;
// StubKeyboardLayoutEngine:
bool Lookup(DomCode dom_code,
int flags,
DomKey* out_dom_key,
KeyboardCode* out_key_code) const override {
*out_dom_key = DomKey::FromCharacter('x');
*out_key_code = ui::VKEY_X;
return true;
}
};
TEST(StructTraitsTest, DifferentKeyboardLayout) {
// Verifies KeyEvent serialization is not impacted by a KeyboardLayoutEngine.
ScopedKeyboardLayoutEngine scoped_keyboard_layout_engine(
std::make_unique<FixedKeyboardLayoutEngine>());
std::unique_ptr<KeyEvent> key_event = std::make_unique<KeyEvent>(
ET_KEY_PRESSED, VKEY_S, DomCode::US_S, EF_NONE,
DomKey::FromCharacter('s'), base::TimeTicks::Now());
std::unique_ptr<Event> expected = std::move(key_event);
std::unique_ptr<Event> output;
ASSERT_TRUE(
mojo::test::SerializeAndDeserialize<mojom::Event>(expected, output));
ExpectEventsEqual(*expected, *output);
}
#endif
} // namespace ui