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.netbeans.modules.turbo;
043:
044: import junit.framework.TestCase;
045: import org.openide.util.Lookup;
046: import org.openide.util.lookup.InstanceContent;
047: import org.openide.util.lookup.AbstractLookup;
048: import org.openide.filesystems.FileObject;
049: import org.openide.filesystems.LocalFileSystem;
050: import org.openide.filesystems.FileSystem;
051:
052: import java.io.File;
053:
054: /**
055: * Tests Turbo
056: *
057: * @author Petr Kuzel
058: */
059: public class TurboTest extends TestCase {
060:
061: private static InstanceContent content = new InstanceContent();
062:
063: private FileSystem fs;
064:
065: private static TestEnvironment env;
066:
067: private int tearDownCounter = countTestCases();
068:
069: // per testcase static setup
070: static {
071: env = new TestEnvironment();
072: Turbo.initEnvironment(env);
073: }
074:
075: // called before every method
076: protected void setUp() throws Exception {
077:
078: // prepare simple LFS
079:
080: LocalFileSystem fs = new LocalFileSystem();
081: File tmp = new File(System.getProperty("java.io.tmpdir")
082: + File.separator + "turbo-test");
083: tmp.mkdir();
084: tmp.deleteOnExit();
085: File theFile = new File(tmp, "theFile");
086: theFile.createNewFile();
087: theFile.deleteOnExit();
088: fs.setRootDirectory(tmp);
089:
090: System.setProperty("netbeans.experimental.vcsTurboStatistics",
091: "performance");
092: this .fs = fs;
093: }
094:
095: protected void tearDown() throws Exception {
096: if (tearDownCounter-- == 1) {
097: try {
098: Turbo.getDefault().finalize();
099: } catch (Throwable t) {
100: t.printStackTrace();
101: }
102: }
103: }
104:
105: /**
106: * Once setting an atribute it must be in memory until
107: * file reference is GCed. Providers must be queried.
108: * Th event must be fired.
109: */
110: public void testHitAfterSet() throws Exception {
111: Turbo faq = Turbo.getDefault();
112:
113: FileObject fo = fs.getRoot().getFileObject("theFile");
114: AttributeListener l = new AttributeListener();
115: faq.addTurboListener(l);
116:
117: faq.writeEntry(fo, "test", "testValue");
118: assertTrue("Missing Turbo event", l.hit());
119:
120: // must not raise fail on providers read layer
121: faq.readEntry(fo, "test");
122:
123: // it shoud not fire on equel values
124: l.reset();
125: faq.writeEntry(fo, "test", "testValue");
126: assertFalse("Missing Turbo event", l.hit());
127: }
128:
129: /**
130: * <code<null</code> value serves for invalidation purposes.
131: */
132: public void testInvalidation() throws Exception {
133: Turbo faq = Turbo.getDefault();
134:
135: FileObject fo = fs.getRoot().getFileObject("theFile");
136:
137: faq.writeEntry(fo, "invalidate", "testValue");
138: faq.writeEntry(fo, "invalidate", null);
139: assertFalse(faq.isPrepared(fo, "invalidate"));
140:
141: // after reading attribute we know that is unknown, it's prepared
142: faq.readEntry(fo, "invalidate");
143: assertTrue(faq.isPrepared(fo, "invalidate"));
144: }
145:
146: private static class TestEnvironment extends Turbo.Environment {
147:
148: private Lookup l;
149:
150: public TestEnvironment() {
151: content.add(new AssertingTurboProvider());
152: l = new AbstractLookup(content);
153: }
154:
155: public Lookup getLookup() {
156: return l;
157: }
158: }
159:
160: private static class AssertingTurboProvider implements
161: TurboProvider {
162:
163: public boolean recognizesAttribute(String name) {
164: return "test".equals(name);
165: }
166:
167: public boolean recognizesEntity(Object key) {
168: return true;
169: }
170:
171: public Object readEntry(Object key, String name,
172: MemoryCache memoryCache) {
173: fail();
174: return null;
175: }
176:
177: public boolean writeEntry(Object key, String name, Object value) {
178: return true;
179: }
180: }
181:
182: private class AttributeListener implements TurboListener {
183:
184: private volatile boolean hit = false;
185:
186: public void reset() {
187: hit = false;
188: }
189:
190: public boolean hit() {
191: return hit;
192: }
193:
194: public synchronized void entryChanged(Object key, String name,
195: Object value) {
196: hit = true;
197: notifyAll();
198: }
199:
200: public synchronized void waitForHit(int timeout) {
201: long start = System.currentTimeMillis();
202: while (hit == false) {
203: try {
204: wait(timeout);
205: if (System.currentTimeMillis() - start > timeout)
206: return;
207: } catch (InterruptedException e) {
208: return;
209: }
210: }
211: }
212: }
213:
214: /**
215: * Test provider that so slow that someone else populates memory faster.
216: */
217: public void testSlowProvider() throws Exception {
218: Turbo faq = Turbo.getDefault();
219:
220: FileObject fo = fs.getRoot().getFileObject("theFile");
221:
222: SlowProvider sp = new SlowProvider();
223: content.add(sp);
224: try {
225: faq.prepareEntry(fo, "slow"); // hey a thread is spawned to load attribute on background
226:
227: // simulate faster client
228:
229: AttributeListener expecting = new AttributeListener();
230: faq.addTurboListener(expecting);
231: faq.writeEntry(fo, "slow", "result"); // expecting synchronous event
232: faq.removeTurboListener(expecting);
233: assertTrue(expecting.hit());
234:
235: // now unblock provider and let it report the same value
236:
237: AttributeListener besilent = new AttributeListener();
238: faq.addTurboListener(besilent);
239: sp.notifyReady(); // an event must not be fired because of the same value
240: besilent.waitForHit(100);
241: faq.removeTurboListener(besilent);
242:
243: // TODO it's safer to fire than to not fire it's just possibly slow
244: //assertFalse(besilent.hit());
245: } finally {
246: content.remove(sp);
247: }
248: }
249:
250: private class SlowProvider implements TurboProvider {
251:
252: volatile boolean ready;
253:
254: public boolean recognizesAttribute(String name) {
255: return "slow".equals(name);
256: }
257:
258: public boolean recognizesEntity(Object key) {
259: return true;
260: }
261:
262: public synchronized Object readEntry(Object key, String name,
263: MemoryCache memoryCache) {
264: while (ready == false) {
265: try {
266: wait();
267: } catch (InterruptedException e) {
268: break;
269: }
270: }
271: return "result";
272: }
273:
274: public boolean writeEntry(Object key, String name, Object value) {
275: // XXX Note this is coming as result of faster FAQ.writeAttribute
276: return true;
277: }
278:
279: public synchronized void notifyReady() {
280: ready = true;
281: notifyAll();
282: }
283: }
284:
285: /**
286: * Prepare must fire event on successfull prepare
287: * XXX yet undefined behaviour otherwise.
288: */
289: public void testPrepare() throws Exception {
290: Turbo faq = Turbo.getDefault();
291: FileObject fo = fs.getRoot().getFileObject("theFile");
292: PrepareProvider pp = new PrepareProvider();
293: content.add(pp);
294: try {
295:
296: AttributeListener l = new AttributeListener();
297: faq.addTurboListener(l);
298: faq.prepareEntry(fo, "prepare");
299: l.waitForHit(500);
300: faq.removeTurboListener(l);
301:
302: assertTrue(l.hit);
303:
304: // be sure that event granularity is given by attr,file pair (not just by file)
305:
306: l.reset();
307: faq.addTurboListener(l);
308: faq.prepareEntry(fo, "prepare2");
309: l.waitForHit(500);
310: faq.removeTurboListener(l);
311:
312: assertTrue(l.hit);
313: } finally {
314: content.remove(pp);
315: }
316: }
317:
318: private class PrepareProvider implements TurboProvider {
319:
320: public boolean recognizesAttribute(String name) {
321: return "prepare".equals(name) || "prepare2".equals(name);
322: }
323:
324: public boolean recognizesEntity(Object key) {
325: return true;
326: }
327:
328: public Object readEntry(Object key, String name,
329: MemoryCache memoryCache) {
330: return "done";
331: }
332:
333: public boolean writeEntry(Object key, String name, Object value) {
334: return true;
335: }
336: }
337:
338: /**
339: * Register new provider and verify that it's called
340: * without throwing an exception.
341: */
342: public void testWrongProvider() {
343: WrongProvider provider = new WrongProvider();
344:
345: Lookup.Template t = new Lookup.Template(TurboProvider.class);
346: Lookup.Result r = env.getLookup().lookup(t);
347: content.add(provider);
348: assert r.allInstances().size() == 2 : "r=" + r.allInstances();
349:
350: Turbo faq = Turbo.getDefault();
351:
352: FileObject fo = fs.getRoot().getFileObject("theFile");
353:
354: try {
355: assertNull(faq.readEntry(fo, "wrong"));
356: } catch (RuntimeException ex) {
357: fail("WrongProvider crashed framework");
358: }
359:
360: assertTrue("Provider registration was missed", provider.queried);
361:
362: content.remove(provider);
363: }
364:
365: private class WrongProvider implements TurboProvider {
366:
367: volatile boolean queried;
368:
369: public boolean recognizesAttribute(String name) {
370: queried = true;
371: return "wrong".equals(name);
372: // throw new RuntimeException(); XXX too early
373: }
374:
375: public boolean recognizesEntity(Object key) {
376: return true;
377: }
378:
379: public Object readEntry(Object key, String name,
380: MemoryCache memoryCache) {
381: throw new RuntimeException();
382: }
383:
384: public boolean writeEntry(Object key, String name, Object value) {
385: throw new RuntimeException();
386: }
387: }
388:
389: }
|