|
1 | 1 | /*
|
2 |
| - * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. |
| 2 | + * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. |
3 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
4 | 4 | *
|
5 | 5 | * This code is free software; you can redistribute it and/or modify it
|
|
21 | 21 | * questions.
|
22 | 22 | */
|
23 | 23 |
|
| 24 | +import java.awt.Component; |
| 25 | +import java.awt.Container; |
24 | 26 | import java.awt.EventQueue;
|
25 | 27 | import java.awt.FlowLayout;
|
| 28 | +import java.util.ArrayList; |
| 29 | +import java.util.List; |
| 30 | +import java.util.concurrent.TimeUnit; |
26 | 31 |
|
27 |
| -import javax.swing.*; |
| 32 | +import javax.swing.JButton; |
| 33 | +import javax.swing.JCheckBox; |
| 34 | +import javax.swing.JCheckBoxMenuItem; |
| 35 | +import javax.swing.JComboBox; |
| 36 | +import javax.swing.JComponent; |
| 37 | +import javax.swing.JDesktopPane; |
| 38 | +import javax.swing.JEditorPane; |
| 39 | +import javax.swing.JFormattedTextField; |
| 40 | +import javax.swing.JFrame; |
| 41 | +import javax.swing.JInternalFrame; |
| 42 | +import javax.swing.JLabel; |
| 43 | +import javax.swing.JList; |
| 44 | +import javax.swing.JMenu; |
| 45 | +import javax.swing.JMenuBar; |
| 46 | +import javax.swing.JMenuItem; |
| 47 | +import javax.swing.JPanel; |
| 48 | +import javax.swing.JPasswordField; |
| 49 | +import javax.swing.JProgressBar; |
| 50 | +import javax.swing.JRadioButton; |
| 51 | +import javax.swing.JRadioButtonMenuItem; |
| 52 | +import javax.swing.JScrollBar; |
| 53 | +import javax.swing.JScrollPane; |
| 54 | +import javax.swing.JSeparator; |
| 55 | +import javax.swing.JSlider; |
| 56 | +import javax.swing.JSpinner; |
| 57 | +import javax.swing.JSplitPane; |
| 58 | +import javax.swing.JTabbedPane; |
| 59 | +import javax.swing.JTable; |
| 60 | +import javax.swing.JTextArea; |
| 61 | +import javax.swing.JTextField; |
| 62 | +import javax.swing.JTextPane; |
| 63 | +import javax.swing.JToggleButton; |
| 64 | +import javax.swing.JToolBar; |
| 65 | +import javax.swing.JToolTip; |
| 66 | +import javax.swing.JTree; |
| 67 | +import javax.swing.JViewport; |
| 68 | +import javax.swing.SwingUtilities; |
28 | 69 | import javax.swing.UIManager.LookAndFeelInfo;
|
29 | 70 |
|
| 71 | +import jdk.test.lib.process.OutputAnalyzer; |
| 72 | +import jdk.test.lib.process.ProcessTools; |
| 73 | + |
30 | 74 | import static javax.swing.UIManager.getInstalledLookAndFeels;
|
31 | 75 |
|
32 | 76 | /**
|
33 | 77 | * @test
|
34 | 78 | * @key headful
|
35 |
| - * @bug 8134947 |
36 |
| - * @author Sergey Bylokhov |
37 |
| - * @run main/timeout=300/othervm -Xmx12m -XX:+HeapDumpOnOutOfMemoryError UnninstallUIMemoryLeaks |
| 79 | + * @bug 8134947 8253977 |
| 80 | + * @library /test/lib |
| 81 | + * @run main/timeout=450/othervm UnninstallUIMemoryLeaks |
38 | 82 | */
|
39 | 83 | public final class UnninstallUIMemoryLeaks {
|
40 | 84 |
|
41 | 85 | private static JFrame frame;
|
42 | 86 |
|
43 |
| - public static void main(final String[] args) throws Exception { |
44 |
| - try { |
45 |
| - createGUI(); |
46 |
| - for (final LookAndFeelInfo laf : getInstalledLookAndFeels()) { |
47 |
| - final String name = laf.getName(); |
| 87 | + public static void main(String[] args) throws Exception { |
| 88 | + if (args.length == 0) { |
| 89 | + long end = System.nanoTime() + TimeUnit.SECONDS.toNanos(400); |
| 90 | + // run one task per look and feel |
| 91 | + List<Process> tasks = new ArrayList<>(); |
| 92 | + for (LookAndFeelInfo laf : getInstalledLookAndFeels()) { |
| 93 | + String name = laf.getName(); |
48 | 94 | if (name.contains("OS X") || name.contains("Metal")) {
|
49 |
| - SwingUtilities.invokeAndWait(() -> setLookAndFeel(laf)); |
50 |
| - SwingUtilities.invokeAndWait(() -> { |
51 |
| - for (int i = 0; i < 4000; ++i) { |
52 |
| - SwingUtilities.updateComponentTreeUI(frame); |
53 |
| - } |
54 |
| - }); |
| 95 | + tasks.add(runProcess(laf)); |
55 | 96 | }
|
56 | 97 | }
|
| 98 | + for (Process p : tasks) { |
| 99 | + if (!p.waitFor(end - System.nanoTime(), TimeUnit.NANOSECONDS)) { |
| 100 | + p.destroyForcibly(); |
| 101 | + } |
| 102 | + } |
| 103 | + for (Process task : tasks) { |
| 104 | + new OutputAnalyzer(task).shouldHaveExitValue(0) |
| 105 | + .stderrShouldBeEmpty(); |
| 106 | + } |
| 107 | + return; |
| 108 | + } |
| 109 | + |
| 110 | + try { |
| 111 | + createGUI(); |
| 112 | + long end = System.nanoTime() + TimeUnit.SECONDS.toNanos(350); |
| 113 | + SwingUtilities.invokeAndWait(() -> { |
| 114 | + while (end > System.nanoTime()) { |
| 115 | + SwingUtilities.updateComponentTreeUI(frame); |
| 116 | + } |
| 117 | + checkListenersCount(frame); |
| 118 | + }); |
57 | 119 | } finally {
|
58 |
| - if (frame != null) { EventQueue.invokeAndWait(() -> frame.dispose()); } |
| 120 | + if (frame != null) {EventQueue.invokeAndWait(frame::dispose);} |
59 | 121 | }
|
60 | 122 | }
|
61 | 123 |
|
62 | 124 | private static void createGUI() throws Exception {
|
63 | 125 | EventQueue.invokeAndWait(() -> {
|
64 | 126 | frame = new JFrame();
|
| 127 | + //TODO we sometimes generate unnecessary repaint events |
| 128 | + frame.setIgnoreRepaint(true); |
65 | 129 | frame.setLayout(new FlowLayout());
|
66 | 130 |
|
67 | 131 | frame.add(new JButton("JButton"));
|
@@ -121,13 +185,51 @@ private static void createGUI() throws Exception {
|
121 | 185 | });
|
122 | 186 | }
|
123 | 187 |
|
124 |
| - private static void setLookAndFeel(final LookAndFeelInfo laf) { |
125 |
| - try { |
126 |
| - UIManager.setLookAndFeel(laf.getClassName()); |
127 |
| - System.out.println("LookAndFeel: " + laf.getClassName()); |
128 |
| - } catch (ClassNotFoundException | InstantiationException | |
129 |
| - UnsupportedLookAndFeelException | IllegalAccessException e) { |
130 |
| - throw new RuntimeException(e); |
| 188 | + private static void checkListenersCount(Component comp) { |
| 189 | + test(comp.getComponentListeners()); |
| 190 | + test(comp.getFocusListeners()); |
| 191 | + test(comp.getHierarchyListeners()); |
| 192 | + test(comp.getHierarchyBoundsListeners()); |
| 193 | + test(comp.getKeyListeners()); |
| 194 | + test(comp.getMouseListeners()); |
| 195 | + test(comp.getMouseMotionListeners()); |
| 196 | + test(comp.getMouseWheelListeners()); |
| 197 | + test(comp.getInputMethodListeners()); |
| 198 | + test(comp.getPropertyChangeListeners()); |
| 199 | + if (comp instanceof JComponent) { |
| 200 | + test(((JComponent) comp).getAncestorListeners()); |
| 201 | + test(((JComponent) comp).getVetoableChangeListeners()); |
| 202 | + } |
| 203 | + if (comp instanceof JMenuItem) { |
| 204 | + test(((JMenuItem) comp).getMenuKeyListeners()); |
| 205 | + test(((JMenuItem) comp).getMenuDragMouseListeners()); |
| 206 | + } |
| 207 | + if (comp instanceof JMenu) { |
| 208 | + test(((JMenu) comp).getMenuListeners()); |
| 209 | + } |
| 210 | + if(comp instanceof Container) { |
| 211 | + for(Component child: ((Container)comp).getComponents()){ |
| 212 | + checkListenersCount(child); |
| 213 | + } |
131 | 214 | }
|
132 | 215 | }
|
| 216 | + |
| 217 | + /** |
| 218 | + * Checks the count of specific listeners, assumes that the proper |
| 219 | + * implementation does not use more than 20 listeners. |
| 220 | + */ |
| 221 | + private static void test(Object[] listeners) { |
| 222 | + int length = listeners.length; |
| 223 | + if (length > 20) { |
| 224 | + throw new RuntimeException("The count of listeners is: " + length); |
| 225 | + } |
| 226 | + } |
| 227 | + |
| 228 | + private static Process runProcess(LookAndFeelInfo laf) throws Exception { |
| 229 | + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( |
| 230 | + "-Dswing.defaultlaf=" + laf.getClassName(), "-mx9m", |
| 231 | + "-XX:+HeapDumpOnOutOfMemoryError", |
| 232 | + UnninstallUIMemoryLeaks.class.getSimpleName(), "mark"); |
| 233 | + return ProcessTools.startProcess(laf.getName(), pb); |
| 234 | + } |
133 | 235 | }
|
0 commit comments