package org.limewire.http.entity;
import java.io.EOFException;
import java.io.File;
import java.io.IOException;
import junit.framework.Test;
import org.limewire.concurrent.ManagedThread;
import org.limewire.http.HttpTestUtils;
import org.limewire.nio.ByteBufferCache;
import org.limewire.util.BaseTestCase;
public class FilePieceReaderTest extends BaseTestCase {
private File file;
private MyPieceListener listener;
private byte[] data;
private int read;
private FilePieceReader reader;
public FilePieceReaderTest(String name) {
super(name);
}
public static Test suite() {
return buildTestSuite(FilePieceReaderTest.class);
}
@Override
protected void setUp() throws Exception {
listener = new MyPieceListener();
read = 0;
}
@Override
protected void tearDown() throws Exception {
if (reader != null) {
if (!reader.isShutdown()) {
reader.shutdownAndWait(5000);
}
reader = null;
}
}
public void testRead() throws Exception {
createFile(50000);
reader = new FilePieceReader(new ByteBufferCache(), file, 0, (int) file
.length(), listener);
reader.start();
while (read < file.length()) {
assertGreaterThan(0, listener.waitForNotification());
Piece piece = reader.next();
if (read == 0) {
assertNotNull("Notification should not have been sent", piece);
}
while (piece != null) {
assertEquals(piece.getBuffer().limit(), piece.getLength());
assertEquals(piece.getBuffer().limit(), piece.getBuffer()
.remaining());
assertEquals(read, piece.getOffset());
assertEqualsToData(data, piece);
read += piece.getBuffer().limit();
reader.release(piece);
if (read == file.length()) {
break;
}
piece = reader.next();
}
}
}
public void testRelease() throws Exception {
int filesize = FilePieceReader.BUFFER_SIZE * 3;
createFile(filesize);
MockByteBufferCache cache = new MockByteBufferCache();
assertEquals(0, cache.getHeapCacheSize());
reader = new FilePieceReader(cache, file, 0, filesize, listener);
assertEquals(0, cache.buffers);
reader.start();
Piece piece1 = getNext();
assertGreaterThanOrEquals(1, cache.buffers);
assertGreaterThanOrEquals(piece1.getLength(), cache.bytes);
Piece piece2 = getNext();
Piece piece3 = getNext();
assertEquals(3, cache.buffers);
assertEquals(filesize, cache.bytes);
reader.release(piece1);
assertEquals(3, cache.buffers);
reader.release(piece3);
assertEquals(3, cache.buffers);
reader.shutdown();
assertEquals(1, cache.buffers);
assertEquals(FilePieceReader.BUFFER_SIZE, cache.bytes);
reader.release(piece2);
assertEquals(0, cache.buffers);
assertEquals(0, cache.bytes);
}
public void testReleaseVerySmall() throws Exception {
int filesize = FilePieceReader.BUFFER_SIZE - 1;
createFile(filesize);
MockByteBufferCache cache = new MockByteBufferCache();
reader = new FilePieceReader(cache, file, 0, filesize, listener);
reader.start();
assertEquals(1, cache.buffers);
}
public void testReleaseSmall() throws Exception {
int filesize = FilePieceReader.BUFFER_SIZE + 1;
createFile(filesize);
MockByteBufferCache cache = new MockByteBufferCache();
reader = new FilePieceReader(cache, file, 0, filesize, listener);
reader.start();
assertEquals(2, cache.buffers);
}
public void testReadTooMuch() throws Exception {
int filesize = FilePieceReader.BUFFER_SIZE;
createFile(filesize);
MockByteBufferCache cache = new MockByteBufferCache();
reader = new FilePieceReader(cache, file, 0, filesize, listener);
reader.start();
getNext();
try {
Piece piece = getNext();
fail("Expected EOFException, got: " + piece);
} catch (EOFException e) {
}
}
public void testReadException() throws Exception {
int filesize = 20000;
createFile(filesize);
MockByteBufferCache cache = new MockByteBufferCache();
reader = new FilePieceReader(cache, file, 0, filesize, listener);
reader.start();
Piece piece1 = getNext();
reader.failed(new IOException());
reader.waitForShutdown(1000);
assertEquals(1, cache.buffers);
try {
Piece piece2 = getNext();
fail("Expected EOFException, got: " + piece2);
} catch (EOFException e) {
}
assertEquals(1, cache.buffers);
reader.release(piece1);
assertEquals(0, cache.buffers);
}
public void testMultipleConcurrentReads() throws Exception {
MockByteBufferCache cache = new MockByteBufferCache();
class Runner implements Runnable {
private File file;
private byte[] data;
private FilePieceReader reader;
private IOException exception;
Runner(ByteBufferCache cache) throws IOException {
this.file = FilePieceReaderTest.this.file;
this.data = FilePieceReaderTest.this.data;
this.reader = new FilePieceReader(cache, file, 0, (int) file
.length(), listener);
}
public Runner(MockByteBufferCache cache, IOException exception)
throws IOException {
this(cache);
this.exception = exception;
}
public void run() {
reader.start();
try {
int read = 0;
Piece piece;
while ((piece = getNext(reader, file)) != null) {
assertEqualsToData(data, piece);
read += piece.getLength();
reader.release(piece);
if (exception != null) {
reader.failed(exception);
return;
}
}
assertEquals(file.length(), read);
} finally {
try {
reader.shutdownAndWait(1000);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
;
createFile(500000);
Runner runner1 = new Runner(cache);
createFile(100000);
Runner runner2 = new Runner(cache);
createFile(1000000);
Runner runner3 = new Runner(cache, new IOException());
Runner runner4 = new Runner(cache);
Runner runner5 = new Runner(cache);
Thread t1 = new ManagedThread(runner1);
Thread t2 = new ManagedThread(runner2);
Thread t3 = new ManagedThread(runner3);
Thread t4 = new ManagedThread(runner4);
Thread t5 = new ManagedThread(runner5);
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t1.join(5000);
t2.join(5000);
t3.join(5000);
t4.join(5000);
t5.join(5000);
assertFalse(t1.isAlive());
assertFalse(t2.isAlive());
assertFalse(t3.isAlive());
assertFalse(t4.isAlive());
assertFalse(t5.isAlive());
}
private Piece getNext() throws Exception {
while (read < file.length()) {
Piece piece = reader.next();
if (piece != null) {
read += piece.getBuffer().limit();
return piece;
}
}
throw new EOFException();
}
private Piece getNext(FilePieceReader reader, File file) {
while (true) {
Piece piece;
try {
piece = reader.next();
} catch (EOFException e) {
return null;
}
if (piece != null) {
read += piece.getBuffer().limit();
return piece;
}
}
}
private void assertEqualsToData(byte[] data, Piece piece) {
byte[] expectedContent = new byte[piece.getBuffer().remaining()];
System.arraycopy(data, (int) piece.getOffset(), expectedContent, 0,
expectedContent.length);
byte[] readContent = new byte[piece.getBuffer().remaining()];
piece.getBuffer().get(readContent);
assertEquals("Unexpected data in piece at offset: " + piece.getOffset()
+ ", length: " + expectedContent.length, //
expectedContent, readContent);
}
private void createFile(int size) throws IOException {
file = File.createTempFile("limewire", "");
file.deleteOnExit();
data = HttpTestUtils.writeRandomData(file, size);
}
private class MyPieceListener implements PieceListener {
// private List<Piece> read = new ArrayList<Piece>();
IOException exception;
int notificationCount;
public synchronized void readSuccessful() {
notificationCount++;
this.notify();
}
public synchronized void readFailed(IOException e) {
exception = e;
this.notify();
}
public synchronized int waitForNotification()
throws InterruptedException, IOException {
while (notificationCount == 0 && exception == null) {
this.wait();
}
if (exception != null) {
throw exception;
}
return notificationCount--;
}
}
}