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