package net.contrapunctus.lzma;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Random;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.zip.CRC32;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized.Parameters;
import org.junit.runners.Parameterized;
import static net.contrapunctus.lzma.ConcurrentBufferOutputStream.*;
/**
* Some tests to exercise the ConcurrentBufferOutputStream. One test
* is randomized, but the seed is printed, so that results should be
* reproducible if needed.
*/
@RunWith(Parameterized.class)
public class ConcurrentBufferOutputTest
{
@Parameters public static Collection<Object[]> parameters()
{
Collection<Object[]> args = new ArrayList<Object[]>();
args.add(new Object[] { 0L });
args.add(new Object[] { System.currentTimeMillis() });
args.add(new Object[] { 1251302491693L });
return args;
}
public static void main(String[] args)
throws InterruptedException
{
new ConcurrentBufferOutputTest(Long.parseLong(args[0])).run();
}
private long seed;
private Writer wr;
public ConcurrentBufferOutputTest(long seed)
{
this.seed = seed;
this.wr = (0L == seed)? new BoundaryWriter() :
new RandomWriter(new Random(seed));
}
public String toString()
{
return "seed " + seed + "L";
}
@Test(timeout=5000) public void run()
throws InterruptedException
{
System.out.printf("%s:", this);
testReadWrite(wr);
}
static final int MAX_BUFFER = BUFSIZE * 2;
static final int MAX_ITERS = QUEUESIZE * 2;
private static final boolean DEBUG;
static {
String ds = null;
try { ds = System.getProperty("DEBUG_ConcurrentTest"); }
catch(SecurityException e) { }
DEBUG = ds != null;
}
static abstract class Summer extends Thread
{
protected CRC32 sum = new CRC32();
Exception exn;
long getSum()
{
return sum.getValue();
}
protected abstract void checkedRun()
throws IOException, InterruptedException;
public void run()
{
try
{
checkedRun();
}
catch(Exception exn)
{
System.out.println(exn);
exn.printStackTrace();
this.exn = exn;
}
}
}
abstract class Writer extends Summer
{
protected OutputStream os;
Writer init(OutputStream os)
{
this.os = os;
return this;
}
void write(int i) throws IOException
{
os.write(i);
sum.update(i);
if(DEBUG) System.out.println("wrote 1 byte");
}
void write(byte[] buf) throws IOException
{
os.write(buf);
sum.update(buf, 0, buf.length);
if(DEBUG) System.out.println("wrote "+ buf.length+ " bytes");
}
void write(byte[] buf, int off, int len) throws IOException
{
os.write(buf, off, len);
sum.update(buf, off, len);
if(DEBUG) System.out.println("wrote "+ len+ " bytes at "+ off);
}
}
class BoundaryWriter extends Writer
{
protected void checkedRun() throws IOException
{
// write all the byte values (incl overflowed ones) as ints
for(int i = -255; i <= 255; i++)
{
write(i);
}
// attempt to write a sentinel
write(new byte[0]);
write(new byte[0], 0, 0);
// one more byte, then close
write(42);
os.close();
}
}
class RandomWriter extends Writer
{
protected Random rng;
RandomWriter(Random rng)
{
this.rng = rng;
}
void write() throws IOException
{
byte[] bs = new byte[rng.nextInt(MAX_BUFFER)+2];
rng.nextBytes(bs);
switch(rng.nextInt(4))
{
case 0: // write single byte
write(bs[0]);
break;
case 1: // write slice of array
int off = rng.nextInt(bs.length-1);
int len = rng.nextInt(bs.length-off-1)+1;
write(bs, off, len);
break;
default:
write(bs);
}
}
protected void checkedRun() throws IOException
{
for(int i = rng.nextInt(MAX_ITERS) + 5; i >= 0; i--)
{
if(rng.nextBoolean()) yield();
write();
}
os.close();
}
}
class Reader extends Summer
{
protected ArrayBlockingQueue<byte[]> q;
Reader(ArrayBlockingQueue<byte[]> q)
{
this.q = q;
}
protected void checkedRun() throws InterruptedException
{
byte[] bs = q.take();
while(bs.length > 0)
{
if(DEBUG) System.out.println("read "+ bs.length+ " bytes");
sum.update(bs, 0, bs.length);
if(bs.length%11==0) Thread.yield();
bs = q.take();
}
}
}
private void testReadWrite(Writer wr) throws InterruptedException
{
ArrayBlockingQueue<byte[]> q = newQueue();
OutputStream os = create(q);
wr.init(os);
wr.start();
Reader rd = new Reader(q);
rd.run();
wr.join();
Assert.assertNull(wr.exn);
Assert.assertNull(rd.exn);
System.out.printf(" sums %x -> %x\n", wr.getSum(), rd.getSum());
Assert.assertEquals(wr.getSum(), rd.getSum());
}
}