1 /* 2 * Copyright (c) 2017, 2018, 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.robot.javafx.scene; 27 28 import javafx.application.Application; 29 import javafx.application.Platform; 30 import javafx.scene.Scene; 31 import javafx.scene.control.Button; 32 import javafx.scene.input.MouseButton; 33 import javafx.scene.layout.HBox; 34 import javafx.scene.robot.Robot; 35 import javafx.stage.Stage; 36 import javafx.stage.StageStyle; 37 import javafx.stage.WindowEvent; 38 39 import java.util.concurrent.CountDownLatch; 40 import java.util.concurrent.TimeUnit; 41 42 import org.junit.AfterClass; 43 import org.junit.Assert; 44 import org.junit.BeforeClass; 45 import org.junit.Test; 46 import static org.junit.Assert.fail; 47 48 /* 49 * Test to verify the events when scene of stage is changed. 50 * Steps of Test: 51 * 1. Create a stage with a scene with a button. 52 * 2. Add MOUSE_EXITED listener & WindowProperty Listener to the same scene. 53 * 3. In onAction of the button change stage's scene to a new scene. 54 * 4. Verify that MOUSE_EXITED & WindowPropery change listener are called in 55 * sequence. 56 */ 57 58 public class SceneChangeEventsTest { 59 static CountDownLatch startupLatch; 60 static Robot robot; 61 static volatile Stage stage; 62 boolean mouseExited = false; 63 boolean mouseWindowEventOrder = false; 64 boolean windowChanged = false; 65 66 public static void main(String[] args) { 67 SceneChangeEventsTest test = new SceneChangeEventsTest(); 68 test.testSceneChange(); 69 exit(); 70 } 71 72 @Test 73 public void testSceneChange() { 74 75 Button button = new Button("onAction"); 76 CountDownLatch onActionLatch = new CountDownLatch(1); 77 button.setOnAction(event -> { 78 stage.setScene(new Scene(new HBox())); 79 onActionLatch.countDown(); 80 }); 81 HBox root = new HBox(); 82 root.getChildren().add(button); 83 Scene scene = new Scene(root); 84 CountDownLatch setSceneLatch = new CountDownLatch(1); 85 stage.sceneProperty().addListener(observable -> setSceneLatch.countDown()); 86 Platform.runLater(() -> { 87 stage.setScene(scene); 88 }); 89 waitForLatch(setSceneLatch, 5, "Timeout while waiting for scene to be set on stage."); 90 91 scene.setOnMouseExited(event -> { 92 mouseExited = true; 93 }); 94 scene.windowProperty().addListener(observable -> { 95 mouseWindowEventOrder = mouseExited; 96 windowChanged = true; 97 }); 98 Platform.runLater(() -> { 99 robot.mouseMove((int)(scene.getWindow().getX() + scene.getX() + button.getLayoutX() + button.getLayoutBounds().getWidth() / 2), 100 (int)(scene.getWindow().getY() + scene.getY() + button.getLayoutY() + button.getLayoutBounds().getHeight() / 2)); 101 robot.mousePress(MouseButton.PRIMARY); 102 robot.mouseRelease(MouseButton.PRIMARY); 103 }); 104 waitForLatch(onActionLatch, 5, "Timeout while waiting for button.onAction()."); 105 106 Assert.assertTrue("MOUSE_EXITED should be received when scene is " + 107 " changed.", mouseExited); 108 Assert.assertTrue("scene.windowProperty() listener should be received" + 109 "on scene change.", windowChanged); 110 Assert.assertTrue("MOUSE_EXITED should have been received before " + 111 "scene.windowProperty().", mouseWindowEventOrder); 112 } 113 114 public static class TestApp extends Application { 115 @Override 116 public void start(Stage primaryStage) { 117 robot = new Robot(); 118 stage = primaryStage; 119 stage.initStyle(StageStyle.UNDECORATED); 120 stage.addEventHandler(WindowEvent.WINDOW_SHOWN, e -> 121 Platform.runLater(startupLatch::countDown)); 122 stage.setAlwaysOnTop(true); 123 stage.show(); 124 } 125 } 126 127 @BeforeClass 128 public static void initFX() { 129 startupLatch = new CountDownLatch(1); 130 new Thread(() -> Application.launch(TestApp.class, (String[])null)).start(); 131 waitForLatch(startupLatch, 10, "Timeout waiting for FX runtime to start"); 132 } 133 134 @AfterClass 135 public static void exit() { 136 Platform.runLater(() -> { 137 stage.hide(); 138 }); 139 Platform.exit(); 140 } 141 142 public static void waitForLatch(CountDownLatch latch, int seconds, String msg) { 143 try { 144 if (!latch.await(seconds, TimeUnit.SECONDS)) { 145 fail(msg); 146 } 147 } catch (Exception ex) { 148 fail("Unexpected exception: " + ex); 149 } 150 } 151 }