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-2007 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: package org.openide.nodes;
042:
043: import java.awt.EventQueue;
044: import java.beans.*;
045: import java.util.*;
046: import junit.framework.TestCase;
047: import org.openide.nodes.*;
048: import org.openide.nodes.NodeAdapter;
049: import org.openide.util.NbBundle;
050:
051: /** Test for AsynchChildren, ChildFactory and SynchChildren.
052: *
053: * @author Tim Boudreau
054: */
055: public class ChildFactoryTest extends TestCase {
056:
057: public ChildFactoryTest(String name) {
058: super (name);
059: }
060:
061: private ProviderImpl factory;
062:
063: private BatchProviderImpl factory2;
064: private AsynchChildren kids2;
065: private Node node2;
066: private AsynchChildren kids;
067: private Node node;
068:
069: protected @Override
070: void setUp() throws Exception {
071: factory = new ProviderImpl();
072: kids = new AsynchChildren<String>(factory);
073: node = new AbstractNode(kids);
074:
075: factory2 = new BatchProviderImpl();
076: kids2 = new AsynchChildren<String>(factory2);
077: node2 = new AbstractNode(kids2);
078: }
079:
080: public void testChildrenCreate() {
081: System.out.println("testChildrenCreate");
082: ChildFactory<?> f = new ProviderImpl();
083: Children kids = Children.create(f, true);
084: assertTrue(kids instanceof AsynchChildren);
085:
086: ChildFactory<?> ff = new ProviderImpl();
087: Children kids2 = Children.create(ff, false);
088: assertFalse(kids2 instanceof AsynchChildren);
089: assertTrue(kids2 instanceof SynchChildren);
090:
091: RuntimeException e = null;
092: Children kids3 = null;
093: try {
094: kids3 = Children.create(ff, true);
095: } catch (RuntimeException ex) {
096: e = ex;
097: }
098: assertNull(kids3);
099: assertNotNull("Exception should have been thrown creating two "
100: + "Children objects over the same ChildFactory", e);
101: }
102:
103: //A word of caution re adding tests:
104: //Almost anything (getNodes(), justCreateNodes(), etc. can trigger a
105: //fresh call to Children.addNotify(). Any test that expects a synchronous
106: //change in the child nodes as a result of having triggered a call
107: //to setKeys() is probably testing a race condition, not the behavior
108: //of the children implementation
109:
110: public void testGetNodesWaits() throws Exception {
111: System.out.println("testGetNodesWaits");
112: factory.wait = false;
113: kids.getNodes(false);
114: synchronized (factory.lock) {
115: factory.lock.wait(300);
116: }
117: Thread.currentThread().yield();
118: new NL(node);
119: Node[] n = kids.getNodes(true);
120: assertEquals(4, n.length);
121: }
122:
123: public void testInitialNodeIsWaitNode() throws Exception {
124: System.out.println("testInitialNodeIsWaitNode");
125: factory.wait = true;
126: kids.addNotify();
127: Node[] n = kids.getNodes(false);
128: factory.wait = false;
129: assertEquals(1, n.length);
130: assertEquals(NbBundle.getMessage(AsynchChildren.class,
131: "LBL_WAIT"), n[0].getDisplayName());
132: factory.wait = false;
133: synchronized (factory) {
134: factory.wait(2000);
135: }
136: for (int i = 0; i < 5 && n.length != 4; i++) {
137: n = kids.getNodes(true);
138: Thread.currentThread().yield();
139: }
140: assertEquals(4, n.length);
141: }
142:
143: public void testBatch() throws Exception {
144: System.out.println("testBatch");
145: kids2.addNotify();
146: Thread.currentThread().yield();
147: synchronized (factory2.lock) {
148: factory2.lock.notifyAll();
149: }
150: new NL(node2);
151: Node[] n = n = kids2.getNodes(true);
152: assertEquals(4, n.length);
153: assertEquals(2, factory2.callCount);
154: }
155:
156: public void testSynchChildren() throws Exception {
157: System.out.println("testSynchChildren");
158: final SynchProviderImpl factory = new SynchProviderImpl();
159: final Children ch = Children.create(factory, false);
160: assertTrue(ch instanceof SynchChildren);
161: factory.assertCreateKeysNotCalled();
162: factory.assertCreateNodesForKeyNotCalled();
163: final Node nd = new AbstractNode(ch);
164: NodeAdapter adap = new NodeAdapter() {
165: };
166: nd.addNodeListener(adap);
167:
168: EventQueue.invokeAndWait(new Runnable() {
169: public void run() {
170: ch.getNodes(true);
171: }
172: });
173: ((SynchChildren) ch).active = true;
174: synchronized (factory) {
175: factory.wait(1000);
176: }
177: factory.assertCreateKeysCalled();
178: factory.assertCreateNodesForKeyCalled();
179: Node[] nodes = nd.getChildren().getNodes(true);
180: assertEquals(factory.CONTENTS1.size(), nodes.length);
181: int ix = 0;
182: for (String s : factory.CONTENTS1) {
183: assertEquals(s, nodes[ix].getName());
184: ix++;
185: }
186: factory.switchChildren();
187: nodes = nd.getChildren().getNodes(true);
188: assertEquals(factory.CONTENTS2.size(), nodes.length);
189: ix = 0;
190: for (String s : factory.CONTENTS2) {
191: assertEquals(s, nodes[ix].getName());
192: ix++;
193: }
194: }
195:
196: public void testCancel() throws Exception {
197: System.out.println("testCancel");
198: Thread.interrupted();
199: factory.wait = true;
200: kids.addNotify();
201: Thread.currentThread().yield();
202: synchronized (factory.lock) {
203: factory.lock.wait(500);
204: }
205: kids.removeNotify();
206: factory.wait = false;
207: synchronized (factory) {
208: factory.wait(2000);
209: }
210: assertTrue(kids.cancelled);
211: assertTrue(factory.cancelled);
212: }
213:
214: static final class ProviderImpl extends ChildFactory<String> {
215: Object lock = new Object();
216: volatile boolean wait = false;
217:
218: public @Override
219: Node[] createNodesForKey(String key) {
220: AbstractNode nd = new AbstractNode(Children.LEAF);
221: nd.setDisplayName(key);
222: return new Node[] { nd };
223: }
224:
225: boolean cancelled = false;
226:
227: public boolean createKeys(List<String> result) {
228: try {
229: while (wait) {
230: Thread.currentThread().yield();
231: }
232: synchronized (lock) {
233: lock.notifyAll();
234: }
235: if (Thread.interrupted()) {
236: cancelled = true;
237: return true;
238: }
239: result.add("A");
240: result.add("B");
241: result.add("C");
242: result.add("D");
243: if (Thread.interrupted()) {
244: cancelled = true;
245: }
246: return true;
247: } finally {
248: synchronized (this ) {
249: notifyAll();
250: }
251: }
252: }
253: }
254:
255: static final class BatchProviderImpl extends ChildFactory<String> {
256: boolean firstCycle = true;
257:
258: public @Override
259: Node[] createNodesForKey(String key) {
260: AbstractNode nd = new AbstractNode(Children.LEAF);
261: nd.setDisplayName(key);
262: return new Node[] { nd };
263: }
264:
265: Object lock = new Object();
266: int callCount = 0;
267:
268: public boolean createKeys(List<String> result) {
269: callCount++;
270: synchronized (lock) {
271: try {
272: lock.wait(500);
273: } catch (InterruptedException ex) {
274: //re-interrupt
275: Thread.currentThread().interrupt();
276: }
277: }
278: if (Thread.interrupted()) {
279: return true;
280: }
281: boolean wasFirstCycle = firstCycle;
282: if (wasFirstCycle) {
283: result.add("A");
284: result.add("B");
285: firstCycle = false;
286: return false;
287: } else {
288: result.add("C");
289: result.add("D");
290: }
291: if (Thread.interrupted()) {
292: return true;
293: }
294: synchronized (this ) {
295: notifyAll();
296: }
297: return true;
298: }
299: }
300:
301: private static final class NL implements NodeListener {
302: NL(Node n) {
303: n.addNodeListener(this );
304: try {
305: waitFor();
306: } catch (InterruptedException ex) {
307: Thread.currentThread().interrupt();
308: } catch (Exception e) {
309: throw new Error(e);
310: }
311: }
312:
313: NL() {
314:
315: }
316:
317: public void childrenAdded(NodeMemberEvent ev) {
318: go();
319: }
320:
321: public void childrenRemoved(NodeMemberEvent ev) {
322: go();
323: }
324:
325: public void childrenReordered(NodeReorderEvent ev) {
326: go();
327: }
328:
329: public void nodeDestroyed(NodeEvent ev) {
330: }
331:
332: public void propertyChange(PropertyChangeEvent arg0) {
333: }
334:
335: private void go() {
336: synchronized (this ) {
337: notifyAll();
338: }
339: }
340:
341: void waitFor() throws Exception {
342: System.err.println("Enter waitfor");
343: synchronized (this ) {
344: wait(1000);
345: }
346: }
347: }
348:
349: private static final class SynchProviderImpl extends
350: ChildFactory<String> {
351: static List<String> CONTENTS1 = Arrays
352: .<String> asList(new String[] { "One", "Two", "Three",
353: "Four" });
354: static List<String> CONTENTS2 = Arrays
355: .<String> asList(new String[] { "Five", "Six", "Seven",
356: "Eight", "Nine" });
357:
358: boolean createNodesForKeyCalled = false;
359:
360: public @Override
361: Node[] createNodesForKey(String key) {
362: createNodesForKeyCalled = true;
363: Node result = new AbstractNode(Children.LEAF);
364: result.setDisplayName(key);
365: result.setName(key);
366: return new Node[] { result };
367: }
368:
369: boolean createKeysCalled = false;
370:
371: public boolean createKeys(List<String> toPopulate) {
372: createKeysCalled = true;
373: List<String> l = switched ? CONTENTS2 : CONTENTS1;
374: toPopulate.addAll(l);
375: return true;
376: }
377:
378: void assertCreateNodesForKeyNotCalled() {
379: assertFalse(createNodesForKeyCalled);
380: }
381:
382: void assertCreateKeysNotCalled() {
383: assertFalse(createKeysCalled);
384: }
385:
386: boolean assertCreateNodesForKeyCalled() {
387: boolean result = createNodesForKeyCalled;
388: createNodesForKeyCalled = false;
389: assertTrue(result);
390: return result;
391: }
392:
393: boolean assertCreateKeysCalled() {
394: boolean result = createKeysCalled;
395: createKeysCalled = false;
396: assertTrue(result);
397: return result;
398: }
399:
400: volatile boolean switched = false;
401:
402: void switchChildren() {
403: switched = !switched;
404: refresh(true);
405: }
406: }
407: }
|