// Copyright 2012 The ChromiumOS Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include "include/gestures.h" #include "include/mouse_interpreter.h" #include "include/unittest_util.h" #include "include/util.h" namespace gestures { HardwareProperties make_hwprops_for_mouse( unsigned has_wheel, unsigned wheel_is_hi_res) { return { .right = 0, .bottom = 0, .res_x = 0, .res_y = 0, .orientation_minimum = 0, .orientation_maximum = 0, .max_finger_cnt = 0, .max_touch_cnt = 0, .supports_t5r2 = 0, .support_semi_mt = 0, .is_button_pad = 0, .has_wheel = has_wheel, .wheel_is_hi_res = wheel_is_hi_res, .is_haptic_pad = 0, }; } class MouseInterpreterTest : public ::testing::Test {}; TEST(MouseInterpreterTest, SimpleTest) { HardwareProperties hwprops = make_hwprops_for_mouse(1, 0); MouseInterpreter mi(nullptr, nullptr); TestInterpreterWrapper wrapper(&mi, &hwprops); Gesture* gs; HardwareState hwstates[] = { { 200000, 0, 0, 0, nullptr, 0, 0, 0, 0, 0, 0.0 }, { 210000, 0, 0, 0, nullptr, 9, -7, 0, 0, 0, 0.0 }, { 220000, 1, 0, 0, nullptr, 0, 0, 0, 0, 0, 0.0 }, { 230000, 0, 0, 0, nullptr, 0, 0, 0, 0, 0, 0.0 }, { 240000, 0, 0, 0, nullptr, 0, 0, -3, -360, 4, 0.0 }, }; mi.output_mouse_wheel_gestures_.val_ = true; gs = wrapper.SyncInterpret(hwstates[0], nullptr); EXPECT_EQ(nullptr, gs); gs = wrapper.SyncInterpret(hwstates[1], nullptr); ASSERT_NE(nullptr, gs); EXPECT_EQ(kGestureTypeMove, gs->type); EXPECT_EQ(9, gs->details.move.dx); EXPECT_EQ(-7, gs->details.move.dy); EXPECT_EQ(200000, gs->start_time); EXPECT_EQ(210000, gs->end_time); gs = wrapper.SyncInterpret(hwstates[2], nullptr); ASSERT_NE(nullptr, gs); EXPECT_EQ(kGestureTypeButtonsChange, gs->type); EXPECT_EQ(1, gs->details.buttons.down); EXPECT_EQ(0, gs->details.buttons.up); EXPECT_EQ(210000, gs->start_time); EXPECT_EQ(220000, gs->end_time); gs = wrapper.SyncInterpret(hwstates[3], nullptr); ASSERT_NE(nullptr, gs); EXPECT_EQ(kGestureTypeButtonsChange, gs->type); EXPECT_EQ(0, gs->details.buttons.down); EXPECT_EQ(1, gs->details.buttons.up); EXPECT_EQ(220000, gs->start_time); EXPECT_EQ(230000, gs->end_time); gs = wrapper.SyncInterpret(hwstates[4], nullptr); ASSERT_NE(nullptr, gs); EXPECT_EQ(kGestureTypeMouseWheel, gs->type); EXPECT_LT(-1, gs->details.wheel.dx); EXPECT_GT(1, gs->details.wheel.dy); EXPECT_EQ(240000, gs->start_time); EXPECT_EQ(240000, gs->end_time); } TEST(MouseInterpreterTest, HighResolutionVerticalScrollTest) { HardwareProperties hwprops = make_hwprops_for_mouse(1, 1); MouseInterpreter mi(nullptr, nullptr); TestInterpreterWrapper wrapper(&mi, &hwprops); Gesture* gs; HardwareState hwstates[] = { { 200000, 0, 0, 0, nullptr, 0, 0, 0, 0, 0, 0.0 }, { 210000, 0, 0, 0, nullptr, 0, 0, 0, -15, 0, 0.0 }, { 220000, 0, 0, 0, nullptr, 0, 0, -1, -15, 0, 0.0 }, { 230000, 0, 0, 0, nullptr, 0, 0, 0,-120, 0, 0.0 }, { 240000, 0, 0, 0, nullptr, 0, 0, -1, 0, 0, 0.0 }, }; mi.output_mouse_wheel_gestures_.val_ = true; mi.hi_res_scrolling_.val_ = 1; mi.scroll_velocity_buffer_size_.val_ = 1; gs = wrapper.SyncInterpret(hwstates[0], nullptr); EXPECT_EQ(nullptr, gs); gs = wrapper.SyncInterpret(hwstates[1], nullptr); ASSERT_NE(nullptr, gs); EXPECT_EQ(kGestureTypeMouseWheel, gs->type); EXPECT_EQ(0, gs->details.wheel.dx); float offset_of_8th_notch_scroll = gs->details.wheel.dy; EXPECT_LT(1, offset_of_8th_notch_scroll); gs = wrapper.SyncInterpret(hwstates[2], nullptr); ASSERT_NE(nullptr, gs); EXPECT_EQ(kGestureTypeMouseWheel, gs->type); EXPECT_EQ(0, gs->details.wheel.dx); // Having a low-res scroll event as well as the high-resolution one shouldn't // change the output value. EXPECT_NEAR(offset_of_8th_notch_scroll, gs->details.wheel.dy, 0.1); gs = wrapper.SyncInterpret(hwstates[3], nullptr); ASSERT_NE(nullptr, gs); EXPECT_EQ(kGestureTypeMouseWheel, gs->type); EXPECT_EQ(0, gs->details.wheel.dx); float offset_of_high_res_scroll = gs->details.wheel.dy; mi.hi_res_scrolling_.val_ = 0; gs = wrapper.SyncInterpret(hwstates[4], nullptr); ASSERT_NE(nullptr, gs); EXPECT_EQ(kGestureTypeMouseWheel, gs->type); EXPECT_EQ(0, gs->details.wheel.dx); // A high-res scroll should yield the same offset as a low-res one with // proper unit conversion. EXPECT_NEAR(offset_of_high_res_scroll, gs->details.wheel.dy, 0.1); } TEST(MouseInterpreterTest, ScrollAccelerationOnAndOffTest) { HardwareProperties hwprops = make_hwprops_for_mouse(1, 1); MouseInterpreter mi(nullptr, nullptr); TestInterpreterWrapper wrapper(&mi, &hwprops); Gesture* gs; HardwareState hwstates[] = { { 200000, 0, 0, 0, nullptr, 0, 0, 0, 0, 0, 0.0 }, { 210000, 0, 0, 0, nullptr, 0, 0, 5, 0, 0, 0.0 }, { 220000, 0, 0, 0, nullptr, 0, 0, 5, 0, 0, 0.0 }, { 230000, 0, 0, 0, nullptr, 0, 0, 10, 0, 0, 0.0 }, { 240000, 0, 0, 0, nullptr, 0, 0, 10, 0, 0, 0.0 }, }; // Scroll acceleration is on. mi.scroll_acceleration_.val_ = true; mi.output_mouse_wheel_gestures_.val_ = true; mi.hi_res_scrolling_.val_ = false; mi.scroll_velocity_buffer_size_.val_ = 1; gs = wrapper.SyncInterpret(hwstates[0], nullptr); EXPECT_EQ(nullptr, gs); gs = wrapper.SyncInterpret(hwstates[1], nullptr); ASSERT_NE(nullptr, gs); EXPECT_EQ(kGestureTypeMouseWheel, gs->type); EXPECT_NE(0, gs->details.scroll.dy); float offset_when_acceleration_on = gs->details.scroll.dy; gs = wrapper.SyncInterpret(hwstates[2], nullptr); ASSERT_NE(nullptr, gs); EXPECT_EQ(kGestureTypeMouseWheel, gs->type); EXPECT_NE(0, gs->details.scroll.dy); // When acceleration is on, the offset is related to scroll speed. Though // the wheel displacement are both 5, since the scroll speeds are different, // the offset are different. EXPECT_NE(offset_when_acceleration_on, gs->details.scroll.dy); // Turn scroll acceleration off. mi.scroll_acceleration_.val_ = false; gs = wrapper.SyncInterpret(hwstates[3], nullptr); ASSERT_NE(nullptr, gs); EXPECT_EQ(kGestureTypeMouseWheel, gs->type); EXPECT_NE(0, gs->details.scroll.dy); float offset_when_acceleration_off = gs->details.scroll.dy; gs = wrapper.SyncInterpret(hwstates[4], nullptr); ASSERT_NE(nullptr, gs); EXPECT_EQ(kGestureTypeMouseWheel, gs->type); EXPECT_NE(0, gs->details.scroll.dy); // When acceleration is off, the offset is not related to scroll speed. // Same wheel displacement yields to same offset. EXPECT_EQ(offset_when_acceleration_off, gs->details.scroll.dy); } TEST(MouseInterpreterTest, JankyScrollTest) { HardwareProperties hwprops = make_hwprops_for_mouse(1, 0); MouseInterpreter mi(nullptr, nullptr); TestInterpreterWrapper wrapper(&mi, &hwprops); Gesture* gs; // Because we do not allow time deltas less than 8ms when calculating scroll // acceleration, the last two scroll events should give the same dy // (timestamp is in units of seconds) HardwareState hwstates[] = { { 200000, 0, 0, 0, nullptr, 0, 0, -1, 0, 0, 0.0 }, { 200000.008, 0, 0, 0, nullptr, 0, 0, -1, 0, 0, 0.0 }, { 200000.0085, 0, 0, 0, nullptr, 0, 0, -1, 0, 0, 0.0 }, }; mi.output_mouse_wheel_gestures_.val_ = true; mi.scroll_velocity_buffer_size_.val_ = 1; gs = wrapper.SyncInterpret(hwstates[0], nullptr); ASSERT_NE(nullptr, gs); EXPECT_EQ(kGestureTypeMouseWheel, gs->type); EXPECT_EQ(0, gs->details.wheel.dx); // Ignore the dy from the first scroll event, as the gesture interpreter // hardcodes that time delta to 1 second, making it invalid for this test. gs = wrapper.SyncInterpret(hwstates[1], nullptr); ASSERT_NE(nullptr, gs); EXPECT_EQ(kGestureTypeMouseWheel, gs->type); EXPECT_EQ(0, gs->details.wheel.dx); float scroll_offset = gs->details.wheel.dy; gs = wrapper.SyncInterpret(hwstates[2], nullptr); ASSERT_NE(nullptr, gs); EXPECT_EQ(kGestureTypeMouseWheel, gs->type); EXPECT_EQ(0, gs->details.wheel.dx); EXPECT_NEAR(scroll_offset, gs->details.wheel.dy, 0.1); } TEST(MouseInterpreterTest, WheelTickReportingHighResTest) { HardwareProperties hwprops = make_hwprops_for_mouse(1, 1); MouseInterpreter mi(nullptr, nullptr); TestInterpreterWrapper wrapper(&mi, &hwprops); Gesture* gs; HardwareState hwstates[] = { { 200000, 0, 0, 0, nullptr, 0, 0, 0, 0, 0, 0.0 }, { 210000, 0, 0, 0, nullptr, 0, 0, 0, -30, 0, 0.0 }, }; mi.output_mouse_wheel_gestures_.val_ = true; mi.hi_res_scrolling_.val_ = true; gs = wrapper.SyncInterpret(hwstates[0], nullptr); EXPECT_EQ(nullptr, gs); gs = wrapper.SyncInterpret(hwstates[1], nullptr); ASSERT_NE(nullptr, gs); EXPECT_EQ(kGestureTypeMouseWheel, gs->type); EXPECT_EQ( 0, gs->details.wheel.tick_120ths_dx); EXPECT_EQ(30, gs->details.wheel.tick_120ths_dy); } TEST(MouseInterpreterTest, WheelTickReportingLowResTest) { HardwareProperties hwprops = make_hwprops_for_mouse(1, 0); MouseInterpreter mi(nullptr, nullptr); TestInterpreterWrapper wrapper(&mi, &hwprops); Gesture* gs; HardwareState hwstates[] = { { 200000, 0, 0, 0, nullptr, 0, 0, 0, 0, 0, 0.0 }, { 210000, 0, 0, 0, nullptr, 0, 0, 1, 0, 0, 0.0 }, { 210000, 0, 0, 0, nullptr, 0, 0, 0, 0, 1, 0.0 }, }; mi.output_mouse_wheel_gestures_.val_ = true; mi.hi_res_scrolling_.val_ = false; gs = wrapper.SyncInterpret(hwstates[0], nullptr); EXPECT_EQ(nullptr, gs); gs = wrapper.SyncInterpret(hwstates[1], nullptr); ASSERT_NE(nullptr, gs); EXPECT_EQ(kGestureTypeMouseWheel, gs->type); EXPECT_EQ( 0, gs->details.wheel.tick_120ths_dx); EXPECT_EQ(-120, gs->details.wheel.tick_120ths_dy); gs = wrapper.SyncInterpret(hwstates[2], nullptr); ASSERT_NE(nullptr, gs); EXPECT_EQ(kGestureTypeMouseWheel, gs->type); EXPECT_EQ(120, gs->details.wheel.tick_120ths_dx); EXPECT_EQ( 0, gs->details.wheel.tick_120ths_dy); } TEST(MouseInterpreterTest, EmulateScrollWheelTest) { HardwareProperties hwprops = make_hwprops_for_mouse(0, 0); MouseInterpreter mi(nullptr, nullptr); TestInterpreterWrapper wrapper(&mi, &hwprops); Gesture* gs; HardwareState hwstates[] = { { 200000, GESTURES_BUTTON_NONE, 0, 0, nullptr, 0, 0, 0, 0, 0, 0.0 }, { 210000, GESTURES_BUTTON_NONE, 0, 0, nullptr, 9, -7, 0, 0, 0, 0.0 }, { 220000, GESTURES_BUTTON_LEFT, 0, 0, nullptr, 0, 0, 0, 0, 0, 0.0 }, { 230000, GESTURES_BUTTON_LEFT + GESTURES_BUTTON_RIGHT, 0, 0, nullptr, 0, 0, 0, 0, 0, 0.0 }, { 240000, GESTURES_BUTTON_LEFT + GESTURES_BUTTON_RIGHT, 0, 0, nullptr, 2, 2, 0, 0, 0, 0.0 }, { 250000, GESTURES_BUTTON_NONE, 0, 0, nullptr, 0, 0, 0, 0, 0, 0.0 }, { 260000, GESTURES_BUTTON_NONE, 0, 0, nullptr, 9, -7, 0, 0, 0, 0.0 }, { 270000, GESTURES_BUTTON_MIDDLE, 0, 0, nullptr, 0, 0, 0, 0, 0, 0.0 }, { 280000, GESTURES_BUTTON_MIDDLE, 0, 0, nullptr, 0, 0, 0, 0, 0, 0.0 }, { 290000, GESTURES_BUTTON_NONE, 0, 0, nullptr, 0, 0, -3, -360, 4, 0.0 }, }; mi.output_mouse_wheel_gestures_.val_ = true; gs = wrapper.SyncInterpret(hwstates[0], nullptr); EXPECT_EQ(nullptr, gs); gs = wrapper.SyncInterpret(hwstates[1], nullptr); ASSERT_NE(nullptr, gs); EXPECT_EQ(kGestureTypeMove, gs->type); EXPECT_EQ(9, gs->details.move.dx); EXPECT_EQ(-7, gs->details.move.dy); EXPECT_EQ(200000, gs->start_time); EXPECT_EQ(210000, gs->end_time); gs = wrapper.SyncInterpret(hwstates[2], nullptr); ASSERT_NE(nullptr, gs); EXPECT_EQ(kGestureTypeButtonsChange, gs->type); EXPECT_EQ(1, gs->details.buttons.down); EXPECT_EQ(0, gs->details.buttons.up); EXPECT_EQ(210000, gs->start_time); EXPECT_EQ(220000, gs->end_time); gs = wrapper.SyncInterpret(hwstates[3], nullptr); ASSERT_EQ(nullptr, gs); // Temporarily adjust the threshold to force wheel_emulation_active_ auto thresh = mi.scroll_wheel_emulation_thresh_.val_; mi.scroll_wheel_emulation_thresh_.val_ = 0.1; EXPECT_FALSE(mi.wheel_emulation_active_); gs = wrapper.SyncInterpret(hwstates[4], nullptr); EXPECT_TRUE(mi.wheel_emulation_active_); ASSERT_NE(nullptr, gs); EXPECT_EQ(kGestureTypeScroll, gs->type); EXPECT_EQ(200, gs->details.scroll.dx); EXPECT_EQ(200, gs->details.scroll.dy); EXPECT_EQ(240000, gs->start_time); EXPECT_EQ(240000, gs->end_time); mi.scroll_wheel_emulation_thresh_.val_ = thresh; gs = wrapper.SyncInterpret(hwstates[5], nullptr); ASSERT_NE(nullptr, gs); EXPECT_EQ(kGestureTypeButtonsChange, gs->type); EXPECT_EQ(0, gs->details.buttons.down); EXPECT_EQ(5, gs->details.buttons.up); EXPECT_EQ(240000, gs->start_time); EXPECT_EQ(250000, gs->end_time); gs = wrapper.SyncInterpret(hwstates[6], nullptr); ASSERT_NE(nullptr, gs); EXPECT_EQ(kGestureTypeMove, gs->type); EXPECT_EQ(9, gs->details.move.dx); EXPECT_EQ(-7, gs->details.move.dy); EXPECT_EQ(250000, gs->start_time); EXPECT_EQ(260000, gs->end_time); gs = wrapper.SyncInterpret(hwstates[7], nullptr); ASSERT_EQ(nullptr, gs); gs = wrapper.SyncInterpret(hwstates[8], nullptr); ASSERT_EQ(nullptr, gs); gs = wrapper.SyncInterpret(hwstates[9], nullptr); ASSERT_NE(nullptr, gs); EXPECT_EQ(kGestureTypeButtonsChange, gs->type); EXPECT_EQ(0, gs->details.buttons.down); EXPECT_EQ(2, gs->details.buttons.up); EXPECT_EQ(280000, gs->start_time); EXPECT_EQ(290000, gs->end_time); } } // namespace gestures