Index: remoting/host/touch_injector_win_unittest.cc |
diff --git a/remoting/host/touch_injector_win_unittest.cc b/remoting/host/touch_injector_win_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..791b95fcca045670d65c26f497d956167df6f0a7 |
--- /dev/null |
+++ b/remoting/host/touch_injector_win_unittest.cc |
@@ -0,0 +1,514 @@ |
+// Copyright 2015 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 "remoting/host/touch_injector_win.h" |
+ |
+#include <map> |
+ |
+#include "base/stl_util.h" |
+#include "remoting/proto/event.pb.h" |
+#include "testing/gmock/include/gmock/gmock.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+using ::testing::_; |
+using ::testing::AtLeast; |
+using ::testing::InSequence; |
+using ::testing::ExpectationSet; |
+using ::testing::Return; |
+ |
+namespace remoting { |
+ |
+using protocol::TouchEvent; |
+using protocol::TouchEventPoint; |
+ |
+namespace { |
+ |
+// Maps touch pointer ID to expected flags [start, move, end, cancel] listed |
+// below. |
+typedef std::map<uint32_t, uint32_t> IdFlagMap; |
+ |
+const uint32_t kStartFlag = |
+ POINTER_FLAG_INRANGE | POINTER_FLAG_INCONTACT | POINTER_FLAG_DOWN; |
+ |
+const uint32_t kMoveFlag = |
+ POINTER_FLAG_INRANGE | POINTER_FLAG_INCONTACT | POINTER_FLAG_UPDATE; |
+ |
+const uint32_t kEndFlag = POINTER_FLAG_UP; |
+ |
+const uint32_t kCancelFlag = POINTER_FLAG_UP | POINTER_FLAG_CANCELED; |
+ |
+MATCHER_P(EqualsSinglePointerTouchInfo, expected, "") { |
+ return arg->touchMask == expected.touchMask && |
+ arg->rcContact.left == expected.rcContact.left && |
+ arg->rcContact.top == expected.rcContact.top && |
+ arg->rcContact.right == expected.rcContact.right && |
+ arg->rcContact.bottom == expected.rcContact.bottom && |
+ arg->orientation == expected.orientation && |
+ arg->pressure == expected.pressure && |
+ arg->pointerInfo.pointerType == expected.pointerInfo.pointerType && |
+ arg->pointerInfo.pointerId == expected.pointerInfo.pointerId && |
+ arg->pointerInfo.ptPixelLocation.x == |
+ expected.pointerInfo.ptPixelLocation.x && |
+ arg->pointerInfo.ptPixelLocation.y == |
+ expected.pointerInfo.ptPixelLocation.y; |
+} |
+ |
+// Make sure that every touch point has the right flag (pointerFlags). |
+MATCHER_P(EqualsPointerTouchInfoFlag, id_to_flag_map, "") { |
+ for (size_t i = 0; i < id_to_flag_map.size(); ++i) { |
+ const POINTER_TOUCH_INFO* touch_info = arg + i; |
+ const uint32_t id = touch_info->pointerInfo.pointerId; |
+ if (!ContainsKey(id_to_flag_map, id)) |
+ return false; |
+ |
+ if (id_to_flag_map.find(id)->second != touch_info->pointerInfo.pointerFlags) |
+ return false; |
+ } |
+ return true; |
+} |
+ |
+class TouchInjectorWinDelegateMock : public TouchInjectorWinDelegate { |
+ public: |
+ TouchInjectorWinDelegateMock() |
+ : TouchInjectorWinDelegate(nullptr, nullptr, nullptr) {} |
+ ~TouchInjectorWinDelegateMock() override {}; |
+ |
+ MOCK_METHOD2(InitializeTouchInjection, BOOL(UINT32 max_count, DWORD dw_mode)); |
+ MOCK_METHOD2(InjectTouchInput, |
+ DWORD(UINT32 count, const POINTER_TOUCH_INFO* contacts)); |
+}; |
+ |
+} // namespace |
+ |
+// A test to make sure that the touch event is converted correctly to |
+// POINTER_TOUCH_INFO. |
+TEST(TouchInjectorWinTest, CheckConversionWithPressure) { |
+ scoped_ptr<TouchInjectorWinDelegateMock> delegate_mock( |
+ new ::testing::StrictMock<TouchInjectorWinDelegateMock>()); |
+ |
+ TouchEvent event; |
+ event.set_event_type(TouchEvent::TOUCH_POINT_START); |
+ TouchEventPoint* point = event.add_touch_points(); |
+ point->set_id(1234u); |
+ point->set_x(321.0f); |
+ point->set_y(123.0f); |
+ point->set_radius_x(10.0f); |
+ point->set_radius_y(20.0f); |
+ point->set_pressure(0.5f); |
+ point->set_angle(45.0f); |
+ |
+ POINTER_TOUCH_INFO expected_touch_info; |
+ expected_touch_info.touchMask = |
+ TOUCH_MASK_CONTACTAREA | TOUCH_MASK_ORIENTATION | TOUCH_MASK_PRESSURE; |
+ expected_touch_info.rcContact.left = 311; |
+ expected_touch_info.rcContact.top = 103; |
+ expected_touch_info.rcContact.right = 331; |
+ expected_touch_info.rcContact.bottom = 143; |
+ expected_touch_info.orientation = 0; |
+ expected_touch_info.pressure = 512; |
+ expected_touch_info.orientation = 45; |
+ |
+ expected_touch_info.pointerInfo.pointerType = PT_TOUCH; |
+ expected_touch_info.pointerInfo.pointerId = 1234u; |
+ expected_touch_info.pointerInfo.ptPixelLocation.x = 321; |
+ expected_touch_info.pointerInfo.ptPixelLocation.y = 123; |
+ |
+ InSequence s; |
+ EXPECT_CALL(*delegate_mock, InitializeTouchInjection(_, _)) |
+ .WillOnce(Return(1)); |
+ EXPECT_CALL( |
+ *delegate_mock, |
+ InjectTouchInput(1, EqualsSinglePointerTouchInfo(expected_touch_info))) |
+ .WillOnce(Return(1)); |
+ |
+ // Check pressure clamping as well. |
+ expected_touch_info.pressure = 1024; // Max |
+ EXPECT_CALL( |
+ *delegate_mock, |
+ InjectTouchInput(1, EqualsSinglePointerTouchInfo(expected_touch_info))) |
+ .WillOnce(Return(1)); |
+ |
+ expected_touch_info.pressure = 0; // Min |
+ EXPECT_CALL( |
+ *delegate_mock, |
+ InjectTouchInput(1, EqualsSinglePointerTouchInfo(expected_touch_info))) |
+ .WillOnce(Return(1)); |
+ |
+ TouchInjectorWin injector; |
+ injector.SetInjectorDelegateForTest(delegate_mock.Pass()); |
+ EXPECT_TRUE(injector.Init()); |
+ injector.InjectTouchEvent(event); |
+ |
+ // Change to MOVE so that there still only one point. |
+ event.set_event_type(TouchEvent::TOUCH_POINT_MOVE); |
+ point->set_pressure(2.0f); |
+ injector.InjectTouchEvent(event); |
+ |
+ point->set_pressure(-3.0f); |
+ injector.InjectTouchEvent(event); |
+} |
+ |
+// Some devices don't detect pressure. This test is a conversion check for |
+// such devices. |
+TEST(TouchInjectorWinTest, CheckConversionNoPressure) { |
+ scoped_ptr<TouchInjectorWinDelegateMock> delegate_mock( |
+ new ::testing::StrictMock<TouchInjectorWinDelegateMock>()); |
+ |
+ TouchEvent event; |
+ event.set_event_type(TouchEvent::TOUCH_POINT_START); |
+ TouchEventPoint* point = event.add_touch_points(); |
+ point->set_id(1234u); |
+ point->set_x(321.0f); |
+ point->set_y(123.0f); |
+ point->set_radius_x(10.0f); |
+ point->set_radius_y(20.0f); |
+ point->set_angle(45.0f); |
+ |
+ POINTER_TOUCH_INFO expected_touch_info; |
+ expected_touch_info.touchMask = |
+ TOUCH_MASK_CONTACTAREA | TOUCH_MASK_ORIENTATION; |
+ expected_touch_info.rcContact.left = 311; |
+ expected_touch_info.rcContact.top = 103; |
+ expected_touch_info.rcContact.right = 331; |
+ expected_touch_info.rcContact.bottom = 143; |
+ expected_touch_info.orientation = 0; |
+ expected_touch_info.pressure = 0; |
+ expected_touch_info.orientation = 45; |
+ |
+ expected_touch_info.pointerInfo.pointerType = PT_TOUCH; |
+ expected_touch_info.pointerInfo.pointerId = 1234u; |
+ expected_touch_info.pointerInfo.ptPixelLocation.x = 321; |
+ expected_touch_info.pointerInfo.ptPixelLocation.y = 123; |
+ |
+ InSequence s; |
+ EXPECT_CALL(*delegate_mock, InitializeTouchInjection(_, _)) |
+ .WillOnce(Return(1)); |
+ EXPECT_CALL( |
+ *delegate_mock, |
+ InjectTouchInput(1, EqualsSinglePointerTouchInfo(expected_touch_info))) |
+ .WillOnce(Return(1)); |
+ |
+ TouchInjectorWin injector; |
+ injector.SetInjectorDelegateForTest(delegate_mock.Pass()); |
+ EXPECT_TRUE(injector.Init()); |
+ injector.InjectTouchEvent(event); |
+} |
+ |
+// If initialization fails, it should not call any touch injection functions. |
+TEST(TouchInjectorWinTest, InitFailed) { |
+ scoped_ptr<TouchInjectorWinDelegateMock> delegate_mock( |
+ new ::testing::StrictMock<TouchInjectorWinDelegateMock>()); |
+ |
+ TouchEvent event; |
+ event.set_event_type(TouchEvent::TOUCH_POINT_START); |
+ |
+ InSequence s; |
+ EXPECT_CALL(*delegate_mock, InitializeTouchInjection(_, _)) |
+ .WillOnce(Return(0)); |
+ EXPECT_CALL(*delegate_mock, InjectTouchInput(_, _)).Times(0); |
+ |
+ TouchInjectorWin injector; |
+ injector.SetInjectorDelegateForTest(delegate_mock.Pass()); |
+ EXPECT_FALSE(injector.Init()); |
+ injector.InjectTouchEvent(event); |
+} |
+ |
+// Deinitialize and initialize should clean the state. |
+TEST(TouchInjectorWinTest, Reinitialize) { |
+ scoped_ptr<TouchInjectorWinDelegateMock> delegate_mock_before_deinitialize( |
+ new ::testing::StrictMock<TouchInjectorWinDelegateMock>()); |
+ scoped_ptr<TouchInjectorWinDelegateMock> delegate_mock_after_deinitialize( |
+ new ::testing::StrictMock<TouchInjectorWinDelegateMock>()); |
+ |
+ TouchEvent first_event; |
+ first_event.set_event_type(TouchEvent::TOUCH_POINT_START); |
+ TouchEventPoint* point0 = first_event.add_touch_points(); |
+ point0->set_id(0u); |
+ |
+ TouchEvent second_event; |
+ second_event.set_event_type(TouchEvent::TOUCH_POINT_START); |
+ TouchEventPoint* point1 = second_event.add_touch_points(); |
+ point1->set_id(1u); |
+ |
+ InSequence s; |
+ EXPECT_CALL(*delegate_mock_before_deinitialize, |
+ InitializeTouchInjection(_, _)).WillOnce(Return(1)); |
+ |
+ IdFlagMap id_to_flags; |
+ id_to_flags[0u] = kStartFlag; |
+ EXPECT_CALL( |
+ *delegate_mock_before_deinitialize, |
+ InjectTouchInput(1, EqualsPointerTouchInfoFlag(id_to_flags))) |
+ .WillOnce(Return(1)); |
+ |
+ EXPECT_CALL(*delegate_mock_after_deinitialize, |
+ InitializeTouchInjection(_, _)).WillOnce(Return(1)); |
+ |
+ // After deinitializing and then initializing, previous touch points should be |
+ // gone. |
+ id_to_flags.clear(); |
+ id_to_flags[1u] = kStartFlag; |
+ EXPECT_CALL( |
+ *delegate_mock_after_deinitialize, |
+ InjectTouchInput(1, EqualsPointerTouchInfoFlag(id_to_flags))) |
+ .WillOnce(Return(1)); |
+ |
+ TouchInjectorWin injector; |
+ injector.SetInjectorDelegateForTest(delegate_mock_before_deinitialize.Pass()); |
+ |
+ EXPECT_TRUE(injector.Init()); |
+ injector.InjectTouchEvent(first_event); |
+ injector.Deinitialize(); |
+ |
+ injector.SetInjectorDelegateForTest(delegate_mock_after_deinitialize.Pass()); |
+ EXPECT_TRUE(injector.Init()); |
+ injector.InjectTouchEvent(second_event); |
+} |
+ |
+// Make sure that the flag is set to kStartFlag. |
+TEST(TouchInjectorWinTest, StartTouchPoint) { |
+ scoped_ptr<TouchInjectorWinDelegateMock> delegate_mock( |
+ new ::testing::StrictMock<TouchInjectorWinDelegateMock>()); |
+ |
+ TouchEvent event; |
+ event.set_event_type(TouchEvent::TOUCH_POINT_START); |
+ TouchEventPoint* point = event.add_touch_points(); |
+ point->set_id(0u); |
+ |
+ InSequence s; |
+ EXPECT_CALL(*delegate_mock, InitializeTouchInjection(_, _)) |
+ .WillOnce(Return(1)); |
+ |
+ IdFlagMap id_to_flags; |
+ id_to_flags[0u] = kStartFlag; |
+ EXPECT_CALL( |
+ *delegate_mock, |
+ InjectTouchInput(1, EqualsPointerTouchInfoFlag(id_to_flags))) |
+ .WillOnce(Return(1)); |
+ |
+ TouchInjectorWin injector; |
+ injector.SetInjectorDelegateForTest(delegate_mock.Pass()); |
+ EXPECT_TRUE(injector.Init()); |
+ injector.InjectTouchEvent(event); |
+} |
+ |
+// Start a point and then move, make sure the flag is set to kMoveFlag. |
+TEST(TouchInjectorWinTest, MoveTouchPoint) { |
+ scoped_ptr<TouchInjectorWinDelegateMock> delegate_mock( |
+ new ::testing::StrictMock<TouchInjectorWinDelegateMock>()); |
+ |
+ TouchEvent event; |
+ event.set_event_type(TouchEvent::TOUCH_POINT_START); |
+ TouchEventPoint* point = event.add_touch_points(); |
+ point->set_id(0u); |
+ |
+ |
+ InSequence s; |
+ EXPECT_CALL(*delegate_mock, InitializeTouchInjection(_, _)) |
+ .WillOnce(Return(1)); |
+ |
+ IdFlagMap id_to_flags; |
+ id_to_flags[0u] = kStartFlag; |
+ EXPECT_CALL( |
+ *delegate_mock, |
+ InjectTouchInput(1, EqualsPointerTouchInfoFlag(id_to_flags))) |
+ .WillOnce(Return(1)); |
+ |
+ id_to_flags[0u] = kMoveFlag; |
+ EXPECT_CALL( |
+ *delegate_mock, |
+ InjectTouchInput(1, EqualsPointerTouchInfoFlag(id_to_flags))) |
+ .WillOnce(Return(1)); |
+ |
+ TouchInjectorWin injector; |
+ injector.SetInjectorDelegateForTest(delegate_mock.Pass()); |
+ EXPECT_TRUE(injector.Init()); |
+ injector.InjectTouchEvent(event); |
+ event.set_event_type(TouchEvent::TOUCH_POINT_MOVE); |
+ injector.InjectTouchEvent(event); |
+} |
+ |
+// Start a point and then move, make sure the flag is set to kEndFlag. |
+TEST(TouchInjectorWinTest, EndTouchPoint) { |
+ scoped_ptr<TouchInjectorWinDelegateMock> delegate_mock( |
+ new ::testing::StrictMock<TouchInjectorWinDelegateMock>()); |
+ |
+ TouchEvent event; |
+ event.set_event_type(TouchEvent::TOUCH_POINT_START); |
+ TouchEventPoint* point = event.add_touch_points(); |
+ point->set_id(0u); |
+ |
+ InSequence s; |
+ EXPECT_CALL(*delegate_mock, InitializeTouchInjection(_, _)) |
+ .WillOnce(Return(1)); |
+ |
+ IdFlagMap id_to_flags; |
+ id_to_flags[0u] = kStartFlag; |
+ EXPECT_CALL( |
+ *delegate_mock, |
+ InjectTouchInput(1, EqualsPointerTouchInfoFlag(id_to_flags))) |
+ .WillOnce(Return(1)); |
+ |
+ id_to_flags[0u] = kEndFlag; |
+ EXPECT_CALL( |
+ *delegate_mock, |
+ InjectTouchInput(1, EqualsPointerTouchInfoFlag(id_to_flags))) |
+ .WillOnce(Return(1)); |
+ |
+ TouchInjectorWin injector; |
+ injector.SetInjectorDelegateForTest(delegate_mock.Pass()); |
+ EXPECT_TRUE(injector.Init()); |
+ injector.InjectTouchEvent(event); |
+ event.set_event_type(TouchEvent::TOUCH_POINT_END); |
+ injector.InjectTouchEvent(event); |
+} |
+ |
+// Start a point and then move, make sure the flag is set to kCancelFlag. |
+TEST(TouchInjectorWinTest, CancelTouchPoint) { |
+ scoped_ptr<TouchInjectorWinDelegateMock> delegate_mock( |
+ new ::testing::StrictMock<TouchInjectorWinDelegateMock>()); |
+ |
+ TouchEvent event; |
+ event.set_event_type(TouchEvent::TOUCH_POINT_START); |
+ TouchEventPoint* point = event.add_touch_points(); |
+ point->set_id(0u); |
+ |
+ InSequence s; |
+ EXPECT_CALL(*delegate_mock, InitializeTouchInjection(_, _)) |
+ .WillOnce(Return(1)); |
+ |
+ IdFlagMap id_to_flags; |
+ id_to_flags[0u] = kStartFlag; |
+ EXPECT_CALL( |
+ *delegate_mock, |
+ InjectTouchInput(1, EqualsPointerTouchInfoFlag(id_to_flags))) |
+ .WillOnce(Return(1)); |
+ |
+ id_to_flags[0u] = kCancelFlag; |
+ EXPECT_CALL( |
+ *delegate_mock, |
+ InjectTouchInput(1, EqualsPointerTouchInfoFlag(id_to_flags))) |
+ .WillOnce(Return(1)); |
+ |
+ TouchInjectorWin injector; |
+ injector.SetInjectorDelegateForTest(delegate_mock.Pass()); |
+ EXPECT_TRUE(injector.Init()); |
+ injector.InjectTouchEvent(event); |
+ event.set_event_type(TouchEvent::TOUCH_POINT_CANCEL); |
+ injector.InjectTouchEvent(event); |
+} |
+ |
+// Note that points that haven't changed should be injected as MOVE. |
+// This tests: |
+// 1. Start first touch point. |
+// 2. Start second touch point. |
+// 3. Move both touch points. |
+// 4. Start third touch point. |
+// 5. End second touch point. |
+// 6. Cancel remaining (first and third) touch points. |
+TEST(TouchInjectorWinTest, MultiTouch) { |
+ scoped_ptr<TouchInjectorWinDelegateMock> delegate_mock( |
+ new ::testing::StrictMock<TouchInjectorWinDelegateMock>()); |
+ |
+ InSequence s; |
+ EXPECT_CALL(*delegate_mock, InitializeTouchInjection(_, _)) |
+ .WillOnce(Return(1)); |
+ |
+ IdFlagMap id_to_flags; |
+ id_to_flags[0u] = kStartFlag; |
+ EXPECT_CALL( |
+ *delegate_mock, |
+ InjectTouchInput(1, EqualsPointerTouchInfoFlag(id_to_flags))) |
+ .WillOnce(Return(1)); |
+ |
+ id_to_flags[0u] = kMoveFlag; |
+ id_to_flags[1u] = kStartFlag; |
+ EXPECT_CALL( |
+ *delegate_mock, |
+ InjectTouchInput(2, EqualsPointerTouchInfoFlag(id_to_flags))) |
+ .WillOnce(Return(1)); |
+ |
+ id_to_flags[0u] = kMoveFlag; |
+ id_to_flags[1u] = kMoveFlag; |
+ EXPECT_CALL( |
+ *delegate_mock, |
+ InjectTouchInput(2, EqualsPointerTouchInfoFlag(id_to_flags))) |
+ .WillOnce(Return(1)); |
+ |
+ id_to_flags[0u] = kMoveFlag; |
+ id_to_flags[1u] = kMoveFlag; |
+ id_to_flags[2u] = kStartFlag; |
+ EXPECT_CALL( |
+ *delegate_mock, |
+ InjectTouchInput(3, EqualsPointerTouchInfoFlag(id_to_flags))) |
+ .WillOnce(Return(1)); |
+ |
+ id_to_flags[0u] = kMoveFlag; |
+ id_to_flags[1u] = kEndFlag; |
+ id_to_flags[2u] = kMoveFlag; |
+ EXPECT_CALL( |
+ *delegate_mock, |
+ InjectTouchInput(3, EqualsPointerTouchInfoFlag(id_to_flags))) |
+ .WillOnce(Return(1)); |
+ |
+ id_to_flags.erase(1u); |
+ id_to_flags[0u] = kCancelFlag; |
+ id_to_flags[2u] = kCancelFlag; |
+ EXPECT_CALL( |
+ *delegate_mock, |
+ InjectTouchInput(2, EqualsPointerTouchInfoFlag(id_to_flags))) |
+ .WillOnce(Return(1)); |
+ |
+ TouchInjectorWin injector; |
+ injector.SetInjectorDelegateForTest(delegate_mock.Pass()); |
+ EXPECT_TRUE(injector.Init()); |
+ |
+ // Start first touch point. |
+ TouchEvent first_touch_start; |
+ first_touch_start.set_event_type(TouchEvent::TOUCH_POINT_START); |
+ TouchEventPoint* point0 = first_touch_start.add_touch_points(); |
+ point0->set_id(0u); |
+ injector.InjectTouchEvent(first_touch_start); |
+ |
+ // Add second touch point. |
+ TouchEvent second_touch_start; |
+ second_touch_start.set_event_type(TouchEvent::TOUCH_POINT_START); |
+ TouchEventPoint* point1 = second_touch_start.add_touch_points(); |
+ point1->set_id(1u); |
+ injector.InjectTouchEvent(second_touch_start); |
+ |
+ // Move both touch points. |
+ TouchEvent move_both; |
+ move_both.set_event_type(TouchEvent::TOUCH_POINT_MOVE); |
+ point0 = second_touch_start.add_touch_points(); |
+ point1 = second_touch_start.add_touch_points(); |
+ point0->set_id(0u); |
+ point1->set_id(1u); |
+ injector.InjectTouchEvent(move_both); |
+ |
+ // Add another. |
+ TouchEvent third_touch_start; |
+ third_touch_start.set_event_type(TouchEvent::TOUCH_POINT_START); |
+ TouchEventPoint* point2 = third_touch_start.add_touch_points(); |
+ point2->set_id(2u); |
+ injector.InjectTouchEvent(third_touch_start); |
+ |
+ // Release second touch point. |
+ TouchEvent release_second; |
+ release_second.set_event_type(TouchEvent::TOUCH_POINT_END); |
+ point1 = release_second.add_touch_points(); |
+ point1->set_id(1u); |
+ injector.InjectTouchEvent(release_second); |
+ |
+ // Cancel the remaining two points. |
+ TouchEvent cancel_rest; |
+ cancel_rest.set_event_type(TouchEvent::TOUCH_POINT_CANCEL); |
+ point0 = cancel_rest.add_touch_points(); |
+ point0->set_id(0u); |
+ point2 = cancel_rest.add_touch_points(); |
+ point2->set_id(2u); |
+ injector.InjectTouchEvent(cancel_rest); |
+} |
+ |
+} // namespace remoting |