blob: 07068771173e397bf5853873fae61c4f17f9110c [file] [log] [blame]
/*
* 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.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.stream.IntStream;
import org.junit.Before;
import org.junit.Test;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.query.QueryService;
import org.apache.geode.cache.query.internal.index.AbstractIndex.InternalIndexStatistics;
import org.apache.geode.internal.cache.GemFireCacheImpl;
import org.apache.geode.internal.cache.LocalRegion;
import org.apache.geode.internal.cache.RegionEntry;
public class MemoryIndexStoreJUnitTest {
Region region;
GemFireCacheImpl cache;
InternalIndexStatistics mockStats;
MemoryIndexStore store;
RegionEntry[] mockEntries;
int numMockEntries = 10;
GemFireCacheImpl actualInstance;
protected void subclassPreSetup() {}
protected Region createRegion() {
return mock(LocalRegion.class);
}
@Before
public void setup() {
subclassPreSetup();
region = createRegion();
cache = mock(GemFireCacheImpl.class);
mockStats = mock(AbstractIndex.InternalIndexStatistics.class);
store = new MemoryIndexStore(region, mockStats, cache);
store.setIndexOnValues(true);
mockEntries = new RegionEntry[numMockEntries];
IntStream.range(0, numMockEntries).forEach(i -> {
mockEntries[i] = createRegionEntry(i, new Object());
});
}
@Test
public void createIteratorWhenCacheNulledWhenShuttingDownShouldNotThrowNPE() {
store.get("T");
}
@Test
public void testSizeOfStoreReturnsNumberOfKeysAndNotActualNumberOfValues() {
IntStream.range(0, 150).forEach(i -> {
try {
store.addMapping(i % 3, createRegionEntry(i, new Object()));
} catch (Exception e) {
throw new AssertionError(e);
}
});
assertEquals(150, numObjectsInStore(store));
}
@Test
public void testAddEnoughEntriesToCreateAConcurrentHashSet() {
IntStream.range(0, 150).forEach(i -> {
try {
store.addMapping(1, createRegionEntry(i, new Object()));
} catch (Exception e) {
throw new AssertionError(e);
}
});
assertEquals(150, numObjectsInStore(store));
}
@Test
public void testUpdateAgainstAConcurrentHashSet() throws Exception {
IntStream.range(0, 150).forEach(i -> {
try {
store.addMapping(1, createRegionEntry(1, new Object()));
} catch (Exception e) {
throw new AssertionError(e);
}
});
RegionEntry entry = createRegionEntry(1, new Object());
store.addMapping(1, entry);
store.updateMapping(2, 1, entry, entry.getValue(null));
assertEquals(151, numObjectsInStore(store));
}
@Test
public void testCanAddObjectWithUndefinedKey() throws Exception {
store.addMapping(QueryService.UNDEFINED, mockEntries[0]);
assertEquals(1, numObjectsIterated(store.get(QueryService.UNDEFINED)));
assertEquals(0, numObjectsInStore(store));
}
@Test
public void testCanAddManyObjectsWithUndefinedKey() throws Exception {
for (int i = 0; i < mockEntries.length; i++) {
store.addMapping(QueryService.UNDEFINED, mockEntries[i]);
}
assertEquals(mockEntries.length, numObjectsIterated(store.get(QueryService.UNDEFINED)));
// Undefined will not return without an explicit get for UNDEFINED);
assertEquals(0, numObjectsInStore(store));
}
@Test
public void testIteratorWithStartInclusiveAndNoKeysToRemoveReturnsCorrectNumberOfResults()
throws Exception {
addMockedEntries(numMockEntries);
assertEquals(2, numObjectsIterated(store.iterator(numMockEntries - 2, true, null)));
}
@Test
public void testIteratorWithStartExclusiveAndNoKeysToRemoveReturnsCorrectNumberOfResults()
throws Exception {
addMockedEntries(numMockEntries);
assertEquals(1, numObjectsIterated(store.iterator(numMockEntries - 2, false, null)));
}
@Test
public void testIteratorWithStartInclusiveAndKeyToRemoveReturnsCorrectNumberOfResults()
throws Exception {
addMockedEntries(numMockEntries);
Set keysToRemove = new HashSet();
keysToRemove.add("1");
assertEquals(9, numObjectsIterated(store.iterator(1, true, keysToRemove)));
}
@Test
public void testIteratorWithStartExclusiveAndKeyToRemoveReturnsCorrectNumberOfResults()
throws Exception {
addMockedEntries(numMockEntries);
Set keysToRemove = new HashSet();
keysToRemove.add("1");
assertEquals(8, numObjectsIterated(store.iterator(1, false, keysToRemove)));
}
@Test
public void testStartAndEndInclusiveReturnsCorrectResults() throws Exception {
addMockedEntries(numMockEntries);
assertEquals(6, numObjectsIterated(store.iterator(1, true, 6, true, null)));
}
@Test
public void testStartInclusiveAndEndExclusiveReturnsCorrectResults() throws Exception {
addMockedEntries(numMockEntries);
assertEquals(5, numObjectsIterated(store.iterator(1, true, 6, false, null)));
}
@Test
public void testStartExclusiveAndEndExclusiveReturnsCorrectResults() throws Exception {
addMockedEntries(numMockEntries);
assertEquals(4, numObjectsIterated(store.iterator(1, false, 6, false, null)));
}
@Test
public void testStartExclusiveAndEndInclusiveReturnsCorrectResults() throws Exception {
addMockedEntries(numMockEntries);
assertEquals(5, numObjectsIterated(store.iterator(1, false, 6, true, null)));
}
@Test
public void testStartIsNull() throws Exception {
addMockedEntries(numMockEntries);
assertEquals(6, numObjectsIterated(store.iterator(null, false, 6, false, null)));
}
@Test
public void testDescendingIteratorReturnsExpectedOrderOfEntries() throws Exception {
RegionEntry mockEntry1 = mockEntries[0];
RegionEntry mockEntry2 = mockEntries[1];
store.addMapping("1", mockEntry1);
store.addMapping("2", mockEntry2);
Iterator iteratorFirst = store.descendingIterator(null);
assertEquals(2, numObjectsIterated(iteratorFirst));
Iterator iterator = store.descendingIterator(null);
iterator.hasNext();
assertEquals(mockEntry2,
((MemoryIndexStore.MemoryIndexStoreEntry) iterator.next()).getRegionEntry());
iterator.hasNext();
assertEquals(mockEntry1,
((MemoryIndexStore.MemoryIndexStoreEntry) iterator.next()).getRegionEntry());
}
@Test
public void testDescendingIteratorWithRemovedKeysReturnsExpectedOrderOfEntries()
throws Exception {
RegionEntry mockEntry1 = mockEntries[0];
RegionEntry mockEntry2 = mockEntries[1];
RegionEntry mockEntry3 = mockEntries[2];
store.addMapping("1", mockEntry1);
store.addMapping("2", mockEntry2);
store.addMapping("3", mockEntry3);
Set keysToRemove = new HashSet();
keysToRemove.add("2");
Iterator iteratorFirst = store.descendingIterator(keysToRemove);
assertEquals(2, numObjectsIterated(iteratorFirst));
// keysToRemove has been modified by the store, we need to readd the key to remove
keysToRemove.add("2");
Iterator iterator = store.descendingIterator(keysToRemove);
iterator.hasNext();
assertEquals(mockEntry3,
((MemoryIndexStore.MemoryIndexStoreEntry) iterator.next()).getRegionEntry());
iterator.hasNext();
assertEquals(mockEntry1,
((MemoryIndexStore.MemoryIndexStoreEntry) iterator.next()).getRegionEntry());
assertFalse(iterator.hasNext());
}
@Test
public void testDescendingIteratorWithMultipleRemovedKeysReturnsExpectedOrderOfEntries()
throws Exception {
RegionEntry mockEntry1 = mockEntries[0];
RegionEntry mockEntry2 = mockEntries[1];
RegionEntry mockEntry3 = mockEntries[2];
store.addMapping("1", mockEntry1);
store.addMapping("2", mockEntry2);
store.addMapping("3", mockEntry3);
Set keysToRemove = new HashSet();
keysToRemove.add("2");
keysToRemove.add("1");
Iterator iteratorFirst = store.descendingIterator(keysToRemove);
assertEquals(1, numObjectsIterated(iteratorFirst));
// keysToRemove has been modified by the store, we need to readd the key to remove
keysToRemove.add("2");
keysToRemove.add("1");
Iterator iterator = store.descendingIterator(keysToRemove);
iterator.hasNext();
assertEquals(mockEntry3,
((MemoryIndexStore.MemoryIndexStoreEntry) iterator.next()).getRegionEntry());
assertFalse(iterator.hasNext());
}
@Test
public void testSizeWithKeyArgumentReturnsCorrectSize() throws Exception {
RegionEntry mockEntry1 = mockEntries[0];
RegionEntry mockEntry2 = mockEntries[1];
store.addMapping("1", mockEntry1);
store.addMapping("2", mockEntry2);
assertEquals(1, store.size("1"));
}
@Test
public void testGetReturnsExpectedIteratorValue() throws Exception {
RegionEntry mockEntry1 = mockEntries[0];
RegionEntry mockEntry2 = mockEntries[1];
store.addMapping("1", mockEntry1);
store.addMapping("2", mockEntry2);
assertEquals(1, numObjectsIterated(store.get("1")));
}
@Test
public void testGetReturnsExpectedIteratorWithMultipleValues() throws Exception {
RegionEntry mockEntry1 = mockEntries[0];
RegionEntry mockEntry2 = mockEntries[1];
RegionEntry mockEntry3 = mockEntries[2];
RegionEntry mockEntry4 = mockEntries[3];
store.addMapping("1", mockEntry1);
store.addMapping("1", mockEntry2);
store.addMapping("1", mockEntry3);
store.addMapping("2", mockEntry4);
assertEquals(3, numObjectsIterated(store.get("1")));
assertEquals(4, numObjectsInStore(store));
}
@Test
public void testGetWithIndexOnKeysReturnsExpectedIteratorValues() throws Exception {
RegionEntry mockEntry1 = mockEntries[0];
RegionEntry mockEntry2 = mockEntries[1];
store.setIndexOnValues(false);
store.setIndexOnRegionKeys(true);
store.addMapping("1", mockEntry1);
store.addMapping("2", mockEntry2);
assertEquals(1, numObjectsIterated(store.get("1")));
}
@Test
public void testCorrectlyRemovesEntryProvidedTheWrongKey() throws Exception {
RegionEntry mockEntry1 = mockEntries[0];
RegionEntry mockEntry2 = mockEntries[1];
store.addMapping("1", mockEntry1);
store.addMapping("2", mockEntry2);
store.removeMapping("1", mockEntry2);
assertEquals(1, numObjectsInStore(store));
assertTrue(objectContainedIn(store, mockEntry1));
}
@Test
public void testRemoveMappingRemovesFromBackingMap() throws Exception {
RegionEntry mockEntry1 = mockEntries[0];
RegionEntry mockEntry2 = mockEntries[1];
store.addMapping("1", mockEntry1);
store.addMapping("2", mockEntry2);
store.removeMapping("1", mockEntry1);
assertEquals(1, numObjectsInStore(store));
assertTrue(objectContainedIn(store, mockEntry2));
}
@Test
public void testAddMappingAddsToBackingMap() throws Exception {
RegionEntry mockEntry1 = mockEntries[0];
RegionEntry mockEntry2 = mockEntries[1];
store.addMapping("1", mockEntry1);
store.addMapping("2", mockEntry2);
assertEquals(2, numObjectsInStore(store));
assertTrue(objectContainedIn(store, mockEntry1));
assertTrue(objectContainedIn(store, mockEntry2));
}
@Test
public void testClear() throws Exception {
RegionEntry mockEntry1 = mockEntries[0];
RegionEntry mockEntry2 = mockEntries[1];
store.addMapping("1", mockEntry1);
store.addMapping("1", mockEntry2);
store.clear();
assertEquals(0, numObjectsInStore(store));
}
private int numObjectsInStore(MemoryIndexStore store) {
Iterator iterator = store.iterator(null);
return numObjectsIterated(iterator);
}
private int numObjectsIterated(Iterator iterator) {
int count = 0;
while (iterator.hasNext()) {
iterator.next();
count++;
}
return count;
}
private boolean objectContainedIn(MemoryIndexStore store, Object o) {
Iterator iterator = store.valueToEntriesMap.values().iterator();
return objectContainedIn(iterator, o);
}
private boolean objectContainedIn(Iterator iterator, Object o) {
while (iterator.hasNext()) {
if (iterator.next().equals(o)) {
return true;
}
}
return false;
}
private void addMockedEntries(int numEntriesToAdd) {
IntStream.range(0, numEntriesToAdd).forEach(i -> {
try {
store.addMapping(mockEntries[i].getKey(), mockEntries[i]);
} catch (Exception e) {
throw new AssertionError(e);
}
});
}
private RegionEntry createRegionEntry(Object key, Object value) {
RegionEntry mockEntry = mock(RegionEntry.class);
when(mockEntry.getValue(any())).thenReturn(value);
when(mockEntry.getKey()).thenReturn(key);
return mockEntry;
}
}