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 }