| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more contributor license |
| * agreements. See the NOTICE file distributed with this work for additional information regarding |
| * copyright ownership. The ASF licenses this file to You 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 org.apache.geode.cache.query.internal.index; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.fail; |
| |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.experimental.categories.Category; |
| |
| import org.apache.geode.cache.AttributesFactory; |
| import org.apache.geode.cache.Cache; |
| import org.apache.geode.cache.DataPolicy; |
| import org.apache.geode.cache.Region; |
| import org.apache.geode.cache.RegionAttributes; |
| import org.apache.geode.cache.query.CacheUtils; |
| import org.apache.geode.cache.query.data.Portfolio; |
| import org.apache.geode.cache.query.internal.index.IndexStore.IndexStoreEntry; |
| import org.apache.geode.internal.cache.LocalRegion; |
| import org.apache.geode.internal.cache.RegionEntry; |
| import org.apache.geode.internal.cache.RegionEntryContext; |
| import org.apache.geode.internal.cache.entries.VMThinRegionEntryHeap; |
| import org.apache.geode.internal.cache.persistence.query.CloseableIterator; |
| import org.apache.geode.test.junit.categories.OQLIndexTest; |
| |
| /** |
| * Test class that will be extended to provide the IndexStorage structure to test Tests apis of the |
| * IndexStorage |
| * |
| */ |
| @Category({OQLIndexTest.class}) |
| public class MapIndexStoreJUnitTest { |
| |
| IndexStore indexDataStructure; |
| List<IndexStoreEntry> entries; |
| LocalRegion region; |
| |
| int numValues = 10; |
| |
| @Before |
| public void setUp() throws Exception { |
| entries = new ArrayList<IndexStoreEntry>(); |
| this.indexDataStructure = getIndexStorage(); |
| } |
| |
| @After |
| public void tearDown() throws Exception { |
| if (region != null) { |
| region.destroyRegion(); |
| } |
| CacheUtils.closeCache(); |
| } |
| |
| protected IndexStore getIndexStorage() { |
| CacheUtils.startCache(); |
| Cache cache = CacheUtils.getCache(); |
| AttributesFactory attributesFactory = new AttributesFactory(); |
| attributesFactory.setDataPolicy(DataPolicy.NORMAL); |
| attributesFactory.setIndexMaintenanceSynchronous(true); |
| RegionAttributes regionAttributes = attributesFactory.create(); |
| region = (LocalRegion) cache.createRegion("portfolios", regionAttributes); |
| |
| IndexStore indexStorage = |
| new MapIndexStore(region.getIndexMap("testIndex", "p.ID", "/portfolios p"), region); |
| return indexStorage; |
| } |
| |
| // ******** HELPERS ********/ |
| /** |
| * adds values to the index storage and to a list for validation |
| */ |
| private void addValues(Region region, int numValues) throws IMQException { |
| for (int i = 0; i < numValues; i++) { |
| String regionKey = "" + i; |
| RegionEntry re = VMThinRegionEntryHeap.getEntryFactory() |
| .createEntry((RegionEntryContext) region, regionKey, new Portfolio(i)); |
| entries.add(i, new IndexRegionTestEntry(re)); |
| indexDataStructure.addMapping(regionKey, re); |
| } |
| } |
| |
| /** |
| * checks the list for an matching IndexEntry |
| */ |
| private boolean entriesContains(IndexStoreEntry ie) { |
| Iterator<IndexStoreEntry> iterator = entries.iterator(); |
| while (iterator.hasNext()) { |
| if (iterator.next().equals(ie)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * iterates through the index storage structure and compares size with the test list |
| */ |
| private void validateIndexStorage() { |
| CloseableIterator<IndexStoreEntry> iterator = null; |
| try { |
| ArrayList structureList = new ArrayList(); |
| iterator = indexDataStructure.iterator(null); |
| while (iterator.hasNext()) { |
| IndexStoreEntry ie = iterator.next(); |
| if (entriesContains(ie)) { |
| structureList.add(ie); |
| } else { |
| fail("IndexDataStructure returned an IndexEntry that should not be present:" + ie); |
| } |
| } |
| assertEquals("Expected Number of entries did not match", entries.size(), |
| structureList.size()); |
| } finally { |
| if (iterator != null) { |
| iterator.close(); |
| } |
| } |
| } |
| |
| /** |
| * iterates through and checks against expected size |
| */ |
| private void validateIteratorSize(CloseableIterator iterator, int expectedSize) { |
| try { |
| int actualSize = 0; |
| while (iterator.hasNext()) { |
| iterator.next(); |
| actualSize++; |
| } |
| assertEquals("Iterator provided differing number of values", expectedSize, actualSize); |
| } finally { |
| if (iterator != null) { |
| iterator.close(); |
| } |
| } |
| } |
| |
| private void validateDescendingIterator(CloseableIterator iterator, int reverseStart, |
| int reverseEnd) { |
| for (int i = reverseStart; i > reverseEnd; i--) { |
| IndexStoreEntry ise = (IndexStore.IndexStoreEntry) iterator.next(); |
| if (Integer.valueOf((String) ise.getDeserializedKey()) != i) { |
| fail("descendingIterator did not return the expected reverse order"); |
| } |
| } |
| } |
| |
| /** |
| * Helper method to test index storage iterators |
| */ |
| public void helpTestStartAndEndIterator(Region region, Object startValue, boolean startInclusive, |
| Object endValue, boolean endInclusive, int expectedSize) throws IMQException { |
| addValues(region, numValues); |
| CloseableIterator<IndexStoreEntry> iterator = |
| indexDataStructure.iterator(startValue, startInclusive, endValue, endInclusive, null); |
| validateIteratorSize(iterator, expectedSize); |
| } |
| |
| // ******** TESTS ********/ |
| /** |
| * this test adds values to the index storage and validates the index storage against the test |
| * list |
| */ |
| @Test |
| public void testAddMapping() throws IMQException { |
| addValues(region, numValues); |
| validateIndexStorage(); |
| } |
| |
| /** |
| * this test adds values to the index storage and then removes an entry. It validates the index |
| * storage against the test list and then removes and validates again |
| */ |
| @Test |
| public void testRemoveMapping() throws IMQException { |
| addValues(region, numValues); |
| |
| IndexRegionTestEntry ire = (IndexRegionTestEntry) entries.remove(8); |
| indexDataStructure.removeMapping("" + 8, ire.regionEntry); |
| validateIndexStorage(); |
| |
| ire = (IndexRegionTestEntry) entries.remove(4); |
| indexDataStructure.removeMapping("" + 4, ire.regionEntry); |
| validateIndexStorage(); |
| } |
| |
| /** |
| * This test will test the descending iterator by iterating the descending iterator and comparing |
| * the results to the test entries in reverse order |
| */ |
| @Test |
| public void testDescendingIterator() throws IMQException { |
| addValues(region, numValues); |
| validateDescendingIterator(indexDataStructure.descendingIterator(null), numValues - 1, 0); |
| } |
| |
| /** |
| * tests start inclusive iterator from beginning to end |
| */ |
| @Test |
| public void testStartInclusiveIterator() throws IMQException { |
| addValues(region, numValues); |
| String startValue = "" + 0; |
| CloseableIterator<IndexStoreEntry> iterator = |
| indexDataStructure.iterator(startValue, true, null); |
| validateIteratorSize(iterator, numValues); |
| } |
| |
| /** |
| * tests start exclusive iterator from beginning. Exclusive should not include the first entry, so |
| * numValues - 1 |
| */ |
| @Test |
| public void testStartExclusiveIterator() throws IMQException { |
| addValues(region, numValues); |
| String startValue = "" + 0; |
| CloseableIterator<IndexStoreEntry> iterator = |
| indexDataStructure.iterator(startValue, false, null); |
| validateIteratorSize(iterator, numValues - 1); |
| } |
| |
| @Test |
| public void testEndInclusiveIterator() throws IMQException { |
| addValues(region, numValues); |
| String endValue = "" + (numValues - 1); |
| CloseableIterator<IndexStoreEntry> iterator = |
| indexDataStructure.descendingIterator(endValue, true, null); |
| validateIteratorSize(iterator, numValues); |
| } |
| |
| @Test |
| public void testEndExclusiveIterator() throws IMQException { |
| addValues(region, numValues); |
| String endValue = "" + (numValues - 1); |
| CloseableIterator<IndexStoreEntry> iterator = |
| indexDataStructure.descendingIterator(endValue, false, null); |
| validateIteratorSize(iterator, numValues - 1); |
| } |
| |
| @Test |
| public void testStartInclusiveEndInclusive() throws IMQException { |
| String startValue = "" + 0; |
| String endValue = "" + 9; |
| helpTestStartAndEndIterator(region, startValue, true, endValue, true, numValues); |
| } |
| |
| @Test |
| public void testStartInclusiveEndExclusive() throws IMQException { |
| String startValue = "" + 0; |
| String endValue = "" + 9; |
| helpTestStartAndEndIterator(region, startValue, true, endValue, false, numValues - 1); |
| } |
| |
| @Test |
| public void testStartExclusiveEndExclusive() throws IMQException { |
| String startValue = "" + 0; |
| String endValue = "" + 9; |
| helpTestStartAndEndIterator(region, startValue, false, endValue, false, numValues - 2); |
| } |
| |
| @Test |
| public void testStartExclusiveEndInclusive() throws IMQException { |
| String startValue = "" + 0; |
| String endValue = "" + 9; |
| helpTestStartAndEndIterator(region, startValue, true, endValue, false, numValues - 1); |
| } |
| |
| |
| |
| private class IndexRegionTestEntry implements IndexStoreEntry { |
| RegionEntry regionEntry; |
| |
| IndexRegionTestEntry(RegionEntry re) { |
| this.regionEntry = re; |
| } |
| |
| public boolean equals(Object object) { |
| if (object instanceof IndexStoreEntry) { |
| Object regionKey = ((IndexStoreEntry) object).getDeserializedRegionKey(); |
| // if (regionKey instanceof CachedDeserializable) { |
| // regionKey = ((CachedDeserializable) regionKey) |
| // .getDeserializedForReading(); |
| // } |
| |
| return regionEntry.getKey().equals(regionKey); |
| } |
| return false; |
| } |
| |
| @Override |
| public Object getDeserializedKey() { |
| return regionEntry.getKey(); |
| } |
| |
| @Override |
| public Object getDeserializedRegionKey() { |
| // TODO Auto-generated method stub |
| return null; |
| } |
| |
| @Override |
| public Object getDeserializedValue() { |
| // TODO Auto-generated method stub |
| return null; |
| } |
| |
| @Override |
| public boolean isUpdateInProgress() { |
| return false; |
| } |
| } |
| } |