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 import android.view.ViewPropertyAnimator;
     24 import android.widget.Button;
     25 import com.android.frameworks.coretests.R;
     26 
     27 import java.util.concurrent.TimeUnit;
     28 
     29 /**
     30  * Tests for the various lifecycle events of Animators. This abstract class is subclassed by
     31  * concrete implementations that provide the actual Animator objects being tested. All of the
     32  * testing mechanisms are in this class; the subclasses are only responsible for providing
     33  * the mAnimator object.
     34  *
     35  * This test is more complicated than a typical synchronous test because much of the functionality
     36  * must happen on the UI thread. Some tests do this by using the UiThreadTest annotation to
     37  * automatically run the whole test on that thread. Other tests must run on the UI thread and also
     38  * wait for some later event to occur before ending. These tests use a combination of an
     39  * AbstractFuture mechanism and a delayed action to release that Future later.
     40  */
     41 public abstract class ViewPropertyAnimatorTest
     42         extends ActivityInstrumentationTestCase2<BasicAnimatorActivity> {
     43 
     44     protected static final int ANIM_DURATION = 400;
     45     protected static final int ANIM_DELAY = 100;
     46     protected static final int ANIM_MID_DURATION = ANIM_DURATION / 2;
     47     protected static final int ANIM_MID_DELAY = ANIM_DELAY / 2;
     48     protected static final int FUTURE_RELEASE_DELAY = 50;
     49 
     50     private boolean mStarted;  // tracks whether we've received the onAnimationStart() callback
     51     protected boolean mRunning;  // tracks whether we've started the animator
     52     private boolean mCanceled; // trackes whether we've canceled the animator
     53     protected Animator.AnimatorListener mFutureListener; // mechanism for delaying the end of the test
     54     protected FutureWaiter mFuture; // Mechanism for waiting for the UI test to complete
     55     private Animator.AnimatorListener mListener; // Listener that handles/tests the events
     56 
     57     protected ViewPropertyAnimator mAnimator; // The animator used in the tests. Must be set in subclass
     58                                   // setup() method prior to calling the superclass setup()
     59 
     60     /**
     61      * Cancels the given animator. Used to delay cancellation until some later time (after the
     62      * animator has started playing).
     63      */
     64     protected static class Canceler implements Runnable {
     65         ViewPropertyAnimator mAnim;
     66         FutureWaiter mFuture;
     67         public Canceler(ViewPropertyAnimator anim, FutureWaiter future) {
     68             mAnim = anim;
     69             mFuture = future;
     70         }
     71         @Override
     72         public void run() {
     73             try {
     74                 mAnim.cancel();
     75             } catch (junit.framework.AssertionFailedError e) {
     76                 mFuture.setException(new RuntimeException(e));
     77             }
     78         }
     79     };
     80 
     81     /**
     82      * Timeout length, based on when the animation should reasonably be complete.
     83      */
     84     protected long getTimeout() {
     85         return ANIM_DURATION + ANIM_DELAY + FUTURE_RELEASE_DELAY;
     86     }
     87 
     88     /**
     89      * Releases the given Future object when the listener's end() event is called. Specifically,
     90      * it releases it after some further delay, to give the test time to do other things right
     91      * after an animation ends.
     92      */
     93     protected static class FutureReleaseListener extends AnimatorListenerAdapter {
     94         FutureWaiter mFuture;
     95 
     96         public FutureReleaseListener(FutureWaiter future) {
     97             mFuture = future;
     98         }
     99 
    100         /**
    101          * Variant constructor that auto-releases the FutureWaiter after the specified timeout.
    102          * @param future
    103          * @param timeout
    104          */
    105         public FutureReleaseListener(FutureWaiter future, long timeout) {
    106             mFuture = future;
    107             Handler handler = new Handler();
    108             handler.postDelayed(new Runnable() {
    109                 @Override
    110                 public void run() {
    111                     mFuture.release();
    112                 }
    113             }, timeout);
    114         }
    115 
    116         @Override
    117         public void onAnimationEnd(Animator animation) {
    118             Handler handler = new Handler();
    119             handler.postDelayed(new Runnable() {
    120                 @Override
    121                 public void run() {
    122                     mFuture.release();
    123                 }
    124             }, FUTURE_RELEASE_DELAY);
    125         }
    126     };
    127 
    128     public ViewPropertyAnimatorTest() {
    129         super(BasicAnimatorActivity.class);
    130     }
    131 
    132     /**
    133      * Sets up the fields used by each test. Subclasses must override this method to create
    134      * the protected mAnimator object used in all tests. Overrides must create that animator
    135      * and then call super.setup(), where further properties are set on that animator.
    136      * @throws Exception
    137      */
    138     @Override
    139     public void setUp() throws Exception {
    140         final BasicAnimatorActivity activity = getActivity();
    141         Button button = (Button) activity.findViewById(R.id.animatingButton);
    142 
    143         mAnimator = button.animate().x(100).y(100);
    144 
    145         super.setUp();
    146 
    147         // mListener is the main testing mechanism of this file. The asserts of each test
    148         // are embedded in the listener callbacks that it implements.
    149         mListener = new AnimatorListenerAdapter() {
    150             @Override
    151             public void onAnimationStart(Animator animation) {
    152                 // This should only be called on an animation that has not yet been started
    153                 assertFalse(mStarted);
    154                 assertTrue(mRunning);
    155                 mStarted = true;
    156             }
    157 
    158             @Override
    159             public void onAnimationCancel(Animator animation) {
    160                 // This should only be called on an animation that has been started and not
    161                 // yet canceled or ended
    162                 assertFalse(mCanceled);
    163                 assertTrue(mRunning);
    164                 assertTrue(mStarted);
    165                 mCanceled = true;
    166             }
    167 
    168             @Override
    169             public void onAnimationEnd(Animator animation) {
    170                 // This should only be called on an animation that has been started and not
    171                 // yet ended
    172                 assertTrue(mRunning);
    173                 assertTrue(mStarted);
    174                 mRunning = false;
    175                 mStarted = false;
    176                 super.onAnimationEnd(animation);
    177             }
    178         };
    179 
    180         mAnimator.setListener(mListener);
    181         mAnimator.setDuration(ANIM_DURATION);
    182 
    183         mFuture = new FutureWaiter();
    184 
    185         mRunning = false;
    186         mCanceled = false;
    187         mStarted = false;
    188     }
    189 
    190     /**
    191      * Verify that calling cancel on an unstarted animator does nothing.
    192      */
    193     @UiThreadTest
    194     @SmallTest
    195     public void testCancel() throws Exception {
    196         mAnimator.cancel();
    197     }
    198 
    199     /**
    200      * Verify that calling cancel on a started animator does the right thing.
    201      */
    202     @UiThreadTest
    203     @SmallTest
    204     public void testStartCancel() throws Exception {
    205         mFutureListener = new FutureReleaseListener(mFuture);
    206         getActivity().runOnUiThread(new Runnable() {
    207             @Override
    208             public void run() {
    209                 try {
    210                     mRunning = true;
    211                     mAnimator.start();
    212                     mAnimator.cancel();
    213                     mFuture.release();
    214                 } catch (junit.framework.AssertionFailedError e) {
    215                     mFuture.setException(new RuntimeException(e));
    216                 }
    217             }
    218         });
    219         mFuture.get(getTimeout(), TimeUnit.MILLISECONDS);
    220     }
    221 
    222     /**
    223      * Same as testStartCancel, but with a startDelayed animator
    224      */
    225     @SmallTest
    226     public void testStartDelayedCancel() throws Exception {
    227         mFutureListener = new FutureReleaseListener(mFuture);
    228         mAnimator.setStartDelay(ANIM_DELAY);
    229         getActivity().runOnUiThread(new Runnable() {
    230             @Override
    231             public void run() {
    232                 try {
    233                     mRunning = true;
    234                     mAnimator.start();
    235                     mAnimator.cancel();
    236                     mFuture.release();
    237                 } catch (junit.framework.AssertionFailedError e) {
    238                     mFuture.setException(new RuntimeException(e));
    239                 }
    240             }
    241         });
    242         mFuture.get(getTimeout(), TimeUnit.MILLISECONDS);
    243     }
    244 
    245     /**
    246      * Verify that canceling an animator that is playing does the right thing.
    247      */
    248     @MediumTest
    249     public void testPlayingCancel() throws Exception {
    250         mFutureListener = new FutureReleaseListener(mFuture);
    251         getActivity().runOnUiThread(new Runnable() {
    252             @Override
    253             public void run() {
    254                 try {
    255                     Handler handler = new Handler();
    256                     mAnimator.setListener(mFutureListener);
    257                     mRunning = true;
    258                     mAnimator.start();
    259                     handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DURATION);
    260                 } catch (junit.framework.AssertionFailedError e) {
    261                     mFuture.setException(new RuntimeException(e));
    262                 }
    263             }
    264         });
    265         mFuture.get(getTimeout(), TimeUnit.MILLISECONDS);
    266     }
    267 
    268     /**
    269      * Same as testPlayingCancel, but with a startDelayed animator
    270      */
    271     @MediumTest
    272     public void testPlayingDelayedCancel() throws Exception {
    273         mAnimator.setStartDelay(ANIM_DELAY);
    274         mFutureListener = new FutureReleaseListener(mFuture);
    275         getActivity().runOnUiThread(new Runnable() {
    276             @Override
    277             public void run() {
    278                 try {
    279                     Handler handler = new Handler();
    280                     mAnimator.setListener(mFutureListener);
    281                     mRunning = true;
    282                     mAnimator.start();
    283                     handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DURATION);
    284                 } catch (junit.framework.AssertionFailedError e) {
    285                     mFuture.setException(new RuntimeException(e));
    286                 }
    287             }
    288         });
    289         mFuture.get(getTimeout(),  TimeUnit.MILLISECONDS);
    290     }
    291 
    292     /**
    293      * Same as testPlayingDelayedCancel, but cancel during the startDelay period
    294      */
    295     @MediumTest
    296     public void testPlayingDelayedCancelMidDelay() throws Exception {
    297         mAnimator.setStartDelay(ANIM_DELAY);
    298         getActivity().runOnUiThread(new Runnable() {
    299             @Override
    300             public void run() {
    301                 try {
    302                     // Set the listener to automatically timeout after an uncanceled animation
    303                     // would have finished. This tests to make sure that we're not calling
    304                     // the listeners with cancel/end callbacks since they won't be called
    305                     // with the start event.
    306                     mFutureListener = new FutureReleaseListener(mFuture, getTimeout());
    307                     Handler handler = new Handler();
    308                     mRunning = true;
    309                     mAnimator.start();
    310                     handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DELAY);
    311                 } catch (junit.framework.AssertionFailedError e) {
    312                     mFuture.setException(new RuntimeException(e));
    313                 }
    314             }
    315         });
    316         mFuture.get(getTimeout() + 100,  TimeUnit.MILLISECONDS);
    317     }
    318 
    319     /**
    320      * Verifies that canceling a started animation after it has already been canceled
    321      * does nothing.
    322      */
    323     @MediumTest
    324     public void testStartDoubleCancel() throws Exception {
    325         mFutureListener = new FutureReleaseListener(mFuture);
    326         getActivity().runOnUiThread(new Runnable() {
    327             @Override
    328             public void run() {
    329                 try {
    330                     mRunning = true;
    331                     mAnimator.start();
    332                     mAnimator.cancel();
    333                     mAnimator.cancel();
    334                     mFuture.release();
    335                 } catch (junit.framework.AssertionFailedError e) {
    336                     mFuture.setException(new RuntimeException(e));
    337                 }
    338             }
    339         });
    340         mFuture.get(getTimeout(), TimeUnit.MILLISECONDS);
    341     }
    342 
    343     /**
    344      * Same as testStartDoubleCancel, but with a startDelayed animator
    345      */
    346     @MediumTest
    347     public void testStartDelayedDoubleCancel() throws Exception {
    348         mAnimator.setStartDelay(ANIM_DELAY);
    349         mFutureListener = new FutureReleaseListener(mFuture);
    350         getActivity().runOnUiThread(new Runnable() {
    351             @Override
    352             public void run() {
    353                 try {
    354                     mRunning = true;
    355                     mAnimator.start();
    356                     mAnimator.cancel();
    357                     mAnimator.cancel();
    358                     mFuture.release();
    359                 } catch (junit.framework.AssertionFailedError e) {
    360                     mFuture.setException(new RuntimeException(e));
    361                 }
    362             }
    363         });
    364         mFuture.get(getTimeout(),  TimeUnit.MILLISECONDS);
    365      }
    366 
    367 }
    368