Skip to content

Commit ba8e23a

Browse files
sshelomentsevtsarn
authored andcommitted
JBR-9563 Add test to verify that new threads aren't spawned infinitely
1 parent 4e04a3d commit ba8e23a

1 file changed

Lines changed: 136 additions & 0 deletions

File tree

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/*
2+
* Copyright 2000-2025 JetBrains s.r.o.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
import javax.swing.*;
25+
import java.awt.*;
26+
import java.awt.event.AWTEventListener;
27+
import java.awt.event.WindowEvent;
28+
import java.util.*;
29+
import java.util.List;
30+
import java.util.concurrent.*;
31+
import java.util.concurrent.atomic.AtomicReference;
32+
import java.util.stream.Collectors;
33+
34+
/* @test
35+
@summary Check if threads aren't spawned indefinitely after creating a window
36+
@key headful
37+
@run main BugJBR9563
38+
*/
39+
public class BugJBR9563 {
40+
public static void main(String[] args) throws Exception {
41+
CountDownLatch windowOpened = new CountDownLatch(1);
42+
AtomicReference<JFrame> frameRef = new AtomicReference<>();
43+
44+
AWTEventListener listener = e -> {
45+
if (e.getID() == WindowEvent.WINDOW_OPENED) {
46+
Window w = ((WindowEvent) e).getWindow();
47+
if (w instanceof JFrame f && "Test".equals(f.getTitle())) {
48+
frameRef.set(f);
49+
windowOpened.countDown();
50+
}
51+
}
52+
};
53+
Toolkit.getDefaultToolkit().addAWTEventListener(listener, AWTEvent.WINDOW_EVENT_MASK);
54+
55+
try {
56+
SwingUtilities.invokeLater(() -> {
57+
JFrame f = new JFrame("Test");
58+
f.add(new JTextField(30));
59+
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); // unchanged
60+
f.pack();
61+
f.setLocationRelativeTo(null);
62+
f.setVisible(true);
63+
});
64+
65+
Assert(windowOpened.await(5, TimeUnit.SECONDS), "Window did not open in time");
66+
67+
EventQueue.invokeAndWait(() -> { /* no-op */ });
68+
69+
Robot robot = new Robot();
70+
robot.setAutoWaitForIdle(true);
71+
robot.waitForIdle(); // waits for paints/repaints to finish
72+
73+
Toolkit.getDefaultToolkit().sync();
74+
75+
JFrame f = frameRef.get();
76+
Assert(f != null, "Frame is not captured");
77+
78+
79+
Set<String> threadsAfterRendering = getCurrentThreads();
80+
System.out.println("Threads after rendering: " + threadsAfterRendering);
81+
82+
Callable<Boolean> task = () -> {
83+
System.out.println("Check threads state...");
84+
Set<String> threads = getCurrentThreads();
85+
List<String> newThreads = threads.stream().filter(it -> !threadsAfterRendering.contains(it)).toList();
86+
87+
System.out.println("New threads list: " + newThreads);
88+
return newThreads.isEmpty();
89+
};
90+
91+
List<Boolean> results = Collections.synchronizedList(new ArrayList<>());
92+
93+
for (int i = 0; i < 10; i++) {
94+
System.out.println("Check threads state...");
95+
Set<String> threads = getCurrentThreads();
96+
List<String> newThreads = threads.stream().filter(it -> !threadsAfterRendering.contains(it)).toList();
97+
98+
System.out.println("New threads list: " + newThreads);
99+
100+
results.add(newThreads.isEmpty());
101+
Thread.sleep(1000);
102+
}
103+
104+
boolean testResult = results.stream().reduce(true, (a, b) -> a && b);
105+
if (testResult) {
106+
System.out.println("Test passed");
107+
} else {
108+
System.out.println("Test failed");
109+
throw new RuntimeException("Test failed");
110+
}
111+
} finally {
112+
Toolkit.getDefaultToolkit().removeAWTEventListener(listener);
113+
JFrame f = frameRef.get();
114+
if (f != null) {
115+
EventQueue.invokeAndWait(f::dispose); // Dispose on EDT
116+
}
117+
}
118+
}
119+
120+
private static Set<String> getCurrentThreads() {
121+
Map<Thread, StackTraceElement[]> allThreads = Thread.getAllStackTraces();
122+
123+
return allThreads
124+
.keySet()
125+
.stream()
126+
.map(t -> t.threadId() + " " + t.getName())
127+
.collect(Collectors.toSet());
128+
}
129+
130+
private static void Assert(boolean condition, String str) {
131+
if (!condition) {
132+
System.err.println(" ASSERT FAILED: " + str);
133+
throw new RuntimeException("Assert Failed: " + str);
134+
}
135+
}
136+
}

0 commit comments

Comments
 (0)