package io.eguan.nrs; /* * #%L * Project eguan * %% * Copyright (C) 2012 - 2017 Oodrive * %% * 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. * #L% */ import io.eguan.nrs.NrsAbstractFile; import io.eguan.nrs.NrsByteBufferCache; import io.eguan.nrs.NrsFileBlock; import io.eguan.nrs.NrsFileFlag; import io.eguan.nrs.NrsFileHeader; import io.eguan.nrs.NrsMsgPostOffice; import io.eguan.utils.ByteBuffers; import io.eguan.utils.mapper.FileMapper; import java.io.IOException; import java.nio.ByteBuffer; import java.security.SecureRandom; import java.util.EnumSet; import java.util.Random; import java.util.Set; import org.junit.Assert; import org.junit.Test; /** * Test {@link NrsFileBlock} as {@link NrsAbstractFile}. * * @author oodrive * @author llambert * */ public final class TestNrsFileBlocks extends TestNrsAbstractFiles<ByteBuffer, NrsFileBlock> { private static final Random rand = new SecureRandom(); public TestNrsFileBlocks() { super(); } @Override final NrsAbstractFile<ByteBuffer, NrsFileBlock> newNrsAbstractFile(final FileMapper fileMapper, final NrsFileHeader<NrsFileBlock> header, final NrsMsgPostOffice postOffice) { return new NrsFileBlock(fileMapper, header, postOffice); } @Override final int getWriteSize(final int hashSize, final int blockSize) { return blockSize; } @Override final ByteBuffer getTrimElement() { return NrsFileBlock.BLOCK_TRIMMED; } @Override final ByteBuffer newRandomElement(final int size) { final ByteBuffer e = newEmptyElement(size); nextRandomElement(e, rand); return e; } @Override final void nextRandomElement(final ByteBuffer e, final Random random) { e.clear(); while (e.position() != e.capacity()) { e.put((byte) random.nextInt()); } // Reset position e.clear(); } @Override final ByteBuffer newEmptyElement(final int size) { return NrsByteBufferCache.allocate(size); } @Override final void releaseElement(final ByteBuffer e) { NrsByteBufferCache.release(e); } @Override final void assertEqualsElements(final ByteBuffer e1, final ByteBuffer e2) { assert e1.position() == 0; assert e2.position() == 0; assert e1.capacity() == e2.capacity(); // Sets the position to check the contents of the buffers e1.position(e1.capacity()); e2.position(e2.capacity()); ByteBuffers.assertEqualsByteBuffers(e1, e2); // Reset the positions for the rest of the test e1.position(0); e2.position(0); } /** * Reads and writes blocks in a {@link ByteBuffer} larger than blockSize. * * @throws Exception */ @Test public void testRWLargeBlock() throws Exception { final int blockSize = 4 * 1024; final int blockCount = 512; final long size = blockSize * blockCount; final int hashSize = 20; final int writeSize = getWriteSize(hashSize, blockSize); assert writeSize == blockSize; final int clusterSize = writeSize * 11; final Set<NrsFileFlag> flags = EnumSet.noneOf(NrsFileFlag.class); flags.add(NrsFileFlag.BLOCKS); // Create NrsAbstractFile final NrsFileHeader.Builder<NrsFileBlock> headerBuilder = newHeaderBuilder(size, blockSize, hashSize, clusterSize, flags); final NrsFileHeader<NrsFileBlock> header = headerBuilder.build(); final NrsAbstractFile<ByteBuffer, NrsFileBlock> nrsFile = newNrsAbstractFile(fileMapper, header, null); nrsFile.create(); nrsFile.open(false); try { final ByteBuffer toWrite = newRandomElement(writeSize * 16); // Write of the beginning of the buffer writeBlock(nrsFile, toWrite, 0, 0, writeSize); { // Takes a new random read area to be sure that the data have been read final ByteBuffer toRead = newRandomElement(writeSize * 16); try { readCheckBlock(nrsFile, toWrite, toRead, 0, 0, writeSize, false, false); } finally { releaseElement(toRead); } } // Write of the beginning of the buffer writeBlock(nrsFile, toWrite, 500, 0, writeSize); writeBlock(nrsFile, toWrite, 501, writeSize, writeSize * 2); writeBlock(nrsFile, toWrite, 502, writeSize * 2, writeSize * 3); { // Takes a new random read area to be sure that the data have been read final ByteBuffer toRead = newRandomElement(writeSize * 16); try { // Last writes readCheckBlock(nrsFile, toWrite, toRead, 501, writeSize, writeSize * 2, true, true); // First write readCheckBlock(nrsFile, toWrite, toRead, 0, 0, writeSize, false, false); } finally { releaseElement(toRead); } } // Write after the first block written final int offsetInSrc1 = 55; writeBlock(nrsFile, toWrite, 1, offsetInSrc1, offsetInSrc1 + writeSize); writeBlock(nrsFile, toWrite, 2, offsetInSrc1 + writeSize, offsetInSrc1 + writeSize * 2); writeBlock(nrsFile, toWrite, 3, offsetInSrc1 + writeSize * 2, offsetInSrc1 + writeSize * 3); { // Takes a new random read area to be sure that the data have been read final ByteBuffer toRead = newRandomElement(writeSize * 16); try { // Check writes readCheckBlock(nrsFile, toWrite, toRead, 2, offsetInSrc1 + writeSize, offsetInSrc1 + writeSize * 2, true, true); readCheckBlock(nrsFile, toWrite, toRead, 501, writeSize, writeSize * 2, true, true); readCheckBlock(nrsFile, toWrite, toRead, 0, 0, writeSize, false, false); } finally { releaseElement(toRead); } } // Re-write some blocks final int offsetInSrc2 = 155; writeBlock(nrsFile, toWrite, 501, offsetInSrc2, offsetInSrc2 + writeSize); writeBlock(nrsFile, toWrite, 502, offsetInSrc2 + writeSize, offsetInSrc2 + writeSize * 2); writeBlock(nrsFile, toWrite, 503, offsetInSrc2 + writeSize * 2, offsetInSrc2 + writeSize * 3); { // Takes a new random read area to be sure that the data have been read final ByteBuffer toRead = newRandomElement(writeSize * 16); try { // Check writes readCheckBlock(nrsFile, toWrite, toRead, 502, offsetInSrc2 + writeSize, offsetInSrc2 + writeSize * 2, true, true); readCheckBlock(nrsFile, toWrite, toRead, 2, offsetInSrc1 + writeSize, offsetInSrc1 + writeSize * 2, true, true); readCheckBlock(nrsFile, toWrite, toRead, 500, 0, writeSize , false, false); readCheckBlock(nrsFile, toWrite, toRead, 0, 0, writeSize, false, false); } finally { releaseElement(toRead); } } } finally { nrsFile.close(); } } private final void writeBlock(final NrsAbstractFile<ByteBuffer, NrsFileBlock> nrsFile, final ByteBuffer toWrite, final int blockIndex, final int position, final int limit) throws IOException { toWrite.position(position); toWrite.limit(limit); nrsFile.write(blockIndex, toWrite); // Check position and limit (unchanged) Assert.assertEquals(position, toWrite.position()); Assert.assertEquals(limit, toWrite.limit()); } private final void readCheckBlock(final NrsAbstractFile<ByteBuffer, NrsFileBlock> nrsFile, final ByteBuffer toWrite, final ByteBuffer toRead, final int blockIndex, final int position, final int limit, final boolean checkPrev, final boolean checkNext) throws IndexOutOfBoundsException, IOException { toRead.clear(); toRead.position(position); toRead.limit(limit); nrsFile.read(blockIndex, toRead); // Check position and limit (unchanged) Assert.assertEquals(position, toRead.position()); Assert.assertEquals(limit, toRead.limit()); // Check contents toWrite.clear(); toWrite.position(position); toWrite.limit(limit); Assert.assertEquals(toWrite, toRead); // Check prev block? final int blockSize = nrsFile.getDescriptor().getBlockSize(); if (checkPrev) { readCheckBlock(nrsFile, toWrite, toRead, blockIndex - 1, position - blockSize, limit - blockSize, false, false); } if (checkNext) { readCheckBlock(nrsFile, toWrite, toRead, blockIndex + 1, position + blockSize, limit + blockSize, false, false); } } }