/** Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved. Contact: SYSTAP, LLC DBA Blazegraph 2501 Calvert ST NW #106 Washington, DC 20008 licenses@blazegraph.com This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Created on Feb 9, 2007 */ package com.bigdata.journal; import java.nio.ByteBuffer; import java.util.Properties; import java.util.Random; import com.bigdata.btree.IndexSegmentBuilder; import com.bigdata.rawstore.AbstractRawStoreTestCase; import com.bigdata.rawstore.IRawStore; import com.bigdata.util.Bytes; /** * Base class for writing test cases for the different {@link IBufferStrategy} * implementations. * * @todo write tests for * {@link IBufferStrategy#transferTo(java.io.RandomAccessFile)}. This * code is currently getting "checked" by the {@link IndexSegmentBuilder}. * * @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a> * @version $Id$ */ abstract public class AbstractBufferStrategyTestCase extends AbstractRawStoreTestCase { public AbstractBufferStrategyTestCase() { } public AbstractBufferStrategyTestCase(String name) { super(name); } abstract protected BufferMode getBufferMode(); public Properties getProperties() { if (properties == null) { properties = super.getProperties(); properties.setProperty(Options.BUFFER_MODE, getBufferMode() .toString()); /* * Use a temporary file for the test. Such files are always deleted when * the journal is closed or the VM exits. */ properties.setProperty(Options.CREATE_TEMP_FILE,"true"); properties.setProperty(Options.DELETE_ON_EXIT,"true"); } return properties; } private Properties properties; protected IRawStore getStore() { return new Journal(getProperties()); } // public void tearDown() throws Exception // { // // super.tearDown(); // // if(properties==null) return; // // String filename = properties.getProperty(Options.FILE); // // if(filename==null) return; // // File file = new File(filename); // // if(file.exists() && !file.delete()) { // // file.deleteOnExit(); // // } // // } /** * Unit test for {@link AbstractBufferStrategy#overflow(long)}. The test * verifies that the extent and the user extent are correctly updated after * an overflow. */ public void test_overflow() { final Journal store = (Journal) getStore(); try { if (!(store.getBufferStrategy() instanceof AbstractBufferStrategy)) return; final AbstractBufferStrategy bufferStrategy = (AbstractBufferStrategy) store .getBufferStrategy(); final long userExtent = bufferStrategy.getUserExtent(); final long extent = bufferStrategy.getExtent(); final long initialExtent = bufferStrategy.getInitialExtent(); final long nextOffset = bufferStrategy.getNextOffset(); assertEquals("extent",initialExtent, extent); final long needed = Bytes.kilobyte32; if(bufferStrategy.getBufferMode()==BufferMode.Mapped) { // operation is not supported for mapped files. try { bufferStrategy.overflow(needed); fail("Expecting: " + UnsupportedOperationException.class); } catch (UnsupportedOperationException ex) { System.err.println("Ignoring expected exception: " + ex); } } else { assertTrue("overflow()",bufferStrategy.overflow(needed)); assertTrue("extent", extent + needed <= bufferStrategy .getExtent()); assertTrue("userExtent", userExtent + needed <= bufferStrategy .getUserExtent()); assertEquals(nextOffset,bufferStrategy.getNextOffset()); } } finally { store.destroy(); } } /** * Test verifies that a write up to the remaining extent does not trigger * an overflow. */ public void test_writeNoExtend() { final Journal store = (Journal) getStore(); try { final IBufferStrategy bufferStrategy = store.getBufferStrategy(); if (bufferStrategy.getBufferMode() == BufferMode.DiskRW) { return; } final long userExtent = bufferStrategy.getUserExtent(); final long extent = bufferStrategy.getExtent(); final long initialExtent = bufferStrategy.getInitialExtent(); final long nextOffset = bufferStrategy.getNextOffset(); assertEquals("extent", initialExtent, extent); final long remaining = userExtent - nextOffset; writeRandomData(store, remaining, bufferStrategy.useChecksums()); // no change in extent. assertEquals("extent", extent, bufferStrategy.getExtent()); // no change in user extent. assertEquals("userExtent", userExtent, bufferStrategy .getUserExtent()); } finally { store.destroy(); } } /** * Write random bytes on the store. * * @param store * The store. * * @param nbytesToWrite * The #of bytes to be written. If this is larger than the * maximum record length then multiple records will be written. * * @return The address of the last record written. */ protected long writeRandomData(final Journal store, final long nbytesToWrite, final boolean allowChecksum) { final int maxRecordSize = store.getMaxRecordSize(); final int chkAdjust = allowChecksum ? 4 : 0; assert nbytesToWrite > 0; long addr = 0L; AbstractBufferStrategy bufferStrategy = (AbstractBufferStrategy) store .getBufferStrategy(); int n = 0; long leftover = nbytesToWrite; while (leftover > 0) { // this will be an int since maxRecordSize is an int. final int nbytes = (int) Math.min(maxRecordSize, leftover-chkAdjust); assert nbytes>0; final byte[] b = new byte[nbytes]; final Random r = new Random(); r.nextBytes(b); final ByteBuffer tmp = ByteBuffer.wrap(b); addr = bufferStrategy.write(tmp); n++; leftover -= nbytes+chkAdjust; System.err.println("Wrote record#" + n + " with " + nbytes + " bytes: addr=" + store.toString(addr) + ", #leftover=" + leftover); } System.err.println("Wrote " + nbytesToWrite + " bytes in " + n + " records: last addr=" + store.toString(addr)); assert addr != 0L; return addr; } /** * Test verifies that a write over the remaining extent triggers an * overflow. The test also makes sure that the existing data is recoverable * and that the new data is also recoverable (when the buffer is extended it * is typically copied while the length of a file is simply changed). */ public void test_writeWithExtend() { final Journal store = (Journal) getStore(); try { final IBufferStrategy bufferStrategy = store.getBufferStrategy(); final BufferMode bm = bufferStrategy.getBufferMode(); if (bm == BufferMode.DiskRW || bm == BufferMode.Mapped) { return; } final long userExtent = bufferStrategy.getUserExtent(); final long extent = bufferStrategy.getExtent(); final long initialExtent = bufferStrategy.getInitialExtent(); final long nextOffset = bufferStrategy.getNextOffset(); assertEquals("extent", initialExtent, extent); final long remaining = userExtent - nextOffset; final long addr = writeRandomData(store, remaining, bufferStrategy.useChecksums()); // no change in extent. assertEquals("extent", extent, bufferStrategy.getExtent()); // no change in user extent. assertEquals("userExtent", userExtent, bufferStrategy .getUserExtent()); // read back the last record of random data written on the store. final ByteBuffer b = bufferStrategy.read(addr); /* * now write some more random bytes forcing an extension of the * buffer. we verify both the original write on the buffer and the * new write. this helps to ensure that data was copied correctly * into the extended buffer. */ final byte[] b2 = new byte[Bytes.kilobyte32]; // new Random(). r.nextBytes(b2); final ByteBuffer tmp2 = ByteBuffer.wrap(b2); final long addr2 = store.write(tmp2); // final long addr2 = writeRandomData(store,Bytes.kilobyte32); /* * Note: The DiskOnly strategy has a write cache. You have to force * it to disk before the new extent will show up. */ bufferStrategy.force(false); // verify extension of buffer. assertTrue("extent", extent + store.getByteCount(addr2) <= bufferStrategy .getExtent()); // verify extension of buffer. assertTrue("userExtent", userExtent + store.getByteCount(addr2) <= bufferStrategy .getUserExtent()); // verify data written before we overflowed the buffer. assertEquals(b, bufferStrategy.read(addr)); // verify data written after we overflowed the buffer. assertEquals(b2, bufferStrategy.read(addr2)); } finally { store.destroy(); } } }