/**
* Copyright 2011-2012 Akiban Technologies, 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.persistit.unit;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.Test;
import com.persistit.Exchange;
import com.persistit.Key;
import com.persistit.KeyFilter;
import com.persistit.PersistitUnitTestCase;
import com.persistit.Transaction;
import com.persistit.Volume;
import com.persistit.exception.KeyTooLongException;
import com.persistit.exception.PersistitException;
public class ExchangeTest extends PersistitUnitTestCase {
@Test
public void testAppend() throws PersistitException {
final Exchange ex = _persistit.getExchange("persistit", "tree", true);
final String mockValue = "PADRAIG";
/* test boolean append */
ex.clear().append(true);
ex.getValue().put(mockValue);
ex.store();
ex.clear().append(true);
ex.fetch();
assertEquals(mockValue, ex.getValue().getString());
/* test float append */
final float floatKey = 5.545454f;
ex.clear().append(floatKey);
ex.getValue().put(mockValue);
ex.store();
ex.clear().append(floatKey);
ex.fetch();
assertEquals(mockValue, ex.getValue().getString());
/* test double append */
final double doubleKey = 6.66;
ex.clear().append(doubleKey);
ex.getValue().put(mockValue);
ex.store();
ex.clear().append(doubleKey);
ex.fetch();
assertEquals(mockValue, ex.getValue().getString());
/* test int append */
final int intKey = 6;
ex.clear().append(intKey);
ex.getValue().put(mockValue);
ex.store();
ex.clear().append(intKey);
ex.fetch();
assertEquals(mockValue, ex.getValue().getString());
/* test byte append */
final byte oneByte = 1;
ex.clear().append(oneByte);
ex.getValue().put(mockValue);
ex.store();
ex.clear().append(oneByte);
ex.fetch();
assertEquals(mockValue, ex.getValue().getString());
/* test short append */
final short smallShort = 1234;
ex.clear().append(smallShort);
ex.getValue().put(mockValue);
ex.store();
ex.clear().append(smallShort);
ex.fetch();
assertEquals(mockValue, ex.getValue().getString());
}
@Test
public void testStringAppend() throws PersistitException {
int initialLength = 256;
final String randomString = createString(initialLength);
final Exchange ex = _persistit.getExchange("persistit", randomString, true);
ex.clear().append(randomString);
ex.getValue().put(randomString);
ex.store();
ex.clear().append(randomString);
ex.fetch();
assertEquals(randomString, ex.getValue().getString());
/* lets double key length but keep value the same */
initialLength *= 2;
String randomKey = createString(initialLength);
ex.clear().append(randomKey);
ex.getValue().put(randomString);
ex.store();
ex.clear().append(randomKey);
ex.fetch();
assertEquals(randomString, ex.getValue().getString());
/* now lets keep doubling value length for kicks */
for (int i = 0; i < 12; i++) {
initialLength *= 2;
final String randomValue = createString(initialLength);
ex.clear().append(randomKey);
ex.getValue().put(randomValue);
ex.store();
ex.clear().append(randomKey);
ex.fetch();
assertEquals(randomValue, ex.getValue().getString());
}
/* now double the key length */
initialLength = 256;
for (int i = 0; i < 2; i++) {
initialLength *= 2;
randomKey = createString(initialLength);
ex.clear().append(randomKey);
ex.getValue().put(randomString);
ex.store();
ex.clear().append(randomKey);
ex.fetch();
assertEquals(randomString, ex.getValue().getString());
}
/*
* set key length to value larger than max and make sure exception is
* thrown
*/
initialLength = 2048; // 2047 is max key length
randomKey = createString(initialLength);
try {
ex.clear().append(randomKey);
fail("KeyTooLongException should have been thrown");
} catch (final KeyTooLongException expected) {
}
}
@Test
public void testConstructors() throws PersistitException {
try {
final Exchange exchange = new Exchange(_persistit, "volume", "tree", true);
fail("NullPointerException should have been thrown for unknown Volume");
} catch (final NullPointerException expected) {
}
try {
final Volume nullVol = null;
final Exchange ex = new Exchange(_persistit, nullVol, "whatever", true);
fail("NullPointerException should have been thrown for null Volume");
} catch (final NullPointerException expected) {
}
}
@Test
public void testTraversal() throws PersistitException {
final Exchange ex = _persistit.getExchange("persistit", "gogo", true);
final String mockValue = createString(256);
/* insert 1000 records */
for (int i = 0; i < 1000; i++) {
final String key = createString(32);
ex.clear().append(key);
ex.getValue().put(mockValue);
ex.store();
}
/* traverse forwards through those values */
ex.clear().to(Key.BEFORE);
while (ex.next()) {
ex.fetch();
assertEquals(mockValue, ex.getValue().getString());
}
ex.clear().to(Key.BEFORE);
while (ex.hasNext()) {
ex.next();
ex.fetch();
assertEquals(mockValue, ex.getValue().getString());
}
/* now traverse backwards through those values */
ex.clear().to(Key.AFTER);
while (ex.previous()) {
ex.fetch();
assertEquals(mockValue, ex.getValue().getString());
}
ex.clear().to(Key.AFTER);
while (ex.hasPrevious()) {
ex.previous();
ex.fetch();
assertEquals(mockValue, ex.getValue().getString());
}
/* now use the traverse method with various directions */
ex.clear().to(Key.BEFORE);
final Key key = ex.getKey();
final KeyFilter kf = new KeyFilter(key);
/* this is mostly to test if we can trigger bad things */
assertEquals(false, ex.traverse(Key.EQ, kf, 4));
assertEquals(false, ex.traverse(Key.GT, kf, 4));
assertEquals(false, ex.traverse(Key.GTEQ, kf, 4));
assertEquals(false, ex.traverse(Key.LT, kf, 4));
assertEquals(false, ex.traverse(Key.LTEQ, kf, 4));
}
@Test
public void testKeyValues() throws PersistitException {
final Exchange ex = _persistit.getExchange("persistit", "gogo", true);
final String firstValue = new String("PADRAIG");
final String secondValue = new String("SARAH");
final String thirdValue = new String("TEENY");
final String fourthValue = new String("NIMBUS");
ex.clear().append(-2);
ex.getValue().put(firstValue);
ex.store();
ex.clear().append(-1);
ex.getValue().put(secondValue);
ex.store();
ex.clear().append(0);
ex.getValue().put(thirdValue);
ex.store();
ex.clear().append(1);
ex.getValue().put(fourthValue);
ex.store();
ex.clear().to(-2);
ex.fetch();
assertEquals(firstValue, ex.getValue().getString());
ex.clear().to(-1);
ex.fetch();
assertEquals(secondValue, ex.getValue().getString());
ex.clear().to(0);
ex.fetch();
assertEquals(thirdValue, ex.getValue().getString());
ex.clear().to(1);
ex.fetch();
assertEquals(fourthValue, ex.getValue().getString());
ex.clear().append(-2);
ex.remove();
ex.getValue().put(-2);
ex.store();
ex.clear().append(-1);
ex.remove();
ex.getValue().put(-1);
ex.store();
ex.clear().append(0);
ex.remove();
ex.getValue().put(0);
ex.store();
ex.clear().append(1);
ex.remove();
ex.getValue().put(1);
ex.store();
ex.clear().to(-2);
ex.fetch();
assertEquals(-2, ex.getValue().getInt());
ex.clear().to(-1);
ex.fetch();
assertEquals(-1, ex.getValue().getInt());
ex.clear().to(0);
ex.fetch();
assertEquals(0, ex.getValue().getInt());
ex.clear().to(1);
ex.fetch();
assertEquals(1, ex.getValue().getInt());
}
@Test
public void testRemoveAndFetch() throws Exception {
testRemoveAndFetch(false);
testRemoveAndFetch(true);
}
private void testRemoveAndFetch(final boolean inTransaction) throws Exception {
final Exchange ex = _persistit.getExchange("persistit", "gogo", true);
final Transaction txn = ex.getTransaction();
ex.getValue().put(RED_FOX);
for (int i = 1; i < 10000; i++) {
if (inTransaction) {
txn.begin();
}
ex.to(i).store();
if (inTransaction) {
txn.commit();
txn.end();
}
}
for (int i = 1; i < 10000; i++) {
if (inTransaction) {
txn.begin();
}
ex.getValue().clear();
ex.to(i).fetchAndRemove();
assertTrue("A value was fetched", ex.getValue().isDefined());
if (inTransaction) {
txn.commit();
txn.end();
}
}
}
@Test
public void testWrongThreadAssertion() throws Exception {
boolean assertsEnabled = false;
try {
assert false;
} catch (final AssertionError e) {
assertsEnabled = true;
}
if (!assertsEnabled) {
// Does not count as a failure
System.out.println("Test requires asserts enabled");
return;
}
final AtomicReference<Exchange> ref = new AtomicReference<Exchange>();
final Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
final Exchange exchange = _persistit.getExchange("persistit", "gogo", true);
ref.set(exchange);
} catch (final Exception e) {
// leave null ref
}
}
});
thread.start();
thread.join();
final Exchange exchange = ref.get();
assertTrue("Failed to get an Exchange in background thread", exchange != null);
// Verifies that all the parameter-less methods assert.
int tested = 0;
for (final Method m : Exchange.class.getMethods()) {
if (m.getParameterTypes().length == 0 && m.getDeclaringClass().equals(Exchange.class)) {
if ("toString".equals(m.getName())) {
continue;
}
try {
System.out.println("Testing " + m.getName());
m.invoke(exchange);
fail("Method " + m + " failed to throw an AssertionError");
} catch (final InvocationTargetException e) {
if (e.getCause() instanceof AssertionError) {
tested++;
// expected
} else {
throw e;
}
}
}
}
assertTrue("Not enough methods were tested: " + tested, tested > 10);
}
/**
* Test for https://bugs.launchpad.net/akiban-persistit/+bug/1023549:
*
* traverse(EQ, false, 0) returns incorrect result
*
* This method returns true even when the tree is empty. traverse(EQ, true,
* 0) returns the correct value.
*
* @throws Exception
*/
@Test
public void traverse_EQ_false_0__IsCorrect() throws Exception {
traverseCases(false);
traverseCases(true);
}
/**
* Test for https://bugs.launchpad.net/akiban-persistit/+bug/1023549:
*
* traverse(EQ, false, 0) returns incorrect result
*
* This method returns true even when the tree is empty. traverse(EQ, true,
* 0) returns the correct value.
*
* @throws Exception
*/
@Test
public void traverse_EQ_false_0__IsCorrect_Txn() throws Exception {
final Transaction txn = _persistit.getTransaction();
txn.begin();
traverseCases(false);
txn.commit();
txn.end();
txn.begin();
traverseCases(true);
txn.commit();
txn.end();
}
private void traverseCases(final boolean deep) throws Exception {
final Exchange ex = _persistit.getExchange("persistit", "gogo", true);
ex.removeAll();
ex.clear();
assertEquals("Should be false", false, ex.traverse(Key.EQ, deep, -1));
ex.clear();
assertEquals("Should be false", false, ex.traverse(Key.GTEQ, deep, -1));
ex.clear();
assertEquals("Should be false", false, ex.traverse(Key.GT, deep, -1));
ex.clear();
assertEquals("Should be false", false, ex.traverse(Key.LTEQ, deep, -1));
ex.clear();
assertEquals("Should be false", false, ex.traverse(Key.LT, deep, -1));
ex.clear();
ex.append(1);
assertEquals("Should be false", false, ex.traverse(Key.EQ, deep, -1));
ex.clear().append(1);
assertEquals("Should be false", false, ex.traverse(Key.GTEQ, deep, -1));
ex.clear().append(1);
assertEquals("Should be false", false, ex.traverse(Key.GT, deep, -1));
ex.clear().append(1);
assertEquals("Should be false", false, ex.traverse(Key.LTEQ, deep, -1));
ex.clear().append(1);
assertEquals("Should be false", false, ex.traverse(Key.LT, deep, -1));
ex.clear().append(1);
ex.append(1).append(2).store();
ex.clear().append(Key.BEFORE);
assertEquals("Should be false", false, ex.traverse(Key.EQ, deep, -1));
assertEquals("Should be true", true, ex.traverse(Key.GTEQ, deep, -1));
assertEquals("Should be true", true, ex.traverse(Key.GTEQ, deep, -1));
ex.clear().append(1);
assertEquals("Should be " + !deep, !deep, ex.traverse(Key.EQ, deep, -1));
ex.clear().append(Key.AFTER);
assertEquals("Should be false", false, ex.traverse(Key.EQ, deep, -1));
ex.clear().append(Key.AFTER);
assertEquals("Should be true", true, ex.traverse(Key.LTEQ, deep, -1));
assertEquals("Should be true", true, ex.traverse(Key.LTEQ, deep, -1));
ex.removeAll();
ex.clear();
assertEquals("Should be false", false, ex.traverse(Key.EQ, deep, 0));
keyCheck(ex, "{{before}}");
ex.clear();
assertEquals("Should be false", false, ex.traverse(Key.GTEQ, deep, 0));
keyCheck(ex, "{{before}}");
ex.clear();
assertEquals("Should be false", false, ex.traverse(Key.GT, deep, 0));
keyCheck(ex, "{{before}}");
ex.clear();
assertEquals("Should be false", false, ex.traverse(Key.LTEQ, deep, 0));
keyCheck(ex, "{{after}}");
ex.clear();
assertEquals("Should be false", false, ex.traverse(Key.LT, deep, 0));
keyCheck(ex, "{{after}}");
ex.clear();
ex.append(1).append(2).store();
ex.clear().append(Key.BEFORE);
assertEquals("Should be false", false, ex.traverse(Key.EQ, deep, 0));
keyCheck(ex, "{{before}}");
assertEquals("Should be true", true, ex.traverse(Key.GTEQ, deep, 0));
keyCheck(ex, deep ? "{1,2}" : "{1}");
assertEquals("Should be true", true, ex.traverse(Key.GTEQ, deep, 0));
keyCheck(ex, deep ? "{1,2}" : "{1}");
assertEquals("Should be true", true, ex.traverse(Key.EQ, deep, 0));
keyCheck(ex, deep ? "{1,2}" : "{1}");
ex.clear().append(Key.AFTER);
assertEquals("Should be false", false, ex.traverse(Key.EQ, deep, 0));
keyCheck(ex, "{{before}}");
ex.clear().append(Key.AFTER);
assertEquals("Should be true", true, ex.traverse(Key.LTEQ, deep, 0));
keyCheck(ex, deep ? "{1,2}" : "{1}");
assertEquals("Should be true", true, ex.traverse(Key.LTEQ, deep, 0));
keyCheck(ex, deep ? "{1,2}" : "{1}");
assertEquals("Should be true", true, ex.traverse(Key.EQ, deep, 0));
keyCheck(ex, deep ? "{1,2}" : "{1}");
}
private void keyCheck(final Exchange ex, final String expected) {
assertEquals("Key should be " + expected, expected, ex.getKey().toString());
}
}