001: /*
002: * Copyright 2003-2004 The Apache Software Foundation
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.apache.commons.collections.buffer;
017:
018: import junit.framework.Test;
019: import junit.framework.TestSuite;
020: import org.apache.commons.collections.AbstractTestObject;
021: import org.apache.commons.collections.Buffer;
022: import org.apache.commons.collections.BufferUnderflowException;
023:
024: import java.util.ArrayList;
025: import java.util.Collections;
026: import java.util.HashSet;
027: import java.util.LinkedList;
028: import java.util.Set;
029:
030: /**
031: * Extension of {@link AbstractTestObject} for exercising the {@link BlockingBuffer} implementation.
032: *
033: * @author Janek Bogucki
034: * @author Phil Steitz
035: * @version $Revision: 348428 $
036: * @since Commons Collections 3.0
037: */
038: public class TestBlockingBuffer extends AbstractTestObject {
039:
040: public TestBlockingBuffer(String testName) {
041: super (testName);
042: }
043:
044: public static Test suite() {
045: return new TestSuite(TestBlockingBuffer.class);
046: }
047:
048: public static void main(String args[]) {
049: String[] testCaseName = { TestBlockingBuffer.class.getName() };
050: junit.textui.TestRunner.main(testCaseName);
051: }
052:
053: public Object makeObject() {
054: return BlockingBuffer.decorate(new MyBuffer());
055: }
056:
057: public boolean isEqualsCheckable() {
058: return false;
059: }
060:
061: //-----------------------------------------------------------------------
062:
063: /**
064: * Tests {@link BlockingBuffer#get()} in combination with {@link BlockingBuffer#add(Object)}.
065: */
066: public void testGetWithAdd() {
067: Buffer blockingBuffer = BlockingBuffer.decorate(new MyBuffer());
068: Object obj = new Object();
069: new DelayedAdd(blockingBuffer, obj).start();
070:
071: // verify does not throw BufferUnderflowException; should block until other thread has added to the buffer .
072: assertSame(obj, blockingBuffer.get());
073: }
074:
075: public void testGetWithAddTimeout() {
076: Buffer blockingBuffer = BlockingBuffer.decorate(new MyBuffer(),
077: 500);
078: Object obj = new Object();
079: new DelayedAdd(blockingBuffer, obj, 100).start();
080:
081: // verify does not throw BufferUnderflowException; should block until other thread has added to the buffer .
082: assertSame(obj, blockingBuffer.get());
083: }
084:
085: //-----------------------------------------------------------------------
086:
087: /**
088: * Tests {@link BlockingBuffer#get()} in combination with {@link BlockingBuffer#addAll(java.util.Collection)}.
089: */
090: public void testGetWithAddAll() {
091: Buffer blockingBuffer = BlockingBuffer.decorate(new MyBuffer());
092: Object obj = new Object();
093: new DelayedAddAll(blockingBuffer, obj).start();
094:
095: // verify does not throw BufferUnderflowException; should block until other thread has added to the buffer .
096: assertSame(obj, blockingBuffer.get());
097: }
098:
099: public void testGetWithAddAllTimeout() {
100: Buffer blockingBuffer = BlockingBuffer.decorate(new MyBuffer(),
101: 500);
102: Object obj = new Object();
103: new DelayedAddAll(blockingBuffer, obj, 100).start();
104:
105: // verify does not throw BufferUnderflowException; should block until other thread has added to the buffer .
106: assertSame(obj, blockingBuffer.get());
107: }
108:
109: //-----------------------------------------------------------------------
110:
111: /**
112: * Tests {@link BlockingBuffer#remove()} in combination with {@link BlockingBuffer#add(Object)}.
113: */
114: public void testRemoveWithAdd() {
115: Buffer blockingBuffer = BlockingBuffer.decorate(new MyBuffer());
116: Object obj = new Object();
117: new DelayedAdd(blockingBuffer, obj).start();
118:
119: // verify does not throw BufferUnderflowException; should block until other thread has added to the buffer .
120: assertSame(obj, blockingBuffer.remove());
121: }
122:
123: public void testRemoveWithAddTimeout() {
124: Buffer blockingBuffer = BlockingBuffer.decorate(new MyBuffer(),
125: 100);
126: Object obj = new Object();
127: new DelayedAdd(blockingBuffer, obj, 500).start();
128: try {
129: blockingBuffer.remove();
130: } catch (BufferUnderflowException e) {
131: }
132: }
133:
134: //-----------------------------------------------------------------------
135:
136: /**
137: * Tests {@link BlockingBuffer#remove()} in combination with {@link BlockingBuffer#addAll(java.util.Collection)}.
138: */
139: public void testRemoveWithAddAll() {
140: Buffer blockingBuffer = BlockingBuffer.decorate(new MyBuffer());
141: Object obj = new Object();
142: new DelayedAddAll(blockingBuffer, obj).start();
143:
144: // verify does not throw BufferUnderflowException; should block until other thread has added to the buffer .
145: assertSame(obj, blockingBuffer.remove());
146: }
147:
148: public void testRemoveWithAddAllTimeout() {
149: Buffer blockingBuffer = BlockingBuffer.decorate(new MyBuffer(),
150: 100);
151: Object obj = new Object();
152: new DelayedAddAll(blockingBuffer, obj, 500).start();
153: try {
154: blockingBuffer.remove();
155: } catch (BufferUnderflowException e) {
156: }
157: }
158:
159: //-----------------------------------------------------------------------
160:
161: /**
162: * Tests {@link BlockingBuffer#get()} in combination with {@link BlockingBuffer#add(Object)} using multiple read
163: * threads.
164: * <p/>
165: * Two read threads should block on an empty buffer until one object is added then both threads should complete.
166: */
167: public void testBlockedGetWithAdd() {
168: Buffer blockingBuffer = BlockingBuffer.decorate(new MyBuffer());
169: Object obj = new Object();
170:
171: // run methods will get and compare -- must wait for add
172: Thread thread1 = new ReadThread(blockingBuffer, obj);
173: Thread thread2 = new ReadThread(blockingBuffer, obj);
174: thread1.start();
175: thread2.start();
176:
177: // give hungry read threads ample time to hang
178: delay();
179:
180: // notifyAll should allow both read threads to complete
181: blockingBuffer.add(obj);
182:
183: // allow notified threads to complete
184: delay();
185:
186: // There should not be any threads waiting.
187: if (thread1.isAlive() || thread2.isAlive()) {
188: fail("Live thread(s) when both should be dead.");
189: }
190: }
191:
192: //-----------------------------------------------------------------------
193:
194: /**
195: * Tests {@link BlockingBuffer#get()} in combination with {@link BlockingBuffer#addAll(java.util.Collection)} using
196: * multiple read threads.
197: * <p/>
198: * Two read threads should block on an empty buffer until a singleton is added then both threads should complete.
199: */
200: public void testBlockedGetWithAddAll() {
201: Buffer blockingBuffer = BlockingBuffer.decorate(new MyBuffer());
202: Object obj = new Object();
203:
204: // run methods will get and compare -- must wait for addAll
205: Thread thread1 = new ReadThread(blockingBuffer, obj);
206: Thread thread2 = new ReadThread(blockingBuffer, obj);
207: thread1.start();
208: thread2.start();
209:
210: // give hungry read threads ample time to hang
211: delay();
212:
213: // notifyAll should allow both read threads to complete
214: blockingBuffer.addAll(Collections.singleton(obj));
215:
216: // allow notified threads to complete
217: delay();
218:
219: // There should not be any threads waiting.
220: if (thread1.isAlive() || thread2.isAlive()) {
221: fail("Live thread(s) when both should be dead.");
222: }
223: }
224:
225: //-----------------------------------------------------------------------
226:
227: /**
228: * Tests interrupted {@link BlockingBuffer#get()}.
229: */
230: public void testInterruptedGet() {
231: Buffer blockingBuffer = BlockingBuffer.decorate(new MyBuffer());
232: Object obj = new Object();
233:
234: // spawn a read thread to wait on the empty buffer
235: ArrayList exceptionList = new ArrayList();
236: Thread thread = new ReadThread(blockingBuffer, obj,
237: exceptionList);
238: thread.start();
239:
240: // Interrupting the thread should cause it to throw BufferUnderflowException
241: thread.interrupt();
242:
243: // Chill, so thread can throw and add message to exceptionList
244: delay();
245: assertTrue("Thread interrupt should have led to underflow",
246: exceptionList.contains("BufferUnderFlow"));
247: if (thread.isAlive()) {
248: fail("Read thread has hung.");
249: }
250:
251: }
252:
253: //-----------------------------------------------------------------------
254:
255: /**
256: * Tests {@link BlockingBuffer#remove()} in combination with {@link BlockingBuffer#add(Object)} using multiple read
257: * threads.
258: * <p/>
259: * Two read threads should block on an empty buffer until one object is added then one thread should complete. The
260: * remaining thread should complete after the addition of a second object.
261: */
262: public void testBlockedRemoveWithAdd() {
263: Buffer blockingBuffer = BlockingBuffer.decorate(new MyBuffer());
264: Object obj = new Object();
265:
266: // run methods will remove and compare -- must wait for add
267: Thread thread1 = new ReadThread(blockingBuffer, obj, null,
268: "remove");
269: Thread thread2 = new ReadThread(blockingBuffer, obj, null,
270: "remove");
271: thread1.start();
272: thread2.start();
273:
274: // give hungry read threads ample time to hang
275: delay();
276: blockingBuffer.add(obj);
277:
278: // allow notified threads to complete
279: delay();
280:
281: // There should be one thread waiting.
282: assertTrue("There is one thread waiting", thread1.isAlive()
283: ^ thread2.isAlive());
284: blockingBuffer.add(obj);
285:
286: // allow notified thread to complete
287: delay();
288:
289: // There should not be any threads waiting.
290: if (thread1.isAlive() || thread2.isAlive()) {
291: fail("Live thread(s) when both should be dead.");
292: }
293: }
294:
295: //-----------------------------------------------------------------------
296:
297: /**
298: * Tests {@link BlockingBuffer#remove()} in combination with {@link BlockingBuffer#addAll(java.util.Collection)}
299: * using multiple read threads.
300: * <p/>
301: * Two read threads should block on an empty buffer until a singleton collection is added then one thread should
302: * complete. The remaining thread should complete after the addition of a second singleton.
303: */
304: public void testBlockedRemoveWithAddAll1() {
305: Buffer blockingBuffer = BlockingBuffer.decorate(new MyBuffer());
306: Object obj = new Object();
307:
308: // run methods will remove and compare -- must wait for addAll
309: Thread thread1 = new ReadThread(blockingBuffer, obj, null,
310: "remove");
311: Thread thread2 = new ReadThread(blockingBuffer, obj, null,
312: "remove");
313: thread1.start();
314: thread2.start();
315:
316: // give hungry read threads ample time to hang
317: delay();
318: blockingBuffer.addAll(Collections.singleton(obj));
319:
320: // allow notified threads to complete
321: delay();
322:
323: // There should be one thread waiting.
324: assertTrue("There is one thread waiting", thread1.isAlive()
325: ^ thread2.isAlive());
326: blockingBuffer.addAll(Collections.singleton(obj));
327:
328: // allow notified thread to complete
329: delay();
330:
331: // There should not be any threads waiting.
332: if (thread1.isAlive() || thread2.isAlive()) {
333: fail("Live thread(s) when both should be dead.");
334: }
335: }
336:
337: //-----------------------------------------------------------------------
338:
339: /**
340: * Tests {@link BlockingBuffer#remove()} in combination with {@link BlockingBuffer#addAll(java.util.Collection)}
341: * using multiple read threads.
342: * <p/>
343: * Two read threads should block on an empty buffer until a collection with two distinct objects is added then both
344: * threads should complete. Each thread should have read a different object.
345: */
346: public void testBlockedRemoveWithAddAll2() {
347: Buffer blockingBuffer = BlockingBuffer.decorate(new MyBuffer());
348: Object obj1 = new Object();
349: Object obj2 = new Object();
350: Set objs = Collections.synchronizedSet(new HashSet());
351: objs.add(obj1);
352: objs.add(obj2);
353:
354: // run methods will remove and compare -- must wait for addAll
355: Thread thread1 = new ReadThread(blockingBuffer, objs, "remove");
356: Thread thread2 = new ReadThread(blockingBuffer, objs, "remove");
357: thread1.start();
358: thread2.start();
359:
360: // give hungry read threads ample time to hang
361: delay();
362: blockingBuffer.addAll(objs);
363:
364: // allow notified threads to complete
365: delay();
366: assertEquals("Both objects were removed", 0, objs.size());
367:
368: // There should not be any threads waiting.
369: if (thread1.isAlive() || thread2.isAlive()) {
370: fail("Live thread(s) when both should be dead.");
371: }
372: }
373:
374: //-----------------------------------------------------------------------
375:
376: /**
377: * Tests interrupted remove.
378: */
379: public void testInterruptedRemove() {
380: Buffer blockingBuffer = BlockingBuffer.decorate(new MyBuffer());
381: Object obj = new Object();
382:
383: // spawn a read thread to wait on the empty buffer
384: ArrayList exceptionList = new ArrayList();
385: Thread thread = new ReadThread(blockingBuffer, obj,
386: exceptionList, "remove");
387: thread.start();
388:
389: // Interrupting the thread should cause it to throw BufferUnderflowException
390: thread.interrupt();
391:
392: // Chill, so thread can throw and add message to exceptionList
393: delay();
394: assertTrue("Thread interrupt should have led to underflow",
395: exceptionList.contains("BufferUnderFlow"));
396: if (thread.isAlive()) {
397: fail("Read thread has hung.");
398: }
399:
400: }
401:
402: public void testTimeoutGet() {
403: final BlockingBuffer buffer = new BlockingBuffer(new MyBuffer());
404: try {
405: buffer.get(100);
406: fail("Get should have timed out.");
407: } catch (BufferUnderflowException e) {
408: }
409: }
410:
411: public void testTimeoutRemove() {
412: final BlockingBuffer buffer = new BlockingBuffer(new MyBuffer());
413: try {
414: buffer.remove(100);
415: fail("Get should have timed out.");
416: } catch (BufferUnderflowException e) {
417: }
418: }
419:
420: protected static class DelayedAdd extends Thread {
421:
422: Buffer buffer;
423:
424: Object obj;
425:
426: long delay = 1000;
427:
428: public DelayedAdd(Buffer buffer, Object obj, long delay) {
429: this .buffer = buffer;
430: this .obj = obj;
431: this .delay = delay;
432: }
433:
434: DelayedAdd(Buffer buffer, Object obj) {
435: super ();
436: this .buffer = buffer;
437: this .obj = obj;
438: }
439:
440: public void run() {
441: try {
442: // wait for other thread to block on get() or remove()
443: Thread.sleep(delay);
444: } catch (InterruptedException e) {
445: }
446: buffer.add(obj);
447: }
448: }
449:
450: protected static class DelayedAddAll extends Thread {
451:
452: Buffer buffer;
453:
454: Object obj;
455:
456: long delay = 100;
457:
458: public DelayedAddAll(Buffer buffer, Object obj, long delay) {
459: this .buffer = buffer;
460: this .obj = obj;
461: this .delay = delay;
462: }
463:
464: DelayedAddAll(Buffer buffer, Object obj) {
465: super ();
466: this .buffer = buffer;
467: this .obj = obj;
468: }
469:
470: public void run() {
471: try {
472: // wait for other thread to block on get() or remove()
473: Thread.sleep(delay);
474: } catch (InterruptedException e) {
475: }
476: buffer.addAll(Collections.singleton(obj));
477: }
478: }
479:
480: protected static class ReadThread extends Thread {
481:
482: Buffer buffer;
483:
484: Object obj;
485:
486: ArrayList exceptionList = null;
487:
488: String action = "get";
489:
490: Set objs;
491:
492: ReadThread(Buffer buffer, Object obj) {
493: super ();
494: this .buffer = buffer;
495: this .obj = obj;
496: }
497:
498: ReadThread(Buffer buffer, Object obj, ArrayList exceptionList) {
499: super ();
500: this .buffer = buffer;
501: this .obj = obj;
502: this .exceptionList = exceptionList;
503: }
504:
505: ReadThread(Buffer buffer, Object obj, ArrayList exceptionList,
506: String action) {
507: super ();
508: this .buffer = buffer;
509: this .obj = obj;
510: this .exceptionList = exceptionList;
511: this .action = action;
512: }
513:
514: ReadThread(Buffer buffer, Set objs, String action) {
515: super ();
516: this .buffer = buffer;
517: this .objs = objs;
518: this .action = action;
519: }
520:
521: public void run() {
522: try {
523: if (action == "get") {
524: assertSame(obj, buffer.get());
525: } else {
526: if (null != obj) {
527: assertSame(obj, buffer.remove());
528: } else {
529: assertTrue(objs.remove(buffer.remove()));
530: }
531: }
532: } catch (BufferUnderflowException ex) {
533: exceptionList.add("BufferUnderFlow");
534: }
535: }
536: }
537:
538: protected static class MyBuffer extends LinkedList implements
539: Buffer {
540:
541: public Object get() {
542: if (isEmpty()) {
543: throw new BufferUnderflowException();
544: }
545: return get(0);
546: }
547:
548: public Object remove() {
549: if (isEmpty()) {
550: throw new BufferUnderflowException();
551: }
552: return remove(0);
553: }
554: }
555:
556: private void delay() {
557: try {
558: Thread.sleep(100);
559: } catch (InterruptedException e) {
560: }
561: }
562:
563: public String getCompatibilityVersion() {
564: return "3.1";
565: }
566:
567: // public void testCreate() throws Exception {
568: // Buffer buffer = BlockingBuffer.decorate(new UnboundedFifoBuffer());
569: // writeExternalFormToDisk((java.io.Serializable) buffer,
570: // "D:/dev/collections/data/test/BlockingBuffer.emptyCollection.version3.1.obj");
571: // buffer = BlockingBuffer.decorate(new UnboundedFifoBuffer());
572: // buffer.add("A");
573: // buffer.add("B");
574: // buffer.add("C");
575: // writeExternalFormToDisk((java.io.Serializable) buffer,
576: // "D:/dev/collections/data/test/BlockingBuffer.fullCollection.version3.1.obj");
577: // }
578: }
|