blob: ffe9cb91f38b9b272c4b624f7ab1c71554463f99 [file] [log] [blame]
package org.apache.cassandra.io.util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UTFDataFormatException;
import java.lang.reflect.Field;
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
import java.nio.channels.WritableByteChannel;
import java.util.Random;
import org.junit.Test;
import static org.junit.Assert.*;
public class BufferedDataOutputStreamTest
{
@Test(expected = BufferOverflowException.class)
public void testDataOutputBufferFixedByes() throws Exception
{
try (DataOutputBufferFixed dob = new DataOutputBufferFixed())
{
try
{
for (int ii = 0; ii < 128; ii++)
dob.write(0);
}
catch (BufferOverflowException e)
{
fail("Should not throw BufferOverflowException yet");
}
dob.write(0);
}
}
@Test(expected = BufferOverflowException.class)
public void testDataOutputBufferFixedByteBuffer() throws Exception
{
try (DataOutputBufferFixed dob = new DataOutputBufferFixed())
{
try
{
dob.write(ByteBuffer.allocateDirect(128));
}
catch (BufferOverflowException e)
{
fail("Should not throw BufferOverflowException yet");
}
dob.write(ByteBuffer.allocateDirect(1));
}
}
WritableByteChannel adapter = new WritableByteChannel()
{
@Override
public boolean isOpen() {return true;}
@Override
public void close() throws IOException {}
@Override
public int write(ByteBuffer src) throws IOException
{
int retval = src.remaining();
while (src.hasRemaining())
generated.write(src.get());
return retval;
}
};
BufferedDataOutputStreamPlus fakeStream = new BufferedDataOutputStreamPlus(adapter, 8);
@SuppressWarnings("resource")
@Test(expected = NullPointerException.class)
public void testNullChannel()
{
new BufferedDataOutputStreamPlus((WritableByteChannel)null, 8);
}
@SuppressWarnings("resource")
@Test(expected = IllegalArgumentException.class)
public void testTooSmallBuffer()
{
new BufferedDataOutputStreamPlus(adapter, 7);
}
@Test(expected = NullPointerException.class)
public void testNullBuffer() throws Exception
{
byte type[] = null;
fakeStream.write(type, 0, 1);
}
@Test(expected = IndexOutOfBoundsException.class)
public void testNegativeOffset() throws Exception
{
byte type[] = new byte[10];
fakeStream.write(type, -1, 1);
}
@Test(expected = IndexOutOfBoundsException.class)
public void testNegativeLength() throws Exception
{
byte type[] = new byte[10];
fakeStream.write(type, 0, -1);
}
@Test(expected = IndexOutOfBoundsException.class)
public void testTooBigLength() throws Exception
{
byte type[] = new byte[10];
fakeStream.write(type, 0, 11);
}
@Test(expected = IndexOutOfBoundsException.class)
public void testTooBigLengthWithOffset() throws Exception
{
byte type[] = new byte[10];
fakeStream.write(type, 8, 3);
}
@Test(expected = IndexOutOfBoundsException.class)
public void testTooBigOffset() throws Exception
{
byte type[] = new byte[10];
fakeStream.write(type, 11, 1);
}
static final Random r;
static Field baos_bytes;
static {
long seed = System.nanoTime();
//seed = 210187780999648L;
System.out.println("Seed " + seed);
r = new Random(seed);
try
{
baos_bytes = ByteArrayOutputStream.class.getDeclaredField("buf");
baos_bytes.setAccessible(true);
}
catch (Throwable t)
{
throw new RuntimeException(t);
}
}
private ByteArrayOutputStream generated;
private BufferedDataOutputStreamPlus ndosp;
private ByteArrayOutputStream canonical;
private DataOutputStreamPlus dosp;
void setUp()
{
generated = new ByteArrayOutputStream();
canonical = new ByteArrayOutputStream();
dosp = new WrappedDataOutputStreamPlus(canonical);
ndosp = new BufferedDataOutputStreamPlus(adapter, 4096);
}
@Test
public void testFuzz() throws Exception
{
for (int ii = 0; ii < 30; ii++)
fuzzOnce();
}
String simple = "foobar42";
public static final String twoByte = "\u0180";
public static final String threeByte = "\u34A8";
public static final String fourByte = "\uD841\uDF79";
@SuppressWarnings("unused")
private void fuzzOnce() throws Exception
{
setUp();
int iteration = 0;
int bytesChecked = 0;
int action = 0;
while (generated.size() < 1024 * 1024 * 8)
{
action = r.nextInt(19);
//System.out.println("Action " + action + " iteration " + iteration);
iteration++;
switch (action)
{
case 0:
{
generated.flush();
dosp.flush();
break;
}
case 1:
{
int val = r.nextInt();
dosp.write(val);
ndosp.write(val);
break;
}
case 2:
{
byte randomBytes[] = new byte[r.nextInt(4096 * 2 + 1)];
r.nextBytes(randomBytes);
dosp.write(randomBytes);
ndosp.write(randomBytes);
break;
}
case 3:
{
byte randomBytes[] = new byte[r.nextInt(4096 * 2 + 1)];
r.nextBytes(randomBytes);
int offset = randomBytes.length == 0 ? 0 : r.nextInt(randomBytes.length);
int length = randomBytes.length == 0 ? 0 : r.nextInt(randomBytes.length - offset);
dosp.write(randomBytes, offset, length);
ndosp.write(randomBytes, offset, length);
break;
}
case 4:
{
boolean val = r.nextInt(2) == 0;
dosp.writeBoolean(val);
ndosp.writeBoolean(val);
break;
}
case 5:
{
int val = r.nextInt();
dosp.writeByte(val);
ndosp.writeByte(val);
break;
}
case 6:
{
int val = r.nextInt();
dosp.writeShort(val);
ndosp.writeShort(val);
break;
}
case 7:
{
int val = r.nextInt();
dosp.writeChar(val);
ndosp.writeChar(val);
break;
}
case 8:
{
int val = r.nextInt();
dosp.writeInt(val);
ndosp.writeInt(val);
break;
}
case 9:
{
int val = r.nextInt();
dosp.writeLong(val);
ndosp.writeLong(val);
break;
}
case 10:
{
float val = r.nextFloat();
dosp.writeFloat(val);
ndosp.writeFloat(val);
break;
}
case 11:
{
double val = r.nextDouble();
dosp.writeDouble(val);
ndosp.writeDouble(val);
break;
}
case 12:
{
dosp.writeBytes(simple);
ndosp.writeBytes(simple);
break;
}
case 13:
{
dosp.writeChars(twoByte);
ndosp.writeChars(twoByte);
break;
}
case 14:
{
StringBuilder sb = new StringBuilder();
int length = r.nextInt(500);
//Some times do big strings
if (r.nextDouble() > .95)
length += 4000;
sb.append(simple + twoByte + threeByte + fourByte);
for (int ii = 0; ii < length; ii++)
{
sb.append((char)(r.nextInt() & 0xffff));
}
String str = sb.toString();
writeUTFLegacy(str, dosp);
ndosp.writeUTF(str);
break;
}
case 15:
{
StringBuilder sb = new StringBuilder();
int length = r.nextInt(500);
sb.append("the very model of a modern major general familiar with all things animal vegetable and mineral");
for (int ii = 0; ii < length; ii++)
{
sb.append(' ');
}
String str = sb.toString();
writeUTFLegacy(str, dosp);
ndosp.writeUTF(str);
break;
}
case 16:
{
ByteBuffer buf = ByteBuffer.allocate(r.nextInt(1024 * 8 + 1));
r.nextBytes(buf.array());
buf.position(buf.capacity() == 0 ? 0 : r.nextInt(buf.capacity()));
buf.limit(buf.position() + (buf.capacity() - buf.position() == 0 ? 0 : r.nextInt(buf.capacity() - buf.position())));
ByteBuffer dup = buf.duplicate();
ndosp.write(buf.duplicate());
assertEquals(dup.position(), buf.position());
assertEquals(dup.limit(), buf.limit());
dosp.write(buf.duplicate());
break;
}
case 17:
{
ByteBuffer buf = ByteBuffer.allocateDirect(r.nextInt(1024 * 8 + 1));
while (buf.hasRemaining())
buf.put((byte)r.nextInt());
buf.position(buf.capacity() == 0 ? 0 : r.nextInt(buf.capacity()));
buf.limit(buf.position() + (buf.capacity() - buf.position() == 0 ? 0 : r.nextInt(buf.capacity() - buf.position())));
ByteBuffer dup = buf.duplicate();
ndosp.write(buf.duplicate());
assertEquals(dup.position(), buf.position());
assertEquals(dup.limit(), buf.limit());
dosp.write(buf.duplicate());
break;
}
case 18:
{
try (Memory buf = Memory.allocate(r.nextInt(1024 * 8 - 1) + 1);)
{
for (int ii = 0; ii < buf.size(); ii++)
buf.setByte(ii, (byte)r.nextInt());
long offset = buf.size() == 0 ? 0 : r.nextInt((int)buf.size());
long length = (buf.size() - offset == 0 ? 0 : r.nextInt((int)(buf.size() - offset)));
ndosp.write(buf, offset, length);
dosp.write(buf, offset, length);
}
break;
}
default:
fail("Shouldn't reach here");
}
//bytesChecked = assertSameOutput(bytesChecked, action, iteration);
}
assertSameOutput(0, -1, iteration);
}
public static void writeUTFLegacy(String str, OutputStream out) throws IOException
{
int utfCount = 0, length = str.length();
for (int i = 0; i < length; i++)
{
int charValue = str.charAt(i);
if (charValue > 0 && charValue <= 127)
{
utfCount++;
}
else if (charValue <= 2047)
{
utfCount += 2;
}
else
{
utfCount += 3;
}
}
if (utfCount > 65535)
{
throw new UTFDataFormatException(); //$NON-NLS-1$
}
byte utfBytes[] = new byte[utfCount + 2];
int utfIndex = 2;
for (int i = 0; i < length; i++)
{
int charValue = str.charAt(i);
if (charValue > 0 && charValue <= 127)
{
utfBytes[utfIndex++] = (byte) charValue;
}
else if (charValue <= 2047)
{
utfBytes[utfIndex++] = (byte) (0xc0 | (0x1f & (charValue >> 6)));
utfBytes[utfIndex++] = (byte) (0x80 | (0x3f & charValue));
}
else
{
utfBytes[utfIndex++] = (byte) (0xe0 | (0x0f & (charValue >> 12)));
utfBytes[utfIndex++] = (byte) (0x80 | (0x3f & (charValue >> 6)));
utfBytes[utfIndex++] = (byte) (0x80 | (0x3f & charValue));
}
}
utfBytes[0] = (byte) (utfCount >> 8);
utfBytes[1] = (byte) utfCount;
out.write(utfBytes);
}
private int assertSameOutput(int bytesChecked, int lastAction, int iteration) throws Exception
{
ndosp.flush();
dosp.flush();
byte generatedBytes[] = (byte[])baos_bytes.get(generated);
byte canonicalBytes[] = (byte[])baos_bytes.get(canonical);
int count = generated.size();
if (count != canonical.size())
System.out.println("Failed at " + bytesChecked + " last action " + lastAction + " iteration " + iteration);
assertEquals(count, canonical.size());
for (;bytesChecked < count; bytesChecked++)
{
byte generatedByte = generatedBytes[bytesChecked];
byte canonicalByte = canonicalBytes[bytesChecked];
if (generatedByte != canonicalByte)
System.out.println("Failed at " + bytesChecked + " last action " + lastAction + " iteration " + iteration);
assertEquals(generatedByte, canonicalByte);
}
return count;
}
@Test
public void testWriteUTF() throws Exception
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutput dataOut = new DataOutputStream(baos);
StringBuilder sb = new StringBuilder(65535);
for (int ii = 0; ii < 1 << 16; ii++)
{
String s = sb.toString();
UnbufferedDataOutputStreamPlus.writeUTF(s, dataOut);
DataInput dataIn = new DataInputStream(new ByteArrayInputStream(baos.toByteArray()));
assertEquals(s, dataIn.readUTF());
baos.reset();
sb.append("a");
}
}
@Test
public void testWriteUTFBigChar() throws Exception
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutput dataOut = new DataOutputStream(baos);
StringBuilder sb = new StringBuilder(65535);
for (int ii = 0; ii < 1 << 15; ii++)
{
String s = sb.toString();
UnbufferedDataOutputStreamPlus.writeUTF(s, dataOut);
DataInput dataIn = new DataInputStream(new ByteArrayInputStream(baos.toByteArray()));
assertEquals(s, dataIn.readUTF());
baos.reset();
if (ii == (1 << 15) - 1)
sb.append("a");
else
sb.append(twoByte);
}
}
}