Skip to content

Commit 868b263

Browse files
committed
Added Robot test
1 parent 229e996 commit 868b263

File tree

1 file changed

+216
-0
lines changed

1 file changed

+216
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
/*
2+
* Copyright (c) 2024, 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 java.util.concurrent.CountDownLatch;
29+
30+
import javafx.application.Application;
31+
import javafx.application.Platform;
32+
import javafx.scene.Scene;
33+
import javafx.scene.control.Label;
34+
import javafx.scene.control.Menu;
35+
import javafx.scene.control.MenuBar;
36+
import javafx.scene.control.MenuItem;
37+
import javafx.scene.input.KeyCode;
38+
import javafx.scene.input.KeyCodeCombination;
39+
import javafx.scene.input.KeyCombination;
40+
import javafx.scene.layout.VBox;
41+
import javafx.scene.robot.Robot;
42+
import javafx.stage.Stage;
43+
44+
import org.junit.jupiter.api.AfterAll;
45+
import org.junit.jupiter.api.Assertions;
46+
import org.junit.jupiter.api.BeforeAll;
47+
import org.junit.jupiter.api.Test;
48+
49+
import com.sun.javafx.PlatformUtil;
50+
51+
import test.util.Util;
52+
53+
// When a key equivalent is sent it may be processed by
54+
// JavaFX and also trigger a menu item if the system menu
55+
// bar is in use.
56+
public class MenuDoubleShortcutTest {
57+
58+
static CountDownLatch startupLatch = new CountDownLatch(1);
59+
60+
static volatile Stage stage;
61+
static volatile TestApp testApp;
62+
static private final int delayMilliseconds = 100;
63+
64+
private enum TestResult {
65+
IGNORED("Key press event triggered no actions"),
66+
FIREDTWICE("Key press event consumed by scene also fired menu bar item"),
67+
FIREDMENUITEM("Key press event fired menu bar item instead of scene"),
68+
FIREDSCENE("Key press event fired scene action instead of menu bar item");
69+
70+
// We provide an explanation of what happened. Since we only see this
71+
// explanation on failure it is worded accordingly.
72+
private String explanation;
73+
TestResult(String e) {
74+
explanation = e;
75+
}
76+
77+
public String errorExplanation() {
78+
return explanation;
79+
}
80+
};
81+
82+
// KeyCode.A will be added to the menu bar and the scene
83+
// KeyCode.B will be added to the menu bar only
84+
// KeyCode.C will be added to the scene only.
85+
86+
@Test
87+
void sceneComesBeforeMenuBar() {
88+
// Assumptions.assumeTrue(PlatformUtil.isMac());
89+
90+
// KeyCode.A is in the menu bar and scene
91+
testApp.testKey(KeyCode.A);
92+
Util.sleep(delayMilliseconds);
93+
TestResult result = testApp.testResult();
94+
Assertions.assertEquals(TestResult.FIREDSCENE, result, result.errorExplanation());
95+
}
96+
97+
@Test
98+
void acceleratorOnlyInMenuBar() {
99+
// Assumptions.assumeTrue(PlatformUtil.isMac());
100+
101+
// KeyCode.B is only in the menu bar.
102+
testApp.testKey(KeyCode.B);
103+
Util.sleep(delayMilliseconds);
104+
TestResult result = testApp.testResult();
105+
Assertions.assertEquals(TestResult.FIREDMENUITEM, result, result.errorExplanation());
106+
}
107+
108+
@Test
109+
void acceleratorOnlyInScene() {
110+
// Assumptions.assumeTrue(PlatformUtil.isMac());
111+
112+
// KeyCode.C is only in the scene.
113+
testApp.testKey(KeyCode.C);
114+
Util.sleep(delayMilliseconds);
115+
TestResult result = testApp.testResult();
116+
Assertions.assertEquals(TestResult.FIREDSCENE, result, result.errorExplanation());
117+
}
118+
119+
@Test
120+
void acceleratorAbsent() {
121+
// Assumptions.assumeTrue(PlatformUtil.isMac());
122+
123+
// KeyCode.D is not registered as an accelerator
124+
testApp.testKey(KeyCode.D);
125+
Util.sleep(delayMilliseconds);
126+
TestResult result = testApp.testResult();
127+
Assertions.assertEquals(TestResult.IGNORED, result, result.errorExplanation());
128+
}
129+
130+
@BeforeAll
131+
static void initFX() throws Exception {
132+
Util.launch(startupLatch, TestApp.class);
133+
}
134+
135+
@AfterAll
136+
static void exit() {
137+
Util.shutdown();
138+
}
139+
140+
public static class TestApp extends Application {
141+
142+
private boolean sceneAcceleratorFired = false;
143+
private boolean menuBarItemFired = false;
144+
145+
private MenuItem createMenuItem(KeyCombination accelerator) {
146+
MenuItem menuItem = new MenuItem("Cmd+A menu item");
147+
menuItem.setAccelerator(accelerator);
148+
menuItem.setOnAction(e -> {
149+
menuBarItemFired = true;
150+
e.consume();
151+
});
152+
return menuItem;
153+
}
154+
155+
@Override
156+
public void start(Stage primaryStage) {
157+
158+
testApp = this;
159+
stage = primaryStage;
160+
161+
Label label = new Label("Testing accelerator double processing");
162+
163+
MenuBar menuBar = new MenuBar();
164+
menuBar.setUseSystemMenuBar(true);
165+
166+
// KeyCode.A will be added to the menu bar and the scene
167+
// KeyCode.B will be added to the menu bar only
168+
// KeyCode.C will be added to the scene only.
169+
KeyCombination acceleratorA = new KeyCodeCombination(KeyCode.A, KeyCombination.SHORTCUT_DOWN);
170+
KeyCombination acceleratorB = new KeyCodeCombination(KeyCode.B, KeyCombination.SHORTCUT_DOWN);
171+
KeyCombination acceleratorC = new KeyCodeCombination(KeyCode.C, KeyCombination.SHORTCUT_DOWN);
172+
173+
Menu menu = new Menu("Top menu");
174+
menu.getItems().add(createMenuItem(acceleratorA));
175+
menu.getItems().add(createMenuItem(acceleratorB));
176+
menuBar.getMenus().add(menu);
177+
178+
Scene scene = new Scene(new VBox(menuBar, label), 200, 200);
179+
scene.getAccelerators().put(acceleratorA, () -> {
180+
sceneAcceleratorFired = true;
181+
});
182+
scene.getAccelerators().put(acceleratorC, () -> {
183+
sceneAcceleratorFired = true;
184+
});
185+
186+
stage.setScene(scene);
187+
stage.setOnShown(e -> { startupLatch.countDown(); });
188+
stage.show();
189+
}
190+
191+
public void testKey(KeyCode code) {
192+
sceneAcceleratorFired = false;
193+
menuBarItemFired = false;
194+
Platform.runLater(() -> {
195+
// Need to ensure Cmd is present so this is handled
196+
// as a key equivalent.
197+
Robot robot = new Robot();
198+
robot.keyPress(KeyCode.COMMAND);
199+
robot.keyPress(code);
200+
robot.keyRelease(code);
201+
robot.keyRelease(KeyCode.COMMAND);
202+
});
203+
}
204+
205+
public TestResult testResult() {
206+
if (sceneAcceleratorFired && menuBarItemFired) {
207+
return TestResult.FIREDTWICE;
208+
} else if (sceneAcceleratorFired) {
209+
return TestResult.FIREDSCENE;
210+
} else if (menuBarItemFired) {
211+
return TestResult.FIREDMENUITEM;
212+
}
213+
return TestResult.IGNORED;
214+
}
215+
}
216+
}

0 commit comments

Comments
 (0)