/**
Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved.
Contact:
SYSTAP, LLC DBA Blazegraph
2501 Calvert ST NW #106
Washington, DC 20008
licenses@blazegraph.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Created on Oct 6, 2010
*/
package com.bigdata.relation.accesspath;
import java.io.Serializable;
import java.util.NoSuchElementException;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import junit.framework.TestCase2;
/**
* Test suite for the {@link MultiSourceSequentialCloseableIterator}.
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
* @version $Id$
*/
public class TestMultiSourceSequentialCloseableIterator extends TestCase2 {
public TestMultiSourceSequentialCloseableIterator() {
}
public TestMultiSourceSequentialCloseableIterator(String name) {
super(name);
}
private final ThickAsynchronousIterator<String> emptyIterator() {
return new ThickAsynchronousIterator<String>(new String[]{});
}
private final ThickAsynchronousIterator<String> iterator(final String... a) {
return new ThickAsynchronousIterator<String>(a);
}
public void test1() throws InterruptedException {
// empty iterator.
final MultiSourceSequentialCloseableIterator<String> itr = new MultiSourceSequentialCloseableIterator<String>(
emptyIterator());
// // nothing available yet.
// assertFalse(itr.hasNext(1, TimeUnit.MILLISECONDS));
// assertNull(itr.next(1, TimeUnit.MILLISECONDS));
// add an empty chunk.
assertTrue(itr.add(new ThickAsynchronousIterator<String>(
new String[] {})));
// // still nothing available yet.
// assertFalse(itr.hasNext(1, TimeUnit.MILLISECONDS));
// assertNull(itr.next(1, TimeUnit.MILLISECONDS));
// add a non-empty chunk.
assertTrue(itr.add(new ThickAsynchronousIterator<String>(
new String[] { "a" })));
// reports data available and visits data.
assertTrue(itr.hasNext());
assertEquals("a", itr.next());
// assertTrue(itr.hasNext(1, TimeUnit.MILLISECONDS));
// assertEquals("a", itr.next(1, TimeUnit.MILLISECONDS));
// add a non-empty chunk.
assertTrue(itr.add(new ThickAsynchronousIterator<String>(
new String[] { "b" })));
// reports data available and visits data.
assertTrue(itr.hasNext());
assertEquals("b", itr.next());
// close the iterator.
itr.close();
// iterator reports nothing available.
assertFalse(itr.hasNext());
// assertFalse(itr.hasNext(1, TimeUnit.MILLISECONDS));
// assertNull(itr.next(1, TimeUnit.MILLISECONDS));
// can not add more sources.
assertFalse(itr.add(new ThickAsynchronousIterator<String>(
new String[] { "b" })));
}
public void test2() throws InterruptedException {
// empty iterator.
final MultiSourceSequentialCloseableIterator<String> itr = new MultiSourceSequentialCloseableIterator<String>(
emptyIterator());
// add a non-empty chunk.
assertTrue(itr.add(new ThickAsynchronousIterator<String>(
new String[] { "a" })));
// add a non-empty chunk.
assertTrue(itr.add(new ThickAsynchronousIterator<String>(
new String[] { "b" })));
// reports data available and visits data.
assertTrue(itr.hasNext());
assertEquals("a", itr.next());
assertTrue(itr.hasNext());
assertEquals("b", itr.next());
// another read on the iterator causes it to be closed.
assertFalse(itr.hasNext());
// can not add more sources.
assertFalse(itr.add(new ThickAsynchronousIterator<String>(
new String[] { "b" })));
}
/**
* Verify that the iterator notices if it is asynchronously closed.
*
* @throws InterruptedException
* @throws ExecutionException
*/
public void test3() throws InterruptedException, ExecutionException {
// empty iterator.
final MultiSourceSequentialCloseableIterator<String> itr = new MultiSourceSequentialCloseableIterator<String>(
emptyIterator());
final ExecutorService service = Executors.newSingleThreadExecutor();
try {
final FutureTask<Void> ft = new FutureTask<Void>(new Callable<Void>() {
public Void call() throws Exception {
log.info("Will wait on iterator.");
assertFalse(itr.hasNext());
// if (itr.hasNext(1000, TimeUnit.MILLISECONDS))
// fail("Iterator should not visit anything.");
// Can not add more sources.
assertFalse(itr.add(new ThickAsynchronousIterator<String>(
new String[] { "b" })));
return null;
}
});
service.submit(ft);
Thread.sleep(500/*ms*/);
log.info("Will close iterator.");
itr.close();
// check future.
ft.get();
} finally {
service.shutdownNow();
}
}
/**
* Verify that the iterator closes all sources iterators when it is closed.
*
* @throws InterruptedException
*/
public void test4_sources_closed() throws InterruptedException {
final ThickAsynchronousIterator<String> itr1 = iterator("a","b","c");
// empty iterator.
final MultiSourceSequentialCloseableIterator<String> itr = new MultiSourceSequentialCloseableIterator<String>(
itr1);
assertEquals("a", itr.next());
// assertEquals("b", itr.next());
// more is available from the high level iterator.
assertTrue(itr.hasNext());
// more is available from the underlying iterator.
assertTrue(itr1.hasNext());
log.info("Will close iterator.");
itr.close();
// can not add more sources.
assertFalse(itr.add(iterator("d")));
// underlying iterator was closed.
assertFalse(itr1.open);
assertFalse(itr1.hasNext());
// high level iterator was closed.
assertFalse(itr.hasNext());
}
/**
* Verify that sources are closed when there is more than one source.
*
* @throws InterruptedException
*/
public void test5_sources_closed() throws InterruptedException {
final ThickAsynchronousIterator<String> itr1 = iterator("a","b","c");
final ThickAsynchronousIterator<String> itr2 = iterator("d","e","f");
final ThickAsynchronousIterator<String> itr3 = iterator("g","h","i");
// empty iterator.
final MultiSourceSequentialCloseableIterator<String> itr = new MultiSourceSequentialCloseableIterator<String>(
itr1);
itr.add(itr2);
itr.add(itr3);
assertEquals("a", itr.next());
assertEquals("b", itr.next());
assertEquals("c", itr.next());
// more is available from the high level iterator.
assertTrue(itr.hasNext());
// 1st underlying iterator was closed.
assertFalse(itr1.hasNext());
log.info("Will close iterator.");
itr.close();
// can not add more sources.
assertFalse(itr.add(iterator("xxx")));
// remaining underlying iterators were closed.
assertFalse(itr1.open);
assertFalse(itr1.hasNext());
assertFalse(itr2.open);
assertFalse(itr2.hasNext());
assertFalse(itr3.open);
assertFalse(itr3.hasNext());
// high level iterator was closed.
assertFalse(itr.hasNext());
}
private static class ThickAsynchronousIterator<E> implements
IAsynchronousIterator<E>, Serializable {
private static final long serialVersionUID = 1L;
private transient boolean open = true;
/**
* Index of the last element visited by {@link #next()} and
* <code>-1</code> if NO elements have been visited.
*/
private int lastIndex;
/**
* The array of elements to be visited by the iterator.
*/
private final E[] a;
/**
* Create a thick iterator.
*
* @param a
* The array of elements to be visited by the iterator (may
* be empty, but may not be <code>null</code>).
*
* @throws IllegalArgumentException
* if <i>a</i> is <code>null</code>.
*/
public ThickAsynchronousIterator(final E[] a) {
if (a == null)
throw new IllegalArgumentException();
this.a = a;
lastIndex = -1;
}
public boolean hasNext() {
if(open && lastIndex + 1 < a.length)
return true;
close();
return false;
}
public E next() {
if (!hasNext())
throw new NoSuchElementException();
return a[++lastIndex];
}
public void remove() {
throw new UnsupportedOperationException();
}
/*
* ICloseableIterator.
*/
public void close() {
open = false;
}
/*
* IAsynchronousIterator.
*/
public boolean isExhausted() {
return !hasNext();
}
/**
* Delegates to {@link #hasNext()} since all data are local and timeouts
* can not occur.
*/
public boolean hasNext(long timeout, TimeUnit unit) {
return hasNext();
}
/**
* Delegates to {@link #next()} since all data are local and timeouts
* can not occur.
*/
public E next(long timeout, TimeUnit unit) {
return next();
}
}
}