From 3df73d90a65115d9fdcf4b8596971e957c6bce2d Mon Sep 17 00:00:00 2001 From: "Brian S. O'Neill" Date: Sat, 14 Oct 2006 16:47:22 +0000 Subject: Moved tests to separate project. --- .../java/com/amazon/carbonado/TestQueryLogic.java | 835 ++++++++ .../java/com/amazon/carbonado/TestStorables.java | 2263 ++++++++++++++++++++ .../carbonado/cursor/EmptyCursorFactory.java | 30 + .../com/amazon/carbonado/cursor/TestCursors.java | 428 ++++ .../amazon/carbonado/cursor/TestGroupedCursor.java | 184 ++ .../carbonado/filter/TestFilterAsJoinedFrom.java | 156 ++ .../carbonado/filter/TestFilterNotJoinedFrom.java | 271 +++ .../carbonado/qe/TestFilteredQueryExecutor.java | 57 + .../amazon/carbonado/qe/TestFilteringScore.java | 746 +++++++ .../carbonado/qe/TestIndexedQueryAnalyzer.java | 460 ++++ .../carbonado/qe/TestIndexedQueryExecutor.java | 754 +++++++ .../carbonado/qe/TestJoinedQueryExecutor.java | 298 +++ .../com/amazon/carbonado/qe/TestOrderingList.java | 225 ++ .../com/amazon/carbonado/qe/TestOrderingScore.java | 673 ++++++ .../carbonado/qe/TestPropertyFilterList.java | 108 + .../com/amazon/carbonado/qe/TestQueryExecutor.java | 186 ++ .../carbonado/qe/TestSortedQueryExecutor.java | 84 + .../carbonado/qe/TestUnionQueryAnalyzer.java | 565 +++++ .../carbonado/qe/TestUnionQueryExecutor.java | 75 + .../com/amazon/carbonado/raw/TestDataEncoding.java | 515 +++++ .../amazon/carbonado/raw/TestEncodingStrategy.java | 857 ++++++++ .../com/amazon/carbonado/raw/TestKeyEncoding.java | 571 +++++ .../amazon/carbonado/repo/toy/ToyRepository.java | 106 + .../carbonado/repo/toy/ToyStorableGenerator.java | 143 ++ .../com/amazon/carbonado/repo/toy/ToyStorage.java | 265 +++ .../amazon/carbonado/repo/toy/ToyTransaction.java | 51 + .../amazon/carbonado/repo/toy/package-info.java | 25 + .../com/amazon/carbonado/sample/FileInfoTest.java | 92 + .../carbonado/spi/TestConversionComparator.java | 179 ++ .../com/amazon/carbonado/spi/TestCursorList.java | 224 ++ .../carbonado/spi/TestSequenceValueGenerator.java | 258 +++ .../carbonado/spi/TestStorableSerializer.java | 115 + .../amazon/carbonado/spi/TestTriggerManager.java | 334 +++ .../java/com/amazon/carbonado/stored/Address.java | 64 + .../java/com/amazon/carbonado/stored/Dummy.java | 145 ++ .../java/com/amazon/carbonado/stored/FileInfo.java | 100 + .../com/amazon/carbonado/stored/ManyKeys2.java | 47 + .../java/com/amazon/carbonado/stored/Order.java | 68 + .../com/amazon/carbonado/stored/OrderItem.java | 68 + .../com/amazon/carbonado/stored/Promotion.java | 55 + .../java/com/amazon/carbonado/stored/Shipment.java | 69 + .../java/com/amazon/carbonado/stored/Shipper.java | 52 + .../amazon/carbonado/stored/StorableDateIndex.java | 46 + .../amazon/carbonado/stored/StorableSequenced.java | 67 + .../carbonado/stored/StorableTestAssymetric.java | 38 + .../amazon/carbonado/stored/StorableTestBasic.java | 114 + .../carbonado/stored/StorableTestBasicIndexed.java | 37 + .../carbonado/stored/StorableTestInvalid.java | 40 + .../carbonado/stored/StorableTestKeyValue.java | 43 + .../carbonado/stored/StorableTestMinimal.java | 32 + .../carbonado/stored/StorableTestMultiPK.java | 40 + .../carbonado/stored/StorableTimestamped.java | 38 + .../amazon/carbonado/stored/StorableVersioned.java | 50 + .../carbonado/stored/StorableVersionedIndexed.java | 36 + .../stored/StorableVersionedWithLong.java | 44 + .../stored/StorableVersionedWithLongObj.java | 46 + .../carbonado/stored/StorableVersionedWithObj.java | 46 + .../com/amazon/carbonado/stored/Timestamped.java | 34 + .../com/amazon/carbonado/stored/UserAddress.java | 66 + .../java/com/amazon/carbonado/stored/UserInfo.java | 57 + .../synthetic/TestSyntheticStorableBuilders.java | 755 +++++++ .../amazon/carbonado/util/TestBelatedCreator.java | 244 +++ .../util/TestQuickConstructorGenerator.java | 102 + .../amazon/carbonado/util/TestThrowUnchecked.java | 54 + 64 files changed, 14830 insertions(+) create mode 100644 src/test/java/com/amazon/carbonado/TestQueryLogic.java create mode 100644 src/test/java/com/amazon/carbonado/TestStorables.java create mode 100644 src/test/java/com/amazon/carbonado/cursor/EmptyCursorFactory.java create mode 100644 src/test/java/com/amazon/carbonado/cursor/TestCursors.java create mode 100644 src/test/java/com/amazon/carbonado/cursor/TestGroupedCursor.java create mode 100644 src/test/java/com/amazon/carbonado/filter/TestFilterAsJoinedFrom.java create mode 100644 src/test/java/com/amazon/carbonado/filter/TestFilterNotJoinedFrom.java create mode 100644 src/test/java/com/amazon/carbonado/qe/TestFilteredQueryExecutor.java create mode 100644 src/test/java/com/amazon/carbonado/qe/TestFilteringScore.java create mode 100644 src/test/java/com/amazon/carbonado/qe/TestIndexedQueryAnalyzer.java create mode 100644 src/test/java/com/amazon/carbonado/qe/TestIndexedQueryExecutor.java create mode 100644 src/test/java/com/amazon/carbonado/qe/TestJoinedQueryExecutor.java create mode 100644 src/test/java/com/amazon/carbonado/qe/TestOrderingList.java create mode 100644 src/test/java/com/amazon/carbonado/qe/TestOrderingScore.java create mode 100644 src/test/java/com/amazon/carbonado/qe/TestPropertyFilterList.java create mode 100644 src/test/java/com/amazon/carbonado/qe/TestQueryExecutor.java create mode 100644 src/test/java/com/amazon/carbonado/qe/TestSortedQueryExecutor.java create mode 100644 src/test/java/com/amazon/carbonado/qe/TestUnionQueryAnalyzer.java create mode 100644 src/test/java/com/amazon/carbonado/qe/TestUnionQueryExecutor.java create mode 100644 src/test/java/com/amazon/carbonado/raw/TestDataEncoding.java create mode 100644 src/test/java/com/amazon/carbonado/raw/TestEncodingStrategy.java create mode 100644 src/test/java/com/amazon/carbonado/raw/TestKeyEncoding.java create mode 100644 src/test/java/com/amazon/carbonado/repo/toy/ToyRepository.java create mode 100644 src/test/java/com/amazon/carbonado/repo/toy/ToyStorableGenerator.java create mode 100644 src/test/java/com/amazon/carbonado/repo/toy/ToyStorage.java create mode 100644 src/test/java/com/amazon/carbonado/repo/toy/ToyTransaction.java create mode 100644 src/test/java/com/amazon/carbonado/repo/toy/package-info.java create mode 100644 src/test/java/com/amazon/carbonado/sample/FileInfoTest.java create mode 100644 src/test/java/com/amazon/carbonado/spi/TestConversionComparator.java create mode 100644 src/test/java/com/amazon/carbonado/spi/TestCursorList.java create mode 100644 src/test/java/com/amazon/carbonado/spi/TestSequenceValueGenerator.java create mode 100644 src/test/java/com/amazon/carbonado/spi/TestStorableSerializer.java create mode 100644 src/test/java/com/amazon/carbonado/spi/TestTriggerManager.java create mode 100644 src/test/java/com/amazon/carbonado/stored/Address.java create mode 100644 src/test/java/com/amazon/carbonado/stored/Dummy.java create mode 100644 src/test/java/com/amazon/carbonado/stored/FileInfo.java create mode 100644 src/test/java/com/amazon/carbonado/stored/ManyKeys2.java create mode 100644 src/test/java/com/amazon/carbonado/stored/Order.java create mode 100644 src/test/java/com/amazon/carbonado/stored/OrderItem.java create mode 100644 src/test/java/com/amazon/carbonado/stored/Promotion.java create mode 100644 src/test/java/com/amazon/carbonado/stored/Shipment.java create mode 100644 src/test/java/com/amazon/carbonado/stored/Shipper.java create mode 100644 src/test/java/com/amazon/carbonado/stored/StorableDateIndex.java create mode 100644 src/test/java/com/amazon/carbonado/stored/StorableSequenced.java create mode 100644 src/test/java/com/amazon/carbonado/stored/StorableTestAssymetric.java create mode 100644 src/test/java/com/amazon/carbonado/stored/StorableTestBasic.java create mode 100644 src/test/java/com/amazon/carbonado/stored/StorableTestBasicIndexed.java create mode 100644 src/test/java/com/amazon/carbonado/stored/StorableTestInvalid.java create mode 100644 src/test/java/com/amazon/carbonado/stored/StorableTestKeyValue.java create mode 100644 src/test/java/com/amazon/carbonado/stored/StorableTestMinimal.java create mode 100644 src/test/java/com/amazon/carbonado/stored/StorableTestMultiPK.java create mode 100644 src/test/java/com/amazon/carbonado/stored/StorableTimestamped.java create mode 100644 src/test/java/com/amazon/carbonado/stored/StorableVersioned.java create mode 100644 src/test/java/com/amazon/carbonado/stored/StorableVersionedIndexed.java create mode 100644 src/test/java/com/amazon/carbonado/stored/StorableVersionedWithLong.java create mode 100644 src/test/java/com/amazon/carbonado/stored/StorableVersionedWithLongObj.java create mode 100644 src/test/java/com/amazon/carbonado/stored/StorableVersionedWithObj.java create mode 100644 src/test/java/com/amazon/carbonado/stored/Timestamped.java create mode 100644 src/test/java/com/amazon/carbonado/stored/UserAddress.java create mode 100644 src/test/java/com/amazon/carbonado/stored/UserInfo.java create mode 100644 src/test/java/com/amazon/carbonado/synthetic/TestSyntheticStorableBuilders.java create mode 100644 src/test/java/com/amazon/carbonado/util/TestBelatedCreator.java create mode 100644 src/test/java/com/amazon/carbonado/util/TestQuickConstructorGenerator.java create mode 100644 src/test/java/com/amazon/carbonado/util/TestThrowUnchecked.java (limited to 'src/test') 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 storage = + getRepository().storageFor(StorableTestBasic.class); + + StorableTestBasic stb; + Query query; + List 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 storage = + getRepository().storageFor(StorableTestBasic.class); + + Query query; + List results; + + { + query = storage.query("intProp = ? & (stringProp >= ? | longProp < ?)"); + Filter filter1 = query.getFilter(); + query = query.not(); + Filter filter2 = query.getFilter(); + query = storage.query("intProp != ? | (stringProp < ? & longProp >= ?)"); + Filter 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 storage = + getRepository().storageFor(StorableTestBasic.class); + + Query query; + List 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 storage = + getRepository().storageFor(StorableTestBasic.class); + + Query query; + List 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 storage = + getRepository().storageFor(StorableTestBasicIndexed.class); + + StorableTestBasicIndexed stb; + Query query; + List 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 storage = + getRepository().storageFor(StorableTestBasicIndexed.class); + + Query query; + List results; + + { + query = storage.query("intProp = ? & (stringProp >= ? | longProp < ?)"); + Filter filter1 = query.getFilter(); + query = query.not(); + Filter filter2 = query.getFilter(); + query = storage.query("intProp != ? | (stringProp < ? & longProp >= ?)"); + Filter 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 storage = + getRepository().storageFor(StorableTestBasicIndexed.class); + + Query query; + List 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 storage = + getRepository().storageFor(StorableTestBasicIndexed.class); + + Query query; + List 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 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 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 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 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 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 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 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 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 storage = + getRepository().storageFor(StorableTestAssymetric.class); + } + catch (Exception e) { + fail("exception creating storable with assymetric concrete getter?" + e); + } + try { + Storage storage = + getRepository().storageFor(StorableTestAssymetricGet.class); + fail("Created assymetric storabl`e"); + } + catch (Exception e) { + } + try { + Storage storage = + getRepository().storageFor(StorableTestAssymetricSet.class); + fail("Created assymetric storable"); + } + catch (Exception e) { + } + try { + Storage 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 q = storage.query("stringProp = ?").with("hello"); + List 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 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 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 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 uaStorage = getRepository().storageFor(UserAddress.class); + Storage 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 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 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 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 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 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 storage = getRepository().storageFor(StorableVersioned.class); + + class InsertTrigger extends Trigger { + 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 { + 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 { + 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 storage = getRepository().storageFor(StorableVersioned.class); + + class InsertTrigger extends Trigger { + 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 { + 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 { + 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 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() { + @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 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 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 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 storage = getRepository().storageFor(ManyKeys2.class); + + final int groupSize = 4; + final int aliasing = groupSize + 1; + + int total = 0; + for (int a=0; a query = storage.query().orderBy(orderBy); + Cursor cursor = query.fetch(); + Comparator 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(null); + fail(); + } catch (IllegalArgumentException e) { + } + + Cursor cursor = new SingletonCursor("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("world"); + List list = cursor.toList(0); + assertEquals(0, list.size()); + list = cursor.toList(10); + assertEquals(1, list.size()); + assertEquals("world", list.get(0)); + + cursor = new SingletonCursor("world"); + cursor.close(); + assertFalse(cursor.hasNext()); + + cursor = new SingletonCursor("world"); + assertEquals(1, cursor.skipNext(2)); + assertEquals(0, cursor.skipNext(1)); + assertFalse(cursor.hasNext()); + } + + public void testUnion() throws Exception { + Cursor left, right, union; + + // Two empty sets. + left = createElements(); + right = createElements(); + union = new UnionCursor(left, right, new ElementComparator()); + + compareElements(union); + + // Right set empty. + left = createElements(1, 2, 3, 4); + right = createElements(); + union = new UnionCursor(left, right, new ElementComparator()); + + compareElements(union, 1, 2, 3, 4); + + // Left set empty. + left = createElements(); + right = createElements(3, 4, 5, 6); + union = new UnionCursor(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(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(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(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(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(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(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 left, right, inter; + + // Two empty sets. + left = createElements(); + right = createElements(); + inter = new IntersectionCursor(left, right, new ElementComparator()); + + compareElements(inter); + + // Right set empty. + left = createElements(1, 2, 3, 4); + right = createElements(); + inter = new IntersectionCursor(left, right, new ElementComparator()); + + compareElements(inter); + + // Left set empty. + left = createElements(); + right = createElements(3, 4, 5, 6); + inter = new IntersectionCursor(left, right, new ElementComparator()); + + compareElements(inter); + + // No overlap. + left = createElements(1, 2, 3 ); + right = createElements( 4, 5, 6); + inter = new IntersectionCursor(left, right, new ElementComparator()); + + compareElements(inter); + + // Overlap. + left = createElements(1, 2, 3, 4 ); + right = createElements( 3, 4, 5, 6); + inter = new IntersectionCursor(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(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(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(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(left, right, new ElementComparator()); + + compareElements(inter, 1, 2, 2); + } + + public void testDifference() throws Exception { + Cursor left, right, diff; + + // Two empty sets. + left = createElements(); + right = createElements(); + diff = new DifferenceCursor(left, right, new ElementComparator()); + + compareElements(diff); + + // Right set empty. + left = createElements(1, 2, 3, 4); + right = createElements(); + diff = new DifferenceCursor(left, right, new ElementComparator()); + + compareElements(diff, 1, 2, 3, 4); + + // Left set empty. + left = createElements(); + right = createElements(3, 4, 5, 6); + diff = new DifferenceCursor(left, right, new ElementComparator()); + + compareElements(diff); + + // No overlap. + left = createElements(1, 2, 3 ); + right = createElements( 4, 5, 6); + diff = new DifferenceCursor(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(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(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(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(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(left, right, new ElementComparator()); + + compareElements(diff, 3, 3, 3, 3); + } + + public void testSymmetricDifference() throws Exception { + Cursor left, right, diff; + + // Two empty sets. + left = createElements(); + right = createElements(); + diff = new SymmetricDifferenceCursor(left, right, new ElementComparator()); + + compareElements(diff); + + // Right set empty. + left = createElements(1, 2, 3, 4); + right = createElements(); + diff = new SymmetricDifferenceCursor(left, right, new ElementComparator()); + + compareElements(diff, 1, 2, 3, 4); + + // Left set empty. + left = createElements(); + right = createElements(3, 4, 5, 6); + diff = new SymmetricDifferenceCursor(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(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(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(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(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(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(left, right, new ElementComparator()); + + compareElements(diff, 1, 1, 1, 1, 2, 3, 3, 3, 3, 4, 4, 4); + } + + private Cursor createElements(int... ids) { + Arrays.sort(ids); + Element[] elements = new Element[ids.length]; + for (int i=0; i(Arrays.asList(elements)); + } + + private void compareElements(Cursor 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 { + 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 triples = new ArrayList(); + 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 cursor = new GroupedCursor + (new IteratorCursor(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 pairs = new ArrayList(); + + 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
filter = Filter.getOpenFilter(Address.class); + + Filter f2 = filter.asJoinedFrom(Order.class, "address"); + assertTrue(f2 instanceof OpenFilter); + assertEquals(Order.class, f2.getStorableType()); + + Filter f3 = filter.asJoinedFrom(Shipment.class, "order.address"); + assertTrue(f3 instanceof OpenFilter); + assertEquals(Shipment.class, f3.getStorableType()); + + Filter f4 = f2.asJoinedFrom(Shipment.class, "order"); + assertEquals(f3, f4); + assertTrue(f3 == f4); + } + + public void testClosed() { + Filter
filter = Filter.getClosedFilter(Address.class); + + Filter f2 = filter.asJoinedFrom(Order.class, "address"); + assertTrue(f2 instanceof ClosedFilter); + assertEquals(Order.class, f2.getStorableType()); + + Filter f3 = filter.asJoinedFrom(Shipment.class, "order.address"); + assertTrue(f3 instanceof ClosedFilter); + assertEquals(Shipment.class, f3.getStorableType()); + + Filter f4 = f2.asJoinedFrom(Shipment.class, "order"); + assertEquals(f3, f4); + assertTrue(f3 == f4); + } + + public void testSimple() { + Filter
filter = Filter.filterFor(Address.class, "addressCity = ?"); + + Filter f2 = filter.asJoinedFrom(Order.class, "address"); + assertEquals(f2, Filter.filterFor(Order.class, "address.addressCity = ?")); + + Filter f3 = filter.asJoinedFrom(Shipment.class, "order.address"); + assertEquals(f3, Filter.filterFor(Shipment.class, "order.address.addressCity = ?")); + + Filter f4 = f2.asJoinedFrom(Shipment.class, "order"); + assertEquals(f3, f4); + assertTrue(f3 == f4); + } + + public void testAnd() { + Filter
filter = Filter.filterFor + (Address.class, "addressCity = ? & addressZip != ?"); + + Filter f2 = filter.asJoinedFrom(Order.class, "address"); + assertEquals(f2, Filter.filterFor + (Order.class, "address.addressCity = ? & address.addressZip != ?")); + + Filter f3 = filter.asJoinedFrom(Shipment.class, "order.address"); + assertEquals(f3, Filter.filterFor + (Shipment.class, + "order.address.addressCity = ? & order.address.addressZip != ?")); + + Filter f4 = f2.asJoinedFrom(Shipment.class, "order"); + assertEquals(f3, f4); + assertTrue(f3 == f4); + } + + public void testOr() { + Filter
filter = Filter.filterFor + (Address.class, "addressCity = ? | addressZip != ?"); + + Filter f2 = filter.asJoinedFrom(Order.class, "address"); + assertEquals(f2, Filter.filterFor + (Order.class, "address.addressCity = ? | address.addressZip != ?")); + + Filter f3 = filter.asJoinedFrom(Shipment.class, "order.address"); + assertEquals(f3, Filter.filterFor + (Shipment.class, + "order.address.addressCity = ? | order.address.addressZip != ?")); + + Filter f4 = f2.asJoinedFrom(Shipment.class, "order"); + assertEquals(f3, f4); + assertTrue(f3 == f4); + } + + public void testMixed() { + Filter
filter = Filter.filterFor + (Address.class, "addressCity = ? & addressZip != ? | addressState > ?"); + + Filter f2 = filter.asJoinedFrom(Order.class, "address"); + assertEquals + (f2, Filter.filterFor + (Order.class, + "address.addressCity = ? & address.addressZip != ? | address.addressState > ?")); + + Filter f3 = filter.asJoinedFrom(Shipment.class, "order.address"); + assertEquals(f3, Filter.filterFor + (Shipment.class, + "order.address.addressCity = ? & order.address.addressZip != ? " + + " | order.address.addressState > ?")); + Filter f4 = f2.asJoinedFrom(Shipment.class, "order"); + assertEquals(f3, f4); + assertTrue(f3 == f4); + } + + public void testError() { + try { + Filter
filter = Filter.filterFor(Address.class, "addressCity = ?"); + Filter 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 filter = Filter.getOpenFilter(Shipment.class); + + Filter.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 filter = Filter.getClosedFilter(Shipment.class); + + Filter.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 filter = Filter.filterFor(Shipment.class, "shipmentNotes != ?"); + filter = filter.bind(); + + Filter.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 filter = Filter.filterFor + (Shipment.class, "shipmentNotes != ? & shipmentDate > ?"); + filter = filter.bind(); + + Filter.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 filter = Filter.filterFor + (Shipment.class, "shipmentNotes != ? | shipmentDate > ?"); + filter = filter.bind(); + + Filter.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 filter = Filter.filterFor + (Shipment.class, + "(order.address.addressCity = ? & order.address.addressZip = ?) " + + "| order.orderTotal != ?"); + filter = filter.bind(); + + Filter.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
unfiltered = createExecutor(1, 2, 3, 4); + Filter
filter = Filter.filterFor(Address.class, "addressCountry > ?"); + FilterValues
values = filter.initialFilterValues(); + + QueryExecutor
executor = new FilteredQueryExecutor
(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 StorableIndex makeIndex(Class type, String... props) { + return TestOrderingScore.makeIndex(type, props); + } + + public TestFilteringScore(String name) { + super(name); + } + + public void testNoFilter() throws Exception { + StorableIndex ix = makeIndex(StorableTestBasic.class, "id"); + Filter 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 ix = makeIndex(StorableTestBasic.class, "intProp"); + // Filter by a property not in index. + Filter 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 ix = makeIndex(StorableTestBasic.class, "id"); + // Filter by a property in index. + Filter 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 ix = makeIndex(StorableTestBasic.class, + "id", "intProp", "stringProp"); + // Filter by a property in index. + Filter 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 ix = makeIndex(StorableTestBasic.class, + "id", "intProp"); + Filter 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 ix_1, ix_2; + Filter filter; + FilteringScore score_1, score_2; + Comparator> 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 ix_1, ix_2; + Filter filter; + FilteringScore score_1, score_2; + Comparator> 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 ix_1, ix_2; + Filter 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> 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 ix = makeIndex(StorableTestBasic.class, "id"); + Filter filter; + FilteringScore score; + + filter = Filter.filterFor(StorableTestBasic.class, "id >= ? & id >= ? & id < ? & id <= ?"); + + score = FilteringScore.evaluate(ix, filter); + + assertEquals(2, score.getRangeStartFilters().size()); + + List> exStart = score.getExclusiveRangeStartFilters(); + assertEquals(0, exStart.size()); + + List> 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> exEnd = score.getExclusiveRangeEndFilters(); + assertEquals(1, exEnd.size()); + assertEquals(Filter.filterFor(StorableTestBasic.class, "id < ?"), exEnd.get(0)); + + List> inEnd = score.getInclusiveRangeEndFilters(); + assertEquals(1, inEnd.size()); + assertEquals(Filter.filterFor(StorableTestBasic.class, "id <= ?"), inEnd.get(0)); + } + + public void testKeyMatch() throws Exception { + StorableIndex ix = makeIndex(StorableTestBasic.class, "id", "intProp"); + ix = ix.unique(true); + Filter 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 StorableIndex makeIndex(Class 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
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
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 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> 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 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 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 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 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 iqa = + new IndexedQueryAnalyzer(Shipment.class, RepoAccess.INSTANCE); + Filter filter = Filter.filterFor + (Shipment.class, "order.address.addressState = ? & order.address.addressZip = ?"); + FilterValues values = filter.initialFilterValues(); + filter = values.getFilter(); + IndexedQueryAnalyzer.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 joinExec = JoinedQueryExecutor.build + (RepoAccess.INSTANCE, + result.getForeignProperty(), result.getFilter(), result.getOrdering()); + + FilterValues 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 iqa = + new IndexedQueryAnalyzer(Shipment.class, RepoAccess.INSTANCE); + Filter filter = Filter.filterFor + (Shipment.class, + "order.address.addressState = ? & order.address.addressID != ? " + + "& order.address.addressZip = ? & order.orderTotal > ? & shipmentNotes <= ? " + + "& order.addressID > ?"); + FilterValues values = filter.initialFilterValues(); + filter = values.getFilter(); + OrderingList 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 joinExec = JoinedQueryExecutor.build + (RepoAccess.INSTANCE, + result.getForeignProperty(), result.getFilter(), result.getOrdering()); + + FilterValues 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 joinExec2 = result.createExecutor(); + + StringBuffer buf2 = new StringBuffer(); + joinExec2.printPlan(buf2, 0, fv); + String plan2 = buf2.toString(); + + assertEquals(expected, plan2); + + Filter 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 StorageAccess storageAccessFor(Class type) { + return new StoreAccess(type); + } + } + + /** + * Partially implemented StorageAccess which only supplies information + * about indexes. + */ + static class StoreAccess + implements StorageAccess, QueryExecutorFactory + { + private final Class mType; + + StoreAccess(Class type) { + mType = type; + } + + public Class getStorableType() { + return mType; + } + + public QueryExecutorFactory getQueryExecutorFactory() { + return this; + } + + public QueryExecutor executor(Filter filter, OrderingList ordering) { + Iterable iterable = Collections.emptyList(); + + QueryExecutor exec = new IterableQueryExecutor + (filter.getStorableType(), iterable); + + if (filter != null) { + exec = new FilteredQueryExecutor(exec, filter); + } + + if (ordering != null && ordering.size() > 0) { + exec = new SortedQueryExecutor(null, exec, null, ordering); + } + + return exec; + } + + public Collection> getAllIndexes() { + StorableIndex[] 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 storageDelegate(StorableIndex index) { + return null; + } + + public SortBuffer createSortBuffer() { + return new ArraySortBuffer(); + } + + public long countAll() { + throw new UnsupportedOperationException(); + } + + public Cursor fetchAll() { + throw new UnsupportedOperationException(); + } + + public Cursor fetchOne(StorableIndex index, Object[] identityValues) { + throw new UnsupportedOperationException(); + } + + public Cursor fetchSubset(StorableIndex 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 StorableIndex makeIndex(Class type, String... props) { + return TestOrderingScore.makeIndex(type, props); + } + + static OrderingList makeOrdering(Class type, String... props) { + return TestOrderingScore.makeOrdering(type, props); + } + + public TestIndexedQueryExecutor(String name) { + super(name); + } + + public void testIdentityMatch() throws Exception { + StorableIndex index = + makeIndex(StorableTestBasic.class, "id", "-intProp", "doubleProp"); + + Filter filter = + Filter.filterFor(StorableTestBasic.class, "id = ?"); + FilterValues values = filter.initialFilterValues(); + filter = values.getFilter(); + + CompositeScore score = CompositeScore.evaluate(index, filter, null); + + Mock executor = new Mock(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(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(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 index = makeIndex(StorableTestBasic.class, "intProp"); + + Filter filter = + Filter.filterFor(StorableTestBasic.class, "intProp > ?"); + FilterValues values = filter.initialFilterValues(); + filter = values.getFilter(); + + CompositeScore score = CompositeScore.evaluate(index, filter, null); + + Mock executor = new Mock(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(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(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(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(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(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(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(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(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(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 index = makeIndex(StorableTestBasic.class, "intProp"); + + Filter filter = + Filter.filterFor(StorableTestBasic.class, "intProp < ?"); + FilterValues values = filter.initialFilterValues(); + filter = values.getFilter(); + + CompositeScore score = CompositeScore.evaluate(index, filter, null); + + Mock executor = new Mock(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(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(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(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(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(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(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(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(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(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 index = makeIndex(StorableTestBasic.class, "intProp"); + + Filter filter = + Filter.filterFor(StorableTestBasic.class, "intProp > ? & intProp < ?"); + FilterValues values = filter.initialFilterValues(); + filter = values.getFilter(); + + CompositeScore score = CompositeScore.evaluate(index, filter, null); + + Mock executor = new Mock(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(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(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 index; + Filter filter; + FilterValues values; + CompositeScore score; + Mock 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(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(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(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(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> 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 index; + Filter filter; + FilterValues values; + CompositeScore score; + Mock 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(index, score); + + assertEquals(values.getFilter(), executor.getFilter()); + List> 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 extends IndexedQueryExecutor + implements IndexedQueryExecutor.Support + { + Object[] mIdentityValues; + BoundaryType mRangeStartBoundary; + Object mRangeStartValue; + BoundaryType mRangeEndBoundary; + Object mRangeEndValue; + boolean mReverseRange; + boolean mReverseOrder; + + public Mock(StorableIndex index, CompositeScore score) { + super(null, index, score); + } + + public Cursor fetchSubset(StorableIndex 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 empty = Collections.emptyList(); + return new IteratorCursor(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 info = StorableIntrospector.examine(UserInfo.class); + Map> properties = info.getAllProperties(); + + RepositoryAccess repoAccess = new RepoAccess(); + + ChainedProperty targetToSourceProperty = + ChainedProperty.get(properties.get("address")); + + Filter targetFilter = Filter.filterFor(UserInfo.class, "address.state = ?"); + OrderingList targetOrdering = + OrderingList.get(UserInfo.class, "+address.country"); + + QueryExecutor 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 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 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 values = Filter + .filterFor(UserInfo.class, "address.state = ?").initialFilterValues().with("IL"); + + Cursor 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 StorageAccess storageAccessFor(Class type) { + return new StoreAccess(type); + } + } + + class StoreAccess implements StorageAccess, QueryExecutorFactory { + private final Class mType; + + StoreAccess(Class type) { + mType = type; + } + + public Class getStorableType() { + return mType; + } + + public QueryExecutorFactory getQueryExecutorFactory() { + return this; + } + + public QueryExecutor executor(Filter filter, OrderingList ordering) + throws RepositoryException + { + Storage storage = mRepository.storageFor(mType); + + QueryExecutor exec = new FullScanQueryExecutor + (new ScanQuerySupport(storage.query())); + + if (filter != null) { + exec = new FilteredQueryExecutor(exec, filter); + } + + if (ordering != null && ordering.size() > 0) { + exec = new SortedQueryExecutor(null, exec, null, ordering); + } + + return exec; + } + + public Collection> getAllIndexes() { + StorableIndex[] indexes = new StorableIndex[0]; + return Arrays.asList(indexes); + } + + public Storage storageDelegate(StorableIndex index) { + return null; + } + + public SortBuffer createSortBuffer() { + return new ArraySortBuffer(); + } + + public long countAll() { + throw new UnsupportedOperationException(); + } + + public Cursor fetchAll() { + throw new UnsupportedOperationException(); + } + + public Cursor fetchOne(StorableIndex index, Object[] identityValues) { + throw new UnsupportedOperationException(); + } + + public Cursor fetchSubset(StorableIndex index, + Object[] identityValues, + BoundaryType rangeStartBoundary, + Object rangeStartValue, + BoundaryType rangeEndBoundary, + Object rangeEndValue, + boolean reverseRange, + boolean reverseOrder) + { + throw new UnsupportedOperationException(); + } + } + + static class ScanQuerySupport implements FullScanQueryExecutor.Support { + private final Query mQuery; + + ScanQuerySupport(Query query) { + mQuery = query; + } + + public Class getStorableType() { + return mQuery.getStorableType(); + } + + public long countAll() throws FetchException { + return mQuery.count(); + } + + public Cursor 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> list_1 = + OrderingList.get(StorableTestBasic.class, "date"); + + assertEquals(1, list_1.size()); + assertEquals("+date", list_1.get(0).toString()); + + List> 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> 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> 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> 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> 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> list = + OrderingList.get(StorableTestBasic.class, "~date"); + try { + list.set(0, list.get(0).reverse()); + fail(); + } catch (UnsupportedOperationException e) { + } + } + + public void testConcatList() throws Exception { + OrderingList list_1 = + OrderingList.get(StorableTestBasic.class, "date", "-intProp", "~stringProp"); + + OrderingList list_2 = + OrderingList.get(StorableTestBasic.class, "longProp", "doubleProp"); + + OrderingList 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 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 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 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 list_1 = + OrderingList.get(StorableTestBasic.class, "date", "-intProp", "~stringProp"); + + OrderedProperty op_0 = list_1.get(0); + OrderedProperty op_1 = list_1.get(1); + OrderedProperty 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 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 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 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 list = + OrderingList.get(StorableTestBasic.class, "date", "intProp", "stringProp"); + + OrderedProperty[] 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 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 StorableIndex makeIndex(Class type, String... props) { + return new StorableIndex(makeOrdering(type, props).asArray(), UNSPECIFIED); + } + + static OrderingList makeOrdering(Class type, String... props) { + return OrderingList.get(type, props); + } + + public TestOrderingScore(String name) { + super(name); + } + + public void testEmpty() throws Exception { + StorableIndex 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 ix; + OrderingList ops; + OrderingScore 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 ix; + OrderingList ops; + OrderingScore 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 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 ix; + OrderingList ops; + OrderingScore score; + Filter 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 ix_1, ix_2; + OrderingList ops; + OrderingScore score_1, score_2; + Filter filter; + Comparator> 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 ix; + OrderingList ops; + OrderingScore score; + Filter 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 ix; + OrderingList ops; + OrderingScore score; + Filter 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 ix; + OrderingList ops; + OrderingScore score; + Filter 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 ix; + OrderingList ops; + OrderingScore score; + Filter 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 ix; + OrderingList ops; + OrderingScore score; + Filter 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 ix; + OrderingList ops; + OrderingScore score; + Filter 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 filter = Filter.getOpenFilter(StorableTestBasic.class); + assertEquals(0, PropertyFilterList.get(filter).size()); + } + + public void testClosed() throws Exception { + Filter filter = Filter.getClosedFilter(StorableTestBasic.class); + assertEquals(0, PropertyFilterList.get(filter).size()); + } + + public void testSingleton() throws Exception { + Filter filter = Filter.filterFor(StorableTestBasic.class, "id = ?"); + + List> list = PropertyFilterList.get(filter); + + assertEquals(1, list.size()); + assertEquals(filter, list.get(0)); + + List> list2 = PropertyFilterList.get(filter); + + assertTrue(list == list2); + } + + public void testMultiple() throws Exception { + Filter filter = + Filter.filterFor(StorableTestBasic.class, "id = ? & intProp > ?"); + + List> list = PropertyFilterList.get(filter); + + assertEquals(2, list.size()); + + Filter subFilter = + Filter.filterFor(StorableTestBasic.class, "id = ?"); + + assertEquals(subFilter, list.get(0)); + + subFilter = Filter.filterFor(StorableTestBasic.class, "intProp > ?"); + + assertEquals(subFilter, list.get(1)); + + List> list2 = PropertyFilterList.get(filter); + + assertTrue(list == list2); + } + + public void testIllegal() throws Exception { + Filter filter = + Filter.filterFor(StorableTestBasic.class, "id = ? | intProp > ?"); + + try { + List> 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
createExecutor(int... ids) { + return new IterableQueryExecutor
(Address.class, createCollection(ids)); + } + + protected Collection
createCollection(int... ids) { + Collection
elements = new ArrayList
(ids.length); + for (int i=0; i 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
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
unsorted = createExecutor(4, 2, 3, 1); + Filter
filter = Filter.getOpenFilter(Address.class); + FilterValues
values = filter.initialFilterValues(); + OrderingList
ordered = createOrdering("addressCountry"); + + QueryExecutor
executor = + new SortedQueryExecutor
(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
unsorted = createExecutor(1, 2, 3, 4); + Filter
filter = Filter.getOpenFilter(Address.class); + FilterValues
values = filter.initialFilterValues(); + OrderingList
handled = createOrdering("addressCountry"); + OrderingList
finisher = createOrdering("addressState"); + + QueryExecutor
executor = + new SortedQueryExecutor
(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 StorableIndex makeIndex(Class type, String... props) { + return TestOrderingScore.makeIndex(type, props); + } + + static OrderingList makeOrdering(Class 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.Result> subResults = result.getSubResults(); + + assertEquals(1, subResults.size()); + } + + public void testSingleSubResult() throws Exception { + UnionQueryAnalyzer uqa = + new UnionQueryAnalyzer(Shipment.class, TestIndexedQueryAnalyzer.RepoAccess.INSTANCE); + Filter filter = Filter.filterFor(Shipment.class, "shipmentID = ?"); + filter = filter.bind(); + UnionQueryAnalyzer.Result result = uqa.analyze(filter, null); + List.Result> subResults = result.getSubResults(); + + assertEquals(1, subResults.size()); + } + + public void testSingleSubResultUnspecifiedDirection() throws Exception { + UnionQueryAnalyzer uqa = + new UnionQueryAnalyzer(Shipment.class, TestIndexedQueryAnalyzer.RepoAccess.INSTANCE); + Filter filter = Filter.filterFor(Shipment.class, "shipmentID > ?"); + filter = filter.bind(); + OrderingList orderings = + makeOrdering(Shipment.class, "~shipmentID", "~orderID"); + UnionQueryAnalyzer.Result result = uqa.analyze(filter, orderings); + List.Result> subResults = result.getSubResults(); + + assertEquals(1, subResults.size()); + List> 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 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.Result> subResults = result.getSubResults(); + + assertEquals(2, subResults.size()); + IndexedQueryAnalyzer.Result res_0 = subResults.get(0); + IndexedQueryAnalyzer.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 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.Result> subResults = result.getSubResults(); + + assertEquals(2, subResults.size()); + IndexedQueryAnalyzer.Result res_0 = subResults.get(0); + IndexedQueryAnalyzer.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 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.Result> subResults = result.getSubResults(); + + assertEquals(2, subResults.size()); + IndexedQueryAnalyzer.Result res_0 = subResults.get(0); + IndexedQueryAnalyzer.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> 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 filter = Filter.filterFor(Shipment.class, + "shipmentID > ? | orderID = ?"); + filter = filter.bind(); + OrderingList orderings = + makeOrdering(Shipment.class, "~shipmentID", "~orderID"); + UnionQueryAnalyzer.Result result = uqa.analyze(filter, orderings); + assertEquals(OrderingList.get(Shipment.class, "+shipmentID", "+orderID"), + result.getTotalOrdering()); + List.Result> subResults = result.getSubResults(); + + assertEquals(2, subResults.size()); + IndexedQueryAnalyzer.Result res_0 = subResults.get(0); + IndexedQueryAnalyzer.Result res_1 = subResults.get(1); + + List> 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 filter = Filter.filterFor + (Shipment.class, + "shipmentID = ? & (shipmentID = ? | orderID = ?)"); + filter = filter.bind(); + UnionQueryAnalyzer.Result result = uqa.analyze(filter, null); + List.Result> subResults = result.getSubResults(); + + assertEquals(1, subResults.size()); + IndexedQueryAnalyzer.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 filter = Filter.filterFor + (Shipment.class, "shipmentNotes = ? | shipperID = ?"); + filter = filter.bind(); + UnionQueryAnalyzer.Result result = uqa.analyze(filter, null); + List.Result> subResults = result.getSubResults(); + + assertEquals(1, subResults.size()); + IndexedQueryAnalyzer.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 filter = Filter.filterFor + (Shipment.class, "shipmentNotes = ? | orderID = ?"); + filter = filter.bind(); + UnionQueryAnalyzer.Result result = uqa.analyze(filter, null); + List.Result> subResults = result.getSubResults(); + + assertEquals(1, subResults.size()); + IndexedQueryAnalyzer.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 filter = Filter.filterFor + (Shipment.class, "shipmentNotes = ? | orderID = ? & order.orderTotal > ?"); + filter = filter.bind(); + UnionQueryAnalyzer.Result result = uqa.analyze(filter, null); + List.Result> subResults = result.getSubResults(); + + assertEquals(2, subResults.size()); + IndexedQueryAnalyzer.Result res_0 = subResults.get(0); + IndexedQueryAnalyzer.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 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 exec = result.createExecutor(); + + assertEquals(filter, exec.getFilter()); + assertEquals(OrderingList.get(StorableTestBasic.class, "+id"), exec.getOrdering()); + + List.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 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 exec = result.createExecutor(); + + assertEquals(filter, exec.getFilter()); + assertEquals(OrderingList.get(StorableTestBasic.class, "+doubleProp", "+stringProp"), + exec.getOrdering()); + + List.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 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 exec = result.createExecutor(); + assertEquals(OrderingList.get(StorableTestBasic.class, "+stringProp", "+doubleProp"), + exec.getOrdering()); + + List.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 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 exec = result.createExecutor(); + + assertEquals(filter, exec.getFilter()); + assertEquals(OrderingList.get(StorableTestBasic.class, "+doubleProp", "+id"), + exec.getOrdering()); + + List.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 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 exec = result.createExecutor(); + + assertEquals(filter, exec.getFilter()); + assertEquals(OrderingList.get(StorableTestBasic.class, "+stringProp", "+doubleProp"), + exec.getOrdering()); + + List.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
primary = new SortedQueryExecutor
+ (null, createExecutor(1, 2, 3, 4, 5, 6, 7, 8), null, createOrdering("addressID")); + + Filter
filter_1 = Filter.filterFor(Address.class, "addressCountry > ?"); + FilterValues
values_1 = filter_1.initialFilterValues(); + QueryExecutor
executor_1 = new FilteredQueryExecutor
(primary, filter_1); + + Filter
filter_2 = Filter.filterFor(Address.class, "addressState <= ?"); + FilterValues
values_2 = filter_2.initialFilterValues(); + QueryExecutor
executor_2 = new FilteredQueryExecutor
(primary, filter_2); + + QueryExecutor
union = new UnionQueryExecutor
(executor_1, executor_2); + + Filter
filter = Filter + .filterFor(Address.class, "addressCountry > ? | addressState <= ?"); + FilterValues
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}. + *

+ * 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 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 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 + * 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[] mProperties; + + private Random mRandom; + + public TestEncodingStrategy(String name) { + super(name); + mSeed = 986184829029842L; + Collection> 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[] 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 0) { + // Fill in with data which should be ignored by decoder. + for (int p=0; p= 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[] 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 0) { + // Fill in with data which should be ignored by decoder. + for (int p=0; p 0) { + for (int p=0; p 0) { + for (int p=0; p 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[] 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 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. + * + *

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[] 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[] 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[] 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[] 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[] selectProperties(int minCount, int maxCount) { + int length = (minCount == maxCount) ? minCount + : (mRandom.nextInt(maxCount - minCount + 1) + minCount); + + StorableProperty[] 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[] makeOrderedProperties + (StorableProperty[] properties, Direction[] directions) { + + int length = properties.length; + OrderedProperty[] 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 + * 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 mStorages; + private final Map mSequences; + + public ToyRepository() { + this("toy"); + } + + public ToyRepository(String name) { + mName = name; + mStorages = new HashMap(); + mSequences = new HashMap(); + } + + public String getName() { + return mName; + } + + public Storage storageFor(Class type) + throws SupportException, RepositoryException + { + synchronized (mStorages) { + Storage storage = (Storage) mStorages.get(type); + if (storage == null) { + storage = new ToyStorage(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 getCapability(Class 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 { + private static final Map cCache; + + static { + cCache = new SoftValuedHashMap(); + } + + /** + * Generated class has a constructor that accepts a ToyStorage instance. + */ + public static Class getGeneratedClass(Class type) + throws SupportException + { + synchronized (cCache) { + Class generatedClass = (Class) cCache.get(type); + if (generatedClass != null) { + return generatedClass; + } + generatedClass = new ToyStorableGenerator(type).generateAndInjectClass(); + cCache.put(type, generatedClass); + return generatedClass; + } + } + + private final Class mStorableType; + + private final ClassInjector mClassInjector; + private final ClassFile mClassFile; + + private ToyStorableGenerator(Class type) throws SupportException { + mStorableType = type; + + EnumSet features = EnumSet + .of(MasterFeature.VERSIONING, MasterFeature.INSERT_SEQUENCES); + + final Class 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 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 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 + implements Storage, MasterSupport, QueryFactory, QueryExecutorFactory +{ + final ToyRepository mRepo; + final Class mType; + + final InstanceFactory mInstanceFactory; + + final Collection mData; + final Lock mDataLock; + + public ToyStorage(ToyRepository repo, Class type) throws SupportException { + StorableIntrospector.examine(type); + mRepo = repo; + mType = type; + + Class generatedStorableClass = ToyStorableGenerator.getGeneratedClass(type); + mInstanceFactory = QuickConstructorGenerator + .getInstance(generatedStorableClass, InstanceFactory.class); + + mData = new LinkedList(); + mDataLock = new ReentrantLock(); + } + + public Class getStorableType() { + return mType; + } + + public S prepare() { + return (S) mInstanceFactory.instantiate(this); + } + + public Query query() { + return new ToyQuery(null, null, null); + } + + public Query query(String filter) { + return query(Filter.filterFor(mType, filter)); + } + + public Query query(Filter filter) { + return new ToyQuery(filter.initialFilterValues(), null, null); + } + + public Query query(FilterValues values, OrderingList ordering) { + return new ToyQuery(values, ordering, null); + } + + public QueryExecutor executor(Filter filter, OrderingList ordering) { + QueryExecutor executor = new IterableQueryExecutor(mType, mData, mDataLock); + + if (filter != null) { + executor = new FilteredQueryExecutor(executor, filter); + } + + if (ordering.size() > 0) { + executor = new SortedQueryExecutor(null, executor, null, ordering); + } + + return executor; + } + + public boolean addTrigger(Trigger trigger) { + return false; + } + + public boolean removeTrigger(Trigger 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 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 getInsertTrigger() { + return null; + } + + public Trigger getUpdateTrigger() { + return null; + } + + public Trigger 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 { + ToyQuery(FilterValues values, OrderingList ordering, QueryExecutor executor) { + super(values, ordering, executor); + } + + protected Transaction enterTransaction(IsolationLevel level) { + return mRepo.enterTransaction(level); + } + + protected QueryFactory queryFactory() { + return ToyStorage.this; + } + + protected QueryExecutorFactory executorFactory() { + return ToyStorage.this; + } + + protected StandardQuery newInstance(FilterValues values, + OrderingList ordering, + QueryExecutor 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 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 storage = openStorage(args[0]); + if (args.length > 1) { + populate(storage, new File(args[1]), null); + } + } + + private static void populate(Storage 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 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 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 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 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 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 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 storage = mRepository.storageFor(StorableWithLobs.class); + StorableWithLobs s = storage.prepare(); + s.setBlobValue(new ByteArrayBlob("Hello Blob".getBytes())); + s.setClobValue(new StringClob("Hello Clob")); + + StorableSerializer 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(); + afterTriggers = new ArrayList(); + failedTriggers = new ArrayList(); + } + + @Override + protected void tearDown() { + } + + List beforeTriggers; + List afterTriggers; + List failedTriggers; + + public void testAddAndRemove() { + TriggerManager set = new TriggerManager(null, null); + Trigger trigger = new TestTrigger(); + + 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 trigger2 = new TestTrigger(); + 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 set = new TriggerManager(null, null); + TestTrigger trigger = new TestTrigger(); + 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 set = new TriggerManager(null, null); + TestTrigger trigger = new TestTrigger(); + 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 set = new TriggerManager(null, null); + TestTrigger trigger = new TestTrigger(null); + TestTrigger trigger2 = new TestTrigger(); + 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 extends Trigger { + 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 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 { + @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 getOrderItems() throws FetchException; + + @Join + Query getShipments() throws FetchException; + + @Join + Query 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 { + @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 { + 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 { + @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 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 { + @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 { + @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 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 { + 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 { + 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 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 builder + = new SyntheticStorableReferenceBuilder(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 + * @param storableType + * @throws SupportException + * @throws RepositoryException + * @throws IllegalAccessException + * @throws InvocationTargetException + * @throws PersistException + * @throws FetchException + */ + protected void exerciseStorable(Class storableType) throws SupportException, RepositoryException, IllegalAccessException, InvocationTargetException, PersistException, FetchException { + T persister = mRepository.storageFor(storableType).prepare(); + Map 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 Map populate(T storable) + throws IllegalAccessException, InvocationTargetException { + ValueVendor vendor = new ValueVendor(); + Map valueMap = new HashMap(); + 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 expectedMethods = new HashMap(); + 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 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 { + 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); + } + } +} -- cgit v1.2.3