blob: cbf4a8a0aa8b5a8cac19a1b8467e0d72ceeabecd [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.sis.internal.storage.io;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Tests {@link ChannelDataInput}. First, a buffer is filled with random data. Then, a view over a portion
* of that buffer is used for the tests, while the original full buffer is used for comparison purpose.
*
* @author Martin Desruisseaux (Geomatys)
* @version 1.2
* @since 0.3
* @module
*/
public final strictfp class ChannelDataInputTest extends ChannelDataTestCase {
/**
* The implementation to test.
*/
private ChannelDataInput testedStream;
/**
* A stream to use as a reference implementation.
*/
private DataInput referenceStream;
/**
* Fills a buffer with random data and compare the result with a standard image input stream.
* We allocate a small buffer for the {@code ChannelDataInput} in order to force frequent
* interactions between the buffer and the channel.
*
* @throws IOException should never happen since we read and write in memory only.
*/
@Test
public void testAllReadMethods() throws IOException {
final byte[] array = createRandomArray(STREAM_LENGTH);
referenceStream = new DataInputStream(new ByteArrayInputStream(array));
testedStream = new ChannelDataInput("testAllReadMethods",
new DripByteChannel(array, random, 1, 1024),
ByteBuffer.allocate(randomBufferCapacity()), false);
transferRandomData(testedStream, array.length - ARRAY_MAX_LENGTH, 16);
}
/**
* Reads a random unit of data using a method selected randomly.
* This method is invoked (indirectly) by {@link #testAllReadMethods()}.
*/
@Override
final void transferRandomData(final int operation) throws IOException {
final ChannelDataInput t = testedStream;
final DataInput r = referenceStream;
switch (operation) {
default: throw new AssertionError(operation);
case 0: assertEquals("readByte()", r.readByte(), t.readByte()); break;
case 1: assertEquals("readShort()", r.readShort(), t.readShort()); break;
case 2: assertEquals("readUnsignedShort()", r.readUnsignedShort(), t.readUnsignedShort()); break;
case 3: assertEquals("readChar()", r.readChar(), t.readChar()); break;
case 4: assertEquals("readInt()", r.readInt(), t.readInt()); break;
case 5: assertEquals("readUnsignedInt()", r.readInt() & 0xFFFFFFFFL, t.readUnsignedInt()); break;
case 6: assertEquals("readLong()", r.readLong(), t.readLong()); break;
case 7: assertEquals("readFloat()", r.readFloat(), t.readFloat(), 0f); break;
case 8: assertEquals("readDouble()", r.readDouble(), t.readDouble(), 0d); break;
case 9: {
final int n = random.nextInt(ARRAY_MAX_LENGTH);
final byte[] tmp = new byte[n];
r.readFully(tmp);
assertArrayEquals("readBytes(int)", tmp, t.readBytes(n));
break;
}
case 10: {
final int n = random.nextInt(ARRAY_MAX_LENGTH / Character.BYTES);
final char[] tmp = new char[n];
for (int i=0; i<n; i++) tmp[i] = r.readChar();
assertArrayEquals("readChars(int)", tmp, t.readChars(n));
break;
}
case 11: {
final int n = random.nextInt(ARRAY_MAX_LENGTH / Short.BYTES);
final short[] tmp = new short[n];
for (int i=0; i<n; i++) tmp[i] = r.readShort();
assertArrayEquals("readShorts(int)", tmp, t.readShorts(n));
break;
}
case 12: {
final int n = random.nextInt(ARRAY_MAX_LENGTH / Integer.BYTES);
final int[] tmp = new int[n];
for (int i=0; i<n; i++) tmp[i] = r.readInt();
assertArrayEquals("readInts(int)", tmp, t.readInts(n));
break;
}
case 13: {
final int n = random.nextInt(ARRAY_MAX_LENGTH / Long.BYTES);
final long[] tmp = new long[n];
for (int i=0; i<n; i++) tmp[i] = r.readLong();
assertArrayEquals("readLongs(int)", tmp, t.readLongs(n));
break;
}
case 14: {
final int n = random.nextInt(ARRAY_MAX_LENGTH / Float.BYTES);
final float[] tmp = new float[n];
for (int i=0; i<n; i++) tmp[i] = r.readFloat();
assertArrayEquals("readFloats(int)", tmp, t.readFloats(n), 0);
break;
}
case 15: {
final int n = random.nextInt(ARRAY_MAX_LENGTH / Double.BYTES);
final double[] tmp = new double[n];
for (int i=0; i<n; i++) tmp[i] = r.readDouble();
assertArrayEquals("readDoubles(int)", tmp, t.readDoubles(n), 0);
break;
}
}
}
/**
* Tests the {@link ChannelDataInput#readString(int, Charset)} method.
*
* @throws IOException should never happen since we read and write in memory only.
*/
@Test
public void testReadString() throws IOException {
final String expected = "お元気ですか";
final byte[] array = expected.getBytes("UTF-8");
assertEquals(expected.length()*3, array.length); // Sanity check.
final ChannelDataInput input = new ChannelDataInput("testReadString",
new DripByteChannel(array, random, 1, 32),
ByteBuffer.allocate(array.length + 4), false);
assertEquals(expected, input.readString(array.length, StandardCharsets.UTF_8));
assertFalse(input.buffer.hasRemaining());
}
/**
* Tests {@link ChannelDataInput#seek(long)} on a channel that do not implement
* {@link java.nio.channels.SeekableByteChannel}.
*
* @throws IOException should never happen since we read and write in memory only.
*/
@Test
public void testSeekOnForwardOnlyChannel() throws IOException {
int length = random.nextInt(2048) + 1024;
final byte[] array = createRandomArray(length);
length -= Long.BYTES; // Safety against buffer underflow.
final ByteBuffer buffer = ByteBuffer.wrap(array);
final ChannelDataInput input = new ChannelDataInput("testSeekOnForwardOnlyChannel",
new DripByteChannel(array, random, 1, 2048),
ByteBuffer.allocate(random.nextInt(64) + 16), false);
int position = 0;
while (position < length) {
input.seek(position);
assertEquals("getStreamPosition()", position, input.getStreamPosition());
assertEquals(buffer.getLong(position), input.readLong());
position += random.nextInt(128);
}
}
/**
* Tests {@link ChannelDataInput#prefetch()}.
*
* @throws IOException should never happen since we read and write in memory only.
*/
@Test
public void testPrefetch() throws IOException {
final int length = random.nextInt(256) + 128;
final byte[] array = createRandomArray(length);
final ByteBuffer buffer = ByteBuffer.allocate(random.nextInt(64) + 16);
final ChannelDataInput input = new ChannelDataInput("testPrefetch",
new DripByteChannel(array, random, 1, 64), buffer, false);
int position = 0;
while (position != length) {
if (random.nextBoolean()) {
assertEquals(array[position++], input.readByte());
}
/*
* Prefetch a random amount of bytes and verifies the buffer status.
*/
final int p = buffer.position();
final int m = buffer.limit();
final int n = input.prefetch();
assertEquals("Position shall be unchanged.", p, buffer.position());
final int limit = buffer.limit();
if (n >= 0) {
// Usual case.
assertTrue("Limit shall be increased.", limit > m);
} else {
// Buffer is full or channel reached the end of stream.
assertEquals("Limit shall be unchanged", m, limit);
}
/*
* Compare the buffer content with the original data array. The comparison starts
* from the buffer begining, in order to ensure that previous data are unchanged.
*/
final int offset = position - buffer.position();
for (int i=0; i<limit; i++) {
assertEquals(array[offset + i], buffer.get(i));
}
}
}
}