package freenet.support.io;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Random;
import freenet.support.api.LockableRandomAccessBuffer.RAFLock;
public class PooledFileRandomAccessBufferTest extends RandomAccessBufferTestBase {
private static final int[] TEST_LIST = new int[] { 0, 1, 32, 64, 32768, 1024*1024, 1024*1024+1 };
public PooledFileRandomAccessBufferTest() {
super(TEST_LIST);
}
private File base = new File("tmp.pooled-random-access-file-wrapper-test");
public void setUp() {
base.mkdir();
}
public void tearDown() {
FileUtil.removeAll(base);
}
private Random r = new Random(222831072);
@Override
protected PooledFileRandomAccessBuffer construct(long size) throws IOException {
File f = File.createTempFile("test", ".tmp", base);
return new PooledFileRandomAccessBuffer(f, false, size, r.nextBoolean() ? r : null, -1, true);
}
/** Simplest test for pooling. TODO Add more. */
public void testSimplePooling() throws IOException {
for(int sz : TEST_LIST)
innerTestSimplePooling(sz);
}
private void innerTestSimplePooling(int sz) throws IOException {
PooledFileRandomAccessBuffer.setMaxFDs(1);
PooledFileRandomAccessBuffer a = construct(sz);
PooledFileRandomAccessBuffer b = construct(sz);
byte[] buf1 = new byte[sz];
byte[] buf2 = new byte[sz];
Random r = new Random(1153);
r.nextBytes(buf1);
r.nextBytes(buf2);
a.pwrite(0, buf1, 0, buf1.length);
b.pwrite(0, buf2, 0, buf2.length);
byte[] cmp1 = new byte[sz];
byte[] cmp2 = new byte[sz];
a.pread(0, cmp1, 0, cmp1.length);
b.pread(0, cmp2, 0, cmp2.length);
assertTrue(Arrays.equals(cmp1, buf1));
assertTrue(Arrays.equals(cmp2, buf2));
a.close();
b.close();
a.free();
b.free();
}
/** Test that locking and unlocking do something */
public void testLock() throws IOException {
int sz = 1024;
PooledFileRandomAccessBuffer.setMaxFDs(1);
assertEquals(PooledFileRandomAccessBuffer.getOpenFDs(), 0);
assertEquals(PooledFileRandomAccessBuffer.getClosableFDs(), 0);
PooledFileRandomAccessBuffer a = construct(sz);
PooledFileRandomAccessBuffer b = construct(sz);
assertEquals(PooledFileRandomAccessBuffer.getOpenFDs(), 1);
assertEquals(PooledFileRandomAccessBuffer.getClosableFDs(), 1);
assertFalse(a.isLocked());
assertFalse(b.isLocked());
RAFLock lock = a.lockOpen();
try {
assertTrue(a.isLocked());
assertFalse(b.isLocked());
assertEquals(PooledFileRandomAccessBuffer.getOpenFDs(), 1);
assertEquals(PooledFileRandomAccessBuffer.getClosableFDs(), 0);
} finally {
lock.unlock();
assertFalse(a.isLocked());
assertEquals(PooledFileRandomAccessBuffer.getOpenFDs(), 1);
assertEquals(PooledFileRandomAccessBuffer.getClosableFDs(), 1);
}
a.close();
b.close();
assertEquals(PooledFileRandomAccessBuffer.getOpenFDs(), 0);
assertEquals(PooledFileRandomAccessBuffer.getClosableFDs(), 0);
a.free();
b.free();
}
/** Thanks bertm */
public void testLocksB() throws IOException {
PooledFileRandomAccessBuffer.setMaxFDs(1);
PooledFileRandomAccessBuffer a = construct(0);
PooledFileRandomAccessBuffer b = construct(0);
RAFLock lock = b.lockOpen();
lock.unlock();
a.close();
b.close();
a.free();
b.free();
assertEquals(PooledFileRandomAccessBuffer.getOpenFDs(), 0);
assertEquals(PooledFileRandomAccessBuffer.getClosableFDs(), 0);
}
public void testLockedNotClosable() throws IOException {
int sz = 1024;
PooledFileRandomAccessBuffer.setMaxFDs(2);
PooledFileRandomAccessBuffer a = construct(sz);
PooledFileRandomAccessBuffer b = construct(sz);
assertEquals(PooledFileRandomAccessBuffer.getOpenFDs(), 2);
assertEquals(PooledFileRandomAccessBuffer.getClosableFDs(), 2);
assertTrue(a.isOpen());
assertTrue(b.isOpen());
assertFalse(a.isLocked());
assertFalse(b.isLocked());
// Open and open FD -> locked
RAFLock la = a.lockOpen();
assertEquals(PooledFileRandomAccessBuffer.getOpenFDs(), 2);
assertEquals(PooledFileRandomAccessBuffer.getClosableFDs(), 1);
RAFLock lb = b.lockOpen();
assertEquals(PooledFileRandomAccessBuffer.getOpenFDs(), 2);
assertEquals(PooledFileRandomAccessBuffer.getClosableFDs(), 0);
la.unlock();
lb.unlock();
assertEquals(PooledFileRandomAccessBuffer.getOpenFDs(), 2);
assertEquals(PooledFileRandomAccessBuffer.getClosableFDs(), 2);
a.close();
b.close();
}
public void testLockedNotClosableFromNotOpenFD() throws IOException {
int sz = 1024;
PooledFileRandomAccessBuffer.setMaxFDs(2);
PooledFileRandomAccessBuffer a = construct(sz);
PooledFileRandomAccessBuffer b = construct(sz);
assertEquals(PooledFileRandomAccessBuffer.getOpenFDs(), 2);
assertEquals(PooledFileRandomAccessBuffer.getClosableFDs(), 2);
assertTrue(a.isOpen());
assertTrue(b.isOpen());
// Close the RAFs to exercise the other code path.
a.closeRAF();
b.closeRAF();
assertFalse(a.isLocked());
assertFalse(b.isLocked());
// Open and open FD -> locked
RAFLock la = a.lockOpen();
assertEquals(PooledFileRandomAccessBuffer.getOpenFDs(), 1);
assertEquals(PooledFileRandomAccessBuffer.getClosableFDs(), 1);
RAFLock lb = b.lockOpen();
assertEquals(PooledFileRandomAccessBuffer.getOpenFDs(), 2);
assertEquals(PooledFileRandomAccessBuffer.getClosableFDs(), 0);
la.unlock();
lb.unlock();
assertEquals(PooledFileRandomAccessBuffer.getOpenFDs(), 2);
assertEquals(PooledFileRandomAccessBuffer.getClosableFDs(), 2);
a.close();
b.close();
}
/** Test that locking enforces limits and blocks when appropriate.
* @throws InterruptedException */
public void testLockBlocking() throws IOException, InterruptedException {
int sz = 1024;
PooledFileRandomAccessBuffer.setMaxFDs(1);
assertEquals(PooledFileRandomAccessBuffer.getOpenFDs(), 0);
final PooledFileRandomAccessBuffer a = construct(sz);
final PooledFileRandomAccessBuffer b = construct(sz);
assertEquals(PooledFileRandomAccessBuffer.getOpenFDs(), 1);
assertFalse(a.isLocked());
assertFalse(b.isLocked());
RAFLock lock = a.lockOpen();
assertTrue(a.isOpen());
assertEquals(PooledFileRandomAccessBuffer.getOpenFDs(), 1);
// Now try to lock on a second thread.
// It should wait until the first thread unlocks.
class Status {
boolean hasStarted;
boolean hasLocked;
boolean canFinish;
boolean hasFinished;
boolean success;
}
final Status s = new Status();
Runnable r = new Runnable() {
@Override
public void run() {
synchronized(s) {
s.hasStarted = true;
s.notify();
}
try {
RAFLock lock = b.lockOpen();
synchronized(s) {
s.hasLocked = true;
s.notify();
}
synchronized(s) {
while(!s.canFinish)
try {
s.wait();
} catch (InterruptedException e) {
// Ignore.
}
}
lock.unlock();
synchronized(s) {
s.success = true;
}
} catch (IOException e) {
e.printStackTrace();
fail("Caught IOException trying to lock: "+e);
} finally {
synchronized(s) {
s.hasFinished = true;
s.notify();
}
}
}
};
new Thread(r).start();
// Wait for it to start.
synchronized(s) {
while(!s.hasStarted) {
s.wait();
}
assertFalse(s.hasLocked);
assertFalse(s.hasFinished);
}
assertEquals(PooledFileRandomAccessBuffer.getOpenFDs(), 1);
assertTrue(a.isOpen());
assertFalse(b.isOpen());
// Wait while holding lock, to give it some time to progress if it's buggy.
Thread.sleep(100);
synchronized(s) {
assertFalse(s.hasLocked);
assertFalse(s.hasFinished);
}
assertEquals(PooledFileRandomAccessBuffer.getOpenFDs(), 1);
assertTrue(a.isOpen());
assertFalse(b.isOpen());
// Now release lock.
lock.unlock();
// Wait for it to proceed.
synchronized(s) {
while(!(s.hasLocked || s.hasFinished))
s.wait();
assertTrue(s.hasLocked);
}
assertFalse(a.isOpen());
assertTrue(b.isOpen());
assertTrue(b.isLocked());
assertEquals(PooledFileRandomAccessBuffer.getOpenFDs(), 1);
// Now let it proceed.
synchronized(s) {
s.canFinish = true;
s.notifyAll();
while(!s.hasFinished) {
s.wait();
}
assertTrue(s.success);
}
assertFalse(a.isLocked());
assertFalse(b.isLocked());
assertEquals(PooledFileRandomAccessBuffer.getClosableFDs(), 1);
assertEquals(PooledFileRandomAccessBuffer.getOpenFDs(), 1);
a.close();
assertEquals(PooledFileRandomAccessBuffer.getOpenFDs(), 1);
b.close();
assertEquals(PooledFileRandomAccessBuffer.getOpenFDs(), 0);
a.free();
b.free();
}
// FIXME more tests???
}