/** * 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; import static org.junit.Assert.assertEquals; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.TreeSet; import com.persistit.exception.PersistitException; import com.persistit.exception.PersistitInterruptedException; import com.persistit.util.Util; public abstract class MVCCTestBase extends PersistitUnitTestCase { protected static final String TEST_VOLUME_NAME = "persistit"; protected static final String TEST_TREE_NAME = "mvcctestbase"; protected Exchange ex1, ex2; protected Transaction trx1, trx2; protected SessionId session1, session2; @Override public void setUp() throws Exception { super.setUp(); ex1 = createUniqueExchange(); trx1 = ex1.getTransaction(); session1 = _persistit.getSessionId(); ex2 = createUniqueExchange(); trx2 = ex2.getTransaction(); session2 = _persistit.getSessionId(); } @Override public void tearDown() throws Exception { try { assertEquals("open read claims", 0, countClaims(false)); assertEquals("open write claims", 0, countClaims(true)); _persistit.releaseExchange(ex1); _persistit.releaseExchange(ex2); ex1 = ex2 = null; trx1 = trx2 = null; session1 = session2 = null; } finally { super.tearDown(); } } // // Internal test methods // protected int countClaims(final boolean writer) { final Volume vol = ex1.getVolume(); int count; int retries = 5; while ((count = _persistit.getBufferPool(vol.getPageSize()).countInUse(vol, writer)) != 0) { try { if (--retries < 0) { break; } Util.sleep(100); } catch (final PersistitInterruptedException e) { break; } } return count; } protected static class KVPair implements Comparable<KVPair> { Object k1, k2, v; public KVPair(final Object k1, final Object k2, final Object v) { this.k1 = k1; this.k2 = k2; this.v = v; } @Override public String toString() { if (k2 == null) { return String.format("%s->%s", k1, v); } return String.format("%s,%s->%s", k1, k2, v); } @Override public boolean equals(final Object o) { if (this == o) return true; if (!(o instanceof KVPair)) return false; final KVPair rhs = (KVPair) o; return k1.equals(rhs.k1) && !(k2 != null ? !k2.equals(rhs.k2) : rhs.k2 != null) && v.equals(rhs.v); } @SuppressWarnings({ "unchecked" }) @Override public int compareTo(final KVPair kvPair) { if (!(k1 instanceof Comparable)) { throw new IllegalArgumentException("Not comparable: " + k1); } int comp = ((Comparable) k1).compareTo(kvPair.k1); if (k2 != null && kvPair.k2 != null && comp == 0) { comp = ((Comparable) k2).compareTo(kvPair.k2); } return comp; } public void fillKey(final Key key) { key.clear(); key.append(k1); if (k2 != null) { key.append(k2); } } } protected static Object[] arr(final Object... values) { return values; } protected static void addTraverseResult(final Collection<KVPair> collection, final Key key, final Value value) { Object k1, k2 = null; switch (key.getDepth()) { default: throw new IllegalArgumentException("Unexpected key depth: " + key.getDepth()); case 2: key.indexTo(1); k2 = key.decode(); case 1: key.indexTo(0); k1 = key.decode(); } final Object v = value.isDefined() ? value.get() : "UD"; collection.add(new KVPair(k1, k2, v)); } protected static List<KVPair> traverseAllFoward(final Exchange e, final boolean deep) throws Exception { e.clear().append(Key.BEFORE); return doTraverse(e, Key.GT, deep); } protected static List<KVPair> traverseAllReverse(final Exchange e, final boolean deep) throws Exception { e.clear().append(Key.AFTER); return doTraverse(e, Key.LT, deep); } protected static List<KVPair> doTraverse(final Key.EdgeValue startAt, final Exchange ex, final Key.Direction dir, final KeyFilter filter) throws Exception { ex.clear().append(startAt); final List<KVPair> out = new ArrayList<KVPair>(); while (ex.traverse(dir, filter, Integer.MAX_VALUE)) { addTraverseResult(out, ex.getKey(), ex.getValue()); } return out; } protected static List<KVPair> doTraverse(final Exchange e, final Key.Direction dir, final boolean deep) throws Exception { final List<KVPair> out = new ArrayList<KVPair>(); while (e.traverse(dir, deep)) { addTraverseResult(out, e.getKey(), e.getValue()); } return out; } protected static List<KVPair> kvList(final Object... values) { if ((values.length % 2) != 0) { throw new IllegalArgumentException("Must be even number of objects to create pairs from"); } final List<KVPair> out = new ArrayList<KVPair>(); for (int i = 0; i < values.length; i += 2) { Object k1, k2 = null; if (values[i].getClass() == values.getClass()) { final Object[] ks = (Object[]) values[i]; k1 = ks[0]; k2 = ks[1]; } else { k1 = values[i]; } out.add(new KVPair(k1, k2, values[i + 1])); } return out; } protected static void storeAll(final Exchange ex, final List<KVPair> list) throws PersistitException { for (final KVPair kv : list) { kv.fillKey(ex.getKey()); ex.getValue().put(kv.v); ex.store(); } } protected static List<KVPair> combine(final List<KVPair> list1, final List<KVPair> list2) { final List<KVPair> outList = new ArrayList<KVPair>(); outList.addAll(list1); outList.addAll(list2); // sort and unique them final TreeSet<KVPair> set = new TreeSet<KVPair>(outList); outList.clear(); outList.addAll(set); return outList; } protected static void store(final Exchange ex, final Object k, final Object v) throws PersistitException { ex.clear().append(k).getValue().put(v); ex.store(); } protected static void store(final Exchange ex, final Object kp1, final Object kp2, final Object v) throws PersistitException { ex.clear().append(kp1).append(kp2).getValue().put(v); ex.store(); } protected static Object fetch(final Exchange ex, final Object k) throws PersistitException { return fetch(ex, k, true); } protected static Object fetch(final Exchange ex, final Object k, final boolean getValue) throws PersistitException { ex.getValue().clear(); ex.clear().append(k).fetch(); return getValue ? ex.getValue().get() : null; } protected static boolean remove(final Exchange ex, final Object k) throws PersistitException { ex.clear().append(k); return ex.remove(); } protected static void removeAll(final Exchange ex, final List<KVPair> list) throws PersistitException { for (final KVPair kv : list) { kv.fillKey(ex.getKey()); ex.remove(); } } protected Exchange createUniqueExchange() throws PersistitException { final SessionId session = new SessionId(); _persistit.setSessionId(session); return _persistit.getExchange(TEST_VOLUME_NAME, TEST_TREE_NAME, true); } protected static void storeLongMVV(final Exchange ex, final Object k) throws PersistitException { ex.clear().append(k); final String longStr = createString(ex.maxValueSize(ex.getKey().getEncodedSize()) - MVV.overheadLength(1)); store(ex, k, longStr); assertEquals("stored long record at key " + k, true, ex.isValueLongRecord()); } protected void showGUI() { try { _persistit.setupGUI(true); } catch (final Exception e) { throw new RuntimeException(e); } } }