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