Loading core/tests/coretests/src/android/animation/ValueAnimatorTests.java +347 −1 Original line number Diff line number Diff line Loading @@ -15,13 +15,21 @@ */ package android.animation; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.SystemClock; import android.test.ActivityInstrumentationTestCase2; import android.test.suitebuilder.annotation.SmallTest; import android.view.Choreographer; import android.view.animation.LinearInterpolator; import java.util.ArrayList; import static android.test.MoreAsserts.assertNotEqual; public class ValueAnimatorTests extends ActivityInstrumentationTestCase2<BasicAnimatorActivity> { private static final long WAIT_TIME_OUT = 5000; private ValueAnimator a1; private ValueAnimator a2; Loading @@ -34,6 +42,9 @@ public class ValueAnimatorTests extends ActivityInstrumentationTestCase2<BasicAn private final static int A2_START_VALUE = 100; private final static int A2_END_VALUE = 200; private final static long DEFAULT_FRAME_INTERVAL = 5; //ms private final static long COMMIT_DELAY = 3; //ms public ValueAnimatorTests() { super(BasicAnimatorActivity.class); } Loading @@ -47,9 +58,9 @@ public class ValueAnimatorTests extends ActivityInstrumentationTestCase2<BasicAn @Override public void tearDown() throws Exception { super.tearDown(); a1 = null; a2 = null; super.tearDown(); } @SmallTest Loading Loading @@ -492,10 +503,265 @@ public class ValueAnimatorTests extends ActivityInstrumentationTestCase2<BasicAn }); } @SmallTest public void testUpdateListener() throws InterruptedException { final MyFrameCallbackProvider provider = new MyFrameCallbackProvider(); long sleep = 0; while (provider.mHandler == null) { Thread.sleep(POLL_INTERVAL); sleep += POLL_INTERVAL; if (sleep > WAIT_TIME_OUT) { break; } } // Either the looper has started, or timed out assertNotNull(provider.mHandler); final MyListener listener = new MyListener(); final MyUpdateListener l1 = new MyUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { long currentTime = SystemClock.uptimeMillis(); long frameDelay = provider.getFrameDelay(); if (lastUpdateTime > 0) { // Error tolerance here is one frame. assertTrue((currentTime - lastUpdateTime) < frameDelay * 2); } else { // First frame: assertTrue(listener.startCalled); assertTrue(listener.startTime > 0); assertTrue(currentTime - listener.startTime < frameDelay * 2); } super.onAnimationUpdate(animation); } }; a1.addUpdateListener(l1); a1.addListener(listener); a1.setStartDelay(100); provider.mHandler.post(new Runnable() { @Override public void run() { AnimationHandler.getInstance().setProvider(provider); a1.start(); } }); Thread.sleep(POLL_INTERVAL); assertTrue(a1.isStarted()); Thread.sleep(a1.getTotalDuration() + TOLERANCE); // Finished by now. assertFalse(a1.isStarted()); assertTrue(listener.endTime > 0); // Check the time difference between last frame and end time. assertTrue(listener.endTime >= l1.lastUpdateTime); assertTrue(listener.endTime - l1.lastUpdateTime < 2 * provider.getFrameDelay()); } @SmallTest public void testConcurrentModification() throws Throwable { // Attempt to modify list of animations as the list is being iterated final ValueAnimator a0 = ValueAnimator.ofInt(100, 200).setDuration(500); final ValueAnimator a3 = ValueAnimator.ofFloat(0, 1).setDuration(500); final ValueAnimator a4 = ValueAnimator.ofInt(200, 300).setDuration(500); final MyListener listener = new MyListener() { @Override public void onAnimationEnd(Animator anim) { super.onAnimationEnd(anim); // AnimationHandler should be iterating the list at the moment, end/cancel all // the other animations. No ConcurrentModificationException should happen. a0.cancel(); a1.end(); a3.end(); a4.cancel(); } }; a2.addListener(listener); runTestOnUiThread(new Runnable() { @Override public void run() { a0.start(); a1.start(); a2.start(); a3.start(); a4.start(); } }); runTestOnUiThread(new Runnable() { @Override public void run() { assertTrue(a0.isStarted()); assertTrue(a1.isStarted()); assertTrue(a2.isStarted()); assertTrue(a3.isStarted()); assertTrue(a4.isStarted()); } }); Thread.sleep(POLL_INTERVAL); runTestOnUiThread(new Runnable() { @Override public void run() { // End the animator that should be in the middle of the list. a2.end(); } }); Thread.sleep(POLL_INTERVAL); assertTrue(listener.endCalled); assertFalse(a0.isStarted()); assertFalse(a1.isStarted()); assertFalse(a2.isStarted()); assertFalse(a3.isStarted()); assertFalse(a4.isStarted()); } @SmallTest public void testASeek() throws Throwable { final MyListener l1 = new MyListener(); final MyListener l2 = new MyListener(); final MyUpdateListener updateListener1 = new MyUpdateListener(); final MyUpdateListener updateListener2 = new MyUpdateListener(); final float a1StartFraction = 0.2f; final float a2StartFraction = 0.3f; // Extend duration so we have plenty of latitude to manipulate the animations when they // are running. a1.setDuration(1000); a2.setDuration(1000); a1.addListener(l1); a2.addListener(l2); a1.addUpdateListener(updateListener1); a2.addUpdateListener(updateListener2); TimeInterpolator interpolator = new LinearInterpolator(); a1.setInterpolator(interpolator); a2.setInterpolator(interpolator); runTestOnUiThread(new Runnable() { @Override public void run() { assertFalse(a1.isStarted()); assertFalse(a1.isRunning()); assertFalse(a2.isStarted()); assertFalse(a2.isRunning()); // Test isRunning() and isStarted() before and after seek a1.setCurrentFraction(a1StartFraction); a2.setCurrentFraction(a2StartFraction); assertFalse(a1.isStarted()); assertFalse(a1.isRunning()); assertFalse(a2.isStarted()); assertFalse(a2.isRunning()); } }); Thread.sleep(POLL_INTERVAL); // Start animation and seek during the animation. runTestOnUiThread(new Runnable() { @Override public void run() { assertFalse(a1.isStarted()); assertFalse(a1.isRunning()); assertFalse(a2.isStarted()); assertFalse(a2.isRunning()); assertEquals(a1StartFraction, a1.getAnimatedFraction()); assertEquals(a2StartFraction, a2.getAnimatedFraction()); a1.start(); a2.start(); } }); Thread.sleep(POLL_INTERVAL); final float halfwayFraction = 0.5f; runTestOnUiThread(new Runnable() { @Override public void run() { assertTrue(l1.startCalled); assertTrue(l2.startCalled); assertFalse(l1.endCalled); assertFalse(l2.endCalled); // Check whether the animations start from the seeking fraction assertTrue(updateListener1.startFraction >= a1StartFraction); assertTrue(updateListener2.startFraction >= a2StartFraction); assertTrue(a1.isStarted()); assertTrue(a1.isRunning()); assertTrue(a2.isStarted()); assertTrue(a2.isRunning()); a1.setCurrentFraction(halfwayFraction); a2.setCurrentFraction(halfwayFraction); } }); Thread.sleep(POLL_INTERVAL); // Check that seeking during running doesn't change animation's internal state runTestOnUiThread(new Runnable() { @Override public void run() { assertTrue(l1.startCalled); assertTrue(l2.startCalled); assertFalse(l1.endCalled); assertFalse(l2.endCalled); assertTrue(a1.isStarted()); assertTrue(a1.isRunning()); assertTrue(a2.isStarted()); assertTrue(a2.isRunning()); } }); // Wait until the animators finish successfully. long wait = Math.max(a1.getTotalDuration(), a2.getTotalDuration()); Thread.sleep(wait); runTestOnUiThread(new Runnable() { @Override public void run() { // Verify that the animators have finished. assertTrue(l1.endCalled); assertTrue(l2.endCalled); assertFalse(a1.isStarted()); assertFalse(a2.isStarted()); assertFalse(a1.isRunning()); assertFalse(a2.isRunning()); } }); // Re-start animator a1 after it ends normally, and check that seek value from last run // does not affect the new run. updateListener1.reset(); runTestOnUiThread(new Runnable() { @Override public void run() { a1.start(); } }); Thread.sleep(POLL_INTERVAL); runTestOnUiThread(new Runnable() { @Override public void run() { assertTrue(updateListener1.wasRunning); assertTrue(updateListener1.startFraction >= 0); assertTrue(updateListener1.startFraction < halfwayFraction); a1.end(); } }); } class MyUpdateListener implements ValueAnimator.AnimatorUpdateListener { boolean wasRunning = false; long firstRunningFrameTime = -1; long lastUpdateTime = -1; float startFraction = 0; @Override public void onAnimationUpdate(ValueAnimator animation) { Loading @@ -503,24 +769,36 @@ public class ValueAnimatorTests extends ActivityInstrumentationTestCase2<BasicAn if (animation.isRunning() && !wasRunning) { // Delay has passed firstRunningFrameTime = lastUpdateTime; startFraction = animation.getAnimatedFraction(); wasRunning = animation.isRunning(); } } void reset() { wasRunning = false; firstRunningFrameTime = -1; lastUpdateTime = -1; startFraction = 0; } } class MyListener implements Animator.AnimatorListener { boolean startCalled = false; boolean cancelCalled = false; boolean endCalled = false; long startTime = -1; long endTime = -1; @Override public void onAnimationStart(Animator animation) { startCalled = true; startTime = SystemClock.uptimeMillis(); } @Override public void onAnimationEnd(Animator animation) { endCalled = true; endTime = SystemClock.uptimeMillis(); } @Override Loading Loading @@ -548,4 +826,72 @@ public class ValueAnimatorTests extends ActivityInstrumentationTestCase2<BasicAn resumeCalled = true; } } class MyFrameCallbackProvider implements AnimationHandler.AnimationFrameCallbackProvider { Handler mHandler = null; private final static int MSG_FRAME = 0; private long mFrameDelay = DEFAULT_FRAME_INTERVAL; private ArrayList<Choreographer.FrameCallback> mFrameCallbacks = new ArrayList<>(); final LooperThread mThread = new LooperThread(); public MyFrameCallbackProvider() { mThread.start(); } @Override public void postFrameCallback(Choreographer.FrameCallback callback) { mHandler.sendEmptyMessageDelayed(MSG_FRAME, mFrameDelay); if (!mFrameCallbacks.contains(callback)) { mFrameCallbacks.add(callback); } } @Override public void postCommitCallback(Runnable runnable) { // Run the runnable after a commit delay mHandler.postDelayed(runnable, COMMIT_DELAY); } @Override public long getFrameTime() { return SystemClock.uptimeMillis(); } @Override public long getFrameDelay() { return mFrameDelay; } @Override public void setFrameDelay(long delay) { mFrameDelay = delay; if (mFrameCallbacks.size() != 0) { mHandler.removeMessages(MSG_FRAME); mHandler.sendEmptyMessageDelayed(MSG_FRAME, mFrameDelay); } } class LooperThread extends Thread { public void run() { Looper.prepare(); mHandler = new Handler() { public void handleMessage(Message msg) { // Handle message here. switch (msg.what) { case MSG_FRAME: for (int i = 0; i < mFrameCallbacks.size(); i++) { mFrameCallbacks.get(i).doFrame(SystemClock.uptimeMillis()); } break; default: break; } } }; Looper.loop(); } } } } Loading
core/tests/coretests/src/android/animation/ValueAnimatorTests.java +347 −1 Original line number Diff line number Diff line Loading @@ -15,13 +15,21 @@ */ package android.animation; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.SystemClock; import android.test.ActivityInstrumentationTestCase2; import android.test.suitebuilder.annotation.SmallTest; import android.view.Choreographer; import android.view.animation.LinearInterpolator; import java.util.ArrayList; import static android.test.MoreAsserts.assertNotEqual; public class ValueAnimatorTests extends ActivityInstrumentationTestCase2<BasicAnimatorActivity> { private static final long WAIT_TIME_OUT = 5000; private ValueAnimator a1; private ValueAnimator a2; Loading @@ -34,6 +42,9 @@ public class ValueAnimatorTests extends ActivityInstrumentationTestCase2<BasicAn private final static int A2_START_VALUE = 100; private final static int A2_END_VALUE = 200; private final static long DEFAULT_FRAME_INTERVAL = 5; //ms private final static long COMMIT_DELAY = 3; //ms public ValueAnimatorTests() { super(BasicAnimatorActivity.class); } Loading @@ -47,9 +58,9 @@ public class ValueAnimatorTests extends ActivityInstrumentationTestCase2<BasicAn @Override public void tearDown() throws Exception { super.tearDown(); a1 = null; a2 = null; super.tearDown(); } @SmallTest Loading Loading @@ -492,10 +503,265 @@ public class ValueAnimatorTests extends ActivityInstrumentationTestCase2<BasicAn }); } @SmallTest public void testUpdateListener() throws InterruptedException { final MyFrameCallbackProvider provider = new MyFrameCallbackProvider(); long sleep = 0; while (provider.mHandler == null) { Thread.sleep(POLL_INTERVAL); sleep += POLL_INTERVAL; if (sleep > WAIT_TIME_OUT) { break; } } // Either the looper has started, or timed out assertNotNull(provider.mHandler); final MyListener listener = new MyListener(); final MyUpdateListener l1 = new MyUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { long currentTime = SystemClock.uptimeMillis(); long frameDelay = provider.getFrameDelay(); if (lastUpdateTime > 0) { // Error tolerance here is one frame. assertTrue((currentTime - lastUpdateTime) < frameDelay * 2); } else { // First frame: assertTrue(listener.startCalled); assertTrue(listener.startTime > 0); assertTrue(currentTime - listener.startTime < frameDelay * 2); } super.onAnimationUpdate(animation); } }; a1.addUpdateListener(l1); a1.addListener(listener); a1.setStartDelay(100); provider.mHandler.post(new Runnable() { @Override public void run() { AnimationHandler.getInstance().setProvider(provider); a1.start(); } }); Thread.sleep(POLL_INTERVAL); assertTrue(a1.isStarted()); Thread.sleep(a1.getTotalDuration() + TOLERANCE); // Finished by now. assertFalse(a1.isStarted()); assertTrue(listener.endTime > 0); // Check the time difference between last frame and end time. assertTrue(listener.endTime >= l1.lastUpdateTime); assertTrue(listener.endTime - l1.lastUpdateTime < 2 * provider.getFrameDelay()); } @SmallTest public void testConcurrentModification() throws Throwable { // Attempt to modify list of animations as the list is being iterated final ValueAnimator a0 = ValueAnimator.ofInt(100, 200).setDuration(500); final ValueAnimator a3 = ValueAnimator.ofFloat(0, 1).setDuration(500); final ValueAnimator a4 = ValueAnimator.ofInt(200, 300).setDuration(500); final MyListener listener = new MyListener() { @Override public void onAnimationEnd(Animator anim) { super.onAnimationEnd(anim); // AnimationHandler should be iterating the list at the moment, end/cancel all // the other animations. No ConcurrentModificationException should happen. a0.cancel(); a1.end(); a3.end(); a4.cancel(); } }; a2.addListener(listener); runTestOnUiThread(new Runnable() { @Override public void run() { a0.start(); a1.start(); a2.start(); a3.start(); a4.start(); } }); runTestOnUiThread(new Runnable() { @Override public void run() { assertTrue(a0.isStarted()); assertTrue(a1.isStarted()); assertTrue(a2.isStarted()); assertTrue(a3.isStarted()); assertTrue(a4.isStarted()); } }); Thread.sleep(POLL_INTERVAL); runTestOnUiThread(new Runnable() { @Override public void run() { // End the animator that should be in the middle of the list. a2.end(); } }); Thread.sleep(POLL_INTERVAL); assertTrue(listener.endCalled); assertFalse(a0.isStarted()); assertFalse(a1.isStarted()); assertFalse(a2.isStarted()); assertFalse(a3.isStarted()); assertFalse(a4.isStarted()); } @SmallTest public void testASeek() throws Throwable { final MyListener l1 = new MyListener(); final MyListener l2 = new MyListener(); final MyUpdateListener updateListener1 = new MyUpdateListener(); final MyUpdateListener updateListener2 = new MyUpdateListener(); final float a1StartFraction = 0.2f; final float a2StartFraction = 0.3f; // Extend duration so we have plenty of latitude to manipulate the animations when they // are running. a1.setDuration(1000); a2.setDuration(1000); a1.addListener(l1); a2.addListener(l2); a1.addUpdateListener(updateListener1); a2.addUpdateListener(updateListener2); TimeInterpolator interpolator = new LinearInterpolator(); a1.setInterpolator(interpolator); a2.setInterpolator(interpolator); runTestOnUiThread(new Runnable() { @Override public void run() { assertFalse(a1.isStarted()); assertFalse(a1.isRunning()); assertFalse(a2.isStarted()); assertFalse(a2.isRunning()); // Test isRunning() and isStarted() before and after seek a1.setCurrentFraction(a1StartFraction); a2.setCurrentFraction(a2StartFraction); assertFalse(a1.isStarted()); assertFalse(a1.isRunning()); assertFalse(a2.isStarted()); assertFalse(a2.isRunning()); } }); Thread.sleep(POLL_INTERVAL); // Start animation and seek during the animation. runTestOnUiThread(new Runnable() { @Override public void run() { assertFalse(a1.isStarted()); assertFalse(a1.isRunning()); assertFalse(a2.isStarted()); assertFalse(a2.isRunning()); assertEquals(a1StartFraction, a1.getAnimatedFraction()); assertEquals(a2StartFraction, a2.getAnimatedFraction()); a1.start(); a2.start(); } }); Thread.sleep(POLL_INTERVAL); final float halfwayFraction = 0.5f; runTestOnUiThread(new Runnable() { @Override public void run() { assertTrue(l1.startCalled); assertTrue(l2.startCalled); assertFalse(l1.endCalled); assertFalse(l2.endCalled); // Check whether the animations start from the seeking fraction assertTrue(updateListener1.startFraction >= a1StartFraction); assertTrue(updateListener2.startFraction >= a2StartFraction); assertTrue(a1.isStarted()); assertTrue(a1.isRunning()); assertTrue(a2.isStarted()); assertTrue(a2.isRunning()); a1.setCurrentFraction(halfwayFraction); a2.setCurrentFraction(halfwayFraction); } }); Thread.sleep(POLL_INTERVAL); // Check that seeking during running doesn't change animation's internal state runTestOnUiThread(new Runnable() { @Override public void run() { assertTrue(l1.startCalled); assertTrue(l2.startCalled); assertFalse(l1.endCalled); assertFalse(l2.endCalled); assertTrue(a1.isStarted()); assertTrue(a1.isRunning()); assertTrue(a2.isStarted()); assertTrue(a2.isRunning()); } }); // Wait until the animators finish successfully. long wait = Math.max(a1.getTotalDuration(), a2.getTotalDuration()); Thread.sleep(wait); runTestOnUiThread(new Runnable() { @Override public void run() { // Verify that the animators have finished. assertTrue(l1.endCalled); assertTrue(l2.endCalled); assertFalse(a1.isStarted()); assertFalse(a2.isStarted()); assertFalse(a1.isRunning()); assertFalse(a2.isRunning()); } }); // Re-start animator a1 after it ends normally, and check that seek value from last run // does not affect the new run. updateListener1.reset(); runTestOnUiThread(new Runnable() { @Override public void run() { a1.start(); } }); Thread.sleep(POLL_INTERVAL); runTestOnUiThread(new Runnable() { @Override public void run() { assertTrue(updateListener1.wasRunning); assertTrue(updateListener1.startFraction >= 0); assertTrue(updateListener1.startFraction < halfwayFraction); a1.end(); } }); } class MyUpdateListener implements ValueAnimator.AnimatorUpdateListener { boolean wasRunning = false; long firstRunningFrameTime = -1; long lastUpdateTime = -1; float startFraction = 0; @Override public void onAnimationUpdate(ValueAnimator animation) { Loading @@ -503,24 +769,36 @@ public class ValueAnimatorTests extends ActivityInstrumentationTestCase2<BasicAn if (animation.isRunning() && !wasRunning) { // Delay has passed firstRunningFrameTime = lastUpdateTime; startFraction = animation.getAnimatedFraction(); wasRunning = animation.isRunning(); } } void reset() { wasRunning = false; firstRunningFrameTime = -1; lastUpdateTime = -1; startFraction = 0; } } class MyListener implements Animator.AnimatorListener { boolean startCalled = false; boolean cancelCalled = false; boolean endCalled = false; long startTime = -1; long endTime = -1; @Override public void onAnimationStart(Animator animation) { startCalled = true; startTime = SystemClock.uptimeMillis(); } @Override public void onAnimationEnd(Animator animation) { endCalled = true; endTime = SystemClock.uptimeMillis(); } @Override Loading Loading @@ -548,4 +826,72 @@ public class ValueAnimatorTests extends ActivityInstrumentationTestCase2<BasicAn resumeCalled = true; } } class MyFrameCallbackProvider implements AnimationHandler.AnimationFrameCallbackProvider { Handler mHandler = null; private final static int MSG_FRAME = 0; private long mFrameDelay = DEFAULT_FRAME_INTERVAL; private ArrayList<Choreographer.FrameCallback> mFrameCallbacks = new ArrayList<>(); final LooperThread mThread = new LooperThread(); public MyFrameCallbackProvider() { mThread.start(); } @Override public void postFrameCallback(Choreographer.FrameCallback callback) { mHandler.sendEmptyMessageDelayed(MSG_FRAME, mFrameDelay); if (!mFrameCallbacks.contains(callback)) { mFrameCallbacks.add(callback); } } @Override public void postCommitCallback(Runnable runnable) { // Run the runnable after a commit delay mHandler.postDelayed(runnable, COMMIT_DELAY); } @Override public long getFrameTime() { return SystemClock.uptimeMillis(); } @Override public long getFrameDelay() { return mFrameDelay; } @Override public void setFrameDelay(long delay) { mFrameDelay = delay; if (mFrameCallbacks.size() != 0) { mHandler.removeMessages(MSG_FRAME); mHandler.sendEmptyMessageDelayed(MSG_FRAME, mFrameDelay); } } class LooperThread extends Thread { public void run() { Looper.prepare(); mHandler = new Handler() { public void handleMessage(Message msg) { // Handle message here. switch (msg.what) { case MSG_FRAME: for (int i = 0; i < mFrameCallbacks.size(); i++) { mFrameCallbacks.get(i).doFrame(SystemClock.uptimeMillis()); } break; default: break; } } }; Looper.loop(); } } } }