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