blob: 0738209a5d3e949a9674d56ad8084ab03ac6b4c7 [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.internal.cache.map;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InOrder;
import org.apache.geode.cache.DiskAccessException;
import org.apache.geode.cache.query.internal.index.IndexManager;
import org.apache.geode.internal.cache.CachePerfStats;
import org.apache.geode.internal.cache.EntryEventImpl;
import org.apache.geode.internal.cache.InternalRegion;
import org.apache.geode.internal.cache.RegionEntry;
import org.apache.geode.internal.cache.RegionEntryFactory;
public class AbstractRegionMapPutTest {
private final InternalRegion internalRegion = mock(InternalRegion.class);
private final FocusedRegionMap focusedRegionMap = mock(FocusedRegionMap.class);
@SuppressWarnings("rawtypes")
private final Map entryMap = mock(Map.class);
private final EntryEventImpl event = mock(EntryEventImpl.class);
private final RegionEntry createdRegionEntry = mock(RegionEntry.class);
private final TestableRegionMapPut instance = spy(new TestableRegionMapPut());
@Before
public void setup() {
RegionEntryFactory regionEntryFactory = mock(RegionEntryFactory.class);
when(regionEntryFactory.createEntry(any(), any(), any())).thenReturn(createdRegionEntry);
when(focusedRegionMap.getEntryFactory()).thenReturn(regionEntryFactory);
when(focusedRegionMap.getEntryMap()).thenReturn(entryMap);
when(internalRegion.getCachePerfStats()).thenReturn(mock(CachePerfStats.class));
}
@Test
public void validateOwnerInitialized() {
when(internalRegion.isInitialized()).thenReturn(true);
TestableRegionMapPut testableRegionMapPut = new TestableRegionMapPut();
assertThat(testableRegionMapPut.isOwnerInitialized()).isTrue();
}
@Test
public void validateOwnerUninitialized() {
when(internalRegion.isInitialized()).thenReturn(false);
TestableRegionMapPut testableRegionMapPut = new TestableRegionMapPut();
assertThat(testableRegionMapPut.isOwnerInitialized()).isFalse();
}
@Test
public void validateSetLastModifiedTime() {
instance.setLastModifiedTime(99L);
assertThat(instance.getLastModifiedTime()).isEqualTo(99L);
}
@Test
public void validateSetClearOccurred() {
instance.setClearOccurred(true);
assertThat(instance.isClearOccurred()).isTrue();
}
@Test
public void putWithUnsatisfiedPreconditionsReturnsNull() {
instance.checkPreconditions = false;
RegionEntry result = instance.put();
assertThat(result).isNull();
verify(focusedRegionMap, times(1)).getEntry(eq(event));
verify(focusedRegionMap, times(1)).putEntryIfAbsent(any(), eq(createdRegionEntry));
verify(instance, times(1)).isOnlyExisting();
verify(instance, never()).entryExists(any());
verify(instance, times(1)).serializeNewValueIfNeeded();
verify(instance, times(1)).runWhileLockedForCacheModification(any());
verify(instance, times(1)).setOldValueForDelta();
verify(instance, times(1)).setOldValueInEvent();
verify(instance, times(1)).unsetOldValueForDelta();
verify(instance, times(1)).checkPreconditions();
verify(instance, never()).invokeCacheWriter();
verify(instance, never()).createOrUpdateEntry();
verify(instance, times(1)).shouldCreatedEntryBeRemoved();
verify(instance, never()).doBeforeCompletionActions();
verify(instance, times(1)).doAfterCompletionActions(anyBoolean());
}
@Test
public void putWithDisableLruUpdateCallbackTrueCallsDoAfterCompletionActionsWithTrue() {
instance.checkPreconditions = false;
when(focusedRegionMap.disableLruUpdateCallback()).thenReturn(true);
instance.put();
verify(instance, times(1)).doAfterCompletionActions(true);
}
@Test
public void putWithDisableLruUpdateCallbackFalseCallsDoAfterCompletionActionsWithFalse() {
instance.checkPreconditions = false;
when(focusedRegionMap.disableLruUpdateCallback()).thenReturn(false);
instance.put();
verify(instance, times(1)).doAfterCompletionActions(false);
}
@Test
public void putWithShouldCreatedEntryBeRemovedCallsRemoveEntry() {
instance.shouldCreatedEntryBeRemoved = true;
instance.put();
verify(focusedRegionMap, times(1)).removeEntry(any(), eq(createdRegionEntry), eq(false));
}
@Test
public void putWithOqlIndexManagerCallInitAndCountDown() {
IndexManager oqlIndexManager = mock(IndexManager.class);
when(internalRegion.getIndexManager()).thenReturn(oqlIndexManager);
instance.checkPreconditions = true;
instance.put();
InOrder inOrder = inOrder(oqlIndexManager, instance);
inOrder.verify(oqlIndexManager, times(1)).waitForIndexInit();
inOrder.verify(instance, times(1)).createOrUpdateEntry();
inOrder.verify(oqlIndexManager, times(1)).countDownIndexUpdaters();
}
@Test
public void putCallsHandleDiskAccessExceptionWhenThrownDuringPut() {
instance.checkPreconditions = true;
doThrow(DiskAccessException.class).when(instance).createOrUpdateEntry();
assertThatThrownBy(instance::put).isInstanceOf(DiskAccessException.class);
verify(internalRegion, times(1)).handleDiskAccessException(any());
}
@Test
public void putWithSatisfiedPreconditionsAndNoExistingEntryReturnsRegionEntryFromFactory() {
instance.checkPreconditions = true;
when(focusedRegionMap.getEntry(event)).thenReturn(null);
RegionEntry result = instance.put();
assertThat(result).isSameAs(createdRegionEntry);
verifyMapContractWhenCreateSucceeds();
verifyAbstractContract();
}
@Test
public void regionWithIndexMaintenanceSynchronousCallsSetUpdateInProgress() {
when(internalRegion.getIndexMaintenanceSynchronous()).thenReturn(true);
instance.checkPreconditions = true;
instance.put();
InOrder inOrder = inOrder(createdRegionEntry, instance);
inOrder.verify(createdRegionEntry, times(1)).setUpdateInProgress(true);
inOrder.verify(instance, times(1)).createOrUpdateEntry();
inOrder.verify(createdRegionEntry, times(1)).setUpdateInProgress(false);
}
@Test
public void putWithOnlyExistingTrueAndNoEntryExistsReturnsNull() {
instance.onlyExisting = true;
instance.entryExists = false;
RegionEntry result = instance.put();
assertThat(result).isNull();
verify(focusedRegionMap, times(1)).getEntry(eq(event));
verify(focusedRegionMap, never()).putEntryIfAbsent(any(), any());
verifyAbstractContract();
}
@Test
public void putWithExistingEntryReturnsExistingEntry() {
instance.checkPreconditions = true;
instance.onlyExisting = true;
instance.entryExists = true;
RegionEntry existingEntry = mock(RegionEntry.class);
when(focusedRegionMap.getEntry(eq(event))).thenReturn(existingEntry);
RegionEntry result = instance.put();
assertThat(result).isSameAs(existingEntry);
verify(focusedRegionMap, times(1)).getEntry(eq(event));
verify(focusedRegionMap, never()).getEntryFactory();
verify(focusedRegionMap, never()).putEntryIfAbsent(any(), eq(createdRegionEntry));
verifyAbstractContract();
}
@Test
public void putWithExistingEntryFromPutIfAbsentReturnsExistingEntry() {
instance.checkPreconditions = true;
RegionEntry existingEntry = mock(RegionEntry.class);
when(focusedRegionMap.putEntryIfAbsent(any(), eq(createdRegionEntry)))
.thenReturn(existingEntry);
RegionEntry result = instance.put();
assertThat(result).isSameAs(existingEntry);
verify(focusedRegionMap, times(1)).getEntry(eq(event));
verify(focusedRegionMap, times(1)).getEntryFactory();
verifyAbstractContract();
}
@Test
public void putWithExistingEntryFromPutIfAbsentThatIsRemovedReturnsExistingEntry() {
instance.checkPreconditions = true;
RegionEntry existingEntry = mock(RegionEntry.class);
when(existingEntry.isRemovedPhase2()).thenReturn(true).thenReturn(false);
when(focusedRegionMap.putEntryIfAbsent(any(), eq(createdRegionEntry)))
.thenReturn(existingEntry);
RegionEntry result = instance.put();
assertThat(result).isSameAs(existingEntry);
verify(focusedRegionMap, times(2)).getEntry(eq(event));
verify(focusedRegionMap, times(2)).getEntryFactory();
verify(entryMap, times(1)).remove(any(), eq(existingEntry));
verifyAbstractContractWithRetry();
}
private void verifyMapContractWhenCreateSucceeds() {
verify(focusedRegionMap, times(1)).getEntry(eq(event));
verify(focusedRegionMap, times(1)).putEntryIfAbsent(any(), eq(createdRegionEntry));
}
private void verifyAbstractContractWithRetry() {
verify(instance, times(2)).isOnlyExisting();
verifyCommonAbstractContract();
}
private void verifyAbstractContract() {
verify(instance, times(1)).isOnlyExisting();
verifyCommonAbstractContract();
}
private void verifyCommonAbstractContract() {
if (instance.onlyExisting) {
verify(instance, times(1)).entryExists(any());
} else {
verify(instance, never()).entryExists(any());
}
verify(instance, times(1)).serializeNewValueIfNeeded();
verify(instance, times(1)).runWhileLockedForCacheModification(any());
if (!instance.onlyExisting || instance.entryExists) {
verify(instance, times(1)).setOldValueForDelta();
verify(instance, times(1)).setOldValueInEvent();
verify(instance, times(1)).unsetOldValueForDelta();
verify(instance, times(1)).checkPreconditions();
} else {
verify(instance, never()).setOldValueForDelta();
verify(instance, never()).setOldValueInEvent();
verify(instance, never()).unsetOldValueForDelta();
verify(instance, never()).checkPreconditions();
}
if (instance.checkPreconditions || !instance.onlyExisting || instance.entryExists) {
verify(instance, times(1)).invokeCacheWriter();
verify(instance, times(1)).createOrUpdateEntry();
verify(instance, times(1)).doBeforeCompletionActions();
verify(instance, times(instance.isCreate() ? 1 : 0)).shouldCreatedEntryBeRemoved();
} else {
verify(instance, never()).invokeCacheWriter();
verify(instance, never()).createOrUpdateEntry();
verify(instance, never()).doBeforeCompletionActions();
verify(instance, never()).shouldCreatedEntryBeRemoved();
}
verify(instance, times(1)).doAfterCompletionActions(anyBoolean());
}
private class TestableRegionMapPut extends AbstractRegionMapPut {
public boolean checkPreconditions;
public boolean onlyExisting;
public boolean entryExists;
public boolean shouldCreatedEntryBeRemoved;
public TestableRegionMapPut() {
super(focusedRegionMap, internalRegion, event);
}
@Override
protected boolean isOnlyExisting() {
return onlyExisting;
}
@Override
protected boolean entryExists(RegionEntry regionEntry) {
return entryExists;
}
@Override
protected void serializeNewValueIfNeeded() {}
@Override
protected void runWhileLockedForCacheModification(Runnable r) {
r.run();
}
@Override
protected void setOldValueForDelta() {}
@Override
protected void setOldValueInEvent() {}
@Override
protected void unsetOldValueForDelta() {}
@Override
protected boolean checkPreconditions() {
return checkPreconditions;
}
@Override
protected void invokeCacheWriter() {}
@Override
protected void createOrUpdateEntry() {}
@Override
protected void doBeforeCompletionActions() {}
@Override
protected boolean shouldCreatedEntryBeRemoved() {
return shouldCreatedEntryBeRemoved;
}
@Override
protected void doAfterCompletionActions(boolean disabledEviction) {}
}
}