diff --git a/tests/manual/text/.classpath b/tests/manual/text/.classpath index eb25bab3655..4cf27eb27b9 100644 --- a/tests/manual/text/.classpath +++ b/tests/manual/text/.classpath @@ -20,6 +20,7 @@ + diff --git a/tests/manual/text/EmojiTest.java b/tests/manual/text/EmojiTest.java index 1d625811e70..7e757c7b706 100644 --- a/tests/manual/text/EmojiTest.java +++ b/tests/manual/text/EmojiTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,64 +23,44 @@ * questions. */ -import javafx.application.Application; -import javafx.application.Platform; -import javafx.scene.layout.HBox; -import javafx.scene.layout.VBox; -import javafx.scene.Group; -import javafx.scene.Scene; -import javafx.scene.control.Button; +import javafx.scene.Node; import javafx.scene.control.Label; import javafx.scene.control.TextField; -import javafx.stage.Stage; +import javafx.scene.layout.VBox; import javafx.scene.text.Font; import javafx.scene.text.Text; +import com.oracle.util.testing.ManualTestWindow; -public class EmojiTest extends Application { - - static String instructions = - """ - This tests rendering of Emoji glyphs which is only supported on macOS. - On macOS you should see a yellow-coloured smiling face image, - embedded between 'ab' and 'cd'. - On other platforms it may be a missing glyph, or an empty space, or - a similar rendering as a greyscale/B&W glyph. - Principally, you are checking that the emoji is rendered on macOS in - each of the controls and nodes displayed in the test, and that the - editable text field handles selection of the emoji glyph with the - same background as other glyphs - this presumes the emoji image has - transparent background pixels. - There are 3 different ways it is displayed to verify - 1) Text node. 2) Label control, 3) TextField Control - Press the Pass or Fail button as appropriate and the test will exit. - If what you see is not explained here, ask before filing a bug. - - - """; - +public class EmojiTest extends ManualTestWindow { public static void main(String[] args) { launch(args); } - private void quit() { - Platform.exit(); + public EmojiTest() { + super( + "Emoji Rendering Test (macOS)", + """ + This tests rendering of Emoji glyphs which is only supported on macOS. + On macOS you should see a yellow-coloured smiling face image, + embedded between 'ab' and 'cd'. + On other platforms it may be a missing glyph, or an empty space, or + a similar rendering as a greyscale/B&W glyph. + Principally, you are checking that the emoji is rendered on macOS in + each of the controls and nodes displayed in the test, and that the + editable text field handles selection of the emoji glyph with the + same background as other glyphs - this presumes the emoji image has + transparent background pixels. + There are 3 different ways it is displayed to verify + 1) Text node. 2) Label control, 3) TextField Control + Press the Pass or Fail button as appropriate and the test will exit. + If what you see is not explained here, ask before filing a bug. + """, + 1200, 800 + ); } @Override - public void start(Stage stage) { - Button passButton = new Button("Pass"); - Button failButton = new Button("Fail"); - passButton.setOnAction(e -> this.quit()); - failButton.setOnAction(e -> { - this.quit(); - throw new AssertionError("The Emoji was not rendered on macOS"); - }); - - HBox hbox = new HBox(10, passButton, failButton); - - Text instTA = new Text(instructions); - instTA.setWrappingWidth(500); - + protected Node createContent() { Font font = new Font(32); String emojiString = "ab\ud83d\ude00cd"; Text text = new Text(emojiString); @@ -90,18 +70,11 @@ public void start(Stage stage) { TextField textField = new TextField(emojiString); textField.setFont(font); - VBox vbox = new VBox(); - Scene scene = new Scene(vbox); - vbox.getChildren().add(instTA); - vbox.getChildren().add(hbox); - vbox.getChildren().add(text); - vbox.getChildren().add(label); - vbox.getChildren().add(textField); - stage.setWidth(600); - stage.setHeight(600); - stage.setScene(scene); - - stage.show(); + return new VBox( + 2, + text, + label, + textField + ); } - } diff --git a/tests/manual/util/.classpath b/tests/manual/util/.classpath new file mode 100644 index 00000000000..42d3197b29c --- /dev/null +++ b/tests/manual/util/.classpath @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/manual/util/.project b/tests/manual/util/.project new file mode 100644 index 00000000000..b8ed69cded9 --- /dev/null +++ b/tests/manual/util/.project @@ -0,0 +1,17 @@ + + + manualTests-util + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/tests/manual/util/.settings/org.eclipse.core.resources.prefs b/tests/manual/util/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000000..99f26c0203a --- /dev/null +++ b/tests/manual/util/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/=UTF-8 diff --git a/tests/manual/util/README.md b/tests/manual/util/README.md new file mode 100644 index 00000000000..b31a38611ea --- /dev/null +++ b/tests/manual/util/README.md @@ -0,0 +1,38 @@ +# Test Utilities + +## ManualTestWindow + +This facility provides the base class for manual tests which displays the test instructions, +the UI under test, and the Pass/Fail buttons. + +Example: + +```java +public class ManualTestExample extends ManualTestWindow { + public ManualTestExample() { + super( + "Manual Test Example", + """ + Instructions: + 1. you will see a button named "Test" + 2. press the button + 3. verify that the button can be pressed""", + 400, 250 + ); + } + + public static void main(String[] args) throws Exception { + launch(args); + } + + @Override + protected Node createContent() { + return new Button("Test"); + } +} +``` + +Resulting application window: + +![screenshot](doc/ManualTestWindow.png) + diff --git a/tests/manual/util/doc/ManualTestWindow.png b/tests/manual/util/doc/ManualTestWindow.png new file mode 100644 index 00000000000..c400b43c4dc Binary files /dev/null and b/tests/manual/util/doc/ManualTestWindow.png differ diff --git a/tests/manual/util/src/com/oracle/util/testing/ManualTestWindow.java b/tests/manual/util/src/com/oracle/util/testing/ManualTestWindow.java new file mode 100644 index 00000000000..5ab388cd648 --- /dev/null +++ b/tests/manual/util/src/com/oracle/util/testing/ManualTestWindow.java @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.util.testing; + +import javafx.application.Application; +import javafx.application.Platform; +import javafx.geometry.Insets; +import javafx.scene.Node; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.scene.control.Button; +import javafx.scene.control.ContextMenu; +import javafx.scene.control.MenuItem; +import javafx.scene.effect.BlurType; +import javafx.scene.effect.DropShadow; +import javafx.scene.input.Clipboard; +import javafx.scene.input.ClipboardContent; +import javafx.scene.layout.Background; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Priority; +import javafx.scene.layout.Region; +import javafx.scene.layout.VBox; +import javafx.scene.paint.Color; +import javafx.scene.text.Text; +import javafx.scene.text.TextFlow; +import javafx.stage.Stage; + +/** + * Provides the base class for manual tests which displays the test instructions, + * the UI under test, and the Pass/Fail buttons. + *

+ * Example: + *

{@code public class ManualTestExample extends ManualTestWindow {
+ *    public ManualTestExample() {
+ *        super(
+ *            "Manual Test Example",
+ *            """
+ *            Instructions:
+ *            1. you will see a button named "Test"
+ *            2. press the button
+ *            3. verify that the button can be pressed""",
+ *            400, 250
+ *        );
+ *     }
+ *
+ *     public static void main(String[] args) throws Exception {
+ *         launch(args);
+ *     }
+ *
+ *     @Override
+ *     protected Node createContent() {
+ *         return new Button("Test");
+ *     }
+ * }
+ * }
+ */ +public abstract class ManualTestWindow extends Application { + /** + * This method creates the {@code Node} containing elements under test, + * to be shown below the instructions and above the "Pass"/"Fail" buttons. + * @return the node + */ + protected abstract Node createContent(); + + private final String title; + private final String instructions; + private double width = 1000; + private double height = 800; + + public ManualTestWindow(String title, String instructions) { + this.title = title; + this.instructions = instructions; + } + + public ManualTestWindow(String title, String instructions, double width, double height) { + this(title, instructions); + this.width = width; + this.height = height; + } + + private Parent createContent(Stage stage) { + Node content = createContent(); + + BlurType blurType = BlurType.GAUSSIAN; + Color color = Color.gray(0, 0.5); + double radius = 10; + double spread = 0; + double offsetX = 1; + double offsetY = 1; + DropShadow shadow = new DropShadow(blurType, color, radius, spread, offsetX, offsetY); + + BorderPane cp = new BorderPane(content); + cp.setMargin(content, new Insets(10)); + cp.setBackground(Background.fill(Color.gray(1))); + cp.setEffect(shadow); + + Node instructionField = toTextFlow(instructions); + + Region fill = new Region(); + + Button failButton = new Button("Fail"); + setIcon(failButton, "✘", Color.RED); + failButton.setMinWidth(100); + failButton.setOnAction((ev) -> { + Platform.exit(); + throw new AssertionError("Failed Manual Test: " + stage.getTitle()); + }); + + Button passButton = new Button("Pass"); + setIcon(passButton, "✔", Color.GREEN); + passButton.setMinWidth(100); + passButton.setOnAction((ev) -> { + Platform.exit(); + }); + + HBox buttons = new HBox( + 10, + fill, + failButton, + passButton + ); + HBox.setHgrow(fill, Priority.ALWAYS); + + VBox vb = new VBox( + 10, + instructionField, + cp, + buttons + ); + vb.setPadding(new Insets(10)); + VBox.setVgrow(cp, Priority.ALWAYS); + return vb; + } + + /** + * Prepares the Application primary stage: creates the content {@code Node} to be tested, + * creates the manual test UI, sets the {@code Scene}. + * This method is called before the primary stage is shown. + * @param stage the primary stage + */ + protected void prepareStage(Stage stage) { + Parent content = createContent(stage); + stage.setWidth(width); + stage.setHeight(height); + stage.setTitle(title); + stage.setScene(new Scene(content)); + } + + @Override + public void start(Stage stage) throws Exception { + prepareStage(stage); + stage.show(); + } + + private static Node toTextFlow(String text) { + TextFlow f = new TextFlow(); + Text t = new Text(text); + f.getChildren().add(t); + f.setOnContextMenuRequested((ev) -> { + ContextMenu m = new ContextMenu(); + MenuItem mi = new MenuItem("Copy Instructions"); + mi.setOnAction((e) -> { + ClipboardContent cc = new ClipboardContent(); + cc.putString(text); + Clipboard.getSystemClipboard().setContent(cc); + }); + m.getItems().setAll(mi); + m.show(f, ev.getScreenX(), ev.getScreenY()); + }); + return f; + } + + private static void setIcon(Button b, String text, Color c) { + Text t = new Text(text); + t.setFill(c); + b.setGraphic(t); + } +}