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 package test.robot.javafx.scene; 26 27 import javafx.application.Application; 28 import javafx.application.Platform; 29 import javafx.collections.ListChangeListener; 30 import javafx.scene.Scene; 31 import javafx.scene.control.Tab; 32 import javafx.scene.control.TabPane; 33 import javafx.scene.input.MouseButton; 34 import javafx.scene.robot.Robot; 35 import javafx.stage.Stage; 36 import javafx.stage.StageStyle; 37 import javafx.stage.WindowEvent; 38 import javafx.geometry.Side; 39 40 import java.util.concurrent.CountDownLatch; 41 import java.util.concurrent.TimeUnit; 42 43 import org.junit.After; 44 import org.junit.AfterClass; 45 import org.junit.Assert; 46 import org.junit.Before; 47 import org.junit.BeforeClass; 48 import org.junit.Test; 49 import static org.junit.Assert.fail; 50 51 import test.util.Util; 52 53 /* 54 * Unit test for verifying DragPolicies. 55 * 56 * There are 8 tests in this file. 57 * Steps of 4 tests for DragPolicy.REORDER 58 * 1. Create TabPane with 4 tabs. 59 * 2. Drag tab0 to last after tab3. 60 * 3. Verify that tab1 is the first tab after reorder. 61 * 4. Verify that a correct permutation change event is received. 62 * 5. Verify that getTabs() is also reordered correctly. 63 * Repeat the test for four Sides. 64 * 65 * Steps of 4 tests for DragPolicy.FIXED 66 * 1. Create TabPane with 4 tabs. 67 * 2. Drag tab0 to last after tab3, the tab0 should not get dragged. 68 * 3. Verify that tab0 is still the first tab. 69 * 4. Verify that permutation change event is not received. 70 * Repeat the test for four Sides. 71 */ 72 public class TabPaneDragPolicyTest { 73 CountDownLatch[] latches; 74 CountDownLatch changeListenerLatch; 75 static CountDownLatch startupLatch; 76 static Robot robot; 77 static TabPane tabPane; 78 static volatile Stage stage; 79 static volatile Scene scene; 80 static final int SCENE_WIDTH = 250; 81 static final int SCENE_HEIGHT = SCENE_WIDTH; 82 final int DRAG_DISTANCE = SCENE_WIDTH - 50; 83 final int DX = 15; 84 final int DY = DX; 85 Tab[] tabs; 86 Tab expectedTab; 87 Tab selectedTab; 88 final String PERMUTED_SEQ = "tab1tab2tab3tab0"; 89 final String REORDER_SEQ = "tab1tab2tab3tab0"; 90 boolean listenerTestResult = false; 91 ReorderChangeListener reorderListener = new ReorderChangeListener(); 92 FixedChangeListener fixedListener = new FixedChangeListener(); 93 94 class ReorderChangeListener implements ListChangeListener<Tab> { 95 @Override 96 public void onChanged(Change<? extends Tab> c) { 97 while (c.next()) { 98 if (c.wasPermutated()) { 99 String list = ""; 100 for (int i = c.getFrom(); i < c.getTo(); i++) { 101 list += tabPane.getTabs().get(i).getText(); 102 } 103 listenerTestResult = list.equals(PERMUTED_SEQ); 104 list = ""; 105 for (Tab t : tabPane.getTabs()) { 106 list += t.getText(); 107 } 108 listenerTestResult = listenerTestResult && list.equals(REORDER_SEQ); 109 changeListenerLatch.countDown(); 110 } 111 } 112 }; 113 } 114 115 class FixedChangeListener implements ListChangeListener<Tab> { 116 @Override 117 public void onChanged(Change<? extends Tab> c) { 118 listenerTestResult = false; 119 }; 120 } 121 122 public static void main(String[] args) { 123 initFX(); 124 TabPaneDragPolicyTest test = new TabPaneDragPolicyTest(); 125 126 test.testReorderTop(); 127 test.testReorderBottom(); 128 test.testReorderLeft(); 129 test.testReorderRight(); 130 131 test.testFixedTop(); 132 test.testFixedBottom(); 133 test.testFixedLeft(); 134 test.testFixedRight(); 135 136 exit(); 137 } 138 139 @Test 140 public void testReorderTop() { 141 expectedTab = tabs[1]; 142 setDragPolicyAndSide(TabPane.TabDragPolicy.REORDER, Side.TOP); 143 tabPane.getTabs().addListener(reorderListener); 144 testReorder(DX, DY, 1, 0, false); 145 tabPane.getTabs().removeListener(reorderListener); 146 selectedTab = (Tab)tabPane.getSelectionModel().getSelectedItem(); 147 Assert.assertEquals("Expected " + expectedTab.getText() + " to be " 148 + "first tab after reordering.", expectedTab.getText(), selectedTab.getText()); 149 Assert.assertTrue("Incorrect permutation change received", listenerTestResult); 150 } 151 152 @Test 153 public void testReorderBottom() { 154 expectedTab = tabs[1]; 155 setDragPolicyAndSide(TabPane.TabDragPolicy.REORDER, Side.BOTTOM); 156 tabPane.getTabs().addListener(reorderListener); 157 testReorder(DX, SCENE_HEIGHT - DY, 1, 0, false); 158 tabPane.getTabs().removeListener(reorderListener); 159 selectedTab = (Tab)tabPane.getSelectionModel().getSelectedItem(); 160 Assert.assertEquals("Expected " + expectedTab.getText() + " to be " 161 + "first tab after reordering.", expectedTab.getText(), selectedTab.getText()); 162 Assert.assertTrue("Incorrect permutation change received", listenerTestResult); 163 } 164 165 @Test 166 public void testReorderLeft() { 167 expectedTab = tabs[1]; 168 setDragPolicyAndSide(TabPane.TabDragPolicy.REORDER, Side.LEFT); 169 tabPane.getTabs().addListener(reorderListener); 170 testReorder(DX, DY, 0, 1, false); 171 tabPane.getTabs().removeListener(reorderListener); 172 selectedTab = (Tab)tabPane.getSelectionModel().getSelectedItem(); 173 Assert.assertEquals("Expected " + expectedTab.getText() + " to be " 174 + "first tab after reordering.", expectedTab.getText(), selectedTab.getText()); 175 Assert.assertTrue("Incorrect permutation change received", listenerTestResult); 176 } 177 178 @Test 179 public void testReorderRight() { 180 expectedTab = tabs[1]; 181 setDragPolicyAndSide(TabPane.TabDragPolicy.REORDER, Side.RIGHT); 182 tabPane.getTabs().addListener(reorderListener); 183 testReorder(SCENE_WIDTH - DX, DY, 0, 1, false); 184 tabPane.getTabs().removeListener(reorderListener); 185 selectedTab = (Tab)tabPane.getSelectionModel().getSelectedItem(); 186 Assert.assertEquals("Expected " + expectedTab.getText() + " to be " 187 + "first tab after reordering.", expectedTab.getText(), selectedTab.getText()); 188 Assert.assertTrue("Incorrect permutation change received", listenerTestResult); 189 } 190 191 @Test 192 public void testFixedTop() { 193 expectedTab = tabs[0]; 194 listenerTestResult = true; 195 setDragPolicyAndSide(TabPane.TabDragPolicy.FIXED, Side.TOP); 196 tabPane.getTabs().addListener(fixedListener); 197 testReorder(DX, DY, 1, 0, true); 198 tabPane.getTabs().removeListener(fixedListener); 199 selectedTab = (Tab)tabPane.getSelectionModel().getSelectedItem(); 200 Assert.assertEquals("Expected " + expectedTab.getText() + " to remain " 201 + "first tab.", expectedTab.getText(), selectedTab.getText()); 202 Assert.assertTrue("Change event should not be received", listenerTestResult); 203 } 204 205 @Test 206 public void testFixedBottom() { 207 expectedTab = tabs[0]; 208 listenerTestResult = true; 209 setDragPolicyAndSide(TabPane.TabDragPolicy.FIXED, Side.BOTTOM); 210 tabPane.getTabs().addListener(fixedListener); 211 testReorder(DX, SCENE_HEIGHT - DY, 1, 0, true); 212 tabPane.getTabs().removeListener(fixedListener); 213 selectedTab = (Tab)tabPane.getSelectionModel().getSelectedItem(); 214 Assert.assertEquals("Expected " + expectedTab.getText() + " to remain " 215 + "first tab.", expectedTab.getText(), selectedTab.getText()); 216 Assert.assertTrue("Change event should not be received", listenerTestResult); 217 } 218 219 @Test 220 public void testFixedLeft() { 221 expectedTab = tabs[0]; 222 listenerTestResult = true; 223 setDragPolicyAndSide(TabPane.TabDragPolicy.FIXED, Side.LEFT); 224 tabPane.getTabs().addListener(fixedListener); 225 testReorder(DX, DY, 0, 1, true); 226 tabPane.getTabs().removeListener(fixedListener); 227 selectedTab = (Tab)tabPane.getSelectionModel().getSelectedItem(); 228 Assert.assertEquals("Expected " + expectedTab.getText() + " to remain " 229 + "first tab.", expectedTab.getText(), selectedTab.getText()); 230 Assert.assertTrue("Change event should not be received", listenerTestResult); 231 } 232 233 @Test 234 public void testFixedRight() { 235 expectedTab = tabs[0]; 236 listenerTestResult = true; 237 setDragPolicyAndSide(TabPane.TabDragPolicy.FIXED, Side.RIGHT); 238 tabPane.getTabs().addListener(fixedListener); 239 testReorder(SCENE_WIDTH - DX, DY, 0, 1, true); 240 tabPane.getTabs().removeListener(fixedListener); 241 selectedTab = (Tab)tabPane.getSelectionModel().getSelectedItem(); 242 Assert.assertEquals("Expected " + expectedTab.getText() + " to remain " 243 + "first tab.", expectedTab.getText(), selectedTab.getText()); 244 Assert.assertTrue("Change event should not be received", listenerTestResult); 245 } 246 247 public void testReorder(int dX, int dY, int xIncr, int yIncr, boolean isFixed) { 248 try { 249 Thread.sleep(1000); // Wait for tabPane to layout 250 } catch (Exception ex) { 251 fail("Thread was interrupted." + ex); 252 } 253 Platform.runLater(() -> { 254 robot.mouseMove((int)(scene.getWindow().getX() + scene.getX() + dX), 255 (int)(scene.getWindow().getY() + scene.getY() + dY)); 256 robot.mousePress(MouseButton.PRIMARY); 257 robot.mouseRelease(MouseButton.PRIMARY); 258 }); 259 waitForLatch(latches[0], 5, "Timeout waiting tabs[0] to get selected."); 260 261 CountDownLatch pressLatch = new CountDownLatch(1); 262 Platform.runLater(() -> { 263 robot.mousePress(MouseButton.PRIMARY); 264 pressLatch.countDown(); 265 }); 266 waitForLatch(pressLatch, 5, "Timeout waiting for robot.mousePress(Robot.MOUSE_LEFT_BTN)."); 267 for (int i = 0; i < DRAG_DISTANCE; i++) { 268 final int c = i; 269 CountDownLatch moveLatch = new CountDownLatch(1); 270 Platform.runLater(() -> { 271 if (xIncr > 0) { 272 // Top & Bottom 273 robot.mouseMove((int)(scene.getWindow().getX() + scene.getX() + dX) + c, 274 (int)(scene.getWindow().getY() + scene.getY() + dY)); 275 } else { 276 // Left & Right 277 robot.mouseMove((int)(scene.getWindow().getX() + scene.getX() + dX), 278 (int)(scene.getWindow().getY() + scene.getY() + dY) + c); 279 } 280 moveLatch.countDown(); 281 }); 282 waitForLatch(moveLatch, 5, "Timeout waiting for robot.mouseMove(023)."); 283 } 284 285 CountDownLatch releaseLatch = new CountDownLatch(1); 286 Platform.runLater(() -> { 287 robot.mouseRelease(MouseButton.PRIMARY); 288 releaseLatch.countDown(); 289 }); 290 waitForLatch(releaseLatch, 5, "Timeout waiting for robot.mouseRelease(Robot.MOUSE_LEFT_BTN)."); 291 292 if (isFixed) { 293 tabPane.getSelectionModel().select(tabs[2]); 294 waitForLatch(latches[2], 5, "Timeout waiting tabs[2] to get selected."); 295 latches[0] = new CountDownLatch(1); 296 } 297 298 Platform.runLater(() -> { 299 robot.mouseMove((int)(scene.getWindow().getX() + scene.getX() + dX), 300 (int)(scene.getWindow().getY() + scene.getY() + dY)); 301 robot.mousePress(MouseButton.PRIMARY); 302 robot.mouseRelease(MouseButton.PRIMARY); 303 }); 304 305 if (isFixed) { 306 // For FIXED drag policy, tabs[0] should remain the first tab. 307 try { 308 Thread.sleep(500); // Wait for ChangeListener to get called. 309 } catch (Exception ex) { 310 fail("Thread was interrupted." + ex); 311 } 312 waitForLatch(latches[0], 5, "Timeout waiting tabs[0] to get selected."); 313 } else { 314 // For REORDER drag policy, tabs[1] should be the first tab. 315 waitForLatch(changeListenerLatch, 5, "Timeout waiting ChangeListener to get called."); 316 waitForLatch(latches[1], 5, "Timeout waiting tabs[1] to get selected."); 317 } 318 } 319 320 public static class TestApp extends Application { 321 @Override 322 public void start(Stage primaryStage) { 323 robot = new Robot(); 324 stage = primaryStage; 325 tabPane = new TabPane(); 326 tabPane.setTabClosingPolicy(TabPane.TabClosingPolicy.UNAVAILABLE); 327 scene = new Scene(tabPane, SCENE_WIDTH, SCENE_HEIGHT); 328 stage.setScene(scene); 329 stage.initStyle(StageStyle.UNDECORATED); 330 stage.addEventHandler(WindowEvent.WINDOW_SHOWN, e -> 331 Platform.runLater(startupLatch::countDown)); 332 stage.setAlwaysOnTop(true); 333 stage.show(); 334 } 335 } 336 337 @BeforeClass 338 public static void initFX() { 339 startupLatch = new CountDownLatch(1); 340 new Thread(() -> Application.launch(TestApp.class, (String[])null)).start(); 341 waitForLatch(startupLatch, 10, "Timeout waiting for FX runtime to start"); 342 } 343 344 @AfterClass 345 public static void exit() { 346 Platform.runLater(() -> { 347 stage.hide(); 348 }); 349 Platform.exit(); 350 } 351 352 @Before 353 public void setupTest() { 354 changeListenerLatch = new CountDownLatch(1); 355 latches = new CountDownLatch[4]; 356 CountDownLatch latch = new CountDownLatch(1); 357 Platform.runLater(() -> { 358 tabs = new Tab[4]; 359 for (int i = 0 ; i < 4; ++i) { 360 tabs[i] = new Tab("tab" + i); 361 } 362 tabPane.getTabs().addAll(tabs); 363 tabPane.getSelectionModel().select(tabs[2]); 364 for (int i = 0 ; i < 4; ++i) { 365 final int c = i; 366 latches[i] = new CountDownLatch(1); 367 tabs[i].setOnSelectionChanged(event -> { 368 latches[c].countDown(); 369 }); 370 } 371 latch.countDown(); 372 }); 373 waitForLatch(latch, 5, "Timeout waiting for setupTest()."); 374 } 375 376 @After 377 public void resetTest() { 378 expectedTab = null; 379 selectedTab = null; 380 listenerTestResult = false; 381 CountDownLatch latch = new CountDownLatch(1); 382 Platform.runLater(() -> { 383 for (int i = 0 ; i < 4; ++i) { 384 tabs[i].setOnSelectionChanged(null); 385 } 386 tabPane.getTabs().removeAll(tabs); 387 tabs = null; 388 latch.countDown(); 389 }); 390 waitForLatch(latch, 5, "Timeout waiting for resetTest()."); 391 } 392 393 public static void waitForLatch(CountDownLatch latch, int seconds, String msg) { 394 try { 395 if (!latch.await(seconds, TimeUnit.SECONDS)) { 396 fail(msg); 397 } 398 } catch (Exception ex) { 399 fail("Unexpected exception: " + ex); 400 } 401 } 402 403 public void setDragPolicyAndSide(TabPane.TabDragPolicy dragPolicy, Side side) { 404 Util.runAndWait(() -> { 405 tabPane.setTabDragPolicy(dragPolicy); 406 tabPane.setSide(side); 407 }); 408 } 409 }