mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2026-05-18 00:00:03 -04:00
481 lines
17 KiB
Java
Executable File
481 lines
17 KiB
Java
Executable File
package com.twelvemonkeys.io;
|
|
|
|
import java.io.ByteArrayInputStream;
|
|
import java.io.EOFException;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
|
|
/**
|
|
* SeekableInputStreamAbstractTestCase
|
|
* <p/>
|
|
*
|
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/SeekableInputStreamAbstractTestCase.java#4 $
|
|
*/
|
|
public abstract class SeekableInputStreamAbstractTestCase extends InputStreamAbstractTestCase implements SeekableInterfaceTest {
|
|
|
|
public SeekableInputStreamAbstractTestCase(String name) {
|
|
super(name);
|
|
}
|
|
|
|
//// TODO: Figure out a better way of creating interface tests without duplicating code
|
|
final SeekableAbstractTestCase mSeekableTestCase = new SeekableAbstractTestCase() {
|
|
protected Seekable createSeekable() {
|
|
return makeInputStream();
|
|
}
|
|
};
|
|
|
|
@Override
|
|
protected SeekableInputStream makeInputStream() {
|
|
return (SeekableInputStream) super.makeInputStream();
|
|
}
|
|
|
|
@Override
|
|
protected SeekableInputStream makeInputStream(final int pSize) {
|
|
return (SeekableInputStream) super.makeInputStream(pSize);
|
|
}
|
|
|
|
protected SeekableInputStream makeInputStream(byte[] pBytes) {
|
|
return makeInputStream(new ByteArrayInputStream(pBytes));
|
|
}
|
|
|
|
protected abstract SeekableInputStream makeInputStream(InputStream pStream);
|
|
|
|
@Override
|
|
public void testResetAfterReset() throws Exception {
|
|
InputStream input = makeInputStream(makeOrderedArray(25));
|
|
|
|
if (!input.markSupported()) {
|
|
return; // Not supported, skip test
|
|
}
|
|
|
|
assertTrue("Expected to read positive value", input.read() >= 0);
|
|
|
|
int readlimit = 5;
|
|
|
|
// Mark
|
|
input.mark(readlimit);
|
|
int read = input.read();
|
|
assertTrue("Expected to read positive value", read >= 0);
|
|
|
|
input.reset();
|
|
assertEquals("Expected value read differes from actual", read, input.read());
|
|
|
|
// Reset after read limit passed, may either throw exception, or reset to last mark
|
|
try {
|
|
input.reset();
|
|
assertEquals("Re-read of reset data should be first", 0, input.read());
|
|
}
|
|
catch (Exception e) {
|
|
assertTrue("Wrong read-limit IOException message", e.getMessage().contains("mark"));
|
|
}
|
|
}
|
|
|
|
public void testSeekable() {
|
|
mSeekableTestCase.testSeekable();
|
|
}
|
|
|
|
public void testFlushBeyondCurrentPos() throws Exception {
|
|
SeekableInputStream seekable = makeInputStream(20);
|
|
|
|
int pos = 10;
|
|
try {
|
|
seekable.flushBefore(pos);
|
|
fail("Flush beyond current position should throw IndexOutOfBoundsException");
|
|
}
|
|
catch (IndexOutOfBoundsException e) {
|
|
// Ignore
|
|
}
|
|
}
|
|
|
|
public void testSeek() throws Exception {
|
|
SeekableInputStream seekable = makeInputStream(55);
|
|
int pos = 37;
|
|
|
|
seekable.seek(pos);
|
|
long streamPos = seekable.getStreamPosition();
|
|
assertEquals("Stream positon should match seeked position", pos, streamPos);
|
|
}
|
|
|
|
public void testSeekFlush() throws Exception {
|
|
SeekableInputStream seekable = makeInputStream(133);
|
|
int pos = 45;
|
|
seekable.seek(pos);
|
|
seekable.flushBefore(pos);
|
|
long flushedPos = seekable.getFlushedPosition();
|
|
assertEquals("Flushed positon should match position", pos, flushedPos);
|
|
|
|
try {
|
|
seekable.seek(pos - 1);
|
|
fail("Read before flushed position succeeded");
|
|
}
|
|
catch (IndexOutOfBoundsException e) {
|
|
// Ignore
|
|
}
|
|
}
|
|
|
|
public void testMarkFlushReset() throws Exception {
|
|
SeekableInputStream seekable = makeInputStream(77);
|
|
|
|
seekable.mark();
|
|
|
|
int position = 55;
|
|
seekable.seek(position);
|
|
seekable.flushBefore(position);
|
|
|
|
try {
|
|
seekable.reset();
|
|
fail("Reset before flushed position succeeded");
|
|
}
|
|
catch (IOException e) {
|
|
// Ignore
|
|
}
|
|
|
|
assertEquals(position, seekable.getStreamPosition());
|
|
}
|
|
|
|
public void testSeekSkipRead() throws Exception {
|
|
SeekableInputStream seekable = makeInputStream(133);
|
|
int pos = 45;
|
|
for (int i = 0; i < 10; i++) {
|
|
seekable.seek(pos);
|
|
//noinspection ResultOfMethodCallIgnored
|
|
seekable.skip(i);
|
|
byte[] bytes = FileUtil.read(seekable);
|
|
assertEquals(133, seekable.getStreamPosition());
|
|
assertEquals(133 - 45- i, bytes.length);
|
|
}
|
|
}
|
|
|
|
public void testSeekSkip(SeekableInputStream pSeekable, String pStr) throws IOException {
|
|
System.out.println();
|
|
pSeekable.seek(pStr.length());
|
|
FileUtil.read(pSeekable);
|
|
for (int i = 0; i < 10; i++) {
|
|
byte[] bytes = FileUtil.read(pSeekable);
|
|
int len = bytes.length;
|
|
if (len != 0) {
|
|
System.err.println("Error in buffer length after full read...");
|
|
System.err.println("len: " + len);
|
|
System.err.println("bytes: \"" + new String(bytes) + "\"");
|
|
break;
|
|
}
|
|
}
|
|
|
|
System.out.println();
|
|
|
|
for (int i = 0; i < 10; i++) {
|
|
pSeekable.seek(0);
|
|
int skip = i * 3;
|
|
//noinspection ResultOfMethodCallIgnored
|
|
pSeekable.skip(skip);
|
|
String str = new String(FileUtil.read(pSeekable));
|
|
System.out.println(str);
|
|
if (str.length() != pStr.length() - skip) {
|
|
throw new Error("Error in buffer length after skip");
|
|
}
|
|
}
|
|
|
|
System.out.println();
|
|
System.out.println("seek/skip ok!");
|
|
System.out.println();
|
|
}
|
|
|
|
protected static void markReset(SeekableInputStream pSeekable) throws IOException {
|
|
for (int i = 0; i < 10; i++) {
|
|
pSeekable.mark();
|
|
System.out.println(new String(FileUtil.read(pSeekable)));
|
|
pSeekable.reset();
|
|
}
|
|
|
|
System.out.println();
|
|
System.out.println("mark/reset ok!");
|
|
}
|
|
|
|
protected static void timeRead(SeekableInputStream pSeekable) throws IOException {
|
|
for (int i = 0; i < 5000; i++) {
|
|
pSeekable.mark();
|
|
FileUtil.read(pSeekable);
|
|
pSeekable.reset();
|
|
}
|
|
|
|
long start = System.currentTimeMillis();
|
|
final int times = 200000;
|
|
for (int i = 0; i < times; i++) {
|
|
pSeekable.mark();
|
|
FileUtil.read(pSeekable);
|
|
pSeekable.reset();
|
|
}
|
|
long time = System.currentTimeMillis() - start;
|
|
|
|
System.out.println("Time; " + time + "ms (" + (time / (float) times) + "ms/inv)");
|
|
}
|
|
|
|
/*
|
|
|
|
// Test code below...
|
|
protected final static String STR = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Fusce massa orci, adipiscing vel, dapibus et, vulputate tristique, tortor. Quisque sodales. Mauris varius turpis et pede. Nam ac dolor vel diam condimentum elementum. Pellentesque eget tellus. Praesent magna. Sed fringilla. Proin ullamcorper tincidunt ante. Fusce dapibus nibh nec dolor. Etiam erat. Nullam dignissim laoreet nibh. Maecenas scelerisque. Pellentesque in quam. Maecenas sollicitudin, magna nec imperdiet facilisis, metus quam tristique ipsum, vitae consequat massa purus eget leo. Nulla ipsum. Proin non purus eget tellus lobortis iaculis. In lorem justo, posuere id, vulputate at, adipiscing ut, nisl. Nunc dui erat, tincidunt ac, interdum quis, rutrum et, libero. Etiam lectus dui, viverra sit amet, elementum ut, malesuada sed, massa. Vestibulum mi nulla, sodales vel, vestibulum sed, congue blandit, velit.";
|
|
|
|
protected static void flushSeek(SeekableInputStream pSeekable, String pStr) throws IOException {
|
|
pSeekable.seek(0);
|
|
pSeekable.mark();
|
|
int pos = pStr.length() / 2;
|
|
try {
|
|
pSeekable.flushBefore(pos);
|
|
System.err.println("Error in flush/seek");
|
|
}
|
|
catch (IndexOutOfBoundsException e) {
|
|
// Ignore
|
|
}
|
|
pSeekable.seek(pos);
|
|
long streamPos = pSeekable.getStreamPosition();
|
|
if (streamPos != pos) {
|
|
System.err.println("Streampos not equal seeked pos");
|
|
}
|
|
|
|
pSeekable.flushBefore(pos);
|
|
long flushedPos = pSeekable.getFlushedPosition();
|
|
if (flushedPos != pos) {
|
|
System.err.println("flushedpos not equal set flushed pos");
|
|
}
|
|
|
|
for (int i = 0; i < 10; i++) {
|
|
pSeekable.seek(pos);
|
|
//noinspection ResultOfMethodCallIgnored
|
|
pSeekable.skip(i);
|
|
System.out.println(new String(FileUtil.read(pSeekable)));
|
|
}
|
|
|
|
try {
|
|
pSeekable.seek(pos - 1);
|
|
System.err.println("Error in flush/seek");
|
|
}
|
|
catch (IndexOutOfBoundsException e) {
|
|
// Ignore
|
|
}
|
|
try {
|
|
pSeekable.reset();
|
|
System.err.println("Error in flush/seek");
|
|
}
|
|
catch (IOException e) {
|
|
// Ignore
|
|
}
|
|
|
|
System.out.println();
|
|
System.out.println("flush/seek ok!");
|
|
}
|
|
|
|
protected static void seekSkip(SeekableInputStream pSeekable, String pStr) throws IOException {
|
|
System.out.println();
|
|
pSeekable.seek(pStr.length());
|
|
FileUtil.read(pSeekable);
|
|
for (int i = 0; i < 10; i++) {
|
|
byte[] bytes = FileUtil.read(pSeekable);
|
|
int len = bytes.length;
|
|
if (len != 0) {
|
|
System.err.println("Error in buffer length after full read...");
|
|
System.err.println("len: " + len);
|
|
System.err.println("bytes: \"" + new String(bytes) + "\"");
|
|
break;
|
|
}
|
|
}
|
|
|
|
System.out.println();
|
|
|
|
for (int i = 0; i < 10; i++) {
|
|
pSeekable.seek(0);
|
|
int skip = i * 3;
|
|
//noinspection ResultOfMethodCallIgnored
|
|
pSeekable.skip(skip);
|
|
String str = new String(FileUtil.read(pSeekable));
|
|
System.out.println(str);
|
|
if (str.length() != pStr.length() - skip) {
|
|
throw new Error("Error in buffer length after skip");
|
|
}
|
|
}
|
|
|
|
System.out.println();
|
|
System.out.println("seek/skip ok!");
|
|
System.out.println();
|
|
}
|
|
|
|
protected static void markReset(SeekableInputStream pSeekable) throws IOException {
|
|
for (int i = 0; i < 10; i++) {
|
|
pSeekable.mark();
|
|
System.out.println(new String(FileUtil.read(pSeekable)));
|
|
pSeekable.reset();
|
|
}
|
|
|
|
System.out.println();
|
|
System.out.println("mark/reset ok!");
|
|
}
|
|
|
|
protected static void timeRead(SeekableInputStream pSeekable) throws IOException {
|
|
for (int i = 0; i < 5000; i++) {
|
|
pSeekable.mark();
|
|
FileUtil.read(pSeekable);
|
|
pSeekable.reset();
|
|
}
|
|
|
|
long start = System.currentTimeMillis();
|
|
final int times = 200000;
|
|
for (int i = 0; i < times; i++) {
|
|
pSeekable.mark();
|
|
FileUtil.read(pSeekable);
|
|
pSeekable.reset();
|
|
}
|
|
long time = System.currentTimeMillis() - start;
|
|
|
|
System.out.println("Time; " + time + "ms (" + (time / (float) times) + "ms/inv)");
|
|
}
|
|
*/
|
|
|
|
public void testReadResetReadDirectBufferBug() throws IOException {
|
|
// Make sure we use the exact size of the buffer
|
|
final int size = 1024;
|
|
|
|
// Fill bytes
|
|
byte[] bytes = new byte[size * 2];
|
|
sRandom.nextBytes(bytes);
|
|
|
|
// Create wrapper stream
|
|
SeekableInputStream stream = makeInputStream(bytes);
|
|
|
|
// Read to fill the buffer, then reset
|
|
int val;
|
|
|
|
val = stream.read();
|
|
assertFalse("Unexepected EOF", val == -1);
|
|
val = stream.read();
|
|
assertFalse("Unexepected EOF", val == -1);
|
|
val = stream.read();
|
|
assertFalse("Unexepected EOF", val == -1);
|
|
val = stream.read();
|
|
assertFalse("Unexepected EOF", val == -1);
|
|
|
|
stream.seek(0);
|
|
|
|
// Read fully and compare
|
|
byte[] result = new byte[size];
|
|
|
|
readFully(stream, result);
|
|
assertTrue(rangeEquals(bytes, 0, result, 0, size));
|
|
|
|
readFully(stream, result);
|
|
assertTrue(rangeEquals(bytes, size, result, 0, size));
|
|
}
|
|
|
|
public void testReadAllByteValuesRegression() throws IOException {
|
|
final int size = 128;
|
|
|
|
// Fill bytes
|
|
byte[] bytes = new byte[256];
|
|
for (int i = 0; i < bytes.length; i++) {
|
|
bytes[i] = (byte) i;
|
|
}
|
|
|
|
// Create wrapper stream
|
|
SeekableInputStream stream = makeInputStream(bytes);
|
|
|
|
// Fill buffer
|
|
byte[] buffer = new byte[size];
|
|
while (stream.read(buffer) >= 0) {
|
|
}
|
|
|
|
stream.seek(0);
|
|
for (int i = 0; i < bytes.length; i += 2) {
|
|
assertEquals("Wrong stream position", i, stream.getStreamPosition());
|
|
int count = stream.read(buffer, 0, 2);
|
|
assertEquals(2, count);
|
|
assertEquals(String.format("Wrong value read at pos %d", stream.getStreamPosition()), bytes[i], buffer[0]);
|
|
assertEquals(String.format("Wrong value read at pos %d", stream.getStreamPosition()), bytes[i + 1], buffer[1]);
|
|
}
|
|
|
|
stream.seek(0);
|
|
for (int i = 0; i < bytes.length; i++) {
|
|
assertEquals("Wrong stream position", i, stream.getStreamPosition());
|
|
int actual = stream.read();
|
|
assertEquals(String.format("Wrong value read at pos %d", stream.getStreamPosition()), bytes[i] & 0xff, actual);
|
|
assertEquals(String.format("Wrong value read at pos %d", stream.getStreamPosition()), bytes[i], (byte) actual);
|
|
}
|
|
|
|
}
|
|
|
|
public void testCloseUnderlyingStream() throws IOException {
|
|
final boolean[] closed = new boolean[1];
|
|
|
|
ByteArrayInputStream input = new ByteArrayInputStream(makeRandomArray(256)) {
|
|
@Override
|
|
public void close() throws IOException {
|
|
closed[0] = true;
|
|
super.close();
|
|
}
|
|
};
|
|
|
|
SeekableInputStream stream = makeInputStream(input);
|
|
|
|
try {
|
|
FileUtil.read(stream); // Read until EOF
|
|
|
|
assertEquals("EOF not reached (test case broken)", -1, stream.read());
|
|
assertFalse("Underlying stream closed before close", closed[0]);
|
|
}
|
|
finally {
|
|
stream.close();
|
|
}
|
|
|
|
assertTrue("Underlying stream not closed", closed[0]);
|
|
|
|
}
|
|
|
|
private void readFully(InputStream pStream, byte[] pResult) throws IOException {
|
|
int pos = 0;
|
|
while (pos < pResult.length) {
|
|
int read = pStream.read(pResult, pos, pResult.length - pos);
|
|
if (read == -1) {
|
|
throw new EOFException();
|
|
}
|
|
pos += read;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test two arrays for range equality. That is, they contain the same elements for some specified range.
|
|
*
|
|
* @param pFirst one array to test for equality
|
|
* @param pFirstOffset the offset into the first array to start testing for equality
|
|
* @param pSecond the other array to test for equality
|
|
* @param pSecondOffset the offset into the second array to start testing for equality
|
|
* @param pLength the length of the range to check for equality
|
|
*
|
|
* @return {@code true} if both arrays are non-{@code null}
|
|
* and have at least {@code offset + pLength} elements
|
|
* and all elements in the range from the first array is equal to the elements from the second array,
|
|
* or if {@code pFirst == pSecond} (including both arrays being {@code null})
|
|
* and {@code pFirstOffset == pSecondOffset}.
|
|
* Otherwise {@code false}.
|
|
*/
|
|
static boolean rangeEquals(byte[] pFirst, int pFirstOffset, byte[] pSecond, int pSecondOffset, int pLength) {
|
|
if (pFirst == pSecond && pFirstOffset == pSecondOffset) {
|
|
return true;
|
|
}
|
|
|
|
if (pFirst == null || pSecond == null) {
|
|
return false;
|
|
}
|
|
|
|
if (pFirst.length < pFirstOffset + pLength || pSecond.length < pSecondOffset + pLength) {
|
|
return false;
|
|
}
|
|
|
|
for (int i = 0; i < pLength; i++) {
|
|
if (pFirst[pFirstOffset + i] != pSecond[pSecondOffset + i]) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
}
|