-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
8339561: The test/jdk/java/awt/Paint/ListRepaint.java may fail after JDK-8327401 #20861
base: master
Are you sure you want to change the base?
Changes from 3 commits
94b798e
a026bd4
4c88385
b2907dc
2a56f4b
4321e29
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -37,22 +37,22 @@ | |
|
||
public class ActiveAWTWindowTest { | ||
|
||
private Frame frame, frame2; | ||
private Button button, button2; | ||
private TextField textField, textField2; | ||
private volatile Frame frame, frame2; | ||
private volatile Button button, button2; | ||
private volatile TextField textField, textField2; | ||
private volatile int eventType; | ||
private final Object lock1 = new Object(); | ||
private final Object lock2 = new Object(); | ||
private final Object lock3 = new Object(); | ||
private boolean passed = true; | ||
private final int delay = 150; | ||
|
||
public static void main(String[] args) { | ||
public static void main(String[] args) throws Exception { | ||
ActiveAWTWindowTest test = new ActiveAWTWindowTest(); | ||
try { | ||
test.doTest(); | ||
} finally { | ||
EventQueue.invokeLater(() -> { | ||
EventQueue.invokeAndWait(() -> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The test should wait until the frame will be disposed. |
||
if (test.frame != null) { | ||
test.frame.dispose(); | ||
} | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -33,9 +33,8 @@ | |||||||||||
*/ | ||||||||||||
|
||||||||||||
import java.awt.BorderLayout; | ||||||||||||
import java.awt.EventQueue; | ||||||||||||
import java.awt.KeyboardFocusManager; | ||||||||||||
import java.awt.Frame; | ||||||||||||
import java.awt.KeyboardFocusManager; | ||||||||||||
import java.awt.List; | ||||||||||||
import java.awt.Panel; | ||||||||||||
import java.awt.Point; | ||||||||||||
|
@@ -51,7 +50,7 @@ | |||||||||||
import jdk.test.lib.Platform; | ||||||||||||
|
||||||||||||
public class KeyEventsTest { | ||||||||||||
TestState currentState; | ||||||||||||
private volatile TestState currentState; | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd rather not declare it a The value is assigned to At the same time, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Submitted JDK-8340196: j.a/List/KeyEventsTest/KeyEventsTest: TestState currentState is not thread-safe. |
||||||||||||
final Object LOCK = new Object(); | ||||||||||||
final int ACTION_TIMEOUT = 500; | ||||||||||||
|
||||||||||||
|
@@ -66,16 +65,14 @@ public static void main(final String[] args) throws Exception { | |||||||||||
r = new Robot(); | ||||||||||||
KeyEventsTest app = new KeyEventsTest(); | ||||||||||||
try { | ||||||||||||
EventQueue.invokeAndWait(app::initAndShowGui); | ||||||||||||
app.initAndShowGui(); | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The test was changed here, the root cause of the failure discussed in https://github.com/openjdk/jdk/pull/19339/files#r1609189736 is a product bug: https://bugs.openjdk.org/browse/JDK-8201307. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure if it's a product bug… AWT components do not explicitly state whether they're thread-safe or not. It's more or less assumed they should be. At the same time, I don't see code which ensures all the cases are thread-safe. As far as I know, clientlibs group has been treating AWT components just like Swing ones: for safety create, access and destroy AWT components on EDT only. I haven't looked at the test code thoroughly. It looks it still needs more investigation. At the same time, I find it weird that jdk/src/java.desktop/unix/classes/sun/awt/X11/XListPeer.java Lines 389 to 390 in 77468c2
Overall, it needs more investigation. It could be that the fix for JDK-6471693 needs revising. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This is one of the implementation details of the AWT, the "native" component should be painted before the paint event will proceed on EDT, and even if EDT is blocked the "native" part should be painted. Since the XAWT is implemented via Swing(or some custom "java peers") which are not thread-safe we should use some additional synchronization. On macOS we have a special lock for that
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And the NPE is triggered for List.select only because it is one method in the test that uses peer w/o synchronization.
|
||||||||||||
r.waitForIdle(); | ||||||||||||
r.delay(500); | ||||||||||||
app.doTest(); | ||||||||||||
} finally { | ||||||||||||
EventQueue.invokeAndWait(() -> { | ||||||||||||
if (app.keyFrame != null) { | ||||||||||||
app.keyFrame.dispose(); | ||||||||||||
} | ||||||||||||
}); | ||||||||||||
if (app.keyFrame != null) { | ||||||||||||
app.keyFrame.dispose(); | ||||||||||||
} | ||||||||||||
} | ||||||||||||
} | ||||||||||||
|
||||||||||||
|
@@ -184,52 +181,51 @@ private void test(TestState currentState) throws Exception { | |||||||||||
throw new RuntimeException("Test failed - list isn't focus owner."); | ||||||||||||
} | ||||||||||||
|
||||||||||||
EventQueue.invokeAndWait(() -> { | ||||||||||||
list.deselect(0); | ||||||||||||
list.deselect(1); | ||||||||||||
list.deselect(2); | ||||||||||||
list.deselect(3); | ||||||||||||
list.deselect(4); | ||||||||||||
list.deselect(5); | ||||||||||||
list.deselect(6); | ||||||||||||
list.deselect(7); | ||||||||||||
list.deselect(8); | ||||||||||||
|
||||||||||||
int selectIndex = 0; | ||||||||||||
int visibleIndex = 0; | ||||||||||||
|
||||||||||||
if (currentState.getScrollMoved()) { | ||||||||||||
if (currentState.getKeyID() == KeyEvent.VK_PAGE_UP || | ||||||||||||
currentState.getKeyID() == KeyEvent.VK_HOME) { | ||||||||||||
selectIndex = 8; | ||||||||||||
visibleIndex = 8; | ||||||||||||
} else if (currentState.getKeyID() == KeyEvent.VK_PAGE_DOWN || | ||||||||||||
currentState.getKeyID() == KeyEvent.VK_END) { | ||||||||||||
list.deselect(0); | ||||||||||||
list.deselect(1); | ||||||||||||
list.deselect(2); | ||||||||||||
list.deselect(3); | ||||||||||||
list.deselect(4); | ||||||||||||
list.deselect(5); | ||||||||||||
list.deselect(6); | ||||||||||||
list.deselect(7); | ||||||||||||
list.deselect(8); | ||||||||||||
|
||||||||||||
int selectIndex = 0; | ||||||||||||
int visibleIndex = 0; | ||||||||||||
|
||||||||||||
if (currentState.getScrollMoved()) { | ||||||||||||
if (currentState.getKeyID() == KeyEvent.VK_PAGE_UP || | ||||||||||||
currentState.getKeyID() == KeyEvent.VK_HOME) { | ||||||||||||
selectIndex = 8; | ||||||||||||
visibleIndex = 8; | ||||||||||||
} else if (currentState.getKeyID() == KeyEvent.VK_PAGE_DOWN || | ||||||||||||
currentState.getKeyID() == KeyEvent.VK_END) { | ||||||||||||
selectIndex = 0; | ||||||||||||
visibleIndex = 0; | ||||||||||||
} | ||||||||||||
} else { | ||||||||||||
if (currentState.getKeyID() == KeyEvent.VK_PAGE_UP || | ||||||||||||
currentState.getKeyID() == KeyEvent.VK_HOME) { | ||||||||||||
if (currentState.getSelectedMoved()) { | ||||||||||||
selectIndex = 1; | ||||||||||||
} else { | ||||||||||||
selectIndex = 0; | ||||||||||||
visibleIndex = 0; | ||||||||||||
} | ||||||||||||
} else { | ||||||||||||
if (currentState.getKeyID() == KeyEvent.VK_PAGE_UP || | ||||||||||||
currentState.getKeyID() == KeyEvent.VK_HOME) { | ||||||||||||
if (currentState.getSelectedMoved()) { | ||||||||||||
selectIndex = 1; | ||||||||||||
} else { | ||||||||||||
selectIndex = 0; | ||||||||||||
} | ||||||||||||
visibleIndex = 0; | ||||||||||||
} else if (currentState.getKeyID() == KeyEvent.VK_PAGE_DOWN || | ||||||||||||
currentState.getKeyID() == KeyEvent.VK_END) { | ||||||||||||
if (currentState.getSelectedMoved()) { | ||||||||||||
selectIndex = 7; | ||||||||||||
} else { | ||||||||||||
selectIndex = 8; | ||||||||||||
} | ||||||||||||
visibleIndex = 8; | ||||||||||||
visibleIndex = 0; | ||||||||||||
} else if (currentState.getKeyID() == KeyEvent.VK_PAGE_DOWN || | ||||||||||||
currentState.getKeyID() == KeyEvent.VK_END) { | ||||||||||||
if (currentState.getSelectedMoved()) { | ||||||||||||
selectIndex = 7; | ||||||||||||
} else { | ||||||||||||
selectIndex = 8; | ||||||||||||
} | ||||||||||||
visibleIndex = 8; | ||||||||||||
} | ||||||||||||
list.select(selectIndex); | ||||||||||||
list.makeVisible(visibleIndex); | ||||||||||||
}); | ||||||||||||
} | ||||||||||||
list.select(selectIndex); | ||||||||||||
list.makeVisible(visibleIndex); | ||||||||||||
|
||||||||||||
|
||||||||||||
r.delay(10); | ||||||||||||
r.waitForIdle(); | ||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
/* | ||
* Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. | ||
* Copyright (c) 2013, 2024, 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 | ||
|
@@ -34,15 +34,21 @@ public final class ButtonRepaint extends Button { | |
|
||
public static void main(final String[] args) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you expand imports, remove |
||
for (int i = 0; i < 10; ++i) { | ||
final Frame frame = new Frame(); | ||
frame.setSize(300, 300); | ||
frame.setLocationRelativeTo(null); | ||
ButtonRepaint button = new ButtonRepaint(); | ||
frame.add(button); | ||
frame.setVisible(true); | ||
sleep(); | ||
button.test(); | ||
frame.dispose(); | ||
Frame frame = null; | ||
try { | ||
frame = new Frame(); | ||
frame.setSize(300, 300); | ||
frame.setLocationRelativeTo(null); | ||
ButtonRepaint button = new ButtonRepaint(); | ||
frame.add(button); | ||
frame.setVisible(true); | ||
sleep(); | ||
button.test(); | ||
} finally { | ||
if (frame != null) { | ||
frame.dispose(); | ||
} | ||
} | ||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
/* | ||
* Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. | ||
* Copyright (c) 2013, 2024, 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 | ||
|
@@ -33,15 +33,21 @@ public final class CheckboxRepaint extends Checkbox { | |
|
||
public static void main(final String[] args) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you expand imports, remove |
||
for (int i = 0; i < 10; ++i) { | ||
final Frame frame = new Frame(); | ||
frame.setSize(300, 300); | ||
frame.setLocationRelativeTo(null); | ||
CheckboxRepaint checkbox = new CheckboxRepaint(); | ||
frame.add(checkbox); | ||
frame.setVisible(true); | ||
sleep(); | ||
checkbox.test(); | ||
frame.dispose(); | ||
Frame frame = null; | ||
try { | ||
frame = new Frame(); | ||
frame.setSize(300, 300); | ||
frame.setLocationRelativeTo(null); | ||
CheckboxRepaint checkbox = new CheckboxRepaint(); | ||
frame.add(checkbox); | ||
frame.setVisible(true); | ||
sleep(); | ||
checkbox.test(); | ||
} finally { | ||
if (frame != null) { | ||
frame.dispose(); | ||
} | ||
} | ||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -36,15 +36,21 @@ public final class LabelRepaint extends Label { | |
|
||
public static void main(final String[] args) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you remove |
||
for (int i = 0; i < 10; ++i) { | ||
final Frame frame = new Frame(); | ||
frame.setSize(300, 300); | ||
frame.setLocationRelativeTo(null); | ||
LabelRepaint label = new LabelRepaint(); | ||
frame.add(label); | ||
frame.setVisible(true); | ||
sleep(); | ||
label.test(); | ||
frame.dispose(); | ||
Frame frame = null; | ||
try { | ||
frame = new Frame(); | ||
frame.setSize(300, 300); | ||
frame.setLocationRelativeTo(null); | ||
LabelRepaint label = new LabelRepaint(); | ||
frame.add(label); | ||
frame.setVisible(true); | ||
sleep(); | ||
label.test(); | ||
} finally { | ||
if (frame != null) { | ||
frame.dispose(); | ||
} | ||
} | ||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,22 +34,27 @@ | |
*/ | ||
public final class ListRepaint extends List { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you remove |
||
|
||
static ListRepaint listRepaint; | ||
static Frame frame; | ||
|
||
public static void main(final String[] args) throws Exception { | ||
public static void main(final String[] args) { | ||
for (int i = 0; i < 10; ++i) { | ||
Frame frame = null; | ||
try { | ||
EventQueue.invokeLater(ListRepaint::createAndShowGUI); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The purpose of the test is to run some methods of the AWT component on the main thread and check if it will be refreshed on the EDT. It was changed here. It is a product bug: https://bugs.openjdk.org/browse/JDK-8201307. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, the previous update has changed the logic of the test so that it no longer reproduces the original problem for which it was written, namely JDK-7090424. |
||
frame = new Frame(); | ||
frame.setSize(300, 300); | ||
frame.setLocationRelativeTo(null); | ||
ListRepaint list = new ListRepaint(); | ||
list.add("1"); | ||
list.add("2"); | ||
list.add("3"); | ||
list.add("4"); | ||
list.select(0); | ||
frame.add(list); | ||
frame.setVisible(true); | ||
sleep(); | ||
EventQueue.invokeAndWait(listRepaint::test); | ||
list.test(); | ||
} finally { | ||
EventQueue.invokeAndWait(() -> { | ||
if (frame != null) { | ||
frame.dispose(); | ||
frame = null; | ||
} | ||
}); | ||
if (frame != null) { | ||
frame.dispose(); | ||
} | ||
} | ||
} | ||
} | ||
|
@@ -61,22 +66,6 @@ private static void sleep() { | |
} | ||
} | ||
|
||
static void createAndShowGUI() { | ||
frame = new Frame(); | ||
frame.setSize(300, 300); | ||
frame.setLocationRelativeTo(null); | ||
|
||
listRepaint = new ListRepaint(); | ||
listRepaint.add("1"); | ||
listRepaint.add("2"); | ||
listRepaint.add("3"); | ||
listRepaint.add("4"); | ||
listRepaint.select(0); | ||
|
||
frame.add(listRepaint); | ||
frame.setVisible(true); | ||
} | ||
|
||
@Override | ||
public void paint(final Graphics g) { | ||
super.paint(g); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,7 +31,10 @@ | |
|
||
import java.awt.Point; | ||
import java.awt.Robot; | ||
import java.awt.event.ActionEvent; | ||
import java.awt.event.ActionListener; | ||
import java.awt.event.InputEvent; | ||
|
||
import javax.swing.JButton; | ||
import javax.swing.JFrame; | ||
import javax.swing.SwingUtilities; | ||
|
@@ -53,10 +56,8 @@ public static void main(String[] args) throws Exception { | |
frame = new JFrame("bug4490179"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of three volatile fields — SwingUtilities.invokeAndWait(() -> {
Point loc = button.getLocationOnScreen();
Dimension size = button.getSize();
pt = new Point(loc.x + size.width / 2,
loc.y + size.height / 2);
}); I think this better conveys the idea, less variables / fields to track in your mind. |
||
button = new JButton("Button"); | ||
frame.getContentPane().add(button); | ||
button.addActionListener(e -> { | ||
if ((e.getModifiers() & InputEvent.BUTTON1_MASK) | ||
!= InputEvent.BUTTON1_MASK) { | ||
System.out.println("Status: Failed"); | ||
button.addActionListener(new ActionListener() { | ||
public void actionPerformed(ActionEvent e) { | ||
passed = false; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe use two if ((e.getModifiers() & InputEvent.BUTTON1_MASK)
!= InputEvent.BUTTON1_MASK) {
wrongMouseButton.countDown();
} else {
mouseButton1.countDown();
} Then in main: robot.mouseRelease(InputEvent.BUTTON3_DOWN_MASK);
// robot.delay(3000);
if (wrongMouseButton.await(2, TimeUnit.SECONDS)) {
// Restore robot state
robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
throw new RuntimeException("ActionEvent delivered from mouse button 3");
}
robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
if (mouseButton1.await(2, TimeUnit.SECONDS)) {
throw new RuntimeException("ActionEvent not delivered from mouse button 1");
} |
||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The updated code here is correct; Alexander has restored the condition that was present in the original test which was closed-source and manual. However, it may be not necessary. Instead, the test should fail immediately if |
||
}); | ||
|
@@ -81,10 +82,11 @@ public static void main(String[] args) throws Exception { | |
robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); | ||
robot.mousePress(InputEvent.BUTTON3_DOWN_MASK); | ||
robot.mouseRelease(InputEvent.BUTTON3_DOWN_MASK); | ||
robot.delay(3000); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The test was discussed here. It seems that the purpose of the test is to check that no events will be generated while the left button is pressed, so we should check that "ActionListener" is not triggered and then release the mouse button. |
||
boolean result = passed; | ||
robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); | ||
robot.delay(500); | ||
|
||
if (!passed) { | ||
if (!result) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps, we should ensure that an |
||
throw new RuntimeException("Test Failed"); | ||
} | ||
} finally { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -88,7 +88,11 @@ public void run() { | |
robot.waitForIdle(); | ||
robot.delay(500); | ||
|
||
SwingUtilities.invokeAndWait(() -> frame.dispose()); | ||
SwingUtilities.invokeAndWait(() -> { | ||
if (frame != null) { | ||
frame.dispose(); | ||
} | ||
}); | ||
|
||
if (!bMenuSelected) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you also expand imports and remove |
||
throw new RuntimeException("shortcuts on menus do not work"); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
probably unnecessary but some of these fields might be used on different threads, so just in case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Likely, it's unnecessary. I don't mind though.
I'd rather refactor the test to simplify it. Using
CountDownLatch
or other synchronisation primitives instead of wait/notify. I submitted JDK-8339791. Feel free to re-assign if you like.