1 /* 2 * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. 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 java.awt.Component; 25 import java.awt.Container; 26 import java.awt.EventQueue; 27 import java.awt.FlowLayout; 28 import java.util.ArrayList; 29 import java.util.List; 30 import java.util.concurrent.TimeUnit; 31 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; 69 import javax.swing.UIManager.LookAndFeelInfo; 70 71 import jdk.test.lib.process.OutputAnalyzer; 72 import jdk.test.lib.process.ProcessTools; 73 74 import static javax.swing.UIManager.getInstalledLookAndFeels; 75 76 /** 77 * @test 78 * @key headful 79 * @bug 8134947 8253977 8240709 80 * @library /test/lib 81 * @run main/timeout=450/othervm UnninstallUIMemoryLeaks 82 */ 83 public final class UnninstallUIMemoryLeaks { 84 85 private static JFrame frame; 86 main(String[] args)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(); 94 tasks.add(runProcess(laf)); 95 } 96 for (Process p : tasks) { 97 if (!p.waitFor(end - System.nanoTime(), TimeUnit.NANOSECONDS)) { 98 p.destroyForcibly(); 99 } 100 } 101 for (Process task : tasks) { 102 new OutputAnalyzer(task).shouldHaveExitValue(0) 103 .stderrShouldBeEmpty(); 104 } 105 return; 106 } 107 108 try { 109 createGUI(); 110 long end = System.nanoTime() + TimeUnit.SECONDS.toNanos(350); 111 SwingUtilities.invokeAndWait(() -> { 112 while (end > System.nanoTime()) { 113 SwingUtilities.updateComponentTreeUI(frame); 114 } 115 checkListenersCount(frame); 116 }); 117 } finally { 118 if (frame != null) {EventQueue.invokeAndWait(frame::dispose);} 119 } 120 } 121 createGUI()122 private static void createGUI() throws Exception { 123 EventQueue.invokeAndWait(() -> { 124 frame = new JFrame(); 125 //TODO we sometimes generate unnecessary repaint events 126 frame.setIgnoreRepaint(true); 127 frame.setLayout(new FlowLayout()); 128 129 frame.add(new JButton("JButton")); 130 frame.add(new JCheckBox("JCheckBox")); 131 frame.add(new JComboBox<>()); 132 frame.add(new JEditorPane()); 133 frame.add(new JFormattedTextField("JFormattedTextField")); 134 frame.add(new JLabel("label")); 135 frame.add(new JPanel()); 136 frame.add(new JPasswordField("JPasswordField")); 137 frame.add(new JProgressBar()); 138 frame.add(new JRadioButton("JRadioButton")); 139 frame.add(new JScrollBar()); 140 frame.add(new JScrollPane()); 141 frame.add(new JSeparator()); 142 frame.add(new JSlider()); 143 frame.add(new JSpinner()); 144 frame.add(new JSplitPane()); 145 frame.add(new JTabbedPane()); 146 frame.add(new JTable()); 147 frame.add(new JTextArea("JTextArea")); 148 frame.add(new JTextField("JTextField")); 149 frame.add(new JTextPane()); 150 frame.add(new JToggleButton()); 151 frame.add(new JToolBar()); 152 frame.add(new JToolTip()); 153 frame.add(new JTree()); 154 frame.add(new JViewport()); 155 156 final JMenuBar bar = new JMenuBar(); 157 final JMenu menu1 = new JMenu("menu1"); 158 final JMenu menu2 = new JMenu("menu2"); 159 menu1.add(new JMenuItem("menuitem")); 160 menu2.add(new JCheckBoxMenuItem("JCheckBoxMenuItem")); 161 menu2.add(new JRadioButtonMenuItem("JRadioButtonMenuItem")); 162 bar.add(menu1); 163 bar.add(menu2); 164 frame.setJMenuBar(bar); 165 166 final String[] data = {"one", "two", "three", "four"}; 167 final JList<String> list = new JList<>(data); 168 frame.add(list); 169 170 final JDesktopPane pane = new JDesktopPane(); 171 final JInternalFrame internalFrame = new JInternalFrame(); 172 internalFrame.setBounds(10, 10, 130, 130); 173 internalFrame.setVisible(true); 174 pane.add(internalFrame); 175 pane.setSize(150, 150); 176 177 frame.add(pane); 178 frame.pack(); 179 frame.setSize(600, 600); 180 frame.setLocationRelativeTo(null); 181 // Commented to prevent a reference from RepaintManager 182 // frame.setVisible(true); 183 }); 184 } 185 checkListenersCount(Component comp)186 private static void checkListenersCount(Component comp) { 187 test(comp.getComponentListeners()); 188 test(comp.getFocusListeners()); 189 test(comp.getHierarchyListeners()); 190 test(comp.getHierarchyBoundsListeners()); 191 test(comp.getKeyListeners()); 192 test(comp.getMouseListeners()); 193 test(comp.getMouseMotionListeners()); 194 test(comp.getMouseWheelListeners()); 195 test(comp.getInputMethodListeners()); 196 test(comp.getPropertyChangeListeners()); 197 if (comp instanceof JComponent) { 198 test(((JComponent) comp).getAncestorListeners()); 199 test(((JComponent) comp).getVetoableChangeListeners()); 200 } 201 if (comp instanceof JMenuItem) { 202 test(((JMenuItem) comp).getMenuKeyListeners()); 203 test(((JMenuItem) comp).getMenuDragMouseListeners()); 204 } 205 if (comp instanceof JMenu) { 206 test(((JMenu) comp).getMenuListeners()); 207 } 208 if(comp instanceof Container) { 209 for(Component child: ((Container)comp).getComponents()){ 210 checkListenersCount(child); 211 } 212 } 213 } 214 215 /** 216 * Checks the count of specific listeners, assumes that the proper 217 * implementation does not use more than 20 listeners. 218 */ test(Object[] listeners)219 private static void test(Object[] listeners) { 220 int length = listeners.length; 221 if (length > 20) { 222 throw new RuntimeException("The count of listeners is: " + length); 223 } 224 } 225 runProcess(LookAndFeelInfo laf)226 private static Process runProcess(LookAndFeelInfo laf) throws Exception { 227 ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( 228 "-Dswing.defaultlaf=" + laf.getClassName(), "-mx9m", 229 "-XX:+HeapDumpOnOutOfMemoryError", 230 UnninstallUIMemoryLeaks.class.getSimpleName(), "mark"); 231 return ProcessTools.startProcess(laf.getName(), pb); 232 } 233 } 234