blob: ca52de9a6d12eea376600610393478d3fcb0eae0 [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.commons.compress.utils;
import org.junit.Ignore;
import org.junit.Test;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SeekableByteChannel;
import java.util.Arrays;
import static java.nio.charset.StandardCharsets.*;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
public class SeekableInMemoryByteChannelTest {
private final byte[] testData = "Some data".getBytes(UTF_8);
@Test
public void shouldReadContentsProperly() throws IOException {
//given
final SeekableInMemoryByteChannel c = new SeekableInMemoryByteChannel(testData);
final ByteBuffer readBuffer = ByteBuffer.allocate(testData.length);
//when
final int readCount = c.read(readBuffer);
//then
assertEquals(testData.length, readCount);
assertArrayEquals(testData, readBuffer.array());
assertEquals(testData.length, c.position());
c.close();
}
@Test
public void shouldReadContentsWhenBiggerBufferSupplied() throws IOException {
//given
final SeekableInMemoryByteChannel c = new SeekableInMemoryByteChannel(testData);
final ByteBuffer readBuffer = ByteBuffer.allocate(testData.length + 1);
//when
final int readCount = c.read(readBuffer);
//then
assertEquals(testData.length, readCount);
assertArrayEquals(testData, Arrays.copyOf(readBuffer.array(), testData.length));
assertEquals(testData.length, c.position());
c.close();
}
@Test
public void shouldReadDataFromSetPosition() throws IOException {
//given
final SeekableInMemoryByteChannel c = new SeekableInMemoryByteChannel(testData);
final ByteBuffer readBuffer = ByteBuffer.allocate(4);
//when
c.position(5L);
final int readCount = c.read(readBuffer);
//then
assertEquals(4L, readCount);
assertEquals("data", new String(readBuffer.array(), UTF_8));
assertEquals(testData.length, c.position());
c.close();
}
@Test
public void shouldSignalEOFWhenPositionAtTheEnd() throws IOException {
//given
final SeekableInMemoryByteChannel c = new SeekableInMemoryByteChannel(testData);
final ByteBuffer readBuffer = ByteBuffer.allocate(testData.length);
//when
c.position(testData.length + 1);
final int readCount = c.read(readBuffer);
//then
assertEquals(0L, readBuffer.position());
assertEquals(-1, readCount);
assertEquals(-1, c.read(readBuffer));
c.close();
}
@Test(expected = ClosedChannelException.class)
public void shouldThrowExceptionOnReadingClosedChannel() throws IOException {
//given
final SeekableInMemoryByteChannel c = new SeekableInMemoryByteChannel();
//when
c.close();
c.read(ByteBuffer.allocate(1));
}
@Test
public void shouldWriteDataProperly() throws IOException {
//given
final SeekableInMemoryByteChannel c = new SeekableInMemoryByteChannel();
final ByteBuffer inData = ByteBuffer.wrap(testData);
//when
final int writeCount = c.write(inData);
//then
assertEquals(testData.length, writeCount);
assertArrayEquals(testData, Arrays.copyOf(c.array(), (int) c.size()));
assertEquals(testData.length, c.position());
c.close();
}
@Test
public void shouldWriteDataProperlyAfterPositionSet() throws IOException {
//given
final SeekableInMemoryByteChannel c = new SeekableInMemoryByteChannel(testData);
final ByteBuffer inData = ByteBuffer.wrap(testData);
final ByteBuffer expectedData = ByteBuffer.allocate(testData.length + 5).put(testData, 0, 5).put(testData);
//when
c.position(5L);
final int writeCount = c.write(inData);
//then
assertEquals(testData.length, writeCount);
assertArrayEquals(expectedData.array(), Arrays.copyOf(c.array(), (int) c.size()));
assertEquals(testData.length + 5, c.position());
c.close();
}
@Test(expected = ClosedChannelException.class)
public void shouldThrowExceptionOnWritingToClosedChannel() throws IOException {
//given
final SeekableInMemoryByteChannel c = new SeekableInMemoryByteChannel();
//when
c.close();
c.write(ByteBuffer.allocate(1));
}
@Test
public void shouldTruncateContentsProperly() {
//given
final SeekableInMemoryByteChannel c = new SeekableInMemoryByteChannel(testData);
//when
c.truncate(4);
//then
final byte[] bytes = Arrays.copyOf(c.array(), (int) c.size());
assertEquals("Some", new String(bytes, UTF_8));
c.close();
}
@Test
public void shouldSetProperPositionOnTruncate() throws IOException {
//given
final SeekableInMemoryByteChannel c = new SeekableInMemoryByteChannel(testData);
//when
c.position(testData.length);
c.truncate(4L);
//then
assertEquals(4L, c.position());
assertEquals(4L, c.size());
c.close();
}
@Test
public void shouldSetProperPosition() throws IOException {
//given
final SeekableInMemoryByteChannel c = new SeekableInMemoryByteChannel(testData);
//when
final long posAtFour = c.position(4L).position();
final long posAtTheEnd = c.position(testData.length).position();
final long posPastTheEnd = c.position(testData.length + 1L).position();
//then
assertEquals(4L, posAtFour);
assertEquals(c.size(), posAtTheEnd);
assertEquals(testData.length + 1L, posPastTheEnd);
c.close();
}
@Test(expected = IOException.class)
public void shouldThrowExceptionWhenSettingIncorrectPosition() throws IOException {
//given
final SeekableInMemoryByteChannel c = new SeekableInMemoryByteChannel();
//when
c.position(Integer.MAX_VALUE + 1L);
c.close();
}
@Test(expected = IllegalArgumentException.class)
public void shouldThrowExceptionWhenTruncatingToIncorrectSize() {
//given
final SeekableInMemoryByteChannel c = new SeekableInMemoryByteChannel();
//when
c.truncate(Integer.MAX_VALUE + 1L);
c.close();
}
// Contract Tests added in response to https://issues.apache.org/jira/browse/COMPRESS-499
// https://docs.oracle.com/javase/7/docs/api/java/io/Closeable.html#close()
/*
* <q>If the stream is already closed then invoking this method has no effect.</q>
*/
@Test
public void closeIsIdempotent() throws Exception {
try (SeekableByteChannel c = new SeekableInMemoryByteChannel()) {
c.close();
assertFalse(c.isOpen());
c.close();
assertFalse(c.isOpen());
}
}
// https://docs.oracle.com/javase/7/docs/api/java/nio/channels/SeekableByteChannel.html#position()
/*
* <q>ClosedChannelException - If this channel is closed</q>
*/
@Test(expected = ClosedChannelException.class)
@Ignore("we deliberately violate the spec")
public void throwsClosedChannelExceptionWhenPositionIsReadOnClosedChannel() throws Exception {
try (SeekableByteChannel c = new SeekableInMemoryByteChannel()) {
c.close();
c.position();
}
}
// https://docs.oracle.com/javase/7/docs/api/java/nio/channels/SeekableByteChannel.html#size()
/*
* <q>ClosedChannelException - If this channel is closed</q>
*/
@Test(expected = ClosedChannelException.class)
@Ignore("we deliberately violate the spec")
public void throwsClosedChannelExceptionWhenSizeIsReadOnClosedChannel() throws Exception {
try (SeekableByteChannel c = new SeekableInMemoryByteChannel()) {
c.close();
c.size();
}
}
// https://docs.oracle.com/javase/7/docs/api/java/nio/channels/SeekableByteChannel.html#position(long)
/*
* <q>ClosedChannelException - If this channel is closed</q>
*/
@Test(expected = ClosedChannelException.class)
public void throwsClosedChannelExceptionWhenPositionIsSetOnClosedChannel() throws Exception {
try (SeekableByteChannel c = new SeekableInMemoryByteChannel()) {
c.close();
c.position(0);
}
}
/*
* <q>Setting the position to a value that is greater than the current size is legal but does not change the size of
* the entity. A later attempt to read bytes at such a position will immediately return an end-of-file
* indication</q>
*/
@Test
public void readingFromAPositionAfterEndReturnsEOF() throws Exception {
try (SeekableByteChannel c = new SeekableInMemoryByteChannel()) {
c.position(2);
assertEquals(2, c.position());
final ByteBuffer readBuffer = ByteBuffer.allocate(5);
assertEquals(-1, c.read(readBuffer));
}
}
/*
* <q>Setting the position to a value that is greater than the current size is legal but does not change the size of
* the entity. A later attempt to write bytes at such a position will cause the entity to grow to accommodate the
* new bytes; the values of any bytes between the previous end-of-file and the newly-written bytes are
* unspecified.</q>
*/
public void writingToAPositionAfterEndGrowsChannel() throws Exception {
try (SeekableByteChannel c = new SeekableInMemoryByteChannel()) {
c.position(2);
assertEquals(2, c.position());
final ByteBuffer inData = ByteBuffer.wrap(testData);
assertEquals(testData.length, c.write(inData));
assertEquals(testData.length + 2, c.size());
c.position(2);
final ByteBuffer readBuffer = ByteBuffer.allocate(testData.length);
c.read(readBuffer);
assertArrayEquals(testData, Arrays.copyOf(readBuffer.array(), testData.length));
}
}
/*
* <q>IOException - If the new position is negative</q>
*/
@Test(expected = IOException.class)
public void throwsIOExceptionWhenPositionIsSetToANegativeValue() throws Exception {
try (SeekableByteChannel c = new SeekableInMemoryByteChannel()) {
c.position(-1);
}
}
// https://docs.oracle.com/javase/7/docs/api/java/nio/channels/SeekableByteChannel.html#truncate(long)
/*
* <q>If the given size is greater than or equal to the current size then the entity is not modified.</q>
*/
@Test
public void truncateToCurrentSizeDoesntChangeAnything() throws Exception {
try (SeekableByteChannel c = new SeekableInMemoryByteChannel(testData)) {
assertEquals(testData.length, c.size());
c.truncate(testData.length);
assertEquals(testData.length, c.size());
final ByteBuffer readBuffer = ByteBuffer.allocate(testData.length);
assertEquals(testData.length, c.read(readBuffer));
assertArrayEquals(testData, Arrays.copyOf(readBuffer.array(), testData.length));
}
}
/*
* <q>If the given size is greater than or equal to the current size then the entity is not modified.</q>
*/
@Test
public void truncateToBiggerSizeDoesntChangeAnything() throws Exception {
try (SeekableByteChannel c = new SeekableInMemoryByteChannel(testData)) {
assertEquals(testData.length, c.size());
c.truncate(testData.length + 1);
assertEquals(testData.length, c.size());
final ByteBuffer readBuffer = ByteBuffer.allocate(testData.length);
assertEquals(testData.length, c.read(readBuffer));
assertArrayEquals(testData, Arrays.copyOf(readBuffer.array(), testData.length));
}
}
/*
* <q> In either case, if the current position is greater than the given size then it is set to that size.</q>
*/
@Test
public void truncateDoesntChangeSmallPosition() throws Exception {
try (SeekableByteChannel c = new SeekableInMemoryByteChannel(testData)) {
c.position(1);
c.truncate(testData.length - 1);
assertEquals(testData.length - 1, c.size());
assertEquals(1, c.position());
}
}
/*
* <q> In either case, if the current position is greater than the given size then it is set to that size.</q>
*/
@Test
public void truncateMovesPositionWhenShrinkingBeyondPosition() throws Exception {
try (SeekableByteChannel c = new SeekableInMemoryByteChannel(testData)) {
c.position(4);
c.truncate(3);
assertEquals(3, c.size());
assertEquals(3, c.position());
}
}
/*
* <q> In either case, if the current position is greater than the given size then it is set to that size.</q>
*/
@Test
public void truncateMovesPositionWhenNotResizingButPositionBiggerThanSize() throws Exception {
try (SeekableByteChannel c = new SeekableInMemoryByteChannel(testData)) {
c.position(2 * testData.length);
c.truncate(testData.length);
assertEquals(testData.length, c.size());
assertEquals(testData.length, c.position());
}
}
/*
* <q> In either case, if the current position is greater than the given size then it is set to that size.</q>
*/
@Test
public void truncateMovesPositionWhenNewSizeIsBiggerThanSizeAndPositionIsEvenBigger() throws Exception {
try (SeekableByteChannel c = new SeekableInMemoryByteChannel(testData)) {
c.position(2 * testData.length);
c.truncate(testData.length + 1);
assertEquals(testData.length, c.size());
assertEquals(testData.length + 1, c.position());
}
}
/*
* <q>IllegalArgumentException - If the new position is negative</q>
*/
@Test(expected = IllegalArgumentException.class)
public void throwsIllegalArgumentExceptionWhenTruncatingToANegativeSize() throws Exception {
try (SeekableByteChannel c = new SeekableInMemoryByteChannel()) {
c.truncate(-1);
}
}
/*
* <q>ClosedChannelException - If this channel is closed</q>
*/
@Test(expected = ClosedChannelException.class)
@Ignore("we deliberately violate the spec")
public void throwsClosedChannelExceptionWhenTruncateIsCalledOnClosedChannel() throws Exception {
try (SeekableByteChannel c = new SeekableInMemoryByteChannel()) {
c.close();
c.truncate(0);
}
}
}