blob: b7aa5ecc86b9286a2e4c7a639cf86e4026d991cb [file] [log] [blame]
// Copyright 2017 Google Inc.
//
// 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 com.google.crypto.tink.subtle;
import static com.google.crypto.tink.testing.TestUtil.assertExceptionContains;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import com.google.crypto.tink.testing.StreamingTestUtil.PseudorandomReadableByteChannel;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.ReadableByteChannel;
import java.util.Arrays;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Tests for RewindableReadableByteChannel */
@RunWith(JUnit4.class)
public class RewindableReadableByteChannelTest {
@SuppressWarnings("GuardedBy")
@Test
public void testSingleReadsOfVariousLengths() throws Exception {
int inputSize = 1234;
ReadableByteChannel baseChannel = new PseudorandomReadableByteChannel(inputSize);
assertTrue(baseChannel.isOpen());
RewindableReadableByteChannel rewindableChannel =
new RewindableReadableByteChannel(baseChannel);
// TODO(b/145386688): This access should be guarded by 'rewindableChannel', which is not
// currently held
assertTrue(rewindableChannel.isOpen());
// Read some initial bytes.
int buffer1Size = 42;
ByteBuffer buffer1 = ByteBuffer.allocate(buffer1Size);
// TODO(b/145386688): This access should be guarded by 'rewindableChannel', which is not
// currently held
assertEquals(buffer1Size, rewindableChannel.read(buffer1));
// Rewind, and read a shorter sequence of initial bytes.
// TODO(b/145386688): This access should be guarded by 'rewindableChannel', which is not
// currently held
rewindableChannel.rewind();
int buffer2Size = 40;
ByteBuffer buffer2 = ByteBuffer.allocate(buffer2Size);
// TODO(b/145386688): This access should be guarded by 'rewindableChannel', which is not
// currently held
assertEquals(buffer2Size, rewindableChannel.read(buffer2));
assertArrayEquals(buffer2.array(), Arrays.copyOfRange(buffer1.array(), 0, buffer2Size));
// Rewind, and read a longer sequence of initial bytes.
// TODO(b/145386688): This access should be guarded by 'rewindableChannel', which is not
// currently held
rewindableChannel.rewind();
int buffer3Size = 60;
ByteBuffer buffer3 = ByteBuffer.allocate(buffer3Size);
// TODO(b/145386688): This access should be guarded by 'rewindableChannel', which is not
// currently held
assertEquals(buffer3Size, rewindableChannel.read(buffer3));
assertArrayEquals(buffer1.array(), Arrays.copyOfRange(buffer3.array(), 0, buffer1Size));
// Read all the remaining bytes.
int buffer4Size = inputSize - buffer3Size;
ByteBuffer buffer4 = ByteBuffer.allocate(buffer4Size);
// TODO(b/145386688): This access should be guarded by 'rewindableChannel', which is not
// currently held
assertEquals(buffer4Size, rewindableChannel.read(buffer4));
// Check that no more bytes are left.
ByteBuffer buffer5 = ByteBuffer.allocate(inputSize);
// TODO(b/145386688): This access should be guarded by 'rewindableChannel', which is not
// currently held
assertEquals(-1, rewindableChannel.read(buffer5));
// Rewind, and read the entire file again.
// TODO(b/145386688): This access should be guarded by 'rewindableChannel', which is not
// currently held
rewindableChannel.rewind();
// TODO(b/145386688): This access should be guarded by 'rewindableChannel', which is not
// currently held
assertEquals(inputSize, rewindableChannel.read(buffer5));
assertArrayEquals(buffer4.array(),
Arrays.copyOfRange(buffer5.array(), buffer3Size, inputSize));
// Close the channel.
// TODO(b/145386688): This access should be guarded by 'rewindableChannel', which is not
// currently held
rewindableChannel.close();
// TODO(b/145386688): This access should be guarded by 'rewindableChannel', which is not
// currently held
assertFalse(rewindableChannel.isOpen());
assertFalse(baseChannel.isOpen());
// Try rewinding or reading after closing.
try {
// TODO(b/145386688): This access should be guarded by 'rewindableChannel', which is not
// currently held
rewindableChannel.rewind();
fail("Should have thrown exception, as cannot rewind after closing.");
} catch (IOException expected) {
assertExceptionContains(expected, "Cannot rewind");
}
ByteBuffer buffer6 = ByteBuffer.allocate(42);
try {
@SuppressWarnings("GuardedBy")
// TODO(b/145386688): This access should be guarded by 'rewindableChannel', which is not
// currently held
int unused = rewindableChannel.read(buffer6);
fail("Should have thrown exception, as cannot read after closing.");
} catch (ClosedChannelException expected) {
}
}
@SuppressWarnings("GuardedBy")
@Test
public void testSubsequentReads() throws Exception {
int inputSize = 1234;
ReadableByteChannel baseChannel = new PseudorandomReadableByteChannel(inputSize);
assertTrue(baseChannel.isOpen());
RewindableReadableByteChannel rewindableChannel =
new RewindableReadableByteChannel(baseChannel);
// TODO(b/145386688): This access should be guarded by 'rewindableChannel', which is not
// currently held
assertTrue(rewindableChannel.isOpen());
// Read some initial bytes.
int buffer1Size = 105;
ByteBuffer buffer1 = ByteBuffer.allocate(buffer1Size);
int limit1 = 42;
buffer1.limit(limit1);
// TODO(b/145386688): This access should be guarded by 'rewindableChannel', which is not
// currently held
assertEquals(limit1, rewindableChannel.read(buffer1));
// Continue reading until the buffer is full.
buffer1.limit(buffer1.capacity());
// TODO(b/145386688): This access should be guarded by 'rewindableChannel', which is not
// currently held
assertEquals(buffer1Size - limit1, rewindableChannel.read(buffer1));
// Rewind, and read a longer sequence of initial bytes.
// TODO(b/145386688): This access should be guarded by 'rewindableChannel', which is not
// currently held
rewindableChannel.rewind();
int buffer2Size = 160;
ByteBuffer buffer2 = ByteBuffer.allocate(buffer2Size);
// TODO(b/145386688): This access should be guarded by 'rewindableChannel', which is not
// currently held
assertEquals(buffer2Size, rewindableChannel.read(buffer2));
assertArrayEquals(buffer1.array(), Arrays.copyOfRange(buffer2.array(), 0, buffer1Size));
// Rewind, and read a longer sequence in multiple steps.
// TODO(b/145386688): This access should be guarded by 'rewindableChannel', which is not
// currently held
rewindableChannel.rewind();
int buffer3Size = 150;
ByteBuffer buffer3 = ByteBuffer.allocate(buffer3Size);
int stepCount = 5;
int blockSize = buffer3Size / stepCount;
for (int i = 1; i <= stepCount; i++) {
buffer3.limit(i * blockSize);
// TODO(b/145386688): This access should be guarded by 'rewindableChannel', which is not
// currently held
assertEquals(blockSize, rewindableChannel.read(buffer3));
}
assertArrayEquals(buffer3.array(), Arrays.copyOfRange(buffer2.array(), 0, buffer3Size));
// Read the remaining bytes and check the size;
ByteBuffer buffer4 = ByteBuffer.allocate(inputSize);
// TODO(b/145386688): This access should be guarded by 'rewindableChannel', which is not
// currently held
assertEquals(inputSize - buffer3Size, rewindableChannel.read(buffer4));
// TODO(b/145386688): This access should be guarded by 'rewindableChannel', which is not
// currently held
assertEquals(-1, rewindableChannel.read(buffer4));
// Close the channel.
// TODO(b/145386688): This access should be guarded by 'rewindableChannel', which is not
// currently held
rewindableChannel.close();
// TODO(b/145386688): This access should be guarded by 'rewindableChannel', which is not
// currently held
assertFalse(rewindableChannel.isOpen());
assertFalse(baseChannel.isOpen());
}
@SuppressWarnings("GuardedBy")
@Test
public void testDisableRewind() throws Exception {
int blockSize = PseudorandomReadableByteChannel.BLOCK_SIZE;
int extraSize = 123;
int blockCount = 5;
int inputSize = blockSize * blockCount + extraSize;
ReadableByteChannel baseChannel = new PseudorandomReadableByteChannel(inputSize);
assertTrue(baseChannel.isOpen());
RewindableReadableByteChannel rewindableChannel =
new RewindableReadableByteChannel(baseChannel);
// TODO(b/145386688): This access should be guarded by 'rewindableChannel', which is not
// currently held
assertTrue(rewindableChannel.isOpen());
// Read two blocks.
ByteBuffer twoBlocksBuffer = ByteBuffer.allocate(2 * blockSize);
// TODO(b/145386688): This access should be guarded by 'rewindableChannel', which is not
// currently held
assertEquals(2 * blockSize, rewindableChannel.read(twoBlocksBuffer));
// Verify that the read bytes are not all the same.
assertFalse(Arrays.equals(Arrays.copyOfRange(twoBlocksBuffer.array(), 0, 42),
Arrays.copyOfRange(twoBlocksBuffer.array(), 42, 2 * 42)));
// Rewind and read 1 block + extraSize;
// TODO(b/145386688): This access should be guarded by 'rewindableChannel', which is not
// currently held
rewindableChannel.rewind();
ByteBuffer blockAndExtraBuffer = ByteBuffer.allocate(blockSize + extraSize);
// TODO(b/145386688): This access should be guarded by 'rewindableChannel', which is not
// currently held
assertEquals(blockSize + extraSize, rewindableChannel.read(blockAndExtraBuffer));
assertArrayEquals(blockAndExtraBuffer.array(),
Arrays.copyOfRange(twoBlocksBuffer.array(), 0, blockSize + extraSize));
// Disable the rewinding feature, and continue reading.
// TODO(b/145386688): This access should be guarded by 'rewindableChannel', which is not
// currently held
rewindableChannel.disableRewinding();
try {
// TODO(b/145386688): This access should be guarded by 'rewindableChannel', which is not
// currently held
rewindableChannel.rewind();
fail("Should have thrown exception, as rewinding has been dropped");
} catch (IOException expected) {
assertExceptionContains(expected, "Cannot rewind");
}
ByteBuffer oneBlockBuffer = ByteBuffer.allocate(blockSize);
// TODO(b/145386688): This access should be guarded by 'rewindableChannel', which is not
// currently held
assertEquals(blockSize, rewindableChannel.read(oneBlockBuffer));
assertArrayEquals(oneBlockBuffer.array(),
Arrays.copyOfRange(twoBlocksBuffer.array(), extraSize, blockSize + extraSize));
int remainingSize = (blockCount - 2) * blockSize;
ByteBuffer remainingBuffer = ByteBuffer.allocate(remainingSize);
// TODO(b/145386688): This access should be guarded by 'rewindableChannel', which is not
// currently held
assertEquals(remainingSize, rewindableChannel.read(remainingBuffer));
assertArrayEquals(blockAndExtraBuffer.array(),
Arrays.copyOfRange(remainingBuffer.array(),
remainingSize - blockSize - extraSize, remainingSize));
// Check EOF.
ByteBuffer buffer = ByteBuffer.allocate(42);
// TODO(b/145386688): This access should be guarded by 'rewindableChannel', which is not
// currently held
assertEquals(-1, rewindableChannel.read(buffer));
// Close the channel.
// TODO(b/145386688): This access should be guarded by 'rewindableChannel', which is not
// currently held
rewindableChannel.close();
// TODO(b/145386688): This access should be guarded by 'rewindableChannel', which is not
// currently held
assertFalse(rewindableChannel.isOpen());
assertFalse(baseChannel.isOpen());
}
@SuppressWarnings("GuardedBy")
@Test
public void testExceptions() throws Exception {
int inputSize = 1234;
ReadableByteChannel baseChannel = new PseudorandomReadableByteChannel(inputSize);
baseChannel.close();
assertFalse(baseChannel.isOpen());
RewindableReadableByteChannel rewindableChannel =
new RewindableReadableByteChannel(baseChannel);
// TODO(b/145386688): This access should be guarded by 'rewindableChannel', which is not
// currently held
assertFalse(rewindableChannel.isOpen());
ByteBuffer buffer = ByteBuffer.allocate(42);
try {
@SuppressWarnings("GuardedBy")
// TODO(b/145386688): This access should be guarded by 'rewindableChannel', which is not
// currently held
int unused = rewindableChannel.read(buffer);
fail("Should have thrown exception, as cannot read after closing.");
} catch (ClosedChannelException expected) {
}
}
}