/** * Copyright 2013 Benjamin Lerer * * 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 io.horizondb.db.btree; import io.horizondb.io.ByteReader; import io.horizondb.io.files.DirectFileDataOutput; import io.horizondb.io.files.DirectSeekableFileDataInput; import io.horizondb.io.files.FileUtils; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import org.junit.After; import org.junit.Before; import org.junit.Test; 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; /** * @author Benjamin * */ public class BlockOrganizedFileDataInputTest { /** * */ private static final int BLOCK_SIZE = 5; /** * The test directory. */ private Path testDirectory; /** * The path to the file used during the tests. */ private Path path; @Before public void setUp() throws IOException { this.testDirectory = Files.createTempDirectory(this.getClass().getSimpleName()); this.path = this.testDirectory.resolve("test.md"); } @After public void tearDown() throws IOException { FileUtils.forceDelete(this.testDirectory); this.path = null; this.testDirectory = null; } @Test public void testSeek() throws IOException { createFile(); try (BlockOrganizedFileDataInput input = new BlockOrganizedFileDataInput(BLOCK_SIZE, new DirectSeekableFileDataInput(this.path, BLOCK_SIZE))) { assertTrue(input.isDataBlock()); input.seek(21); assertEquals(21, input.getPosition()); assertTrue(input.isHeaderBlock()); input.seek(5); assertEquals(5, input.getPosition()); assertTrue(input.isDataBlock()); } } @Test public void testSeekOutsideFileBoundary() throws IOException { createFile(); try (BlockOrganizedFileDataInput input = new BlockOrganizedFileDataInput(BLOCK_SIZE, new DirectSeekableFileDataInput(this.path, BLOCK_SIZE))) { try { input.seek(150); fail(); } catch (IllegalArgumentException e) { assertTrue(true); } } } @Test public void testSkip() throws IOException { createFile(); try (BlockOrganizedFileDataInput input = new BlockOrganizedFileDataInput(BLOCK_SIZE, new DirectSeekableFileDataInput(this.path, BLOCK_SIZE))) { assertEquals(0, input.getPosition()); assertTrue(input.isDataBlock()); input.skipBytes(1); input.skipBytes(1); input.skipBytes(1); input.skipBytes(1); assertEquals(4, input.getPosition()); assertTrue(input.isDataBlock()); input.skipBytes(20); assertEquals(24, input.getPosition()); assertTrue(input.isHeaderBlock()); } } @Test public void testReadByte() throws IOException { createFile(); try (BlockOrganizedFileDataInput input = new BlockOrganizedFileDataInput(BLOCK_SIZE, new DirectSeekableFileDataInput(this.path, BLOCK_SIZE))) { assertEquals(0, input.getPosition()); assertTrue(input.isDataBlock()); assertEquals(0, input.readByte()); assertEquals(1, input.readByte()); assertEquals(2, input.readByte()); assertEquals(3, input.readByte()); assertEquals(4, input.getPosition()); assertTrue(input.isDataBlock()); assertEquals(4, input.readByte()); assertEquals(5, input.readByte()); assertEquals(6, input.readByte()); assertEquals(0, input.readByte()); assertEquals(8, input.getPosition()); assertTrue(input.isDataBlock()); assertEquals(10, input.readByte()); assertEquals(9, input.getPosition()); assertTrue(input.isHeaderBlock()); assertEquals(11, input.readByte()); assertTrue(input.isReadable()); } } @Test public void testSize() throws IOException { createFile(); try (BlockOrganizedFileDataInput input = new BlockOrganizedFileDataInput(BLOCK_SIZE, new DirectSeekableFileDataInput(this.path, BLOCK_SIZE))) { assertEquals(6 * (BLOCK_SIZE - 1), input.size()); } } @Test public void testSizeWithEmptyFile() throws IOException { createEmptyFile(); try (BlockOrganizedFileDataInput input = new BlockOrganizedFileDataInput(BLOCK_SIZE, new DirectSeekableFileDataInput(this.path, BLOCK_SIZE))) { assertEquals(0, input.size()); assertTrue(input.isDataBlock()); } } @Test public void testSizeWithPartialBlock() throws IOException { createFileWithUncompletedBlock(); try (BlockOrganizedFileDataInput input = new BlockOrganizedFileDataInput(BLOCK_SIZE, new DirectSeekableFileDataInput(this.path, BLOCK_SIZE))) { assertEquals((6 * (BLOCK_SIZE - 1)) + 2, input.size()); } } @Test public void testReadBytes() throws IOException { createFile(); try (BlockOrganizedFileDataInput input = new BlockOrganizedFileDataInput(BLOCK_SIZE, new DirectSeekableFileDataInput(this.path, BLOCK_SIZE))) { assertEquals(0, input.getPosition()); assertTrue(input.isDataBlock()); byte[] bytes = new byte[6]; input.readBytes(bytes, 0, 1); assertArrayEquals(new byte[] { 0, 0, 0, 0, 0, 0 }, bytes); assertTrue(input.isReadable()); input.readBytes(bytes); assertArrayEquals(new byte[] { 1, 2, 3, 4, 5, 6 }, bytes); assertTrue(input.isReadable()); input.seek(14); input.readBytes(bytes); assertArrayEquals(new byte[] { 2, 8, 9, 0, 0, 0 }, bytes); assertTrue(input.isReadable()); } } @Test public void testSlice() throws IOException { createFile(); try (BlockOrganizedFileDataInput input = new BlockOrganizedFileDataInput(BLOCK_SIZE, new DirectSeekableFileDataInput(this.path, 2 * BLOCK_SIZE))) { BlockOrganizedReadableBuffer slice = (BlockOrganizedReadableBuffer) input.slice(4); assertTrue(input.isDataBlock()); assertTrue(slice.isDataBlock()); assertEquals(4, slice.readableBytes()); assertEquals(0, slice.getByte(0)); assertEquals(1, slice.getByte(1)); assertEquals(2, slice.getByte(2)); assertEquals(3, slice.getByte(3)); byte[] bytes = new byte[4]; assertTrue(slice.isReadable()); assertEquals(0, slice.readerIndex()); slice.readBytes(bytes); assertEquals(0, slice.readableBytes()); assertFalse(slice.isReadable()); assertEquals(4, slice.readerIndex()); assertArrayEquals(new byte[] { 0, 1, 2, 3 }, bytes); slice = (BlockOrganizedReadableBuffer) input.slice(6); assertEquals(6, slice.readableBytes()); assertTrue(input.isHeaderBlock()); assertTrue(slice.isDataBlock()); assertEquals(0, slice.readerIndex()); assertTrue(slice.isReadable()); slice.readBytes(bytes); assertEquals(2, slice.readableBytes()); assertTrue(slice.isReadable()); assertEquals(4, slice.readerIndex()); assertEquals(4, slice.getByte(0)); assertEquals(5, slice.getByte(1)); assertEquals(6, slice.getByte(2)); assertEquals(0, slice.getByte(3)); assertEquals(10, slice.getByte(4)); assertEquals(11, slice.getByte(5)); assertArrayEquals(new byte[] { 4, 5, 6, 0 }, bytes); slice.readBytes(bytes, 1, 2); assertEquals(0, slice.readableBytes()); assertEquals(6, slice.readerIndex()); assertArrayEquals(new byte[] { 4, 10, 11, 0 }, bytes); assertFalse(slice.isReadable()); slice = (BlockOrganizedReadableBuffer) input.slice(2); assertTrue(input.isHeaderBlock()); assertTrue(slice.isHeaderBlock()); assertEquals(0, slice.readerIndex()); assertEquals(2, slice.readableBytes()); assertTrue(slice.isReadable()); slice.readBytes(bytes, 1, 2); assertEquals(2, slice.readerIndex()); assertEquals(0, slice.readableBytes()); assertArrayEquals(new byte[] { 4, 12, 0, 0 }, bytes); assertFalse(slice.isReadable()); assertTrue(slice.isHeaderBlock()); slice = (BlockOrganizedReadableBuffer) input.slice(2); assertTrue(input.isDataBlock()); assertTrue(slice.isHeaderBlock()); assertEquals(0, slice.readerIndex()); assertTrue(slice.isReadable()); assertEquals(0, slice.readByte()); assertEquals(1, slice.readerIndex()); assertTrue(slice.isReadable()); assertEquals(1, slice.readByte()); assertEquals(2, slice.readerIndex()); assertFalse(slice.isReadable()); slice.readerIndex(0); assertTrue(input.isDataBlock()); assertTrue(slice.isHeaderBlock()); assertTrue(slice.isReadable()); assertEquals(0, slice.readByte()); assertTrue(slice.isReadable()); assertEquals(1, slice.readByte()); assertFalse(slice.isReadable()); slice = (BlockOrganizedReadableBuffer) input.slice(8); ByteReader subSlice = slice.slice(4); assertTrue(subSlice.isReadable()); assertEquals(2, subSlice.readByte()); assertTrue(subSlice.isReadable()); assertEquals(8, subSlice.readByte()); assertTrue(subSlice.isReadable()); subSlice.readBytes(bytes, 0, 2); assertArrayEquals(new byte[] { 9, 0, 0, 0 }, bytes); } } @Test public void testSliceGetWithMultipleBytes() throws IOException { createFile(); try (BlockOrganizedFileDataInput input = new BlockOrganizedFileDataInput(BLOCK_SIZE, new DirectSeekableFileDataInput(this.path, 2 * BLOCK_SIZE))) { BlockOrganizedReadableBuffer slice = (BlockOrganizedReadableBuffer) input.slice(4); assertTrue(input.isDataBlock()); assertTrue(slice.isDataBlock()); assertEquals(4, slice.readableBytes()); byte[] bytes = new byte[4]; slice.getBytes(0, bytes, 0, 4); assertArrayEquals(new byte[] { 0, 1, 2, 3 }, bytes); assertEquals(4, slice.readableBytes()); slice.getBytes(1, bytes, 0, 2); assertArrayEquals(new byte[] { 1, 2, 2, 3 }, bytes); assertEquals(4, slice.readableBytes()); slice = (BlockOrganizedReadableBuffer) input.slice(6); slice.getBytes(0, bytes, 0, 4); assertArrayEquals(new byte[] { 4, 5, 6, 0 }, bytes); assertEquals(6, slice.readableBytes()); slice.getBytes(2, bytes, 0, 4); assertArrayEquals(new byte[] { 6, 0, 10, 11 }, bytes); assertEquals(6, slice.readableBytes()); } } @Test public void testChangingIndexReaderWithinSlice() throws IOException { createFile(); try (BlockOrganizedFileDataInput input = new BlockOrganizedFileDataInput(BLOCK_SIZE, new DirectSeekableFileDataInput(this.path, 2 * BLOCK_SIZE))) { BlockOrganizedReadableBuffer slice = (BlockOrganizedReadableBuffer) input.slice(4); assertTrue(input.isDataBlock()); assertTrue(slice.isDataBlock()); byte[] bytes = new byte[4]; assertTrue(slice.isReadable()); assertEquals(0, slice.readerIndex()); slice.readBytes(bytes); assertFalse(slice.isReadable()); assertEquals(4, slice.readerIndex()); assertArrayEquals(new byte[] { 0, 1, 2, 3 }, bytes); slice.readerIndex(2); assertTrue(slice.isDataBlock()); assertTrue(slice.isReadable()); assertEquals(2, slice.readerIndex()); slice.readBytes(bytes, 0, 2); assertFalse(slice.isReadable()); assertEquals(4, slice.readerIndex()); assertArrayEquals(new byte[] { 2, 3, 2, 3 }, bytes); slice = (BlockOrganizedReadableBuffer) input.slice(6); assertTrue(slice.isDataBlock()); assertEquals(0, slice.readerIndex()); assertTrue(slice.isReadable()); slice.readBytes(bytes); assertTrue(slice.isReadable()); assertEquals(4, slice.readerIndex()); assertArrayEquals(new byte[] { 4, 5, 6, 0 }, bytes); slice.readBytes(bytes, 1, 2); assertEquals(6, slice.readerIndex()); assertArrayEquals(new byte[] { 4, 10, 11, 0 }, bytes); assertFalse(slice.isReadable()); assertTrue(slice.isHeaderBlock()); slice.readerIndex(2); assertTrue(slice.isDataBlock()); slice.readBytes(bytes); assertArrayEquals(new byte[] { 6, 0, 10, 11 }, bytes); assertTrue(slice.isHeaderBlock()); assertEquals(6, slice.readerIndex()); } } @Test public void testSliceBlockType() throws IOException { createFile(); try (BlockOrganizedFileDataInput input = new BlockOrganizedFileDataInput(BLOCK_SIZE, new DirectSeekableFileDataInput(this.path, 2 * BLOCK_SIZE))) { BlockOrganizedReadableBuffer slice = (BlockOrganizedReadableBuffer) input.slice(4); assertTrue(input.isDataBlock()); assertTrue(slice.isDataBlock()); slice = (BlockOrganizedReadableBuffer) input.slice(6); assertTrue(input.isHeaderBlock()); assertTrue(slice.isDataBlock()); slice = (BlockOrganizedReadableBuffer) input.slice(2); assertTrue(input.isHeaderBlock()); assertTrue(slice.isHeaderBlock()); slice = (BlockOrganizedReadableBuffer) input.slice(2); assertTrue(input.isDataBlock()); assertTrue(slice.isHeaderBlock()); assertTrue(slice.isReadable()); assertEquals(0, slice.readByte()); assertTrue(slice.isReadable()); assertEquals(1, slice.readByte()); assertFalse(slice.isReadable()); } } @Test public void testSeekHeader() throws IOException { createFile(); try (BlockOrganizedFileDataInput input = new BlockOrganizedFileDataInput(BLOCK_SIZE, new DirectSeekableFileDataInput(this.path, 2 * BLOCK_SIZE))) { assertTrue(input.seekHeader()); assertEquals(20, input.readByte()); } } @Test public void testSeekHeaderWithUncompletedBlock() throws IOException { createFileWithUncompletedBlock(); try (BlockOrganizedFileDataInput input = new BlockOrganizedFileDataInput(BLOCK_SIZE, new DirectSeekableFileDataInput(this.path, 2 * BLOCK_SIZE))) { assertTrue(input.seekHeader()); assertEquals(20, input.readByte()); } } @Test public void testSeekHeaderWithNoHeader() throws IOException { createFileWithNoHeader(); try (BlockOrganizedFileDataInput input = new BlockOrganizedFileDataInput(BLOCK_SIZE, new DirectSeekableFileDataInput(this.path, 2 * BLOCK_SIZE))) { assertFalse(input.seekHeader()); } } @Test public void testSeekHeaderWithEmptyFile() throws IOException { createEmptyFile(); try (BlockOrganizedFileDataInput input = new BlockOrganizedFileDataInput(BLOCK_SIZE, new DirectSeekableFileDataInput(this.path, 2 * BLOCK_SIZE))) { assertFalse(input.seekHeader()); } } /** * Creates the file used by the tests. * * @throws IOException if a problem occurs while creating the file. */ private void createFile() throws IOException { try (BlockOrganizedFileDataOutput output = new BlockOrganizedFileDataOutput(BLOCK_SIZE, new DirectFileDataOutput(this.path))) { output.writeBytes(new byte[] { 0, 1, 2, 3, 4, 5, 6 }); output.switchBlockType(); output.writeBytes(new byte[] { 10, 11, 12 }); output.switchBlockType(); output.writeBytes(new byte[] { 0, 1, 2, 8, 9 }); output.switchBlockType(); output.writeBytes(new byte[] { 20, 21, 22 }); output.switchBlockType(); output.flush(); } } /** * Creates the file used by the tests with an uncompleted block. * * @throws IOException if a problem occurs while creating the file. */ private void createFileWithUncompletedBlock() throws IOException { try (BlockOrganizedFileDataOutput output = new BlockOrganizedFileDataOutput(BLOCK_SIZE, new DirectFileDataOutput(this.path))) { output.writeBytes(new byte[] { 0, 1, 2, 3, 4, 5, 6 }); output.switchBlockType(); output.writeBytes(new byte[] { 10, 11, 12 }); output.switchBlockType(); output.writeBytes(new byte[] { 0, 1, 2, 8, 9 }); output.switchBlockType(); output.writeBytes(new byte[] { 20, 21, 22 }); output.switchBlockType(); output.writeBytes(new byte[] { 30, 31 }); output.flush(); } } /** * Creates the file used by the tests. * * @throws IOException if a problem occurs while creating the file. */ private void createFileWithNoHeader() throws IOException { try (BlockOrganizedFileDataOutput output = new BlockOrganizedFileDataOutput(BLOCK_SIZE, new DirectFileDataOutput(this.path))) { output.writeBytes(new byte[] { 0, 1, 2, 3, 4, 5, 6 }); output.flush(); } } private void createEmptyFile() throws IOException { try (BlockOrganizedFileDataOutput output = new BlockOrganizedFileDataOutput(BLOCK_SIZE, new DirectFileDataOutput(this.path))) { } } }