summaryrefslogtreecommitdiff
path: root/db-4.8.30/test/scr024/src/com/sleepycat/collections
diff options
context:
space:
mode:
authorJesse Morgan <jesse@jesterpm.net>2016-12-17 21:28:53 -0800
committerJesse Morgan <jesse@jesterpm.net>2016-12-17 21:28:53 -0800
commit54df2afaa61c6a03cbb4a33c9b90fa572b6d07b8 (patch)
tree18147b92b969d25ffbe61935fb63035cac820dd0 /db-4.8.30/test/scr024/src/com/sleepycat/collections
Berkeley DB 4.8 with rust build script for linux.
Diffstat (limited to 'db-4.8.30/test/scr024/src/com/sleepycat/collections')
-rw-r--r--db-4.8.30/test/scr024/src/com/sleepycat/collections/KeyRangeTest.java440
-rw-r--r--db-4.8.30/test/scr024/src/com/sleepycat/collections/test/CollectionTest.java3048
-rw-r--r--db-4.8.30/test/scr024/src/com/sleepycat/collections/test/DbTestUtil.java129
-rw-r--r--db-4.8.30/test/scr024/src/com/sleepycat/collections/test/ForeignKeyTest.java342
-rw-r--r--db-4.8.30/test/scr024/src/com/sleepycat/collections/test/IterDeadlockTest.java228
-rw-r--r--db-4.8.30/test/scr024/src/com/sleepycat/collections/test/JoinTest.java225
-rw-r--r--db-4.8.30/test/scr024/src/com/sleepycat/collections/test/NullTransactionRunner.java32
-rw-r--r--db-4.8.30/test/scr024/src/com/sleepycat/collections/test/SecondaryDeadlockTest.java206
-rw-r--r--db-4.8.30/test/scr024/src/com/sleepycat/collections/test/TestDataBinding.java33
-rw-r--r--db-4.8.30/test/scr024/src/com/sleepycat/collections/test/TestEntity.java44
-rw-r--r--db-4.8.30/test/scr024/src/com/sleepycat/collections/test/TestEntityBinding.java63
-rw-r--r--db-4.8.30/test/scr024/src/com/sleepycat/collections/test/TestEnv.java130
-rw-r--r--db-4.8.30/test/scr024/src/com/sleepycat/collections/test/TestKeyAssigner.java41
-rw-r--r--db-4.8.30/test/scr024/src/com/sleepycat/collections/test/TestKeyCreator.java56
-rw-r--r--db-4.8.30/test/scr024/src/com/sleepycat/collections/test/TestSR15721.java119
-rw-r--r--db-4.8.30/test/scr024/src/com/sleepycat/collections/test/TestStore.java279
-rw-r--r--db-4.8.30/test/scr024/src/com/sleepycat/collections/test/TransactionTest.java838
-rw-r--r--db-4.8.30/test/scr024/src/com/sleepycat/collections/test/serial/CatalogCornerCaseTest.java97
-rw-r--r--db-4.8.30/test/scr024/src/com/sleepycat/collections/test/serial/StoredClassCatalogTest.java177
-rw-r--r--db-4.8.30/test/scr024/src/com/sleepycat/collections/test/serial/StoredClassCatalogTestInit.java154
-rw-r--r--db-4.8.30/test/scr024/src/com/sleepycat/collections/test/serial/TestSerial.java70
-rw-r--r--db-4.8.30/test/scr024/src/com/sleepycat/collections/test/serial/TestSerial.java.original72
-rw-r--r--db-4.8.30/test/scr024/src/com/sleepycat/collections/test/serial/TupleSerialFactoryTest.java245
23 files changed, 7068 insertions, 0 deletions
diff --git a/db-4.8.30/test/scr024/src/com/sleepycat/collections/KeyRangeTest.java b/db-4.8.30/test/scr024/src/com/sleepycat/collections/KeyRangeTest.java
new file mode 100644
index 0000000..aeba1bc
--- /dev/null
+++ b/db-4.8.30/test/scr024/src/com/sleepycat/collections/KeyRangeTest.java
@@ -0,0 +1,440 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.collections;
+
+import java.io.File;
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.Comparator;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.sleepycat.bind.ByteArrayBinding;
+import com.sleepycat.compat.DbCompat;
+import com.sleepycat.db.Database;
+import com.sleepycat.db.DatabaseConfig;
+import com.sleepycat.db.DatabaseEntry;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.EnvironmentConfig;
+import com.sleepycat.db.OperationStatus;
+import com.sleepycat.util.keyrange.KeyRange;
+import com.sleepycat.util.keyrange.KeyRangeException;
+import com.sleepycat.util.test.SharedTestUtils;
+
+/**
+ * @author Mark Hayes
+ */
+public class KeyRangeTest extends TestCase {
+
+ private static boolean VERBOSE = false;
+
+ private static final byte FF = (byte) 0xFF;
+
+ private static final byte[][] KEYS = {
+ /* 0 */ {1},
+ /* 1 */ {FF},
+ /* 2 */ {FF, 0},
+ /* 3 */ {FF, 0x7F},
+ /* 4 */ {FF, FF},
+ /* 5 */ {FF, FF, 0},
+ /* 6 */ {FF, FF, 0x7F},
+ /* 7 */ {FF, FF, FF},
+ };
+ private static byte[][] EXTREME_KEY_BYTES = {
+ /* 0 */ {0},
+ /* 1 */ {FF, FF, FF, FF},
+ };
+
+ private Environment env;
+ private Database store;
+ private DataView view;
+ private DataCursor cursor;
+
+ public static void main(String[] args) {
+ junit.framework.TestResult tr =
+ junit.textui.TestRunner.run(suite());
+ if (tr.errorCount() > 0 ||
+ tr.failureCount() > 0) {
+ System.exit(1);
+ } else {
+ System.exit(0);
+ }
+ }
+
+ public static Test suite() {
+
+ return new TestSuite(KeyRangeTest.class);
+ }
+
+ public KeyRangeTest(String name) {
+
+ super(name);
+ }
+
+ @Override
+ public void setUp() {
+ SharedTestUtils.printTestName(SharedTestUtils.qualifiedTestName(this));
+ }
+
+ private void openDb(Comparator<byte []> comparator)
+ throws Exception {
+
+ File dir = SharedTestUtils.getNewDir();
+ ByteArrayBinding dataBinding = new ByteArrayBinding();
+ EnvironmentConfig envConfig = new EnvironmentConfig();
+ envConfig.setAllowCreate(true);
+ DbCompat.setInitializeCache(envConfig, true);
+ env = new Environment(dir, envConfig);
+ DatabaseConfig dbConfig = new DatabaseConfig();
+ DbCompat.setTypeBtree(dbConfig);
+ dbConfig.setAllowCreate(true);
+ if (comparator != null) {
+ DbCompat.setBtreeComparator(dbConfig, comparator);
+ }
+ store = DbCompat.testOpenDatabase
+ (env, null, "test.db", null, dbConfig);
+ view = new DataView(store, dataBinding, dataBinding, null, true, null);
+ }
+
+ private void closeDb()
+ throws Exception {
+
+ store.close();
+ store = null;
+ env.close();
+ env = null;
+ }
+
+ @Override
+ public void tearDown() {
+ try {
+ if (store != null) {
+ store.close();
+ }
+ } catch (Exception e) {
+ System.out.println("Exception ignored during close: " + e);
+ }
+ try {
+ if (env != null) {
+ env.close();
+ }
+ } catch (Exception e) {
+ System.out.println("Exception ignored during close: " + e);
+ }
+ /* Ensure that GC can cleanup. */
+ env = null;
+ store = null;
+ view = null;
+ cursor = null;
+ }
+
+ public void testScan() throws Exception {
+ openDb(null);
+ doScan(false);
+ closeDb();
+ }
+
+ public void testScanComparator() throws Exception {
+ openDb(new ReverseComparator());
+ doScan(true);
+ closeDb();
+ }
+
+ private void doScan(boolean reversed) throws Exception {
+
+ byte[][] keys = new byte[KEYS.length][];
+ final int end = KEYS.length - 1;
+ cursor = new DataCursor(view, true);
+ for (int i = 0; i <= end; i++) {
+ keys[i] = KEYS[i];
+ cursor.put(keys[i], KEYS[i], null, false);
+ }
+ cursor.close();
+ byte[][] extremeKeys = new byte[EXTREME_KEY_BYTES.length][];
+ for (int i = 0; i < extremeKeys.length; i++) {
+ extremeKeys[i] = EXTREME_KEY_BYTES[i];
+ }
+
+ // with empty range
+
+ cursor = new DataCursor(view, false);
+ expectRange(KEYS, 0, end, reversed);
+ cursor.close();
+
+ // begin key only, inclusive
+
+ for (int i = 0; i <= end; i++) {
+ cursor = newCursor(view, keys[i], true, null, false, reversed);
+ expectRange(KEYS, i, end, reversed);
+ cursor.close();
+ }
+
+ // begin key only, exclusive
+
+ for (int i = 0; i <= end; i++) {
+ cursor = newCursor(view, keys[i], false, null, false, reversed);
+ expectRange(KEYS, i + 1, end, reversed);
+ cursor.close();
+ }
+
+ // end key only, inclusive
+
+ for (int i = 0; i <= end; i++) {
+ cursor = newCursor(view, null, false, keys[i], true, reversed);
+ expectRange(KEYS, 0, i, reversed);
+ cursor.close();
+ }
+
+ // end key only, exclusive
+
+ for (int i = 0; i <= end; i++) {
+ cursor = newCursor(view, null, false, keys[i], false, reversed);
+ expectRange(KEYS, 0, i - 1, reversed);
+ cursor.close();
+ }
+
+ // begin and end keys, inclusive and exclusive
+
+ for (int i = 0; i <= end; i++) {
+ for (int j = i; j <= end; j++) {
+ // begin inclusive, end inclusive
+
+ cursor = newCursor(view, keys[i], true, keys[j],
+ true, reversed);
+ expectRange(KEYS, i, j, reversed);
+ cursor.close();
+
+ // begin inclusive, end exclusive
+
+ cursor = newCursor(view, keys[i], true, keys[j],
+ false, reversed);
+ expectRange(KEYS, i, j - 1, reversed);
+ cursor.close();
+
+ // begin exclusive, end inclusive
+
+ cursor = newCursor(view, keys[i], false, keys[j],
+ true, reversed);
+ expectRange(KEYS, i + 1, j, reversed);
+ cursor.close();
+
+ // begin exclusive, end exclusive
+
+ cursor = newCursor(view, keys[i], false, keys[j],
+ false, reversed);
+ expectRange(KEYS, i + 1, j - 1, reversed);
+ cursor.close();
+ }
+ }
+
+ // single key range
+
+ for (int i = 0; i <= end; i++) {
+ cursor = new DataCursor(view, false, keys[i]);
+ expectRange(KEYS, i, i, reversed);
+ cursor.close();
+ }
+
+ // start with lower extreme (before any existing key)
+
+ cursor = newCursor(view, extremeKeys[0], true, null, false, reversed);
+ expectRange(KEYS, 0, end, reversed);
+ cursor.close();
+
+ // start with higher extreme (after any existing key)
+
+ cursor = newCursor(view, null, false, extremeKeys[1], true, reversed);
+ expectRange(KEYS, 0, end, reversed);
+ cursor.close();
+ }
+
+ private DataCursor newCursor(DataView view,
+ Object beginKey, boolean beginInclusive,
+ Object endKey, boolean endInclusive,
+ boolean reversed)
+ throws Exception {
+
+ if (reversed) {
+ return new DataCursor(view, false,
+ endKey, endInclusive,
+ beginKey, beginInclusive);
+ } else {
+ return new DataCursor(view, false,
+ beginKey, beginInclusive,
+ endKey, endInclusive);
+ }
+ }
+
+ private void expectRange(byte[][] bytes, int first, int last,
+ boolean reversed)
+ throws DatabaseException {
+
+ int i;
+ boolean init;
+ for (init = true, i = first;; i++, init = false) {
+ if (checkRange(bytes, first, last, i <= last,
+ reversed, !reversed, init, i)) {
+ break;
+ }
+ }
+ for (init = true, i = last;; i--, init = false) {
+ if (checkRange(bytes, first, last, i >= first,
+ reversed, reversed, init, i)) {
+ break;
+ }
+ }
+ }
+
+ private boolean checkRange(byte[][] bytes, int first, int last,
+ boolean inRange, boolean reversed,
+ boolean forward, boolean init,
+ int i)
+ throws DatabaseException {
+
+ OperationStatus s;
+ if (forward) {
+ if (init) {
+ s = cursor.getFirst(false);
+ } else {
+ s = cursor.getNext(false);
+ }
+ } else {
+ if (init) {
+ s = cursor.getLast(false);
+ } else {
+ s = cursor.getPrev(false);
+ }
+ }
+
+ String msg = " " + (forward ? "next" : "prev") + " i=" + i +
+ " first=" + first + " last=" + last +
+ (reversed ? " reversed" : " not reversed");
+
+ // check that moving past ends doesn't move the cursor
+ if (s == OperationStatus.SUCCESS && i == first) {
+ OperationStatus s2 = reversed ? cursor.getNext(false)
+ : cursor.getPrev(false);
+ assertEquals(msg, OperationStatus.NOTFOUND, s2);
+ }
+ if (s == OperationStatus.SUCCESS && i == last) {
+ OperationStatus s2 = reversed ? cursor.getPrev(false)
+ : cursor.getNext(false);
+ assertEquals(msg, OperationStatus.NOTFOUND, s2);
+ }
+
+ byte[] val = (s == OperationStatus.SUCCESS)
+ ? ((byte[]) cursor.getCurrentValue())
+ : null;
+
+ if (inRange) {
+ assertNotNull("RangeNotFound" + msg, val);
+
+ if (!Arrays.equals(val, bytes[i])){
+ printBytes(val);
+ printBytes(bytes[i]);
+ fail("RangeKeyNotEqual" + msg);
+ }
+ if (VERBOSE) {
+ System.out.println("GotRange" + msg);
+ }
+ return false;
+ } else {
+ assertEquals("RangeExceeded" + msg, OperationStatus.NOTFOUND, s);
+ return true;
+ }
+ }
+
+ private void printBytes(byte[] bytes) {
+
+ for (int i = 0; i < bytes.length; i += 1) {
+ System.out.print(Integer.toHexString(bytes[i] & 0xFF));
+ System.out.print(' ');
+ }
+ System.out.println();
+ }
+
+ public void testSubRanges() {
+
+ DatabaseEntry begin = new DatabaseEntry();
+ DatabaseEntry begin2 = new DatabaseEntry();
+ DatabaseEntry end = new DatabaseEntry();
+ DatabaseEntry end2 = new DatabaseEntry();
+ KeyRange range = new KeyRange(null);
+ KeyRange range2 = null;
+
+ /* Base range [1, 2] */
+ begin.setData(new byte[] { 1 });
+ end.setData(new byte[] { 2 });
+ range = range.subRange(begin, true, end, true);
+
+ /* Subrange (0, 1] is invalid **. */
+ begin2.setData(new byte[] { 0 });
+ end2.setData(new byte[] { 1 });
+ try {
+ range2 = range.subRange(begin2, false, end2, true);
+ fail();
+ } catch (KeyRangeException expected) {}
+
+ /* Subrange [1, 3) is invalid. */
+ begin2.setData(new byte[] { 1 });
+ end2.setData(new byte[] { 3 });
+ try {
+ range2 = range.subRange(begin2, true, end2, false);
+ fail();
+ } catch (KeyRangeException expected) {}
+
+ /* Subrange [2, 2] is valid. */
+ begin2.setData(new byte[] { 2 });
+ end2.setData(new byte[] { 2 });
+ range2 = range.subRange(begin2, true, end2, true);
+
+ /* Subrange [0, 1] is invalid. */
+ begin2.setData(new byte[] { 0 });
+ end2.setData(new byte[] { 1 });
+ try {
+ range2 = range.subRange(begin2, true, end2, true);
+ fail();
+ } catch (KeyRangeException expected) {}
+
+ /* Subrange (0, 3] is invalid. */
+ begin2.setData(new byte[] { 0 });
+ end2.setData(new byte[] { 3 });
+ try {
+ range2 = range.subRange(begin2, false, end2, true);
+ fail();
+ } catch (KeyRangeException expected) {}
+
+ /* Subrange [3, 3) is invalid. */
+ begin2.setData(new byte[] { 3 });
+ end2.setData(new byte[] { 3 });
+ try {
+ range2 = range.subRange(begin2, true, end2, false);
+ fail();
+ } catch (KeyRangeException expected) {}
+ }
+
+ @SuppressWarnings("serial")
+ public static class ReverseComparator implements Comparator<byte[]>,
+ Serializable {
+ public int compare(byte[] d1, byte[] d2) {
+ int cmp = KeyRange.compareBytes(d1, 0, d1.length,
+ d2, 0, d2.length);
+ if (cmp < 0) {
+ return 1;
+ } else if (cmp > 0) {
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+ }
+}
diff --git a/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/CollectionTest.java b/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/CollectionTest.java
new file mode 100644
index 0000000..e690c01
--- /dev/null
+++ b/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/CollectionTest.java
@@ -0,0 +1,3048 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.collections.test;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.concurrent.ConcurrentMap;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.sleepycat.bind.EntityBinding;
+import com.sleepycat.bind.EntryBinding;
+import com.sleepycat.collections.MapEntryParameter;
+import com.sleepycat.collections.StoredCollection;
+import com.sleepycat.collections.StoredCollections;
+import com.sleepycat.collections.StoredContainer;
+import com.sleepycat.collections.StoredEntrySet;
+import com.sleepycat.collections.StoredIterator;
+import com.sleepycat.collections.StoredKeySet;
+import com.sleepycat.collections.StoredList;
+import com.sleepycat.collections.StoredMap;
+import com.sleepycat.collections.StoredSortedEntrySet;
+import com.sleepycat.collections.StoredSortedKeySet;
+import com.sleepycat.collections.StoredSortedMap;
+import com.sleepycat.collections.StoredSortedValueSet;
+import com.sleepycat.collections.StoredValueSet;
+import com.sleepycat.collections.TransactionRunner;
+import com.sleepycat.collections.TransactionWorker;
+import com.sleepycat.compat.DbCompat;
+import com.sleepycat.db.Database;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.Environment;
+import com.sleepycat.util.ExceptionUnwrapper;
+import com.sleepycat.util.test.SharedTestUtils;
+import com.sleepycat.util.test.TestEnv;
+
+/**
+ * @author Mark Hayes
+ */
+public class CollectionTest extends TestCase {
+
+ private static final int NONE = 0;
+ private static final int SUB = 1;
+ private static final int HEAD = 2;
+ private static final int TAIL = 3;
+
+ /*
+ * For long tests we permute testStoredIterator to test both StoredIterator
+ * and BlockIterator. When testing BlockIterator, we permute the maxKey
+ * over the array values below. BlockIterator's block size is 10. So we
+ * test below the block size (6), at the block size (10), and above it (14
+ * and 22).
+ */
+ private static final int DEFAULT_MAX_KEY = 6;
+ private static final int[] MAX_KEYS = {6, 10, 14, 22};
+
+ private boolean testStoredIterator;
+ private int maxKey; /* Must be a multiple of 2. */
+ private int beginKey = 1;
+ private int endKey;
+
+ private Environment env;
+ private Database store;
+ private Database index;
+ private final boolean isEntityBinding;
+ private final boolean isAutoCommit;
+ private TestStore testStore;
+ private String testName;
+ private final EntryBinding keyBinding;
+ private final EntryBinding valueBinding;
+ private final EntityBinding entityBinding;
+ private TransactionRunner readRunner;
+ private TransactionRunner writeRunner;
+ private TransactionRunner writeIterRunner;
+ private TestEnv testEnv;
+
+ private StoredMap map;
+ private StoredMap imap; // insertable map (primary store for indexed map)
+ private StoredSortedMap smap; // sorted map (null or equal to map)
+ private StoredMap saveMap;
+ private StoredSortedMap saveSMap;
+ private int rangeType;
+ private StoredList list;
+ private StoredList ilist; // insertable list (primary store for index list)
+ private StoredList saveList;
+ private StoredKeySet keySet;
+ private StoredValueSet valueSet;
+
+ /**
+ * Runs a command line collection test.
+ * @see #usage
+ */
+ public static void main(String[] args) {
+ if (args.length == 1 &&
+ (args[0].equals("-h") || args[0].equals("-help"))) {
+ usage();
+ } else {
+ junit.framework.TestResult tr =
+ junit.textui.TestRunner.run(suite(args));
+ if (tr.errorCount() > 0 ||
+ tr.failureCount() > 0) {
+ System.exit(1);
+ } else {
+ System.exit(0);
+ }
+ }
+ }
+
+ private static void usage() {
+
+ System.out.println(
+ "Usage: java com.sleepycat.collections.test.CollectionTest\n" +
+ " -h | -help\n" +
+ " [testName]...\n" +
+ " where testName has the format:\n" +
+ " <env>-<store>-{entity|value}\n" +
+ " <env> is:\n" +
+ " bdb | cdb | txn\n" +
+ " <store> is:\n" +
+ " btree-uniq | btree-dup | btree-dupsort | btree-recnum |\n" +
+ " hash-uniq | hash-dup | hash-dupsort |\n" +
+ " queue | recno | recno-renum\n" +
+ " For example: bdb-btree-uniq-entity\n" +
+ " If no arguments are given then all tests are run.");
+ System.exit(2);
+ }
+
+ public static Test suite() {
+ return suite(null);
+ }
+
+ static Test suite(String[] args) {
+ if (SharedTestUtils.runLongTests()) {
+ TestSuite suite = new TestSuite();
+
+ /* StoredIterator tests. */
+ permuteTests(args, suite, true, DEFAULT_MAX_KEY);
+
+ /* BlockIterator tests with different maxKey values. */
+ for (int i = 0; i < MAX_KEYS.length; i += 1) {
+ permuteTests(args, suite, false, MAX_KEYS[i]);
+ }
+
+ return suite;
+ } else {
+ return baseSuite(args);
+ }
+ }
+
+ private static void permuteTests(String[] args,
+ TestSuite suite,
+ boolean storedIter,
+ int maxKey) {
+ TestSuite baseTests = baseSuite(args);
+ Enumeration e = baseTests.tests();
+ while (e.hasMoreElements()) {
+ CollectionTest t = (CollectionTest) e.nextElement();
+ t.setParams(storedIter, maxKey);
+ suite.addTest(t);
+ }
+ }
+
+ private static TestSuite baseSuite(String[] args) {
+ TestSuite suite = new TestSuite();
+ for (int i = 0; i < TestEnv.ALL.length; i += 1) {
+ for (int j = 0; j < TestStore.ALL.length; j += 1) {
+ for (int k = 0; k < 2; k += 1) {
+ boolean entityBinding = (k != 0);
+
+ addTest(args, suite, new CollectionTest(
+ TestEnv.ALL[i], TestStore.ALL[j],
+ entityBinding, false));
+
+ if (TestEnv.ALL[i].isTxnMode()) {
+ addTest(args, suite, new CollectionTest(
+ TestEnv.ALL[i], TestStore.ALL[j],
+ entityBinding, true));
+ }
+ }
+ }
+ }
+ return suite;
+ }
+
+ private static void addTest(String[] args, TestSuite suite,
+ CollectionTest test) {
+
+ if (args == null || args.length == 0) {
+ suite.addTest(test);
+ } else {
+ for (int t = 0; t < args.length; t += 1) {
+ if (args[t].equals(test.testName)) {
+ suite.addTest(test);
+ break;
+ }
+ }
+ }
+ }
+
+ public CollectionTest(TestEnv testEnv, TestStore testStore,
+ boolean isEntityBinding, boolean isAutoCommit) {
+
+ super(null);
+
+ this.testEnv = testEnv;
+ this.testStore = testStore;
+ this.isEntityBinding = isEntityBinding;
+ this.isAutoCommit = isAutoCommit;
+
+ keyBinding = testStore.getKeyBinding();
+ valueBinding = testStore.getValueBinding();
+ entityBinding = testStore.getEntityBinding();
+
+ setParams(false, DEFAULT_MAX_KEY);
+ }
+
+ private void setParams(boolean storedIter, int maxKey) {
+
+ this.testStoredIterator = storedIter;
+ this.maxKey = maxKey;
+ this.endKey = maxKey;
+
+ testName = testEnv.getName() + '-' + testStore.getName() +
+ (isEntityBinding ? "-entity" : "-value") +
+ (isAutoCommit ? "-autoCommit" : "") +
+ (testStoredIterator ? "-storedIter" : "") +
+ ((maxKey != DEFAULT_MAX_KEY) ? ("-maxKey-" + maxKey) : "");
+ }
+
+ @Override
+ public void tearDown() {
+ setName(testName);
+ }
+
+ @Override
+ public void runTest()
+ throws Exception {
+
+ SharedTestUtils.printTestName(SharedTestUtils.qualifiedTestName(this));
+ try {
+ env = testEnv.open(testName);
+
+ // For testing auto-commit, use a normal (transactional) runner for
+ // all reading and for writing via an iterator, and a do-nothing
+ // runner for writing via collections; if auto-commit is tested,
+ // the per-collection auto-commit property will be set elsewhere.
+ //
+ TransactionRunner normalRunner = newTransactionRunner(env);
+ normalRunner.setAllowNestedTransactions(
+ DbCompat.NESTED_TRANSACTIONS);
+ TransactionRunner nullRunner = new NullTransactionRunner(env);
+ readRunner = nullRunner;
+ if (isAutoCommit) {
+ writeRunner = nullRunner;
+ writeIterRunner = testStoredIterator ? normalRunner
+ : nullRunner;
+ } else {
+ writeRunner = normalRunner;
+ writeIterRunner = normalRunner;
+ }
+
+ store = testStore.open(env, "unindexed.db");
+ testUnindexed();
+ store.close();
+ store = null;
+
+ TestStore indexOf = testStore.getIndexOf();
+ if (indexOf != null) {
+ store = indexOf.open(env, "indexed.db");
+ index = testStore.openIndex(store, "index.db");
+ testIndexed();
+ index.close();
+ index = null;
+ store.close();
+ store = null;
+ }
+ env.close();
+ env = null;
+ } catch (Exception e) {
+ throw ExceptionUnwrapper.unwrap(e);
+ } finally {
+ if (index != null) {
+ try {
+ index.close();
+ } catch (Exception e) {
+ }
+ }
+ if (store != null) {
+ try {
+ store.close();
+ } catch (Exception e) {
+ }
+ }
+ if (env != null) {
+ try {
+ env.close();
+ } catch (Exception e) {
+ }
+ }
+ /* Ensure that GC can cleanup. */
+ index = null;
+ store = null;
+ env = null;
+ readRunner = null;
+ writeRunner = null;
+ writeIterRunner = null;
+ map = null;
+ imap = null;
+ smap = null;
+ saveMap = null;
+ saveSMap = null;
+ list = null;
+ ilist = null;
+ saveList = null;
+ keySet = null;
+ valueSet = null;
+ testEnv = null;
+ testStore = null;
+ }
+ }
+
+ /**
+ * Is overridden in XACollectionTest.
+ * @throws DatabaseException from subclasses.
+ */
+ protected TransactionRunner newTransactionRunner(Environment env)
+ throws DatabaseException {
+
+ return new TransactionRunner(env);
+ }
+
+ void testCreation(StoredContainer cont, int expectSize) {
+ assertEquals(index != null, cont.isSecondary());
+ assertEquals(testStore.isOrdered(), cont.isOrdered());
+ assertEquals(testStore.areKeyRangesAllowed(),
+ cont.areKeyRangesAllowed());
+ assertEquals(testStore.areKeysRenumbered(), cont.areKeysRenumbered());
+ assertEquals(testStore.areDuplicatesAllowed(),
+ cont.areDuplicatesAllowed());
+ assertEquals(testEnv.isTxnMode(), cont.isTransactional());
+ assertEquals(expectSize, cont.size());
+ }
+
+ void testMapCreation(ConcurrentMap map) {
+ assertTrue(map.values() instanceof Set);
+ assertEquals(testStore.areKeyRangesAllowed(),
+ map.keySet() instanceof SortedSet);
+ assertEquals(testStore.areKeyRangesAllowed(),
+ map.entrySet() instanceof SortedSet);
+ assertEquals(testStore.areKeyRangesAllowed() && isEntityBinding,
+ map.values() instanceof SortedSet);
+ }
+
+ void testUnindexed()
+ throws Exception {
+
+ // create primary map
+ if (testStore.areKeyRangesAllowed()) {
+ if (isEntityBinding) {
+ smap = new StoredSortedMap(store, keyBinding,
+ entityBinding,
+ testStore.getKeyAssigner());
+ valueSet = new StoredSortedValueSet(store, entityBinding,
+ true);
+ } else {
+ smap = new StoredSortedMap(store, keyBinding,
+ valueBinding,
+ testStore.getKeyAssigner());
+ // sorted value set is not possible since key cannot be derived
+ // for performing subSet, etc.
+ }
+ keySet = new StoredSortedKeySet(store, keyBinding, true);
+ map = smap;
+ } else {
+ if (isEntityBinding) {
+ map = new StoredMap(store, keyBinding, entityBinding,
+ testStore.getKeyAssigner());
+ valueSet = new StoredValueSet(store, entityBinding, true);
+ } else {
+ map = new StoredMap(store, keyBinding, valueBinding,
+ testStore.getKeyAssigner());
+ valueSet = new StoredValueSet(store, valueBinding, true);
+ }
+ smap = null;
+ keySet = new StoredKeySet(store, keyBinding, true);
+ }
+ imap = map;
+
+ // create primary list
+ if (testStore.hasRecNumAccess()) {
+ if (isEntityBinding) {
+ ilist = new StoredList(store, entityBinding,
+ testStore.getKeyAssigner());
+ } else {
+ ilist = new StoredList(store, valueBinding,
+ testStore.getKeyAssigner());
+ }
+ list = ilist;
+ } else {
+ try {
+ if (isEntityBinding) {
+ ilist = new StoredList(store, entityBinding,
+ testStore.getKeyAssigner());
+ } else {
+ ilist = new StoredList(store, valueBinding,
+ testStore.getKeyAssigner());
+ }
+ fail();
+ } catch (IllegalArgumentException expected) {}
+ }
+
+ testCreation(map, 0);
+ if (list != null) {
+ testCreation(list, 0);
+ }
+ testMapCreation(map);
+ addAll();
+ testAll();
+ }
+
+ void testIndexed()
+ throws Exception {
+
+ // create primary map
+ if (isEntityBinding) {
+ map = new StoredMap(store, keyBinding, entityBinding,
+ testStore.getKeyAssigner());
+ } else {
+ map = new StoredMap(store, keyBinding, valueBinding,
+ testStore.getKeyAssigner());
+ }
+ imap = map;
+ smap = null;
+ // create primary list
+ if (testStore.hasRecNumAccess()) {
+ if (isEntityBinding) {
+ list = new StoredList(store, entityBinding,
+ testStore.getKeyAssigner());
+ } else {
+ list = new StoredList(store, valueBinding,
+ testStore.getKeyAssigner());
+ }
+ ilist = list;
+ }
+
+ addAll();
+ readAll();
+
+ // create indexed map (keySet/valueSet)
+ if (testStore.areKeyRangesAllowed()) {
+ if (isEntityBinding) {
+ map = smap = new StoredSortedMap(index, keyBinding,
+ entityBinding, true);
+ valueSet = new StoredSortedValueSet(index, entityBinding,
+ true);
+ } else {
+ map = smap = new StoredSortedMap(index, keyBinding,
+ valueBinding, true);
+ // sorted value set is not possible since key cannot be derived
+ // for performing subSet, etc.
+ }
+ keySet = new StoredSortedKeySet(index, keyBinding, true);
+ } else {
+ if (isEntityBinding) {
+ map = new StoredMap(index, keyBinding, entityBinding, true);
+ valueSet = new StoredValueSet(index, entityBinding, true);
+ } else {
+ map = new StoredMap(index, keyBinding, valueBinding, true);
+ valueSet = new StoredValueSet(index, valueBinding, true);
+ }
+ smap = null;
+ keySet = new StoredKeySet(index, keyBinding, true);
+ }
+
+ // create indexed list
+ if (testStore.hasRecNumAccess()) {
+ if (isEntityBinding) {
+ list = new StoredList(index, entityBinding, true);
+ } else {
+ list = new StoredList(index, valueBinding, true);
+ }
+ } else {
+ try {
+ if (isEntityBinding) {
+ list = new StoredList(index, entityBinding, true);
+ } else {
+ list = new StoredList(index, valueBinding, true);
+ }
+ fail();
+ } catch (IllegalArgumentException expected) {}
+ }
+
+ testCreation(map, maxKey);
+ testCreation((StoredContainer) map.values(), maxKey);
+ testCreation((StoredContainer) map.keySet(), maxKey);
+ testCreation((StoredContainer) map.entrySet(), maxKey);
+ if (list != null) {
+ testCreation(list, maxKey);
+ }
+ testMapCreation(map);
+ testAll();
+ }
+
+ void testAll()
+ throws Exception {
+
+ checkKeySetAndValueSet();
+ readAll();
+ updateAll();
+ readAll();
+ if (!map.areKeysRenumbered()) {
+ removeOdd();
+ readEven();
+ addOdd();
+ readAll();
+ removeOddIter();
+ readEven();
+ if (imap.areDuplicatesAllowed()) {
+ addOddDup();
+ } else {
+ addOdd();
+ }
+ readAll();
+ removeOddEntry();
+ readEven();
+ addOdd();
+ readAll();
+ if (isEntityBinding) {
+ removeOddEntity();
+ readEven();
+ addOddEntity();
+ readAll();
+ }
+ bulkOperations();
+ }
+ if (isListAddAllowed()) {
+ removeOddList();
+ readEvenList();
+ addOddList();
+ readAll();
+ if (!isEntityBinding) {
+ removeOddListValue();
+ readEvenList();
+ addOddList();
+ readAll();
+ }
+ }
+ if (list != null) {
+ bulkListOperations();
+ } else {
+ listOperationsNotAllowed();
+ }
+ if (smap != null) {
+ readWriteRange(SUB, 1, 1);
+ readWriteRange(HEAD, 1, 1);
+ readWriteRange(SUB, 1, maxKey);
+ readWriteRange(HEAD, 1, maxKey);
+ readWriteRange(TAIL, 1, maxKey);
+ readWriteRange(SUB, 1, 3);
+ readWriteRange(HEAD, 1, 3);
+ readWriteRange(SUB, 2, 2);
+ readWriteRange(SUB, 2, maxKey);
+ readWriteRange(TAIL, 2, maxKey);
+ readWriteRange(SUB, maxKey, maxKey);
+ readWriteRange(TAIL, maxKey, maxKey);
+ readWriteRange(SUB, maxKey + 1, maxKey + 1);
+ readWriteRange(TAIL, maxKey + 1, maxKey + 1);
+ readWriteRange(SUB, 0, 0);
+ readWriteRange(HEAD, 0, 0);
+ }
+ updateAll();
+ readAll();
+ if (map.areDuplicatesAllowed()) {
+ readWriteDuplicates();
+ readAll();
+ } else {
+ duplicatesNotAllowed();
+ readAll();
+ }
+ if (testEnv.isCdbMode()) {
+ testCdbLocking();
+ }
+ removeAll();
+ if (!map.areKeysRenumbered()) {
+ testConcurrentMap();
+ }
+ if (isListAddAllowed()) {
+ testIterAddList();
+ clearAll();
+ }
+ if (imap.areDuplicatesAllowed()) {
+ testIterAddDuplicates();
+ clearAll();
+ }
+ if (isListAddAllowed()) {
+ addAllList();
+ readAll();
+ removeAllList();
+ }
+ appendAll();
+ }
+
+ void checkKeySetAndValueSet() {
+
+ // use bulk operations to check that explicitly constructed
+ // keySet/valueSet are equivalent
+ assertEquals(keySet, imap.keySet());
+ if (valueSet != null) {
+ assertEquals(valueSet, imap.values());
+ }
+ }
+
+ Iterator iterator(Collection storedCollection) {
+
+ if (testStoredIterator) {
+ return ((StoredCollection) storedCollection).storedIterator();
+ } else {
+ return storedCollection.iterator();
+ }
+ }
+
+ void addAll()
+ throws Exception {
+
+ writeRunner.run(new TransactionWorker() {
+ public void doWork() {
+ assertTrue(imap.isEmpty());
+ Iterator iter = iterator(imap.entrySet());
+ try {
+ assertTrue(!iter.hasNext());
+ } finally {
+ StoredIterator.close(iter);
+ }
+ assertEquals(0, imap.keySet().toArray().length);
+ assertEquals(0, imap.keySet().toArray(new Object[0]).length);
+ assertEquals(0, imap.entrySet().toArray().length);
+ assertEquals(0, imap.entrySet().toArray(new Object[0]).length);
+ assertEquals(0, imap.values().toArray().length);
+ assertEquals(0, imap.values().toArray(new Object[0]).length);
+
+ for (int i = beginKey; i <= endKey; i += 1) {
+ Long key = makeKey(i);
+ Object val = makeVal(i);
+ assertNull(imap.get(key));
+ assertTrue(!imap.keySet().contains(key));
+ assertTrue(!imap.values().contains(val));
+ assertNull(imap.put(key, val));
+ assertEquals(val, imap.get(key));
+ assertTrue(imap.keySet().contains(key));
+ assertTrue(imap.values().contains(val));
+ assertTrue(imap.duplicates(key).contains(val));
+ if (!imap.areDuplicatesAllowed()) {
+ assertEquals(val, imap.put(key, val));
+ }
+ checkDupsSize(1, imap.duplicates(key));
+ }
+ assertTrue(!imap.isEmpty());
+ }
+ });
+ }
+
+ void appendAll()
+ throws Exception {
+
+ writeRunner.run(new TransactionWorker() {
+ public void doWork() {
+ assertTrue(imap.isEmpty());
+
+ TestKeyAssigner keyAssigner = testStore.getKeyAssigner();
+ if (keyAssigner != null) {
+ keyAssigner.reset();
+ }
+
+ for (int i = beginKey; i <= endKey; i += 1) {
+ boolean useList = (i & 1) == 0;
+ Long key = makeKey(i);
+ Object val = makeVal(i);
+ assertNull(imap.get(key));
+ if (keyAssigner != null) {
+ if (useList && ilist != null) {
+ assertEquals(i - 1, ilist.append(val));
+ } else {
+ assertEquals(key, imap.append(val));
+ }
+ assertEquals(val, imap.get(key));
+ } else {
+ Long recnoKey;
+ if (useList && ilist != null) {
+ recnoKey = new Long(ilist.append(val) + 1);
+ } else {
+ recnoKey = (Long) imap.append(val);
+ }
+ assertNotNull(recnoKey);
+ Object recnoVal;
+ if (isEntityBinding) {
+ recnoVal = makeEntity(recnoKey.intValue(), i);
+ } else {
+ recnoVal = val;
+ }
+ assertEquals(recnoVal, imap.get(recnoKey));
+ }
+ }
+ }
+ });
+ }
+
+ void updateAll()
+ throws Exception {
+
+ writeRunner.run(new TransactionWorker() {
+ public void doWork() throws Exception {
+ for (int i = beginKey; i <= endKey; i += 1) {
+ Long key = makeKey(i);
+ Object val = makeVal(i);
+ if (!imap.areDuplicatesAllowed()) {
+ assertEquals(val, imap.put(key, val));
+ }
+ if (isEntityBinding) {
+ assertTrue(!imap.values().add(val));
+ }
+ checkDupsSize(1, imap.duplicates(key));
+ if (ilist != null) {
+ int idx = i - 1;
+ assertEquals(val, ilist.set(idx, val));
+ }
+ }
+ updateIter(map.entrySet());
+ updateIter(map.values());
+ if (beginKey <= endKey) {
+ ListIterator iter = (ListIterator) iterator(map.keySet());
+ try {
+ assertNotNull(iter.next());
+ iter.set(makeKey(beginKey));
+ fail();
+ } catch (UnsupportedOperationException e) {
+ } finally {
+ StoredIterator.close(iter);
+ }
+ }
+ if (list != null) {
+ updateIter(list);
+ }
+ }
+ });
+ }
+
+ void updateIter(final Collection coll)
+ throws Exception {
+
+ writeIterRunner.run(new TransactionWorker() {
+ public void doWork() {
+ ListIterator iter = (ListIterator) iterator(coll);
+ try {
+ for (int i = beginKey; i <= endKey; i += 1) {
+ assertTrue(iter.hasNext());
+ Object obj = iter.next();
+ if (map.isOrdered()) {
+ assertEquals(i, intIter(coll, obj));
+ }
+ if (index != null) {
+ try {
+ setValuePlusOne(iter, obj);
+ fail();
+ } catch (UnsupportedOperationException e) {}
+ } else if
+ (((StoredCollection) coll).areDuplicatesOrdered()) {
+ try {
+ setValuePlusOne(iter, obj);
+ fail();
+ } catch (RuntimeException e) {
+ Exception e2 = ExceptionUnwrapper.unwrap(e);
+ assertTrue(e2.getClass().getName(),
+ e2 instanceof IllegalArgumentException ||
+ e2 instanceof DatabaseException);
+ }
+ } else {
+ setValuePlusOne(iter, obj);
+ /* Ensure iterator position is correct. */
+ if (map.isOrdered()) {
+ assertTrue(iter.hasPrevious());
+ obj = iter.previous();
+ assertEquals(i, intIter(coll, obj));
+ assertTrue(iter.hasNext());
+ obj = iter.next();
+ assertEquals(i, intIter(coll, obj));
+ }
+ }
+ }
+ assertTrue(!iter.hasNext());
+ } finally {
+ StoredIterator.close(iter);
+ }
+ }
+ });
+ }
+
+ void setValuePlusOne(ListIterator iter, Object obj) {
+
+ if (obj instanceof Map.Entry) {
+ Map.Entry entry = (Map.Entry) obj;
+ Long key = (Long) entry.getKey();
+ Object oldVal = entry.getValue();
+ Object val = makeVal(key.intValue() + 1);
+ if (isEntityBinding) {
+ try {
+ // must fail on attempt to change the key via an entity
+ entry.setValue(val);
+ fail();
+ } catch (IllegalArgumentException e) {}
+ val = makeEntity(key.intValue(), key.intValue() + 1);
+ }
+ entry.setValue(val);
+ assertEquals(val, entry.getValue());
+ assertEquals(val, map.get(key));
+ assertTrue(map.duplicates(key).contains(val));
+ checkDupsSize(1, map.duplicates(key));
+ entry.setValue(oldVal);
+ assertEquals(oldVal, entry.getValue());
+ assertEquals(oldVal, map.get(key));
+ assertTrue(map.duplicates(key).contains(oldVal));
+ checkDupsSize(1, map.duplicates(key));
+ } else {
+ Object oldVal = obj;
+ Long key = makeKey(intVal(obj));
+ Object val = makeVal(key.intValue() + 1);
+ if (isEntityBinding) {
+ try {
+ // must fail on attempt to change the key via an entity
+ iter.set(val);
+ fail();
+ } catch (IllegalArgumentException e) {}
+ val = makeEntity(key.intValue(), key.intValue() + 1);
+ }
+ iter.set(val);
+ assertEquals(val, map.get(key));
+ assertTrue(map.duplicates(key).contains(val));
+ checkDupsSize(1, map.duplicates(key));
+ iter.set(oldVal);
+ assertEquals(oldVal, map.get(key));
+ assertTrue(map.duplicates(key).contains(oldVal));
+ checkDupsSize(1, map.duplicates(key));
+ }
+ }
+
+ void removeAll()
+ throws Exception {
+
+ writeIterRunner.run(new TransactionWorker() {
+ public void doWork() {
+ assertTrue(!map.isEmpty());
+ ListIterator iter = null;
+ try {
+ if (list != null) {
+ iter = (ListIterator) iterator(list);
+ } else {
+ iter = (ListIterator) iterator(map.values());
+ }
+ iteratorSetAndRemoveNotAllowed(iter);
+
+ Object val = iter.next();
+ assertNotNull(val);
+ iter.remove();
+ iteratorSetAndRemoveNotAllowed(iter);
+
+ if (index == null) {
+ val = iter.next();
+ assertNotNull(val);
+ iter.set(val);
+
+ if (map.areDuplicatesAllowed()) {
+ iter.add(makeVal(intVal(val), intVal(val) + 1));
+ iteratorSetAndRemoveNotAllowed(iter);
+ }
+ }
+ } finally {
+ StoredIterator.close(iter);
+ }
+ map.clear();
+ assertTrue(map.isEmpty());
+ assertTrue(map.entrySet().isEmpty());
+ assertTrue(map.keySet().isEmpty());
+ assertTrue(map.values().isEmpty());
+ for (int i = beginKey; i <= endKey; i += 1) {
+ Long key = makeKey(i);
+ Object val = makeVal(i);
+ assertNull(map.get(key));
+ assertTrue(!map.duplicates(key).contains(val));
+ checkDupsSize(0, map.duplicates(key));
+ }
+ }
+ });
+ }
+
+ void clearAll()
+ throws Exception {
+
+ writeRunner.run(new TransactionWorker() {
+ public void doWork() {
+ map.clear();
+ assertTrue(map.isEmpty());
+ }
+ });
+ }
+
+ /**
+ * Tests that removing while iterating works properly, especially when
+ * removing everything in the key range or everything from some point to
+ * the end of the range. [#15858]
+ */
+ void removeIter()
+ throws Exception {
+
+ writeIterRunner.run(new TransactionWorker() {
+ public void doWork() {
+ ListIterator iter;
+
+ /* Save contents. */
+ HashMap<Object,Object> savedMap =
+ new HashMap<Object,Object>(map);
+ assertEquals(savedMap, map);
+
+ /* Remove all moving forward. */
+ iter = (ListIterator) iterator(map.keySet());
+ try {
+ while (iter.hasNext()) {
+ assertNotNull(iter.next());
+ iter.remove();
+ }
+ assertTrue(!iter.hasNext());
+ assertTrue(!iter.hasPrevious());
+ assertTrue(map.isEmpty());
+ } finally {
+ StoredIterator.close(iter);
+ }
+
+ /* Restore contents. */
+ imap.putAll(savedMap);
+ assertEquals(savedMap, map);
+
+ /* Remove all moving backward. */
+ iter = (ListIterator) iterator(map.keySet());
+ try {
+ while (iter.hasNext()) {
+ assertNotNull(iter.next());
+ }
+ while (iter.hasPrevious()) {
+ assertNotNull(iter.previous());
+ iter.remove();
+ }
+ assertTrue(!iter.hasNext());
+ assertTrue(!iter.hasPrevious());
+ assertTrue(map.isEmpty());
+ } finally {
+ StoredIterator.close(iter);
+ }
+
+ /* Restore contents. */
+ imap.putAll(savedMap);
+ assertEquals(savedMap, map);
+
+ int first = Math.max(1, beginKey);
+ int last = Math.min(maxKey, endKey);
+
+ /* Skip N forward, remove all from that point forward. */
+ for (int readTo = first + 1; readTo <= last; readTo += 1) {
+ iter = (ListIterator) iterator(map.keySet());
+ try {
+ for (int i = first; i < readTo; i += 1) {
+ assertTrue(iter.hasNext());
+ assertNotNull(iter.next());
+ }
+ for (int i = readTo; i <= last; i += 1) {
+ assertTrue(iter.hasNext());
+ assertNotNull(iter.next());
+ iter.remove();
+ }
+ assertTrue(!iter.hasNext());
+ assertTrue(iter.hasPrevious());
+ assertEquals(readTo - first, map.size());
+ } finally {
+ StoredIterator.close(iter);
+ }
+
+ /* Restore contents. */
+ for (Map.Entry entry : savedMap.entrySet()) {
+ if (!imap.entrySet().contains(entry)) {
+ imap.put(entry.getKey(), entry.getValue());
+ }
+ }
+ assertEquals(savedMap, map);
+ }
+
+ /* Skip N backward, remove all from that point backward. */
+ for (int readTo = last - 1; readTo >= first; readTo -= 1) {
+ iter = (ListIterator) iterator(map.keySet());
+ try {
+ while (iter.hasNext()) {
+ assertNotNull(iter.next());
+ }
+ for (int i = last; i > readTo; i -= 1) {
+ assertTrue(iter.hasPrevious());
+ assertNotNull(iter.previous());
+ }
+ for (int i = readTo; i >= first; i -= 1) {
+ assertTrue(iter.hasPrevious());
+ assertNotNull(iter.previous());
+ iter.remove();
+ }
+ assertTrue(!iter.hasPrevious());
+ assertTrue(iter.hasNext());
+ assertEquals(last - readTo, map.size());
+ } finally {
+ StoredIterator.close(iter);
+ }
+
+ /* Restore contents. */
+ for (Map.Entry entry : savedMap.entrySet()) {
+ if (!imap.entrySet().contains(entry)) {
+ imap.put(entry.getKey(), entry.getValue());
+ }
+ }
+ assertEquals(savedMap, map);
+ }
+ }
+ });
+ }
+
+ void iteratorSetAndRemoveNotAllowed(ListIterator i) {
+
+ try {
+ i.remove();
+ fail();
+ } catch (IllegalStateException e) {}
+
+ if (index == null) {
+ try {
+ Object val = makeVal(1);
+ i.set(val);
+ fail();
+ } catch (IllegalStateException e) {}
+ }
+ }
+
+ void removeOdd()
+ throws Exception {
+
+ writeRunner.run(new TransactionWorker() {
+ public void doWork() {
+ boolean toggle = false;
+ for (int i = beginKey; i <= endKey; i += 2) {
+ toggle = !toggle;
+ Long key = makeKey(i);
+ Object val = makeVal(i);
+ if (toggle) {
+ assertTrue(map.keySet().contains(key));
+ assertTrue(map.keySet().remove(key));
+ assertTrue(!map.keySet().contains(key));
+ } else {
+ assertTrue(map.containsValue(val));
+ Object oldVal = map.remove(key);
+ assertEquals(oldVal, val);
+ assertTrue(!map.containsKey(key));
+ assertTrue(!map.containsValue(val));
+ }
+ assertNull(map.get(key));
+ assertTrue(!map.duplicates(key).contains(val));
+ checkDupsSize(0, map.duplicates(key));
+ }
+ }
+ });
+ }
+
+ void removeOddEntity()
+ throws Exception {
+
+ writeRunner.run(new TransactionWorker() {
+ public void doWork() {
+ for (int i = beginKey; i <= endKey; i += 2) {
+ Long key = makeKey(i);
+ Object val = makeVal(i);
+ assertTrue(map.values().contains(val));
+ assertTrue(map.values().remove(val));
+ assertTrue(!map.values().contains(val));
+ assertNull(map.get(key));
+ assertTrue(!map.duplicates(key).contains(val));
+ checkDupsSize(0, map.duplicates(key));
+ }
+ }
+ });
+ }
+
+ void removeOddEntry()
+ throws Exception {
+
+ writeRunner.run(new TransactionWorker() {
+ public void doWork() {
+ for (int i = beginKey; i <= endKey; i += 2) {
+ Long key = makeKey(i);
+ Object val = mapEntry(i);
+ assertTrue(map.entrySet().contains(val));
+ assertTrue(map.entrySet().remove(val));
+ assertTrue(!map.entrySet().contains(val));
+ assertNull(map.get(key));
+ }
+ }
+ });
+ }
+
+ void removeOddIter()
+ throws Exception {
+
+ writeIterRunner.run(new TransactionWorker() {
+ public void doWork() {
+ Iterator iter = iterator(map.keySet());
+ try {
+ for (int i = beginKey; i <= endKey; i += 1) {
+ assertTrue(iter.hasNext());
+ Long key = (Long) iter.next();
+ assertNotNull(key);
+ if (map instanceof SortedMap) {
+ assertEquals(makeKey(i), key);
+ }
+ if ((key.intValue() & 1) != 0) {
+ iter.remove();
+ }
+ }
+ } finally {
+ StoredIterator.close(iter);
+ }
+ }
+ });
+ }
+
+ void removeOddList()
+ throws Exception {
+
+ writeRunner.run(new TransactionWorker() {
+ public void doWork() {
+ for (int i = beginKey; i <= endKey; i += 2) {
+ // remove by index
+ // (with entity binding, embbeded keys in values are
+ // being changed so we can't use values for comparison)
+ int idx = (i - beginKey) / 2;
+ Object val = makeVal(i);
+ if (!isEntityBinding) {
+ assertTrue(list.contains(val));
+ assertEquals(val, list.get(idx));
+ assertEquals(idx, list.indexOf(val));
+ }
+ assertNotNull(list.get(idx));
+ if (isEntityBinding) {
+ assertNotNull(list.remove(idx));
+ } else {
+ assertTrue(list.contains(val));
+ assertEquals(val, list.remove(idx));
+ }
+ assertTrue(!list.remove(val));
+ assertTrue(!list.contains(val));
+ assertTrue(!val.equals(list.get(idx)));
+ }
+ }
+ });
+ }
+
+ void removeOddListValue()
+ throws Exception {
+
+ writeRunner.run(new TransactionWorker() {
+ public void doWork() {
+ for (int i = beginKey; i <= endKey; i += 2) {
+ // for non-entity case remove by value
+ // (with entity binding, embbeded keys in values are
+ // being changed so we can't use values for comparison)
+ int idx = (i - beginKey) / 2;
+ Object val = makeVal(i);
+ assertTrue(list.contains(val));
+ assertEquals(val, list.get(idx));
+ assertEquals(idx, list.indexOf(val));
+ assertTrue(list.remove(val));
+ assertTrue(!list.remove(val));
+ assertTrue(!list.contains(val));
+ assertTrue(!val.equals(list.get(idx)));
+ }
+ }
+ });
+ }
+
+ void addOdd()
+ throws Exception {
+
+ writeRunner.run(new TransactionWorker() {
+ public void doWork() {
+ // add using Map.put()
+ for (int i = beginKey; i <= endKey; i += 2) {
+ Long key = makeKey(i);
+ Object val = makeVal(i);
+ assertNull(imap.get(key));
+ assertNull(imap.put(key, val));
+ assertEquals(val, imap.get(key));
+ assertTrue(imap.duplicates(key).contains(val));
+ checkDupsSize(1, imap.duplicates(key));
+ if (isEntityBinding) {
+ assertTrue(!imap.values().add(val));
+ }
+ if (!imap.areDuplicatesAllowed()) {
+ assertEquals(val, imap.put(key, val));
+ }
+ }
+ }
+ });
+ }
+
+ void addOddEntity()
+ throws Exception {
+
+ writeRunner.run(new TransactionWorker() {
+ public void doWork() {
+ // add using Map.values().add()
+ for (int i = beginKey; i <= endKey; i += 2) {
+ Long key = makeKey(i);
+ Object val = makeVal(i);
+ assertNull(imap.get(key));
+ assertTrue(!imap.values().contains(val));
+ assertTrue(imap.values().add(val));
+ assertEquals(val, imap.get(key));
+ assertTrue(imap.values().contains(val));
+ assertTrue(imap.duplicates(key).contains(val));
+ checkDupsSize(1, imap.duplicates(key));
+ if (isEntityBinding) {
+ assertTrue(!imap.values().add(val));
+ }
+ }
+ }
+ });
+ }
+
+ void addOddDup()
+ throws Exception {
+
+ writeRunner.run(new TransactionWorker() {
+ public void doWork() {
+ // add using Map.duplicates().add()
+ for (int i = beginKey; i <= endKey; i += 2) {
+ Long key = makeKey(i);
+ Object val = makeVal(i);
+ assertNull(imap.get(key));
+ assertTrue(!imap.values().contains(val));
+ assertTrue(imap.duplicates(key).add(val));
+ assertEquals(val, imap.get(key));
+ assertTrue(imap.values().contains(val));
+ assertTrue(imap.duplicates(key).contains(val));
+ checkDupsSize(1, imap.duplicates(key));
+ assertTrue(!imap.duplicates(key).add(val));
+ if (isEntityBinding) {
+ assertTrue(!imap.values().add(val));
+ }
+ }
+ }
+ });
+ }
+
+ void addOddList()
+ throws Exception {
+
+ writeRunner.run(new TransactionWorker() {
+ public void doWork() {
+ for (int i = beginKey; i <= endKey; i += 2) {
+ int idx = i - beginKey;
+ Object val = makeVal(i);
+ assertTrue(!list.contains(val));
+ assertTrue(!val.equals(list.get(idx)));
+ list.add(idx, val);
+ assertTrue(list.contains(val));
+ assertEquals(val, list.get(idx));
+ }
+ }
+ });
+ }
+
+ void addAllList()
+ throws Exception {
+
+ writeRunner.run(new TransactionWorker() {
+ public void doWork() {
+ for (int i = beginKey; i <= endKey; i += 1) {
+ int idx = i - beginKey;
+ Object val = makeVal(i);
+ assertTrue(!list.contains(val));
+ assertTrue(list.add(val));
+ assertTrue(list.contains(val));
+ assertEquals(val, list.get(idx));
+ }
+ }
+ });
+ }
+
+ void removeAllList()
+ throws Exception {
+
+ writeRunner.run(new TransactionWorker() {
+ public void doWork() {
+ assertTrue(!list.isEmpty());
+ list.clear();
+ assertTrue(list.isEmpty());
+ for (int i = beginKey; i <= endKey; i += 1) {
+ int idx = i - beginKey;
+ assertNull(list.get(idx));
+ }
+ }
+ });
+ }
+
+ /**
+ * Tests ConcurentMap methods implemented by StordMap. Starts with an
+ * empty DB and ends with an empty DB. [#16218]
+ */
+ void testConcurrentMap()
+ throws Exception {
+
+ writeRunner.run(new TransactionWorker() {
+ public void doWork() {
+ for (int i = beginKey; i <= endKey; i += 1) {
+ Long key = makeKey(i);
+ Object val = makeVal(i);
+ Object valPlusOne = makeVal(i, i + 1);
+ assertFalse(imap.containsKey(key));
+
+ assertNull(imap.putIfAbsent(key, val));
+ assertEquals(val, imap.get(key));
+
+ assertEquals(val, imap.putIfAbsent(key, val));
+ assertEquals(val, imap.get(key));
+
+ if (!imap.areDuplicatesAllowed()) {
+ assertEquals(val, imap.replace(key, valPlusOne));
+ assertEquals(valPlusOne, imap.get(key));
+
+ assertEquals(valPlusOne, imap.replace(key, val));
+ assertEquals(val, imap.get(key));
+
+ assertFalse(imap.replace(key, valPlusOne, val));
+ assertEquals(val, imap.get(key));
+
+ assertTrue(imap.replace(key, val, valPlusOne));
+ assertEquals(valPlusOne, imap.get(key));
+
+ assertTrue(imap.replace(key, valPlusOne, val));
+ assertEquals(val, imap.get(key));
+ }
+
+ assertFalse(imap.remove(key, valPlusOne));
+ assertTrue(imap.containsKey(key));
+
+ assertTrue(imap.remove(key, val));
+ assertFalse(imap.containsKey(key));
+
+ assertNull(imap.replace(key, val));
+ assertFalse(imap.containsKey(key));
+ }
+ }
+ });
+ }
+
+ void testIterAddList()
+ throws Exception {
+
+ writeIterRunner.run(new TransactionWorker() {
+ public void doWork() {
+ ListIterator i = (ListIterator) iterator(list);
+ try {
+ assertTrue(!i.hasNext());
+ i.add(makeVal(3));
+ assertTrue(!i.hasNext());
+ assertTrue(i.hasPrevious());
+ assertEquals(3, intVal(i.previous()));
+
+ i.add(makeVal(1));
+ assertTrue(i.hasPrevious());
+ assertTrue(i.hasNext());
+ assertEquals(1, intVal(i.previous()));
+ assertTrue(i.hasNext());
+ assertEquals(1, intVal(i.next()));
+ assertTrue(i.hasNext());
+ assertEquals(3, intVal(i.next()));
+ assertEquals(3, intVal(i.previous()));
+
+ assertTrue(i.hasNext());
+ i.add(makeVal(2));
+ assertTrue(i.hasNext());
+ assertTrue(i.hasPrevious());
+ assertEquals(2, intVal(i.previous()));
+ assertTrue(i.hasNext());
+ assertEquals(2, intVal(i.next()));
+ assertTrue(i.hasNext());
+ assertEquals(3, intVal(i.next()));
+
+ assertTrue(!i.hasNext());
+ i.add(makeVal(4));
+ i.add(makeVal(5));
+ assertTrue(!i.hasNext());
+ assertEquals(5, intVal(i.previous()));
+ assertEquals(4, intVal(i.previous()));
+ assertEquals(3, intVal(i.previous()));
+ assertEquals(2, intVal(i.previous()));
+ assertEquals(1, intVal(i.previous()));
+ assertTrue(!i.hasPrevious());
+ } finally {
+ StoredIterator.close(i);
+ }
+ }
+ });
+ }
+
+ void testIterAddDuplicates()
+ throws Exception {
+
+ writeIterRunner.run(new TransactionWorker() {
+ public void doWork() {
+ assertNull(imap.put(makeKey(1), makeVal(1)));
+ ListIterator i =
+ (ListIterator) iterator(imap.duplicates(makeKey(1)));
+ try {
+ if (imap.areDuplicatesOrdered()) {
+ i.add(makeVal(1, 4));
+ i.add(makeVal(1, 2));
+ i.add(makeVal(1, 3));
+ while (i.hasPrevious()) i.previous();
+ assertEquals(1, intVal(i.next()));
+ assertEquals(2, intVal(i.next()));
+ assertEquals(3, intVal(i.next()));
+ assertEquals(4, intVal(i.next()));
+ assertTrue(!i.hasNext());
+ } else {
+ assertEquals(1, intVal(i.next()));
+ i.add(makeVal(1, 2));
+ i.add(makeVal(1, 3));
+ assertTrue(!i.hasNext());
+ assertTrue(i.hasPrevious());
+ assertEquals(3, intVal(i.previous()));
+ assertEquals(2, intVal(i.previous()));
+ assertEquals(1, intVal(i.previous()));
+ assertTrue(!i.hasPrevious());
+ i.add(makeVal(1, 4));
+ i.add(makeVal(1, 5));
+ assertTrue(i.hasNext());
+ assertEquals(5, intVal(i.previous()));
+ assertEquals(4, intVal(i.previous()));
+ assertTrue(!i.hasPrevious());
+ assertEquals(4, intVal(i.next()));
+ assertEquals(5, intVal(i.next()));
+ assertEquals(1, intVal(i.next()));
+ assertEquals(2, intVal(i.next()));
+ assertEquals(3, intVal(i.next()));
+ assertTrue(!i.hasNext());
+ }
+ } finally {
+ StoredIterator.close(i);
+ }
+ }
+ });
+ }
+
+ void readAll()
+ throws Exception {
+
+ readRunner.run(new TransactionWorker() {
+ public void doWork() {
+ // map
+
+ assertNotNull(map.toString());
+ for (int i = beginKey; i <= endKey; i += 1) {
+ Long key = makeKey(i);
+ Object val = map.get(key);
+ assertEquals(makeVal(i), val);
+ assertTrue(map.containsKey(key));
+ assertTrue(map.containsValue(val));
+ assertTrue(map.keySet().contains(key));
+ assertTrue(map.values().contains(val));
+ assertTrue(map.duplicates(key).contains(val));
+ checkDupsSize(1, map.duplicates(key));
+ }
+ assertNull(map.get(makeKey(-1)));
+ assertNull(map.get(makeKey(0)));
+ assertNull(map.get(makeKey(beginKey - 1)));
+ assertNull(map.get(makeKey(endKey + 1)));
+ checkDupsSize(0, map.duplicates(makeKey(-1)));
+ checkDupsSize(0, map.duplicates(makeKey(0)));
+ checkDupsSize(0, map.duplicates(makeKey(beginKey - 1)));
+ checkDupsSize(0, map.duplicates(makeKey(endKey + 1)));
+
+ // entrySet
+
+ Set set = map.entrySet();
+ assertNotNull(set.toString());
+ assertEquals(beginKey > endKey, set.isEmpty());
+ Iterator iter = iterator(set);
+ try {
+ for (int i = beginKey; i <= endKey; i += 1) {
+ assertTrue(iter.hasNext());
+ Map.Entry entry = (Map.Entry) iter.next();
+ Long key = (Long) entry.getKey();
+ Object val = entry.getValue();
+ if (map instanceof SortedMap) {
+ assertEquals(intKey(key), i);
+ }
+ assertEquals(intKey(key), intVal(val));
+ assertTrue(set.contains(entry));
+ }
+ assertTrue(!iter.hasNext());
+ } finally {
+ StoredIterator.close(iter);
+ }
+ Map.Entry[] entries =
+ (Map.Entry[]) set.toArray(new Map.Entry[0]);
+ assertNotNull(entries);
+ assertEquals(endKey - beginKey + 1, entries.length);
+ for (int i = beginKey; i <= endKey; i += 1) {
+ Map.Entry entry = entries[i - beginKey];
+ assertNotNull(entry);
+ if (map instanceof SortedMap) {
+ assertEquals(makeKey(i), entry.getKey());
+ assertEquals(makeVal(i), entry.getValue());
+ }
+ }
+ readIterator(set, iterator(set), beginKey, endKey);
+ if (smap != null) {
+ SortedSet sset = (SortedSet) set;
+ if (beginKey == 1 && endKey >= 1) {
+ readIterator(sset,
+ iterator(sset.subSet(mapEntry(1),
+ mapEntry(2))),
+ 1, 1);
+ }
+ if (beginKey <= 2 && endKey >= 2) {
+ readIterator(sset,
+ iterator(sset.subSet(mapEntry(2),
+ mapEntry(3))),
+ 2, 2);
+ }
+ if (beginKey <= endKey) {
+ readIterator(sset,
+ iterator(sset.subSet
+ (mapEntry(endKey),
+ mapEntry(endKey + 1))),
+ endKey, endKey);
+ }
+ if (isSubMap()) {
+ if (beginKey <= endKey) {
+ if (rangeType != TAIL) {
+ try {
+ sset.subSet(mapEntry(endKey + 1),
+ mapEntry(endKey + 2));
+ fail();
+ } catch (IllegalArgumentException e) {}
+ }
+ if (rangeType != HEAD) {
+ try {
+ sset.subSet(mapEntry(0),
+ mapEntry(1));
+ fail();
+ } catch (IllegalArgumentException e) {}
+ }
+ }
+ } else {
+ readIterator(sset,
+ iterator(sset.subSet
+ (mapEntry(endKey + 1),
+ mapEntry(endKey + 2))),
+ endKey, endKey - 1);
+ readIterator(sset,
+ iterator(sset.subSet(mapEntry(0),
+ mapEntry(1))),
+ 0, -1);
+ }
+ }
+
+ // keySet
+
+ set = map.keySet();
+ assertNotNull(set.toString());
+ assertEquals(beginKey > endKey, set.isEmpty());
+ iter = iterator(set);
+ try {
+ for (int i = beginKey; i <= endKey; i += 1) {
+ assertTrue(iter.hasNext());
+ Long key = (Long) iter.next();
+ assertTrue(set.contains(key));
+ Object val = map.get(key);
+ if (map instanceof SortedMap) {
+ assertEquals(key, makeKey(i));
+ }
+ assertEquals(intKey(key), intVal(val));
+ }
+ assertTrue("" + beginKey + ' ' + endKey, !iter.hasNext());
+ } finally {
+ StoredIterator.close(iter);
+ }
+ Long[] keys = (Long[]) set.toArray(new Long[0]);
+ assertNotNull(keys);
+ assertEquals(endKey - beginKey + 1, keys.length);
+ for (int i = beginKey; i <= endKey; i += 1) {
+ Long key = keys[i - beginKey];
+ assertNotNull(key);
+ if (map instanceof SortedMap) {
+ assertEquals(makeKey(i), key);
+ }
+ }
+ readIterator(set, iterator(set), beginKey, endKey);
+
+ // values
+
+ Collection coll = map.values();
+ assertNotNull(coll.toString());
+ assertEquals(beginKey > endKey, coll.isEmpty());
+ iter = iterator(coll);
+ try {
+ for (int i = beginKey; i <= endKey; i += 1) {
+ assertTrue(iter.hasNext());
+ Object val = iter.next();
+ if (map instanceof SortedMap) {
+ assertEquals(makeVal(i), val);
+ }
+ }
+ assertTrue(!iter.hasNext());
+ } finally {
+ StoredIterator.close(iter);
+ }
+ Object[] values = coll.toArray();
+ assertNotNull(values);
+ assertEquals(endKey - beginKey + 1, values.length);
+ for (int i = beginKey; i <= endKey; i += 1) {
+ Object val = values[i - beginKey];
+ assertNotNull(val);
+ if (map instanceof SortedMap) {
+ assertEquals(makeVal(i), val);
+ }
+ }
+ readIterator(coll, iterator(coll), beginKey, endKey);
+
+ // list
+
+ if (list != null) {
+ assertNotNull(list.toString());
+ assertEquals(beginKey > endKey, list.isEmpty());
+ for (int i = beginKey; i <= endKey; i += 1) {
+ int idx = i - beginKey;
+ Object val = list.get(idx);
+ assertEquals(makeVal(i), val);
+ assertTrue(list.contains(val));
+ assertEquals(idx, list.indexOf(val));
+ assertEquals(idx, list.lastIndexOf(val));
+ }
+ ListIterator li = (ListIterator) iterator(list);
+ try {
+ for (int i = beginKey; i <= endKey; i += 1) {
+ int idx = i - beginKey;
+ assertTrue(li.hasNext());
+ assertEquals(idx, li.nextIndex());
+ Object val = li.next();
+ assertEquals(makeVal(i), val);
+ assertEquals(idx, li.previousIndex());
+ }
+ assertTrue(!li.hasNext());
+ } finally {
+ StoredIterator.close(li);
+ }
+ if (beginKey < endKey) {
+ li = list.listIterator(1);
+ try {
+ for (int i = beginKey + 1; i <= endKey; i += 1) {
+ int idx = i - beginKey;
+ assertTrue(li.hasNext());
+ assertEquals(idx, li.nextIndex());
+ Object val = li.next();
+ assertEquals(makeVal(i), val);
+ assertEquals(idx, li.previousIndex());
+ }
+ assertTrue(!li.hasNext());
+ } finally {
+ StoredIterator.close(li);
+ }
+ }
+ values = list.toArray();
+ assertNotNull(values);
+ assertEquals(endKey - beginKey + 1, values.length);
+ for (int i = beginKey; i <= endKey; i += 1) {
+ Object val = values[i - beginKey];
+ assertNotNull(val);
+ assertEquals(makeVal(i), val);
+ }
+ readIterator(list, iterator(list), beginKey, endKey);
+ }
+
+ // first/last
+
+ if (smap != null) {
+ if (beginKey <= endKey &&
+ beginKey >= 1 && beginKey <= maxKey) {
+ assertEquals(makeKey(beginKey),
+ smap.firstKey());
+ assertEquals(makeKey(beginKey),
+ ((SortedSet) smap.keySet()).first());
+ Object entry = ((SortedSet) smap.entrySet()).first();
+ assertEquals(makeKey(beginKey),
+ ((Map.Entry) entry).getKey());
+ if (smap.values() instanceof SortedSet) {
+ assertEquals(makeVal(beginKey),
+ ((SortedSet) smap.values()).first());
+ }
+ } else {
+ assertNull(smap.firstKey());
+ assertNull(((SortedSet) smap.keySet()).first());
+ assertNull(((SortedSet) smap.entrySet()).first());
+ if (smap.values() instanceof SortedSet) {
+ assertNull(((SortedSet) smap.values()).first());
+ }
+ }
+ if (beginKey <= endKey &&
+ endKey >= 1 && endKey <= maxKey) {
+ assertEquals(makeKey(endKey),
+ smap.lastKey());
+ assertEquals(makeKey(endKey),
+ ((SortedSet) smap.keySet()).last());
+ Object entry = ((SortedSet) smap.entrySet()).last();
+ assertEquals(makeKey(endKey),
+ ((Map.Entry) entry).getKey());
+ if (smap.values() instanceof SortedSet) {
+ assertEquals(makeVal(endKey),
+ ((SortedSet) smap.values()).last());
+ }
+ } else {
+ assertNull(smap.lastKey());
+ assertNull(((SortedSet) smap.keySet()).last());
+ assertNull(((SortedSet) smap.entrySet()).last());
+ if (smap.values() instanceof SortedSet) {
+ assertNull(((SortedSet) smap.values()).last());
+ }
+ }
+ }
+ }
+ });
+ }
+
+ void readEven()
+ throws Exception {
+
+ readRunner.run(new TransactionWorker() {
+ public void doWork() {
+ int readBegin = ((beginKey & 1) != 0) ?
+ (beginKey + 1) : beginKey;
+ int readEnd = ((endKey & 1) != 0) ? (endKey - 1) : endKey;
+ int readIncr = 2;
+
+ // map
+
+ for (int i = beginKey; i <= endKey; i += 1) {
+ Long key = makeKey(i);
+ if ((i & 1) == 0) {
+ Object val = map.get(key);
+ assertEquals(makeVal(i), val);
+ assertTrue(map.containsKey(key));
+ assertTrue(map.containsValue(val));
+ assertTrue(map.keySet().contains(key));
+ assertTrue(map.values().contains(val));
+ assertTrue(map.duplicates(key).contains(val));
+ checkDupsSize(1, map.duplicates(key));
+ } else {
+ Object val = makeVal(i);
+ assertTrue(!map.containsKey(key));
+ assertTrue(!map.containsValue(val));
+ assertTrue(!map.keySet().contains(key));
+ assertTrue(!map.values().contains(val));
+ assertTrue(!map.duplicates(key).contains(val));
+ checkDupsSize(0, map.duplicates(key));
+ }
+ }
+
+ // entrySet
+
+ Set set = map.entrySet();
+ assertEquals(beginKey > endKey, set.isEmpty());
+ Iterator iter = iterator(set);
+ try {
+ for (int i = readBegin; i <= readEnd; i += readIncr) {
+ assertTrue(iter.hasNext());
+ Map.Entry entry = (Map.Entry) iter.next();
+ Long key = (Long) entry.getKey();
+ Object val = entry.getValue();
+ if (map instanceof SortedMap) {
+ assertEquals(intKey(key), i);
+ }
+ assertEquals(intKey(key), intVal(val));
+ assertTrue(set.contains(entry));
+ }
+ assertTrue(!iter.hasNext());
+ } finally {
+ StoredIterator.close(iter);
+ }
+
+ // keySet
+
+ set = map.keySet();
+ assertEquals(beginKey > endKey, set.isEmpty());
+ iter = iterator(set);
+ try {
+ for (int i = readBegin; i <= readEnd; i += readIncr) {
+ assertTrue(iter.hasNext());
+ Long key = (Long) iter.next();
+ assertTrue(set.contains(key));
+ Object val = map.get(key);
+ if (map instanceof SortedMap) {
+ assertEquals(key, makeKey(i));
+ }
+ assertEquals(intKey(key), intVal(val));
+ }
+ assertTrue(!iter.hasNext());
+ } finally {
+ StoredIterator.close(iter);
+ }
+
+ // values
+
+ Collection coll = map.values();
+ assertEquals(beginKey > endKey, coll.isEmpty());
+ iter = iterator(coll);
+ try {
+ for (int i = readBegin; i <= readEnd; i += readIncr) {
+ assertTrue(iter.hasNext());
+ Object val = iter.next();
+ if (map instanceof SortedMap) {
+ assertEquals(makeVal(i), val);
+ }
+ }
+ assertTrue(!iter.hasNext());
+ } finally {
+ StoredIterator.close(iter);
+ }
+
+ // list not used since keys may not be renumbered for this
+ // method to work in general
+
+ // first/last
+
+ if (smap != null) {
+ if (readBegin <= readEnd &&
+ readBegin >= 1 && readBegin <= maxKey) {
+ assertEquals(makeKey(readBegin),
+ smap.firstKey());
+ assertEquals(makeKey(readBegin),
+ ((SortedSet) smap.keySet()).first());
+ Object entry = ((SortedSet) smap.entrySet()).first();
+ assertEquals(makeKey(readBegin),
+ ((Map.Entry) entry).getKey());
+ if (smap.values() instanceof SortedSet) {
+ assertEquals(makeVal(readBegin),
+ ((SortedSet) smap.values()).first());
+ }
+ } else {
+ assertNull(smap.firstKey());
+ assertNull(((SortedSet) smap.keySet()).first());
+ assertNull(((SortedSet) smap.entrySet()).first());
+ if (smap.values() instanceof SortedSet) {
+ assertNull(((SortedSet) smap.values()).first());
+ }
+ }
+ if (readBegin <= readEnd &&
+ readEnd >= 1 && readEnd <= maxKey) {
+ assertEquals(makeKey(readEnd),
+ smap.lastKey());
+ assertEquals(makeKey(readEnd),
+ ((SortedSet) smap.keySet()).last());
+ Object entry = ((SortedSet) smap.entrySet()).last();
+ assertEquals(makeKey(readEnd),
+ ((Map.Entry) entry).getKey());
+ if (smap.values() instanceof SortedSet) {
+ assertEquals(makeVal(readEnd),
+ ((SortedSet) smap.values()).last());
+ }
+ } else {
+ assertNull(smap.lastKey());
+ assertNull(((SortedSet) smap.keySet()).last());
+ assertNull(((SortedSet) smap.entrySet()).last());
+ if (smap.values() instanceof SortedSet) {
+ assertNull(((SortedSet) smap.values()).last());
+ }
+ }
+ }
+ }
+ });
+ }
+
+ void readEvenList()
+ throws Exception {
+
+ readRunner.run(new TransactionWorker() {
+ public void doWork() {
+ int readBegin = ((beginKey & 1) != 0) ?
+ (beginKey + 1) : beginKey;
+ int readEnd = ((endKey & 1) != 0) ? (endKey - 1) : endKey;
+ int readIncr = 2;
+
+ assertEquals(beginKey > endKey, list.isEmpty());
+ ListIterator iter = (ListIterator) iterator(list);
+ try {
+ int idx = 0;
+ for (int i = readBegin; i <= readEnd; i += readIncr) {
+ assertTrue(iter.hasNext());
+ assertEquals(idx, iter.nextIndex());
+ Object val = iter.next();
+ assertEquals(idx, iter.previousIndex());
+ if (isEntityBinding) {
+ assertEquals(i, intVal(val));
+ } else {
+ assertEquals(makeVal(i), val);
+ }
+ idx += 1;
+ }
+ assertTrue(!iter.hasNext());
+ } finally {
+ StoredIterator.close(iter);
+ }
+ }
+ });
+ }
+
+ void readIterator(Collection coll, Iterator iter,
+ int beginValue, int endValue) {
+
+ ListIterator li = (ListIterator) iter;
+ boolean isList = (coll instanceof List);
+ Iterator clone = null;
+ try {
+ // at beginning
+ assertTrue(!li.hasPrevious());
+ assertTrue(!li.hasPrevious());
+ try { li.previous(); } catch (NoSuchElementException e) {}
+ if (isList) {
+ assertEquals(-1, li.previousIndex());
+ }
+ if (endValue < beginValue) {
+ // is empty
+ assertTrue(!iter.hasNext());
+ try { iter.next(); } catch (NoSuchElementException e) {}
+ if (isList) {
+ assertEquals(Integer.MAX_VALUE, li.nextIndex());
+ }
+ }
+ // loop thru all and collect in array
+ int[] values = new int[endValue - beginValue + 1];
+ for (int i = beginValue; i <= endValue; i += 1) {
+ assertTrue(iter.hasNext());
+ int idx = i - beginKey;
+ if (isList) {
+ assertEquals(idx, li.nextIndex());
+ }
+ int value = intIter(coll, iter.next());
+ if (isList) {
+ assertEquals(idx, li.previousIndex());
+ }
+ values[i - beginValue] = value;
+ if (((StoredCollection) coll).isOrdered()) {
+ assertEquals(i, value);
+ } else {
+ assertTrue(value >= beginValue);
+ assertTrue(value <= endValue);
+ }
+ }
+ // at end
+ assertTrue(!iter.hasNext());
+ try { iter.next(); } catch (NoSuchElementException e) {}
+ if (isList) {
+ assertEquals(Integer.MAX_VALUE, li.nextIndex());
+ }
+ // clone at same position
+ clone = StoredCollections.iterator(iter);
+ assertTrue(!clone.hasNext());
+ // loop thru in reverse
+ for (int i = endValue; i >= beginValue; i -= 1) {
+ assertTrue(li.hasPrevious());
+ int idx = i - beginKey;
+ if (isList) {
+ assertEquals(idx, li.previousIndex());
+ }
+ int value = intIter(coll, li.previous());
+ if (isList) {
+ assertEquals(idx, li.nextIndex());
+ }
+ assertEquals(values[i - beginValue], value);
+ }
+ // clone should not have changed
+ assertTrue(!clone.hasNext());
+ // at beginning
+ assertTrue(!li.hasPrevious());
+ try { li.previous(); } catch (NoSuchElementException e) {}
+ if (isList) {
+ assertEquals(-1, li.previousIndex());
+ }
+ // loop thru with some back-and-forth
+ for (int i = beginValue; i <= endValue; i += 1) {
+ assertTrue(iter.hasNext());
+ int idx = i - beginKey;
+ if (isList) {
+ assertEquals(idx, li.nextIndex());
+ }
+ Object obj = iter.next();
+ if (isList) {
+ assertEquals(idx, li.previousIndex());
+ }
+ assertEquals(obj, li.previous());
+ if (isList) {
+ assertEquals(idx, li.nextIndex());
+ }
+ assertEquals(obj, iter.next());
+ if (isList) {
+ assertEquals(idx, li.previousIndex());
+ }
+ int value = intIter(coll, obj);
+ assertEquals(values[i - beginValue], value);
+ }
+ // at end
+ assertTrue(!iter.hasNext());
+ try { iter.next(); } catch (NoSuchElementException e) {}
+ if (isList) {
+ assertEquals(Integer.MAX_VALUE, li.nextIndex());
+ }
+ } finally {
+ StoredIterator.close(iter);
+ StoredIterator.close(clone);
+ }
+ }
+
+ void bulkOperations()
+ throws Exception {
+
+ writeRunner.run(new TransactionWorker() {
+ public void doWork() {
+ HashMap hmap = new HashMap();
+ for (int i = Math.max(1, beginKey);
+ i <= Math.min(maxKey, endKey);
+ i += 1) {
+ hmap.put(makeKey(i), makeVal(i));
+ }
+ assertEquals(hmap, map);
+ assertEquals(hmap.entrySet(), map.entrySet());
+ assertEquals(hmap.keySet(), map.keySet());
+ assertEquals(map.values(), hmap.values());
+
+ assertTrue(map.entrySet().containsAll(hmap.entrySet()));
+ assertTrue(map.keySet().containsAll(hmap.keySet()));
+ assertTrue(map.values().containsAll(hmap.values()));
+
+ map.clear();
+ assertTrue(map.isEmpty());
+ imap.putAll(hmap);
+ assertEquals(hmap, map);
+
+ assertTrue(map.entrySet().removeAll(hmap.entrySet()));
+ assertTrue(map.entrySet().isEmpty());
+ assertTrue(!map.entrySet().removeAll(hmap.entrySet()));
+ assertTrue(imap.entrySet().addAll(hmap.entrySet()));
+ assertTrue(map.entrySet().containsAll(hmap.entrySet()));
+ assertTrue(!imap.entrySet().addAll(hmap.entrySet()));
+ assertEquals(hmap, map);
+
+ assertTrue(!map.entrySet().retainAll(hmap.entrySet()));
+ assertEquals(hmap, map);
+ assertTrue(map.entrySet().retainAll(Collections.EMPTY_SET));
+ assertTrue(map.isEmpty());
+ imap.putAll(hmap);
+ assertEquals(hmap, map);
+
+ assertTrue(map.values().removeAll(hmap.values()));
+ assertTrue(map.values().isEmpty());
+ assertTrue(!map.values().removeAll(hmap.values()));
+ if (isEntityBinding) {
+ assertTrue(imap.values().addAll(hmap.values()));
+ assertTrue(map.values().containsAll(hmap.values()));
+ assertTrue(!imap.values().addAll(hmap.values()));
+ } else {
+ imap.putAll(hmap);
+ }
+ assertEquals(hmap, map);
+
+ assertTrue(!map.values().retainAll(hmap.values()));
+ assertEquals(hmap, map);
+ assertTrue(map.values().retainAll(Collections.EMPTY_SET));
+ assertTrue(map.isEmpty());
+ imap.putAll(hmap);
+ assertEquals(hmap, map);
+
+ assertTrue(map.keySet().removeAll(hmap.keySet()));
+ assertTrue(map.keySet().isEmpty());
+ assertTrue(!map.keySet().removeAll(hmap.keySet()));
+ assertTrue(imap.keySet().addAll(hmap.keySet()));
+ assertTrue(imap.keySet().containsAll(hmap.keySet()));
+ if (index != null) {
+ assertTrue(map.keySet().isEmpty());
+ }
+ assertTrue(!imap.keySet().addAll(hmap.keySet()));
+ // restore values to non-null
+ imap.keySet().removeAll(hmap.keySet());
+ imap.putAll(hmap);
+ assertEquals(hmap, map);
+
+ assertTrue(!map.keySet().retainAll(hmap.keySet()));
+ assertEquals(hmap, map);
+ assertTrue(map.keySet().retainAll(Collections.EMPTY_SET));
+ assertTrue(map.isEmpty());
+ imap.putAll(hmap);
+ assertEquals(hmap, map);
+ }
+ });
+ }
+
+ void bulkListOperations()
+ throws Exception {
+
+ writeRunner.run(new TransactionWorker() {
+ public void doWork() {
+ ArrayList alist = new ArrayList();
+ for (int i = beginKey; i <= endKey; i += 1) {
+ alist.add(makeVal(i));
+ }
+
+ assertEquals(alist, list);
+ assertTrue(list.containsAll(alist));
+
+ if (isListAddAllowed()) {
+ list.clear();
+ assertTrue(list.isEmpty());
+ assertTrue(ilist.addAll(alist));
+ assertEquals(alist, list);
+ }
+
+ assertTrue(!list.retainAll(alist));
+ assertEquals(alist, list);
+
+ if (isListAddAllowed()) {
+ assertTrue(list.retainAll(Collections.EMPTY_SET));
+ assertTrue(list.isEmpty());
+ assertTrue(ilist.addAll(alist));
+ assertEquals(alist, list);
+ }
+
+ if (isListAddAllowed() && !isEntityBinding) {
+ // deleting in a renumbered list with entity binding will
+ // change the values dynamically, making it very difficult
+ // to test
+ assertTrue(list.removeAll(alist));
+ assertTrue(list.isEmpty());
+ assertTrue(!list.removeAll(alist));
+ assertTrue(ilist.addAll(alist));
+ assertTrue(list.containsAll(alist));
+ assertEquals(alist, list);
+ }
+
+ if (isListAddAllowed() && !isEntityBinding) {
+ // addAll at an index is also very difficult to test with
+ // an entity binding
+
+ // addAll at first index
+ ilist.addAll(beginKey, alist);
+ assertTrue(list.containsAll(alist));
+ assertEquals(2 * alist.size(), countElements(list));
+ for (int i = beginKey; i <= endKey; i += 1)
+ ilist.remove(beginKey);
+ assertEquals(alist, list);
+
+ // addAll at last index
+ ilist.addAll(endKey, alist);
+ assertTrue(list.containsAll(alist));
+ assertEquals(2 * alist.size(), countElements(list));
+ for (int i = beginKey; i <= endKey; i += 1)
+ ilist.remove(endKey);
+ assertEquals(alist, list);
+
+ // addAll in the middle
+ ilist.addAll(endKey - 1, alist);
+ assertTrue(list.containsAll(alist));
+ assertEquals(2 * alist.size(), countElements(list));
+ for (int i = beginKey; i <= endKey; i += 1)
+ ilist.remove(endKey - 1);
+ assertEquals(alist, list);
+ }
+ }
+ });
+ }
+
+ void readWriteRange(final int type, final int rangeBegin,
+ final int rangeEnd)
+ throws Exception {
+
+ writeRunner.run(new TransactionWorker() {
+ public void doWork() throws Exception {
+ setRange(type, rangeBegin, rangeEnd);
+ createOutOfRange(rangeBegin, rangeEnd);
+ if (rangeType != TAIL) {
+ writeOutOfRange(new Long(rangeEnd + 1));
+ }
+ if (rangeType != HEAD) {
+ writeOutOfRange(new Long(rangeBegin - 1));
+ }
+ if (rangeBegin <= rangeEnd) {
+ updateAll();
+ }
+ if (rangeBegin < rangeEnd && !map.areKeysRenumbered()) {
+ bulkOperations();
+ removeIter();
+ }
+ readAll();
+ clearRange();
+ }
+ });
+ }
+
+ void setRange(int type, int rangeBegin, int rangeEnd) {
+
+ rangeType = type;
+ saveMap = map;
+ saveSMap = smap;
+ saveList = list;
+ int listBegin = rangeBegin - beginKey;
+ boolean canMakeSubList = (list != null && listBegin>= 0);
+ if (!canMakeSubList) {
+ list = null;
+ }
+ if (list != null) {
+ try {
+ list.subList(-1, 0);
+ fail();
+ } catch (IndexOutOfBoundsException e) { }
+ }
+ switch (type) {
+
+ case SUB:
+ smap = (StoredSortedMap) smap.subMap(makeKey(rangeBegin),
+ makeKey(rangeEnd + 1));
+ if (canMakeSubList) {
+ list = (StoredList) list.subList(listBegin,
+ rangeEnd + 1 - beginKey);
+ }
+ // check for equivalent ranges
+ assertEquals(smap,
+ (saveSMap).subMap(
+ makeKey(rangeBegin), true,
+ makeKey(rangeEnd + 1), false));
+ assertEquals(smap.entrySet(),
+ ((StoredSortedEntrySet) saveSMap.entrySet()).subSet(
+ mapEntry(rangeBegin), true,
+ mapEntry(rangeEnd + 1), false));
+ assertEquals(smap.keySet(),
+ ((StoredSortedKeySet) saveSMap.keySet()).subSet(
+ makeKey(rangeBegin), true,
+ makeKey(rangeEnd + 1), false));
+ if (smap.values() instanceof SortedSet) {
+ assertEquals(smap.values(),
+ ((StoredSortedValueSet) saveSMap.values()).subSet(
+ makeVal(rangeBegin), true,
+ makeVal(rangeEnd + 1), false));
+ }
+ break;
+ case HEAD:
+ smap = (StoredSortedMap) smap.headMap(makeKey(rangeEnd + 1));
+ if (canMakeSubList) {
+ list = (StoredList) list.subList(0,
+ rangeEnd + 1 - beginKey);
+ }
+ // check for equivalent ranges
+ assertEquals(smap,
+ (saveSMap).headMap(
+ makeKey(rangeEnd + 1), false));
+ assertEquals(smap.entrySet(),
+ ((StoredSortedEntrySet) saveSMap.entrySet()).headSet(
+ mapEntry(rangeEnd + 1), false));
+ assertEquals(smap.keySet(),
+ ((StoredSortedKeySet) saveSMap.keySet()).headSet(
+ makeKey(rangeEnd + 1), false));
+ if (smap.values() instanceof SortedSet) {
+ assertEquals(smap.values(),
+ ((StoredSortedValueSet) saveSMap.values()).headSet(
+ makeVal(rangeEnd + 1), false));
+ }
+ break;
+ case TAIL:
+ smap = (StoredSortedMap) smap.tailMap(makeKey(rangeBegin));
+ if (canMakeSubList) {
+ list = (StoredList) list.subList(listBegin,
+ maxKey + 1 - beginKey);
+ }
+ // check for equivalent ranges
+ assertEquals(smap,
+ (saveSMap).tailMap(
+ makeKey(rangeBegin), true));
+ assertEquals(smap.entrySet(),
+ ((StoredSortedEntrySet) saveSMap.entrySet()).tailSet(
+ mapEntry(rangeBegin), true));
+ assertEquals(smap.keySet(),
+ ((StoredSortedKeySet) saveSMap.keySet()).tailSet(
+ makeKey(rangeBegin), true));
+ if (smap.values() instanceof SortedSet) {
+ assertEquals(smap.values(),
+ ((StoredSortedValueSet) saveSMap.values()).tailSet(
+ makeVal(rangeBegin), true));
+ }
+ break;
+ default: throw new RuntimeException();
+ }
+ map = smap;
+ beginKey = rangeBegin;
+ if (rangeBegin < 1 || rangeEnd > maxKey) {
+ endKey = rangeBegin - 1; // force empty range for readAll()
+ } else {
+ endKey = rangeEnd;
+ }
+ }
+
+ void clearRange() {
+
+ rangeType = NONE;
+ beginKey = 1;
+ endKey = maxKey;
+ map = saveMap;
+ smap = saveSMap;
+ list = saveList;
+ }
+
+ void createOutOfRange(int rangeBegin, int rangeEnd) {
+ // map
+
+ if (rangeType != TAIL) {
+ try {
+ smap.subMap(makeKey(rangeBegin), makeKey(rangeEnd + 2));
+ fail();
+ } catch (IllegalArgumentException e) { }
+ try {
+ smap.headMap(makeKey(rangeEnd + 2));
+ fail();
+ } catch (IllegalArgumentException e) { }
+ checkDupsSize(0, smap.duplicates(makeKey(rangeEnd + 2)));
+ }
+ if (rangeType != HEAD) {
+ try {
+ smap.subMap(makeKey(rangeBegin - 1), makeKey(rangeEnd + 1));
+ fail();
+ } catch (IllegalArgumentException e) { }
+ try {
+ smap.tailMap(makeKey(rangeBegin - 1));
+ fail();
+ } catch (IllegalArgumentException e) { }
+ checkDupsSize(0, smap.duplicates(makeKey(rangeBegin - 1)));
+ }
+
+ // keySet
+
+ if (rangeType != TAIL) {
+ SortedSet sset = (SortedSet) map.keySet();
+ try {
+ sset.subSet(makeKey(rangeBegin), makeKey(rangeEnd + 2));
+ fail();
+ } catch (IllegalArgumentException e) { }
+ try {
+ sset.headSet(makeKey(rangeEnd + 2));
+ fail();
+ } catch (IllegalArgumentException e) { }
+ try {
+ iterator(sset.subSet(makeKey(rangeEnd + 1),
+ makeKey(rangeEnd + 2)));
+ fail();
+ } catch (IllegalArgumentException e) { }
+ }
+ if (rangeType != HEAD) {
+ SortedSet sset = (SortedSet) map.keySet();
+ try {
+ sset.subSet(makeKey(rangeBegin - 1), makeKey(rangeEnd + 1));
+ fail();
+ } catch (IllegalArgumentException e) { }
+ try {
+ sset.tailSet(makeKey(rangeBegin - 1));
+ fail();
+ } catch (IllegalArgumentException e) { }
+ try {
+ iterator(sset.subSet(makeKey(rangeBegin - 1),
+ makeKey(rangeBegin)));
+ fail();
+ } catch (IllegalArgumentException e) { }
+ }
+
+ // entrySet
+
+ if (rangeType != TAIL) {
+ SortedSet sset = (SortedSet) map.entrySet();
+ try {
+ sset.subSet(mapEntry(rangeBegin), mapEntry(rangeEnd + 2));
+ fail();
+ } catch (IllegalArgumentException e) { }
+ try {
+ sset.headSet(mapEntry(rangeEnd + 2));
+ fail();
+ } catch (IllegalArgumentException e) { }
+ try {
+ iterator(sset.subSet(mapEntry(rangeEnd + 1),
+ mapEntry(rangeEnd + 2)));
+ fail();
+ } catch (IllegalArgumentException e) { }
+ }
+ if (rangeType != HEAD) {
+ SortedSet sset = (SortedSet) map.entrySet();
+ try {
+ sset.subSet(mapEntry(rangeBegin - 1), mapEntry(rangeEnd + 1));
+ fail();
+ } catch (IllegalArgumentException e) { }
+ try {
+ sset.tailSet(mapEntry(rangeBegin - 1));
+ fail();
+ } catch (IllegalArgumentException e) { }
+ try {
+ iterator(sset.subSet(mapEntry(rangeBegin - 1),
+ mapEntry(rangeBegin)));
+ fail();
+ } catch (IllegalArgumentException e) { }
+ }
+
+ // values
+
+ if (map.values() instanceof SortedSet) {
+ SortedSet sset = (SortedSet) map.values();
+ if (rangeType != TAIL) {
+ try {
+ sset.subSet(makeVal(rangeBegin),
+ makeVal(rangeEnd + 2));
+ fail();
+ } catch (IllegalArgumentException e) { }
+ try {
+ sset.headSet(makeVal(rangeEnd + 2));
+ fail();
+ } catch (IllegalArgumentException e) { }
+ }
+ if (rangeType != HEAD) {
+ try {
+ sset.subSet(makeVal(rangeBegin - 1),
+ makeVal(rangeEnd + 1));
+ fail();
+ } catch (IllegalArgumentException e) { }
+ try {
+ sset.tailSet(makeVal(rangeBegin - 1));
+ fail();
+ } catch (IllegalArgumentException e) { }
+ }
+ }
+
+ // list
+
+ if (list != null) {
+ int size = rangeEnd - rangeBegin + 1;
+ try {
+ list.subList(0, size + 1);
+ fail();
+ } catch (IndexOutOfBoundsException e) { }
+ try {
+ list.subList(-1, size);
+ fail();
+ } catch (IndexOutOfBoundsException e) { }
+ try {
+ list.subList(2, 1);
+ fail();
+ } catch (IndexOutOfBoundsException e) { }
+ try {
+ list.subList(size, size);
+ fail();
+ } catch (IndexOutOfBoundsException e) { }
+ }
+ }
+
+ void writeOutOfRange(Long badNewKey) {
+ try {
+ map.put(badNewKey, makeVal(badNewKey));
+ fail();
+ } catch (IllegalArgumentException e) {
+ assertTrue(e.toString(), index == null);
+ } catch (UnsupportedOperationException e) {
+ assertTrue(index != null);
+ }
+ try {
+ map.keySet().add(badNewKey);
+ fail();
+ } catch (IllegalArgumentException e) {
+ assertTrue(index == null);
+ } catch (UnsupportedOperationException e) {
+ assertTrue(index != null);
+ }
+ try {
+ map.values().add(makeEntity(badNewKey));
+ fail();
+ } catch (IllegalArgumentException e) {
+ assertTrue(isEntityBinding && index == null);
+ } catch (UnsupportedOperationException e) {
+ assertTrue(!(isEntityBinding && index == null));
+ }
+ if (list != null) {
+ int i = badNewKey.intValue() - beginKey;
+ try {
+ list.set(i, makeVal(i));
+ fail();
+ } catch (IndexOutOfBoundsException e) {
+ assertTrue(index == null);
+ } catch (UnsupportedOperationException e) {
+ assertTrue(index != null);
+ }
+ try {
+ list.add(i, makeVal(badNewKey));
+ fail();
+ } catch (UnsupportedOperationException e) {
+ }
+ }
+ }
+
+ void readWriteDuplicates()
+ throws Exception {
+
+ writeRunner.run(new TransactionWorker() {
+ public void doWork() throws Exception {
+ if (index == null) {
+ readWritePrimaryDuplicates(beginKey);
+ readWritePrimaryDuplicates(beginKey + 1);
+ readWritePrimaryDuplicates(endKey);
+ readWritePrimaryDuplicates(endKey - 1);
+ } else {
+ readWriteIndexedDuplicates(beginKey);
+ readWriteIndexedDuplicates(beginKey + 1);
+ readWriteIndexedDuplicates(endKey);
+ readWriteIndexedDuplicates(endKey - 1);
+ }
+ }
+ });
+ }
+
+ void readWritePrimaryDuplicates(int i)
+ throws Exception {
+
+ Collection dups;
+ // make duplicate values
+ final Long key = makeKey(i);
+ final Object[] values = new Object[5];
+ for (int j = 0; j < values.length; j += 1) {
+ values[j] = isEntityBinding
+ ? makeEntity(i, i + j)
+ : makeVal(i + j);
+ }
+ // add duplicates
+ outerLoop: for (int writeMode = 0;; writeMode += 1) {
+ //System.out.println("write mode " + writeMode);
+ switch (writeMode) {
+ case 0:
+ case 1: {
+ // write with Map.put()
+ for (int j = 1; j < values.length; j += 1) {
+ map.put(key, values[j]);
+ }
+ break;
+ }
+ case 2: {
+ // write with Map.duplicates().add()
+ dups = map.duplicates(key);
+ for (int j = 1; j < values.length; j += 1) {
+ dups.add(values[j]);
+ }
+ break;
+ }
+ case 3: {
+ // write with Map.duplicates().iterator().add()
+ writeIterRunner.run(new TransactionWorker() {
+ public void doWork() {
+ Collection dups = map.duplicates(key);
+ Iterator iter = iterator(dups);
+ assertEquals(values[0], iter.next());
+ assertTrue(!iter.hasNext());
+ try {
+ for (int j = 1; j < values.length; j += 1) {
+ ((ListIterator) iter).add(values[j]);
+ }
+ } finally {
+ StoredIterator.close(iter);
+ }
+ }
+ });
+ break;
+ }
+ case 4: {
+ // write with Map.values().add()
+ if (!isEntityBinding) {
+ continue;
+ }
+ Collection set = map.values();
+ for (int j = 1; j < values.length; j += 1) {
+ set.add(values[j]);
+ }
+ break;
+ }
+ default: {
+ break outerLoop;
+ }
+ }
+ checkDupsSize(values.length, map.duplicates(key));
+ // read duplicates
+ readDuplicates(i, key, values);
+ // remove duplicates
+ switch (writeMode) {
+ case 0: {
+ // remove with Map.remove()
+ checkDupsSize(values.length, map.duplicates(key));
+ map.remove(key); // remove all values
+ checkDupsSize(0, map.duplicates(key));
+ map.put(key, values[0]); // put back original value
+ checkDupsSize(1, map.duplicates(key));
+ break;
+ }
+ case 1: {
+ // remove with Map.keySet().remove()
+ map.keySet().remove(key); // remove all values
+ map.put(key, values[0]); // put back original value
+ break;
+ }
+ case 2: {
+ // remove with Map.duplicates().clear()
+ dups = map.duplicates(key);
+ dups.clear(); // remove all values
+ dups.add(values[0]); // put back original value
+ break;
+ }
+ case 3: {
+ // remove with Map.duplicates().iterator().remove()
+ writeIterRunner.run(new TransactionWorker() {
+ public void doWork() {
+ Collection dups = map.duplicates(key);
+ Iterator iter = iterator(dups);
+ try {
+ for (int j = 0; j < values.length; j += 1) {
+ assertEquals(values[j], iter.next());
+ if (j != 0) {
+ iter.remove();
+ }
+ }
+ } finally {
+ StoredIterator.close(iter);
+ }
+ }
+ });
+ break;
+ }
+ case 4: {
+ // remove with Map.values().remove()
+ if (!isEntityBinding) {
+ throw new IllegalStateException();
+ }
+ Collection set = map.values();
+ for (int j = 1; j < values.length; j += 1) {
+ set.remove(values[j]);
+ }
+ break;
+ }
+ default: throw new IllegalStateException();
+ }
+ // verify that only original value is present
+ dups = map.duplicates(key);
+ assertTrue(dups.contains(values[0]));
+ for (int j = 1; j < values.length; j += 1) {
+ assertTrue(!dups.contains(values[j]));
+ }
+ checkDupsSize(1, dups);
+ }
+ }
+
+ void readWriteIndexedDuplicates(int i) {
+ Object key = makeKey(i);
+ Object[] values = new Object[3];
+ values[0] = makeVal(i);
+ for (int j = 1; j < values.length; j += 1) {
+ values[j] = isEntityBinding
+ ? makeEntity(endKey + j, i)
+ : makeVal(i);
+ }
+ // add duplicates
+ for (int j = 1; j < values.length; j += 1) {
+ imap.put(makeKey(endKey + j), values[j]);
+ }
+ // read duplicates
+ readDuplicates(i, key, values);
+ // remove duplicates
+ for (int j = 1; j < values.length; j += 1) {
+ imap.remove(makeKey(endKey + j));
+ }
+ checkDupsSize(1, map.duplicates(key));
+ }
+
+ void readDuplicates(int i, Object key, Object[] values) {
+
+ boolean isOrdered = map.isOrdered();
+ Collection dups;
+ Iterator iter;
+ // read with Map.duplicates().iterator()
+ dups = map.duplicates(key);
+ checkDupsSize(values.length, dups);
+ iter = iterator(dups);
+ try {
+ for (int j = 0; j < values.length; j += 1) {
+ assertTrue(iter.hasNext());
+ Object val = iter.next();
+ assertEquals(values[j], val);
+ }
+ assertTrue(!iter.hasNext());
+ } finally {
+ StoredIterator.close(iter);
+ }
+ // read with Map.values().iterator()
+ Collection clone = ((StoredCollection) map.values()).toList();
+ iter = iterator(map.values());
+ try {
+ for (int j = beginKey; j < i; j += 1) {
+ Object val = iter.next();
+ assertTrue(clone.remove(makeVal(j)));
+ if (isOrdered) {
+ assertEquals(makeVal(j), val);
+ }
+ }
+ for (int j = 0; j < values.length; j += 1) {
+ Object val = iter.next();
+ assertTrue(clone.remove(values[j]));
+ if (isOrdered) {
+ assertEquals(values[j], val);
+ }
+ }
+ for (int j = i + 1; j <= endKey; j += 1) {
+ Object val = iter.next();
+ assertTrue(clone.remove(makeVal(j)));
+ if (isOrdered) {
+ assertEquals(makeVal(j), val);
+ }
+ }
+ assertTrue(!iter.hasNext());
+ assertTrue(clone.isEmpty());
+ } finally {
+ StoredIterator.close(iter);
+ }
+ // read with Map.entrySet().iterator()
+ clone = ((StoredCollection) map.entrySet()).toList();
+ iter = iterator(map.entrySet());
+ try {
+ for (int j = beginKey; j < i; j += 1) {
+ Map.Entry entry = (Map.Entry) iter.next();
+ assertTrue(clone.remove(mapEntry(j)));
+ if (isOrdered) {
+ assertEquals(makeVal(j), entry.getValue());
+ assertEquals(makeKey(j), entry.getKey());
+ }
+ }
+ for (int j = 0; j < values.length; j += 1) {
+ Map.Entry entry = (Map.Entry) iter.next();
+ assertTrue(clone.remove(mapEntry(makeKey(i), values[j])));
+ if (isOrdered) {
+ assertEquals(values[j], entry.getValue());
+ assertEquals(makeKey(i), entry.getKey());
+ }
+ }
+ for (int j = i + 1; j <= endKey; j += 1) {
+ Map.Entry entry = (Map.Entry) iter.next();
+ assertTrue(clone.remove(mapEntry(j)));
+ if (isOrdered) {
+ assertEquals(makeVal(j), entry.getValue());
+ assertEquals(makeKey(j), entry.getKey());
+ }
+ }
+ assertTrue(!iter.hasNext());
+ assertTrue(clone.isEmpty());
+ } finally {
+ StoredIterator.close(iter);
+ }
+ // read with Map.keySet().iterator()
+ clone = ((StoredCollection) map.keySet()).toList();
+ iter = iterator(map.keySet());
+ try {
+ for (int j = beginKey; j < i; j += 1) {
+ Object val = iter.next();
+ assertTrue(clone.remove(makeKey(j)));
+ if (isOrdered) {
+ assertEquals(makeKey(j), val);
+ }
+ }
+ if (true) {
+ // only one key is iterated for all duplicates
+ Object val = iter.next();
+ assertTrue(clone.remove(makeKey(i)));
+ if (isOrdered) {
+ assertEquals(makeKey(i), val);
+ }
+ }
+ for (int j = i + 1; j <= endKey; j += 1) {
+ Object val = iter.next();
+ assertTrue(clone.remove(makeKey(j)));
+ if (isOrdered) {
+ assertEquals(makeKey(j), val);
+ }
+ }
+ assertTrue(!iter.hasNext());
+ assertTrue(clone.isEmpty());
+ } finally {
+ StoredIterator.close(iter);
+ }
+ }
+
+ void duplicatesNotAllowed() {
+
+ Collection dups = map.duplicates(makeKey(beginKey));
+ try {
+ dups.add(makeVal(beginKey));
+ fail();
+ } catch (UnsupportedOperationException expected) { }
+ ListIterator iter = (ListIterator) iterator(dups);
+ try {
+ iter.add(makeVal(beginKey));
+ fail();
+ } catch (UnsupportedOperationException expected) {
+ } finally {
+ StoredIterator.close(iter);
+ }
+ }
+
+ void listOperationsNotAllowed() {
+
+ ListIterator iter = (ListIterator) iterator(map.values());
+ try {
+ try {
+ iter.nextIndex();
+ fail();
+ } catch (UnsupportedOperationException expected) { }
+ try {
+ iter.previousIndex();
+ fail();
+ } catch (UnsupportedOperationException expected) { }
+ } finally {
+ StoredIterator.close(iter);
+ }
+ }
+
+ void testCdbLocking() {
+
+ Iterator readIterator;
+ Iterator writeIterator;
+ StoredKeySet set = (StoredKeySet) map.keySet();
+
+ // can open two CDB read cursors
+ readIterator = set.storedIterator(false);
+ try {
+ Iterator readIterator2 = set.storedIterator(false);
+ StoredIterator.close(readIterator2);
+ } finally {
+ StoredIterator.close(readIterator);
+ }
+
+ // can open two CDB write cursors
+ writeIterator = set.storedIterator(true);
+ try {
+ Iterator writeIterator2 = set.storedIterator(true);
+ StoredIterator.close(writeIterator2);
+ } finally {
+ StoredIterator.close(writeIterator);
+ }
+
+ // cannot open CDB write cursor when read cursor is open,
+ readIterator = set.storedIterator(false);
+ try {
+ writeIterator = set.storedIterator(true);
+ fail();
+ StoredIterator.close(writeIterator);
+ } catch (IllegalStateException e) {
+ } finally {
+ StoredIterator.close(readIterator);
+ }
+
+ if (index == null) {
+ // cannot put() with read cursor open
+ readIterator = set.storedIterator(false);
+ try {
+ map.put(makeKey(1), makeVal(1));
+ fail();
+ } catch (IllegalStateException e) {
+ } finally {
+ StoredIterator.close(readIterator);
+ }
+
+ // cannot append() with write cursor open with RECNO/QUEUE only
+ writeIterator = set.storedIterator(true);
+ try {
+ if (testStore.isQueueOrRecno()) {
+ try {
+ map.append(makeVal(1));
+ fail();
+ } catch (IllegalStateException e) {}
+ } else {
+ map.append(makeVal(1));
+ }
+ } finally {
+ StoredIterator.close(writeIterator);
+ }
+ }
+ }
+
+ Object makeVal(int key) {
+
+ if (isEntityBinding) {
+ return makeEntity(key);
+ } else {
+ return new Long(key + 100);
+ }
+ }
+
+ Object makeVal(int key, int val) {
+
+ if (isEntityBinding) {
+ return makeEntity(key, val);
+ } else {
+ return makeVal(val);
+ }
+ }
+
+ Object makeEntity(int key, int val) {
+
+ return new TestEntity(key, val + 100);
+ }
+
+ int intVal(Object val) {
+
+ if (isEntityBinding) {
+ return ((TestEntity) val).value - 100;
+ } else {
+ return ((Long) val).intValue() - 100;
+ }
+ }
+
+ int intKey(Object key) {
+
+ return ((Long) key).intValue();
+ }
+
+ Object makeVal(Long key) {
+
+ return makeVal(key.intValue());
+ }
+
+ Object makeEntity(int key) {
+
+ return makeEntity(key, key);
+ }
+
+ Object makeEntity(Long key) {
+
+ return makeEntity(key.intValue());
+ }
+
+ int intIter(Collection coll, Object value) {
+
+ if (coll instanceof StoredKeySet) {
+ return intKey(value);
+ } else {
+ if (coll instanceof StoredEntrySet) {
+ value = ((Map.Entry) value).getValue();
+ }
+ return intVal(value);
+ }
+ }
+
+ Map.Entry mapEntry(Object key, Object val) {
+
+ return new MapEntryParameter(key, val);
+ }
+
+ Map.Entry mapEntry(int key) {
+
+ return new MapEntryParameter(makeKey(key), makeVal(key));
+ }
+
+ Long makeKey(int key) {
+
+ return new Long(key);
+ }
+
+ boolean isSubMap() {
+
+ return rangeType != NONE;
+ }
+
+ void checkDupsSize(int expected, Collection coll) {
+
+ assertEquals(expected, coll.size());
+ if (coll instanceof StoredCollection) {
+ StoredIterator i = ((StoredCollection) coll).storedIterator(false);
+ try {
+ int actual = 0;
+ if (i.hasNext()) {
+ i.next();
+ actual = i.count();
+ }
+ assertEquals(expected, actual);
+ } finally {
+ StoredIterator.close(i);
+ }
+ }
+ }
+
+ private boolean isListAddAllowed() {
+
+ return list != null && testStore.isQueueOrRecno() &&
+ list.areKeysRenumbered();
+ }
+
+ private int countElements(Collection coll) {
+
+ int count = 0;
+ Iterator iter = iterator(coll);
+ try {
+ while (iter.hasNext()) {
+ iter.next();
+ count += 1;
+ }
+ } finally {
+ StoredIterator.close(iter);
+ }
+ return count;
+ }
+}
diff --git a/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/DbTestUtil.java b/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/DbTestUtil.java
new file mode 100644
index 0000000..584982c
--- /dev/null
+++ b/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/DbTestUtil.java
@@ -0,0 +1,129 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.collections.test;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import junit.framework.TestCase;
+
+import com.sleepycat.db.DatabaseConfig;
+
+/**
+ * @author Mark Hayes
+ */
+public class DbTestUtil {
+
+ public static final DatabaseConfig DBCONFIG_CREATE = new DatabaseConfig();
+ static {
+ DBCONFIG_CREATE.setAllowCreate(true);
+ }
+
+ private static final File TEST_DIR;
+ static {
+ String dir = System.getProperty("testdestdir");
+ if (dir == null || dir.length() == 0) {
+ dir = ".";
+ }
+ TEST_DIR = new File(dir, "tmp");
+ }
+
+ public static void printTestName(String name) {
+ // don't want verbose printing for now
+ // System.out.println(name);
+ }
+
+ public static File getExistingDir(String name)
+ throws IOException {
+
+ File dir = new File(TEST_DIR, name);
+ if (!dir.exists() || !dir.isDirectory()) {
+ throw new IllegalStateException(
+ "Not an existing directory: " + dir);
+ }
+ return dir;
+ }
+
+ public static File getNewDir()
+ throws IOException {
+
+ return getNewDir("test-dir");
+ }
+
+ public static File getNewDir(String name)
+ throws IOException {
+
+ File dir = new File(TEST_DIR, name);
+ if (dir.isDirectory()) {
+ String[] files = dir.list();
+ if (files != null) {
+ for (int i = 0; i < files.length; i += 1) {
+ new File(dir, files[i]).delete();
+ }
+ }
+ } else {
+ dir.delete();
+ dir.mkdirs();
+ }
+ return dir;
+ }
+
+ public static File getNewFile()
+ throws IOException {
+
+ return getNewFile("test-file");
+ }
+
+ public static File getNewFile(String name)
+ throws IOException {
+
+ return getNewFile(TEST_DIR, name);
+ }
+
+ public static File getNewFile(File dir, String name)
+ throws IOException {
+
+ File file = new File(dir, name);
+ file.delete();
+ return file;
+ }
+
+ public static boolean copyResource(Class cls, String fileName, File toDir)
+ throws IOException {
+
+ InputStream in = cls.getResourceAsStream("testdata/" + fileName);
+ if (in == null) {
+ return false;
+ }
+ in = new BufferedInputStream(in);
+ File file = new File(toDir, fileName);
+ OutputStream out = new FileOutputStream(file);
+ out = new BufferedOutputStream(out);
+ int c;
+ while ((c = in.read()) >= 0) out.write(c);
+ in.close();
+ out.close();
+ return true;
+ }
+
+ public static String qualifiedTestName(TestCase test) {
+
+ String s = test.getClass().getName();
+ int i = s.lastIndexOf('.');
+ if (i >= 0) {
+ s = s.substring(i + 1);
+ }
+ return s + '.' + test.getName();
+ }
+}
diff --git a/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/ForeignKeyTest.java b/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/ForeignKeyTest.java
new file mode 100644
index 0000000..8dc2759
--- /dev/null
+++ b/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/ForeignKeyTest.java
@@ -0,0 +1,342 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.collections.test;
+
+import java.util.Map;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.sleepycat.bind.serial.StoredClassCatalog;
+import com.sleepycat.bind.serial.TupleSerialMarshalledKeyCreator;
+import com.sleepycat.bind.serial.test.MarshalledObject;
+import com.sleepycat.collections.CurrentTransaction;
+import com.sleepycat.collections.TupleSerialFactory;
+import com.sleepycat.compat.DbCompat;
+import com.sleepycat.db.Database;
+import com.sleepycat.db.DatabaseConfig;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.ForeignKeyDeleteAction;
+import com.sleepycat.db.SecondaryConfig;
+import com.sleepycat.db.SecondaryDatabase;
+import com.sleepycat.util.ExceptionUnwrapper;
+import com.sleepycat.util.RuntimeExceptionWrapper;
+import com.sleepycat.util.test.SharedTestUtils;
+import com.sleepycat.util.test.TestEnv;
+
+/**
+ * @author Mark Hayes
+ */
+public class ForeignKeyTest extends TestCase {
+
+ private static final ForeignKeyDeleteAction[] ACTIONS = {
+ ForeignKeyDeleteAction.ABORT,
+ ForeignKeyDeleteAction.NULLIFY,
+ ForeignKeyDeleteAction.CASCADE,
+ };
+ private static final String[] ACTION_LABELS = {
+ "ABORT",
+ "NULLIFY",
+ "CASCADE",
+ };
+
+ public static void main(String[] args) {
+ junit.framework.TestResult tr =
+ junit.textui.TestRunner.run(suite());
+ if (tr.errorCount() > 0 ||
+ tr.failureCount() > 0) {
+ System.exit(1);
+ } else {
+ System.exit(0);
+ }
+ }
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite();
+ for (int i = 0; i < TestEnv.ALL.length; i += 1) {
+ for (int j = 0; j < ACTIONS.length; j += 1) {
+ suite.addTest(new ForeignKeyTest(TestEnv.ALL[i],
+ ACTIONS[j],
+ ACTION_LABELS[j]));
+ }
+ }
+ return suite;
+ }
+
+ private TestEnv testEnv;
+ private Environment env;
+ private StoredClassCatalog catalog;
+ private TupleSerialFactory factory;
+ private Database store1;
+ private Database store2;
+ private SecondaryDatabase index1;
+ private SecondaryDatabase index2;
+ private Map storeMap1;
+ private Map storeMap2;
+ private Map indexMap1;
+ private Map indexMap2;
+ private final ForeignKeyDeleteAction onDelete;
+
+ public ForeignKeyTest(TestEnv testEnv, ForeignKeyDeleteAction onDelete,
+ String onDeleteLabel) {
+
+ super("ForeignKeyTest-" + testEnv.getName() + '-' + onDeleteLabel);
+
+ this.testEnv = testEnv;
+ this.onDelete = onDelete;
+ }
+
+ @Override
+ public void setUp()
+ throws Exception {
+
+ SharedTestUtils.printTestName(getName());
+ env = testEnv.open(getName());
+
+ createDatabase();
+ }
+
+ @Override
+ public void tearDown() {
+
+ try {
+ if (index1 != null) {
+ index1.close();
+ }
+ if (index2 != null) {
+ index2.close();
+ }
+ if (store1 != null) {
+ store1.close();
+ }
+ if (store2 != null) {
+ store2.close();
+ }
+ if (catalog != null) {
+ catalog.close();
+ }
+ if (env != null) {
+ env.close();
+ }
+ } catch (Exception e) {
+ System.out.println("Ignored exception during tearDown: " + e);
+ } finally {
+ /* Ensure that GC can cleanup. */
+ env = null;
+ testEnv = null;
+ catalog = null;
+ store1 = null;
+ store2 = null;
+ index1 = null;
+ index2 = null;
+ factory = null;
+ storeMap1 = null;
+ storeMap2 = null;
+ indexMap1 = null;
+ indexMap2 = null;
+ }
+ }
+
+ @Override
+ public void runTest()
+ throws Exception {
+
+ try {
+ createViews();
+ writeAndRead();
+ } catch (Exception e) {
+ throw ExceptionUnwrapper.unwrap(e);
+ }
+ }
+
+ private void createDatabase()
+ throws Exception {
+
+ catalog = new StoredClassCatalog(openDb("catalog.db"));
+ factory = new TupleSerialFactory(catalog);
+ assertSame(catalog, factory.getCatalog());
+
+ store1 = openDb("store1.db");
+ store2 = openDb("store2.db");
+ index1 = openSecondaryDb(factory, "1", store1, "index1.db", null);
+ index2 = openSecondaryDb(factory, "2", store2, "index2.db", store1);
+ }
+
+ private Database openDb(String file)
+ throws Exception {
+
+ DatabaseConfig config = new DatabaseConfig();
+ DbCompat.setTypeBtree(config);
+ config.setTransactional(testEnv.isTxnMode());
+ config.setAllowCreate(true);
+
+ return DbCompat.testOpenDatabase(env, null, file, null, config);
+ }
+
+ private SecondaryDatabase openSecondaryDb(TupleSerialFactory factory,
+ String keyName,
+ Database primary,
+ String file,
+ Database foreignStore)
+ throws Exception {
+
+ TupleSerialMarshalledKeyCreator keyCreator =
+ factory.getKeyCreator(MarshalledObject.class, keyName);
+
+ SecondaryConfig secConfig = new SecondaryConfig();
+ DbCompat.setTypeBtree(secConfig);
+ secConfig.setTransactional(testEnv.isTxnMode());
+ secConfig.setAllowCreate(true);
+ secConfig.setKeyCreator(keyCreator);
+ if (foreignStore != null) {
+ secConfig.setForeignKeyDatabase(foreignStore);
+ secConfig.setForeignKeyDeleteAction(onDelete);
+ if (onDelete == ForeignKeyDeleteAction.NULLIFY) {
+ secConfig.setForeignKeyNullifier(keyCreator);
+ }
+ }
+
+ return DbCompat.testOpenSecondaryDatabase
+ (env, null, file, null, primary, secConfig);
+ }
+
+ private void createViews() {
+ storeMap1 = factory.newMap(store1, String.class,
+ MarshalledObject.class, true);
+ storeMap2 = factory.newMap(store2, String.class,
+ MarshalledObject.class, true);
+ indexMap1 = factory.newMap(index1, String.class,
+ MarshalledObject.class, true);
+ indexMap2 = factory.newMap(index2, String.class,
+ MarshalledObject.class, true);
+ }
+
+ private void writeAndRead()
+ throws Exception {
+
+ CurrentTransaction txn = CurrentTransaction.getInstance(env);
+ if (txn != null) {
+ txn.beginTransaction(null);
+ }
+
+ MarshalledObject o1 = new MarshalledObject("data1", "pk1", "ik1", "");
+ assertNull(storeMap1.put(null, o1));
+
+ assertEquals(o1, storeMap1.get("pk1"));
+ assertEquals(o1, indexMap1.get("ik1"));
+
+ MarshalledObject o2 = new MarshalledObject("data2", "pk2", "", "pk1");
+ assertNull(storeMap2.put(null, o2));
+
+ assertEquals(o2, storeMap2.get("pk2"));
+ assertEquals(o2, indexMap2.get("pk1"));
+
+ if (txn != null) {
+ txn.commitTransaction();
+ txn.beginTransaction(null);
+ }
+
+ /*
+ * store1 contains o1 with primary key "pk1" and index key "ik1".
+ *
+ * store2 contains o2 with primary key "pk2" and foreign key "pk1",
+ * which is the primary key of store1.
+ */
+
+ if (onDelete == ForeignKeyDeleteAction.ABORT) {
+
+ /* Test that we abort trying to delete a referenced key. */
+
+ try {
+ storeMap1.remove("pk1");
+ fail();
+ } catch (RuntimeExceptionWrapper expected) {
+ assertTrue(expected.getCause() instanceof DatabaseException);
+ assertTrue(!DbCompat.NEW_JE_EXCEPTIONS);
+ }
+ if (txn != null) {
+ txn.abortTransaction();
+ txn.beginTransaction(null);
+ }
+
+ /* Test that we can put a record into store2 with a null foreign
+ * key value. */
+
+ o2 = new MarshalledObject("data2", "pk2", "", "");
+ assertNotNull(storeMap2.put(null, o2));
+ assertEquals(o2, storeMap2.get("pk2"));
+
+ /* The index2 record should have been deleted since the key was set
+ * to null above. */
+
+ assertNull(indexMap2.get("pk1"));
+
+ /* Test that now we can delete the record in store1, since it is no
+ * longer referenced. */
+
+ assertNotNull(storeMap1.remove("pk1"));
+ assertNull(storeMap1.get("pk1"));
+ assertNull(indexMap1.get("ik1"));
+
+ } else if (onDelete == ForeignKeyDeleteAction.NULLIFY) {
+
+ /* Delete the referenced key. */
+
+ assertNotNull(storeMap1.remove("pk1"));
+ assertNull(storeMap1.get("pk1"));
+ assertNull(indexMap1.get("ik1"));
+
+ /* The store2 record should still exist, but should have an empty
+ * secondary key since it was nullified. */
+
+ o2 = (MarshalledObject) storeMap2.get("pk2");
+ assertNotNull(o2);
+ assertEquals("data2", o2.getData());
+ assertEquals("pk2", o2.getPrimaryKey());
+ assertEquals("", o2.getIndexKey1());
+ assertEquals("", o2.getIndexKey2());
+
+ } else if (onDelete == ForeignKeyDeleteAction.CASCADE) {
+
+ /* Delete the referenced key. */
+
+ assertNotNull(storeMap1.remove("pk1"));
+ assertNull(storeMap1.get("pk1"));
+ assertNull(indexMap1.get("ik1"));
+
+ /* The store2 record should have deleted also. */
+
+ assertNull(storeMap2.get("pk2"));
+ assertNull(indexMap2.get("pk1"));
+
+ } else {
+ throw new IllegalStateException();
+ }
+
+ /*
+ * Test that a foreign key value may not be used that is not present
+ * in the foreign store. "pk2" is not in store1 in this case.
+ */
+ assertNull(storeMap1.get("pk2"));
+ MarshalledObject o3 = new MarshalledObject("data3", "pk3", "", "pk2");
+ try {
+ storeMap2.put(null, o3);
+ fail();
+ } catch (RuntimeExceptionWrapper expected) {
+ assertTrue(expected.getCause() instanceof DatabaseException);
+ assertTrue(!DbCompat.NEW_JE_EXCEPTIONS);
+ }
+
+ if (txn != null) {
+ txn.abortTransaction();
+ }
+ }
+}
diff --git a/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/IterDeadlockTest.java b/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/IterDeadlockTest.java
new file mode 100644
index 0000000..59509e9
--- /dev/null
+++ b/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/IterDeadlockTest.java
@@ -0,0 +1,228 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.collections.test;
+
+import java.util.Iterator;
+import java.util.ListIterator;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.sleepycat.bind.ByteArrayBinding;
+import com.sleepycat.collections.StoredIterator;
+import com.sleepycat.collections.StoredSortedMap;
+import com.sleepycat.collections.TransactionRunner;
+import com.sleepycat.collections.TransactionWorker;
+import com.sleepycat.compat.DbCompat;
+import com.sleepycat.db.Database;
+import com.sleepycat.db.DatabaseConfig;
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.DeadlockException;
+import com.sleepycat.util.test.TestEnv;
+
+/**
+ * Tests the fix for [#10516], where the StoredIterator constructor was not
+ * closing the cursor when an exception occurred. For example, a deadlock
+ * exception might occur if the constructor was unable to move the cursor to
+ * the first element.
+ * @author Mark Hayes
+ */
+public class IterDeadlockTest extends TestCase {
+
+ private static final byte[] ONE = { 1 };
+
+ public static void main(String[] args) {
+ junit.framework.TestResult tr =
+ junit.textui.TestRunner.run(suite());
+ if (tr.errorCount() > 0 ||
+ tr.failureCount() > 0) {
+ System.exit(1);
+ } else {
+ System.exit(0);
+ }
+ }
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite(IterDeadlockTest.class);
+ return suite;
+ }
+
+ private Environment env;
+ private Database store1;
+ private Database store2;
+ private StoredSortedMap map1;
+ private StoredSortedMap map2;
+ private final ByteArrayBinding binding = new ByteArrayBinding();
+
+ public IterDeadlockTest(String name) {
+
+ super(name);
+ }
+
+ @Override
+ public void setUp()
+ throws Exception {
+
+ env = TestEnv.TXN.open("IterDeadlockTest");
+ store1 = openDb("store1.db");
+ store2 = openDb("store2.db");
+ map1 = new StoredSortedMap(store1, binding, binding, true);
+ map2 = new StoredSortedMap(store2, binding, binding, true);
+ }
+
+ @Override
+ public void tearDown() {
+
+ if (store1 != null) {
+ try {
+ store1.close();
+ } catch (Exception e) {
+ System.out.println("Ignored exception during tearDown: " + e);
+ }
+ }
+ if (store2 != null) {
+ try {
+ store2.close();
+ } catch (Exception e) {
+ System.out.println("Ignored exception during tearDown: " + e);
+ }
+ }
+ if (env != null) {
+ try {
+ env.close();
+ } catch (Exception e) {
+ System.out.println("Ignored exception during tearDown: " + e);
+ }
+ }
+ /* Allow GC of DB objects in the test case. */
+ env = null;
+ store1 = null;
+ store2 = null;
+ map1 = null;
+ map2 = null;
+ }
+
+ private Database openDb(String file)
+ throws Exception {
+
+ DatabaseConfig config = new DatabaseConfig();
+ DbCompat.setTypeBtree(config);
+ config.setTransactional(true);
+ config.setAllowCreate(true);
+
+ return DbCompat.testOpenDatabase(env, null, file, null, config);
+ }
+
+ public void testIterDeadlock()
+ throws Exception {
+
+ final Object parent = new Object();
+ final Object child1 = new Object();
+ final Object child2 = new Object();
+ final TransactionRunner runner = new TransactionRunner(env);
+ runner.setMaxRetries(0);
+
+ /* Write a record in each db. */
+ runner.run(new TransactionWorker() {
+ public void doWork() {
+ assertNull(map1.put(ONE, ONE));
+ assertNull(map2.put(ONE, ONE));
+ }
+ });
+
+ /*
+ * A thread to open iterator 1, then wait to be notified, then open
+ * iterator 2.
+ */
+ final Thread thread1 = new Thread(new Runnable() {
+ public void run() {
+ try {
+ runner.run(new TransactionWorker() {
+ public void doWork() throws Exception {
+ synchronized (child1) {
+ ListIterator i1 =
+ (ListIterator) map1.values().iterator();
+ i1.next();
+ i1.set(ONE); /* Write lock. */
+ StoredIterator.close(i1);
+ synchronized (parent) { parent.notify(); }
+ child1.wait();
+ Iterator i2 = map2.values().iterator();
+ assertTrue(i2.hasNext());
+ StoredIterator.close(i2);
+ }
+ }
+ });
+ } catch (DeadlockException expected) {
+ } catch (Exception e) {
+ e.printStackTrace();
+ fail(e.toString());
+ }
+ }
+ });
+
+ /*
+ * A thread to open iterator 2, then wait to be notified, then open
+ * iterator 1.
+ */
+ final Thread thread2 = new Thread(new Runnable() {
+ public void run() {
+ try {
+ runner.run(new TransactionWorker() {
+ public void doWork() throws Exception {
+ synchronized (child2) {
+ ListIterator i2 =
+ (ListIterator) map2.values().iterator();
+ i2.next();
+ i2.set(ONE); /* Write lock. */
+ StoredIterator.close(i2);
+ synchronized (parent) { parent.notify(); }
+ child2.wait();
+ Iterator i1 = map1.values().iterator();
+ assertTrue(i1.hasNext());
+ StoredIterator.close(i1);
+ }
+ }
+ });
+ } catch (DeadlockException expected) {
+ } catch (Exception e) {
+ e.printStackTrace();
+ fail(e.toString());
+ }
+ }
+ });
+
+ /*
+ * Open iterator 1 in thread 1, then iterator 2 in thread 2, then let
+ * the threads run to open the other iterators and cause a deadlock.
+ */
+ synchronized (parent) {
+ thread1.start();
+ parent.wait();
+ thread2.start();
+ parent.wait();
+ synchronized (child1) { child1.notify(); }
+ synchronized (child2) { child2.notify(); }
+ thread1.join();
+ thread2.join();
+ }
+
+ /*
+ * Before the fix for [#10516] we would get an exception indicating
+ * that cursors were not closed, when closing the stores below.
+ */
+ store1.close();
+ store1 = null;
+ store2.close();
+ store2 = null;
+ env.close();
+ env = null;
+ }
+}
diff --git a/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/JoinTest.java b/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/JoinTest.java
new file mode 100644
index 0000000..495f785
--- /dev/null
+++ b/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/JoinTest.java
@@ -0,0 +1,225 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.collections.test;
+
+import java.util.Map;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+
+import com.sleepycat.bind.serial.StoredClassCatalog;
+import com.sleepycat.bind.serial.test.MarshalledObject;
+import com.sleepycat.collections.StoredCollection;
+import com.sleepycat.collections.StoredContainer;
+import com.sleepycat.collections.StoredIterator;
+import com.sleepycat.collections.StoredMap;
+import com.sleepycat.collections.TransactionRunner;
+import com.sleepycat.collections.TransactionWorker;
+import com.sleepycat.collections.TupleSerialFactory;
+import com.sleepycat.compat.DbCompat;
+import com.sleepycat.db.Database;
+import com.sleepycat.db.DatabaseConfig;
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.SecondaryConfig;
+import com.sleepycat.db.SecondaryDatabase;
+import com.sleepycat.util.test.SharedTestUtils;
+import com.sleepycat.util.test.TestEnv;
+
+/**
+ * @author Mark Hayes
+ */
+public class JoinTest extends TestCase
+ implements TransactionWorker {
+
+ private static final String MATCH_DATA = "d4"; // matches both keys = "yes"
+ private static final String MATCH_KEY = "k4"; // matches both keys = "yes"
+ private static final String[] VALUES = {"yes", "yes"};
+
+ public static void main(String[] args) {
+ junit.framework.TestResult tr =
+ junit.textui.TestRunner.run(suite());
+ if (tr.errorCount() > 0 ||
+ tr.failureCount() > 0) {
+ System.exit(1);
+ } else {
+ System.exit(0);
+ }
+ }
+
+ public static Test suite() {
+ return new JoinTest();
+ }
+
+ private Environment env;
+ private TransactionRunner runner;
+ private StoredClassCatalog catalog;
+ private TupleSerialFactory factory;
+ private Database store;
+ private SecondaryDatabase index1;
+ private SecondaryDatabase index2;
+ private StoredMap storeMap;
+ private StoredMap indexMap1;
+ private StoredMap indexMap2;
+
+ public JoinTest() {
+
+ super("JoinTest");
+ }
+
+ @Override
+ public void setUp()
+ throws Exception {
+
+ SharedTestUtils.printTestName(getName());
+ env = TestEnv.TXN.open(getName());
+ runner = new TransactionRunner(env);
+ createDatabase();
+ }
+
+ @Override
+ public void tearDown() {
+
+ try {
+ if (index1 != null) {
+ index1.close();
+ }
+ if (index2 != null) {
+ index2.close();
+ }
+ if (store != null) {
+ store.close();
+ }
+ if (catalog != null) {
+ catalog.close();
+ }
+ if (env != null) {
+ env.close();
+ }
+ } catch (Exception e) {
+ System.out.println("Ignored exception during tearDown: " + e);
+ } finally {
+ /* Ensure that GC can cleanup. */
+ index1 = null;
+ index2 = null;
+ store = null;
+ catalog = null;
+ env = null;
+ runner = null;
+ factory = null;
+ storeMap = null;
+ indexMap1 = null;
+ indexMap2 = null;
+ }
+ }
+
+ @Override
+ public void runTest()
+ throws Exception {
+
+ runner.run(this);
+ }
+
+ public void doWork() {
+ createViews();
+ writeAndRead();
+ }
+
+ private void createDatabase()
+ throws Exception {
+
+ catalog = new StoredClassCatalog(openDb("catalog.db"));
+ factory = new TupleSerialFactory(catalog);
+ assertSame(catalog, factory.getCatalog());
+
+ store = openDb("store.db");
+ index1 = openSecondaryDb(store, "index1.db", "1");
+ index2 = openSecondaryDb(store, "index2.db", "2");
+ }
+
+ private Database openDb(String file)
+ throws Exception {
+
+ DatabaseConfig config = new DatabaseConfig();
+ DbCompat.setTypeBtree(config);
+ config.setTransactional(true);
+ config.setAllowCreate(true);
+
+ return DbCompat.testOpenDatabase(env, null, file, null, config);
+ }
+
+ private SecondaryDatabase openSecondaryDb(Database primary,
+ String file,
+ String keyName)
+ throws Exception {
+
+ SecondaryConfig secConfig = new SecondaryConfig();
+ DbCompat.setTypeBtree(secConfig);
+ secConfig.setTransactional(true);
+ secConfig.setAllowCreate(true);
+ DbCompat.setSortedDuplicates(secConfig, true);
+ secConfig.setKeyCreator(factory.getKeyCreator(MarshalledObject.class,
+ keyName));
+
+ return DbCompat.testOpenSecondaryDatabase
+ (env, null, file, null, primary, secConfig);
+ }
+
+ private void createViews() {
+ storeMap = factory.newMap(store, String.class,
+ MarshalledObject.class, true);
+ indexMap1 = factory.newMap(index1, String.class,
+ MarshalledObject.class, true);
+ indexMap2 = factory.newMap(index2, String.class,
+ MarshalledObject.class, true);
+ }
+
+ private void writeAndRead() {
+ // write records: Data, PrimaryKey, IndexKey1, IndexKey2
+ assertNull(storeMap.put(null,
+ new MarshalledObject("d1", "k1", "no", "yes")));
+ assertNull(storeMap.put(null,
+ new MarshalledObject("d2", "k2", "no", "no")));
+ assertNull(storeMap.put(null,
+ new MarshalledObject("d3", "k3", "no", "yes")));
+ assertNull(storeMap.put(null,
+ new MarshalledObject("d4", "k4", "yes", "yes")));
+ assertNull(storeMap.put(null,
+ new MarshalledObject("d5", "k5", "yes", "no")));
+
+ Object o;
+ Map.Entry e;
+
+ // join values with index maps
+ o = doJoin((StoredCollection) storeMap.values());
+ assertEquals(MATCH_DATA, ((MarshalledObject) o).getData());
+
+ // join keySet with index maps
+ o = doJoin((StoredCollection) storeMap.keySet());
+ assertEquals(MATCH_KEY, o);
+
+ // join entrySet with index maps
+ o = doJoin((StoredCollection) storeMap.entrySet());
+ e = (Map.Entry) o;
+ assertEquals(MATCH_KEY, e.getKey());
+ assertEquals(MATCH_DATA, ((MarshalledObject) e.getValue()).getData());
+ }
+
+ private Object doJoin(StoredCollection coll) {
+
+ StoredContainer[] indices = { indexMap1, indexMap2 };
+ StoredIterator i = coll.join(indices, VALUES, null);
+ try {
+ assertTrue(i.hasNext());
+ Object result = i.next();
+ assertNotNull(result);
+ assertFalse(i.hasNext());
+ return result;
+ } finally { i.close(); }
+ }
+}
diff --git a/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/NullTransactionRunner.java b/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/NullTransactionRunner.java
new file mode 100644
index 0000000..2ccfd66
--- /dev/null
+++ b/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/NullTransactionRunner.java
@@ -0,0 +1,32 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.collections.test;
+
+import com.sleepycat.collections.TransactionRunner;
+import com.sleepycat.collections.TransactionWorker;
+import com.sleepycat.db.Environment;
+import com.sleepycat.util.ExceptionUnwrapper;
+
+class NullTransactionRunner extends TransactionRunner {
+
+ NullTransactionRunner(Environment env) {
+
+ super(env);
+ }
+
+ public void run(TransactionWorker worker)
+ throws Exception {
+
+ try {
+ worker.doWork();
+ } catch (Exception e) {
+ throw ExceptionUnwrapper.unwrap(e);
+ }
+ }
+}
diff --git a/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/SecondaryDeadlockTest.java b/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/SecondaryDeadlockTest.java
new file mode 100644
index 0000000..b52e2cb
--- /dev/null
+++ b/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/SecondaryDeadlockTest.java
@@ -0,0 +1,206 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.collections.test;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.sleepycat.collections.StoredSortedMap;
+import com.sleepycat.collections.TransactionRunner;
+import com.sleepycat.collections.TransactionWorker;
+import com.sleepycat.db.Database;
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.DeadlockException;
+import com.sleepycat.db.TransactionConfig;
+import com.sleepycat.util.ExceptionUnwrapper;
+import com.sleepycat.util.test.TestEnv;
+
+/**
+ * Tests whether secondary access can cause a self-deadlock when reading via a
+ * secondary because the collections API secondary implementation in DB 4.2
+ * opens two cursors. Part of the problem in [#10516] was because the
+ * secondary get() was not done in a txn. This problem should not occur in DB
+ * 4.3 and JE -- an ordinary deadlock occurs instead and is detected.
+ *
+ * @author Mark Hayes
+ */
+public class SecondaryDeadlockTest extends TestCase {
+
+ private static final Long N_ONE = new Long(1);
+ private static final Long N_101 = new Long(101);
+ private static final int N_ITERS = 20;
+ private static final int MAX_RETRIES = 1000;
+
+ public static void main(String[] args) {
+ junit.framework.TestResult tr =
+ junit.textui.TestRunner.run(suite());
+ if (tr.errorCount() > 0 ||
+ tr.failureCount() > 0) {
+ System.exit(1);
+ } else {
+ System.exit(0);
+ }
+ }
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite(SecondaryDeadlockTest.class);
+ return suite;
+ }
+
+ private Environment env;
+ private Database store;
+ private Database index;
+ private StoredSortedMap storeMap;
+ private StoredSortedMap indexMap;
+ private Exception exception;
+
+ public SecondaryDeadlockTest(String name) {
+
+ super(name);
+ }
+
+ @Override
+ public void setUp()
+ throws Exception {
+
+ env = TestEnv.TXN.open("SecondaryDeadlockTest");
+ store = TestStore.BTREE_UNIQ.open(env, "store.db");
+ index = TestStore.BTREE_UNIQ.openIndex(store, "index.db");
+ storeMap = new StoredSortedMap(store,
+ TestStore.BTREE_UNIQ.getKeyBinding(),
+ TestStore.BTREE_UNIQ.getValueBinding(),
+ true);
+ indexMap = new StoredSortedMap(index,
+ TestStore.BTREE_UNIQ.getKeyBinding(),
+ TestStore.BTREE_UNIQ.getValueBinding(),
+ true);
+ }
+
+ @Override
+ public void tearDown() {
+
+ if (index != null) {
+ try {
+ index.close();
+ } catch (Exception e) {
+ System.out.println("Ignored exception during tearDown: " + e);
+ }
+ }
+ if (store != null) {
+ try {
+ store.close();
+ } catch (Exception e) {
+ System.out.println("Ignored exception during tearDown: " + e);
+ }
+ }
+ if (env != null) {
+ try {
+ env.close();
+ } catch (Exception e) {
+ System.out.println("Ignored exception during tearDown: " + e);
+ }
+ }
+ /* Allow GC of DB objects in the test case. */
+ env = null;
+ store = null;
+ index = null;
+ storeMap = null;
+ indexMap = null;
+ }
+
+ public void testSecondaryDeadlock()
+ throws Exception {
+
+ final TransactionRunner runner = new TransactionRunner(env);
+ runner.setMaxRetries(MAX_RETRIES);
+
+ /*
+ * This test deadlocks a lot at degree 3 serialization. In debugging
+ * this I discovered it was not due to phantom prevention per se but
+ * just to a change in timing.
+ */
+ TransactionConfig txnConfig = new TransactionConfig();
+ runner.setTransactionConfig(txnConfig);
+
+ /*
+ * A thread to do put() and delete() via the primary, which will lock
+ * the primary first then the secondary. Uses transactions.
+ */
+ final Thread thread1 = new Thread(new Runnable() {
+ public void run() {
+ try {
+ /* The TransactionRunner performs retries. */
+ for (int i = 0; i < N_ITERS; i +=1 ) {
+ runner.run(new TransactionWorker() {
+ public void doWork() {
+ assertEquals(null, storeMap.put(N_ONE, N_101));
+ }
+ });
+ runner.run(new TransactionWorker() {
+ public void doWork() {
+ assertEquals(N_101, storeMap.remove(N_ONE));
+ }
+ });
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ exception = e;
+ }
+ }
+ }, "ThreadOne");
+
+ /*
+ * A thread to get() via the secondary, which will lock the secondary
+ * first then the primary. Does not use a transaction.
+ */
+ final Thread thread2 = new Thread(new Runnable() {
+ public void run() {
+ try {
+ for (int i = 0; i < N_ITERS; i +=1 ) {
+ for (int j = 0; j < MAX_RETRIES; j += 1) {
+ try {
+ Object value = indexMap.get(N_ONE);
+ assertTrue(value == null ||
+ N_101.equals(value));
+ break;
+ } catch (Exception e) {
+ e = ExceptionUnwrapper.unwrap(e);
+ if (e instanceof DeadlockException) {
+ continue; /* Retry on deadlock. */
+ } else {
+ throw e;
+ }
+ }
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ exception = e;
+ }
+ }
+ }, "ThreadTwo");
+
+ thread1.start();
+ thread2.start();
+ thread1.join();
+ thread2.join();
+
+ index.close();
+ index = null;
+ store.close();
+ store = null;
+ env.close();
+ env = null;
+
+ if (exception != null) {
+ fail(exception.toString());
+ }
+ }
+}
diff --git a/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/TestDataBinding.java b/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/TestDataBinding.java
new file mode 100644
index 0000000..96cd41d
--- /dev/null
+++ b/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/TestDataBinding.java
@@ -0,0 +1,33 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.collections.test;
+
+import com.sleepycat.bind.EntryBinding;
+import com.sleepycat.db.DatabaseEntry;
+
+/**
+ * @author Mark Hayes
+ */
+class TestDataBinding implements EntryBinding {
+
+ public Object entryToObject(DatabaseEntry data) {
+
+ if (data.getSize() != 1) {
+ throw new IllegalStateException("size=" + data.getSize());
+ }
+ byte val = data.getData()[data.getOffset()];
+ return new Long(val);
+ }
+
+ public void objectToEntry(Object object, DatabaseEntry data) {
+
+ byte val = ((Number) object).byteValue();
+ data.setData(new byte[] { val }, 0, 1);
+ }
+}
diff --git a/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/TestEntity.java b/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/TestEntity.java
new file mode 100644
index 0000000..3c895d7
--- /dev/null
+++ b/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/TestEntity.java
@@ -0,0 +1,44 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.collections.test;
+
+/**
+ * @author Mark Hayes
+ */
+class TestEntity {
+
+ int key;
+ int value;
+
+ TestEntity(int key, int value) {
+
+ this.key = key;
+ this.value = value;
+ }
+
+ public boolean equals(Object o) {
+
+ try {
+ TestEntity e = (TestEntity) o;
+ return e.key == key && e.value == value;
+ } catch (ClassCastException e) {
+ return false;
+ }
+ }
+
+ public int hashCode() {
+
+ return key;
+ }
+
+ public String toString() {
+
+ return "[key " + key + " value " + value + ']';
+ }
+}
diff --git a/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/TestEntityBinding.java b/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/TestEntityBinding.java
new file mode 100644
index 0000000..a6d7632
--- /dev/null
+++ b/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/TestEntityBinding.java
@@ -0,0 +1,63 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.collections.test;
+
+import com.sleepycat.bind.EntityBinding;
+import com.sleepycat.bind.RecordNumberBinding;
+import com.sleepycat.db.DatabaseEntry;
+
+/**
+ * @author Mark Hayes
+ */
+class TestEntityBinding implements EntityBinding {
+
+ private boolean isRecNum;
+
+ TestEntityBinding(boolean isRecNum) {
+
+ this.isRecNum = isRecNum;
+ }
+
+ public Object entryToObject(DatabaseEntry key, DatabaseEntry value) {
+
+ byte keyByte;
+ if (isRecNum) {
+ if (key.getSize() != 4) {
+ throw new IllegalStateException();
+ }
+ keyByte = (byte) RecordNumberBinding.entryToRecordNumber(key);
+ } else {
+ if (key.getSize() != 1) {
+ throw new IllegalStateException();
+ }
+ keyByte = key.getData()[key.getOffset()];
+ }
+ if (value.getSize() != 1) {
+ throw new IllegalStateException();
+ }
+ byte valByte = value.getData()[value.getOffset()];
+ return new TestEntity(keyByte, valByte);
+ }
+
+ public void objectToKey(Object object, DatabaseEntry key) {
+
+ byte val = (byte) ((TestEntity) object).key;
+ if (isRecNum) {
+ RecordNumberBinding.recordNumberToEntry(val, key);
+ } else {
+ key.setData(new byte[] { val }, 0, 1);
+ }
+ }
+
+ public void objectToData(Object object, DatabaseEntry value) {
+
+ byte val = (byte) ((TestEntity) object).value;
+ value.setData(new byte[] { val }, 0, 1);
+ }
+}
diff --git a/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/TestEnv.java b/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/TestEnv.java
new file mode 100644
index 0000000..aed6b5a
--- /dev/null
+++ b/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/TestEnv.java
@@ -0,0 +1,130 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.collections.test;
+
+import java.io.File;
+import java.io.IOException;
+
+import com.sleepycat.compat.DbCompat;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.EnvironmentConfig;
+import com.sleepycat.util.test.SharedTestUtils;
+
+/**
+ * @author Mark Hayes
+ */
+public class TestEnv {
+
+ public static final TestEnv BDB;
+ public static final TestEnv CDB;
+ public static final TestEnv TXN;
+ static {
+ EnvironmentConfig config;
+
+ config = newEnvConfig();
+ BDB = new TestEnv("bdb", config);
+
+ if (DbCompat.CDB) {
+ config = newEnvConfig();
+ DbCompat.setInitializeCDB(config, true);
+ CDB = new TestEnv("cdb", config);
+ } else {
+ CDB = null;
+ }
+
+ config = newEnvConfig();
+ config.setTransactional(true);
+ DbCompat.setInitializeLocking(config, true);
+ TXN = new TestEnv("txn", config);
+ }
+
+ private static EnvironmentConfig newEnvConfig() {
+
+ EnvironmentConfig config = new EnvironmentConfig();
+ if (DbCompat.MEMORY_SUBSYSTEM) {
+ DbCompat.setInitializeCache(config, true);
+ }
+ return config;
+ }
+
+ public static final TestEnv[] ALL;
+ static {
+ if (DbCompat.CDB) {
+ ALL = new TestEnv[] { BDB, CDB, TXN };
+ } else {
+ ALL = new TestEnv[] { BDB, TXN };
+ }
+ }
+
+ private String name;
+ private EnvironmentConfig config;
+
+ TestEnv(String name, EnvironmentConfig config) {
+
+ this.name = name;
+ this.config = config;
+ }
+
+ public String getName() {
+
+ return name;
+ }
+
+ public boolean isTxnMode() {
+
+ return config.getTransactional();
+ }
+
+ public boolean isCdbMode() {
+
+ return DbCompat.getInitializeCDB(config);
+ }
+
+ public Environment open(String testName)
+ throws IOException, DatabaseException {
+
+ return open(testName, true);
+ }
+
+ public Environment open(String testName, boolean create)
+ throws IOException, DatabaseException {
+
+ config.setAllowCreate(create);
+ /* OLDEST deadlock detection on DB matches the use of timeouts on JE.*/
+ DbCompat.setLockDetectModeOldest(config);
+ File dir = getDirectory(testName, create);
+ return newEnvironment(dir, config);
+ }
+
+ /**
+ * Is overridden in XACollectionTest.
+ */
+ protected Environment newEnvironment(File dir, EnvironmentConfig config)
+ throws DatabaseException, IOException {
+
+ return new Environment(dir, config);
+ }
+
+ public File getDirectory(String testName)
+ throws IOException {
+
+ return getDirectory(testName, true);
+ }
+
+ public File getDirectory(String testName, boolean create)
+ throws IOException {
+
+ if (create) {
+ return SharedTestUtils.getNewDir("db-test/" + testName);
+ } else {
+ return SharedTestUtils.getExistingDir("db-test/" + testName);
+ }
+ }
+}
diff --git a/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/TestKeyAssigner.java b/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/TestKeyAssigner.java
new file mode 100644
index 0000000..0e5ea4f
--- /dev/null
+++ b/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/TestKeyAssigner.java
@@ -0,0 +1,41 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.collections.test;
+
+import com.sleepycat.bind.RecordNumberBinding;
+import com.sleepycat.collections.PrimaryKeyAssigner;
+import com.sleepycat.db.DatabaseEntry;
+
+/**
+ * @author Mark Hayes
+ */
+class TestKeyAssigner implements PrimaryKeyAssigner {
+
+ private byte next = 1;
+ private final boolean isRecNum;
+
+ TestKeyAssigner(boolean isRecNum) {
+
+ this.isRecNum = isRecNum;
+ }
+
+ public void assignKey(DatabaseEntry keyData) {
+ if (isRecNum) {
+ RecordNumberBinding.recordNumberToEntry(next, keyData);
+ } else {
+ keyData.setData(new byte[] { next }, 0, 1);
+ }
+ next += 1;
+ }
+
+ void reset() {
+
+ next = 1;
+ }
+}
diff --git a/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/TestKeyCreator.java b/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/TestKeyCreator.java
new file mode 100644
index 0000000..bcee3ef
--- /dev/null
+++ b/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/TestKeyCreator.java
@@ -0,0 +1,56 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.collections.test;
+
+import com.sleepycat.bind.RecordNumberBinding;
+import com.sleepycat.db.DatabaseEntry;
+import com.sleepycat.db.SecondaryDatabase;
+import com.sleepycat.db.SecondaryKeyCreator;
+
+/**
+ * Unused until secondaries are available.
+ * @author Mark Hayes
+ */
+class TestKeyCreator implements SecondaryKeyCreator {
+
+ private final boolean isRecNum;
+
+ TestKeyCreator(boolean isRecNum) {
+
+ this.isRecNum = isRecNum;
+ }
+
+ public boolean createSecondaryKey(SecondaryDatabase db,
+ DatabaseEntry primaryKeyData,
+ DatabaseEntry valueData,
+ DatabaseEntry indexKeyData) {
+ if (valueData.getSize() == 0) {
+ return false;
+ }
+ if (valueData.getSize() != 1) {
+ throw new IllegalStateException();
+ }
+ byte val = valueData.getData()[valueData.getOffset()];
+ if (val == 0) {
+ return false; // fixed-len pad value
+ }
+ val -= 100;
+ if (isRecNum) {
+ RecordNumberBinding.recordNumberToEntry(val, indexKeyData);
+ } else {
+ indexKeyData.setData(new byte[] { val }, 0, 1);
+ }
+ return true;
+ }
+
+ public void clearIndexKey(DatabaseEntry valueData) {
+
+ throw new RuntimeException("not supported");
+ }
+}
diff --git a/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/TestSR15721.java b/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/TestSR15721.java
new file mode 100644
index 0000000..749e375
--- /dev/null
+++ b/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/TestSR15721.java
@@ -0,0 +1,119 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.collections.test;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.sleepycat.collections.CurrentTransaction;
+import com.sleepycat.db.Environment;
+import com.sleepycat.util.test.TestEnv;
+
+/**
+ * @author Chao Huang
+ */
+public class TestSR15721 extends TestCase {
+
+ /**
+ * Runs a command line collection test.
+ * @see #usage
+ */
+ public static void main(String[] args) {
+ if (args.length == 1 &&
+ (args[0].equals("-h") || args[0].equals("-help"))) {
+ usage();
+ } else {
+ junit.framework.TestResult tr =
+ junit.textui.TestRunner.run(suite());
+ if (tr.errorCount() > 0 ||
+ tr.failureCount() > 0) {
+ System.exit(1);
+ } else {
+ System.exit(0);
+ }
+ }
+ }
+
+ private static void usage() {
+
+ System.out.println(
+ "Usage: java com.sleepycat.collections.test.TestSR15721"
+ + " [-h | -help]\n");
+ System.exit(2);
+ }
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite(TestSR15721.class);
+ return suite;
+ }
+
+ private Environment env;
+ private CurrentTransaction currentTxn;
+
+ @Override
+ public void setUp()
+ throws Exception {
+
+ env = TestEnv.TXN.open("TestSR15721");
+ currentTxn = CurrentTransaction.getInstance(env);
+ }
+
+ @Override
+ public void tearDown() {
+ try {
+ if (env != null) {
+ env.close();
+ }
+ } catch (Exception e) {
+ System.out.println("Ignored exception during tearDown: " + e);
+ } finally {
+ /* Ensure that GC can cleanup. */
+ env = null;
+ currentTxn = null;
+ }
+ }
+
+ /**
+ * Tests that the CurrentTransaction instance doesn't indeed allow GC to
+ * reclaim while attached environment is open. [#15721]
+ */
+ public void testSR15721Fix()
+ throws Exception {
+
+ int hash = currentTxn.hashCode();
+ int hash2 = -1;
+
+ currentTxn = CurrentTransaction.getInstance(env);
+ hash2 = currentTxn.hashCode();
+ assertTrue(hash == hash2);
+
+ currentTxn.beginTransaction(null);
+ currentTxn = null;
+ hash2 = -1;
+
+ for (int i = 0; i < 10; i += 1) {
+ byte[] x = null;
+ try {
+ x = new byte[Integer.MAX_VALUE - 1];
+ fail();
+ } catch (OutOfMemoryError expected) {
+ }
+ assertNull(x);
+
+ System.gc();
+ }
+
+ currentTxn = CurrentTransaction.getInstance(env);
+ hash2 = currentTxn.hashCode();
+ currentTxn.commitTransaction();
+
+ assertTrue(hash == hash2);
+ }
+}
diff --git a/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/TestStore.java b/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/TestStore.java
new file mode 100644
index 0000000..f719193
--- /dev/null
+++ b/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/TestStore.java
@@ -0,0 +1,279 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.collections.test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.sleepycat.bind.EntityBinding;
+import com.sleepycat.bind.EntryBinding;
+import com.sleepycat.bind.RecordNumberBinding;
+import com.sleepycat.collections.CurrentTransaction;
+import com.sleepycat.compat.DbCompat;
+import com.sleepycat.db.Database;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.SecondaryConfig;
+
+/**
+ * @author Mark Hayes
+ */
+class TestStore {
+
+ static final TestKeyCreator BYTE_EXTRACTOR = new TestKeyCreator(false);
+ static final TestKeyCreator RECNO_EXTRACTOR = new TestKeyCreator(true);
+ static final EntryBinding VALUE_BINDING = new TestDataBinding();
+ static final EntryBinding BYTE_KEY_BINDING = VALUE_BINDING;
+ static final EntryBinding RECNO_KEY_BINDING = new RecordNumberBinding();
+ static final EntityBinding BYTE_ENTITY_BINDING =
+ new TestEntityBinding(false);
+ static final EntityBinding RECNO_ENTITY_BINDING =
+ new TestEntityBinding(true);
+ static final TestKeyAssigner BYTE_KEY_ASSIGNER =
+ new TestKeyAssigner(false);
+ static final TestKeyAssigner RECNO_KEY_ASSIGNER =
+ new TestKeyAssigner(true);
+
+ static final TestStore BTREE_UNIQ;
+ static final TestStore BTREE_DUP;
+ static final TestStore BTREE_DUPSORT;
+ static final TestStore BTREE_RECNUM;
+ static final TestStore HASH_UNIQ;
+ static final TestStore HASH_DUP;
+ static final TestStore HASH_DUPSORT;
+ static final TestStore QUEUE;
+ static final TestStore RECNO;
+ static final TestStore RECNO_RENUM;
+
+ static final TestStore[] ALL;
+ static {
+ List list = new ArrayList();
+ SecondaryConfig config;
+
+ config = new SecondaryConfig();
+ DbCompat.setTypeBtree(config);
+ BTREE_UNIQ = new TestStore("btree-uniq", config);
+ BTREE_UNIQ.indexOf = BTREE_UNIQ;
+ list.add(BTREE_UNIQ);
+
+ if (DbCompat.INSERTION_ORDERED_DUPLICATES) {
+ config = new SecondaryConfig();
+ DbCompat.setTypeBtree(config);
+ DbCompat.setUnsortedDuplicates(config, true);
+ BTREE_DUP = new TestStore("btree-dup", config);
+ BTREE_DUP.indexOf = null; // indexes must use sorted dups
+ list.add(BTREE_DUP);
+ } else {
+ BTREE_DUP = null;
+ }
+
+ config = new SecondaryConfig();
+ DbCompat.setTypeBtree(config);
+ DbCompat.setSortedDuplicates(config, true);
+ BTREE_DUPSORT = new TestStore("btree-dupsort", config);
+ BTREE_DUPSORT.indexOf = BTREE_UNIQ;
+ list.add(BTREE_DUPSORT);
+
+ if (DbCompat.BTREE_RECNUM_METHOD) {
+ config = new SecondaryConfig();
+ DbCompat.setTypeBtree(config);
+ DbCompat.setBtreeRecordNumbers(config, true);
+ BTREE_RECNUM = new TestStore("btree-recnum", config);
+ BTREE_RECNUM.indexOf = BTREE_RECNUM;
+ list.add(BTREE_RECNUM);
+ } else {
+ BTREE_RECNUM = null;
+ }
+
+ if (DbCompat.HASH_METHOD) {
+ config = new SecondaryConfig();
+ DbCompat.setTypeHash(config);
+ HASH_UNIQ = new TestStore("hash-uniq", config);
+ HASH_UNIQ.indexOf = HASH_UNIQ;
+ list.add(HASH_UNIQ);
+
+ if (DbCompat.INSERTION_ORDERED_DUPLICATES) {
+ config = new SecondaryConfig();
+ DbCompat.setTypeHash(config);
+ DbCompat.setUnsortedDuplicates(config, true);
+ HASH_DUP = new TestStore("hash-dup", config);
+ HASH_DUP.indexOf = null; // indexes must use sorted dups
+ list.add(HASH_DUP);
+ } else {
+ HASH_DUP = null;
+ }
+
+ config = new SecondaryConfig();
+ DbCompat.setTypeHash(config);
+ DbCompat.setSortedDuplicates(config, true);
+ HASH_DUPSORT = new TestStore("hash-dupsort", config);
+ HASH_DUPSORT.indexOf = HASH_UNIQ;
+ list.add(HASH_DUPSORT);
+ } else {
+ HASH_UNIQ = null;
+ HASH_DUP = null;
+ HASH_DUPSORT = null;
+ }
+
+ if (DbCompat.QUEUE_METHOD) {
+ config = new SecondaryConfig();
+ DbCompat.setTypeQueue(config);
+ QUEUE = new TestStore("queue", config);
+ QUEUE.indexOf = QUEUE;
+ list.add(QUEUE);
+ } else {
+ QUEUE = null;
+ }
+
+ if (DbCompat.RECNO_METHOD) {
+ config = new SecondaryConfig();
+ DbCompat.setTypeRecno(config);
+ RECNO = new TestStore("recno", config);
+ RECNO.indexOf = RECNO;
+ list.add(RECNO);
+
+ config = new SecondaryConfig();
+ DbCompat.setTypeRecno(config);
+ DbCompat.setRenumbering(config, true);
+ RECNO_RENUM = new TestStore("recno-renum", config);
+ RECNO_RENUM.indexOf = null; // indexes must have stable keys
+ list.add(RECNO_RENUM);
+ } else {
+ RECNO = null;
+ RECNO_RENUM = null;
+ }
+
+ ALL = new TestStore[list.size()];
+ list.toArray(ALL);
+ }
+
+ private String name;
+ private SecondaryConfig config;
+ private TestStore indexOf;
+ private boolean isRecNumFormat;
+
+ private TestStore(String name, SecondaryConfig config) {
+
+ this.name = name;
+ this.config = config;
+
+ isRecNumFormat = isQueueOrRecno() ||
+ (DbCompat.isTypeBtree(config) &&
+ DbCompat.getBtreeRecordNumbers(config));
+ }
+
+ EntryBinding getValueBinding() {
+
+ return VALUE_BINDING;
+ }
+
+ EntryBinding getKeyBinding() {
+
+ return isRecNumFormat ? RECNO_KEY_BINDING : BYTE_KEY_BINDING;
+ }
+
+ EntityBinding getEntityBinding() {
+
+ return isRecNumFormat ? RECNO_ENTITY_BINDING : BYTE_ENTITY_BINDING;
+ }
+
+ TestKeyAssigner getKeyAssigner() {
+
+ if (isQueueOrRecno()) {
+ return null;
+ } else {
+ if (isRecNumFormat) {
+ return RECNO_KEY_ASSIGNER;
+ } else {
+ return BYTE_KEY_ASSIGNER;
+ }
+ }
+ }
+
+ String getName() {
+
+ return name;
+ }
+
+ boolean isOrdered() {
+
+ return !DbCompat.isTypeHash(config);
+ }
+
+ boolean isQueueOrRecno() {
+
+ return DbCompat.isTypeQueue(config) || DbCompat.isTypeRecno(config);
+ }
+
+ boolean areKeyRangesAllowed() {
+ return isOrdered() && !isQueueOrRecno();
+ }
+
+ boolean areDuplicatesAllowed() {
+
+ return DbCompat.getSortedDuplicates(config) ||
+ DbCompat.getUnsortedDuplicates(config);
+ }
+
+ boolean hasRecNumAccess() {
+
+ return isRecNumFormat;
+ }
+
+ boolean areKeysRenumbered() {
+
+ return hasRecNumAccess() &&
+ (DbCompat.isTypeBtree(config) ||
+ DbCompat.getRenumbering(config));
+ }
+
+ TestStore getIndexOf() {
+
+ return DbCompat.SECONDARIES ? indexOf : null;
+ }
+
+ Database open(Environment env, String fileName)
+ throws DatabaseException {
+
+ int fixedLen = (isQueueOrRecno() ? 1 : 0);
+ return openDb(env, fileName, fixedLen, null);
+ }
+
+ Database openIndex(Database primary, String fileName)
+ throws DatabaseException {
+
+ int fixedLen = (isQueueOrRecno() ? 4 : 0);
+ config.setKeyCreator(isRecNumFormat ? RECNO_EXTRACTOR
+ : BYTE_EXTRACTOR);
+ Environment env = primary.getEnvironment();
+ return openDb(env, fileName, fixedLen, primary);
+ }
+
+ private Database openDb(Environment env, String fileName, int fixedLen,
+ Database primary)
+ throws DatabaseException {
+
+ if (fixedLen > 0) {
+ DbCompat.setRecordLength(config, fixedLen);
+ DbCompat.setRecordPad(config, 0);
+ } else {
+ DbCompat.setRecordLength(config, 0);
+ }
+ config.setAllowCreate(true);
+ DbCompat.setReadUncommitted(config, true);
+ config.setTransactional(CurrentTransaction.getInstance(env) != null);
+ if (primary != null) {
+ return DbCompat.testOpenSecondaryDatabase
+ (env, null, fileName, null, primary, config);
+ } else {
+ return DbCompat.testOpenDatabase
+ (env, null, fileName, null, config);
+ }
+ }
+}
diff --git a/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/TransactionTest.java b/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/TransactionTest.java
new file mode 100644
index 0000000..a427f2a
--- /dev/null
+++ b/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/TransactionTest.java
@@ -0,0 +1,838 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package com.sleepycat.collections.test;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.Iterator;
+import java.util.List;
+import java.util.SortedSet;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.sleepycat.collections.CurrentTransaction;
+import com.sleepycat.collections.StoredCollections;
+import com.sleepycat.collections.StoredContainer;
+import com.sleepycat.collections.StoredIterator;
+import com.sleepycat.collections.StoredList;
+import com.sleepycat.collections.StoredSortedMap;
+import com.sleepycat.collections.TransactionRunner;
+import com.sleepycat.collections.TransactionWorker;
+import com.sleepycat.compat.DbCompat;
+import com.sleepycat.db.Cursor;
+import com.sleepycat.db.CursorConfig;
+import com.sleepycat.db.Database;
+import com.sleepycat.db.DatabaseConfig;
+import com.sleepycat.db.DatabaseEntry;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.EnvironmentConfig;
+import com.sleepycat.db.DeadlockException;
+import com.sleepycat.db.OperationStatus;
+import com.sleepycat.db.Transaction;
+import com.sleepycat.db.TransactionConfig;
+import com.sleepycat.util.RuntimeExceptionWrapper;
+import com.sleepycat.util.test.SharedTestUtils;
+import com.sleepycat.util.test.TestEnv;
+
+/**
+ * @author Mark Hayes
+ */
+public class TransactionTest extends TestCase {
+
+ private static final Long ONE = new Long(1);
+ private static final Long TWO = new Long(2);
+ private static final Long THREE = new Long(3);
+
+ /**
+ * Runs a command line collection test.
+ * @see #usage
+ */
+ public static void main(String[] args) {
+ if (args.length == 1 &&
+ (args[0].equals("-h") || args[0].equals("-help"))) {
+ usage();
+ } else {
+ junit.framework.TestResult tr =
+ junit.textui.TestRunner.run(suite());
+ if (tr.errorCount() > 0 ||
+ tr.failureCount() > 0) {
+ System.exit(1);
+ } else {
+ System.exit(0);
+ }
+ }
+ }
+
+ private static void usage() {
+
+ System.out.println(
+ "Usage: java com.sleepycat.collections.test.TransactionTest"
+ + " [-h | -help]\n");
+ System.exit(2);
+ }
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite(TransactionTest.class);
+ return suite;
+ }
+
+ private Environment env;
+ private CurrentTransaction currentTxn;
+ private Database store;
+ private StoredSortedMap map;
+ private TestStore testStore = TestStore.BTREE_UNIQ;
+
+ public TransactionTest(String name) {
+
+ super(name);
+ }
+
+ @Override
+ public void setUp()
+ throws Exception {
+
+ SharedTestUtils.printTestName(SharedTestUtils.qualifiedTestName(this));
+ env = TestEnv.TXN.open("TransactionTests");
+ currentTxn = CurrentTransaction.getInstance(env);
+ store = testStore.open(env, dbName(0));
+ map = new StoredSortedMap(store, testStore.getKeyBinding(),
+ testStore.getValueBinding(), true);
+ }
+
+ @Override
+ public void tearDown() {
+
+ try {
+ if (store != null) {
+ store.close();
+ }
+ if (env != null) {
+ env.close();
+ }
+ } catch (Exception e) {
+ System.out.println("Ignored exception during tearDown: " + e);
+ } finally {
+ /* Ensure that GC can cleanup. */
+ store = null;
+ env = null;
+ currentTxn = null;
+ map = null;
+ testStore = null;
+ }
+ }
+
+ private String dbName(int i) {
+
+ return "txn-test-" + getName() + '-' + i;
+ }
+
+ public void testGetters()
+ throws Exception {
+
+ assertNotNull(env);
+ assertNotNull(currentTxn);
+ assertNull(currentTxn.getTransaction());
+
+ currentTxn.beginTransaction(null);
+ assertNotNull(currentTxn.getTransaction());
+ currentTxn.commitTransaction();
+ assertNull(currentTxn.getTransaction());
+
+ currentTxn.beginTransaction(null);
+ assertNotNull(currentTxn.getTransaction());
+ currentTxn.abortTransaction();
+ assertNull(currentTxn.getTransaction());
+
+ // read-uncommitted property should be inherited
+
+ assertTrue(!isReadUncommitted(map));
+ assertTrue(!isReadUncommitted(map.values()));
+ assertTrue(!isReadUncommitted(map.keySet()));
+ assertTrue(!isReadUncommitted(map.entrySet()));
+
+ StoredSortedMap other = (StoredSortedMap)
+ StoredCollections.configuredMap
+ (map, CursorConfig.READ_UNCOMMITTED);
+ assertTrue(isReadUncommitted(other));
+ assertTrue(isReadUncommitted(other.values()));
+ assertTrue(isReadUncommitted(other.keySet()));
+ assertTrue(isReadUncommitted(other.entrySet()));
+ assertTrue(!isReadUncommitted(map));
+ assertTrue(!isReadUncommitted(map.values()));
+ assertTrue(!isReadUncommitted(map.keySet()));
+ assertTrue(!isReadUncommitted(map.entrySet()));
+
+ // read-committed property should be inherited
+
+ assertTrue(!isReadCommitted(map));
+ assertTrue(!isReadCommitted(map.values()));
+ assertTrue(!isReadCommitted(map.keySet()));
+ assertTrue(!isReadCommitted(map.entrySet()));
+
+ other = (StoredSortedMap)
+ StoredCollections.configuredMap
+ (map, CursorConfig.READ_COMMITTED);
+ assertTrue(isReadCommitted(other));
+ assertTrue(isReadCommitted(other.values()));
+ assertTrue(isReadCommitted(other.keySet()));
+ assertTrue(isReadCommitted(other.entrySet()));
+ assertTrue(!isReadCommitted(map));
+ assertTrue(!isReadCommitted(map.values()));
+ assertTrue(!isReadCommitted(map.keySet()));
+ assertTrue(!isReadCommitted(map.entrySet()));
+ }
+
+ public void testTransactional()
+ throws Exception {
+
+ // is transactional because DB_AUTO_COMMIT was passed to
+ // Database.open()
+ //
+ assertTrue(map.isTransactional());
+ store.close();
+ store = null;
+
+ // is not transactional
+ //
+ DatabaseConfig dbConfig = new DatabaseConfig();
+ DbCompat.setTypeBtree(dbConfig);
+ dbConfig.setAllowCreate(true);
+ Database db = DbCompat.testOpenDatabase
+ (env, null, dbName(1), null, dbConfig);
+ map = new StoredSortedMap(db, testStore.getKeyBinding(),
+ testStore.getValueBinding(), true);
+ assertTrue(!map.isTransactional());
+ map.put(ONE, ONE);
+ readCheck(map, ONE, ONE);
+ db.close();
+
+ // is transactional
+ //
+ dbConfig.setTransactional(true);
+ currentTxn.beginTransaction(null);
+ db = DbCompat.testOpenDatabase
+ (env, currentTxn.getTransaction(), dbName(2), null, dbConfig);
+ currentTxn.commitTransaction();
+ map = new StoredSortedMap(db, testStore.getKeyBinding(),
+ testStore.getValueBinding(), true);
+ assertTrue(map.isTransactional());
+ currentTxn.beginTransaction(null);
+ map.put(ONE, ONE);
+ readCheck(map, ONE, ONE);
+ currentTxn.commitTransaction();
+ db.close();
+ }
+
+ public void testExceptions()
+ throws Exception {
+
+ try {
+ currentTxn.commitTransaction();
+ fail();
+ } catch (IllegalStateException expected) {}
+
+ try {
+ currentTxn.abortTransaction();
+ fail();
+ } catch (IllegalStateException expected) {}
+ }
+
+ public void testNested()
+ throws Exception {
+
+ if (!DbCompat.NESTED_TRANSACTIONS) {
+ return;
+ }
+ assertNull(currentTxn.getTransaction());
+
+ Transaction txn1 = currentTxn.beginTransaction(null);
+ assertNotNull(txn1);
+ assertTrue(txn1 == currentTxn.getTransaction());
+
+ assertNull(map.get(ONE));
+ assertNull(map.put(ONE, ONE));
+ assertEquals(ONE, map.get(ONE));
+
+ Transaction txn2 = currentTxn.beginTransaction(null);
+ assertNotNull(txn2);
+ assertTrue(txn2 == currentTxn.getTransaction());
+ assertTrue(txn1 != txn2);
+
+ assertNull(map.put(TWO, TWO));
+ assertEquals(TWO, map.get(TWO));
+
+ Transaction txn3 = currentTxn.beginTransaction(null);
+ assertNotNull(txn3);
+ assertTrue(txn3 == currentTxn.getTransaction());
+ assertTrue(txn1 != txn2);
+ assertTrue(txn1 != txn3);
+ assertTrue(txn2 != txn3);
+
+ assertNull(map.put(THREE, THREE));
+ assertEquals(THREE, map.get(THREE));
+
+ Transaction txn = currentTxn.abortTransaction();
+ assertTrue(txn == txn2);
+ assertTrue(txn == currentTxn.getTransaction());
+ assertNull(map.get(THREE));
+ assertEquals(TWO, map.get(TWO));
+
+ txn3 = currentTxn.beginTransaction(null);
+ assertNotNull(txn3);
+ assertTrue(txn3 == currentTxn.getTransaction());
+ assertTrue(txn1 != txn2);
+ assertTrue(txn1 != txn3);
+ assertTrue(txn2 != txn3);
+
+ assertNull(map.put(THREE, THREE));
+ assertEquals(THREE, map.get(THREE));
+
+ txn = currentTxn.commitTransaction();
+ assertTrue(txn == txn2);
+ assertTrue(txn == currentTxn.getTransaction());
+ assertEquals(THREE, map.get(THREE));
+ assertEquals(TWO, map.get(TWO));
+
+ txn = currentTxn.commitTransaction();
+ assertTrue(txn == txn1);
+ assertTrue(txn == currentTxn.getTransaction());
+ assertEquals(THREE, map.get(THREE));
+ assertEquals(TWO, map.get(TWO));
+ assertEquals(ONE, map.get(ONE));
+
+ txn = currentTxn.commitTransaction();
+ assertNull(txn);
+ assertNull(currentTxn.getTransaction());
+ assertEquals(THREE, map.get(THREE));
+ assertEquals(TWO, map.get(TWO));
+ assertEquals(ONE, map.get(ONE));
+ }
+
+ public void testRunnerCommit()
+ throws Exception {
+
+ commitTest(false);
+ }
+
+ public void testExplicitCommit()
+ throws Exception {
+
+ commitTest(true);
+ }
+
+ private void commitTest(final boolean explicit)
+ throws Exception {
+
+ final TransactionRunner runner = new TransactionRunner(env);
+ runner.setAllowNestedTransactions(DbCompat.NESTED_TRANSACTIONS);
+
+ assertNull(currentTxn.getTransaction());
+
+ runner.run(new TransactionWorker() {
+ public void doWork() throws Exception {
+ final Transaction txn1 = currentTxn.getTransaction();
+ assertNotNull(txn1);
+ assertNull(map.put(ONE, ONE));
+ assertEquals(ONE, map.get(ONE));
+
+ runner.run(new TransactionWorker() {
+ public void doWork() throws Exception {
+ final Transaction txn2 = currentTxn.getTransaction();
+ assertNotNull(txn2);
+ if (DbCompat.NESTED_TRANSACTIONS) {
+ assertTrue(txn1 != txn2);
+ } else {
+ assertTrue(txn1 == txn2);
+ }
+ assertNull(map.put(TWO, TWO));
+ assertEquals(TWO, map.get(TWO));
+ assertEquals(ONE, map.get(ONE));
+ if (DbCompat.NESTED_TRANSACTIONS && explicit) {
+ currentTxn.commitTransaction();
+ }
+ }
+ });
+
+ Transaction txn3 = currentTxn.getTransaction();
+ assertSame(txn1, txn3);
+
+ assertEquals(TWO, map.get(TWO));
+ assertEquals(ONE, map.get(ONE));
+ }
+ });
+
+ assertNull(currentTxn.getTransaction());
+ }
+
+ public void testRunnerAbort()
+ throws Exception {
+
+ abortTest(false);
+ }
+
+ public void testExplicitAbort()
+ throws Exception {
+
+ abortTest(true);
+ }
+
+ private void abortTest(final boolean explicit)
+ throws Exception {
+
+ final TransactionRunner runner = new TransactionRunner(env);
+ runner.setAllowNestedTransactions(DbCompat.NESTED_TRANSACTIONS);
+
+ assertNull(currentTxn.getTransaction());
+
+ runner.run(new TransactionWorker() {
+ public void doWork() throws Exception {
+ final Transaction txn1 = currentTxn.getTransaction();
+ assertNotNull(txn1);
+ assertNull(map.put(ONE, ONE));
+ assertEquals(ONE, map.get(ONE));
+
+ if (DbCompat.NESTED_TRANSACTIONS) {
+ try {
+ runner.run(new TransactionWorker() {
+ public void doWork() throws Exception {
+ final Transaction txn2 =
+ currentTxn.getTransaction();
+ assertNotNull(txn2);
+ assertTrue(txn1 != txn2);
+ assertNull(map.put(TWO, TWO));
+ assertEquals(TWO, map.get(TWO));
+ if (explicit) {
+ currentTxn.abortTransaction();
+ } else {
+ throw new IllegalArgumentException(
+ "test-abort");
+ }
+ }
+ });
+ assertTrue(explicit);
+ } catch (IllegalArgumentException e) {
+ assertTrue(!explicit);
+ assertEquals("test-abort", e.getMessage());
+ }
+ }
+
+ Transaction txn3 = currentTxn.getTransaction();
+ assertSame(txn1, txn3);
+
+ assertEquals(ONE, map.get(ONE));
+ assertNull(map.get(TWO));
+ }
+ });
+
+ assertNull(currentTxn.getTransaction());
+ }
+
+ public void testReadCommittedCollection()
+ throws Exception {
+
+ StoredSortedMap degree2Map = (StoredSortedMap)
+ StoredCollections.configuredSortedMap
+ (map, CursorConfig.READ_COMMITTED);
+
+ // original map is not read-committed
+ assertTrue(!isReadCommitted(map));
+
+ // all read-committed containers are read-uncommitted
+ assertTrue(isReadCommitted(degree2Map));
+ assertTrue(isReadCommitted
+ (StoredCollections.configuredMap
+ (map, CursorConfig.READ_COMMITTED)));
+ assertTrue(isReadCommitted
+ (StoredCollections.configuredCollection
+ (map.values(), CursorConfig.READ_COMMITTED)));
+ assertTrue(isReadCommitted
+ (StoredCollections.configuredSet
+ (map.keySet(), CursorConfig.READ_COMMITTED)));
+ assertTrue(isReadCommitted
+ (StoredCollections.configuredSortedSet
+ ((SortedSet) map.keySet(),
+ CursorConfig.READ_COMMITTED)));
+
+ if (DbCompat.RECNO_METHOD) {
+ // create a list just so we can call configuredList()
+ Database listStore = TestStore.RECNO_RENUM.open(env, "foo");
+ List list = new StoredList(listStore, TestStore.VALUE_BINDING,
+ true);
+ assertTrue(isReadCommitted
+ (StoredCollections.configuredList
+ (list, CursorConfig.READ_COMMITTED)));
+ listStore.close();
+ }
+
+ map.put(ONE, ONE);
+ doReadCommitted(degree2Map, null);
+ }
+
+ private static boolean isReadCommitted(Object container) {
+ StoredContainer storedContainer = (StoredContainer) container;
+ /* We can't use getReadCommitted until is is added to DB core. */
+ return storedContainer.getCursorConfig() != null &&
+ storedContainer.getCursorConfig().getReadCommitted();
+ }
+
+ public void testReadCommittedTransaction()
+ throws Exception {
+
+ TransactionConfig config = new TransactionConfig();
+ config.setReadCommitted(true);
+ doReadCommitted(map, config);
+ }
+
+ private void doReadCommitted(final StoredSortedMap degree2Map,
+ TransactionConfig txnConfig)
+ throws Exception {
+
+ map.put(ONE, ONE);
+ TransactionRunner runner = new TransactionRunner(env);
+ runner.setTransactionConfig(txnConfig);
+ assertNull(currentTxn.getTransaction());
+ runner.run(new TransactionWorker() {
+ public void doWork() throws Exception {
+ assertNotNull(currentTxn.getTransaction());
+
+ /* Do a read-committed get(), the lock is not retained. */
+ assertEquals(ONE, degree2Map.get(ONE));
+
+ /*
+ * If we were not using read-committed, the following write of
+ * key ONE with an auto-commit transaction would self-deadlock
+ * since two transactions in the same thread would be
+ * attempting to lock the same key, one for write and one for
+ * read. This test passes if we do not deadlock.
+ */
+ DatabaseEntry key = new DatabaseEntry();
+ DatabaseEntry value = new DatabaseEntry();
+ testStore.getKeyBinding().objectToEntry(ONE, key);
+ testStore.getValueBinding().objectToEntry(TWO, value);
+ store.put(null, key, value);
+ }
+ });
+ assertNull(currentTxn.getTransaction());
+ }
+
+ public void testReadUncommittedCollection()
+ throws Exception {
+
+ StoredSortedMap dirtyMap = (StoredSortedMap)
+ StoredCollections.configuredSortedMap
+ (map, CursorConfig.READ_UNCOMMITTED);
+
+ // original map is not read-uncommitted
+ assertTrue(!isReadUncommitted(map));
+
+ // all read-uncommitted containers are read-uncommitted
+ assertTrue(isReadUncommitted(dirtyMap));
+ assertTrue(isReadUncommitted
+ (StoredCollections.configuredMap
+ (map, CursorConfig.READ_UNCOMMITTED)));
+ assertTrue(isReadUncommitted
+ (StoredCollections.configuredCollection
+ (map.values(), CursorConfig.READ_UNCOMMITTED)));
+ assertTrue(isReadUncommitted
+ (StoredCollections.configuredSet
+ (map.keySet(), CursorConfig.READ_UNCOMMITTED)));
+ assertTrue(isReadUncommitted
+ (StoredCollections.configuredSortedSet
+ ((SortedSet) map.keySet(), CursorConfig.READ_UNCOMMITTED)));
+
+ if (DbCompat.RECNO_METHOD) {
+ // create a list just so we can call configuredList()
+ Database listStore = TestStore.RECNO_RENUM.open(env, "foo");
+ List list = new StoredList(listStore, TestStore.VALUE_BINDING,
+ true);
+ assertTrue(isReadUncommitted
+ (StoredCollections.configuredList
+ (list, CursorConfig.READ_UNCOMMITTED)));
+ listStore.close();
+ }
+
+ doReadUncommitted(dirtyMap);
+ }
+
+ private static boolean isReadUncommitted(Object container) {
+ StoredContainer storedContainer = (StoredContainer) container;
+ return storedContainer.getCursorConfig() != null &&
+ storedContainer.getCursorConfig().getReadUncommitted();
+ }
+
+ public void testReadUncommittedTransaction()
+ throws Exception {
+
+ TransactionRunner runner = new TransactionRunner(env);
+ TransactionConfig config = new TransactionConfig();
+ config.setReadUncommitted(true);
+ runner.setTransactionConfig(config);
+ assertNull(currentTxn.getTransaction());
+ runner.run(new TransactionWorker() {
+ public void doWork() throws Exception {
+ assertNotNull(currentTxn.getTransaction());
+ doReadUncommitted(map);
+ }
+ });
+ assertNull(currentTxn.getTransaction());
+ }
+
+ /**
+ * Tests that the CurrentTransaction static WeakHashMap does indeed allow
+ * GC to reclaim tine environment when it is closed. At one point this was
+ * not working because the value object in the map has a reference to the
+ * environment. This was fixed by wrapping the Environment in a
+ * WeakReference. [#15444]
+ *
+ * This test only succeeds intermittently, probably due to its reliance
+ * on the GC call.
+ */
+ public void testCurrentTransactionGC()
+ throws Exception {
+
+ /*
+ * This test can have indeterminate results because it depends on
+ * a finalize count, so it's not part of the default run.
+ */
+ if (!SharedTestUtils.runLongTests()) {
+ return;
+ }
+
+ final StringBuffer finalizedFlag = new StringBuffer();
+
+ class MyEnv extends Environment {
+
+ /**
+ * @throws FileNotFoundException from DB core.
+ */
+ MyEnv(File home, EnvironmentConfig config)
+ throws DatabaseException, FileNotFoundException {
+
+ super(home, config);
+ }
+
+ @Override
+ protected void finalize() {
+ finalizedFlag.append('.');
+ }
+ }
+
+ MyEnv myEnv = new MyEnv(env.getHome(), env.getConfig());
+ CurrentTransaction myCurrTxn = CurrentTransaction.getInstance(myEnv);
+
+ store.close();
+ store = null;
+ map = null;
+
+ env.close();
+ env = null;
+
+ myEnv.close();
+ myEnv = null;
+
+ myCurrTxn = null;
+ currentTxn = null;
+
+ for (int i = 0; i < 10; i += 1) {
+ byte[] x = null;
+ try {
+ x = new byte[Integer.MAX_VALUE - 1];
+ } catch (OutOfMemoryError expected) {
+ }
+ assertNull(x);
+ System.gc();
+ }
+
+ for (int i = 0; i < 10; i += 1) {
+ System.gc();
+ }
+
+ assertTrue(finalizedFlag.length() > 0);
+ }
+
+ private synchronized void doReadUncommitted(StoredSortedMap dirtyMap)
+ throws Exception {
+
+ // start thread one
+ ReadUncommittedThreadOne t1 = new ReadUncommittedThreadOne(env, this);
+ t1.start();
+ wait();
+
+ // put ONE
+ synchronized (t1) { t1.notify(); }
+ wait();
+ readCheck(dirtyMap, ONE, ONE);
+ assertTrue(!dirtyMap.isEmpty());
+
+ // abort ONE
+ synchronized (t1) { t1.notify(); }
+ t1.join();
+ readCheck(dirtyMap, ONE, null);
+ assertTrue(dirtyMap.isEmpty());
+
+ // start thread two
+ ReadUncommittedThreadTwo t2 = new ReadUncommittedThreadTwo(env, this);
+ t2.start();
+ wait();
+
+ // put TWO
+ synchronized (t2) { t2.notify(); }
+ wait();
+ readCheck(dirtyMap, TWO, TWO);
+ assertTrue(!dirtyMap.isEmpty());
+
+ // commit TWO
+ synchronized (t2) { t2.notify(); }
+ t2.join();
+ readCheck(dirtyMap, TWO, TWO);
+ assertTrue(!dirtyMap.isEmpty());
+ }
+
+ private static class ReadUncommittedThreadOne extends Thread {
+
+ private final CurrentTransaction currentTxn;
+ private final TransactionTest parent;
+ private final StoredSortedMap map;
+
+ private ReadUncommittedThreadOne(Environment env,
+ TransactionTest parent) {
+
+ this.currentTxn = CurrentTransaction.getInstance(env);
+ this.parent = parent;
+ this.map = parent.map;
+ }
+
+ @Override
+ public synchronized void run() {
+
+ try {
+ assertNull(currentTxn.getTransaction());
+ assertNotNull(currentTxn.beginTransaction(null));
+ assertNotNull(currentTxn.getTransaction());
+ readCheck(map, ONE, null);
+ synchronized (parent) { parent.notify(); }
+ wait();
+
+ // put ONE
+ assertNull(map.put(ONE, ONE));
+ readCheck(map, ONE, ONE);
+ synchronized (parent) { parent.notify(); }
+ wait();
+
+ // abort ONE
+ assertNull(currentTxn.abortTransaction());
+ assertNull(currentTxn.getTransaction());
+ } catch (Exception e) {
+ throw new RuntimeExceptionWrapper(e);
+ }
+ }
+ }
+
+ private static class ReadUncommittedThreadTwo extends Thread {
+
+ private final Environment env;
+ private final CurrentTransaction currentTxn;
+ private final TransactionTest parent;
+ private final StoredSortedMap map;
+
+ private ReadUncommittedThreadTwo(Environment env,
+ TransactionTest parent) {
+
+ this.env = env;
+ this.currentTxn = CurrentTransaction.getInstance(env);
+ this.parent = parent;
+ this.map = parent.map;
+ }
+
+ @Override
+ public synchronized void run() {
+
+ try {
+ final TransactionRunner runner = new TransactionRunner(env);
+ final Object thread = this;
+ assertNull(currentTxn.getTransaction());
+
+ runner.run(new TransactionWorker() {
+ public void doWork() throws Exception {
+ assertNotNull(currentTxn.getTransaction());
+ readCheck(map, TWO, null);
+ synchronized (parent) { parent.notify(); }
+ thread.wait();
+
+ // put TWO
+ assertNull(map.put(TWO, TWO));
+ readCheck(map, TWO, TWO);
+ synchronized (parent) { parent.notify(); }
+ thread.wait();
+
+ // commit TWO
+ }
+ });
+ assertNull(currentTxn.getTransaction());
+ } catch (Exception e) {
+ throw new RuntimeExceptionWrapper(e);
+ }
+ }
+ }
+
+ private static void readCheck(StoredSortedMap checkMap, Object key,
+ Object expect) {
+ if (expect == null) {
+ assertNull(checkMap.get(key));
+ assertTrue(checkMap.tailMap(key).isEmpty());
+ assertTrue(!checkMap.tailMap(key).containsKey(key));
+ assertTrue(!checkMap.keySet().contains(key));
+ assertTrue(checkMap.duplicates(key).isEmpty());
+ Iterator i = checkMap.keySet().iterator();
+ try {
+ while (i.hasNext()) {
+ assertTrue(!key.equals(i.next()));
+ }
+ } finally { StoredIterator.close(i); }
+ } else {
+ assertEquals(expect, checkMap.get(key));
+ assertEquals(expect, checkMap.tailMap(key).get(key));
+ assertTrue(!checkMap.tailMap(key).isEmpty());
+ assertTrue(checkMap.tailMap(key).containsKey(key));
+ assertTrue(checkMap.keySet().contains(key));
+ assertTrue(checkMap.values().contains(expect));
+ assertTrue(!checkMap.duplicates(key).isEmpty());
+ assertTrue(checkMap.duplicates(key).contains(expect));
+ Iterator i = checkMap.keySet().iterator();
+ try {
+ boolean found = false;
+ while (i.hasNext()) {
+ if (expect.equals(i.next())) {
+ found = true;
+ }
+ }
+ assertTrue(found);
+ }
+ finally { StoredIterator.close(i); }
+ }
+ }
+
+ /**
+ * Tests transaction retries performed by TransationRunner.
+ *
+ * This test is too sensitive to how lock conflict detection works on JE to
+ * make it work properly on DB core.
+ */
+
+ /**
+ * Tests transaction retries performed by TransationRunner.
+ *
+ * This test is too sensitive to how lock conflict detection works on JE to
+ * make it work properly on DB core.
+ */
+}
diff --git a/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/serial/CatalogCornerCaseTest.java b/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/serial/CatalogCornerCaseTest.java
new file mode 100644
index 0000000..797a0bb
--- /dev/null
+++ b/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/serial/CatalogCornerCaseTest.java
@@ -0,0 +1,97 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+package com.sleepycat.collections.test.serial;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.sleepycat.bind.serial.StoredClassCatalog;
+import com.sleepycat.compat.DbCompat;
+import com.sleepycat.db.Database;
+import com.sleepycat.db.DatabaseConfig;
+import com.sleepycat.db.Environment;
+import com.sleepycat.util.test.SharedTestUtils;
+import com.sleepycat.util.test.TestEnv;
+
+/**
+ * @author Mark Hayes
+ */
+public class CatalogCornerCaseTest extends TestCase {
+
+ public static void main(String[] args) {
+ junit.framework.TestResult tr =
+ junit.textui.TestRunner.run(suite());
+ if (tr.errorCount() > 0 ||
+ tr.failureCount() > 0) {
+ System.exit(1);
+ } else {
+ System.exit(0);
+ }
+ }
+
+ public static Test suite() {
+ return new TestSuite(CatalogCornerCaseTest.class);
+ }
+
+ private Environment env;
+
+ public CatalogCornerCaseTest(String name) {
+
+ super(name);
+ }
+
+ @Override
+ public void setUp()
+ throws Exception {
+
+ SharedTestUtils.printTestName(getName());
+ env = TestEnv.BDB.open(getName());
+ }
+
+ @Override
+ public void tearDown() {
+
+ try {
+ if (env != null) {
+ env.close();
+ }
+ } catch (Exception e) {
+ System.out.println("Ignored exception during tearDown: " + e);
+ } finally {
+ /* Ensure that GC can cleanup. */
+ env = null;
+ }
+ }
+
+ public void testReadOnlyEmptyCatalog()
+ throws Exception {
+
+ String file = "catalog.db";
+
+ /* Create an empty database. */
+ DatabaseConfig config = new DatabaseConfig();
+ config.setAllowCreate(true);
+ DbCompat.setTypeBtree(config);
+ Database db =
+ DbCompat.testOpenDatabase(env, null, file, null, config);
+ db.close();
+
+ /* Open the empty database read-only. */
+ config.setAllowCreate(false);
+ config.setReadOnly(true);
+ db = DbCompat.testOpenDatabase(env, null, file, null, config);
+
+ /* Expect exception when creating the catalog. */
+ try {
+ new StoredClassCatalog(db);
+ fail();
+ } catch (IllegalStateException e) { }
+ db.close();
+ }
+}
diff --git a/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/serial/StoredClassCatalogTest.java b/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/serial/StoredClassCatalogTest.java
new file mode 100644
index 0000000..f7a8c3f
--- /dev/null
+++ b/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/serial/StoredClassCatalogTest.java
@@ -0,0 +1,177 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+package com.sleepycat.collections.test.serial;
+
+import java.io.ObjectStreamClass;
+import java.util.Map;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.sleepycat.bind.serial.SerialBinding;
+import com.sleepycat.bind.serial.StoredClassCatalog;
+import com.sleepycat.collections.StoredMap;
+import com.sleepycat.collections.TransactionRunner;
+import com.sleepycat.collections.TransactionWorker;
+import com.sleepycat.compat.DbCompat;
+import com.sleepycat.db.Database;
+import com.sleepycat.db.DatabaseConfig;
+import com.sleepycat.db.Environment;
+import com.sleepycat.util.test.SharedTestUtils;
+import com.sleepycat.util.test.TestEnv;
+
+/**
+ * Runs part two of the StoredClassCatalogTest. This part is run with the
+ * new/updated version of TestSerial in the classpath. It uses the
+ * environment and databases created by StoredClassCatalogTestInit. It
+ * verifies that it can read objects serialized using the old class format,
+ * and that it can create new objects with the new class format.
+ *
+ * @author Mark Hayes
+ */
+public class StoredClassCatalogTest extends TestCase
+ implements TransactionWorker {
+
+ static final String CATALOG_FILE = "catalogtest-catalog.db";
+ static final String STORE_FILE = "catalogtest-store.db";
+
+ public static void main(String[] args) {
+ junit.framework.TestResult tr =
+ junit.textui.TestRunner.run(suite());
+ if (tr.errorCount() > 0 ||
+ tr.failureCount() > 0) {
+ System.exit(1);
+ } else {
+ System.exit(0);
+ }
+ }
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite();
+ for (int i = 0; i < TestEnv.ALL.length; i += 1) {
+ suite.addTest(new StoredClassCatalogTest(TestEnv.ALL[i]));
+ }
+ return suite;
+ }
+
+ private TestEnv testEnv;
+ private Environment env;
+ private StoredClassCatalog catalog;
+ private StoredClassCatalog catalog2;
+ private Database store;
+ private Map map;
+ private TransactionRunner runner;
+
+ public StoredClassCatalogTest(TestEnv testEnv) {
+
+ super(makeTestName(testEnv));
+ this.testEnv = testEnv;
+ }
+
+ static String makeTestName(TestEnv testEnv) {
+ return "StoredClassCatalogTest-" + testEnv.getName();
+ }
+
+ @Override
+ public void setUp()
+ throws Exception {
+
+ SharedTestUtils.printTestName(getName());
+ env = testEnv.open(makeTestName(testEnv), false);
+ runner = new TransactionRunner(env);
+
+ catalog = new StoredClassCatalog(openDb(CATALOG_FILE, false));
+ catalog2 = new StoredClassCatalog(openDb("catalog2.db", true));
+
+ SerialBinding keyBinding = new SerialBinding(catalog,
+ String.class);
+ SerialBinding valueBinding = new SerialBinding(catalog,
+ TestSerial.class);
+ store = openDb(STORE_FILE, false);
+
+ map = new StoredMap(store, keyBinding, valueBinding, true);
+ }
+
+ private Database openDb(String file, boolean create)
+ throws Exception {
+
+ DatabaseConfig config = new DatabaseConfig();
+ DbCompat.setTypeBtree(config);
+ config.setTransactional(testEnv.isTxnMode());
+ config.setAllowCreate(create);
+
+ return DbCompat.testOpenDatabase(env, null, file, null, config);
+ }
+
+ @Override
+ public void tearDown() {
+
+ try {
+ if (catalog != null) {
+ catalog.close();
+ catalog.close(); // should have no effect
+ }
+ if (catalog2 != null) {
+ catalog2.close();
+ }
+ if (store != null) {
+ store.close();
+ }
+ if (env != null) {
+ env.close();
+ }
+ } catch (Exception e) {
+ System.err.println("Ignored exception during tearDown: ");
+ e.printStackTrace();
+ } finally {
+ /* Ensure that GC can cleanup. */
+ catalog = null;
+ catalog2 = null;
+ store = null;
+ env = null;
+ testEnv = null;
+ map = null;
+ runner = null;
+ }
+ }
+
+ @Override
+ public void runTest()
+ throws Exception {
+
+ runner.run(this);
+ }
+
+ public void doWork()
+ throws Exception {
+
+ TestSerial one = (TestSerial) map.get("one");
+ TestSerial two = (TestSerial) map.get("two");
+ assertNotNull(one);
+ assertNotNull(two);
+ assertEquals(one, two.getOther());
+ assertNull(one.getStringField());
+ assertNull(two.getStringField());
+
+ TestSerial three = new TestSerial(two);
+ assertNotNull(three.getStringField());
+ map.put("three", three);
+ three = (TestSerial) map.get("three");
+ assertEquals(two, three.getOther());
+
+ ObjectStreamClass desc = ObjectStreamClass.lookup(TestSerial.class);
+
+ assertNotNull(catalog.getClassID(desc));
+ assertNotNull(catalog.getClassID(desc));
+
+ // test with empty catalog
+ assertNotNull(catalog2.getClassID(desc));
+ assertNotNull(catalog2.getClassID(desc));
+ }
+}
diff --git a/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/serial/StoredClassCatalogTestInit.java b/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/serial/StoredClassCatalogTestInit.java
new file mode 100644
index 0000000..96ad345
--- /dev/null
+++ b/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/serial/StoredClassCatalogTestInit.java
@@ -0,0 +1,154 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+package com.sleepycat.collections.test.serial;
+
+import java.util.Map;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.sleepycat.bind.serial.SerialBinding;
+import com.sleepycat.bind.serial.StoredClassCatalog;
+import com.sleepycat.collections.StoredMap;
+import com.sleepycat.collections.TransactionRunner;
+import com.sleepycat.collections.TransactionWorker;
+import com.sleepycat.compat.DbCompat;
+import com.sleepycat.db.Database;
+import com.sleepycat.db.DatabaseConfig;
+import com.sleepycat.db.Environment;
+import com.sleepycat.util.test.SharedTestUtils;
+import com.sleepycat.util.test.TestEnv;
+
+/**
+ * Runs part one of the StoredClassCatalogTest. This part is run with the
+ * old/original version of TestSerial in the classpath. It creates a fresh
+ * environment and databases containing serialized versions of the old class.
+ * When StoredClassCatalogTest is run, it will read these objects from the
+ * database created here.
+ *
+ * @author Mark Hayes
+ */
+public class StoredClassCatalogTestInit extends TestCase
+ implements TransactionWorker {
+
+ static final String CATALOG_FILE = StoredClassCatalogTest.CATALOG_FILE;
+ static final String STORE_FILE = StoredClassCatalogTest.STORE_FILE;
+
+ public static void main(String[] args) {
+ junit.framework.TestResult tr =
+ junit.textui.TestRunner.run(suite());
+ if (tr.errorCount() > 0 ||
+ tr.failureCount() > 0) {
+ System.exit(1);
+ } else {
+ System.exit(0);
+ }
+ }
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite();
+ for (int i = 0; i < TestEnv.ALL.length; i += 1) {
+ suite.addTest(new StoredClassCatalogTestInit(TestEnv.ALL[i]));
+ }
+ return suite;
+ }
+
+ private TestEnv testEnv;
+ private Environment env;
+ private StoredClassCatalog catalog;
+ private Database store;
+ private Map map;
+ private TransactionRunner runner;
+
+ public StoredClassCatalogTestInit(TestEnv testEnv) {
+
+ super("StoredClassCatalogTestInit-" + testEnv.getName());
+ this.testEnv = testEnv;
+ }
+
+ @Override
+ public void setUp()
+ throws Exception {
+
+ SharedTestUtils.printTestName(getName());
+ env = testEnv.open(StoredClassCatalogTest.makeTestName(testEnv));
+ runner = new TransactionRunner(env);
+
+ catalog = new StoredClassCatalog(openDb(CATALOG_FILE));
+
+ SerialBinding keyBinding = new SerialBinding(catalog, String.class);
+ SerialBinding valueBinding =
+ new SerialBinding(catalog, TestSerial.class);
+ store = openDb(STORE_FILE);
+
+ map = new StoredMap(store, keyBinding, valueBinding, true);
+ }
+
+ private Database openDb(String file)
+ throws Exception {
+
+ DatabaseConfig config = new DatabaseConfig();
+ DbCompat.setTypeBtree(config);
+ config.setTransactional(testEnv.isTxnMode());
+ config.setAllowCreate(true);
+
+ return DbCompat.testOpenDatabase(env, null, file, null, config);
+ }
+
+ @Override
+ public void tearDown() {
+
+ try {
+ if (catalog != null) {
+ catalog.close();
+ catalog.close(); // should have no effect
+ }
+ if (store != null) {
+ store.close();
+ }
+ if (env != null) {
+ env.close();
+ }
+ } catch (Exception e) {
+ System.err.println("Ignored exception during tearDown: ");
+ e.printStackTrace();
+ } finally {
+ /* Ensure that GC can cleanup. */
+ catalog = null;
+ store = null;
+ env = null;
+ testEnv = null;
+ map = null;
+ runner = null;
+ }
+ }
+
+ @Override
+ public void runTest()
+ throws Exception {
+
+ runner.run(this);
+ }
+
+ public void doWork() {
+ TestSerial one = new TestSerial(null);
+ TestSerial two = new TestSerial(one);
+ assertNull("Likely the classpath contains the wrong version of the" +
+ " TestSerial class, the 'original' version is required",
+ one.getStringField());
+ assertNull(two.getStringField());
+ map.put("one", one);
+ map.put("two", two);
+ one = (TestSerial) map.get("one");
+ two = (TestSerial) map.get("two");
+ assertEquals(one, two.getOther());
+ assertNull(one.getStringField());
+ assertNull(two.getStringField());
+ }
+}
diff --git a/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/serial/TestSerial.java b/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/serial/TestSerial.java
new file mode 100644
index 0000000..df13cf4
--- /dev/null
+++ b/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/serial/TestSerial.java
@@ -0,0 +1,70 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+package com.sleepycat.collections.test.serial;
+
+/**
+ * @see StoredClassCatalogTest
+ * @author Mark Hayes
+ */
+class TestSerial implements java.io.Serializable {
+
+ static final long serialVersionUID = -3738980000390384920L;
+
+ private int i = 123;
+ private TestSerial other;
+
+ // The following field 's' was added after this class was compiled and
+ // serialized instances were saved in resource files. This allows testing
+ // that the original stored instances can be deserialized after changing
+ // the class. The serialVersionUID is needed for this according to Java
+ // serialization rules, and was generated with the serialver tool.
+ //
+ private String s = "string";
+
+ TestSerial(TestSerial other) {
+
+ this.other = other;
+ }
+
+ TestSerial getOther() {
+
+ return other;
+ }
+
+ int getIntField() {
+
+ return i;
+ }
+
+ String getStringField() {
+
+ return s; // this returned null before field 's' was added.
+ }
+
+ public boolean equals(Object object) {
+
+ try {
+ TestSerial o = (TestSerial) object;
+ if ((o.other == null) ? (this.other != null)
+ : (!o.other.equals(this.other))) {
+ return false;
+ }
+ if (this.i != o.i) {
+ return false;
+ }
+ // the following test was not done before field 's' was added
+ if ((o.s == null) ? (this.s != null)
+ : (!o.s.equals(this.s))) {
+ return false;
+ }
+ return true;
+ } catch (ClassCastException e) {
+ return false;
+ }
+ }
+}
diff --git a/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/serial/TestSerial.java.original b/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/serial/TestSerial.java.original
new file mode 100644
index 0000000..7eef274
--- /dev/null
+++ b/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/serial/TestSerial.java.original
@@ -0,0 +1,72 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+package com.sleepycat.collections.test.serial;
+
+/**
+ * @see StoredClassCatalogTest
+ * @author Mark Hayes
+ */
+class TestSerial implements java.io.Serializable
+{
+ static final long serialVersionUID = -3738980000390384920L;
+
+ private int i = 123;
+ private TestSerial other;
+
+ // The following field 's' was added after this class was compiled and
+ // serialized instances were saved in resource files. This allows testing
+ // that the original stored instances can be deserialized after changing
+ // the class. The serialVersionUID is needed for this according to Java
+ // serialization rules, and was generated with the serialver tool.
+ //
+ //private String s = "string";
+
+ TestSerial(TestSerial other)
+ {
+ this.other = other;
+ }
+
+ TestSerial getOther()
+ {
+ return other;
+ }
+
+ int getIntField()
+ {
+ return i;
+ }
+
+ String getStringField()
+ {
+ return null; // this returned null before field 's' was added.
+ }
+
+ public boolean equals(Object object)
+ {
+ try
+ {
+ TestSerial o = (TestSerial) object;
+ if ((o.other == null) ? (this.other != null)
+ : (!o.other.equals(this.other)))
+ return false;
+ if (this.i != o.i)
+ return false;
+ // the following test was not done before field 's' was added
+ /*
+ if ((o.s == null) ? (this.s != null)
+ : (!o.s.equals(this.s)))
+ return false;
+ */
+ return true;
+ }
+ catch (ClassCastException e)
+ {
+ return false;
+ }
+ }
+}
diff --git a/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/serial/TupleSerialFactoryTest.java b/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/serial/TupleSerialFactoryTest.java
new file mode 100644
index 0000000..d13f851
--- /dev/null
+++ b/db-4.8.30/test/scr024/src/com/sleepycat/collections/test/serial/TupleSerialFactoryTest.java
@@ -0,0 +1,245 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2000-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+package com.sleepycat.collections.test.serial;
+
+import java.util.Map;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.sleepycat.bind.serial.StoredClassCatalog;
+import com.sleepycat.bind.serial.test.MarshalledObject;
+import com.sleepycat.collections.TransactionRunner;
+import com.sleepycat.collections.TransactionWorker;
+import com.sleepycat.collections.TupleSerialFactory;
+import com.sleepycat.compat.DbCompat;
+import com.sleepycat.db.Database;
+import com.sleepycat.db.DatabaseConfig;
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.ForeignKeyDeleteAction;
+import com.sleepycat.db.SecondaryConfig;
+import com.sleepycat.db.SecondaryDatabase;
+import com.sleepycat.util.test.SharedTestUtils;
+import com.sleepycat.util.test.TestEnv;
+
+/**
+ * @author Mark Hayes
+ */
+public class TupleSerialFactoryTest extends TestCase
+ implements TransactionWorker {
+
+ public static void main(String[] args) {
+ junit.framework.TestResult tr =
+ junit.textui.TestRunner.run(suite());
+ if (tr.errorCount() > 0 ||
+ tr.failureCount() > 0) {
+ System.exit(1);
+ } else {
+ System.exit(0);
+ }
+ }
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite();
+ for (int i = 0; i < TestEnv.ALL.length; i += 1) {
+ for (int sorted = 0; sorted < 2; sorted += 1) {
+ suite.addTest(new TupleSerialFactoryTest(TestEnv.ALL[i],
+ sorted != 0));
+ }
+ }
+ return suite;
+ }
+
+ private TestEnv testEnv;
+ private Environment env;
+ private StoredClassCatalog catalog;
+ private TransactionRunner runner;
+ private TupleSerialFactory factory;
+ private Database store1;
+ private Database store2;
+ private SecondaryDatabase index1;
+ private SecondaryDatabase index2;
+ private final boolean isSorted;
+ private Map storeMap1;
+ private Map storeMap2;
+ private Map indexMap1;
+ private Map indexMap2;
+
+ public TupleSerialFactoryTest(TestEnv testEnv, boolean isSorted) {
+
+ super(null);
+
+ this.testEnv = testEnv;
+ this.isSorted = isSorted;
+
+ String name = "TupleSerialFactoryTest-" + testEnv.getName();
+ name += isSorted ? "-sorted" : "-unsorted";
+ setName(name);
+ }
+
+ @Override
+ public void setUp()
+ throws Exception {
+
+ SharedTestUtils.printTestName(getName());
+ env = testEnv.open(getName());
+ runner = new TransactionRunner(env);
+
+ createDatabase();
+ }
+
+ @Override
+ public void tearDown() {
+
+ try {
+ if (index1 != null) {
+ index1.close();
+ }
+ if (index2 != null) {
+ index2.close();
+ }
+ if (store1 != null) {
+ store1.close();
+ }
+ if (store2 != null) {
+ store2.close();
+ }
+ if (catalog != null) {
+ catalog.close();
+ }
+ if (env != null) {
+ env.close();
+ }
+ } catch (Exception e) {
+ System.out.println("Ignored exception during tearDown: " + e);
+ } finally {
+ /* Ensure that GC can cleanup. */
+ index1 = null;
+ index2 = null;
+ store1 = null;
+ store2 = null;
+ catalog = null;
+ env = null;
+ testEnv = null;
+ runner = null;
+ factory = null;
+ storeMap1 = null;
+ storeMap2 = null;
+ indexMap1 = null;
+ indexMap2 = null;
+ }
+ }
+
+ @Override
+ public void runTest()
+ throws Exception {
+
+ runner.run(this);
+ }
+
+ public void doWork() {
+ createViews();
+ writeAndRead();
+ }
+
+ private void createDatabase()
+ throws Exception {
+
+ catalog = new StoredClassCatalog(openDb("catalog.db"));
+ factory = new TupleSerialFactory(catalog);
+ assertSame(catalog, factory.getCatalog());
+
+ store1 = openDb("store1.db");
+ store2 = openDb("store2.db");
+ index1 = openSecondaryDb(factory, "1", store1, "index1.db", null);
+ index2 = openSecondaryDb(factory, "2", store2, "index2.db", store1);
+ }
+
+ private Database openDb(String file)
+ throws Exception {
+
+ DatabaseConfig config = new DatabaseConfig();
+ config.setTransactional(testEnv.isTxnMode());
+ config.setAllowCreate(true);
+ DbCompat.setTypeBtree(config);
+
+ return DbCompat.testOpenDatabase(env, null, file, null, config);
+ }
+
+ private SecondaryDatabase openSecondaryDb(TupleSerialFactory factory,
+ String keyName,
+ Database primary,
+ String file,
+ Database foreignStore)
+ throws Exception {
+
+ SecondaryConfig secConfig = new SecondaryConfig();
+ secConfig.setTransactional(testEnv.isTxnMode());
+ secConfig.setAllowCreate(true);
+ DbCompat.setTypeBtree(secConfig);
+ secConfig.setKeyCreator(factory.getKeyCreator(MarshalledObject.class,
+ keyName));
+ if (foreignStore != null) {
+ secConfig.setForeignKeyDatabase(foreignStore);
+ secConfig.setForeignKeyDeleteAction(
+ ForeignKeyDeleteAction.CASCADE);
+ }
+
+ return DbCompat.testOpenSecondaryDatabase
+ (env, null, file, null, primary, secConfig);
+ }
+
+ private void createViews() {
+ if (isSorted) {
+ storeMap1 = factory.newSortedMap(store1, String.class,
+ MarshalledObject.class, true);
+ storeMap2 = factory.newSortedMap(store2, String.class,
+ MarshalledObject.class, true);
+ indexMap1 = factory.newSortedMap(index1, String.class,
+ MarshalledObject.class, true);
+ indexMap2 = factory.newSortedMap(index2, String.class,
+ MarshalledObject.class, true);
+ } else {
+ storeMap1 = factory.newMap(store1, String.class,
+ MarshalledObject.class, true);
+ storeMap2 = factory.newMap(store2, String.class,
+ MarshalledObject.class, true);
+ indexMap1 = factory.newMap(index1, String.class,
+ MarshalledObject.class, true);
+ indexMap2 = factory.newMap(index2, String.class,
+ MarshalledObject.class, true);
+ }
+ }
+
+ private void writeAndRead() {
+ MarshalledObject o1 = new MarshalledObject("data1", "pk1", "ik1", "");
+ assertNull(storeMap1.put(null, o1));
+
+ assertEquals(o1, storeMap1.get("pk1"));
+ assertEquals(o1, indexMap1.get("ik1"));
+
+ MarshalledObject o2 = new MarshalledObject("data2", "pk2", "", "pk1");
+ assertNull(storeMap2.put(null, o2));
+
+ assertEquals(o2, storeMap2.get("pk2"));
+ assertEquals(o2, indexMap2.get("pk1"));
+
+ /*
+ * store1 contains o1 with primary key "pk1" and index key "ik1"
+ * store2 contains o2 with primary key "pk2" and foreign key "pk1"
+ * which is the primary key of store1
+ */
+
+ storeMap1.remove("pk1");
+ assertNull(storeMap1.get("pk1"));
+ assertNull(indexMap1.get("ik1"));
+ assertNull(storeMap2.get("pk2"));
+ assertNull(indexMap2.get("pk1"));
+ }
+}