001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.openide.explorer.view;
043:
044: import java.awt.BorderLayout;
045: import java.awt.event.ActionEvent;
046: import java.awt.event.ActionListener;
047: import java.beans.PropertyChangeListener;
048: import java.lang.ref.WeakReference;
049: import java.util.ArrayList;
050: import java.util.List;
051: import javax.swing.JComponent;
052: import javax.swing.JPanel;
053: import javax.swing.SwingUtilities;
054: import javax.swing.Timer;
055: import junit.textui.TestRunner;
056: import org.netbeans.junit.NbTestCase;
057: import org.netbeans.junit.NbTestSuite;
058: import org.openide.explorer.ExplorerManager;
059: import org.openide.explorer.ExplorerPanel;
060: import org.openide.loaders.DataFilter;
061: import org.openide.loaders.RepositoryNodeFactory;
062: import org.openide.nodes.AbstractNode;
063: import org.openide.nodes.Children;
064: import org.openide.nodes.Node;
065: import org.openide.nodes.PropertySupport;
066: import org.openide.nodes.Sheet;
067: import org.openide.windows.TopComponent;
068:
069: /** Tests for TreeTableView.
070: *
071: * @author Dafe Simonek
072: */
073: public class TTVTest extends NbTestCase {
074:
075: private TreeTableView ttv;
076: private ExplorerPanel ep;
077: private NodeHolderProperty[] props;
078: private NodeStructure nodeStructure;
079: private WeakReference[] weakNodes;
080: private int result;
081:
082: public TTVTest(String testName) {
083: super (testName);
084: }
085:
086: public static void main(java.lang.String[] args) {
087: TestRunner.run(new NbTestSuite(TTVTest.class));
088: //new TTVTest("bleble").testNodesReleasing();
089: }
090:
091: public void testNodesReleasing() {
092: SwingUtilities.invokeLater(new Runnable() {
093: public void run() {
094: fillAndShowTTV();
095: // wait for a while to be sure that TTV was completely painted
096: // and references between property panels -> properties -> nodes
097: // established
098: Timer timer = new Timer(5000, new ActionListener() {
099: public void actionPerformed(ActionEvent evt) {
100: TTVTest.this .cleanAndCheckTTV();
101: }
102: });
103: timer.setRepeats(false);
104: timer.start();
105: }
106: });
107: // wait for results
108: synchronized (this ) {
109: try {
110: wait();
111: } catch (InterruptedException exc) {
112: fail("Test was interrupted somehow.");
113: }
114: }
115: // result needn't be synced, was set before we were notified
116: if (result > 0) {
117: System.out
118: .println("OK, TreeTableView released nodes after "
119: + result + " GC cycles");
120: } else {
121: System.out
122: .println("TreeTableView leaks memory! Nodes were not freed even after 10 GC cycles.");
123: fail("TreeTableView leaks memory! Nodes were not freed even after 10 GC cycles.");
124: }
125: }
126:
127: private void fillAndShowTTV() {
128: ttv = createTTV();
129: props = createProperties();
130: nodeStructure = createNodeStructure(props);
131: setupTTV(nodeStructure.rootNode, props);
132: showTTV();
133: weakNodes = createWeakNodes(nodeStructure.childrenNodes);
134: }
135:
136: private int repaintCount = 0;
137: private Timer repaintTimer;
138:
139: private void cleanAndCheckTTV() {
140: // make nodes and props gc'able
141: replaceTTVContent();
142: nodeStructure = null;
143: props = null;
144: // assure that weak hash map cache in TreeViewCell is busy a bit,
145: // so that it really releases refs to its values
146: repaintTimer = new Timer(1000, new ActionListener() {
147: public void actionPerformed(ActionEvent evt) {
148: if (repaintCount < 10) {
149: ep.invalidate();
150: ep.validate();
151: ep.repaint();
152: repaintCount++;
153: // test if nodes were released correctly
154: // invokeLater so that it comes really after explorer
155: // panel repaint
156: SwingUtilities.invokeLater(new Runnable() {
157: public void run() {
158: System.gc();
159: if (checkWeakRefs(weakNodes)) {
160: repaintTimer.stop();
161: result = repaintCount;
162: // wake up testNodeReleasing method, so that it can finish properly
163: synchronized (TTVTest.this ) {
164: TTVTest.this .notifyAll();
165: }
166: } else {
167: System.out
168: .println("Refs still alive after GC #"
169: + repaintCount);
170: }
171: }
172: });
173: } else {
174: repaintTimer.stop();
175: result = -1;
176: // wake up testNodeReleasing method, so that it can finish properly
177: synchronized (TTVTest.this ) {
178: TTVTest.this .notifyAll();
179: }
180: }
181: }
182: });
183: repaintTimer.start();
184: }
185:
186: private boolean checkWeakRefs(WeakReference[] weakNodes) {
187: for (int j = 0; j < weakNodes.length; j++) {
188: if (weakNodes[j].get() != null) {
189: return false;
190: }
191: }
192: return true;
193: }
194:
195: private TreeTableView createTTV() {
196: return new TreeTableView();
197: }
198:
199: private static NodeHolderProperty[] createProperties() {
200: return new NodeHolderProperty[] {
201: new NodeHolderProperty("boolean_prop", Boolean.TYPE, // NOI18N
202: "boolean prop.", "Short desc") { // NOI18N
203: public Object getValue() {
204: return Boolean.TRUE;
205: }
206: }, new NodeHolderProperty("string_prop", String.class, // NOI18N
207: "string prop", "Test string prop") { // NOI18N
208: public Object getValue() {
209: return "value"; // NOI18N
210: }
211: } };
212: }
213:
214: private static final class NodeStructure {
215: public Node[] childrenNodes;
216: public Node rootNode;
217: }
218:
219: /** Specialized property that will hold reference to any node method
220: * holdNode was called on.
221: */
222: private static abstract class NodeHolderProperty extends
223: PropertySupport.ReadOnly {
224: private Node heldNode;
225:
226: NodeHolderProperty(String propName, Class propClass,
227: String name, String hint) {
228: super (propName, propClass, name, hint);
229: }
230:
231: public void holdNode(Node node) {
232: heldNode = node;
233: }
234:
235: }
236:
237: private NodeStructure createNodeStructure(NodeHolderProperty[] props) {
238: NodeStructure createdData = new NodeStructure();
239: createdData.childrenNodes = new Node[100];
240: Children rootChildren = new Children.Array();
241: createdData.rootNode = new AbstractNode(rootChildren);
242: createdData.rootNode.setDisplayName("Root test node");
243: for (int i = 0; i < 100; i++) {
244: Node newNode = new TestNode();
245: newNode.setDisplayName("node #" + i);
246: createdData.childrenNodes[i] = newNode;
247: }
248: rootChildren.add(createdData.childrenNodes);
249: return createdData;
250: }
251:
252: private static final class TestNode extends AbstractNode {
253:
254: TestNode() {
255: super (Children.LEAF);
256: }
257:
258: public Sheet createSheet() {
259: Sheet s = Sheet.createDefault();
260: Sheet.Set ss = s.get(Sheet.PROPERTIES);
261: NodeHolderProperty[] props = createProperties();
262: ss.put(props);
263: wireNode(this , props);
264: return s;
265: }
266:
267: } // end of TestNode
268:
269: private static void wireNode(Node node, NodeHolderProperty[] props) {
270: for (int i = 0; i < props.length; i++) {
271: props[i].holdNode(node);
272: }
273: }
274:
275: private void setupTTV(Node rootNode, Node.Property[] props) {
276: ttv.setProperties(props);
277: ttv.setRootVisible(false);
278:
279: ExplorerManager em = new ExplorerManager();
280: em.setRootContext(rootNode);
281: ep = new ExplorerPanel(em);
282: ep.add(ttv, BorderLayout.CENTER);
283: }
284:
285: private void replaceTTVContent() {
286: Children children = new Children.Array();
287: children.add(new Node[] { new TestNode() });
288:
289: ep.getExplorerManager().setRootContext(
290: new AbstractNode(children));
291: }
292:
293: private void showTTV() {
294: ep.open();
295: }
296:
297: private WeakReference[] createWeakNodes(Node[] nodes) {
298: WeakReference[] weakNodes = new WeakReference[nodes.length];
299: for (int i = 0; i < nodes.length; i++) {
300: weakNodes[i] = new WeakReference(nodes[i]);
301: }
302: return weakNodes;
303: }
304:
305: }
|