| /* | |
| * Copyright 2008 Google Inc. | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may not | |
| * use this file except in compliance with the License. You may obtain a copy of | |
| * the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |
| * License for the specific language governing permissions and limitations under | |
| * the License. | |
| */ | |
| package com.google.gwt.animation.client; | |
| import com.google.gwt.animation.client.AnimationScheduler.AnimationCallback; | |
| import com.google.gwt.animation.client.testing.StubAnimationScheduler; | |
| import com.google.gwt.core.client.Duration; | |
| import com.google.gwt.junit.client.GWTTestCase; | |
| import java.util.List; | |
| /** | |
| * Tests the {@link Animation} class. | |
| * | |
| * <p> | |
| * This class uses the {@link StubAnimationScheduler} to manually trigger | |
| * callbacks. | |
| * </p> | |
| */ | |
| public class AnimationTest extends GWTTestCase { | |
| /** | |
| * A default implementation of {@link Animation} used for testing. | |
| */ | |
| private class DefaultAnimation extends Animation { | |
| protected boolean canceled = false; | |
| protected boolean completed = false; | |
| protected double curProgress = -1.0; | |
| protected boolean started = false; | |
| protected boolean updated = false; | |
| public DefaultAnimation() { | |
| super(scheduler); | |
| } | |
| /** | |
| * Assert the value of canceled. | |
| */ | |
| public void assertCancelled(boolean expected) { | |
| assertEquals(expected, canceled); | |
| } | |
| /** | |
| * Assert the value of completed. | |
| */ | |
| public void assertCompleted(boolean expected) { | |
| assertEquals(expected, completed); | |
| } | |
| /** | |
| * Assert that the progress equals the specified value. | |
| */ | |
| public void assertProgress(double expected) { | |
| assertEquals(expected, curProgress); | |
| } | |
| /** | |
| * Assert the value of started. | |
| */ | |
| public void assertStarted(boolean expected) { | |
| assertEquals(expected, started); | |
| } | |
| /** | |
| * Assert the value of updated. | |
| */ | |
| public void assertUpdated(boolean expected) { | |
| assertEquals(expected, updated); | |
| } | |
| public void reset() { | |
| canceled = false; | |
| completed = false; | |
| updated = false; | |
| started = false; | |
| curProgress = -1.0; | |
| } | |
| @Override | |
| protected void onCancel() { | |
| super.onCancel(); | |
| canceled = true; | |
| } | |
| @Override | |
| protected void onComplete() { | |
| super.onComplete(); | |
| completed = true; | |
| } | |
| @Override | |
| protected void onStart() { | |
| super.onStart(); | |
| started = true; | |
| } | |
| @Override | |
| protected void onUpdate(double progress) { | |
| updated = true; | |
| curProgress = progress; | |
| } | |
| } | |
| /** | |
| * A custom {@link Animation} used for testing. | |
| */ | |
| private class TestAnimation extends DefaultAnimation { | |
| @Override | |
| protected void onCancel() { | |
| canceled = true; | |
| } | |
| @Override | |
| protected void onComplete() { | |
| completed = true; | |
| } | |
| @Override | |
| protected void onStart() { | |
| started = true; | |
| } | |
| } | |
| /** | |
| * The maximum delay before an animation will run. Animations may run slowly | |
| * if the browser tab is not focused. | |
| * | |
| * Increase this multiplier to increase the duration of the tests, reducing | |
| * the potential of an error caused by timing issues. | |
| */ | |
| private static int DELAY_MULTIPLIER = 3000; | |
| private List<AnimationCallback> callbacks; | |
| private double curTime; | |
| private StubAnimationScheduler scheduler; | |
| @Override | |
| public String getModuleName() { | |
| return "com.google.gwt.animation.Animation"; | |
| } | |
| /** | |
| * Test canceling an {@link Animation} after it completes. | |
| */ | |
| public void testCancelAfterOnComplete() { | |
| final TestAnimation anim = new TestAnimation(); | |
| assertFalse(anim.isRunning()); | |
| anim.run(DELAY_MULTIPLIER); | |
| assertTrue(anim.isRunning()); | |
| anim.assertStarted(true); | |
| anim.assertUpdated(false); | |
| anim.assertCompleted(false); | |
| anim.assertCancelled(false); | |
| anim.reset(); | |
| // Complete the animation. | |
| // TODO(cromwellian) remove this 4000 hack after the failure is found | |
| executeLastCallbackAt(curTime + DELAY_MULTIPLIER + 4000); | |
| assertFalse(anim.isRunning()); | |
| anim.assertStarted(false); | |
| anim.assertUpdated(false); | |
| anim.assertCompleted(true); | |
| anim.assertCancelled(false); | |
| assertEquals(0, callbacks.size()); | |
| anim.reset(); | |
| // Cancel the animation. | |
| anim.cancel(); // no-op. | |
| assertFalse(anim.isRunning()); | |
| anim.assertStarted(false); | |
| anim.assertUpdated(false); | |
| anim.assertProgress(-1); | |
| anim.assertCancelled(false); | |
| } | |
| /** | |
| * Test canceling an {@link Animation} before onStart is called. | |
| */ | |
| public void testCancelBeforeOnStart() { | |
| final TestAnimation anim = new TestAnimation(); | |
| assertFalse(anim.isRunning()); | |
| // Run the animation in the future. | |
| anim.run(DELAY_MULTIPLIER, curTime + 1000); | |
| assertTrue(anim.isRunning()); | |
| anim.assertStarted(false); | |
| anim.assertUpdated(false); | |
| anim.assertCompleted(false); | |
| anim.assertCancelled(false); | |
| assertEquals(1, callbacks.size()); | |
| anim.reset(); | |
| // Cancel the animation before it starts. | |
| anim.cancel(); | |
| assertFalse(anim.isRunning()); | |
| anim.assertStarted(false); | |
| anim.assertUpdated(false); | |
| anim.assertCompleted(false); | |
| anim.assertCancelled(true); | |
| assertEquals(0, callbacks.size()); | |
| } | |
| /** | |
| * Test canceling an {@link Animation} between updates. | |
| */ | |
| public void testCancelBetweenUpdates() { | |
| TestAnimation anim = new TestAnimation(); | |
| assertFalse(anim.isRunning()); | |
| anim.run(10 * DELAY_MULTIPLIER); | |
| assertTrue(anim.isRunning()); | |
| anim.assertStarted(true); | |
| anim.assertUpdated(false); | |
| anim.assertCompleted(false); | |
| anim.assertCancelled(false); | |
| anim.reset(); | |
| // Update the animation. | |
| executeLastCallbackAt(curTime + DELAY_MULTIPLIER); | |
| assertTrue(anim.isRunning()); | |
| anim.assertStarted(false); | |
| anim.assertUpdated(true); | |
| anim.assertCompleted(false); | |
| anim.assertCancelled(false); | |
| anim.reset(); | |
| // Cancel the animation. | |
| assertEquals(1, callbacks.size()); | |
| anim.cancel(); | |
| assertFalse(anim.isRunning()); | |
| anim.assertStarted(false); | |
| anim.assertUpdated(false); | |
| anim.assertCompleted(false); | |
| anim.assertCancelled(true); | |
| anim.assertProgress(-1.0); | |
| assertEquals(0, callbacks.size()); | |
| } | |
| /** | |
| * Test canceling an {@link Animation} within onComplete. | |
| */ | |
| public void testCancelDuringOnComplete() { | |
| final TestAnimation anim = new TestAnimation() { | |
| @Override | |
| protected void onComplete() { | |
| super.onComplete(); | |
| assertStarted(false); | |
| assertUpdated(false); | |
| assertCompleted(true); | |
| assertCancelled(false); | |
| reset(); | |
| // Cancel the animation. | |
| cancel(); // no-op. | |
| } | |
| }; | |
| assertFalse(anim.isRunning()); | |
| // Run the animation. | |
| anim.run(DELAY_MULTIPLIER); | |
| assertTrue(anim.isRunning()); | |
| anim.assertStarted(true); | |
| anim.assertUpdated(false); | |
| anim.assertCompleted(false); | |
| anim.assertCancelled(false); | |
| anim.reset(); | |
| // Force the animation to complete. | |
| executeLastCallbackAt(curTime + DELAY_MULTIPLIER + 100); | |
| anim.assertStarted(false); | |
| anim.assertUpdated(false); | |
| anim.assertCompleted(false); | |
| anim.assertCancelled(false); | |
| assertEquals(0, callbacks.size()); | |
| assertFalse(anim.isRunning()); | |
| } | |
| /** | |
| * Test canceling an {@link Animation} within onStart. | |
| */ | |
| public void testCancelDuringOnStart() { | |
| final TestAnimation anim = new TestAnimation() { | |
| @Override | |
| protected void onStart() { | |
| super.onStart(); | |
| assertStarted(true); | |
| assertUpdated(false); | |
| assertCompleted(false); | |
| assertCancelled(false); | |
| reset(); | |
| // Cancel the animation. | |
| cancel(); | |
| } | |
| }; | |
| assertFalse(anim.isRunning()); | |
| // Run the animation. | |
| anim.run(DELAY_MULTIPLIER); | |
| assertFalse(anim.isRunning()); | |
| anim.assertStarted(false); | |
| anim.assertUpdated(false); | |
| anim.assertCancelled(true); | |
| anim.assertCompleted(false); | |
| assertEquals(0, callbacks.size()); | |
| } | |
| /** | |
| * Test canceling an {@link Animation} during an update. | |
| */ | |
| public void testCancelDuringOnUpdate() { | |
| final TestAnimation anim = new TestAnimation() { | |
| @Override | |
| protected void onUpdate(double progress) { | |
| super.onUpdate(progress); | |
| assertStarted(false); | |
| assertUpdated(true); | |
| assertCompleted(false); | |
| assertCancelled(false); | |
| reset(); | |
| // Cancel the test while it is running. | |
| cancel(); | |
| } | |
| }; | |
| assertFalse(anim.isRunning()); | |
| // Run the animation. | |
| anim.run(10 * DELAY_MULTIPLIER); | |
| assertTrue(anim.isRunning()); | |
| anim.assertStarted(true); | |
| anim.assertUpdated(false); | |
| anim.assertCompleted(false); | |
| anim.assertCancelled(false); | |
| anim.assertProgress(-1.0); | |
| anim.reset(); | |
| // Force the update. | |
| executeLastCallbackAt(curTime + DELAY_MULTIPLIER); | |
| anim.assertStarted(false); | |
| anim.assertUpdated(false); | |
| anim.assertCompleted(false); | |
| anim.assertCancelled(true); | |
| anim.assertProgress(-1.0); | |
| assertEquals(0, callbacks.size()); | |
| } | |
| /** | |
| * Test the default implementations of events in {@link Animation}. | |
| */ | |
| public void testDefaultAnimation() { | |
| // Verify initial state | |
| final DefaultAnimation anim = new DefaultAnimation(); | |
| assertFalse(anim.isRunning()); | |
| anim.assertStarted(false); | |
| anim.assertUpdated(false); | |
| anim.assertCancelled(false); | |
| anim.assertCompleted(false); | |
| // Starting an animation calls onUpdate(interpolate(0.0)) | |
| anim.reset(); | |
| anim.onStart(); | |
| anim.assertProgress(0.0); | |
| anim.assertStarted(true); | |
| anim.assertCompleted(false); | |
| anim.assertCancelled(false); | |
| // Completing an animation calls onUpdate(interpolate(1.0)) | |
| anim.reset(); | |
| anim.onComplete(); | |
| anim.assertProgress(1.0); | |
| anim.assertStarted(false); | |
| anim.assertCompleted(true); | |
| anim.assertCancelled(false); | |
| // Canceling an animation that is not running does not call onStart or | |
| // onComplete | |
| anim.reset(); | |
| anim.onCancel(); | |
| anim.assertProgress(-1.0); | |
| anim.assertStarted(false); | |
| anim.assertCompleted(false); | |
| anim.assertCancelled(true); | |
| // Canceling an animation before it starts does not call onStart or | |
| // onComplete | |
| anim.reset(); | |
| anim.run(10 * DELAY_MULTIPLIER, curTime + DELAY_MULTIPLIER); | |
| assertTrue(anim.isRunning()); | |
| anim.cancel(); | |
| assertFalse(anim.isRunning()); | |
| anim.assertProgress(-1.0); | |
| anim.assertStarted(false); | |
| anim.assertCompleted(false); | |
| anim.assertCancelled(true); | |
| } | |
| /** | |
| * Test that restarting an {@link Animation} within onComplete does not break. | |
| * See issue 5639. | |
| */ | |
| public void testRunDuringOnComplete() { | |
| final TestAnimation anim = new TestAnimation() { | |
| @Override | |
| protected void onComplete() { | |
| super.onComplete(); | |
| assertStarted(false); | |
| assertUpdated(false); | |
| assertCompleted(true); | |
| assertCancelled(false); | |
| reset(); | |
| // Run the animation. | |
| run(DELAY_MULTIPLIER); | |
| } | |
| }; | |
| assertFalse(anim.isRunning()); | |
| // Run the animation. | |
| anim.run(DELAY_MULTIPLIER); | |
| assertTrue(anim.isRunning()); | |
| anim.assertStarted(true); | |
| anim.assertUpdated(false); | |
| anim.assertCompleted(false); | |
| anim.assertCancelled(false); | |
| anim.reset(); | |
| // Force the animation to complete. | |
| executeLastCallbackAt(curTime + DELAY_MULTIPLIER + 100); | |
| anim.assertStarted(true); | |
| anim.assertUpdated(false); | |
| anim.assertCompleted(false); | |
| anim.assertCancelled(false); | |
| assertEquals(1, callbacks.size()); | |
| } | |
| /** | |
| * Test that an animation runs in the future. | |
| */ | |
| public void testRunFuture() { | |
| final TestAnimation anim = new TestAnimation(); | |
| assertFalse(anim.isRunning()); | |
| anim.run(2 * DELAY_MULTIPLIER, curTime + 2 * DELAY_MULTIPLIER); | |
| assertTrue(anim.isRunning()); | |
| anim.assertStarted(false); | |
| anim.assertUpdated(false); | |
| anim.assertCompleted(false); | |
| anim.reset(); | |
| // Update, but still before the start time. | |
| executeLastCallbackAt(curTime + DELAY_MULTIPLIER); | |
| anim.assertStarted(false); | |
| anim.assertUpdated(false); | |
| anim.assertCompleted(false); | |
| anim.reset(); | |
| // Start the animation. | |
| executeLastCallbackAt(curTime + 2 * DELAY_MULTIPLIER); | |
| anim.assertStarted(true); | |
| anim.assertUpdated(false); | |
| anim.assertCompleted(false); | |
| anim.reset(); | |
| // Update the animation. | |
| executeLastCallbackAt(curTime + 3 * DELAY_MULTIPLIER); | |
| anim.assertStarted(false); | |
| anim.assertUpdated(true); | |
| anim.assertCompleted(false); | |
| anim.reset(); | |
| // Complete the animation. | |
| executeLastCallbackAt(curTime + 4 * DELAY_MULTIPLIER + 100); | |
| anim.assertStarted(false); | |
| anim.assertUpdated(false); | |
| anim.assertCompleted(true); | |
| assertFalse(anim.isRunning()); | |
| } | |
| /** | |
| * Test that an animation runs synchronously if its duration is 0. | |
| */ | |
| public void testRunNow() { | |
| final TestAnimation anim = new TestAnimation(); | |
| assertFalse(anim.isRunning()); | |
| anim.run(2 * DELAY_MULTIPLIER); | |
| assertTrue(anim.isRunning()); | |
| anim.assertStarted(true); | |
| anim.assertUpdated(false); | |
| anim.assertCompleted(false); | |
| anim.reset(); | |
| // Update the progress. | |
| executeLastCallbackAt(curTime + DELAY_MULTIPLIER); | |
| anim.assertStarted(false); | |
| anim.assertUpdated(true); | |
| anim.assertCompleted(false); | |
| anim.reset(); | |
| // Complete the animation. | |
| executeLastCallbackAt(curTime + 2 * DELAY_MULTIPLIER + 100); | |
| anim.assertStarted(false); | |
| anim.assertUpdated(false); | |
| anim.assertCompleted(true); | |
| assertFalse(anim.isRunning()); | |
| } | |
| /** | |
| * Test running an animation that started in the past. | |
| */ | |
| public void testRunPast() { | |
| final TestAnimation anim = new TestAnimation(); | |
| assertFalse(anim.isRunning()); | |
| anim.run(3 * DELAY_MULTIPLIER, curTime - DELAY_MULTIPLIER); | |
| assertTrue(anim.isRunning()); | |
| anim.assertStarted(true); | |
| anim.assertUpdated(false); | |
| anim.assertCompleted(false); | |
| anim.reset(); | |
| // Update the progress. | |
| executeLastCallbackAt(curTime + DELAY_MULTIPLIER); | |
| anim.assertStarted(false); | |
| anim.assertUpdated(true); | |
| anim.assertCompleted(false); | |
| anim.reset(); | |
| // Complete the animation. | |
| executeLastCallbackAt(curTime + 2 * DELAY_MULTIPLIER + 100); | |
| anim.assertStarted(false); | |
| anim.assertUpdated(false); | |
| anim.assertCompleted(true); | |
| assertFalse(anim.isRunning()); | |
| } | |
| /** | |
| * Test running an animation that started and finished in the past. | |
| */ | |
| public void testRunPaster() { | |
| final TestAnimation anim = new TestAnimation(); | |
| assertFalse(anim.isRunning()); | |
| anim.run(DELAY_MULTIPLIER, curTime - 2 * DELAY_MULTIPLIER); | |
| anim.assertStarted(true); | |
| anim.assertUpdated(false); | |
| anim.assertCompleted(true); | |
| assertFalse(anim.isRunning()); | |
| } | |
| @Override | |
| protected void gwtSetUp() throws Exception { | |
| scheduler = new StubAnimationScheduler(); | |
| callbacks = scheduler.getAnimationCallbacks(); | |
| curTime = Duration.currentTimeMillis(); | |
| } | |
| @Override | |
| protected void gwtTearDown() throws Exception { | |
| scheduler = null; | |
| callbacks = null; | |
| } | |
| /** | |
| * Execute the last callback requested from the scheduler at the specified | |
| * time. | |
| * | |
| * @param timestamp the time to pass to the callback | |
| */ | |
| private void executeLastCallbackAt(double timestamp) { | |
| assertTrue(callbacks.size() > 0); | |
| AnimationCallback callback = callbacks.remove(callbacks.size() - 1); | |
| callback.execute(timestamp); | |
| } | |
| } |