EventsTest.java revision b8f574a165bf6ec5b316734b367ac274ded4809b
1/*
2 * Copyright (C) 2011 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 */
16package android.animation;
17
18import android.os.Handler;
19import android.test.ActivityInstrumentationTestCase2;
20import android.test.UiThreadTest;
21import android.test.suitebuilder.annotation.MediumTest;
22import android.test.suitebuilder.annotation.SmallTest;
23
24import java.util.concurrent.TimeUnit;
25
26/**
27 * Tests for the various lifecycle events of Animators. This abstract class is subclassed by
28 * concrete implementations that provide the actual Animator objects being tested. All of the
29 * testing mechanisms are in this class; the subclasses are only responsible for providing
30 * the mAnimator object.
31 *
32 * This test is more complicated than a typical synchronous test because much of the functionality
33 * must happen on the UI thread. Some tests do this by using the UiThreadTest annotation to
34 * automatically run the whole test on that thread. Other tests must run on the UI thread and also
35 * wait for some later event to occur before ending. These tests use a combination of an
36 * AbstractFuture mechanism and a delayed action to release that Future later.
37 */
38public abstract class EventsTest
39        extends ActivityInstrumentationTestCase2<BasicAnimatorActivity> {
40
41    private static final int ANIM_DURATION = 400;
42    private static final int ANIM_DELAY = 100;
43    private static final int ANIM_MID_DURATION = ANIM_DURATION / 2;
44    private static final int ANIM_MID_DELAY = ANIM_DELAY / 2;
45    private static final int FUTURE_RELEASE_DELAY = 50;
46    private static final int TIMEOUT = ANIM_DURATION + ANIM_DELAY + FUTURE_RELEASE_DELAY;
47
48    private boolean mStarted;  // tracks whether we've received the onAnimationStart() callback
49    private boolean mRunning;  // tracks whether we've started the animator
50    private boolean mCanceled; // trackes whether we've canceled the animator
51    private Animator.AnimatorListener mFutureListener; // mechanism for delaying the end of the test
52    private FutureWaiter mFuture; // Mechanism for waiting for the UI test to complete
53    private Animator.AnimatorListener mListener; // Listener that handles/tests the events
54
55    protected Animator mAnimator; // The animator used in the tests. Must be set in subclass
56                                  // setup() method prior to calling the superclass setup()
57
58    /**
59     * Cancels the given animator. Used to delay cancellation until some later time (after the
60     * animator has started playing).
61     */
62    static class Canceler implements Runnable {
63        Animator mAnim;
64        FutureWaiter mFuture;
65        public Canceler(Animator anim, FutureWaiter future) {
66            mAnim = anim;
67            mFuture = future;
68        }
69        @Override
70        public void run() {
71            try {
72                mAnim.cancel();
73            } catch (junit.framework.AssertionFailedError e) {
74                mFuture.setException(new RuntimeException(e));
75            }
76        }
77    };
78
79    /**
80     * Ends the given animator. Used to delay ending until some later time (after the
81     * animator has started playing).
82     */
83    static class Ender implements Runnable {
84        Animator mAnim;
85        FutureWaiter mFuture;
86        public Ender(Animator anim, FutureWaiter future) {
87            mAnim = anim;
88            mFuture = future;
89        }
90        @Override
91        public void run() {
92            try {
93                mAnim.end();
94            } catch (junit.framework.AssertionFailedError e) {
95                mFuture.setException(new RuntimeException(e));
96            }
97        }
98    };
99
100    /**
101     * Releases the given Future object when the listener's end() event is called. Specifically,
102     * it releases it after some further delay, to give the test time to do other things right
103     * after an animation ends.
104     */
105    static class FutureReleaseListener extends AnimatorListenerAdapter {
106        FutureWaiter mFuture;
107
108        public FutureReleaseListener(FutureWaiter future) {
109            mFuture = future;
110        }
111
112        /**
113         * Variant constructor that auto-releases the FutureWaiter after the specified timeout.
114         * @param future
115         * @param timeout
116         */
117        public FutureReleaseListener(FutureWaiter future, long timeout) {
118            mFuture = future;
119            Handler handler = new Handler();
120            handler.postDelayed(new Runnable() {
121                @Override
122                public void run() {
123                    mFuture.release();
124                }
125            }, timeout);
126        }
127
128        @Override
129        public void onAnimationEnd(Animator animation) {
130            Handler handler = new Handler();
131            handler.postDelayed(new Runnable() {
132                @Override
133                public void run() {
134                    mFuture.release();
135                }
136            }, FUTURE_RELEASE_DELAY);
137        }
138    };
139
140    public EventsTest() {
141        super(BasicAnimatorActivity.class);
142    }
143
144    /**
145     * Sets up the fields used by each test. Subclasses must override this method to create
146     * the protected mAnimator object used in all tests. Overrides must create that animator
147     * and then call super.setup(), where further properties are set on that animator.
148     * @throws Exception
149     */
150    @Override
151    public void setUp() throws Exception {
152        super.setUp();
153
154        // mListener is the main testing mechanism of this file. The asserts of each test
155        // are embedded in the listener callbacks that it implements.
156        mListener = new AnimatorListenerAdapter() {
157            @Override
158            public void onAnimationStart(Animator animation) {
159                // This should only be called on an animation that has not yet been started
160                assertFalse(mStarted);
161                assertTrue(mRunning);
162                mStarted = true;
163            }
164
165            @Override
166            public void onAnimationCancel(Animator animation) {
167                // This should only be called on an animation that has been started and not
168                // yet canceled or ended
169                assertFalse(mCanceled);
170                assertTrue(mRunning);
171                assertTrue(mStarted);
172                mCanceled = true;
173            }
174
175            @Override
176            public void onAnimationEnd(Animator animation) {
177                // This should only be called on an animation that has been started and not
178                // yet ended
179                assertTrue(mRunning);
180                assertTrue(mStarted);
181                mRunning = false;
182                mStarted = false;
183                super.onAnimationEnd(animation);
184            }
185        };
186
187        mAnimator.addListener(mListener);
188        mAnimator.setDuration(ANIM_DURATION);
189
190        mFuture = new FutureWaiter();
191
192        mRunning = false;
193        mCanceled = false;
194        mStarted = false;
195    }
196
197    /**
198     * Verify that calling cancel on an unstarted animator does nothing.
199     */
200    @UiThreadTest
201    @SmallTest
202    public void testCancel() throws Exception {
203        mAnimator.cancel();
204    }
205
206    /**
207     * Verify that calling end on an unstarted animator does nothing.
208     */
209    @UiThreadTest
210    @SmallTest
211    public void testEnd() throws Exception {
212        mAnimator.end();
213    }
214
215    /**
216     * Verify that calling cancel on a started animator does the right thing.
217     */
218    @UiThreadTest
219    @SmallTest
220    public void testStartCancel() throws Exception {
221        mFutureListener = new FutureReleaseListener(mFuture);
222        getActivity().runOnUiThread(new Runnable() {
223            @Override
224            public void run() {
225                try {
226                    mRunning = true;
227                    mAnimator.start();
228                    mAnimator.cancel();
229                    mFuture.release();
230                } catch (junit.framework.AssertionFailedError e) {
231                    mFuture.setException(new RuntimeException(e));
232                }
233            }
234        });
235        mFuture.get(TIMEOUT, TimeUnit.MILLISECONDS);
236    }
237
238    /**
239     * Verify that calling end on a started animator does the right thing.
240     */
241    @UiThreadTest
242    @SmallTest
243    public void testStartEnd() throws Exception {
244        mFutureListener = new FutureReleaseListener(mFuture);
245        getActivity().runOnUiThread(new Runnable() {
246            @Override
247            public void run() {
248                try {
249                    mRunning = true;
250                    mAnimator.start();
251                    mAnimator.end();
252                    mFuture.release();
253                } catch (junit.framework.AssertionFailedError e) {
254                    mFuture.setException(new RuntimeException(e));
255                }
256            }
257        });
258        mFuture.get(TIMEOUT, TimeUnit.MILLISECONDS);
259    }
260
261    /**
262     * Same as testStartCancel, but with a startDelayed animator
263     */
264    @SmallTest
265    public void testStartDelayedCancel() throws Exception {
266        mFutureListener = new FutureReleaseListener(mFuture);
267        mAnimator.setStartDelay(ANIM_DELAY);
268        getActivity().runOnUiThread(new Runnable() {
269            @Override
270            public void run() {
271                try {
272                    mRunning = true;
273                    mAnimator.start();
274                    mAnimator.cancel();
275                    mFuture.release();
276                } catch (junit.framework.AssertionFailedError e) {
277                    mFuture.setException(new RuntimeException(e));
278                }
279            }
280        });
281        mFuture.get(TIMEOUT, TimeUnit.MILLISECONDS);
282    }
283
284    /**
285     * Same as testStartEnd, but with a startDelayed animator
286     */
287    @SmallTest
288    public void testStartDelayedEnd() throws Exception {
289        mFutureListener = new FutureReleaseListener(mFuture);
290        mAnimator.setStartDelay(ANIM_DELAY);
291        getActivity().runOnUiThread(new Runnable() {
292            @Override
293            public void run() {
294                try {
295                    mRunning = true;
296                    mAnimator.start();
297                    mAnimator.end();
298                    mFuture.release();
299                } catch (junit.framework.AssertionFailedError e) {
300                    mFuture.setException(new RuntimeException(e));
301                }
302            }
303        });
304        mFuture.get(TIMEOUT, TimeUnit.MILLISECONDS);
305    }
306
307    /**
308     * Verify that canceling an animator that is playing does the right thing.
309     */
310    @MediumTest
311    public void testPlayingCancel() throws Exception {
312        mFutureListener = new FutureReleaseListener(mFuture);
313        getActivity().runOnUiThread(new Runnable() {
314            @Override
315            public void run() {
316                try {
317                    Handler handler = new Handler();
318                    mAnimator.addListener(mFutureListener);
319                    mRunning = true;
320                    mAnimator.start();
321                    handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DURATION);
322                } catch (junit.framework.AssertionFailedError e) {
323                    mFuture.setException(new RuntimeException(e));
324                }
325            }
326        });
327        mFuture.get(TIMEOUT, TimeUnit.MILLISECONDS);
328    }
329
330    /**
331     * Verify that ending an animator that is playing does the right thing.
332     */
333    @MediumTest
334    public void testPlayingEnd() throws Exception {
335        mFutureListener = new FutureReleaseListener(mFuture);
336        getActivity().runOnUiThread(new Runnable() {
337            @Override
338            public void run() {
339                try {
340                    Handler handler = new Handler();
341                    mAnimator.addListener(mFutureListener);
342                    mRunning = true;
343                    mAnimator.start();
344                    handler.postDelayed(new Ender(mAnimator, mFuture), ANIM_MID_DURATION);
345                } catch (junit.framework.AssertionFailedError e) {
346                    mFuture.setException(new RuntimeException(e));
347                }
348            }
349        });
350        mFuture.get(TIMEOUT, TimeUnit.MILLISECONDS);
351    }
352
353    /**
354     * Same as testPlayingCancel, but with a startDelayed animator
355     */
356    @MediumTest
357    public void testPlayingDelayedCancel() throws Exception {
358        mAnimator.setStartDelay(ANIM_DELAY);
359        mFutureListener = new FutureReleaseListener(mFuture);
360        getActivity().runOnUiThread(new Runnable() {
361            @Override
362            public void run() {
363                try {
364                    Handler handler = new Handler();
365                    mAnimator.addListener(mFutureListener);
366                    mRunning = true;
367                    mAnimator.start();
368                    handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DURATION);
369                } catch (junit.framework.AssertionFailedError e) {
370                    mFuture.setException(new RuntimeException(e));
371                }
372            }
373        });
374        mFuture.get(TIMEOUT,  TimeUnit.MILLISECONDS);
375    }
376
377    /**
378     * Same as testPlayingEnd, but with a startDelayed animator
379     */
380    @MediumTest
381    public void testPlayingDelayedEnd() throws Exception {
382        mAnimator.setStartDelay(ANIM_DELAY);
383        mFutureListener = new FutureReleaseListener(mFuture);
384        getActivity().runOnUiThread(new Runnable() {
385            @Override
386            public void run() {
387                try {
388                    Handler handler = new Handler();
389                    mAnimator.addListener(mFutureListener);
390                    mRunning = true;
391                    mAnimator.start();
392                    handler.postDelayed(new Ender(mAnimator, mFuture), ANIM_MID_DURATION);
393                } catch (junit.framework.AssertionFailedError e) {
394                    mFuture.setException(new RuntimeException(e));
395                }
396            }
397        });
398        mFuture.get(TIMEOUT,  TimeUnit.MILLISECONDS);
399    }
400
401    /**
402     * Same as testPlayingDelayedCancel, but cancel during the startDelay period
403     */
404    @MediumTest
405    public void testPlayingDelayedCancelMidDelay() throws Exception {
406        mAnimator.setStartDelay(ANIM_DELAY);
407        getActivity().runOnUiThread(new Runnable() {
408            @Override
409            public void run() {
410                try {
411                    // Set the listener to automatically timeout after an uncanceled animation
412                    // would have finished. This tests to make sure that we're not calling
413                    // the listeners with cancel/end callbacks since they won't be called
414                    // with the start event.
415                    mFutureListener = new FutureReleaseListener(mFuture, TIMEOUT);
416                    Handler handler = new Handler();
417                    mRunning = true;
418                    mAnimator.start();
419                    handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DELAY);
420                } catch (junit.framework.AssertionFailedError e) {
421                    mFuture.setException(new RuntimeException(e));
422                }
423            }
424        });
425        mFuture.get(TIMEOUT + 100,  TimeUnit.MILLISECONDS);
426    }
427
428    /**
429     * Same as testPlayingDelayedEnd, but end during the startDelay period
430     */
431    @MediumTest
432    public void testPlayingDelayedEndMidDelay() throws Exception {
433        mAnimator.setStartDelay(ANIM_DELAY);
434        getActivity().runOnUiThread(new Runnable() {
435            @Override
436            public void run() {
437                try {
438                    // Set the listener to automatically timeout after an uncanceled animation
439                    // would have finished. This tests to make sure that we're not calling
440                    // the listeners with cancel/end callbacks since they won't be called
441                    // with the start event.
442                    mFutureListener = new FutureReleaseListener(mFuture, TIMEOUT);
443                    Handler handler = new Handler();
444                    mRunning = true;
445                    mAnimator.start();
446                    handler.postDelayed(new Ender(mAnimator, mFuture), ANIM_MID_DELAY);
447                } catch (junit.framework.AssertionFailedError e) {
448                    mFuture.setException(new RuntimeException(e));
449                }
450            }
451        });
452        mFuture.get(TIMEOUT + 100,  TimeUnit.MILLISECONDS);
453    }
454
455    /**
456     * Verifies that canceling a started animation after it has already been canceled
457     * does nothing.
458     */
459    @MediumTest
460    public void testStartDoubleCancel() throws Exception {
461        mFutureListener = new FutureReleaseListener(mFuture);
462        getActivity().runOnUiThread(new Runnable() {
463            @Override
464            public void run() {
465                try {
466                    mRunning = true;
467                    mAnimator.start();
468                    mAnimator.cancel();
469                    mAnimator.cancel();
470                    mFuture.release();
471                } catch (junit.framework.AssertionFailedError e) {
472                    mFuture.setException(new RuntimeException(e));
473                }
474            }
475        });
476        mFuture.get(TIMEOUT,  TimeUnit.MILLISECONDS);
477    }
478
479    /**
480     * Verifies that ending a started animation after it has already been ended
481     * does nothing.
482     */
483    @MediumTest
484    public void testStartDoubleEnd() throws Exception {
485        mFutureListener = new FutureReleaseListener(mFuture);
486        getActivity().runOnUiThread(new Runnable() {
487            @Override
488            public void run() {
489                try {
490                    mRunning = true;
491                    mAnimator.start();
492                    mAnimator.end();
493                    mAnimator.end();
494                    mFuture.release();
495                } catch (junit.framework.AssertionFailedError e) {
496                    mFuture.setException(new RuntimeException(e));
497                }
498            }
499        });
500        mFuture.get(TIMEOUT,  TimeUnit.MILLISECONDS);
501    }
502
503    /**
504     * Same as testStartDoubleCancel, but with a startDelayed animator
505     */
506    @MediumTest
507    public void testStartDelayedDoubleCancel() throws Exception {
508        mAnimator.setStartDelay(ANIM_DELAY);
509        mFutureListener = new FutureReleaseListener(mFuture);
510        getActivity().runOnUiThread(new Runnable() {
511            @Override
512            public void run() {
513                try {
514                    mRunning = true;
515                    mAnimator.start();
516                    mAnimator.cancel();
517                    mAnimator.cancel();
518                    mFuture.release();
519                } catch (junit.framework.AssertionFailedError e) {
520                    mFuture.setException(new RuntimeException(e));
521                }
522            }
523        });
524        mFuture.get(TIMEOUT,  TimeUnit.MILLISECONDS);
525     }
526
527    /**
528     * Same as testStartDoubleEnd, but with a startDelayed animator
529     */
530    @MediumTest
531    public void testStartDelayedDoubleEnd() throws Exception {
532        mAnimator.setStartDelay(ANIM_DELAY);
533        mFutureListener = new FutureReleaseListener(mFuture);
534        getActivity().runOnUiThread(new Runnable() {
535            @Override
536            public void run() {
537                try {
538                    mRunning = true;
539                    mAnimator.start();
540                    mAnimator.end();
541                    mAnimator.end();
542                    mFuture.release();
543                } catch (junit.framework.AssertionFailedError e) {
544                    mFuture.setException(new RuntimeException(e));
545                }
546            }
547        });
548        mFuture.get(TIMEOUT,  TimeUnit.MILLISECONDS);
549     }
550
551}
552