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