/* This code is part of Freenet. It is distributed under the GNU General
* Public License, version 2 (or at your option any later version). See
* http://www.gnu.org/ for further details of the GPL. */
package freenet.support.io;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.Security;
import java.util.Random;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import freenet.crypt.DummyRandomSource;
import freenet.crypt.MasterSecret;
import freenet.crypt.RandomSource;
import freenet.support.Executor;
import freenet.support.SerialExecutor;
import freenet.support.api.Bucket;
import freenet.support.io.TempBucketFactory.TempBucket;
public class TempBucketTest extends TestSuite {
private static final long MIN_DISK_SPACE = 2*1024*1024;
static final MasterSecret secret = new MasterSecret();
static{
Security.addProvider(new BouncyCastleProvider());
}
public static class TempBucketMigrationTest extends TestCase {
private Random weakPRNG = new Random(12340);
private Executor exec = new SerialExecutor(NativeThread.NORM_PRIORITY);
private FilenameGenerator fg;
public TempBucketMigrationTest() throws IOException {
fg = new FilenameGenerator(weakPRNG, false, null, "junit");
}
public void testRamLimitCreate() throws IOException {
TempBucketFactory tbf = new TempBucketFactory(exec, fg, 16, 128, weakPRNG, false, MIN_DISK_SPACE, secret);
int maxRamBucket = 128 / 16;
// create excess maxTotalRamSize, last one should be on disk
TempBucket[] b = new TempBucket[maxRamBucket + 1];
for (int i = 0; i < maxRamBucket + 1; i++) {
b[i] = (TempBucket) tbf.makeBucket(16);
OutputStream os = b[i].getOutputStream();
os.write(new byte[16]);
os.close();
}
try {
assertTrue(b[0].isRAMBucket());
assertFalse(b[maxRamBucket].isRAMBucket());
// Free some, reused the space
b[0].free();
b[maxRamBucket].free();
b[0] = (TempBucket) tbf.makeBucket(8);
b[maxRamBucket] = (TempBucket) tbf.makeBucket(8);
assertTrue(b[0].isRAMBucket());
assertTrue(b[maxRamBucket].isRAMBucket());
} finally {
for (Bucket bb : b)
bb.free();
}
}
public void testWriteExcessConversionFactor() throws IOException {
TempBucketFactory tbf = new TempBucketFactory(exec, fg, 16, 128, weakPRNG, false, MIN_DISK_SPACE, secret);
TempBucket b = (TempBucket) tbf.makeBucket(16);
try {
assertTrue(b.isRAMBucket());
OutputStream os = b.getOutputStreamUnbuffered();
os.write(new byte[16]);
assertTrue(b.isRAMBucket());
for (int i = 0; i < TempBucketFactory.RAMBUCKET_CONVERSION_FACTOR - 1; i++) {
os.write(new byte[16]);
}
assertFalse(b.isRAMBucket());
} finally {
b.free();
}
}
public void testWriteExcessLimit() throws IOException {
TempBucketFactory tbf = new TempBucketFactory(exec, fg, 16, 17, weakPRNG, false, MIN_DISK_SPACE, secret);
TempBucket b = (TempBucket) tbf.makeBucket(16);
try {
assertTrue(b.isRAMBucket());
OutputStream os = b.getOutputStreamUnbuffered();
os.write(new byte[16]);
assertTrue(b.isRAMBucket());
os.write(new byte[2]);
assertFalse(b.isRAMBucket());
} finally {
b.free();
}
}
// This CAN happen due to memory pressure.
public void testConversionWhileReading() throws IOException {
TempBucketFactory tbf = new TempBucketFactory(exec, fg, 1024, 65536, weakPRNG, false, MIN_DISK_SPACE, secret);
TempBucket bucket = (TempBucket) tbf.makeBucket(64);
OutputStream os = bucket.getOutputStreamUnbuffered();
os.write(new byte[16]);
InputStream is = bucket.getInputStream();
bucket.migrateToDisk();
byte[] readTo = new byte[16];
assertTrue(is.read(readTo, 0, 16) == 16);
for(int i=0;i<readTo.length;i++)
assertTrue(readTo[i] == 0);
is.close();
os.close();
}
// Do a bigger read, verify contents.
public void testBigConversionWhileReading() throws IOException {
TempBucketFactory tbf = new TempBucketFactory(exec, fg, 4096, 65536, weakPRNG, false, MIN_DISK_SPACE, secret);
TempBucket bucket = (TempBucket) tbf.makeBucket(2048);
OutputStream os = bucket.getOutputStreamUnbuffered();
byte[] data = new byte[2048];
new Random(89).nextBytes(data);
os.write(data);
InputStream is = bucket.getInputStream();
bucket.migrateToDisk();
byte[] readTo = new byte[2048];
new DataInputStream(is).readFully(readTo);
for(int i=0;i<readTo.length;i++)
assertTrue(readTo[i] == data[i]);
is.close();
os.close();
}
}
// Private because we only use it as a base class for the actual tests.
private static class RealTempBucketTest_ extends BucketTestBase {
private RandomSource strongPRNG = new DummyRandomSource(12345);
private Random weakPRNG = new DummyRandomSource(54321);
private Executor exec = new SerialExecutor(NativeThread.NORM_PRIORITY);
private FilenameGenerator fg;
private TempBucketFactory tbf;
public RealTempBucketTest_(int maxRamSize, int maxTotalRamSize, boolean encrypted) throws IOException {
fg = new FilenameGenerator(weakPRNG, false, null, "junit");
tbf = new TempBucketFactory(exec, fg, maxRamSize, maxTotalRamSize, weakPRNG, encrypted, MIN_DISK_SPACE, secret);
canOverwrite = false;
}
@Override
protected void freeBucket(Bucket bucket) throws IOException {
bucket.free();
}
@Override
protected Bucket makeBucket(long size) throws IOException {
return tbf.makeBucket(1); // TempBucket allow resize
}
}
public static class RealTempBucketTest_8_16_F extends RealTempBucketTest_ {
public RealTempBucketTest_8_16_F() throws IOException {
super(8, 16, false);
}
}
public static class RealTempBucketTest_64_128_F extends RealTempBucketTest_ {
public RealTempBucketTest_64_128_F() throws IOException {
super(64, 128, false);
}
}
public static class RealTempBucketTest_64k_128k_F extends RealTempBucketTest_ {
public RealTempBucketTest_64k_128k_F() throws IOException {
super(64 * 1024, 128 * 1024, false);
}
}
public static class RealTempBucketTest_8_16_T extends RealTempBucketTest_ {
public RealTempBucketTest_8_16_T() throws IOException {
super(8, 16, true);
}
}
public static class RealTempBucketTest_64k_128k_T extends RealTempBucketTest_ {
public RealTempBucketTest_64k_128k_T() throws IOException {
super(64 * 1024, 128 * 1024, true);
}
}
public TempBucketTest() {
super("TempBucketTest");
addTest(new TestSuite(RealTempBucketTest_8_16_F.class));
addTest(new TestSuite(RealTempBucketTest_64_128_F.class));
addTest(new TestSuite(RealTempBucketTest_64k_128k_F.class));
addTest(new TestSuite(RealTempBucketTest_8_16_T.class));
addTest(new TestSuite(RealTempBucketTest_64k_128k_T.class));
addTest(new TestSuite(TempBucketMigrationTest.class));
}
public static Test suite() {
TestSuite suite = new TestSuite("TempBucketTest");
suite.addTest(new TestSuite(RealTempBucketTest_8_16_F.class));
suite.addTest(new TestSuite(RealTempBucketTest_64_128_F.class));
suite.addTest(new TestSuite(RealTempBucketTest_64k_128k_F.class));
suite.addTest(new TestSuite(RealTempBucketTest_8_16_T.class));
suite.addTest(new TestSuite(RealTempBucketTest_64k_128k_T.class));
suite.addTest(new TestSuite(TempBucketMigrationTest.class));
return suite;
}
}