1// Copyright 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5package org.chromium.content.browser;
6
7import android.os.SystemClock;
8import android.test.InstrumentationTestCase;
9import android.test.suitebuilder.annotation.LargeTest;
10import android.test.suitebuilder.annotation.SmallTest;
11import android.view.MotionEvent;
12import android.view.ViewConfiguration;
13
14import org.chromium.base.test.util.Feature;
15import org.chromium.base.test.util.ScalableTimeout;
16
17import java.util.concurrent.CountDownLatch;
18import java.util.concurrent.TimeUnit;
19
20/**
21 * Test suite for LongPressDetector.
22 */
23public class LongPressDetectorTest extends InstrumentationTestCase {
24    private static final int FAKE_COORD_X = 42;
25    private static final int FAKE_COORD_Y = 24;
26    private LongPressDetector mLongPressDetector;
27
28    private MotionEvent motionEvent(int action, long downTime, long eventTime) {
29        return MotionEvent.obtain(downTime, eventTime, action, FAKE_COORD_X, FAKE_COORD_Y, 0);
30    }
31
32    @Override
33    public void setUp() {
34        mLongPressDetector = new LongPressDetector(getInstrumentation().getTargetContext(), null);
35    }
36
37    /**
38     * Verify a DOWN without a corresponding UP will have a pending DOWN.
39     *
40     * @throws Exception
41     */
42    @SmallTest
43    @Feature({"AndroidWebView"})
44    public void testGestureSimpleLongPress() throws Exception {
45        final long downTime = SystemClock.uptimeMillis();
46        final long eventTime = SystemClock.uptimeMillis();
47
48        MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, eventTime);
49        mLongPressDetector.startLongPressTimerIfNeeded(event);
50
51        assertTrue("Should have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());
52    }
53
54    private void gestureNoLongPressTestHelper(int cancelActionType) throws Exception {
55        final long downTime = SystemClock.uptimeMillis();
56        final long eventTime = SystemClock.uptimeMillis();
57
58        MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, eventTime);
59        mLongPressDetector.startLongPressTimerIfNeeded(event);
60
61        assertTrue("Should have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());
62
63        event = motionEvent(cancelActionType, downTime, eventTime + 10);
64        mLongPressDetector.cancelLongPressIfNeeded(event);
65        assertTrue("Should not have a pending LONG_PRESS", !mLongPressDetector.hasPendingMessage());
66    }
67
68    /**
69     * Verify a DOWN with a corresponding UP will not have a pending Gesture.
70     *
71     * @throws Exception
72     */
73    @SmallTest
74    @Feature({"AndroidWebView"})
75    public void testGestureNoLongPressOnUp() throws Exception {
76        gestureNoLongPressTestHelper(MotionEvent.ACTION_UP);
77    }
78
79    /**
80     * Verify a DOWN with a corresponding CANCEL will not have a pending Gesture.
81     *
82     * @throws Exception
83     */
84    @SmallTest
85    @Feature({"AndroidWebView"})
86    public void testGestureNoLongPressOnCancel() throws Exception {
87        gestureNoLongPressTestHelper(MotionEvent.ACTION_CANCEL);
88    }
89
90    /**
91     * Verify that a DOWN followed by an UP after the long press timer would
92     * detect a long press (that is, the UP will not trigger a tap or cancel the
93     * long press).
94     *
95     * @throws Exception
96     */
97    @SmallTest
98    @Feature({"AndroidWebView"})
99    public void testGestureLongWithDelayedUp() throws Exception {
100        final long downTime = SystemClock.uptimeMillis();
101        final long eventTime = SystemClock.uptimeMillis();
102
103        MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, eventTime);
104        mLongPressDetector.startLongPressTimerIfNeeded(event);
105
106        assertTrue("Should have a pending LONG_PRESS", mLongPressDetector.hasPendingMessage());
107
108        // Event time must be larger than LONG_PRESS_TIMEOUT.
109        event = motionEvent(MotionEvent.ACTION_UP, downTime, eventTime + 1000);
110        mLongPressDetector.cancelLongPressIfNeeded(event);
111        assertTrue("Should still have a pending gesture", mLongPressDetector.hasPendingMessage());
112    }
113
114    /**
115     * Verify that the touch move threshold (slop) is working for events offered to native.
116     */
117    @SmallTest
118    @Feature({"AndroidWebView"})
119    public void testConfirmOfferMoveEventToNative() {
120        final int slop = ViewConfiguration.get(getInstrumentation().getTargetContext())
121                .getScaledTouchSlop();
122
123        long eventTime = SystemClock.uptimeMillis();
124        final MotionEvent downEvent = MotionEvent.obtain(
125                eventTime, eventTime, MotionEvent.ACTION_DOWN, FAKE_COORD_X, FAKE_COORD_Y, 0);
126
127        // Test a small move, where confirmOfferMoveEventToNative should return false.
128        mLongPressDetector.onOfferTouchEventToJavaScript(downEvent);
129        eventTime = SystemClock.uptimeMillis();
130        final MotionEvent smallMove = MotionEvent.obtain(
131                eventTime, eventTime, MotionEvent.ACTION_MOVE,
132                FAKE_COORD_X + slop / 2, FAKE_COORD_Y + slop / 2, 0);
133        assertFalse(mLongPressDetector.confirmOfferMoveEventToJavaScript(smallMove));
134
135        // Test a big move, where confirmOfferMoveEventToNative should return true.
136        mLongPressDetector.onOfferTouchEventToJavaScript(downEvent);
137        eventTime = SystemClock.uptimeMillis();
138        final MotionEvent largeMove = MotionEvent.obtain(
139                eventTime, eventTime, MotionEvent.ACTION_MOVE,
140                FAKE_COORD_X + slop * 2, FAKE_COORD_Y + slop * 2, 0);
141        assertTrue(mLongPressDetector.confirmOfferMoveEventToJavaScript(largeMove));
142    }
143
144    /**
145     * This is an example of a large test running delayed messages.
146     * It exercises GestureDetector itself, and expects the onLongPress to be called.
147     * Note that GestureDetector creates a Handler and posts message to it for detecting
148     * long press. It needs to be created on the Main thread.
149     *
150     * @throws Exception
151     */
152    @LargeTest
153    @Feature({"AndroidWebView"})
154    public void testGestureLongPressDetected() throws Exception {
155        final CountDownLatch longPressCalled = new CountDownLatch(1);
156        getInstrumentation().runOnMainSync(new Runnable() {
157            @Override
158            public void run() {
159                LongPressDetector longPressDetector = new LongPressDetector(
160                        getInstrumentation().getTargetContext(),
161                        new LongPressDetector.LongPressDelegate() {
162                            @Override
163                            public void onLongPress(MotionEvent event) {
164                                longPressCalled.countDown();
165                            }
166                });
167
168                final long downTime = SystemClock.uptimeMillis();
169                final long eventTime = SystemClock.uptimeMillis();
170                MotionEvent event = motionEvent(MotionEvent.ACTION_DOWN, downTime, eventTime);
171                longPressDetector.startLongPressTimerIfNeeded(event);
172            }
173        });
174        assertTrue(longPressCalled.await(
175                ScalableTimeout.ScaleTimeout(1000), TimeUnit.MILLISECONDS));
176    }
177}
178