/*
* Copyright 2009-2010 Brian S O'Neill
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.cojen.dirmi.io;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Random;
import org.junit.*;
import static org.junit.Assert.*;
import org.cojen.dirmi.ClosedException;
/**
*
*
* @author Brian S O'Neill
*/
public class TestPacketOutputStream {
public static void main(String[] args) {
org.junit.runner.JUnitCore.main(TestPacketOutputStream.class.getName());
}
@Test
public void empty() throws Exception {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
Writer pout = new Writer(bout);
Writer recycled = pout.recycle();
assertFalse(pout == recycled);
assertTrue(recycled != null);
byte[] bytes = bout.toByteArray();
assertEquals(1, bytes.length);
assertEquals(0, bytes[0]);
assertNull(pout.recycle());
try {
pout.write(0);
fail();
} catch (ClosedException e) {
}
recycled = recycled.recycle();
assertTrue(recycled != null);
bytes = bout.toByteArray();
assertEquals(2, bytes.length);
assertEquals(0, bytes[0]);
assertEquals(0, bytes[1]);
}
@Test
public void one() throws Exception {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
Writer pout = new Writer(bout);
pout.write(10);
PacketOutputStream recycled = pout.recycle();
assertFalse(pout == recycled);
assertTrue(recycled != null);
byte[] bytes = bout.toByteArray();
assertEquals(1 + 1 + 1, bytes.length);
assertEquals(1, bytes[0]);
assertEquals(10, bytes[1]);
assertEquals(0, bytes[2]);
}
@Test
public void two() throws Exception {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
Writer pout = new Writer(bout);
pout.write(new byte[] {34, 78});
PacketOutputStream recycled = pout.recycle();
assertFalse(pout == recycled);
assertTrue(recycled != null);
byte[] bytes = bout.toByteArray();
assertEquals(1 + 2 + 1, bytes.length);
assertEquals(2, bytes[0]);
assertEquals(34, bytes[1]);
assertEquals(78, bytes[2]);
assertEquals(0, bytes[3]);
}
@Test
public void many() throws Exception {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
Writer pout = new Writer(bout);
byte[] data = new byte[200];
new Random().nextBytes(data);
pout.write(data);
PacketOutputStream recycled = pout.recycle();
assertFalse(pout == recycled);
assertTrue(recycled != null);
byte[] bytes = bout.toByteArray();
assertEquals(2 + 200 + 1, bytes.length);
assertEquals(0x80, bytes[0] & 0xff);
assertEquals(200 - 128, bytes[1]);
assertEquals(0, bytes[bytes.length - 1]);
for (int i=0; i<200; i++) {
assertEquals(data[i], bytes[i + 2]);
}
}
@Test
public void randomBuffering() throws Exception {
// Seeds triggered a bug, so keep using them.
randomBuffering(1262037188096L, 258214034288994L);
}
@Test
public void randomBuffering2() throws Throwable {
long dataSeed = System.currentTimeMillis();
long sizeSeed = System.nanoTime();
try {
randomBuffering(dataSeed, sizeSeed);
} catch (Throwable e) {
System.out.println("Data seed: " + dataSeed);
System.out.println("Size seed: " + sizeSeed);
throw e;
}
}
public void randomBuffering(long dataSeed, long sizeSeed) throws Exception {
Random dataSource = new Random(dataSeed);
PipedOutputStream out = new PipedOutputStream();
Reader reader = new Reader(out, dataSeed);
Thread t = new Thread(reader);
t.start();
Writer pout = new Writer(out, 8192);
Random sizeSource = new Random(sizeSeed);
long total = 0;
try {
for (int i=0; i<10000; i++) {
if (sizeSource.nextInt(100) == 0) {
pout.write(dataSource.nextInt());
total++;
} else {
int size = sizeSource.nextInt(10000) + 1;
byte[] data = new byte[size];
for (int j=0; j<size; j++) {
data[j] = (byte) dataSource.nextInt();
}
pout.write(data);
total += size;
}
}
} catch (IOException e) {
assertEquals(null, reader.mFailed);
throw e;
}
pout.flush();
PacketOutputStream recycled = pout.recycle();
t.join(10000);
assertEquals(null, reader.mFailed);
assertEquals(total, reader.mTotal);
assertFalse(pout == recycled);
assertTrue(recycled != null);
}
static class Writer extends PacketOutputStream<Writer> {
private volatile Writer mRecycled;
Writer(OutputStream out) {
super(out);
}
Writer(OutputStream out, int size) {
super(out, size);
}
private Writer() {
}
@Override
protected Writer newInstance() {
return new Writer();
}
@Override
protected void recycled(Writer newInstance) {
mRecycled = newInstance;
}
Writer recycle() throws IOException {
mRecycled = null;
close();
return mRecycled;
}
Writer getRecycled() {
return mRecycled;
}
}
private static class Reader extends PipedInputStream implements Runnable {
private final Random mDataSource;
volatile long mTotal;
volatile IOException mFailed;
Reader(PipedOutputStream out, long seed) throws IOException {
super(out);
mDataSource = new Random(seed);
}
public void run() {
try {
mTotal = drain();
} catch (IOException e) {
mFailed = e;
close();
}
}
private long drain() throws IOException {
long total = 0;
while (true) {
int size = read();
if (size >= 0x80) {
size = (((size & 0x7f) << 8) | (read() & 0xff)) + 0x80;
}
if (size == 0) {
return total;
}
total += size;
for (int i=0; i<size; i++) {
int b = read();
int expected = mDataSource.nextInt() & 0xff;
if (b != expected) {
throw new IOException("" + expected + " != " + b);
}
}
}
}
}
}