blob: aa7c87a157a9f66c971bcce5cec24f62fa41338a [file] [log] [blame]
// 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.
package org.chromium.chromoting;
import android.graphics.PointF;
import android.support.test.filters.SmallTest;
import android.view.MotionEvent;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.test.BaseJUnit4ClassRunner;
import org.chromium.base.test.util.Feature;
import org.chromium.chromoting.jni.TouchEventData;
/** Tests for {@link TouchInputStrategy}. */
@RunWith(BaseJUnit4ClassRunner.class)
public class TouchInputStrategyTest {
// Tests are run using a screen which is smaller than the size of the remote desktop and is
// translated to the middle of the remote desktop area. This allows us to verify that the
// remote events which are 'injected' are correctly mapped and represent the remote coordinates.
private static final int SCREEN_SIZE_PX = 100;
private static final int REMOTE_DESKTOP_SIZE_PX = 300;
private static final int TRANSLATE_OFFSET_PX = 100;
private RenderData mRenderData;
private TouchInputStrategy mInputStrategy;
private MockInputStub mInputInjector;
private TouchEventGenerator mEventGenerator;
/** Injects movement of a single finger (keeping other fingers in place). */
private void injectMoveEvent(int id, float x, float y) {
MotionEvent event = mEventGenerator.obtainMoveEvent(id, x, y);
mInputStrategy.onMotionEvent(event);
event.recycle();
}
/** Injects a finger-down event (keeping other fingers in place). */
private void injectDownEvent(int id, float x, float y) {
MotionEvent event = mEventGenerator.obtainDownEvent(id, x, y);
mInputStrategy.onMotionEvent(event);
event.recycle();
}
/** Injects a finger-up event (keeping other fingers in place). */
private void injectUpEvent(int id) {
MotionEvent event = mEventGenerator.obtainUpEvent(id);
mInputStrategy.onMotionEvent(event);
event.recycle();
}
@Before
public void setUp() {
mRenderData = new RenderData();
mInputInjector = new MockInputStub();
mInputStrategy =
new TouchInputStrategy(mRenderData, new InputEventSender(mInputInjector));
mEventGenerator = new TouchEventGenerator();
mRenderData.screenWidth = SCREEN_SIZE_PX;
mRenderData.screenHeight = SCREEN_SIZE_PX;
mRenderData.imageWidth = REMOTE_DESKTOP_SIZE_PX;
mRenderData.imageHeight = REMOTE_DESKTOP_SIZE_PX;
mRenderData.transform.postTranslate(-TRANSLATE_OFFSET_PX, -TRANSLATE_OFFSET_PX);
}
@Test
@SmallTest
@Feature({"Chromoting"})
public void testOnTapWithNoEvents() throws Exception {
Assert.assertFalse(mInputStrategy.onTap(InputStub.BUTTON_LEFT));
mInputInjector.assertEmpty();
}
@Test
@SmallTest
@Feature({"Chromoting"})
public void testOneFingerTap() throws Exception {
injectDownEvent(0, 0, 0);
injectUpEvent(0);
mInputInjector.assertEmpty();
Assert.assertTrue(mInputStrategy.onTap(InputStub.BUTTON_LEFT));
mInputInjector.assertTapInjected(TRANSLATE_OFFSET_PX, TRANSLATE_OFFSET_PX);
mInputInjector.assertEmpty();
}
@Test
@SmallTest
@Feature({"Chromoting"})
public void testLifoTwoFingerTap() throws Exception {
// Verify that the right click coordinates occur at the point of the first tap when the
// initial finger is lifted up last.
injectDownEvent(0, 0, 0);
injectDownEvent(1, 25, 25);
injectUpEvent(1);
injectUpEvent(0);
mInputInjector.assertEmpty();
Assert.assertTrue(mInputStrategy.onTap(InputStub.BUTTON_RIGHT));
mInputInjector.assertRightClickInjected(TRANSLATE_OFFSET_PX, TRANSLATE_OFFSET_PX);
mInputInjector.assertEmpty();
}
@Test
@SmallTest
@Feature({"Chromoting"})
public void testFifoTwoFingerTap() throws Exception {
// Verify that the right click coordinates occur at the point of the first tap when the
// initial finger is lifted up first.
injectDownEvent(0, 0, 0);
injectDownEvent(1, 25, 25);
injectUpEvent(0);
injectUpEvent(1);
mInputInjector.assertEmpty();
Assert.assertTrue(mInputStrategy.onTap(InputStub.BUTTON_RIGHT));
mInputInjector.assertRightClickInjected(TRANSLATE_OFFSET_PX, TRANSLATE_OFFSET_PX);
mInputInjector.assertEmpty();
}
@Test
@SmallTest
@Feature({"Chromoting"})
public void testThreeFingerTap() throws Exception {
injectDownEvent(0, 0, 0);
injectDownEvent(1, 25, 25);
injectDownEvent(2, 50, 50);
injectUpEvent(2);
injectUpEvent(1);
injectUpEvent(0);
mInputInjector.assertEmpty();
Assert.assertFalse(mInputStrategy.onTap(InputStub.BUTTON_MIDDLE));
mInputInjector.assertEmpty();
}
@Test
@SmallTest
@Feature({"Chromoting"})
public void testOneFingerTapSequence() throws Exception {
int tapSequenceCount = 10;
for (int i = 0; i < tapSequenceCount; i++) {
injectDownEvent(0, i, i);
injectUpEvent(0);
mInputInjector.assertEmpty();
Assert.assertTrue(mInputStrategy.onTap(InputStub.BUTTON_LEFT));
int remoteOffsetPx = TRANSLATE_OFFSET_PX + i;
mInputInjector.assertTapInjected(remoteOffsetPx, remoteOffsetPx);
}
mInputInjector.assertEmpty();
}
@Test
@SmallTest
@Feature({"Chromoting"})
public void testInvalidThenValidTap() throws Exception {
// First an invalid tap, verify it is ignored.
injectDownEvent(0, 0, 0);
injectDownEvent(1, 25, 25);
injectDownEvent(2, 50, 50);
injectUpEvent(2);
injectUpEvent(1);
injectUpEvent(0);
mInputInjector.assertEmpty();
Assert.assertFalse(mInputStrategy.onTap(InputStub.BUTTON_MIDDLE));
mInputInjector.assertEmpty();
// Next a valid tap, verify it is handled.
injectDownEvent(0, 0, 0);
injectUpEvent(0);
mInputInjector.assertEmpty();
Assert.assertTrue(mInputStrategy.onTap(InputStub.BUTTON_LEFT));
mInputInjector.assertTapInjected(TRANSLATE_OFFSET_PX, TRANSLATE_OFFSET_PX);
mInputInjector.assertEmpty();
}
@Test
@SmallTest
@Feature({"Chromoting"})
public void testOnPressAndHoldWithNoEvents() throws Exception {
Assert.assertFalse(mInputStrategy.onPressAndHold(InputStub.BUTTON_LEFT));
mInputInjector.assertEmpty();
}
@Test
@SmallTest
@Feature({"Chromoting"})
public void testOneFingerLongPress() throws Exception {
injectDownEvent(0, 0, 0);
mInputInjector.assertEmpty();
Assert.assertTrue(mInputStrategy.onPressAndHold(InputStub.BUTTON_LEFT));
mInputInjector.assertTouchEventInjected(TouchEventData.EventType.TOUCH_EVENT_START,
TRANSLATE_OFFSET_PX, TRANSLATE_OFFSET_PX);
injectUpEvent(0);
mInputInjector.assertTouchEventInjected(
TouchEventData.EventType.TOUCH_EVENT_END, TRANSLATE_OFFSET_PX, TRANSLATE_OFFSET_PX);
mInputInjector.assertEmpty();
}
@Test
@SmallTest
@Feature({"Chromoting"})
public void testOneFingerLongPressThenPan() throws Exception {
injectDownEvent(0, 0, 0);
mInputInjector.assertEmpty();
Assert.assertTrue(mInputStrategy.onPressAndHold(InputStub.BUTTON_LEFT));
mInputInjector.assertTouchEventInjected(TouchEventData.EventType.TOUCH_EVENT_START,
TRANSLATE_OFFSET_PX, TRANSLATE_OFFSET_PX);
final int panEventCount = 50;
for (int i = 0; i <= panEventCount; i++) {
injectMoveEvent(0, 0, i);
mInputInjector.assertTouchEventInjected(TouchEventData.EventType.TOUCH_EVENT_MOVE);
}
injectUpEvent(0);
mInputInjector.assertTouchEventInjected(TouchEventData.EventType.TOUCH_EVENT_END,
TRANSLATE_OFFSET_PX, TRANSLATE_OFFSET_PX + panEventCount);
mInputInjector.assertEmpty();
}
@Test
@SmallTest
@Feature({"Chromoting"})
public void testTwoFingerLongPress() throws Exception {
injectDownEvent(0, 0, 0);
injectDownEvent(1, 1, 1);
mInputInjector.assertEmpty();
Assert.assertFalse(mInputStrategy.onPressAndHold(InputStub.BUTTON_RIGHT));
mInputInjector.assertEmpty();
injectUpEvent(0);
injectUpEvent(1);
mInputInjector.assertEmpty();
}
@Test
@SmallTest
@Feature({"Chromoting"})
public void testOneFingerPan() throws Exception {
injectDownEvent(0, 0, 0);
// Inject a few move events to simulate a pan.
injectMoveEvent(0, 1, 1);
injectMoveEvent(0, 2, 2);
injectMoveEvent(0, 3, 3);
mInputInjector.assertEmpty();
injectUpEvent(0);
mInputInjector.assertEmpty();
}
@Test
@SmallTest
@Feature({"Chromoting"})
public void testVerticalTwoFingerPan() throws Exception {
final int fingerOnePosX = 0;
final int fingerTwoPosX = 10;
injectDownEvent(0, fingerOnePosX, 0);
injectDownEvent(1, fingerTwoPosX, 0);
final int eventNum = 10;
for (int i = 0; i < eventNum; i++) {
injectMoveEvent(0, fingerOnePosX, i);
injectMoveEvent(1, fingerTwoPosX, i);
}
mInputInjector.assertEmpty();
mInputStrategy.onScroll(0.0f, 0.0f);
mInputInjector.assertTouchMoveEventInjected(
new PointF[] {
new PointF(fingerOnePosX + TRANSLATE_OFFSET_PX, TRANSLATE_OFFSET_PX),
new PointF(fingerTwoPosX + TRANSLATE_OFFSET_PX, TRANSLATE_OFFSET_PX),
},
0, 1, eventNum);
// Verify events are sent in realtime now.
for (int i = eventNum; i < eventNum + 5; i++) {
injectMoveEvent(0, fingerOnePosX, i);
mInputInjector.assertTouchEventInjected(TouchEventData.EventType.TOUCH_EVENT_MOVE);
injectMoveEvent(1, fingerTwoPosX, i);
mInputInjector.assertTouchEventInjected(TouchEventData.EventType.TOUCH_EVENT_MOVE);
}
injectUpEvent(0);
mInputInjector.assertTouchEventInjected(TouchEventData.EventType.TOUCH_EVENT_END);
injectUpEvent(1);
mInputInjector.assertTouchEventInjected(TouchEventData.EventType.TOUCH_EVENT_END);
mInputInjector.assertEmpty();
}
@Test
@SmallTest
@Feature({"Chromoting"})
public void testHorizontalTwoFingerPan() throws Exception {
final int fingerOnePosY = 0;
final int fingerTwoPosY = 10;
injectDownEvent(0, 0, fingerOnePosY);
injectDownEvent(1, 0, fingerTwoPosY);
final int eventNum = 10;
for (int i = 0; i < eventNum; i++) {
injectMoveEvent(0, i, fingerOnePosY);
injectMoveEvent(1, i, fingerTwoPosY);
}
mInputInjector.assertEmpty();
mInputStrategy.onScroll(0.0f, 0.0f);
mInputInjector.assertTouchMoveEventInjected(
new PointF[] {
new PointF(TRANSLATE_OFFSET_PX, fingerOnePosY + TRANSLATE_OFFSET_PX),
new PointF(TRANSLATE_OFFSET_PX, fingerTwoPosY + TRANSLATE_OFFSET_PX),
},
1, 0, eventNum);
// Verify events are sent in realtime now.
for (int i = eventNum; i < eventNum + 5; i++) {
injectMoveEvent(0, i, fingerOnePosY);
mInputInjector.assertTouchEventInjected(TouchEventData.EventType.TOUCH_EVENT_MOVE);
injectMoveEvent(1, i, fingerTwoPosY);
mInputInjector.assertTouchEventInjected(TouchEventData.EventType.TOUCH_EVENT_MOVE);
}
injectUpEvent(0);
mInputInjector.assertTouchEventInjected(TouchEventData.EventType.TOUCH_EVENT_END);
injectUpEvent(1);
mInputInjector.assertTouchEventInjected(TouchEventData.EventType.TOUCH_EVENT_END);
mInputInjector.assertEmpty();
}
@Test
@SmallTest
@Feature({"Chromoting"})
public void testCancelledTwoFingerPan() throws Exception {
final int fingerOnePosX = 0;
final int fingerTwoPosX = 10;
injectDownEvent(0, fingerOnePosX, 0);
injectDownEvent(1, fingerTwoPosX, 0);
final int eventNum = 10;
for (int i = 0; i < eventNum; i++) {
injectMoveEvent(0, fingerOnePosX, i);
injectMoveEvent(1, fingerTwoPosX, i);
}
mInputInjector.assertEmpty();
mInputStrategy.onScroll(0.0f, 0.0f);
mInputInjector.assertTouchMoveEventInjected(
new PointF[] {
new PointF(fingerOnePosX + TRANSLATE_OFFSET_PX, TRANSLATE_OFFSET_PX),
new PointF(fingerTwoPosX + TRANSLATE_OFFSET_PX, TRANSLATE_OFFSET_PX),
},
0, 1, eventNum);
// Verify events are sent in realtime now.
for (int i = eventNum; i < eventNum + 5; i++) {
injectMoveEvent(0, fingerOnePosX, i);
mInputInjector.assertTouchEventInjected(TouchEventData.EventType.TOUCH_EVENT_MOVE);
injectMoveEvent(1, fingerTwoPosX, i);
mInputInjector.assertTouchEventInjected(TouchEventData.EventType.TOUCH_EVENT_MOVE);
}
// Once a third finger goes down, no more events should be sent.
injectDownEvent(2, 0, 0);
mInputInjector.assertEmpty();
injectMoveEvent(0, 0, 0);
injectMoveEvent(1, 0, 0);
injectMoveEvent(2, 0, 0);
mInputInjector.assertEmpty();
injectUpEvent(2);
mInputInjector.assertEmpty();
injectMoveEvent(0, 5, 5);
injectMoveEvent(1, 5, 5);
mInputInjector.assertEmpty();
}
@Test
@SmallTest
@Feature({"Chromoting"})
public void testTooManyEventsCancelsGesture() throws Exception {
final int fingerOnePosX = 0;
final int fingerTwoPosX = 10;
injectDownEvent(0, fingerOnePosX, 0);
injectDownEvent(1, fingerTwoPosX, 0);
for (int i = 0; i < 10000; i++) {
injectMoveEvent(0, fingerOnePosX, i % 10);
injectMoveEvent(1, fingerTwoPosX, i % 10);
}
mInputInjector.assertEmpty();
mInputStrategy.onScroll(0.0f, 0.0f);
mInputInjector.assertEmpty();
}
}