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