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 ---