1 /* 2 * Copyright (c) 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 java.util.*; 29 import java.util.concurrent.CountDownLatch; 30 import java.util.concurrent.TimeUnit; 31 import java.util.concurrent.atomic.AtomicBoolean; 32 import java.util.concurrent.atomic.AtomicLong; 33 import java.util.concurrent.atomic.AtomicReference; 34 import javafx.animation.KeyFrame; 35 import javafx.animation.Timeline; 36 import javafx.application.Application; 37 import javafx.application.Platform; 38 import javafx.print.PrinterJob; 39 import javafx.scene.Group; 40 import javafx.scene.Scene; 41 import javafx.scene.control.Alert; 42 import javafx.scene.paint.Color; 43 import javafx.scene.shape.Rectangle; 44 import javafx.stage.Stage; 45 import javafx.util.Duration; 46 import junit.framework.AssertionFailedError; 47 import org.junit.After; 48 import org.junit.AfterClass; 49 import org.junit.Before; 50 import org.junit.BeforeClass; 51 import org.junit.Test; 52 import org.junit.runner.RunWith; 53 import org.junit.runners.Parameterized; 54 import org.junit.runners.Parameterized.Parameters; 55 import test.util.Util; 56 57 import static org.junit.Assert.*; 58 import static org.junit.Assume.*; 59 import static test.util.Util.TIMEOUT; 60 61 /** 62 * Test program for nested event loop functionality. 63 */ 64 public class NestedEventLoopTest { 65 66 // Used to launch the application before running any test 67 private static final CountDownLatch launchLatch = new CountDownLatch(1); 68 69 // Singleton Application instance 70 private static MyApp myApp; 71 72 // Application class. An instance is created and initialized before running 73 // the first test, and it lives through the execution of all tests. 74 public static class MyApp extends Application { 75 private Stage primaryStage; 76 77 @Override public void init() { 78 NestedEventLoopTest.myApp = this; 79 } 80 81 @Override public void start(Stage primaryStage) throws Exception { 82 primaryStage.setTitle("Primary stage"); 83 Group root = new Group(); 84 Scene scene = new Scene(root); 85 scene.setFill(Color.LIGHTYELLOW); 86 primaryStage.setScene(scene); 87 primaryStage.setX(0); 88 primaryStage.setY(0); 89 primaryStage.setWidth(210); 90 primaryStage.setHeight(180); 91 92 this.primaryStage = primaryStage; 93 launchLatch.countDown(); 94 } 95 } 96 97 @BeforeClass 98 public static void setupOnce() { 99 // Start the Application 100 new Thread(() -> Application.launch(MyApp.class, (String[])null)).start(); 101 102 try { 103 if (!launchLatch.await(TIMEOUT, TimeUnit.MILLISECONDS)) { 104 throw new AssertionFailedError("Timeout waiting for Application to launch"); 105 } 106 } catch (InterruptedException ex) { 107 AssertionFailedError err = new AssertionFailedError("Unexpected exception"); 108 err.initCause(ex); 109 throw err; 110 } 111 } 112 113 @AfterClass 114 public static void teardownOnce() { 115 Platform.exit(); 116 } 117 118 // Verify that we cannot enter a nested event loop on a thread other than 119 // the FX Application thread 120 @Test (expected=IllegalStateException.class) 121 public void testMustRunOnAppThread() { 122 assertFalse(Platform.isFxApplicationThread()); 123 Platform.enterNestedEventLoop(new Object()); 124 } 125 126 // Verify that we can enter and exit a nested event loop 127 @Test public void testCanEnterAndExitNestedEventLoop() { 128 final long key = 1024L; 129 final long result = 2048L; 130 final AtomicLong returnedValue = new AtomicLong(); 131 132 Util.runAndWait( 133 () -> { 134 assertFalse(Platform.isNestedLoopRunning()); 135 Long actual = (Long) Platform.enterNestedEventLoop(key); 136 returnedValue.set(actual); 137 }, 138 () -> { 139 assertTrue(Platform.isNestedLoopRunning()); 140 Platform.exitNestedEventLoop(key, result); 141 }, 142 () -> { 143 assertFalse(Platform.isNestedLoopRunning()); 144 assertEquals(result, returnedValue.get()); 145 } 146 ); 147 } 148 149 // Verify that we cannot enter a nested event loop with the same key twice 150 @Test (expected=IllegalArgumentException.class) 151 public void testUniqueKeyRequired() { 152 final Object key = new Object(); 153 Util.runAndWait( 154 () -> Platform.enterNestedEventLoop(key), 155 () -> Platform.enterNestedEventLoop(key), 156 () -> Platform.exitNestedEventLoop(key, null) 157 ); 158 } 159 160 // Verify that we cannot enter a nested event loop with a null key 161 @Test (expected=NullPointerException.class) 162 public void testNonNullKeyRequired() { 163 Util.runAndWait( 164 () -> Platform.enterNestedEventLoop(null) 165 ); 166 } 167 168 // Verify that we cannot exit a nested event loop with a null key 169 @Test (expected=NullPointerException.class) 170 public void testNonNullExitKeyRequired() { 171 Util.runAndWait( 172 () -> Platform.enterNestedEventLoop("validKey"), 173 () -> Platform.exitNestedEventLoop(null, null), 174 () -> Platform.exitNestedEventLoop("validKey", null) 175 ); 176 } 177 178 // Verify that we cannot exit a nested event loop with a key that has not been used 179 @Test (expected=IllegalArgumentException.class) 180 public void testExitLoopKeyHasBeenRegistered() { 181 Util.runAndWait( 182 () -> Platform.enterNestedEventLoop("validKey"), 183 () -> Platform.exitNestedEventLoop("invalidKey", null), 184 () -> Platform.exitNestedEventLoop("validKey", null) 185 ); 186 } 187 188 // Verify that we can enter and exit multiple nested event loops, in the order they are started 189 @Test public void testCanEnterMultipleNestedLoops_andExitInOrder() { 190 final long key1 = 1024L; 191 final long key2 = 1025L; 192 final long result1 = 2048L; 193 final long result2 = 2049L; 194 final AtomicLong returnedValue1 = new AtomicLong(); 195 final AtomicLong returnedValue2 = new AtomicLong(); 196 197 Util.runAndWait( 198 () -> { 199 // enter loop one 200 assertFalse(Platform.isNestedLoopRunning()); 201 Long actual = (Long) Platform.enterNestedEventLoop(key1); 202 returnedValue1.set(actual); 203 }, 204 () -> { 205 // enter loop two 206 assertTrue(Platform.isNestedLoopRunning()); 207 Long actual = (Long) Platform.enterNestedEventLoop(key2); 208 returnedValue2.set(actual); 209 }, 210 () -> { 211 // exit loop two 212 assertTrue(Platform.isNestedLoopRunning()); 213 Platform.exitNestedEventLoop(key2, result2); 214 }, 215 () -> { 216 // check loop two is done 217 assertTrue(Platform.isNestedLoopRunning()); 218 assertEquals(result2, returnedValue2.get()); 219 }, 220 () -> { 221 // exit loop one 222 assertTrue(Platform.isNestedLoopRunning()); 223 Platform.exitNestedEventLoop(key1, result1); 224 }, 225 () -> { 226 // check loop one is done 227 assertFalse(Platform.isNestedLoopRunning()); 228 assertEquals(result1, returnedValue1.get()); 229 } 230 ); 231 } 232 }