From 27f27dac965874e6a8342fe58cbabc29df9352dd Mon Sep 17 00:00:00 2001 From: "lawrence.andrews" Date: Fri, 5 Jun 2026 19:05:39 +0000 Subject: [PATCH 01/17] 8385302: Open source accessibility AWT tests Reviewed-by: prr, kizune --- .../javax/accessibility/awt/CheckboxTest.java | 145 ++++ .../javax/accessibility/awt/FrameTest.java | 158 ++++ .../javax/accessibility/awt/LabelTest.java | 100 +++ .../jdk/javax/accessibility/awt/ListTest.java | 157 ++++ .../javax/accessibility/awt/MenuBarTest.java | 102 +++ .../javax/accessibility/awt/MenuItemTest.java | 103 +++ .../jdk/javax/accessibility/awt/MenuTest.java | 99 +++ .../javax/accessibility/awt/PanelTest.java | 105 +++ .../accessibility/awt/PopupMenuTest.java | 100 +++ .../accessibility/awt/ScrollPaneTest.java | 104 +++ .../javax/accessibility/awt/TextAreaTest.java | 155 ++++ .../accessibility/awt/TextFieldTest.java | 148 ++++ .../javax/accessibility/awt/WindowTest.java | 129 ++++ .../AccessibleMenuComponentTester.java | 114 +++ .../accessibility/AccessibleTestUtils.java | 712 ++++++++++++++---- 15 files changed, 2271 insertions(+), 160 deletions(-) create mode 100644 test/jdk/javax/accessibility/awt/CheckboxTest.java create mode 100644 test/jdk/javax/accessibility/awt/FrameTest.java create mode 100644 test/jdk/javax/accessibility/awt/LabelTest.java create mode 100644 test/jdk/javax/accessibility/awt/ListTest.java create mode 100644 test/jdk/javax/accessibility/awt/MenuBarTest.java create mode 100644 test/jdk/javax/accessibility/awt/MenuItemTest.java create mode 100644 test/jdk/javax/accessibility/awt/MenuTest.java create mode 100644 test/jdk/javax/accessibility/awt/PanelTest.java create mode 100644 test/jdk/javax/accessibility/awt/PopupMenuTest.java create mode 100644 test/jdk/javax/accessibility/awt/ScrollPaneTest.java create mode 100644 test/jdk/javax/accessibility/awt/TextAreaTest.java create mode 100644 test/jdk/javax/accessibility/awt/TextFieldTest.java create mode 100644 test/jdk/javax/accessibility/awt/WindowTest.java create mode 100644 test/jdk/javax/swing/regtesthelpers/accessibility/AccessibleMenuComponentTester.java diff --git a/test/jdk/javax/accessibility/awt/CheckboxTest.java b/test/jdk/javax/accessibility/awt/CheckboxTest.java new file mode 100644 index 00000000000..7c690c16848 --- /dev/null +++ b/test/jdk/javax/accessibility/awt/CheckboxTest.java @@ -0,0 +1,145 @@ +/* + * Copyright (c) 1997, 2026, 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. + * + * 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. + */ + +/* + * @test + * @key headful + * @summary Checkbox Accessibility test. + * @library ../../swing/regtesthelpers/accessibility/ + * @build AccessibleTestUtils AccessibleComponentTester AccessibleStateSetTester + * @run main CheckboxTest + */ + +import java.awt.AWTException; +import java.awt.Checkbox; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Robot; +import java.lang.reflect.InvocationTargetException; + +import javax.accessibility.AccessibleState; +import javax.accessibility.AccessibleStateSet; + +public class CheckboxTest { + + private static Checkbox checkbox; + private static Frame frame; + + private static final String ACCESSIBLE_NAME = "Checkbox Test"; + private static final String ACCESSIBLE_DESCRIPTION = + "Regression Test: javax.accessibility, Checkbox"; + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException, AWTException { + CheckboxTest checkboxTest = new CheckboxTest(); + EventQueue.invokeAndWait(checkboxTest::createGUI); + + Robot robot = new Robot(); + robot.waitForIdle(); + robot.delay(5000); + + try { + EventQueue.invokeAndWait(checkboxTest::test); + } finally { + checkboxTest.dispose(); + } + } + + private void createGUI() { + frame = new Frame("Checkbox Test"); + checkbox = new Checkbox("This is a checkbox", true); + + checkbox.getAccessibleContext().setAccessibleName(ACCESSIBLE_NAME); + checkbox.getAccessibleContext().setAccessibleDescription( + ACCESSIBLE_DESCRIPTION); + + frame.add(checkbox); + frame.setSize(200, 200); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + private void dispose() throws InterruptedException, + InvocationTargetException { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + + private void test() { + AccessibleTestUtils.verifyCheckboxAccessibility( + checkbox, + ACCESSIBLE_NAME, + ACCESSIBLE_DESCRIPTION + ); + + new CheckboxStateTester( + checkbox, + checkbox.getAccessibleContext().getAccessibleStateSet() + ).testAll(); + + checkbox.setState(!checkbox.getState()); + + AccessibleTestUtils.verifyCheckboxAccessibility( + checkbox, + ACCESSIBLE_NAME, + ACCESSIBLE_DESCRIPTION + ); + + new CheckboxStateTester( + checkbox, + checkbox.getAccessibleContext().getAccessibleStateSet() + ).testAll(); + } + + private static final class CheckboxStateTester + extends AccessibleStateSetTester { + private final Checkbox checkbox; + private final AccessibleStateSet set; + + private CheckboxStateTester(Checkbox checkbox, AccessibleStateSet set) { + super(checkbox, set); + this.checkbox = checkbox; + this.set = set; + } + + @Override + public void testChecked() { + if (set.contains(AccessibleState.CHECKED)) { + if (!checkbox.getState()) { + throw new RuntimeException( + "AccessibleStateSet contains CHECKED but " + + "this component is not checked"); + } + } else { + if (checkbox.getState()) { + throw new RuntimeException( + "AccessibleStateSet does not contain CHECKED " + + "but this component is checked"); + } + } + } + } +} diff --git a/test/jdk/javax/accessibility/awt/FrameTest.java b/test/jdk/javax/accessibility/awt/FrameTest.java new file mode 100644 index 00000000000..4abe3246574 --- /dev/null +++ b/test/jdk/javax/accessibility/awt/FrameTest.java @@ -0,0 +1,158 @@ +/* + * Copyright (c) 1997, 2026, 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. + * + * 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. + */ + +/* + * @test + * @key headful + * @summary Frame Accessibility test. + * @library ../../swing/regtesthelpers/accessibility/ + * @build AccessibleTestUtils AccessibleComponentTester AccessibleStateSetTester + * @run main FrameTest + */ + +import java.awt.AWTException; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Robot; +import java.lang.reflect.InvocationTargetException; + +import javax.accessibility.AccessibleState; +import javax.accessibility.AccessibleStateSet; + +public class FrameTest { + + private static Frame frame; + + private static final String ACCESSIBLE_NAME = "Frame Test"; + private static final String ACCESSIBLE_DESCRIPTION = + "Regression Test: javax.accessibility, Frame"; + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException, AWTException { + FrameTest frameTest = new FrameTest(); + EventQueue.invokeAndWait(frameTest::createGUI); + + Robot robot = new Robot(); + robot.waitForIdle(); + robot.delay(5000); + + try { + EventQueue.invokeAndWait(frameTest::test); + } finally { + frameTest.dispose(); + } + } + + private void createGUI() { + frame = new Frame("Frame Test"); + + frame.getAccessibleContext().setAccessibleName(ACCESSIBLE_NAME); + frame.getAccessibleContext().setAccessibleDescription( + ACCESSIBLE_DESCRIPTION); + + frame.setSize(300, 300); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + private void dispose() throws InterruptedException, + InvocationTargetException { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + + private void test() { + AccessibleTestUtils.verifyFrameAccessibility( + frame, + ACCESSIBLE_NAME, + ACCESSIBLE_DESCRIPTION + ); + + new FrameStateTester( + frame, + frame.getAccessibleContext().getAccessibleStateSet() + ).testAll(); + + frame.setResizable(false); + + AccessibleTestUtils.verifyFrameAccessibility( + frame, + ACCESSIBLE_NAME, + ACCESSIBLE_DESCRIPTION + ); + + new FrameStateTester( + frame, + frame.getAccessibleContext().getAccessibleStateSet() + ).testAll(); + } + + private static final class FrameStateTester + extends AccessibleStateSetTester { + private final Frame component; + private final AccessibleStateSet set; + + private FrameStateTester(Frame frame, AccessibleStateSet set) { + super(frame, set); + this.component = frame; + this.set = set; + } + + @Override + public void testResizable() { + if (set.contains(AccessibleState.RESIZABLE)) { + if (!component.isResizable()) { + throw new RuntimeException( + "AccessibleStateSet contains RESIZABLE but " + + "this component is not resizable"); + } + } else { + if (component.isResizable()) { + throw new RuntimeException( + "AccessibleStateSet does not contain RESIZABLE " + + "but this component is resizable"); + } + } + } + + @Override + public void testActive() { + if (set.contains(AccessibleState.ACTIVE)) { + if (component.getFocusOwner() == null) { + throw new RuntimeException( + "AccessibleStateSet contains ACTIVE but " + + "this component is not active"); + } + } else { + if (component.getFocusOwner() != null) { + throw new RuntimeException( + "AccessibleStateSet does not contain ACTIVE but " + + "this component is active"); + } + } + } + } +} diff --git a/test/jdk/javax/accessibility/awt/LabelTest.java b/test/jdk/javax/accessibility/awt/LabelTest.java new file mode 100644 index 00000000000..dd837793eb5 --- /dev/null +++ b/test/jdk/javax/accessibility/awt/LabelTest.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 1997, 2026, 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. + * + * 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. + */ + +/* + * @test + * @key headful + * @summary Label Accessibility test. + * @library ../../swing/regtesthelpers/accessibility/ + * @build AccessibleTestUtils AccessibleComponentTester AccessibleStateSetTester + * @run main LabelTest + */ + +import java.awt.AWTException; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Label; +import java.awt.Robot; +import java.lang.reflect.InvocationTargetException; + +public class LabelTest { + + private static Label label; + private static Frame frame; + + private static final String ACCESSIBLE_NAME = "Label Test"; + private static final String ACCESSIBLE_DESCRIPTION = + "Regression Test: javax.accessibility, Label"; + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException, AWTException { + LabelTest labelTest = new LabelTest(); + EventQueue.invokeAndWait(labelTest::createGUI); + + Robot robot = new Robot(); + robot.waitForIdle(); + robot.delay(5000); + + try { + EventQueue.invokeAndWait(labelTest::test); + } finally { + labelTest.dispose(); + } + } + + private void createGUI() { + frame = new Frame("Label Test"); + label = new Label("This is a label"); + + label.getAccessibleContext().setAccessibleName(ACCESSIBLE_NAME); + label.getAccessibleContext().setAccessibleDescription( + ACCESSIBLE_DESCRIPTION); + + frame.add(label); + frame.setSize(200, 200); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + private void dispose() throws InterruptedException, + InvocationTargetException { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + + private void test() { + AccessibleTestUtils.verifyLabelAccessibility( + label, + ACCESSIBLE_NAME, + ACCESSIBLE_DESCRIPTION + ); + + new AccessibleStateSetTester( + label, + label.getAccessibleContext().getAccessibleStateSet() + ).testAll(); + } +} diff --git a/test/jdk/javax/accessibility/awt/ListTest.java b/test/jdk/javax/accessibility/awt/ListTest.java new file mode 100644 index 00000000000..011d850a4cb --- /dev/null +++ b/test/jdk/javax/accessibility/awt/ListTest.java @@ -0,0 +1,157 @@ +/* + * Copyright (c) 1997, 2026, 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. + * + * 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. + */ + +/* + * @test + * @key headful + * @summary List Accessibility test. + * @library ../../swing/regtesthelpers/accessibility/ + * @build AccessibleTestUtils AccessibleComponentTester AccessibleStateSetTester + * @run main ListTest + */ + +import java.awt.AWTException; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.List; +import java.awt.Robot; +import java.lang.reflect.InvocationTargetException; + +import javax.accessibility.AccessibleState; +import javax.accessibility.AccessibleStateSet; + +public class ListTest { + + private static List list; + private static Frame frame; + + private static final String ACCESSIBLE_NAME = "List Test"; + private static final String ACCESSIBLE_DESCRIPTION = + "Regression Test: javax.accessibility, List"; + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException, AWTException { + ListTest listTest = new ListTest(); + EventQueue.invokeAndWait(listTest::createGUI); + + Robot robot = new Robot(); + robot.waitForIdle(); + robot.delay(5000); + + try { + EventQueue.invokeAndWait(listTest::test); + } finally { + listTest.dispose(); + } + } + + private void createGUI() { + frame = new Frame("List Test"); + list = new List(); + + list.add("Mercury"); + list.add("Venus"); + list.add("Earth"); + list.add("JavaSoft"); + list.add("Mars"); + list.add("Jupiter"); + list.add("Saturn"); + list.add("Uranus"); + list.add("Neptune"); + list.add("Pluto"); + + list.getAccessibleContext().setAccessibleName(ACCESSIBLE_NAME); + list.getAccessibleContext().setAccessibleDescription( + ACCESSIBLE_DESCRIPTION); + + frame.add(list); + frame.setSize(200, 200); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + private void dispose() throws InterruptedException, + InvocationTargetException { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + + private void test() { + AccessibleTestUtils.verifyListAccessibility( + list, + ACCESSIBLE_NAME, + ACCESSIBLE_DESCRIPTION + ); + + new ListStateTester( + list, + list.getAccessibleContext().getAccessibleStateSet() + ).testAll(); + + list.setMultipleMode(!list.isMultipleMode()); + + AccessibleTestUtils.verifyListAccessibility( + list, + ACCESSIBLE_NAME, + ACCESSIBLE_DESCRIPTION + ); + + new ListStateTester( + list, + list.getAccessibleContext().getAccessibleStateSet() + ).testAll(); + } + + private static final class ListStateTester + extends AccessibleStateSetTester { + private final List list; + private final AccessibleStateSet set; + + private ListStateTester(List list, AccessibleStateSet set) { + super(list, set); + this.list = list; + this.set = set; + } + + @Override + public void testMultiSelectable() { + if (set.contains(AccessibleState.MULTISELECTABLE)) { + if (!list.isMultipleMode()) { + throw new RuntimeException( + "AccessibleStateSet contains MULTISELECTABLE " + + "but this component is not multiselectable"); + } + } else { + if (list.isMultipleMode()) { + throw new RuntimeException( + "AccessibleStateSet does not contain " + + "MULTISELECTABLE but this component is " + + "multiselectable"); + } + } + } + } +} diff --git a/test/jdk/javax/accessibility/awt/MenuBarTest.java b/test/jdk/javax/accessibility/awt/MenuBarTest.java new file mode 100644 index 00000000000..83445fde61b --- /dev/null +++ b/test/jdk/javax/accessibility/awt/MenuBarTest.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 1997, 2026, 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. + * + * 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. + */ + +/* + * @test + * @key headful + * @summary MenuBar Accessibility test. + * @library ../../swing/regtesthelpers/accessibility/ + * @build AccessibleTestUtils AccessibleMenuComponentTester + * @run main MenuBarTest + */ + +import java.awt.AWTException; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.Robot; +import java.lang.reflect.InvocationTargetException; + +public class MenuBarTest { + + private static MenuBar menuBar; + private static Frame frame; + + private static final String ACCESSIBLE_NAME = "Menu Test"; + private static final String ACCESSIBLE_DESCRIPTION = + "Regression Test: javax.accessibility, MenuBar"; + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException, AWTException { + MenuBarTest menuBarTest = new MenuBarTest(); + EventQueue.invokeAndWait(menuBarTest::createGUI); + + Robot robot = new Robot(); + robot.waitForIdle(); + robot.delay(5000); + + try { + EventQueue.invokeAndWait(menuBarTest::test); + } finally { + menuBarTest.dispose(); + } + } + + private void createGUI() { + frame = new Frame("MenuBar Test"); + menuBar = new MenuBar(); + + Menu menu = new Menu("Menu 1"); + menu.add(new MenuItem("One")); + menu.add(new MenuItem("Two")); + menuBar.add(menu); + + menuBar.getAccessibleContext().setAccessibleName(ACCESSIBLE_NAME); + menuBar.getAccessibleContext().setAccessibleDescription( + ACCESSIBLE_DESCRIPTION); + + frame.setMenuBar(menuBar); + frame.setSize(200, 200); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + private void dispose() throws InterruptedException, + InvocationTargetException { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + + private void test() { + AccessibleTestUtils.verifyMenuBarAccessibility( + menuBar, + ACCESSIBLE_NAME, + ACCESSIBLE_DESCRIPTION + ); + } +} diff --git a/test/jdk/javax/accessibility/awt/MenuItemTest.java b/test/jdk/javax/accessibility/awt/MenuItemTest.java new file mode 100644 index 00000000000..0cfe342e843 --- /dev/null +++ b/test/jdk/javax/accessibility/awt/MenuItemTest.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 1997, 2026, 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. + * + * 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. + */ + +/* + * @test + * @key headful + * @summary MenuItem Accessibility test. + * @library ../../swing/regtesthelpers/accessibility/ + * @build AccessibleTestUtils AccessibleMenuComponentTester + * @run main MenuItemTest + */ + +import java.awt.AWTException; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.Robot; +import java.lang.reflect.InvocationTargetException; + +public class MenuItemTest { + + private static MenuItem menuItem; + private static Frame frame; + + private static final String ACCESSIBLE_NAME = "MenuItem Test"; + private static final String ACCESSIBLE_DESCRIPTION = + "Regression Test: javax.accessibility, MenuItem"; + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException, AWTException { + MenuItemTest menuItemTest = new MenuItemTest(); + EventQueue.invokeAndWait(menuItemTest::createGUI); + + Robot robot = new Robot(); + robot.waitForIdle(); + robot.delay(5000); + + try { + EventQueue.invokeAndWait(menuItemTest::test); + } finally { + menuItemTest.dispose(); + } + } + + private void createGUI() { + frame = new Frame("MenuItem Test"); + + MenuBar menuBar = new MenuBar(); + Menu menu = new Menu("Menu"); + menuItem = new MenuItem("This here's a MenuItem"); + + menu.add(menuItem); + menuBar.add(menu); + + menuItem.getAccessibleContext().setAccessibleName(ACCESSIBLE_NAME); + menuItem.getAccessibleContext().setAccessibleDescription( + ACCESSIBLE_DESCRIPTION); + + frame.setMenuBar(menuBar); + frame.setSize(300, 300); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + private void dispose() throws InterruptedException, + InvocationTargetException { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + + private void test() { + AccessibleTestUtils.verifyMenuItemAccessibility( + menuItem, + ACCESSIBLE_NAME, + ACCESSIBLE_DESCRIPTION + ); + } +} diff --git a/test/jdk/javax/accessibility/awt/MenuTest.java b/test/jdk/javax/accessibility/awt/MenuTest.java new file mode 100644 index 00000000000..15c51ca4214 --- /dev/null +++ b/test/jdk/javax/accessibility/awt/MenuTest.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 1997, 2026, 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. + * + * 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. + */ + +/* + * @test + * @key headful + * @summary Menu Accessibility test. + * @library ../../swing/regtesthelpers/accessibility/ + * @build AccessibleTestUtils AccessibleMenuComponentTester + * @run main MenuTest + */ + +import java.awt.AWTException; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.Robot; +import java.lang.reflect.InvocationTargetException; + +public class MenuTest { + + private static Menu menu; + private static Frame frame; + + private static final String ACCESSIBLE_NAME = "Menu Test"; + private static final String ACCESSIBLE_DESCRIPTION = + "Regression Test: javax.accessibility, Menu"; + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException, AWTException { + MenuTest menuTest = new MenuTest(); + EventQueue.invokeAndWait(menuTest::createGUI); + + Robot robot = new Robot(); + robot.waitForIdle(); + robot.delay(5000); + + try { + EventQueue.invokeAndWait(menuTest::test); + } finally { + menuTest.dispose(); + } + } + + private void createGUI() { + frame = new Frame("Menu Test"); + + MenuBar menuBar = new MenuBar(); + menu = new Menu("File"); + menuBar.add(menu); + + menu.getAccessibleContext().setAccessibleName(ACCESSIBLE_NAME); + menu.getAccessibleContext().setAccessibleDescription( + ACCESSIBLE_DESCRIPTION); + + frame.setMenuBar(menuBar); + frame.setSize(200, 200); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + private void dispose() throws InterruptedException, + InvocationTargetException { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + + private void test() { + AccessibleTestUtils.verifyMenuAccessibility( + menu, + ACCESSIBLE_NAME, + ACCESSIBLE_DESCRIPTION + ); + } +} diff --git a/test/jdk/javax/accessibility/awt/PanelTest.java b/test/jdk/javax/accessibility/awt/PanelTest.java new file mode 100644 index 00000000000..abfacc7485b --- /dev/null +++ b/test/jdk/javax/accessibility/awt/PanelTest.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 1997, 2026, 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. + * + * 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. + */ + +/* + * @test + * @key headful + * @summary Panel Accessibility test. + * @library ../../swing/regtesthelpers/accessibility/ + * @build AccessibleTestUtils AccessibleComponentTester AccessibleStateSetTester + * @run main PanelTest + */ + +import java.awt.AWTException; +import java.awt.Button; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Panel; +import java.awt.Robot; +import java.lang.reflect.InvocationTargetException; + +public class PanelTest { + + private static Panel panel; + private static Frame frame; + + private static final String ACCESSIBLE_NAME = "Panel Test"; + private static final String ACCESSIBLE_DESCRIPTION = + "Regression Test: javax.accessibility, Panel"; + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException, AWTException { + PanelTest panelTest = new PanelTest(); + EventQueue.invokeAndWait(panelTest::createGUI); + + Robot robot = new Robot(); + robot.waitForIdle(); + robot.delay(5000); + + try { + EventQueue.invokeAndWait(panelTest::test); + } finally { + panelTest.dispose(); + } + } + + private void createGUI() { + frame = new Frame("Panel Test"); + panel = new Panel(); + + for (int i = 0; i < 5; i++) { + panel.add(new Button("Button #" + i)); + } + + panel.getAccessibleContext().setAccessibleName(ACCESSIBLE_NAME); + panel.getAccessibleContext().setAccessibleDescription( + ACCESSIBLE_DESCRIPTION); + + frame.add(panel); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + private void dispose() throws InterruptedException, + InvocationTargetException { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + + private void test() { + AccessibleTestUtils.verifyPanelAccessibility( + panel, + ACCESSIBLE_NAME, + ACCESSIBLE_DESCRIPTION + ); + + new AccessibleStateSetTester( + panel, + panel.getAccessibleContext().getAccessibleStateSet() + ).testAll(); + } +} diff --git a/test/jdk/javax/accessibility/awt/PopupMenuTest.java b/test/jdk/javax/accessibility/awt/PopupMenuTest.java new file mode 100644 index 00000000000..8d329a92322 --- /dev/null +++ b/test/jdk/javax/accessibility/awt/PopupMenuTest.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 1997, 2026, 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. + * + * 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. + */ + +/* + * @test + * @key headful + * @summary PopupMenu Accessibility test. + * @library ../../swing/regtesthelpers/accessibility/ + * @build AccessibleTestUtils AccessibleMenuComponentTester + * @run main PopupMenuTest + */ + +import java.awt.AWTException; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.PopupMenu; +import java.awt.Robot; +import java.lang.reflect.InvocationTargetException; + +public class PopupMenuTest { + + private static PopupMenu popupMenu; + private static Frame frame; + + private static final String ACCESSIBLE_NAME = "PopupMenu Test"; + private static final String ACCESSIBLE_DESCRIPTION = + "Regression Test: javax.accessibility, PopupMenu"; + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException, AWTException { + PopupMenuTest popupMenuTest = new PopupMenuTest(); + EventQueue.invokeAndWait(popupMenuTest::createGUI); + + Robot robot = new Robot(); + robot.waitForIdle(); + robot.delay(5000); + + try { + EventQueue.invokeAndWait(popupMenuTest::test); + } finally { + popupMenuTest.dispose(); + } + } + + private void createGUI() { + frame = new Frame("PopupMenu Test"); + popupMenu = new PopupMenu("PopupMenu"); + + popupMenu.add("Sleepy"); + popupMenu.add("Happy"); + popupMenu.add("Grumpy"); + popupMenu.add("Dopey"); + + popupMenu.getAccessibleContext().setAccessibleName(ACCESSIBLE_NAME); + popupMenu.getAccessibleContext().setAccessibleDescription( + ACCESSIBLE_DESCRIPTION); + + frame.add(popupMenu); + frame.setSize(200, 200); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + private void dispose() throws InterruptedException, + InvocationTargetException { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + + private void test() { + AccessibleTestUtils.verifyPopupMenuAccessibility( + popupMenu, + ACCESSIBLE_NAME, + ACCESSIBLE_DESCRIPTION + ); + } +} diff --git a/test/jdk/javax/accessibility/awt/ScrollPaneTest.java b/test/jdk/javax/accessibility/awt/ScrollPaneTest.java new file mode 100644 index 00000000000..7f706647482 --- /dev/null +++ b/test/jdk/javax/accessibility/awt/ScrollPaneTest.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 1997, 2026, 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. + * + * 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. + */ + +/* + * @test + * @key headful + * @summary ScrollPane Accessibility test. + * @library ../../swing/regtesthelpers/accessibility/ + * @build AccessibleTestUtils AccessibleComponentTester AccessibleStateSetTester + * @run main ScrollPaneTest + */ + +import java.awt.AWTException; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Label; +import java.awt.Robot; +import java.awt.ScrollPane; +import java.lang.reflect.InvocationTargetException; + +public class ScrollPaneTest { + + private static ScrollPane scrollPane; + private static Frame frame; + + private static final String ACCESSIBLE_NAME = "ScrollPane Test"; + private static final String ACCESSIBLE_DESCRIPTION = + "Regression Test: javax.accessibility, ScrollPane"; + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException, AWTException { + ScrollPaneTest scrollPaneTest = new ScrollPaneTest(); + EventQueue.invokeAndWait(scrollPaneTest::createGUI); + + Robot robot = new Robot(); + robot.waitForIdle(); + robot.delay(5000); + + try { + EventQueue.invokeAndWait(scrollPaneTest::test); + } finally { + scrollPaneTest.dispose(); + } + } + + private void createGUI() { + frame = new Frame("ScrollPane Test"); + frame.setSize(300, 300); + frame.setLocationRelativeTo(null); + + scrollPane = new ScrollPane(ScrollPane.SCROLLBARS_ALWAYS); + scrollPane.setSize(frame.getSize().width, frame.getSize().height); + scrollPane.add(new Label("This is a label with quite a bit of text.")); + + scrollPane.getAccessibleContext().setAccessibleName(ACCESSIBLE_NAME); + scrollPane.getAccessibleContext().setAccessibleDescription( + ACCESSIBLE_DESCRIPTION); + + frame.add(scrollPane); + frame.setVisible(true); + } + + private void dispose() throws InterruptedException, + InvocationTargetException { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + + private void test() { + AccessibleTestUtils.verifyScrollPaneAccessibility( + scrollPane, + ACCESSIBLE_NAME, + ACCESSIBLE_DESCRIPTION + ); + + new AccessibleStateSetTester( + scrollPane, + scrollPane.getAccessibleContext().getAccessibleStateSet() + ).testAll(); + } +} diff --git a/test/jdk/javax/accessibility/awt/TextAreaTest.java b/test/jdk/javax/accessibility/awt/TextAreaTest.java new file mode 100644 index 00000000000..388a22324f9 --- /dev/null +++ b/test/jdk/javax/accessibility/awt/TextAreaTest.java @@ -0,0 +1,155 @@ +/* + * Copyright (c) 1997, 2026, 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. + * + * 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. + */ + +/* + * @test + * @key headful + * @summary TextArea Accessibility test. + * @library ../../swing/regtesthelpers/accessibility/ + * @build AccessibleTestUtils AccessibleComponentTester AccessibleStateSetTester + * @run main TextAreaTest + */ + +import java.awt.AWTException; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Robot; +import java.awt.TextArea; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.lang.reflect.InvocationTargetException; + +import javax.accessibility.AccessibleState; +import javax.accessibility.AccessibleStateSet; + +public class TextAreaTest { + + private static TextArea textArea; + private static Frame frame; + + private static final String ACCESSIBLE_NAME = "TextArea Test"; + private static final String ACCESSIBLE_DESCRIPTION = + "Regression Test: javax.accessibility, TextArea"; + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException, AWTException { + TextAreaTest textAreaTest = new TextAreaTest(); + EventQueue.invokeAndWait(textAreaTest::createGUI); + + Robot robot = new Robot(); + robot.waitForIdle(); + robot.delay(5000); + + try { + EventQueue.invokeAndWait(textAreaTest::test); + } finally { + textAreaTest.dispose(); + } + } + + private void createGUI() { + frame = new Frame("TextAreaTest"); + String TEXT_CONTENT = """ + 1. Test TextArea javax.accessibility methods + 2. Test TextArea javax.accessibility setAccessibleName + 3. Test TextArea javax.accessibility setAccessibleDescription + 4. Test TextArea javax.accessibility setAccessibleStateSet + """; + textArea = new TextArea(TEXT_CONTENT, 24, 80); + + textArea.getAccessibleContext().setAccessibleName(ACCESSIBLE_NAME); + textArea.getAccessibleContext().setAccessibleDescription( + ACCESSIBLE_DESCRIPTION); + + frame.add(textArea); + frame.setSize(200, 200); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + private void dispose() throws InterruptedException, + InvocationTargetException { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + + private void test() { + AccessibleTestUtils.verifyTextAreaAccessibility( + textArea, + ACCESSIBLE_NAME, + ACCESSIBLE_DESCRIPTION + ); + + new TextStateTester( + textArea, + textArea.getAccessibleContext().getAccessibleStateSet() + ).testAll(); + + textArea.setEditable(false); + + AccessibleTestUtils.verifyTextAreaAccessibility( + textArea, + ACCESSIBLE_NAME, + ACCESSIBLE_DESCRIPTION + ); + + new TextStateTester( + textArea, + textArea.getAccessibleContext().getAccessibleStateSet() + ).testAll(); + } + + private static final class TextStateTester + extends AccessibleStateSetTester { + private final TextArea textArea; + private final AccessibleStateSet stateSet; + + private TextStateTester(TextArea textArea, + AccessibleStateSet stateSet) { + super(textArea, stateSet); + this.textArea = textArea; + this.stateSet = stateSet; + } + + @Override + public void testEditable() { + if (stateSet.contains(AccessibleState.EDITABLE)) { + if (!textArea.isEditable()) { + throw new RuntimeException( + "AccessibleStateSet contains EDITABLE but " + + "this component is not editable"); + } + } else { + if (textArea.isEditable()) { + throw new RuntimeException( + "AccessibleStateSet does not contain EDITABLE " + + "but this component is editable"); + } + } + } + } +} diff --git a/test/jdk/javax/accessibility/awt/TextFieldTest.java b/test/jdk/javax/accessibility/awt/TextFieldTest.java new file mode 100644 index 00000000000..96deaf3b7bc --- /dev/null +++ b/test/jdk/javax/accessibility/awt/TextFieldTest.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 1997, 2026, 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. + * + * 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. + */ + +/* + * @test + * @key headful + * @summary TextField Accessibility test. + * @library ../../swing/regtesthelpers/accessibility/ + * @build AccessibleTestUtils AccessibleComponentTester AccessibleStateSetTester + * @run main TextFieldTest + */ + +import java.awt.AWTException; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Robot; +import java.awt.TextField; +import java.lang.reflect.InvocationTargetException; + +import javax.accessibility.AccessibleState; +import javax.accessibility.AccessibleStateSet; + +public class TextFieldTest { + + private static TextField textField; + private static Frame frame; + + private static final String ACCESSIBLE_NAME = "TextField Test"; + private static final String ACCESSIBLE_DESCRIPTION = + "Regression Test: javax.accessibility, TextField"; + private static final String TEXT = + "I love Cheesy Poofs you love Cheesy Poofs if we didn't " + + "eat Cheesy Poofs we'd be lame!"; + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException, AWTException { + TextFieldTest textFieldTest = new TextFieldTest(); + EventQueue.invokeAndWait(textFieldTest::createGUI); + + Robot robot = new Robot(); + robot.waitForIdle(); + robot.delay(5000); + + try { + EventQueue.invokeAndWait(textFieldTest::test); + } finally { + textFieldTest.dispose(); + } + } + + private void createGUI() { + frame = new Frame("TextField Test"); + textField = new TextField(TEXT, 80); + + textField.getAccessibleContext().setAccessibleName(ACCESSIBLE_NAME); + textField.getAccessibleContext().setAccessibleDescription( + ACCESSIBLE_DESCRIPTION); + + frame.add(textField); + frame.setSize(200, 200); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + private void dispose() throws InterruptedException, + InvocationTargetException { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + + private void test() { + AccessibleTestUtils.verifyTextFieldAccessibility( + textField, + ACCESSIBLE_NAME, + ACCESSIBLE_DESCRIPTION + ); + + new TextStateTester( + textField, + textField.getAccessibleContext().getAccessibleStateSet() + ).testAll(); + + textField.setEditable(false); + + AccessibleTestUtils.verifyTextFieldAccessibility( + textField, + ACCESSIBLE_NAME, + ACCESSIBLE_DESCRIPTION + ); + + new TextStateTester( + textField, + textField.getAccessibleContext().getAccessibleStateSet() + ).testAll(); + } + + private static final class TextStateTester + extends AccessibleStateSetTester { + private final TextField textField; + private final AccessibleStateSet set; + + private TextStateTester(TextField textField, AccessibleStateSet set) { + super(textField, set); + this.textField = textField; + this.set = set; + } + + @Override + public void testEditable() { + if (set.contains(AccessibleState.EDITABLE)) { + if (!textField.isEditable()) { + throw new RuntimeException( + "AccessibleStateSet contains EDITABLE but " + + "this component is not editable"); + } + } else { + if (textField.isEditable()) { + throw new RuntimeException( + "AccessibleStateSet does not contain EDITABLE " + + "but this component is editable"); + } + } + } + } +} diff --git a/test/jdk/javax/accessibility/awt/WindowTest.java b/test/jdk/javax/accessibility/awt/WindowTest.java new file mode 100644 index 00000000000..dc70ce75d90 --- /dev/null +++ b/test/jdk/javax/accessibility/awt/WindowTest.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 1997, 2026, 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. + * + * 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. + */ + +/* + * @test + * @key headful + * @summary Window Accessibility test. + * @library ../../swing/regtesthelpers/accessibility/ + * @build AccessibleTestUtils AccessibleComponentTester AccessibleStateSetTester + * @run main WindowTest + */ + +import java.awt.AWTException; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Robot; +import java.awt.Window; +import java.lang.reflect.InvocationTargetException; + +import javax.accessibility.AccessibleState; +import javax.accessibility.AccessibleStateSet; + +public class WindowTest { + + private static Window window; + + private static final String ACCESSIBLE_NAME = "Window Test"; + private static final String ACCESSIBLE_DESCRIPTION = + "Regression Test: javax.accessibility, Window"; + + public static void main(String[] args) throws InterruptedException, + InvocationTargetException, AWTException { + WindowTest windowTest = new WindowTest(); + EventQueue.invokeAndWait(windowTest::createGUI); + + Robot robot = new Robot(); + robot.waitForIdle(); + robot.delay(5000); + + try { + EventQueue.invokeAndWait(windowTest::test); + } finally { + windowTest.dispose(); + } + } + + private void createGUI() { + window = new Window(new Frame("Frame")); + window.setSize(300, 300); + window.setLocationRelativeTo(null); + + window.getAccessibleContext().setAccessibleName(ACCESSIBLE_NAME); + window.getAccessibleContext().setAccessibleDescription( + ACCESSIBLE_DESCRIPTION); + + window.setVisible(true); + } + + private void dispose() throws InterruptedException, + InvocationTargetException { + EventQueue.invokeAndWait(() -> { + if (window != null) { + window.dispose(); + } + }); + } + + private void test() { + AccessibleTestUtils.verifyWindowAccessibility( + window, + ACCESSIBLE_NAME, + ACCESSIBLE_DESCRIPTION + ); + + new WindowStateTester( + window, + window.getAccessibleContext().getAccessibleStateSet() + ).testAll(); + } + + private static final class WindowStateTester + extends AccessibleStateSetTester { + private final Window window; + private final AccessibleStateSet set; + + private WindowStateTester(Window window, AccessibleStateSet set) { + super(window, set); + this.window = window; + this.set = set; + } + + @Override + public void testActive() { + if (set.contains(AccessibleState.ACTIVE)) { + if (window.getFocusOwner() == null) { + throw new RuntimeException( + "AccessibleStateSet contains ACTIVE but " + + "this component is not active"); + } + } else { + if (window.getFocusOwner() != null) { + throw new RuntimeException( + "AccessibleStateSet does not contain ACTIVE " + + "but this component is active"); + } + } + } + } +} diff --git a/test/jdk/javax/swing/regtesthelpers/accessibility/AccessibleMenuComponentTester.java b/test/jdk/javax/swing/regtesthelpers/accessibility/AccessibleMenuComponentTester.java new file mode 100644 index 00000000000..21b91aafbcd --- /dev/null +++ b/test/jdk/javax/swing/regtesthelpers/accessibility/AccessibleMenuComponentTester.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 1997, 2026, 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. + * + * 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. + */ + +import java.awt.Font; +import java.awt.MenuComponent; + +import javax.accessibility.AccessibleComponent; + +public class AccessibleMenuComponentTester { + + private final AccessibleComponent acomp; + private final MenuComponent comp; + + public AccessibleMenuComponentTester(MenuComponent menuComponent, + AccessibleComponent ac) { + if (menuComponent == null) { + throw new RuntimeException("MenuComponent should not be null"); + } + + if (ac == null) { + throw new RuntimeException("AccessibleComponent should not be null"); + } + + this.comp = menuComponent; + this.acomp = ac; + } + + // The only method supported by MenuComponents is getFont(). Everything + // else should return null. + public void test() { + testGetBackground(); + testGetBounds(); + testGetCursor(); + testGetFont(); + testGetForeground(); + testGetLocation(); + testGetLocationOnScreen(); + testGetSize(); + testIsEnabled(); + testIsFocusTraversable(); + testIsShowing(); + testIsVisible(); + } + + public void testGetBackground() { + } + + public void testGetBounds() { + } + + public void testGetCursor() { + } + + public void testGetFont() { + Font accessibleFont = acomp.getFont(); + Font componentFont = comp.getFont(); + + if (componentFont == null) { + if (accessibleFont != null) { + throw new RuntimeException( + "MenuComponent.getFont returned null but " + + "AccessibleComponent.getFont did not"); + } + } else if (!componentFont.equals(accessibleFont)) { + throw new RuntimeException( + "AccessibleComponent.getFont does not match " + + "MenuComponent.getFont"); + } + } + + public void testGetForeground() { + } + + public void testGetLocation() { + } + + public void testGetLocationOnScreen() { + } + + public void testGetSize() { + } + + public void testIsEnabled() { + } + + public void testIsFocusTraversable() { + } + + public void testIsShowing() { + } + + public void testIsVisible() { + } +} diff --git a/test/jdk/javax/swing/regtesthelpers/accessibility/AccessibleTestUtils.java b/test/jdk/javax/swing/regtesthelpers/accessibility/AccessibleTestUtils.java index 2410cff997a..9de4a8d8089 100644 --- a/test/jdk/javax/swing/regtesthelpers/accessibility/AccessibleTestUtils.java +++ b/test/jdk/javax/swing/regtesthelpers/accessibility/AccessibleTestUtils.java @@ -22,17 +22,33 @@ */ import java.awt.Button; +import java.awt.Checkbox; import java.awt.Choice; import java.awt.Component; +import java.awt.Frame; +import java.awt.Label; +import java.awt.List; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuComponent; +import java.awt.MenuItem; +import java.awt.Panel; +import java.awt.PopupMenu; import java.awt.Scrollbar; +import java.awt.ScrollPane; +import java.awt.TextArea; +import java.awt.TextField; +import java.awt.Window; import java.util.Locale; import java.util.Objects; +import javax.accessibility.Accessible; import javax.accessibility.AccessibleAction; import javax.accessibility.AccessibleComponent; import javax.accessibility.AccessibleContext; import javax.accessibility.AccessibleRole; import javax.accessibility.AccessibleSelection; +import javax.accessibility.AccessibleState; import javax.accessibility.AccessibleStateSet; import javax.accessibility.AccessibleText; import javax.accessibility.AccessibleValue; @@ -51,32 +67,22 @@ public final class AccessibleTestUtils { boolean expectSelection, boolean expectText, boolean expectValue) { - Objects.requireNonNull(context, "AccessibleContext must not be null"); - assertExpectedString( - "getAccessibleName", - expectedName, - context.getAccessibleName() - ); + assertExpectedString("getAccessibleName", + expectedName, context.getAccessibleName()); + assertExpectedString("getAccessibleDescription", + expectedDescription, context.getAccessibleDescription()); - assertExpectedString( - "getAccessibleDescription", - expectedDescription, - context.getAccessibleDescription() - ); + AccessibleRole actualRole = context.getAccessibleRole(); + if (actualRole == null) { + throw new RuntimeException("getAccessibleRole returned null"); + } - if (expectedRole != null) { - AccessibleRole actualRole = context.getAccessibleRole(); - if (actualRole == null) { - throw new RuntimeException("getAccessibleRole returned null"); - } - if (!expectedRole.equals(actualRole)) { - throw new RuntimeException(String.format( - "getAccessibleRole returned [%s]; expected [%s]", - actualRole, expectedRole - )); - } + if (!expectedRole.equals(actualRole)) { + throw new RuntimeException( + "getAccessibleRole returned [" + actualRole + + "]; expected [" + expectedRole + "]"); } AccessibleStateSet stateSet = context.getAccessibleStateSet(); @@ -84,10 +90,14 @@ public final class AccessibleTestUtils { throw new RuntimeException("getAccessibleStateSet returned null"); } - assertPresence("getAccessibleAction", expectAction, context.getAccessibleAction()); - assertPresence("getAccessibleSelection", expectSelection, context.getAccessibleSelection()); - assertPresence("getAccessibleText", expectText, context.getAccessibleText()); - assertPresence("getAccessibleValue", expectValue, context.getAccessibleValue()); + assertPresence("getAccessibleAction", + expectAction, context.getAccessibleAction()); + assertPresence("getAccessibleSelection", + expectSelection, context.getAccessibleSelection()); + assertPresence("getAccessibleText", + expectText, context.getAccessibleText()); + assertPresence("getAccessibleValue", + expectValue, context.getAccessibleValue()); } public static void verifyAWTComponentAccessibility( @@ -99,10 +109,13 @@ public final class AccessibleTestUtils { boolean expectSelection, boolean expectText, boolean expectValue) { - - Objects.requireNonNull(component, "Component under test must not be null"); + Objects.requireNonNull(component, "Component must not be null"); AccessibleContext context = component.getAccessibleContext(); + if (context == null) { + throw new RuntimeException("getAccessibleContext returned null"); + } + verifyAccessibleContextCommon( context, expectedName, @@ -111,12 +124,12 @@ public final class AccessibleTestUtils { expectAction, expectSelection, expectText, - expectValue - ); + expectValue); assertLocaleMatches(component, context); - AccessibleComponent accessibleComponent = context.getAccessibleComponent(); + AccessibleComponent accessibleComponent = + context.getAccessibleComponent(); if (accessibleComponent == null) { throw new RuntimeException("getAccessibleComponent returned null"); } @@ -124,101 +137,10 @@ public final class AccessibleTestUtils { new AccessibleComponentTester(component, accessibleComponent).test(); } - public static void verifyChoiceAccessibility( - Choice choice, - String expectedName, - String expectedDescription) { - - Objects.requireNonNull(choice, "Choice must not be null"); - - verifyAWTComponentAccessibility( - choice, - expectedName, - expectedDescription, - AccessibleRole.COMBO_BOX, - true, - false, - false, - false - ); - - AccessibleContext context = choice.getAccessibleContext(); - - AccessibleAction action = context.getAccessibleAction(); - if (action == null) { - throw new RuntimeException("getAccessibleAction should not return null for Choice"); - } - - AccessibleValue value = context.getAccessibleValue(); - if (value != null) { - throw new RuntimeException("getAccessibleValue should return null for Choice"); - } - } - - public static void verifyScrollbarAccessibility( - Scrollbar scrollbar, - String expectedName, - String expectedDescription) { - - Objects.requireNonNull(scrollbar, "Scrollbar must not be null"); - - verifyAWTComponentAccessibility( - scrollbar, - expectedName, - expectedDescription, - AccessibleRole.SCROLL_BAR, - false, - false, - false, - true - ); - - AccessibleValue value = scrollbar.getAccessibleContext().getAccessibleValue(); - if (value == null) { - throw new RuntimeException("getAccessibleValue should not return null for Scrollbar"); - } - - assertIntValueEquals( - "getCurrentAccessibleValue", - scrollbar.getValue(), - value.getCurrentAccessibleValue() - ); - - assertIntValueEquals( - "getMinimumAccessibleValue", - scrollbar.getMinimum(), - value.getMinimumAccessibleValue() - ); - - assertIntValueEquals( - "getMaximumAccessibleValue", - scrollbar.getMaximum(), - value.getMaximumAccessibleValue() - ); - - if (!value.setCurrentAccessibleValue(Integer.valueOf(5))) { - throw new RuntimeException("setCurrentAccessibleValue(5) returned false for Scrollbar"); - } - - assertIntValueEquals( - "getCurrentAccessibleValue after setCurrentAccessibleValue(5)", - 5, - value.getCurrentAccessibleValue() - ); - - if (scrollbar.getValue() != 5) { - throw new RuntimeException( - "setCurrentAccessibleValue(5) did not update Scrollbar.getValue(); actual value: " - + scrollbar.getValue() - ); - } - } - public static void verifyButtonAccessibility( Button button, String expectedName, String expectedDescription) { - Objects.requireNonNull(button, "Button must not be null"); verifyAWTComponentAccessibility( @@ -229,106 +151,576 @@ public final class AccessibleTestUtils { true, false, false, - true - ); + true); AccessibleContext context = button.getAccessibleContext(); + verifyClickAction(context, "Button"); + verifyZeroAccessibleValue(context, "Button"); + } + public static void verifyCheckboxAccessibility( + Checkbox checkbox, + String expectedName, + String expectedDescription) { + Objects.requireNonNull(checkbox, "Checkbox must not be null"); + + verifyAWTComponentAccessibility( + checkbox, + expectedName, + expectedDescription, + AccessibleRole.CHECK_BOX, + true, + false, + false, + true); + } + + public static void verifyChoiceAccessibility( + Choice choice, + String expectedName, + String expectedDescription) { + Objects.requireNonNull(choice, "Choice must not be null"); + + verifyAWTComponentAccessibility( + choice, + expectedName, + expectedDescription, + AccessibleRole.COMBO_BOX, + true, + false, + false, + false); + } + + public static void verifyFrameAccessibility( + Frame frame, + String expectedName, + String expectedDescription) { + Objects.requireNonNull(frame, "Frame must not be null"); + + verifyAWTComponentAccessibility( + frame, + expectedName, + expectedDescription, + AccessibleRole.FRAME, + false, + false, + false, + false); + } + + public static void verifyLabelAccessibility( + Label label, + String expectedName, + String expectedDescription) { + Objects.requireNonNull(label, "Label must not be null"); + + verifyAWTComponentAccessibility( + label, + expectedName, + expectedDescription, + AccessibleRole.LABEL, + false, + false, + false, + false); + } + + public static void verifyListAccessibility( + List list, + String expectedName, + String expectedDescription) { + Objects.requireNonNull(list, "List must not be null"); + + verifyAWTComponentAccessibility( + list, + expectedName, + expectedDescription, + AccessibleRole.LIST, + false, + true, + false, + false); + + verifyListChildren(list); + } + + public static void verifyPanelAccessibility( + Panel panel, + String expectedName, + String expectedDescription) { + Objects.requireNonNull(panel, "Panel must not be null"); + + verifyAWTComponentAccessibility( + panel, + expectedName, + expectedDescription, + AccessibleRole.PANEL, + false, + false, + false, + false); + + verifyContainerChildren(panel, "Panel"); + } + + public static void verifyScrollbarAccessibility( + Scrollbar scrollbar, + String expectedName, + String expectedDescription) { + Objects.requireNonNull(scrollbar, "Scrollbar must not be null"); + + verifyAWTComponentAccessibility( + scrollbar, + expectedName, + expectedDescription, + AccessibleRole.SCROLL_BAR, + false, + false, + false, + true); + + AccessibleValue value = + scrollbar.getAccessibleContext().getAccessibleValue(); + + assertIntValueEquals( + "getCurrentAccessibleValue", + scrollbar.getValue(), + value.getCurrentAccessibleValue()); + + assertIntValueEquals( + "getMinimumAccessibleValue", + scrollbar.getMinimum(), + value.getMinimumAccessibleValue()); + + assertIntValueEquals( + "getMaximumAccessibleValue", + scrollbar.getMaximum(), + value.getMaximumAccessibleValue()); + + if (!value.setCurrentAccessibleValue(Integer.valueOf(5))) { + throw new RuntimeException( + "setCurrentAccessibleValue should not return false " + + "for Scrollbar"); + } + + assertIntValueEquals( + "getCurrentAccessibleValue after setCurrentAccessibleValue(5)", + 5, + value.getCurrentAccessibleValue()); + + if (scrollbar.getValue() != 5) { + throw new RuntimeException( + "setCurrentAccessibleValue should change the Scrollbar " + + "value"); + } + } + + public static void verifyScrollPaneAccessibility( + ScrollPane scrollPane, + String expectedName, + String expectedDescription) { + Objects.requireNonNull(scrollPane, "ScrollPane must not be null"); + + verifyAWTComponentAccessibility( + scrollPane, + expectedName, + expectedDescription, + AccessibleRole.SCROLL_PANE, + false, + false, + false, + false); + } + + public static void verifyTextAreaAccessibility( + TextArea textArea, + String expectedName, + String expectedDescription) { + Objects.requireNonNull(textArea, "TextArea must not be null"); + + verifyAWTComponentAccessibility( + textArea, + expectedName, + expectedDescription, + AccessibleRole.TEXT, + false, + false, + true, + false); + } + + public static void verifyTextFieldAccessibility( + TextField textField, + String expectedName, + String expectedDescription) { + Objects.requireNonNull(textField, "TextField must not be null"); + + verifyAWTComponentAccessibility( + textField, + expectedName, + expectedDescription, + AccessibleRole.TEXT, + false, + false, + true, + false); + } + + public static void verifyWindowAccessibility( + Window window, + String expectedName, + String expectedDescription) { + Objects.requireNonNull(window, "Window must not be null"); + + verifyAWTComponentAccessibility( + window, + expectedName, + expectedDescription, + AccessibleRole.WINDOW, + false, + false, + false, + false); + } + + public static void verifyMenuAccessibility( + Menu menu, + String expectedName, + String expectedDescription) { + Objects.requireNonNull(menu, "Menu must not be null"); + + verifyMenuComponentAccessibility( + menu, + expectedName, + expectedDescription, + AccessibleRole.MENU, + true, + true, + false, + true, + "Menu"); + + AccessibleContext context = menu.getAccessibleContext(); + verifyClickAction(context, "Menu"); + verifyZeroAccessibleValue(context, "Menu"); + } + + public static void verifyMenuBarAccessibility( + MenuBar menuBar, + String expectedName, + String expectedDescription) { + Objects.requireNonNull(menuBar, "MenuBar must not be null"); + + verifyMenuComponentAccessibility( + menuBar, + expectedName, + expectedDescription, + AccessibleRole.MENU_BAR, + false, + true, + false, + false, + "MenuBar"); + } + + public static void verifyMenuItemAccessibility( + MenuItem menuItem, + String expectedName, + String expectedDescription) { + Objects.requireNonNull(menuItem, "MenuItem must not be null"); + + verifyMenuComponentAccessibility( + menuItem, + expectedName, + expectedDescription, + AccessibleRole.MENU_ITEM, + true, + true, + false, + true, + "MenuItem"); + + AccessibleContext context = menuItem.getAccessibleContext(); + verifyClickAction(context, "MenuItem"); + verifyZeroAccessibleValue(context, "MenuItem"); + } + + public static void verifyPopupMenuAccessibility( + PopupMenu popupMenu, + String expectedName, + String expectedDescription) { + Objects.requireNonNull(popupMenu, "PopupMenu must not be null"); + + verifyMenuComponentAccessibility( + popupMenu, + expectedName, + expectedDescription, + AccessibleRole.POPUP_MENU, + true, + true, + false, + true, + "PopupMenu"); + + AccessibleContext context = popupMenu.getAccessibleContext(); + verifyClickAction(context, "PopupMenu"); + verifyZeroAccessibleValue(context, "PopupMenu"); + } + + private static void verifyMenuComponentAccessibility( + MenuComponent menuComponent, + String expectedName, + String expectedDescription, + AccessibleRole expectedRole, + boolean expectAction, + boolean expectSelection, + boolean expectText, + boolean expectValue, + String componentName) { + Objects.requireNonNull(menuComponent, + componentName + " must not be null"); + + AccessibleContext context = menuComponent.getAccessibleContext(); + if (context == null) { + throw new RuntimeException("getAccessibleContext returned null"); + } + + verifyAccessibleContextCommon( + context, + expectedName, + expectedDescription, + expectedRole, + expectAction, + expectSelection, + expectText, + expectValue); + + AccessibleComponent accessibleComponent = + context.getAccessibleComponent(); + if (accessibleComponent == null) { + throw new RuntimeException("getAccessibleComponent returned null"); + } + + new AccessibleMenuComponentTester( + menuComponent, accessibleComponent).test(); + } + + private static void verifyClickAction(AccessibleContext context, + String componentName) { AccessibleAction action = context.getAccessibleAction(); if (action == null) { - throw new RuntimeException("getAccessibleAction should not return null for Button"); + throw new RuntimeException( + "getAccessibleAction should not return null for " + + componentName); } int actionCount = action.getAccessibleActionCount(); if (actionCount != 1) { throw new RuntimeException( - "getAccessibleActionCount should return 1 for Button; got " + actionCount - ); + "getAccessibleActionCount returned the wrong number for " + + componentName); } String actionDescription = action.getAccessibleActionDescription(0); if (!"click".equals(actionDescription)) { throw new RuntimeException( - "getAccessibleActionDescription(0) should return \"click\" for Button; got [" - + actionDescription + "]" - ); + "getAccessibleActionDescription returned the wrong " + + "description for " + componentName); } + } + private static void verifyZeroAccessibleValue(AccessibleContext context, + String componentName) { AccessibleValue value = context.getAccessibleValue(); if (value == null) { - throw new RuntimeException("getAccessibleValue should not return null for Button"); + throw new RuntimeException( + "getAccessibleValue should not return null for " + + componentName); } - assertIntValueEquals("getCurrentAccessibleValue", 0, value.getCurrentAccessibleValue()); - assertIntValueEquals("getMinimumAccessibleValue", 0, value.getMinimumAccessibleValue()); - assertIntValueEquals("getMaximumAccessibleValue", 0, value.getMaximumAccessibleValue()); + assertIntValueEquals( + "getCurrentAccessibleValue", + 0, + value.getCurrentAccessibleValue()); + + assertIntValueEquals( + "getMinimumAccessibleValue", + 0, + value.getMinimumAccessibleValue()); + + assertIntValueEquals( + "getMaximumAccessibleValue", + 0, + value.getMaximumAccessibleValue()); if (value.setCurrentAccessibleValue(Integer.valueOf(5))) { throw new RuntimeException( - "setCurrentAccessibleValue(5) should return false for Button" - ); + "setCurrentAccessibleValue should return false for " + + componentName); } assertIntValueEquals( "getCurrentAccessibleValue after setCurrentAccessibleValue(5)", 0, - value.getCurrentAccessibleValue() - ); + value.getCurrentAccessibleValue()); } - private static void assertExpectedString(String methodName, String expected, String actual) { + private static void verifyListChildren(List list) { + AccessibleContext context = list.getAccessibleContext(); + + int childCount = context.getAccessibleChildrenCount(); + if (childCount != list.getItemCount()) { + throw new RuntimeException( + "getAccessibleChildrenCount returned an incorrect value " + + "for List"); + } + + for (int i = 0; i < childCount; i++) { + Accessible child = context.getAccessibleChild(i); + if (child == null) { + throw new RuntimeException( + "getAccessibleChild returned null for child " + i); + } + + AccessibleContext childContext = child.getAccessibleContext(); + if (childContext == null) { + throw new RuntimeException( + "getAccessibleContext returned null for List child " + + i); + } + + if (childContext.getAccessibleRole() != AccessibleRole.LIST_ITEM) { + throw new RuntimeException( + "The AccessibleRole of AccessibleAWTListChild is " + + "incorrect for child " + i); + } + + if (childContext.getAccessibleIndexInParent() != i) { + throw new RuntimeException( + "getAccessibleIndexInParent returned an incorrect " + + "value for AccessibleAWTListChild " + i); + } + + AccessibleStateSet childStateSet = + childContext.getAccessibleStateSet(); + if (childStateSet == null) { + throw new RuntimeException( + "getAccessibleStateSet returned null for List child " + + i); + } + + boolean accessibleSelected = + childStateSet.contains(AccessibleState.SELECTED); + boolean listSelected = list.isIndexSelected(i); + + if (accessibleSelected != listSelected) { + throw new RuntimeException( + "getAccessibleStateSet reports that list item " + i + + (accessibleSelected ? " is " : " is not ") + + "selected but List reports that it " + + (listSelected ? "is" : "is not") + " selected"); + } + } + } + + private static void verifyContainerChildren(Component component, + String componentName) { + AccessibleContext context = component.getAccessibleContext(); + + int childCount = context.getAccessibleChildrenCount(); + if (childCount != component.getAccessibleContext() + .getAccessibleChildrenCount()) { + throw new RuntimeException( + "getAccessibleChildrenCount returned an incorrect value " + + "for " + componentName); + } + + for (int i = 0; i < childCount; i++) { + Accessible child = context.getAccessibleChild(i); + if (child == null) { + throw new RuntimeException( + "getAccessibleChild returned null for " + + componentName + " child " + i); + } + + AccessibleContext childContext = child.getAccessibleContext(); + if (childContext == null) { + throw new RuntimeException( + "getAccessibleContext returned null for " + + componentName + " child " + i); + } + } + } + + private static void assertExpectedString(String methodName, + String expected, + String actual) { if (expected == null) { - throw new RuntimeException("Excepted value is null. Provide " + - "excepted value"); + throw new RuntimeException( + "Expected value for " + methodName + " should not be null"); } if (actual == null) { - throw new RuntimeException(methodName + " returned null; expected" + - " [" + expected + "]"); + throw new RuntimeException( + methodName + " returned null; expected [" + expected + "]"); } + if (!expected.equals(actual)) { - throw new RuntimeException(methodName + " returned [" + actual + - "]; expected [" + expected + "]"); + throw new RuntimeException( + methodName + " returned [" + actual + + "]; expected [" + expected + "]"); } } - private static void assertPresence(String methodName, boolean expectedPresent, Object value) { + private static void assertPresence(String methodName, + boolean expectedPresent, + Object value) { if (expectedPresent && value == null) { - throw new RuntimeException(methodName + " returned null but was expected"); + throw new RuntimeException( + methodName + " returned null but should not"); } + if (!expectedPresent && value != null) { - throw new RuntimeException(methodName + " returned non-null but " + - "was expected to be null"); + throw new RuntimeException( + methodName + " returned non-null but should return null"); } } - private static void assertLocaleMatches(Component component, AccessibleContext context) { + private static void assertLocaleMatches(Component component, + AccessibleContext context) { Locale componentLocale = component.getLocale(); Locale accessibleLocale = context.getLocale(); if (componentLocale == null) { throw new RuntimeException("Component.getLocale returned null"); } + if (accessibleLocale == null) { - throw new RuntimeException("AccessibleContext.getLocale returned null"); + throw new RuntimeException( + "AccessibleContext.getLocale returned null"); } + if (!componentLocale.equals(accessibleLocale)) { - throw new RuntimeException(String.format( - "AccessibleContext.getLocale returned [%s], but Component" + - ".getLocale returned [%s]", - accessibleLocale, componentLocale - )); + throw new RuntimeException( + "AccessibleContext.getLocale returned [" + + accessibleLocale + "], but Component.getLocale returned [" + + componentLocale + "]"); } } - private static void assertIntValueEquals(String methodName, int expected, Number actual) { + private static void assertIntValueEquals(String methodName, + int expected, + Number actual) { if (actual == null) { - throw new RuntimeException(methodName + " returned null; expected [" + expected + "]"); + throw new RuntimeException( + methodName + " returned null; expected [" + expected + "]"); } + if (actual.intValue() != expected) { throw new RuntimeException( - methodName + " returned [" + actual + "]; expected [" + expected + "]" - ); + methodName + " returned [" + actual + + "]; expected [" + expected + "]"); } } } From 5f7422ed83a0f7722e8a9b01ed799a8787c2636d Mon Sep 17 00:00:00 2001 From: Ashay Rane <253344819+raneashay@users.noreply.github.com> Date: Sat, 6 Jun 2026 01:37:24 +0000 Subject: [PATCH 02/17] 8383248: Reduce buffer allocations for HTTP headers instead of allocating 16KB per request Reviewed-by: jpai, dfuchs --- .../internal/net/http/Http2ClientImpl.java | 7 +- .../internal/net/http/Http2Connection.java | 51 +++++-- .../net/http/HttpClientImplAccess.java | 30 +++- .../http2/HeaderEncodingBufferReuseTest.java | 139 ++++++++++++++++++ 4 files changed, 210 insertions(+), 17 deletions(-) create mode 100644 test/jdk/java/net/httpclient/http2/HeaderEncodingBufferReuseTest.java diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/Http2ClientImpl.java b/src/java.net.http/share/classes/jdk/internal/net/http/Http2ClientImpl.java index 6121a0c1673..c4573317785 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/Http2ClientImpl.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/Http2ClientImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -351,4 +351,9 @@ class Http2ClientImpl { public boolean stopping() { return stopping; } + + // getConnections() is used only by tests. + Map getConnections() { + return connections; + } } diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/Http2Connection.java b/src/java.net.http/share/classes/jdk/internal/net/http/Http2Connection.java index 94b8505da47..11022ca5c96 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/Http2Connection.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/Http2Connection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2026, 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 @@ -52,7 +52,6 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; -import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Function; import java.util.function.Supplier; @@ -1610,16 +1609,37 @@ class Http2Connection implements Closeable { return frames; } - // Dedicated cache for headers encoding ByteBuffer. + // Dedicated reusable ByteBuffer for headers encoding. // There can be no concurrent access to this buffer as all access to this buffer // and its content happen within a single critical code block section protected - // by the sendLock. / (see sendFrame()) - // private final ByteBufferPool headerEncodingPool = new ByteBufferPool(); + // by the sendlock (see sendFrame()). + private ByteBuffer cachedHeaderBuffer; + + // getCachedHeaderBuffer() is used only by tests and it should not be + // called in source code without also holding `sendlock`. + ByteBuffer getCachedHeaderBuffer() { + return cachedHeaderBuffer; + } private ByteBuffer getHeaderBuffer(int size) { - ByteBuffer buf = ByteBuffer.allocate(size); - buf.limit(size); - return buf; + assert sendlock.isHeldByCurrentThread() : "current thread is not holding sendlock"; + + if (cachedHeaderBuffer == null || cachedHeaderBuffer.capacity() < size) { + cachedHeaderBuffer = ByteBuffer.allocate(size); + return cachedHeaderBuffer; + } + + cachedHeaderBuffer.clear(); + cachedHeaderBuffer.limit(size); + return cachedHeaderBuffer; + } + + private static ByteBuffer copyBuffer(ByteBuffer buffer) { + buffer.flip(); + ByteBuffer copy = ByteBuffer.allocate(buffer.remaining()); + copy.put(buffer); + copy.flip(); + return copy; } /* @@ -1634,8 +1654,8 @@ class Http2Connection implements Closeable { * encoding in HTTP/2... */ private List encodeHeadersImpl(int bufferSize, HttpHeaders... headers) { - ByteBuffer buffer = getHeaderBuffer(bufferSize); List buffers = new ArrayList<>(); + ByteBuffer buffer = getHeaderBuffer(bufferSize); for (HttpHeaders header : headers) { for (Map.Entry> e : header.map().entrySet()) { String lKey = e.getKey().toLowerCase(Locale.US); @@ -1644,16 +1664,17 @@ class Http2Connection implements Closeable { hpackOut.header(lKey, value); while (!hpackOut.encode(buffer)) { if (!buffer.hasRemaining()) { - buffer.flip(); - buffers.add(buffer); - buffer = getHeaderBuffer(bufferSize); + ByteBuffer copy = copyBuffer(buffer); + buffers.add(copy); + buffer.clear(); + buffer.limit(bufferSize); } } } } } - buffer.flip(); - buffers.add(buffer); + ByteBuffer copy = copyBuffer(buffer); + buffers.add(copy); return buffers; } @@ -1710,7 +1731,7 @@ class Http2Connection implements Closeable { } } - private final Lock sendlock = new ReentrantLock(); + private final ReentrantLock sendlock = new ReentrantLock(); void sendFrame(Http2Frame frame) { try { diff --git a/test/jdk/java/net/httpclient/access/java.net.http/jdk/internal/net/http/HttpClientImplAccess.java b/test/jdk/java/net/httpclient/access/java.net.http/jdk/internal/net/http/HttpClientImplAccess.java index 875ac5426dd..ce57f654e93 100644 --- a/test/jdk/java/net/httpclient/access/java.net.http/jdk/internal/net/http/HttpClientImplAccess.java +++ b/test/jdk/java/net/httpclient/access/java.net.http/jdk/internal/net/http/HttpClientImplAccess.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, 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 @@ -26,6 +26,9 @@ import java.lang.reflect.Field; import java.net.http.HttpClient; import java.util.Objects; import java.util.Set; +import java.nio.ByteBuffer; +import java.util.Collection; +import java.util.Map; public final class HttpClientImplAccess { @@ -64,4 +67,29 @@ public final class HttpClientImplAccess { openedConnections.setAccessible(true); return (Set) openedConnections.get(clientImpl); } + + /** + * Returns all connections in the client's HTTP/2 pool. + */ + public static Collection getHttp2Connections(final HttpClient client) { + Objects.requireNonNull(client, "client"); + final HttpClientImpl clientImpl = impl(client); + if (clientImpl == null) { + throw new IllegalStateException("Unsupported HttpClient implementation"); + } + Http2ClientImpl client2 = clientImpl.client2(); + Map connections = client2.getConnections(); + return connections.values(); + } + + /** + * Returns the cached header encoding buffer for the given Http2Connection. + */ + public static ByteBuffer getCachedHeaderBuffer(final Object conn) { + // The argument to this method is of type Object and not + // Http2Connection because callers outside the module cannot reference + // the package-private Http2Connection class. + Objects.requireNonNull(conn, "conn"); + return ((Http2Connection) conn).getCachedHeaderBuffer(); + } } diff --git a/test/jdk/java/net/httpclient/http2/HeaderEncodingBufferReuseTest.java b/test/jdk/java/net/httpclient/http2/HeaderEncodingBufferReuseTest.java new file mode 100644 index 00000000000..26fb06ec47d --- /dev/null +++ b/test/jdk/java/net/httpclient/http2/HeaderEncodingBufferReuseTest.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2026, 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. + * + * 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. + */ + +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpRequest.BodyPublishers; +import java.net.http.HttpResponse; +import java.net.http.HttpResponse.BodyHandlers; +import java.nio.ByteBuffer; +import java.util.Collection; +import javax.net.ssl.SSLContext; +import static java.net.http.HttpClient.Version.HTTP_2; + +import jdk.httpclient.test.lib.common.HttpServerAdapters; +import jdk.httpclient.test.lib.common.HttpServerAdapters.HttpTestServer; +import jdk.httpclient.test.lib.common.HttpServerAdapters.HttpTestExchange; +import jdk.httpclient.test.lib.common.HttpServerAdapters.HttpTestHandler; +import jdk.internal.net.http.HttpClientImplAccess; +import jdk.test.lib.net.URIBuilder; +import jdk.test.lib.net.SimpleSSLContext; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +/* + * @test + * @bug 8383248 + * @summary Verifies that Http2Connection.cachedHeaderBuffer is reused + * across multiple requests on the same connection. + * @library /test/lib /test/jdk/java/net/httpclient/lib + * /test/jdk/java/net/httpclient/access + * @build java.net.http/jdk.internal.net.http.HttpClientImplAccess + * jdk.httpclient.test.lib.common.HttpServerAdapters + * jdk.test.lib.net.SimpleSSLContext + * @run junit/othervm + * ${test.main.class} + */ + +public class HeaderEncodingBufferReuseTest implements HttpServerAdapters { + + static String httpUri; + static SSLContext sslContext; + static HttpTestServer testServer; + + @BeforeAll + static void init() throws Exception { + sslContext = SimpleSSLContext.findSSLContext(); + testServer = HttpTestServer.create(HTTP_2, sslContext); + testServer.addHandler(new OkHandler(), "/test"); + testServer.start(); + httpUri = URIBuilder.newBuilder() + .scheme("https") + .loopback() + .port(testServer.getAddress().getPort()) + .path("/test") + .build() + .toString(); + } + + @AfterAll + static void teardown() { + testServer.stop(); + } + + @Test + void test() throws Exception { + try (HttpClient client = HttpClient.newBuilder() + .proxy(HttpClient.Builder.NO_PROXY) + .version(HttpClient.Version.HTTP_2) + .sslContext(sslContext) + .build()) { + + // Send an initial request to allocate the header encoding buffer. + assertEquals(200, send(client, httpUri, 2).statusCode()); + + Collection connections = HttpClientImplAccess.getHttp2Connections(client); + assertEquals(1, connections.size()); + Object conn = connections.iterator().next(); + + ByteBuffer cached = HttpClientImplAccess.getCachedHeaderBuffer(conn); + assertNotNull(cached); + + // Send another request and verify the same buffer is reused. + assertEquals(200, send(client, httpUri, 2).statusCode()); + connections = HttpClientImplAccess.getHttp2Connections(client); + assertEquals(1, connections.size()); + assertSame(conn, connections.iterator().next()); + assertSame(cached, HttpClientImplAccess.getCachedHeaderBuffer(conn)); + + // Verify that the buffer is reused when headers span multiple frames. + assertEquals(200, send(client, httpUri, 300).statusCode()); + connections = HttpClientImplAccess.getHttp2Connections(client); + assertEquals(1, connections.size()); + assertSame(conn, connections.iterator().next()); + assertSame(cached, HttpClientImplAccess.getCachedHeaderBuffer(conn)); + } + } + + static HttpResponse send(HttpClient client, String uri, int headerCount) throws Exception { + HttpRequest.Builder builder = HttpRequest.newBuilder(URI.create(uri)) + .POST(BodyPublishers.ofString("test")); + for (int i = 0; i < headerCount; i++) { + builder.header("X-Header-" + i, "value-" + "x".repeat(50) + "-" + i); + } + return client.send(builder.build(), BodyHandlers.discarding()); + } + + static class OkHandler implements HttpTestHandler { + @Override + public void handle(HttpTestExchange exchange) throws IOException { + exchange.sendResponseHeaders(200, 0); + exchange.getResponseBody().close(); + } + } +} From a0c8f7cb3640bf4f44e5e366641d30c477eef81e Mon Sep 17 00:00:00 2001 From: Yasumasa Suenaga Date: Sat, 6 Jun 2026 02:28:15 +0000 Subject: [PATCH 03/17] 8385988: Linux devkits does not work with dnf5 Reviewed-by: erikj --- make/devkit/Sysroot.gmk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/make/devkit/Sysroot.gmk b/make/devkit/Sysroot.gmk index fd3d8c1b891..13395172074 100644 --- a/make/devkit/Sysroot.gmk +++ b/make/devkit/Sysroot.gmk @@ -109,7 +109,7 @@ endif EMPTY := SPACE := $(EMPTY) $(EMPTY) COMMA := , -DNF_ARCHS := $(subst $(SPACE),$(COMMA),$(RPM_ARCHS)) +DNF_ARCHS := $(foreach arch,$(RPM_ARCHS),--arch $(arch)) # Specify a dummy installation root, otherwise dnf will run into # problems trying to reconcile with the local/system state @@ -123,7 +123,7 @@ DNF_DOWNLOAD_FLAGS := \ --disablerepo='*' \ $(DNF_REPOS) \ --resolve \ - --archlist $(DNF_ARCHS) \ + $(DNF_ARCHS) \ --forcearch $(RPM_ARCH) \ --installroot $(DNF_DUMMY_INSTALL_ROOT) \ --releasever $(BASE_OS_VERSION) \ From a1ff7b16d40f543add5f815da8a80bdd1dc86224 Mon Sep 17 00:00:00 2001 From: Robert Toyonaga Date: Sat, 6 Jun 2026 05:31:13 +0000 Subject: [PATCH 04/17] 8385586: Fix race in Windows map_or_reserve_memory_aligned using VirtualAlloc2 and MapViewOfFile3 Reviewed-by: stuefe, asmehra, jsjolen --- src/hotspot/os/windows/os_windows.cpp | 193 +++++++++++++++++++++---- src/hotspot/os/windows/os_windows.hpp | 13 ++ test/hotspot/gtest/runtime/test_os.cpp | 51 ++++++- 3 files changed, 219 insertions(+), 38 deletions(-) diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index 9137723fa58..d00babef40f 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -243,6 +243,16 @@ static LPVOID virtualAllocExNuma(HANDLE hProcess, LPVOID lpAddress, SIZE_T dwSiz return result; } +void* os::win32::lookup_kernelbase_symbol(const char* name) { + // Pass a small ebuf so dll_load logs failures, but don't use it here to avoid redundancy. + char ebuf[1024]; + static void* const handle = os::dll_load("KernelBase", ebuf, sizeof(ebuf)); + if (handle == nullptr) { + return nullptr; + } + return os::dll_lookup(handle, name); +} + // Logging wrapper for MapViewOfFileEx static LPVOID mapViewOfFileEx(HANDLE hFileMappingObject, DWORD dwDesiredAccess, DWORD dwFileOffsetHigh, DWORD dwFileOffsetLow, SIZE_T dwNumberOfBytesToMap, LPVOID lpBaseAddress) { @@ -3250,9 +3260,9 @@ char* os::map_memory_to_file(char* base, size_t size, int fd) { assert(fd != -1, "File descriptor is not valid"); HANDLE fh = (HANDLE)_get_osfhandle(fd); - HANDLE fileMapping = CreateFileMapping(fh, nullptr, PAGE_READWRITE, + HANDLE file_mapping = CreateFileMapping(fh, nullptr, PAGE_READWRITE, (DWORD)(size >> 32), (DWORD)(size & 0xFFFFFFFF), nullptr); - if (fileMapping == nullptr) { + if (file_mapping == nullptr) { if (GetLastError() == ERROR_DISK_FULL) { vm_exit_during_initialization(err_msg("Could not allocate sufficient disk space for Java heap")); } @@ -3263,9 +3273,9 @@ char* os::map_memory_to_file(char* base, size_t size, int fd) { return nullptr; } - LPVOID addr = mapViewOfFileEx(fileMapping, FILE_MAP_WRITE, 0, 0, size, base); + LPVOID addr = mapViewOfFileEx(file_mapping, FILE_MAP_WRITE, 0, 0, size, base); - CloseHandle(fileMapping); + CloseHandle(file_mapping); return (char*)addr; } @@ -3278,40 +3288,75 @@ char* os::replace_existing_mapping_with_file_mapping(char* base, size_t size, in return map_memory_to_file(base, size, fd); } +// VirtualAlloc2 / MapViewOfFile3 (Windows 1803+). Resolved in os::init_2() via lookup_kernelbase_symbol. +os::win32::VirtualAlloc2Fn os::win32::VirtualAlloc2 = nullptr; + +os::win32::MapViewOfFile3Fn os::win32::MapViewOfFile3 = nullptr; + +static bool is_VirtualAlloc2_supported() { + return os::win32::VirtualAlloc2 != nullptr; +} + +static bool is_MapViewOfFile3_supported() { + return os::win32::MapViewOfFile3 != nullptr; +} + // Multiple threads can race in this code but it's not possible to unmap small sections of // virtual space to get requested alignment, like posix-like os's. // Windows prevents multiple thread from remapping over each other so this loop is thread-safe. -static char* map_or_reserve_memory_aligned(size_t size, size_t alignment, int file_desc, MemTag mem_tag) { +static char* reserve_memory_aligned(size_t size, size_t alignment, MemTag mem_tag) { assert(is_aligned(alignment, os::vm_allocation_granularity()), - "Alignment must be a multiple of allocation granularity (page size)"); + "Alignment must be a multiple of allocation granularity"); assert(is_aligned(size, os::vm_allocation_granularity()), - "Size must be a multiple of allocation granularity (page size)"); + "Size must be a multiple of allocation granularity"); size_t extra_size = size + alignment; assert(extra_size >= size, "overflow, size is too large to allow alignment"); char* aligned_base = nullptr; - static const int max_attempts = 20; + constexpr int max_attempts = 20; for (int attempt = 0; attempt < max_attempts && aligned_base == nullptr; attempt ++) { - char* extra_base = file_desc != -1 ? os::map_memory_to_file(extra_size, file_desc, mem_tag) : - os::reserve_memory(extra_size, mem_tag); + char* extra_base = os::reserve_memory(extra_size, mem_tag); if (extra_base == nullptr) { return nullptr; } - // Do manual alignment aligned_base = align_up(extra_base, alignment); + os::release_memory(extra_base, extra_size); - if (file_desc != -1) { - os::unmap_memory(extra_base, extra_size); - } else { - os::release_memory(extra_base, extra_size); + // A racing thread may have taken this region instead of us, which is why we loop and retry. + aligned_base = os::attempt_reserve_memory_at(aligned_base, size, mem_tag); + } + + assert(aligned_base != nullptr, + "Did not manage to reserve after %d attempts (size %zu, alignment %zu)", max_attempts, size, alignment); + + return aligned_base; +} + +// Similar to reserve_memory_aligned, other reservation/mapping requests can race with this function. +static char* map_memory_aligned(size_t size, size_t alignment, int file_desc, MemTag mem_tag) { + assert(is_aligned(alignment, os::vm_allocation_granularity()), + "Alignment must be a multiple of allocation granularity"); + assert(is_aligned(size, os::vm_allocation_granularity()), + "Size must be a multiple of allocation granularity"); + + size_t extra_size = size + alignment; + assert(extra_size >= size, "overflow, size is too large to allow alignment"); + + char* aligned_base = nullptr; + constexpr int max_attempts = 20; + + for (int attempt = 0; attempt < max_attempts && aligned_base == nullptr; attempt ++) { + char* extra_base = os::map_memory_to_file(extra_size, file_desc, mem_tag); + if (extra_base == nullptr) { + return nullptr; } + aligned_base = align_up(extra_base, alignment); + os::unmap_memory(extra_base, extra_size); - // Attempt to map, into the just vacated space, the slightly smaller aligned area. - // Which may fail, hence the loop. - aligned_base = file_desc != -1 ? os::attempt_map_memory_to_file_at(aligned_base, size, file_desc, mem_tag) : - os::attempt_reserve_memory_at(aligned_base, size, mem_tag); + // A racing thread may have taken this region instead of us, which is why we loop and retry. + aligned_base = os::attempt_map_memory_to_file_at(aligned_base, size, file_desc, mem_tag); } assert(aligned_base != nullptr, @@ -3320,6 +3365,84 @@ static char* map_or_reserve_memory_aligned(size_t size, size_t alignment, int fi return aligned_base; } +// MapViewOfFile3 supports alignment natively. +static char* map_memory_aligned_va2(size_t size, size_t alignment, int file_desc, MemTag mem_tag) { + assert(file_desc != -1, "File descriptor should not be -1"); + assert(is_aligned(alignment, os::vm_allocation_granularity()), + "Alignment must be a multiple of allocation granularity"); + assert(is_aligned(size, os::vm_allocation_granularity()), + "Size must be a multiple of allocation granularity"); + + MEM_ADDRESS_REQUIREMENTS requirements = {0}; + requirements.Alignment = alignment; + + MEM_EXTENDED_PARAMETER param = {0}; + param.Type = MemExtendedParameterAddressRequirements; + param.Pointer = &requirements; + + char* aligned_base = nullptr; + + // File-backed aligned mapping. + HANDLE fh = (HANDLE)_get_osfhandle(file_desc); + HANDLE file_mapping = CreateFileMapping(fh, nullptr, PAGE_READWRITE,(DWORD)(size >> 32), (DWORD)(size & 0xFFFFFFFF), nullptr); + DWORD err = GetLastError(); + if (file_mapping != nullptr) { + aligned_base = (char*)os::win32::MapViewOfFile3( + file_mapping, + GetCurrentProcess(), + nullptr, // let the system choose an aligned address + 0, // offset + size, + 0, // no special allocation type flags + PAGE_READWRITE, + ¶m, 1); + err = GetLastError(); + CloseHandle(file_mapping); + } + + if (aligned_base != nullptr) { + assert(is_aligned(aligned_base, alignment), "Result must be aligned"); + MemTracker::record_virtual_memory_reserve_and_commit(aligned_base, size, CALLER_PC, mem_tag); + return aligned_base; + } + log_trace(os)("Aligned allocation via MapViewOfFile3 failed, falling back to retry loop. GetLastError->%lu.", err); + return map_memory_aligned(size, alignment, file_desc, mem_tag); +} + +// VirtualAlloc2 supports alignment natively. +static char* reserve_memory_aligned_va2(size_t size, size_t alignment, MemTag mem_tag) { + assert(is_aligned(alignment, os::vm_allocation_granularity()), + "Alignment must be a multiple of allocation granularity"); + assert(is_aligned(size, os::vm_allocation_granularity()), + "Size must be a multiple of allocation granularity"); + + MEM_ADDRESS_REQUIREMENTS requirements = {0}; + requirements.Alignment = alignment; + + MEM_EXTENDED_PARAMETER param = {0}; + param.Type = MemExtendedParameterAddressRequirements; + param.Pointer = &requirements; + + char* aligned_base = nullptr; + + // Anonymous aligned reservation. + aligned_base = (char*)os::win32::VirtualAlloc2( + GetCurrentProcess(), + nullptr, // let the system choose an aligned address + size, + MEM_RESERVE, + PAGE_READWRITE, + ¶m, 1); + + if (aligned_base != nullptr) { + assert(is_aligned(aligned_base, alignment), "Result must be aligned"); + MemTracker::record_virtual_memory_reserve(aligned_base, size, CALLER_PC, mem_tag); + return aligned_base; + } + log_trace(os)("Aligned allocation via VirtualAlloc2 failed, falling back to retry loop. GetLastError->%lu.", GetLastError()); + return reserve_memory_aligned(size, alignment, mem_tag); +} + size_t os::commit_memory_limit() { BOOL is_in_job_object = false; BOOL res = IsProcessInJob(GetCurrentProcess(), nullptr, &is_in_job_object); @@ -3367,11 +3490,17 @@ size_t os::reserve_memory_limit() { char* os::reserve_memory_aligned(size_t size, size_t alignment, MemTag mem_tag, bool exec) { // exec can be ignored - return map_or_reserve_memory_aligned(size, alignment, -1/* file_desc */, mem_tag); + if (is_VirtualAlloc2_supported()) { + return reserve_memory_aligned_va2(size, alignment, mem_tag); + } + return reserve_memory_aligned(size, alignment, mem_tag); } char* os::map_memory_to_file_aligned(size_t size, size_t alignment, int fd, MemTag mem_tag) { - return map_or_reserve_memory_aligned(size, alignment, fd, mem_tag); + if (is_MapViewOfFile3_supported()) { + return map_memory_aligned_va2(size, alignment, fd, mem_tag); + } + return map_memory_aligned(size, alignment, fd, mem_tag); } char* os::pd_reserve_memory(size_t bytes, bool exec) { @@ -4588,21 +4717,21 @@ jint os::init_2(void) { // Lookup SetThreadDescription - the docs state we must use runtime-linking of // kernelbase.dll, so that is what we do. - HINSTANCE _kernelbase = LoadLibrary(TEXT("kernelbase.dll")); - if (_kernelbase != nullptr) { - _SetThreadDescription = - reinterpret_cast( - GetProcAddress(_kernelbase, - "SetThreadDescription")); + _SetThreadDescription = reinterpret_cast( + os::win32::lookup_kernelbase_symbol("SetThreadDescription")); #ifdef ASSERT - _GetThreadDescription = - reinterpret_cast( - GetProcAddress(_kernelbase, - "GetThreadDescription")); + _GetThreadDescription = reinterpret_cast( + os::win32::lookup_kernelbase_symbol("GetThreadDescription")); #endif - } log_info(os, thread)("The SetThreadDescription API is%s available.", _SetThreadDescription == nullptr ? " not" : ""); + // Prepare KernelBase APIs (VirtualAlloc2, MapViewOfFile3) if available (Windows version 1803). + os::win32::VirtualAlloc2 = reinterpret_cast( + os::win32::lookup_kernelbase_symbol("VirtualAlloc2")); + os::win32::MapViewOfFile3 = reinterpret_cast( + os::win32::lookup_kernelbase_symbol("MapViewOfFile3")); + log_debug(os)("VirtualAlloc2 is%s available.", os::win32::VirtualAlloc2 == nullptr ? " not" : ""); + log_debug(os)("MapViewOfFile3 is%s available.", os::win32::MapViewOfFile3 == nullptr ? " not" : ""); return JNI_OK; } diff --git a/src/hotspot/os/windows/os_windows.hpp b/src/hotspot/os/windows/os_windows.hpp index d4a7d51c59b..5ebc80c817b 100644 --- a/src/hotspot/os/windows/os_windows.hpp +++ b/src/hotspot/os/windows/os_windows.hpp @@ -109,6 +109,19 @@ class os::win32 { // load dll from Windows system directory or Windows directory static HINSTANCE load_Windows_dll(const char* name, char *ebuf, int ebuflen); + // Resolve a symbol from KernelBase.dll, returns nullptr if not found. + static void* lookup_kernelbase_symbol(const char* name); + + // VirtualAlloc2 (since Windows version 1803) + // Resolved from KernelBase during os::init_2() or nullptr if unavailable. + typedef PVOID (WINAPI *VirtualAlloc2Fn)(HANDLE, PVOID, SIZE_T, ULONG, ULONG, MEM_EXTENDED_PARAMETER*, ULONG); + static VirtualAlloc2Fn VirtualAlloc2; + + // MapViewOfFile3 (since Windows version 1803) + // Resolved from KernelBase during os::init_2() or nullptr if unavailable. + typedef PVOID (WINAPI *MapViewOfFile3Fn)(HANDLE, HANDLE, PVOID, ULONG64, SIZE_T, ULONG, ULONG, MEM_EXTENDED_PARAMETER*, ULONG); + static MapViewOfFile3Fn MapViewOfFile3; + private: static void initialize_performance_counter(); diff --git a/test/hotspot/gtest/runtime/test_os.cpp b/test/hotspot/gtest/runtime/test_os.cpp index 26c8fb8cd72..cc059a93199 100644 --- a/test/hotspot/gtest/runtime/test_os.cpp +++ b/test/hotspot/gtest/runtime/test_os.cpp @@ -1200,16 +1200,22 @@ TEST_VM(os, map_unmap_memory) { TEST_VM(os, map_memory_to_file_aligned) { const char* letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - const size_t size = strlen(letters) + 1; + const size_t content_size = strlen(letters) + 1; + const size_t granularity = os::vm_allocation_granularity(); + const size_t alignments[] = { granularity, 2 * granularity, 4 * granularity, 16 * granularity, 1 * M }; int fd = os::open("map_memory_to_file.txt", O_RDWR | O_CREAT, 0666); EXPECT_TRUE(fd > 0); - EXPECT_TRUE(os::write(fd, letters, size)); + ASSERT_TRUE(os::write(fd, letters, content_size)); - char* result = os::map_memory_to_file_aligned(os::vm_allocation_granularity(), os::vm_allocation_granularity(), fd, mtTest); - ASSERT_NOT_NULL(result); - EXPECT_EQ(strcmp(letters, result), 0); - os::unmap_memory(result, os::vm_allocation_granularity()); + const size_t size = granularity; + for (size_t alignment : alignments) { + char* result = os::map_memory_to_file_aligned(size, alignment, fd, mtTest); + ASSERT_NOT_NULL(result) << "Mapping failed for alignment=" << alignment; + EXPECT_TRUE(is_aligned(result, alignment)) << "Failed to aligned to " << alignment; + EXPECT_EQ(strcmp(letters, result), 0) << "Text mismatch at alignment=" << alignment; + os::unmap_memory(result, size); + } ::close(fd); } @@ -1220,3 +1226,36 @@ TEST_VM(os, dll_load_null_error_buf) { void* lib = os::dll_load("NoSuchLib", nullptr, 0); ASSERT_NULL(lib); } + +TEST_VM(os, reserve_memory_aligned_basic) { + const size_t granularity = os::vm_allocation_granularity(); + const size_t alignments[] = { granularity, 2 * granularity, 4 * granularity, 16 * granularity }; + + for (size_t alignment : alignments) { + const size_t size = alignment; + char* result = os::reserve_memory_aligned(size, alignment, mtTest); + ASSERT_NE(result, (char*)nullptr) << "reserve_memory_aligned failed for alignment=" << alignment; + EXPECT_TRUE(is_aligned(result, alignment)) << "Result " << result << " not aligned to " << alignment; + + ASSERT_TRUE(os::commit_memory(result, size, false)); + memset(result, 0xCD, size); + EXPECT_EQ((unsigned char)result[0], 0xCD); + + os::release_memory(result, size); + } +} + +TEST_VM(os, reserve_memory_aligned_large) { + const size_t alignment = 1 * M; + const size_t size = alignment; + + char* result = os::reserve_memory_aligned(size, alignment, mtTest); + ASSERT_NE(result, (char*)nullptr); + EXPECT_TRUE(is_aligned(result, alignment)); + + ASSERT_TRUE(os::commit_memory(result, size, false)); + memset(result, 0xEF, size); + EXPECT_EQ((unsigned char)result[size - 1], 0xEF); + + os::release_memory(result, size); +} From 2e364c3a0675e5ec29cac5ccc7ebc38d9d5699c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20Sj=C3=B6len?= Date: Mon, 8 Jun 2026 07:39:58 +0000 Subject: [PATCH 05/17] 8385991: Use StringTable's statistics method in Dictionary Reviewed-by: jsikstro, coleenp --- src/hotspot/share/classfile/dictionary.cpp | 23 ++++++++++++++++--- .../share/services/diagnosticCommand.hpp | 17 ++++++++------ .../share/utilities/concurrentHashTable.hpp | 5 ---- .../utilities/concurrentHashTable.inline.hpp | 16 ------------- 4 files changed, 30 insertions(+), 31 deletions(-) diff --git a/src/hotspot/share/classfile/dictionary.cpp b/src/hotspot/share/classfile/dictionary.cpp index 0f79e7a5a69..a0cfe2a9893 100644 --- a/src/hotspot/share/classfile/dictionary.cpp +++ b/src/hotspot/share/classfile/dictionary.cpp @@ -31,7 +31,10 @@ #include "memory/metaspaceClosure.hpp" #include "memory/resourceArea.hpp" #include "oops/instanceKlass.hpp" +#include "runtime/interfaceSupport.inline.hpp" +#include "runtime/timerTrace.hpp" #include "utilities/concurrentHashTable.inline.hpp" +#include "utilities/concurrentHashTableTasks.inline.hpp" #include "utilities/ostream.hpp" #include "utilities/tableStatistics.hpp" @@ -239,10 +242,24 @@ void Dictionary::verify() { } void Dictionary::print_table_statistics(outputStream* st, const char* table_name) { - static TableStatistics ts; + TableStatistics stats; auto sz = [&] (InstanceKlass** val) { return sizeof(**val); }; - ts = _table->statistics_get(Thread::current(), sz, ts); - ts.print(st, table_name); + Thread* thread = Thread::current(); + ConcurrentTable::StatisticsTask sts(_table); + if (!sts.prepare(thread)) { + st->print_cr("Failed to take statistics"); + return; + } + TraceTime timer("GetStatistics", TRACETIME_LOG(Debug, perf)); + while (sts.do_task(thread, sz)) { + sts.pause(thread); + if (thread->is_Java_thread()) { + ThreadBlockInVM tbivm(JavaThread::cast(thread)); + } + sts.cont(thread); + } + stats = sts.done(thread); + stats.print(st, table_name); } diff --git a/src/hotspot/share/services/diagnosticCommand.hpp b/src/hotspot/share/services/diagnosticCommand.hpp index 97ceb19d0ad..b720871c389 100644 --- a/src/hotspot/share/services/diagnosticCommand.hpp +++ b/src/hotspot/share/services/diagnosticCommand.hpp @@ -654,17 +654,20 @@ public: // VM.systemdictionary -verbose: for dumping the system dictionary table // class VM_DumpHashtable : public VM_Operation { +public: + enum DumpKind { + DumpSymbols, + DumpStrings, + DumpSysDict + }; + private: outputStream* _out; - int _which; + DumpKind _which; bool _verbose; + public: - enum { - DumpSymbols = 1 << 0, - DumpStrings = 1 << 1, - DumpSysDict = 1 << 2 // not implemented yet - }; - VM_DumpHashtable(outputStream* out, int which, bool verbose) { + VM_DumpHashtable(outputStream* out, DumpKind which, bool verbose) { _out = out; _which = which; _verbose = verbose; diff --git a/src/hotspot/share/utilities/concurrentHashTable.hpp b/src/hotspot/share/utilities/concurrentHashTable.hpp index d2317847307..dfba84ca519 100644 --- a/src/hotspot/share/utilities/concurrentHashTable.hpp +++ b/src/hotspot/share/utilities/concurrentHashTable.hpp @@ -534,11 +534,6 @@ class ConcurrentHashTable : public CHeapObj { template void bulk_delete(Thread* thread, EVALUATE_FUNC& eval_f, DELETE_FUNC& del_f); - // Gets statistics if available, if not return old one. Item sizes are calculated with - // VALUE_SIZE_FUNC. - template - TableStatistics statistics_get(Thread* thread, VALUE_SIZE_FUNC& vs_f, TableStatistics old); - // Moves all nodes from this table to to_cht with new hash code. // Must be done at a safepoint. void rehash_nodes_to(Thread* thread, ConcurrentHashTable* to_cht); diff --git a/src/hotspot/share/utilities/concurrentHashTable.inline.hpp b/src/hotspot/share/utilities/concurrentHashTable.inline.hpp index d5f6dee336b..312d118a647 100644 --- a/src/hotspot/share/utilities/concurrentHashTable.inline.hpp +++ b/src/hotspot/share/utilities/concurrentHashTable.inline.hpp @@ -1266,22 +1266,6 @@ inline TableStatistics ConcurrentHashTable:: } } -template -template -inline TableStatistics ConcurrentHashTable:: - statistics_get(Thread* thread, VALUE_SIZE_FUNC& vs_f, TableStatistics old) -{ - if (!try_resize_lock(thread)) { - return old; - } - InternalTable* table = get_table(); - NumberSeq summary; - size_t literal_bytes = 0; - - internal_statistics_range(thread, 0, table->_size, vs_f, summary, literal_bytes); - return internal_statistics_epilog(thread, summary, literal_bytes); -} - template inline void ConcurrentHashTable:: rehash_nodes_to(Thread* thread, ConcurrentHashTable* to_cht) From dce75d65ff49a72d71861b01cd4bf007673e0c9a Mon Sep 17 00:00:00 2001 From: Kevin Walls Date: Mon, 8 Jun 2026 08:48:33 +0000 Subject: [PATCH 06/17] 8385964: AttachProvider docs update: 'doors' to 'socket' Reviewed-by: alanb, cjplummer --- .../classes/com/sun/tools/attach/spi/AttachProvider.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/jdk.attach/share/classes/com/sun/tools/attach/spi/AttachProvider.java b/src/jdk.attach/share/classes/com/sun/tools/attach/spi/AttachProvider.java index 6446473d9d6..362368cb2c5 100644 --- a/src/jdk.attach/share/classes/com/sun/tools/attach/spi/AttachProvider.java +++ b/src/jdk.attach/share/classes/com/sun/tools/attach/spi/AttachProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2026, 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 @@ -58,8 +58,8 @@ import java.util.ServiceLoader; * for example, ships with attach providers that use the package name "sun" * (for historical reasons). The * type typically corresponds to the attach mechanism. For example, an - * implementation that uses the Doors inter-process communication mechanism - * might use the type "doors". The purpose of the name and type is to + * implementation that uses the UNIX Domain Socket inter-process communication mechanism + * might use the type "socket". The purpose of the name and type is to * identify providers in environments where there are multiple providers * installed. * From 59e40b04162c35755e40e3480a25738cce1b8c08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20Sj=C3=B6len?= Date: Mon, 8 Jun 2026 08:55:56 +0000 Subject: [PATCH 07/17] 8303612: runtime/StackGuardPages/TestStackGuardPagesNative.java fails with exit code 139 Reviewed-by: dholmes, lmesnik --- test/hotspot/jtreg/ProblemList.txt | 1 - .../jtreg/runtime/StackGuardPages/exeinvoke.c | 46 ++++++++++--------- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index 422e6ddd060..9257486eabf 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -103,7 +103,6 @@ runtime/os/TestTracePageSizes.java#compiler-options 8267460 linux-aarch64 runtime/os/TestTracePageSizes.java#G1 8267460 linux-aarch64 runtime/os/TestTracePageSizes.java#Parallel 8267460 linux-aarch64 runtime/os/TestTracePageSizes.java#Serial 8267460 linux-aarch64 -runtime/StackGuardPages/TestStackGuardPagesNative.java 8303612 linux-all runtime/ErrorHandling/MachCodeFramesInErrorFile.java 8313315 linux-ppc64le runtime/NMT/VirtualAllocCommitMerge.java 8309698 linux-s390x runtime/Thread/TestAlwaysPreTouchStacks.java 8383372 macosx-aarch64 diff --git a/test/hotspot/jtreg/runtime/StackGuardPages/exeinvoke.c b/test/hotspot/jtreg/runtime/StackGuardPages/exeinvoke.c index abef2ea050a..4608627688c 100644 --- a/test/hotspot/jtreg/runtime/StackGuardPages/exeinvoke.c +++ b/test/hotspot/jtreg/runtime/StackGuardPages/exeinvoke.c @@ -56,17 +56,13 @@ static jmp_buf context; static volatile int _last_si_code = -1; static volatile int _failures = 0; static volatile int _rec_count = 0; // Number of allocations to hit stack guard page -static volatile int _kp_rec_count = 0; // Kept record of rec_count, for retrying +static volatile int _previous_rec_count = 0; // Kept record of rec_count, for retrying static int _peek_value = 0; // Used for accessing memory to cause SIGSEGV -pid_t gettid() { - return (pid_t) syscall(SYS_gettid); -} - static void handler(int sig, siginfo_t *si, void *unused) { _last_si_code = si->si_code; printf("Got SIGSEGV(%d) at address: 0x%lx\n",si->si_code, (long) si->si_addr); - longjmp(context, 1); + siglongjmp(context, 1); } static char* altstack = NULL; @@ -159,8 +155,16 @@ void *run_java_overflow (void *p) { void do_overflow(){ volatile int *p = NULL; - if (_kp_rec_count == 0 || _rec_count < _kp_rec_count) { - for(;;) { + if (_previous_rec_count == 0) { + // We need to find the appropriate depth to probe into + for (;;) { + _rec_count++; + p = (int*)alloca(128); + _peek_value = p[0]; // Peek + } + } else { + while (_rec_count < _previous_rec_count) { + // This is our second round, we can do exactly 1 less allocation _rec_count++; p = (int*)alloca(128); _peek_value = p[0]; // Peek @@ -168,19 +172,19 @@ void do_overflow(){ } } -void *run_native_overflow(void *p) { +void *run_native_overflow(void *is_other_thread) { // Test that stack guard page is correctly set for initial and non initial thread // and correctly removed for the initial thread volatile int res; - printf("run_native_overflow %ld\n", (long) gettid()); + printf("run_native_overflow, %s", *(int *)is_other_thread ? "in other thread\n" : "in initial thread\n"); call_method_on_jvm("printAlive"); // Initialize statics used in do_overflow - _kp_rec_count = 0; + _previous_rec_count = 0; _rec_count = 0; set_signal_handler(); - if (! setjmp(context)) { + if (! sigsetjmp(context, 1)) { do_overflow(); } @@ -194,7 +198,7 @@ void *run_native_overflow(void *p) { exit(7); } - if (getpid() != gettid()) { + if (*(int *)is_other_thread == 1) { // For non-initial thread we don't unmap the region but call os::uncommit_memory and keep PROT_NONE // so if host has enough swap space we will get the same SEGV with code SEGV_ACCERR(2) trying // to access it as if the guard page is present. @@ -204,11 +208,11 @@ void *run_native_overflow(void *p) { } // Limit depth of recursion for second run. It can't exceed one for first run. - _kp_rec_count = _rec_count; + _previous_rec_count = _rec_count; _rec_count = 0; set_signal_handler(); - if (! setjmp(context)) { + if (!sigsetjmp(context, 1)) { do_overflow(); } @@ -314,8 +318,8 @@ int main (int argc, const char** argv) { if (strcmp(argv[1], "test_native_overflow_initial") == 0) { printf("\nTesting NATIVE_OVERFLOW\n"); printf("Testing stack guard page behaviour for initial thread\n"); - - run_native_overflow(NULL); + int is_other_thread = 0; + run_native_overflow(&is_other_thread); exit((_failures > 0) ? 1 : 0); } @@ -324,11 +328,11 @@ int main (int argc, const char** argv) { init_thread_or_die(&thr, &thread_attr); printf("\nTesting NATIVE_OVERFLOW\n"); printf("Testing stack guard page behaviour for other thread\n"); - - pthread_create(&thr, &thread_attr, run_native_overflow, NULL); + int is_other_thread = 1; + pthread_create(&thr, &thread_attr, run_native_overflow, &is_other_thread); pthread_join(thr, NULL); - - exit((_failures > 0) ? 1 : 0); + // Other-thread test cannot increase _failure count + exit(0); } fprintf(stderr, "Test ERROR. Unknown parameter %s\n", ((argc > 1) ? argv[1] : "none")); From 70ba725b07853871a1553add2b4f117ce2dcec5e Mon Sep 17 00:00:00 2001 From: Amit Kumar Date: Mon, 8 Jun 2026 09:46:54 +0000 Subject: [PATCH 08/17] 8386130: TestPrintMethodData.java failing with VirtualThread as main thread Reviewed-by: shade, galder --- test/hotspot/jtreg/compiler/profiling/TestPrintMethodData.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/compiler/profiling/TestPrintMethodData.java b/test/hotspot/jtreg/compiler/profiling/TestPrintMethodData.java index 9e14cd6ea4b..7cc5cda88b6 100644 --- a/test/hotspot/jtreg/compiler/profiling/TestPrintMethodData.java +++ b/test/hotspot/jtreg/compiler/profiling/TestPrintMethodData.java @@ -64,7 +64,7 @@ public class TestPrintMethodData { private static final Generator GEN_LONG = Generators.G.longs(); private static final int SIZE = 1024; - static void main(String[] args) throws Exception { + public static void main(String[] args) throws Exception { long[] longs = new long[SIZE]; Generators.G.fill(GEN_LONG, longs); From 2d2d59dfcfd0edd3e0a151fc6df3a641f4798349 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20Walln=C3=B6fer?= Date: Mon, 8 Jun 2026 10:57:57 +0000 Subject: [PATCH 09/17] 8384065: Improve wrapping of link labels in the table of contents Reviewed-by: nbenalla --- .../html/AbstractExecutableMemberWriter.java | 20 +++++++ .../formats/html/ConstructorWriter.java | 4 +- .../doclets/formats/html/MethodWriter.java | 4 +- .../formats/html/resources/stylesheet.css | 8 ++- .../doclet/testErasure/TestErasure.java | 20 +++---- .../doclet/testNavigation/TestNavigation.java | 55 ++++++++++++++++++- .../doclet/testStylesheet/TestStylesheet.java | 1 - 7 files changed, 93 insertions(+), 19 deletions(-) diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractExecutableMemberWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractExecutableMemberWriter.java index 15f55d18cc0..c1c4dd512d6 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractExecutableMemberWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractExecutableMemberWriter.java @@ -137,6 +137,26 @@ public abstract class AbstractExecutableMemberWriter extends AbstractMemberWrite .title(title))); } + /** + * Returns a label for the given element to be used the table of contents sidebar. + * + * @param executableElement method or constructor + * @return the link label + */ + protected Content getTOCLabel(ExecutableElement executableElement) { + var signature = utils.makeSignature(executableElement, typeElement, false, true); + var label = new ContentBuilder(Text.of(utils.getSimpleName(executableElement))); + // Insert line break opportunity before first parameter. + if (signature.length() > 2) { + label.add(Text.of(signature.substring(0, 1))) + .add(HtmlTree.WBR()) + .add(Text.of(signature.substring(1))); + } else { + label.add(signature); + } + return label; + } + /** * Adds the generic type parameters. * diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ConstructorWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ConstructorWriter.java index d8d01a4e6ad..48e269445ae 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ConstructorWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ConstructorWriter.java @@ -119,9 +119,7 @@ public class ConstructorWriter extends AbstractExecutableMemberWriter { constructorContent.add(div); memberList.add(getMemberListItem(constructorContent)); writer.tableOfContents.addLink(htmlIds.forMember(currentConstructor).getFirst(), - Text.of(utils.getSimpleName(constructor) - + utils.makeSignature(currentConstructor, typeElement, false, true)), - TableOfContents.Level.SECOND); + getTOCLabel(currentConstructor), TableOfContents.Level.SECOND); } Content constructorDetails = getConstructorDetails(constructorDetailsHeader, memberList); target.add(constructorDetails); diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/MethodWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/MethodWriter.java index 3bc5b7617b0..5edfd9e174d 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/MethodWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/MethodWriter.java @@ -118,9 +118,7 @@ public class MethodWriter extends AbstractExecutableMemberWriter { methodContent.add(div); memberList.add(writer.getMemberListItem(methodContent)); writer.tableOfContents.addLink(htmlIds.forMember(currentMethod).getFirst(), - Text.of(utils.getSimpleName(method) - + utils.makeSignature(currentMethod, typeElement, false, true)), - TableOfContents.Level.SECOND); + getTOCLabel(currentMethod), TableOfContents.Level.SECOND); } Content methodDetails = getMethodDetails(methodDetailsHeader, memberList); detailsList.add(methodDetails); diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/stylesheet.css b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/stylesheet.css index fc0fa68f029..86230a0e8fd 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/stylesheet.css +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/stylesheet.css @@ -661,9 +661,12 @@ a.current-selection { } nav.toc a { display: block; - padding: 8px; + padding: 7px 8px; overflow: hidden; text-overflow: ellipsis; + text-wrap: balance; + text-indent: 0.5em hanging; + line-height: 1.35; } nav.toc ol.toc-list ol.toc-list a { padding-left: 24px; @@ -940,6 +943,9 @@ div.checkboxes > label > input { .col-first, .col-second, .col-constructor-name { overflow: auto; } +.col-constructor-name, .method-summary .col-second { + text-indent: 0.5em hanging; +} body:not(.class-declaration-page) .col-first a:link, .col-summary-item-name a:link { font-weight:bold; diff --git a/test/langtools/jdk/javadoc/doclet/testErasure/TestErasure.java b/test/langtools/jdk/javadoc/doclet/testErasure/TestErasure.java index ffa8a530859..16a48fcc626 100644 --- a/test/langtools/jdk/javadoc/doclet/testErasure/TestErasure.java +++ b/test/langtools/jdk/javadoc/doclet/testErasure/TestErasure.java @@ -103,9 +103,9 @@ public class TestErasure extends JavadocTester { checkOutput("Foo.html", true, """
  • Constructor Details
      -
    1. Foo(T)
    2. -
    3. Foo(T)
    4. -
    5. Foo(T)
    6. +
    7. Foo(T)
    8. +
    9. Foo(T)
    10. +
    11. Foo(T)
  • """); checkOutput("index-all.html", true, """ @@ -145,9 +145,9 @@ public class TestErasure extends JavadocTester { checkOutput("Foo.html", true, """
  • Method Details
      -
    1. m(T)
    2. -
    3. m(T)
    4. -
    5. m(T)
    6. +
    7. m(T)
    8. +
    9. m(T)
    10. +
    11. m(T)
  • """); checkOutput("index-all.html", true, """ @@ -210,8 +210,8 @@ public class TestErasure extends JavadocTester { checkOutput("Foo.html", true, """
  • Constructor Details
      -
    1. Foo(T)
    2. -
    3. Foo(T)
    4. +
    5. Foo(T)
    6. +
    7. Foo(T)
  • """); checkOutput("index-all.html", true, """ @@ -242,8 +242,8 @@ public class TestErasure extends JavadocTester { checkOutput("Foo.html", true, """
  • Method Details
      -
    1. m(T)
    2. -
    3. m(T)
    4. +
    5. m(T)
    6. +
    7. m(T)
  • """); checkOutput("index-all.html", true, """ diff --git a/test/langtools/jdk/javadoc/doclet/testNavigation/TestNavigation.java b/test/langtools/jdk/javadoc/doclet/testNavigation/TestNavigation.java index 17ab9734845..02100d84a1a 100644 --- a/test/langtools/jdk/javadoc/doclet/testNavigation/TestNavigation.java +++ b/test/langtools/jdk/javadoc/doclet/testNavigation/TestNavigation.java @@ -25,7 +25,7 @@ * @test * @bug 7025314 8023700 7198273 8025633 8026567 8081854 8196027 8182765 * 8196200 8196202 8223378 8258659 8261976 8320458 8329537 8350638 - * 8342705 8371021 8373526 + * 8342705 8371021 8373526 8384065 * @summary Make sure the Next/Prev Class links iterate through all types. * Make sure the navagation is 2 columns, not 3. * @library /tools/lib ../../lib @@ -172,6 +172,36 @@ public class TestNavigation extends JavadocTester { tb.writeJavaFiles(src, """ package pkg1; public class A { + /** + * Empty ctor + */ + public A() {} + + /** + * Single param ctor + */ + public A(int i) {} + + /** + * A ctor with many params + */ + public A(int i, int j, int x, int y, String s, boolean b) {} + + /** + * A method without parameters + */ + public void noParams() {} + + /** + * A method with a single parameter + */ + public void oneParam(String s) {} + + /** + * A method with lots of parameters + */ + public void manyParams(String s, int i, int j, boolean b, double d, double e) {} + /** * Class with members. */ @@ -217,6 +247,29 @@ public class TestNavigation extends JavadocTester { "pkg1"); checkExit(Exit.OK); + checkOrder("pkg1/A.html", + """ +
      +
    1. Description
    2. +
    3. Nested Class Summary
    4. +
    5. Constructor Summary
    6. +
    7. Method Summary
    8. +
    9. Constructor Details +
        +
      1. A()
      2. +
      3. A(int)
      4. +
      5. A(int, int, int, int, String, boolean)
      6. +
      +
    10. +
    11. Method Details +
        +
      1. noParams()
      2. +
      3. oneParam(String)
      4. +
      5. manyParams(String, int, int, boolean, double, double)
      6. +
      +
    12. +
    """); + checkOrder("pkg1/A.X.html", """