From a73097f9fbcddac3d4f8770f35635a3b7d632c69 Mon Sep 17 00:00:00 2001 From: "lawrence.andrews" Date: Thu, 21 May 2026 17:31:30 +0000 Subject: [PATCH] 8384940: Open source accessibility tests Reviewed-by: prr, kizune --- .../javax/accessibility/awt/ButtonTest.java | 115 ++++++ .../javax/accessibility/awt/ChoiceTest.java | 123 +++++++ .../accessibility/awt/ScrollbarTest.java | 158 +++++++++ .../AccessibleComponentTester.java | 198 +++++++++++ .../AccessibleStateSetTester.java | 318 +++++++++++++++++ .../accessibility/AccessibleTestUtils.java | 334 ++++++++++++++++++ 6 files changed, 1246 insertions(+) create mode 100644 test/jdk/javax/accessibility/awt/ButtonTest.java create mode 100644 test/jdk/javax/accessibility/awt/ChoiceTest.java create mode 100644 test/jdk/javax/accessibility/awt/ScrollbarTest.java create mode 100644 test/jdk/javax/swing/regtesthelpers/accessibility/AccessibleComponentTester.java create mode 100644 test/jdk/javax/swing/regtesthelpers/accessibility/AccessibleStateSetTester.java create mode 100644 test/jdk/javax/swing/regtesthelpers/accessibility/AccessibleTestUtils.java diff --git a/test/jdk/javax/accessibility/awt/ButtonTest.java b/test/jdk/javax/accessibility/awt/ButtonTest.java new file mode 100644 index 00000000000..6164c2fd7a8 --- /dev/null +++ b/test/jdk/javax/accessibility/awt/ButtonTest.java @@ -0,0 +1,115 @@ +/* + * 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 Regression Test: javax.accessibility, Button + * @library ../../swing/regtesthelpers/accessibility/ + * @build AccessibleTestUtils AccessibleComponentTester AccessibleStateSetTester + * @run main ButtonTest + */ + +import java.awt.AWTException; +import java.awt.Button; +import java.awt.Component; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Robot; +import java.lang.reflect.InvocationTargetException; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; +import javax.accessibility.AccessibleStateSet; + +public class ButtonTest { + + static Button button; + final String accName = "Button Test"; + final String accDesc = "Regression Test: javax.accessibility, Button"; + final AccessibleRole role = AccessibleRole.PUSH_BUTTON; + static Frame frame; + + public void createGUI() { + frame = new Frame("ButtonTest"); + button = new Button("This is a Button!"); + AccessibleContext ac = button.getAccessibleContext(); + + ac.setAccessibleName(accName); + ac.setAccessibleDescription(accDesc); + + frame.add(button); + frame.setSize(200, 200); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + public static void main(String[] args) + throws InterruptedException, InvocationTargetException, AWTException { + + ButtonTest buttonTest = new ButtonTest(); + EventQueue.invokeAndWait(buttonTest::createGUI); + + Robot rbt = new Robot(); + rbt.waitForIdle(); + rbt.delay(5000); + + try { + EventQueue.invokeAndWait(buttonTest::test); + } finally { + buttonTest.dispose(); + } + } + + private void dispose() + throws InterruptedException, InvocationTargetException { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + + public Component getComponent() { + return button; + } + + public void test() { + Button b = (Button) getComponent(); + + AccessibleContext ac = b.getAccessibleContext(); + AccessibleStateSet aset = ac.getAccessibleStateSet(); + if (aset == null) { + throw new RuntimeException("getAccessibleStateSet should not return null"); + } + AccessibleStateSetTester astr = + new AccessibleStateSetTester(b, aset); + astr.testAll(); + + AccessibleTestUtils.verifyButtonAccessibility( + b, + accName, + accDesc + ); + } +} diff --git a/test/jdk/javax/accessibility/awt/ChoiceTest.java b/test/jdk/javax/accessibility/awt/ChoiceTest.java new file mode 100644 index 00000000000..59ca688a7e3 --- /dev/null +++ b/test/jdk/javax/accessibility/awt/ChoiceTest.java @@ -0,0 +1,123 @@ +/* + * 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 Regression Test: javax.accessibility, Choice + * @library ../../swing/regtesthelpers/accessibility/ + * @build AccessibleTestUtils AccessibleComponentTester AccessibleStateSetTester + * @run main ChoiceTest + */ + +import java.awt.AWTException; +import java.awt.Choice; +import java.awt.Component; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Robot; +import java.lang.reflect.InvocationTargetException; +import java.util.Locale; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; +import javax.accessibility.AccessibleStateSet; + +public class ChoiceTest { + + static Choice choice; + final String accName = "Choice Test"; + final String accDesc = "Regression Test: javax.accessibility, Choice"; + final AccessibleRole role = AccessibleRole.COMBO_BOX; + static Frame frame; + + public void createGUI() { + frame = new Frame("Choice Test"); + choice = new Choice(); + choice.add("One"); + choice.add("Two"); + choice.add("Three"); + + AccessibleContext ac = choice.getAccessibleContext(); + ac.setAccessibleName(accName); + ac.setAccessibleDescription(accDesc); + + frame.add(choice); + frame.setSize(200, 200); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + public static void main(String[] args) + throws InterruptedException, InvocationTargetException, AWTException { + + ChoiceTest choiceTest = new ChoiceTest(); + EventQueue.invokeAndWait(choiceTest::createGUI); + + Robot rbt = new Robot(); + rbt.waitForIdle(); + rbt.delay(5000); + + try { + EventQueue.invokeAndWait(choiceTest::test); + } finally { + choiceTest.dispose(); + } + + } + + private void dispose() + throws InterruptedException, InvocationTargetException { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + + public Component getComponent() { + return choice; + } + + public void test() { + Choice c = (Choice) getComponent(); + + // If you want to keep using AccessibleStateSetTester explicitly: + AccessibleContext ac = c.getAccessibleContext(); + AccessibleStateSet aset = ac.getAccessibleStateSet(); + if (aset == null) { + throw new RuntimeException( + "getAccessibleStateSet should not return a null value"); + } + AccessibleStateSetTester astr = + new AccessibleStateSetTester(c, aset); + astr.testAll(); + + // All remaining checks (name, desc, role, locale, AccessibleAction, + // AccessibleComponent, selection/text/value) are in one place: + AccessibleTestUtils.verifyChoiceAccessibility( + c, + accName, + accDesc + ); + } +} diff --git a/test/jdk/javax/accessibility/awt/ScrollbarTest.java b/test/jdk/javax/accessibility/awt/ScrollbarTest.java new file mode 100644 index 00000000000..1e0d1551da4 --- /dev/null +++ b/test/jdk/javax/accessibility/awt/ScrollbarTest.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 Scrollbar Accessibility test. + * @library ../../swing/regtesthelpers/accessibility/ + * @build AccessibleTestUtils AccessibleComponentTester AccessibleStateSetTester + * @run main ScrollbarTest + */ + +import java.awt.AWTException; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Robot; +import java.awt.Scrollbar; +import java.lang.reflect.InvocationTargetException; + +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleState; +import javax.accessibility.AccessibleStateSet; + +public final class ScrollbarTest { + + private static Scrollbar scrollbar; + private static Frame frame; + + private static final String ACCESSIBLE_NAME = "Scrollbar Test"; + private static final String ACCESSIBLE_DESCRIPTION = + "Regression Test: javax.accessibility, Scrollbar"; + + public static void main(String[] args) + throws InterruptedException, InvocationTargetException, AWTException { + + ScrollbarTest test = new ScrollbarTest(); + EventQueue.invokeAndWait(test::createGui); + + Robot robot = new Robot(); + robot.waitForIdle(); + robot.delay(5000); + + try { + EventQueue.invokeAndWait(test::testAccessibility); + } finally { + test.dispose(); + } + } + + private void createGui() { + frame = new Frame("ScrollbarTest"); + scrollbar = new Scrollbar(Scrollbar.VERTICAL, 0, 60, 0, 300); + + AccessibleContext context = scrollbar.getAccessibleContext(); + context.setAccessibleName(ACCESSIBLE_NAME); + context.setAccessibleDescription(ACCESSIBLE_DESCRIPTION); + + frame.add(scrollbar); + frame.setSize(300, 300); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + private void dispose() throws InterruptedException, InvocationTargetException { + EventQueue.invokeAndWait(() -> { + if (frame != null) { + frame.dispose(); + } + }); + } + + private Scrollbar getComponent() { + return scrollbar; + } + + private void testAccessibility() { + Scrollbar component = getComponent(); + + AccessibleTestUtils.verifyScrollbarAccessibility( + component, + ACCESSIBLE_NAME, + ACCESSIBLE_DESCRIPTION + ); + + AccessibleStateSet stateSet = component.getAccessibleContext().getAccessibleStateSet(); + if (stateSet == null) { + throw new RuntimeException("getAccessibleStateSet returned null"); + } + + new ScrollAccessibleStateSetTester(component, stateSet).testAll(); + } + + private static final class ScrollAccessibleStateSetTester extends AccessibleStateSetTester { + + private final Scrollbar scrollbar; + private final AccessibleStateSet stateSet; + + private ScrollAccessibleStateSetTester(Scrollbar scrollbar, AccessibleStateSet stateSet) { + super(scrollbar, stateSet); + this.scrollbar = scrollbar; + this.stateSet = stateSet; + } + + @Override + public void testHorizontal() { + if (scrollbar.getOrientation() == Scrollbar.HORIZONTAL) { + if (!stateSet.contains(AccessibleState.HORIZONTAL)) { + throw new RuntimeException( + "Scrollbar is horizontal but AccessibleStateSet does not contain HORIZONTAL" + ); + } + + if (stateSet.contains(AccessibleState.VERTICAL)) { + throw new RuntimeException( + "Scrollbar is horizontal but AccessibleStateSet contains both HORIZONTAL and VERTICAL" + ); + } + } + } + + @Override + public void testVertical() { + if (scrollbar.getOrientation() == Scrollbar.VERTICAL) { + if (!stateSet.contains(AccessibleState.VERTICAL)) { + throw new RuntimeException( + "Scrollbar is vertical but AccessibleStateSet does not contain VERTICAL" + ); + } + + if (stateSet.contains(AccessibleState.HORIZONTAL)) { + throw new RuntimeException( + "Scrollbar is vertical but AccessibleStateSet contains both VERTICAL and HORIZONTAL" + ); + } + } + } + } +} diff --git a/test/jdk/javax/swing/regtesthelpers/accessibility/AccessibleComponentTester.java b/test/jdk/javax/swing/regtesthelpers/accessibility/AccessibleComponentTester.java new file mode 100644 index 00000000000..ae529b22567 --- /dev/null +++ b/test/jdk/javax/swing/regtesthelpers/accessibility/AccessibleComponentTester.java @@ -0,0 +1,198 @@ +/* + * 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.Color; +import java.awt.Component; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Point; +import java.awt.Rectangle; +import java.util.Objects; +import java.util.function.Supplier; + +import javax.accessibility.AccessibleComponent; + +public final class AccessibleComponentTester { + + private final AccessibleComponent accessibleComponent; + private final Component component; + + public AccessibleComponentTester(Component component, AccessibleComponent accessibleComponent) { + this.component = Objects.requireNonNull(component, "component must not be null"); + this.accessibleComponent = Objects.requireNonNull(accessibleComponent, "accessibleComponent must not be null"); + } + + public void test() { + testGetBackground(); + testGetBounds(); + testGetCursor(); + testGetFont(); + testGetForeground(); + testGetLocation(); + testGetLocationOnScreen(); + testGetSize(); + testIsEnabled(); + testIsFocusTraversable(); + testIsShowing(); + testIsVisible(); + } + + public void testGetBackground() { + assertEqual( + "getBackground", + component.getBackground(), + accessibleComponent.getBackground() + ); + } + + public void testGetBounds() { + assertEqual( + "getBounds", + component.getBounds(), + accessibleComponent.getBounds() + ); + } + + public void testGetCursor() { + assertEqual( + "getCursor", + component.getCursor(), + accessibleComponent.getCursor() + ); + } + + public void testGetFont() { + assertEqual( + "getFont", + component.getFont(), + accessibleComponent.getFont() + ); + } + + public void testGetForeground() { + assertEqual( + "getForeground", + component.getForeground(), + accessibleComponent.getForeground() + ); + } + + public void testGetLocation() { + assertEqualWithStateHandling( + "getLocation", + component::getLocation, + accessibleComponent::getLocation + ); + } + + public void testGetLocationOnScreen() { + assertEqualWithStateHandling( + "getLocationOnScreen", + component::getLocationOnScreen, + accessibleComponent::getLocationOnScreen + ); + } + + public void testGetSize() { + assertEqual( + "getSize", + component.getSize(), + accessibleComponent.getSize() + ); + } + + public void testIsEnabled() { + assertBooleanEqual( + "isEnabled", + component.isEnabled(), + accessibleComponent.isEnabled() + ); + } + + public void testIsFocusTraversable() { + assertBooleanEqual( + "isFocusTraversable", + component.isFocusTraversable(), + accessibleComponent.isFocusTraversable() + ); + } + + public void testIsShowing() { + assertBooleanEqual( + "isShowing", + component.isShowing(), + accessibleComponent.isShowing() + ); + } + + public void testIsVisible() { + assertBooleanEqual( + "isVisible", + component.isVisible(), + accessibleComponent.isVisible() + ); + } + + private void assertEqual(String methodName, Object componentValue, Object accessibleValue) { + if (!Objects.equals(componentValue, accessibleValue)) { + throw new RuntimeException(buildMismatchMessage(methodName, componentValue, accessibleValue)); + } + } + + private void assertBooleanEqual(String methodName, boolean componentValue, boolean accessibleValue) { + if (componentValue != accessibleValue) { + throw new RuntimeException( + String.format( + "Mismatch in %s: Component returned [%s], AccessibleComponent returned [%s]", + methodName, componentValue, accessibleValue + ) + ); + } + } + + private void assertEqualWithStateHandling( + String methodName, + Supplier componentSupplier, + Supplier accessibleSupplier + ) { + try { + T componentValue = componentSupplier.get(); + T accessibleValue = accessibleSupplier.get(); + assertEqual(methodName, componentValue, accessibleValue); + } catch (java.awt.IllegalComponentStateException ex) { + throw new RuntimeException( + "Component was not in a valid state when " + methodName + " was called. " + + "This is not necessarily an accessibility issue.", + ex + ); + } + } + + private String buildMismatchMessage(String methodName, Object componentValue, Object accessibleValue) { + return String.format( + "Mismatch in %s: Component returned [%s], AccessibleComponent returned [%s]", + methodName, componentValue, accessibleValue + ); + } +} diff --git a/test/jdk/javax/swing/regtesthelpers/accessibility/AccessibleStateSetTester.java b/test/jdk/javax/swing/regtesthelpers/accessibility/AccessibleStateSetTester.java new file mode 100644 index 00000000000..2f772859e76 --- /dev/null +++ b/test/jdk/javax/swing/regtesthelpers/accessibility/AccessibleStateSetTester.java @@ -0,0 +1,318 @@ +/* + * 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.Component; +import java.util.Objects; + +import javax.accessibility.Accessible; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleSelection; +import javax.accessibility.AccessibleState; +import javax.accessibility.AccessibleStateSet; +import javax.swing.JComponent; + +/** + * Validates an {@link AccessibleStateSet} against the state of a component. + * + *

This class provides generic validation for common accessibility states. + * Subclasses can extend it to add component-specific checks.

+ * + *

This works for both Swing and AWT components that implement + * {@link Accessible}.

+ */ +public class AccessibleStateSetTester { + + private final AccessibleStateSet stateSet; + private final Component component; + + public AccessibleStateSetTester(Component component, AccessibleStateSet stateSet) { + this.component = Objects.requireNonNull(component, "component must not be null"); + this.stateSet = Objects.requireNonNull(stateSet, "stateSet must not be null"); + } + + /** + * Runs all generic state validations plus component-specific hooks. + */ + public void testAll() { + testActive(); + testArmed(); + testBusy(); + testChecked(); + testCollapsed(); + testEditable(); + testEnabled(); + testExpandable(); + testExpanded(); + testFocusable(); + testFocused(); + testHorizontal(); + testIconified(); + testModal(); + testMultiLine(); + testMultiSelectable(); + testOpaque(); + testPressed(); + testResizable(); + testSelectable(); + testSelected(); + testShowing(); + testSingleLine(); + testTransient(); + testVertical(); + testVisible(); + } + + public void testEnabled() { + assertStateMatches( + AccessibleState.ENABLED, + component.isEnabled(), + "component is enabled", + "component is not enabled" + ); + } + + public void testFocusable() { + assertStateMatches( + AccessibleState.FOCUSABLE, + component.isFocusable(), + "component is focusable", + "component is not focusable" + ); + } + + public void testFocused() { + boolean focused = isFocused(); + + if (stateSet.contains(AccessibleState.FOCUSED)) { + if (!stateSet.contains(AccessibleState.FOCUSABLE)) { + throw new RuntimeException( + "AccessibleStateSet contains FOCUSED but not FOCUSABLE" + ); + } + if (!focused) { + throw new RuntimeException( + "AccessibleStateSet contains FOCUSED but the component does not have focus" + ); + } + } else if (focused) { + throw new RuntimeException( + "AccessibleStateSet does not contain FOCUSED but the component has focus" + ); + } + } + + public void testShowing() { + assertStateMatches( + AccessibleState.SHOWING, + component.isShowing(), + "component is showing", + "component is not showing" + ); + } + + public void testVisible() { + assertStateMatches( + AccessibleState.VISIBLE, + component.isVisible(), + "component is visible", + "component is not visible" + ); + } + + public void testSelectable() { + AccessibleState selectionStatus = getSelectionStatus(); + + if (stateSet.contains(AccessibleState.SELECTABLE)) { + if (selectionStatus != AccessibleState.SELECTABLE + && selectionStatus != AccessibleState.SELECTED) { + throw new RuntimeException( + "AccessibleStateSet contains SELECTABLE but the component is not selectable" + ); + } + } else if (selectionStatus == AccessibleState.SELECTABLE) { + throw new RuntimeException( + "AccessibleStateSet does not contain SELECTABLE but the component is selectable" + ); + } + } + + public void testSelected() { + AccessibleState selectionStatus = getSelectionStatus(); + + if (stateSet.contains(AccessibleState.SELECTED)) { + if (!stateSet.contains(AccessibleState.SELECTABLE)) { + throw new RuntimeException( + "AccessibleStateSet contains SELECTED but not SELECTABLE" + ); + } + if (selectionStatus != AccessibleState.SELECTED) { + throw new RuntimeException( + "AccessibleStateSet contains SELECTED but the component is not selected" + ); + } + } else if (selectionStatus == AccessibleState.SELECTED) { + throw new RuntimeException( + "AccessibleStateSet does not contain SELECTED but the component is selected" + ); + } + } + + public void testOpaque() { + if (!(component instanceof JComponent jComponent)) { + return; + } + + assertStateMatches( + AccessibleState.OPAQUE, + jComponent.isOpaque(), + "component is opaque", + "component is not opaque" + ); + } + + /** + * Returns true if the component currently has focus. + */ + public boolean isFocused() { + return component.hasFocus(); + } + + /** + * Determines whether the component is selectable or selected. + * + * @return {@link AccessibleState#SELECTED} if selected, + * {@link AccessibleState#SELECTABLE} if selectable but not selected, + * or {@code null} if neither applies + */ + public AccessibleState getSelectionStatus() { + if (!(component instanceof Accessible accessible)) { + return null; + } + + AccessibleContext context = accessible.getAccessibleContext(); + if (context == null) { + return null; + } + + Accessible parent = context.getAccessibleParent(); + if (parent == null) { + return null; + } + + AccessibleContext parentContext = parent.getAccessibleContext(); + if (parentContext == null) { + return null; + } + + AccessibleSelection selection = parentContext.getAccessibleSelection(); + if (selection == null) { + return null; + } + + int index = context.getAccessibleIndexInParent(); + if (index < 0) { + return AccessibleState.SELECTABLE; + } + + return selection.isAccessibleChildSelected(index) + ? AccessibleState.SELECTED + : AccessibleState.SELECTABLE; + } + + private void assertStateMatches( + AccessibleState state, + boolean actualCondition, + String presentMessage, + String absentMessage) { + + boolean presentInStateSet = stateSet.contains(state); + + if (presentInStateSet && !actualCondition) { + throw new RuntimeException( + "AccessibleStateSet contains " + state + " but " + absentMessage + ); + } + + if (!presentInStateSet && actualCondition) { + throw new RuntimeException( + "AccessibleStateSet does not contain " + state + " but " + presentMessage + ); + } + } + + // Component-specific hooks (intentionally empty by default) + + public void testActive() { + } + + public void testArmed() { + } + + public void testBusy() { + } + + public void testChecked() { + } + + public void testCollapsed() { + } + + public void testEditable() { + } + + public void testExpandable() { + } + + public void testExpanded() { + } + + public void testHorizontal() { + } + + public void testIconified() { + } + + public void testModal() { + } + + public void testMultiLine() { + } + + public void testMultiSelectable() { + } + + public void testPressed() { + } + + public void testResizable() { + } + + public void testSingleLine() { + } + + public void testTransient() { + } + + public void testVertical() { + } +} diff --git a/test/jdk/javax/swing/regtesthelpers/accessibility/AccessibleTestUtils.java b/test/jdk/javax/swing/regtesthelpers/accessibility/AccessibleTestUtils.java new file mode 100644 index 00000000000..2410cff997a --- /dev/null +++ b/test/jdk/javax/swing/regtesthelpers/accessibility/AccessibleTestUtils.java @@ -0,0 +1,334 @@ +/* + * 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.awt.Button; +import java.awt.Choice; +import java.awt.Component; +import java.awt.Scrollbar; +import java.util.Locale; +import java.util.Objects; + +import javax.accessibility.AccessibleAction; +import javax.accessibility.AccessibleComponent; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; +import javax.accessibility.AccessibleSelection; +import javax.accessibility.AccessibleStateSet; +import javax.accessibility.AccessibleText; +import javax.accessibility.AccessibleValue; + +public final class AccessibleTestUtils { + + private AccessibleTestUtils() { + } + + public static void verifyAccessibleContextCommon( + AccessibleContext context, + String expectedName, + String expectedDescription, + AccessibleRole expectedRole, + boolean expectAction, + boolean expectSelection, + boolean expectText, + boolean expectValue) { + + Objects.requireNonNull(context, "AccessibleContext must not be null"); + + assertExpectedString( + "getAccessibleName", + expectedName, + context.getAccessibleName() + ); + + assertExpectedString( + "getAccessibleDescription", + expectedDescription, + context.getAccessibleDescription() + ); + + 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 + )); + } + } + + AccessibleStateSet stateSet = context.getAccessibleStateSet(); + if (stateSet == null) { + 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()); + } + + public static void verifyAWTComponentAccessibility( + Component component, + String expectedName, + String expectedDescription, + AccessibleRole expectedRole, + boolean expectAction, + boolean expectSelection, + boolean expectText, + boolean expectValue) { + + Objects.requireNonNull(component, "Component under test must not be null"); + + AccessibleContext context = component.getAccessibleContext(); + verifyAccessibleContextCommon( + context, + expectedName, + expectedDescription, + expectedRole, + expectAction, + expectSelection, + expectText, + expectValue + ); + + assertLocaleMatches(component, context); + + AccessibleComponent accessibleComponent = context.getAccessibleComponent(); + if (accessibleComponent == null) { + throw new RuntimeException("getAccessibleComponent returned null"); + } + + 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( + button, + expectedName, + expectedDescription, + AccessibleRole.PUSH_BUTTON, + true, + false, + false, + true + ); + + AccessibleContext context = button.getAccessibleContext(); + + AccessibleAction action = context.getAccessibleAction(); + if (action == null) { + throw new RuntimeException("getAccessibleAction should not return null for Button"); + } + + int actionCount = action.getAccessibleActionCount(); + if (actionCount != 1) { + throw new RuntimeException( + "getAccessibleActionCount should return 1 for Button; got " + actionCount + ); + } + + String actionDescription = action.getAccessibleActionDescription(0); + if (!"click".equals(actionDescription)) { + throw new RuntimeException( + "getAccessibleActionDescription(0) should return \"click\" for Button; got [" + + actionDescription + "]" + ); + } + + AccessibleValue value = context.getAccessibleValue(); + if (value == null) { + throw new RuntimeException("getAccessibleValue should not return null for Button"); + } + + 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" + ); + } + + assertIntValueEquals( + "getCurrentAccessibleValue after setCurrentAccessibleValue(5)", + 0, + value.getCurrentAccessibleValue() + ); + } + + private static void assertExpectedString(String methodName, String expected, String actual) { + if (expected == null) { + throw new RuntimeException("Excepted value is null. Provide " + + "excepted value"); + } + + if (actual == null) { + throw new RuntimeException(methodName + " returned null; expected" + + " [" + expected + "]"); + } + if (!expected.equals(actual)) { + throw new RuntimeException(methodName + " returned [" + actual + + "]; expected [" + expected + "]"); + } + } + + private static void assertPresence(String methodName, boolean expectedPresent, Object value) { + if (expectedPresent && value == null) { + throw new RuntimeException(methodName + " returned null but was expected"); + } + if (!expectedPresent && value != null) { + throw new RuntimeException(methodName + " returned non-null but " + + "was expected to be null"); + } + } + + 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"); + } + if (!componentLocale.equals(accessibleLocale)) { + throw new RuntimeException(String.format( + "AccessibleContext.getLocale returned [%s], but Component" + + ".getLocale returned [%s]", + accessibleLocale, componentLocale + )); + } + } + + private static void assertIntValueEquals(String methodName, int expected, Number actual) { + if (actual == null) { + throw new RuntimeException(methodName + " returned null; expected [" + expected + "]"); + } + if (actual.intValue() != expected) { + throw new RuntimeException( + methodName + " returned [" + actual + "]; expected [" + expected + "]" + ); + } + } +}