diff options
| author | Brian S. O'Neill <bronee@gmail.com> | 2006-10-14 16:47:22 +0000 |
|---|---|---|
| committer | Brian S. O'Neill <bronee@gmail.com> | 2006-10-14 16:47:22 +0000 |
| commit | 3df73d90a65115d9fdcf4b8596971e957c6bce2d (patch) | |
| tree | 12e11eb3b3ab52d5bb86a2cc7fce1d60f54d5721 /src/test/java | |
Moved tests to separate project.
Diffstat (limited to 'src/test/java')
64 files changed, 14830 insertions, 0 deletions
diff --git a/src/test/java/com/amazon/carbonado/TestQueryLogic.java b/src/test/java/com/amazon/carbonado/TestQueryLogic.java new file mode 100644 index 0000000..4b4e917 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/TestQueryLogic.java @@ -0,0 +1,835 @@ +/*
+ * 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.List;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.amazon.carbonado.filter.Filter;
+
+import com.amazon.carbonado.repo.toy.ToyRepository;
+
+import com.amazon.carbonado.stored.StorableTestBasic;
+import com.amazon.carbonado.stored.StorableTestBasicIndexed;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+public class TestQueryLogic extends TestCase {
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static TestSuite suite() {
+ TestSuite suite = new TestSuite();
+ suite.addTestSuite(TestQueryLogic.class);
+ return suite;
+ }
+
+ private Repository mRepository;
+
+ protected void setUp() throws Exception {
+ mRepository = new ToyRepository();
+ }
+
+ protected void tearDown() throws Exception {
+ if (mRepository != null) {
+ mRepository.close();
+ mRepository = null;
+ }
+ }
+
+ protected Repository getRepository() {
+ return mRepository;
+ }
+
+ public TestQueryLogic(String name) {
+ super(name);
+ }
+
+ public void test_not() throws Exception {
+ final int count = 10;
+ populate(count);
+
+ final Storage<StorableTestBasic> storage =
+ getRepository().storageFor(StorableTestBasic.class);
+
+ StorableTestBasic stb;
+ Query<StorableTestBasic> query;
+ List<StorableTestBasic> results;
+
+ {
+ // Create query that returns nothing and verify.
+ query = storage.query();
+ assertEquals(Filter.getOpenFilter(StorableTestBasic.class), query.getFilter());
+ query = query.not();
+ assertEquals(Filter.getClosedFilter(StorableTestBasic.class), query.getFilter());
+ results = query.fetch().toList();
+ assertEquals(0, results.size());
+
+ // Verify that "not" again produces everything.
+ query = query.not();
+ results = query.fetch().toList();
+ assertEquals(count, results.size());
+ assertOrder(results, true);
+ }
+
+ {
+ // Create ordered query that returns nothing and verify.
+ query = storage.query();
+ query = query.orderBy("-intProp");
+ assertEquals(Filter.getOpenFilter(StorableTestBasic.class), query.getFilter());
+ query = query.not();
+ assertEquals(Filter.getClosedFilter(StorableTestBasic.class), query.getFilter());
+ results = query.fetch().toList();
+ assertEquals(0, results.size());
+
+ // Verify that order is preserved when "not" again.
+ query = query.not();
+ assertEquals(Filter.getOpenFilter(StorableTestBasic.class), query.getFilter());
+ results = query.fetch().toList();
+ assertEquals(count, results.size());
+ assertOrder(results, false);
+ }
+
+ {
+ // Do the ordered "not" test again, but change the sequence to build the query.
+ query = storage.query().not(); // do "not" before orderBy.
+ query = query.orderBy("-intProp");
+ assertEquals(Filter.getClosedFilter(StorableTestBasic.class), query.getFilter());
+ results = query.fetch().toList();
+ assertEquals(0, results.size());
+
+ // Verify that order is preserved when "not" again.
+ query = query.not();
+ assertEquals(Filter.getOpenFilter(StorableTestBasic.class), query.getFilter());
+ results = query.fetch().toList();
+ assertEquals(count, results.size());
+ assertOrder(results, false);
+ }
+ }
+
+ public void test_demorgans() throws Exception {
+ final int count = 10;
+ populate(count);
+
+ final Storage<StorableTestBasic> storage =
+ getRepository().storageFor(StorableTestBasic.class);
+
+ Query<StorableTestBasic> query;
+ List<StorableTestBasic> results;
+
+ {
+ query = storage.query("intProp = ? & (stringProp >= ? | longProp < ?)");
+ Filter<StorableTestBasic> filter1 = query.getFilter();
+ query = query.not();
+ Filter<StorableTestBasic> filter2 = query.getFilter();
+ query = storage.query("intProp != ? | (stringProp < ? & longProp >= ?)");
+ Filter<StorableTestBasic> filter3 = query.getFilter();
+
+ assertEquals(filter3, filter2);
+ }
+
+ // Try again, making sure that values are preserved in the query.
+ {
+ query = storage.query("intProp = ? & (stringProp >= ? | longProp < ?)");
+ query = query.with(5);
+
+ results = query.with("a").with(10).fetch().toList();
+ assertEquals(1, results.size());
+
+ query = query.not();
+ results = query.with("a").with(10).fetch().toList();
+ assertEquals(count - 1, results.size());
+
+ // Make sure record 5 is the one that's missing.
+ for (StorableTestBasic stb : results) {
+ assertFalse(5 == stb.getId());
+ }
+
+ // Should be back to the original query.
+ query = query.not();
+
+ results = query.with("a").with(10).fetch().toList();
+ assertEquals(1, results.size());
+ }
+
+ // Try again, making sure that a full set of values is preserved in the query.
+ {
+ query = storage.query("intProp = ? & (stringProp >= ? | longProp < ?)");
+ query = query.with(2).with("a").with(10);
+
+ results = query.fetch().toList();
+ assertEquals(1, results.size());
+
+ query = query.not();
+ results = query.fetch().toList();
+ assertEquals(count - 1, results.size());
+
+ // Make sure record 2 is the one that's missing.
+ for (StorableTestBasic stb : results) {
+ assertFalse(2 == stb.getId());
+ }
+
+ // Should be back to the original query.
+ query = query.not();
+
+ results = query.fetch().toList();
+ assertEquals(1, results.size());
+ }
+ }
+
+ public void test_and() throws Exception {
+ final int count = 10;
+ populate(count, new long[] {45, 12, 34, 12, 12, 45, 0, 0, 10, 2});
+
+ final Storage<StorableTestBasic> storage =
+ getRepository().storageFor(StorableTestBasic.class);
+
+ Query<StorableTestBasic> query;
+ List<StorableTestBasic> results;
+
+ {
+ query = storage.query("longProp = ?");
+ try {
+ query = query.and("stringProp != ?");
+ fail();
+ } catch (IllegalStateException e) {
+ // Good. Blank params exist.
+ }
+ }
+
+ {
+ query = storage.query("longProp = ?").with(12);
+ query = query.and("stringProp != ?").with("str_4");
+
+ results = query.fetch().toList();
+ assertEquals(2, results.size());
+ assertEquals(2, results.get(0).getId());
+ assertEquals(5, results.get(1).getId());
+ }
+
+ // Different value.
+ {
+ query = storage.query("longProp = ?").with(45);
+ query = query.and("stringProp != ?").with("str_4");
+
+ results = query.fetch().toList();
+ assertEquals(2, results.size());
+ assertEquals(1, results.get(0).getId());
+ assertEquals(6, results.get(1).getId());
+ }
+
+ // Different value with ordering.
+ {
+ query = storage.query("longProp = ?").with(45);
+ query = query.orderBy("-intProp");
+ query = query.and("stringProp != ?").with("str_4");
+
+ results = query.fetch().toList();
+ assertEquals(2, results.size());
+ assertEquals(1, results.get(1).getId());
+ assertEquals(6, results.get(0).getId());
+ }
+
+ // Try again with ordering.
+ {
+ query = storage.query("longProp = ?").with(12);
+ query = query.orderBy("-intProp");
+ query = query.and("stringProp != ?").with("str_4");
+
+ results = query.fetch().toList();
+ assertEquals(2, results.size());
+ assertEquals(2, results.get(1).getId());
+ assertEquals(5, results.get(0).getId());
+ }
+
+ // Invert the selection
+ {
+ query = storage.query("longProp = ?").with(12);
+ query = query.and("stringProp != ?").with("str_4").not();
+
+ results = query.fetch().toList();
+ assertEquals(count - 2, results.size());
+ assertEquals(1, results.get(0).getId());
+ assertEquals(3, results.get(1).getId());
+ assertEquals(4, results.get(2).getId());
+ assertEquals(6, results.get(3).getId());
+ assertEquals(7, results.get(4).getId());
+ assertEquals(8, results.get(5).getId());
+ assertEquals(9, results.get(6).getId());
+ assertEquals(10, results.get(7).getId());
+ }
+
+ // Invert the ordered selection
+ {
+ query = storage.query("longProp = ?").with(12);
+ query = query.orderBy("-intProp");
+ query = query.and("stringProp != ?").with("str_4").not();
+
+ results = query.fetch().toList();
+ assertEquals(count - 2, results.size());
+ assertEquals(1, results.get(7).getId());
+ assertEquals(3, results.get(6).getId());
+ assertEquals(4, results.get(5).getId());
+ assertEquals(6, results.get(4).getId());
+ assertEquals(7, results.get(3).getId());
+ assertEquals(8, results.get(2).getId());
+ assertEquals(9, results.get(1).getId());
+ assertEquals(10, results.get(0).getId());
+ }
+ }
+
+ public void test_or() throws Exception {
+ final int count = 10;
+ populate(count, new long[] {45, 12, 34, 12, 12, 45, 0, 0, 10, 2});
+
+ final Storage<StorableTestBasic> storage =
+ getRepository().storageFor(StorableTestBasic.class);
+
+ Query<StorableTestBasic> query;
+ List<StorableTestBasic> results;
+
+ {
+ query = storage.query("longProp = ?");
+ try {
+ query = query.or("stringProp = ?");
+ fail();
+ } catch (IllegalStateException e) {
+ // Good. Blank params exist.
+ }
+ }
+
+ {
+ query = storage.query("longProp = ?").with(45);
+ query = query.or("stringProp = ?").with("str_4");
+
+ results = query.fetch().toList();
+ assertEquals(3, results.size());
+ assertEquals(1, results.get(0).getId());
+ assertEquals(4, results.get(1).getId());
+ assertEquals(6, results.get(2).getId());
+ }
+
+ // Different value.
+ {
+ query = storage.query("longProp = ?").with(12);
+ query = query.or("stringProp = ?").with("str_3");
+
+ results = query.fetch().toList();
+ assertEquals(4, results.size());
+ assertEquals(2, results.get(0).getId());
+ assertEquals(3, results.get(1).getId());
+ assertEquals(4, results.get(2).getId());
+ assertEquals(5, results.get(3).getId());
+ }
+
+ // Different value with ordering.
+ {
+ query = storage.query("longProp = ?").with(12);
+ query = query.orderBy("-intProp");
+ query = query.or("stringProp = ?").with("str_3");
+
+ results = query.fetch().toList();
+ assertEquals(4, results.size());
+ assertEquals(2, results.get(3).getId());
+ assertEquals(3, results.get(2).getId());
+ assertEquals(4, results.get(1).getId());
+ assertEquals(5, results.get(0).getId());
+ }
+
+ // Try again with ordering.
+ {
+ query = storage.query("longProp = ?").with(45);
+ query = query.orderBy("-intProp");
+ query = query.or("stringProp = ?").with("str_4");
+
+ results = query.fetch().toList();
+ assertEquals(3, results.size());
+ assertEquals(1, results.get(2).getId());
+ assertEquals(4, results.get(1).getId());
+ assertEquals(6, results.get(0).getId());
+ }
+
+ // Invert the selection
+ {
+ query = storage.query("longProp = ?").with(45);
+ query = query.or("stringProp = ?").with("str_4").not();
+
+ results = query.fetch().toList();
+ assertEquals(count - 3, results.size());
+ assertEquals(2, results.get(0).getId());
+ assertEquals(3, results.get(1).getId());
+ assertEquals(5, results.get(2).getId());
+ assertEquals(7, results.get(3).getId());
+ assertEquals(8, results.get(4).getId());
+ assertEquals(9, results.get(5).getId());
+ assertEquals(10, results.get(6).getId());
+ }
+
+ // Invert the ordered selection
+ {
+ query = storage.query("longProp = ?").with(45);
+ query = query.orderBy("-intProp");
+ query = query.or("stringProp = ?").with("str_4").not();
+
+ results = query.fetch().toList();
+ assertEquals(count - 3, results.size());
+ assertEquals(2, results.get(6).getId());
+ assertEquals(3, results.get(5).getId());
+ assertEquals(5, results.get(4).getId());
+ assertEquals(7, results.get(3).getId());
+ assertEquals(8, results.get(2).getId());
+ assertEquals(9, results.get(1).getId());
+ assertEquals(10, results.get(0).getId());
+ }
+ }
+
+ //
+ // Same tests again, except against an indexed storable.
+ //
+
+ public void test_indexed_not() throws Exception {
+ final int count = 10;
+ populateIndexed(count);
+
+ final Storage<StorableTestBasicIndexed> storage =
+ getRepository().storageFor(StorableTestBasicIndexed.class);
+
+ StorableTestBasicIndexed stb;
+ Query<StorableTestBasicIndexed> query;
+ List<StorableTestBasicIndexed> results;
+
+ {
+ // Create query that returns nothing and verify.
+ query = storage.query();
+ assertEquals(Filter.getOpenFilter(StorableTestBasicIndexed.class), query.getFilter());
+ query = query.not();
+ assertEquals(Filter.getClosedFilter(StorableTestBasicIndexed.class), query.getFilter());
+ results = query.fetch().toList();
+ assertEquals(0, results.size());
+
+ // Verify that "not" again produces everything.
+ query = query.not();
+ results = query.fetch().toList();
+ assertEquals(count, results.size());
+ assertOrderIndexed(results, true);
+ }
+
+ {
+ // Create ordered query that returns nothing and verify.
+ query = storage.query();
+ query = query.orderBy("-intProp");
+ assertEquals(Filter.getOpenFilter(StorableTestBasicIndexed.class), query.getFilter());
+ query = query.not();
+ assertEquals(Filter.getClosedFilter(StorableTestBasicIndexed.class), query.getFilter());
+ results = query.fetch().toList();
+ assertEquals(0, results.size());
+
+ // Verify that order is preserved when "not" again.
+ query = query.not();
+ assertEquals(Filter.getOpenFilter(StorableTestBasicIndexed.class), query.getFilter());
+ results = query.fetch().toList();
+ assertEquals(count, results.size());
+ assertOrderIndexed(results, false);
+ }
+
+ {
+ // Do the ordered "not" test again, but change the sequence to build the query.
+ query = storage.query().not(); // do "not" before orderBy.
+ query = query.orderBy("-intProp");
+ assertEquals(Filter.getClosedFilter(StorableTestBasicIndexed.class), query.getFilter());
+ results = query.fetch().toList();
+ assertEquals(0, results.size());
+
+ // Verify that order is preserved when "not" again.
+ query = query.not();
+ assertEquals(Filter.getOpenFilter(StorableTestBasicIndexed.class), query.getFilter());
+ results = query.fetch().toList();
+ assertEquals(count, results.size());
+ assertOrderIndexed(results, false);
+ }
+ }
+
+ public void test_indexed_demorgans() throws Exception {
+ final int count = 10;
+ populateIndexed(count);
+
+ final Storage<StorableTestBasicIndexed> storage =
+ getRepository().storageFor(StorableTestBasicIndexed.class);
+
+ Query<StorableTestBasicIndexed> query;
+ List<StorableTestBasicIndexed> results;
+
+ {
+ query = storage.query("intProp = ? & (stringProp >= ? | longProp < ?)");
+ Filter<StorableTestBasicIndexed> filter1 = query.getFilter();
+ query = query.not();
+ Filter<StorableTestBasicIndexed> filter2 = query.getFilter();
+ query = storage.query("intProp != ? | (stringProp < ? & longProp >= ?)");
+ Filter<StorableTestBasicIndexed> filter3 = query.getFilter();
+
+ assertEquals(filter3, filter2);
+ }
+
+ // Try again, making sure that values are preserved in the query.
+ {
+ query = storage.query("intProp = ? & (stringProp >= ? | longProp < ?)");
+ query = query.with(5);
+
+ results = query.with("a").with(10).fetch().toList();
+ assertEquals(1, results.size());
+
+ query = query.not();
+ results = query.with("a").with(10).fetch().toList();
+ assertEquals(count - 1, results.size());
+
+ // Make sure record 5 is the one that's missing.
+ for (StorableTestBasicIndexed stb : results) {
+ assertFalse(5 == stb.getId());
+ }
+
+ // Should be back to the original query.
+ query = query.not();
+
+ results = query.with("a").with(10).fetch().toList();
+ assertEquals(1, results.size());
+ }
+
+ // Try again, making sure that a full set of values is preserved in the query.
+ {
+ query = storage.query("intProp = ? & (stringProp >= ? | longProp < ?)");
+ query = query.with(2).with("a").with(10);
+
+ results = query.fetch().toList();
+ assertEquals(1, results.size());
+
+ query = query.not();
+ results = query.fetch().toList();
+ assertEquals(count - 1, results.size());
+
+ // Make sure record 2 is the one that's missing.
+ for (StorableTestBasicIndexed stb : results) {
+ assertFalse(2 == stb.getId());
+ }
+
+ // Should be back to the original query.
+ query = query.not();
+
+ results = query.fetch().toList();
+ assertEquals(1, results.size());
+ }
+ }
+
+ public void test_indexed_and() throws Exception {
+ final int count = 10;
+ populateIndexed(count, new long[] {45, 12, 34, 12, 12, 45, 0, 0, 10, 2});
+
+ final Storage<StorableTestBasicIndexed> storage =
+ getRepository().storageFor(StorableTestBasicIndexed.class);
+
+ Query<StorableTestBasicIndexed> query;
+ List<StorableTestBasicIndexed> results;
+
+ {
+ query = storage.query("longProp = ?");
+ try {
+ query = query.and("stringProp != ?");
+ fail();
+ } catch (IllegalStateException e) {
+ // Good. Blank params exist.
+ }
+ }
+
+ {
+ query = storage.query("longProp = ?").with(12);
+ query = query.and("stringProp != ?").with("str_4");
+
+ results = query.fetch().toList();
+ assertEquals(2, results.size());
+ assertEquals(2, results.get(0).getId());
+ assertEquals(5, results.get(1).getId());
+ }
+
+ // Different value.
+ {
+ query = storage.query("longProp = ?").with(45);
+ query = query.and("stringProp != ?").with("str_4");
+
+ results = query.fetch().toList();
+ assertEquals(2, results.size());
+ assertEquals(1, results.get(0).getId());
+ assertEquals(6, results.get(1).getId());
+ }
+
+ // Different value with ordering.
+ {
+ query = storage.query("longProp = ?").with(45);
+ query = query.orderBy("-intProp");
+ query = query.and("stringProp != ?").with("str_4");
+
+ results = query.fetch().toList();
+ assertEquals(2, results.size());
+ assertEquals(1, results.get(1).getId());
+ assertEquals(6, results.get(0).getId());
+ }
+
+ // Try again with ordering.
+ {
+ query = storage.query("longProp = ?").with(12);
+ query = query.orderBy("-intProp");
+ query = query.and("stringProp != ?").with("str_4");
+
+ results = query.fetch().toList();
+ assertEquals(2, results.size());
+ assertEquals(2, results.get(1).getId());
+ assertEquals(5, results.get(0).getId());
+ }
+
+ // Invert the selection
+ {
+ query = storage.query("longProp = ?").with(12);
+ query = query.and("stringProp != ?").with("str_4").not();
+
+ results = query.fetch().toList();
+ assertEquals(count - 2, results.size());
+ assertEquals(1, results.get(0).getId());
+ assertEquals(3, results.get(1).getId());
+ assertEquals(4, results.get(2).getId());
+ assertEquals(6, results.get(3).getId());
+ assertEquals(7, results.get(4).getId());
+ assertEquals(8, results.get(5).getId());
+ assertEquals(9, results.get(6).getId());
+ assertEquals(10, results.get(7).getId());
+ }
+
+ // Invert the ordered selection
+ {
+ query = storage.query("longProp = ?").with(12);
+ query = query.orderBy("-intProp");
+ query = query.and("stringProp != ?").with("str_4").not();
+
+ results = query.fetch().toList();
+ assertEquals(count - 2, results.size());
+ assertEquals(1, results.get(7).getId());
+ assertEquals(3, results.get(6).getId());
+ assertEquals(4, results.get(5).getId());
+ assertEquals(6, results.get(4).getId());
+ assertEquals(7, results.get(3).getId());
+ assertEquals(8, results.get(2).getId());
+ assertEquals(9, results.get(1).getId());
+ assertEquals(10, results.get(0).getId());
+ }
+ }
+
+ public void test_indexed_or() throws Exception {
+ final int count = 10;
+ populateIndexed(count, new long[] {45, 12, 34, 12, 12, 45, 0, 0, 10, 2});
+
+ final Storage<StorableTestBasicIndexed> storage =
+ getRepository().storageFor(StorableTestBasicIndexed.class);
+
+ Query<StorableTestBasicIndexed> query;
+ List<StorableTestBasicIndexed> results;
+
+ {
+ query = storage.query("longProp = ?");
+ try {
+ query = query.or("stringProp = ?");
+ fail();
+ } catch (IllegalStateException e) {
+ // Good. Blank params exist.
+ }
+ }
+
+ {
+ query = storage.query("longProp = ?").with(45);
+ query = query.or("stringProp = ?").with("str_4");
+
+ results = query.fetch().toList();
+ assertEquals(3, results.size());
+ assertEquals(1, results.get(0).getId());
+ assertEquals(4, results.get(1).getId());
+ assertEquals(6, results.get(2).getId());
+ }
+
+ // Different value.
+ {
+ query = storage.query("longProp = ?").with(12);
+ query = query.or("stringProp = ?").with("str_3");
+
+ results = query.fetch().toList();
+ assertEquals(4, results.size());
+ assertEquals(2, results.get(0).getId());
+ assertEquals(3, results.get(1).getId());
+ assertEquals(4, results.get(2).getId());
+ assertEquals(5, results.get(3).getId());
+ }
+
+ // Different value with ordering.
+ {
+ query = storage.query("longProp = ?").with(12);
+ query = query.orderBy("-intProp");
+ query = query.or("stringProp = ?").with("str_3");
+
+ results = query.fetch().toList();
+ assertEquals(4, results.size());
+ assertEquals(2, results.get(3).getId());
+ assertEquals(3, results.get(2).getId());
+ assertEquals(4, results.get(1).getId());
+ assertEquals(5, results.get(0).getId());
+ }
+
+ // Try again with ordering.
+ {
+ query = storage.query("longProp = ?").with(45);
+ query = query.orderBy("-intProp");
+ query = query.or("stringProp = ?").with("str_4");
+
+ results = query.fetch().toList();
+ assertEquals(3, results.size());
+ assertEquals(1, results.get(2).getId());
+ assertEquals(4, results.get(1).getId());
+ assertEquals(6, results.get(0).getId());
+ }
+
+ // Invert the selection
+ {
+ query = storage.query("longProp = ?").with(45);
+ query = query.or("stringProp = ?").with("str_4").not();
+
+ results = query.fetch().toList();
+ assertEquals(count - 3, results.size());
+ assertEquals(2, results.get(0).getId());
+ assertEquals(3, results.get(1).getId());
+ assertEquals(5, results.get(2).getId());
+ assertEquals(7, results.get(3).getId());
+ assertEquals(8, results.get(4).getId());
+ assertEquals(9, results.get(5).getId());
+ assertEquals(10, results.get(6).getId());
+ }
+
+ // Invert the ordered selection
+ {
+ query = storage.query("longProp = ?").with(45);
+ query = query.orderBy("-intProp");
+ query = query.or("stringProp = ?").with("str_4").not();
+
+ results = query.fetch().toList();
+ assertEquals(count - 3, results.size());
+ assertEquals(2, results.get(6).getId());
+ assertEquals(3, results.get(5).getId());
+ assertEquals(5, results.get(4).getId());
+ assertEquals(7, results.get(3).getId());
+ assertEquals(8, results.get(2).getId());
+ assertEquals(9, results.get(1).getId());
+ assertEquals(10, results.get(0).getId());
+ }
+ }
+
+ private void populate(int count) throws Exception {
+ populate(count, null);
+ }
+
+ private void populate(int count, long[] longValues) throws Exception {
+ final Storage<StorableTestBasic> storage =
+ getRepository().storageFor(StorableTestBasic.class);
+
+ StorableTestBasic stb;
+
+ // Insert some test data
+ for (int i=1; i<=count; i++) {
+ stb = storage.prepare();
+ stb.initBasicProperties();
+ stb.setId(i);
+ stb.setIntProp(i);
+ stb.setStringProp("str_" + i);
+ if (longValues != null && i <= longValues.length) {
+ stb.setLongProp(longValues[i - 1]);
+ }
+ stb.insert();
+ }
+ }
+
+ private void populateIndexed(int count) throws Exception {
+ populateIndexed(count, null);
+ }
+
+ private void populateIndexed(int count, long[] longValues) throws Exception {
+ final Storage<StorableTestBasicIndexed> storage =
+ getRepository().storageFor(StorableTestBasicIndexed.class);
+
+ StorableTestBasicIndexed stb;
+
+ // Insert some test data
+ for (int i=1; i<=count; i++) {
+ stb = storage.prepare();
+ stb.initBasicProperties();
+ stb.setId(i);
+ stb.setIntProp(i);
+ stb.setStringProp("str_" + i);
+ if (longValues != null && i <= longValues.length) {
+ stb.setLongProp(longValues[i - 1]);
+ }
+ stb.insert();
+ }
+ }
+
+ private void assertOrder(List<StorableTestBasic> list, boolean ascending) throws Exception {
+ StorableTestBasic last = null;
+ for (StorableTestBasic stb : list) {
+ if (last != null) {
+ if (ascending) {
+ if (stb.getIntProp() <= last.getIntProp()) {
+ fail();
+ }
+ } else {
+ if (stb.getIntProp() >= last.getIntProp()) {
+ fail();
+ }
+ }
+ }
+ last = stb;
+ }
+ }
+
+ private void assertOrderIndexed(List<StorableTestBasicIndexed> list, boolean ascending)
+ throws Exception
+ {
+ StorableTestBasicIndexed last = null;
+ for (StorableTestBasicIndexed stb : list) {
+ if (last != null) {
+ if (ascending) {
+ if (stb.getIntProp() <= last.getIntProp()) {
+ fail();
+ }
+ } else {
+ if (stb.getIntProp() >= last.getIntProp()) {
+ fail();
+ }
+ }
+ }
+ last = stb;
+ }
+ }
+}
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/cursor/EmptyCursorFactory.java b/src/test/java/com/amazon/carbonado/cursor/EmptyCursorFactory.java new file mode 100644 index 0000000..fa27350 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/cursor/EmptyCursorFactory.java @@ -0,0 +1,30 @@ +/*
+ * 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.cursor;
+
+/**
+ * Creates new EmptyCursor instances, for tests in other packages.
+ *
+ * @author Brian S O'Neill
+ */
+public class EmptyCursorFactory {
+ public static EmptyCursor newEmptyCursor() {
+ return new EmptyCursor();
+ }
+}
diff --git a/src/test/java/com/amazon/carbonado/cursor/TestCursors.java b/src/test/java/com/amazon/carbonado/cursor/TestCursors.java new file mode 100644 index 0000000..fa5d8a7 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/cursor/TestCursors.java @@ -0,0 +1,428 @@ +/*
+ * 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.cursor;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.amazon.carbonado.Cursor;
+import com.amazon.carbonado.FetchException;
+
+import com.amazon.carbonado.stored.Dummy;
+import com.amazon.carbonado.stored.StorableTestMinimal;
+
+/**
+ *
+ * @author Brian S O'Neill
+ */
+public class TestCursors extends TestCase {
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static TestSuite suite() {
+ return new TestSuite(TestCursors.class);
+ }
+
+ public TestCursors(String name) {
+ super(name);
+ }
+
+ protected void setUp() {
+ }
+
+ protected void tearDown() {
+ }
+
+ public void testSingleton() throws Exception {
+ try {
+ new SingletonCursor<Object>(null);
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+
+ Cursor<String> cursor = new SingletonCursor<String>("hello");
+
+ assertTrue(cursor.hasNext());
+ assertEquals("hello", cursor.next());
+ assertFalse(cursor.hasNext());
+ try {
+ cursor.next();
+ fail();
+ } catch (NoSuchElementException e) {
+ }
+
+ assertEquals(0, cursor.skipNext(1));
+
+ cursor = new SingletonCursor<String>("world");
+ List<String> list = cursor.toList(0);
+ assertEquals(0, list.size());
+ list = cursor.toList(10);
+ assertEquals(1, list.size());
+ assertEquals("world", list.get(0));
+
+ cursor = new SingletonCursor<String>("world");
+ cursor.close();
+ assertFalse(cursor.hasNext());
+
+ cursor = new SingletonCursor<String>("world");
+ assertEquals(1, cursor.skipNext(2));
+ assertEquals(0, cursor.skipNext(1));
+ assertFalse(cursor.hasNext());
+ }
+
+ public void testUnion() throws Exception {
+ Cursor<Element> left, right, union;
+
+ // Two empty sets.
+ left = createElements();
+ right = createElements();
+ union = new UnionCursor<Element>(left, right, new ElementComparator());
+
+ compareElements(union);
+
+ // Right set empty.
+ left = createElements(1, 2, 3, 4);
+ right = createElements();
+ union = new UnionCursor<Element>(left, right, new ElementComparator());
+
+ compareElements(union, 1, 2, 3, 4);
+
+ // Left set empty.
+ left = createElements();
+ right = createElements(3, 4, 5, 6);
+ union = new UnionCursor<Element>(left, right, new ElementComparator());
+
+ compareElements(union, 3, 4, 5, 6);
+
+ // No overlap.
+ left = createElements(1, 2, 3 );
+ right = createElements( 4, 5, 6);
+ union = new UnionCursor<Element>(left, right, new ElementComparator());
+
+ compareElements(union, 1, 2, 3, 4, 5, 6);
+
+ // Overlap.
+ left = createElements(1, 2, 3, 4 );
+ right = createElements( 3, 4, 5, 6);
+ union = new UnionCursor<Element>(left, right, new ElementComparator());
+
+ compareElements(union, 1, 2, 3, 4, 5, 6);
+
+ // Swapped overlap.
+ left = createElements( 3, 4, 5, 6);
+ right = createElements(1, 2, 3, 4 );
+ union = new UnionCursor<Element>(left, right, new ElementComparator());
+
+ compareElements(union, 1, 2, 3, 4, 5, 6);
+
+ // Equivalent.
+ left = createElements(1, 2, 3, 4, 5, 6);
+ right = createElements(1, 2, 3, 4, 5, 6);
+ union = new UnionCursor<Element>(left, right, new ElementComparator());
+
+ compareElements(union, 1, 2, 3, 4, 5, 6);
+
+ // Complex.
+ left = createElements(1, 2, 3, 5, 6, 7, 11, 12, 13, 17, 18, 19);
+ right = createElements(1, 2, 4, 5, 6, 9, 10, 13, 14, 17, 18 );
+ union = new UnionCursor<Element>(left, right, new ElementComparator());
+
+ compareElements(union, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14, 17, 18, 19);
+
+ // Dups.
+ left = createElements(1, 2, 2, 3, 3, 3, 3 );
+ right = createElements(1, 1, 1, 1, 1, 2, 2, 2, 4, 4, 4);
+ union = new UnionCursor<Element>(left, right, new ElementComparator());
+
+ compareElements(union, 1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4);
+ }
+
+ public void testIntersection() throws Exception {
+ Cursor<Element> left, right, inter;
+
+ // Two empty sets.
+ left = createElements();
+ right = createElements();
+ inter = new IntersectionCursor<Element>(left, right, new ElementComparator());
+
+ compareElements(inter);
+
+ // Right set empty.
+ left = createElements(1, 2, 3, 4);
+ right = createElements();
+ inter = new IntersectionCursor<Element>(left, right, new ElementComparator());
+
+ compareElements(inter);
+
+ // Left set empty.
+ left = createElements();
+ right = createElements(3, 4, 5, 6);
+ inter = new IntersectionCursor<Element>(left, right, new ElementComparator());
+
+ compareElements(inter);
+
+ // No overlap.
+ left = createElements(1, 2, 3 );
+ right = createElements( 4, 5, 6);
+ inter = new IntersectionCursor<Element>(left, right, new ElementComparator());
+
+ compareElements(inter);
+
+ // Overlap.
+ left = createElements(1, 2, 3, 4 );
+ right = createElements( 3, 4, 5, 6);
+ inter = new IntersectionCursor<Element>(left, right, new ElementComparator());
+
+ compareElements(inter, 3, 4);
+
+ // Swapped overlap.
+ left = createElements( 3, 4, 5, 6);
+ right = createElements(1, 2, 3, 4 );
+ inter = new IntersectionCursor<Element>(left, right, new ElementComparator());
+
+ compareElements(inter, 3, 4);
+
+ // Equivalent.
+ left = createElements(1, 2, 3, 4, 5, 6);
+ right = createElements(1, 2, 3, 4, 5, 6);
+ inter = new IntersectionCursor<Element>(left, right, new ElementComparator());
+
+ compareElements(inter, 1, 2, 3, 4, 5, 6);
+
+ // Complex.
+ left = createElements(1, 2, 3, 5, 6, 7, 11, 12, 13, 17, 18, 19);
+ right = createElements(1, 2, 4, 5, 6, 9, 10, 13, 14, 17, 18 );
+ inter = new IntersectionCursor<Element>(left, right, new ElementComparator());
+
+ compareElements(inter, 1, 2, 5, 6, 13, 17, 18);
+
+ // Dups.
+ left = createElements(1, 2, 2, 3, 3, 3, 3 );
+ right = createElements(1, 1, 1, 1, 1, 2, 2, 2, 4, 4, 4);
+ inter = new IntersectionCursor<Element>(left, right, new ElementComparator());
+
+ compareElements(inter, 1, 2, 2);
+ }
+
+ public void testDifference() throws Exception {
+ Cursor<Element> left, right, diff;
+
+ // Two empty sets.
+ left = createElements();
+ right = createElements();
+ diff = new DifferenceCursor<Element>(left, right, new ElementComparator());
+
+ compareElements(diff);
+
+ // Right set empty.
+ left = createElements(1, 2, 3, 4);
+ right = createElements();
+ diff = new DifferenceCursor<Element>(left, right, new ElementComparator());
+
+ compareElements(diff, 1, 2, 3, 4);
+
+ // Left set empty.
+ left = createElements();
+ right = createElements(3, 4, 5, 6);
+ diff = new DifferenceCursor<Element>(left, right, new ElementComparator());
+
+ compareElements(diff);
+
+ // No overlap.
+ left = createElements(1, 2, 3 );
+ right = createElements( 4, 5, 6);
+ diff = new DifferenceCursor<Element>(left, right, new ElementComparator());
+
+ compareElements(diff, 1, 2, 3);
+
+ // Overlap.
+ left = createElements(1, 2, 3, 4 );
+ right = createElements( 3, 4, 5, 6);
+ diff = new DifferenceCursor<Element>(left, right, new ElementComparator());
+
+ compareElements(diff, 1, 2);
+
+ // Swapped overlap.
+ left = createElements( 3, 4, 5, 6);
+ right = createElements(1, 2, 3, 4 );
+ diff = new DifferenceCursor<Element>(left, right, new ElementComparator());
+
+ compareElements(diff, 5, 6);
+
+ // Equivalent.
+ left = createElements(1, 2, 3, 4, 5, 6);
+ right = createElements(1, 2, 3, 4, 5, 6);
+ diff = new DifferenceCursor<Element>(left, right, new ElementComparator());
+
+ compareElements(diff);
+
+ // Complex.
+ left = createElements(1, 2, 3, 5, 6, 7, 11, 12, 13, 17, 18, 19);
+ right = createElements(1, 2, 4, 5, 6, 9, 10, 13, 14, 17, 18 );
+ diff = new DifferenceCursor<Element>(left, right, new ElementComparator());
+
+ compareElements(diff, 3, 7, 11, 12, 19);
+
+ // Dups.
+ left = createElements(1, 2, 2, 3, 3, 3, 3 );
+ right = createElements(1, 1, 1, 1, 1, 2, 2, 2, 4, 4, 4);
+ diff = new DifferenceCursor<Element>(left, right, new ElementComparator());
+
+ compareElements(diff, 3, 3, 3, 3);
+ }
+
+ public void testSymmetricDifference() throws Exception {
+ Cursor<Element> left, right, diff;
+
+ // Two empty sets.
+ left = createElements();
+ right = createElements();
+ diff = new SymmetricDifferenceCursor<Element>(left, right, new ElementComparator());
+
+ compareElements(diff);
+
+ // Right set empty.
+ left = createElements(1, 2, 3, 4);
+ right = createElements();
+ diff = new SymmetricDifferenceCursor<Element>(left, right, new ElementComparator());
+
+ compareElements(diff, 1, 2, 3, 4);
+
+ // Left set empty.
+ left = createElements();
+ right = createElements(3, 4, 5, 6);
+ diff = new SymmetricDifferenceCursor<Element>(left, right, new ElementComparator());
+
+ compareElements(diff, 3, 4, 5, 6);
+
+ // No overlap.
+ left = createElements(1, 2, 3 );
+ right = createElements( 4, 5, 6);
+ diff = new SymmetricDifferenceCursor<Element>(left, right, new ElementComparator());
+
+ compareElements(diff, 1, 2, 3, 4, 5, 6);
+
+ // Overlap.
+ left = createElements(1, 2, 3, 4 );
+ right = createElements( 3, 4, 5, 6);
+ diff = new SymmetricDifferenceCursor<Element>(left, right, new ElementComparator());
+
+ compareElements(diff, 1, 2, 5, 6);
+
+ // Swapped overlap.
+ left = createElements( 3, 4, 5, 6);
+ right = createElements(1, 2, 3, 4 );
+ diff = new SymmetricDifferenceCursor<Element>(left, right, new ElementComparator());
+
+ compareElements(diff, 1, 2, 5, 6);
+
+ // Equivalent.
+ left = createElements(1, 2, 3, 4, 5, 6);
+ right = createElements(1, 2, 3, 4, 5, 6);
+ diff = new SymmetricDifferenceCursor<Element>(left, right, new ElementComparator());
+
+ compareElements(diff);
+
+ // Complex.
+ left = createElements(1, 2, 3, 5, 6, 7, 11, 12, 13, 17, 18, 19);
+ right = createElements(1, 2, 4, 5, 6, 9, 10, 13, 14, 17, 18 );
+ diff = new SymmetricDifferenceCursor<Element>(left, right, new ElementComparator());
+
+ compareElements(diff, 3, 4, 7, 9, 10, 11, 12, 14, 19);
+
+ // Dups.
+ left = createElements(1, 2, 2, 3, 3, 3, 3 );
+ right = createElements(1, 1, 1, 1, 1, 2, 2, 2, 4, 4, 4);
+ diff = new SymmetricDifferenceCursor<Element>(left, right, new ElementComparator());
+
+ compareElements(diff, 1, 1, 1, 1, 2, 3, 3, 3, 3, 4, 4, 4);
+ }
+
+ private Cursor<Element> createElements(int... ids) {
+ Arrays.sort(ids);
+ Element[] elements = new Element[ids.length];
+ for (int i=0; i<ids.length; i++) {
+ elements[i] = new Element(ids[i]);
+ }
+ return new IteratorCursor<Element>(Arrays.asList(elements));
+ }
+
+ private void compareElements(Cursor<Element> elements, int... expectedIDs)
+ throws FetchException
+ {
+ for (int id : expectedIDs) {
+ if (elements.hasNext()) {
+ Element e = elements.next();
+ if (e.getId() != id) {
+ fail("Element mismatch: expected=" + id + ", actual=" + e.getId());
+ elements.close();
+ return;
+ }
+ } else {
+ fail("Too few elements in cursor");
+ return;
+ }
+ }
+
+ if (elements.hasNext()) {
+ Element e = elements.next();
+ fail("Too many elements in cursor: " + e.getId());
+ elements.close();
+ }
+ }
+
+ private static class Element extends Dummy implements StorableTestMinimal {
+ private int mID;
+
+ Element(int id) {
+ mID = id;
+ }
+
+ public int getId() {
+ return mID;
+ }
+
+ public void setId(int id) {
+ mID = id;
+ }
+
+ public String toString() {
+ return "element " + mID;
+ }
+ }
+
+ private static class ElementComparator implements Comparator<Element> {
+ public int compare(Element a, Element b) {
+ int ai = a.getId();
+ int bi = b.getId();
+ if (ai < bi) {
+ return -1;
+ } else if (ai > bi) {
+ return 1;
+ }
+ return 0;
+ }
+ }
+}
diff --git a/src/test/java/com/amazon/carbonado/cursor/TestGroupedCursor.java b/src/test/java/com/amazon/carbonado/cursor/TestGroupedCursor.java new file mode 100644 index 0000000..3d5e42f --- /dev/null +++ b/src/test/java/com/amazon/carbonado/cursor/TestGroupedCursor.java @@ -0,0 +1,184 @@ +/*
+ * 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.cursor;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.amazon.carbonado.Cursor;
+import com.amazon.carbonado.FetchException;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+public class TestGroupedCursor extends TestCase {
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static TestSuite suite() {
+ return new TestSuite(TestGroupedCursor.class);
+ }
+
+ public TestGroupedCursor(String name) {
+ super(name);
+ }
+
+ protected void setUp() {
+ }
+
+ protected void tearDown() {
+ }
+
+ public void testGrouping() throws Exception {
+ List<Triple> triples = new ArrayList<Triple>();
+ triples.add(new Triple(1, 1, 1));
+ triples.add(new Triple(1, 1, 2));
+ triples.add(new Triple(1, 1, 3));
+ triples.add(new Triple(1, 2, 5));
+ triples.add(new Triple(1, 2, 9));
+ triples.add(new Triple(3, 1, 10));
+ triples.add(new Triple(3, 2, 16));
+ triples.add(new Triple(3, 2, 100));
+
+ // Should already be sorted, but no harm done
+ Collections.sort(triples);
+
+ Cursor<Pair> cursor = new GroupedCursor<Triple, Pair>
+ (new IteratorCursor<Triple>(triples), Triple.class, "a", "b")
+ {
+ private Pair aggregate;
+
+ protected void beginGroup(Triple groupLeader) {
+ aggregate = new Pair(groupLeader.getA(), groupLeader.getB());
+ aggregate.add(groupLeader.getC());
+ }
+
+ protected void addToGroup(Triple groupMember) {
+ aggregate.add(groupMember.getC());
+ }
+
+ protected Pair finishGroup() {
+ return aggregate;
+ }
+ };
+
+ List<Pair> pairs = new ArrayList<Pair>();
+
+ while (cursor.hasNext()) {
+ pairs.add(cursor.next());
+ }
+
+ assertEquals(4, pairs.size());
+
+ assertEquals(1, pairs.get(0).getA());
+ assertEquals(1, pairs.get(0).getB());
+ assertEquals(6, pairs.get(0).sum);
+
+ assertEquals(1, pairs.get(1).getA());
+ assertEquals(2, pairs.get(1).getB());
+ assertEquals(14, pairs.get(1).sum);
+
+ assertEquals(3, pairs.get(2).getA());
+ assertEquals(1, pairs.get(2).getB());
+ assertEquals(10, pairs.get(2).sum);
+
+ assertEquals(3, pairs.get(3).getA());
+ assertEquals(2, pairs.get(3).getB());
+ assertEquals(116, pairs.get(3).sum);
+ }
+
+ static int compare(int x, int y) {
+ if (x < y) {
+ return -1;
+ } else if (x > y) {
+ return 1;
+ }
+ return 0;
+ }
+
+ public static class Pair implements Comparable {
+ final int a;
+ final int b;
+
+ int sum;
+
+ Pair(int a, int b) {
+ this.a = a;
+ this.b = b;
+ }
+
+ public int getA() {
+ return a;
+ }
+
+ public int getB() {
+ return b;
+ }
+
+ public void add(int x) {
+ sum += x;
+ }
+
+ public int compareTo(Object obj) {
+ Pair other = (Pair) obj;
+ int result = compare(a, other.a);
+ if (result == 0) {
+ result = compare(b, other.b);
+ }
+ return result;
+ }
+
+ public String toString() {
+ return "a=" + a + ", b=" + b + ", sum=" + sum;
+ }
+ }
+
+ public static class Triple extends Pair {
+ final int c;
+
+ Triple(int a, int b, int c) {
+ super(a, b);
+ this.c = c;
+ }
+
+ public int getC() {
+ return c;
+ }
+
+ public int compareTo(Object obj) {
+ int result = super.compareTo(obj);
+ if (result == 0) {
+ Triple other = (Triple) obj;
+ result = compare(c, other.c);
+ }
+ return result;
+ }
+
+ public String toString() {
+ return "a=" + a + ", b=" + b + ", c=" + c;
+ }
+ }
+}
diff --git a/src/test/java/com/amazon/carbonado/filter/TestFilterAsJoinedFrom.java b/src/test/java/com/amazon/carbonado/filter/TestFilterAsJoinedFrom.java new file mode 100644 index 0000000..3b6bfde --- /dev/null +++ b/src/test/java/com/amazon/carbonado/filter/TestFilterAsJoinedFrom.java @@ -0,0 +1,156 @@ +/*
+ * 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.filter;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.amazon.carbonado.stored.Address;
+import com.amazon.carbonado.stored.Order;
+import com.amazon.carbonado.stored.Shipment;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+public class TestFilterAsJoinedFrom extends TestCase {
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static TestSuite suite() {
+ return new TestSuite(TestFilterAsJoinedFrom.class);
+ }
+
+ public TestFilterAsJoinedFrom(String name) {
+ super(name);
+ }
+
+ public void testOpen() {
+ Filter<Address> filter = Filter.getOpenFilter(Address.class);
+
+ Filter<Order> f2 = filter.asJoinedFrom(Order.class, "address");
+ assertTrue(f2 instanceof OpenFilter);
+ assertEquals(Order.class, f2.getStorableType());
+
+ Filter<Shipment> f3 = filter.asJoinedFrom(Shipment.class, "order.address");
+ assertTrue(f3 instanceof OpenFilter);
+ assertEquals(Shipment.class, f3.getStorableType());
+
+ Filter<Shipment> f4 = f2.asJoinedFrom(Shipment.class, "order");
+ assertEquals(f3, f4);
+ assertTrue(f3 == f4);
+ }
+
+ public void testClosed() {
+ Filter<Address> filter = Filter.getClosedFilter(Address.class);
+
+ Filter<Order> f2 = filter.asJoinedFrom(Order.class, "address");
+ assertTrue(f2 instanceof ClosedFilter);
+ assertEquals(Order.class, f2.getStorableType());
+
+ Filter<Shipment> f3 = filter.asJoinedFrom(Shipment.class, "order.address");
+ assertTrue(f3 instanceof ClosedFilter);
+ assertEquals(Shipment.class, f3.getStorableType());
+
+ Filter<Shipment> f4 = f2.asJoinedFrom(Shipment.class, "order");
+ assertEquals(f3, f4);
+ assertTrue(f3 == f4);
+ }
+
+ public void testSimple() {
+ Filter<Address> filter = Filter.filterFor(Address.class, "addressCity = ?");
+
+ Filter<Order> f2 = filter.asJoinedFrom(Order.class, "address");
+ assertEquals(f2, Filter.filterFor(Order.class, "address.addressCity = ?"));
+
+ Filter<Shipment> f3 = filter.asJoinedFrom(Shipment.class, "order.address");
+ assertEquals(f3, Filter.filterFor(Shipment.class, "order.address.addressCity = ?"));
+
+ Filter<Shipment> f4 = f2.asJoinedFrom(Shipment.class, "order");
+ assertEquals(f3, f4);
+ assertTrue(f3 == f4);
+ }
+
+ public void testAnd() {
+ Filter<Address> filter = Filter.filterFor
+ (Address.class, "addressCity = ? & addressZip != ?");
+
+ Filter<Order> f2 = filter.asJoinedFrom(Order.class, "address");
+ assertEquals(f2, Filter.filterFor
+ (Order.class, "address.addressCity = ? & address.addressZip != ?"));
+
+ Filter<Shipment> f3 = filter.asJoinedFrom(Shipment.class, "order.address");
+ assertEquals(f3, Filter.filterFor
+ (Shipment.class,
+ "order.address.addressCity = ? & order.address.addressZip != ?"));
+
+ Filter<Shipment> f4 = f2.asJoinedFrom(Shipment.class, "order");
+ assertEquals(f3, f4);
+ assertTrue(f3 == f4);
+ }
+
+ public void testOr() {
+ Filter<Address> filter = Filter.filterFor
+ (Address.class, "addressCity = ? | addressZip != ?");
+
+ Filter<Order> f2 = filter.asJoinedFrom(Order.class, "address");
+ assertEquals(f2, Filter.filterFor
+ (Order.class, "address.addressCity = ? | address.addressZip != ?"));
+
+ Filter<Shipment> f3 = filter.asJoinedFrom(Shipment.class, "order.address");
+ assertEquals(f3, Filter.filterFor
+ (Shipment.class,
+ "order.address.addressCity = ? | order.address.addressZip != ?"));
+
+ Filter<Shipment> f4 = f2.asJoinedFrom(Shipment.class, "order");
+ assertEquals(f3, f4);
+ assertTrue(f3 == f4);
+ }
+
+ public void testMixed() {
+ Filter<Address> filter = Filter.filterFor
+ (Address.class, "addressCity = ? & addressZip != ? | addressState > ?");
+
+ Filter<Order> f2 = filter.asJoinedFrom(Order.class, "address");
+ assertEquals
+ (f2, Filter.filterFor
+ (Order.class,
+ "address.addressCity = ? & address.addressZip != ? | address.addressState > ?"));
+
+ Filter<Shipment> f3 = filter.asJoinedFrom(Shipment.class, "order.address");
+ assertEquals(f3, Filter.filterFor
+ (Shipment.class,
+ "order.address.addressCity = ? & order.address.addressZip != ? " +
+ " | order.address.addressState > ?"));
+ Filter<Shipment> f4 = f2.asJoinedFrom(Shipment.class, "order");
+ assertEquals(f3, f4);
+ assertTrue(f3 == f4);
+ }
+
+ public void testError() {
+ try {
+ Filter<Address> filter = Filter.filterFor(Address.class, "addressCity = ?");
+ Filter<Order> f2 = filter.asJoinedFrom(Order.class, "orderTotal");
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+ }
+}
diff --git a/src/test/java/com/amazon/carbonado/filter/TestFilterNotJoinedFrom.java b/src/test/java/com/amazon/carbonado/filter/TestFilterNotJoinedFrom.java new file mode 100644 index 0000000..1e97d89 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/filter/TestFilterNotJoinedFrom.java @@ -0,0 +1,271 @@ +/*
+ * 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.filter;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.amazon.carbonado.stored.Address;
+import com.amazon.carbonado.stored.Order;
+import com.amazon.carbonado.stored.Shipment;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+public class TestFilterNotJoinedFrom extends TestCase {
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static TestSuite suite() {
+ return new TestSuite(TestFilterNotJoinedFrom.class);
+ }
+
+ public TestFilterNotJoinedFrom(String name) {
+ super(name);
+ }
+
+ public void testOpen() {
+ Filter<Shipment> filter = Filter.getOpenFilter(Shipment.class);
+
+ Filter<Shipment>.NotJoined nj = filter.notJoinedFrom("order");
+ assertTrue(nj.getNotJoinedFilter() instanceof OpenFilter);
+ assertEquals(filter, nj.getRemainderFilter());
+
+ nj = filter.notJoinedFrom("order.address");
+ assertTrue(nj.getNotJoinedFilter() instanceof OpenFilter);
+ assertEquals(filter, nj.getRemainderFilter());
+ }
+
+ public void testClosed() {
+ Filter<Shipment> filter = Filter.getClosedFilter(Shipment.class);
+
+ Filter<Shipment>.NotJoined nj = filter.notJoinedFrom("order");
+ assertTrue(nj.getNotJoinedFilter() instanceof OpenFilter);
+ assertEquals(filter, nj.getRemainderFilter());
+
+ nj = filter.notJoinedFrom("order.address");
+ assertTrue(nj.getNotJoinedFilter() instanceof OpenFilter);
+ assertEquals(filter, nj.getRemainderFilter());
+ }
+
+ public void testSimple() {
+ Filter<Shipment> filter = Filter.filterFor(Shipment.class, "shipmentNotes != ?");
+ filter = filter.bind();
+
+ Filter<Shipment>.NotJoined nj = filter.notJoinedFrom("order");
+ assertTrue(nj.getNotJoinedFilter() instanceof OpenFilter);
+ assertEquals(filter, nj.getRemainderFilter());
+
+ nj = filter.notJoinedFrom("order.address");
+ assertTrue(nj.getNotJoinedFilter() instanceof OpenFilter);
+ assertEquals(filter, nj.getRemainderFilter());
+
+ filter = Filter.filterFor(Shipment.class, "order.orderTotal < ?");
+ filter = filter.bind();
+
+ nj = filter.notJoinedFrom("order");
+ assertEquals(Filter.filterFor(Order.class, "orderTotal < ?").bind(),
+ nj.getNotJoinedFilter());
+ assertEquals(Filter.getOpenFilter(Shipment.class), nj.getRemainderFilter());
+
+ nj = filter.notJoinedFrom("order.address");
+ assertTrue(nj.getNotJoinedFilter() instanceof OpenFilter);
+ assertEquals(filter, nj.getRemainderFilter());
+
+ filter = Filter.filterFor(Shipment.class, "order.address.addressCity = ?");
+ filter = filter.bind();
+
+ nj = filter.notJoinedFrom("order");
+ assertEquals(Filter.filterFor(Order.class, "address.addressCity = ?").bind(),
+ nj.getNotJoinedFilter());
+ assertEquals(Filter.getOpenFilter(Shipment.class), nj.getRemainderFilter());
+
+ nj = filter.notJoinedFrom("order.address");
+ assertEquals(Filter.filterFor(Address.class, "addressCity = ?").bind(),
+ nj.getNotJoinedFilter());
+ assertEquals(Filter.getOpenFilter(Shipment.class), nj.getRemainderFilter());
+
+ try {
+ nj = filter.notJoinedFrom("order.address.addressCity");
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ public void testAnd() {
+ Filter<Shipment> filter = Filter.filterFor
+ (Shipment.class, "shipmentNotes != ? & shipmentDate > ?");
+ filter = filter.bind();
+
+ Filter<Shipment>.NotJoined nj = filter.notJoinedFrom("order");
+ assertTrue(nj.getNotJoinedFilter() instanceof OpenFilter);
+ assertEquals(filter, nj.getRemainderFilter());
+
+ nj = filter.notJoinedFrom("order.address");
+ assertTrue(nj.getNotJoinedFilter() instanceof OpenFilter);
+ assertEquals(filter, nj.getRemainderFilter());
+
+ filter = Filter.filterFor
+ (Shipment.class,
+ "order.orderTotal < ? & shipmentNotes != ? & order.orderComments = ?");
+ filter = filter.bind();
+
+ nj = filter.notJoinedFrom("order");
+ assertEquals(Filter.filterFor(Order.class, "orderTotal < ? & orderComments = ?").bind(),
+ nj.getNotJoinedFilter());
+ assertEquals(Filter.filterFor(Shipment.class, "shipmentNotes != ?").bind(),
+ nj.getRemainderFilter());
+
+ nj = filter.notJoinedFrom("order.address");
+ assertTrue(nj.getNotJoinedFilter() instanceof OpenFilter);
+ assertEquals(filter, nj.getRemainderFilter());
+
+ filter = Filter.filterFor
+ (Shipment.class,
+ "order.orderTotal < ? & order.address.addressCity != ? " +
+ "& order.address.addressZip = ? & shipmentNotes != ?");
+ filter = filter.bind();
+
+ nj = filter.notJoinedFrom("order");
+ assertEquals(Filter.filterFor(Order.class, "orderTotal < ? & address.addressCity != ? " +
+ "& address.addressZip = ?").bind(),
+ nj.getNotJoinedFilter());
+ assertEquals(Filter.filterFor(Shipment.class, "shipmentNotes != ?").bind(),
+ nj.getRemainderFilter());
+
+ nj = filter.notJoinedFrom("order.address");
+ assertEquals(Filter.filterFor(Address.class, "addressCity != ? & addressZip = ?").bind(),
+ nj.getNotJoinedFilter());
+ assertEquals(Filter.filterFor
+ (Shipment.class, "order.orderTotal < ? & shipmentNotes != ?").bind(),
+ nj.getRemainderFilter());
+ }
+
+ public void testOr() {
+ Filter<Shipment> filter = Filter.filterFor
+ (Shipment.class, "shipmentNotes != ? | shipmentDate > ?");
+ filter = filter.bind();
+
+ Filter<Shipment>.NotJoined nj = filter.notJoinedFrom("order");
+ assertTrue(nj.getNotJoinedFilter() instanceof OpenFilter);
+ assertEquals(filter, nj.getRemainderFilter());
+
+ nj = filter.notJoinedFrom("order.address");
+ assertTrue(nj.getNotJoinedFilter() instanceof OpenFilter);
+ assertEquals(filter, nj.getRemainderFilter());
+
+ filter = Filter.filterFor
+ (Shipment.class,
+ "order.orderTotal < ? | order.orderComments = ?");
+ filter = filter.bind();
+
+ nj = filter.notJoinedFrom("order");
+ assertEquals(Filter.filterFor(Order.class, "orderTotal < ? | orderComments = ?").bind(),
+ nj.getNotJoinedFilter());
+ assertEquals(Filter.getOpenFilter(Shipment.class), nj.getRemainderFilter());
+
+ filter = Filter.filterFor
+ (Shipment.class,
+ "order.orderTotal < ? | shipmentNotes != ? | order.orderComments = ?");
+ filter = filter.bind();
+
+ nj = filter.notJoinedFrom("order");
+ assertEquals(Filter.getOpenFilter(Order.class), nj.getNotJoinedFilter());
+ assertEquals(filter, nj.getRemainderFilter());
+
+ nj = filter.notJoinedFrom("order.address");
+ assertTrue(nj.getNotJoinedFilter() instanceof OpenFilter);
+ assertEquals(filter, nj.getRemainderFilter());
+
+ filter = Filter.filterFor
+ (Shipment.class,
+ "order.address.addressCity != ? | order.address.addressZip = ?");
+ filter = filter.bind();
+
+ nj = filter.notJoinedFrom("order");
+ assertEquals(Filter.filterFor(Order.class,
+ "address.addressCity != ? | address.addressZip = ?").bind(),
+ nj.getNotJoinedFilter());
+ assertEquals(Filter.getOpenFilter(Shipment.class), nj.getRemainderFilter());
+
+ nj = filter.notJoinedFrom("order.address");
+ assertEquals(Filter.filterFor(Address.class, "addressCity != ? | addressZip = ?").bind(),
+ nj.getNotJoinedFilter());
+ assertEquals(Filter.getOpenFilter(Shipment.class), nj.getRemainderFilter());
+
+ filter = Filter.filterFor
+ (Shipment.class,
+ "order.orderTotal < ? | order.address.addressCity != ? " +
+ "| order.address.addressZip = ? | shipmentNotes != ?");
+ filter = filter.bind();
+
+ nj = filter.notJoinedFrom("order");
+ assertEquals(Filter.getOpenFilter(Order.class), nj.getNotJoinedFilter());
+ assertEquals(filter, nj.getRemainderFilter());
+
+ nj = filter.notJoinedFrom("order.address");
+ assertEquals(Filter.getOpenFilter(Address.class), nj.getNotJoinedFilter());
+ assertEquals(filter, nj.getRemainderFilter());
+ }
+
+ public void testMixed() {
+ Filter<Shipment> filter = Filter.filterFor
+ (Shipment.class,
+ "(order.address.addressCity = ? & order.address.addressZip = ?) " +
+ "| order.orderTotal != ?");
+ filter = filter.bind();
+
+ Filter<Shipment>.NotJoined nj = filter.notJoinedFrom("order");
+ assertEquals(Filter.filterFor
+ (Order.class,
+ "address.addressCity = ? & address.addressZip = ? | orderTotal != ?").bind(),
+ nj.getNotJoinedFilter());
+ assertEquals(Filter.getOpenFilter(Shipment.class), nj.getRemainderFilter());
+
+ nj = filter.notJoinedFrom("order.address");
+ assertEquals(Filter.getOpenFilter(Address.class), nj.getNotJoinedFilter());
+ assertEquals(filter, nj.getRemainderFilter());
+
+ filter = filter.and("order.address.customData > ?");
+ filter = filter.bind();
+
+ nj = filter.notJoinedFrom("order.address");
+ assertEquals(Filter.filterFor(Address.class, "customData > ?").bind(),
+ nj.getNotJoinedFilter());
+ assertEquals(Filter.filterFor
+ (Shipment.class,
+ "(order.address.addressCity = ? | order.orderTotal != ?) " +
+ "& (order.address.addressZip = ? | order.orderTotal != ?)"),
+ nj.getRemainderFilter().unbind());
+
+ filter = filter.disjunctiveNormalForm();
+
+ nj = filter.notJoinedFrom("order.address");
+ assertEquals(Filter.filterFor(Address.class, "customData > ?").bind(),
+ nj.getNotJoinedFilter());
+ assertEquals(Filter.filterFor
+ (Shipment.class,
+ "order.address.addressCity = ? & order.address.addressZip = ? " +
+ "| order.orderTotal != ?").bind(),
+ nj.getRemainderFilter());
+ }
+}
diff --git a/src/test/java/com/amazon/carbonado/qe/TestFilteredQueryExecutor.java b/src/test/java/com/amazon/carbonado/qe/TestFilteredQueryExecutor.java new file mode 100644 index 0000000..1210bd4 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/qe/TestFilteredQueryExecutor.java @@ -0,0 +1,57 @@ +/*
+ * 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.qe;
+
+import junit.framework.TestSuite;
+
+import com.amazon.carbonado.filter.Filter;
+import com.amazon.carbonado.filter.FilterValues;
+
+import com.amazon.carbonado.stored.Address;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+public class TestFilteredQueryExecutor extends TestQueryExecutor {
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static TestSuite suite() {
+ return new TestSuite(TestFilteredQueryExecutor.class);
+ }
+
+ public void testBasicFiltering() throws Exception {
+ QueryExecutor<Address> unfiltered = createExecutor(1, 2, 3, 4);
+ Filter<Address> filter = Filter.filterFor(Address.class, "addressCountry > ?");
+ FilterValues<Address> values = filter.initialFilterValues();
+
+ QueryExecutor<Address> executor = new FilteredQueryExecutor<Address>(unfiltered, filter);
+
+ assertEquals(values.getFilter(), executor.getFilter());
+
+ assertEquals(2, executor.count(values.with("country_2")));
+
+ assertEquals(0, executor.getOrdering().size());
+
+ compareElements(executor.fetch(values.with("country_2")), 3, 4);
+ }
+}
diff --git a/src/test/java/com/amazon/carbonado/qe/TestFilteringScore.java b/src/test/java/com/amazon/carbonado/qe/TestFilteringScore.java new file mode 100644 index 0000000..e6b4374 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/qe/TestFilteringScore.java @@ -0,0 +1,746 @@ +/*
+ * 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.qe;
+
+import java.util.Comparator;
+import java.util.List;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.amazon.carbonado.Storable;
+
+import com.amazon.carbonado.info.Direction;
+import static com.amazon.carbonado.info.Direction.*;
+import com.amazon.carbonado.info.OrderedProperty;
+import com.amazon.carbonado.info.StorableIndex;
+import com.amazon.carbonado.info.StorableInfo;
+import com.amazon.carbonado.info.StorableIntrospector;
+import com.amazon.carbonado.info.StorableProperty;
+
+import com.amazon.carbonado.filter.Filter;
+import com.amazon.carbonado.filter.PropertyFilter;
+
+import com.amazon.carbonado.stored.StorableTestBasic;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+public class TestFilteringScore extends TestCase {
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static TestSuite suite() {
+ return new TestSuite(TestFilteringScore.class);
+ }
+
+ static <S extends Storable> StorableIndex<S> makeIndex(Class<S> type, String... props) {
+ return TestOrderingScore.makeIndex(type, props);
+ }
+
+ public TestFilteringScore(String name) {
+ super(name);
+ }
+
+ public void testNoFilter() throws Exception {
+ StorableIndex<StorableTestBasic> ix = makeIndex(StorableTestBasic.class, "id");
+ Filter<StorableTestBasic> filter = Filter.getOpenFilter(StorableTestBasic.class);
+ FilteringScore score = FilteringScore.evaluate(ix, filter);
+
+ assertFalse(score.isIndexClustered());
+ assertEquals(1, score.getIndexPropertyCount());
+
+ assertEquals(0, score.getIdentityCount());
+ assertEquals(0, score.getIdentityFilters().size());
+ assertFalse(score.hasRangeStart());
+ assertEquals(0, score.getRangeStartFilters().size());
+ assertFalse(score.hasRangeEnd());
+ assertEquals(0, score.getRangeEndFilters().size());
+ assertFalse(score.hasRangeMatch());
+ assertFalse(score.hasAnyMatches());
+ assertEquals(0, score.getRemainderCount());
+ assertEquals(0, score.getRemainderFilters().size());
+ }
+
+ public void testSimpleIndexMisses() throws Exception {
+ StorableIndex<StorableTestBasic> ix = makeIndex(StorableTestBasic.class, "intProp");
+ // Filter by a property not in index.
+ Filter<StorableTestBasic> filter = Filter.filterFor(StorableTestBasic.class, "id = ?");
+
+ FilteringScore score = FilteringScore.evaluate(ix, filter);
+
+ assertFalse(score.isIndexClustered());
+ assertEquals(1, score.getIndexPropertyCount());
+
+ assertEquals(0, score.getIdentityCount());
+ assertEquals(0, score.getIdentityFilters().size());
+ assertFalse(score.hasRangeStart());
+ assertEquals(0, score.getRangeStartFilters().size());
+ assertFalse(score.hasRangeEnd());
+ assertEquals(0, score.getRangeEndFilters().size());
+ assertFalse(score.hasRangeMatch());
+ assertFalse(score.hasAnyMatches());
+ assertEquals(1, score.getRemainderCount());
+ assertEquals(1, score.getRemainderFilters().size());
+ assertEquals(filter, score.getRemainderFilters().get(0));
+ assertEquals(filter, score.getRemainderFilter());
+
+ // Try again with matching property, but with an operator that cannot be used by index.
+ filter = Filter.filterFor(StorableTestBasic.class, "intProp != ?");
+
+ score = FilteringScore.evaluate(ix, filter);
+
+ assertFalse(score.isIndexClustered());
+ assertEquals(1, score.getIndexPropertyCount());
+
+ assertEquals(0, score.getIdentityCount());
+ assertEquals(0, score.getIdentityFilters().size());
+ assertFalse(score.hasRangeStart());
+ assertEquals(0, score.getRangeStartFilters().size());
+ assertFalse(score.hasRangeEnd());
+ assertEquals(0, score.getRangeEndFilters().size());
+ assertFalse(score.hasRangeMatch());
+ assertFalse(score.hasAnyMatches());
+ assertEquals(1, score.getRemainderCount());
+ assertEquals(1, score.getRemainderFilters().size());
+ assertEquals(filter, score.getRemainderFilters().get(0));
+ assertEquals(filter, score.getRemainderFilter());
+ }
+
+ public void testSimpleIndexMatches() throws Exception {
+ StorableIndex<StorableTestBasic> ix = makeIndex(StorableTestBasic.class, "id");
+ // Filter by a property in index.
+ Filter<StorableTestBasic> filter = Filter.filterFor(StorableTestBasic.class, "id = ?");
+
+ FilteringScore score = FilteringScore.evaluate(ix, filter);
+
+ assertFalse(score.isIndexClustered());
+ assertEquals(1, score.getIndexPropertyCount());
+
+ assertEquals(1, score.getIdentityCount());
+ assertEquals(1, score.getArrangementScore());
+ assertEquals(filter, score.getIdentityFilters().get(0));
+ assertFalse(score.hasRangeStart());
+ assertEquals(0, score.getRangeStartFilters().size());
+ assertFalse(score.hasRangeEnd());
+ assertEquals(0, score.getRangeEndFilters().size());
+ assertFalse(score.hasRangeMatch());
+ assertTrue(score.hasAnyMatches());
+ assertEquals(0, score.getRemainderCount());
+ assertEquals(0, score.getRemainderFilters().size());
+ assertEquals(null, score.getRemainderFilter());
+
+ // Try again with open ranges.
+ for (int i=0; i<4; i++) {
+ String expr;
+ switch (i) {
+ default: expr = "id > ?"; break;
+ case 1: expr = "id >= ?"; break;
+ case 2: expr = "id < ?"; break;
+ case 3: expr = "id <= ?"; break;
+ }
+
+ filter = Filter.filterFor(StorableTestBasic.class, expr);
+
+ score = FilteringScore.evaluate(ix, filter);
+
+ assertEquals(0, score.getIdentityCount());
+ assertEquals(0, score.getIdentityFilters().size());
+ assertFalse(score.hasRangeMatch());
+ assertTrue(score.hasAnyMatches());
+ assertEquals(0, score.getRemainderCount());
+ assertEquals(0, score.getRemainderFilters().size());
+ assertEquals(null, score.getRemainderFilter());
+
+ if (i < 2) {
+ assertTrue(score.hasRangeStart());
+ assertEquals(1, score.getRangeStartFilters().size());
+ assertEquals(filter, score.getRangeStartFilters().get(0));
+ assertFalse(score.hasRangeEnd());
+ assertEquals(0, score.getRangeEndFilters().size());
+ } else {
+ assertFalse(score.hasRangeStart());
+ assertEquals(0, score.getRangeStartFilters().size());
+ assertTrue(score.hasRangeEnd());
+ assertEquals(1, score.getRangeEndFilters().size());
+ assertEquals(filter, score.getRangeEndFilters().get(0));
+ }
+ }
+
+ // Try with duplicate open ranges.
+ filter = Filter.filterFor(StorableTestBasic.class, "id > ? & id > ?");
+
+ score = FilteringScore.evaluate(ix, filter);
+
+ assertEquals(0, score.getIdentityCount());
+ assertEquals(0, score.getIdentityFilters().size());
+ assertFalse(score.hasRangeMatch());
+ assertTrue(score.hasAnyMatches());
+ assertEquals(0, score.getRemainderCount());
+ assertEquals(0, score.getRemainderFilters().size());
+ assertEquals(null, score.getRemainderFilter());
+
+ assertTrue(score.hasRangeStart());
+ assertEquals(2, score.getRangeStartFilters().size());
+ assertEquals(Filter.filterFor(StorableTestBasic.class, "id > ?"),
+ score.getRangeStartFilters().get(0));
+ assertEquals(Filter.filterFor(StorableTestBasic.class, "id > ?"),
+ score.getRangeStartFilters().get(1));
+ assertFalse(score.hasRangeEnd());
+ assertEquals(0, score.getRangeEndFilters().size());
+
+ // Try with duplicate end ranges and slightly different operator.
+ filter = Filter.filterFor(StorableTestBasic.class, "id < ? & id <= ?");
+
+ score = FilteringScore.evaluate(ix, filter);
+
+ assertEquals(0, score.getIdentityCount());
+ assertEquals(0, score.getIdentityFilters().size());
+ assertFalse(score.hasRangeMatch());
+ assertTrue(score.hasAnyMatches());
+ assertEquals(0, score.getRemainderCount());
+ assertEquals(0, score.getRemainderFilters().size());
+ assertEquals(null, score.getRemainderFilter());
+
+ assertFalse(score.hasRangeStart());
+ assertEquals(0, score.getRangeStartFilters().size());
+ assertTrue(score.hasRangeEnd());
+ assertEquals(2, score.getRangeEndFilters().size());
+ assertEquals(Filter.filterFor(StorableTestBasic.class, "id < ?"),
+ score.getRangeEndFilters().get(0));
+ assertEquals(Filter.filterFor(StorableTestBasic.class, "id <= ?"),
+ score.getRangeEndFilters().get(1));
+
+ // Try with complete range.
+ filter = Filter.filterFor(StorableTestBasic.class, "id > ? & id < ?");
+
+ score = FilteringScore.evaluate(ix, filter);
+
+ assertEquals(0, score.getIdentityCount());
+ assertEquals(0, score.getIdentityFilters().size());
+ assertTrue(score.hasRangeMatch());
+ assertTrue(score.hasAnyMatches());
+ assertEquals(0, score.getRemainderCount());
+ assertEquals(0, score.getRemainderFilters().size());
+ assertEquals(null, score.getRemainderFilter());
+
+ assertTrue(score.hasRangeStart());
+ assertEquals(1, score.getRangeStartFilters().size());
+ assertEquals(Filter.filterFor(StorableTestBasic.class, "id > ?"),
+ score.getRangeStartFilters().get(0));
+ assertTrue(score.hasRangeEnd());
+ assertEquals(1, score.getRangeEndFilters().size());
+ assertEquals(Filter.filterFor(StorableTestBasic.class, "id < ?"),
+ score.getRangeEndFilters().get(0));
+
+ // Try with an identity filter consuming others.
+ filter = Filter.filterFor(StorableTestBasic.class, "id > ? & id = ? & id = ?");
+
+ score = FilteringScore.evaluate(ix, filter);
+
+ assertEquals(1, score.getIdentityCount());
+ assertEquals(1, score.getIdentityFilters().size());
+ assertEquals(1, score.getArrangementScore());
+ assertEquals(Filter.filterFor(StorableTestBasic.class, "id = ?"),
+ score.getIdentityFilters().get(0));
+
+ assertFalse(score.hasRangeStart());
+ assertFalse(score.hasRangeEnd());
+ assertFalse(score.hasRangeMatch());
+ assertTrue(score.hasAnyMatches());
+ assertEquals(2, score.getRemainderCount());
+ assertEquals(2, score.getRemainderFilters().size());
+ // Order of properties in filter accounts for sorting by PropertyFilterList.
+ assertEquals(Filter.filterFor(StorableTestBasic.class, "id = ? & id > ?"),
+ score.getRemainderFilter());
+
+ // Try with complex mix of non-identity matches.
+ filter = Filter.filterFor
+ (StorableTestBasic.class,
+ "id > ? & id != ? & id <= ? & id >= ? & id != ? & id > ?");
+
+ score = FilteringScore.evaluate(ix, filter);
+
+ assertEquals(0, score.getIdentityCount());
+ assertEquals(0, score.getIdentityFilters().size());
+
+ assertTrue(score.hasRangeMatch());
+ assertTrue(score.hasAnyMatches());
+
+ assertTrue(score.hasRangeStart());
+ assertEquals(3, score.getRangeStartFilters().size());
+ assertEquals(Filter.filterFor(StorableTestBasic.class, "id > ?"),
+ score.getRangeStartFilters().get(0));
+ assertEquals(Filter.filterFor(StorableTestBasic.class, "id >= ?"),
+ score.getRangeStartFilters().get(1));
+ assertEquals(Filter.filterFor(StorableTestBasic.class, "id > ?"),
+ score.getRangeStartFilters().get(2));
+
+ assertTrue(score.hasRangeEnd());
+ assertEquals(1, score.getRangeEndFilters().size());
+ assertEquals(Filter.filterFor(StorableTestBasic.class, "id <= ?"),
+ score.getRangeEndFilters().get(0));
+
+ assertEquals(2, score.getRemainderCount());
+ assertEquals(2, score.getRemainderFilters().size());
+ assertEquals(Filter.filterFor(StorableTestBasic.class, "id != ? & id != ?"),
+ score.getRemainderFilter());
+ }
+
+ public void testCompositeIndexMatches() throws Exception {
+ StorableIndex<StorableTestBasic> ix = makeIndex(StorableTestBasic.class,
+ "id", "intProp", "stringProp");
+ // Filter by a property in index.
+ Filter<StorableTestBasic> filter = Filter.filterFor(StorableTestBasic.class, "id = ?");
+
+ FilteringScore score = FilteringScore.evaluate(ix, filter);
+
+ assertFalse(score.isIndexClustered());
+ assertEquals(3, score.getIndexPropertyCount());
+
+ assertEquals(1, score.getIdentityCount());
+ assertEquals(1, score.getArrangementScore());
+ assertEquals(filter, score.getIdentityFilters().get(0));
+ assertFalse(score.hasRangeStart());
+ assertEquals(0, score.getRangeStartFilters().size());
+ assertFalse(score.hasRangeEnd());
+ assertEquals(0, score.getRangeEndFilters().size());
+ assertFalse(score.hasRangeMatch());
+ assertTrue(score.hasAnyMatches());
+ assertEquals(0, score.getRemainderCount());
+ assertEquals(0, score.getRemainderFilters().size());
+ assertEquals(null, score.getRemainderFilter());
+
+ // Filter by a property with a gap. (filter must include "id" to use index)
+ filter = Filter.filterFor(StorableTestBasic.class, "intProp = ?");
+
+ score = FilteringScore.evaluate(ix, filter);
+
+ assertEquals(0, score.getIdentityCount());
+ assertFalse(score.hasAnyMatches());
+ assertEquals(1, score.getRemainderCount());
+ assertEquals(1, score.getRemainderFilters().size());
+ assertEquals(filter, score.getRemainderFilter());
+
+ // Filter by a property with a gap and a range operator. (index still cannot be used)
+ filter = Filter.filterFor(StorableTestBasic.class, "intProp = ? & stringProp < ?");
+
+ score = FilteringScore.evaluate(ix, filter);
+
+ assertEquals(0, score.getIdentityCount());
+ assertFalse(score.hasAnyMatches());
+ assertEquals(2, score.getRemainderCount());
+ assertEquals(2, score.getRemainderFilters().size());
+ assertEquals(filter, score.getRemainderFilter());
+
+ // Filter with range match before identity match. Identity cannot be used.
+ filter = Filter.filterFor(StorableTestBasic.class, "intProp = ? & id < ?");
+
+ score = FilteringScore.evaluate(ix, filter);
+
+ assertEquals(0, score.getIdentityCount());
+ assertTrue(score.hasAnyMatches());
+
+ assertTrue(score.hasRangeEnd());
+ assertEquals(1, score.getRangeEndFilters().size());
+ assertEquals(Filter.filterFor(StorableTestBasic.class, "id < ?"),
+ score.getRangeEndFilters().get(0));
+
+ assertEquals(1, score.getRemainderCount());
+ assertEquals(1, score.getRemainderFilters().size());
+ assertEquals(Filter.filterFor(StorableTestBasic.class, "intProp = ?"),
+ score.getRemainderFilter());
+
+ // Filter with fully specified identity match, but a few remaining.
+ filter = Filter.filterFor
+ (StorableTestBasic.class,
+ "intProp = ? & id = ? & stringProp = ? & stringProp > ? & doubleProp = ?");
+
+ score = FilteringScore.evaluate(ix, filter);
+
+ assertTrue(score.hasAnyMatches());
+ assertEquals(3, score.getIdentityCount());
+ assertEquals(2, score.getArrangementScore());
+
+ assertEquals(Filter.filterFor(StorableTestBasic.class, "id = ?"),
+ score.getIdentityFilters().get(0));
+ assertEquals(Filter.filterFor(StorableTestBasic.class, "intProp = ?"),
+ score.getIdentityFilters().get(1));
+ assertEquals(Filter.filterFor(StorableTestBasic.class, "stringProp = ?"),
+ score.getIdentityFilters().get(2));
+
+ assertEquals(2, score.getRemainderCount());
+ assertEquals(2, score.getRemainderFilters().size());
+ // Order of remainder properties accounts for sorting by PropertyFilterList.
+ assertEquals(Filter.filterFor(StorableTestBasic.class, "doubleProp = ?"),
+ score.getRemainderFilters().get(0));
+ assertEquals(Filter.filterFor(StorableTestBasic.class, "stringProp > ?"),
+ score.getRemainderFilters().get(1));
+
+ // Filter with identity and range matches.
+ filter = Filter.filterFor
+ (StorableTestBasic.class,
+ "intProp > ? & id = ? & stringProp = ? & intProp <= ? & " +
+ "stringProp < ? & doubleProp = ?");
+
+ score = FilteringScore.evaluate(ix, filter);
+
+ assertTrue(score.hasAnyMatches());
+ assertEquals(1, score.getIdentityCount());
+ assertEquals(1, score.getArrangementScore());
+
+ assertEquals(3, score.getRemainderCount());
+ assertEquals(3, score.getRemainderFilters().size());
+ // Order of remainder properties accounts for sorting by PropertyFilterList.
+ assertEquals(Filter.filterFor(StorableTestBasic.class, "stringProp = ?"),
+ score.getRemainderFilters().get(0));
+ assertEquals(Filter.filterFor(StorableTestBasic.class, "doubleProp = ?"),
+ score.getRemainderFilters().get(1));
+ assertEquals(Filter.filterFor(StorableTestBasic.class, "stringProp < ?"),
+ score.getRemainderFilters().get(2));
+
+ assertTrue(score.hasRangeMatch());
+ assertTrue(score.hasRangeStart());
+ assertEquals(1, score.getRangeStartFilters().size());
+ assertEquals(Filter.filterFor(StorableTestBasic.class, "intProp > ?"),
+ score.getRangeStartFilters().get(0));
+
+ assertTrue(score.hasRangeEnd());
+ assertEquals(1, score.getRangeEndFilters().size());
+ assertEquals(Filter.filterFor(StorableTestBasic.class, "intProp <= ?"),
+ score.getRangeEndFilters().get(0));
+
+ // Filter with fully specified identity match, with backwards arrangement.
+ filter = Filter.filterFor
+ (StorableTestBasic.class, "stringProp = ? & intProp = ? & id = ?");
+
+ score = FilteringScore.evaluate(ix, filter);
+
+ assertTrue(score.hasAnyMatches());
+ assertEquals(3, score.getIdentityCount());
+ // Arrangement score is always at least one if identity count is not zero.
+ assertEquals(1, score.getArrangementScore());
+ }
+
+ public void testReverseRange() throws Exception {
+ StorableIndex<StorableTestBasic> ix = makeIndex(StorableTestBasic.class,
+ "id", "intProp");
+ Filter<StorableTestBasic> filter =
+ Filter.filterFor(StorableTestBasic.class, "id = ? & intProp > ?");
+
+ FilteringScore score = FilteringScore.evaluate(ix, filter);
+
+ assertFalse(score.shouldReverseRange());
+
+ ix = makeIndex(StorableTestBasic.class, "id", "-intProp");
+ score = FilteringScore.evaluate(ix, filter);
+
+ assertTrue(score.shouldReverseRange());
+ }
+
+ public void testRangeComparator() throws Exception {
+ StorableIndex<StorableTestBasic> ix_1, ix_2;
+ Filter<StorableTestBasic> filter;
+ FilteringScore score_1, score_2;
+ Comparator<FilteringScore<?>> comp = FilteringScore.rangeComparator();
+
+ ix_1 = makeIndex(StorableTestBasic.class, "id", "intProp", "doubleProp", "-stringProp");
+ ix_2 = makeIndex(StorableTestBasic.class, "id", "stringProp", "intProp");
+
+ filter = Filter.filterFor(StorableTestBasic.class, "id = ?");
+ score_1 = FilteringScore.evaluate(ix_1, filter);
+ score_2 = FilteringScore.evaluate(ix_2, filter);
+
+ assertEquals(0, comp.compare(score_1, score_2));
+ assertEquals(0, comp.compare(score_2, score_1));
+
+ score_1 = FilteringScore.evaluate(ix_1.clustered(true), filter);
+ score_2 = FilteringScore.evaluate(ix_2, filter);
+
+ assertEquals(-1, comp.compare(score_1, score_2));
+
+ filter = Filter.filterFor(StorableTestBasic.class, "stringProp = ?");
+ score_1 = FilteringScore.evaluate(ix_1, filter);
+ score_2 = FilteringScore.evaluate(ix_2, filter);
+
+ assertEquals(0, comp.compare(score_1, score_2));
+ assertEquals(0, comp.compare(score_2, score_1));
+
+ filter = Filter.filterFor(StorableTestBasic.class, "stringProp = ?");
+ score_1 = FilteringScore.evaluate(ix_1.clustered(true), filter);
+ score_2 = FilteringScore.evaluate(ix_2, filter);
+
+ assertEquals(0, comp.compare(score_1, score_2));
+ assertEquals(0, comp.compare(score_2, score_1));
+
+ filter = Filter.filterFor(StorableTestBasic.class, "id = ? & intProp = ?");
+ score_1 = FilteringScore.evaluate(ix_1, filter);
+ score_2 = FilteringScore.evaluate(ix_2, filter);
+
+ assertEquals(-1, comp.compare(score_1, score_2));
+ assertEquals(1, comp.compare(score_2, score_1));
+
+ filter = Filter.filterFor(StorableTestBasic.class, "id = ? & stringProp != ?");
+ score_1 = FilteringScore.evaluate(ix_1, filter);
+ score_2 = FilteringScore.evaluate(ix_2, filter);
+
+ assertEquals(0, comp.compare(score_1, score_2));
+ assertEquals(0, comp.compare(score_2, score_1));
+
+ filter = Filter.filterFor(StorableTestBasic.class, "id = ? & stringProp = ?");
+ score_1 = FilteringScore.evaluate(ix_1, filter);
+ score_2 = FilteringScore.evaluate(ix_2, filter);
+
+ assertEquals(1, comp.compare(score_1, score_2));
+ assertEquals(-1, comp.compare(score_2, score_1));
+
+ // Both indexes are as good, since range is open.
+ filter = Filter.filterFor(StorableTestBasic.class, "id = ? & stringProp > ?");
+ score_1 = FilteringScore.evaluate(ix_1, filter);
+ score_2 = FilteringScore.evaluate(ix_2, filter);
+
+ assertEquals(0, comp.compare(score_1, score_2));
+ assertEquals(0, comp.compare(score_2, score_1));
+
+ filter = Filter.filterFor(StorableTestBasic.class,
+ "id = ? & intProp = ? & stringProp > ?");
+ score_1 = FilteringScore.evaluate(ix_1, filter);
+ score_2 = FilteringScore.evaluate(ix_2, filter);
+
+ assertEquals(-1, comp.compare(score_1, score_2));
+ assertEquals(1, comp.compare(score_2, score_1));
+
+ // Test range match with tie resolved by clustered index.
+ filter = Filter.filterFor(StorableTestBasic.class, "id >= ? & id < ?");
+ score_1 = FilteringScore.evaluate(ix_1, filter);
+ score_2 = FilteringScore.evaluate(ix_2, filter);
+
+ assertEquals(0, comp.compare(score_1, score_2));
+ assertEquals(0, comp.compare(score_2, score_1));
+
+ filter = Filter.filterFor(StorableTestBasic.class, "id >= ? & id < ?");
+ score_1 = FilteringScore.evaluate(ix_1, filter);
+ score_2 = FilteringScore.evaluate(ix_2.clustered(true), filter);
+
+ assertEquals(1, comp.compare(score_1, score_2));
+ assertEquals(-1, comp.compare(score_2, score_1));
+
+ // Test open range match with tie still not resolved by clustered index.
+ filter = Filter.filterFor(StorableTestBasic.class, "id >= ?");
+ score_1 = FilteringScore.evaluate(ix_1, filter);
+ score_2 = FilteringScore.evaluate(ix_2, filter);
+
+ assertEquals(0, comp.compare(score_1, score_2));
+ assertEquals(0, comp.compare(score_2, score_1));
+
+ filter = Filter.filterFor(StorableTestBasic.class, "id >= ?");
+ score_1 = FilteringScore.evaluate(ix_1, filter);
+ score_2 = FilteringScore.evaluate(ix_2.clustered(true), filter);
+
+ assertEquals(0, comp.compare(score_1, score_2));
+ assertEquals(0, comp.compare(score_2, score_1));
+ }
+
+ public void testFullComparator() throws Exception {
+ StorableIndex<StorableTestBasic> ix_1, ix_2;
+ Filter<StorableTestBasic> filter;
+ FilteringScore score_1, score_2;
+ Comparator<FilteringScore<?>> comp = FilteringScore.fullComparator();
+
+ ix_1 = makeIndex(StorableTestBasic.class, "id", "intProp", "doubleProp", "-stringProp");
+ ix_2 = makeIndex(StorableTestBasic.class, "id", "stringProp", "intProp");
+
+ filter = Filter.filterFor(StorableTestBasic.class, "id = ?");
+ score_1 = FilteringScore.evaluate(ix_1, filter);
+ score_2 = FilteringScore.evaluate(ix_2, filter);
+
+ // Second is better because it has fewer properties.
+ assertEquals(1, comp.compare(score_1, score_2));
+ assertEquals(-1, comp.compare(score_2, score_1));
+
+ score_1 = FilteringScore.evaluate(ix_1.clustered(true), filter);
+ score_2 = FilteringScore.evaluate(ix_2, filter);
+
+ assertEquals(-1, comp.compare(score_1, score_2));
+
+ filter = Filter.filterFor(StorableTestBasic.class, "stringProp = ?");
+ score_1 = FilteringScore.evaluate(ix_1, filter);
+ score_2 = FilteringScore.evaluate(ix_2, filter);
+
+ // Although no property matches, second is better just because it has fewer properties.
+ assertEquals(1, comp.compare(score_1, score_2));
+ assertEquals(-1, comp.compare(score_2, score_1));
+
+ filter = Filter.filterFor(StorableTestBasic.class, "stringProp = ?");
+ score_1 = FilteringScore.evaluate(ix_1.clustered(true), filter);
+ score_2 = FilteringScore.evaluate(ix_2, filter);
+
+ // Although no property matches, first is better just because it is clustered.
+ assertEquals(-1, comp.compare(score_1, score_2));
+ assertEquals(1, comp.compare(score_2, score_1));
+
+ filter = Filter.filterFor(StorableTestBasic.class, "id = ? & intProp = ?");
+ score_1 = FilteringScore.evaluate(ix_1, filter);
+ score_2 = FilteringScore.evaluate(ix_2, filter);
+
+ assertEquals(-1, comp.compare(score_1, score_2));
+ assertEquals(1, comp.compare(score_2, score_1));
+
+ filter = Filter.filterFor(StorableTestBasic.class, "id = ? & stringProp != ?");
+ score_1 = FilteringScore.evaluate(ix_1, filter);
+ score_2 = FilteringScore.evaluate(ix_2, filter);
+
+ // Second is better because it has fewer properties.
+ assertEquals(1, comp.compare(score_1, score_2));
+ assertEquals(-1, comp.compare(score_2, score_1));
+
+ filter = Filter.filterFor(StorableTestBasic.class, "id = ? & stringProp = ?");
+ score_1 = FilteringScore.evaluate(ix_1, filter);
+ score_2 = FilteringScore.evaluate(ix_2, filter);
+
+ assertEquals(1, comp.compare(score_1, score_2));
+ assertEquals(-1, comp.compare(score_2, score_1));
+
+ // Second index is better since the open range matches.
+ filter = Filter.filterFor(StorableTestBasic.class, "id = ? & stringProp > ?");
+ score_1 = FilteringScore.evaluate(ix_1, filter);
+ score_2 = FilteringScore.evaluate(ix_2, filter);
+
+ assertEquals(1, comp.compare(score_1, score_2));
+ assertEquals(-1, comp.compare(score_2, score_1));
+
+ filter = Filter.filterFor(StorableTestBasic.class,
+ "id = ? & intProp = ? & stringProp > ?");
+ score_1 = FilteringScore.evaluate(ix_1, filter);
+ score_2 = FilteringScore.evaluate(ix_2, filter);
+
+ assertEquals(-1, comp.compare(score_1, score_2));
+ assertEquals(1, comp.compare(score_2, score_1));
+
+ // Test range match with tie resolved by clustered index.
+ filter = Filter.filterFor(StorableTestBasic.class, "id >= ? & id < ?");
+ score_1 = FilteringScore.evaluate(ix_1, filter);
+ score_2 = FilteringScore.evaluate(ix_2, filter);
+
+ // Second is better because it has fewer properties.
+ assertEquals(1, comp.compare(score_1, score_2));
+ assertEquals(-1, comp.compare(score_2, score_1));
+
+ filter = Filter.filterFor(StorableTestBasic.class, "id >= ? & id < ?");
+ score_1 = FilteringScore.evaluate(ix_1.clustered(true), filter);
+ score_2 = FilteringScore.evaluate(ix_2, filter);
+
+ // First is better because it is clusted.
+ assertEquals(-1, comp.compare(score_1, score_2));
+ assertEquals(1, comp.compare(score_2, score_1));
+
+ // Test range match with tie resolved by clustered index.
+ filter = Filter.filterFor(StorableTestBasic.class, "id >= ? & id < ?");
+ score_1 = FilteringScore.evaluate(ix_1.clustered(true), filter);
+ score_2 = FilteringScore.evaluate(ix_2.clustered(true), filter);
+
+ // Second is better because it has fewer properties.
+ assertEquals(1, comp.compare(score_1, score_2));
+ assertEquals(-1, comp.compare(score_2, score_1));
+ }
+
+ public void testArrangementFullComparator() throws Exception {
+ StorableIndex<StorableTestBasic> ix_1, ix_2;
+ Filter<StorableTestBasic> filter;
+ FilteringScore score_1, score_2;
+
+ ix_1 = makeIndex(StorableTestBasic.class, "id", "intProp", "-stringProp");
+ ix_2 = makeIndex(StorableTestBasic.class, "id", "stringProp", "intProp");
+
+ filter = Filter.filterFor(StorableTestBasic.class,
+ "id = ? & intProp = ? & stringProp = ?");
+
+ score_1 = FilteringScore.evaluate(ix_1, filter);
+ score_2 = FilteringScore.evaluate(ix_2, filter);
+
+ Comparator<FilteringScore<?>> comp = FilteringScore.rangeComparator();
+
+ // With just range comparison, either index works.
+ assertEquals(0, comp.compare(score_1, score_2));
+ assertEquals(0, comp.compare(score_2, score_1));
+
+ comp = FilteringScore.fullComparator();
+
+ // First index is better because property arrangement matches.
+ assertEquals(-1, comp.compare(score_1, score_2));
+ assertEquals(1, comp.compare(score_2, score_1));
+ }
+
+ public void testRangeFilterSubset() throws Exception {
+ StorableIndex<StorableTestBasic> ix = makeIndex(StorableTestBasic.class, "id");
+ Filter<StorableTestBasic> filter;
+ FilteringScore score;
+
+ filter = Filter.filterFor(StorableTestBasic.class, "id >= ? & id >= ? & id < ? & id <= ?");
+
+ score = FilteringScore.evaluate(ix, filter);
+
+ assertEquals(2, score.getRangeStartFilters().size());
+
+ List<PropertyFilter<?>> exStart = score.getExclusiveRangeStartFilters();
+ assertEquals(0, exStart.size());
+
+ List<PropertyFilter<?>> inStart = score.getInclusiveRangeStartFilters();
+ assertEquals(2, inStart.size());
+ assertEquals(Filter.filterFor(StorableTestBasic.class, "id >= ?"), inStart.get(0));
+ assertEquals(Filter.filterFor(StorableTestBasic.class, "id >= ?"), inStart.get(1));
+
+ assertEquals(2, score.getRangeEndFilters().size());
+
+ List<PropertyFilter<?>> exEnd = score.getExclusiveRangeEndFilters();
+ assertEquals(1, exEnd.size());
+ assertEquals(Filter.filterFor(StorableTestBasic.class, "id < ?"), exEnd.get(0));
+
+ List<PropertyFilter<?>> inEnd = score.getInclusiveRangeEndFilters();
+ assertEquals(1, inEnd.size());
+ assertEquals(Filter.filterFor(StorableTestBasic.class, "id <= ?"), inEnd.get(0));
+ }
+
+ public void testKeyMatch() throws Exception {
+ StorableIndex<StorableTestBasic> ix = makeIndex(StorableTestBasic.class, "id", "intProp");
+ ix = ix.unique(true);
+ Filter<StorableTestBasic> filter;
+ FilteringScore score;
+
+ filter = Filter.filterFor(StorableTestBasic.class, "id = ?");
+ score = FilteringScore.evaluate(ix, filter);
+ assertFalse(score.isKeyMatch());
+
+ filter = Filter.filterFor(StorableTestBasic.class, "id = ? & intProp > ?");
+ score = FilteringScore.evaluate(ix, filter);
+ assertFalse(score.isKeyMatch());
+
+ filter = Filter.filterFor(StorableTestBasic.class, "id = ? & intProp = ?");
+ score = FilteringScore.evaluate(ix, filter);
+ assertTrue(score.isKeyMatch());
+
+ filter = Filter.filterFor(StorableTestBasic.class,
+ "id = ? & intProp = ? & doubleProp = ?");
+ score = FilteringScore.evaluate(ix, filter);
+ assertTrue(score.isKeyMatch());
+ }
+}
diff --git a/src/test/java/com/amazon/carbonado/qe/TestIndexedQueryAnalyzer.java b/src/test/java/com/amazon/carbonado/qe/TestIndexedQueryAnalyzer.java new file mode 100644 index 0000000..6b1919a --- /dev/null +++ b/src/test/java/com/amazon/carbonado/qe/TestIndexedQueryAnalyzer.java @@ -0,0 +1,460 @@ +/*
+ * 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.qe;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.amazon.carbonado.Cursor;
+import com.amazon.carbonado.FetchException;
+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.cursor.ArraySortBuffer;
+import com.amazon.carbonado.cursor.SortBuffer;
+
+import com.amazon.carbonado.info.OrderedProperty;
+import com.amazon.carbonado.info.StorableIndex;
+
+import com.amazon.carbonado.filter.Filter;
+import com.amazon.carbonado.filter.FilterValues;
+import com.amazon.carbonado.filter.PropertyFilter;
+
+import com.amazon.carbonado.repo.toy.ToyRepository;
+
+import com.amazon.carbonado.stored.Address;
+import com.amazon.carbonado.stored.Order;
+import com.amazon.carbonado.stored.Shipment;
+import com.amazon.carbonado.stored.Shipper;
+import com.amazon.carbonado.stored.StorableTestBasic;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+public class TestIndexedQueryAnalyzer extends TestCase {
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static TestSuite suite() {
+ return new TestSuite(TestIndexedQueryAnalyzer.class);
+ }
+
+ static <S extends Storable> StorableIndex<S> makeIndex(Class<S> type, String... props) {
+ return TestOrderingScore.makeIndex(type, props);
+ }
+
+ public TestIndexedQueryAnalyzer(String name) {
+ super(name);
+ }
+
+ // Note: these tests don't perform exhaustive tests to find the best index, as those tests
+ // are performed by TestFilteringScore and TestOrderingScore.
+
+ public void testFullScan() throws Exception {
+ IndexedQueryAnalyzer iqa = new IndexedQueryAnalyzer(Address.class, RepoAccess.INSTANCE);
+ Filter<Address> filter = Filter.filterFor(Address.class, "addressZip = ?");
+ filter = filter.bind();
+ IndexedQueryAnalyzer.Result result = iqa.analyze(filter, null);
+
+ assertFalse(result.handlesAnything());
+ assertEquals(filter, result.getCompositeScore().getFilteringScore().getRemainderFilter());
+ assertEquals(makeIndex(Address.class, "addressID"), result.getLocalIndex());
+ assertEquals(null, result.getForeignIndex());
+ assertEquals(null, result.getForeignProperty());
+ }
+
+ public void testIndexScan() throws Exception {
+ IndexedQueryAnalyzer iqa = new IndexedQueryAnalyzer(Address.class, RepoAccess.INSTANCE);
+ Filter<Address> filter = Filter.filterFor(Address.class, "addressID = ?");
+ filter = filter.bind();
+ IndexedQueryAnalyzer.Result result = iqa.analyze(filter, null);
+
+ assertTrue(result.handlesAnything());
+ assertEquals(filter, result.getCompositeScore().getFilteringScore().getIdentityFilter());
+ assertEquals(makeIndex(Address.class, "addressID"), result.getLocalIndex());
+ assertEquals(null, result.getForeignIndex());
+ assertEquals(null, result.getForeignProperty());
+ }
+
+ public void testBasic() throws Exception {
+ IndexedQueryAnalyzer iqa = new IndexedQueryAnalyzer(Shipment.class, RepoAccess.INSTANCE);
+ Filter<Shipment> filter = Filter.filterFor(Shipment.class, "shipmentID = ?");
+ filter = filter.bind();
+ IndexedQueryAnalyzer.Result result = iqa.analyze(filter, null);
+
+ assertTrue(result.handlesAnything());
+ assertEquals(filter, result.getCompositeScore().getFilteringScore().getIdentityFilter());
+ assertEquals(makeIndex(Shipment.class, "shipmentID"), result.getLocalIndex());
+ assertEquals(null, result.getForeignIndex());
+ assertEquals(null, result.getForeignProperty());
+
+ filter = Filter.filterFor(Shipment.class, "orderID = ?");
+ filter = filter.bind();
+ result = iqa.analyze(filter, null);
+
+ assertTrue(result.handlesAnything());
+ assertEquals(filter, result.getCompositeScore().getFilteringScore().getIdentityFilter());
+ assertEquals(makeIndex(Shipment.class, "orderID", "shipmentNotes"),
+ result.getLocalIndex());
+ assertEquals(null, result.getForeignIndex());
+ assertEquals(null, result.getForeignProperty());
+
+ filter = Filter.filterFor(Shipment.class, "orderID > ?");
+ filter = filter.bind();
+ result = iqa.analyze(filter, null);
+
+ assertTrue(result.handlesAnything());
+ assertTrue(result.getCompositeScore().getFilteringScore().hasRangeStart());
+ assertFalse(result.getCompositeScore().getFilteringScore().hasRangeEnd());
+ List<PropertyFilter<Shipment>> rangeFilters =
+ result.getCompositeScore().getFilteringScore().getRangeStartFilters();
+ assertEquals(1, rangeFilters.size());
+ assertEquals(filter, rangeFilters.get(0));
+ assertEquals(makeIndex(Shipment.class, "orderID", "shipmentNotes"),
+ result.getLocalIndex());
+ assertEquals(null, result.getForeignIndex());
+ assertEquals(null, result.getForeignProperty());
+ }
+
+ public void testSimpleJoin() throws Exception {
+ IndexedQueryAnalyzer iqa = new IndexedQueryAnalyzer(Shipment.class, RepoAccess.INSTANCE);
+ Filter<Shipment> filter = Filter.filterFor(Shipment.class, "order.orderTotal >= ?");
+ filter = filter.bind();
+ IndexedQueryAnalyzer.Result result = iqa.analyze(filter, null);
+
+ assertTrue(result.handlesAnything());
+ assertTrue(result.getCompositeScore().getFilteringScore().hasRangeStart());
+ assertEquals(null, result.getLocalIndex());
+ assertEquals(makeIndex(Order.class, "orderTotal"), result.getForeignIndex());
+ assertEquals("order", result.getForeignProperty().toString());
+ }
+
+ public void testJoinPriority() throws Exception {
+ // Selects foreign index because filter score is better.
+
+ IndexedQueryAnalyzer iqa = new IndexedQueryAnalyzer(Shipment.class, RepoAccess.INSTANCE);
+ Filter<Shipment> filter = Filter.filterFor
+ (Shipment.class, "shipmentNotes = ? & order.orderTotal >= ?");
+ filter = filter.bind();
+ IndexedQueryAnalyzer.Result result = iqa.analyze(filter, null);
+
+ assertTrue(result.handlesAnything());
+ assertTrue(result.getCompositeScore().getFilteringScore().hasRangeStart());
+ assertEquals(Filter.filterFor(Shipment.class, "shipmentNotes = ?").bind(),
+ result.getCompositeScore().getFilteringScore().getRemainderFilter());
+ assertEquals(null, result.getLocalIndex());
+ assertEquals(makeIndex(Order.class, "orderTotal"), result.getForeignIndex());
+ assertEquals("order", result.getForeignProperty().toString());
+ }
+
+ public void testJoinNonPriority() throws Exception {
+ // Selects local index because filter score is just as good and local
+ // indexes are preferred.
+
+ IndexedQueryAnalyzer iqa = new IndexedQueryAnalyzer(Shipment.class, RepoAccess.INSTANCE);
+ Filter<Shipment> filter = Filter.filterFor
+ (Shipment.class, "orderID >= ? & order.orderTotal >= ?");
+ filter = filter.bind();
+ IndexedQueryAnalyzer.Result result = iqa.analyze(filter, null);
+
+ assertTrue(result.handlesAnything());
+ assertTrue(result.getCompositeScore().getFilteringScore().hasRangeStart());
+ assertEquals(Filter.filterFor(Shipment.class, "order.orderTotal >= ?").bind(),
+ result.getCompositeScore().getFilteringScore().getRemainderFilter());
+ assertEquals(makeIndex(Shipment.class, "orderID", "shipmentNotes"),
+ result.getLocalIndex());
+ assertEquals(null, result.getForeignIndex());
+ assertEquals(null, result.getForeignProperty());
+ }
+
+ public void testChainedJoin() throws Exception {
+ IndexedQueryAnalyzer iqa = new IndexedQueryAnalyzer(Shipment.class, RepoAccess.INSTANCE);
+ Filter<Shipment> filter = Filter.filterFor
+ (Shipment.class, "order.address.addressState = ?");
+ filter = filter.bind();
+ IndexedQueryAnalyzer.Result result = iqa.analyze(filter, null);
+
+ assertTrue(result.handlesAnything());
+ assertEquals(filter, result.getCompositeScore().getFilteringScore().getIdentityFilter());
+ assertEquals(null, result.getLocalIndex());
+ assertEquals(makeIndex(Address.class, "addressState", "-addressCity"),
+ result.getForeignIndex());
+ assertEquals("order.address", result.getForeignProperty().toString());
+ }
+
+ public void testChainedJoinExecutor() throws Exception {
+ IndexedQueryAnalyzer<Shipment> iqa =
+ new IndexedQueryAnalyzer<Shipment>(Shipment.class, RepoAccess.INSTANCE);
+ Filter<Shipment> filter = Filter.filterFor
+ (Shipment.class, "order.address.addressState = ? & order.address.addressZip = ?");
+ FilterValues<Shipment> values = filter.initialFilterValues();
+ filter = values.getFilter();
+ IndexedQueryAnalyzer<Shipment>.Result result = iqa.analyze(filter, null);
+
+ assertTrue(result.handlesAnything());
+ assertEquals(Filter.filterFor(Shipment.class, "order.address.addressState = ?").bind(),
+ result.getCompositeScore().getFilteringScore().getIdentityFilter());
+ assertEquals(Filter.filterFor(Shipment.class, "order.address.addressZip = ?").bind(),
+ result.getCompositeScore().getFilteringScore().getRemainderFilter());
+ assertEquals(null, result.getLocalIndex());
+ assertEquals(makeIndex(Address.class, "addressState", "-addressCity"),
+ result.getForeignIndex());
+ assertEquals("order.address", result.getForeignProperty().toString());
+
+ QueryExecutor<Shipment> joinExec = JoinedQueryExecutor.build
+ (RepoAccess.INSTANCE,
+ result.getForeignProperty(), result.getFilter(), result.getOrdering());
+
+ FilterValues<Shipment> fv = values.with("WA").with("12345");
+
+ StringBuffer buf = new StringBuffer();
+ joinExec.printPlan(buf, 0, fv);
+ String plan = buf.toString();
+
+ // This is actually a pretty terrible plan due to the iterators. This
+ // is expected however, since we lied and said we had indexes.
+ String expected =
+ "join: com.amazon.carbonado.stored.Shipment\n" +
+ "...inner loop: order\n" +
+ " filter: orderID = ?\n" +
+ " collection iterator: com.amazon.carbonado.stored.Shipment\n" +
+ "...outer loop\n" +
+ " join: com.amazon.carbonado.stored.Order\n" +
+ " ...inner loop: address\n" +
+ " filter: addressID = ?\n" +
+ " collection iterator: com.amazon.carbonado.stored.Order\n" +
+ " ...outer loop\n" +
+ " filter: addressState = WA & addressZip = 12345\n" +
+ " collection iterator: com.amazon.carbonado.stored.Address\n";
+
+ assertEquals(expected, plan);
+
+ joinExec.fetch(fv);
+
+ }
+
+ public void testComplexChainedJoinExecutor() throws Exception {
+ IndexedQueryAnalyzer<Shipment> iqa =
+ new IndexedQueryAnalyzer<Shipment>(Shipment.class, RepoAccess.INSTANCE);
+ Filter<Shipment> filter = Filter.filterFor
+ (Shipment.class,
+ "order.address.addressState = ? & order.address.addressID != ? " +
+ "& order.address.addressZip = ? & order.orderTotal > ? & shipmentNotes <= ? " +
+ "& order.addressID > ?");
+ FilterValues<Shipment> values = filter.initialFilterValues();
+ filter = values.getFilter();
+ OrderingList<Shipment> ordering = OrderingList
+ .get(Shipment.class, "order.address.addressCity", "shipmentNotes", "order.orderTotal");
+ IndexedQueryAnalyzer.Result result = iqa.analyze(filter, ordering);
+
+ assertTrue(result.handlesAnything());
+ assertEquals(Filter.filterFor(Shipment.class, "order.address.addressState = ?").bind(),
+ result.getCompositeScore().getFilteringScore().getIdentityFilter());
+ assertEquals(null, result.getLocalIndex());
+ assertEquals(makeIndex(Address.class, "addressState", "-addressCity"),
+ result.getForeignIndex());
+ assertEquals("order.address", result.getForeignProperty().toString());
+
+ QueryExecutor<Shipment> joinExec = JoinedQueryExecutor.build
+ (RepoAccess.INSTANCE,
+ result.getForeignProperty(), result.getFilter(), result.getOrdering());
+
+ FilterValues<Shipment> fv =
+ values.with("WA").with(45).with("12345").with(100).with("Z").with(2);
+
+ StringBuffer buf = new StringBuffer();
+ joinExec.printPlan(buf, 0, fv);
+ String plan = buf.toString();
+
+ // This is actually a pretty terrible plan due to the iterators. This
+ // is expected however, since we lied and said we had indexes.
+ String expected =
+ "sort: [+order.address.addressCity, +shipmentNotes], [+order.orderTotal]\n" +
+ " join: com.amazon.carbonado.stored.Shipment\n" +
+ " ...inner loop: order\n" +
+ " sort: [+shipmentNotes]\n" +
+ " filter: shipmentNotes <= Z & orderID = ?\n" +
+ " collection iterator: com.amazon.carbonado.stored.Shipment\n" +
+ " ...outer loop\n" +
+ " join: com.amazon.carbonado.stored.Order\n" +
+ " ...inner loop: address\n" +
+ " filter: orderTotal > 100 & addressID > 2 & addressID = ?\n" +
+ " collection iterator: com.amazon.carbonado.stored.Order\n" +
+ " ...outer loop\n" +
+ " sort: [+addressCity]\n" +
+ " filter: addressState = WA & addressID != 45 & addressZip = 12345\n" +
+ " collection iterator: com.amazon.carbonado.stored.Address\n";
+
+ //System.out.println(plan);
+ assertEquals(expected, plan);
+
+ joinExec.fetch(fv);
+
+ // Now do it the easier way and compare plans.
+ QueryExecutor<Shipment> joinExec2 = result.createExecutor();
+
+ StringBuffer buf2 = new StringBuffer();
+ joinExec2.printPlan(buf2, 0, fv);
+ String plan2 = buf2.toString();
+
+ assertEquals(expected, plan2);
+
+ Filter<Shipment> expectedFilter = Filter.filterFor
+ (Shipment.class,
+ "order.address.addressState = ? & order.address.addressID != ? " +
+ "& order.address.addressZip = ? & order.orderTotal > ? " +
+ "& order.addressID > ?" +
+ "& shipmentNotes <= ? ");
+
+ assertEquals(expectedFilter.disjunctiveNormalForm(),
+ joinExec2.getFilter().unbind().disjunctiveNormalForm());
+ }
+
+ static class RepoAccess implements RepositoryAccess {
+ static final RepoAccess INSTANCE = new RepoAccess();
+
+ public Repository getRootRepository() {
+ throw new UnsupportedOperationException();
+ }
+
+ public <S extends Storable> StorageAccess<S> storageAccessFor(Class<S> type) {
+ return new StoreAccess<S>(type);
+ }
+ }
+
+ /**
+ * Partially implemented StorageAccess which only supplies information
+ * about indexes.
+ */
+ static class StoreAccess<S extends Storable>
+ implements StorageAccess<S>, QueryExecutorFactory<S>
+ {
+ private final Class<S> mType;
+
+ StoreAccess(Class<S> type) {
+ mType = type;
+ }
+
+ public Class<S> getStorableType() {
+ return mType;
+ }
+
+ public QueryExecutorFactory<S> getQueryExecutorFactory() {
+ return this;
+ }
+
+ public QueryExecutor<S> executor(Filter<S> filter, OrderingList<S> ordering) {
+ Iterable<S> iterable = Collections.emptyList();
+
+ QueryExecutor<S> exec = new IterableQueryExecutor<S>
+ (filter.getStorableType(), iterable);
+
+ if (filter != null) {
+ exec = new FilteredQueryExecutor<S>(exec, filter);
+ }
+
+ if (ordering != null && ordering.size() > 0) {
+ exec = new SortedQueryExecutor<S>(null, exec, null, ordering);
+ }
+
+ return exec;
+ }
+
+ public Collection<StorableIndex<S>> getAllIndexes() {
+ StorableIndex<S>[] indexes;
+
+ if (Address.class.isAssignableFrom(mType)) {
+ indexes = new StorableIndex[] {
+ makeIndex(mType, "addressID"),
+ makeIndex(mType, "addressState", "-addressCity")
+ };
+ } else if (Order.class.isAssignableFrom(mType)) {
+ indexes = new StorableIndex[] {
+ makeIndex(mType, "orderID"),
+ makeIndex(mType, "orderTotal"),
+ makeIndex(mType, "addressID", "orderTotal")
+ };
+ } else if (Shipment.class.isAssignableFrom(mType)) {
+ indexes = new StorableIndex[] {
+ makeIndex(mType, "shipmentID"),
+ makeIndex(mType, "orderID", "shipmentNotes"),
+ };
+ } else if (Shipper.class.isAssignableFrom(mType)) {
+ indexes = new StorableIndex[] {
+ makeIndex(mType, "shipperID")
+ };
+ } else if (StorableTestBasic.class.isAssignableFrom(mType)) {
+ indexes = new StorableIndex[] {
+ makeIndex(mType, "id").unique(true).clustered(true),
+ makeIndex(mType, "stringProp", "doubleProp").unique(true),
+ makeIndex(mType, "-stringProp", "-intProp", "~id").unique(true),
+ makeIndex(mType, "+intProp", "stringProp", "~id").unique(true),
+ makeIndex(mType, "-doubleProp", "+longProp", "~id").unique(true),
+ };
+ } else {
+ indexes = new StorableIndex[0];
+ }
+
+ return Arrays.asList(indexes);
+ }
+
+ public Storage<S> storageDelegate(StorableIndex<S> index) {
+ return null;
+ }
+
+ public SortBuffer<S> createSortBuffer() {
+ return new ArraySortBuffer<S>();
+ }
+
+ public long countAll() {
+ throw new UnsupportedOperationException();
+ }
+
+ public Cursor<S> fetchAll() {
+ throw new UnsupportedOperationException();
+ }
+
+ public Cursor<S> fetchOne(StorableIndex<S> index, Object[] identityValues) {
+ throw new UnsupportedOperationException();
+ }
+
+ public Cursor<S> fetchSubset(StorableIndex<S> index,
+ Object[] identityValues,
+ BoundaryType rangeStartBoundary,
+ Object rangeStartValue,
+ BoundaryType rangeEndBoundary,
+ Object rangeEndValue,
+ boolean reverseRange,
+ boolean reverseOrder)
+ {
+ throw new UnsupportedOperationException();
+ }
+ }
+}
diff --git a/src/test/java/com/amazon/carbonado/qe/TestIndexedQueryExecutor.java b/src/test/java/com/amazon/carbonado/qe/TestIndexedQueryExecutor.java new file mode 100644 index 0000000..a68fbac --- /dev/null +++ b/src/test/java/com/amazon/carbonado/qe/TestIndexedQueryExecutor.java @@ -0,0 +1,754 @@ +/*
+ * 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.qe;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.amazon.carbonado.Cursor;
+import com.amazon.carbonado.Storable;
+
+import com.amazon.carbonado.cursor.IteratorCursor;
+
+import com.amazon.carbonado.filter.Filter;
+import com.amazon.carbonado.filter.FilterValues;
+
+import com.amazon.carbonado.info.OrderedProperty;
+import com.amazon.carbonado.info.StorableIndex;
+
+import com.amazon.carbonado.stored.StorableTestBasic;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+public class TestIndexedQueryExecutor extends TestCase {
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static TestSuite suite() {
+ return new TestSuite(TestIndexedQueryExecutor.class);
+ }
+
+ static <S extends Storable> StorableIndex<S> makeIndex(Class<S> type, String... props) {
+ return TestOrderingScore.makeIndex(type, props);
+ }
+
+ static <S extends Storable> OrderingList<S> makeOrdering(Class<S> type, String... props) {
+ return TestOrderingScore.makeOrdering(type, props);
+ }
+
+ public TestIndexedQueryExecutor(String name) {
+ super(name);
+ }
+
+ public void testIdentityMatch() throws Exception {
+ StorableIndex<StorableTestBasic> index =
+ makeIndex(StorableTestBasic.class, "id", "-intProp", "doubleProp");
+
+ Filter<StorableTestBasic> filter =
+ Filter.filterFor(StorableTestBasic.class, "id = ?");
+ FilterValues<StorableTestBasic> values = filter.initialFilterValues();
+ filter = values.getFilter();
+
+ CompositeScore<StorableTestBasic> score = CompositeScore.evaluate(index, filter, null);
+
+ Mock<StorableTestBasic> executor = new Mock<StorableTestBasic>(index, score);
+
+ executor.fetch(values.with(100));
+
+ assertEquals(1, executor.mIdentityValues.length);
+ assertEquals(100, executor.mIdentityValues[0]);
+ assertEquals(BoundaryType.OPEN, executor.mRangeStartBoundary);
+ assertEquals(null, executor.mRangeStartValue);
+ assertEquals(BoundaryType.OPEN, executor.mRangeEndBoundary);
+ assertEquals(null, executor.mRangeEndValue);
+ assertFalse(executor.mReverseRange);
+ assertFalse(executor.mReverseOrder);
+
+ ///////
+ filter = Filter.filterFor(StorableTestBasic.class, "id = ? & intProp = ?");
+ values = filter.initialFilterValues();
+ filter = values.getFilter();
+
+ score = CompositeScore.evaluate(index, filter, null);
+
+ executor = new Mock<StorableTestBasic>(index, score);
+
+ executor.fetch(values.with(100).with(5));
+
+ assertEquals(2, executor.mIdentityValues.length);
+ assertEquals(100, executor.mIdentityValues[0]);
+ assertEquals(5, executor.mIdentityValues[1]);
+ assertEquals(BoundaryType.OPEN, executor.mRangeStartBoundary);
+ assertEquals(null, executor.mRangeStartValue);
+ assertEquals(BoundaryType.OPEN, executor.mRangeEndBoundary);
+ assertEquals(null, executor.mRangeEndValue);
+ assertFalse(executor.mReverseRange);
+ assertFalse(executor.mReverseOrder);
+
+ ///////
+ filter = Filter.filterFor(StorableTestBasic.class, "intProp = ?");
+ values = filter.initialFilterValues();
+ filter = values.getFilter();
+
+ score = CompositeScore.evaluate(index, filter, null);
+
+ executor = new Mock<StorableTestBasic>(index, score);
+
+ executor.fetch(values.with(200));
+
+ assertEquals(null, executor.mIdentityValues);
+ assertEquals(BoundaryType.OPEN, executor.mRangeStartBoundary);
+ assertEquals(null, executor.mRangeStartValue);
+ assertEquals(BoundaryType.OPEN, executor.mRangeEndBoundary);
+ assertEquals(null, executor.mRangeEndValue);
+ assertFalse(executor.mReverseRange);
+ assertFalse(executor.mReverseOrder);
+ }
+
+ public void testOpenRangeStartMatch() throws Exception {
+ StorableIndex<StorableTestBasic> index = makeIndex(StorableTestBasic.class, "intProp");
+
+ Filter<StorableTestBasic> filter =
+ Filter.filterFor(StorableTestBasic.class, "intProp > ?");
+ FilterValues<StorableTestBasic> values = filter.initialFilterValues();
+ filter = values.getFilter();
+
+ CompositeScore<StorableTestBasic> score = CompositeScore.evaluate(index, filter, null);
+
+ Mock<StorableTestBasic> executor = new Mock<StorableTestBasic>(index, score);
+
+ executor.fetch(values.with(100));
+
+ assertEquals(null, executor.mIdentityValues);
+ assertEquals(BoundaryType.EXCLUSIVE, executor.mRangeStartBoundary);
+ assertEquals(100, executor.mRangeStartValue);
+ assertEquals(BoundaryType.OPEN, executor.mRangeEndBoundary);
+ assertEquals(null, executor.mRangeEndValue);
+ assertFalse(executor.mReverseRange);
+ assertFalse(executor.mReverseOrder);
+
+ ///////
+ filter = Filter.filterFor(StorableTestBasic.class, "intProp >= ?");
+ values = filter.initialFilterValues();
+ filter = values.getFilter();
+
+ score = CompositeScore.evaluate(index, filter, null);
+
+ executor = new Mock<StorableTestBasic>(index, score);
+
+ executor.fetch(values.with(100));
+
+ assertEquals(null, executor.mIdentityValues);
+ assertEquals(BoundaryType.INCLUSIVE, executor.mRangeStartBoundary);
+ assertEquals(100, executor.mRangeStartValue);
+ assertEquals(BoundaryType.OPEN, executor.mRangeEndBoundary);
+ assertEquals(null, executor.mRangeEndValue);
+ assertFalse(executor.mReverseRange);
+ assertFalse(executor.mReverseOrder);
+
+ ///////
+ filter = Filter.filterFor(StorableTestBasic.class, "intProp > ? & intProp > ?");
+ values = filter.initialFilterValues();
+ filter = values.getFilter();
+
+ score = CompositeScore.evaluate(index, filter, null);
+
+ executor = new Mock<StorableTestBasic>(index, score);
+
+ executor.fetch(values.with(10).with(30));
+
+ assertEquals(null, executor.mIdentityValues);
+ assertEquals(BoundaryType.EXCLUSIVE, executor.mRangeStartBoundary);
+ assertEquals(30, executor.mRangeStartValue);
+ assertEquals(BoundaryType.OPEN, executor.mRangeEndBoundary);
+ assertEquals(null, executor.mRangeEndValue);
+ assertFalse(executor.mReverseRange);
+ assertFalse(executor.mReverseOrder);
+
+ ///////
+ filter = Filter.filterFor(StorableTestBasic.class, "intProp >= ? & intProp > ?");
+ values = filter.initialFilterValues();
+ filter = values.getFilter();
+
+ score = CompositeScore.evaluate(index, filter, null);
+
+ executor = new Mock<StorableTestBasic>(index, score);
+
+ executor.fetch(values.with(10).with(30));
+
+ assertEquals(null, executor.mIdentityValues);
+ assertEquals(BoundaryType.EXCLUSIVE, executor.mRangeStartBoundary);
+ assertEquals(30, executor.mRangeStartValue);
+ assertEquals(BoundaryType.OPEN, executor.mRangeEndBoundary);
+ assertEquals(null, executor.mRangeEndValue);
+ assertFalse(executor.mReverseRange);
+ assertFalse(executor.mReverseOrder);
+
+ ///////
+ filter = Filter.filterFor(StorableTestBasic.class, "intProp >= ? & intProp > ?");
+ values = filter.initialFilterValues();
+ filter = values.getFilter();
+
+ score = CompositeScore.evaluate(index, filter, null);
+
+ executor = new Mock<StorableTestBasic>(index, score);
+
+ executor.fetch(values.with(10).with(10));
+
+ assertEquals(null, executor.mIdentityValues);
+ assertEquals(BoundaryType.EXCLUSIVE, executor.mRangeStartBoundary);
+ assertEquals(10, executor.mRangeStartValue);
+ assertEquals(BoundaryType.OPEN, executor.mRangeEndBoundary);
+ assertEquals(null, executor.mRangeEndValue);
+ assertFalse(executor.mReverseRange);
+ assertFalse(executor.mReverseOrder);
+
+ ///////
+ filter = Filter.filterFor(StorableTestBasic.class, "intProp >= ? & intProp > ?");
+ values = filter.initialFilterValues();
+ filter = values.getFilter();
+
+ score = CompositeScore.evaluate(index, filter, null);
+
+ executor = new Mock<StorableTestBasic>(index, score);
+
+ executor.fetch(values.with(30).with(10));
+
+ assertEquals(null, executor.mIdentityValues);
+ assertEquals(BoundaryType.INCLUSIVE, executor.mRangeStartBoundary);
+ assertEquals(30, executor.mRangeStartValue);
+ assertEquals(BoundaryType.OPEN, executor.mRangeEndBoundary);
+ assertEquals(null, executor.mRangeEndValue);
+ assertFalse(executor.mReverseRange);
+ assertFalse(executor.mReverseOrder);
+
+ ///////
+ filter = Filter.filterFor(StorableTestBasic.class, "intProp > ? & intProp > ?");
+ values = filter.initialFilterValues();
+ filter = values.getFilter();
+
+ score = CompositeScore.evaluate(index, filter,
+ makeOrdering(StorableTestBasic.class, "-intProp"));
+
+ executor = new Mock<StorableTestBasic>(index, score);
+
+ executor.fetch(values.with(100).with(30));
+
+ assertEquals(null, executor.mIdentityValues);
+ assertEquals(BoundaryType.EXCLUSIVE, executor.mRangeStartBoundary);
+ assertEquals(100, executor.mRangeStartValue);
+ assertEquals(BoundaryType.OPEN, executor.mRangeEndBoundary);
+ assertEquals(null, executor.mRangeEndValue);
+ assertFalse(executor.mReverseRange);
+ assertTrue(executor.mReverseOrder);
+
+ ///////
+ index = makeIndex(StorableTestBasic.class, "-intProp");
+
+ filter = Filter.filterFor(StorableTestBasic.class, "intProp > ?");
+ values = filter.initialFilterValues();
+ filter = values.getFilter();
+
+ score = CompositeScore.evaluate(index, filter, null);
+
+ executor = new Mock<StorableTestBasic>(index, score);
+
+ executor.fetch(values.with(100));
+
+ assertEquals(null, executor.mIdentityValues);
+ assertEquals(BoundaryType.EXCLUSIVE, executor.mRangeStartBoundary);
+ assertEquals(100, executor.mRangeStartValue);
+ assertEquals(BoundaryType.OPEN, executor.mRangeEndBoundary);
+ assertEquals(null, executor.mRangeEndValue);
+ assertTrue(executor.mReverseRange);
+ assertFalse(executor.mReverseOrder);
+
+ ///////
+ filter = Filter.filterFor(StorableTestBasic.class, "intProp > ?");
+ values = filter.initialFilterValues();
+ filter = values.getFilter();
+
+ score = CompositeScore.evaluate(index, filter,
+ makeOrdering(StorableTestBasic.class, "-intProp"));
+
+ executor = new Mock<StorableTestBasic>(index, score);
+
+ executor.fetch(values.with(100));
+
+ assertEquals(null, executor.mIdentityValues);
+ assertEquals(BoundaryType.EXCLUSIVE, executor.mRangeStartBoundary);
+ assertEquals(100, executor.mRangeStartValue);
+ assertEquals(BoundaryType.OPEN, executor.mRangeEndBoundary);
+ assertEquals(null, executor.mRangeEndValue);
+ assertTrue(executor.mReverseRange);
+ assertFalse(executor.mReverseOrder);
+
+ ///////
+ filter = Filter.filterFor(StorableTestBasic.class, "intProp > ?");
+ values = filter.initialFilterValues();
+ filter = values.getFilter();
+
+ score = CompositeScore.evaluate(index, filter,
+ makeOrdering(StorableTestBasic.class, "intProp"));
+
+ executor = new Mock<StorableTestBasic>(index, score);
+
+ executor.fetch(values.with(100));
+
+ assertEquals(null, executor.mIdentityValues);
+ assertEquals(BoundaryType.EXCLUSIVE, executor.mRangeStartBoundary);
+ assertEquals(100, executor.mRangeStartValue);
+ assertEquals(BoundaryType.OPEN, executor.mRangeEndBoundary);
+ assertEquals(null, executor.mRangeEndValue);
+ assertTrue(executor.mReverseRange);
+ assertTrue(executor.mReverseOrder);
+ }
+
+ public void testOpenRangeEndMatch() throws Exception {
+ StorableIndex<StorableTestBasic> index = makeIndex(StorableTestBasic.class, "intProp");
+
+ Filter<StorableTestBasic> filter =
+ Filter.filterFor(StorableTestBasic.class, "intProp < ?");
+ FilterValues<StorableTestBasic> values = filter.initialFilterValues();
+ filter = values.getFilter();
+
+ CompositeScore<StorableTestBasic> score = CompositeScore.evaluate(index, filter, null);
+
+ Mock<StorableTestBasic> executor = new Mock<StorableTestBasic>(index, score);
+
+ executor.fetch(values.with(100));
+
+ assertEquals(null, executor.mIdentityValues);
+ assertEquals(BoundaryType.OPEN, executor.mRangeStartBoundary);
+ assertEquals(null, executor.mRangeStartValue);
+ assertEquals(BoundaryType.EXCLUSIVE, executor.mRangeEndBoundary);
+ assertEquals(100, executor.mRangeEndValue);
+ assertFalse(executor.mReverseRange);
+ assertFalse(executor.mReverseOrder);
+
+ ///////
+ filter = Filter.filterFor(StorableTestBasic.class, "intProp <= ?");
+ values = filter.initialFilterValues();
+ filter = values.getFilter();
+
+ score = CompositeScore.evaluate(index, filter, null);
+
+ executor = new Mock<StorableTestBasic>(index, score);
+
+ executor.fetch(values.with(100));
+
+ assertEquals(null, executor.mIdentityValues);
+ assertEquals(BoundaryType.OPEN, executor.mRangeStartBoundary);
+ assertEquals(null, executor.mRangeStartValue);
+ assertEquals(BoundaryType.INCLUSIVE, executor.mRangeEndBoundary);
+ assertEquals(100, executor.mRangeEndValue);
+ assertFalse(executor.mReverseRange);
+ assertFalse(executor.mReverseOrder);
+
+ ///////
+ filter = Filter.filterFor(StorableTestBasic.class, "intProp < ? & intProp < ?");
+ values = filter.initialFilterValues();
+ filter = values.getFilter();
+
+ score = CompositeScore.evaluate(index, filter, null);
+
+ executor = new Mock<StorableTestBasic>(index, score);
+
+ executor.fetch(values.with(10).with(30));
+
+ assertEquals(null, executor.mIdentityValues);
+ assertEquals(BoundaryType.OPEN, executor.mRangeStartBoundary);
+ assertEquals(null, executor.mRangeStartValue);
+ assertEquals(BoundaryType.EXCLUSIVE, executor.mRangeEndBoundary);
+ assertEquals(10, executor.mRangeEndValue);
+ assertFalse(executor.mReverseRange);
+ assertFalse(executor.mReverseOrder);
+
+ ///////
+ filter = Filter.filterFor(StorableTestBasic.class, "intProp <= ? & intProp < ?");
+ values = filter.initialFilterValues();
+ filter = values.getFilter();
+
+ score = CompositeScore.evaluate(index, filter, null);
+
+ executor = new Mock<StorableTestBasic>(index, score);
+
+ executor.fetch(values.with(10).with(30));
+
+ assertEquals(null, executor.mIdentityValues);
+ assertEquals(BoundaryType.OPEN, executor.mRangeStartBoundary);
+ assertEquals(null, executor.mRangeStartValue);
+ assertEquals(BoundaryType.INCLUSIVE, executor.mRangeEndBoundary);
+ assertEquals(10, executor.mRangeEndValue);
+ assertFalse(executor.mReverseRange);
+ assertFalse(executor.mReverseOrder);
+
+ ///////
+ filter = Filter.filterFor(StorableTestBasic.class, "intProp <= ? & intProp < ?");
+ values = filter.initialFilterValues();
+ filter = values.getFilter();
+
+ score = CompositeScore.evaluate(index, filter, null);
+
+ executor = new Mock<StorableTestBasic>(index, score);
+
+ executor.fetch(values.with(10).with(10));
+
+ assertEquals(null, executor.mIdentityValues);
+ assertEquals(BoundaryType.OPEN, executor.mRangeStartBoundary);
+ assertEquals(null, executor.mRangeStartValue);
+ assertEquals(BoundaryType.EXCLUSIVE, executor.mRangeEndBoundary);
+ assertEquals(10, executor.mRangeEndValue);
+ assertFalse(executor.mReverseRange);
+ assertFalse(executor.mReverseOrder);
+
+ ///////
+ filter = Filter.filterFor(StorableTestBasic.class, "intProp <= ? & intProp < ?");
+ values = filter.initialFilterValues();
+ filter = values.getFilter();
+
+ score = CompositeScore.evaluate(index, filter, null);
+
+ executor = new Mock<StorableTestBasic>(index, score);
+
+ executor.fetch(values.with(30).with(10));
+
+ assertEquals(null, executor.mIdentityValues);
+ assertEquals(BoundaryType.OPEN, executor.mRangeStartBoundary);
+ assertEquals(null, executor.mRangeStartValue);
+ assertEquals(BoundaryType.EXCLUSIVE, executor.mRangeEndBoundary);
+ assertEquals(10, executor.mRangeEndValue);
+ assertFalse(executor.mReverseRange);
+ assertFalse(executor.mReverseOrder);
+
+ ///////
+ filter = Filter.filterFor(StorableTestBasic.class, "intProp < ? & intProp < ?");
+ values = filter.initialFilterValues();
+ filter = values.getFilter();
+
+ score = CompositeScore.evaluate(index, filter,
+ makeOrdering(StorableTestBasic.class, "-intProp"));
+
+ executor = new Mock<StorableTestBasic>(index, score);
+
+ executor.fetch(values.with(100).with(30));
+
+ assertEquals(null, executor.mIdentityValues);
+ assertEquals(BoundaryType.OPEN, executor.mRangeStartBoundary);
+ assertEquals(null, executor.mRangeStartValue);
+ assertEquals(BoundaryType.EXCLUSIVE, executor.mRangeEndBoundary);
+ assertEquals(30, executor.mRangeEndValue);
+ assertFalse(executor.mReverseRange);
+ assertTrue(executor.mReverseOrder);
+
+ ///////
+ index = makeIndex(StorableTestBasic.class, "-intProp");
+
+ filter = Filter.filterFor(StorableTestBasic.class, "intProp < ?");
+ values = filter.initialFilterValues();
+ filter = values.getFilter();
+
+ score = CompositeScore.evaluate(index, filter, null);
+
+ executor = new Mock<StorableTestBasic>(index, score);
+
+ executor.fetch(values.with(100));
+
+ assertEquals(null, executor.mIdentityValues);
+ assertEquals(BoundaryType.OPEN, executor.mRangeStartBoundary);
+ assertEquals(null, executor.mRangeStartValue);
+ assertEquals(BoundaryType.EXCLUSIVE, executor.mRangeEndBoundary);
+ assertEquals(100, executor.mRangeEndValue);
+ assertTrue(executor.mReverseRange);
+ assertFalse(executor.mReverseOrder);
+
+ ///////
+ filter = Filter.filterFor(StorableTestBasic.class, "intProp < ?");
+ values = filter.initialFilterValues();
+ filter = values.getFilter();
+
+ score = CompositeScore.evaluate(index, filter,
+ makeOrdering(StorableTestBasic.class, "-intProp"));
+
+ executor = new Mock<StorableTestBasic>(index, score);
+
+ executor.fetch(values.with(100));
+
+ assertEquals(null, executor.mIdentityValues);
+ assertEquals(BoundaryType.OPEN, executor.mRangeStartBoundary);
+ assertEquals(null, executor.mRangeStartValue);
+ assertEquals(BoundaryType.EXCLUSIVE, executor.mRangeEndBoundary);
+ assertEquals(100, executor.mRangeEndValue);
+ assertTrue(executor.mReverseRange);
+ assertFalse(executor.mReverseOrder);
+
+ ///////
+ filter = Filter.filterFor(StorableTestBasic.class, "intProp < ?");
+ values = filter.initialFilterValues();
+ filter = values.getFilter();
+
+ score = CompositeScore.evaluate(index, filter,
+ makeOrdering(StorableTestBasic.class, "intProp"));
+
+ executor = new Mock<StorableTestBasic>(index, score);
+
+ executor.fetch(values.with(100));
+
+ assertEquals(null, executor.mIdentityValues);
+ assertEquals(BoundaryType.OPEN, executor.mRangeStartBoundary);
+ assertEquals(null, executor.mRangeStartValue);
+ assertEquals(BoundaryType.EXCLUSIVE, executor.mRangeEndBoundary);
+ assertEquals(100, executor.mRangeEndValue);
+ assertTrue(executor.mReverseRange);
+ assertTrue(executor.mReverseOrder);
+ }
+
+ public void testClosedRangeMatch() throws Exception {
+ // These tests are not as exhaustive, as I don't expect the combination
+ // of start and end ranges to interfere with each other.
+
+ StorableIndex<StorableTestBasic> index = makeIndex(StorableTestBasic.class, "intProp");
+
+ Filter<StorableTestBasic> filter =
+ Filter.filterFor(StorableTestBasic.class, "intProp > ? & intProp < ?");
+ FilterValues<StorableTestBasic> values = filter.initialFilterValues();
+ filter = values.getFilter();
+
+ CompositeScore<StorableTestBasic> score = CompositeScore.evaluate(index, filter, null);
+
+ Mock<StorableTestBasic> executor = new Mock<StorableTestBasic>(index, score);
+
+ executor.fetch(values.with(100).with(200));
+
+ assertEquals(null, executor.mIdentityValues);
+ assertEquals(BoundaryType.EXCLUSIVE, executor.mRangeStartBoundary);
+ assertEquals(100, executor.mRangeStartValue);
+ assertEquals(BoundaryType.EXCLUSIVE, executor.mRangeEndBoundary);
+ assertEquals(200, executor.mRangeEndValue);
+ assertFalse(executor.mReverseRange);
+ assertFalse(executor.mReverseOrder);
+
+ ///////
+ filter = Filter.filterFor(StorableTestBasic.class, "intProp >= ? & intProp <= ?");
+ values = filter.initialFilterValues();
+ filter = values.getFilter();
+
+ score = CompositeScore.evaluate(index, filter, null);
+
+ executor = new Mock<StorableTestBasic>(index, score);
+
+ executor.fetch(values.with(100).with(10));
+
+ assertEquals(null, executor.mIdentityValues);
+ assertEquals(BoundaryType.INCLUSIVE, executor.mRangeStartBoundary);
+ assertEquals(100, executor.mRangeStartValue);
+ assertEquals(BoundaryType.INCLUSIVE, executor.mRangeEndBoundary);
+ assertEquals(10, executor.mRangeEndValue);
+ assertFalse(executor.mReverseRange);
+ assertFalse(executor.mReverseOrder);
+
+ ///////
+ filter = Filter.filterFor(StorableTestBasic.class,
+ "intProp > ? & intProp < ? & intProp > ?");
+ values = filter.initialFilterValues();
+ filter = values.getFilter();
+
+ score = CompositeScore.evaluate(index, filter, null);
+
+ executor = new Mock<StorableTestBasic>(index, score);
+
+ executor.fetch(values.with(10).with(100).with(30));
+
+ assertEquals(null, executor.mIdentityValues);
+ assertEquals(BoundaryType.EXCLUSIVE, executor.mRangeStartBoundary);
+ assertEquals(30, executor.mRangeStartValue);
+ assertEquals(BoundaryType.EXCLUSIVE, executor.mRangeEndBoundary);
+ assertEquals(100, executor.mRangeEndValue);
+ assertFalse(executor.mReverseRange);
+ assertFalse(executor.mReverseOrder);
+ }
+
+ public void testIdentityAndRangeMatch() throws Exception {
+ // These tests are not as exhaustive, as I don't expect the combination
+ // of identity and ranges to interfere with each other.
+
+ StorableIndex<StorableTestBasic> index;
+ Filter<StorableTestBasic> filter;
+ FilterValues<StorableTestBasic> values;
+ CompositeScore<StorableTestBasic> score;
+ Mock<StorableTestBasic> executor;
+
+ index = makeIndex(StorableTestBasic.class, "intProp", "-doubleProp", "stringProp");
+
+ filter = Filter.filterFor(StorableTestBasic.class,
+ "intProp = ? & doubleProp > ? & doubleProp < ?");
+ values = filter.initialFilterValues();
+ filter = values.getFilter();
+
+ score = CompositeScore.evaluate(index, filter, null);
+
+ executor = new Mock<StorableTestBasic>(index, score);
+
+ executor.fetch(values.with(3).with(56.5).with(200.2));
+
+ assertEquals(3, executor.mIdentityValues[0]);
+ assertEquals(BoundaryType.EXCLUSIVE, executor.mRangeStartBoundary);
+ assertEquals(56.5, executor.mRangeStartValue);
+ assertEquals(BoundaryType.EXCLUSIVE, executor.mRangeEndBoundary);
+ assertEquals(200.2, executor.mRangeEndValue);
+ assertTrue(executor.mReverseRange);
+ assertFalse(executor.mReverseOrder);
+
+ ///////
+ score = CompositeScore.evaluate(index, filter,
+ makeOrdering(StorableTestBasic.class, "doubleProp"));
+
+ executor = new Mock<StorableTestBasic>(index, score);
+
+ executor.fetch(values.with(3).with(56.5).with(200.2));
+
+ assertEquals(3, executor.mIdentityValues[0]);
+ assertEquals(BoundaryType.EXCLUSIVE, executor.mRangeStartBoundary);
+ assertEquals(56.5, executor.mRangeStartValue);
+ assertEquals(BoundaryType.EXCLUSIVE, executor.mRangeEndBoundary);
+ assertEquals(200.2, executor.mRangeEndValue);
+ assertTrue(executor.mReverseRange);
+ assertTrue(executor.mReverseOrder);
+
+ ///////
+ score = CompositeScore.evaluate(index, filter,
+ makeOrdering(StorableTestBasic.class, "stringProp"));
+
+ executor = new Mock<StorableTestBasic>(index, score);
+
+ executor.fetch(values.with(3).with(56.5).with(200.2));
+
+ assertEquals(3, executor.mIdentityValues[0]);
+ assertEquals(BoundaryType.EXCLUSIVE, executor.mRangeStartBoundary);
+ assertEquals(56.5, executor.mRangeStartValue);
+ assertEquals(BoundaryType.EXCLUSIVE, executor.mRangeEndBoundary);
+ assertEquals(200.2, executor.mRangeEndValue);
+ assertTrue(executor.mReverseRange);
+ assertFalse(executor.mReverseOrder);
+
+ ///////
+ filter = Filter.filterFor(StorableTestBasic.class,
+ "intProp = ? & doubleProp = ? & stringProp < ?");
+ values = filter.initialFilterValues();
+ filter = values.getFilter();
+
+ score = CompositeScore.evaluate(index, filter,
+ makeOrdering(StorableTestBasic.class, "-stringProp"));
+
+ executor = new Mock<StorableTestBasic>(index, score);
+
+ executor.fetch(values.with(3).with(56.5).with("foo"));
+
+ assertEquals(3, executor.mIdentityValues[0]);
+ assertEquals(56.5, executor.mIdentityValues[1]);
+ assertEquals(BoundaryType.OPEN, executor.mRangeStartBoundary);
+ assertEquals(null, executor.mRangeStartValue);
+ assertEquals(BoundaryType.EXCLUSIVE, executor.mRangeEndBoundary);
+ assertEquals("foo", executor.mRangeEndValue);
+ assertFalse(executor.mReverseRange);
+ assertTrue(executor.mReverseOrder);
+
+ assertEquals(values.getFilter(), executor.getFilter());
+ List<OrderedProperty<StorableTestBasic>> expectedOrdering =
+ makeOrdering(StorableTestBasic.class, "-stringProp");
+ assertEquals(expectedOrdering, executor.getOrdering());
+ }
+
+ public void testHandledOrdering() throws Exception {
+ // Tests that ordering of executor only reveals what it actually uses.
+
+ StorableIndex<StorableTestBasic> index;
+ Filter<StorableTestBasic> filter;
+ FilterValues<StorableTestBasic> values;
+ CompositeScore<StorableTestBasic> score;
+ Mock<StorableTestBasic> executor;
+
+ index = makeIndex(StorableTestBasic.class, "intProp", "-doubleProp", "stringProp");
+
+ filter = Filter.filterFor(StorableTestBasic.class, "intProp = ?");
+ values = filter.initialFilterValues();
+ filter = values.getFilter();
+
+ score = CompositeScore.evaluate
+ (index, filter,
+ makeOrdering(StorableTestBasic.class, "intProp", "doubleProp"));
+
+ executor = new Mock<StorableTestBasic>(index, score);
+
+ assertEquals(values.getFilter(), executor.getFilter());
+ List<OrderedProperty<StorableTestBasic>> expectedOrdering =
+ makeOrdering(StorableTestBasic.class, "+doubleProp");
+ assertEquals(expectedOrdering, executor.getOrdering());
+ }
+
+ /**
+ * Mock object doesn't really open a cursor -- it just captures the passed
+ * parameters.
+ */
+ static class Mock<S extends Storable> extends IndexedQueryExecutor<S>
+ implements IndexedQueryExecutor.Support<S>
+ {
+ Object[] mIdentityValues;
+ BoundaryType mRangeStartBoundary;
+ Object mRangeStartValue;
+ BoundaryType mRangeEndBoundary;
+ Object mRangeEndValue;
+ boolean mReverseRange;
+ boolean mReverseOrder;
+
+ public Mock(StorableIndex<S> index, CompositeScore<S> score) {
+ super(null, index, score);
+ }
+
+ public Cursor<S> fetchSubset(StorableIndex<S> index,
+ Object[] identityValues,
+ BoundaryType rangeStartBoundary,
+ Object rangeStartValue,
+ BoundaryType rangeEndBoundary,
+ Object rangeEndValue,
+ boolean reverseRange,
+ boolean reverseOrder)
+ {
+ mIdentityValues = identityValues;
+ mRangeStartBoundary = rangeStartBoundary;
+ mRangeStartValue = rangeStartValue;
+ mRangeEndBoundary = rangeEndBoundary;
+ mRangeEndValue = rangeEndValue;
+ mReverseRange = reverseRange;
+ mReverseOrder = reverseOrder;
+
+ Collection<S> empty = Collections.emptyList();
+ return new IteratorCursor<S>(empty);
+ }
+ }
+}
diff --git a/src/test/java/com/amazon/carbonado/qe/TestJoinedQueryExecutor.java b/src/test/java/com/amazon/carbonado/qe/TestJoinedQueryExecutor.java new file mode 100644 index 0000000..e1abca9 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/qe/TestJoinedQueryExecutor.java @@ -0,0 +1,298 @@ +/*
+ * 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.qe;
+
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestSuite;
+
+import com.amazon.carbonado.Cursor;
+import com.amazon.carbonado.FetchException;
+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.cursor.ArraySortBuffer;
+import com.amazon.carbonado.cursor.SortBuffer;
+
+import com.amazon.carbonado.filter.Filter;
+import com.amazon.carbonado.filter.FilterValues;
+
+import com.amazon.carbonado.info.ChainedProperty;
+import com.amazon.carbonado.info.Direction;
+import com.amazon.carbonado.info.OrderedProperty;
+import com.amazon.carbonado.info.StorableIndex;
+import com.amazon.carbonado.info.StorableInfo;
+import com.amazon.carbonado.info.StorableIntrospector;
+import com.amazon.carbonado.info.StorableProperty;
+
+import com.amazon.carbonado.repo.toy.ToyRepository;
+
+import com.amazon.carbonado.stored.UserAddress;
+import com.amazon.carbonado.stored.UserInfo;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+public class TestJoinedQueryExecutor extends TestQueryExecutor {
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static TestSuite suite() {
+ return new TestSuite(TestJoinedQueryExecutor.class);
+ }
+
+ private Repository mRepository;
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ mRepository = new ToyRepository();
+ }
+
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ public void testJoin() throws Exception {
+ StorableInfo<UserInfo> info = StorableIntrospector.examine(UserInfo.class);
+ Map<String, ? extends StorableProperty<UserInfo>> properties = info.getAllProperties();
+
+ RepositoryAccess repoAccess = new RepoAccess();
+
+ ChainedProperty<UserInfo> targetToSourceProperty =
+ ChainedProperty.get(properties.get("address"));
+
+ Filter<UserInfo> targetFilter = Filter.filterFor(UserInfo.class, "address.state = ?");
+ OrderingList<UserInfo> targetOrdering =
+ OrderingList.get(UserInfo.class, "+address.country");
+
+ QueryExecutor<UserInfo> userExecutor = JoinedQueryExecutor.build
+ (repoAccess, targetToSourceProperty, targetFilter, targetOrdering);
+
+ //System.out.println();
+ //userExecutor.printPlan(System.out, 0, null);
+
+ assertEquals("address.state = ?", userExecutor.getFilter().toString());
+ assertEquals("+address.country", userExecutor.getOrdering().get(0).toString());
+
+ // Create some addresses
+ Storage<UserAddress> addressStorage = mRepository.storageFor(UserAddress.class);
+ UserAddress addr = addressStorage.prepare();
+ addr.setAddressID(1);
+ addr.setLine1("4567, 123 Street");
+ addr.setCity("Springfield");
+ addr.setState("IL");
+ addr.setCountry("USA");
+ addr.insert();
+
+ addr = addressStorage.prepare();
+ addr.setAddressID(2);
+ addr.setLine1("1111 Apt 1, 1st Ave");
+ addr.setCity("Somewhere");
+ addr.setState("AA");
+ addr.setCountry("USA");
+ addr.setNeighborAddressID(1);
+ addr.insert();
+
+ addr = addressStorage.prepare();
+ addr.setAddressID(3);
+ addr.setLine1("9999");
+ addr.setCity("Chicago");
+ addr.setState("IL");
+ addr.setCountry("USA");
+ addr.insert();
+
+ // Create some users
+ Storage<UserInfo> userStorage = mRepository.storageFor(UserInfo.class);
+ UserInfo user = userStorage.prepare();
+ user.setUserID(1);
+ user.setStateID(1);
+ user.setFirstName("Bob");
+ user.setLastName("Loblaw");
+ user.setAddressID(1);
+ user.insert();
+
+ user = userStorage.prepare();
+ user.setUserID(2);
+ user.setStateID(1);
+ user.setFirstName("Deb");
+ user.setLastName("Loblaw");
+ user.setAddressID(1);
+ user.insert();
+
+ user = userStorage.prepare();
+ user.setUserID(3);
+ user.setStateID(1);
+ user.setFirstName("No");
+ user.setLastName("Body");
+ user.setAddressID(2);
+ user.insert();
+
+ // Now do a basic join, finding everyone in IL.
+
+ FilterValues<UserInfo> values = Filter
+ .filterFor(UserInfo.class, "address.state = ?").initialFilterValues().with("IL");
+
+ Cursor<UserInfo> cursor = userExecutor.fetch(values);
+ assertTrue(cursor.hasNext());
+ assertEquals(1, cursor.next().getUserID());
+ assertEquals(2, cursor.next().getUserID());
+ assertFalse(cursor.hasNext());
+ cursor.close();
+
+ assertEquals(2L, userExecutor.count(values));
+
+ // Now do a multi join, finding everyone with an explicit neighbor in IL.
+
+ targetToSourceProperty = ChainedProperty.parse(info, "address.neighbor");
+
+ targetFilter = Filter.filterFor(UserInfo.class, "address.neighbor.state = ?");
+ targetOrdering = OrderingList.get(UserInfo.class, "+address.neighbor.country");
+
+ userExecutor = JoinedQueryExecutor.build
+ (repoAccess, targetToSourceProperty, targetFilter, targetOrdering);
+
+ assertEquals("address.neighbor.state = ?", userExecutor.getFilter().toString());
+ assertEquals("+address.neighbor.country", userExecutor.getOrdering().get(0).toString());
+
+ values = Filter
+ .filterFor(UserInfo.class, "address.neighbor.state = ?")
+ .initialFilterValues().with("IL");
+
+ cursor = userExecutor.fetch(values);
+ assertTrue(cursor.hasNext());
+ assertEquals(3, cursor.next().getUserID());
+ assertFalse(cursor.hasNext());
+ cursor.close();
+
+ assertEquals(1L, userExecutor.count(values));
+ }
+
+ class RepoAccess implements RepositoryAccess {
+ public Repository getRootRepository() {
+ return mRepository;
+ }
+
+ public <S extends Storable> StorageAccess<S> storageAccessFor(Class<S> type) {
+ return new StoreAccess<S>(type);
+ }
+ }
+
+ class StoreAccess<S extends Storable> implements StorageAccess<S>, QueryExecutorFactory<S> {
+ private final Class<S> mType;
+
+ StoreAccess(Class<S> type) {
+ mType = type;
+ }
+
+ public Class<S> getStorableType() {
+ return mType;
+ }
+
+ public QueryExecutorFactory<S> getQueryExecutorFactory() {
+ return this;
+ }
+
+ public QueryExecutor<S> executor(Filter<S> filter, OrderingList<S> ordering)
+ throws RepositoryException
+ {
+ Storage<S> storage = mRepository.storageFor(mType);
+
+ QueryExecutor<S> exec = new FullScanQueryExecutor<S>
+ (new ScanQuerySupport<S>(storage.query()));
+
+ if (filter != null) {
+ exec = new FilteredQueryExecutor<S>(exec, filter);
+ }
+
+ if (ordering != null && ordering.size() > 0) {
+ exec = new SortedQueryExecutor<S>(null, exec, null, ordering);
+ }
+
+ return exec;
+ }
+
+ public Collection<StorableIndex<S>> getAllIndexes() {
+ StorableIndex<S>[] indexes = new StorableIndex[0];
+ return Arrays.asList(indexes);
+ }
+
+ public Storage<S> storageDelegate(StorableIndex<S> index) {
+ return null;
+ }
+
+ public SortBuffer<S> createSortBuffer() {
+ return new ArraySortBuffer<S>();
+ }
+
+ public long countAll() {
+ throw new UnsupportedOperationException();
+ }
+
+ public Cursor<S> fetchAll() {
+ throw new UnsupportedOperationException();
+ }
+
+ public Cursor<S> fetchOne(StorableIndex<S> index, Object[] identityValues) {
+ throw new UnsupportedOperationException();
+ }
+
+ public Cursor<S> fetchSubset(StorableIndex<S> index,
+ Object[] identityValues,
+ BoundaryType rangeStartBoundary,
+ Object rangeStartValue,
+ BoundaryType rangeEndBoundary,
+ Object rangeEndValue,
+ boolean reverseRange,
+ boolean reverseOrder)
+ {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ static class ScanQuerySupport<S extends Storable> implements FullScanQueryExecutor.Support<S> {
+ private final Query<S> mQuery;
+
+ ScanQuerySupport(Query<S> query) {
+ mQuery = query;
+ }
+
+ public Class<S> getStorableType() {
+ return mQuery.getStorableType();
+ }
+
+ public long countAll() throws FetchException {
+ return mQuery.count();
+ }
+
+ public Cursor<S> fetchAll() throws FetchException {
+ return mQuery.fetch();
+ }
+ }
+}
diff --git a/src/test/java/com/amazon/carbonado/qe/TestOrderingList.java b/src/test/java/com/amazon/carbonado/qe/TestOrderingList.java new file mode 100644 index 0000000..8af7a2e --- /dev/null +++ b/src/test/java/com/amazon/carbonado/qe/TestOrderingList.java @@ -0,0 +1,225 @@ +/*
+ * 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.qe;
+
+import java.util.List;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.amazon.carbonado.info.OrderedProperty;
+
+import com.amazon.carbonado.stored.StorableTestBasic;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+public class TestOrderingList extends TestCase {
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static TestSuite suite() {
+ return new TestSuite(TestOrderingList.class);
+ }
+
+ public TestOrderingList(String name) {
+ super(name);
+ }
+
+ public void testEmpty() throws Exception {
+ assertEquals(0, OrderingList.get(StorableTestBasic.class).size());
+ }
+
+ public void testSingle() throws Exception {
+ List<OrderedProperty<StorableTestBasic>> list_1 =
+ OrderingList.get(StorableTestBasic.class, "date");
+
+ assertEquals(1, list_1.size());
+ assertEquals("+date", list_1.get(0).toString());
+
+ List<OrderedProperty<StorableTestBasic>> list_2 =
+ OrderingList.get(StorableTestBasic.class, "+date");
+
+ assertEquals(1, list_2.size());
+ assertEquals("+date", list_2.get(0).toString());
+ assertEquals(list_1, list_2);
+ assertTrue(list_1 == list_2);
+
+ List<OrderedProperty<StorableTestBasic>> list_3 =
+ OrderingList.get(StorableTestBasic.class, "-date");
+
+ assertEquals(1, list_3.size());
+ assertEquals("-date", list_3.get(0).toString());
+ assertFalse(list_2.equals(list_3));
+ assertFalse(list_2 == list_3);
+ }
+
+ public void testDouble() throws Exception {
+ List<OrderedProperty<StorableTestBasic>> list_1 =
+ OrderingList.get(StorableTestBasic.class, "date", "intProp");
+
+ assertEquals(2, list_1.size());
+ assertEquals("+date", list_1.get(0).toString());
+ assertEquals("+intProp", list_1.get(1).toString());
+
+ List<OrderedProperty<StorableTestBasic>> list_2 =
+ OrderingList.get(StorableTestBasic.class, "+date", "+intProp");
+
+ assertEquals(2, list_2.size());
+ assertEquals("+date", list_2.get(0).toString());
+ assertEquals("+intProp", list_2.get(1).toString());
+ assertEquals(list_1, list_2);
+ assertTrue(list_1 == list_2);
+
+ List<OrderedProperty<StorableTestBasic>> list_3 =
+ OrderingList.get(StorableTestBasic.class, "-date", "-intProp");
+
+ assertEquals(2, list_3.size());
+ assertEquals("-date", list_3.get(0).toString());
+ assertEquals("-intProp", list_3.get(1).toString());
+ assertFalse(list_2.equals(list_3));
+ assertFalse(list_2 == list_3);
+ }
+
+ public void testIllegal() throws Exception {
+ try {
+ OrderingList.get(StorableTestBasic.class, "foo");
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ public void testImmutable() throws Exception {
+ List<OrderedProperty<StorableTestBasic>> list =
+ OrderingList.get(StorableTestBasic.class, "~date");
+ try {
+ list.set(0, list.get(0).reverse());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ }
+ }
+
+ public void testConcatList() throws Exception {
+ OrderingList<StorableTestBasic> list_1 =
+ OrderingList.get(StorableTestBasic.class, "date", "-intProp", "~stringProp");
+
+ OrderingList<StorableTestBasic> list_2 =
+ OrderingList.get(StorableTestBasic.class, "longProp", "doubleProp");
+
+ OrderingList<StorableTestBasic> list_3 = list_1.concat(list_2);
+
+ assertEquals(5, list_3.size());
+ assertEquals("+date", list_3.get(0).toString());
+ assertEquals("-intProp", list_3.get(1).toString());
+ assertEquals("~stringProp", list_3.get(2).toString());
+ assertEquals("+longProp", list_3.get(3).toString());
+ assertEquals("+doubleProp", list_3.get(4).toString());
+
+ OrderingList<StorableTestBasic> list_4 =
+ OrderingList.get(StorableTestBasic.class,
+ "+date", "-intProp", "~stringProp", "longProp", "+doubleProp");
+
+ assertEquals(list_3, list_4);
+ assertTrue(list_3 == list_4);
+ }
+
+ public void testReverseDirections() throws Exception {
+ OrderingList<StorableTestBasic> list_1 =
+ OrderingList.get(StorableTestBasic.class, "date", "-intProp", "~stringProp");
+
+ list_1 = list_1.reverseDirections();
+
+ assertEquals(3, list_1.size());
+ assertEquals("-date", list_1.get(0).toString());
+ assertEquals("+intProp", list_1.get(1).toString());
+ assertEquals("~stringProp", list_1.get(2).toString());
+
+ OrderingList<StorableTestBasic> list_2 =
+ OrderingList.get(StorableTestBasic.class, "-date", "intProp", "~stringProp");
+
+ assertEquals(list_1, list_2);
+ assertTrue(list_1 == list_2);
+ }
+
+ public void testReplace() throws Exception {
+ OrderingList<StorableTestBasic> list_1 =
+ OrderingList.get(StorableTestBasic.class, "date", "-intProp", "~stringProp");
+
+ OrderedProperty<StorableTestBasic> op_0 = list_1.get(0);
+ OrderedProperty<StorableTestBasic> op_1 = list_1.get(1);
+ OrderedProperty<StorableTestBasic> op_2 = list_1.get(2);
+
+ list_1 = list_1.replace(0, op_1);
+ list_1 = list_1.replace(1, op_2);
+ list_1 = list_1.replace(2, op_0);
+
+ assertEquals(3, list_1.size());
+ assertEquals("-intProp", list_1.get(0).toString());
+ assertEquals("~stringProp", list_1.get(1).toString());
+ assertEquals("+date", list_1.get(2).toString());
+
+ OrderingList<StorableTestBasic> list_2 =
+ OrderingList.get(StorableTestBasic.class, "-intProp", "~stringProp", "+date");
+
+ assertEquals(list_1, list_2);
+ assertTrue(list_1 == list_2);
+ }
+
+ public void testSubList() throws Exception {
+ OrderingList<StorableTestBasic> list_1 =
+ OrderingList.get(StorableTestBasic.class, "date", "-intProp", "~stringProp");
+
+ assertEquals(0, list_1.subList(0, 0).size());
+ assertEquals(list_1, list_1.subList(0, 3));
+
+ OrderingList<StorableTestBasic> sub = list_1.subList(0, 1);
+ assertEquals(1, sub.size());
+ assertEquals("+date", sub.get(0).toString());
+
+ sub = list_1.subList(1, 3);
+ assertEquals(2, sub.size());
+ }
+
+ public void testAsArray() throws Exception {
+ OrderingList<StorableTestBasic> list =
+ OrderingList.get(StorableTestBasic.class, "date", "intProp", "stringProp");
+
+ OrderedProperty<StorableTestBasic>[] array = list.asArray();
+
+ assertEquals(3, array.length);
+ assertEquals("+date", array[0].toString());
+ assertEquals("+intProp", array[1].toString());
+ assertEquals("+stringProp", array[2].toString());
+ }
+
+ public void testAsStringArray() throws Exception {
+ OrderingList<StorableTestBasic> list =
+ OrderingList.get(StorableTestBasic.class, "date", "intProp", "stringProp");
+
+ String[] array = list.asStringArray();
+
+ assertEquals(3, array.length);
+ assertEquals("+date", array[0]);
+ assertEquals("+intProp", array[1]);
+ assertEquals("+stringProp", array[2]);
+ }
+}
diff --git a/src/test/java/com/amazon/carbonado/qe/TestOrderingScore.java b/src/test/java/com/amazon/carbonado/qe/TestOrderingScore.java new file mode 100644 index 0000000..785ef69 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/qe/TestOrderingScore.java @@ -0,0 +1,673 @@ +/*
+ * 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.qe;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.amazon.carbonado.Storable;
+
+import com.amazon.carbonado.info.Direction;
+import static com.amazon.carbonado.info.Direction.*;
+import com.amazon.carbonado.info.OrderedProperty;
+import com.amazon.carbonado.info.StorableIndex;
+import com.amazon.carbonado.info.StorableInfo;
+import com.amazon.carbonado.info.StorableIntrospector;
+import com.amazon.carbonado.info.StorableProperty;
+
+import com.amazon.carbonado.filter.Filter;
+
+import com.amazon.carbonado.stored.StorableTestBasic;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+public class TestOrderingScore extends TestCase {
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static TestSuite suite() {
+ return new TestSuite(TestOrderingScore.class);
+ }
+
+ static <S extends Storable> StorableIndex<S> makeIndex(Class<S> type, String... props) {
+ return new StorableIndex<S>(makeOrdering(type, props).asArray(), UNSPECIFIED);
+ }
+
+ static <S extends Storable> OrderingList<S> makeOrdering(Class<S> type, String... props) {
+ return OrderingList.get(type, props);
+ }
+
+ public TestOrderingScore(String name) {
+ super(name);
+ }
+
+ public void testEmpty() throws Exception {
+ StorableIndex<StorableTestBasic> ix = makeIndex(StorableTestBasic.class, "id");
+
+ OrderingScore score = OrderingScore.evaluate(ix, null, null);
+
+ assertEquals(0, score.getHandledCount());
+ assertEquals(0, score.getRemainderCount());
+ assertEquals(false, score.shouldReverseOrder());
+ }
+
+ public void testOneProp() throws Exception {
+ StorableIndex<StorableTestBasic> ix;
+ OrderingList<StorableTestBasic> ops;
+ OrderingScore<StorableTestBasic> score;
+
+ /////////////
+ ix = makeIndex(StorableTestBasic.class, "id");
+
+ ops = makeOrdering(StorableTestBasic.class, "id");
+ score = OrderingScore.evaluate(ix, null, ops);
+
+ assertEquals(1, score.getHandledCount());
+ assertEquals("+id", score.getHandledOrdering().get(0).toString());
+ assertEquals(0, score.getRemainderCount());
+ assertEquals(false, score.shouldReverseOrder());
+
+ ops = makeOrdering(StorableTestBasic.class, "+id");
+ score = OrderingScore.evaluate(ix, null, ops);
+
+ assertEquals(1, score.getHandledCount());
+ assertEquals("+id", score.getHandledOrdering().get(0).toString());
+ assertEquals(0, score.getRemainderCount());
+ assertEquals(false, score.shouldReverseOrder());
+
+ ops = makeOrdering(StorableTestBasic.class, "-id");
+ score = OrderingScore.evaluate(ix, null, ops);
+
+ assertEquals(1, score.getHandledCount());
+ assertEquals("-id", score.getHandledOrdering().get(0).toString());
+ assertEquals(0, score.getRemainderCount());
+ assertEquals(true, score.shouldReverseOrder());
+
+ /////////////
+ ix = makeIndex(StorableTestBasic.class, "+id");
+
+ ops = makeOrdering(StorableTestBasic.class, "id");
+ score = OrderingScore.evaluate(ix, null, ops);
+
+ assertEquals(1, score.getHandledCount());
+ assertEquals("+id", score.getHandledOrdering().get(0).toString());
+ assertEquals(0, score.getRemainderCount());
+ assertEquals(false, score.shouldReverseOrder());
+
+ ops = makeOrdering(StorableTestBasic.class, "+id");
+ score = OrderingScore.evaluate(ix, null, ops);
+
+ assertEquals(1, score.getHandledCount());
+ assertEquals("+id", score.getHandledOrdering().get(0).toString());
+ assertEquals(0, score.getRemainderCount());
+ assertEquals(false, score.shouldReverseOrder());
+
+ ops = makeOrdering(StorableTestBasic.class, "-id");
+ score = OrderingScore.evaluate(ix, null, ops);
+
+ assertEquals(1, score.getHandledCount());
+ assertEquals("-id", score.getHandledOrdering().get(0).toString());
+ assertEquals(0, score.getRemainderCount());
+ assertEquals(true, score.shouldReverseOrder());
+
+ /////////////
+ ix = makeIndex(StorableTestBasic.class, "-id");
+
+ ops = makeOrdering(StorableTestBasic.class, "id");
+ score = OrderingScore.evaluate(ix, null, ops);
+
+ assertEquals(1, score.getHandledCount());
+ assertEquals("+id", score.getHandledOrdering().get(0).toString());
+ assertEquals(0, score.getRemainderCount());
+ assertEquals(true, score.shouldReverseOrder());
+
+ ops = makeOrdering(StorableTestBasic.class, "+id");
+ score = OrderingScore.evaluate(ix, null, ops);
+
+ assertEquals(1, score.getHandledCount());
+ assertEquals("+id", score.getHandledOrdering().get(0).toString());
+ assertEquals(0, score.getRemainderCount());
+ assertEquals(true, score.shouldReverseOrder());
+
+ ops = makeOrdering(StorableTestBasic.class, "-id");
+ score = OrderingScore.evaluate(ix, null, ops);
+
+ assertEquals(1, score.getHandledCount());
+ assertEquals("-id", score.getHandledOrdering().get(0).toString());
+ assertEquals(0, score.getRemainderCount());
+ assertEquals(false, score.shouldReverseOrder());
+
+ /////////////
+ ix = makeIndex(StorableTestBasic.class, "intProp");
+
+ ops = makeOrdering(StorableTestBasic.class, "id");
+ score = OrderingScore.evaluate(ix, null, ops);
+
+ assertEquals(0, score.getHandledCount());
+ assertEquals(1, score.getRemainderCount());
+ assertEquals("+id", score.getRemainderOrdering().get(0).toString());
+ assertEquals(false, score.shouldReverseOrder());
+ }
+
+ public void testMultipleProps() throws Exception {
+ final StorableIndex<StorableTestBasic> ix;
+ OrderingList<StorableTestBasic> ops;
+ OrderingScore<StorableTestBasic> score;
+
+ ix = makeIndex(StorableTestBasic.class, "id", "intProp");
+
+ ops = makeOrdering(StorableTestBasic.class, "id");
+ score = OrderingScore.evaluate(ix, null, ops);
+ assertEquals(1, score.getHandledCount());
+ assertEquals("+id", score.getHandledOrdering().get(0).toString());
+ assertEquals(0, score.getRemainderCount());
+ assertEquals(false, score.shouldReverseOrder());
+
+ ops = makeOrdering(StorableTestBasic.class, "-id");
+ score = OrderingScore.evaluate(ix, null, ops);
+ assertEquals(1, score.getHandledCount());
+ assertEquals("-id", score.getHandledOrdering().get(0).toString());
+ assertEquals(0, score.getRemainderCount());
+ assertEquals(true, score.shouldReverseOrder());
+
+ ops = makeOrdering(StorableTestBasic.class, "id", "intProp");
+ score = OrderingScore.evaluate(ix, null, ops);
+ assertEquals(2, score.getHandledCount());
+ assertEquals("+id", score.getHandledOrdering().get(0).toString());
+ assertEquals("+intProp", score.getHandledOrdering().get(1).toString());
+ assertEquals(0, score.getRemainderCount());
+ assertEquals(false, score.shouldReverseOrder());
+
+ ops = makeOrdering(StorableTestBasic.class, "-id", "-intProp");
+ score = OrderingScore.evaluate(ix, null, ops);
+ assertEquals(2, score.getHandledCount());
+ assertEquals("-id", score.getHandledOrdering().get(0).toString());
+ assertEquals("-intProp", score.getHandledOrdering().get(1).toString());
+ assertEquals(0, score.getRemainderCount());
+ assertEquals(true, score.shouldReverseOrder());
+
+ ops = makeOrdering(StorableTestBasic.class, "-id", "+intProp");
+ score = OrderingScore.evaluate(ix, null, ops);
+ assertEquals(1, score.getHandledCount());
+ assertEquals("-id", score.getHandledOrdering().get(0).toString());
+ assertEquals(1, score.getRemainderCount());
+ assertEquals("+intProp", score.getRemainderOrdering().get(0).toString());
+ assertEquals(true, score.shouldReverseOrder());
+
+ ops = makeOrdering(StorableTestBasic.class, "+id", "-intProp");
+ score = OrderingScore.evaluate(ix, null, ops);
+ assertEquals(1, score.getHandledCount());
+ assertEquals("+id", score.getHandledOrdering().get(0).toString());
+ assertEquals(1, score.getRemainderCount());
+ assertEquals("-intProp", score.getRemainderOrdering().get(0).toString());
+ assertEquals(false, score.shouldReverseOrder());
+
+ ops = makeOrdering(StorableTestBasic.class, "intProp");
+ score = OrderingScore.evaluate(ix, null, ops);
+ assertEquals(0, score.getHandledCount());
+ assertEquals(1, score.getRemainderCount());
+ assertEquals("+intProp", score.getRemainderOrdering().get(0).toString());
+ assertEquals(false, score.shouldReverseOrder());
+
+ // Gap is allowed if identity filtered.
+
+ Filter<StorableTestBasic> filter;
+
+ filter = Filter.filterFor(StorableTestBasic.class, "id = ?");
+
+ ops = makeOrdering(StorableTestBasic.class, "intProp");
+ score = OrderingScore.evaluate(ix, filter, ops);
+ assertEquals(1, score.getHandledCount());
+ assertEquals("+intProp", score.getHandledOrdering().get(0).toString());
+ assertEquals(0, score.getRemainderCount());
+ assertEquals(false, score.shouldReverseOrder());
+
+ ops = makeOrdering(StorableTestBasic.class, "-intProp");
+ score = OrderingScore.evaluate(ix, filter, ops);
+ assertEquals(1, score.getHandledCount());
+ assertEquals("-intProp", score.getHandledOrdering().get(0).toString());
+ assertEquals(0, score.getRemainderCount());
+ assertEquals(true, score.shouldReverseOrder());
+
+ ops = makeOrdering(StorableTestBasic.class, "intProp", "id");
+ score = OrderingScore.evaluate(ix, filter, ops);
+ assertEquals(1, score.getHandledCount());
+ assertEquals("+intProp", score.getHandledOrdering().get(0).toString());
+ // Since "id" is filtered, don't count as remainder.
+ assertEquals(0, score.getRemainderCount());
+ assertEquals(false, score.shouldReverseOrder());
+
+ ops = makeOrdering(StorableTestBasic.class, "-intProp", "id");
+ score = OrderingScore.evaluate(ix, filter, ops);
+ assertEquals(1, score.getHandledCount());
+ assertEquals("-intProp", score.getHandledOrdering().get(0).toString());
+ // Since "id" is filtered, don't count as remainder.
+ assertEquals(0, score.getRemainderCount());
+ assertEquals(true, score.shouldReverseOrder());
+
+ ops = makeOrdering(StorableTestBasic.class, "intProp", "doubleProp");
+ score = OrderingScore.evaluate(ix, filter, ops);
+ assertEquals(1, score.getHandledCount());
+ assertEquals("+intProp", score.getHandledOrdering().get(0).toString());
+ assertEquals(1, score.getRemainderCount());
+ assertEquals("+doubleProp", score.getRemainderOrdering().get(0).toString());
+ assertEquals(false, score.shouldReverseOrder());
+
+ ops = makeOrdering(StorableTestBasic.class, "intProp", "-doubleProp");
+ score = OrderingScore.evaluate(ix, filter, ops);
+ assertEquals(1, score.getHandledCount());
+ assertEquals("+intProp", score.getHandledOrdering().get(0).toString());
+ assertEquals(1, score.getRemainderCount());
+ assertEquals("-doubleProp", score.getRemainderOrdering().get(0).toString());
+ assertEquals(false, score.shouldReverseOrder());
+
+ filter = Filter.filterFor(StorableTestBasic.class, "id > ? & doubleProp = ?");
+
+ ops = makeOrdering(StorableTestBasic.class, "intProp");
+ score = OrderingScore.evaluate(ix, filter, ops);
+ assertEquals(0, score.getHandledCount());
+ assertEquals(1, score.getRemainderCount());
+ assertEquals("+intProp", score.getRemainderOrdering().get(0).toString());
+ assertEquals(false, score.shouldReverseOrder());
+
+ ops = makeOrdering(StorableTestBasic.class, "doubleProp");
+ score = OrderingScore.evaluate(ix, filter, ops);
+ assertEquals(0, score.getHandledCount());
+ assertEquals(0, score.getRemainderCount());
+ assertEquals(false, score.shouldReverseOrder());
+
+ filter = Filter.filterFor(StorableTestBasic.class, "doubleProp = ? & id = ?");
+
+ ops = makeOrdering(StorableTestBasic.class, "doubleProp", "-intProp");
+ score = OrderingScore.evaluate(ix, filter, ops);
+ assertEquals(1, score.getHandledCount());
+ assertEquals("-intProp", score.getHandledOrdering().get(0).toString());
+ assertEquals(0, score.getRemainderCount());
+ assertEquals(true, score.shouldReverseOrder());
+ }
+
+ public void testMidGap() throws Exception {
+ final StorableIndex<StorableTestBasic> ix;
+ OrderingList<StorableTestBasic> ops;
+ OrderingScore score;
+ Filter<StorableTestBasic> filter;
+
+ ix = makeIndex(StorableTestBasic.class, "id", "intProp", "doubleProp", "-stringProp");
+
+ ops = makeOrdering(StorableTestBasic.class, "id", "intProp", "doubleProp", "-stringProp");
+ score = OrderingScore.evaluate(ix, null, ops);
+ assertEquals(4, score.getHandledCount());
+ assertEquals("+id", score.getHandledOrdering().get(0).toString());
+ assertEquals("+intProp", score.getHandledOrdering().get(1).toString());
+ assertEquals("+doubleProp", score.getHandledOrdering().get(2).toString());
+ assertEquals("-stringProp", score.getHandledOrdering().get(3).toString());
+ assertEquals(0, score.getRemainderCount());
+ assertEquals(false, score.shouldReverseOrder());
+
+ // Now ignore mid index properties, creating a gap.
+
+ ops = makeOrdering(StorableTestBasic.class, "id", "-stringProp");
+ score = OrderingScore.evaluate(ix, null, ops);
+ assertEquals(1, score.getHandledCount());
+ assertEquals("+id", score.getHandledOrdering().get(0).toString());
+ assertEquals(1, score.getRemainderCount());
+ assertEquals("-stringProp", score.getRemainderOrdering().get(0).toString());
+ assertEquals(false, score.shouldReverseOrder());
+
+ // Gap can be bridged if property is filtered out. First test with
+ // incomplete bridges.
+
+ filter = Filter.filterFor(StorableTestBasic.class, "doubleProp = ? & intProp > ?");
+
+ ops = makeOrdering(StorableTestBasic.class, "id", "-stringProp");
+ score = OrderingScore.evaluate(ix, filter, ops);
+ assertEquals(1, score.getHandledCount());
+ assertEquals("+id", score.getHandledOrdering().get(0).toString());
+ assertEquals(1, score.getRemainderCount());
+ assertEquals("-stringProp", score.getRemainderOrdering().get(0).toString());
+ assertEquals(false, score.shouldReverseOrder());
+
+ filter = Filter.filterFor(StorableTestBasic.class, "doubleProp >= ? & intProp = ?");
+
+ ops = makeOrdering(StorableTestBasic.class, "id", "-stringProp");
+ score = OrderingScore.evaluate(ix, filter, ops);
+ assertEquals(1, score.getHandledCount());
+ assertEquals("+id", score.getHandledOrdering().get(0).toString());
+ assertEquals(1, score.getRemainderCount());
+ assertEquals("-stringProp", score.getRemainderOrdering().get(0).toString());
+ assertEquals(false, score.shouldReverseOrder());
+
+ // Now a complete bridge.
+
+ filter = Filter.filterFor(StorableTestBasic.class, "doubleProp = ? & intProp = ?");
+
+ ops = makeOrdering(StorableTestBasic.class, "id", "-stringProp");
+ score = OrderingScore.evaluate(ix, filter, ops);
+ assertEquals(2, score.getHandledCount());
+ assertEquals("+id", score.getHandledOrdering().get(0).toString());
+ assertEquals("-stringProp", score.getHandledOrdering().get(1).toString());
+ assertEquals(0, score.getRemainderCount());
+ assertEquals(false, score.shouldReverseOrder());
+
+ // Again in reverse.
+
+ ops = makeOrdering(StorableTestBasic.class, "-id", "stringProp");
+ score = OrderingScore.evaluate(ix, filter, ops);
+ assertEquals(2, score.getHandledCount());
+ assertEquals("-id", score.getHandledOrdering().get(0).toString());
+ assertEquals("+stringProp", score.getHandledOrdering().get(1).toString());
+ assertEquals(0, score.getRemainderCount());
+ assertEquals(true, score.shouldReverseOrder());
+
+ // Failed double reverse.
+
+ ops = makeOrdering(StorableTestBasic.class, "-id", "-stringProp");
+ score = OrderingScore.evaluate(ix, filter, ops);
+ assertEquals(1, score.getHandledCount());
+ assertEquals("-id", score.getHandledOrdering().get(0).toString());
+ assertEquals(1, score.getRemainderCount());
+ assertEquals("-stringProp", score.getRemainderOrdering().get(0).toString());
+ assertEquals(true, score.shouldReverseOrder());
+ }
+
+ public void testComparator() throws Exception {
+ StorableIndex<StorableTestBasic> ix_1, ix_2;
+ OrderingList<StorableTestBasic> ops;
+ OrderingScore score_1, score_2;
+ Filter<StorableTestBasic> filter;
+ Comparator<OrderingScore<?>> comp = OrderingScore.fullComparator();
+
+ ix_1 = makeIndex(StorableTestBasic.class, "id", "intProp", "doubleProp", "-stringProp");
+ ix_2 = makeIndex(StorableTestBasic.class, "intProp", "doubleProp", "id");
+
+ ops = makeOrdering(StorableTestBasic.class, "-id", "-intProp");
+ score_1 = OrderingScore.evaluate(ix_1, null, ops);
+ score_2 = OrderingScore.evaluate(ix_2, null, ops);
+
+ assertEquals(-1, comp.compare(score_1, score_2));
+ assertEquals(1, comp.compare(score_2, score_1));
+
+ filter = Filter.filterFor(StorableTestBasic.class, "id = ?");
+ score_1 = OrderingScore.evaluate(ix_1, filter, ops);
+ score_2 = OrderingScore.evaluate(ix_2, filter, ops);
+
+ // Index 2 has less properties, so it is better.
+ assertEquals(1, comp.compare(score_1, score_2));
+ assertEquals(-1, comp.compare(score_2, score_1));
+
+ // Keep ix_2 slightly better by matching desired order.
+ ix_2 = makeIndex(StorableTestBasic.class, "-intProp", "doubleProp", "id", "stringProp");
+
+ score_1 = OrderingScore.evaluate(ix_1, filter, ops);
+ score_2 = OrderingScore.evaluate(ix_2, filter, ops);
+
+ assertEquals(1, comp.compare(score_1, score_2));
+ assertEquals(-1, comp.compare(score_2, score_1));
+
+ // Make ix_1 slightly better by making it clustered.
+ ix_1 = ix_1.clustered(true);
+
+ score_1 = OrderingScore.evaluate(ix_1, filter, ops);
+ score_2 = OrderingScore.evaluate(ix_2, filter, ops);
+
+ assertEquals(-1, comp.compare(score_1, score_2));
+ assertEquals(1, comp.compare(score_2, score_1));
+
+ // Make ix_2 better when clustered.
+ ix_2 = ix_2.clustered(true);
+
+ score_1 = OrderingScore.evaluate(ix_1, filter, ops);
+ score_2 = OrderingScore.evaluate(ix_2, filter, ops);
+
+ assertEquals(1, comp.compare(score_1, score_2));
+ assertEquals(-1, comp.compare(score_2, score_1));
+
+ // Make ix_1 same by reversing order.
+ ix_1 = ix_1.reverse();
+
+ score_1 = OrderingScore.evaluate(ix_1, filter, ops);
+ score_2 = OrderingScore.evaluate(ix_2, filter, ops);
+
+ assertEquals(0, comp.compare(score_1, score_2));
+ assertEquals(0, comp.compare(score_2, score_1));
+ }
+
+ public void testIndexNotNeeded() throws Exception {
+ // Test an index which matches desired orderings, but ordering
+ // properties are filtered out. Thus the index is not needed.
+
+ final StorableIndex<StorableTestBasic> ix;
+ OrderingList<StorableTestBasic> ops;
+ OrderingScore score;
+ Filter<StorableTestBasic> filter;
+
+ ix = makeIndex(StorableTestBasic.class, "id", "intProp", "doubleProp");
+
+ ops = makeOrdering(StorableTestBasic.class, "id", "intProp", "doubleProp");
+ score = OrderingScore.evaluate(ix, null, ops);
+ assertEquals(3, score.getHandledCount());
+ assertEquals("+id", score.getHandledOrdering().get(0).toString());
+ assertEquals("+intProp", score.getHandledOrdering().get(1).toString());
+ assertEquals("+doubleProp", score.getHandledOrdering().get(2).toString());
+ assertEquals(0, score.getRemainderCount());
+ assertEquals(false, score.shouldReverseOrder());
+
+ filter = Filter.filterFor(StorableTestBasic.class, "id = ? & intProp = ?");
+
+ score = OrderingScore.evaluate(ix, filter, ops);
+ assertEquals(1, score.getHandledCount());
+ assertEquals("+doubleProp", score.getHandledOrdering().get(0).toString());
+ assertEquals(0, score.getRemainderCount());
+ assertEquals(false, score.shouldReverseOrder());
+
+ filter = Filter.filterFor(StorableTestBasic.class, "id = ? & intProp = ? & doubleProp =?");
+
+ score = OrderingScore.evaluate(ix, filter, ops);
+ assertEquals(0, score.getHandledCount());
+ assertEquals(0, score.getRemainderCount());
+ assertEquals(false, score.shouldReverseOrder());
+ }
+
+ public void testUniqueIndexNotNeeded() throws Exception {
+ // Test a unique index which has been fully specified. Ordering is not
+ // needed at all.
+ final StorableIndex<StorableTestBasic> ix;
+ OrderingList<StorableTestBasic> ops;
+ OrderingScore score;
+ Filter<StorableTestBasic> filter;
+
+ ix = makeIndex(StorableTestBasic.class, "id", "intProp", "doubleProp").unique(true);
+ ops = makeOrdering(StorableTestBasic.class, "stringProp", "doubleProp");
+ filter = Filter.filterFor(StorableTestBasic.class, "id = ? & intProp = ? & doubleProp =?");
+
+ score = OrderingScore.evaluate(ix, filter, ops);
+ assertEquals(0, score.getHandledCount());
+ assertEquals(0, score.getRemainderCount());
+ assertEquals(false, score.shouldReverseOrder());
+ }
+
+ public void testReduce() throws Exception {
+ // Tests that redundant ordering properties are removed.
+ final StorableIndex<StorableTestBasic> ix;
+ OrderingList<StorableTestBasic> ops;
+ OrderingScore score;
+ Filter<StorableTestBasic> filter;
+
+ ix = makeIndex(StorableTestBasic.class, "id", "intProp", "doubleProp");
+ ops = makeOrdering(StorableTestBasic.class,
+ "intProp", "intProp", "id", "doubleProp", "intProp", "doubleProp",
+ "longProp", "longProp", "id", "intProp", "doubleProp");
+ filter = Filter.filterFor(StorableTestBasic.class, "id = ?");
+
+ score = OrderingScore.evaluate(ix, filter, ops);
+ assertEquals(2, score.getHandledCount());
+ assertEquals(1, score.getRemainderCount());
+ assertEquals(false, score.shouldReverseOrder());
+
+ assertEquals("+intProp", score.getHandledOrdering().get(0).toString());
+ assertEquals("+doubleProp", score.getHandledOrdering().get(1).toString());
+ assertEquals("+longProp", score.getRemainderOrdering().get(0).toString());
+ }
+
+ public void testUnspecifiedDirection() throws Exception {
+ // Tests that an originally unspecified ordering direction is determined.
+ final StorableIndex<StorableTestBasic> ix;
+ OrderingList<StorableTestBasic> ops;
+ OrderingScore score;
+ Filter<StorableTestBasic> filter;
+
+ ix = makeIndex(StorableTestBasic.class, "id", "intProp", "doubleProp");
+ ops = makeOrdering(StorableTestBasic.class, "~intProp", "-doubleProp");
+ filter = Filter.filterFor(StorableTestBasic.class, "id = ?");
+
+ score = OrderingScore.evaluate(ix, filter, ops);
+ assertEquals(2, score.getHandledCount());
+ assertEquals(0, score.getRemainderCount());
+ assertEquals(true, score.shouldReverseOrder());
+
+ assertEquals("-intProp", score.getHandledOrdering().get(0).toString());
+ assertEquals("-doubleProp", score.getHandledOrdering().get(1).toString());
+
+ ops = makeOrdering(StorableTestBasic.class, "~id", "intProp", "~doubleProp");
+
+ score = OrderingScore.evaluate(ix, null, ops);
+ assertEquals(3, score.getHandledCount());
+ assertEquals(0, score.getRemainderCount());
+ assertEquals(false, score.shouldReverseOrder());
+
+ assertEquals("+id", score.getHandledOrdering().get(0).toString());
+ assertEquals("+intProp", score.getHandledOrdering().get(1).toString());
+ assertEquals("+doubleProp", score.getHandledOrdering().get(2).toString());
+
+ ops = makeOrdering(StorableTestBasic.class, "~id", "-intProp", "~doubleProp");
+
+ score = OrderingScore.evaluate(ix, null, ops);
+ assertEquals(3, score.getHandledCount());
+ assertEquals(0, score.getRemainderCount());
+ assertEquals(true, score.shouldReverseOrder());
+
+ assertEquals("-id", score.getHandledOrdering().get(0).toString());
+ assertEquals("-intProp", score.getHandledOrdering().get(1).toString());
+ assertEquals("-doubleProp", score.getHandledOrdering().get(2).toString());
+
+ ops = makeOrdering(StorableTestBasic.class, "~id", "-intProp", "~longProp");
+
+ score = OrderingScore.evaluate(ix, null, ops);
+ assertEquals(2, score.getHandledCount());
+ assertEquals(1, score.getRemainderCount());
+ assertEquals(true, score.shouldReverseOrder());
+
+ assertEquals("-id", score.getHandledOrdering().get(0).toString());
+ assertEquals("-intProp", score.getHandledOrdering().get(1).toString());
+ assertEquals("~longProp", score.getRemainderOrdering().get(0).toString());
+ }
+
+ public void testFreeOrdering() throws Exception {
+ final StorableIndex<StorableTestBasic> ix;
+ OrderingList<StorableTestBasic> ops;
+ OrderingScore score;
+ Filter<StorableTestBasic> filter = null;
+
+ ix = makeIndex(StorableTestBasic.class, "id", "intProp", "doubleProp");
+ ops = makeOrdering(StorableTestBasic.class, "~id");
+
+ score = OrderingScore.evaluate(ix, filter, ops);
+ assertEquals(1, score.getHandledCount());
+ assertEquals(0, score.getRemainderCount());
+ assertEquals(false, score.shouldReverseOrder());
+ assertEquals(2, score.getFreeOrdering().size());
+ assertEquals("~intProp", score.getFreeOrdering().get(0).toString());
+ assertEquals("~doubleProp", score.getFreeOrdering().get(1).toString());
+ assertEquals(0, score.getUnusedOrdering().size());
+
+ ops = makeOrdering(StorableTestBasic.class, "~id", "-intProp");
+
+ score = OrderingScore.evaluate(ix, filter, ops);
+ assertEquals(2, score.getHandledCount());
+ assertEquals(0, score.getRemainderCount());
+ assertEquals(true, score.shouldReverseOrder());
+ assertEquals(1, score.getFreeOrdering().size());
+ assertEquals("-doubleProp", score.getFreeOrdering().get(0).toString());
+ assertEquals(0, score.getUnusedOrdering().size());
+
+ ops = makeOrdering(StorableTestBasic.class, "~id", "-intProp", "+doubleProp");
+
+ score = OrderingScore.evaluate(ix, filter, ops);
+ assertEquals(2, score.getHandledCount());
+ assertEquals(1, score.getRemainderCount());
+ assertEquals("+doubleProp", score.getRemainderOrdering().get(0).toString());
+ assertEquals(true, score.shouldReverseOrder());
+ assertEquals(1, score.getFreeOrdering().size());
+ assertEquals("-doubleProp", score.getFreeOrdering().get(0).toString());
+ assertEquals(0, score.getUnusedOrdering().size());
+ }
+
+ public void testFreeAndUnusedOrdering() throws Exception {
+ final StorableIndex<StorableTestBasic> ix;
+ OrderingList<StorableTestBasic> ops;
+ OrderingScore score;
+ Filter<StorableTestBasic> filter;
+
+ ix = makeIndex(StorableTestBasic.class, "stringProp", "id", "intProp", "doubleProp");
+ ops = makeOrdering(StorableTestBasic.class, "~id");
+ filter = Filter.filterFor(StorableTestBasic.class, "stringProp = ?");
+
+ score = OrderingScore.evaluate(ix, filter, ops);
+ assertEquals(1, score.getHandledCount());
+ assertEquals(0, score.getRemainderCount());
+ assertEquals(false, score.shouldReverseOrder());
+ assertEquals(2, score.getFreeOrdering().size());
+ assertEquals("~intProp", score.getFreeOrdering().get(0).toString());
+ assertEquals("~doubleProp", score.getFreeOrdering().get(1).toString());
+ assertEquals(1, score.getUnusedOrdering().size());
+ assertEquals("~stringProp", score.getUnusedOrdering().get(0).toString());
+
+ ops = makeOrdering(StorableTestBasic.class, "~id", "-intProp");
+
+ score = OrderingScore.evaluate(ix, filter, ops);
+ assertEquals(2, score.getHandledCount());
+ assertEquals(0, score.getRemainderCount());
+ assertEquals(true, score.shouldReverseOrder());
+ assertEquals(1, score.getFreeOrdering().size());
+ assertEquals("-doubleProp", score.getFreeOrdering().get(0).toString());
+ assertEquals(1, score.getUnusedOrdering().size());
+ assertEquals("~stringProp", score.getUnusedOrdering().get(0).toString());
+
+ ops = makeOrdering(StorableTestBasic.class, "~id", "-intProp", "+doubleProp");
+
+ score = OrderingScore.evaluate(ix, filter, ops);
+ assertEquals(2, score.getHandledCount());
+ assertEquals(1, score.getRemainderCount());
+ assertEquals("+doubleProp", score.getRemainderOrdering().get(0).toString());
+ assertEquals(true, score.shouldReverseOrder());
+ assertEquals(1, score.getFreeOrdering().size());
+ assertEquals("-doubleProp", score.getFreeOrdering().get(0).toString());
+ assertEquals(1, score.getUnusedOrdering().size());
+ assertEquals("~stringProp", score.getUnusedOrdering().get(0).toString());
+ }
+}
diff --git a/src/test/java/com/amazon/carbonado/qe/TestPropertyFilterList.java b/src/test/java/com/amazon/carbonado/qe/TestPropertyFilterList.java new file mode 100644 index 0000000..b0af52f --- /dev/null +++ b/src/test/java/com/amazon/carbonado/qe/TestPropertyFilterList.java @@ -0,0 +1,108 @@ +/*
+ * 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.qe;
+
+import java.util.List;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.amazon.carbonado.filter.Filter;
+import com.amazon.carbonado.filter.PropertyFilter;
+
+import com.amazon.carbonado.stored.StorableTestBasic;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+public class TestPropertyFilterList extends TestCase {
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static TestSuite suite() {
+ return new TestSuite(TestPropertyFilterList.class);
+ }
+
+ public TestPropertyFilterList(String name) {
+ super(name);
+ }
+
+ public void testNull() throws Exception {
+ assertEquals(0, PropertyFilterList.get(null).size());
+ }
+
+ public void testOpen() throws Exception {
+ Filter<StorableTestBasic> filter = Filter.getOpenFilter(StorableTestBasic.class);
+ assertEquals(0, PropertyFilterList.get(filter).size());
+ }
+
+ public void testClosed() throws Exception {
+ Filter<StorableTestBasic> filter = Filter.getClosedFilter(StorableTestBasic.class);
+ assertEquals(0, PropertyFilterList.get(filter).size());
+ }
+
+ public void testSingleton() throws Exception {
+ Filter<StorableTestBasic> filter = Filter.filterFor(StorableTestBasic.class, "id = ?");
+
+ List<PropertyFilter<StorableTestBasic>> list = PropertyFilterList.get(filter);
+
+ assertEquals(1, list.size());
+ assertEquals(filter, list.get(0));
+
+ List<PropertyFilter<StorableTestBasic>> list2 = PropertyFilterList.get(filter);
+
+ assertTrue(list == list2);
+ }
+
+ public void testMultiple() throws Exception {
+ Filter<StorableTestBasic> filter =
+ Filter.filterFor(StorableTestBasic.class, "id = ? & intProp > ?");
+
+ List<PropertyFilter<StorableTestBasic>> list = PropertyFilterList.get(filter);
+
+ assertEquals(2, list.size());
+
+ Filter<StorableTestBasic> subFilter =
+ Filter.filterFor(StorableTestBasic.class, "id = ?");
+
+ assertEquals(subFilter, list.get(0));
+
+ subFilter = Filter.filterFor(StorableTestBasic.class, "intProp > ?");
+
+ assertEquals(subFilter, list.get(1));
+
+ List<PropertyFilter<StorableTestBasic>> list2 = PropertyFilterList.get(filter);
+
+ assertTrue(list == list2);
+ }
+
+ public void testIllegal() throws Exception {
+ Filter<StorableTestBasic> filter =
+ Filter.filterFor(StorableTestBasic.class, "id = ? | intProp > ?");
+
+ try {
+ List<PropertyFilter<StorableTestBasic>> list = PropertyFilterList.get(filter);
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+ }
+}
diff --git a/src/test/java/com/amazon/carbonado/qe/TestQueryExecutor.java b/src/test/java/com/amazon/carbonado/qe/TestQueryExecutor.java new file mode 100644 index 0000000..07558f1 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/qe/TestQueryExecutor.java @@ -0,0 +1,186 @@ +/*
+ * 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.qe;
+
+import java.io.IOException;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import com.amazon.carbonado.Cursor;
+import com.amazon.carbonado.FetchException;
+
+import com.amazon.carbonado.info.Direction;
+import com.amazon.carbonado.info.OrderedProperty;
+import com.amazon.carbonado.info.StorableInfo;
+import com.amazon.carbonado.info.StorableIntrospector;
+import com.amazon.carbonado.info.StorableProperty;
+
+import com.amazon.carbonado.stored.Dummy;
+import com.amazon.carbonado.stored.Address;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+public abstract class TestQueryExecutor extends TestCase {
+
+ protected QueryExecutor<Address> createExecutor(int... ids) {
+ return new IterableQueryExecutor<Address>(Address.class, createCollection(ids));
+ }
+
+ protected Collection<Address> createCollection(int... ids) {
+ Collection<Address> elements = new ArrayList<Address>(ids.length);
+ for (int i=0; i<ids.length; i++) {
+ elements.add(new DummyAddress(ids[i]));
+ }
+ return elements;
+ }
+
+ protected void compareElements(Cursor<Address> elements, int... expectedIDs)
+ throws FetchException
+ {
+ for (int id : expectedIDs) {
+ if (elements.hasNext()) {
+ Address e = elements.next();
+ if (e.getAddressID() != id) {
+ fail("Element mismatch: expected=" + id + ", actual=" + e.getAddressID());
+ elements.close();
+ return;
+ }
+ } else {
+ fail("Too few elements in cursor");
+ return;
+ }
+ }
+
+ if (elements.hasNext()) {
+ Address e = elements.next();
+ fail("Too many elements in cursor: " + e.getAddressID());
+ elements.close();
+ }
+ }
+
+ protected OrderingList<Address> createOrdering(String... properties) {
+ return OrderingList.get(Address.class, properties);
+ }
+
+ static void printPlan(QueryExecutor executor) {
+ try {
+ executor.printPlan(System.out, 0, null);
+ } catch (IOException e) {
+ }
+ }
+
+ private static class DummyAddress extends Dummy implements Address {
+ private long mID;
+ private String mLine1;
+ private String mLine2;
+ private String mCity;
+ private String mState;
+ private String mZip;
+ private String mCountry;
+ private String mData;
+
+ DummyAddress(long id) {
+ mID = id;
+ mLine1 = "line1_" + id;
+ mLine2 = "line2_" + id;
+ mCity = "city_" + id;
+ mState = "state_" + id;
+ mZip = "zip_" + id;
+ mCountry = "country_" + id;
+ mData = "data_" + id;
+ }
+
+ public long getAddressID() {
+ return mID;
+ }
+
+ public void setAddressID(long id) {
+ mID = id;
+ }
+
+ public String getAddressLine1() {
+ return mLine1;
+ }
+
+ public void setAddressLine1(String value) {
+ mLine1 = value;
+ }
+
+ public String getAddressLine2() {
+ return mLine2;
+ }
+
+ public void setAddressLine2(String value) {
+ mLine2 = value;
+ }
+
+ public String getAddressCity() {
+ return mCity;
+ }
+
+ public void setAddressCity(String value) {
+ mCity = value;
+ }
+
+ public String getAddressState() {
+ return mState;
+ }
+
+ public void setAddressState(String value) {
+ mState = value;
+ }
+
+ public String getAddressZip() {
+ return mZip;
+ }
+
+ public void setAddressZip(String value) {
+ mZip = value;
+ }
+
+ public String getAddressCountry() {
+ return mCountry;
+ }
+
+ public void setAddressCountry(String value) {
+ mCountry = value;
+ }
+
+ public String getCustomData() {
+ return mData;
+ }
+
+ public void setCustomData(String str) {
+ mData = str;
+ }
+
+ public String toString() {
+ return "address " + mID;
+ }
+ }
+}
diff --git a/src/test/java/com/amazon/carbonado/qe/TestSortedQueryExecutor.java b/src/test/java/com/amazon/carbonado/qe/TestSortedQueryExecutor.java new file mode 100644 index 0000000..53c47d6 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/qe/TestSortedQueryExecutor.java @@ -0,0 +1,84 @@ +/*
+ * 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.qe;
+
+import java.util.List;
+
+import junit.framework.TestSuite;
+
+import com.amazon.carbonado.filter.Filter;
+import com.amazon.carbonado.filter.FilterValues;
+
+import com.amazon.carbonado.info.OrderedProperty;
+
+import com.amazon.carbonado.stored.Address;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+public class TestSortedQueryExecutor extends TestQueryExecutor {
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static TestSuite suite() {
+ return new TestSuite(TestSortedQueryExecutor.class);
+ }
+
+ public void testBasicSorting() throws Exception {
+ QueryExecutor<Address> unsorted = createExecutor(4, 2, 3, 1);
+ Filter<Address> filter = Filter.getOpenFilter(Address.class);
+ FilterValues<Address> values = filter.initialFilterValues();
+ OrderingList<Address> ordered = createOrdering("addressCountry");
+
+ QueryExecutor<Address> executor =
+ new SortedQueryExecutor<Address>(null, unsorted, null, ordered);
+
+ assertEquals(filter, executor.getFilter());
+
+ assertEquals(4, executor.count(values));
+
+ assertEquals(ordered, executor.getOrdering());
+
+ compareElements(executor.fetch(values), 1, 2, 3, 4);
+ }
+
+ public void testBasicFinisherSorting() throws Exception {
+ QueryExecutor<Address> unsorted = createExecutor(1, 2, 3, 4);
+ Filter<Address> filter = Filter.getOpenFilter(Address.class);
+ FilterValues<Address> values = filter.initialFilterValues();
+ OrderingList<Address> handled = createOrdering("addressCountry");
+ OrderingList<Address> finisher = createOrdering("addressState");
+
+ QueryExecutor<Address> executor =
+ new SortedQueryExecutor<Address>(null, unsorted, handled, finisher);
+
+ assertEquals(filter, executor.getFilter());
+
+ assertEquals(4, executor.count(values));
+
+ assertEquals(2, executor.getOrdering().size());
+ assertEquals(handled.get(0), executor.getOrdering().get(0));
+ assertEquals(finisher.get(0), executor.getOrdering().get(1));
+
+ compareElements(executor.fetch(values), 1, 2, 3, 4);
+ }
+}
diff --git a/src/test/java/com/amazon/carbonado/qe/TestUnionQueryAnalyzer.java b/src/test/java/com/amazon/carbonado/qe/TestUnionQueryAnalyzer.java new file mode 100644 index 0000000..21d0b0c --- /dev/null +++ b/src/test/java/com/amazon/carbonado/qe/TestUnionQueryAnalyzer.java @@ -0,0 +1,565 @@ +/*
+ * 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.qe;
+
+import java.util.List;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.amazon.carbonado.Repository;
+import com.amazon.carbonado.Storable;
+
+import com.amazon.carbonado.info.OrderedProperty;
+import com.amazon.carbonado.info.StorableIndex;
+
+import com.amazon.carbonado.filter.Filter;
+import com.amazon.carbonado.filter.FilterValues;
+import com.amazon.carbonado.filter.PropertyFilter;
+
+import com.amazon.carbonado.repo.toy.ToyRepository;
+
+import com.amazon.carbonado.stored.Address;
+import com.amazon.carbonado.stored.Order;
+import com.amazon.carbonado.stored.Shipment;
+import com.amazon.carbonado.stored.Shipper;
+import com.amazon.carbonado.stored.StorableTestBasic;
+
+import static com.amazon.carbonado.qe.TestIndexedQueryExecutor.Mock;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+public class TestUnionQueryAnalyzer extends TestCase {
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static TestSuite suite() {
+ return new TestSuite(TestUnionQueryAnalyzer.class);
+ }
+
+ static <S extends Storable> StorableIndex<S> makeIndex(Class<S> type, String... props) {
+ return TestOrderingScore.makeIndex(type, props);
+ }
+
+ static <S extends Storable> OrderingList<S> makeOrdering(Class<S> type, String... props) {
+ return TestOrderingScore.makeOrdering(type, props);
+ }
+
+ public TestUnionQueryAnalyzer(String name) {
+ super(name);
+ }
+
+ public void testNullFilter() throws Exception {
+ UnionQueryAnalyzer uqa =
+ new UnionQueryAnalyzer(Shipment.class, TestIndexedQueryAnalyzer.RepoAccess.INSTANCE);
+ UnionQueryAnalyzer.Result result = uqa.analyze(null, null);
+ List<IndexedQueryAnalyzer<Shipment>.Result> subResults = result.getSubResults();
+
+ assertEquals(1, subResults.size());
+ }
+
+ public void testSingleSubResult() throws Exception {
+ UnionQueryAnalyzer uqa =
+ new UnionQueryAnalyzer(Shipment.class, TestIndexedQueryAnalyzer.RepoAccess.INSTANCE);
+ Filter<Shipment> filter = Filter.filterFor(Shipment.class, "shipmentID = ?");
+ filter = filter.bind();
+ UnionQueryAnalyzer.Result result = uqa.analyze(filter, null);
+ List<IndexedQueryAnalyzer<Shipment>.Result> subResults = result.getSubResults();
+
+ assertEquals(1, subResults.size());
+ }
+
+ public void testSingleSubResultUnspecifiedDirection() throws Exception {
+ UnionQueryAnalyzer uqa =
+ new UnionQueryAnalyzer(Shipment.class, TestIndexedQueryAnalyzer.RepoAccess.INSTANCE);
+ Filter<Shipment> filter = Filter.filterFor(Shipment.class, "shipmentID > ?");
+ filter = filter.bind();
+ OrderingList<Shipment> orderings =
+ makeOrdering(Shipment.class, "~shipmentID", "~orderID");
+ UnionQueryAnalyzer.Result result = uqa.analyze(filter, orderings);
+ List<IndexedQueryAnalyzer<Shipment>.Result> subResults = result.getSubResults();
+
+ assertEquals(1, subResults.size());
+ List<OrderedProperty<Shipment>> handled =
+ subResults.get(0).getCompositeScore().getOrderingScore().getHandledOrdering();
+ assertEquals(1, handled.size());
+ assertEquals("+shipmentID", handled.get(0).toString());
+ }
+
+ public void testSimpleUnion() throws Exception {
+ UnionQueryAnalyzer uqa =
+ new UnionQueryAnalyzer(Shipment.class, TestIndexedQueryAnalyzer.RepoAccess.INSTANCE);
+ Filter<Shipment> filter = Filter.filterFor(Shipment.class,
+ "shipmentID = ? | orderID = ?");
+ filter = filter.bind();
+ UnionQueryAnalyzer.Result result = uqa.analyze(filter, null);
+ assertEquals(OrderingList.get(Shipment.class, "+shipmentID"), result.getTotalOrdering());
+ List<IndexedQueryAnalyzer<Shipment>.Result> subResults = result.getSubResults();
+
+ assertEquals(2, subResults.size());
+ IndexedQueryAnalyzer<Shipment>.Result res_0 = subResults.get(0);
+ IndexedQueryAnalyzer<Shipment>.Result res_1 = subResults.get(1);
+
+ assertTrue(res_0.handlesAnything());
+ assertEquals(Filter.filterFor(Shipment.class, "shipmentID = ?").bind(),
+ res_0.getCompositeScore().getFilteringScore().getIdentityFilter());
+ assertEquals(makeIndex(Shipment.class, "shipmentID"), res_0.getLocalIndex());
+ assertEquals(null, res_0.getForeignIndex());
+ assertEquals(null, res_0.getForeignProperty());
+ assertEquals(0, res_0.getRemainderOrdering().size());
+
+ assertTrue(res_1.handlesAnything());
+ assertEquals(Filter.filterFor(Shipment.class, "orderID = ?").bind(),
+ res_1.getCompositeScore().getFilteringScore().getIdentityFilter());
+ assertEquals(makeIndex(Shipment.class, "orderID", "shipmentNotes"),
+ res_1.getLocalIndex());
+ assertEquals(null, res_1.getForeignIndex());
+ assertEquals(null, res_1.getForeignProperty());
+ assertEquals(1, res_1.getRemainderOrdering().size());
+ assertEquals("+shipmentID", res_1.getRemainderOrdering().get(0).toString());
+ }
+
+ public void testSimpleUnion2() throws Exception {
+ UnionQueryAnalyzer uqa =
+ new UnionQueryAnalyzer(Shipment.class, TestIndexedQueryAnalyzer.RepoAccess.INSTANCE);
+ Filter<Shipment> filter = Filter.filterFor(Shipment.class,
+ "shipmentID = ? | orderID > ?");
+ filter = filter.bind();
+ UnionQueryAnalyzer.Result result = uqa.analyze(filter, null);
+ assertEquals(OrderingList.get(Shipment.class, "+shipmentID"), result.getTotalOrdering());
+ List<IndexedQueryAnalyzer<Shipment>.Result> subResults = result.getSubResults();
+
+ assertEquals(2, subResults.size());
+ IndexedQueryAnalyzer<Shipment>.Result res_0 = subResults.get(0);
+ IndexedQueryAnalyzer<Shipment>.Result res_1 = subResults.get(1);
+
+ assertTrue(res_0.handlesAnything());
+ assertEquals(Filter.filterFor(Shipment.class, "shipmentID = ?").bind(),
+ res_0.getCompositeScore().getFilteringScore().getIdentityFilter());
+ assertEquals(makeIndex(Shipment.class, "shipmentID"), res_0.getLocalIndex());
+ assertEquals(null, res_0.getForeignIndex());
+ assertEquals(null, res_0.getForeignProperty());
+ assertEquals(0, res_0.getRemainderOrdering().size());
+
+ assertTrue(res_1.handlesAnything());
+ assertTrue(res_1.getCompositeScore().getFilteringScore().hasRangeStart());
+ assertFalse(res_1.getCompositeScore().getFilteringScore().hasRangeEnd());
+ assertEquals(makeIndex(Shipment.class, "orderID", "shipmentNotes"),
+ res_1.getLocalIndex());
+ assertEquals(null, res_1.getForeignIndex());
+ assertEquals(null, res_1.getForeignProperty());
+ assertEquals(1, res_1.getRemainderOrdering().size());
+ assertEquals("+shipmentID", res_1.getRemainderOrdering().get(0).toString());
+ }
+
+ public void testSimpleUnion3() throws Exception {
+ UnionQueryAnalyzer uqa =
+ new UnionQueryAnalyzer(Shipment.class, TestIndexedQueryAnalyzer.RepoAccess.INSTANCE);
+ Filter<Shipment> filter = Filter.filterFor(Shipment.class,
+ "shipmentID = ? | orderID > ? & orderID <= ?");
+ filter = filter.bind();
+ UnionQueryAnalyzer.Result result = uqa.analyze(filter, null);
+ assertEquals(OrderingList.get(Shipment.class, "+shipmentID"), result.getTotalOrdering());
+ List<IndexedQueryAnalyzer<Shipment>.Result> subResults = result.getSubResults();
+
+ assertEquals(2, subResults.size());
+ IndexedQueryAnalyzer<Shipment>.Result res_0 = subResults.get(0);
+ IndexedQueryAnalyzer<Shipment>.Result res_1 = subResults.get(1);
+
+ assertTrue(res_0.handlesAnything());
+ assertEquals(Filter.filterFor(Shipment.class, "shipmentID = ?").bind(),
+ res_0.getCompositeScore().getFilteringScore().getIdentityFilter());
+ assertEquals(makeIndex(Shipment.class, "shipmentID"), res_0.getLocalIndex());
+ assertEquals(null, res_0.getForeignIndex());
+ assertEquals(null, res_0.getForeignProperty());
+ assertEquals(0, res_0.getRemainderOrdering().size());
+
+ // Note: index that has proper filtering is preferred because
+ // "orderId > ? & orderID <= ?" filter specifies a complete range.
+ // We'll have to do a sort, but it isn't expected to be over that many records.
+ assertTrue(res_1.handlesAnything());
+ assertTrue(res_1.getCompositeScore().getFilteringScore().hasRangeStart());
+ assertTrue(res_1.getCompositeScore().getFilteringScore().hasRangeEnd());
+ List<PropertyFilter<Shipment>> rangeFilters =
+ res_1.getCompositeScore().getFilteringScore().getRangeStartFilters();
+ assertEquals(1, rangeFilters.size());
+ assertEquals(Filter.filterFor(Shipment.class, "orderID > ?").bind(), rangeFilters.get(0));
+ rangeFilters = res_1.getCompositeScore().getFilteringScore().getRangeEndFilters();
+ assertEquals(1, rangeFilters.size());
+ assertEquals(Filter.filterFor(Shipment.class, "orderID <= ?").bind(), rangeFilters.get(0));
+ assertEquals(makeIndex(Shipment.class, "orderID", "shipmentNotes"), res_1.getLocalIndex());
+ assertEquals(null, res_1.getForeignIndex());
+ assertEquals(null, res_1.getForeignProperty());
+ // Sort operation required because the "shipmentID" index was not chosen.
+ assertEquals("+shipmentID", res_1.getRemainderOrdering().get(0).toString());
+ }
+
+ public void testSimpleUnionUnspecifiedDirection() throws Exception {
+ UnionQueryAnalyzer uqa =
+ new UnionQueryAnalyzer(Shipment.class, TestIndexedQueryAnalyzer.RepoAccess.INSTANCE);
+ Filter<Shipment> filter = Filter.filterFor(Shipment.class,
+ "shipmentID > ? | orderID = ?");
+ filter = filter.bind();
+ OrderingList<Shipment> orderings =
+ makeOrdering(Shipment.class, "~shipmentID", "~orderID");
+ UnionQueryAnalyzer.Result result = uqa.analyze(filter, orderings);
+ assertEquals(OrderingList.get(Shipment.class, "+shipmentID", "+orderID"),
+ result.getTotalOrdering());
+ List<IndexedQueryAnalyzer<Shipment>.Result> subResults = result.getSubResults();
+
+ assertEquals(2, subResults.size());
+ IndexedQueryAnalyzer<Shipment>.Result res_0 = subResults.get(0);
+ IndexedQueryAnalyzer<Shipment>.Result res_1 = subResults.get(1);
+
+ List<OrderedProperty<Shipment>> handled =
+ res_0.getCompositeScore().getOrderingScore().getHandledOrdering();
+ assertEquals(1, handled.size());
+ assertEquals("+shipmentID", handled.get(0).toString());
+
+ handled = res_1.getCompositeScore().getOrderingScore().getHandledOrdering();
+ assertEquals(0, handled.size());
+
+ assertTrue(res_0.handlesAnything());
+ assertTrue(res_0.getCompositeScore().getFilteringScore().hasRangeStart());
+ assertFalse(res_0.getCompositeScore().getFilteringScore().hasRangeEnd());
+ assertEquals(makeIndex(Shipment.class, "shipmentID"), res_0.getLocalIndex());
+ assertEquals(null, res_0.getForeignIndex());
+ assertEquals(null, res_0.getForeignProperty());
+ assertEquals(1, res_0.getRemainderOrdering().size());
+ assertEquals("+orderID", res_0.getRemainderOrdering().get(0).toString());
+
+ assertTrue(res_1.handlesAnything());
+ assertEquals(Filter.filterFor(Shipment.class, "orderID = ?").bind(),
+ res_1.getCompositeScore().getFilteringScore().getIdentityFilter());
+ assertEquals(makeIndex(Shipment.class, "orderID", "shipmentNotes"), res_1.getLocalIndex());
+ assertEquals(null, res_1.getForeignIndex());
+ assertEquals(null, res_1.getForeignProperty());
+ assertEquals(1, res_1.getRemainderOrdering().size());
+ assertEquals("+shipmentID", res_1.getRemainderOrdering().get(0).toString());
+ }
+
+ public void testSimpleMerge() throws Exception {
+ // Because query has an 'or' operation, the analyzer will initially
+ // split this into a union. After futher analysis, it should decide
+ // that this offers no benefit and will merge them back.
+ UnionQueryAnalyzer uqa =
+ new UnionQueryAnalyzer(Shipment.class, TestIndexedQueryAnalyzer.RepoAccess.INSTANCE);
+ Filter<Shipment> filter = Filter.filterFor
+ (Shipment.class,
+ "shipmentID = ? & (shipmentID = ? | orderID = ?)");
+ filter = filter.bind();
+ UnionQueryAnalyzer.Result result = uqa.analyze(filter, null);
+ List<IndexedQueryAnalyzer<Shipment>.Result> subResults = result.getSubResults();
+
+ assertEquals(1, subResults.size());
+ IndexedQueryAnalyzer<Shipment>.Result res_0 = subResults.get(0);
+
+ assertTrue(res_0.handlesAnything());
+ assertEquals(Filter.filterFor(Shipment.class, "shipmentID = ?").bind(),
+ res_0.getCompositeScore().getFilteringScore().getIdentityFilter());
+ assertEquals(makeIndex(Shipment.class, "shipmentID"), res_0.getLocalIndex());
+ assertEquals(null, res_0.getForeignIndex());
+ assertEquals(null, res_0.getForeignProperty());
+ assertEquals(0, res_0.getRemainderOrdering().size());
+ assertEquals(Filter.filterFor(Shipment.class, "shipmentID = ? | orderID = ?"),
+ res_0.getRemainderFilter().unbind());
+ }
+
+ public void testFullScan() throws Exception {
+ // Because no indexes were selected, there's no union to perform.
+ UnionQueryAnalyzer uqa =
+ new UnionQueryAnalyzer(Shipment.class, TestIndexedQueryAnalyzer.RepoAccess.INSTANCE);
+ Filter<Shipment> filter = Filter.filterFor
+ (Shipment.class, "shipmentNotes = ? | shipperID = ?");
+ filter = filter.bind();
+ UnionQueryAnalyzer.Result result = uqa.analyze(filter, null);
+ List<IndexedQueryAnalyzer<Shipment>.Result> subResults = result.getSubResults();
+
+ assertEquals(1, subResults.size());
+ IndexedQueryAnalyzer<Shipment>.Result res_0 = subResults.get(0);
+
+ assertFalse(res_0.handlesAnything());
+ assertEquals(null, res_0.getForeignIndex());
+ assertEquals(null, res_0.getForeignProperty());
+ assertEquals(0, res_0.getRemainderOrdering().size());
+ assertEquals(Filter.filterFor(Shipment.class, "shipmentNotes = ? | shipperID = ?").bind(),
+ res_0.getRemainderFilter());
+ }
+
+ public void testFullScanFallback() throws Exception {
+ // Because not all sub-results of union use an index, just fallback to
+ // doing a full scan.
+ UnionQueryAnalyzer uqa =
+ new UnionQueryAnalyzer(Shipment.class, TestIndexedQueryAnalyzer.RepoAccess.INSTANCE);
+ Filter<Shipment> filter = Filter.filterFor
+ (Shipment.class, "shipmentNotes = ? | orderID = ?");
+ filter = filter.bind();
+ UnionQueryAnalyzer.Result result = uqa.analyze(filter, null);
+ List<IndexedQueryAnalyzer<Shipment>.Result> subResults = result.getSubResults();
+
+ assertEquals(1, subResults.size());
+ IndexedQueryAnalyzer<Shipment>.Result res_0 = subResults.get(0);
+
+ assertFalse(res_0.handlesAnything());
+ assertEquals(null, res_0.getForeignIndex());
+ assertEquals(null, res_0.getForeignProperty());
+ assertEquals(0, res_0.getRemainderOrdering().size());
+ assertEquals(Filter.filterFor(Shipment.class, "shipmentNotes = ? | orderID = ?").bind(),
+ res_0.getRemainderFilter());
+ }
+
+ public void testFullScanExempt() throws Exception {
+ // Although not all sub-results use an index, one that does has a join
+ // so it is exempt from folding into the full scan.
+ UnionQueryAnalyzer uqa =
+ new UnionQueryAnalyzer(Shipment.class, TestIndexedQueryAnalyzer.RepoAccess.INSTANCE);
+ Filter<Shipment> filter = Filter.filterFor
+ (Shipment.class, "shipmentNotes = ? | orderID = ? & order.orderTotal > ?");
+ filter = filter.bind();
+ UnionQueryAnalyzer.Result result = uqa.analyze(filter, null);
+ List<IndexedQueryAnalyzer<Shipment>.Result> subResults = result.getSubResults();
+
+ assertEquals(2, subResults.size());
+ IndexedQueryAnalyzer<Shipment>.Result res_0 = subResults.get(0);
+ IndexedQueryAnalyzer<Shipment>.Result res_1 = subResults.get(1);
+
+ assertTrue(res_0.handlesAnything());
+ assertEquals(makeIndex(Shipment.class, "orderID", "shipmentNotes"), res_0.getLocalIndex());
+ assertEquals(null, res_0.getForeignIndex());
+ assertEquals(null, res_0.getForeignProperty());
+ assertEquals(1, res_0.getRemainderOrdering().size());
+ assertEquals("+shipmentID", res_0.getRemainderOrdering().get(0).toString());
+ assertEquals(Filter.filterFor(Shipment.class, "order.orderTotal > ?").bind(),
+ res_0.getRemainderFilter());
+
+ assertTrue(res_1.handlesAnything());
+ assertEquals(makeIndex(Shipment.class, "shipmentID"), res_1.getLocalIndex());
+ assertEquals(null, res_1.getForeignIndex());
+ assertEquals(null, res_1.getForeignProperty());
+ assertEquals(0, res_1.getRemainderOrdering().size());
+ assertEquals(Filter.filterFor(Shipment.class, "shipmentNotes = ?").bind(),
+ res_1.getRemainderFilter());
+
+ }
+
+ public void testComplexUnionPlan() throws Exception {
+ UnionQueryAnalyzer uqa =
+ new UnionQueryAnalyzer(StorableTestBasic.class,
+ TestIndexedQueryAnalyzer.RepoAccess.INSTANCE);
+ Filter<StorableTestBasic> filter = Filter.filterFor
+ (StorableTestBasic.class, "doubleProp = ? | (stringProp = ? & intProp = ?) | id > ?");
+ filter = filter.bind();
+ UnionQueryAnalyzer.Result result = uqa.analyze(filter, null);
+ assertEquals(OrderingList.get(StorableTestBasic.class, "+id"), result.getTotalOrdering());
+
+ QueryExecutor<StorableTestBasic> exec = result.createExecutor();
+
+ assertEquals(filter, exec.getFilter());
+ assertEquals(OrderingList.get(StorableTestBasic.class, "+id"), exec.getOrdering());
+
+ List<IndexedQueryAnalyzer<Shipment>.Result> subResults = result.getSubResults();
+
+ assertEquals(3, subResults.size());
+
+ StringBuffer buf = new StringBuffer();
+ exec.printPlan(buf, 0, null);
+ String plan = buf.toString();
+
+ String expected =
+ "union\n" +
+ " sort: [+id]\n" +
+ " index scan: com.amazon.carbonado.stored.StorableTestBasic\n" +
+ " ...index: {properties=[-doubleProp, +longProp, ~id], unique=true}\n" +
+ " ...identity filter: doubleProp = ?\n" +
+ " index scan: com.amazon.carbonado.stored.StorableTestBasic\n" +
+ " ...index: {properties=[-stringProp, -intProp, ~id], unique=true}\n" +
+ " ...identity filter: stringProp = ? & intProp = ?\n" +
+ " clustered index scan: com.amazon.carbonado.stored.StorableTestBasic\n" +
+ " ...index: {properties=[+id], unique=true}\n" +
+ " ...range filter: id > ?\n";
+
+ // Test test will fail if the format of the plan changes.
+ assertEquals(expected, plan);
+ }
+
+ public void testComplexUnionPlan2() throws Exception {
+ UnionQueryAnalyzer uqa =
+ new UnionQueryAnalyzer(StorableTestBasic.class,
+ TestIndexedQueryAnalyzer.RepoAccess.INSTANCE);
+ Filter<StorableTestBasic> filter = Filter.filterFor
+ (StorableTestBasic.class, "doubleProp = ? | stringProp = ?");
+ filter = filter.bind();
+ UnionQueryAnalyzer.Result result = uqa.analyze(filter, null);
+ assertEquals(OrderingList.get(StorableTestBasic.class, "+doubleProp", "+stringProp"),
+ result.getTotalOrdering());
+
+ QueryExecutor<StorableTestBasic> exec = result.createExecutor();
+
+ assertEquals(filter, exec.getFilter());
+ assertEquals(OrderingList.get(StorableTestBasic.class, "+doubleProp", "+stringProp"),
+ exec.getOrdering());
+
+ List<IndexedQueryAnalyzer<Shipment>.Result> subResults = result.getSubResults();
+
+ assertEquals(2, subResults.size());
+
+ StringBuffer buf = new StringBuffer();
+ exec.printPlan(buf, 0, null);
+ String plan = buf.toString();
+
+ String expected =
+ "union\n" +
+ " sort: [+stringProp]\n" +
+ " index scan: com.amazon.carbonado.stored.StorableTestBasic\n" +
+ " ...index: {properties=[-doubleProp, +longProp, ~id], unique=true}\n" +
+ " ...identity filter: doubleProp = ?\n" +
+ " index scan: com.amazon.carbonado.stored.StorableTestBasic\n" +
+ " ...index: {properties=[+stringProp, +doubleProp], unique=true}\n" +
+ " ...identity filter: stringProp = ?\n";
+
+ // Test test will fail if the format of the plan changes.
+ assertEquals(expected, plan);
+ }
+
+ public void testComplexUnionPlan3() throws Exception {
+ UnionQueryAnalyzer uqa =
+ new UnionQueryAnalyzer(StorableTestBasic.class,
+ TestIndexedQueryAnalyzer.RepoAccess.INSTANCE);
+ Filter<StorableTestBasic> filter = Filter.filterFor
+ (StorableTestBasic.class, "stringProp = ? | stringProp = ?");
+ filter = filter.bind();
+ UnionQueryAnalyzer.Result result = uqa.analyze(filter, null);
+ assertEquals(OrderingList.get(StorableTestBasic.class, "+stringProp", "+doubleProp"),
+ result.getTotalOrdering());
+
+ QueryExecutor<StorableTestBasic> exec = result.createExecutor();
+ assertEquals(OrderingList.get(StorableTestBasic.class, "+stringProp", "+doubleProp"),
+ exec.getOrdering());
+
+ List<IndexedQueryAnalyzer<Shipment>.Result> subResults = result.getSubResults();
+
+ assertEquals(2, subResults.size());
+
+ StringBuffer buf = new StringBuffer();
+ exec.printPlan(buf, 0, null);
+ String plan = buf.toString();
+
+ String expected =
+ "union\n" +
+ " index scan: com.amazon.carbonado.stored.StorableTestBasic\n" +
+ " ...index: {properties=[+stringProp, +doubleProp], unique=true}\n" +
+ " ...identity filter: stringProp = ?\n" +
+ " index scan: com.amazon.carbonado.stored.StorableTestBasic\n" +
+ " ...index: {properties=[+stringProp, +doubleProp], unique=true}\n" +
+ " ...identity filter: stringProp = ?[2]\n";
+
+ // Test test will fail if the format of the plan changes.
+ assertEquals(expected, plan);
+ }
+
+ public void testComplexUnionPlan4() throws Exception {
+ UnionQueryAnalyzer uqa =
+ new UnionQueryAnalyzer(StorableTestBasic.class,
+ TestIndexedQueryAnalyzer.RepoAccess.INSTANCE);
+ Filter<StorableTestBasic> filter = Filter.filterFor
+ (StorableTestBasic.class, "doubleProp = ? | stringProp = ? | id > ?");
+ filter = filter.bind();
+ UnionQueryAnalyzer.Result result = uqa.analyze(filter, null);
+ assertEquals(OrderingList.get(StorableTestBasic.class, "+doubleProp", "+id"),
+ result.getTotalOrdering());
+
+ QueryExecutor<StorableTestBasic> exec = result.createExecutor();
+
+ assertEquals(filter, exec.getFilter());
+ assertEquals(OrderingList.get(StorableTestBasic.class, "+doubleProp", "+id"),
+ exec.getOrdering());
+
+ List<IndexedQueryAnalyzer<Shipment>.Result> subResults = result.getSubResults();
+
+ assertEquals(3, subResults.size());
+
+ StringBuffer buf = new StringBuffer();
+ exec.printPlan(buf, 0, null);
+ String plan = buf.toString();
+
+ String expected =
+ "union\n" +
+ " sort: [+id]\n" +
+ " index scan: com.amazon.carbonado.stored.StorableTestBasic\n" +
+ " ...index: {properties=[-doubleProp, +longProp, ~id], unique=true}\n" +
+ " ...identity filter: doubleProp = ?\n" +
+ " sort: [+doubleProp], [+id]\n" +
+ " index scan: com.amazon.carbonado.stored.StorableTestBasic\n" +
+ " ...index: {properties=[+stringProp, +doubleProp], unique=true}\n" +
+ " ...identity filter: stringProp = ?\n" +
+ " sort: [+doubleProp, +id]\n" +
+ " clustered index scan: com.amazon.carbonado.stored.StorableTestBasic\n" +
+ " ...index: {properties=[+id], unique=true}\n" +
+ " ...range filter: id > ?\n";
+
+ // Test test will fail if the format of the plan changes.
+ assertEquals(expected, plan);
+ }
+
+ public void testComplexUnionPlan5() throws Exception {
+ UnionQueryAnalyzer uqa =
+ new UnionQueryAnalyzer(StorableTestBasic.class,
+ TestIndexedQueryAnalyzer.RepoAccess.INSTANCE);
+ Filter<StorableTestBasic> filter = Filter.filterFor
+ (StorableTestBasic.class, "stringProp = ? | stringProp = ? | id > ?");
+ filter = filter.bind();
+ UnionQueryAnalyzer.Result result = uqa.analyze(filter, null);
+ assertEquals(OrderingList.get(StorableTestBasic.class, "+stringProp", "+doubleProp"),
+ result.getTotalOrdering());
+
+ QueryExecutor<StorableTestBasic> exec = result.createExecutor();
+
+ assertEquals(filter, exec.getFilter());
+ assertEquals(OrderingList.get(StorableTestBasic.class, "+stringProp", "+doubleProp"),
+ exec.getOrdering());
+
+ List<IndexedQueryAnalyzer<Shipment>.Result> subResults = result.getSubResults();
+
+ assertEquals(3, subResults.size());
+
+ StringBuffer buf = new StringBuffer();
+ exec.printPlan(buf, 0, null);
+ String plan = buf.toString();
+
+ String expected =
+ "union\n" +
+ " index scan: com.amazon.carbonado.stored.StorableTestBasic\n" +
+ " ...index: {properties=[+stringProp, +doubleProp], unique=true}\n" +
+ " ...identity filter: stringProp = ?\n" +
+ " index scan: com.amazon.carbonado.stored.StorableTestBasic\n" +
+ " ...index: {properties=[+stringProp, +doubleProp], unique=true}\n" +
+ " ...identity filter: stringProp = ?[2]\n" +
+ " sort: [+stringProp, +doubleProp]\n" +
+ " clustered index scan: com.amazon.carbonado.stored.StorableTestBasic\n" +
+ " ...index: {properties=[+id], unique=true}\n" +
+ " ...range filter: id > ?\n";
+
+ // Test test will fail if the format of the plan changes.
+ assertEquals(expected, plan);
+ }
+}
diff --git a/src/test/java/com/amazon/carbonado/qe/TestUnionQueryExecutor.java b/src/test/java/com/amazon/carbonado/qe/TestUnionQueryExecutor.java new file mode 100644 index 0000000..9fccbc7 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/qe/TestUnionQueryExecutor.java @@ -0,0 +1,75 @@ +/*
+ * 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.qe;
+
+import java.util.List;
+
+import junit.framework.TestSuite;
+
+import com.amazon.carbonado.filter.Filter;
+import com.amazon.carbonado.filter.FilterValues;
+
+import com.amazon.carbonado.info.OrderedProperty;
+
+import com.amazon.carbonado.stored.Address;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+public class TestUnionQueryExecutor extends TestQueryExecutor {
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static TestSuite suite() {
+ return new TestSuite(TestUnionQueryExecutor.class);
+ }
+
+ public void testBasicUnion() throws Exception {
+ QueryExecutor<Address> primary = new SortedQueryExecutor<Address>
+ (null, createExecutor(1, 2, 3, 4, 5, 6, 7, 8), null, createOrdering("addressID"));
+
+ Filter<Address> filter_1 = Filter.filterFor(Address.class, "addressCountry > ?");
+ FilterValues<Address> values_1 = filter_1.initialFilterValues();
+ QueryExecutor<Address> executor_1 = new FilteredQueryExecutor<Address>(primary, filter_1);
+
+ Filter<Address> filter_2 = Filter.filterFor(Address.class, "addressState <= ?");
+ FilterValues<Address> values_2 = filter_2.initialFilterValues();
+ QueryExecutor<Address> executor_2 = new FilteredQueryExecutor<Address>(primary, filter_2);
+
+ QueryExecutor<Address> union = new UnionQueryExecutor<Address>(executor_1, executor_2);
+
+ Filter<Address> filter = Filter
+ .filterFor(Address.class, "addressCountry > ? | addressState <= ?");
+ FilterValues<Address> values = filter.initialFilterValues();
+ filter = values.getFilter();
+
+ assertEquals(filter, union.getFilter());
+
+ values = values.with("country_6").with("state_3");
+
+ assertEquals(5, union.count(values));
+
+ assertEquals(primary.getOrdering(), union.getOrdering());
+
+ compareElements(union.fetch(values), 1, 2, 3, 7, 8);
+ }
+}
diff --git a/src/test/java/com/amazon/carbonado/raw/TestDataEncoding.java b/src/test/java/com/amazon/carbonado/raw/TestDataEncoding.java new file mode 100644 index 0000000..bff5820 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/raw/TestDataEncoding.java @@ -0,0 +1,515 @@ +/*
+ * 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.raw;
+
+import java.util.Random;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Test case for {@link DataEncoder} and {@link DataDecoder}.
+ * <p>
+ * It generates random data values, checks that the decoding produces the
+ * original results, and it checks that the order of the encoded bytes matches
+ * the order of the original data values.
+ *
+ * @author Brian S O'Neill
+ */
+public class TestDataEncoding extends TestCase {
+ private static final int SHORT_TEST = 100;
+ private static final int MEDIUM_TEST = 500;
+ private static final int LONG_TEST = 1000;
+
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static TestSuite suite() {
+ return new TestSuite(TestDataEncoding.class);
+ }
+
+ /**
+ * @return -1, 0, or 1
+ */
+ static int byteArrayCompare(byte[] aa, byte[] ab) {
+ int len = Math.min(aa.length, ab.length);
+ int result = byteArrayCompare(aa, ab, len);
+ if (result == 0 && aa.length != ab.length) {
+ if (aa.length == len) {
+ return -1;
+ }
+ if (ab.length == len) {
+ return 1;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * @return -1, 0, or 1
+ */
+ static int byteArrayCompare(byte[] aa, byte[] ab, int len) {
+ for (int i=0; i<len; i++) {
+ int a = aa[i] & 0xff;
+ int b = ab[i] & 0xff;
+ if (a < b) {
+ return -1;
+ }
+ if (a > b) {
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * @return -1, 0, or 1
+ */
+ static int byteArrayOrNullCompare(byte[] aa, byte[] ab) {
+ if (aa == null) {
+ if (ab == null) {
+ return 0;
+ } else {
+ return 1;
+ }
+ } else if (ab == null) {
+ return -1;
+ } else {
+ return byteArrayCompare(aa, ab);
+ }
+ }
+
+ /**
+ * @return -1, 0, or 1
+ */
+ static int byteArrayOrNullCompare(byte[] aa, byte[] ab, int len) {
+ if (aa == null) {
+ if (ab == null) {
+ return 0;
+ } else {
+ return 1;
+ }
+ } else if (ab == null) {
+ return -1;
+ } else {
+ return byteArrayCompare(aa, ab, len);
+ }
+ }
+
+ /**
+ * @return -1, 0, or 1
+ */
+ static <C extends Comparable> int compare(C a, C b) {
+ if (a == null) {
+ if (b == null) {
+ return 0;
+ } else {
+ return 1;
+ }
+ } else if (b == null) {
+ return -1;
+ } else {
+ return Integer.signum(a.compareTo(b));
+ }
+ }
+
+ private final long mSeed;
+
+ private Random mRandom;
+
+ public TestDataEncoding(String name) {
+ super(name);
+ mSeed = 5399777425345431L;
+ }
+
+ protected void setUp() {
+ mRandom = new Random(mSeed);
+ }
+
+ protected void tearDown() {
+ }
+
+ public void test_boolean() throws Exception {
+ byte[] bytes = new byte[1];
+ boolean lastValue = false;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ boolean value = mRandom.nextBoolean();
+ DataEncoder.encode(value, bytes, 0);
+ assertEquals(value, DataDecoder.decodeBoolean(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = compare(value, lastValue);
+ assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_Boolean() throws Exception {
+ byte[] bytes = new byte[1];
+ Boolean lastValue = false;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ Boolean value;
+ if (mRandom.nextInt(10) == 1) {
+ value = null;
+ } else {
+ value = mRandom.nextBoolean();
+ }
+ DataEncoder.encode(value, bytes, 0);
+ assertEquals(value, DataDecoder.decodeBooleanObj(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = compare(value, lastValue);
+ assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_byte() throws Exception {
+ byte[] bytes = new byte[1];
+ byte lastValue = 0;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ byte value = (byte) mRandom.nextInt();
+ DataEncoder.encode(value, bytes, 0);
+ assertEquals(value, DataDecoder.decodeByte(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = compare(value, lastValue);
+ assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_Byte() throws Exception {
+ byte[] bytes = new byte[2];
+ Byte lastValue = 0;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ Byte value;
+ if (mRandom.nextInt(10) == 1) {
+ value = null;
+ assertEquals(1, DataEncoder.encode(value, bytes, 0));
+ } else {
+ value = (byte) mRandom.nextInt();
+ assertEquals(2, DataEncoder.encode(value, bytes, 0));
+ }
+ assertEquals(value, DataDecoder.decodeByteObj(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = compare(value, lastValue);
+ assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_short() throws Exception {
+ byte[] bytes = new byte[2];
+ short lastValue = 0;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ short value = (short) mRandom.nextInt();
+ DataEncoder.encode(value, bytes, 0);
+ assertEquals(value, DataDecoder.decodeShort(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = compare(value, lastValue);
+ assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_Short() throws Exception {
+ byte[] bytes = new byte[3];
+ Short lastValue = 0;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ Short value;
+ if (mRandom.nextInt(10) == 1) {
+ value = null;
+ assertEquals(1, DataEncoder.encode(value, bytes, 0));
+ } else {
+ value = (short) mRandom.nextInt();
+ assertEquals(3, DataEncoder.encode(value, bytes, 0));
+ }
+ assertEquals(value, DataDecoder.decodeShortObj(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = compare(value, lastValue);
+ assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_char() throws Exception {
+ byte[] bytes = new byte[2];
+ char lastValue = 0;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ char value = (char) mRandom.nextInt();
+ DataEncoder.encode(value, bytes, 0);
+ assertEquals(value, DataDecoder.decodeChar(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = compare(value, lastValue);
+ assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_Character() throws Exception {
+ byte[] bytes = new byte[3];
+ Character lastValue = 0;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ Character value;
+ if (mRandom.nextInt(10) == 1) {
+ value = null;
+ assertEquals(1, DataEncoder.encode(value, bytes, 0));
+ } else {
+ value = (char) mRandom.nextInt();
+ assertEquals(3, DataEncoder.encode(value, bytes, 0));
+ }
+ assertEquals(value, DataDecoder.decodeCharacterObj(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = compare(value, lastValue);
+ assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_int() throws Exception {
+ byte[] bytes = new byte[4];
+ int lastValue = 0;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ int value = mRandom.nextInt();
+ DataEncoder.encode(value, bytes, 0);
+ assertEquals(value, DataDecoder.decodeInt(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = compare(value, lastValue);
+ assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_Integer() throws Exception {
+ byte[] bytes = new byte[5];
+ Integer lastValue = 0;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ Integer value;
+ if (mRandom.nextInt(10) == 1) {
+ value = null;
+ assertEquals(1, DataEncoder.encode(value, bytes, 0));
+ } else {
+ value = mRandom.nextInt();
+ assertEquals(5, DataEncoder.encode(value, bytes, 0));
+ }
+ assertEquals(value, DataDecoder.decodeIntegerObj(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = compare(value, lastValue);
+ assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_long() throws Exception {
+ byte[] bytes = new byte[8];
+ long lastValue = 0;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ long value = mRandom.nextLong();
+ DataEncoder.encode(value, bytes, 0);
+ assertEquals(value, DataDecoder.decodeLong(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = compare(value, lastValue);
+ assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_Long() throws Exception {
+ byte[] bytes = new byte[9];
+ Long lastValue = 0L;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ Long value;
+ if (mRandom.nextInt(10) == 1) {
+ value = null;
+ assertEquals(1, DataEncoder.encode(value, bytes, 0));
+ } else {
+ value = mRandom.nextLong();
+ assertEquals(9, DataEncoder.encode(value, bytes, 0));
+ }
+ assertEquals(value, DataDecoder.decodeLongObj(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = compare(value, lastValue);
+ assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_float() throws Exception {
+ byte[] bytes = new byte[4];
+ float lastValue = 0;
+ byte[] lastBytes = null;
+ for (int i=0; i<LONG_TEST; i++) {
+ float value = Float.intBitsToFloat(mRandom.nextInt());
+ DataEncoder.encode(value, bytes, 0);
+ assertEquals(value, DataDecoder.decodeFloat(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = compare(value, lastValue);
+ assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_Float() throws Exception {
+ byte[] bytes = new byte[4];
+ Float lastValue = 0f;
+ byte[] lastBytes = null;
+ for (int i=0; i<LONG_TEST; i++) {
+ Float value;
+ if (mRandom.nextInt(10) == 1) {
+ value = null;
+ } else {
+ value = Float.intBitsToFloat(mRandom.nextInt());
+ }
+ DataEncoder.encode(value, bytes, 0);
+ assertEquals(value, DataDecoder.decodeFloatObj(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = compare(value, lastValue);
+ assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_double() throws Exception {
+ byte[] bytes = new byte[8];
+ double lastValue = 0;
+ byte[] lastBytes = null;
+ for (int i=0; i<LONG_TEST; i++) {
+ double value = Double.longBitsToDouble(mRandom.nextLong());
+ DataEncoder.encode(value, bytes, 0);
+ assertEquals(value, DataDecoder.decodeDouble(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = compare(value, lastValue);
+ assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_Double() throws Exception {
+ byte[] bytes = new byte[8];
+ Double lastValue = 0d;
+ byte[] lastBytes = null;
+ for (int i=0; i<LONG_TEST; i++) {
+ Double value;
+ if (mRandom.nextInt(10) == 1) {
+ value = null;
+ } else {
+ value = Double.longBitsToDouble(mRandom.nextLong());
+ }
+ DataEncoder.encode(value, bytes, 0);
+ assertEquals(value, DataDecoder.decodeDoubleObj(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = compare(value, lastValue);
+ assertEquals(sgn, byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_String() throws Exception {
+ String[] ref = new String[1];
+ for (int i=0; i<SHORT_TEST; i++) {
+ String value;
+ if (mRandom.nextInt(10) == 1) {
+ value = null;
+ } else {
+ int length;
+ switch (mRandom.nextInt(15)) {
+ default:
+ case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
+ length = mRandom.nextInt(100);
+ break;
+ case 8: case 9: case 10: case 11:
+ length = mRandom.nextInt(200);
+ break;
+ case 12: case 13:
+ length = mRandom.nextInt(20000);
+ break;
+ case 14:
+ length = mRandom.nextInt(3000000);
+ break;
+ }
+ char[] chars = new char[length];
+ for (int j=0; j<length; j++) {
+ char c;
+ switch (mRandom.nextInt(7)) {
+ default:
+ case 0: case 1: case 2: case 3:
+ c = (char) mRandom.nextInt(128);
+ break;
+ case 4: case 5:
+ c = (char) mRandom.nextInt(4000);
+ break;
+ case 6:
+ c = (char) mRandom.nextInt();
+ break;
+ }
+ chars[j] = c;
+ }
+ value = new String(chars);
+ }
+
+ byte[] bytes = new byte[DataEncoder.calculateEncodedStringLength(value)];
+ assertEquals(bytes.length, DataEncoder.encode(value, bytes, 0));
+ assertEquals(bytes.length, DataDecoder.decodeString(bytes, 0, ref));
+ assertEquals(value, ref[0]);
+ }
+ }
+}
diff --git a/src/test/java/com/amazon/carbonado/raw/TestEncodingStrategy.java b/src/test/java/com/amazon/carbonado/raw/TestEncodingStrategy.java new file mode 100644 index 0000000..11a4eca --- /dev/null +++ b/src/test/java/com/amazon/carbonado/raw/TestEncodingStrategy.java @@ -0,0 +1,857 @@ +/*
+ * 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.raw;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.cojen.classfile.*;
+import org.cojen.util.*;
+
+import com.amazon.carbonado.*;
+import com.amazon.carbonado.info.*;
+import com.amazon.carbonado.spi.*;
+
+/**
+ * Test case for {@link GenericEncodingStrategy}.
+ * <p>
+ * It generates random selections of properties, encodes with random values,
+ * and checks that the decoding produces the original results. In addition, the
+ * proper ordering of encoded keys is checked.
+ *
+ * @author Brian S O'Neill
+ */
+public class TestEncodingStrategy extends TestCase {
+ private static final int SHORT_TEST = 100;
+ private static final int MEDIUM_TEST = 500;
+ private static final int LONG_TEST = 1000;
+
+ private static final int ENCODE_OBJECT_ARRAY = 0;
+ private static final int DECODE_OBJECT_ARRAY = 1;
+ private static final int ENCODE_OBJECT_ARRAY_PARTIAL = 2;
+
+ private static final int BOGUS_GENERATION = 99;
+
+ // Make sure BOGUS_GENERATION is not included.
+ private static final int[] GENERATIONS = {-1, 0, 1, 2, 127, 128, 129, Integer.MAX_VALUE};
+
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static TestSuite suite() {
+ return new TestSuite(TestEncodingStrategy.class);
+ }
+
+ private final long mSeed;
+ private final StorableProperty<TestStorable>[] mProperties;
+
+ private Random mRandom;
+
+ public TestEncodingStrategy(String name) {
+ super(name);
+ mSeed = 986184829029842L;
+ Collection<? extends StorableProperty<TestStorable>> properties =
+ StorableIntrospector.examine(TestStorable.class).getAllProperties().values();
+ mProperties = properties.toArray(new StorableProperty[0]);
+ }
+
+ protected void setUp() {
+ mRandom = new Random(mSeed);
+ }
+
+ protected void tearDown() {
+ }
+
+ public void test_dataEncoding_noProperties() throws Exception {
+ for (int generation : GENERATIONS) {
+ test_dataEncoding_noProperties(0, 0, generation);
+ }
+ }
+
+ public void test_dataEncoding_noProperties_prefix() throws Exception {
+ for (int generation : GENERATIONS) {
+ test_dataEncoding_noProperties(5, 0, generation);
+ }
+ }
+
+ public void test_dataEncoding_noProperties_suffix() throws Exception {
+ for (int generation : GENERATIONS) {
+ test_dataEncoding_noProperties(0, 7, generation);
+ }
+ }
+
+ public void test_dataEncoding_noProperties_prefixAndSuffix() throws Exception {
+ for (int generation : GENERATIONS) {
+ test_dataEncoding_noProperties(5, 7, generation);
+ }
+ }
+
+ private void test_dataEncoding_noProperties(int prefix, int suffix, int generation)
+ throws Exception
+ {
+ GenericEncodingStrategy strategy = new GenericEncodingStrategy
+ (TestStorable.class, null, 0, 0, prefix, suffix);
+
+ Method[] methods = generateCodecMethods
+ (strategy, new StorableProperty[0], null, generation);
+
+ byte[] encoded = (byte[]) methods[ENCODE_OBJECT_ARRAY]
+ .invoke(null, new Object[] {new Object[0]});
+
+ int generationPrefix;
+ if (generation < 0) {
+ generationPrefix = 0;
+ } else if (generation < 128) {
+ generationPrefix = 1;
+ } else {
+ generationPrefix = 4;
+ }
+
+ assertEquals(encoded.length, prefix + generationPrefix + suffix);
+
+ if (generation >= 0) {
+ if (generationPrefix == 1) {
+ assertEquals(generation, encoded[prefix]);
+ } else {
+ int actualGeneration = DataDecoder.decodeInt(encoded, prefix);
+ assertEquals(generation, actualGeneration);
+ }
+ }
+
+ // Decode should not throw an exception.
+ methods[DECODE_OBJECT_ARRAY].invoke(null, new Object[0], encoded);
+
+ // Generation mismatch should throw an exception.
+ if (generation >= 0) {
+ encoded[prefix] = BOGUS_GENERATION;
+ try {
+ methods[DECODE_OBJECT_ARRAY].invoke(null, new Object[0], encoded);
+ fail();
+ } catch (InvocationTargetException e) {
+ Throwable cause = e.getCause();
+ if (cause instanceof CorruptEncodingException) {
+ CorruptEncodingException cee = (CorruptEncodingException) cause;
+ // Make sure error message includes actual generation.
+ assertTrue(cee.getMessage().indexOf(String.valueOf(BOGUS_GENERATION)) >= 0);
+ } else {
+ throw e;
+ }
+ }
+ }
+ }
+
+ public void test_dataEncoding_multipleProperties() throws Exception {
+ for (int generation : GENERATIONS) {
+ test_dataEncoding_multipleProperties(0, 0, generation);
+ }
+ }
+
+ public void test_dataEncoding_multipleProperties_prefix() throws Exception {
+ for (int generation : GENERATIONS) {
+ test_dataEncoding_multipleProperties(5, 0, generation);
+ }
+ }
+
+ public void test_dataEncoding_multipleProperties_suffix() throws Exception {
+ for (int generation : GENERATIONS) {
+ test_dataEncoding_multipleProperties(0, 7, generation);
+ }
+ }
+
+ public void test_dataEncoding_multipleProperties_prefixAndSuffix() throws Exception {
+ for (int generation : GENERATIONS) {
+ test_dataEncoding_multipleProperties(5, 7, generation);
+ }
+ }
+
+ /**
+ * @param generation when non-negative, encode a storable layout generation
+ * value in one or four bytes.
+ */
+ private void test_dataEncoding_multipleProperties(int prefix, int suffix, int generation)
+ throws Exception
+ {
+ GenericEncodingStrategy strategy = new GenericEncodingStrategy
+ (TestStorable.class, null, 0, 0, prefix, suffix);
+
+ for (int i=0; i<SHORT_TEST; i++) {
+ StorableProperty<TestStorable>[] properties = selectProperties(1, 50);
+
+ Method[] methods = generateCodecMethods(strategy, properties, null, generation);
+ Object[] values = selectPropertyValues(properties);
+
+ byte[] encoded = (byte[]) methods[ENCODE_OBJECT_ARRAY]
+ .invoke(null, new Object[] {values});
+
+ int generationPrefix;
+ if (generation < 0) {
+ generationPrefix = 0;
+ } else if (generation < 128) {
+ generationPrefix = 1;
+ } else {
+ generationPrefix = 4;
+ }
+
+ assertTrue(encoded.length > (prefix + generationPrefix + suffix));
+
+ if (generation >= 0) {
+ if (generationPrefix == 1) {
+ assertEquals(generation, encoded[prefix]);
+ } else {
+ int actualGeneration = DataDecoder.decodeInt(encoded, prefix);
+ assertEquals(generation, actualGeneration);
+ }
+ }
+
+ if (prefix > 0) {
+ // Fill in with data which should be ignored by decoder.
+ for (int p=0; p<prefix; p++) {
+ encoded[p] = (byte) mRandom.nextInt();
+ }
+ }
+
+ if (suffix > 0) {
+ // Fill in with data which should be ignored by decoder.
+ for (int p=0; p<suffix; p++) {
+ encoded[encoded.length - p - 1] = (byte) mRandom.nextInt();
+ }
+ }
+
+ Object[] decodedValues = new Object[values.length];
+ methods[DECODE_OBJECT_ARRAY].invoke(null, decodedValues, encoded);
+
+ for (int j=0; j<properties.length; j++) {
+ Object a = values[j];
+ Object b = decodedValues[j];
+ if (properties[j].getType() == byte[].class) {
+ assertTrue(0 == TestDataEncoding.byteArrayOrNullCompare
+ ((byte[]) a, (byte[]) b));
+ } else {
+ assertEquals(a, b);
+ }
+ }
+
+ // Generation mismatch should throw an exception.
+ if (generation >= 0) {
+ encoded[prefix] = BOGUS_GENERATION;
+ try {
+ methods[DECODE_OBJECT_ARRAY].invoke(null, new Object[0], encoded);
+ fail();
+ } catch (InvocationTargetException e) {
+ Throwable cause = e.getCause();
+ if (cause instanceof CorruptEncodingException) {
+ CorruptEncodingException cee = (CorruptEncodingException) cause;
+ // Make sure error message includes actual generation.
+ assertTrue
+ (cee.getMessage().indexOf(String.valueOf(BOGUS_GENERATION)) >= 0);
+ } else {
+ throw e;
+ }
+ }
+ }
+ }
+ }
+
+ public void test_keyEncoding_noProperties() throws Exception {
+ test_keyEncoding_noProperties(0, 0);
+ }
+
+ public void test_keyEncoding_noProperties_prefix() throws Exception {
+ test_keyEncoding_noProperties(5, 0);
+ }
+
+ public void test_keyEncoding_noProperties_suffix() throws Exception {
+ test_keyEncoding_noProperties(0, 7);
+ }
+
+ public void test_keyEncoding_noProperties_prefixAndSuffix() throws Exception {
+ test_keyEncoding_noProperties(5, 7);
+ }
+
+ private void test_keyEncoding_noProperties(int prefix, int suffix) throws Exception {
+ GenericEncodingStrategy strategy = new GenericEncodingStrategy
+ (TestStorable.class, null, prefix, suffix, 0, 0);
+
+ Method[] methods = generateCodecMethods
+ (strategy, new StorableProperty[0], new Direction[0], -1);
+
+ // Encode should return an empty array.
+ byte[] encoded = (byte[]) methods[ENCODE_OBJECT_ARRAY]
+ .invoke(null, new Object[] {new Object[0]});
+ assertEquals(encoded.length, prefix + suffix);
+
+ // Decode should not throw an exception.
+ methods[DECODE_OBJECT_ARRAY].invoke(null, new Object[0], encoded);
+ }
+
+ public void test_keyEncoding_multipleProperties() throws Exception {
+ test_keyEncoding_multipleProperties(0, 0);
+ }
+
+ public void test_keyEncoding_multipleProperties_prefix() throws Exception {
+ test_keyEncoding_multipleProperties(5, 0);
+ }
+
+ public void test_keyEncoding_multipleProperties_suffix() throws Exception {
+ test_keyEncoding_multipleProperties(0, 7);
+ }
+
+ public void test_keyEncoding_multipleProperties_prefixAndSuffix() throws Exception {
+ test_keyEncoding_multipleProperties(5, 7);
+ }
+
+ private void test_keyEncoding_multipleProperties(int prefix, int suffix) throws Exception {
+ GenericEncodingStrategy strategy = new GenericEncodingStrategy
+ (TestStorable.class, null, prefix, suffix, 0, 0);
+
+ for (int i=0; i<MEDIUM_TEST; i++) {
+ StorableProperty<TestStorable>[] properties = selectProperties(1, 50);
+ Direction[] directions = selectDirections(properties.length);
+
+ Method[] methods = generateCodecMethods(strategy, properties, directions, -1);
+ Object[] values = selectPropertyValues(properties);
+
+ byte[] encoded = (byte[]) methods[ENCODE_OBJECT_ARRAY]
+ .invoke(null, new Object[] {values});
+
+ assertTrue(encoded.length > (prefix + suffix));
+
+ // Encode using partial encoding method, but do all
+ // properties. Ensure that the encoding is exactly the same.
+ byte[] encoded2 = (byte[]) methods[ENCODE_OBJECT_ARRAY_PARTIAL]
+ .invoke(null, new Object[] {values, 0, properties.length});
+ assertTrue(Arrays.equals(encoded, encoded2));
+
+ if (prefix > 0) {
+ // Fill in with data which should be ignored by decoder.
+ for (int p=0; p<prefix; p++) {
+ encoded[p] = (byte) mRandom.nextInt();
+ }
+ }
+
+ if (suffix > 0) {
+ // Fill in with data which should be ignored by decoder.
+ for (int p=0; p<suffix; p++) {
+ encoded[encoded.length - p - 1] = (byte) mRandom.nextInt();
+ }
+ }
+
+ Object[] decodedValues = new Object[values.length];
+ methods[DECODE_OBJECT_ARRAY].invoke(null, decodedValues, encoded);
+
+ for (int j=0; j<properties.length; j++) {
+ Object a = values[j];
+ Object b = decodedValues[j];
+ if (properties[j].getType() == byte[].class) {
+ assertTrue(0 == TestDataEncoding.byteArrayOrNullCompare
+ ((byte[]) a, (byte[]) b));
+ } else {
+ assertEquals(a, b);
+ }
+ }
+
+ // Now test partial encoding of keys.
+
+ // Clear out random affixes, since we don't need specific values
+ // anymore and it interferes with next test.
+ if (prefix > 0) {
+ for (int p=0; p<prefix; p++) {
+ encoded[p] = 0;
+ }
+ }
+ if (suffix > 0) {
+ for (int p=0; p<suffix; p++) {
+ encoded[encoded.length - p - 1] = 0;
+ }
+ }
+
+ for (int j=0; j<SHORT_TEST; j++) {
+ int start, end;
+ if (properties.length == 1) {
+ start = 0;
+ end = 1;
+ } else {
+ start = mRandom.nextInt(properties.length - 1);
+ // Partial encoding doesn't support zero properties, so
+ // ensure randomly selected stride is more than zero.
+ int stride;
+ do {
+ stride = mRandom.nextInt(properties.length - start);
+ } while (stride == 0);
+ end = start + stride + 1;
+ }
+
+ Object[] partialValues = new Object[end - start];
+ System.arraycopy(values, start, partialValues, 0, partialValues.length);
+
+ byte[] partial = (byte[]) methods[ENCODE_OBJECT_ARRAY_PARTIAL]
+ .invoke(null, new Object[] {partialValues, start, end});
+
+ // Partial key must be substring of full key.
+ int searchStart = start == 0 ? 0 : prefix;
+ int index = indexOf(encoded, searchStart, encoded.length - searchStart,
+ partial, 0, partial.length, 0);
+
+ if (start == 0) {
+ assertEquals(0, index);
+ } else {
+ assertTrue(index > 0);
+ }
+
+ if (properties.length == 1) {
+ break;
+ }
+ }
+ }
+ }
+
+ public void test_keyEncoding_ordering() throws Exception {
+ GenericEncodingStrategy strategy =
+ new GenericEncodingStrategy(TestStorable.class, null, 0, 0, 0, 0);
+
+ for (int i=0; i<MEDIUM_TEST; i++) {
+ StorableProperty<TestStorable>[] properties = selectProperties(1, 50);
+ Direction[] directions = selectDirections(properties.length);
+ Method[] methods = generateCodecMethods(strategy, properties, directions, -1);
+
+ Object[] values_1 = selectPropertyValues(properties);
+ byte[] encoded_1 = (byte[]) methods[ENCODE_OBJECT_ARRAY]
+ .invoke(null, new Object[] {values_1});
+
+ Object[] values_2 = selectPropertyValues(properties);
+ byte[] encoded_2 = (byte[]) methods[ENCODE_OBJECT_ARRAY]
+ .invoke(null, new Object[] {values_2});
+
+ int byteOrder = TestDataEncoding.byteArrayCompare(encoded_1, encoded_2);
+ int valueOrder = compareValues(properties, directions, values_1, values_2);
+
+ assertEquals(valueOrder, byteOrder);
+ }
+ }
+
+ private int compareValues(StorableProperty<?>[] properties, Direction[] directions,
+ Object[] values_1, Object[] values_2) {
+ int length = directions.length;
+ for (int i=0; i<length; i++) {
+ StorableProperty<?> property = properties[i];
+ Direction direction = directions[i];
+
+ Object value_1 = values_1[i];
+ Object value_2 = values_2[i];
+
+ int result;
+ if (property.getType() == byte[].class) {
+ result = TestDataEncoding.byteArrayOrNullCompare
+ ((byte[]) value_1, (byte[]) value_2);
+ } else {
+ if (value_1 == null) {
+ if (value_2 == null) {
+ result = 0;
+ } else {
+ result = 1;
+ }
+ } else if (value_2 == null) {
+ result = -1;
+ } else {
+ result = Integer.signum(((Comparable) value_1).compareTo(value_2));
+ }
+ }
+
+ if (result != 0) {
+ if (direction == Direction.DESCENDING) {
+ result = -result;
+ }
+ return result;
+ }
+ }
+
+ return 0;
+ }
+
+ // Method taken from String class and modified a bit.
+ private static int indexOf(byte[] source, int sourceOffset, int sourceCount,
+ byte[] target, int targetOffset, int targetCount,
+ int fromIndex) {
+ if (fromIndex >= sourceCount) {
+ return (targetCount == 0 ? sourceCount : -1);
+ }
+ if (fromIndex < 0) {
+ fromIndex = 0;
+ }
+ if (targetCount == 0) {
+ return fromIndex;
+ }
+
+ byte first = target[targetOffset];
+ int max = sourceOffset + (sourceCount - targetCount);
+
+ for (int i = sourceOffset + fromIndex; i <= max; i++) {
+ // Look for first byte.
+ if (source[i] != first) {
+ while (++i <= max && source[i] != first);
+ }
+
+ // Found first byte, now look at the rest of v2
+ if (i <= max) {
+ int j = i + 1;
+ int end = j + targetCount - 1;
+ for (int k = targetOffset + 1; j < end && source[j] == target[k]; j++, k++);
+
+ if (j == end) {
+ // Found whole string.
+ return i - sourceOffset;
+ }
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * First method is the encoder, second is the decoder. Both methods are
+ * static. Encoder accepts an object array of property values, and it
+ * returns a byte array. Decoder accepts an object array to receive
+ * property values, an encoded byte array, and it returns void.
+ *
+ * <p>If generating key encoding and the property count is more than zero,
+ * then third element in array is a key encoder that supports partial
+ * encoding. In addition to the object array argument, it also accepts an
+ * int start and an int end argument. The start of the range is inclusive,
+ * and the end is exclusive.
+ *
+ * @param directions when supplied, build key encoding/decoding. Otherwise,
+ * build data encoding/decoding.
+ * @param generation when non-negative, encode a storable layout generation
+ * value in one or four bytes.
+ */
+ private Method[] generateCodecMethods(GenericEncodingStrategy strategy,
+ StorableProperty<TestStorable>[] properties,
+ Direction[] directions,
+ int generation)
+ throws Exception
+ {
+ ClassInjector ci = ClassInjector.create(TestStorable.class.getName(), null);
+ ClassFile cf = new ClassFile(ci.getClassName());
+ cf.markSynthetic();
+ cf.setTarget("1.5");
+
+ cf.addDefaultConstructor();
+
+ TypeDesc byteArrayType = TypeDesc.forClass(byte[].class);
+ TypeDesc objectArrayType = TypeDesc.forClass(Object[].class);
+
+ // Build encode method.
+ MethodInfo mi = cf.addMethod(Modifiers.PUBLIC_STATIC, "encode", byteArrayType,
+ new TypeDesc[] {objectArrayType});
+ CodeBuilder b = new CodeBuilder(mi);
+ LocalVariable encodedVar;
+ if (directions != null) {
+ OrderedProperty<TestStorable>[] ordered =
+ makeOrderedProperties(properties, directions);
+ encodedVar =
+ strategy.buildKeyEncoding(b, ordered, b.getParameter(0), null, false, null, null);
+ } else {
+ encodedVar = strategy.buildDataEncoding
+ (b, properties, b.getParameter(0), null, false, generation);
+ }
+ b.loadLocal(encodedVar);
+ b.returnValue(byteArrayType);
+
+ // Build decode method.
+ mi = cf.addMethod(Modifiers.PUBLIC_STATIC, "decode", null,
+ new TypeDesc[] {objectArrayType, byteArrayType});
+ b = new CodeBuilder(mi);
+ if (directions != null) {
+ OrderedProperty<TestStorable>[] ordered =
+ makeOrderedProperties(properties, directions);
+ strategy.buildKeyDecoding
+ (b, ordered, b.getParameter(0), null, false, b.getParameter(1));
+ } else {
+ strategy.buildDataDecoding
+ (b, properties, b.getParameter(0), null, false,
+ generation, null, b.getParameter(1));
+ }
+ b.returnVoid();
+
+ if (directions != null && properties.length > 0) {
+ // Build encode partial key method.
+ mi = cf.addMethod
+ (Modifiers.PUBLIC_STATIC, "encodePartial", byteArrayType,
+ new TypeDesc[] {objectArrayType, TypeDesc.INT, TypeDesc.INT});
+ b = new CodeBuilder(mi);
+ OrderedProperty<TestStorable>[] ordered =
+ makeOrderedProperties(properties, directions);
+ encodedVar =
+ strategy.buildKeyEncoding(b, ordered, b.getParameter(0), null, false,
+ b.getParameter(1), b.getParameter(2));
+ b.loadLocal(encodedVar);
+ b.returnValue(byteArrayType);
+ }
+
+ Class<?> clazz = ci.defineClass(cf);
+
+ Method encode = clazz.getMethod("encode", Object[].class);
+ Method decode = clazz.getMethod("decode", Object[].class, byte[].class);
+ Method encodePartial = null;
+ if (directions != null && properties.length > 0) {
+ encodePartial = clazz.getMethod("encodePartial", Object[].class, int.class, int.class);
+ }
+
+ Method[] methods = new Method[3];
+ methods[ENCODE_OBJECT_ARRAY] = encode;
+ methods[DECODE_OBJECT_ARRAY] = decode;
+ methods[ENCODE_OBJECT_ARRAY_PARTIAL] = encodePartial;
+
+ return methods;
+ }
+
+ private StorableProperty<TestStorable>[] selectProperties(int minCount, int maxCount) {
+ int length = (minCount == maxCount) ? minCount
+ : (mRandom.nextInt(maxCount - minCount + 1) + minCount);
+
+ StorableProperty<TestStorable>[] selection = new StorableProperty[length];
+
+ for (int i=length; --i>=0; ) {
+ selection[i] = mProperties[mRandom.nextInt(mProperties.length)];
+ }
+
+ return selection;
+ }
+
+ private Direction[] selectDirections(int count) {
+ Direction[] directions = new Direction[count];
+
+ for (int i=count; --i>=0; ) {
+ Direction dir;
+ switch (mRandom.nextInt(3)) {
+ default:
+ dir = Direction.UNSPECIFIED;
+ break;
+ case 1:
+ dir = Direction.ASCENDING;
+ break;
+ case 2:
+ dir = Direction.DESCENDING;
+ break;
+ }
+ directions[i] = dir;
+ }
+
+ return directions;
+ }
+
+ private OrderedProperty<TestStorable>[] makeOrderedProperties
+ (StorableProperty<TestStorable>[] properties, Direction[] directions) {
+
+ int length = properties.length;
+ OrderedProperty<TestStorable>[] ordered = new OrderedProperty[length];
+ for (int i=length; --i>=0; ) {
+ ordered[i] = OrderedProperty.get(properties[i], directions[i]);
+ }
+
+ return ordered;
+ }
+
+ /**
+ * Returns an array of the same size with randomly selected values filled
+ * in that match the property type.
+ */
+ private Object[] selectPropertyValues(StorableProperty<?>[] properties) {
+ int length = properties.length;
+ Object[] values = new Object[length];
+
+ for (int i=length; --i>=0; ) {
+ StorableProperty<?> property = properties[i];
+ TypeDesc type = TypeDesc.forClass(property.getType());
+
+ Object value;
+
+ if (property.isNullable() && mRandom.nextInt(100) == 0) {
+ value = null;
+ } else {
+ TypeDesc prim = type.toPrimitiveType();
+ if (prim != null) {
+ switch (prim.getTypeCode()) {
+ case TypeDesc.BOOLEAN_CODE: default:
+ value = mRandom.nextBoolean();
+ break;
+ case TypeDesc.CHAR_CODE:
+ value = (char) mRandom.nextInt();
+ break;
+ case TypeDesc.FLOAT_CODE:
+ value = Float.intBitsToFloat(mRandom.nextInt());
+ break;
+ case TypeDesc.DOUBLE_CODE:
+ value = Double.longBitsToDouble(mRandom.nextLong());
+ break;
+ case TypeDesc.BYTE_CODE:
+ value = (byte) mRandom.nextInt();
+ break;
+ case TypeDesc.SHORT_CODE:
+ value = (short) mRandom.nextInt();
+ break;
+ case TypeDesc.INT_CODE:
+ value = mRandom.nextInt();
+ break;
+ case TypeDesc.LONG_CODE:
+ value = mRandom.nextLong();
+ break;
+ }
+ } else if (type == TypeDesc.STRING) {
+ int len = mRandom.nextInt(100);
+ StringBuilder sb = new StringBuilder(len);
+ for (int j=0; j<len; j++) {
+ sb.append((char) mRandom.nextInt());
+ }
+ value = sb.toString();
+ } else {
+ int len = mRandom.nextInt(100);
+ byte[] bytes = new byte[len];
+ for (int j=0; j<len; j++) {
+ bytes[j] = (byte) mRandom.nextInt();
+ }
+ value = bytes;
+ }
+ }
+
+ values[i] = value;
+ }
+
+ return values;
+ }
+
+ /**
+ * Just a collection of storable properties. At least one property defined
+ * per supported type.
+ */
+ @PrimaryKey("byte") // I don't really care what the primary key is.
+ public static interface TestStorable extends Storable {
+ byte getByte();
+
+ void setByte(byte b);
+
+ Byte getByteObj();
+
+ void setByteObj(Byte b);
+
+ @Nullable
+ Byte getNullableByteObj();
+
+ void setNullableByteObj(Byte b);
+
+ short getShort();
+
+ void setShort(short s);
+
+ Short getShortObj();
+
+ void setShortObj(Short s);
+
+ @Nullable
+ Short getNullableShortObj();
+
+ void setNullableShortObj(Short s);
+
+ char getChar();
+
+ void setChar(char c);
+
+ Character getCharacterObj();
+
+ void setCharacterObj(Character c);
+
+ @Nullable
+ Character getNullableCharacterObj();
+
+ void setNullableCharacterObj(Character c);
+
+ int getInt();
+
+ void setInt(int i);
+
+ Integer getIntegerObj();
+
+ void setIntegerObj(Integer obj);
+
+ @Nullable
+ Integer getNullableIntegerObj();
+
+ void setNullableIntegerObj(Integer obj);
+
+ long getLong();
+
+ void setLong(long i);
+
+ Long getLongObj();
+
+ void setLongObj(Long i);
+
+ @Nullable
+ Long getNullableLongObj();
+
+ void setNullableLongObj(Long i);
+
+ float getFloat();
+
+ void setFloat(float f);
+
+ Float getFloatObj();
+
+ void setFloatObj(Float f);
+
+ @Nullable
+ Float getNullableFloatObj();
+
+ void setNullableFloatObj(Float f);
+
+ double getDouble();
+
+ void setDouble(double d);
+
+ Double getDoubleObj();
+
+ void setDoubleObj(Double d);
+
+ @Nullable
+ Double getNullableDoubleObj();
+
+ void setNullableDoubleObj(Double d);
+
+ byte[] getByteArray();
+
+ void setByteArray(byte[] b);
+
+ @Nullable
+ byte[] getNullableByteArray();
+
+ void setNullableByteArray(byte[] b);
+
+ String getString();
+
+ void setString(String s);
+
+ @Nullable
+ String getNullableString();
+
+ void setNullableString(String s);
+ }
+}
diff --git a/src/test/java/com/amazon/carbonado/raw/TestKeyEncoding.java b/src/test/java/com/amazon/carbonado/raw/TestKeyEncoding.java new file mode 100644 index 0000000..3dba00e --- /dev/null +++ b/src/test/java/com/amazon/carbonado/raw/TestKeyEncoding.java @@ -0,0 +1,571 @@ +/*
+ * 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.raw;
+
+import java.util.Random;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Test case for {@link KeyEncoder} and {@link KeyDecoder}.
+ * <p>
+ * It generates random data values, checks that the decoding produces the
+ * original results, and it checks that the order of the encoded bytes matches
+ * the order of the original data values.
+ *
+ * @author Brian S O'Neill
+ */
+public class TestKeyEncoding extends TestCase {
+ private static final int SHORT_TEST = 100;
+ private static final int MEDIUM_TEST = 500;
+ private static final int LONG_TEST = 1000;
+
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static TestSuite suite() {
+ return new TestSuite(TestKeyEncoding.class);
+ }
+
+ private final long mSeed;
+
+ private Random mRandom;
+
+ public TestKeyEncoding(String name) {
+ super(name);
+ mSeed = 5399777425345431L;
+ }
+
+ protected void setUp() {
+ mRandom = new Random(mSeed);
+ }
+
+ protected void tearDown() {
+ }
+
+ public void test_booleanDesc() throws Exception {
+ byte[] bytes = new byte[1];
+ boolean lastValue = false;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ boolean value = mRandom.nextBoolean();
+ KeyEncoder.encodeDesc(value, bytes, 0);
+ assertEquals(value, KeyDecoder.decodeBooleanDesc(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = -TestDataEncoding.compare(value, lastValue);
+ assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_BooleanDesc() throws Exception {
+ byte[] bytes = new byte[1];
+ Boolean lastValue = false;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ Boolean value;
+ if (mRandom.nextInt(10) == 1) {
+ value = null;
+ } else {
+ value = mRandom.nextBoolean();
+ }
+ KeyEncoder.encodeDesc(value, bytes, 0);
+ assertEquals(value, KeyDecoder.decodeBooleanObjDesc(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = -TestDataEncoding.compare(value, lastValue);
+ assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_byteDesc() throws Exception {
+ byte[] bytes = new byte[1];
+ byte lastValue = 0;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ byte value = (byte) mRandom.nextInt();
+ KeyEncoder.encodeDesc(value, bytes, 0);
+ assertEquals(value, KeyDecoder.decodeByteDesc(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = -TestDataEncoding.compare(value, lastValue);
+ assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_ByteDesc() throws Exception {
+ byte[] bytes = new byte[2];
+ Byte lastValue = 0;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ Byte value;
+ if (mRandom.nextInt(10) == 1) {
+ value = null;
+ assertEquals(1, KeyEncoder.encodeDesc(value, bytes, 0));
+ } else {
+ value = (byte) mRandom.nextInt();
+ assertEquals(2, KeyEncoder.encodeDesc(value, bytes, 0));
+ }
+ assertEquals(value, KeyDecoder.decodeByteObjDesc(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = -TestDataEncoding.compare(value, lastValue);
+ assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_shortDesc() throws Exception {
+ byte[] bytes = new byte[2];
+ short lastValue = 0;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ short value = (short) mRandom.nextInt();
+ KeyEncoder.encodeDesc(value, bytes, 0);
+ assertEquals(value, KeyDecoder.decodeShortDesc(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = -TestDataEncoding.compare(value, lastValue);
+ assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_ShortDesc() throws Exception {
+ byte[] bytes = new byte[3];
+ Short lastValue = 0;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ Short value;
+ if (mRandom.nextInt(10) == 1) {
+ value = null;
+ assertEquals(1, KeyEncoder.encodeDesc(value, bytes, 0));
+ } else {
+ value = (short) mRandom.nextInt();
+ assertEquals(3, KeyEncoder.encodeDesc(value, bytes, 0));
+ }
+ assertEquals(value, KeyDecoder.decodeShortObjDesc(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = -TestDataEncoding.compare(value, lastValue);
+ assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_charDesc() throws Exception {
+ byte[] bytes = new byte[2];
+ char lastValue = 0;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ char value = (char) mRandom.nextInt();
+ KeyEncoder.encodeDesc(value, bytes, 0);
+ assertEquals(value, KeyDecoder.decodeCharDesc(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = -TestDataEncoding.compare(value, lastValue);
+ assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_CharacterDesc() throws Exception {
+ byte[] bytes = new byte[3];
+ Character lastValue = 0;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ Character value;
+ if (mRandom.nextInt(10) == 1) {
+ value = null;
+ assertEquals(1, KeyEncoder.encodeDesc(value, bytes, 0));
+ } else {
+ value = (char) mRandom.nextInt();
+ assertEquals(3, KeyEncoder.encodeDesc(value, bytes, 0));
+ }
+ assertEquals(value, KeyDecoder.decodeCharacterObjDesc(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = -TestDataEncoding.compare(value, lastValue);
+ assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_intDesc() throws Exception {
+ byte[] bytes = new byte[4];
+ int lastValue = 0;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ int value = mRandom.nextInt();
+ KeyEncoder.encodeDesc(value, bytes, 0);
+ assertEquals(value, KeyDecoder.decodeIntDesc(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = -TestDataEncoding.compare(value, lastValue);
+ assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_IntegerDesc() throws Exception {
+ byte[] bytes = new byte[5];
+ Integer lastValue = 0;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ Integer value;
+ if (mRandom.nextInt(10) == 1) {
+ value = null;
+ assertEquals(1, KeyEncoder.encodeDesc(value, bytes, 0));
+ } else {
+ value = mRandom.nextInt();
+ assertEquals(5, KeyEncoder.encodeDesc(value, bytes, 0));
+ }
+ assertEquals(value, KeyDecoder.decodeIntegerObjDesc(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = -TestDataEncoding.compare(value, lastValue);
+ assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_longDesc() throws Exception {
+ byte[] bytes = new byte[8];
+ long lastValue = 0;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ long value = mRandom.nextLong();
+ KeyEncoder.encodeDesc(value, bytes, 0);
+ assertEquals(value, KeyDecoder.decodeLongDesc(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = -TestDataEncoding.compare(value, lastValue);
+ assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_LongDesc() throws Exception {
+ byte[] bytes = new byte[9];
+ Long lastValue = 0L;
+ byte[] lastBytes = null;
+ for (int i=0; i<SHORT_TEST; i++) {
+ Long value;
+ if (mRandom.nextInt(10) == 1) {
+ value = null;
+ assertEquals(1, KeyEncoder.encodeDesc(value, bytes, 0));
+ } else {
+ value = mRandom.nextLong();
+ assertEquals(9, KeyEncoder.encodeDesc(value, bytes, 0));
+ }
+ assertEquals(value, KeyDecoder.decodeLongObjDesc(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = -TestDataEncoding.compare(value, lastValue);
+ assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_floatDesc() throws Exception {
+ byte[] bytes = new byte[4];
+ float lastValue = 0;
+ byte[] lastBytes = null;
+ for (int i=0; i<LONG_TEST; i++) {
+ float value = Float.intBitsToFloat(mRandom.nextInt());
+ KeyEncoder.encodeDesc(value, bytes, 0);
+ assertEquals(value, KeyDecoder.decodeFloatDesc(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = -TestDataEncoding.compare(value, lastValue);
+ assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_FloatDesc() throws Exception {
+ byte[] bytes = new byte[4];
+ Float lastValue = 0f;
+ byte[] lastBytes = null;
+ for (int i=0; i<LONG_TEST; i++) {
+ Float value;
+ if (mRandom.nextInt(10) == 1) {
+ value = null;
+ } else {
+ value = Float.intBitsToFloat(mRandom.nextInt());
+ }
+ KeyEncoder.encodeDesc(value, bytes, 0);
+ assertEquals(value, KeyDecoder.decodeFloatObjDesc(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = -TestDataEncoding.compare(value, lastValue);
+ assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_doubleDesc() throws Exception {
+ byte[] bytes = new byte[8];
+ double lastValue = 0;
+ byte[] lastBytes = null;
+ for (int i=0; i<LONG_TEST; i++) {
+ double value = Double.longBitsToDouble(mRandom.nextLong());
+ KeyEncoder.encodeDesc(value, bytes, 0);
+ assertEquals(value, KeyDecoder.decodeDoubleDesc(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = -TestDataEncoding.compare(value, lastValue);
+ assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_DoubleDesc() throws Exception {
+ byte[] bytes = new byte[8];
+ Double lastValue = 0d;
+ byte[] lastBytes = null;
+ for (int i=0; i<LONG_TEST; i++) {
+ Double value;
+ if (mRandom.nextInt(10) == 1) {
+ value = null;
+ } else {
+ value = Double.longBitsToDouble(mRandom.nextLong());
+ }
+ KeyEncoder.encodeDesc(value, bytes, 0);
+ assertEquals(value, KeyDecoder.decodeDoubleObjDesc(bytes, 0));
+ if (lastBytes != null) {
+ int sgn = -TestDataEncoding.compare(value, lastValue);
+ assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_String() throws Exception {
+ String lastValue = null;
+ byte[] lastBytes = null;
+ String[] ref = new String[1];
+ for (int i=0; i<SHORT_TEST; i++) {
+ String value;
+ if (mRandom.nextInt(10) == 1) {
+ value = null;
+ } else {
+ int length;
+ switch (mRandom.nextInt(15)) {
+ default:
+ case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
+ length = mRandom.nextInt(100);
+ break;
+ case 8: case 9: case 10: case 11:
+ length = mRandom.nextInt(200);
+ break;
+ case 12: case 13:
+ length = mRandom.nextInt(20000);
+ break;
+ case 14:
+ length = mRandom.nextInt(3000000);
+ break;
+ }
+ char[] chars = new char[length];
+ for (int j=0; j<length; j++) {
+ char c;
+ switch (mRandom.nextInt(7)) {
+ default:
+ case 0: case 1: case 2: case 3:
+ c = (char) mRandom.nextInt(128);
+ break;
+ case 4: case 5:
+ c = (char) mRandom.nextInt(4000);
+ break;
+ case 6:
+ c = (char) mRandom.nextInt();
+ break;
+ }
+ chars[j] = c;
+ }
+ value = new String(chars);
+ }
+
+ byte[] bytes = new byte[KeyEncoder.calculateEncodedStringLength(value)];
+ assertEquals(bytes.length, KeyEncoder.encode(value, bytes, 0));
+ assertEquals(bytes.length, KeyDecoder.decodeString(bytes, 0, ref));
+ assertEquals(value, ref[0]);
+
+ if (lastBytes != null) {
+ int sgn = TestDataEncoding.compare(value, lastValue);
+ assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_StringDesc() throws Exception {
+ String lastValue = null;
+ byte[] lastBytes = null;
+ String[] ref = new String[1];
+ for (int i=0; i<SHORT_TEST; i++) {
+ String value;
+ if (mRandom.nextInt(10) == 1) {
+ value = null;
+ } else {
+ int length;
+ switch (mRandom.nextInt(15)) {
+ default:
+ case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
+ length = mRandom.nextInt(100);
+ break;
+ case 8: case 9: case 10: case 11:
+ length = mRandom.nextInt(200);
+ break;
+ case 12: case 13:
+ length = mRandom.nextInt(20000);
+ break;
+ case 14:
+ length = mRandom.nextInt(3000000);
+ break;
+ }
+ char[] chars = new char[length];
+ for (int j=0; j<length; j++) {
+ char c;
+ switch (mRandom.nextInt(7)) {
+ default:
+ case 0: case 1: case 2: case 3:
+ c = (char) mRandom.nextInt(128);
+ break;
+ case 4: case 5:
+ c = (char) mRandom.nextInt(4000);
+ break;
+ case 6:
+ c = (char) mRandom.nextInt();
+ break;
+ }
+ chars[j] = c;
+ }
+ value = new String(chars);
+ }
+
+ byte[] bytes = new byte[KeyEncoder.calculateEncodedStringLength(value)];
+ assertEquals(bytes.length, KeyEncoder.encodeDesc(value, bytes, 0));
+ assertEquals(bytes.length, KeyDecoder.decodeStringDesc(bytes, 0, ref));
+ assertEquals(value, ref[0]);
+
+
+ if (lastBytes != null) {
+ int sgn = -TestDataEncoding.compare(value, lastValue);
+ assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_byteArray() throws Exception {
+ byte[] lastValue = null;
+ byte[] lastBytes = null;
+ byte[][] ref = new byte[1][];
+ for (int i=0; i<LONG_TEST; i++) {
+ byte[] value;
+ if (mRandom.nextInt(10) == 1) {
+ value = null;
+ } else {
+ int length = mRandom.nextInt(4000);
+ value = new byte[length];
+ for (int j=0; j<length; j++) {
+ value[j] = (byte) mRandom.nextInt();
+ }
+ }
+
+ byte[] bytes = new byte[KeyEncoder.calculateEncodedLength(value)];
+ assertEquals(bytes.length, KeyEncoder.encode(value, bytes, 0));
+ assertEquals(bytes.length, KeyDecoder.decode(bytes, 0, ref));
+ if (ref[0] == null) {
+ assertEquals(value, null);
+ } else if (value == null) {
+ assertEquals(value, ref[0]);
+ } else {
+ assertEquals(0, TestDataEncoding.byteArrayCompare(value, ref[0], value.length));
+ }
+
+ if (lastBytes != null) {
+ int sgn = TestDataEncoding.byteArrayOrNullCompare(value, lastValue);
+ assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+
+ public void test_byteArrayDesc() throws Exception {
+ byte[] lastValue = null;
+ byte[] lastBytes = null;
+ byte[][] ref = new byte[1][];
+ for (int i=0; i<LONG_TEST; i++) {
+ byte[] value;
+ if (mRandom.nextInt(10) == 1) {
+ value = null;
+ } else {
+ int length = mRandom.nextInt(4000);
+ value = new byte[length];
+ for (int j=0; j<length; j++) {
+ value[j] = (byte) mRandom.nextInt();
+ }
+ }
+
+ byte[] bytes = new byte[KeyEncoder.calculateEncodedLength(value)];
+ assertEquals(bytes.length, KeyEncoder.encodeDesc(value, bytes, 0));
+ assertEquals(bytes.length, KeyDecoder.decodeDesc(bytes, 0, ref));
+ if (ref[0] == null) {
+ assertEquals(value, null);
+ } else if (value == null) {
+ assertEquals(value, ref[0]);
+ } else {
+ assertEquals(0, TestDataEncoding.byteArrayCompare(value, ref[0], value.length));
+ }
+
+ if (lastBytes != null) {
+ int sgn = -TestDataEncoding.byteArrayOrNullCompare(value, lastValue);
+ assertEquals(sgn, TestDataEncoding.byteArrayCompare(bytes, lastBytes));
+ }
+ lastValue = value;
+ lastBytes = bytes.clone();
+ }
+ }
+}
diff --git a/src/test/java/com/amazon/carbonado/repo/toy/ToyRepository.java b/src/test/java/com/amazon/carbonado/repo/toy/ToyRepository.java new file mode 100644 index 0000000..159f451 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/repo/toy/ToyRepository.java @@ -0,0 +1,106 @@ +/*
+ * 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.repo.toy;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.amazon.carbonado.IsolationLevel;
+import com.amazon.carbonado.Repository;
+import com.amazon.carbonado.RepositoryException;
+import com.amazon.carbonado.Storable;
+import com.amazon.carbonado.Storage;
+import com.amazon.carbonado.SupportException;
+import com.amazon.carbonado.Transaction;
+
+import com.amazon.carbonado.capability.Capability;
+
+import com.amazon.carbonado.spi.SequenceValueGenerator;
+import com.amazon.carbonado.spi.SequenceValueProducer;
+
+/**
+ *
+ * @author Brian S O'Neill
+ */
+public class ToyRepository implements Repository {
+ private final String mName;
+ private final Map<Class, Storage> mStorages;
+ private final Map<String, SequenceValueProducer> mSequences;
+
+ public ToyRepository() {
+ this("toy");
+ }
+
+ public ToyRepository(String name) {
+ mName = name;
+ mStorages = new HashMap<Class, Storage>();
+ mSequences = new HashMap<String, SequenceValueProducer>();
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public <S extends Storable> Storage<S> storageFor(Class<S> type)
+ throws SupportException, RepositoryException
+ {
+ synchronized (mStorages) {
+ Storage<S> storage = (Storage<S>) mStorages.get(type);
+ if (storage == null) {
+ storage = new ToyStorage<S>(this, type);
+ mStorages.put(type, storage);
+ }
+ return storage;
+ }
+ }
+
+ public Transaction enterTransaction() {
+ return new ToyTransaction();
+ }
+
+ public Transaction enterTransaction(IsolationLevel level) {
+ return enterTransaction();
+ }
+
+ public Transaction enterTopTransaction(IsolationLevel level) {
+ return enterTransaction(level);
+ }
+
+ public IsolationLevel getTransactionIsolationLevel() {
+ return null;
+ }
+
+ public <C extends Capability> C getCapability(Class<C> capabilityType) {
+ return null;
+ }
+
+ public void close() {
+ }
+
+ SequenceValueProducer getSequenceValueProducer(String name) throws RepositoryException {
+ synchronized (mSequences) {
+ SequenceValueProducer producer = mSequences.get(name);
+ if (producer == null) {
+ producer = new SequenceValueGenerator(this, name);
+ mSequences.put(name, producer);
+ }
+ return producer;
+ }
+ }
+}
diff --git a/src/test/java/com/amazon/carbonado/repo/toy/ToyStorableGenerator.java b/src/test/java/com/amazon/carbonado/repo/toy/ToyStorableGenerator.java new file mode 100644 index 0000000..a72c7d5 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/repo/toy/ToyStorableGenerator.java @@ -0,0 +1,143 @@ +/*
+ * 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.repo.toy;
+
+import java.util.EnumSet;
+import java.util.Map;
+
+import org.cojen.classfile.ClassFile;
+import org.cojen.classfile.CodeBuilder;
+import org.cojen.classfile.MethodInfo;
+import org.cojen.classfile.Modifiers;
+import org.cojen.classfile.TypeDesc;
+
+import org.cojen.util.ClassInjector;
+import org.cojen.util.SoftValuedHashMap;
+
+import com.amazon.carbonado.Storable;
+import com.amazon.carbonado.SupportException;
+import com.amazon.carbonado.Trigger;
+
+import com.amazon.carbonado.spi.MasterFeature;
+import com.amazon.carbonado.spi.MasterStorableGenerator;
+import com.amazon.carbonado.spi.MasterSupport;
+import com.amazon.carbonado.spi.StorableGenerator;
+import com.amazon.carbonado.spi.TriggerSupport;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+public class ToyStorableGenerator<S extends Storable> {
+ private static final Map<Class, Class> cCache;
+
+ static {
+ cCache = new SoftValuedHashMap();
+ }
+
+ /**
+ * Generated class has a constructor that accepts a ToyStorage instance.
+ */
+ public static <S extends Storable> Class<? extends S> getGeneratedClass(Class<S> type)
+ throws SupportException
+ {
+ synchronized (cCache) {
+ Class<? extends S> generatedClass = (Class<? extends S>) cCache.get(type);
+ if (generatedClass != null) {
+ return generatedClass;
+ }
+ generatedClass = new ToyStorableGenerator<S>(type).generateAndInjectClass();
+ cCache.put(type, generatedClass);
+ return generatedClass;
+ }
+ }
+
+ private final Class<S> mStorableType;
+
+ private final ClassInjector mClassInjector;
+ private final ClassFile mClassFile;
+
+ private ToyStorableGenerator(Class<S> type) throws SupportException {
+ mStorableType = type;
+
+ EnumSet<MasterFeature> features = EnumSet
+ .of(MasterFeature.VERSIONING, MasterFeature.INSERT_SEQUENCES);
+
+ final Class<? extends S> abstractClass =
+ MasterStorableGenerator.getAbstractClass(mStorableType, features);
+
+ mClassInjector = ClassInjector.create(mStorableType.getName(),
+ abstractClass.getClassLoader());
+
+ mClassFile = new ClassFile(mClassInjector.getClassName(), abstractClass);
+ mClassFile.markSynthetic();
+ mClassFile.setSourceFile(ToyStorableGenerator.class.getName());
+ mClassFile.setTarget("1.5");
+ }
+
+ private Class<? extends S> generateAndInjectClass() {
+ TypeDesc masterSupportType = TypeDesc.forClass(MasterSupport.class);
+ TypeDesc toyStorageType = TypeDesc.forClass(ToyStorage.class);
+
+ // Add constructor that accepts a ToyStorage.
+ {
+ TypeDesc[] params = {toyStorageType};
+ MethodInfo mi = mClassFile.addConstructor(Modifiers.PUBLIC, params);
+ CodeBuilder b = new CodeBuilder(mi);
+ b.loadThis();
+ b.loadLocal(b.getParameter(0));
+ b.invokeSuperConstructor(new TypeDesc[] {masterSupportType});
+ b.returnVoid();
+ }
+
+ // Implement abstract methods which all delegate to ToyStorage instance.
+
+ generateDelegatedMethod
+ (MasterStorableGenerator.DO_TRY_LOAD_MASTER_METHOD_NAME, "doTryLoad");
+ generateDelegatedMethod
+ (MasterStorableGenerator.DO_TRY_INSERT_MASTER_METHOD_NAME, "doTryInsert");
+ generateDelegatedMethod
+ (MasterStorableGenerator.DO_TRY_UPDATE_MASTER_METHOD_NAME, "doTryUpdate");
+ generateDelegatedMethod
+ (MasterStorableGenerator.DO_TRY_DELETE_MASTER_METHOD_NAME, "doTryDelete");
+
+ Class<? extends S> generatedClass = mClassInjector.defineClass(mClassFile);
+
+ return generatedClass;
+ }
+
+ private void generateDelegatedMethod(String masterMethodName, String supportMethodName) {
+ TypeDesc triggerSupportType = TypeDesc.forClass(TriggerSupport.class);
+ TypeDesc toyStorageType = TypeDesc.forClass(ToyStorage.class);
+
+ TypeDesc[] storableParam = {TypeDesc.forClass(Storable.class)};
+
+ MethodInfo mi = mClassFile.addMethod
+ (Modifiers.PROTECTED, masterMethodName, TypeDesc.BOOLEAN, null);
+ CodeBuilder b = new CodeBuilder(mi);
+
+ b.loadThis();
+ b.loadField(StorableGenerator.SUPPORT_FIELD_NAME, triggerSupportType);
+ b.checkCast(toyStorageType);
+ b.loadThis();
+ b.invokeVirtual(toyStorageType, supportMethodName, TypeDesc.BOOLEAN, storableParam);
+ b.returnValue(TypeDesc.BOOLEAN);
+ }
+}
diff --git a/src/test/java/com/amazon/carbonado/repo/toy/ToyStorage.java b/src/test/java/com/amazon/carbonado/repo/toy/ToyStorage.java new file mode 100644 index 0000000..71a597d --- /dev/null +++ b/src/test/java/com/amazon/carbonado/repo/toy/ToyStorage.java @@ -0,0 +1,265 @@ +/*
+ * 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.repo.toy;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedList;
+
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import com.amazon.carbonado.FetchException;
+import com.amazon.carbonado.IsolationLevel;
+import com.amazon.carbonado.PersistException;
+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.SupportException;
+import com.amazon.carbonado.Transaction;
+import com.amazon.carbonado.Trigger;
+
+import com.amazon.carbonado.spi.MasterSupport;
+import com.amazon.carbonado.spi.SequenceValueProducer;
+
+import com.amazon.carbonado.util.QuickConstructorGenerator;
+
+import com.amazon.carbonado.filter.Filter;
+import com.amazon.carbonado.filter.FilterValues;
+
+import com.amazon.carbonado.info.OrderedProperty;
+import com.amazon.carbonado.info.StorableIntrospector;
+
+import com.amazon.carbonado.qe.QueryExecutorFactory;
+import com.amazon.carbonado.qe.QueryFactory;
+import com.amazon.carbonado.qe.SortedQueryExecutor;
+import com.amazon.carbonado.qe.FilteredQueryExecutor;
+import com.amazon.carbonado.qe.IterableQueryExecutor;
+import com.amazon.carbonado.qe.OrderingList;
+import com.amazon.carbonado.qe.QueryExecutor;
+import com.amazon.carbonado.qe.StandardQuery;
+
+/**
+ *
+ * @author Brian S O'Neill
+ */
+public class ToyStorage<S extends Storable>
+ implements Storage<S>, MasterSupport<S>, QueryFactory<S>, QueryExecutorFactory<S>
+{
+ final ToyRepository mRepo;
+ final Class<S> mType;
+
+ final InstanceFactory mInstanceFactory;
+
+ final Collection<S> mData;
+ final Lock mDataLock;
+
+ public ToyStorage(ToyRepository repo, Class<S> type) throws SupportException {
+ StorableIntrospector.examine(type);
+ mRepo = repo;
+ mType = type;
+
+ Class<? extends S> generatedStorableClass = ToyStorableGenerator.getGeneratedClass(type);
+ mInstanceFactory = QuickConstructorGenerator
+ .getInstance(generatedStorableClass, InstanceFactory.class);
+
+ mData = new LinkedList<S>();
+ mDataLock = new ReentrantLock();
+ }
+
+ public Class<S> getStorableType() {
+ return mType;
+ }
+
+ public S prepare() {
+ return (S) mInstanceFactory.instantiate(this);
+ }
+
+ public Query<S> query() {
+ return new ToyQuery(null, null, null);
+ }
+
+ public Query<S> query(String filter) {
+ return query(Filter.filterFor(mType, filter));
+ }
+
+ public Query<S> query(Filter<S> filter) {
+ return new ToyQuery(filter.initialFilterValues(), null, null);
+ }
+
+ public Query<S> query(FilterValues<S> values, OrderingList<S> ordering) {
+ return new ToyQuery(values, ordering, null);
+ }
+
+ public QueryExecutor<S> executor(Filter<S> filter, OrderingList<S> ordering) {
+ QueryExecutor<S> executor = new IterableQueryExecutor<S>(mType, mData, mDataLock);
+
+ if (filter != null) {
+ executor = new FilteredQueryExecutor<S>(executor, filter);
+ }
+
+ if (ordering.size() > 0) {
+ executor = new SortedQueryExecutor<S>(null, executor, null, ordering);
+ }
+
+ return executor;
+ }
+
+ public boolean addTrigger(Trigger<? super S> trigger) {
+ return false;
+ }
+
+ public boolean removeTrigger(Trigger<? super S> trigger) {
+ return false;
+ }
+
+ public boolean doTryLoad(S storable) {
+ mDataLock.lock();
+ try {
+ for (S existing : mData) {
+ if (existing.equalPrimaryKeys(storable)) {
+ storable.markAllPropertiesDirty();
+ existing.copyAllProperties(storable);
+ storable.markAllPropertiesClean();
+ return true;
+ }
+ }
+ return false;
+ } finally {
+ mDataLock.unlock();
+ }
+ }
+
+ public boolean doTryInsert(S storable) {
+ mDataLock.lock();
+ try {
+ for (S existing : mData) {
+ if (existing.equalPrimaryKeys(storable)) {
+ return false;
+ }
+ }
+ storable.markAllPropertiesClean();
+ mData.add((S) storable.copy());
+ return true;
+ } finally {
+ mDataLock.unlock();
+ }
+ }
+
+ public boolean doTryUpdate(S storable) {
+ mDataLock.lock();
+ try {
+ for (S existing : mData) {
+ if (existing.equalPrimaryKeys(storable)) {
+ // Copy altered values to existing object.
+ existing.markAllPropertiesDirty();
+ storable.copyAllProperties(existing);
+ existing.markAllPropertiesClean();
+
+ // Copy all values to user object, to simulate a reload.
+ storable.markAllPropertiesDirty();
+ existing.copyAllProperties(storable);
+ storable.markAllPropertiesClean();
+
+ return true;
+ }
+ }
+ return false;
+ } finally {
+ mDataLock.unlock();
+ }
+ }
+
+ public boolean doTryDelete(S storable) {
+ mDataLock.lock();
+ try {
+ Iterator<S> it = mData.iterator();
+ while (it.hasNext()) {
+ S existing = it.next();
+ if (existing.equalPrimaryKeys(storable)) {
+ it.remove();
+ return true;
+ }
+ }
+ return false;
+ } finally {
+ mDataLock.unlock();
+ }
+ }
+
+ public Repository getRootRepository() {
+ return mRepo;
+ }
+
+ public boolean isPropertySupported(String propertyName) {
+ return StorableIntrospector.examine(mType)
+ .getAllProperties().containsKey(propertyName);
+ }
+
+ public Trigger<? super S> getInsertTrigger() {
+ return null;
+ }
+
+ public Trigger<? super S> getUpdateTrigger() {
+ return null;
+ }
+
+ public Trigger<? super S> getDeleteTrigger() {
+ return null;
+ }
+
+ public SequenceValueProducer getSequenceValueProducer(String name) throws PersistException {
+ try {
+ return mRepo.getSequenceValueProducer(name);
+ } catch (RepositoryException e) {
+ throw e.toPersistException();
+ }
+ }
+
+ public static interface InstanceFactory {
+ Storable instantiate(ToyStorage storage);
+ }
+
+ private class ToyQuery extends StandardQuery<S> {
+ ToyQuery(FilterValues<S> values, OrderingList<S> ordering, QueryExecutor<S> executor) {
+ super(values, ordering, executor);
+ }
+
+ protected Transaction enterTransaction(IsolationLevel level) {
+ return mRepo.enterTransaction(level);
+ }
+
+ protected QueryFactory<S> queryFactory() {
+ return ToyStorage.this;
+ }
+
+ protected QueryExecutorFactory<S> executorFactory() {
+ return ToyStorage.this;
+ }
+
+ protected StandardQuery<S> newInstance(FilterValues<S> values,
+ OrderingList<S> ordering,
+ QueryExecutor<S> executor)
+ {
+ return new ToyQuery(values, ordering, executor);
+ }
+ }
+}
diff --git a/src/test/java/com/amazon/carbonado/repo/toy/ToyTransaction.java b/src/test/java/com/amazon/carbonado/repo/toy/ToyTransaction.java new file mode 100644 index 0000000..c16d1f1 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/repo/toy/ToyTransaction.java @@ -0,0 +1,51 @@ +/*
+ * 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.repo.toy;
+
+import java.util.concurrent.TimeUnit;
+
+import com.amazon.carbonado.IsolationLevel;
+import com.amazon.carbonado.PersistException;
+import com.amazon.carbonado.Transaction;
+
+/**
+ *
+ * @author Brian S O'Neill
+ */
+public class ToyTransaction implements Transaction {
+ public void commit() throws PersistException {
+ }
+
+ public void exit() throws PersistException {
+ }
+
+ public void setForUpdate(boolean forUpdate) {
+ }
+
+ public boolean isForUpdate() {
+ return false;
+ }
+
+ public void setDesiredLockTimeout(int timeout, TimeUnit unit) {
+ }
+
+ public IsolationLevel getIsolationLevel() {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/src/test/java/com/amazon/carbonado/repo/toy/package-info.java b/src/test/java/com/amazon/carbonado/repo/toy/package-info.java new file mode 100644 index 0000000..769ea8b --- /dev/null +++ b/src/test/java/com/amazon/carbonado/repo/toy/package-info.java @@ -0,0 +1,25 @@ +/*
+ * 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.
+ */
+
+/**
+ * Standalone toy repository implementation. This repository is only suitable
+ * for running tests that don't require anything sophisticated. It doesn't
+ * support transactions, nothing is actually persisted, and all queries do full
+ * scans. The repository is thread-safe, however.
+ */
+package com.amazon.carbonado.repo.toy;
diff --git a/src/test/java/com/amazon/carbonado/sample/FileInfoTest.java b/src/test/java/com/amazon/carbonado/sample/FileInfoTest.java new file mode 100644 index 0000000..0f38de7 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/sample/FileInfoTest.java @@ -0,0 +1,92 @@ +/*
+ * 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.sample;
+
+import java.io.File;
+import java.io.InputStream;
+
+import org.joda.time.DateTime;
+
+import com.amazon.carbonado.Cursor;
+import com.amazon.carbonado.Query;
+import com.amazon.carbonado.Repository;
+import com.amazon.carbonado.Storage;
+
+import com.amazon.carbonado.lob.FileBlob;
+
+import com.amazon.carbonado.repo.sleepycat.BDBRepositoryBuilder;
+
+import com.amazon.carbonado.stored.FileInfo;
+
+/**
+ * Test program which implements storage for a simple indexed file system.
+ *
+ * @author Brian S O'Neill
+ */
+public class FileInfoTest {
+ public static Storage<FileInfo> openStorage(String envHome) throws Exception {
+ BDBRepositoryBuilder builder = new BDBRepositoryBuilder();
+ builder.setName("Simple file system");
+ builder.setEnvironmentHome(envHome);
+ builder.setTransactionNoSync(true);
+ //builder.setProduct("DB");
+
+ Repository repository = builder.build();
+ return repository.storageFor(FileInfo.class);
+ }
+
+ /**
+ * @param args [0] - BDB environment home, [1] - optional directory to read
+ * files from for populating the simple file system
+ */
+ public static void main(String[] args) throws Exception {
+ Storage<FileInfo> storage = openStorage(args[0]);
+ if (args.length > 1) {
+ populate(storage, new File(args[1]), null);
+ }
+ }
+
+ private static void populate(Storage<FileInfo> storage, File file, FileInfo parent)
+ throws Exception
+ {
+ FileInfo info = storage.prepare();
+ if (parent != null) {
+ info.setParentID(parent.getID());
+ }
+ info.setName(file.getName());
+ info.setLastModified(new DateTime(file.lastModified()));
+
+ if (file.isFile()) {
+ info.setDirectory(false);
+ info.setLength(file.length());
+ info.setFileData(new FileBlob(file));
+ info.insert();
+ } else if (file.isDirectory()) {
+ info.setDirectory(true);
+ info.setLength(-1);
+ info.insert();
+ File[] children = file.listFiles();
+ if (children != null) {
+ for (int i=0; i<children.length; i++) {
+ populate(storage, children[i], info);
+ }
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/amazon/carbonado/spi/TestConversionComparator.java b/src/test/java/com/amazon/carbonado/spi/TestConversionComparator.java new file mode 100644 index 0000000..5cd7caa --- /dev/null +++ b/src/test/java/com/amazon/carbonado/spi/TestConversionComparator.java @@ -0,0 +1,179 @@ +/*
+ * 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 junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.amazon.carbonado.spi.ConversionComparator;
+
+/**
+ * Test cases for {@link ConversionComparator}.
+ *
+ * @author Brian S O'Neill
+ */
+public class TestConversionComparator extends TestCase {
+ private static final int BOOLEAN_CODE = 0;
+ private static final int BYTE_CODE = 1;
+ private static final int SHORT_CODE = 2;
+ private static final int CHAR_CODE = 3;
+ private static final int INT_CODE = 4;
+ private static final int FLOAT_CODE = 5;
+ private static final int LONG_CODE = 6;
+ private static final int DOUBLE_CODE = 7;
+
+ private static final Class[] PRIMITIVE_CLASSES = {
+ boolean.class, byte.class, short.class, char.class,
+ int.class, float.class, long.class, double.class
+ };
+
+ private static final Class[] BOXED_PRIMITIVE_CLASSES = {
+ Boolean.class, Byte.class, Short.class, Character.class,
+ Integer.class, Float.class, Long.class, Double.class
+ };
+
+ // States which primitive conversions are allowed.
+ private static final boolean[][] PRIMITIVE_MATRIX = {
+ // from...
+ // boolean byte short char int float long double
+ { true, false, false, false, false, false, false, false }, // to boolean
+ { false, true, false, false, false, false, false, false }, // to byte
+ { false, true, true, false, false, false, false, false }, // to short
+ { false, false, false, true, false, false, false, false }, // to char
+ { false, true, true, false, true, false, false, false }, // to int
+ { false, true, true, false, false, true, false, false }, // to float
+ { false, true, true, false, true, false, true, false }, // to long
+ { false, true, true, false, true, true, false, true }, // to double
+ };
+
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static TestSuite suite() {
+ return new TestSuite(TestConversionComparator.class);
+ }
+
+ public TestConversionComparator(String name) {
+ super(name);
+ }
+
+ public void test_isConversionPossible_basics() {
+ ConversionComparator cc = new ConversionComparator(Object.class);
+ assertEquals(true, cc.isConversionPossible(Object.class));
+ assertEquals(false, cc.isConversionPossible(String.class));
+ assertEquals(false, cc.isConversionPossible(boolean.class));
+ assertEquals(false, cc.isConversionPossible(Integer.class));
+ assertEquals(false, cc.isConversionPossible(int.class));
+
+ cc = new ConversionComparator(String.class);
+ assertEquals(true, cc.isConversionPossible(Object.class));
+ assertEquals(true, cc.isConversionPossible(String.class));
+ assertEquals(false, cc.isConversionPossible(boolean.class));
+ assertEquals(false, cc.isConversionPossible(Integer.class));
+ assertEquals(false, cc.isConversionPossible(int.class));
+
+ cc = new ConversionComparator(boolean.class);
+ assertEquals(true, cc.isConversionPossible(Object.class));
+ assertEquals(false, cc.isConversionPossible(String.class));
+ assertEquals(true, cc.isConversionPossible(boolean.class));
+ assertEquals(false, cc.isConversionPossible(Integer.class));
+ assertEquals(false, cc.isConversionPossible(int.class));
+
+ cc = new ConversionComparator(Integer.class);
+ assertEquals(true, cc.isConversionPossible(Object.class));
+ assertEquals(false, cc.isConversionPossible(String.class));
+ assertEquals(false, cc.isConversionPossible(boolean.class));
+ assertEquals(true, cc.isConversionPossible(Integer.class));
+ assertEquals(true, cc.isConversionPossible(int.class));
+
+ cc = new ConversionComparator(int.class);
+ assertEquals(true, cc.isConversionPossible(Object.class));
+ assertEquals(false, cc.isConversionPossible(String.class));
+ assertEquals(false, cc.isConversionPossible(boolean.class));
+ assertEquals(true, cc.isConversionPossible(Integer.class));
+ assertEquals(true, cc.isConversionPossible(int.class));
+ }
+
+ public void test_isConversionPossible_primitives() {
+ test_isConversionPossible_primitives(false, false);
+ test_isConversionPossible_primitives(false, true);
+ test_isConversionPossible_primitives(true, false);
+ test_isConversionPossible_primitives(true, true);
+ }
+
+ private void test_isConversionPossible_primitives(boolean fromBoxed, boolean toBoxed) {
+ for (int fromCode = BOOLEAN_CODE; fromCode <= DOUBLE_CODE; fromCode++) {
+ ConversionComparator cc = new ConversionComparator
+ (fromBoxed ? BOXED_PRIMITIVE_CLASSES[fromCode] : PRIMITIVE_CLASSES[fromCode]);
+ for (int toCode = BOOLEAN_CODE; toCode <= DOUBLE_CODE; toCode++) {
+ boolean expected = PRIMITIVE_MATRIX[toCode][fromCode];
+ Class toType = toBoxed ? BOXED_PRIMITIVE_CLASSES[toCode] : PRIMITIVE_CLASSES[toCode];
+ assertEquals(expected, cc.isConversionPossible(toType));
+ }
+ }
+ }
+
+ public void test_compare() {
+ ConversionComparator cc = new ConversionComparator(Object.class);
+ assertEquals(true, cc.compare(Object.class, String.class) < 0);
+ assertEquals(true, cc.compare(String.class, Object.class) > 0);
+ assertEquals(0, cc.compare(Object.class, Object.class));
+ assertEquals(0, cc.compare(String.class, String.class));
+ assertEquals(0, cc.compare(String.class, String.class));
+ assertEquals(0, cc.compare(int.class, Number.class));
+
+ cc = new ConversionComparator(String.class);
+ assertEquals(true, cc.compare(String.class, Object.class) < 0);
+ assertEquals(true, cc.compare(Object.class, String.class) > 0);
+ assertEquals(0, cc.compare(String.class, String.class));
+ assertEquals(true, cc.compare(int.class, String.class) > 0);
+
+ cc = new ConversionComparator(Integer.class);
+ assertEquals(true, cc.compare(String.class, Object.class) > 0);
+ assertEquals(true, cc.compare(Object.class, String.class) < 0);
+ assertEquals(true, cc.compare(Object.class, Number.class) > 0);
+ assertEquals(true, cc.compare(Integer.class, Number.class) < 0);
+ assertEquals(true, cc.compare(int.class, Number.class) > 0);
+ assertEquals(true, cc.compare(long.class, Number.class) > 0);
+ assertEquals(true, cc.compare(long.class, Long.class) < 0);
+
+ cc = new ConversionComparator(int.class);
+ assertEquals(true, cc.compare(String.class, Object.class) > 0);
+ assertEquals(true, cc.compare(Object.class, String.class) < 0);
+ assertEquals(true, cc.compare(Object.class, Number.class) > 0);
+ assertEquals(true, cc.compare(Integer.class, Number.class) < 0);
+ assertEquals(true, cc.compare(int.class, Number.class) < 0);
+ assertEquals(true, cc.compare(long.class, Number.class) < 0);
+ assertEquals(true, cc.compare(long.class, Long.class) < 0);
+
+ cc = new ConversionComparator(Byte.class);
+ assertEquals(true, cc.compare(int.class, Number.class) > 0);
+ assertEquals(true, cc.compare(long.class, Number.class) > 0);
+ assertEquals(true, cc.compare(long.class, Integer.class) < 0);
+
+ cc = new ConversionComparator(byte.class);
+ assertEquals(true, cc.compare(short.class, int.class) < 0);
+ assertEquals(true, cc.compare(long.class, int.class) > 0);
+
+ cc = new ConversionComparator(java.util.Date.class);
+ assertEquals(true, cc.compare(Object.class, Comparable.class) > 0);
+ assertEquals(0, cc.compare(java.io.Serializable.class, Comparable.class));
+ }
+}
diff --git a/src/test/java/com/amazon/carbonado/spi/TestCursorList.java b/src/test/java/com/amazon/carbonado/spi/TestCursorList.java new file mode 100644 index 0000000..605e981 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/spi/TestCursorList.java @@ -0,0 +1,224 @@ +/*
+ * 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 com.amazon.carbonado.Cursor;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.amazon.carbonado.cursor.EmptyCursorFactory;
+
+/**
+ * Test case for TransactionManager.CursorList.
+ *
+ * @author Brian S O'Neill
+ */
+public class TestCursorList extends TestCase {
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static TestSuite suite() {
+ return new TestSuite(TestCursorList.class);
+ }
+
+ TransactionManager.CursorList mList;
+
+ public TestCursorList(String name) {
+ super(name);
+ }
+
+ protected void setUp() {
+ mList = new TransactionManager.CursorList();
+ }
+
+ public void testRegisterFew() {
+ assertEquals(0, mList.size());
+
+ {
+ Cursor cursor = EmptyCursorFactory.newEmptyCursor();
+ mList.register(cursor, null);
+ assertEquals(1, mList.size());
+ assertEquals(cursor, mList.getCursor(0));
+ assertEquals(null, mList.getValue(0));
+ Object value = mList.unregister(cursor);
+ assertEquals(0, mList.size());
+ assertEquals(null, value);
+ }
+
+ {
+ Cursor cursor_1 = EmptyCursorFactory.newEmptyCursor();
+ Cursor cursor_2 = EmptyCursorFactory.newEmptyCursor();
+ mList.register(cursor_1, null);
+ assertEquals(1, mList.size());
+ mList.register(cursor_2, null);
+ assertEquals(2, mList.size());
+ assertEquals(cursor_1, mList.getCursor(0));
+ assertEquals(cursor_2, mList.getCursor(1));
+ assertEquals(null, mList.getValue(0));
+ assertEquals(null, mList.getValue(1));
+
+ Object value = mList.unregister(cursor_2);
+ assertEquals(1, mList.size());
+ assertEquals(cursor_1, mList.getCursor(0));
+ assertEquals(null, value);
+ mList.unregister(cursor_2);
+ assertEquals(1, mList.size());
+ mList.unregister(cursor_1);
+ assertEquals(0, mList.size());
+ }
+
+ // unregister in reverse
+ {
+ Cursor cursor_1 = EmptyCursorFactory.newEmptyCursor();
+ Cursor cursor_2 = EmptyCursorFactory.newEmptyCursor();
+ mList.register(cursor_1, null);
+ mList.register(cursor_2, null);
+
+ mList.unregister(cursor_1);
+ assertEquals(1, mList.size());
+ assertEquals(cursor_2, mList.getCursor(0));
+
+ mList.unregister(cursor_1);
+ assertEquals(1, mList.size());
+ mList.unregister(cursor_2);
+ assertEquals(0, mList.size());
+ }
+ }
+
+ public void testRegisterFewValue() {
+ Cursor cursor_1 = EmptyCursorFactory.newEmptyCursor();
+ Cursor cursor_2 = EmptyCursorFactory.newEmptyCursor();
+ String value_1 = "1";
+ String value_2 = "2";
+
+ mList.register(cursor_1, value_1);
+ assertEquals(1, mList.size());
+ assertEquals(cursor_1, mList.getCursor(0));
+ assertEquals(value_1, mList.getValue(0));
+
+ mList.register(cursor_2, value_2);
+ assertEquals(2, mList.size());
+ assertEquals(cursor_1, mList.getCursor(0));
+ assertEquals(value_1, mList.getValue(0));
+ assertEquals(cursor_2, mList.getCursor(1));
+ assertEquals(value_2, mList.getValue(1));
+
+ Object value = mList.unregister(cursor_2);
+ assertEquals(1, mList.size());
+ assertEquals(cursor_1, mList.getCursor(0));
+ assertEquals(value_1, mList.getValue(0));
+ assertEquals(value_2, value);
+
+ value = mList.unregister(cursor_2);
+ assertEquals(1, mList.size());
+ assertEquals(null, value);
+ value = mList.unregister(cursor_1);
+ assertEquals(0, mList.size());
+ assertEquals(value_1, value);
+ }
+
+ // Tests that the array expands properly.
+ public void testRegisterMany() {
+ final int count = 50;
+ Cursor[] cursors = new Cursor[count];
+ for (int i=0; i<count; i++) {
+ cursors[i] = EmptyCursorFactory.newEmptyCursor();
+ mList.register(cursors[i], null);
+ assertEquals(i + 1, mList.size());
+ }
+
+ for (int i=0; i<count; i++) {
+ assertEquals(cursors[i], mList.getCursor(i));
+ assertEquals(null, mList.getValue(i));
+ }
+
+ for (int i=0; i<count; i++) {
+ mList.unregister(cursors[i]);
+ assertEquals(count - i - 1, mList.size());
+ }
+ }
+
+ // Tests that the arrays expand properly and store values.
+ public void testRegisterManyValues() {
+ final int count = 50;
+ Cursor[] cursors = new Cursor[count];
+ Integer[] values = new Integer[count];
+ for (int i=0; i<count; i++) {
+ cursors[i] = EmptyCursorFactory.newEmptyCursor();
+ values[i] = i;
+ mList.register(cursors[i], values[i]);
+ assertEquals(i + 1, mList.size());
+ }
+
+ for (int i=0; i<count; i++) {
+ assertEquals(cursors[i], mList.getCursor(i));
+ assertEquals(values[i], mList.getValue(i));
+ }
+
+ for (int i=0; i<count; i++) {
+ Object value = mList.unregister(cursors[i]);
+ assertEquals(count - i - 1, mList.size());
+ assertEquals(values[i], value);
+ }
+ }
+
+ public void testCloseCursors() throws Exception {
+ final int count = 50;
+
+ // Without values
+ {
+ Cursor[] cursors = new Cursor[count];
+ for (int i=0; i<count; i++) {
+ cursors[i] = EmptyCursorFactory.newEmptyCursor();
+ mList.register(cursors[i], null);
+ }
+
+ mList.closeCursors();
+ assertEquals(0, mList.size());
+
+ /*
+ for (int i=0; i<count; i++) {
+ assertEquals(true, cursors[i].isClosed());
+ }
+ */
+ }
+
+ // With values
+ {
+ Cursor[] cursors = new Cursor[count];
+ Integer[] values = new Integer[count];
+ for (int i=0; i<count; i++) {
+ cursors[i] = EmptyCursorFactory.newEmptyCursor();
+ values[i] = i;
+ mList.register(cursors[i], values[i]);
+ }
+
+ mList.closeCursors();
+ assertEquals(0, mList.size());
+
+ /*
+ for (int i=0; i<count; i++) {
+ assertEquals(true, cursors[i].isClosed());
+ }
+ */
+ }
+ }
+}
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/spi/TestStorableSerializer.java b/src/test/java/com/amazon/carbonado/spi/TestStorableSerializer.java new file mode 100644 index 0000000..664bf12 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/spi/TestStorableSerializer.java @@ -0,0 +1,115 @@ +/*
+ * 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.io.*;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.amazon.carbonado.*;
+import com.amazon.carbonado.lob.*;
+
+import com.amazon.carbonado.repo.toy.ToyRepository;
+import com.amazon.carbonado.stored.*;
+
+/**
+ * Test case for {@link StorableSerializer}.
+ *
+ * @author Brian S O'Neill
+ */
+public class TestStorableSerializer extends TestCase {
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static TestSuite suite() {
+ return new TestSuite(TestStorableSerializer.class);
+ }
+
+ private Repository mRepository;
+
+ public TestStorableSerializer(String name) {
+ super(name);
+ }
+
+ protected void setUp() {
+ mRepository = new ToyRepository();
+ }
+
+ protected void tearDown() {
+ mRepository.close();
+ mRepository = null;
+ }
+
+ public void testReadAndWrite() throws Exception {
+ Storage<StorableTestBasic> storage = mRepository.storageFor(StorableTestBasic.class);
+ StorableTestBasic stb = storage.prepare();
+ stb.setId(50);
+ stb.setStringProp("hello");
+ stb.setIntProp(100);
+ stb.setLongProp(999);
+ stb.setDoubleProp(2.718281828d);
+
+ StorableSerializer<StorableTestBasic> serializer =
+ StorableSerializer.forType(StorableTestBasic.class);
+
+ ByteArrayOutputStream bout = new ByteArrayOutputStream();
+ DataOutputStream dout = new DataOutputStream(bout);
+
+ serializer.write(stb, (DataOutput) dout);
+ dout.flush();
+
+ byte[] bytes = bout.toByteArray();
+
+ ByteArrayInputStream bin = new ByteArrayInputStream(bytes);
+ DataInputStream din = new DataInputStream(bin);
+
+ StorableTestBasic stb2 = serializer.read(storage, (DataInput) din);
+
+ assertEquals(stb, stb2);
+ }
+
+ /*
+ public void testReadAndWriteLobs() throws Exception {
+ Storage<StorableWithLobs> storage = mRepository.storageFor(StorableWithLobs.class);
+ StorableWithLobs s = storage.prepare();
+ s.setBlobValue(new ByteArrayBlob("Hello Blob".getBytes()));
+ s.setClobValue(new StringClob("Hello Clob"));
+
+ StorableSerializer<StorableWithLobs> serializer =
+ StorableSerializer.forType(StorableWithLobs.class);
+
+ ByteArrayOutputStream bout = new ByteArrayOutputStream();
+ DataOutputStream dout = new DataOutputStream(bout);
+
+ serializer.write(s, (DataOutput) dout);
+ dout.flush();
+
+ byte[] bytes = bout.toByteArray();
+
+ ByteArrayInputStream bin = new ByteArrayInputStream(bytes);
+ DataInputStream din = new DataInputStream(bin);
+
+ StorableWithLobs s2 = serializer.read(storage, (DataInput) din);
+
+ assertEquals(s, s2);
+ }
+ */
+}
diff --git a/src/test/java/com/amazon/carbonado/spi/TestTriggerManager.java b/src/test/java/com/amazon/carbonado/spi/TestTriggerManager.java new file mode 100644 index 0000000..cad4f0f --- /dev/null +++ b/src/test/java/com/amazon/carbonado/spi/TestTriggerManager.java @@ -0,0 +1,334 @@ +/*
+ * 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.ArrayList;
+import java.util.List;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.amazon.carbonado.Storable;
+import com.amazon.carbonado.Trigger;
+
+import com.amazon.carbonado.stored.Dummy;
+
+/**
+ * Tests for TriggerManager.
+ *
+ * @author Brian S O'Neill
+ */
+public class TestTriggerManager extends TestCase {
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static TestSuite suite() {
+ return new TestSuite(TestTriggerManager.class);
+ }
+
+ public TestTriggerManager(String name) {
+ super(name);
+ }
+
+ @Override
+ protected void setUp() {
+ beforeTriggers = new ArrayList<TestTrigger>();
+ afterTriggers = new ArrayList<TestTrigger>();
+ failedTriggers = new ArrayList<TestTrigger>();
+ }
+
+ @Override
+ protected void tearDown() {
+ }
+
+ List<TestTrigger> beforeTriggers;
+ List<TestTrigger> afterTriggers;
+ List<TestTrigger> failedTriggers;
+
+ public void testAddAndRemove() {
+ TriggerManager<Dummy> set = new TriggerManager<Dummy>(null, null);
+ Trigger<Dummy> trigger = new TestTrigger<Dummy>();
+
+ assertNull(set.getInsertTrigger());
+ assertNull(set.getUpdateTrigger());
+ assertNull(set.getDeleteTrigger());
+
+ assertTrue(set.addTrigger(trigger));
+ assertNotNull(set.getInsertTrigger());
+ assertNotNull(set.getUpdateTrigger());
+ assertNotNull(set.getDeleteTrigger());
+
+ assertFalse(set.addTrigger(trigger));
+ assertNotNull(set.getInsertTrigger());
+ assertNotNull(set.getUpdateTrigger());
+ assertNotNull(set.getDeleteTrigger());
+
+ assertTrue(set.removeTrigger(trigger));
+ assertNull(set.getInsertTrigger());
+ assertNull(set.getUpdateTrigger());
+ assertNull(set.getDeleteTrigger());
+
+ assertFalse(set.removeTrigger(trigger));
+ assertNull(set.getInsertTrigger());
+ assertNull(set.getUpdateTrigger());
+ assertNull(set.getDeleteTrigger());
+
+ Trigger<Dummy> trigger2 = new TestTrigger<Dummy>();
+ assertTrue(set.addTrigger(trigger));
+ assertTrue(set.addTrigger(trigger2));
+ assertNotNull(set.getInsertTrigger());
+ assertNotNull(set.getUpdateTrigger());
+ assertNotNull(set.getDeleteTrigger());
+
+ assertTrue(set.removeTrigger(trigger));
+ assertNotNull(set.getInsertTrigger());
+ assertNotNull(set.getUpdateTrigger());
+ assertNotNull(set.getDeleteTrigger());
+ assertTrue(set.removeTrigger(trigger2));
+ assertNull(set.getInsertTrigger());
+ assertNull(set.getUpdateTrigger());
+ assertNull(set.getDeleteTrigger());
+ }
+
+ public void testBeforeAndAfterOps() throws Exception {
+ TriggerManager<Dummy> set = new TriggerManager<Dummy>(null, null);
+ TestTrigger<Dummy> trigger = new TestTrigger<Dummy>();
+ set.addTrigger(trigger);
+ Dummy d = new Dummy();
+
+ Object state = set.getInsertTrigger().beforeInsert(d);
+ assertEquals(1, trigger.beforeInsertCount);
+ assertEquals(0, trigger.beforeUpdateCount);
+ assertEquals(0, trigger.beforeDeleteCount);
+
+ set.getInsertTrigger().afterInsert(d, state);
+ assertEquals(1, trigger.afterInsertCount);
+ assertEquals(0, trigger.afterUpdateCount);
+ assertEquals(0, trigger.afterDeleteCount);
+
+ state = set.getUpdateTrigger().beforeUpdate(d);
+ assertEquals(1, trigger.beforeUpdateCount);
+ assertEquals(0, trigger.beforeDeleteCount);
+
+ set.getUpdateTrigger().afterUpdate(d, state);
+ assertEquals(1, trigger.afterUpdateCount);
+ assertEquals(0, trigger.afterDeleteCount);
+
+ state = set.getDeleteTrigger().beforeDelete(d);
+ assertEquals(1, trigger.beforeDeleteCount);
+
+ set.getDeleteTrigger().afterDelete(d, state);
+ assertEquals(1, trigger.afterDeleteCount);
+ }
+
+ public void testBeforeAndFailedOps() throws Exception {
+ TriggerManager<Dummy> set = new TriggerManager<Dummy>(null, null);
+ TestTrigger<Dummy> trigger = new TestTrigger<Dummy>();
+ set.addTrigger(trigger);
+ Dummy d = new Dummy();
+
+ Object state = set.getInsertTrigger().beforeInsert(d);
+ assertEquals(1, trigger.beforeInsertCount);
+ assertEquals(0, trigger.beforeUpdateCount);
+ assertEquals(0, trigger.beforeDeleteCount);
+
+ set.getInsertTrigger().failedInsert(d, state);
+ assertEquals(1, trigger.failedInsertCount);
+ assertEquals(0, trigger.failedUpdateCount);
+ assertEquals(0, trigger.failedDeleteCount);
+
+ state = set.getUpdateTrigger().beforeUpdate(d);
+ assertEquals(1, trigger.beforeUpdateCount);
+ assertEquals(0, trigger.beforeDeleteCount);
+
+ set.getUpdateTrigger().failedUpdate(d, state);
+ assertEquals(1, trigger.failedUpdateCount);
+ assertEquals(0, trigger.failedDeleteCount);
+
+ state = set.getDeleteTrigger().beforeDelete(d);
+ assertEquals(1, trigger.beforeDeleteCount);
+
+ set.getDeleteTrigger().failedDelete(d, state);
+ assertEquals(1, trigger.failedDeleteCount);
+ }
+
+ public void testExecutionOrder() throws Exception {
+ TriggerManager<Dummy> set = new TriggerManager<Dummy>(null, null);
+ TestTrigger<Dummy> trigger = new TestTrigger<Dummy>(null);
+ TestTrigger<Dummy> trigger2 = new TestTrigger<Dummy>();
+ set.addTrigger(trigger);
+ set.addTrigger(trigger2);
+ Dummy d = new Dummy();
+
+ // Insert
+ {
+ Object state = set.getInsertTrigger().beforeInsert(d);
+ assertEquals(2, beforeTriggers.size());
+ assertEquals(trigger2, beforeTriggers.get(0));
+ assertEquals(trigger, beforeTriggers.get(1));
+
+ set.getInsertTrigger().afterInsert(d, state);
+ assertEquals(2, afterTriggers.size());
+ assertEquals(trigger, afterTriggers.get(0));
+ assertEquals(trigger2, afterTriggers.get(1));
+
+ state = set.getInsertTrigger().beforeInsert(d);
+ set.getInsertTrigger().failedInsert(d, state);
+ assertEquals(2, failedTriggers.size());
+ assertEquals(trigger, failedTriggers.get(0));
+ assertEquals(trigger2, failedTriggers.get(1));
+ }
+
+ beforeTriggers.clear();
+ afterTriggers.clear();
+ failedTriggers.clear();
+
+ // Update
+ {
+ Object state = set.getUpdateTrigger().beforeUpdate(d);
+ assertEquals(2, beforeTriggers.size());
+ assertEquals(trigger2, beforeTriggers.get(0));
+ assertEquals(trigger, beforeTriggers.get(1));
+
+ set.getUpdateTrigger().afterUpdate(d, state);
+ assertEquals(2, afterTriggers.size());
+ assertEquals(trigger, afterTriggers.get(0));
+ assertEquals(trigger2, afterTriggers.get(1));
+
+ state = set.getUpdateTrigger().beforeUpdate(d);
+ set.getUpdateTrigger().failedUpdate(d, state);
+ assertEquals(2, failedTriggers.size());
+ assertEquals(trigger, failedTriggers.get(0));
+ assertEquals(trigger2, failedTriggers.get(1));
+ }
+
+ beforeTriggers.clear();
+ afterTriggers.clear();
+ failedTriggers.clear();
+
+ // Delete
+ {
+ Object state = set.getDeleteTrigger().beforeDelete(d);
+ assertEquals(2, beforeTriggers.size());
+ assertEquals(trigger2, beforeTriggers.get(0));
+ assertEquals(trigger, beforeTriggers.get(1));
+
+ set.getDeleteTrigger().afterDelete(d, state);
+ assertEquals(2, afterTriggers.size());
+ assertEquals(trigger, afterTriggers.get(0));
+ assertEquals(trigger2, afterTriggers.get(1));
+
+ state = set.getDeleteTrigger().beforeDelete(d);
+ set.getDeleteTrigger().failedDelete(d, state);
+ assertEquals(2, failedTriggers.size());
+ assertEquals(trigger, failedTriggers.get(0));
+ assertEquals(trigger2, failedTriggers.get(1));
+ }
+ }
+
+ class TestTrigger<S extends Storable> extends Trigger<S> {
+ final Object stateObj;
+
+ int beforeInsertCount;
+ int afterInsertCount;
+ int failedInsertCount;
+
+ int beforeUpdateCount;
+ int afterUpdateCount;
+ int failedUpdateCount;
+
+ int beforeDeleteCount;
+ int afterDeleteCount;
+ int failedDeleteCount;
+
+ TestTrigger() {
+ this.stateObj = new Object();
+ }
+
+ TestTrigger(Object stateObj) {
+ this.stateObj = stateObj;
+ }
+
+ @Override
+ public Object beforeInsert(S storable) {
+ beforeInsertCount++;
+ beforeTriggers.add(this);
+ return stateObj;
+ }
+
+ @Override
+ public void afterInsert(S storable, Object state) {
+ Assert.assertEquals(stateObj, state);
+ afterTriggers.add(this);
+ afterInsertCount++;
+ }
+
+ @Override
+ public void failedInsert(S storable, Object state) {
+ Assert.assertEquals(stateObj, state);
+ failedTriggers.add(this);
+ failedInsertCount++;
+ }
+
+ @Override
+ public Object beforeUpdate(S storable) {
+ beforeUpdateCount++;
+ beforeTriggers.add(this);
+ return stateObj;
+ }
+
+ @Override
+ public void afterUpdate(S storable, Object state) {
+ Assert.assertEquals(stateObj, state);
+ afterTriggers.add(this);
+ afterUpdateCount++;
+ }
+
+ @Override
+ public void failedUpdate(S storable, Object state) {
+ Assert.assertEquals(stateObj, state);
+ failedTriggers.add(this);
+ failedUpdateCount++;
+ }
+
+ @Override
+ public Object beforeDelete(S storable) {
+ beforeDeleteCount++;
+ beforeTriggers.add(this);
+ return stateObj;
+ }
+
+ @Override
+ public void afterDelete(S storable, Object state) {
+ Assert.assertEquals(stateObj, state);
+ afterTriggers.add(this);
+ afterDeleteCount++;
+ }
+
+ @Override
+ public void failedDelete(S storable, Object state) {
+ Assert.assertEquals(stateObj, state);
+ failedTriggers.add(this);
+ failedDeleteCount++;
+ }
+
+ }
+}
diff --git a/src/test/java/com/amazon/carbonado/stored/Address.java b/src/test/java/com/amazon/carbonado/stored/Address.java new file mode 100644 index 0000000..3043e54 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/stored/Address.java @@ -0,0 +1,64 @@ +/*
+ * 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.Alias;
+import com.amazon.carbonado.Independent;
+import com.amazon.carbonado.Storable;
+import com.amazon.carbonado.Nullable;
+import com.amazon.carbonado.PrimaryKey;
+import com.amazon.carbonado.Sequence;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+@Alias("TEST_ADDRESS")
+@PrimaryKey("addressID")
+public interface Address extends Storable {
+ @Sequence("TEST_ADDRESS_ID_SEQ")
+ long getAddressID();
+ void setAddressID(long id);
+
+ String getAddressLine1();
+ void setAddressLine1(String value);
+
+ @Nullable
+ String getAddressLine2();
+ void setAddressLine2(String value);
+
+ String getAddressCity();
+ void setAddressCity(String value);
+
+ @Nullable
+ String getAddressState();
+ void setAddressState(String value);
+
+ String getAddressZip();
+ void setAddressZip(String value);
+
+ String getAddressCountry();
+ void setAddressCountry(String value);
+
+ @Independent
+ String getCustomData();
+ void setCustomData(String str);
+}
+
diff --git a/src/test/java/com/amazon/carbonado/stored/Dummy.java b/src/test/java/com/amazon/carbonado/stored/Dummy.java new file mode 100644 index 0000000..980c718 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/stored/Dummy.java @@ -0,0 +1,145 @@ +/*
+ * 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.*;
+
+/**
+ * Implements all the Storable methods, but each throws
+ * UnsupportedOperationException. Methods defined in Object are left alone.
+ *
+ * @author Brian S O'Neill
+ */
+public class Dummy implements Storable {
+ public void load() throws FetchException {
+ throw error();
+ }
+
+ public boolean tryLoad() throws FetchException {
+ throw error();
+ }
+
+ public void insert() throws PersistException {
+ throw error();
+ }
+
+ public boolean tryInsert() throws PersistException {
+ throw error();
+ }
+
+ public void update() throws PersistException {
+ throw error();
+ }
+
+ public boolean tryUpdate() throws PersistException {
+ throw error();
+ }
+
+ public void delete() throws PersistException {
+ throw error();
+ }
+
+ public boolean tryDelete() throws PersistException {
+ throw error();
+ }
+
+ public Storage storage() {
+ throw error();
+ }
+
+ public Class storableType() {
+ throw error();
+ }
+
+ public void copyAllProperties(Storable target) {
+ throw error();
+ }
+
+ public void copyPrimaryKeyProperties(Storable target) {
+ throw error();
+ }
+
+ public void copyVersionProperty(Storable target) {
+ throw error();
+ }
+
+ public void copyUnequalProperties(Storable target) {
+ throw error();
+ }
+
+ public void copyDirtyProperties(Storable target) {
+ throw error();
+ }
+
+ public boolean hasDirtyProperties() {
+ throw error();
+ }
+
+ public void markPropertiesClean() {
+ throw error();
+ }
+
+ public void markAllPropertiesClean() {
+ throw error();
+ }
+
+ public void markPropertiesDirty() {
+ throw error();
+ }
+
+ public void markAllPropertiesDirty() {
+ throw error();
+ }
+
+ public boolean isPropertyUninitialized(String propertyName) {
+ throw error();
+ }
+
+ public boolean isPropertyDirty(String propertyName) {
+ throw error();
+ }
+
+ public boolean isPropertyClean(String propertyName) {
+ throw error();
+ }
+
+ public boolean isPropertySupported(String propertyName) {
+ throw error();
+ }
+
+ public Storable copy() {
+ throw error();
+ }
+
+ public boolean equalPrimaryKeys(Object obj) {
+ throw error();
+ }
+
+ public boolean equalProperties(Object obj) {
+ throw error();
+ }
+
+ public String toStringKeyOnly() {
+ throw error();
+ }
+
+ protected UnsupportedOperationException error() {
+ return new UnsupportedOperationException();
+ }
+}
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..c3245e3 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/stored/FileInfo.java @@ -0,0 +1,100 @@ +/*
+ * 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/Order.java b/src/test/java/com/amazon/carbonado/stored/Order.java new file mode 100644 index 0000000..91804d9 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/stored/Order.java @@ -0,0 +1,68 @@ +/*
+ * 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.Alias;
+import com.amazon.carbonado.Storable;
+import com.amazon.carbonado.FetchException;
+import com.amazon.carbonado.Join;
+import com.amazon.carbonado.Nullable;
+import com.amazon.carbonado.PrimaryKey;
+import com.amazon.carbonado.Query;
+import com.amazon.carbonado.Sequence;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+@Alias("TEST_ORDER")
+@PrimaryKey("orderID")
+public interface Order extends Storable<Order> {
+ @Sequence("TEST_ORDER_ID_SEQ")
+ long getOrderID();
+ void setOrderID(long id);
+
+ String getOrderNumber();
+ void setOrderNumber(String value);
+
+ int getOrderTotal();
+ void setOrderTotal(int value);
+
+ @Nullable
+ String getOrderComments();
+ void setOrderComments(String value);
+
+ long getAddressID();
+ void setAddressID(long value);
+
+ @Join
+ @Nullable
+ Address getAddress() throws FetchException;
+ void setAddress(Address value);
+
+ @Join
+ Query<OrderItem> getOrderItems() throws FetchException;
+
+ @Join
+ Query<Shipment> getShipments() throws FetchException;
+
+ @Join
+ Query<Promotion> getPromotions() throws FetchException;
+}
diff --git a/src/test/java/com/amazon/carbonado/stored/OrderItem.java b/src/test/java/com/amazon/carbonado/stored/OrderItem.java new file mode 100644 index 0000000..dba2477 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/stored/OrderItem.java @@ -0,0 +1,68 @@ +/*
+ * 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.Alias;
+import com.amazon.carbonado.Storable;
+import com.amazon.carbonado.FetchException;
+import com.amazon.carbonado.Index;
+import com.amazon.carbonado.Indexes;
+import com.amazon.carbonado.Join;
+import com.amazon.carbonado.PrimaryKey;
+import com.amazon.carbonado.Sequence;
+import com.amazon.carbonado.constraint.IntegerConstraint;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+@Alias("TEST_ORDER_ITEM")
+@Indexes({@Index("+orderID"), @Index("+shipmentID")})
+@PrimaryKey("orderItemID")
+public interface OrderItem extends Storable<OrderItem> {
+ @Sequence("TEST_ORDER_ITEM_ID_SEQ")
+ long getOrderItemID();
+ void setOrderItemID(long id);
+
+ long getOrderID();
+ void setOrderID(long value);
+
+ @Join
+ Order getOrder() throws FetchException;
+ void setOrder(Order value);
+
+ String getItemDescription();
+ void setItemDescription(String value);
+
+ int getItemQuantity();
+ @IntegerConstraint(min=1, max=100)
+ void setItemQuantity(int value);
+
+ int getItemPrice();
+ void setItemPrice(int value);
+
+ long getShipmentID();
+ void setShipmentID(long value);
+
+ @Join
+ Shipment getShipment() throws FetchException;
+ void setShipment(Shipment value);
+}
+
diff --git a/src/test/java/com/amazon/carbonado/stored/Promotion.java b/src/test/java/com/amazon/carbonado/stored/Promotion.java new file mode 100644 index 0000000..18f4a2e --- /dev/null +++ b/src/test/java/com/amazon/carbonado/stored/Promotion.java @@ -0,0 +1,55 @@ +/*
+ * 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.Alias;
+import com.amazon.carbonado.Storable;
+import com.amazon.carbonado.FetchException;
+import com.amazon.carbonado.Join;
+import com.amazon.carbonado.PrimaryKey;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+@Alias("TEST_PROMOTION")
+@PrimaryKey({"orderID", "promotionID"})
+public interface Promotion extends Storable<Promotion> {
+ long getOrderID();
+ void setOrderID(long value);
+
+ String getPromotionID();
+ void setPromotionID(String value);
+
+ @Join
+ Order getOrder() throws FetchException;
+ void setOrder(Order value);
+
+ @Join
+ Promotion getPromotion() throws FetchException;
+ void setPromotion(Promotion value);
+
+ String getPromotionTitle();
+ void setPromotionTitle(String value);
+
+ String getPromotionDetails();
+ void setPromotionDetails(String value);
+}
+
diff --git a/src/test/java/com/amazon/carbonado/stored/Shipment.java b/src/test/java/com/amazon/carbonado/stored/Shipment.java new file mode 100644 index 0000000..670806d --- /dev/null +++ b/src/test/java/com/amazon/carbonado/stored/Shipment.java @@ -0,0 +1,69 @@ +/*
+ * 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.Alias;
+import com.amazon.carbonado.Storable;
+import com.amazon.carbonado.FetchException;
+import com.amazon.carbonado.Index;
+import com.amazon.carbonado.Indexes;
+import com.amazon.carbonado.Join;
+import com.amazon.carbonado.PrimaryKey;
+import com.amazon.carbonado.Query;
+import com.amazon.carbonado.Sequence;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+@Alias("TEST_SHIPMENT")
+@Indexes(@Index("+orderID"))
+@PrimaryKey("shipmentID")
+public interface Shipment extends Storable<Shipment> {
+ @Sequence("TEST_SHIPMENT_ID_SEQ")
+ long getShipmentID();
+ void setShipmentID(long id);
+
+ String getShipmentNotes();
+ void setShipmentNotes(String value);
+
+ DateTime getShipmentDate();
+ void setShipmentDate(DateTime value);
+
+ long getOrderID();
+ void setOrderID(long value);
+
+ @Join
+ Order getOrder() throws FetchException;
+ void setOrder(Order value);
+
+ long getShipperID();
+ void setShipperID(long value);
+
+ @Join
+ Shipper getShipper() throws FetchException;
+ void setShipper(Shipper value);
+
+ @Join
+ Query<OrderItem> getOrderItems() throws FetchException;
+}
+
diff --git a/src/test/java/com/amazon/carbonado/stored/Shipper.java b/src/test/java/com/amazon/carbonado/stored/Shipper.java new file mode 100644 index 0000000..41e60f4 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/stored/Shipper.java @@ -0,0 +1,52 @@ +/*
+ * 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.Alias;
+import com.amazon.carbonado.Storable;
+import com.amazon.carbonado.FetchException;
+import com.amazon.carbonado.Join;
+import com.amazon.carbonado.PrimaryKey;
+import com.amazon.carbonado.Sequence;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+@Alias("TEST_SHIPPER")
+@PrimaryKey("shipperID")
+public interface Shipper extends Storable<Shipper> {
+ @Sequence("TEST_SHIPPER_ID_SEQ")
+ long getShipperID();
+ void setShipperID(long id);
+
+ String getShipperName();
+ void setShipperName(String value);
+
+ String getShipperDetails();
+ void setShipperDetails(String value);
+
+ long getAddressID();
+ void setAddressID(long value);
+
+ @Join
+ Address getAddress() throws FetchException;
+ void setAddress(Address 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/StorableTestBasic.java b/src/test/java/com/amazon/carbonado/stored/StorableTestBasic.java new file mode 100644 index 0000000..0857518 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/stored/StorableTestBasic.java @@ -0,0 +1,114 @@ +/*
+ * 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.util.Random;
+
+import org.joda.time.DateTime;
+
+import com.amazon.carbonado.Nullable;
+import com.amazon.carbonado.Storable;
+import com.amazon.carbonado.PrimaryKey;
+import com.amazon.carbonado.Storage;
+import com.amazon.carbonado.Repository;
+import com.amazon.carbonado.RepositoryException;
+
+/**
+ * StorableTestBasic
+ *
+ * @author Don Schneider
+ */
+@PrimaryKey("id")
+public abstract class StorableTestBasic implements Storable {
+ public abstract int getId();
+ public abstract void setId(int id);
+
+ // Basic coverage of the primitives
+ public abstract String getStringProp();
+ public abstract void setStringProp(String aStringThing);
+
+ public abstract int getIntProp();
+ public abstract void setIntProp(int anInt);
+
+ public abstract long getLongProp();
+ public abstract void setLongProp(long aLong);
+
+ public abstract double getDoubleProp();
+ public abstract void setDoubleProp(double aDouble);
+
+ @Nullable
+ public abstract DateTime getDate();
+ public abstract void setDate(DateTime aDate);
+
+ public void initPrimaryKeyProperties() {
+ setId(10);
+ }
+
+ public void initBasicProperties() {
+ setStringProp("foo");
+ setIntProp(10);
+ setLongProp(120);
+ setDoubleProp(1.2);
+ }
+
+ public void initPropertiesRandomly(int id) {
+ setId(id);
+
+ Random random = new Random(1000);
+
+ setIntProp(random.nextInt());
+ setLongProp(random.nextLong());
+ setDoubleProp(random.nextDouble());
+ setStringProp("imaString_" + id % 10);
+ }
+
+ public void initPropertiesPredictably(int id) {
+ setId(id);
+
+ setIntProp(id*10);
+ setLongProp(id*10);
+ setDoubleProp(id/2.0);
+ setStringProp("string-" + id % 100);
+ }
+
+ public static void insertBunches(Repository repository, int count)
+ throws RepositoryException
+ {
+ insertBunches(repository, count, 0, true);
+ }
+
+
+ public static void insertBunches(Repository repository,
+ int count, int startId,
+ boolean doRandom)
+ throws RepositoryException
+ {
+ Storage<StorableTestBasic> storage = repository.storageFor(StorableTestBasic.class);
+ StorableTestBasic s;
+
+ for (int i = 0; i < count; i ++) {
+ s = storage.prepare();
+ if (doRandom) {
+ s.initPropertiesRandomly(i);
+ } else {
+ s.initPropertiesPredictably(i+startId);
+ }
+ s.insert();
+ }
+ }
+}
diff --git a/src/test/java/com/amazon/carbonado/stored/StorableTestBasicIndexed.java b/src/test/java/com/amazon/carbonado/stored/StorableTestBasicIndexed.java new file mode 100644 index 0000000..c9de396 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/stored/StorableTestBasicIndexed.java @@ -0,0 +1,37 @@ +/* + * 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; + +/** + * StorableTestBasicIndexed + * + * @author Brian S O'Neill + */ +@Indexes({ + @Index("stringProp"), + @Index("intProp"), + @Index("longProp"), + @Index("doubleProp") +}) +@PrimaryKey("id") +public abstract class StorableTestBasicIndexed extends StorableTestBasic { +} 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/StorableTestMinimal.java b/src/test/java/com/amazon/carbonado/stored/StorableTestMinimal.java new file mode 100644 index 0000000..a1aae70 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/stored/StorableTestMinimal.java @@ -0,0 +1,32 @@ +/*
+ * 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;
+
+/**
+ * StorableTestBasic
+ *
+ * @author Don Schneider
+ */
+@PrimaryKey("id")
+public interface StorableTestMinimal extends Storable {
+ int getId();
+ void setId(int id);
+}
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);
+}
diff --git a/src/test/java/com/amazon/carbonado/stored/UserAddress.java b/src/test/java/com/amazon/carbonado/stored/UserAddress.java new file mode 100644 index 0000000..466f6eb --- /dev/null +++ b/src/test/java/com/amazon/carbonado/stored/UserAddress.java @@ -0,0 +1,66 @@ +/*
+ * 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 (boneill)
+ */
+@Indexes({
+ @Index({"country", "state", "city"}),
+ @Index({"state", "city"}),
+ @Index("city")
+})
+@PrimaryKey("addressID")
+public abstract class UserAddress implements Storable<UserAddress> {
+ public abstract int getAddressID();
+ public abstract void setAddressID(int id);
+
+ public abstract String getLine1();
+ public abstract void setLine1(String value);
+
+ @Nullable
+ public abstract String getLine2();
+ public abstract void setLine2(String value);
+
+ public abstract String getCity();
+ public abstract void setCity(String value);
+
+ public abstract String getState();
+ public abstract void setState(String value);
+
+ public abstract String getCountry();
+ public abstract void setCountry(String value);
+
+ @Nullable
+ public abstract String getPostalCode();
+ public abstract void setPostalCode(String value);
+
+ @Nullable
+ public abstract Integer getNeighborAddressID();
+ public abstract void setNeighborAddressID(Integer id);
+
+ @Nullable
+ @Join(internal="neighborAddressID", external="addressID")
+ public abstract UserAddress getNeighbor() throws FetchException;
+ public abstract void setNeighbor(UserAddress address) throws FetchException;
+}
diff --git a/src/test/java/com/amazon/carbonado/stored/UserInfo.java b/src/test/java/com/amazon/carbonado/stored/UserInfo.java new file mode 100644 index 0000000..e0bffa4 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/stored/UserInfo.java @@ -0,0 +1,57 @@ +/*
+ * 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.*;
+import com.amazon.carbonado.constraint.*;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill (boneill)
+ */
+@Indexes({
+ @Index("firstName"),
+ @Index("lastName"),
+ @Index("addressID")
+})
+@PrimaryKey("userID")
+public abstract class UserInfo implements Storable<UserInfo> {
+ public abstract int getUserID();
+ public abstract void setUserID(int id);
+
+ public abstract int getStateID();
+ @IntegerConstraint(allowed={1, 2, 3})
+ public abstract void setStateID(int state);
+
+ public abstract String getFirstName();
+ @LengthConstraint(min=1, max=50)
+ public abstract void setFirstName(String value);
+
+ public abstract String getLastName();
+ @LengthConstraint(min=1, max=50)
+ public abstract void setLastName(String value);
+
+ public abstract int getAddressID();
+ public abstract void setAddressID(int id);
+
+ @Join
+ public abstract UserAddress getAddress() throws FetchException;
+ public abstract void setAddress(UserAddress address);
+}
diff --git a/src/test/java/com/amazon/carbonado/synthetic/TestSyntheticStorableBuilders.java b/src/test/java/com/amazon/carbonado/synthetic/TestSyntheticStorableBuilders.java new file mode 100644 index 0000000..0b93822 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/synthetic/TestSyntheticStorableBuilders.java @@ -0,0 +1,755 @@ +/* + * 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.synthetic; + +import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Random; +import java.util.Set; +import java.util.Map; +import java.util.HashMap; + +import junit.framework.TestCase; +import junit.framework.TestSuite; + +import org.joda.time.DateTime; + +import com.amazon.carbonado.FetchException; +import com.amazon.carbonado.PersistException; +import com.amazon.carbonado.Repository; +import com.amazon.carbonado.RepositoryException; +import com.amazon.carbonado.Storable; +import com.amazon.carbonado.PrimaryKey; +import com.amazon.carbonado.SupportException; +import com.amazon.carbonado.Version; +import com.amazon.carbonado.Nullable; +import com.amazon.carbonado.Join; + +import com.amazon.carbonado.adapter.YesNoAdapter; +import com.amazon.carbonado.adapter.TextAdapter; +import com.amazon.carbonado.info.Direction; +import com.amazon.carbonado.info.StorableInfo; +import com.amazon.carbonado.info.StorableIntrospector; +import com.amazon.carbonado.info.StorableProperty; + +import com.amazon.carbonado.repo.toy.ToyRepository; + +import com.amazon.carbonado.stored.StorableTestBasic; + +/** + * @author Don Schneider + */ +public class TestSyntheticStorableBuilders extends TestCase { + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + + public static TestSuite suite() { + return new TestSuite(TestSyntheticStorableBuilders.class); + } + + private static final ValueVendor VENDOR = new ValueVendor(); + + public static final String VERSION = '@' + + Version.class.toString().substring(10) + "()"; + + public static final String NULLABLE = '@' + + Nullable.class.toString().substring(10) + "()"; + + public static final String JOIN = '@' + Join.class.toString().substring(10) + '('; + + public static final String INTERNAL = "internal=[]"; + + public static final String EXTERNAL = "external=[]"; + + public final static String YESNO = '@' + + YesNoAdapter.class.toString().substring(10) + "(lenient=true)"; + + public static final String TEXT = '@' + + TextAdapter.class.toString().substring(10) + "(charset=UTF-8, altCharsets=[])"; + + public static final String TEXT_2 = '@' + + TextAdapter.class.toString().substring(10) + "(altCharsets=[], charset=UTF-8)"; + + private static Set s_storableInterfaceMethods = new HashSet(); + + public static final TestDef[] TESTS = new TestDef[] { + new TestDef("string/long", + StorableTestBasic.class, + StorableTestBasic.class.getName() + "~N+stringProp-longProp", + new IndexDef[] { + new IndexDef("stringProp", Direction.ASCENDING), + new IndexDef("longProp", Direction.DESCENDING), }, + new MethodDef[] { + new MethodDef("getStringProp", new String[] { TEXT }), + new MethodDef("getLongProp", null), + new MethodDef("getId", null), + new MethodDef("getIntProp", null), + new MethodDef("getDoubleProp", null), + // FIXME: add version prop to STB +// new MethodDef("getVersionNumber", +// new String[] { VERSION }), + new MethodDef("getMaster_0", new String[] { + NULLABLE, JOIN }), + new MethodDef("getIsConsistent_0", null), +// new MethodDef("setVersionNumber", null), + new MethodDef("setAllProperties_0", null), + new MethodDef("setMaster_0", null), + new MethodDef("setId", null), + + new MethodDef("setStringProp", null), + new MethodDef("setLongProp", null), + new MethodDef("setID", null), + new MethodDef("setIntProp", null), + new MethodDef("setDoubleProp", null), + + new MethodDef("getClass", null), + new MethodDef("wait", null), + new MethodDef("notify", null), + new MethodDef("notifyAll", null), }, + null), + new TestDef("string/long+synthetic", + StorableTestBasic.class, + StorableTestBasic.class.getName() + "~N+stringProp-longProp", + new IndexDef[] { + new IndexDef("stringProp", Direction.ASCENDING), + new IndexDef("longProp", Direction.DESCENDING), }, + new MethodDef[] { + new MethodDef("getStringProp", new String[] { TEXT }), + new MethodDef("getLongProp", null), + new MethodDef("getId", null), + new MethodDef("getIntProp", null), + new MethodDef("getDoubleProp", null), + // FIXME: add version prop to STB +// new MethodDef("getVersionNumber", +// new String[] { VERSION }), + new MethodDef("getMaster_0", new String[] { + NULLABLE, JOIN }), + new MethodDef("getIsConsistent_0", null), +// new MethodDef("setVersionNumber", null), + new MethodDef("setAllProperties_0", null), + new MethodDef("setMaster_0", null), + new MethodDef("setId", null), + + new MethodDef("setStringProp", null), + new MethodDef("setLongProp", null), + new MethodDef("setID", null), + new MethodDef("setIntProp", null), + new MethodDef("setDoubleProp", null), + + new MethodDef("getClass", null), + new MethodDef("wait", null), + new MethodDef("notify", null), + new MethodDef("notifyAll", null), + + new MethodDef("getTestSynth", new String[] { NULLABLE }), + new MethodDef("setTestSynth", null), + }, + new SyntheticProperty[] { new SyntheticProperty("testSynth", + String.class, + true, + false) }), + new TestDef("string/long+versionedSynthetic", + StorableTestBasic.class, + StorableTestBasic.class.getName() + "~N+stringProp-longProp", + new IndexDef[] { + new IndexDef("stringProp", Direction.ASCENDING), + new IndexDef("longProp", Direction.DESCENDING), }, + new MethodDef[] { + + new MethodDef("getStringProp", new String[] { TEXT }), + new MethodDef("getLongProp", null), + new MethodDef("getId", null), + new MethodDef("getIntProp", null), + new MethodDef("getDoubleProp", null), + new MethodDef("getMaster_0", new String[] { + NULLABLE, JOIN }), + new MethodDef("getIsConsistent_0", null), + new MethodDef("setAllProperties_0", null), + new MethodDef("setMaster_0", null), + new MethodDef("setId", null), + + new MethodDef("setStringProp", null), + new MethodDef("setLongProp", null), + new MethodDef("setID", null), + new MethodDef("setIntProp", null), + new MethodDef("setDoubleProp", null), + + new MethodDef("getClass", null), + new MethodDef("wait", null), + new MethodDef("notify", null), + new MethodDef("notifyAll", null), + + new MethodDef("getTestSynth", new String[] { VERSION }), + new MethodDef("setTestSynth", null), + }, + new SyntheticProperty[] { new SyntheticProperty("testSynth", + int.class, + false, + true) }) }; + + static { + for (Method m : Storable.class.getMethods()) { + s_storableInterfaceMethods.add(m.getName()); + } + } + + protected Repository mRepository; + + public TestSyntheticStorableBuilders(String name) { + super(name); + } + + /* (non-Javadoc) + * @see junit.framework.TestCase#setUp() + */ + @Override + protected void setUp() throws Exception + { + super.setUp(); + mRepository = new ToyRepository(); + } + + @Override + protected void tearDown() throws Exception { + if (mRepository != null) { + mRepository.close(); + mRepository = null; + } + } + + public void testSanity() throws Exception { + exerciseStorable(StorableTestBasic.class); + } + + + /** + * Test syntheticBuilder + */ + public <S extends Storable> void test_syntheticBuilder() throws Exception { + SyntheticProperty[] props = new SyntheticProperty[] { + new SyntheticProperty("synthId", + int.class, + false, + false), + new SyntheticProperty("synthStr", + String.class, + true, + false), + new SyntheticProperty("synthInt", + int.class, + false, + false), + new SyntheticProperty("synthVers", + int.class, + false, + true), + new SyntheticProperty("synthBool", + boolean.class, + false, + false), }; + + TestDef test = new TestDef("synthetic", + null, + "TSSB~synthId~synthStr~synthInt~synthVers~synthBool", + null, + new MethodDef[] { + new MethodDef("getSynthId", + null), + new MethodDef("getSynthStr", + new String[] { NULLABLE }), + new MethodDef("getSynthInt", null), + new MethodDef("getSynthVers", + new String[] { VERSION }), + new MethodDef("isSynthBool", null), + + new MethodDef("setSynthId", null), + new MethodDef("setSynthStr", null), + new MethodDef("setSynthInt", null), + new MethodDef("setSynthVers", null), + new MethodDef("setSynthBool", null), + + new MethodDef("getClass", null), + new MethodDef("wait", null), + new MethodDef("notify", null), + new MethodDef("notifyAll", null), }, + props); + + SyntheticStorableBuilder b = new SyntheticStorableBuilder( + "TSSB", this.getClass().getClassLoader()); + int i = 0; + for (SyntheticProperty source : props) { + b.addProperty(source); + if (i == 0) { + // First is primary key. + b.addPrimaryKey().addProperty(source.getName()); + } + i++; + } + Class synth = b.build(); + validateIndexEntry(test, synth); + + exerciseStorable(synth); + } + + + + public void testSyntheticReference() throws Exception { + for (TestDef test : TESTS) { + StorableInfo info = StorableIntrospector.examine(test.mClass); + + SyntheticStorableReferenceBuilder<StorableTestBasic> builder + = new SyntheticStorableReferenceBuilder<StorableTestBasic>(test.mClass, false); + + for (IndexDef refProp : test.mProps) { + StorableProperty storableProperty = ((StorableProperty) info.getAllProperties() + .get(refProp.mProp)); + builder.addKeyProperty(refProp.mProp, refProp.getDir()); + } + builder.build(); + Class s = builder.getStorableClass(); + validateIndexEntry(test, s); + exerciseStorable(s); + + StorableTestBasic master = + mRepository.storageFor(StorableTestBasic.class).prepare(); + populate(master); + master.insert(); + + Storable index = mRepository.storageFor(s).prepare(); + builder.setAllProperties(index, master); + index.insert(); + + Storable indexChecker = mRepository.storageFor(s).prepare(); + builder.setAllProperties(indexChecker, master); + assertTrue(indexChecker.tryLoad()); + + StorableTestBasic masterChecker = builder.loadMaster(indexChecker); + assertEquals(master, masterChecker); + + assertTrue(builder.isConsistent(index, master)); + masterChecker = + mRepository.storageFor(StorableTestBasic.class).prepare(); + master.copyAllProperties(masterChecker); + assertTrue(builder.isConsistent(index, masterChecker)); + masterChecker.setId(-42); + assertFalse(builder.isConsistent(index, masterChecker)); + + } + } + + /** + * @param <S> + * @param storableType + * @throws SupportException + * @throws RepositoryException + * @throws IllegalAccessException + * @throws InvocationTargetException + * @throws PersistException + * @throws FetchException + */ + protected <T extends Storable> void exerciseStorable(Class<T> storableType) throws SupportException, RepositoryException, IllegalAccessException, InvocationTargetException, PersistException, FetchException { + T persister = mRepository.storageFor(storableType).prepare(); + Map<String, Object> valueMap = populate(persister); + + persister.insert(); + T reader = mRepository.storageFor(storableType).prepare(); + persister.copyPrimaryKeyProperties(reader); + assertTrue(reader.tryLoad()); + + for (Method method : storableType.getMethods()) { + if (method.getName().startsWith("get") ) { + Class returnType = method.getReturnType(); + Class[] paramTypes = method.getParameterTypes(); + if (VENDOR.hasValue(returnType) && paramTypes.length == 0) { + Object expectedValue = valueMap.get(method.getName().substring(3)); + Object actualValue = method.invoke(persister, (Object[]) null); + assertEquals(expectedValue, actualValue); + } + } + } + } + + /** + * Populates a storable with random values. Any complex properties will be skipped. + * @param storable to populate + * @return map from property name to value set into it + * @throws IllegalAccessException + * @throws InvocationTargetException + */ + protected <T extends Storable> Map<String, Object> populate(T storable) + throws IllegalAccessException, InvocationTargetException { + ValueVendor vendor = new ValueVendor(); + Map<String, Object> valueMap = new HashMap<String, Object>(); + vendor.reset(); + for (Method method : storable.getClass().getMethods()) { + if (method.getName().startsWith("set") ) { + Class[] paramTypes = method.getParameterTypes(); + Class type = paramTypes[0]; + if (vendor.hasValue(type)) { + Object value = vendor.getValue(type); + method.invoke(storable, new Object[] {value}); + valueMap.put(method.getName().substring(3), value); + } + } + } + return valueMap; + } + + + + + protected void validateIndexEntry(TestDef test, Class syntheticClass) { + assertEquals(test.mClassName, syntheticClass.getName()); + Map<String, String[]> expectedMethods = new HashMap<String, String[]>(); + if (null != test.mMethods) { + for (MethodDef m : test.mMethods) { + expectedMethods.put(m.mName, m.mAnnotations); + } + } + + Class iface = syntheticClass.getInterfaces()[0]; + assertEquals(test.mTestMoniker, Storable.class, iface); + + boolean missed = false; + int storableIfaceMethodCount = 0; + int methodCount = 0; + for (Method m : syntheticClass.getMethods()) { + if (s_storableInterfaceMethods.contains(m.getName())) { + storableIfaceMethodCount++; + } else { + Set expectedAnnotations = null; + if (expectedMethods.containsKey(m.getName())) { + expectedAnnotations = new HashSet(); + String[] expectedAnnotationArray = expectedMethods.get(m.getName()); + if (null != expectedAnnotationArray) { + for (String a : expectedAnnotationArray) { + expectedAnnotations.add(a); + } + } + } else { + System.out.println("missed method " + methodCount + + "\nMethodDef(\"" + m.getName() + "\", null),"); + missed = true; + } + + if (expectedAnnotations != null) { + assertEquals(test.mTestMoniker + ": " + m.getName(), + expectedAnnotations.size(), + m.getDeclaredAnnotations().length); + } else { + assertEquals(test.mTestMoniker + ": " + m.getName(), + 0, + m.getDeclaredAnnotations().length); + } + + for (Annotation a : m.getDeclaredAnnotations()) { + if (missed) { + System.out.println(" " + a); + } + if (expectedAnnotations != null) { + if (!expectedAnnotations.contains(a.toString())) { + boolean found = false; + for (Object candidate : expectedAnnotations.toArray()) { + if (a.toString().startsWith((String) candidate)) { + found = true; + break; + } + // Special case to handle arbitrary ordering of + // annotation parameters. + if (candidate == TEXT && a.toString().startsWith(TEXT_2)) { + found = true; + break; + } + } + assertTrue(test.mTestMoniker + ':' + m.getName() + + " -- unexpected annotation " + a, found); + } // else we're ok + } + ; + } + } + methodCount++; + } + assertEquals(test.mTestMoniker, + s_storableInterfaceMethods.size(), + storableIfaceMethodCount); + assertFalse(test.mTestMoniker, missed); + } + + static class MethodDef { + String mName; + + String[] mAnnotations; + + public MethodDef(String name, String[] annotations) { + mName = name; + mAnnotations = annotations; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + @Override + public String toString() + { + String annotations = null; + if (null != mAnnotations) { + for (int i = 0; i < mAnnotations.length; i++) { + if (null == annotations) { + annotations = ""; + } else { + annotations += ", "; + } + annotations += mAnnotations[i]; + } + } else { + annotations = " -- "; + } + return mName + " [" + annotations + ']'; + } + + } + + /** + * More intuitive mechanism for defining index properties; propClass is + * optionally used for synthetic references (since we don't formally define + * properties in this test environment) + */ + public static class IndexDef { + private String mProp; + + private Direction mDir; + + public IndexDef(String prop, Direction dir) { + mProp = prop; + mDir = dir; + } + + public String getProp() { + return mProp; + } + + public Direction getDir() { + return mDir; + } + } + + public static class TestDef { + public String mTestMoniker; + + public Class mClass; + + public String mClassName; + + public IndexDef[] mProps; + + public MethodDef[] mMethods; + + public SyntheticProperty[] mSynths; + + public TestDef(String moniker, + Class aClass, + String aClassName, + IndexDef[] props, + MethodDef[] methods, + SyntheticProperty[] synths) { + mTestMoniker = moniker; + mClass = aClass; + mClassName = aClassName; + mProps = props; + mMethods = methods; + mSynths = synths; + } + + public String toString() { + return mTestMoniker; + } + } + + public interface ValueGetter { + public void reset(); + public Object getValue(); + } + + static class BoolGetter implements ValueGetter { + final boolean start = false; + boolean mNext = start; + public void reset() { + mNext = start; + } + public Boolean getValue() { + mNext = !mNext; + return mNext; + } + } + + static class IntGetter implements ValueGetter { + // hang onto the seed so we get a reproducible stream + int mSeed; + Random mNext; + + IntGetter() { + reset(); + } + + public void reset() { + Random seeder = new Random(); + while (mSeed == 0) { + mSeed = seeder.nextInt(); + } + mNext = new Random(mSeed); + } + + public Integer getValue() { + return mNext.nextInt(); + } + + public Integer getPositiveValue() { + return Math.abs(mNext.nextInt()); + } + } + + static class LongGetter implements ValueGetter { + final long start = 56789; + long mNext = start; + public void reset() { + mNext = start; + } + public Long getValue() { + return mNext++; + } + } + + static class FloatGetter implements ValueGetter { + final float start = (float) Math.PI; + float mNext = start; + public void reset() { + mNext = start; + } + public Float getValue() { + float next = mNext; + mNext = next + 0.1f; + return next; + } + } + + static class DoubleGetter implements ValueGetter { + final double start = Math.PI; + double mNext = start; + public void reset() { + mNext = start; + } + public Double getValue() { + double next = mNext; + mNext = next + 0.1; + return next; + } + } + + static class CharGetter implements ValueGetter { + + static final String source = "zookeepers fly piglatin homogeneous travesty"; + + IntGetter mNext = new IntGetter(); + + public void reset() { + mNext.reset(); + } + + + public Character getValue() { + return source.charAt(mNext.getPositiveValue() % source.length()); + } + } + + static class StringGetter implements ValueGetter { + IntGetter mSizer = new IntGetter(); + CharGetter mNext = new CharGetter(); + + public void reset() { + mSizer.reset(); + mNext.reset(); + } + public String getValue() { + int size = mSizer.getPositiveValue() % 255; + StringBuilder sb = new StringBuilder(size); + for (int i = 0; i<size; i++) { + sb.append(mNext.getValue()); + } + return sb.toString(); + } + } + + static class DateTimeGetter implements ValueGetter { + static final DateTime start = new DateTime("2005-01-02"); + DateTime mNext = start; + public void reset() { + mNext = start; + } + public DateTime getValue() { + DateTime next = mNext; + mNext = mNext.dayOfYear().addToCopy(1); + return next; + } + } + + static class ValueVendor { + Map<Class, ValueGetter> getters = new HashMap(10); + ValueVendor() { + getters.put(String.class, new StringGetter()); + getters.put(DateTime.class, new DateTimeGetter()); + + getters.put(Boolean.class, new BoolGetter()); + getters.put(Integer.class, new IntGetter()); + getters.put(Long.class, new LongGetter()); + getters.put(Float.class, new FloatGetter()); + getters.put(Double.class, new DoubleGetter()); + getters.put(Character.class, new CharGetter()); + + getters.put(boolean.class, new BoolGetter()); + getters.put(int.class, new IntGetter()); + getters.put(long.class, new LongGetter()); + getters.put(float.class, new FloatGetter()); + getters.put(double.class, new DoubleGetter()); + getters.put(char.class, new CharGetter()); + } + + void reset() { + for (Iterator iter = getters.values().iterator(); iter.hasNext();) { + ValueGetter getter = (ValueGetter) iter.next(); + getter.reset(); + } + } + + boolean hasValue(Class type) { + return getters.containsKey(type); + } + + + Object getValue(Class type) { + return getters.get(type).getValue(); + } + } +} diff --git a/src/test/java/com/amazon/carbonado/util/TestBelatedCreator.java b/src/test/java/com/amazon/carbonado/util/TestBelatedCreator.java new file mode 100644 index 0000000..99e927f --- /dev/null +++ b/src/test/java/com/amazon/carbonado/util/TestBelatedCreator.java @@ -0,0 +1,244 @@ +/*
+ * 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.util;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+public class TestBelatedCreator extends TestCase {
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static TestSuite suite() {
+ return new TestSuite(TestBelatedCreator.class);
+ }
+
+ public TestBelatedCreator(String name) {
+ super(name);
+ }
+
+ public void test_noProblems() {
+ Creator c = new Creator(0, 0, false);
+ TheObject obj = c.get(1000);
+ assertTrue(obj.isReal());
+ assertEquals("real", obj.toString());
+
+ obj.doSomething();
+ assertEquals(100, obj.getValue());
+ assertEquals("12a", obj.doSomething(1, 2, "a"));
+
+ assertEquals(0, c.mTimedOutCount);
+ assertEquals(1, c.mCreatedCount);
+ }
+
+ public void test_immediateFailure() {
+ Creator c = new Creator(60000, 0, true);
+ TheObject obj = c.get(1000);
+ assertFalse(obj.isReal());
+ assertEquals("bogus", obj.toString());
+
+ try {
+ obj.doSomething();
+ fail();
+ } catch (RuntimeException e) {
+ }
+ assertEquals(-1, obj.getValue());
+ assertNull(obj.doSomething(1, 2, "a"));
+
+ assertEquals(0, c.mTimedOutCount);
+ assertEquals(0, c.mCreatedCount);
+
+ assertTrue(obj == c.get(1000));
+ assertTrue(obj.equals(c.get(1000)));
+ }
+
+ public void test_longDelay() {
+ Creator c = new Creator(60000, 5000, false);
+
+ long start, end;
+ TheObject obj;
+
+ start = System.nanoTime();
+ obj = c.get(1000);
+ end = System.nanoTime();
+ assertFalse(obj.isReal());
+ assertEquals("bogus", obj.toString());
+ assertTrue((end - start) >= 1000L * 1000000);
+
+ start = System.nanoTime();
+ obj = c.get(1000);
+ end = System.nanoTime();
+ assertFalse(obj.isReal());
+ assertEquals("bogus", obj.toString());
+ assertTrue((end - start) >= 1000L * 1000000);
+
+ assertEquals(-1, obj.getValue());
+ assertNull(obj.doSomething(1, 2, "a"));
+
+ assertEquals(2, c.mTimedOutCount);
+ assertEquals(0, c.mCreatedCount);
+
+ start = System.nanoTime();
+ TheObject obj2 = c.get(5000);
+ end = System.nanoTime();
+ assertTrue(obj2.isReal());
+ assertEquals("real", obj.toString());
+ assertTrue((end - start) <= 5000L * 1000000);
+
+ assertFalse(obj == obj2);
+ assertTrue(obj.isReal());
+ assertEquals("real", obj.toString());
+
+ assertEquals(100, obj.getValue());
+ assertEquals("12a", obj.doSomething(1, 2, "a"));
+ assertEquals(100, obj2.getValue());
+ assertEquals("23b", obj2.doSomething(2, 3, "b"));
+
+ assertEquals(2, c.mTimedOutCount);
+ assertEquals(1, c.mCreatedCount);
+
+ start = System.nanoTime();
+ TheObject obj3 = c.get(1000);
+ end = System.nanoTime();
+ assertTrue(obj3.isReal());
+ assertEquals("real", obj3.toString());
+ assertTrue((end - start) <= 1000L * 1000000);
+
+ assertTrue(obj2 == obj3);
+ assertEquals(2, c.mTimedOutCount);
+ assertEquals(1, c.mCreatedCount);
+ }
+
+ public void test_retry() throws Exception {
+ Creator c = new Creator(5000, 0, true);
+ TheObject obj = c.get(1000);
+ assertFalse(obj.isReal());
+ assertEquals("bogus", obj.toString());
+
+ Thread.sleep(6000);
+
+ assertTrue(obj.isReal());
+ assertEquals("real", obj.toString());
+ obj = c.get(1000);
+ assertTrue(obj.isReal());
+ assertEquals("real", obj.toString());
+ }
+
+ public interface TheObject {
+ public boolean isReal();
+
+ public void doSomething();
+
+ public int getValue();
+
+ public String doSomething(int a, long b, Object q);
+
+ public String toString();
+ }
+
+ private class Creator extends BelatedCreator<TheObject, RuntimeException> {
+ final int mCreateDelay;
+ boolean mFailOnce;
+
+ int mTimedOutCount;
+ int mCreatedCount;
+
+ Creator(int retryMillis, int createDelay, boolean failOnce) {
+ super(TheObject.class, retryMillis);
+ mCreateDelay = createDelay;
+ mFailOnce = failOnce;
+ }
+
+ protected TheObject createReal() {
+ assertTrue(mCreatedCount == 0);
+ try {
+ Thread.sleep(mCreateDelay);
+ } catch (InterruptedException e) {
+ }
+
+ if (mFailOnce) {
+ mFailOnce = false;
+ return null;
+ }
+
+ return new TheObject() {
+ public boolean isReal() {
+ return true;
+ }
+
+ public void doSomething() {
+ }
+
+ public int getValue() {
+ return 100;
+ }
+
+ public String doSomething(int a, long b, Object q) {
+ return "" + a + b + q;
+ }
+
+ public String toString() {
+ return "real";
+ }
+ };
+ }
+
+ protected TheObject createBogus() {
+ return new TheObject() {
+ public boolean isReal() {
+ return false;
+ }
+
+ public void doSomething() {
+ throw new RuntimeException();
+ }
+
+ public int getValue() {
+ return -1;
+ }
+
+ public String doSomething(int a, long b, Object q) {
+ return null;
+ }
+
+ public String toString() {
+ return "bogus";
+ }
+ };
+ }
+
+ protected void timedOutNotification(long timedOutMillis) {
+ assertTrue(timedOutMillis >= 0);
+ mTimedOutCount++;
+ }
+
+ @Override
+ protected void createdNotification(TheObject object) {
+ assertNotNull(object);
+ mCreatedCount++;
+ }
+ }
+}
+
diff --git a/src/test/java/com/amazon/carbonado/util/TestQuickConstructorGenerator.java b/src/test/java/com/amazon/carbonado/util/TestQuickConstructorGenerator.java new file mode 100644 index 0000000..c9ba833 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/util/TestQuickConstructorGenerator.java @@ -0,0 +1,102 @@ +/*
+ * 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.util;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+public class TestQuickConstructorGenerator extends TestCase {
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static TestSuite suite() {
+ return new TestSuite(TestQuickConstructorGenerator.class);
+ }
+
+ public TestQuickConstructorGenerator(String name) {
+ super(name);
+ }
+
+ public void testStringMaker() throws Exception {
+ StringMaker maker = QuickConstructorGenerator.getInstance(String.class, StringMaker.class);
+ assertEquals("", maker.newEmptyString());
+ assertEquals("hello", maker.newStringFromChars(new char[] {'h', 'e', 'l', 'l', 'o'}));
+ assertEquals("hello",
+ maker.newStringFromBytes(new byte[] {'h', 'e', 'l', 'l', 'o'}, "US-ASCII"));
+ }
+
+ public void testIllegalArgs() {
+ try {
+ QuickConstructorGenerator.getInstance(String.class, String.class);
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+
+ try {
+ QuickConstructorGenerator.getInstance(String.class, BadStringMaker.class);
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+
+ try {
+ QuickConstructorGenerator.getInstance(String.class, BadStringMaker2.class);
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+
+ try {
+ QuickConstructorGenerator.getInstance(String.class, BadStringMaker3.class);
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+
+ try {
+ QuickConstructorGenerator.getInstance(byte[].class, StringMaker.class);
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ public static interface StringMaker {
+ Object newEmptyString();
+
+ String newStringFromChars(char[] chars);
+
+ String newStringFromBytes(byte[] bytes, String charsetName)
+ throws java.io.UnsupportedEncodingException;
+ }
+
+ public static interface BadStringMaker {
+ String newStringFromBytes(byte[] bytes, String charsetName);
+ }
+
+ public static interface BadStringMaker2 {
+ String newStringFromClass(Class clazz);
+ }
+
+ public static interface BadStringMaker3 {
+ Class newEmptyString();
+ }
+}
diff --git a/src/test/java/com/amazon/carbonado/util/TestThrowUnchecked.java b/src/test/java/com/amazon/carbonado/util/TestThrowUnchecked.java new file mode 100644 index 0000000..739a709 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/util/TestThrowUnchecked.java @@ -0,0 +1,54 @@ +/*
+ * 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.util;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ *
+ *
+ * @author Brian S O'Neill
+ */
+public class TestThrowUnchecked extends TestCase {
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(suite());
+ }
+
+ public static TestSuite suite() {
+ return new TestSuite(TestThrowUnchecked.class);
+ }
+
+ public TestThrowUnchecked(String name) {
+ super(name);
+ }
+
+ public void test() {
+ ThrowUnchecked.fire(null);
+
+ Exception e = new java.io.IOException();
+
+ try {
+ ThrowUnchecked.fire(e);
+ fail();
+ } catch (Exception e2) {
+ assertEquals(e, e2);
+ }
+ }
+}
|
