package net.contrapunctus.lzma;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Random;
import java.util.concurrent.ArrayBlockingQueue;
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.ConcurrentBufferInputStream.create;
import static net.contrapunctus.lzma.ConcurrentBufferOutputStream.newQueue;
import static net.contrapunctus.lzma.ConcurrentBufferOutputTest.*;
@RunWith(Parameterized.class)
public class ConcurrentBufferInputTest
{
@Parameters public static Collection<Object[]> parameters()
throws IOException
{
Collection<Object[]> args = new ArrayList<Object[]>();
args.add(new Object[] { System.currentTimeMillis(), true });
args.add(new Object[] { System.currentTimeMillis(), false });
args.add(new Object[] { 1251234417455L, false });
return args;
}
public static void main(String[] args) throws InterruptedException
{
new ConcurrentBufferInputTest(Long.parseLong(args[0]),
Boolean.parseBoolean(args[1])).run();
}
boolean boundary;
long seed;
public ConcurrentBufferInputTest(long seed, boolean boundary)
{
this.seed = seed;
this.boundary = boundary;
}
public String toString()
{
return (boundary? "boundary" : "random") +
" seed " + seed + 'L';
}
@Test(timeout=5000) public void run() throws InterruptedException
{
System.out.printf("%s: ", this);
Random rng = new Random(seed);
if(boundary)
{
testReadWrite(rng, new BoundaryWriter());
}
else
{
Random rng2 = new Random(rng.nextLong());
testReadWrite(rng, new RandomWriter(rng2));
}
}
abstract class Writer extends Summer
{
protected ArrayBlockingQueue<byte[]> q;
Writer init(ArrayBlockingQueue<byte[]> q)
{
this.q = q;
return this;
}
void write(int i) throws InterruptedException
{
byte b[] = new byte[1];
b[0] = (byte) i;
q.put(b);
sum.update(i);
}
void write(byte[] buf) throws InterruptedException
{
q.put(buf);
sum.update(buf, 0, buf.length);
}
}
class BoundaryWriter extends Writer
{
protected void checkedRun() throws InterruptedException
{
// write all the byte values (incl overflowed ones) as ints
for(int i = -255; i <= 255; i++)
{
write(i);
}
write(42); // one more byte
write(new byte[0]); // sentinel
}
}
class RandomWriter extends Writer
{
protected Random rng;
RandomWriter(Random rng)
{
this.rng = rng;
}
void write() throws InterruptedException
{
byte[] bs = new byte[rng.nextInt(MAX_BUFFER)+1];
rng.nextBytes(bs);
switch(rng.nextInt(4))
{
case 0: // single byte
write(bs[0]);
break;
default:
write(bs);
}
}
protected void checkedRun() throws InterruptedException
{
for(int i = rng.nextInt(MAX_ITERS)+5; i >= 0; i--)
{
if(rng.nextBoolean()) yield();
write();
}
// write sentinel
q.put(new byte[0]);
}
}
class Reader extends Summer
{
protected Random rng;
protected InputStream is;
Reader(Random rng, InputStream is)
{
this.rng = rng;
this.is = is;
}
boolean read() throws IOException
{
byte[] bs = new byte[rng.nextInt(MAX_BUFFER)+2];
int n;
switch(rng.nextInt(4))
{
case 0: // single byte
int b = is.read();
if(b != -1)
{
sum.update(b);
return true;
}
return false;
case 1: // slice of array
int off = rng.nextInt(bs.length-1);
int len = rng.nextInt(bs.length-off-1)+1;
n = is.read(bs, off, len);
if(n != -1)
{
sum.update(bs, off, n);
return true;
}
return false;
default: // entire array
n = is.read(bs);
if(n != -1)
{
sum.update(bs, 0, n);
return true;
}
return false;
}
}
protected void checkedRun() throws IOException
{
while(read())
{
}
is.close();
}
}
private void testReadWrite(Random rng, Writer wr) throws InterruptedException
{
ArrayBlockingQueue<byte[]> q = newQueue();
InputStream is = create(q);
wr.init(q);
wr.start();
Reader rd = new Reader(rng, is);
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());
}
}