From ee3de4cabc79aabf21197b24af6d233e5e4a50a4 Mon Sep 17 00:00:00 2001 From: "Brian S. O'Neill" Date: Wed, 30 Aug 2006 21:12:19 +0000 Subject: Added small set of tests that don't need an actual Repository instance. --- .../carbonado/cursor/EmptyCursorFactory.java | 30 + .../com/amazon/carbonado/cursor/TestCursors.java | 389 +++++++++++ .../amazon/carbonado/cursor/TestGroupedCursor.java | 184 +++++ .../carbonado/qe/TestFilteredQueryExecutor.java | 57 ++ .../amazon/carbonado/qe/TestFilteringScore.java | 746 +++++++++++++++++++++ .../carbonado/qe/TestIndexedQueryExecutor.java | 725 ++++++++++++++++++++ .../com/amazon/carbonado/qe/TestOrderingScore.java | 517 ++++++++++++++ .../carbonado/qe/TestPropertyFilterList.java | 108 +++ .../com/amazon/carbonado/qe/TestQueryExecutor.java | 195 ++++++ .../carbonado/qe/TestSortedQueryExecutor.java | 84 +++ .../carbonado/qe/TestUnionQueryExecutor.java | 75 +++ .../carbonado/spi/TestConversionComparator.java | 179 +++++ .../com/amazon/carbonado/spi/TestCursorList.java | 224 +++++++ .../amazon/carbonado/spi/TestTriggerManager.java | 334 +++++++++ .../java/com/amazon/carbonado/stored/Address.java | 64 ++ .../java/com/amazon/carbonado/stored/Dummy.java | 145 ++++ .../amazon/carbonado/stored/StorableTestBasic.java | 114 ++++ .../carbonado/stored/StorableTestMinimal.java | 32 + .../amazon/carbonado/util/TestBelatedCreator.java | 244 +++++++ .../util/TestQuickConstructorGenerator.java | 102 +++ .../amazon/carbonado/util/TestThrowUnchecked.java | 54 ++ 21 files changed, 4602 insertions(+) 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/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/TestIndexedQueryExecutor.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/TestUnionQueryExecutor.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/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/StorableTestBasic.java create mode 100644 src/test/java/com/amazon/carbonado/stored/StorableTestMinimal.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/java/com/amazon/carbonado') 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..f063a59 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/cursor/TestCursors.java @@ -0,0 +1,389 @@ +/* + * 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 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 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/qe/TestFilteredQueryExecutor.java b/src/test/java/com/amazon/carbonado/qe/TestFilteredQueryExecutor.java new file mode 100644 index 0000000..76ea081 --- /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.openCursor(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/TestIndexedQueryExecutor.java b/src/test/java/com/amazon/carbonado/qe/TestIndexedQueryExecutor.java new file mode 100644 index 0000000..a039e63 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/qe/TestIndexedQueryExecutor.java @@ -0,0 +1,725 @@ +/* + * 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 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 OrderedProperty[] makeOrderings(Class type, String... props) + { + return TestOrderingScore.makeOrderings(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); + + Mock executor = new Mock(index, score); + + executor.openCursor(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); + + executor = new Mock(index, score); + + executor.openCursor(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); + + executor = new Mock(index, score); + + executor.openCursor(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); + + Mock executor = new Mock(index, score); + + executor.openCursor(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); + + executor = new Mock(index, score); + + executor.openCursor(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); + + executor = new Mock(index, score); + + executor.openCursor(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); + + executor = new Mock(index, score); + + executor.openCursor(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); + + executor = new Mock(index, score); + + executor.openCursor(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); + + executor = new Mock(index, score); + + executor.openCursor(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, + makeOrderings(StorableTestBasic.class, "-intProp")); + + executor = new Mock(index, score); + + executor.openCursor(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); + + executor = new Mock(index, score); + + executor.openCursor(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, + makeOrderings(StorableTestBasic.class, "-intProp")); + + executor = new Mock(index, score); + + executor.openCursor(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, + makeOrderings(StorableTestBasic.class, "intProp")); + + executor = new Mock(index, score); + + executor.openCursor(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); + + Mock executor = new Mock(index, score); + + executor.openCursor(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); + + executor = new Mock(index, score); + + executor.openCursor(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); + + executor = new Mock(index, score); + + executor.openCursor(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); + + executor = new Mock(index, score); + + executor.openCursor(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); + + executor = new Mock(index, score); + + executor.openCursor(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); + + executor = new Mock(index, score); + + executor.openCursor(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, + makeOrderings(StorableTestBasic.class, "-intProp")); + + executor = new Mock(index, score); + + executor.openCursor(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); + + executor = new Mock(index, score); + + executor.openCursor(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, + makeOrderings(StorableTestBasic.class, "-intProp")); + + executor = new Mock(index, score); + + executor.openCursor(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, + makeOrderings(StorableTestBasic.class, "intProp")); + + executor = new Mock(index, score); + + executor.openCursor(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); + + Mock executor = new Mock(index, score); + + executor.openCursor(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); + + executor = new Mock(index, score); + + executor.openCursor(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); + + executor = new Mock(index, score); + + executor.openCursor(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); + + executor = new Mock(index, score); + + executor.openCursor(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, + makeOrderings(StorableTestBasic.class, "doubleProp")); + + executor = new Mock(index, score); + + executor.openCursor(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, + makeOrderings(StorableTestBasic.class, "stringProp")); + + executor = new Mock(index, score); + + executor.openCursor(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, + makeOrderings(StorableTestBasic.class, "-stringProp")); + + executor = new Mock(index, score); + + executor.openCursor(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()); + OrderedProperty[] expectedOrdering = + makeOrderings(StorableTestBasic.class, "intProp", "-doubleProp", "stringProp"); + assertEquals(Arrays.asList(expectedOrdering), executor.getOrdering()); + } + + /** + * Mock object doesn't really open a cursor -- it just captures the passed + * parameters. + */ + static class Mock extends IndexedQueryExecutor { + Object[] mIdentityValues; + BoundaryType mRangeStartBoundary; + Object mRangeStartValue; + BoundaryType mRangeEndBoundary; + Object mRangeEndValue; + boolean mReverseRange; + boolean mReverseOrder; + + public Mock(StorableIndex index, CompositeScore score) { + super(index, score); + } + + protected Cursor openCursor(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/TestOrderingScore.java b/src/test/java/com/amazon/carbonado/qe/TestOrderingScore.java new file mode 100644 index 0000000..3929f59 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/qe/TestOrderingScore.java @@ -0,0 +1,517 @@ +/* + * 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.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(makeOrderings(type, props), UNSPECIFIED); + } + + static OrderedProperty[] makeOrderings(Class type, String... props) + { + StorableInfo info = StorableIntrospector.examine(type); + OrderedProperty[] ops = new OrderedProperty[props.length]; + for (int i=0; i ix = makeIndex(StorableTestBasic.class, "id"); + + OrderingScore score = OrderingScore.evaluate(ix, null); + + assertEquals(0, score.getHandledCount()); + assertEquals(0, score.getRemainderCount()); + assertEquals(false, score.shouldReverseOrder()); + } + + public void testOneProp() throws Exception { + StorableIndex ix; + OrderedProperty[] ops; + OrderingScore score; + + ///////////// + ix = makeIndex(StorableTestBasic.class, "id"); + + ops = makeOrderings(StorableTestBasic.class, "id"); + score = OrderingScore.evaluate(ix, null, ops); + + assertEquals(1, score.getHandledCount()); + assertEquals("+id", score.getHandledOrderings().get(0).toString()); + assertEquals(0, score.getRemainderCount()); + assertEquals(false, score.shouldReverseOrder()); + + ops = makeOrderings(StorableTestBasic.class, "+id"); + score = OrderingScore.evaluate(ix, null, ops); + + assertEquals(1, score.getHandledCount()); + assertEquals("+id", score.getHandledOrderings().get(0).toString()); + assertEquals(0, score.getRemainderCount()); + assertEquals(false, score.shouldReverseOrder()); + + ops = makeOrderings(StorableTestBasic.class, "-id"); + score = OrderingScore.evaluate(ix, null, ops); + + assertEquals(1, score.getHandledCount()); + assertEquals("-id", score.getHandledOrderings().get(0).toString()); + assertEquals(0, score.getRemainderCount()); + assertEquals(true, score.shouldReverseOrder()); + + ///////////// + ix = makeIndex(StorableTestBasic.class, "+id"); + + ops = makeOrderings(StorableTestBasic.class, "id"); + score = OrderingScore.evaluate(ix, null, ops); + + assertEquals(1, score.getHandledCount()); + assertEquals("+id", score.getHandledOrderings().get(0).toString()); + assertEquals(0, score.getRemainderCount()); + assertEquals(false, score.shouldReverseOrder()); + + ops = makeOrderings(StorableTestBasic.class, "+id"); + score = OrderingScore.evaluate(ix, null, ops); + + assertEquals(1, score.getHandledCount()); + assertEquals("+id", score.getHandledOrderings().get(0).toString()); + assertEquals(0, score.getRemainderCount()); + assertEquals(false, score.shouldReverseOrder()); + + ops = makeOrderings(StorableTestBasic.class, "-id"); + score = OrderingScore.evaluate(ix, null, ops); + + assertEquals(1, score.getHandledCount()); + assertEquals("-id", score.getHandledOrderings().get(0).toString()); + assertEquals(0, score.getRemainderCount()); + assertEquals(true, score.shouldReverseOrder()); + + ///////////// + ix = makeIndex(StorableTestBasic.class, "-id"); + + ops = makeOrderings(StorableTestBasic.class, "id"); + score = OrderingScore.evaluate(ix, null, ops); + + assertEquals(1, score.getHandledCount()); + assertEquals("+id", score.getHandledOrderings().get(0).toString()); + assertEquals(0, score.getRemainderCount()); + assertEquals(true, score.shouldReverseOrder()); + + ops = makeOrderings(StorableTestBasic.class, "+id"); + score = OrderingScore.evaluate(ix, null, ops); + + assertEquals(1, score.getHandledCount()); + assertEquals("+id", score.getHandledOrderings().get(0).toString()); + assertEquals(0, score.getRemainderCount()); + assertEquals(true, score.shouldReverseOrder()); + + ops = makeOrderings(StorableTestBasic.class, "-id"); + score = OrderingScore.evaluate(ix, null, ops); + + assertEquals(1, score.getHandledCount()); + assertEquals("-id", score.getHandledOrderings().get(0).toString()); + assertEquals(0, score.getRemainderCount()); + assertEquals(false, score.shouldReverseOrder()); + + ///////////// + ix = makeIndex(StorableTestBasic.class, "intProp"); + + ops = makeOrderings(StorableTestBasic.class, "id"); + score = OrderingScore.evaluate(ix, null, ops); + + assertEquals(0, score.getHandledCount()); + assertEquals(1, score.getRemainderCount()); + assertEquals("+id", score.getRemainderOrderings().get(0).toString()); + assertEquals(false, score.shouldReverseOrder()); + } + + public void testMultipleProps() throws Exception { + final StorableIndex ix; + OrderedProperty[] ops; + OrderingScore score; + + ix = makeIndex(StorableTestBasic.class, "id", "intProp"); + + ops = makeOrderings(StorableTestBasic.class, "id"); + score = OrderingScore.evaluate(ix, null, ops); + assertEquals(1, score.getHandledCount()); + assertEquals("+id", score.getHandledOrderings().get(0).toString()); + assertEquals(0, score.getRemainderCount()); + assertEquals(false, score.shouldReverseOrder()); + + ops = makeOrderings(StorableTestBasic.class, "-id"); + score = OrderingScore.evaluate(ix, null, ops); + assertEquals(1, score.getHandledCount()); + assertEquals("-id", score.getHandledOrderings().get(0).toString()); + assertEquals(0, score.getRemainderCount()); + assertEquals(true, score.shouldReverseOrder()); + + ops = makeOrderings(StorableTestBasic.class, "id", "intProp"); + score = OrderingScore.evaluate(ix, null, ops); + assertEquals(2, score.getHandledCount()); + assertEquals("+id", score.getHandledOrderings().get(0).toString()); + assertEquals("+intProp", score.getHandledOrderings().get(1).toString()); + assertEquals(0, score.getRemainderCount()); + assertEquals(false, score.shouldReverseOrder()); + + ops = makeOrderings(StorableTestBasic.class, "-id", "-intProp"); + score = OrderingScore.evaluate(ix, null, ops); + assertEquals(2, score.getHandledCount()); + assertEquals("-id", score.getHandledOrderings().get(0).toString()); + assertEquals("-intProp", score.getHandledOrderings().get(1).toString()); + assertEquals(0, score.getRemainderCount()); + assertEquals(true, score.shouldReverseOrder()); + + ops = makeOrderings(StorableTestBasic.class, "-id", "+intProp"); + score = OrderingScore.evaluate(ix, null, ops); + assertEquals(1, score.getHandledCount()); + assertEquals("-id", score.getHandledOrderings().get(0).toString()); + assertEquals(1, score.getRemainderCount()); + assertEquals("+intProp", score.getRemainderOrderings().get(0).toString()); + assertEquals(true, score.shouldReverseOrder()); + + ops = makeOrderings(StorableTestBasic.class, "+id", "-intProp"); + score = OrderingScore.evaluate(ix, null, ops); + assertEquals(1, score.getHandledCount()); + assertEquals("+id", score.getHandledOrderings().get(0).toString()); + assertEquals(1, score.getRemainderCount()); + assertEquals("-intProp", score.getRemainderOrderings().get(0).toString()); + assertEquals(false, score.shouldReverseOrder()); + + ops = makeOrderings(StorableTestBasic.class, "intProp"); + score = OrderingScore.evaluate(ix, null, ops); + assertEquals(0, score.getHandledCount()); + assertEquals(1, score.getRemainderCount()); + assertEquals("+intProp", score.getRemainderOrderings().get(0).toString()); + assertEquals(false, score.shouldReverseOrder()); + + // Gap is allowed if identity filtered. + + Filter filter; + + filter = Filter.filterFor(StorableTestBasic.class, "id = ?"); + + ops = makeOrderings(StorableTestBasic.class, "intProp"); + score = OrderingScore.evaluate(ix, filter, ops); + assertEquals(1, score.getHandledCount()); + assertEquals("+intProp", score.getHandledOrderings().get(0).toString()); + assertEquals(0, score.getRemainderCount()); + assertEquals(false, score.shouldReverseOrder()); + + ops = makeOrderings(StorableTestBasic.class, "-intProp"); + score = OrderingScore.evaluate(ix, filter, ops); + assertEquals(1, score.getHandledCount()); + assertEquals("-intProp", score.getHandledOrderings().get(0).toString()); + assertEquals(0, score.getRemainderCount()); + assertEquals(true, score.shouldReverseOrder()); + + ops = makeOrderings(StorableTestBasic.class, "intProp", "id"); + score = OrderingScore.evaluate(ix, filter, ops); + assertEquals(1, score.getHandledCount()); + assertEquals("+intProp", score.getHandledOrderings().get(0).toString()); + // Since "id" is filtered, don't count as remainder. + assertEquals(0, score.getRemainderCount()); + assertEquals(false, score.shouldReverseOrder()); + + ops = makeOrderings(StorableTestBasic.class, "-intProp", "id"); + score = OrderingScore.evaluate(ix, filter, ops); + assertEquals(1, score.getHandledCount()); + assertEquals("-intProp", score.getHandledOrderings().get(0).toString()); + // Since "id" is filtered, don't count as remainder. + assertEquals(0, score.getRemainderCount()); + assertEquals(true, score.shouldReverseOrder()); + + ops = makeOrderings(StorableTestBasic.class, "intProp", "doubleProp"); + score = OrderingScore.evaluate(ix, filter, ops); + assertEquals(1, score.getHandledCount()); + assertEquals("+intProp", score.getHandledOrderings().get(0).toString()); + assertEquals(1, score.getRemainderCount()); + assertEquals("+doubleProp", score.getRemainderOrderings().get(0).toString()); + assertEquals(false, score.shouldReverseOrder()); + + ops = makeOrderings(StorableTestBasic.class, "intProp", "-doubleProp"); + score = OrderingScore.evaluate(ix, filter, ops); + assertEquals(1, score.getHandledCount()); + assertEquals("+intProp", score.getHandledOrderings().get(0).toString()); + assertEquals(1, score.getRemainderCount()); + assertEquals("-doubleProp", score.getRemainderOrderings().get(0).toString()); + assertEquals(false, score.shouldReverseOrder()); + + filter = Filter.filterFor(StorableTestBasic.class, "id > ? & doubleProp = ?"); + + ops = makeOrderings(StorableTestBasic.class, "intProp"); + score = OrderingScore.evaluate(ix, filter, ops); + assertEquals(0, score.getHandledCount()); + assertEquals(1, score.getRemainderCount()); + assertEquals("+intProp", score.getRemainderOrderings().get(0).toString()); + assertEquals(false, score.shouldReverseOrder()); + + ops = makeOrderings(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 = makeOrderings(StorableTestBasic.class, "doubleProp", "-intProp"); + score = OrderingScore.evaluate(ix, filter, ops); + assertEquals(1, score.getHandledCount()); + assertEquals("-intProp", score.getHandledOrderings().get(0).toString()); + assertEquals(0, score.getRemainderCount()); + assertEquals(true, score.shouldReverseOrder()); + } + + public void testMidGap() throws Exception { + final StorableIndex ix; + OrderedProperty[] ops; + OrderingScore score; + Filter filter; + + ix = makeIndex(StorableTestBasic.class, "id", "intProp", "doubleProp", "-stringProp"); + + ops = makeOrderings(StorableTestBasic.class, "id", "intProp", "doubleProp", "-stringProp"); + score = OrderingScore.evaluate(ix, null, ops); + assertEquals(4, score.getHandledCount()); + assertEquals("+id", score.getHandledOrderings().get(0).toString()); + assertEquals("+intProp", score.getHandledOrderings().get(1).toString()); + assertEquals("+doubleProp", score.getHandledOrderings().get(2).toString()); + assertEquals("-stringProp", score.getHandledOrderings().get(3).toString()); + assertEquals(0, score.getRemainderCount()); + assertEquals(false, score.shouldReverseOrder()); + + // Now ignore mid index properties, creating a gap. + + ops = makeOrderings(StorableTestBasic.class, "id", "-stringProp"); + score = OrderingScore.evaluate(ix, null, ops); + assertEquals(1, score.getHandledCount()); + assertEquals("+id", score.getHandledOrderings().get(0).toString()); + assertEquals(1, score.getRemainderCount()); + assertEquals("-stringProp", score.getRemainderOrderings().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 = makeOrderings(StorableTestBasic.class, "id", "-stringProp"); + score = OrderingScore.evaluate(ix, filter, ops); + assertEquals(1, score.getHandledCount()); + assertEquals("+id", score.getHandledOrderings().get(0).toString()); + assertEquals(1, score.getRemainderCount()); + assertEquals("-stringProp", score.getRemainderOrderings().get(0).toString()); + assertEquals(false, score.shouldReverseOrder()); + + filter = Filter.filterFor(StorableTestBasic.class, "doubleProp >= ? & intProp = ?"); + + ops = makeOrderings(StorableTestBasic.class, "id", "-stringProp"); + score = OrderingScore.evaluate(ix, filter, ops); + assertEquals(1, score.getHandledCount()); + assertEquals("+id", score.getHandledOrderings().get(0).toString()); + assertEquals(1, score.getRemainderCount()); + assertEquals("-stringProp", score.getRemainderOrderings().get(0).toString()); + assertEquals(false, score.shouldReverseOrder()); + + // Now a complete bridge. + + filter = Filter.filterFor(StorableTestBasic.class, "doubleProp = ? & intProp = ?"); + + ops = makeOrderings(StorableTestBasic.class, "id", "-stringProp"); + score = OrderingScore.evaluate(ix, filter, ops); + assertEquals(2, score.getHandledCount()); + assertEquals("+id", score.getHandledOrderings().get(0).toString()); + assertEquals("-stringProp", score.getHandledOrderings().get(1).toString()); + assertEquals(0, score.getRemainderCount()); + assertEquals(false, score.shouldReverseOrder()); + + // Again in reverse. + + ops = makeOrderings(StorableTestBasic.class, "-id", "stringProp"); + score = OrderingScore.evaluate(ix, filter, ops); + assertEquals(2, score.getHandledCount()); + assertEquals("-id", score.getHandledOrderings().get(0).toString()); + assertEquals("+stringProp", score.getHandledOrderings().get(1).toString()); + assertEquals(0, score.getRemainderCount()); + assertEquals(true, score.shouldReverseOrder()); + + // Failed double reverse. + + ops = makeOrderings(StorableTestBasic.class, "-id", "-stringProp"); + score = OrderingScore.evaluate(ix, filter, ops); + assertEquals(1, score.getHandledCount()); + assertEquals("-id", score.getHandledOrderings().get(0).toString()); + assertEquals(1, score.getRemainderCount()); + assertEquals("-stringProp", score.getRemainderOrderings().get(0).toString()); + assertEquals(true, score.shouldReverseOrder()); + } + + public void testComparator() throws Exception { + StorableIndex ix_1, ix_2; + OrderedProperty[] 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 = makeOrderings(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; + OrderedProperty[] ops; + OrderingScore score; + Filter filter; + + ix = makeIndex(StorableTestBasic.class, "id", "intProp", "doubleProp"); + + ops = makeOrderings(StorableTestBasic.class, "id", "intProp", "doubleProp"); + score = OrderingScore.evaluate(ix, null, ops); + assertEquals(3, score.getHandledCount()); + assertEquals("+id", score.getHandledOrderings().get(0).toString()); + assertEquals("+intProp", score.getHandledOrderings().get(1).toString()); + assertEquals("+doubleProp", score.getHandledOrderings().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.getHandledOrderings().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; + OrderedProperty[] ops; + OrderingScore score; + Filter filter; + + ix = makeIndex(StorableTestBasic.class, "id", "intProp", "doubleProp").unique(true); + ops = makeOrderings(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()); + } +} 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..4727a56 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/qe/TestQueryExecutor.java @@ -0,0 +1,195 @@ +/* + * 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 List> createOrdering(String... properties) { + StorableInfo
info = StorableIntrospector.examine(Address.class); + Map> props = info.getAllProperties(); + + List> ordered = new ArrayList>(); + + for (String prop : properties) { + ordered.add(OrderedProperty.get(props.get(prop), Direction.ASCENDING)); + } + + return ordered; + } + + 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..0972e94 --- /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(); + List> ordered = createOrdering("addressCountry"); + + QueryExecutor
executor = + new ArraySortedQueryExecutor
(unsorted, null, ordered); + + assertEquals(filter, executor.getFilter()); + + assertEquals(4, executor.count(values)); + + assertEquals(ordered, executor.getOrdering()); + + compareElements(executor.openCursor(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(); + List> handled = createOrdering("addressCountry"); + List> finisher = createOrdering("addressState"); + + QueryExecutor
executor = + new ArraySortedQueryExecutor
(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.openCursor(values), 1, 2, 3, 4); + } +} 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..639176c --- /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 ArraySortedQueryExecutor
+ (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.openCursor(values), 1, 2, 3, 7, 8); + } +} diff --git a/src/test/java/com/amazon/carbonado/spi/TestConversionComparator.java b/src/test/java/com/amazon/carbonado/spi/TestConversionComparator.java new file mode 100644 index 0000000..5cd7caa --- /dev/null +++ b/src/test/java/com/amazon/carbonado/spi/TestConversionComparator.java @@ -0,0 +1,179 @@ +/* + * Copyright 2006 Amazon Technologies, Inc. or its affiliates. + * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks + * of Amazon Technologies, Inc. or its affiliates. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazon.carbonado.spi; + +import junit.framework.TestCase; +import junit.framework.TestSuite; + +import com.amazon.carbonado.spi.ConversionComparator; + +/** + * Test cases for {@link ConversionComparator}. + * + * @author Brian S O'Neill + */ +public class TestConversionComparator extends TestCase { + private static final int BOOLEAN_CODE = 0; + private static final int BYTE_CODE = 1; + private static final int SHORT_CODE = 2; + private static final int CHAR_CODE = 3; + private static final int INT_CODE = 4; + private static final int FLOAT_CODE = 5; + private static final int LONG_CODE = 6; + private static final int DOUBLE_CODE = 7; + + private static final Class[] PRIMITIVE_CLASSES = { + boolean.class, byte.class, short.class, char.class, + int.class, float.class, long.class, double.class + }; + + private static final Class[] BOXED_PRIMITIVE_CLASSES = { + Boolean.class, Byte.class, Short.class, Character.class, + Integer.class, Float.class, Long.class, Double.class + }; + + // States which primitive conversions are allowed. + private static final boolean[][] PRIMITIVE_MATRIX = { + // from... + // boolean byte short char int float long double + { true, false, false, false, false, false, false, false }, // to boolean + { false, true, false, false, false, false, false, false }, // to byte + { false, true, true, false, false, false, false, false }, // to short + { false, false, false, true, false, false, false, false }, // to char + { false, true, true, false, true, false, false, false }, // to int + { false, true, true, false, false, true, false, false }, // to float + { false, true, true, false, true, false, true, false }, // to long + { false, true, true, false, true, true, false, true }, // to double + }; + + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + + public static TestSuite suite() { + return new TestSuite(TestConversionComparator.class); + } + + public TestConversionComparator(String name) { + super(name); + } + + public void test_isConversionPossible_basics() { + ConversionComparator cc = new ConversionComparator(Object.class); + assertEquals(true, cc.isConversionPossible(Object.class)); + assertEquals(false, cc.isConversionPossible(String.class)); + assertEquals(false, cc.isConversionPossible(boolean.class)); + assertEquals(false, cc.isConversionPossible(Integer.class)); + assertEquals(false, cc.isConversionPossible(int.class)); + + cc = new ConversionComparator(String.class); + assertEquals(true, cc.isConversionPossible(Object.class)); + assertEquals(true, cc.isConversionPossible(String.class)); + assertEquals(false, cc.isConversionPossible(boolean.class)); + assertEquals(false, cc.isConversionPossible(Integer.class)); + assertEquals(false, cc.isConversionPossible(int.class)); + + cc = new ConversionComparator(boolean.class); + assertEquals(true, cc.isConversionPossible(Object.class)); + assertEquals(false, cc.isConversionPossible(String.class)); + assertEquals(true, cc.isConversionPossible(boolean.class)); + assertEquals(false, cc.isConversionPossible(Integer.class)); + assertEquals(false, cc.isConversionPossible(int.class)); + + cc = new ConversionComparator(Integer.class); + assertEquals(true, cc.isConversionPossible(Object.class)); + assertEquals(false, cc.isConversionPossible(String.class)); + assertEquals(false, cc.isConversionPossible(boolean.class)); + assertEquals(true, cc.isConversionPossible(Integer.class)); + assertEquals(true, cc.isConversionPossible(int.class)); + + cc = new ConversionComparator(int.class); + assertEquals(true, cc.isConversionPossible(Object.class)); + assertEquals(false, cc.isConversionPossible(String.class)); + assertEquals(false, cc.isConversionPossible(boolean.class)); + assertEquals(true, cc.isConversionPossible(Integer.class)); + assertEquals(true, cc.isConversionPossible(int.class)); + } + + public void test_isConversionPossible_primitives() { + test_isConversionPossible_primitives(false, false); + test_isConversionPossible_primitives(false, true); + test_isConversionPossible_primitives(true, false); + test_isConversionPossible_primitives(true, true); + } + + private void test_isConversionPossible_primitives(boolean fromBoxed, boolean toBoxed) { + for (int fromCode = BOOLEAN_CODE; fromCode <= DOUBLE_CODE; fromCode++) { + ConversionComparator cc = new ConversionComparator + (fromBoxed ? BOXED_PRIMITIVE_CLASSES[fromCode] : PRIMITIVE_CLASSES[fromCode]); + for (int toCode = BOOLEAN_CODE; toCode <= DOUBLE_CODE; toCode++) { + boolean expected = PRIMITIVE_MATRIX[toCode][fromCode]; + Class toType = toBoxed ? BOXED_PRIMITIVE_CLASSES[toCode] : PRIMITIVE_CLASSES[toCode]; + assertEquals(expected, cc.isConversionPossible(toType)); + } + } + } + + public void test_compare() { + ConversionComparator cc = new ConversionComparator(Object.class); + assertEquals(true, cc.compare(Object.class, String.class) < 0); + assertEquals(true, cc.compare(String.class, Object.class) > 0); + assertEquals(0, cc.compare(Object.class, Object.class)); + assertEquals(0, cc.compare(String.class, String.class)); + assertEquals(0, cc.compare(String.class, String.class)); + assertEquals(0, cc.compare(int.class, Number.class)); + + cc = new ConversionComparator(String.class); + assertEquals(true, cc.compare(String.class, Object.class) < 0); + assertEquals(true, cc.compare(Object.class, String.class) > 0); + assertEquals(0, cc.compare(String.class, String.class)); + assertEquals(true, cc.compare(int.class, String.class) > 0); + + cc = new ConversionComparator(Integer.class); + assertEquals(true, cc.compare(String.class, Object.class) > 0); + assertEquals(true, cc.compare(Object.class, String.class) < 0); + assertEquals(true, cc.compare(Object.class, Number.class) > 0); + assertEquals(true, cc.compare(Integer.class, Number.class) < 0); + assertEquals(true, cc.compare(int.class, Number.class) > 0); + assertEquals(true, cc.compare(long.class, Number.class) > 0); + assertEquals(true, cc.compare(long.class, Long.class) < 0); + + cc = new ConversionComparator(int.class); + assertEquals(true, cc.compare(String.class, Object.class) > 0); + assertEquals(true, cc.compare(Object.class, String.class) < 0); + assertEquals(true, cc.compare(Object.class, Number.class) > 0); + assertEquals(true, cc.compare(Integer.class, Number.class) < 0); + assertEquals(true, cc.compare(int.class, Number.class) < 0); + assertEquals(true, cc.compare(long.class, Number.class) < 0); + assertEquals(true, cc.compare(long.class, Long.class) < 0); + + cc = new ConversionComparator(Byte.class); + assertEquals(true, cc.compare(int.class, Number.class) > 0); + assertEquals(true, cc.compare(long.class, Number.class) > 0); + assertEquals(true, cc.compare(long.class, Integer.class) < 0); + + cc = new ConversionComparator(byte.class); + assertEquals(true, cc.compare(short.class, int.class) < 0); + assertEquals(true, cc.compare(long.class, int.class) > 0); + + cc = new ConversionComparator(java.util.Date.class); + assertEquals(true, cc.compare(Object.class, Comparable.class) > 0); + assertEquals(0, cc.compare(java.io.Serializable.class, Comparable.class)); + } +} diff --git a/src/test/java/com/amazon/carbonado/spi/TestCursorList.java b/src/test/java/com/amazon/carbonado/spi/TestCursorList.java new file mode 100644 index 0000000..605e981 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/spi/TestCursorList.java @@ -0,0 +1,224 @@ +/* + * Copyright 2006 Amazon Technologies, Inc. or its affiliates. + * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks + * of Amazon Technologies, Inc. or its affiliates. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.amazon.carbonado.spi; + +import com.amazon.carbonado.Cursor; + +import junit.framework.TestCase; +import junit.framework.TestSuite; + +import com.amazon.carbonado.cursor.EmptyCursorFactory; + +/** + * Test case for TransactionManager.CursorList. + * + * @author Brian S O'Neill + */ +public class TestCursorList extends TestCase { + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + + public static TestSuite suite() { + return new TestSuite(TestCursorList.class); + } + + TransactionManager.CursorList mList; + + public TestCursorList(String name) { + super(name); + } + + protected void setUp() { + mList = new TransactionManager.CursorList(); + } + + public void testRegisterFew() { + assertEquals(0, mList.size()); + + { + Cursor cursor = EmptyCursorFactory.newEmptyCursor(); + mList.register(cursor, null); + assertEquals(1, mList.size()); + assertEquals(cursor, mList.getCursor(0)); + assertEquals(null, mList.getValue(0)); + Object value = mList.unregister(cursor); + assertEquals(0, mList.size()); + assertEquals(null, value); + } + + { + Cursor cursor_1 = EmptyCursorFactory.newEmptyCursor(); + Cursor cursor_2 = EmptyCursorFactory.newEmptyCursor(); + mList.register(cursor_1, null); + assertEquals(1, mList.size()); + mList.register(cursor_2, null); + assertEquals(2, mList.size()); + assertEquals(cursor_1, mList.getCursor(0)); + assertEquals(cursor_2, mList.getCursor(1)); + assertEquals(null, mList.getValue(0)); + assertEquals(null, mList.getValue(1)); + + Object value = mList.unregister(cursor_2); + assertEquals(1, mList.size()); + assertEquals(cursor_1, mList.getCursor(0)); + assertEquals(null, value); + mList.unregister(cursor_2); + assertEquals(1, mList.size()); + mList.unregister(cursor_1); + assertEquals(0, mList.size()); + } + + // unregister in reverse + { + Cursor cursor_1 = EmptyCursorFactory.newEmptyCursor(); + Cursor cursor_2 = EmptyCursorFactory.newEmptyCursor(); + mList.register(cursor_1, null); + mList.register(cursor_2, null); + + mList.unregister(cursor_1); + assertEquals(1, mList.size()); + assertEquals(cursor_2, mList.getCursor(0)); + + mList.unregister(cursor_1); + assertEquals(1, mList.size()); + mList.unregister(cursor_2); + assertEquals(0, mList.size()); + } + } + + public void testRegisterFewValue() { + Cursor cursor_1 = EmptyCursorFactory.newEmptyCursor(); + Cursor cursor_2 = EmptyCursorFactory.newEmptyCursor(); + String value_1 = "1"; + String value_2 = "2"; + + mList.register(cursor_1, value_1); + assertEquals(1, mList.size()); + assertEquals(cursor_1, mList.getCursor(0)); + assertEquals(value_1, mList.getValue(0)); + + mList.register(cursor_2, value_2); + assertEquals(2, mList.size()); + assertEquals(cursor_1, mList.getCursor(0)); + assertEquals(value_1, mList.getValue(0)); + assertEquals(cursor_2, mList.getCursor(1)); + assertEquals(value_2, mList.getValue(1)); + + Object value = mList.unregister(cursor_2); + assertEquals(1, mList.size()); + assertEquals(cursor_1, mList.getCursor(0)); + assertEquals(value_1, mList.getValue(0)); + assertEquals(value_2, value); + + value = mList.unregister(cursor_2); + assertEquals(1, mList.size()); + assertEquals(null, value); + value = mList.unregister(cursor_1); + assertEquals(0, mList.size()); + assertEquals(value_1, value); + } + + // Tests that the array expands properly. + public void testRegisterMany() { + final int count = 50; + Cursor[] cursors = new Cursor[count]; + for (int i=0; i(); + afterTriggers = new ArrayList(); + failedTriggers = new ArrayList(); + } + + @Override + protected void tearDown() { + } + + List beforeTriggers; + List afterTriggers; + List failedTriggers; + + public void testAddAndRemove() { + TriggerManager set = new TriggerManager(); + 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(); + 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(); + 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(); + 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/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/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/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