summaryrefslogtreecommitdiff
path: root/src/test/java/com/amazon/carbonado
diff options
context:
space:
mode:
authorBrian S. O'Neill <bronee@gmail.com>2006-09-20 04:01:56 +0000
committerBrian S. O'Neill <bronee@gmail.com>2006-09-20 04:01:56 +0000
commitbdb79fe169b556de85d690c088b7f0fbf075778a (patch)
tree0310ed7a52a07ee848e5a0ed992d3bb016ec7473 /src/test/java/com/amazon/carbonado
parentc16716f2d8e15f6539995c656ad05e4e8f799a21 (diff)
Integrated more tests.
Diffstat (limited to 'src/test/java/com/amazon/carbonado')
-rw-r--r--src/test/java/com/amazon/carbonado/TestStorables.java2263
-rw-r--r--src/test/java/com/amazon/carbonado/spi/TestSequenceValueGenerator.java258
-rw-r--r--src/test/java/com/amazon/carbonado/stored/FileInfo.java109
-rw-r--r--src/test/java/com/amazon/carbonado/stored/ManyKeys2.java47
-rw-r--r--src/test/java/com/amazon/carbonado/stored/StorableDateIndex.java46
-rw-r--r--src/test/java/com/amazon/carbonado/stored/StorableSequenced.java67
-rw-r--r--src/test/java/com/amazon/carbonado/stored/StorableTestAssymetric.java38
-rw-r--r--src/test/java/com/amazon/carbonado/stored/StorableTestInvalid.java40
-rw-r--r--src/test/java/com/amazon/carbonado/stored/StorableTestKeyValue.java43
-rw-r--r--src/test/java/com/amazon/carbonado/stored/StorableTestMultiPK.java40
-rw-r--r--src/test/java/com/amazon/carbonado/stored/StorableTimestamped.java38
-rw-r--r--src/test/java/com/amazon/carbonado/stored/StorableVersioned.java50
-rw-r--r--src/test/java/com/amazon/carbonado/stored/StorableVersionedIndexed.java36
-rw-r--r--src/test/java/com/amazon/carbonado/stored/StorableVersionedWithLong.java44
-rw-r--r--src/test/java/com/amazon/carbonado/stored/StorableVersionedWithLongObj.java46
-rw-r--r--src/test/java/com/amazon/carbonado/stored/StorableVersionedWithObj.java46
-rw-r--r--src/test/java/com/amazon/carbonado/stored/Timestamped.java34
17 files changed, 3245 insertions, 0 deletions
diff --git a/src/test/java/com/amazon/carbonado/TestStorables.java b/src/test/java/com/amazon/carbonado/TestStorables.java
new file mode 100644
index 0000000..90048d7
--- /dev/null
+++ b/src/test/java/com/amazon/carbonado/TestStorables.java
@@ -0,0 +1,2263 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.amazon.carbonado;
+
+import java.util.Comparator;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+
+import com.amazon.carbonado.ConstraintException;
+import com.amazon.carbonado.Cursor;
+import com.amazon.carbonado.FetchException;
+import com.amazon.carbonado.FetchNoneException;
+import com.amazon.carbonado.OptimisticLockException;
+import com.amazon.carbonado.PersistException;
+import com.amazon.carbonado.PersistMultipleException;
+import com.amazon.carbonado.PersistNoneException;
+import com.amazon.carbonado.PrimaryKey;
+import com.amazon.carbonado.Query;
+import com.amazon.carbonado.Repository;
+import com.amazon.carbonado.RepositoryException;
+import com.amazon.carbonado.Storable;
+import com.amazon.carbonado.Storage;
+import com.amazon.carbonado.Trigger;
+import com.amazon.carbonado.UniqueConstraintException;
+
+import com.amazon.carbonado.cursor.SortedCursor;
+
+import com.amazon.carbonado.spi.RepairExecutor;
+import com.amazon.carbonado.spi.WrappedSupport;
+
+import com.amazon.carbonado.stored.*;
+
+/**
+ * Runs an extensive set of acceptance tests for a repository. Must be
+ * subclassed to specify a repository to use.
+ *
+ * @author Don Schneider
+ * @author Brian S O'Neill
+ */
+public abstract class TestStorables extends TestCase {
+
+ public static final long sSetId = 0x1L << 0; // 0x0001
+ public static final long sGetStringProp = 0x1L << 1; // 0x0002
+ public static final long sSetStringProp = 0x1L << 2; // 0x0004
+ public static final long sGetIntProp = 0x1L << 3; // 0x0008
+ public static final long sSetIntProp = 0x1L << 4; // 0x0010
+ public static final long sGetLongProp = 0x1L << 5; // 0x0020
+ public static final long sSetLongProp = 0x1L << 6; // 0x0040
+ public static final long sGetDoubleProp = 0x1L << 7; // 0x0080
+ public static final long sSetDoubleProp = 0x1L << 8; // 0x0100
+ public static final long sLoad = 0x1L << 9; // 0x0200
+ public static final long sTryLoad = 0x1L << 10; // 0x0400
+ public static final long sInsert = 0x1L << 11; // 0x0800
+ public static final long sTryInsert = 0x1L << 31; // 0x8000 0000
+ public static final long sUpdate = 0x1L << 32; // 0x0001 0000 0000
+ public static final long sTryUpdate = 0x1L << 12; // 0x1000
+ public static final long sDelete = 0x1L << 33; // 0x0002 0000 0000
+ public static final long sTryDelete = 0x1L << 13; // 0x2000
+ public static final long sStorage = 0x1L << 14; // 0x4000
+ public static final long sCopy = 0x1L << 15; // 0x8000
+ public static final long sToStringKeyOnly = 0x1L << 16; // 0x0001 0000
+ public static final long sGetId = 0x1L << 17; // 0x0002 0000
+ public static final long sCopyAllProperties = 0x1L << 18; // 0x0004 0000
+ public static final long sCopyPrimaryKeyProperties = 0x1L << 19; // 0x0080 0000
+ public static final long sCopyUnequalProperties = 0x1L << 20; // 0x0010 0000
+ public static final long sCopyDirtyProperties = 0x1L << 21; // 0x0020 0000
+ public static final long sHasDirtyProperties = 0x1L << 25; // 0x0040 0000
+ public static final long sEqualKeys = 0x1L << 22; // 0x0080 0000
+ public static final long sEqualProperties = 0x1L << 23; // 0x0100 0000
+ public static final long sCopyVersionProperty = 0x1L << 24; // 0x0200 0000
+ public static final long sMarkPropertiesClean = 0x1L << 26; // 0x0400 0000
+ public static final long sMarkAllPropertiesClean = 0x1L << 27; // 0x0800 0000
+ public static final long sMarkPropertiesDirty = 0x1L << 28; // 0x1000 0000
+ public static final long sMarkAllPropertiesDirty = 0x1L << 29; // 0x2000 0000
+ public static final long sStorableType = 0x1L << 30; // 0x4000 0000
+
+ public static final long ALL_SET_METHODS = // 0x00000155;
+ sSetId + sSetStringProp + sSetIntProp + sSetLongProp + sSetDoubleProp;
+ public static final long ALL_GET_METHODS = // 0x000200AA;
+ sGetId + sGetStringProp + sGetIntProp + sGetLongProp + sGetDoubleProp;
+ public static final long ALL_PRIMARY_KEYS = sSetId; // 0x00000001;
+ public static final long ALL_COPY_PROP_METHODS = // 0x003C0000;
+ sCopyAllProperties + sCopyPrimaryKeyProperties + sCopyUnequalProperties +
+ sCopyDirtyProperties + sCopyVersionProperty;
+ public static final long ALL_INTERFACE_METHODS = // 0x43C1FE00;
+ sLoad + sTryLoad + sInsert + sUpdate + sDelete + sStorage + sCopy + sToStringKeyOnly +
+ ALL_COPY_PROP_METHODS + sHasDirtyProperties + sEqualKeys + sEqualProperties +
+ sMarkPropertiesClean + sMarkPropertiesDirty +
+ sMarkAllPropertiesClean + sMarkAllPropertiesDirty + sStorableType;
+
+ private Repository mRepository;
+ private static int s_Ids = 0;
+
+ public TestStorables(String s) {
+ super(s);
+ }
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ if (mRepository != null) {
+ // The test may have thrown exceptions which cause some
+ // repositories to kick off asynchronous repairs. They will
+ // immediately fail since the repository is about to be
+ // closed. This just eliminates uninteresting errors from being
+ // logged.
+ try {
+ RepairExecutor.waitForRepairsToFinish(10000);
+ }
+ catch (InterruptedException e) {
+ }
+
+ mRepository.close();
+ mRepository = null;
+ }
+ }
+
+ /**
+ * Subclasses must implement this method to specify a repository.
+ */
+ protected abstract Repository newRepository(boolean isMaster)
+ throws RepositoryException;
+
+ /**
+ * provide subsequent access to the repository so the tests can do fancy things if
+ * interested.
+ * @return
+ */
+ protected Repository getRepository() throws RepositoryException {
+ if (mRepository == null) {
+ mRepository = newRepository(true);
+ }
+ return mRepository;
+ }
+
+ /**
+ * Create a random ID to eliminate optimistic lock conflicts due to ID collisions
+ * @param seed
+ * @return
+ */
+ private int generateId(int seed) {
+ return seed*10000 + ((int)System.currentTimeMillis()) + s_Ids++;
+ }
+
+ public void test_createAndRetrieve() throws Exception {
+ Storage<StorableTestBasic> storageSB =
+ getRepository().storageFor(StorableTestBasic.class);
+
+ StorableTestBasic sb = storageSB.prepare();
+ final int id = generateId(0);
+ sb.setId(id);
+ sb.setIntProp(1);
+ sb.setLongProp(1);
+ sb.setDoubleProp(1.1);
+ sb.setStringProp("one");
+ sb.setDate(new DateTime("2005-08-26T08:09:00.000"));
+ sb.insert();
+
+ StorableTestBasic sb_load = storageSB.prepare();
+ sb_load.setId(id);
+ sb_load.load();
+ assertEquals(sb, sb_load);
+
+ // Try re-inserting
+ // First, make sure the system disallows setting pk for loaded object
+ try {
+ sb.setId(id);
+ fail("successfully set pk on loaded object");
+ }
+ catch (Exception e) {
+ }
+
+ // Then the more common way: just create an identical new one
+ StorableTestBasic sbr = storageSB.prepare();
+ sbr.setId(id);
+ sbr.setIntProp(1);
+ sbr.setLongProp(1);
+ sbr.setDoubleProp(1.1);
+ sbr.setStringProp("one");
+ sb.setDate(new DateTime("2005-08-26T08:09:00.000"));
+ try {
+ sbr.insert();
+ fail("PK constraint violation ignored");
+ }
+ catch (UniqueConstraintException e) {
+ }
+
+
+ Storage<StorableTestMultiPK> storageMPK =
+ getRepository().storageFor(StorableTestMultiPK.class);
+ StorableTestMultiPK smpk = storageMPK.prepare();
+ smpk.setIdPK(0);
+ smpk.setStringPK("zero");
+ smpk.setStringData("and some data");
+ smpk.insert();
+
+ StorableTestMultiPK smpk_load = storageMPK.prepare();
+ smpk_load.setIdPK(0);
+ smpk_load.setStringPK("zero");
+ smpk_load.load();
+ assertEquals(smpk, smpk_load);
+ }
+
+ public void test_storableStorableStates() throws Exception {
+
+ Storage<StorableTestKeyValue> storageMinimal =
+ getRepository().storageFor(StorableTestKeyValue.class);
+
+ // Start by just putting some targets in the repository
+ for (int i = 0; i < 10; i++) {
+ insert(storageMinimal, 100+i, 200+i);
+ }
+
+ StorableTestKeyValue s = storageMinimal.prepare();
+ StorableTestKeyValue s2 = storageMinimal.prepare();
+
+ // State: unloaded
+ // pk incomplete
+ assertInoperable(s, "new - untouched");
+ assertFalse(s.hasDirtyProperties());
+
+ // new --set(pk)--> loadable incomplete
+ s.setKey1(0);
+ assertInoperable(s, "Loadable Incomplete");
+ assertFalse(s.hasDirtyProperties());
+
+ s.setKey1(101);
+ assertInoperable(s, "loadable incomplete (2nd)");
+ assertFalse(s.hasDirtyProperties());
+
+ // loadable incomplete --pkFilled--> loadable ready
+ s.setKey2(201);
+ assertEquals(true, s.tryDelete());
+ assertFalse(s.hasDirtyProperties());
+
+ s.setKey1(102);
+ s.setKey2(202);
+ assertEquals(true, s.tryDelete());
+ assertFalse(s.hasDirtyProperties());
+
+ // loadable ready --load()--> loaded
+ s.setKey1(103);
+ s.setKey2(203);
+ s.load();
+ assertEquals(s.getValue1(), 1030);
+ assertEquals(s.getValue2(), 20300);
+ assertNoInsert(s, "written");
+ assertNoSetPK(s, "written");
+ assertFalse(s.hasDirtyProperties());
+
+ s2.setKey1(103);
+ s2.setKey2(203);
+ assertFalse(s2.hasDirtyProperties());
+ s2.load();
+ assertFalse(s2.hasDirtyProperties());
+
+ assertTrue(s.equalPrimaryKeys(s2));
+ assertTrue(s.equalProperties(s2));
+ assertEquals(s.storableType(), s2.storableType());
+ assertTrue(s.equals(s2));
+ assertEquals(s, s2);
+ s.setValue1(11);
+ s.setValue2(11111);
+ assertEquals(true, s.tryUpdate());
+ assertEquals(11, s.getValue1());
+ assertEquals(11111, s.getValue2());
+ s2.load();
+ assertEquals(s, s2);
+
+ StorableTestKeyValue s3 = storageMinimal.prepare();
+ s.copyPrimaryKeyProperties(s3);
+ s3.tryUpdate();
+ assertEquals(s, s3);
+
+ s.setValue2(222222);
+ assertTrue(s.hasDirtyProperties());
+ s.load();
+ assertFalse(s.hasDirtyProperties());
+ assertEquals(s, s2);
+
+ // Update should return true, even though it probably didn't actually
+ // touch the storage layer.
+ assertEquals(true, s.tryUpdate());
+
+ s.tryDelete();
+ assertNoLoad(s, "deleted");
+ // After delete, saved properties remain dirty.
+ assertTrue(s.hasDirtyProperties());
+
+ s.insert();
+ assertFalse(s.hasDirtyProperties());
+ s.load();
+ assertFalse(s.hasDirtyProperties());
+ assertEquals(s, s2);
+ }
+
+ public void test_storableInteractions() throws Exception {
+ Storage<StorableTestBasic> storage = getRepository().storageFor(StorableTestBasic.class);
+ StorableTestBasic s = storage.prepare();
+ final int id = generateId(111);
+ s.setId(id);
+ s.initBasicProperties();
+ assertTrue(s.hasDirtyProperties());
+ s.insert();
+ assertFalse(s.hasDirtyProperties());
+
+ StorableTestBasic s2 = storage.prepare();
+ s2.setId(id);
+ s2.load();
+ assertTrue(s2.equalPrimaryKeys(s));
+ assertTrue(s2.equalProperties(s));
+ assertTrue(s2.equals(s));
+
+ StorableTestBasic s3 = storage.prepare();
+ s3.setId(id);
+ s3.tryDelete();
+
+ // Should be able to re-insert.
+ s2.insert();
+ s2.load();
+ assertTrue(s2.equalPrimaryKeys(s));
+ assertTrue(s2.equalProperties(s));
+ assertTrue(s2.equals(s));
+
+ // Delete in preparation for next test.
+ s3.tryDelete();
+
+ s.setStringProp("updated by s");
+ // The object is gone, we can't update it
+ assertEquals(false, s.tryUpdate());
+ // ...or load it
+ try {
+ s2.load();
+ fail("shouldn't be able to load a deleted object");
+ }
+ catch (FetchException e) {
+ }
+ }
+
+ public void test_assymetricStorable() throws Exception {
+ try {
+ Storage<StorableTestAssymetric> storage =
+ getRepository().storageFor(StorableTestAssymetric.class);
+ }
+ catch (Exception e) {
+ fail("exception creating storable with assymetric concrete getter?" + e);
+ }
+ try {
+ Storage<StorableTestAssymetricGet> storage =
+ getRepository().storageFor(StorableTestAssymetricGet.class);
+ fail("Created assymetric storabl`e");
+ }
+ catch (Exception e) {
+ }
+ try {
+ Storage<StorableTestAssymetricSet> storage =
+ getRepository().storageFor(StorableTestAssymetricSet.class);
+ fail("Created assymetric storable");
+ }
+ catch (Exception e) {
+ }
+ try {
+ Storage<StorableTestAssymetricGetSet> storage =
+ getRepository().storageFor(StorableTestAssymetricGetSet.class);
+ fail("Created assymetric storable");
+ }
+ catch (Exception e) {
+ }
+ }
+
+ private void assertNoSetPK(StorableTestKeyValue aStorableTestKeyValue,
+ String aState)
+ {
+ try {
+ aStorableTestKeyValue.setKey1(1111);
+ fail("'set pk' for '" + aState + "' succeeded");
+ }
+ catch (Exception e) {
+ }
+ }
+
+ private void assertInoperable(Storable aStorable, String aState) {
+ assertNoInsert(aStorable, aState);
+ assertNoUpdate(aStorable, aState);
+ assertNoLoad(aStorable, aState);
+ assertNoDelete(aStorable, aState);
+ }
+
+ private void assertNoDelete(Storable aStorable, String aState) {
+ try {
+ aStorable.tryDelete();
+ fail("'delete' for '" + aState + "' succeeded");
+ }
+ catch (PersistException e) {
+ }
+ catch (IllegalStateException e) {
+ }
+ }
+
+ private void assertNoLoad(Storable aStorable, String aState) {
+ try {
+ aStorable.load();
+ fail("'load' for '" + aState + "' succeeded");
+ }
+ catch (FetchException e) {
+ }
+ catch (IllegalStateException e) {
+ }
+ }
+
+ private void assertNoInsert(Storable aStorable, String aState) {
+ try {
+ aStorable.insert();
+ fail("'insert' for '" + aState + "' succeeded");
+ }
+ catch (PersistException e) {
+ }
+ catch (IllegalStateException e) {
+ }
+ }
+
+ private void assertNoUpdate(Storable aStorable, String aState) {
+ try {
+ aStorable.tryUpdate();
+ fail("'update' for '" + aState + "' succeeded");
+ }
+ catch (PersistException e) {
+ }
+ catch (IllegalStateException e) {
+ }
+ }
+
+ private void insert(Storage<StorableTestKeyValue> aStorageMinimal,
+ int key1,
+ int key2) throws PersistException
+ {
+ StorableTestKeyValue s = aStorageMinimal.prepare();
+ s.setKey1(key1);
+ s.setKey2(key2);
+ s.setValue1(key1*10);
+ s.setValue2(key2*100);
+ s.insert();
+ }
+
+ public void test_copyStorableProperties() throws Exception {
+ Storage<StorableTestBasic> storage = getRepository().storageFor(StorableTestBasic.class);
+ StorableTestBasic storable = storage.prepare();
+
+ InvocationTracker tracker = new InvocationTracker("tracker", false);
+ storable.copyAllProperties(tracker);
+ // unloaded, untouched; nothing happens
+ tracker.assertTrack(0);
+
+ storable.setId(generateId(1));
+ storable.setIntProp(1);
+ storable.copyAllProperties(tracker);
+ tracker.assertTrack(0x1 + 0x10);
+ tracker.clearTracks();
+
+ storable.initBasicProperties();
+ storable.copyAllProperties(tracker);
+ tracker.assertTrack(ALL_SET_METHODS);
+ tracker.clearTracks();
+
+ storable = storage.prepare();
+ storable.copyPrimaryKeyProperties(tracker);
+ tracker.assertTrack(0);
+ storable.initPrimaryKeyProperties();
+ storable.copyPrimaryKeyProperties(tracker);
+ tracker.assertTrack(ALL_PRIMARY_KEYS);
+ tracker.clearTracks();
+
+ storable = storage.prepare();
+ storable.copyUnequalProperties(tracker);
+ tracker.assertTrack(0);
+ storable.setIntProp(0); // this will now be dirty, and equal
+ storable.copyUnequalProperties(tracker);
+ tracker.assertTrack(0x8);
+ storable.setIntProp(1); // this will now be dirty and not equal
+ storable.copyUnequalProperties(tracker);
+ tracker.assertTrack(0x8 | 0x10);
+
+ // get a fresh one
+ storable = storage.prepare();
+ storable.setStringProp("hi");
+ storable.setId(22);
+ storable.copyPrimaryKeyProperties(tracker);
+ storable.copyDirtyProperties(tracker);
+ tracker.assertTrack(0x05);
+ }
+
+ public void test_copy() throws Exception {
+ Storage<StorableTestBasic> storage = getRepository().storageFor(StorableTestBasic.class);
+ StorableTestBasic storable = storage.prepare();
+
+ storable.setId(5);
+ storable.setStringProp("hello");
+ storable.setIntProp(512);
+ storable.setLongProp(45354L);
+ storable.setDoubleProp(56734.234);
+
+ Storable copy = storable.copy();
+
+ assertEquals(storable.getClass(), copy.getClass());
+ assertEquals(storable, copy);
+
+ StorableTestBasic castedCopy = (StorableTestBasic) copy;
+
+ assertEquals(storable.getId(), castedCopy.getId());
+ assertEquals(storable.getStringProp(), castedCopy.getStringProp());
+ assertEquals(storable.getIntProp(), castedCopy.getIntProp());
+ assertEquals(storable.getLongProp(), castedCopy.getLongProp());
+ assertEquals(storable.getDoubleProp(), castedCopy.getDoubleProp());
+ }
+
+ public void test_invalidStorables() throws Exception {
+ try {
+ getRepository().storageFor(StorableTestInvalid.class);
+ fail("prepared invalid storable");
+ }
+ catch (RepositoryException e) {
+ }
+ }
+
+ public void test_invalidPatterns() throws Exception {
+ // Minimal -- try setting with no PK
+ Storage<StorableTestMinimal> storageMinimal =
+ getRepository().storageFor(StorableTestMinimal.class);
+ StorableTestMinimal s = storageMinimal.prepare();
+ assertNoInsert(s, "new (minimal)");
+
+ s.setId(generateId(0));
+ s.insert();
+
+ // Basic -- try combinations of PK, fields
+ // First, fill in all the fields but no PK
+ Storage<StorableTestBasic> storageBasic =
+ getRepository().storageFor(StorableTestBasic.class);
+ StorableTestBasic sb = storageBasic.prepare();
+ assertNoInsert(sb, "new (basic)");;
+
+ sb.setIntProp(0);
+ sb.setIntProp(1);
+ sb.setLongProp(1);
+ sb.setDoubleProp(1.1);
+ sb.setStringProp("one");
+ sb.setDate(new DateTime("2005-08-26T08:09:00.000"));
+ assertNoInsert(sb, "SB: Storable incomplete (pkMissing)");
+
+ sb.setId(generateId(2));
+ sb.insert();
+
+ // Now try leaving one of the fields empty.
+ sb = storageBasic.prepare();
+ final int id = generateId(3);
+ sb.setId(id);
+ sb.setIntProp(0);
+ sb.setIntProp(1);
+ sb.setLongProp(1);
+ sb.setDoubleProp(1.1);
+ try {
+ sb.insert();
+ fail();
+ } catch (ConstraintException e) {
+ }
+
+ sb = storageBasic.prepare();
+ sb.setId(id);
+ try {
+ sb.load();
+ fail();
+ } catch (FetchNoneException e) {
+ }
+ }
+
+ public void test_nonDestructiveUpdate() throws Exception {
+ Storage<StorableTestBasic> storage = getRepository().storageFor(StorableTestBasic.class);
+ StorableTestBasic s = storage.prepare();
+
+ int id = generateId(3943945);
+ s.setId(id);
+ s.setStringProp("hello");
+ s.setIntProp(56);
+ s.setLongProp(99999999999999999L);
+ s.setDoubleProp(Double.NaN);
+
+ s.insert();
+
+ s = storage.prepare();
+
+ s.setId(id);
+ s.setIntProp(100);
+ assertEquals(true, s.tryUpdate());
+
+ assertEquals("hello", s.getStringProp());
+ assertEquals(100, s.getIntProp());
+ assertEquals(99999999999999999L, s.getLongProp());
+ assertEquals(Double.NaN, s.getDoubleProp());
+
+ s = storage.prepare();
+
+ s.setId(id);
+ s.load();
+
+ assertEquals("hello", s.getStringProp());
+ assertEquals(100, s.getIntProp());
+ assertEquals(99999999999999999L, s.getLongProp());
+ assertEquals(Double.NaN, s.getDoubleProp());
+ }
+
+ public void test_updateLoadSideEffect() throws Exception {
+ Storage<StorableTestBasic> storage = getRepository().storageFor(StorableTestBasic.class);
+
+ StorableTestBasic s = storage.prepare();
+ final int id = generateId(500);
+ s.setId(id);
+ s.setStringProp("hello");
+ s.setIntProp(10);
+ s.setLongProp(123456789012345L);
+ s.setDoubleProp(Double.POSITIVE_INFINITY);
+ s.insert();
+
+ s = storage.prepare();
+ s.setId(id);
+
+ assertEquals(id, s.getId());
+ assertEquals(0, s.getIntProp());
+ assertEquals(0L, s.getLongProp());
+ assertEquals(0.0, s.getDoubleProp());
+
+ assertFalse(s.hasDirtyProperties());
+
+ // Even if nothing was updated, must load fresh copy.
+ assertTrue(s.tryUpdate());
+ assertEquals(id, s.getId());
+ assertEquals(10, s.getIntProp());
+ assertEquals(123456789012345L, s.getLongProp());
+ assertEquals(Double.POSITIVE_INFINITY, s.getDoubleProp());
+ }
+
+ public void test_versioning() throws Exception {
+ Storage<StorableVersioned> storage = getRepository().storageFor(StorableVersioned.class);
+
+ StorableVersioned s = storage.prepare();
+ s.setID(500);
+ s.setValue("hello");
+ try {
+ // Require version property to be set.
+ s.tryUpdate();
+ fail();
+ } catch (IllegalStateException e) {
+ }
+
+ s.setVersion(1);
+ try {
+ // Cannot update that which does not exist.
+ s.update();
+ fail();
+ } catch (PersistNoneException e) {
+ }
+
+ s.insert();
+
+ s.setVersion(2);
+ try {
+ // Record version mismatch.
+ s.tryUpdate();
+ fail();
+ } catch (OptimisticLockException e) {
+ }
+
+ s.setVersion(1);
+ s.setValue("world");
+ s.tryUpdate();
+
+ assertEquals(2, s.getVersion());
+ assertEquals("world", s.getValue());
+
+ // Since no properties changed, update does not increase version.
+ assertEquals(true, s.tryUpdate());
+ assertEquals(2, s.getVersion());
+
+ // Simple test to ensure that version property doesn't need to be
+ // dirtied.
+ s = storage.prepare();
+ s.setID(500);
+ s.load();
+ s.setValue("hello");
+ assertTrue(s.tryUpdate());
+ }
+
+ public void test_versioningWithLong() throws Exception {
+ Storage<StorableVersionedWithLong> storage =
+ getRepository().storageFor(StorableVersionedWithLong.class);
+
+ StorableVersionedWithLong s = storage.prepare();
+ s.setID(500);
+ s.setValue("hello");
+ try {
+ // Require version property to be set.
+ s.tryUpdate();
+ fail();
+ } catch (IllegalStateException e) {
+ }
+
+ s.setVersion(1);
+ try {
+ // Cannot update that which does not exist.
+ s.update();
+ fail();
+ } catch (PersistNoneException e) {
+ }
+
+ s.insert();
+
+ s.setVersion(2);
+ try {
+ // Record version mismatch.
+ s.tryUpdate();
+ fail();
+ } catch (OptimisticLockException e) {
+ }
+
+ s.setVersion(1);
+ s.setValue("world");
+ s.tryUpdate();
+
+ assertEquals(2, s.getVersion());
+ assertEquals("world", s.getValue());
+
+ // Since no properties changed, update does not increase version.
+ assertEquals(true, s.tryUpdate());
+ assertEquals(2, s.getVersion());
+ }
+
+ public void test_versioningWithObj() throws Exception {
+ Storage<StorableVersionedWithObj> storage =
+ getRepository().storageFor(StorableVersionedWithObj.class);
+
+ StorableVersionedWithObj s = storage.prepare();
+ s.setID(generateId(500));
+ s.setValue("hello");
+ try {
+ // Require version property to be set.
+ s.tryUpdate();
+ fail();
+ } catch (IllegalStateException e) {
+ }
+
+ s.setVersion(null);
+ try {
+ // Cannot update that which does not exist.
+ s.update();
+ fail();
+ } catch (PersistNoneException e) {
+ }
+
+ s.insert();
+
+ assertNull(s.getVersion());
+
+ s.setVersion(2);
+ try {
+ // Record version mismatch.
+ s.tryUpdate();
+ fail();
+ } catch (OptimisticLockException e) {
+ }
+
+ s.setVersion(null);
+ s.setValue("world");
+ s.tryUpdate();
+
+ assertEquals((Integer) 1, s.getVersion());
+ assertEquals("world", s.getValue());
+
+ s.setValue("value");
+ s.tryUpdate();
+
+ assertEquals((Integer) 2, s.getVersion());
+ assertEquals("value", s.getValue());
+
+ // Since no properties changed, update does not increase version.
+ assertEquals(true, s.tryUpdate());
+ assertEquals((Integer) 2, s.getVersion());
+ }
+
+ public void test_versioningWithLongObj() throws Exception {
+ Storage<StorableVersionedWithLongObj> storage =
+ getRepository().storageFor(StorableVersionedWithLongObj.class);
+
+ StorableVersionedWithLongObj s = storage.prepare();
+ s.setID(500);
+ s.setValue("hello");
+ try {
+ // Require version property to be set.
+ s.tryUpdate();
+ fail();
+ } catch (IllegalStateException e) {
+ }
+
+ s.setVersion(null);
+ try {
+ // Cannot update that which does not exist.
+ s.update();
+ fail();
+ } catch (PersistNoneException e) {
+ }
+
+ s.insert();
+
+ assertNull(s.getVersion());
+
+ s.setVersion(2L);
+ try {
+ // Record version mismatch.
+ s.tryUpdate();
+ fail();
+ } catch (OptimisticLockException e) {
+ }
+
+ s.setVersion(null);
+ s.setValue("world");
+ s.tryUpdate();
+
+ assertEquals((Long) 1L, s.getVersion());
+ assertEquals("world", s.getValue());
+
+ s.setValue("value");
+ s.tryUpdate();
+
+ assertEquals((Long) 2L, s.getVersion());
+ assertEquals("value", s.getValue());
+
+ // Since no properties changed, update does not increase version.
+ assertEquals(true, s.tryUpdate());
+ assertEquals((Long) 2L, s.getVersion());
+ }
+
+ public void test_initialVersion() throws Exception {
+ Storage<StorableVersioned> storage = getRepository().storageFor(StorableVersioned.class);
+
+ StorableVersioned s = storage.prepare();
+ s.setID(987);
+ s.setValue("hello");
+ assertEquals(0, s.getVersion());
+ s.insert();
+ assertEquals(1, s.getVersion());
+
+ s = storage.prepare();
+ s.setID(12345);
+ s.setValue("world");
+ assertEquals(0, s.getVersion());
+ s.setVersion(56);
+ assertEquals(56, s.getVersion());
+ s.insert();
+ assertEquals(56, s.getVersion());
+ }
+
+ public void test_initialVersionWithLong() throws Exception {
+ Storage<StorableVersionedWithLong> storage =
+ getRepository().storageFor(StorableVersionedWithLong.class);
+
+ StorableVersionedWithLong s = storage.prepare();
+ s.setID(987);
+ s.setValue("hello");
+ assertEquals(0, s.getVersion());
+ s.insert();
+ assertEquals(1, s.getVersion());
+
+ s = storage.prepare();
+ s.setID(12345);
+ s.setValue("world");
+ assertEquals(0, s.getVersion());
+ s.setVersion(56);
+ assertEquals(56, s.getVersion());
+ s.insert();
+ assertEquals(56, s.getVersion());
+ }
+
+ public void test_initialVersionWithObj() throws Exception {
+ Storage<StorableVersionedWithObj> storage =
+ getRepository().storageFor(StorableVersionedWithObj.class);
+
+ StorableVersionedWithObj s = storage.prepare();
+ s.setID(987);
+ s.setValue("hello");
+ assertNull(s.getVersion());
+ s.insert();
+ assertEquals((Integer) 1, s.getVersion());
+
+ s = storage.prepare();
+ s.setID(12345);
+ s.setValue("world");
+ assertNull(s.getVersion());
+ s.setVersion(56);
+ assertEquals((Integer) 56, s.getVersion());
+ s.insert();
+ assertEquals((Integer) 56, s.getVersion());
+ }
+
+ public void test_initialVersionWithLongObj() throws Exception {
+ Storage<StorableVersionedWithLongObj> storage =
+ getRepository().storageFor(StorableVersionedWithLongObj.class);
+
+ StorableVersionedWithLongObj s = storage.prepare();
+ s.setID(987);
+ s.setValue("hello");
+ assertNull(s.getVersion());
+ s.insert();
+ assertEquals((Long) 1L, s.getVersion());
+
+ s = storage.prepare();
+ s.setID(12345);
+ s.setValue("world");
+ assertNull(s.getVersion());
+ s.setVersion(56L);
+ assertEquals((Long) 56L, s.getVersion());
+ s.insert();
+ assertEquals((Long) 56L, s.getVersion());
+ }
+
+ public void test_versioningMissingRecord() throws Exception {
+ Storage<StorableVersioned> storage = getRepository().storageFor(StorableVersioned.class);
+
+ StorableVersioned s = storage.prepare();
+ s.setID(500);
+ s.setValue("hello");
+ s.insert();
+
+ // Now delete it from under our feet.
+ StorableVersioned s2 = storage.prepare();
+ s2.setID(500);
+ s2.delete();
+
+ s.setValue("world");
+ s.tryUpdate();
+
+ s.insert();
+
+ // Delete it again.
+ s2.delete();
+
+ // Update without changing and properties must still reload, which should fail.
+ assertFalse(s.tryUpdate());
+ }
+
+ public void test_versioningDisabled() throws Exception {
+ // Make sure repository works properly when configured as non-master.
+ Repository repo = newRepository(false);
+ Storage<StorableVersioned> storage = repo.storageFor(StorableVersioned.class);
+
+ StorableVersioned s = storage.prepare();
+ s.setID(500);
+ s.setValue("hello");
+ try {
+ // Require version property to be set.
+ s.tryUpdate();
+ fail();
+ } catch (IllegalStateException e) {
+ }
+
+ s.setVersion(1);
+ assertEquals(false, s.tryUpdate());
+
+ s.insert();
+
+ s.setVersion(2);
+ assertEquals(true, s.tryUpdate());
+
+ s.setVersion(1);
+ s.setValue("world");
+ s.tryUpdate();
+
+ assertEquals(1, s.getVersion());
+ assertEquals("world", s.getValue());
+
+ RepairExecutor.waitForRepairsToFinish(10000);
+
+ repo.close();
+ repo = null;
+ }
+
+ public void test_sequences() throws Exception {
+ Storage<StorableSequenced> storage = getRepository().storageFor(StorableSequenced.class);
+
+ StorableSequenced seq = storage.prepare();
+ seq.setData("hello");
+ seq.insert();
+
+ assertEquals(1L, seq.getID());
+ assertEquals(1, seq.getSomeInt());
+ assertEquals(Integer.valueOf(1), seq.getSomeIntegerObj());
+ assertEquals(1L, seq.getSomeLong());
+ assertEquals(Long.valueOf(1L), seq.getSomeLongObj());
+ assertEquals("1", seq.getSomeString());
+ assertEquals("hello", seq.getData());
+
+ seq = storage.prepare();
+ seq.setData("foo");
+ seq.insert();
+
+ assertEquals(2L, seq.getID());
+ assertEquals(2, seq.getSomeInt());
+ assertEquals(Integer.valueOf(2), seq.getSomeIntegerObj());
+ assertEquals(2L, seq.getSomeLong());
+ assertEquals(Long.valueOf(2L), seq.getSomeLongObj());
+ assertEquals("2", seq.getSomeString());
+ assertEquals("foo", seq.getData());
+
+ seq = storage.prepare();
+ seq.setSomeInt(100);
+ seq.setSomeLongObj(null);
+ seq.setData("data");
+ seq.insert();
+
+ assertEquals(3L, seq.getID());
+ assertEquals(100, seq.getSomeInt());
+ assertEquals(null, seq.getSomeLongObj());
+
+ seq = storage.prepare();
+ seq.setData("world");
+ seq.insert();
+
+ assertEquals(4L, seq.getID());
+ assertEquals(3, seq.getSomeInt());
+ assertEquals(Integer.valueOf(4), seq.getSomeIntegerObj());
+ assertEquals(4L, seq.getSomeLong());
+ assertEquals(Long.valueOf(3L), seq.getSomeLongObj());
+ assertEquals("4", seq.getSomeString());
+ }
+
+ public void test_oldIndexEntryDeletion() throws Exception {
+ // Very simple test that ensures that old index entries are deleted
+ // when the master record is updated. There is no guarantee that the
+ // chosen repository supports indexes, and there is no guarantee that
+ // it is selecting the desired index. Since the index set is simple and
+ // so are the queries, I think it is safe to assume that the selected
+ // index is what I expect it to be. Repositories that support
+ // custom indexing should have more rigorous tests.
+
+ Storage<StorableTestBasicIndexed> storage =
+ getRepository().storageFor(StorableTestBasicIndexed.class);
+
+ StorableTestBasicIndexed s = storage.prepare();
+ final int id1 = generateId(1);
+ s.setId(id1);
+ s.setStringProp("hello");
+ s.setIntProp(3);
+ s.setLongProp(4);
+ s.setDoubleProp(5);
+ s.insert();
+
+ s = storage.prepare();
+ final int id6 = generateId(6);
+ s.setId(id6);
+ s.setStringProp("hello");
+ s.setIntProp(8);
+ s.setLongProp(9);
+ s.setDoubleProp(10);
+ s.insert();
+
+ s = storage.prepare();
+ final int id11 = generateId(11);
+ s.setId(id11);
+ s.setStringProp("world");
+ s.setIntProp(3);
+ s.setLongProp(14);
+ s.setDoubleProp(15);
+ s.insert();
+
+ // First verify that queries report what we expect. Don't perform an
+ // orderBy on query, as that might interfere with index selection.
+ Query<StorableTestBasicIndexed> q = storage.query("stringProp = ?").with("hello");
+ List<StorableTestBasicIndexed> list = q.fetch().toList();
+ assertEquals(2, list.size());
+ if (list.get(0).getId() == id1) {
+ assertEquals(id6, list.get(1).getId());
+ } else {
+ assertEquals(id6, list.get(0).getId());
+ }
+
+ q = storage.query("stringProp = ?").with("world");
+ list = q.fetch().toList();
+ assertEquals(1, list.size());
+ assertEquals(id11, list.get(0).getId());
+
+ q = storage.query("intProp = ?").with(3);
+ list = q.fetch().toList();
+ assertEquals(2, list.size());
+ if (list.get(0).getId() == id1) {
+ assertEquals(id11, list.get(1).getId());
+ } else {
+ assertEquals(id11, list.get(0).getId());
+ }
+
+ // Now update and verify changes to query results.
+ s = storage.prepare();
+ s.setId(id1);
+ s.load();
+ s.setStringProp("world");
+ s.tryUpdate();
+
+ q = storage.query("stringProp = ?").with("hello");
+ list = q.fetch().toList();
+ assertEquals(1, list.size());
+ assertEquals(id6, list.get(0).getId());
+
+ q = storage.query("stringProp = ?").with("world");
+ list = q.fetch().toList();
+ assertEquals(2, list.size());
+ if (list.get(0).getId() == id1) {
+ assertEquals(id11, list.get(1).getId());
+ } else {
+ assertEquals(id11, list.get(0).getId());
+ }
+
+ q = storage.query("intProp = ?").with(3);
+ list = q.fetch().toList();
+ assertEquals(2, list.size());
+ if (list.get(0).getId() == id1) {
+ assertEquals(id11, list.get(1).getId());
+ } else {
+ assertEquals(id11, list.get(0).getId());
+ }
+ }
+
+ public void test_falseDoubleUpdate() throws Exception {
+ Storage<StorableTestBasic> storage = getRepository().storageFor(StorableTestBasic.class);
+
+ StorableTestBasic s = storage.prepare();
+ s.setId(56789);
+
+ assertFalse(s.tryUpdate());
+ assertFalse(s.tryUpdate());
+ }
+
+ public void test_dateTimeIndex() throws Exception {
+ Storage<StorableDateIndex> storage = getRepository().storageFor(StorableDateIndex.class);
+
+ DateTimeZone original = DateTimeZone.getDefault();
+ // Set time zone different than defined in storable.
+ DateTimeZone.setDefault(DateTimeZone.forID("America/Los_Angeles"));
+ try {
+ DateTime now = new DateTime();
+
+ StorableDateIndex sdi = storage.prepare();
+ sdi.setID(1);
+ sdi.setOrderDate(now);
+ sdi.insert();
+
+ sdi.load();
+
+ assertEquals(now.getMillis(), sdi.getOrderDate().getMillis());
+ // Time zones will differ, since adapter is applied upon load.
+ assertFalse(now.equals(sdi.getOrderDate()));
+
+ Query<StorableDateIndex> query = storage.query("orderDate=?").with(now);
+ // Tests that adapter is applied to index. Otherwise, consistency
+ // check will reject loaded storable.
+ StorableDateIndex sdi2 = query.tryLoadOne();
+ assertNotNull(sdi2);
+ } finally {
+ DateTimeZone.setDefault(original);
+ }
+ }
+
+ public void test_joinCache() throws Exception {
+ Storage<UserAddress> uaStorage = getRepository().storageFor(UserAddress.class);
+ Storage<UserInfo> uiStorage = getRepository().storageFor(UserInfo.class);
+
+ UserAddress addr = uaStorage.prepare();
+ addr.setAddressID(5);
+ addr.setLine1("123");
+ addr.setCity("Seattle");
+ addr.setState("WA");
+ addr.setCountry("USA");
+ addr.insert();
+
+ UserInfo user = uiStorage.prepare();
+ user.setUserID(1);
+ user.setStateID(1);
+ user.setFirstName("John");
+ user.setLastName("Smith");
+ user.setAddress(addr);
+
+ assertEquals("Seattle", user.getAddress().getCity());
+
+ user.insert();
+
+ addr.setCity("Bellevue");
+ addr.tryUpdate();
+
+ // Should still refer to same address instance.
+ assertEquals("Bellevue", user.getAddress().getCity());
+
+ UserAddress addr2 = uaStorage.prepare();
+ addr2.setAddressID(5);
+ addr2.setCity("Kirkland");
+ addr2.tryUpdate();
+
+ // Should still refer to same address instance.
+ assertEquals("Bellevue", user.getAddress().getCity());
+
+ // Force reload of user should flush cache.
+ user.load();
+
+ assertEquals("Kirkland", user.getAddress().getCity());
+
+ addr2.setCity("Redmond");
+ addr2.tryUpdate();
+
+ // Should still refer to same address instance.
+ assertEquals("Kirkland", user.getAddress().getCity());
+
+ // Update of user should flush cache (even if nothing changed)
+ assertEquals(true, user.tryUpdate());
+
+ assertEquals("Redmond", user.getAddress().getCity());
+
+ addr2.setCity("Renton");
+ addr2.tryUpdate();
+
+ // Should still refer to same address instance.
+ assertEquals("Redmond", user.getAddress().getCity());
+
+ // Update of user should flush cache (when something changed)
+ user.setFirstName("Jim");
+ assertEquals(true, user.tryUpdate());
+
+ assertEquals("Renton", user.getAddress().getCity());
+
+ addr2.setCity("Tacoma");
+ addr2.tryUpdate();
+
+ // Should still refer to same address instance.
+ assertEquals("Renton", user.getAddress().getCity());
+
+ // Delete of user should flush cache
+ assertEquals(true, user.tryDelete());
+
+ assertEquals("Tacoma", user.getAddress().getCity());
+
+ addr2.setCity("Shoreline");
+ addr2.tryUpdate();
+
+ // Should still refer to same address instance.
+ assertEquals("Tacoma", user.getAddress().getCity());
+
+ // Failed load of user should flush cache
+ assertEquals(false, user.tryLoad());
+
+ assertEquals("Shoreline", user.getAddress().getCity());
+
+ addr2.setCity("Vancouver");
+ addr2.tryUpdate();
+
+ // Should still refer to same address instance.
+ assertEquals("Shoreline", user.getAddress().getCity());
+
+ // Insert of user should flush cache
+ user.insert();
+
+ assertEquals("Vancouver", user.getAddress().getCity());
+ }
+
+ public void test_updateReload() throws Exception {
+ Storage<UserInfo> uiStorage = getRepository().storageFor(UserInfo.class);
+
+ UserInfo user = uiStorage.prepare();
+ user.setUserID(1);
+ user.setStateID(1);
+ user.setFirstName("John");
+ user.setLastName("Smith");
+ user.setAddressID(0);
+ user.insert();
+
+ UserInfo user2 = uiStorage.prepare();
+ user2.setUserID(1);
+ user2.setFirstName("Jim");
+ user2.tryUpdate();
+
+ assertEquals("John", user.getFirstName());
+ user.tryUpdate();
+ assertEquals("Jim", user.getFirstName());
+
+ user2.setFirstName("Bob");
+ user2.tryUpdate();
+
+ assertEquals("Jim", user.getFirstName());
+ user.setLastName("Jones");
+ user.tryUpdate();
+ assertEquals("Bob", user.getFirstName());
+ assertEquals("Jones", user.getLastName());
+ }
+
+ public void test_deleteState() throws Exception {
+ Storage<UserInfo> uiStorage = getRepository().storageFor(UserInfo.class);
+
+ UserInfo user = uiStorage.prepare();
+ user.setUserID(1);
+ user.setStateID(1);
+ user.setFirstName("John");
+ user.setLastName("Smith");
+ user.setAddressID(0);
+ user.insert();
+
+ UserInfo user2 = uiStorage.prepare();
+ user2.setUserID(1);
+ user2.tryDelete();
+
+ assertFalse(user.tryLoad());
+
+ // Should be able to change pk now.
+ user.setUserID(2);
+ assertFalse(user.tryUpdate());
+ user.setUserID(1);
+ user.insert();
+
+ user2.tryDelete();
+
+ assertFalse(user.tryUpdate());
+
+ // Should be able to change pk now.
+ user.setUserID(2);
+ assertFalse(user.tryUpdate());
+ user.setUserID(1);
+ user.insert();
+ user2.tryDelete();
+
+ user.setFirstName("Jim");
+ assertFalse(user.tryUpdate());
+
+ // Should be able to change pk now.
+ user.setUserID(2);
+ assertFalse(user.tryUpdate());
+ user.setUserID(1);
+ user.insert();
+ assertEquals("Jim", user.getFirstName());
+ }
+
+ public void test_deleteUpdate() throws Exception {
+ Storage<UserInfo> uiStorage = getRepository().storageFor(UserInfo.class);
+
+ UserInfo user = uiStorage.prepare();
+ user.setUserID(1);
+ user.setStateID(1);
+ user.setFirstName("John");
+ user.setLastName("Smith");
+ user.setAddressID(0);
+ user.insert();
+
+ // Just want to change first name now
+ user.setFirstName("Bob");
+
+ // Concurrently, someone else deletes the user
+ UserInfo user2 = uiStorage.prepare();
+ user2.setUserID(1);
+ assertTrue(user2.tryDelete());
+
+ assertFalse(user.tryUpdate());
+
+ // Update failed... perhaps we should try again... (wrong decision)
+
+ // Concurrently, someone inserts a different user with re-used id. (wrong decision)
+ user2 = uiStorage.prepare();
+ user2.setUserID(1);
+ user2.setStateID(1);
+ user2.setFirstName("Mike");
+ user2.setLastName("Jones");
+ user2.setAddressID(0);
+ user2.insert();
+
+ // Trying the failed update again should totally blow away the sneaked in insert.
+ assertTrue(user.tryUpdate());
+
+ assertEquals("Bob", user.getFirstName());
+ assertEquals("Smith", user.getLastName());
+
+ // The failed update earlier dirtied all the properties, including ones
+ // that were not modified. As a result, the second update replaces all
+ // properties. This is a special edge case destructive update for which
+ // there is no clear solution. If just the one property was changed as
+ // instructed earlier, then the record would have been corrupted. This
+ // feels much worse. This can still happen if the record was not
+ // originally fully clean.
+
+ // The cause of the error is the user creating a new record with a
+ // re-used id. This problem could be prevented using transactions, or
+ // it might be detected with optimistic locking.
+ }
+
+ public void test_deleteOne() throws Exception {
+ Storage<UserInfo> uiStorage = getRepository().storageFor(UserInfo.class);
+
+ UserInfo user = uiStorage.prepare();
+ user.setUserID(1);
+ user.setStateID(1);
+ user.setFirstName("Bob");
+ user.setLastName("Smith");
+ user.setAddressID(0);
+ user.insert();
+
+ user = uiStorage.prepare();
+ user.setUserID(2);
+ user.setStateID(1);
+ user.setFirstName("Bob");
+ user.setLastName("Jones");
+ user.setAddressID(0);
+ user.insert();
+
+ user = uiStorage.prepare();
+ user.setUserID(3);
+ user.setStateID(1);
+ user.setFirstName("Indiana");
+ user.setLastName("Jones");
+ user.setAddressID(0);
+ user.insert();
+
+ try {
+ uiStorage.query("lastName = ?").with("Jones").deleteOne();
+ fail();
+ } catch (PersistMultipleException e) {
+ }
+
+ List<UserInfo> list = uiStorage.query().fetch().toList();
+ assertEquals(3, list.size());
+
+ uiStorage.query("lastName = ? & firstName = ?").with("Jones").with("Bob").deleteOne();
+
+ list = uiStorage.query().orderBy("userID").fetch().toList();
+ assertEquals(2, list.size());
+
+ assertEquals("Bob", list.get(0).getFirstName());
+ assertEquals("Smith", list.get(0).getLastName());
+ assertEquals("Indiana", list.get(1).getFirstName());
+ assertEquals("Jones", list.get(1).getLastName());
+ }
+
+ public void test_triggers() throws Exception {
+ Storage<StorableVersioned> storage = getRepository().storageFor(StorableVersioned.class);
+
+ class InsertTrigger extends Trigger<StorableVersioned> {
+ Object mState;
+
+ @Override
+ public Object beforeInsert(StorableVersioned s) {
+ assertEquals(1, s.getID());
+ mState = new Object();
+ return mState;
+ }
+
+ @Override
+ public void afterInsert(StorableVersioned s, Object state) {
+ assertEquals(1, s.getID());
+ assertEquals(mState, state);
+ }
+ };
+
+ InsertTrigger it = new InsertTrigger();
+
+ assertTrue(storage.addTrigger(it));
+
+ StorableVersioned s = storage.prepare();
+ s.setID(1);
+ s.setValue("value");
+ s.insert();
+
+ assertTrue(it.mState != null);
+
+ assertTrue(storage.removeTrigger(it));
+
+ class UpdateTrigger extends Trigger<StorableVersioned> {
+ Object mState;
+ int mVersion;
+
+ @Override
+ public Object beforeUpdate(StorableVersioned s) {
+ assertEquals(1, s.getID());
+ mState = new Object();
+ mVersion = s.getVersion();
+ return mState;
+ }
+
+ @Override
+ public void afterUpdate(StorableVersioned s, Object state) {
+ assertEquals(1, s.getID());
+ assertEquals(mState, state);
+ assertEquals(mVersion + 1, s.getVersion());
+ }
+ };
+
+ UpdateTrigger ut = new UpdateTrigger();
+
+ assertTrue(storage.addTrigger(ut));
+
+ s.setValue("value2");
+ s.update();
+
+ assertTrue(ut.mState != null);
+
+ assertTrue(storage.removeTrigger(ut));
+
+ class DeleteTrigger extends Trigger<StorableVersioned> {
+ Object mState;
+
+ @Override
+ public Object beforeDelete(StorableVersioned s) {
+ assertEquals(1, s.getID());
+ mState = new Object();
+ return mState;
+ }
+
+ @Override
+ public void afterDelete(StorableVersioned s, Object state) {
+ assertEquals(1, s.getID());
+ assertEquals(mState, state);
+ }
+ };
+
+ DeleteTrigger dt = new DeleteTrigger();
+
+ assertTrue(storage.addTrigger(dt));
+
+ s.delete();
+
+ assertTrue(dt.mState != null);
+
+ assertTrue(storage.removeTrigger(dt));
+ }
+
+ public void test_triggerFailure() throws Exception {
+ Storage<StorableVersioned> storage = getRepository().storageFor(StorableVersioned.class);
+
+ class InsertTrigger extends Trigger<StorableVersioned> {
+ boolean failed;
+
+ @Override
+ public Object beforeInsert(StorableVersioned s) {
+ throw new RuntimeException();
+ }
+
+ @Override
+ public void afterInsert(StorableVersioned s, Object state) {
+ fail();
+ }
+
+ @Override
+ public void failedInsert(StorableVersioned s, Object state) {
+ failed = true;
+ }
+ };
+
+ InsertTrigger it = new InsertTrigger();
+
+ assertTrue(storage.addTrigger(it));
+
+ StorableVersioned s = storage.prepare();
+ s.setID(1);
+ s.setValue("value");
+ try {
+ s.insert();
+ fail();
+ } catch (RuntimeException e) {
+ }
+
+ assertTrue(it.failed);
+
+ class UpdateTrigger extends Trigger<StorableVersioned> {
+ boolean failed;
+
+ @Override
+ public Object beforeUpdate(StorableVersioned s) {
+ throw new RuntimeException();
+ }
+
+ @Override
+ public void afterUpdate(StorableVersioned s, Object state) {
+ fail();
+ }
+
+ @Override
+ public void failedUpdate(StorableVersioned s, Object state) {
+ failed = true;
+ }
+ };
+
+ UpdateTrigger ut = new UpdateTrigger();
+
+ assertTrue(storage.addTrigger(ut));
+
+ s = storage.prepare();
+ s.setID(1);
+ s.setVersion(3);
+ s.setValue("value");
+ try {
+ s.update();
+ fail();
+ } catch (RuntimeException e) {
+ }
+
+ assertTrue(ut.failed);
+
+ class DeleteTrigger extends Trigger<StorableVersioned> {
+ boolean failed;
+
+ @Override
+ public Object beforeDelete(StorableVersioned s) {
+ throw new RuntimeException();
+ }
+
+ @Override
+ public void afterDelete(StorableVersioned s, Object state) {
+ fail();
+ }
+
+ @Override
+ public void failedDelete(StorableVersioned s, Object state) {
+ failed = true;
+ }
+ };
+
+ DeleteTrigger dt = new DeleteTrigger();
+
+ assertTrue(storage.addTrigger(dt));
+
+ s = storage.prepare();
+ s.setID(1);
+ try {
+ s.delete();
+ fail();
+ } catch (RuntimeException e) {
+ }
+
+ assertTrue(dt.failed);
+ }
+
+ public void test_triggerChecks() throws Exception {
+ Storage<StorableTimestamped> storage =
+ getRepository().storageFor(StorableTimestamped.class);
+
+ StorableTimestamped st = storage.prepare();
+ st.setId(1);
+ st.setValue("value");
+
+ try {
+ st.insert();
+ fail();
+ } catch (ConstraintException e) {
+ // We forgot to set submitDate and modifyDate.
+ }
+
+ // Install trigger that sets the timestamp properties.
+
+ storage.addTrigger(new Trigger<Timestamped>() {
+ @Override
+ public Object beforeInsert(Timestamped st) {
+ DateTime now = new DateTime();
+ st.setSubmitDateTime(now);
+ st.setModifyDateTime(now);
+ return null;
+ }
+
+ @Override
+ public Object beforeUpdate(Timestamped st) {
+ DateTime now = new DateTime();
+ st.setModifyDateTime(now);
+ return null;
+ }
+ });
+
+ st.insert();
+
+ assertNotNull(st.getSubmitDateTime());
+ assertNotNull(st.getModifyDateTime());
+
+ DateTime dt = st.getModifyDateTime();
+
+ Thread.sleep(500);
+
+ st.setValue("foo");
+ st.update();
+
+ assertTrue(st.getModifyDateTime().getMillis() >= dt.getMillis() + 450);
+ }
+
+ public void test_hashCode() throws Exception {
+ Storage<StorableTestBasic> storage =
+ getRepository().storageFor(StorableTestBasic.class);
+
+ StorableTestBasic stb = storage.prepare();
+ // Just tests to make sure generated code doesn't throw an error.
+ int hashCode = stb.hashCode();
+ }
+
+ public void test_stateMethods() throws Exception {
+ Storage<Order> storage = getRepository().storageFor(Order.class);
+
+ Order order = storage.prepare();
+
+ assertUninitialized(true, order, "orderID", "orderNumber", "orderTotal", "orderComments");
+ assertDirty(false, order, "orderID", "orderNumber", "orderTotal", "orderComments");
+ assertClean(false, order, "orderID", "orderNumber", "orderTotal", "orderComments");
+
+ order.setOrderID(1);
+
+ assertUninitialized(false, order, "orderID");
+ assertUninitialized(true, order, "orderNumber", "orderTotal", "orderComments");
+ assertDirty(false, order, "orderNumber", "orderTotal", "orderComments");
+ assertDirty(true, order, "orderID");
+ assertClean(false, order, "orderID", "orderNumber", "orderTotal", "orderComments");
+
+ order.setOrderNumber("123");
+ order.setOrderTotal(456);
+ order.setAddressID(789);
+
+ assertUninitialized(false, order, "orderID", "orderNumber", "orderTotal");
+ assertUninitialized(true, order, "orderComments");
+ assertDirty(true, order, "orderID", "orderNumber", "orderTotal");
+ assertDirty(false, order, "orderComments");
+ assertClean(false, order, "orderID", "orderNumber", "orderTotal", "orderComments");
+
+ // Get unknown property
+ try {
+ order.isPropertyUninitialized("foo");
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+
+ // Get unknown property
+ try {
+ order.isPropertyDirty("foo");
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+
+ // Get unknown property
+ try {
+ order.isPropertyClean("foo");
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+
+ // Get join property
+ try {
+ order.isPropertyUninitialized("address");
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+
+ // Get join property
+ try {
+ order.isPropertyDirty("address");
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+
+ // Get join property
+ try {
+ order.isPropertyClean("address");
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+
+ order.insert();
+
+ assertUninitialized(false, order, "orderID", "orderNumber", "orderTotal", "orderComments");
+ assertDirty(false, order, "orderID", "orderNumber", "orderTotal", "orderComments");
+ assertClean(true, order, "orderID", "orderNumber", "orderTotal", "orderComments");
+ }
+
+ public void test_count() throws Exception {
+ test_count(false);
+ }
+ public void test_countIndexed() throws Exception {
+ test_count(true);
+ }
+
+ private void test_count(boolean indexed) throws Exception {
+ Storage<? extends StorableTestBasic> storage;
+ if (indexed) {
+ storage = getRepository().storageFor(StorableTestBasicIndexed.class);
+ } else {
+ storage = getRepository().storageFor(StorableTestBasic.class);
+ }
+
+ assertEquals(0, storage.query().count());
+
+ StorableTestBasic sb = storage.prepare();
+ sb.setId(1);
+ sb.setIntProp(1);
+ sb.setLongProp(1);
+ sb.setDoubleProp(1.1);
+ sb.setStringProp("one");
+ sb.insert();
+
+ assertEquals(1, storage.query().count());
+ assertEquals(0, storage.query().not().count());
+ assertEquals(1, storage.query("stringProp = ?").with("one").count());
+ assertEquals(0, storage.query("stringProp = ?").with("two").count());
+
+ sb = storage.prepare();
+ sb.setId(2);
+ sb.setIntProp(2);
+ sb.setLongProp(2);
+ sb.setDoubleProp(2.1);
+ sb.setStringProp("two");
+ sb.insert();
+
+ sb = storage.prepare();
+ sb.setId(3);
+ sb.setIntProp(3);
+ sb.setLongProp(3);
+ sb.setDoubleProp(3.1);
+ sb.setStringProp("three");
+ sb.insert();
+
+ assertEquals(3, storage.query().count());
+ assertEquals(0, storage.query().not().count());
+ assertEquals(1, storage.query("stringProp = ?").with("one").count());
+ assertEquals(1, storage.query("stringProp = ?").with("two").count());
+ assertEquals(1, storage.query("stringProp = ?").with("three").count());
+ assertEquals(2, storage.query("stringProp = ?").not().with("one").count());
+ assertEquals(2, storage.query("stringProp = ?").not().with("two").count());
+ assertEquals(2, storage.query("stringProp = ?").not().with("three").count());
+ assertEquals(2, storage.query("stringProp > ?").with("one").count());
+ assertEquals(0, storage.query("stringProp > ?").with("two").count());
+ assertEquals(1, storage.query("stringProp > ?").with("three").count());
+ }
+
+ public void test_fetchAfter() throws Exception {
+ Storage<ManyKeys2> storage = getRepository().storageFor(ManyKeys2.class);
+
+ final int groupSize = 4;
+ final int aliasing = groupSize + 1;
+
+ int total = 0;
+ for (int a=0; a<groupSize; a++) {
+ for (int b=0; b<groupSize; b++) {
+ for (int c=0; c<groupSize; c++) {
+ for (int d=0; d<groupSize; d++) {
+ for (int e=0; e<groupSize; e++) {
+ for (int f=0; f<groupSize; f++) {
+ ManyKeys2 obj = storage.prepare();
+ obj.setA(a);
+ obj.setB(b);
+ obj.setC(c);
+ obj.setD(d);
+ obj.setE(e);
+ obj.setF(f);
+ obj.insert();
+ total++;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ String[] orderBy = {"a", "b", "c", "d", "e", "f"};
+ Query<ManyKeys2> query = storage.query().orderBy(orderBy);
+ Cursor<ManyKeys2> cursor = query.fetch();
+ Comparator<ManyKeys2> comparator = SortedCursor.createComparator(ManyKeys2.class, orderBy);
+
+ int actual = 0;
+ ManyKeys2 last = null;
+ while (cursor.hasNext()) {
+ actual++;
+ ManyKeys2 obj = cursor.next();
+ if (last != null) {
+ assertTrue(comparator.compare(last, obj) < 0);
+ }
+ if (actual % aliasing == 0) {
+ cursor.close();
+ cursor = query.fetchAfter(obj);
+ }
+ last = obj;
+ }
+
+ assertEquals(total, actual);
+
+ // Try again in reverse
+
+ orderBy = new String[] {"-a", "-b", "-c", "-d", "-e", "-f"};
+ query = storage.query().orderBy(orderBy);
+ cursor = query.fetch();
+
+ actual = 0;
+ last = null;
+ while (cursor.hasNext()) {
+ actual++;
+ ManyKeys2 obj = cursor.next();
+ if (last != null) {
+ assertTrue(comparator.compare(last, obj) > 0);
+ }
+ if (actual % aliasing == 0) {
+ cursor.close();
+ cursor = query.fetchAfter(obj);
+ }
+ last = obj;
+ }
+
+ assertEquals(total, actual);
+
+ // Try again with funny mix of orderings. This will likely cause sort
+ // operations to be performed, thus making it very slow.
+
+ orderBy = new String[] {"-a", "b", "-c", "d", "-e", "f"};
+ query = storage.query().orderBy(orderBy);
+ cursor = query.fetch();
+ comparator = SortedCursor.createComparator(ManyKeys2.class, orderBy);
+
+ actual = 0;
+ last = null;
+ while (cursor.hasNext()) {
+ actual++;
+ ManyKeys2 obj = cursor.next();
+ if (last != null) {
+ assertTrue(comparator.compare(last, obj) < 0);
+ }
+ if (actual % aliasing == 0) {
+ cursor.close();
+ cursor = query.fetchAfter(obj);
+ }
+ last = obj;
+ }
+
+ assertEquals(total, actual);
+ }
+
+ private void assertUninitialized(boolean expected, Storable storable, String... properties) {
+ for (String property : properties) {
+ assertEquals(expected, storable.isPropertyUninitialized(property));
+ }
+ }
+
+ private void assertDirty(boolean expected, Storable storable, String... properties) {
+ for (String property : properties) {
+ assertEquals(expected, storable.isPropertyDirty(property));
+ }
+ }
+
+ private void assertClean(boolean expected, Storable storable, String... properties) {
+ for (String property : properties) {
+ assertEquals(expected, storable.isPropertyClean(property));
+ }
+ }
+
+ @PrimaryKey("id")
+ public interface StorableTestAssymetricGet extends Storable{
+ public abstract int getId();
+ public abstract void setId(int id);
+
+ public abstract int getAssymetricGET();
+ }
+
+ @PrimaryKey("id")
+ public interface StorableTestAssymetricSet extends Storable{
+ public abstract int getId();
+ public abstract void setId(int id);
+
+ public abstract int setAssymetricSET();
+ }
+
+ @PrimaryKey("id")
+ public interface StorableTestAssymetricGetSet extends Storable{
+ public abstract int getId();
+ public abstract void setId(int id);
+
+ public abstract int getAssymetricGET();
+
+ public abstract int setAssymetricSET();
+ }
+
+ public static class InvocationTracker extends StorableTestBasic implements WrappedSupport {
+ String mName;
+ long mInvocationTracks;
+
+ boolean mTrace;
+
+ public InvocationTracker(String name) {
+ this(name, false);
+ }
+
+ public InvocationTracker(final String name, boolean trace) {
+ mName = name;
+ mTrace = trace;
+ clearTracks();
+ }
+
+ public void clearTracks() {
+ mInvocationTracks = 0;
+ }
+
+ public long getTracks() {
+ return mInvocationTracks;
+ }
+
+ public void assertTrack(long value) {
+ assertEquals(value, getTracks());
+ clearTracks();
+ }
+
+
+ public void setId(int id) {
+ if (mTrace) System.out.println("setId");
+ mInvocationTracks |= sSetId;
+ }
+
+ // Basic coverage of the primitives
+ public String getStringProp() {
+ if (mTrace) System.out.println("getStringProp");
+ mInvocationTracks |= sGetStringProp; // 0x2
+ return null;
+ }
+
+ public void setStringProp(String aStringThing) {
+ if (mTrace) System.out.println("setStringProp");
+ mInvocationTracks |= sSetStringProp; // 0x4
+ }
+
+ public int getIntProp() {
+ if (mTrace) System.out.println("getIntProp");
+ mInvocationTracks |= sGetIntProp; // 0x8
+ return 0;
+ }
+
+ public void setIntProp(int anInt) {
+ if (mTrace) System.out.println("setIntProp");
+ mInvocationTracks |= sSetIntProp; // 0x10
+ }
+
+ public long getLongProp() {
+ if (mTrace) System.out.println("getLongProp");
+ mInvocationTracks |= sGetLongProp; // 0x20
+ return 0;
+ }
+
+ public void setLongProp(long aLong) {
+ if (mTrace) System.out.println("setLongProp");
+ mInvocationTracks |= sSetLongProp; // 0x40
+ }
+
+ public double getDoubleProp() {
+ if (mTrace) System.out.println("getDoubleProp");
+ mInvocationTracks |= sGetDoubleProp; // 0x80
+ return 0;
+ }
+
+ public void setDoubleProp(double aDouble) {
+ if (mTrace) System.out.println("setDoubleProp");
+ mInvocationTracks |= sSetDoubleProp; // 0x100
+ }
+
+ public DateTime getDate() {
+ return null;
+ }
+
+ public void setDate(DateTime date) {
+ }
+
+ public void load() throws FetchException {
+ if (mTrace) System.out.println("load");
+ mInvocationTracks |= sLoad; // 0x200
+ }
+
+ public boolean tryLoad() throws FetchException {
+ if (mTrace) System.out.println("tryLoad");
+ mInvocationTracks |= sTryLoad; // 0x400
+ return false;
+ }
+
+ public void insert() throws PersistException {
+ if (mTrace) System.out.println("insert");
+ mInvocationTracks |= sInsert; // 0x800
+ }
+
+ public boolean tryInsert() throws PersistException {
+ if (mTrace) System.out.println("tryInsert");
+ mInvocationTracks |= sTryInsert;
+ return false;
+ }
+
+ public void update() throws PersistException {
+ if (mTrace) System.out.println("update");
+ mInvocationTracks |= sUpdate;
+ }
+
+ public boolean tryUpdate() throws PersistException {
+ if (mTrace) System.out.println("tryUpdate");
+ mInvocationTracks |= sTryUpdate; // 0x1000
+ return false;
+ }
+
+ public void delete() throws PersistException {
+ if (mTrace) System.out.println("delete");
+ mInvocationTracks |= sDelete;
+ }
+
+ public boolean tryDelete() throws PersistException {
+ if (mTrace) System.out.println("tryDelete");
+ mInvocationTracks |= sTryDelete; // 0x2000
+ return false;
+ }
+
+ public WrappedSupport createSupport(Storable storable) {
+ return new InvocationTracker(mName, mTrace);
+ }
+
+ public Storage storage() {
+ if (mTrace) System.out.println("storage");
+ mInvocationTracks |= sStorage; // 0x4000
+ return null;
+ }
+
+ public Storable copy() {
+ if (mTrace) System.out.println("copy");
+ mInvocationTracks |= sCopy; // 0x8000
+ return null;
+ }
+
+ public String toStringKeyOnly() {
+ if (mTrace) System.out.println("toStringKeyOnly");
+ mInvocationTracks |= sToStringKeyOnly; // 0x10000
+ return null;
+ }
+
+ public int getId() {
+ if (mTrace) System.out.println("getId");
+ mInvocationTracks |= sGetId; // 0x20000
+ return 0;
+ }
+
+ public void copyAllProperties(Storable storable) {
+ if (mTrace) System.out.println("copyAllProperties");
+ mInvocationTracks |= sCopyAllProperties; // 0x40000
+ }
+
+ public void copyPrimaryKeyProperties(Storable storable) {
+ if (mTrace) System.out.println("copyPrimaryKeyProperties");
+ mInvocationTracks |= sCopyPrimaryKeyProperties; // 0x80000
+ }
+
+ public void copyUnequalProperties(Storable storable) {
+ if (mTrace) System.out.println("copyUnequalProperties");
+ mInvocationTracks |= sCopyUnequalProperties; // 0x10 0000
+ }
+
+ public void copyDirtyProperties(Storable storable) {
+ if (mTrace) System.out.println("copyDirtyProperties");
+ mInvocationTracks |= sCopyDirtyProperties; // 0x20 0000
+ }
+
+ public boolean hasDirtyProperties() {
+ if (mTrace) System.out.println("hasDirtyProperties");
+ mInvocationTracks |= sHasDirtyProperties; // 0x200 0000
+ return false;
+ }
+
+ public boolean equalPrimaryKeys(Object obj) {
+ if (mTrace) System.out.println("equalPrimaryKeys");
+ mInvocationTracks |= sEqualKeys; // 0x40 0000
+ return true;
+ }
+
+ public boolean equalProperties(Object obj) {
+ if (mTrace) System.out.println("equalProperties");
+ mInvocationTracks |= sEqualProperties; // 0x80 0000
+ return true;
+ }
+
+ public void copyVersionProperty(Storable storable) {
+ if (mTrace) System.out.println("copyVersionProperty");
+ mInvocationTracks |= sCopyVersionProperty; // 0x100 0000
+ }
+
+ public void markPropertiesClean() {
+ if (mTrace) System.out.println("markPropertiesClean");
+ mInvocationTracks |= sMarkPropertiesClean; // 0x400 0000
+ }
+
+ public void markAllPropertiesClean() {
+ if (mTrace) System.out.println("markAllPropertiesClean");
+ mInvocationTracks |= sMarkAllPropertiesClean; // 0x800 0000
+ }
+
+ public void markPropertiesDirty() {
+ if (mTrace) System.out.println("markPropertiesDirty");
+ mInvocationTracks |= sMarkPropertiesDirty; // 0x1000 0000
+ }
+
+ public void markAllPropertiesDirty() {
+ if (mTrace) System.out.println("markAllPropertiesDirty");
+ mInvocationTracks |= sMarkAllPropertiesDirty; // 0x2000 0000
+ }
+
+ public Class storableType() {
+ if (mTrace) System.out.println("storableType");
+ mInvocationTracks |= sStorableType; // 0x4000 0000
+ return Storable.class;
+ }
+
+ public boolean isPropertyUninitialized(String name) {
+ if (mTrace) System.out.println("isPropertyUninitialized");
+ return false;
+ }
+
+ public boolean isPropertyDirty(String name) {
+ if (mTrace) System.out.println("isPropertyDirty");
+ return false;
+ }
+
+ public boolean isPropertyClean(String name) {
+ if (mTrace) System.out.println("isPropertyClean");
+ return false;
+ }
+
+ public boolean isPropertySupported(String name) {
+ if (mTrace) System.out.println("isPropertySupported");
+ return false;
+ }
+
+ public Repository getRootRepository() {
+ return null;
+ }
+
+ public Trigger getInsertTrigger() {
+ return null;
+ }
+
+ public Trigger getUpdateTrigger() {
+ return null;
+ }
+
+ public Trigger getDeleteTrigger() {
+ return null;
+ }
+ }
+
+}
diff --git a/src/test/java/com/amazon/carbonado/spi/TestSequenceValueGenerator.java b/src/test/java/com/amazon/carbonado/spi/TestSequenceValueGenerator.java
new file mode 100644
index 0000000..ae17e5d
--- /dev/null
+++ b/src/test/java/com/amazon/carbonado/spi/TestSequenceValueGenerator.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.amazon.carbonado.spi;
+
+import java.util.Random;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.amazon.carbonado.PersistException;
+import com.amazon.carbonado.Repository;
+import com.amazon.carbonado.Storage;
+
+import com.amazon.carbonado.repo.toy.ToyRepository;
+
+import com.amazon.carbonado.stored.StorableTestBasic;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+public class TestSequenceValueGenerator extends TestCase {
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static TestSuite suite() {
+ return new TestSuite(TestSequenceValueGenerator.class);
+ }
+
+ private Repository mRepository;
+
+ public TestSequenceValueGenerator(String name) {
+ super(name);
+ }
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ mRepository = new ToyRepository();
+ }
+
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ public void test_basics() throws Exception {
+ SequenceValueGenerator generator = new SequenceValueGenerator(mRepository, "foo");
+
+ for (int i=1; i<=950; i++) {
+ assertEquals(i, generator.nextLongValue());
+ }
+
+ generator.reset(1);
+
+ for (int i=1; i<=950; i++) {
+ assertEquals(i, generator.nextIntValue());
+ }
+
+ generator.reset(1);
+
+ for (int i=1; i<=950; i++) {
+ assertEquals(String.valueOf(i), generator.nextDecimalValue());
+ }
+
+ // Make sure data is persisted
+
+ generator = new SequenceValueGenerator(mRepository, "foo");
+
+ assertTrue(generator.nextLongValue() > 950);
+
+ // Make sure data is isolated
+
+ generator = new SequenceValueGenerator(mRepository, "another");
+
+ for (int i=1; i<=1050; i++) {
+ assertEquals(i, generator.nextLongValue());
+ }
+
+ // Make sure reserved values can be returned
+
+ generator.returnReservedValues();
+ generator = new SequenceValueGenerator(mRepository, "another");
+
+ assertEquals(1051, generator.nextLongValue());
+ }
+
+ public void test_highIncrement() throws Exception {
+ SequenceValueGenerator generator =
+ new SequenceValueGenerator(mRepository, "foo", 1, 125);
+
+ for (int i=0; i<950; i++) {
+ assertEquals(i * 125 + 1, generator.nextLongValue());
+ }
+ }
+
+ public void test_highInitialAndHighIncrement() throws Exception {
+ SequenceValueGenerator generator =
+ new SequenceValueGenerator(mRepository, "foo", 0x500000000L, 125);
+
+ for (int i=0; i<950; i++) {
+ assertEquals(i * 125 + 0x500000000L, generator.nextLongValue());
+ }
+
+ try {
+ // Doesn't fit in an int.
+ generator.nextIntValue();
+ fail();
+ } catch (PersistException e) {
+ }
+ }
+
+ public void test_lowReserve() throws Exception {
+ SequenceValueGenerator generator =
+ new SequenceValueGenerator(mRepository, "goo", 1, 1, 1);
+
+ for (int i=1; i<=950; i++) {
+ assertEquals(i, generator.nextLongValue());
+ }
+ }
+
+ public void test_overflow() throws Exception {
+ Storage<StoredSequence> storage = mRepository.storageFor(StoredSequence.class);
+ StoredSequence seq = storage.prepare();
+ seq.setName("overflow");
+ seq.setInitialValue(1);
+ seq.setNextValue(Long.MAX_VALUE - 50);
+ seq.insert();
+
+ SequenceValueGenerator generator = new SequenceValueGenerator(mRepository, "overflow");
+
+ for (int i=-50; i<=-1; i++) {
+ assertEquals(i, generator.nextLongValue());
+ }
+
+ // Although next value could be zero, overflow logic doesn't work this
+ // way. Its not really worth the trouble to allow zero to be returned
+ // before overflowing.
+
+ try {
+ // Overflow.
+ generator.nextLongValue();
+ fail();
+ } catch (PersistException e) {
+ }
+ }
+
+ public void test_largeNumericalValue() throws Exception {
+ // Tests string conversion to ensure large unsigned values are properly
+ // generated.
+
+ SequenceValueGenerator generator =
+ new SequenceValueGenerator(mRepository, "goo", Long.MAX_VALUE, 1);
+
+ assertEquals("9223372036854775807", generator.nextDecimalValue());
+ // Next values are too large to fit in an unsigned long
+ assertEquals("9223372036854775808", generator.nextDecimalValue());
+ assertEquals("9223372036854775809", generator.nextDecimalValue());
+ }
+
+ public void test_radix() throws Exception {
+ SequenceValueGenerator generator = new SequenceValueGenerator(mRepository, "goo");
+
+ for (int i=1; i<=1000; i++) {
+ assertEquals(Integer.toString(i, 36), generator.nextNumericalValue(36, 1));
+ }
+ }
+
+ public void test_pad() throws Exception {
+ SequenceValueGenerator generator = new SequenceValueGenerator(mRepository, "goo");
+
+ for (int i=1; i<=2000; i++) {
+ String next = generator.nextNumericalValue(10, 3);
+ assertTrue(next.length() >= 3);
+ int value = Integer.parseInt(next);
+ assertEquals(i, value);
+ }
+ }
+
+ public void test_concurrentAccess() throws Exception {
+ // Simple test ensuring that values are reserved properly even when
+ // multiple processes may be sharing the sequence.
+
+ SequenceValueGenerator g1 = new SequenceValueGenerator(mRepository, "goo", 1, 1, 100);
+ SequenceValueGenerator g2 = new SequenceValueGenerator(mRepository, "goo", 1, 1, 100);
+
+ for (int i=1; i<=100; i++) {
+ assertEquals(i, g1.nextLongValue());
+ assertEquals(i + 100, g2.nextLongValue());
+ }
+
+ for (int i=201; i<=300; i++) {
+ assertEquals(i, g2.nextLongValue());
+ assertEquals(i + 100, g1.nextLongValue());
+ }
+
+ assertTrue(g1.returnReservedValues());
+ assertFalse(g2.returnReservedValues());
+ }
+
+ // FIXME: move this test somewhere else
+ /* Takes too long
+ public void test_heavyConcurrentAccess() throws Exception {
+ // Heavy test with multiple processes sharing the sequence.
+
+ final Storage<StorableTestBasic> storage =
+ mRepository.storageFor(StorableTestBasic.class);
+ final Random rnd = new Random(376296292);
+ final int loopCount = 10000;
+
+ Thread[] threads = new Thread[10];
+ for (int i=0; i<threads.length; i++) {
+ threads[i] = new Thread() {
+ public void run() {
+ try {
+ SequenceValueGenerator generator =
+ new SequenceValueGenerator(mRepository, "seq");
+ for (int i=0; i<loopCount; i++) {
+ StorableTestBasic stb = storage.prepare();
+ stb.setId(generator.nextIntValue());
+ stb.initBasicProperties();
+ stb.insert();
+ if (rnd.nextInt(500) == 0) {
+ generator.returnReservedValues();
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace(System.out);
+ fail(e.toString());
+ }
+ }
+ };
+
+ threads[i].start();
+ }
+
+ for (Thread t : threads) {
+ t.join();
+ }
+ }
+ */
+}
diff --git a/src/test/java/com/amazon/carbonado/stored/FileInfo.java b/src/test/java/com/amazon/carbonado/stored/FileInfo.java
new file mode 100644
index 0000000..7d35faf
--- /dev/null
+++ b/src/test/java/com/amazon/carbonado/stored/FileInfo.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.amazon.carbonado.stored;
+
+import java.io.*;
+
+import org.joda.time.DateTime;
+
+import com.amazon.carbonado.*;
+import com.amazon.carbonado.lob.*;
+import com.amazon.carbonado.adapter.*;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+@Indexes({
+ @Index("name"),
+ @Index({"length", "lastModified"}),
+ @Index("lastModified"),
+ @Index("parentID")
+})
+@Alias("CBN_TEST_FILE_INFO")
+@AlternateKeys({
+ @Key({"parentID", "name"})
+})
+@PrimaryKey("ID")
+public abstract class FileInfo implements Storable {
+ @Sequence("com.amazon.carbonado.storables.FileInfo")
+ public abstract int getID();
+
+ public abstract void setID(int value);
+
+ @Nullable
+ public abstract Integer getParentID();
+
+ public abstract void setParentID(Integer id);
+
+ @Nullable
+ @Join(internal="parentID", external="ID")
+ public abstract FileInfo getParent() throws FetchException;
+
+ public abstract void setParent(FileInfo value);
+
+ @Join(internal="ID", external="parentID")
+ public abstract Query<FileInfo> getChildren() throws FetchException;
+
+ @Alias("FILE_NAME")
+ public abstract String getName();
+
+ public abstract void setName(String value);
+
+ @YesNoAdapter
+ public abstract boolean isDirectory();
+
+ public abstract void setDirectory(boolean value);
+
+ @Alias("FILE_LENGTH")
+ public abstract long getLength();
+
+ public abstract void setLength(long value);
+
+ @Nullable
+ public abstract DateTime getLastModified();
+
+ public abstract void setLastModified(DateTime value);
+
+ @Version
+ @Alias("RECORD_VERSION_NUMBER")
+ public abstract int getVersionNumber();
+
+ public abstract void setVersionNumber(int version);
+
+ @Nullable
+ public abstract Blob getFileData();
+
+ public abstract void setFileData(Blob data);
+
+ public String getFullPath() throws FetchException {
+ FileInfo parent;
+ try {
+ parent = getParent();
+ } catch (FetchNoneException e) {
+ parent = null;
+ }
+ if (parent == null) {
+ return getName();
+ } else {
+ return parent.getFullPath() + '/' + getName();
+ }
+ }
+}
diff --git a/src/test/java/com/amazon/carbonado/stored/ManyKeys2.java b/src/test/java/com/amazon/carbonado/stored/ManyKeys2.java
new file mode 100644
index 0000000..282ff02
--- /dev/null
+++ b/src/test/java/com/amazon/carbonado/stored/ManyKeys2.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.amazon.carbonado.stored;
+
+import com.amazon.carbonado.*;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+@PrimaryKey({"a", "b", "c", "d", "e", "f"})
+public interface ManyKeys2 extends Storable {
+ int getA();
+ void setA(int value);
+
+ int getB();
+ void setB(int value);
+
+ int getC();
+ void setC(int value);
+
+ int getD();
+ void setD(int value);
+
+ int getE();
+ void setE(int value);
+
+ int getF();
+ void setF(int value);
+}
diff --git a/src/test/java/com/amazon/carbonado/stored/StorableDateIndex.java b/src/test/java/com/amazon/carbonado/stored/StorableDateIndex.java
new file mode 100644
index 0000000..5e7d063
--- /dev/null
+++ b/src/test/java/com/amazon/carbonado/stored/StorableDateIndex.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.amazon.carbonado.stored;
+
+import org.joda.time.DateTime;
+
+
+import com.amazon.carbonado.Index;
+import com.amazon.carbonado.Indexes;
+import com.amazon.carbonado.PrimaryKey;
+import com.amazon.carbonado.Storable;
+import com.amazon.carbonado.adapter.DateTimeAdapter;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+@Indexes(@Index("orderDate"))
+@PrimaryKey("ID")
+public interface StorableDateIndex extends Storable {
+ int getID();
+
+ void setID(int id);
+
+ @DateTimeAdapter(timeZone="America/New_York")
+ DateTime getOrderDate();
+
+ void setOrderDate(DateTime date);
+}
diff --git a/src/test/java/com/amazon/carbonado/stored/StorableSequenced.java b/src/test/java/com/amazon/carbonado/stored/StorableSequenced.java
new file mode 100644
index 0000000..770d6f0
--- /dev/null
+++ b/src/test/java/com/amazon/carbonado/stored/StorableSequenced.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.amazon.carbonado.stored;
+
+import com.amazon.carbonado.Nullable;
+import com.amazon.carbonado.PrimaryKey;
+import com.amazon.carbonado.Sequence;
+import com.amazon.carbonado.Storable;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+@PrimaryKey("ID")
+public interface StorableSequenced extends Storable<StorableSequenced> {
+ @Sequence("pk")
+ long getID();
+
+ void setID(long id);
+
+ @Sequence("some_int")
+ int getSomeInt();
+
+ void setSomeInt(int i);
+
+ @Sequence("some_IntegerObj")
+ Integer getSomeIntegerObj();
+
+ void setSomeIntegerObj(Integer i);
+
+ @Sequence("some_long")
+ long getSomeLong();
+
+ void setSomeLong(long i);
+
+ @Sequence("some_LongObj")
+ @Nullable
+ Long getSomeLongObj();
+
+ void setSomeLongObj(Long i);
+
+ @Sequence("some_String")
+ String getSomeString();
+
+ void setSomeString(String str);
+
+ String getData();
+
+ void setData(String data);
+}
diff --git a/src/test/java/com/amazon/carbonado/stored/StorableTestAssymetric.java b/src/test/java/com/amazon/carbonado/stored/StorableTestAssymetric.java
new file mode 100644
index 0000000..3808c1f
--- /dev/null
+++ b/src/test/java/com/amazon/carbonado/stored/StorableTestAssymetric.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.amazon.carbonado.stored;
+
+import com.amazon.carbonado.Storable;
+import com.amazon.carbonado.PrimaryKey;
+
+/*
+ * StorableTestAssymetric
+ *
+ * @author Don Schneider
+ */
+@PrimaryKey("id")
+public abstract class StorableTestAssymetric implements Storable {
+ public abstract int getId();
+ public abstract void setId(int id);
+
+ public int getAssymetricGet()
+ {
+ return getId()*3;
+ };
+}
diff --git a/src/test/java/com/amazon/carbonado/stored/StorableTestInvalid.java b/src/test/java/com/amazon/carbonado/stored/StorableTestInvalid.java
new file mode 100644
index 0000000..bd9fed2
--- /dev/null
+++ b/src/test/java/com/amazon/carbonado/stored/StorableTestInvalid.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.amazon.carbonado.stored;
+
+import com.amazon.carbonado.Storable;
+import com.amazon.carbonado.PrimaryKey;
+
+/**
+ * StorableTestInvalid
+ *
+ * @author Don Schneider
+ */
+@PrimaryKey("pk")
+public interface StorableTestInvalid extends Storable {
+ int getPk();
+ void setPk(int id);
+
+ Custom getCustom();
+ void setCustom(Custom aCustom);
+
+
+ // Probably more than is needed
+ class Custom { }
+}
diff --git a/src/test/java/com/amazon/carbonado/stored/StorableTestKeyValue.java b/src/test/java/com/amazon/carbonado/stored/StorableTestKeyValue.java
new file mode 100644
index 0000000..44ecc2f
--- /dev/null
+++ b/src/test/java/com/amazon/carbonado/stored/StorableTestKeyValue.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.amazon.carbonado.stored;
+
+import com.amazon.carbonado.Storable;
+import com.amazon.carbonado.PrimaryKey;
+
+/**
+ * StorableTestKeyValue
+ *
+ * @author Don Schneider
+ */
+@PrimaryKey({"key1", "key2"})
+public interface StorableTestKeyValue extends Storable {
+ int getKey1();
+ void setKey1(int id);
+
+ int getKey2();
+ void setKey2(int id);
+
+ int getValue1();
+ void setValue1(int value);
+
+ int getValue2();
+ void setValue2(int value);
+
+}
diff --git a/src/test/java/com/amazon/carbonado/stored/StorableTestMultiPK.java b/src/test/java/com/amazon/carbonado/stored/StorableTestMultiPK.java
new file mode 100644
index 0000000..ace031b
--- /dev/null
+++ b/src/test/java/com/amazon/carbonado/stored/StorableTestMultiPK.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.amazon.carbonado.stored;
+
+import com.amazon.carbonado.PrimaryKey;
+import com.amazon.carbonado.Storable;
+
+/**
+ * StorableTestMultiPK
+ *
+ * @author Don Schneider
+ */
+@PrimaryKey({"idPK", "stringPK"})
+public interface StorableTestMultiPK extends Storable {
+ int getIdPK();
+ void setIdPK(int id);
+
+ // Basic coverage of the primitives
+ String getStringPK();
+ void setStringPK(String aString);
+
+ String getStringData();
+ void setStringData(String aData);
+}
diff --git a/src/test/java/com/amazon/carbonado/stored/StorableTimestamped.java b/src/test/java/com/amazon/carbonado/stored/StorableTimestamped.java
new file mode 100644
index 0000000..d93ffeb
--- /dev/null
+++ b/src/test/java/com/amazon/carbonado/stored/StorableTimestamped.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.amazon.carbonado.stored;
+
+import org.joda.time.DateTime;
+
+import com.amazon.carbonado.Storable;
+import com.amazon.carbonado.PrimaryKey;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+@PrimaryKey("id")
+public interface StorableTimestamped extends Storable, Timestamped {
+ int getId();
+ void setId(int id);
+
+ String getValue();
+ void setValue(String str);
+}
diff --git a/src/test/java/com/amazon/carbonado/stored/StorableVersioned.java b/src/test/java/com/amazon/carbonado/stored/StorableVersioned.java
new file mode 100644
index 0000000..2de9640
--- /dev/null
+++ b/src/test/java/com/amazon/carbonado/stored/StorableVersioned.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.amazon.carbonado.stored;
+
+import com.amazon.carbonado.Nullable;
+import com.amazon.carbonado.Storable;
+import com.amazon.carbonado.PrimaryKey;
+import com.amazon.carbonado.Version;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+@PrimaryKey("ID")
+public interface StorableVersioned extends Storable {
+ int getID();
+
+ void setID(int id);
+
+ String getValue();
+
+ void setValue(String value);
+
+ @Nullable
+ String getName();
+
+ void setName(String name);
+
+ @Version
+ int getVersion();
+
+ void setVersion(int version);
+}
diff --git a/src/test/java/com/amazon/carbonado/stored/StorableVersionedIndexed.java b/src/test/java/com/amazon/carbonado/stored/StorableVersionedIndexed.java
new file mode 100644
index 0000000..906e8be
--- /dev/null
+++ b/src/test/java/com/amazon/carbonado/stored/StorableVersionedIndexed.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.amazon.carbonado.stored;
+
+import com.amazon.carbonado.Index;
+import com.amazon.carbonado.Indexes;
+import com.amazon.carbonado.PrimaryKey;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+@Indexes({
+ @Index("value"),
+ @Index("name")
+})
+@PrimaryKey("ID")
+public interface StorableVersionedIndexed extends StorableVersioned {
+}
diff --git a/src/test/java/com/amazon/carbonado/stored/StorableVersionedWithLong.java b/src/test/java/com/amazon/carbonado/stored/StorableVersionedWithLong.java
new file mode 100644
index 0000000..6d55d54
--- /dev/null
+++ b/src/test/java/com/amazon/carbonado/stored/StorableVersionedWithLong.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.amazon.carbonado.stored;
+
+import com.amazon.carbonado.Storable;
+import com.amazon.carbonado.PrimaryKey;
+import com.amazon.carbonado.Version;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+@PrimaryKey("ID")
+public interface StorableVersionedWithLong extends Storable {
+ int getID();
+
+ void setID(int id);
+
+ String getValue();
+
+ void setValue(String value);
+
+ @Version
+ long getVersion();
+
+ void setVersion(long version);
+}
diff --git a/src/test/java/com/amazon/carbonado/stored/StorableVersionedWithLongObj.java b/src/test/java/com/amazon/carbonado/stored/StorableVersionedWithLongObj.java
new file mode 100644
index 0000000..2acfdf8
--- /dev/null
+++ b/src/test/java/com/amazon/carbonado/stored/StorableVersionedWithLongObj.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.amazon.carbonado.stored;
+
+import com.amazon.carbonado.Nullable;
+import com.amazon.carbonado.Storable;
+import com.amazon.carbonado.PrimaryKey;
+import com.amazon.carbonado.Version;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+@PrimaryKey("ID")
+public interface StorableVersionedWithLongObj extends Storable {
+ int getID();
+
+ void setID(int id);
+
+ String getValue();
+
+ void setValue(String value);
+
+ @Version
+ @Nullable
+ Long getVersion();
+
+ void setVersion(Long version);
+}
diff --git a/src/test/java/com/amazon/carbonado/stored/StorableVersionedWithObj.java b/src/test/java/com/amazon/carbonado/stored/StorableVersionedWithObj.java
new file mode 100644
index 0000000..29f313c
--- /dev/null
+++ b/src/test/java/com/amazon/carbonado/stored/StorableVersionedWithObj.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.amazon.carbonado.stored;
+
+import com.amazon.carbonado.Nullable;
+import com.amazon.carbonado.Storable;
+import com.amazon.carbonado.PrimaryKey;
+import com.amazon.carbonado.Version;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+@PrimaryKey("ID")
+public interface StorableVersionedWithObj extends Storable {
+ int getID();
+
+ void setID(int id);
+
+ String getValue();
+
+ void setValue(String value);
+
+ @Version
+ @Nullable
+ Integer getVersion();
+
+ void setVersion(Integer version);
+}
diff --git a/src/test/java/com/amazon/carbonado/stored/Timestamped.java b/src/test/java/com/amazon/carbonado/stored/Timestamped.java
new file mode 100644
index 0000000..b38ee28
--- /dev/null
+++ b/src/test/java/com/amazon/carbonado/stored/Timestamped.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.amazon.carbonado.stored;
+
+import org.joda.time.DateTime;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+public interface Timestamped {
+ DateTime getSubmitDateTime();
+ void setSubmitDateTime(DateTime dt);
+
+ DateTime getModifyDateTime();
+ void setModifyDateTime(DateTime dt);
+}