1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.frameworktest.view;
18
19import junit.framework.Assert;
20
21import android.test.InstrumentationTestCase;
22import android.test.suitebuilder.annotation.MediumTest;
23import android.view.MotionEvent;
24import android.view.VelocityTracker;
25import android.view.animation.AccelerateInterpolator;
26import android.view.animation.DecelerateInterpolator;
27import android.view.animation.Interpolator;
28import android.view.animation.LinearInterpolator;
29
30/**
31 * Exercises {@link android.view.VelocityTracker} to compute correct velocity.<br>
32 * To launch this test, use :<br>
33 * <code>./development/testrunner/runtest.py framework -c com.android.frameworktest.view.VelocityTest</code>
34 */
35public class VelocityTest extends InstrumentationTestCase {
36
37    @MediumTest
38    public void testInitialCondiditions() {
39        VelocityTracker vt = VelocityTracker.obtain();
40        assertNotNull(vt);
41        vt.recycle();
42    }
43
44    /**
45     * Test that {@link android.view.VelocityTracker}.clear() clears
46     * the previous values after a call to computeCurrentVelocity()
47     */
48    @MediumTest
49    public void testClear() {
50        long t = System.currentTimeMillis();
51        VelocityTracker vt = VelocityTracker.obtain();
52        drag(vt, 100, 200, 100, 200, 10, t, 300);
53        vt.computeCurrentVelocity(1);
54        assertFalse("Velocity should not be null", vt.getXVelocity() == 0.0f);
55        assertFalse("Velocity should not be null", vt.getYVelocity() == 0.0f);
56        vt.clear();
57        vt.computeCurrentVelocity(1);
58        assertEquals(0.0f, vt.getXVelocity());
59        assertEquals(0.0f, vt.getYVelocity());
60        vt.recycle();
61    }
62
63    @MediumTest
64    public void testDragAcceleration () {
65        long t = System.currentTimeMillis();
66        VelocityTracker vt = VelocityTracker.obtain();
67        drag(vt, 100, 200, 100, 200, 15, t, 400, new AccelerateInterpolator());
68        vt.computeCurrentVelocity(1000);
69        assertGreater(250.0f, vt.getXVelocity());
70        assertGreater(250.0f, vt.getYVelocity());
71        vt.recycle();
72    }
73
74    @MediumTest
75    public void testDragDeceleration () {
76        long t = System.currentTimeMillis();
77        VelocityTracker vt = VelocityTracker.obtain();
78        drag(vt, 100, 200, 100, 200, 15, t, 400, new DecelerateInterpolator());
79        vt.computeCurrentVelocity(1000);
80        assertLower(250.0f, vt.getXVelocity());
81        assertLower(250.0f, vt.getYVelocity());
82        vt.recycle();
83    }
84
85    @MediumTest
86    public void testDragLinearHorizontal() {
87        long t = System.currentTimeMillis();
88        VelocityTracker vt = VelocityTracker.obtain();
89        // 100px in 400ms => 250px/s
90        drag(vt, 100, 200, 200, 200, 15, t, 400);
91        vt.computeCurrentVelocity(1000);
92        assertEquals(0.0f, vt.getYVelocity());
93        assertEqualFuzzy(250.0f, vt.getXVelocity(), 4f);
94        vt.recycle();
95    }
96
97    @MediumTest
98    public void testDragLinearVertical() {
99        long t = System.currentTimeMillis();
100        VelocityTracker vt = VelocityTracker.obtain();
101        // 100px in 400ms => 250px/s
102        drag(vt, 200, 200, 100, 200, 15, t, 400);
103        vt.computeCurrentVelocity(1000);
104        assertEquals(0.0f, vt.getXVelocity());
105        assertEqualFuzzy(250.0f, vt.getYVelocity(), 4f);
106        vt.recycle();
107    }
108
109    /**
110     * Test dragging with two points only
111     * (velocity must be an exact value)
112     */
113    @MediumTest
114    public void testDragWith2Points () {
115        long t = System.currentTimeMillis();
116        VelocityTracker vt = VelocityTracker.obtain();
117        // 100px, 2 steps, 100ms => 1000px/s
118        drag(vt, 100, 200, 100, 200, 2, t, 100);
119        vt.computeCurrentVelocity(1000);
120        assertEquals(1000.0f, vt.getXVelocity());
121        assertEquals(1000.0f, vt.getYVelocity());
122        vt.recycle();
123    }
124
125    /**
126     * Velocity is independent of the number of points used during
127     * the same interval
128     */
129    @MediumTest
130    public void testStabilityInNbPoints () {
131        long t = System.currentTimeMillis();
132        VelocityTracker vt = VelocityTracker.obtain();
133        drag(vt, 100, 200, 100, 200, 10, t, 400); // 10 steps over 400ms
134        vt.computeCurrentVelocity(1);
135        float firstX = vt.getXVelocity();
136        float firstY = vt.getYVelocity();
137        vt.clear();
138        drag(vt, 100, 200, 100, 200, 20, t, 400); // 20 steps over 400ms
139        vt.computeCurrentVelocity(1);
140        float secondX = vt.getXVelocity();
141        float secondY = vt.getYVelocity();
142        assertEqualFuzzy(firstX, secondX, 0.1f);
143        assertEqualFuzzy(firstY, secondY, 0.1f);
144        vt.recycle();
145    }
146
147    /**
148     * Velocity is independent of the time when the events occurs,
149     * it only depends on delays between the events.
150     */
151    @MediumTest
152    public void testStabilityInTime () {
153        long t = System.currentTimeMillis();
154        VelocityTracker vt = VelocityTracker.obtain();
155        drag(vt, 100, 200, 100, 200, 10, t, 400);
156        vt.computeCurrentVelocity(1);
157        float firstX = vt.getXVelocity();
158        float firstY = vt.getYVelocity();
159        vt.clear();
160        drag(vt, 100, 200, 100, 200, 10, t + 3600*1000, 400); // on hour later
161        vt.computeCurrentVelocity(1);
162        float secondX = vt.getXVelocity();
163        float secondY = vt.getYVelocity();
164        assertEqualFuzzy(firstX, secondX, 0.1f);
165        assertEqualFuzzy(firstY, secondY, 0.1f);
166        vt.recycle();
167    }
168
169    /**
170     * Velocity is independent of the position of the events,
171     * it only depends on their relative distance.
172     */
173    @MediumTest
174    public void testStabilityInSpace () {
175        long t = System.currentTimeMillis();
176        VelocityTracker vt = VelocityTracker.obtain();
177        drag(vt, 100, 200, 100, 200, 10, t, 400);
178        vt.computeCurrentVelocity(1);
179        float firstX = vt.getXVelocity();
180        float firstY = vt.getYVelocity();
181        vt.clear();
182        drag(vt, 200, 300, 200, 300, 10, t, 400); // 100px further
183        vt.computeCurrentVelocity(1);
184        float secondX = vt.getXVelocity();
185        float secondY = vt.getYVelocity();
186        assertEqualFuzzy(firstX, secondX, 0.1f);
187        assertEqualFuzzy(firstY, secondY, 0.1f);
188        vt.recycle();
189    }
190
191    /**
192     * Test that calls to {@link android.view.VelocityTracker}.computeCurrentVelocity()
193     * will output same values when using the same data.
194     */
195    @MediumTest
196    public void testStabilityOfComputation() {
197        long t = System.currentTimeMillis();
198        VelocityTracker vt = VelocityTracker.obtain();
199        drag(vt, 100, 200, 100, 200, 10, t, 300);
200        vt.computeCurrentVelocity(1);
201        float firstX = vt.getXVelocity();
202        float firstY = vt.getYVelocity();
203        vt.computeCurrentVelocity(1);
204        float secondX = vt.getXVelocity();
205        float secondY = vt.getYVelocity();
206        assertEquals(firstX, secondX);
207        assertEquals(firstY, secondY);
208        vt.recycle();
209    }
210
211    /**
212     * Test the units parameter of {@link android.view.VelocityTracker}.computeCurrentVelocity()
213     */
214    @MediumTest
215    public void testStabilityOfUnits() {
216        long t = System.currentTimeMillis();
217        VelocityTracker vt = VelocityTracker.obtain();
218        drag(vt, 100, 200, 100, 200, 10, t, 300);
219        vt.computeCurrentVelocity(1);
220        float firstX = vt.getXVelocity();
221        float firstY = vt.getYVelocity();
222        vt.computeCurrentVelocity(1000);
223        float secondX = vt.getXVelocity();
224        float secondY = vt.getYVelocity();
225        assertEqualFuzzy(firstX, secondX / 1000.0f, 0.1f);
226        assertEqualFuzzy(firstY, secondY / 1000.0f, 0.1f);
227        vt.recycle();
228    }
229
230    /**
231     * Simulate a drag by giving directly MotionEvents to
232     * the VelocityTracker using a linear interpolator
233     */
234    private void drag(VelocityTracker vt, int startX, int endX, int startY, int endY, int steps,
235            long startime, int duration) {
236        drag(vt, startX, endX, startY, endY, steps, startime, duration, new LinearInterpolator());
237    }
238
239    /**
240     * Simulate a drag by giving directly MotionEvents to
241     * the VelocityTracker using a given interpolator
242     */
243    private void drag(VelocityTracker vt, int startX, int endX, int startY, int endY, int steps,
244            long startime, int duration, Interpolator interpolator) {
245        addMotionEvent(vt, startX, startY, startime, MotionEvent.ACTION_DOWN);
246        float dt = duration / (float)steps;
247        int distX = endX - startX;
248        int distY = endY - startY;
249        for (int i=1; i<steps-1; i++) {
250            float ii = interpolator.getInterpolation(i / (float)steps);
251            int x = (int) (startX + distX * ii);
252            int y = (int) (startY + distY * ii);
253            long time = startime + (int) (i * dt);
254            addMotionEvent(vt, x, y, time, MotionEvent.ACTION_MOVE);
255        }
256        addMotionEvent(vt, endX, endY, startime + duration, MotionEvent.ACTION_UP);
257    }
258
259    private void addMotionEvent(VelocityTracker vt, int x, int y, long time, int action) {
260        MotionEvent me = MotionEvent.obtain(time, time, action, x, y, 0);
261        vt.addMovement(me);
262        me.recycle();
263    }
264
265    /**
266     * Float imprecision of the average computations and filtering
267     * (removing last MotionEvent for N > 3) implies that tests
268     *  accepts some approximated values.
269     */
270    private void assertEqualFuzzy(float expected, float actual, float threshold) {
271        boolean fuzzyEqual = actual >= expected - threshold && actual <= expected + threshold;
272        Assert.assertTrue("Expected: <"+expected+"> but was: <"+actual+
273                "> while accepting a variation of: <"+threshold+">", fuzzyEqual);
274    }
275
276    private void assertGreater(float minExpected, float actual) {
277        Assert.assertTrue("Expected: minimum <"+minExpected+"> but was: <"+actual+">",
278                actual > minExpected);
279    }
280
281    private void assertLower(float maxExpected, float actual) {
282        Assert.assertTrue("Expected: maximum <"+maxExpected+"> but was: <"+actual+">",
283                actual < maxExpected);
284    }
285}
286