1 /*
   2  * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package test.javafx.animation;
  27 
  28 import com.sun.javafx.tk.Toolkit;
  29 import test.com.sun.scenario.animation.shared.ClipEnvelopeMock;
  30 import java.io.IOException;
  31 import java.io.OutputStream;
  32 import java.io.PrintStream;
  33 import javafx.animation.Animation;
  34 import javafx.animation.Animation.Status;
  35 import javafx.event.ActionEvent;
  36 import javafx.event.EventHandler;
  37 import javafx.util.Duration;
  38 import org.junit.After;
  39 import static org.junit.Assert.assertEquals;
  40 import static org.junit.Assert.assertFalse;
  41 import static org.junit.Assert.assertTrue;
  42 import org.junit.Before;
  43 import org.junit.Test;
  44 
  45 public class AnimationTest {
  46 
  47     private static final Duration ONE_SEC = Duration.millis(1000);
  48     private static final Duration TWO_SECS = Duration.millis(2000);
  49     private static final Duration THREE_SECS = Duration.millis(3000);
  50     private static final Duration FOUR_SECS = Duration.millis(4000);
  51 
  52     private static final double DEFAULT_RATE = 1.0;
  53     private static final int DEFAULT_REPEAT_COUNT = 1;
  54     private static final boolean DEFAULT_AUTO_REVERSE = false;
  55 
  56     private static final double EPSILON = 1e-12;
  57 
  58     private AbstractMasterTimerMock timer;
  59     private AnimationImpl animation;
  60     private ClipEnvelopeMock clipEnvelope;
  61 
  62     @Before
  63     public void setUp() {
  64         timer = new AbstractMasterTimerMock();
  65         clipEnvelope = new ClipEnvelopeMock();
  66         animation = new AnimationImpl(timer, clipEnvelope, 1);
  67         animation.shim_setCycleDuration(ONE_SEC);
  68         clipEnvelope.setAnimation(animation);
  69     }
  70 
  71     @After
  72     public void tearDown() {
  73         animation.stop();
  74     }
  75 
  76     @Test
  77     public void testConstructors() {
  78         final Animation animation0 = new AnimationImpl();
  79         assertEquals(DEFAULT_RATE, animation0.getRate(), EPSILON);
  80         assertEquals(0.0, animation0.getCurrentRate(), EPSILON);
  81         assertEquals(Duration.ZERO, animation0.getCycleDuration());
  82         assertEquals(Duration.ZERO, animation0.getTotalDuration());
  83         assertEquals(Duration.ZERO, animation0.getCurrentTime());
  84         assertEquals(DEFAULT_REPEAT_COUNT, animation0.getCycleCount());
  85         assertEquals(DEFAULT_AUTO_REVERSE, animation0.isAutoReverse());
  86         assertEquals(Status.STOPPED, animation0.getStatus());
  87         assertEquals(6000.0 / Toolkit.getToolkit().getMasterTimer().getDefaultResolution(), animation0.getTargetFramerate(), EPSILON);
  88         assertEquals(null, animation0.getOnFinished());
  89         assertEquals(0, animation0.getCuePoints().size());
  90 
  91         final Animation animation1 = new AnimationImpl(timer, clipEnvelope, 600);
  92         assertEquals(10.0, animation1.getTargetFramerate(), EPSILON);
  93     }
  94 
  95     @Test
  96     public void testReadOnlyProperties() {
  97         // currentRate
  98         assertEquals("currentRate", animation.currentRateProperty().getName());
  99         assertEquals(animation, animation.currentRateProperty().getBean());
 100 
 101         // cycleDuration
 102         assertEquals("cycleDuration", animation.cycleDurationProperty().getName());
 103         assertEquals(animation, animation.cycleDurationProperty().getBean());
 104 
 105         // totalDuration
 106         assertEquals("totalDuration", animation.totalDurationProperty().getName());
 107         assertEquals(animation, animation.totalDurationProperty().getBean());
 108 
 109         // currentTime
 110         assertEquals("currentTime", animation.currentTimeProperty().getName());
 111         assertEquals(animation, animation.currentTimeProperty().getBean());
 112 
 113         // status
 114         assertEquals("status", animation.statusProperty().getName());
 115         assertEquals(animation, animation.statusProperty().getBean());
 116 
 117     }
 118 
 119     @Test
 120     public void testCalculationOfTotalDuration() {
 121         // 1000ms
 122         assertEquals(ONE_SEC, animation.getTotalDuration());
 123         animation.setCycleCount(0);
 124         assertEquals(ONE_SEC, animation.getTotalDuration());
 125         animation.setCycleCount(7);
 126         assertEquals(ONE_SEC.multiply(7), animation.getTotalDuration());
 127         animation.setCycleCount(Animation.INDEFINITE);
 128         assertEquals(Duration.INDEFINITE, animation.getTotalDuration());
 129         animation.setCycleCount(1);
 130 
 131         // Duration.ZERO
 132         animation.shim_setCycleDuration(Duration.ZERO);
 133         assertEquals(Duration.ZERO, animation.getTotalDuration());
 134         animation.setCycleCount(0);
 135         assertEquals(Duration.ZERO, animation.getTotalDuration());
 136         animation.setCycleCount(7);
 137         assertEquals(Duration.ZERO, animation.getTotalDuration());
 138         animation.setCycleCount(Animation.INDEFINITE);
 139         assertEquals(Duration.ZERO, animation.getTotalDuration());
 140         animation.setCycleCount(1);
 141 
 142         // Duration.INDEFINITE
 143         animation.shim_setCycleDuration(Duration.INDEFINITE);
 144         assertEquals(Duration.INDEFINITE, animation.getTotalDuration());
 145         animation.setCycleCount(0);
 146         assertEquals(Duration.INDEFINITE, animation.getTotalDuration());
 147         animation.setCycleCount(7);
 148         assertEquals(Duration.INDEFINITE, animation.getTotalDuration());
 149         animation.setCycleCount(Animation.INDEFINITE);
 150         assertEquals(Duration.INDEFINITE, animation.getTotalDuration());
 151         animation.setCycleCount(1);
 152     }
 153 
 154     @Test
 155     public void testDecreaseTotalDuration() {
 156         animation.jumpTo(ONE_SEC);
 157         animation.shim_setCycleDuration(ONE_SEC.divide(2));
 158         assertEquals(ONE_SEC.divide(2), animation.getCurrentTime());
 159         animation.shim_setCycleDuration(Duration.ZERO);
 160         assertEquals(Duration.ZERO, animation.getCurrentTime());
 161     }
 162 
 163     @Test
 164     public void testJumpTo() {
 165         animation.shim_setCycleDuration(TWO_SECS);
 166         assertEquals(TWO_SECS,animation.getCycleDuration());
 167 
 168         // cycleCount = 1
 169         animation.jumpTo(ONE_SEC);
 170         assertEquals(ONE_SEC, animation.getCurrentTime());
 171         assertEquals(6000, clipEnvelope.getLastJumpTo());
 172         animation.jumpTo(TWO_SECS);
 173         assertEquals(TWO_SECS, animation.getCurrentTime());
 174         assertEquals(12000, clipEnvelope.getLastJumpTo());
 175         animation.jumpTo(Duration.ZERO);
 176         assertEquals(Duration.ZERO, animation.getCurrentTime());
 177         assertEquals(0, clipEnvelope.getLastJumpTo());
 178         animation.jumpTo(TWO_SECS.add(Duration.ONE));
 179         assertEquals(TWO_SECS, animation.getCurrentTime());
 180         assertEquals(12000, clipEnvelope.getLastJumpTo());
 181         animation.jumpTo(Duration.ONE.negate());
 182         assertEquals(Duration.ZERO, animation.getCurrentTime());
 183         assertEquals(0, clipEnvelope.getLastJumpTo());
 184 
 185         // cycleCount = 2
 186         animation.setCycleCount(2);
 187         animation.jumpTo(ONE_SEC);
 188         assertEquals(ONE_SEC, animation.getCurrentTime());
 189         assertEquals(6000, clipEnvelope.getLastJumpTo());
 190         animation.jumpTo(TWO_SECS);
 191         assertEquals(TWO_SECS, animation.getCurrentTime());
 192         assertEquals(12000, clipEnvelope.getLastJumpTo());
 193         animation.jumpTo(THREE_SECS);
 194         assertEquals(ONE_SEC, animation.getCurrentTime());
 195         assertEquals(18000, clipEnvelope.getLastJumpTo());
 196         animation.jumpTo(FOUR_SECS);
 197         assertEquals(TWO_SECS, animation.getCurrentTime());
 198         assertEquals(24000, clipEnvelope.getLastJumpTo());
 199         animation.jumpTo(Duration.ZERO);
 200         assertEquals(Duration.ZERO, animation.getCurrentTime());
 201         assertEquals(0, clipEnvelope.getLastJumpTo());
 202         animation.jumpTo(FOUR_SECS.add(Duration.ONE));
 203         assertEquals(TWO_SECS, animation.getCurrentTime());
 204         assertEquals(24000, clipEnvelope.getLastJumpTo());
 205         animation.jumpTo(Duration.ONE.negate());
 206         assertEquals(Duration.ZERO, animation.getCurrentTime());
 207         assertEquals(0, clipEnvelope.getLastJumpTo());
 208     }
 209 
 210     @Test
 211     public void testJumpTo_ZeroLengthAnimation() {
 212         animation.shim_setCycleDuration(Duration.ZERO);
 213 
 214         // cycleCount = 1
 215         animation.jumpTo(Duration.ZERO);
 216         assertEquals(Duration.ZERO, animation.getCurrentTime());
 217         assertEquals(0, clipEnvelope.getLastJumpTo());
 218         animation.jumpTo(ONE_SEC);
 219         assertEquals(Duration.ZERO, animation.getCurrentTime());
 220         assertEquals(0, clipEnvelope.getLastJumpTo());
 221 
 222         // cycleCount = 2
 223         animation.setCycleCount(2);
 224         animation.jumpTo(Duration.ZERO);
 225         assertEquals(Duration.ZERO, animation.getCurrentTime());
 226         assertEquals(0, clipEnvelope.getLastJumpTo());
 227         animation.jumpTo(ONE_SEC);
 228         assertEquals(Duration.ZERO, animation.getCurrentTime());
 229         assertEquals(0, clipEnvelope.getLastJumpTo());
 230     }
 231 
 232     @Test
 233     public void testDurationRoundingError() {
 234         final Duration duration = Duration.millis(0.01);
 235         animation.shim_setCycleDuration(duration);
 236         assertTrue(animation.getCycleDuration().greaterThan(Duration.ZERO));
 237 
 238         // should not be startable
 239         assertFalse(animation.impl_startable(true));
 240 
 241         // jump
 242         animation.jumpTo(Duration.ZERO);
 243         assertEquals(Duration.ZERO, animation.getCurrentTime());
 244         assertEquals(0, clipEnvelope.getLastJumpTo());
 245     }
 246 
 247     @Test(expected=NullPointerException.class)
 248     public void testJumpTo_Null() {
 249         animation.jumpTo((Duration)null);
 250     }
 251 
 252     @Test(expected=IllegalArgumentException.class)
 253     public void testJumpTo_UNKNOWN() {
 254         animation.jumpTo(Duration.UNKNOWN);
 255     }
 256 
 257     @Test
 258     public void testJumpToCuePoint_Default() {
 259         animation.getCuePoints().put("ONE_SEC", ONE_SEC);
 260         animation.getCuePoints().put("THREE_SECS", THREE_SECS);
 261         animation.shim_setCycleDuration(TWO_SECS);
 262 
 263         // normal jumps
 264         animation.jumpTo("end");
 265         assertEquals(TWO_SECS, animation.getCurrentTime());
 266         animation.jumpTo("start");
 267         assertEquals(Duration.ZERO, animation.getCurrentTime());
 268         animation.jumpTo("ONE_SEC");
 269         assertEquals(ONE_SEC, animation.getCurrentTime());
 270 
 271         // jump to non-existing cue-point
 272         animation.jumpTo("undefined");
 273         assertEquals(ONE_SEC, animation.getCurrentTime());
 274 
 275         // jump to cue-point behind end of animation
 276         animation.jumpTo("THREE_SECS");
 277         assertEquals(TWO_SECS, animation.getCurrentTime());
 278     }
 279 
 280     @Test
 281     public void testJumpToCuePoint_ZeroLengthAnimation() {
 282         animation.getCuePoints().put("ONE_SEC", ONE_SEC);
 283         animation.shim_setCycleDuration(Duration.ZERO);
 284 
 285         animation.jumpTo("start");
 286         assertEquals(Duration.ZERO, animation.getCurrentTime());
 287         animation.jumpTo("end");
 288         assertEquals(Duration.ZERO, animation.getCurrentTime());
 289         animation.jumpTo("ONE_SEC");
 290         assertEquals(Duration.ZERO, animation.getCurrentTime());
 291     }
 292 
 293     @Test(expected=NullPointerException.class)
 294     public void testJumpToCuePoint_Null() {
 295         animation.jumpTo((String)null);
 296     }
 297 
 298     @Test
 299     public void testPlay() {
 300         final OnFinishedListener listener = new OnFinishedListener();
 301         animation.setOnFinished(listener);
 302 
 303         // stopped timeline
 304         listener.wasCalled = false;
 305         animation.play();
 306         assertEquals(Status.RUNNING, animation.getStatus());
 307         assertFalse(listener.wasCalled);
 308         assertTrue(timer.containsPulseReceiver(animation.shim_pulseReceiver()));
 309 
 310         // calling play on playing timeline
 311         animation.play();
 312         assertEquals(Status.RUNNING, animation.getStatus());
 313         assertFalse(listener.wasCalled);
 314         assertTrue(timer.containsPulseReceiver(animation.shim_pulseReceiver()));
 315         animation.stop();
 316 
 317         // stopped timeline, rate = 0
 318         listener.wasCalled = false;
 319         animation.setRate(0.0);
 320         animation.play();
 321         assertEquals(Status.RUNNING, animation.getStatus());
 322         assertFalse(listener.wasCalled);
 323         assertFalse(timer.containsPulseReceiver(animation.shim_pulseReceiver()));
 324         animation.stop();
 325         animation.setRate(1.0);
 326 
 327         // stopped timeline, cycleDuration = 0
 328         listener.wasCalled = false;
 329         animation.shim_setCycleDuration(Duration.ZERO);
 330         animation.play();
 331         assertEquals(Status.STOPPED, animation.getStatus());
 332         assertTrue(listener.wasCalled);
 333         assertFalse(timer.containsPulseReceiver(animation.shim_pulseReceiver()));
 334         animation.stop();
 335         animation.shim_setCycleDuration(ONE_SEC);
 336 
 337         // paused timeline
 338         animation.play();
 339         animation.pause();
 340         animation.play();
 341         assertEquals(Status.RUNNING, animation.getStatus());
 342         assertTrue(timer.containsPulseReceiver(animation.shim_pulseReceiver()));
 343         animation.stop();
 344 
 345         // paused timeline, rate = 0
 346         animation.play();
 347         animation.pause();
 348         animation.setRate(0.0);
 349         animation.play();
 350         assertEquals(Status.RUNNING, animation.getStatus());
 351         assertFalse(timer.containsPulseReceiver(animation.shim_pulseReceiver()));
 352     }
 353 
 354     @Test
 355     public void testStop() {
 356         // stopped timeline
 357         animation.jumpTo(ONE_SEC);
 358         animation.stop();
 359         assertEquals(Status.STOPPED, animation.getStatus());
 360         assertEquals(0.0, animation.getCurrentRate(), EPSILON);
 361         assertEquals(ONE_SEC, animation.getCurrentTime());
 362         assertFalse(timer.containsPulseReceiver(animation.shim_pulseReceiver()));
 363 
 364         // playing timeline
 365         animation.jumpTo(ONE_SEC);
 366         animation.play();
 367         animation.stop();
 368         assertEquals(Status.STOPPED, animation.getStatus());
 369         assertEquals(0.0, animation.getCurrentRate(), EPSILON);
 370         assertEquals(Duration.ZERO, animation.getCurrentTime());
 371         assertFalse(timer.containsPulseReceiver(animation.shim_pulseReceiver()));
 372 
 373         // paused timeline
 374         animation.jumpTo(ONE_SEC);
 375         animation.play();
 376         animation.pause();
 377         animation.stop();
 378         assertEquals(Status.STOPPED, animation.getStatus());
 379         assertEquals(0.0, animation.getCurrentRate(), EPSILON);
 380         assertEquals(Duration.ZERO, animation.getCurrentTime());
 381         assertFalse(timer.containsPulseReceiver(animation.shim_pulseReceiver()));
 382     }
 383 
 384     @Test
 385     public void testPause() {
 386         // stopped timeline
 387         animation.jumpTo(ONE_SEC);
 388         animation.pause();
 389         assertEquals(Status.STOPPED, animation.getStatus());
 390         assertEquals(0.0, animation.getCurrentRate(), EPSILON);
 391         assertFalse(timer.containsPulseReceiver(animation.shim_pulseReceiver()));
 392 
 393         // playing timeline
 394         animation.jumpTo(ONE_SEC);
 395         animation.play();
 396         animation.pause();
 397         assertEquals(Status.PAUSED, animation.getStatus());
 398         assertEquals(0.0, animation.getCurrentRate(), EPSILON);
 399         assertFalse(timer.containsPulseReceiver(animation.shim_pulseReceiver()));
 400 
 401         // paused timeline
 402         animation.jumpTo(ONE_SEC);
 403         animation.play();
 404         animation.pause();
 405         animation.pause();
 406         assertEquals(Status.PAUSED, animation.getStatus());
 407         assertEquals(0.0, animation.getCurrentRate(), EPSILON);
 408         assertFalse(timer.containsPulseReceiver(animation.shim_pulseReceiver()));
 409     }
 410 
 411     @Test
 412     public void testStart() {
 413         // cycleDuration = 1000ms
 414         assertTrue(animation.impl_startable(true));
 415         animation.impl_start(true);
 416         assertEquals(Status.RUNNING, animation.getStatus());
 417         assertEquals(1.0, animation.getCurrentRate(), EPSILON);
 418         assertEquals(6000, clipEnvelope.getTimelineTicks());
 419         assertEquals(1.0, clipEnvelope.getRate(), EPSILON);
 420         assertEquals(false, clipEnvelope.getAutoReverse());
 421         assertEquals(1, clipEnvelope.getCycleCount());
 422         animation.shim_impl_finished();
 423 
 424         // change all values and try again
 425         animation.shim_setCycleDuration(TWO_SECS);
 426         animation.setRate(-2.0);
 427         animation.setAutoReverse(true);
 428         animation.setCycleCount(Animation.INDEFINITE);
 429         assertTrue(animation.impl_startable(true));
 430         animation.impl_start(true);
 431         assertEquals(Status.RUNNING, animation.getStatus());
 432         assertEquals(-2.0, animation.getCurrentRate(), EPSILON);
 433         assertEquals(12000, clipEnvelope.getTimelineTicks());
 434         assertEquals(-2.0, clipEnvelope.getRate(), EPSILON);
 435         assertEquals(true, clipEnvelope.getAutoReverse());
 436         assertEquals(Animation.INDEFINITE, clipEnvelope.getCycleCount());
 437         animation.shim_impl_finished();
 438 
 439         // cycleDuration = 0
 440         animation.shim_setCycleDuration(Duration.ZERO);
 441         assertFalse(animation.impl_startable(true));
 442     }
 443 
 444     @Test
 445     public void testChangeCycleDurationAfterFinish_RT32657() {
 446         animation.shim_setCycleDuration(TWO_SECS);
 447         animation.play();
 448         assertEquals(Status.RUNNING, animation.getStatus());
 449         assertEquals(Duration.ZERO, animation.getCurrentTime());
 450         animation.impl_setCurrentTicks(12000);
 451         assertEquals(TWO_SECS, animation.getCurrentTime());
 452         animation.shim_impl_finished();
 453 
 454         animation.shim_setCycleDuration(ONE_SEC);
 455         animation.play();
 456         assertEquals(Status.RUNNING, animation.getStatus());
 457         assertEquals(Duration.ZERO, animation.getCurrentTime());
 458     }
 459 
 460     @Test
 461     public void testFinished() {
 462         final OnFinishedListener listener = new OnFinishedListener();
 463         animation.setOnFinished(listener);
 464 
 465         // stopped timeline
 466         animation.shim_impl_finished();
 467         assertEquals(Status.STOPPED, animation.getStatus());
 468         assertEquals(0.0, animation.getCurrentRate(), EPSILON);
 469         assertTrue(listener.wasCalled);
 470 
 471         // playing timeline
 472         animation.play();
 473         animation.shim_impl_finished();
 474         assertEquals(Status.STOPPED, animation.getStatus());
 475         assertEquals(0.0, animation.getCurrentRate(), EPSILON);
 476         assertTrue(listener.wasCalled);
 477 
 478         // paused timeline
 479         animation.play();
 480         animation.pause();
 481         animation.shim_impl_finished();
 482         assertEquals(Status.STOPPED, animation.getStatus());
 483         assertEquals(0.0, animation.getCurrentRate(), EPSILON);
 484         assertTrue(listener.wasCalled);
 485     }
 486 
 487     @Test
 488     public void testFinished_ThrowsException() {
 489         final OnFinishedExceptionListener listener = new OnFinishedExceptionListener();
 490         final PrintStream defaultErrorStream = System.err;
 491         final PrintStream nirvana = new PrintStream(new OutputStream() {
 492             @Override
 493             public void write(int i) throws IOException {
 494             }
 495         });
 496         animation.setOnFinished(listener);
 497 
 498         // stopped timeline
 499         try {
 500             System.setErr(nirvana);
 501         } catch (SecurityException ex) {
 502             // ignore
 503         }
 504         animation.shim_impl_finished();
 505         try {
 506             System.setErr(defaultErrorStream);
 507         } catch (SecurityException ex) {
 508             // ignore
 509         }
 510         assertEquals(Status.STOPPED, animation.getStatus());
 511         assertEquals(0.0, animation.getCurrentRate(), EPSILON);
 512         assertTrue(listener.wasCalled);
 513 
 514         // playing timeline
 515         animation.play();
 516         try {
 517             System.setErr(nirvana);
 518         } catch (SecurityException ex) {
 519             // ignore
 520         }
 521         animation.shim_impl_finished();
 522         try {
 523             System.setErr(defaultErrorStream);
 524         } catch (SecurityException ex) {
 525             // ignore
 526         }
 527         assertEquals(Status.STOPPED, animation.getStatus());
 528         assertEquals(0.0, animation.getCurrentRate(), EPSILON);
 529         assertTrue(listener.wasCalled);
 530 
 531         // paused timeline
 532         animation.play();
 533         animation.pause();
 534         try {
 535             System.setErr(nirvana);
 536         } catch (SecurityException ex) {
 537             // ignore
 538         }
 539         animation.shim_impl_finished();
 540         try {
 541             System.setErr(defaultErrorStream);
 542         } catch (SecurityException ex) {
 543             // ignore
 544         }
 545         assertEquals(Status.STOPPED, animation.getStatus());
 546         assertEquals(0.0, animation.getCurrentRate(), EPSILON);
 547         assertTrue(listener.wasCalled);
 548     }
 549 
 550     @Test
 551     public void testFullSpeedResolution() {
 552         final int resolution = Toolkit.getToolkit().getMasterTimer().getDefaultResolution();
 553 
 554         // send pulse
 555         animation.impl_timePulse(4 * resolution);
 556         assertEquals(4 * resolution, clipEnvelope.getLastTimePulse());
 557 
 558         // send half pulse
 559         animation.impl_timePulse(Math.round(4.5 * resolution));
 560         assertEquals(Math.round(4.5 * resolution), clipEnvelope.getLastTimePulse());
 561 
 562         // send full pulse
 563         animation.impl_timePulse(Math.round(5.5 * resolution));
 564         assertEquals(Math.round(5.5 * resolution), clipEnvelope.getLastTimePulse());
 565 
 566         // send half pulse
 567         animation.impl_timePulse(6 * resolution);
 568         assertEquals(6 * resolution, clipEnvelope.getLastTimePulse());
 569 
 570     }
 571 
 572     @Test
 573     public void testCustomResolution() {
 574         final int resolution = 100;
 575         animation = new AnimationImpl(timer, clipEnvelope, resolution);
 576 
 577         // send pulse
 578         animation.impl_timePulse(4 * resolution);
 579         assertEquals(4 * resolution, clipEnvelope.getLastTimePulse());
 580 
 581         // send half pulse
 582         animation.impl_timePulse(Math.round(4.5 * resolution));
 583         assertEquals(0, clipEnvelope.getLastTimePulse());
 584 
 585         // send full pulse
 586         animation.impl_timePulse(Math.round(5.5 * resolution));
 587         assertEquals(Math.round(5.5 * resolution), clipEnvelope.getLastTimePulse());
 588 
 589         // send half pulse, this time it should trigger a pulse
 590         animation.impl_timePulse(6 * resolution);
 591         assertEquals(6 * resolution, clipEnvelope.getLastTimePulse());
 592 
 593     }
 594 
 595     private static class OnFinishedListener implements EventHandler<ActionEvent> {
 596 
 597         private boolean wasCalled = false;
 598 
 599         @Override
 600         public void handle(ActionEvent event) {
 601             wasCalled = true;
 602         }
 603 
 604     }
 605 
 606     private static class OnFinishedExceptionListener implements EventHandler<ActionEvent> {
 607 
 608         private boolean wasCalled = false;
 609 
 610         @Override
 611         public void handle(ActionEvent event) {
 612             wasCalled = true;
 613             throw new RuntimeException("Test Exception");
 614         }
 615 
 616     }
 617 
 618 }