1 /* 2 * Copyright (c) 2012, 2015, 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.stage; 27 28 import javafx.stage.StageShim; 29 import java.util.Arrays; 30 import java.util.Collection; 31 import java.util.HashSet; 32 import java.util.concurrent.CountDownLatch; 33 import java.util.concurrent.TimeUnit; 34 import java.util.concurrent.atomic.AtomicBoolean; 35 import java.util.concurrent.atomic.AtomicReference; 36 import javafx.animation.KeyFrame; 37 import javafx.animation.Timeline; 38 import javafx.application.Application; 39 import javafx.application.Platform; 40 import javafx.print.PrinterJob; 41 import javafx.scene.Group; 42 import javafx.scene.Scene; 43 import javafx.scene.control.Alert; 44 import javafx.scene.paint.Color; 45 import javafx.scene.shape.Rectangle; 46 import javafx.stage.Modality; 47 import javafx.stage.Stage; 48 import javafx.stage.StageShim; 49 import javafx.stage.Window; 50 import javafx.util.Duration; 51 import junit.framework.AssertionFailedError; 52 import org.junit.After; 53 import org.junit.AfterClass; 54 import org.junit.Before; 55 import org.junit.BeforeClass; 56 import org.junit.Test; 57 import org.junit.runner.RunWith; 58 import org.junit.runners.Parameterized; 59 import org.junit.runners.Parameterized.Parameters; 60 import test.util.Util; 61 62 import static org.junit.Assert.*; 63 import static org.junit.Assume.*; 64 import static test.util.Util.TIMEOUT; 65 66 /** 67 * Test program for showAndWait functionality. 68 */ 69 @RunWith(Parameterized.class) 70 public class ShowAndWaitTest { 71 72 // Maximum number of stages 73 private static final int MAX_STAGES = 10; 74 75 // Used to launch the application before running any test 76 private static final CountDownLatch launchLatch = new CountDownLatch(1); 77 78 // Singleton Application instance 79 private static MyApp myApp; 80 81 // Application class. An instance is created and initialized before running 82 // the first test, and it lives through the execution of all tests. 83 public static class MyApp extends Application { 84 private Stage primaryStage; 85 86 @Override public void init() { 87 ShowAndWaitTest.myApp = this; 88 } 89 90 @Override public void start(Stage primaryStage) throws Exception { 91 primaryStage.setTitle("Primary stage"); 92 Group root = new Group(); 93 Scene scene = new Scene(root); 94 scene.setFill(Color.LIGHTYELLOW); 95 primaryStage.setScene(scene); 96 primaryStage.setX(0); 97 primaryStage.setY(0); 98 primaryStage.setWidth(210); 99 primaryStage.setHeight(180); 100 101 this.primaryStage = primaryStage; 102 launchLatch.countDown(); 103 } 104 } 105 106 private static class TestStage extends Stage { 107 108 private TestStage(Modality modality) { 109 this(modality, modality == Modality.WINDOW_MODAL ? myApp.primaryStage : null); 110 } 111 112 private TestStage(Modality modality, Window owner) { 113 this.setTitle("Test stage"); 114 this.initModality(modality); 115 this.initOwner(owner); 116 117 Group root = new Group(); 118 Scene scene = new Scene(root); 119 this.setScene(scene); 120 this.setWidth(200); 121 this.setHeight(150); 122 this.setX(225); 123 this.setY(0); 124 } 125 } 126 127 @BeforeClass 128 public static void setupOnce() { 129 // Start the Application 130 new Thread(() -> Application.launch(MyApp.class, (String[])null)).start(); 131 132 try { 133 if (!launchLatch.await(TIMEOUT, TimeUnit.MILLISECONDS)) { 134 throw new AssertionFailedError("Timeout waiting for Application to launch"); 135 } 136 } catch (InterruptedException ex) { 137 AssertionFailedError err = new AssertionFailedError("Unexpected exception"); 138 err.initCause(ex); 139 throw err; 140 } 141 } 142 143 @AfterClass 144 public static void teardownOnce() { 145 Platform.exit(); 146 } 147 148 // Modality of the secondary stage(s) for a particular tests 149 final Modality modality; 150 151 // Set of stages being tested so that they can be hidden at the 152 // end of of a failing test 153 private HashSet<Stage> stages = new HashSet<Stage>(); 154 155 // Secondary stages used for testing 156 private Stage tmpStage1 = null; 157 private Stage tmpStage2 = null; 158 159 @Parameters 160 public static Collection getParams() { 161 return Arrays.asList(new Object[][] { 162 { Modality.NONE }, 163 { Modality.WINDOW_MODAL }, 164 { Modality.APPLICATION_MODAL }, 165 }); 166 } 167 168 public ShowAndWaitTest(Modality modality) { 169 this.modality = modality; 170 } 171 172 @Before 173 public void setupEach() { 174 assertNotNull(myApp); 175 assertNotNull(myApp.primaryStage); 176 } 177 178 @After 179 public void teardownEach() { 180 for (final Stage stage : stages) { 181 if (stage.isShowing()) { 182 System.err.println("Cleaning up stage after a failed test..."); 183 try { 184 Util.runAndWait(stage::hide); 185 } catch (Throwable t) { 186 System.err.println("WARNING: unable to hide stage after test failure"); 187 t.printStackTrace(System.err); 188 } 189 } 190 } 191 } 192 193 // ========================== TEST CASES ========================== 194 195 // This test must be run before any other test, because it first verifies 196 // that the primary stage is not yet showing, and finally shows it. 197 // Since JUnit does not guarantee test execution order, each test that 198 // relies on this must call ensureTest1(). This test does not use the 199 // test parameters and only runs one in total. 200 // 201 // Consider moving to the setupOnce method. 202 private static boolean test1Run = false; 203 public void ensureTest1() { 204 if (!test1Run) { 205 test1(); 206 } 207 } 208 209 @Test 210 public void test1() { 211 if (test1Run) { 212 return; 213 } 214 test1Run = true; 215 216 assertEquals(0, launchLatch.getCount()); 217 Util.runAndWait(() -> { 218 assertTrue(Platform.isFxApplicationThread()); 219 assertTrue(StageShim.isPrimary(myApp.primaryStage)); 220 assertFalse(myApp.primaryStage.isShowing()); 221 222 // Verify that we cannot call showAndWait on the primaryStage 223 try { 224 myApp.primaryStage.showAndWait(); 225 throw new AssertionFailedError("Expected IllegalStateException was not thrown"); 226 } catch (IllegalStateException ex) { 227 } 228 229 myApp.primaryStage.show(); 230 }); 231 } 232 233 // Verify that we cannot construct a stage on a thread other than 234 // the FX Application thread 235 @Test (expected=IllegalStateException.class) 236 public void testConstructWrongThread() { 237 ensureTest1(); 238 assertFalse(Platform.isFxApplicationThread()); 239 240 // The following should throw IllegalStateException 241 tmpStage1 = new TestStage(modality); 242 stages.add(tmpStage1); 243 } 244 245 246 // Verify that we cannot call showAndWait on a thread other than 247 // the FX Application thread 248 @Test (expected=IllegalStateException.class) 249 public void testShowWaitWrongThread() { 250 ensureTest1(); 251 assertFalse(Platform.isFxApplicationThread()); 252 Util.runAndWait(() -> { 253 tmpStage1 = new TestStage(modality); 254 stages.add(tmpStage1); 255 assertFalse(StageShim.isPrimary(tmpStage1)); 256 assertFalse(tmpStage1.isShowing()); 257 }); 258 assertNotNull(tmpStage1); 259 260 // The following should throw IllegalStateException 261 tmpStage1.showAndWait(); 262 } 263 264 // Verify that we cannot call showAndWait on a visible stage 265 @Test (expected=IllegalStateException.class) 266 public void testVisibleThrow() { 267 ensureTest1(); 268 Util.runAndWait(() -> { 269 tmpStage1 = new TestStage(modality); 270 stages.add(tmpStage1); 271 assertFalse(StageShim.isPrimary(tmpStage1)); 272 assertFalse(tmpStage1.isShowing()); 273 tmpStage1.show(); 274 assertTrue(tmpStage1.isShowing()); 275 276 try { 277 // The following should throw IllegalStateException 278 tmpStage1.showAndWait(); 279 } finally { 280 tmpStage1.hide(); 281 } 282 }); 283 } 284 285 // Verify that show returns right away; hide the stage after 500 msec 286 @Test 287 public void testNotBlocking() { 288 ensureTest1(); 289 290 final AtomicBoolean stageShowReturned = new AtomicBoolean(false); 291 final AtomicBoolean hideActionReached = new AtomicBoolean(false); 292 293 Runnable rShow = () -> { 294 tmpStage1 = new TestStage(modality); 295 stages.add(tmpStage1); 296 assertFalse(StageShim.isPrimary(tmpStage1)); 297 assertFalse(tmpStage1.isShowing()); 298 tmpStage1.show(); 299 stageShowReturned.set(true); 300 assertTrue(tmpStage1.isShowing()); 301 assertFalse(hideActionReached.get()); 302 }; 303 304 Runnable rHide = () -> { 305 assertNotNull(tmpStage1); 306 assertTrue(tmpStage1.isShowing()); 307 assertTrue(stageShowReturned.get()); 308 hideActionReached.set(true); 309 tmpStage1.hide(); 310 }; 311 312 Util.runAndWait(rShow, rHide); 313 314 assertFalse(tmpStage1.isShowing()); 315 } 316 317 // Verify that showAndWait blocks until the stage is hidden. 318 // Verify that the nested event loop exits immediately after 319 // the event handler that calls hide returns, before running 320 // the next Runnable. 321 @Test 322 public void testSingle() { 323 ensureTest1(); 324 325 final AtomicBoolean stage1ShowReturned = new AtomicBoolean(false); 326 final AtomicBoolean hide1EventReached = new AtomicBoolean(false); 327 final AtomicBoolean nextRunnableReached = new AtomicBoolean(false); 328 329 Runnable rShow1 = () -> { 330 tmpStage1 = new TestStage(modality); 331 stages.add(tmpStage1); 332 assertFalse(StageShim.isPrimary(tmpStage1)); 333 assertFalse(tmpStage1.isShowing()); 334 tmpStage1.showAndWait(); 335 stage1ShowReturned.set(true); 336 assertFalse(tmpStage1.isShowing()); 337 assertTrue(hide1EventReached.get()); 338 assertFalse(nextRunnableReached.get()); 339 }; 340 341 Runnable rHide1 = () -> { 342 hide1EventReached.set(true); 343 assertFalse(stage1ShowReturned.get()); 344 assertNotNull(tmpStage1); 345 tmpStage1.hide(); 346 Util.sleep(1); 347 assertFalse(stage1ShowReturned.get()); 348 }; 349 350 Runnable rNext = () -> { 351 // This should happen after the nested event loop exits 352 nextRunnableReached.set(true); 353 }; 354 355 Util.runAndWait(rShow1, rHide1, rNext); 356 357 assertFalse(tmpStage1.isShowing()); 358 } 359 360 // Verify that showAndWait blocks until the stage is hidden. 361 // Verify that the nested event loop exits immediately after 362 // the event handler that calls hide returns, before running 363 // the next Runnable (called from rShow1 after showAndWait returns). 364 365 @Test 366 public void testSingle_Chained() { 367 ensureTest1(); 368 369 final AtomicBoolean stage1ShowReturned = new AtomicBoolean(false); 370 final AtomicBoolean hide1EventReached = new AtomicBoolean(false); 371 final AtomicBoolean nextRunnableReached = new AtomicBoolean(false); 372 373 Runnable rShow1 = () -> { 374 tmpStage1 = new TestStage(modality); 375 stages.add(tmpStage1); 376 assertFalse(StageShim.isPrimary(tmpStage1)); 377 assertFalse(tmpStage1.isShowing()); 378 tmpStage1.showAndWait(); 379 stage1ShowReturned.set(true); 380 assertFalse(tmpStage1.isShowing()); 381 assertTrue(hide1EventReached.get()); 382 assertFalse(nextRunnableReached.get()); 383 }; 384 385 Runnable rHide1 = () -> { 386 hide1EventReached.set(true); 387 assertFalse(stage1ShowReturned.get()); 388 assertNotNull(tmpStage1); 389 tmpStage1.hide(); 390 Util.sleep(1); 391 assertFalse(stage1ShowReturned.get()); 392 Platform.runLater(() -> { 393 // This should happen after the nested event loop exits 394 nextRunnableReached.set(true); 395 }); 396 }; 397 398 Util.runAndWait(rShow1, rHide1); 399 400 assertFalse(tmpStage1.isShowing()); 401 } 402 403 // Verify two nested event loops, with the stages being hidden in the 404 // reverse order that they are shown 405 @Test 406 public void testTwoNested() { 407 ensureTest1(); 408 409 final AtomicBoolean stage1ShowReturned = new AtomicBoolean(false); 410 final AtomicBoolean hide1EventReached = new AtomicBoolean(false); 411 final AtomicBoolean stage2ShowReturned = new AtomicBoolean(false); 412 final AtomicBoolean hide2EventReached = new AtomicBoolean(false); 413 414 Runnable rShow1 = () -> { 415 tmpStage1 = new TestStage(modality); 416 stages.add(tmpStage1); 417 assertFalse(StageShim.isPrimary(tmpStage1)); 418 assertFalse(tmpStage1.isShowing()); 419 tmpStage1.showAndWait(); 420 stage1ShowReturned.set(true); 421 assertFalse(tmpStage1.isShowing()); 422 assertTrue(stage2ShowReturned.get()); 423 assertTrue(hide1EventReached.get()); 424 assertTrue(hide2EventReached.get()); 425 }; 426 427 Runnable rShow2 = () -> { 428 tmpStage2 = new TestStage(modality); 429 stages.add(tmpStage2); 430 assertFalse(StageShim.isPrimary(tmpStage2)); 431 assertFalse(tmpStage2.isShowing()); 432 tmpStage2.showAndWait(); 433 stage2ShowReturned.set(true); 434 assertFalse(stage1ShowReturned.get()); 435 assertFalse(tmpStage2.isShowing()); 436 assertTrue(hide2EventReached.get()); 437 assertFalse(hide1EventReached.get()); 438 }; 439 440 Runnable rHide1 = () -> { 441 hide1EventReached.set(true); 442 assertFalse(stage1ShowReturned.get()); 443 assertTrue(stage2ShowReturned.get()); 444 assertTrue(hide2EventReached.get()); 445 assertNotNull(tmpStage1); 446 tmpStage1.hide(); 447 Util.sleep(1); 448 assertFalse(stage1ShowReturned.get()); 449 }; 450 451 Runnable rHide2 = () -> { 452 hide2EventReached.set(true); 453 assertFalse(stage2ShowReturned.get()); 454 assertFalse(stage1ShowReturned.get()); 455 assertFalse(hide1EventReached.get()); 456 assertNotNull(tmpStage2); 457 tmpStage2.hide(); 458 Util.sleep(1); 459 assertFalse(stage2ShowReturned.get()); 460 }; 461 462 Util.runAndWait(rShow1, rShow2, rHide2, rHide1); 463 464 assertFalse(tmpStage1.isShowing()); 465 assertFalse(tmpStage2.isShowing()); 466 } 467 468 // Verify two nested event loops, with the stages being hidden in the 469 // same order that they are shown 470 @Test 471 public void testTwoInterleaved() { 472 ensureTest1(); 473 474 final AtomicBoolean stage1ShowReturned = new AtomicBoolean(false); 475 final AtomicBoolean hide1EventReached = new AtomicBoolean(false); 476 final AtomicBoolean stage2ShowReturned = new AtomicBoolean(false); 477 final AtomicBoolean hide2EventReached = new AtomicBoolean(false); 478 479 Runnable rShow1 = () -> { 480 tmpStage1 = new TestStage(modality); 481 stages.add(tmpStage1); 482 assertFalse(StageShim.isPrimary(tmpStage1)); 483 assertFalse(tmpStage1.isShowing()); 484 tmpStage1.showAndWait(); 485 stage1ShowReturned.set(true); 486 assertFalse(tmpStage1.isShowing()); 487 assertTrue(stage2ShowReturned.get()); 488 assertTrue(hide1EventReached.get()); 489 assertTrue(hide2EventReached.get()); 490 }; 491 492 Runnable rShow2 = () -> { 493 tmpStage2 = new TestStage(modality); 494 stages.add(tmpStage2); 495 assertFalse(StageShim.isPrimary(tmpStage2)); 496 assertFalse(tmpStage2.isShowing()); 497 tmpStage2.showAndWait(); 498 stage2ShowReturned.set(true); 499 assertFalse(tmpStage2.isShowing()); 500 assertFalse(stage1ShowReturned.get()); 501 assertTrue(hide2EventReached.get()); 502 assertTrue(hide1EventReached.get()); 503 }; 504 505 Runnable rHide1 = () -> { 506 hide1EventReached.set(true); 507 assertFalse(stage1ShowReturned.get()); 508 assertFalse(stage2ShowReturned.get()); 509 assertFalse(hide2EventReached.get()); 510 assertNotNull(tmpStage1); 511 tmpStage1.hide(); 512 Util.sleep(1); 513 assertFalse(stage1ShowReturned.get()); 514 }; 515 516 Runnable rHide2 = () -> { 517 hide2EventReached.set(true); 518 assertFalse(stage2ShowReturned.get()); 519 assertFalse(stage1ShowReturned.get()); 520 assertTrue(hide1EventReached.get()); 521 assertNotNull(tmpStage2); 522 tmpStage2.hide(); 523 Util.sleep(1); 524 assertFalse(stage2ShowReturned.get()); 525 }; 526 527 Util.runAndWait(rShow1, rShow2, rHide1, rHide2); 528 529 assertFalse(tmpStage1.isShowing()); 530 assertFalse(tmpStage2.isShowing()); 531 } 532 533 // Verify multiple nested event loops, with the stages being hidden in the 534 // reverse order that they are shown 535 @Test 536 public void testMultipleNested() { 537 ensureTest1(); 538 539 final int N = MAX_STAGES; 540 final Stage[] tmpStage = new Stage[N]; 541 final AtomicBoolean[] stageShowReturned = new AtomicBoolean[N]; 542 final AtomicBoolean[] hideEventReached = new AtomicBoolean[N]; 543 final Runnable[] rShow = new Runnable[N]; 544 final Runnable[] rHide = new Runnable[N]; 545 546 for (int i = 0; i < N; i++) { 547 final int idx = i; 548 stageShowReturned[idx] = new AtomicBoolean(false); 549 hideEventReached[idx] = new AtomicBoolean(false); 550 rShow[idx] = () -> { 551 tmpStage[idx] = new TestStage(modality); 552 stages.add(tmpStage[idx]); 553 assertFalse(tmpStage[idx].isShowing()); 554 tmpStage[idx].showAndWait(); 555 stageShowReturned[idx].set(true); 556 assertFalse(tmpStage[idx].isShowing()); 557 assertTrue(hideEventReached[idx].get()); 558 for (int j = 0; j < idx; j++) { 559 assertFalse(stageShowReturned[j].get()); 560 assertFalse(hideEventReached[j].get()); 561 } 562 for (int j = idx+1; j < N; j++) { 563 assertTrue(stageShowReturned[j].get()); 564 assertTrue(hideEventReached[j].get()); 565 } 566 }; 567 568 rHide[idx] = () -> { 569 hideEventReached[idx].set(true); 570 assertFalse(stageShowReturned[idx].get()); 571 for (int j = 0; j < idx; j++) { 572 assertFalse(stageShowReturned[j].get()); 573 assertFalse(hideEventReached[j].get()); 574 } 575 for (int j = idx+1; j < N; j++) { 576 assertTrue(stageShowReturned[j].get()); 577 assertTrue(hideEventReached[j].get()); 578 } 579 assertNotNull(tmpStage[idx]); 580 tmpStage[idx].hide(); 581 Util.sleep(1); 582 assertFalse(stageShowReturned[idx].get()); 583 }; 584 } 585 586 final Runnable[] runnables = new Runnable[2*N]; 587 for (int i = 0; i < N; i++) { 588 runnables[i] = rShow[i]; 589 runnables[(2*N - i - 1)] = rHide[i]; 590 } 591 Util.runAndWait(runnables); 592 593 for (int i = 0; i < N; i++) { 594 assertFalse(tmpStage[i].isShowing()); 595 } 596 } 597 598 // Verify multiple nested event loops, with the stages being hidden in the 599 // reverse order that they are shown 600 @Test 601 public void testMultipleInterleaved() { 602 ensureTest1(); 603 604 final int N = MAX_STAGES; 605 final Stage[] tmpStage = new Stage[N]; 606 final AtomicBoolean[] stageShowReturned = new AtomicBoolean[N]; 607 final AtomicBoolean[] hideEventReached = new AtomicBoolean[N]; 608 final Runnable[] rShow = new Runnable[N]; 609 final Runnable[] rHide = new Runnable[N]; 610 611 for (int i = 0; i < N; i++) { 612 final int idx = i; 613 stageShowReturned[idx] = new AtomicBoolean(false); 614 hideEventReached[idx] = new AtomicBoolean(false); 615 rShow[idx] = () -> { 616 tmpStage[idx] = new TestStage(modality); 617 stages.add(tmpStage[idx]); 618 assertFalse(tmpStage[idx].isShowing()); 619 tmpStage[idx].showAndWait(); 620 stageShowReturned[idx].set(true); 621 assertFalse(tmpStage[idx].isShowing()); 622 assertTrue(hideEventReached[idx].get()); 623 for (int j = 0; j < idx; j++) { 624 assertFalse(stageShowReturned[j].get()); 625 assertTrue(hideEventReached[j].get()); 626 } 627 for (int j = idx+1; j < N; j++) { 628 assertTrue(stageShowReturned[j].get()); 629 assertTrue(hideEventReached[j].get()); 630 } 631 }; 632 633 rHide[idx] = () -> { 634 hideEventReached[idx].set(true); 635 assertFalse(stageShowReturned[idx].get()); 636 for (int j = 0; j < idx; j++) { 637 assertFalse(stageShowReturned[j].get()); 638 assertTrue(hideEventReached[j].get()); 639 } 640 for (int j = idx+1; j < N; j++) { 641 assertFalse(stageShowReturned[j].get()); 642 assertFalse(hideEventReached[j].get()); 643 } 644 assertNotNull(tmpStage[idx]); 645 tmpStage[idx].hide(); 646 Util.sleep(1); 647 assertFalse(stageShowReturned[idx].get()); 648 }; 649 } 650 651 final Runnable[] runnables = new Runnable[2*N]; 652 for (int i = 0; i < N; i++) { 653 runnables[i] = rShow[i]; 654 runnables[N+i] = rHide[i]; 655 } 656 Util.runAndWait(runnables); 657 658 for (int i = 0; i < N; i++) { 659 assertFalse(tmpStage[i].isShowing()); 660 } 661 } 662 663 // Verify that Stage.showAndWait throws an exception if called from an 664 // animation timeline. 665 @Test 666 public void testTimeline() throws Throwable { 667 ensureTest1(); 668 669 final CountDownLatch animationDone = new CountDownLatch(1); 670 final AtomicReference<Throwable> error = new AtomicReference<>(null); 671 672 KeyFrame kf = new KeyFrame(Duration.millis(200), e -> { 673 try { 674 tmpStage1 = new TestStage(modality); 675 stages.add(tmpStage1); 676 assertFalse(StageShim.isPrimary(tmpStage1)); 677 assertFalse(tmpStage1.isShowing()); 678 try { 679 tmpStage1.showAndWait(); 680 fail("Did not get expected exception from showAndWait"); 681 } catch (IllegalStateException ex) { 682 // Good 683 } 684 assertFalse(tmpStage1.isShowing()); 685 } catch (Throwable t) { 686 error.set(t); 687 } 688 animationDone.countDown(); 689 }); 690 Timeline timeline = new Timeline(kf); 691 timeline.play(); 692 693 try { 694 if (!animationDone.await(TIMEOUT, TimeUnit.MILLISECONDS)) { 695 fail("Timeout waiting for animation"); 696 } 697 } catch (InterruptedException ex) { 698 fail("Unexpected exception: " + ex); 699 } 700 701 final Throwable t = error.get(); 702 if (t != null) { 703 throw t; 704 } 705 706 assertFalse(tmpStage1.isShowing()); 707 } 708 709 // Verify that Alert.showAndWait throws an exception if called from an 710 // animation timeline. 711 @Test 712 public void testTimelineDialog() throws Throwable { 713 ensureTest1(); 714 715 final CountDownLatch animationDone = new CountDownLatch(1); 716 final AtomicReference<Throwable> error = new AtomicReference<>(null); 717 718 KeyFrame kf = new KeyFrame(Duration.millis(200), e -> { 719 Alert alert = null; 720 try { 721 alert = new Alert(Alert.AlertType.INFORMATION); 722 assertFalse(alert.isShowing()); 723 try { 724 alert.showAndWait(); 725 fail("Did not get expected exception from showAndWait"); 726 } catch (IllegalStateException ex) { 727 // Good 728 } 729 assertFalse(alert.isShowing()); 730 } catch (Throwable t) { 731 error.set(t); 732 try { 733 if (alert.isShowing()) { 734 alert.close(); 735 } 736 } catch (RuntimeException ex) {} 737 } 738 animationDone.countDown(); 739 }); 740 Timeline timeline = new Timeline(kf); 741 timeline.play(); 742 743 try { 744 if (!animationDone.await(TIMEOUT, TimeUnit.MILLISECONDS)) { 745 fail("Timeout waiting for animation"); 746 } 747 } catch (InterruptedException ex) { 748 fail("Unexpected exception: " + ex); 749 } 750 751 final Throwable t = error.get(); 752 if (t != null) { 753 throw t; 754 } 755 } 756 757 // Verify that printing throws an exception if called from an 758 // animation timeline. 759 @Test 760 public void testTimelinePrint() throws Throwable { 761 assumeNotNull(PrinterJob.createPrinterJob()); 762 763 ensureTest1(); 764 765 final CountDownLatch animationDone = new CountDownLatch(1); 766 final AtomicReference<Throwable> error = new AtomicReference<>(null); 767 768 KeyFrame kf = new KeyFrame(Duration.millis(200), e -> { 769 try { 770 PrinterJob job = PrinterJob.createPrinterJob(); 771 try { 772 job.showPrintDialog(myApp.primaryStage); 773 fail("Did not get expected exception from showPrintDialog"); 774 } catch (IllegalStateException ex) { 775 // Good 776 } 777 try { 778 job.showPageSetupDialog(myApp.primaryStage); 779 fail("Did not get expected exception from showPageSetupDialog"); 780 } catch (IllegalStateException ex) { 781 // Good 782 } 783 try { 784 Rectangle rect = new Rectangle(200, 100, Color.GREEN); 785 job.printPage(rect); 786 fail("Did not get expected exception from printPage"); 787 } catch (IllegalStateException ex) { 788 // Good 789 } 790 } catch (Throwable t) { 791 error.set(t); 792 } 793 animationDone.countDown(); 794 }); 795 Timeline timeline = new Timeline(kf); 796 timeline.play(); 797 798 try { 799 if (!animationDone.await(TIMEOUT, TimeUnit.MILLISECONDS)) { 800 fail("Timeout waiting for animation"); 801 } 802 } catch (InterruptedException ex) { 803 fail("Unexpected exception: " + ex); 804 } 805 806 final Throwable t = error.get(); 807 if (t != null) { 808 throw t; 809 } 810 } 811 812 }