// Copyright (c) 2012 The Chromium 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 "ui/events/event.h" #include #include #include #include #include #include "base/callback_helpers.h" #include "base/strings/strcat.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/simple_test_tick_clock.h" #include "base/test/task_environment.h" #include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/ui_base_features.h" #include "ui/events/event_constants.h" #include "ui/events/event_utils.h" #include "ui/events/keycodes/dom/dom_code.h" #include "ui/events/keycodes/dom/keycode_converter.h" #include "ui/events/keycodes/keyboard_code_conversion.h" #include "ui/events/test/events_test_utils.h" #include "ui/events/test/keyboard_layout.h" #include "ui/events/test/test_event_target.h" #include "ui/gfx/geometry/transform.h" #if BUILDFLAG(IS_WIN) #include "ui/events/win/events_win_utils.h" #endif namespace ui { TEST(EventTest, NoNativeEvent) { KeyEvent keyev(ET_KEY_PRESSED, VKEY_SPACE, EF_NONE); EXPECT_FALSE(keyev.HasNativeEvent()); } TEST(EventTest, NativeEvent) { #if BUILDFLAG(IS_WIN) CHROME_MSG native_event = {nullptr, WM_KEYUP, VKEY_A, 0}; KeyEvent keyev(native_event); EXPECT_TRUE(keyev.HasNativeEvent()); #endif } TEST(EventTest, GetCharacter) { ui::ScopedKeyboardLayout keyboard_layout(ui::KEYBOARD_LAYOUT_ENGLISH_US); // Check if Control+Enter returns 10. KeyEvent keyev1(ET_KEY_PRESSED, VKEY_RETURN, EF_CONTROL_DOWN); EXPECT_EQ(10, keyev1.GetCharacter()); // Check if Enter returns 13. KeyEvent keyev2(ET_KEY_PRESSED, VKEY_RETURN, EF_NONE); EXPECT_EQ(13, keyev2.GetCharacter()); // Check if expected Unicode character was returned for a key combination // contains Control. // e.g. Control+Shift+2 produces U+200C on "Persian" keyboard. // http://crbug.com/582453 KeyEvent keyev5(0x200C, VKEY_UNKNOWN, ui::DomCode::NONE, EF_CONTROL_DOWN | EF_SHIFT_DOWN); EXPECT_EQ(0x200C, keyev5.GetCharacter()); } TEST(EventTest, ClickCount) { const gfx::Point origin(0, 0); MouseEvent mouseev(ET_MOUSE_PRESSED, origin, origin, EventTimeForNow(), 0, 0); for (int i = 1; i <= 3; ++i) { mouseev.SetClickCount(i); EXPECT_EQ(i, mouseev.GetClickCount()); } } TEST(EventTest, RepeatedClick) { const gfx::Point origin(0, 0); MouseEvent event1(ET_MOUSE_PRESSED, origin, origin, EventTimeForNow(), 0, 0); MouseEvent event2(ET_MOUSE_PRESSED, origin, origin, EventTimeForNow(), 0, 0); LocatedEventTestApi test_event1(&event1); LocatedEventTestApi test_event2(&event2); base::TimeTicks start = base::TimeTicks::Now(); base::TimeTicks soon = start + base::Milliseconds(1); base::TimeTicks later = start + base::Milliseconds(1000); // Same time stamp (likely the same native event). test_event1.set_location(gfx::Point(0, 0)); test_event2.set_location(gfx::Point(1, 0)); test_event1.set_time_stamp(start); test_event2.set_time_stamp(start); EXPECT_FALSE(MouseEvent::IsRepeatedClickEvent(event1, event2)); MouseEvent mouse_ev3(event1); EXPECT_FALSE(MouseEvent::IsRepeatedClickEvent(event1, mouse_ev3)); // Close point. test_event1.set_location(gfx::Point(0, 0)); test_event2.set_location(gfx::Point(1, 0)); test_event1.set_time_stamp(start); test_event2.set_time_stamp(soon); EXPECT_TRUE(MouseEvent::IsRepeatedClickEvent(event1, event2)); // Too far. test_event1.set_location(gfx::Point(0, 0)); test_event2.set_location(gfx::Point(10, 0)); test_event1.set_time_stamp(start); test_event2.set_time_stamp(soon); EXPECT_FALSE(MouseEvent::IsRepeatedClickEvent(event1, event2)); // Too long a time between clicks. test_event1.set_location(gfx::Point(0, 0)); test_event2.set_location(gfx::Point(0, 0)); test_event1.set_time_stamp(start); test_event2.set_time_stamp(later); EXPECT_FALSE(MouseEvent::IsRepeatedClickEvent(event1, event2)); } // Automatic repeat flag setting is disabled on Lacros, // because the repeated event is generated inside ui/ozone/platform/wayland // and reliable. TEST(EventTest, RepeatedKeyEvent) { base::TimeTicks start = base::TimeTicks::Now(); base::TimeTicks time1 = start + base::Milliseconds(1); base::TimeTicks time2 = start + base::Milliseconds(2); base::TimeTicks time3 = start + base::Milliseconds(3); KeyEvent event1(ET_KEY_PRESSED, VKEY_A, 0, start); KeyEvent event2(ET_KEY_PRESSED, VKEY_A, 0, time1); KeyEvent event3(ET_KEY_PRESSED, VKEY_A, EF_LEFT_MOUSE_BUTTON, time2); KeyEvent event4(ET_KEY_PRESSED, VKEY_A, 0, time3); event1.InitializeNative(); EXPECT_EQ(event1.flags() & EF_IS_REPEAT, 0); event2.InitializeNative(); EXPECT_NE(event2.flags() & EF_IS_REPEAT, 0); event3.InitializeNative(); EXPECT_NE(event3.flags() & EF_IS_REPEAT, 0); event4.InitializeNative(); EXPECT_NE(event4.flags() & EF_IS_REPEAT, 0); } TEST(EventTest, NoRepeatedKeyEvent) { // Temporarily set the global synthesize_key_repeat_enabled to false. base::ScopedClosureRunner runner(base::BindOnce( [](bool old_value) { KeyEvent::SetSynthesizeKeyRepeatEnabled(old_value); }, KeyEvent::IsSynthesizeKeyRepeatEnabled())); KeyEvent::SetSynthesizeKeyRepeatEnabled(false); base::TimeTicks start = base::TimeTicks::Now(); base::TimeTicks time1 = start + base::Milliseconds(1); base::TimeTicks time2 = start + base::Milliseconds(2); base::TimeTicks time3 = start + base::Milliseconds(3); KeyEvent event1(ET_KEY_PRESSED, VKEY_A, 0, start); KeyEvent event2(ET_KEY_PRESSED, VKEY_A, 0, time1); KeyEvent event3(ET_KEY_PRESSED, VKEY_A, EF_LEFT_MOUSE_BUTTON, time2); KeyEvent event4(ET_KEY_PRESSED, VKEY_A, 0, time3); event1.InitializeNative(); EXPECT_EQ(event1.flags() & EF_IS_REPEAT, 0); event2.InitializeNative(); EXPECT_EQ(event2.flags() & EF_IS_REPEAT, 0); event3.InitializeNative(); EXPECT_EQ(event3.flags() & EF_IS_REPEAT, 0); event4.InitializeNative(); EXPECT_EQ(event4.flags() & EF_IS_REPEAT, 0); } // Tests that re-processing the same mouse press event (detected by timestamp) // does not yield a double click event: http://crbug.com/389162 TEST(EventTest, DoubleClickRequiresUniqueTimestamp) { const gfx::Point point(0, 0); base::TimeTicks time1 = base::TimeTicks::Now(); base::TimeTicks time2 = time1 + base::Milliseconds(1); // Re-processing the same press doesn't yield a double-click. MouseEvent event(ET_MOUSE_PRESSED, point, point, time1, 0, 0); EXPECT_EQ(1, MouseEvent::GetRepeatCount(event)); EXPECT_EQ(1, MouseEvent::GetRepeatCount(event)); // Processing a press with the same timestamp doesn't yield a double-click. event = MouseEvent(ET_MOUSE_PRESSED, point, point, time1, 0, 0); EXPECT_EQ(1, MouseEvent::GetRepeatCount(event)); // Processing a press with a later timestamp does yield a double-click. event = MouseEvent(ET_MOUSE_PRESSED, point, point, time2, 0, 0); EXPECT_EQ(2, MouseEvent::GetRepeatCount(event)); MouseEvent::ResetLastClickForTest(); // Test processing a double press and release sequence with one timestamp. event = MouseEvent(ET_MOUSE_PRESSED, point, point, time1, 0, 0); EXPECT_EQ(1, MouseEvent::GetRepeatCount(event)); event = MouseEvent(ET_MOUSE_RELEASED, point, point, time1, 0, 0); EXPECT_EQ(1, MouseEvent::GetRepeatCount(event)); event = MouseEvent(ET_MOUSE_PRESSED, point, point, time1, 0, 0); EXPECT_EQ(1, MouseEvent::GetRepeatCount(event)); event = MouseEvent(ET_MOUSE_RELEASED, point, point, time1, 0, 0); EXPECT_EQ(1, MouseEvent::GetRepeatCount(event)); MouseEvent::ResetLastClickForTest(); // Test processing a double press and release sequence with two timestamps. event = MouseEvent(ET_MOUSE_PRESSED, point, point, time1, 0, 0); EXPECT_EQ(1, MouseEvent::GetRepeatCount(event)); event = MouseEvent(ET_MOUSE_RELEASED, point, point, time1, 0, 0); EXPECT_EQ(1, MouseEvent::GetRepeatCount(event)); event = MouseEvent(ET_MOUSE_PRESSED, point, point, time2, 0, 0); EXPECT_EQ(2, MouseEvent::GetRepeatCount(event)); event = MouseEvent(ET_MOUSE_RELEASED, point, point, time2, 0, 0); EXPECT_EQ(2, MouseEvent::GetRepeatCount(event)); MouseEvent::ResetLastClickForTest(); } // Tests that right clicking, then left clicking does not yield double clicks. TEST(EventTest, SingleClickRightLeft) { const gfx::Point point(0, 0); base::TimeTicks time1 = base::TimeTicks::Now(); base::TimeTicks time2 = time1 + base::Milliseconds(1); base::TimeTicks time3 = time1 + base::Milliseconds(2); MouseEvent event(ET_MOUSE_PRESSED, point, point, time1, ui::EF_RIGHT_MOUSE_BUTTON, ui::EF_RIGHT_MOUSE_BUTTON); EXPECT_EQ(1, MouseEvent::GetRepeatCount(event)); event = MouseEvent(ET_MOUSE_PRESSED, point, point, time2, ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); EXPECT_EQ(1, MouseEvent::GetRepeatCount(event)); event = MouseEvent(ET_MOUSE_RELEASED, point, point, time2, ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); EXPECT_EQ(1, MouseEvent::GetRepeatCount(event)); event = MouseEvent(ET_MOUSE_PRESSED, point, point, time3, ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); EXPECT_EQ(2, MouseEvent::GetRepeatCount(event)); MouseEvent::ResetLastClickForTest(); } TEST(EventTest, KeyEvent) { ui::ScopedKeyboardLayout keyboard_layout(ui::KEYBOARD_LAYOUT_ENGLISH_US); static const struct { KeyboardCode key_code; int flags; uint16_t character; } kTestData[] = { {VKEY_A, 0, 'a'}, {VKEY_A, EF_SHIFT_DOWN, 'A'}, {VKEY_A, EF_CAPS_LOCK_ON, 'A'}, {VKEY_A, EF_SHIFT_DOWN | EF_CAPS_LOCK_ON, 'a'}, {VKEY_A, EF_CONTROL_DOWN, 0x01}, {VKEY_A, EF_SHIFT_DOWN | EF_CONTROL_DOWN, '\x01'}, {VKEY_Z, 0, 'z'}, {VKEY_Z, EF_SHIFT_DOWN, 'Z'}, {VKEY_Z, EF_CAPS_LOCK_ON, 'Z'}, {VKEY_Z, EF_SHIFT_DOWN | EF_CAPS_LOCK_ON, 'z'}, {VKEY_Z, EF_CONTROL_DOWN, '\x1A'}, {VKEY_Z, EF_SHIFT_DOWN | EF_CONTROL_DOWN, '\x1A'}, {VKEY_2, EF_CONTROL_DOWN, '\x12'}, {VKEY_2, EF_SHIFT_DOWN | EF_CONTROL_DOWN, '\0'}, {VKEY_6, EF_CONTROL_DOWN, '\x16'}, {VKEY_6, EF_SHIFT_DOWN | EF_CONTROL_DOWN, '\x1E'}, {VKEY_OEM_MINUS, EF_CONTROL_DOWN, '\x0D'}, {VKEY_OEM_MINUS, EF_SHIFT_DOWN | EF_CONTROL_DOWN, '\x1F'}, {VKEY_OEM_4, EF_CONTROL_DOWN, '\x1B'}, {VKEY_OEM_4, EF_SHIFT_DOWN | EF_CONTROL_DOWN, '\x1B'}, {VKEY_OEM_5, EF_CONTROL_DOWN, '\x1C'}, {VKEY_OEM_5, EF_SHIFT_DOWN | EF_CONTROL_DOWN, '\x1C'}, {VKEY_OEM_6, EF_CONTROL_DOWN, '\x1D'}, {VKEY_OEM_6, EF_SHIFT_DOWN | EF_CONTROL_DOWN, '\x1D'}, {VKEY_RETURN, EF_CONTROL_DOWN, '\x0A'}, {VKEY_0, 0, '0'}, {VKEY_0, EF_SHIFT_DOWN, ')'}, {VKEY_0, EF_SHIFT_DOWN | EF_CAPS_LOCK_ON, ')'}, {VKEY_0, EF_SHIFT_DOWN | EF_CONTROL_DOWN, '\x09'}, {VKEY_9, 0, '9'}, {VKEY_9, EF_SHIFT_DOWN, '('}, {VKEY_9, EF_SHIFT_DOWN | EF_CAPS_LOCK_ON, '('}, {VKEY_9, EF_SHIFT_DOWN | EF_CONTROL_DOWN, '\x08'}, {VKEY_NUMPAD0, EF_CONTROL_DOWN, '\x10'}, {VKEY_NUMPAD0, EF_SHIFT_DOWN, '0'}, {VKEY_NUMPAD9, EF_CONTROL_DOWN, '\x19'}, {VKEY_NUMPAD9, EF_SHIFT_DOWN, '9'}, {VKEY_TAB, EF_NONE, '\t'}, {VKEY_TAB, EF_CONTROL_DOWN, '\t'}, {VKEY_TAB, EF_SHIFT_DOWN, '\t'}, {VKEY_MULTIPLY, EF_CONTROL_DOWN, '\x0A'}, {VKEY_MULTIPLY, EF_SHIFT_DOWN, '*'}, {VKEY_ADD, EF_CONTROL_DOWN, '\x0B'}, {VKEY_ADD, EF_SHIFT_DOWN, '+'}, {VKEY_SUBTRACT, EF_CONTROL_DOWN, '\x0D'}, {VKEY_SUBTRACT, EF_SHIFT_DOWN, '-'}, {VKEY_DECIMAL, EF_CONTROL_DOWN, '\x0E'}, {VKEY_DECIMAL, EF_SHIFT_DOWN, '.'}, {VKEY_DIVIDE, EF_CONTROL_DOWN, '\x0F'}, {VKEY_DIVIDE, EF_SHIFT_DOWN, '/'}, {VKEY_OEM_1, EF_CONTROL_DOWN, '\x1B'}, {VKEY_OEM_1, EF_SHIFT_DOWN, ':'}, {VKEY_OEM_PLUS, EF_CONTROL_DOWN, '\x1D'}, {VKEY_OEM_PLUS, EF_SHIFT_DOWN, '+'}, {VKEY_OEM_COMMA, EF_CONTROL_DOWN, '\x0C'}, {VKEY_OEM_COMMA, EF_SHIFT_DOWN, '<'}, {VKEY_OEM_PERIOD, EF_CONTROL_DOWN, '\x0E'}, {VKEY_OEM_PERIOD, EF_SHIFT_DOWN, '>'}, {VKEY_OEM_3, EF_CONTROL_DOWN, '\x0'}, {VKEY_OEM_3, EF_SHIFT_DOWN, '~'}, }; for (size_t i = 0; i < std::size(kTestData); ++i) { KeyEvent key(ET_KEY_PRESSED, kTestData[i].key_code, kTestData[i].flags); EXPECT_EQ(kTestData[i].character, key.GetCharacter()) << " Index:" << i << " key_code:" << kTestData[i].key_code; } } TEST(EventTest, KeyEventDirectUnicode) { KeyEvent key(0x1234U, ui::VKEY_UNKNOWN, ui::DomCode::NONE, ui::EF_NONE); EXPECT_EQ(0x1234U, key.GetCharacter()); EXPECT_EQ(ET_KEY_PRESSED, key.type()); EXPECT_TRUE(key.is_char()); } TEST(EventTest, NormalizeKeyEventFlags) { // Do not normalize flags for synthesized events without // KeyEvent::NormalizeFlags called explicitly. { KeyEvent keyev(ET_KEY_PRESSED, VKEY_SHIFT, EF_SHIFT_DOWN); EXPECT_EQ(EF_SHIFT_DOWN, keyev.flags()); } { KeyEvent keyev(ET_KEY_RELEASED, VKEY_SHIFT, EF_SHIFT_DOWN); EXPECT_EQ(EF_SHIFT_DOWN, keyev.flags()); keyev.NormalizeFlags(); EXPECT_EQ(EF_NONE, keyev.flags()); } { KeyEvent keyev(ET_KEY_PRESSED, VKEY_CONTROL, EF_CONTROL_DOWN); EXPECT_EQ(EF_CONTROL_DOWN, keyev.flags()); } { KeyEvent keyev(ET_KEY_RELEASED, VKEY_CONTROL, EF_CONTROL_DOWN); EXPECT_EQ(EF_CONTROL_DOWN, keyev.flags()); keyev.NormalizeFlags(); EXPECT_EQ(EF_NONE, keyev.flags()); } { KeyEvent keyev(ET_KEY_PRESSED, VKEY_MENU, EF_ALT_DOWN); EXPECT_EQ(EF_ALT_DOWN, keyev.flags()); } { KeyEvent keyev(ET_KEY_RELEASED, VKEY_MENU, EF_ALT_DOWN); EXPECT_EQ(EF_ALT_DOWN, keyev.flags()); keyev.NormalizeFlags(); EXPECT_EQ(EF_NONE, keyev.flags()); } } TEST(EventTest, KeyEventCopy) { KeyEvent key(ET_KEY_PRESSED, VKEY_A, EF_NONE); std::unique_ptr copied_key(new KeyEvent(key)); EXPECT_EQ(copied_key->type(), key.type()); EXPECT_EQ(copied_key->key_code(), key.key_code()); } TEST(EventTest, KeyEventCode) { const DomCode kDomCodeForSpace = DomCode::SPACE; const char kCodeForSpace[] = "Space"; ASSERT_EQ(kDomCodeForSpace, ui::KeycodeConverter::CodeStringToDomCode(kCodeForSpace)); const int kNativeCodeSpace = ui::KeycodeConverter::DomCodeToNativeKeycode(kDomCodeForSpace); ASSERT_NE(ui::KeycodeConverter::InvalidNativeKeycode(), kNativeCodeSpace); ASSERT_EQ(kNativeCodeSpace, ui::KeycodeConverter::DomCodeToNativeKeycode(kDomCodeForSpace)); { KeyEvent key(ET_KEY_PRESSED, VKEY_SPACE, kDomCodeForSpace, EF_NONE); EXPECT_EQ(kCodeForSpace, key.GetCodeString()); } { // Regardless the KeyEvent.key_code (VKEY_RETURN), code should be // the specified value. KeyEvent key(ET_KEY_PRESSED, VKEY_RETURN, kDomCodeForSpace, EF_NONE); EXPECT_EQ(kCodeForSpace, key.GetCodeString()); } { // If the synthetic event is initialized without code, the code is // determined from the KeyboardCode assuming a US keyboard layout. KeyEvent key(ET_KEY_PRESSED, VKEY_SPACE, EF_NONE); EXPECT_EQ(kCodeForSpace, key.GetCodeString()); } #if BUILDFLAG(IS_WIN) { // Test a non extended key. ASSERT_EQ((kNativeCodeSpace & 0xFF), kNativeCodeSpace); const LPARAM lParam = GetLParamFromScanCode(kNativeCodeSpace); CHROME_MSG native_event = {nullptr, WM_KEYUP, VKEY_SPACE, lParam}; KeyEvent key(native_event); // KeyEvent converts from the native keycode (scan code) to the code. EXPECT_EQ(kCodeForSpace, key.GetCodeString()); } { const char kCodeForHome[] = "Home"; const uint16_t kNativeCodeHome = 0xe047; // 'Home' is an extended key with 0xe000 bits. ASSERT_NE((kNativeCodeHome & 0xFF), kNativeCodeHome); const LPARAM lParam = GetLParamFromScanCode(kNativeCodeHome); CHROME_MSG native_event = {nullptr, WM_KEYUP, VKEY_HOME, lParam}; KeyEvent key(native_event); // KeyEvent converts from the native keycode (scan code) to the code. EXPECT_EQ(kCodeForHome, key.GetCodeString()); } #endif // BUILDFLAG(IS_WIN) } TEST(EventTest, TouchEventRadiusDefaultsToOtherAxis) { const base::TimeTicks time = base::TimeTicks::Now(); const float non_zero_length1 = 30; const float non_zero_length2 = 46; TouchEvent event1(ui::ET_TOUCH_PRESSED, gfx::Point(0, 0), time, PointerDetails(ui::EventPointerType::kTouch, /* pointer_id*/ 0, /* radius_x */ non_zero_length1, /* radius_y */ 0.0f, /* force */ 0)); EXPECT_EQ(non_zero_length1, event1.pointer_details().radius_x); EXPECT_EQ(non_zero_length1, event1.pointer_details().radius_y); TouchEvent event2(ui::ET_TOUCH_PRESSED, gfx::Point(0, 0), time, PointerDetails(ui::EventPointerType::kTouch, /* pointer_id*/ 0, /* radius_x */ 0.0f, /* radius_y */ non_zero_length2, /* force */ 0)); EXPECT_EQ(non_zero_length2, event2.pointer_details().radius_x); EXPECT_EQ(non_zero_length2, event2.pointer_details().radius_y); } TEST(EventTest, TouchEventRotationAngleFixing) { const base::TimeTicks time = base::TimeTicks::Now(); const float radius_x = 20; const float radius_y = 10; { const float angle_in_range = 0; TouchEvent event(ui::ET_TOUCH_PRESSED, gfx::Point(0, 0), time, PointerDetails(ui::EventPointerType::kTouch, /* pointer_id*/ 0, radius_x, radius_y, /* force */ 0, angle_in_range), 0); EXPECT_FLOAT_EQ(angle_in_range, event.ComputeRotationAngle()); } { const float angle_in_range = 179.9f; TouchEvent event(ui::ET_TOUCH_PRESSED, gfx::Point(0, 0), time, PointerDetails(ui::EventPointerType::kTouch, /* pointer_id*/ 0, radius_x, radius_y, /* force */ 0, angle_in_range), 0); EXPECT_FLOAT_EQ(angle_in_range, event.ComputeRotationAngle()); } { const float angle_negative = -0.1f; TouchEvent event(ui::ET_TOUCH_PRESSED, gfx::Point(0, 0), time, PointerDetails(ui::EventPointerType::kTouch, /* pointer_id*/ 0, radius_x, radius_y, /* force */ 0, angle_negative), 0); EXPECT_FLOAT_EQ(180 - 0.1f, event.ComputeRotationAngle()); } { const float angle_negative = -200; TouchEvent event(ui::ET_TOUCH_PRESSED, gfx::Point(0, 0), time, PointerDetails(ui::EventPointerType::kTouch, /* pointer_id*/ 0, radius_x, radius_y, /* force */ 0, angle_negative), 0); EXPECT_FLOAT_EQ(360 - 200, event.ComputeRotationAngle()); } { const float angle_too_big = 180; TouchEvent event(ui::ET_TOUCH_PRESSED, gfx::Point(0, 0), time, PointerDetails(ui::EventPointerType::kTouch, /* pointer_id*/ 0, radius_x, radius_y, /* force */ 0, angle_too_big), 0); EXPECT_FLOAT_EQ(0, event.ComputeRotationAngle()); } { const float angle_too_big = 400; TouchEvent event(ui::ET_TOUCH_PRESSED, gfx::Point(0, 0), time, PointerDetails(ui::EventPointerType::kTouch, /* pointer_id*/ 0, radius_x, radius_y, /* force */ 0, angle_too_big), 0); EXPECT_FLOAT_EQ(400 - 360, event.ComputeRotationAngle()); } } TEST(EventTest, PointerDetailsTouch) { ui::TouchEvent touch_event_plain( ET_TOUCH_PRESSED, gfx::Point(0, 0), ui::EventTimeForNow(), PointerDetails(ui::EventPointerType::kTouch, 0)); EXPECT_EQ(EventPointerType::kTouch, touch_event_plain.pointer_details().pointer_type); EXPECT_EQ(0.0f, touch_event_plain.pointer_details().radius_x); EXPECT_EQ(0.0f, touch_event_plain.pointer_details().radius_y); EXPECT_TRUE(std::isnan(touch_event_plain.pointer_details().force)); EXPECT_EQ(0.0f, touch_event_plain.pointer_details().tilt_x); EXPECT_EQ(0.0f, touch_event_plain.pointer_details().tilt_y); ui::TouchEvent touch_event_with_details( ET_TOUCH_PRESSED, gfx::Point(0, 0), ui::EventTimeForNow(), PointerDetails(ui::EventPointerType::kTouch, /* pointer_id*/ 0, /* radius_x */ 10.0f, /* radius_y */ 5.0f, /* force */ 15.0f)); EXPECT_EQ(EventPointerType::kTouch, touch_event_with_details.pointer_details().pointer_type); EXPECT_EQ(10.0f, touch_event_with_details.pointer_details().radius_x); EXPECT_EQ(5.0f, touch_event_with_details.pointer_details().radius_y); EXPECT_EQ(15.0f, touch_event_with_details.pointer_details().force); EXPECT_EQ(0.0f, touch_event_with_details.pointer_details().tilt_x); EXPECT_EQ(0.0f, touch_event_with_details.pointer_details().tilt_y); ui::TouchEvent touch_event_copy(touch_event_with_details); EXPECT_EQ(touch_event_with_details.pointer_details(), touch_event_copy.pointer_details()); } TEST(EventTest, PointerDetailsMouse) { ui::MouseEvent mouse_event(ET_MOUSE_PRESSED, gfx::Point(0, 0), gfx::Point(0, 0), ui::EventTimeForNow(), 0, 0); EXPECT_EQ(EventPointerType::kMouse, mouse_event.pointer_details().pointer_type); EXPECT_EQ(0.0f, mouse_event.pointer_details().radius_x); EXPECT_EQ(0.0f, mouse_event.pointer_details().radius_y); EXPECT_TRUE(std::isnan(mouse_event.pointer_details().force)); EXPECT_EQ(0.0f, mouse_event.pointer_details().tilt_x); EXPECT_EQ(0.0f, mouse_event.pointer_details().tilt_y); ui::MouseEvent mouse_event_copy(mouse_event); EXPECT_EQ(mouse_event.pointer_details(), mouse_event_copy.pointer_details()); } TEST(EventTest, PointerDetailsStylus) { ui::PointerDetails pointer_details(EventPointerType::kPen, /* pointer_id*/ 0, /* radius_x */ 0.0f, /* radius_y */ 0.0f, /* force */ 21.0f, /* twist */ 196, /* tilt_x */ 45.0f, /* tilt_y */ -45.0f, /* tangential_pressure */ 0.7f); ui::MouseEvent stylus_event(ET_MOUSE_PRESSED, gfx::Point(0, 0), gfx::Point(0, 0), ui::EventTimeForNow(), 0, 0, pointer_details); EXPECT_EQ(EventPointerType::kPen, stylus_event.pointer_details().pointer_type); EXPECT_EQ(21.0f, stylus_event.pointer_details().force); EXPECT_EQ(45.0f, stylus_event.pointer_details().tilt_x); EXPECT_EQ(-45.0f, stylus_event.pointer_details().tilt_y); EXPECT_EQ(0.0f, stylus_event.pointer_details().radius_x); EXPECT_EQ(0.0f, stylus_event.pointer_details().radius_y); EXPECT_EQ(0.7f, stylus_event.pointer_details().tangential_pressure); EXPECT_EQ(196, stylus_event.pointer_details().twist); ui::MouseEvent stylus_event_copy(stylus_event); EXPECT_EQ(stylus_event.pointer_details(), stylus_event_copy.pointer_details()); } TEST(EventTest, PointerDetailsCustomTouch) { ui::TouchEvent touch_event(ET_TOUCH_PRESSED, gfx::Point(0, 0), ui::EventTimeForNow(), PointerDetails(ui::EventPointerType::kTouch, 0)); EXPECT_EQ(EventPointerType::kTouch, touch_event.pointer_details().pointer_type); EXPECT_EQ(0.0f, touch_event.pointer_details().radius_x); EXPECT_EQ(0.0f, touch_event.pointer_details().radius_y); EXPECT_TRUE(std::isnan(touch_event.pointer_details().force)); EXPECT_EQ(0.0f, touch_event.pointer_details().tilt_x); EXPECT_EQ(0.0f, touch_event.pointer_details().tilt_y); ui::PointerDetails pointer_details(EventPointerType::kPen, /* pointer_id*/ 0, /* radius_x */ 5.0f, /* radius_y */ 6.0f, /* force */ 21.0f, /* twist */ 196, /* tilt_x */ 45.0f, /* tilt_y */ -45.0f, /* tangential_pressure */ 0.7f); touch_event.SetPointerDetailsForTest(pointer_details); EXPECT_EQ(EventPointerType::kPen, touch_event.pointer_details().pointer_type); EXPECT_EQ(21.0f, touch_event.pointer_details().force); EXPECT_EQ(45.0f, touch_event.pointer_details().tilt_x); EXPECT_EQ(-45.0f, touch_event.pointer_details().tilt_y); EXPECT_EQ(5.0f, touch_event.pointer_details().radius_x); EXPECT_EQ(6.0f, touch_event.pointer_details().radius_y); EXPECT_EQ(0.7f, touch_event.pointer_details().tangential_pressure); EXPECT_EQ(196, touch_event.pointer_details().twist); ui::TouchEvent touch_event_copy(touch_event); EXPECT_EQ(touch_event.pointer_details(), touch_event_copy.pointer_details()); } TEST(EventTest, MouseEventLatencyUIComponentExists) { const gfx::Point origin(0, 0); MouseEvent mouseev(ET_MOUSE_PRESSED, origin, origin, EventTimeForNow(), 0, 0); EXPECT_TRUE(mouseev.latency()->FindLatency( ui::INPUT_EVENT_LATENCY_UI_COMPONENT, nullptr)); } TEST(EventTest, MouseWheelEventLatencyUIComponentExists) { const gfx::Point origin(0, 0); MouseWheelEvent mouseWheelev(gfx::Vector2d(), origin, origin, EventTimeForNow(), 0, 0); EXPECT_TRUE(mouseWheelev.latency()->FindLatency( ui::INPUT_EVENT_LATENCY_UI_COMPONENT, nullptr)); } TEST(EventTest, MouseWheelEventLinearTickCalculation) { const gfx::Point origin; MouseWheelEvent mouse_wheel_ev( gfx::Vector2d(-2 * MouseWheelEvent::kWheelDelta, MouseWheelEvent::kWheelDelta), origin, origin, EventTimeForNow(), 0, 0); EXPECT_EQ(mouse_wheel_ev.tick_120ths().x(), -240); EXPECT_EQ(mouse_wheel_ev.tick_120ths().y(), 120); } TEST(EventTest, OrdinalMotionConversion) { const gfx::Point origin(0, 0); const gfx::Vector2dF movement(2.67, 3.14); // Model conversion depends on the class having a specific static method. struct OrdinalMotionConversionModel { static void ConvertPointToTarget(const OrdinalMotionConversionModel*, const OrdinalMotionConversionModel*, gfx::Point*) { // Do nothing. } } src, dst; MouseEvent mouseev1(ET_MOUSE_PRESSED, origin, origin, EventTimeForNow(), 0, 0); MouseEvent::DispatcherApi(&mouseev1).set_movement(movement); EXPECT_EQ(mouseev1.movement(), movement); EXPECT_TRUE(mouseev1.flags() & EF_UNADJUSTED_MOUSE); MouseEvent mouseev2(mouseev1, &src, &dst); EXPECT_EQ(mouseev2.movement(), movement); EXPECT_TRUE(mouseev2.flags() & EF_UNADJUSTED_MOUSE); // Setting the flags in construction should override the model's. MouseEvent mouseev3(mouseev1, &src, &dst, EventType::ET_MOUSE_MOVED, /* flags */ 0); EXPECT_EQ(mouseev3.movement(), movement); EXPECT_FALSE(mouseev3.flags() & EF_UNADJUSTED_MOUSE); } // Checks that Event.Latency.OS2.MOUSE_WHEEL histogram is computed properly. TEST(EventTest, EventLatencyOSMouseWheelHistogram) { #if BUILDFLAG(IS_WIN) base::HistogramTester histogram_tester; CHROME_MSG event = {nullptr, WM_MOUSEWHEEL, 0, 0}; MouseWheelEvent mouseWheelEvent(event); histogram_tester.ExpectTotalCount("Event.Latency.OS2.MOUSE_WHEEL", 1); #endif } TEST(EventTest, UpdateForRootTransformation) { gfx::Transform identity_transform; const gfx::Point location(10, 10); const gfx::Point root_location(20, 20); const gfx::PointF f_location(10, 10); const gfx::PointF f_root_location(20, 20); // A mouse event that is untargeted should reset the root location when // transformed. Though the events start out with different locations and // root_locations, they should be equal afterwards. ui::MouseEvent untargeted(ET_MOUSE_PRESSED, location, root_location, EventTimeForNow(), 0, 0); untargeted.UpdateForRootTransform(identity_transform, identity_transform); EXPECT_EQ(location, untargeted.location()); EXPECT_EQ(location, untargeted.root_location()); ui::test::TestEventTarget target; // A touch event should behave the same way as others. { PointerDetails pointer_details(EventPointerType::kTouch, 0 /* pointer id */, 3, 4, 50, 0 /* twist */, 0, 0); ui::TouchEvent targeted(ET_TOUCH_PRESSED, f_location, f_root_location, EventTimeForNow(), pointer_details); targeted.UpdateForRootTransform(identity_transform, identity_transform); EXPECT_EQ(location, targeted.location()); EXPECT_EQ(location, targeted.root_location()); EXPECT_EQ(pointer_details, targeted.pointer_details()); } // A touch event should scale the same way as others. { // Targeted event with 2x and 3x scales. gfx::Transform transform2x; transform2x.Scale(2, 2); gfx::Transform transform3x; transform3x.Scale(3, 3); PointerDetails pointer_details(EventPointerType::kTouch, 0 /* pointer id */, 3, 4, 50, 0 /* twist */, 17.2 /* tilt_x */, -28.3 /* tilt_y */); ui::TouchEvent targeted(ET_TOUCH_PRESSED, f_location, f_root_location, EventTimeForNow(), pointer_details); targeted.UpdateForRootTransform(transform2x, transform3x); auto updated_location = ScalePoint(f_location, 2.0f); EXPECT_EQ(updated_location, targeted.location_f()); EXPECT_EQ(updated_location, targeted.root_location_f()); auto updated_pointer_details(pointer_details); updated_pointer_details.radius_x *= 2; updated_pointer_details.radius_y *= 2; EXPECT_EQ(updated_pointer_details, targeted.pointer_details()) << " orig: " << pointer_details.ToString() << " vs " << targeted.pointer_details().ToString(); } // A touch event should rotate appropriately. { // Rotate by 90 degrees, then scale by a half or 0.75 (depending on axis), // and then offset by 720/1080. Note that the offset should have no impact // on vectors, i.e. radius. // The scale happens after rotation, so x should be 0.75 * the y. gfx::Transform rotate90; rotate90.Rotate(90.0f); rotate90.Translate(gfx::Vector2dF(720.0f, 1080.0f)); rotate90.Scale(0.5, 0.75); gfx::Transform transform3x; transform3x.Scale(3, 3); PointerDetails pointer_details(EventPointerType::kTouch, 0 /* pointer id */, 3, 4, 50, 0 /* twist */, -17.4 /* tilt_x */, 31.2 /* tilt_y */); ui::TouchEvent targeted(ET_TOUCH_PRESSED, f_location, f_root_location, EventTimeForNow(), pointer_details); Event::DispatcherApi(&targeted).set_target(&target); targeted.UpdateForRootTransform(rotate90, transform3x); auto updated_pointer_details(pointer_details); updated_pointer_details.radius_x = pointer_details.radius_y * 0.75; updated_pointer_details.radius_y = pointer_details.radius_x * 0.5; updated_pointer_details.tilt_x = -31.2; updated_pointer_details.tilt_y = -17.4; EXPECT_EQ(updated_pointer_details, targeted.pointer_details()) << " orig: " << updated_pointer_details.ToString() << " vs " << targeted.pointer_details().ToString(); } // A mouse event that is targeted should not set the root location to the // local location. They start with different locations and should stay // unequal after a transform is applied. { ui::MouseEvent targeted(ET_MOUSE_PRESSED, location, root_location, EventTimeForNow(), 0, 0); Event::DispatcherApi(&targeted).set_target(&target); targeted.UpdateForRootTransform(identity_transform, identity_transform); EXPECT_EQ(location, targeted.location()); EXPECT_EQ(root_location, targeted.root_location()); } { // Targeted event with 2x and 3x scales. gfx::Transform transform2x; transform2x.Scale(2, 2); gfx::Transform transform3x; transform3x.Scale(3, 3); ui::MouseEvent targeted(ET_MOUSE_PRESSED, location, root_location, EventTimeForNow(), 0, 0); Event::DispatcherApi(&targeted).set_target(&target); targeted.UpdateForRootTransform(transform2x, transform3x); EXPECT_EQ(gfx::Point(30, 30), targeted.location()); EXPECT_EQ(gfx::Point(40, 40), targeted.root_location()); } } TEST(EventTest, OperatorEqual) { MouseEvent m1(ET_MOUSE_PRESSED, gfx::Point(1, 2), gfx::Point(2, 3), EventTimeForNow(), EF_LEFT_MOUSE_BUTTON, EF_RIGHT_MOUSE_BUTTON); base::flat_map> properties; properties["a"] = {1u}; m1.SetProperties(properties); EXPECT_EQ(properties, *(m1.properties())); MouseEvent m2(ET_MOUSE_RELEASED, gfx::Point(11, 21), gfx::Point(2, 2), EventTimeForNow(), EF_RIGHT_MOUSE_BUTTON, EF_LEFT_MOUSE_BUTTON); m2 = m1; ASSERT_TRUE(m2.properties()); EXPECT_EQ(properties, *(m2.properties())); } // Verifies that ToString() generates something and doesn't crash. The specific // format isn't important. TEST(EventTest, ToStringNotEmpty) { MouseEvent mouse_event(ET_MOUSE_PRESSED, gfx::Point(1, 2), gfx::Point(2, 3), EventTimeForNow(), EF_LEFT_MOUSE_BUTTON, EF_RIGHT_MOUSE_BUTTON); EXPECT_FALSE(mouse_event.ToString().empty()); ScrollEvent scroll_event(ET_SCROLL, gfx::Point(1, 2), EventTimeForNow(), EF_NONE, 1.f, 2.f, 3.f, 4.f, 1); EXPECT_FALSE(scroll_event.ToString().empty()); } #if BUILDFLAG(IS_WIN) namespace { const struct AltGraphEventTestCase { KeyboardCode key_code; KeyboardLayout layout; std::vector modifier_key_codes; int expected_flags; } kAltGraphEventTestCases[] = { // US English -> AltRight never behaves as AltGraph. {VKEY_C, KEYBOARD_LAYOUT_ENGLISH_US, {VKEY_RMENU, VKEY_LCONTROL, VKEY_MENU, VKEY_CONTROL}, EF_ALT_DOWN | EF_CONTROL_DOWN}, {VKEY_E, KEYBOARD_LAYOUT_ENGLISH_US, {VKEY_RMENU, VKEY_LCONTROL, VKEY_MENU, VKEY_CONTROL}, EF_ALT_DOWN | EF_CONTROL_DOWN}, // French -> Always expect AltGraph if VKEY_RMENU is pressed. {VKEY_C, KEYBOARD_LAYOUT_FRENCH, {VKEY_RMENU, VKEY_LCONTROL, VKEY_MENU, VKEY_CONTROL}, EF_ALTGR_DOWN}, {VKEY_E, KEYBOARD_LAYOUT_FRENCH, {VKEY_RMENU, VKEY_LCONTROL, VKEY_MENU, VKEY_CONTROL}, EF_ALTGR_DOWN}, // French -> Expect Control+Alt is AltGraph on AltGraph-shifted keys. {VKEY_C, KEYBOARD_LAYOUT_FRENCH, {VKEY_LMENU, VKEY_LCONTROL, VKEY_MENU, VKEY_CONTROL}, EF_ALT_DOWN | EF_CONTROL_DOWN}, {VKEY_E, KEYBOARD_LAYOUT_FRENCH, {VKEY_LMENU, VKEY_LCONTROL, VKEY_MENU, VKEY_CONTROL}, EF_ALTGR_DOWN}, }; class AltGraphEventTest : public testing::TestWithParam> { public: AltGraphEventTest() : msg_({nullptr, message_type(), static_cast(test_case().key_code)}) { // Save the current keyboard layout and state, to restore later. CHECK(GetKeyboardState(original_keyboard_state_)); original_keyboard_layout_ = GetKeyboardLayout(0); // Configure specified layout, and update keyboard state for specified // modifier keys. CHECK(ActivateKeyboardLayout(GetPlatformKeyboardLayout(test_case().layout), 0)); BYTE test_keyboard_state[256] = {}; for (const auto& key_code : test_case().modifier_key_codes) test_keyboard_state[key_code] = 0x80; CHECK(SetKeyboardState(test_keyboard_state)); } ~AltGraphEventTest() { // Restore the original keyboard layout & key states. CHECK(ActivateKeyboardLayout(original_keyboard_layout_, 0)); CHECK(SetKeyboardState(original_keyboard_state_)); } protected: UINT message_type() const { return std::get<0>(GetParam()); } const AltGraphEventTestCase& test_case() const { return std::get<1>(GetParam()); } const CHROME_MSG msg_; BYTE original_keyboard_state_[256] = {}; HKL original_keyboard_layout_ = nullptr; }; } // namespace TEST_P(AltGraphEventTest, KeyEventAltGraphModifer) { KeyEvent event(msg_); if (message_type() == WM_CHAR) { // By definition, if we receive a WM_CHAR message when Control and Alt are // pressed, it indicates AltGraph. EXPECT_EQ(event.flags() & (EF_CONTROL_DOWN | EF_ALT_DOWN | EF_ALTGR_DOWN), EF_ALTGR_DOWN); } else { EXPECT_EQ(event.flags() & (EF_CONTROL_DOWN | EF_ALT_DOWN | EF_ALTGR_DOWN), test_case().expected_flags); } } INSTANTIATE_TEST_SUITE_P( WM_KEY, AltGraphEventTest, ::testing::Combine(::testing::Values(WM_KEYDOWN, WM_KEYUP), ::testing::ValuesIn(kAltGraphEventTestCases))); INSTANTIATE_TEST_SUITE_P( WM_CHAR, AltGraphEventTest, ::testing::Combine(::testing::Values(WM_CHAR), ::testing::ValuesIn(kAltGraphEventTestCases))); // Tests for ComputeEventLatencyOS variants. class EventLatencyTest : public ::testing::Test { public: EventLatencyTest() { SetEventLatencyTickClockForTesting(&tick_clock_); } ~EventLatencyTest() override { SetEventLatencyTickClockForTesting(nullptr); } protected: void UpdateTickClock(DWORD timestamp) { tick_clock_.SetNowTicks(base::TimeTicks() + base::Milliseconds(timestamp)); } base::test::TaskEnvironment task_environment_{ base::test::TaskEnvironment::TimeSource::MOCK_TIME}; // |task_environment_| mocks the base::TimeTicks clock while |tick_clock_| // mocks ::GetTickCount. base::SimpleTestTickClock tick_clock_; }; TEST_F(EventLatencyTest, ComputeEventLatencyOSFromTickCount) { // Create events whose timestamps are very close to the max range of // ::GetTickCount. constexpr DWORD timestamp_msec = std::numeric_limits::max() - 10; constexpr TOUCHINPUT touch_input = { .dwTime = timestamp_msec, }; constexpr POINTER_INFO pointer_info = { .dwTime = timestamp_msec, .PerformanceCount = 0UL, }; // This test will create several events with the same timestamp, and change // the mocked result of ::GetTickCount for each measurement. This makes it // easier to test the edge case when the 32-bit ::GetTickCount overflows. // Measure the latency of an event that's processed not long after the OS // timestamp. UpdateTickClock(timestamp_msec + 5); { base::HistogramTester histogram_tester; ComputeEventLatencyOSFromTOUCHINPUT(ET_TOUCH_PRESSED, touch_input, base::TimeTicks::Now()); ComputeEventLatencyOSFromPOINTER_INFO(ET_TOUCH_PRESSED, pointer_info, base::TimeTicks::Now()); histogram_tester.ExpectUniqueTimeSample("Event.Latency.OS2.TOUCH_PRESSED", base::Milliseconds(5), 2); } // Simulate ::GetTickCount advancing 15 msec, which wraps around past 0. constexpr DWORD wrapped_timestamp_msec = timestamp_msec + 15; static_assert(wrapped_timestamp_msec == 4, "timestamp should have wrapped around"); UpdateTickClock(wrapped_timestamp_msec); { base::HistogramTester histogram_tester; ComputeEventLatencyOSFromTOUCHINPUT(ET_TOUCH_PRESSED, touch_input, base::TimeTicks::Now()); ComputeEventLatencyOSFromPOINTER_INFO(ET_TOUCH_PRESSED, pointer_info, base::TimeTicks::Now()); histogram_tester.ExpectUniqueTimeSample("Event.Latency.OS2.TOUCH_PRESSED", base::Milliseconds(15), 2); } // Simulate an event with a bogus timestamp. The delta should be recorded as // 0. UpdateTickClock(timestamp_msec - 1000); { base::HistogramTester histogram_tester; ComputeEventLatencyOSFromTOUCHINPUT(ET_TOUCH_PRESSED, touch_input, base::TimeTicks::Now()); ComputeEventLatencyOSFromPOINTER_INFO(ET_TOUCH_PRESSED, pointer_info, base::TimeTicks::Now()); histogram_tester.ExpectUniqueTimeSample("Event.Latency.OS2.TOUCH_PRESSED", base::TimeDelta(), 2); } } TEST_F(EventLatencyTest, ComputeEventLatencyOSFromPerformanceCounter) { // Make sure there's enough time before Now() to create an event that's // several minutes old. task_environment_.AdvanceClock(base::Minutes(5)); // Convert the current time to units directly compatible with the Performance // Counter. LARGE_INTEGER ticks_per_sec = {}; if (!::QueryPerformanceFrequency(&ticks_per_sec) || ticks_per_sec.QuadPart <= 0 || !base::TimeTicks::IsHighResolution()) { // Skip this test when the performance counter is unavailable or // unreliable. (It's unlikely, but possible, that IsHighResolution is false // even if the performance counter works - see InitializeNowFunctionPointer // in time_win.cc - so also skip the test in this case.) return; } const auto ticks_per_second = ticks_per_sec.QuadPart; UINT64 current_timestamp = base::TimeTicks::Now().since_origin().InSecondsF() * ticks_per_second; // Event created shortly before now. { const POINTER_INFO pointer_info = { .dwTime = 0U, .PerformanceCount = current_timestamp - ticks_per_second, }; base::HistogramTester histogram_tester; ComputeEventLatencyOSFromPOINTER_INFO(ET_TOUCH_PRESSED, pointer_info, base::TimeTicks::Now()); histogram_tester.ExpectUniqueTimeSample("Event.Latency.OS2.TOUCH_PRESSED", base::Seconds(1), 1); } // Event created several minutes before now (IsValidTimebase should return // false). The delta should be recorded as 0. { const POINTER_INFO pointer_info = { .dwTime = 0U, .PerformanceCount = current_timestamp - 5 * 60 * ticks_per_second, }; base::HistogramTester histogram_tester; ComputeEventLatencyOSFromPOINTER_INFO(ET_TOUCH_PRESSED, pointer_info, base::TimeTicks::Now()); histogram_tester.ExpectUniqueTimeSample("Event.Latency.OS2.TOUCH_PRESSED", base::TimeDelta(), 1); } // Event created in the future (IsValidTimebase should return false). The // delta should be recorded as 0. { const POINTER_INFO pointer_info = { .dwTime = 0U, .PerformanceCount = current_timestamp + ticks_per_second, }; base::HistogramTester histogram_tester; ComputeEventLatencyOSFromPOINTER_INFO(ET_TOUCH_PRESSED, pointer_info, base::TimeTicks::Now()); histogram_tester.ExpectUniqueTimeSample("Event.Latency.OS2.TOUCH_PRESSED", base::TimeDelta(), 1); } // Invalid event with no timestamp. { const POINTER_INFO pointer_info = { .dwTime = 0U, .PerformanceCount = 0UL, }; base::HistogramTester histogram_tester; ComputeEventLatencyOSFromPOINTER_INFO(ET_TOUCH_PRESSED, pointer_info, base::TimeTicks::Now()); histogram_tester.ExpectTotalCount("Event.Latency.OS2.TOUCH_PRESSED", 0); } // Invalid event with 2 timestamps should take the higher-precision one. { const DWORD now_msec = 1000; UpdateTickClock(now_msec); const POINTER_INFO pointer_info = { // 10 milliseconds ago. .dwTime = now_msec - 10, // 1 second ago. .PerformanceCount = current_timestamp - ticks_per_second, }; base::HistogramTester histogram_tester; ComputeEventLatencyOSFromPOINTER_INFO(ET_TOUCH_PRESSED, pointer_info, base::TimeTicks::Now()); histogram_tester.ExpectUniqueTimeSample("Event.Latency.OS2.TOUCH_PRESSED", base::Seconds(1), 1); } } #endif // BUILDFLAG(IS_WIN) // Verifies that copied events never copy target_. TEST(EventTest, NeverCopyTarget) { const gfx::Point location(10, 10); const gfx::Point root_location(20, 20); ui::test::TestEventTarget target; ui::MouseEvent targeted(ET_MOUSE_PRESSED, location, root_location, EventTimeForNow(), 0, 0); Event::DispatcherApi(&targeted).set_target(&target); ui::MouseEvent targeted_copy1(targeted); EXPECT_EQ(nullptr, targeted_copy1.target()); ui::MouseEvent targeted_copy2 = targeted; EXPECT_EQ(nullptr, targeted_copy2.target()); } } // namespace ui