1 /* 2 * Copyright (c) 2019, 2020, 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.scene.Scene; 30 import javafx.scene.control.Tab; 31 import javafx.scene.control.TabPane; 32 import javafx.scene.input.KeyCode; 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 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.Before; 46 import org.junit.Test; 47 import static org.junit.Assert.fail; 48 49 import test.util.Util; 50 51 /* 52 * Test for verifying that the tab headers are also permuted when the 53 * tabs which are already added to TabPane are removed and added back, 54 * using TabPane.getTabs().setAll(). See JDK-8222457 for details. 55 * 56 * Steps: 57 * a. testPermuteGetTabsWithSameTabs() 58 * 1. Add tabs 0,1,2,3,4 59 * 2. Permute the tabs to 4,3,2,1,0 using TabPane.getTabs().setAll(). 60 * 3. Verify that, 61 * 3.1 tab[0] remains selected tab. 62 * 3.2 tab[4] is the first tab in tab header. 63 * 3.3 Pressing RIGHT key should select tabs in order: tab 3,2,1,0 64 * 65 * b. testPermuteGetTabsWithMoreTabs() 66 * 1. Add tabs 0,1 67 * 2. Permute tabs to tab 1,4,3,2,0 using TabPane.getTabs().setAll(). 68 * 3. Verify that, 69 * 3.1 tab[0] should remain selected tab. 70 * 3.2 tab[1] is the first tab in tab header. 71 * 3.3 Pressing RIGHT key should select tabs in order: tab 4,3,2,0 72 * 73 * b1. testPermuteGetTabsWithMoreTabs1() 74 * 1. Add tabs 0,1 75 * 2. Permute tabs to tab 0,1,2,3 using TabPane.getTabs().setAll(). 76 * 3. Verify that, 77 * 3.1 tab[1] should remain selected tab. 78 * 3.2 tab[0] is the first tab in tab header. 79 * 3.3 Pressing RIGHT key should select tabs in order: tab 1,2,3 80 * 81 * c. testPermuteGetTabsWithLessTabs() 82 * 1. Add tab 3,1 and some(6) more tabs, and select tab 1. 83 * 2. Permute tabs to, tab 1,4,3,2 using TabPane.getTabs().setAll(). 84 * 3. Verify that, 85 * 3.1 tab[1] should remain selected tab. 86 * 3.2 Pressing RIGHT key should select tabs in order: tab 4,3,2 87 */ 88 89 public class TabPanePermuteGetTabsTest { 90 91 static CountDownLatch startupLatch; 92 static Robot robot; 93 static TabPane tabPane; 94 static volatile Stage stage; 95 static volatile Scene scene; 96 static final int firstTabdXY = 15; 97 static final int NUM_TABS = 5; 98 Tab[] tab; 99 CountDownLatch[] selectionLatch; 100 101 @Test 102 public void testPermuteGetTabsWithSameTabs() { 103 // Step #1 104 Util.runAndWait(() -> { 105 tabPane.getTabs().setAll(tab[0], tab[1], tab[2], tab[3], tab[4]); 106 }); 107 delay(); 108 Assert.assertSame("tab[0], the first tab should be the selected tab.", 109 tab[0], tabPane.getSelectionModel().getSelectedItem()); 110 111 // Step #2 112 Util.runAndWait(() -> { 113 tabPane.getTabs().setAll(tab[4], tab[3], tab[2], tab[1], tab[0]); 114 }); 115 delay(); 116 117 // Step #3.1 118 Assert.assertSame("tab[0], The selected tab should remain same after permuting.", 119 tab[0], tabPane.getSelectionModel().getSelectedItem()); 120 121 // Step #3.2 122 // Click on first tab header 123 Util.runAndWait(() -> { 124 robot.mouseMove((int) (scene.getWindow().getX() + scene.getX() + firstTabdXY), 125 (int) (scene.getWindow().getY() + scene.getY() + firstTabdXY)); 126 robot.mousePress(MouseButton.PRIMARY); 127 robot.mouseRelease(MouseButton.PRIMARY); 128 }); 129 waitForLatch(selectionLatch[4], 5, "Timeout: Waiting for tab[4] to get selected."); 130 Assert.assertSame("tab[4] should be the first tab after permuting.", 131 tab[4], tabPane.getSelectionModel().getSelectedItem()); 132 133 // Step #3.3 134 for (int i = 3; i >= 0; i--) { 135 Util.runAndWait(() -> { 136 robot.keyPress(KeyCode.RIGHT); 137 robot.keyRelease(KeyCode.RIGHT); 138 }); 139 waitForLatch(selectionLatch[i], 5, 140 "Timeout: Waiting for tab[" + i + "] to get selected."); 141 Assert.assertSame("tab[" + i + "] should get selected on RIGHT key press.", 142 tab[i], tabPane.getSelectionModel().getSelectedItem()); 143 } 144 } 145 146 @Test 147 public void testPermuteGetTabsWithMoreTabs() { 148 // Step #1 149 Util.runAndWait(() -> { 150 tabPane.getTabs().setAll(tab[0], tab[1]); 151 }); 152 delay(); 153 Assert.assertSame("tab[0], the first tab should be the selected tab.", 154 tab[0], tabPane.getSelectionModel().getSelectedItem()); 155 156 // Step #2 157 Util.runAndWait(() -> { 158 tabPane.getTabs().setAll(tab[1], tab[4], tab[3], tab[2], tab[0]); 159 }); 160 delay(); 161 162 // Step #3.1 163 Assert.assertSame("tab[0], The selected tab should remain same after permuting.", 164 tab[0], tabPane.getSelectionModel().getSelectedItem()); 165 166 // Step #3.2 167 // Click on first tab header 168 Util.runAndWait(() -> { 169 robot.mouseMove((int) (scene.getWindow().getX() + scene.getX() + firstTabdXY), 170 (int) (scene.getWindow().getY() + scene.getY() + firstTabdXY)); 171 robot.mousePress(MouseButton.PRIMARY); 172 robot.mouseRelease(MouseButton.PRIMARY); 173 }); 174 waitForLatch(selectionLatch[1], 5, "Timeout: Waiting for tab[1] to get selected."); 175 Assert.assertSame("tab[1] should be the first tab after permuting.", 176 tab[1], tabPane.getSelectionModel().getSelectedItem()); 177 178 // Step #3.3 179 for (int i = 4; i >= 0; i--) { 180 if (i == 1) continue; 181 Util.runAndWait(() -> { 182 robot.keyPress(KeyCode.RIGHT); 183 robot.keyRelease(KeyCode.RIGHT); 184 }); 185 waitForLatch(selectionLatch[i], 5, 186 "Timeout: Waiting for tab[" + i + "] to get selected."); 187 Assert.assertSame("tab[" + i + "] should get selected on RIGHT key press.", 188 tab[i], tabPane.getSelectionModel().getSelectedItem()); 189 } 190 } 191 192 // Test for JDK-8237602 193 @Test 194 public void testPermutGetTabsWithMoreTabs1() { 195 // Step #1 196 Util.runAndWait(() -> { 197 tabPane.getTabs().setAll(tab[0], tab[1]); 198 tabPane.getSelectionModel().select(tab[1]); 199 }); 200 delay(); 201 202 Assert.assertSame("Sanity: tab[1] should be the selected tab.", 203 tab[1], tabPane.getSelectionModel().getSelectedItem()); 204 205 // Step #2 206 Util.runAndWait(() -> { 207 tabPane.getTabs().setAll(tab[0], tab[1], tab[2], tab[3]); 208 }); 209 delay(); 210 211 // Step #3.1 212 Assert.assertSame("Sanity: tab[1] should remain selected tab after permuting.", 213 tab[1], tabPane.getSelectionModel().getSelectedItem()); 214 215 // Step #3.2 216 // Click on first tab header 217 selectionLatch[0] = new CountDownLatch(1); 218 Util.runAndWait(() -> { 219 robot.mouseMove((int) (scene.getWindow().getX() + scene.getX() + firstTabdXY), 220 (int) (scene.getWindow().getY() + scene.getY() + firstTabdXY)); 221 robot.mousePress(MouseButton.PRIMARY); 222 robot.mouseRelease(MouseButton.PRIMARY); 223 }); 224 delay(); 225 226 waitForLatch(selectionLatch[0], 5, 227 "Timeout: Waiting for tab[" + 0 + "] to get selected."); 228 Assert.assertSame("tab[0] should be first tab after permuting.", 229 tab[0], tabPane.getSelectionModel().getSelectedItem()); 230 231 // step #3.3 232 selectionLatch[1] = new CountDownLatch(1); 233 for (int i = 1; i <= 3; i++) { 234 Util.runAndWait(() -> { 235 robot.keyPress(KeyCode.RIGHT); 236 robot.keyRelease(KeyCode.RIGHT); 237 }); 238 waitForLatch(selectionLatch[i], 5, 239 "Timeout: Waiting for tab[" + i + "] to get selected."); 240 Assert.assertSame("tab[" + i + "] should get selected on RIGHT key press.", 241 tab[i], tabPane.getSelectionModel().getSelectedItem()); 242 } 243 } 244 245 @Test 246 public void testPermutGetTabsWithLessTabs() { 247 // Step #1 248 Util.runAndWait(() -> { 249 tabPane.getTabs().setAll(tab[3], tab[1], new Tab("t1"), new Tab("t2"), 250 new Tab("t3"), new Tab("t4"), new Tab("t5"), new Tab("t6")); 251 tabPane.getSelectionModel().select(tab[1]); 252 }); 253 delay(); 254 255 Assert.assertSame("tab[1] should be the selected tab.", 256 tab[1], tabPane.getSelectionModel().getSelectedItem()); 257 258 // Step #2 259 Util.runAndWait(() -> { 260 tabPane.getTabs().setAll(tab[1], tab[4], tab[3], tab[2]); 261 }); 262 delay(); 263 264 // Step #3.1 265 Assert.assertSame("tab[1] should remain selected tab after permuting.", 266 tab[1], tabPane.getSelectionModel().getSelectedItem()); 267 268 // Step #3.2 269 // Click on first tab header 270 Util.runAndWait(() -> { 271 robot.mouseMove((int) (scene.getWindow().getX() + scene.getX() + firstTabdXY), 272 (int) (scene.getWindow().getY() + scene.getY() + firstTabdXY)); 273 robot.mousePress(MouseButton.PRIMARY); 274 robot.mouseRelease(MouseButton.PRIMARY); 275 }); 276 for (int i = 4; i >= 2; i--) { 277 Util.runAndWait(() -> { 278 robot.keyPress(KeyCode.RIGHT); 279 robot.keyRelease(KeyCode.RIGHT); 280 }); 281 waitForLatch(selectionLatch[i], 5, 282 "Timeout: Waiting for tab[" + i + "] to get selected."); 283 Assert.assertSame("tab[" + i + "] should get selected on RIGHT key press.", 284 tab[i], tabPane.getSelectionModel().getSelectedItem()); 285 } 286 } 287 288 public static class TestApp extends Application { 289 @Override 290 public void start(Stage primaryStage) { 291 stage = primaryStage; 292 robot = new Robot(); 293 tabPane = new TabPane(); 294 tabPane.setTabClosingPolicy(TabPane.TabClosingPolicy.UNAVAILABLE); 295 296 scene = new Scene(tabPane, 300, 100); 297 stage.setScene(scene); 298 stage.initStyle(StageStyle.UNDECORATED); 299 stage.addEventHandler(WindowEvent.WINDOW_SHOWN, e -> 300 Platform.runLater(startupLatch::countDown)); 301 stage.setAlwaysOnTop(true); 302 stage.show(); 303 } 304 } 305 306 @BeforeClass 307 public static void initFX() { 308 startupLatch = new CountDownLatch(1); 309 new Thread(() -> Application.launch(TestApp.class, (String[])null)).start(); 310 waitForLatch(startupLatch, 10, "Timeout waiting for FX runtime to start"); 311 } 312 313 @Before 314 public void setupTest() { 315 Util.runAndWait(() -> { 316 tab = new Tab[NUM_TABS]; 317 selectionLatch = new CountDownLatch[NUM_TABS]; 318 for (int i = 0; i < NUM_TABS; i++) { 319 final int c = i; 320 tab[i] = new Tab("tab" + i); 321 selectionLatch[i] = new CountDownLatch(1); 322 tab[i].setOnSelectionChanged(event -> selectionLatch[c].countDown()); 323 } 324 }); 325 } 326 327 @AfterClass 328 public static void exit() { 329 Platform.runLater(() -> { 330 stage.hide(); 331 }); 332 Platform.exit(); 333 } 334 335 public static void delay() { 336 try { 337 Thread.sleep(500); // Wait for tabPane to layout 338 } catch (Exception ex) { 339 fail("Thread was interrupted." + ex); 340 } 341 } 342 343 public static void waitForLatch(CountDownLatch latch, int seconds, String msg) { 344 try { 345 if (!latch.await(seconds, TimeUnit.SECONDS)) { 346 fail(msg); 347 } 348 } catch (Exception ex) { 349 fail("Unexpected exception: " + ex); 350 } 351 } 352 } --- EOF ---