This is an automated email from the ASF dual-hosted git repository.

dschneider pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/geode.git


The following commit(s) were added to refs/heads/develop by this push:
     new 586d7c3  GEODE-5195: add unit tests for RegionMapPut (#1964)
586d7c3 is described below

commit 586d7c3076cce04f66ab3d975f3b4eccbe6b1c67
Author: Darrel Schneider <[email protected]>
AuthorDate: Wed May 16 10:25:00 2018 -0700

    GEODE-5195: add unit tests for RegionMapPut (#1964)
---
 .../geode/internal/cache/map/RegionMapPut.java     | 152 ++---
 .../geode/internal/cache/map/RegionMapPutTest.java | 654 ++++++++++++++++++++-
 2 files changed, 716 insertions(+), 90 deletions(-)

diff --git 
a/geode-core/src/main/java/org/apache/geode/internal/cache/map/RegionMapPut.java
 
b/geode-core/src/main/java/org/apache/geode/internal/cache/map/RegionMapPut.java
index 506b7b7..50dc4fa 100644
--- 
a/geode-core/src/main/java/org/apache/geode/internal/cache/map/RegionMapPut.java
+++ 
b/geode-core/src/main/java/org/apache/geode/internal/cache/map/RegionMapPut.java
@@ -21,7 +21,6 @@ import java.util.Set;
 import org.apache.geode.cache.CacheWriter;
 import org.apache.geode.cache.DiskAccessException;
 import org.apache.geode.cache.Operation;
-import org.apache.geode.internal.cache.CachePerfStats;
 import org.apache.geode.internal.cache.EntryEventImpl;
 import org.apache.geode.internal.cache.EntryEventSerialization;
 import org.apache.geode.internal.cache.InternalRegion;
@@ -35,7 +34,6 @@ import 
org.apache.geode.internal.cache.wan.GatewaySenderEventImpl;
 import org.apache.geode.internal.offheap.OffHeapHelper;
 import org.apache.geode.internal.offheap.ReferenceCountHelper;
 import org.apache.geode.internal.offheap.annotations.Released;
-import org.apache.geode.internal.offheap.annotations.Retained;
 import org.apache.geode.internal.offheap.annotations.Unretained;
 import org.apache.geode.internal.sequencelog.EntryLogger;
 
@@ -169,53 +167,49 @@ public class RegionMapPut extends AbstractRegionMapPut {
     final EntryEventImpl event = getEvent();
     final RegionEntry re = getRegionEntry();
     event.setRegionEntry(re);
-    boolean needToSetOldValue =
-        isCacheWrite() || isRequireOldValue() || 
event.getOperation().guaranteesOldValue();
-    if (needToSetOldValue) {
-      if (event.getOperation().guaranteesOldValue()) {
-        // In these cases we want to even get the old value from disk if it is 
not in memory
-        ReferenceCountHelper.skipRefCountTracking();
-        @Released
-        Object oldValueInVMOrDisk = 
re.getValueOffHeapOrDiskWithoutFaultIn(event.getRegion());
-        ReferenceCountHelper.unskipRefCountTracking();
-        try {
-          event.setOldValue(oldValueInVMOrDisk, true);
-        } finally {
-          OffHeapHelper.releaseWithNoTracking(oldValueInVMOrDisk);
-        }
-      } else {
-        // In these cases only need the old value if it is in memory
-        ReferenceCountHelper.skipRefCountTracking();
-
-        @Retained
-        @Released
-        Object oldValueInVM = re.getValueRetain(event.getRegion(), true); // 
OFFHEAP: re
-        // synced so can use
-        // its ref.
-        if (oldValueInVM == null) {
-          oldValueInVM = Token.NOT_AVAILABLE;
-        }
-        ReferenceCountHelper.unskipRefCountTracking();
-        try {
-          event.setOldValue(oldValueInVM);
-        } finally {
-          OffHeapHelper.releaseWithNoTracking(oldValueInVM);
-        }
-      }
+    if (event.getOperation().guaranteesOldValue()) {
+      setOldValueEvenIfFaultedOut();
+    } else if (isCacheWrite() || isRequireOldValue()) {
+      setOldValueIfNotFaultedOut();
     } else {
-      // if the old value is in memory then if it is a GatewaySenderEventImpl 
then
-      // we want to set the old value.
       @Unretained
-      Object ov = re.getValue(); // OFFHEAP _getValue is ok since re is synced 
and we only use it
-      // if its a GatewaySenderEventImpl.
-      // Since GatewaySenderEventImpl is never stored in an off-heap region 
nor a compressed region
-      // we don't need to worry about ov being compressed.
-      if (ov instanceof GatewaySenderEventImpl) {
-        event.setOldValue(ov, true);
+      Object existingValue = re.getValue();
+      if (existingValue instanceof GatewaySenderEventImpl) {
+        event.setOldValue(existingValue, true);
       }
     }
   }
 
+  private void setOldValueIfNotFaultedOut() {
+    final EntryEventImpl event = getEvent();
+    ReferenceCountHelper.skipRefCountTracking();
+    @Released
+    Object oldValueInVM = getRegionEntry().getValueRetain(event.getRegion(), 
true);
+    if (oldValueInVM == null) {
+      oldValueInVM = Token.NOT_AVAILABLE;
+    }
+    ReferenceCountHelper.unskipRefCountTracking();
+    try {
+      event.setOldValue(oldValueInVM);
+    } finally {
+      OffHeapHelper.releaseWithNoTracking(oldValueInVM);
+    }
+  }
+
+  private void setOldValueEvenIfFaultedOut() {
+    final EntryEventImpl event = getEvent();
+    ReferenceCountHelper.skipRefCountTracking();
+    @Released
+    Object oldValueInVMOrDisk =
+        
getRegionEntry().getValueOffHeapOrDiskWithoutFaultIn(event.getRegion());
+    ReferenceCountHelper.unskipRefCountTracking();
+    try {
+      event.setOldValue(oldValueInVMOrDisk, true);
+    } finally {
+      OffHeapHelper.releaseWithNoTracking(oldValueInVMOrDisk);
+    }
+  }
+
   @Override
   protected void unsetOldValueForDelta() {
     OffHeapHelper.release(getOldValueForDelta());
@@ -300,20 +294,24 @@ public class RegionMapPut extends AbstractRegionMapPut {
             getLastModifiedTime(), invokeListeners, isIfNew(), isIfOld(), 
getExpectedOldValue(),
             isRequireOldValue());
       } finally {
-        if (!isClearOccurred()) {
-          try {
-            getRegionMap().lruUpdateCallback();
-          } catch (DiskAccessException dae) {
-            getOwner().handleDiskAccessException(dae);
-            throw dae;
-          }
-        }
+        lruUpdateCallbackIfNotCleared();
       }
     } else {
       getRegionMap().resetThreadLocals();
     }
   }
 
+  private void lruUpdateCallbackIfNotCleared() {
+    if (!isClearOccurred()) {
+      try {
+        getRegionMap().lruUpdateCallback();
+      } catch (DiskAccessException dae) {
+        getOwner().handleDiskAccessException(dae);
+        throw dae;
+      }
+    }
+  }
+
   private boolean isUpdate() {
     if (isCacheWrite() && getEvent().getOperation().isUpdate()) {
       // if there is a cacheWriter, type of event has already been set
@@ -329,18 +327,27 @@ public class RegionMapPut extends AbstractRegionMapPut {
   }
 
   /**
-   * @return false if precondition indicates that
+   * @return false if preconditions indicate that
    *         the put should not be done.
    */
   @Override
   protected boolean checkPreconditions() {
-    if (continueUpdate() && continueOverwriteDestroyed() && 
satisfiesExpectedOldValue()) {
-      return true;
+    if (!checkUpdatePreconditions()) {
+      return false;
     }
-    return false;
+    if (!checkUninitializedRegionPreconditions()) {
+      return false;
+    }
+    if (!checkCreatePreconditions()) {
+      return false;
+    }
+    if (!checkExpectedOldValuePrecondition()) {
+      return false;
+    }
+    return true;
   }
 
-  private boolean continueUpdate() {
+  private boolean checkUpdatePreconditions() {
     if (isIfOld()) {
       final EntryEventImpl event = getEvent();
       final RegionEntry re = getRegionEntry();
@@ -364,23 +371,29 @@ public class RegionMapPut extends AbstractRegionMapPut {
     return true;
   }
 
-  private boolean continueOverwriteDestroyed() {
-    Token oldValueInVM = getRegionEntry().getValueAsToken();
-    // if region is under GII, check if token is destroyed
-    if (!isOverwriteDestroyed()) {
-      if (!getOwner().isInitialized()
-          && (oldValueInVM == Token.DESTROYED || oldValueInVM == 
Token.TOMBSTONE)) {
-        getEvent().setOldValueDestroyedToken();
-        return false;
+  private boolean checkUninitializedRegionPreconditions() {
+    if (!getOwner().isInitialized()) {
+      if (!isOverwriteDestroyed()) {
+        Token oldValueInVM = getRegionEntry().getValueAsToken();
+        if (oldValueInVM == Token.DESTROYED || oldValueInVM == 
Token.TOMBSTONE) {
+          getEvent().setOldValueDestroyedToken();
+          return false;
+        }
       }
     }
-    if (isIfNew() && !Token.isRemoved(oldValueInVM)) {
-      return false;
+    return true;
+  }
+
+  private boolean checkCreatePreconditions() {
+    if (isIfNew()) {
+      if (!getRegionEntry().isDestroyedOrRemoved()) {
+        return false;
+      }
     }
     return true;
   }
 
-  private boolean satisfiesExpectedOldValue() {
+  private boolean checkExpectedOldValuePrecondition() {
     // replace is propagated to server, so no need to check
     // satisfiesOldValue on client
     final EntryEventImpl event = getEvent();
@@ -431,10 +444,7 @@ public class RegionMapPut extends AbstractRegionMapPut {
     } else {
       getOwner().updateSizeOnCreate(key, newBucketSize);
       if (!wasTombstone) {
-        CachePerfStats stats = getOwner().getCachePerfStats();
-        if (stats != null) {
-          stats.incEntryCount(1);
-        }
+        getOwner().getCachePerfStats().incEntryCount(1);
       }
     }
   }
diff --git 
a/geode-core/src/test/java/org/apache/geode/internal/cache/map/RegionMapPutTest.java
 
b/geode-core/src/test/java/org/apache/geode/internal/cache/map/RegionMapPutTest.java
index 391575e..dfbe381 100644
--- 
a/geode-core/src/test/java/org/apache/geode/internal/cache/map/RegionMapPutTest.java
+++ 
b/geode-core/src/test/java/org/apache/geode/internal/cache/map/RegionMapPutTest.java
@@ -17,10 +17,15 @@
 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.AdditionalMatchers.not;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.same;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
@@ -28,22 +33,30 @@ import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import java.util.Map;
+import java.util.Set;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
 
 import org.apache.geode.cache.CacheWriter;
+import org.apache.geode.cache.DiskAccessException;
 import org.apache.geode.cache.Operation;
+import org.apache.geode.cache.RegionAttributes;
 import org.apache.geode.cache.Scope;
 import org.apache.geode.internal.cache.CachePerfStats;
 import org.apache.geode.internal.cache.EntryEventImpl;
 import org.apache.geode.internal.cache.EntryEventSerialization;
+import org.apache.geode.internal.cache.ImageState;
 import org.apache.geode.internal.cache.InternalRegion;
 import org.apache.geode.internal.cache.RegionClearedException;
 import org.apache.geode.internal.cache.RegionEntry;
 import org.apache.geode.internal.cache.RegionEntryFactory;
 import org.apache.geode.internal.cache.Token;
+import 
org.apache.geode.internal.cache.versions.ConcurrentCacheModificationException;
+import org.apache.geode.internal.cache.versions.VersionStamp;
+import org.apache.geode.internal.cache.versions.VersionTag;
+import org.apache.geode.internal.cache.wan.GatewaySenderEventImpl;
 import org.apache.geode.test.junit.categories.UnitTest;
 
 @Category(UnitTest.class)
@@ -53,7 +66,7 @@ public class RegionMapPutTest {
   private final CacheModificationLock cacheModificationLock = 
mock(CacheModificationLock.class);
   private final EntryEventSerialization entryEventSerialization =
       mock(EntryEventSerialization.class);
-  private final RegionEntry regionEntry = mock(RegionEntry.class);
+  private final RegionEntry createdRegionEntry = mock(RegionEntry.class);
   private final EntryEventImpl event = mock(EntryEventImpl.class);
   private boolean ifNew = false;
   private boolean ifOld = false;
@@ -61,21 +74,25 @@ public class RegionMapPutTest {
   private Object expectedOldValue = null;
   private boolean overwriteDestroyed = false;
   private RegionMapPut instance;
+  private final RegionEntry existingRegionEntry = mock(RegionEntry.class);
 
   @Before
   public void setup() {
     RegionEntryFactory regionEntryFactory = mock(RegionEntryFactory.class);
-    when(regionEntryFactory.createEntry(any(), any(), 
any())).thenReturn(regionEntry);
+    when(regionEntryFactory.createEntry(any(), any(), 
any())).thenReturn(createdRegionEntry);
     when(internalRegion.getScope()).thenReturn(Scope.LOCAL);
     when(internalRegion.isInitialized()).thenReturn(true);
     
when(internalRegion.getCachePerfStats()).thenReturn(mock(CachePerfStats.class));
+    
when(internalRegion.getAttributes()).thenReturn(mock(RegionAttributes.class));
+    when(internalRegion.getImageState()).thenReturn(mock(ImageState.class));
     when(focusedRegionMap.getEntryMap()).thenReturn(mock(Map.class));
     when(focusedRegionMap.getEntryFactory()).thenReturn(regionEntryFactory);
-    when(event.getOperation()).thenReturn(Operation.UPDATE);
+    givenAnOperationThatDoesNotGuaranteeOldValue();
     when(event.getKey()).thenReturn("key");
     when(event.getRegion()).thenReturn(internalRegion);
-    when(regionEntry.getValueAsToken()).thenReturn(Token.REMOVED_PHASE1);
-    when(regionEntry.isRemoved()).thenReturn(true);
+    
when(createdRegionEntry.getValueAsToken()).thenReturn(Token.REMOVED_PHASE1);
+    when(createdRegionEntry.isRemoved()).thenReturn(true);
+    when(createdRegionEntry.isDestroyedOrRemoved()).thenReturn(true);
   }
 
   private void createInstance() {
@@ -90,12 +107,298 @@ public class RegionMapPutTest {
   }
 
   @Test
+  public void 
doesNotSetEventOldValueToExistingRegionEntryValue_ifNotRequired() {
+    givenExistingRegionEntry();
+    givenAnOperationThatDoesNotGuaranteeOldValue();
+    givenPutDoesNotNeedToDoCacheWrite();
+    this.requireOldValue = false;
+
+    Object oldValue = new Object();
+    when(existingRegionEntry.getValue()).thenReturn(oldValue);
+
+    doPut();
+
+    verify(event, never()).setOldValue(any());
+    verify(event, never()).setOldValue(any(), anyBoolean());
+  }
+
+  @Test
+  public void 
setsEventOldValueToExistingRegionEntryValue_ifOldValueIsGatewaySenderEvent() {
+    givenExistingRegionEntry();
+
+    GatewaySenderEventImpl oldValue = new GatewaySenderEventImpl();
+    when(existingRegionEntry.getValue()).thenReturn(oldValue);
+
+    doPut();
+
+    verify(event, times(1)).setOldValue(same(oldValue), eq(true));
+    verify(event, never()).setOldValue(not(same(oldValue)), eq(true));
+  }
+
+  @Test
+  public void 
setsEventOldValueToExistingRegionEntryValue_ifOperationGuaranteesOldValue() {
+    givenExistingRegionEntry();
+    givenAnOperationThatGuaranteesOldValue();
+
+    Object oldValue = new Object();
+    
when(existingRegionEntry.getValueOffHeapOrDiskWithoutFaultIn(same(internalRegion)))
+        .thenReturn(oldValue);
+
+    doPut();
+
+    verify(event, times(1)).setOldValue(same(oldValue), eq(true));
+    verify(event, never()).setOldValue(not(same(oldValue)), eq(true));
+  }
+
+  @Test
+  public void eventPutExistingEntryGivenOldValue_ifRetrieveOldValueForDelta()
+      throws RegionClearedException {
+    givenThatRunWhileEvictionDisabledCallsItsRunnable();
+    givenExistingRegionEntry();
+    Object oldValue = new Object();
+    when(existingRegionEntry.getValue(any())).thenReturn(oldValue);
+    when(event.getDeltaBytes()).thenReturn(new byte[1]);
+
+    doPut();
+
+    verify(event, times(1)).putExistingEntry(same(internalRegion), 
same(existingRegionEntry),
+        eq(false), same(oldValue));
+  }
+
+  @Test
+  public void 
eventPutExistingEntryGivenNullOldValue_ifNotRetrieveOldValueForDelta()
+      throws RegionClearedException {
+    givenThatRunWhileEvictionDisabledCallsItsRunnable();
+    givenExistingRegionEntry();
+    Object oldValue = new Object();
+    when(existingRegionEntry.getValue(any())).thenReturn(oldValue);
+    when(event.getDeltaBytes()).thenReturn(null);
+
+    doPut();
+
+    verify(event, times(1)).putExistingEntry(same(internalRegion), 
same(existingRegionEntry),
+        eq(false), eq(null));
+  }
+
+  @Test
+  public void 
setsEventOldValueToExistingRegionEntryValue_ifIsRequiredOldValueAndOperationDoesNotGuaranteeOldValue()
 {
+    this.requireOldValue = true;
+    givenExistingRegionEntry();
+    givenAnOperationThatDoesNotGuaranteeOldValue();
+
+    Object oldValue = new Object();
+    when(existingRegionEntry.getValueRetain(same(internalRegion), 
eq(true))).thenReturn(oldValue);
+
+    doPut();
+
+    verify(event, times(1)).setOldValue(same(oldValue));
+    verify(event, never()).setOldValue(not(same(oldValue)));
+  }
+
+  @Test
+  public void 
setsEventOldValueToExistingRegionEntryValue_ifIsCacheWriteAndOperationDoesNotGuaranteeOldValue()
 {
+    givenExistingRegionEntry();
+    givenAnOperationThatDoesNotGuaranteeOldValue();
+    givenPutNeedsToDoCacheWrite();
+
+    Object oldValue = new Object();
+    when(existingRegionEntry.getValueRetain(same(internalRegion), 
eq(true))).thenReturn(oldValue);
+
+    doPut();
+
+    verify(event, times(1)).setOldValue(same(oldValue));
+    verify(event, never()).setOldValue(not(same(oldValue)));
+  }
+
+  @Test
+  public void 
setsEventOldValueToNotAvailable_ifIsCacheWriteAndOperationDoesNotGuaranteeOldValue_andExistingValueIsNull()
 {
+    givenPutNeedsToDoCacheWrite();
+    givenAnOperationThatDoesNotGuaranteeOldValue();
+    givenExistingRegionEntry();
+
+    when(existingRegionEntry.getValueRetain(same(internalRegion), 
eq(true))).thenReturn(null);
+
+    doPut();
+
+    verify(event, times(1)).setOldValue(Token.NOT_AVAILABLE);
+  }
+
+  @Test
   public void retrieveOldValueForDeltaDefaultToFalse() {
     createInstance();
 
     assertThat(instance.isRetrieveOldValueForDelta()).isFalse();
   }
 
+
+  @Test
+  public void 
eventOldValueNotAvailableCalled_ifCacheWriteNotNeededAndNotInitialized() {
+    givenPutDoesNotNeedToDoCacheWrite();
+    when(internalRegion.isInitialized()).thenReturn(false);
+
+    doPut();
+
+    verify(event, times(1)).oldValueNotAvailable();
+  }
+
+  @Test
+  public void 
eventOperationNotSet_ifCacheWriteNeededAndInitializedAndReplaceOnClient() {
+    givenPutNeedsToDoCacheWrite();
+    when(internalRegion.isInitialized()).thenReturn(true);
+    givenReplaceOnClient();
+
+    doPut();
+
+    verify(event, never()).makeCreate();
+    verify(event, never()).makeUpdate();
+  }
+
+  @Test
+  public void putExistingEntryCalled_ifReplaceOnClient() throws 
RegionClearedException {
+    givenPutDoesNotNeedToDoCacheWrite();
+    when(internalRegion.isInitialized()).thenReturn(true);
+    givenReplaceOnClient();
+
+    doPut();
+
+    verify(event, times(1)).putExistingEntry(any(), any(), anyBoolean(), 
any());
+  }
+
+  @Test
+  public void 
eventOperationMadeCreate_ifCacheWriteNeededAndInitializedAndNotReplaceOnClientAndEntryRemoved()
 {
+    givenPutNeedsToDoCacheWrite();
+    when(internalRegion.isInitialized()).thenReturn(true);
+    givenReplaceOnPeer();
+    when(createdRegionEntry.isDestroyedOrRemoved()).thenReturn(true);
+
+    doPut();
+
+    verify(event, times(1)).makeCreate();
+    verify(event, never()).makeUpdate();
+  }
+
+  @Test
+  public void 
eventOperationMadeUpdate_ifCacheWriteNeededAndInitializedAndNotReplaceOnClientAndEntryExists()
 {
+    givenPutNeedsToDoCacheWrite();
+    when(internalRegion.isInitialized()).thenReturn(true);
+    givenReplaceOnPeer();
+    when(createdRegionEntry.isDestroyedOrRemoved()).thenReturn(false);
+
+    doPut();
+
+    verify(event, never()).makeCreate();
+    verify(event, times(1)).makeUpdate();
+  }
+
+  @Test
+  public void cacheWriteBeforePutNotCalled_ifNotInitialized() {
+    givenPutNeedsToDoCacheWrite();
+    when(internalRegion.isInitialized()).thenReturn(false);
+
+    doPut();
+
+    verify(internalRegion, never()).cacheWriteBeforePut(any(), any(), any(), 
anyBoolean(), any());
+  }
+
+  @Test
+  public void cacheWriteBeforePutNotCalled_ifCacheWriteNotNeeded() {
+    givenPutDoesNotNeedToDoCacheWrite();
+    when(internalRegion.isInitialized()).thenReturn(true);
+
+    doPut();
+
+    verify(internalRegion, never()).cacheWriteBeforePut(any(), any(), any(), 
anyBoolean(), any());
+  }
+
+  @Test
+  public void 
cacheWriteBeforePutCalledWithRequireOldValue_givenRequireOldValueTrue() {
+    givenPutNeedsToDoCacheWrite();
+    when(internalRegion.isInitialized()).thenReturn(true);
+    this.requireOldValue = true;
+
+    doPut();
+
+    verify(internalRegion, times(1)).cacheWriteBeforePut(same(event), any(), 
any(),
+        eq(this.requireOldValue), eq(null));
+  }
+
+  @Test
+  public void 
cacheWriteBeforePutCalledWithExpectedOldValue_givenRequireOldValueTrue() {
+    givenPutNeedsToDoCacheWrite();
+    when(internalRegion.isInitialized()).thenReturn(true);
+    givenAnOperationThatGuaranteesOldValue();
+    givenExistingRegionEntry();
+    when(existingRegionEntry.getValueRetain(same(internalRegion), 
eq(true))).thenReturn(null);
+    expectedOldValue = "expectedOldValue";
+    when(event.getRawOldValue()).thenReturn(expectedOldValue);
+
+    doPut();
+
+    verify(internalRegion, times(1)).cacheWriteBeforePut(same(event), any(), 
any(), anyBoolean(),
+        same(expectedOldValue));
+  }
+
+  @Test
+  public void 
putWithExpectedOldValueReturnsNull_ifExistingValueIsNotExpected() {
+    givenAnOperationThatGuaranteesOldValue();
+    expectedOldValue = "expectedOldValue";
+    when(event.getRawOldValue()).thenReturn("unexpectedValue");
+
+    RegionEntry result = doPut();
+
+    assertThat(result).isNull();
+  }
+
+  @Test
+  public void 
putWithExpectedOldValueReturnsRegionEntry_ifExistingValueIsExpected() {
+    givenAnOperationThatGuaranteesOldValue();
+    expectedOldValue = "expectedOldValue";
+    when(event.getRawOldValue()).thenReturn(expectedOldValue);
+
+    RegionEntry result = doPut();
+
+    assertThat(result).isSameAs(createdRegionEntry);
+  }
+
+  @Test
+  public void 
putWithExpectedOldValueReturnsRegionEntry_ifExistingValueIsNotExpectedButIsReplaceOnClient()
 {
+    givenAnOperationThatGuaranteesOldValue();
+    expectedOldValue = "expectedOldValue";
+    when(event.getRawOldValue()).thenReturn("unexpectedValue");
+    givenReplaceOnClient();
+
+    RegionEntry result = doPut();
+
+    assertThat(result).isSameAs(createdRegionEntry);
+  }
+
+  @Test
+  public void cacheWriteBeforePutCalledWithCacheWriter_givenACacheWriter() {
+    givenPutNeedsToDoCacheWrite();
+    when(internalRegion.isInitialized()).thenReturn(true);
+    CacheWriter cacheWriter = mock(CacheWriter.class);
+    when(internalRegion.basicGetWriter()).thenReturn(cacheWriter);
+
+    doPut();
+
+    verify(internalRegion, times(1)).cacheWriteBeforePut(same(event), 
eq(null), same(cacheWriter),
+        anyBoolean(), eq(null));
+  }
+
+  @Test
+  public void 
cacheWriteBeforePutCalledWithNetWriteRecipients_ifAdviseNetWrite() {
+    givenPutNeedsToDoCacheWrite();
+    when(internalRegion.isInitialized()).thenReturn(true);
+    when(internalRegion.basicGetWriter()).thenReturn(null);
+    Set netWriteRecipients = mock(Set.class);
+    when(internalRegion.adviseNetWrite()).thenReturn(netWriteRecipients);
+
+    doPut();
+
+    verify(internalRegion, times(1)).cacheWriteBeforePut(same(event), 
eq(netWriteRecipients),
+        eq(null), anyBoolean(), eq(null));
+  }
+
   @Test
   public void retrieveOldValueForDeltaTrueIfEventHasDeltaBytes() {
     when(event.getDeltaBytes()).thenReturn(new byte[1]);
@@ -124,8 +427,7 @@ public class RegionMapPutTest {
 
   @Test
   public void replaceOnClientIsTrueIfOperationIsReplaceAndOwnerIsClient() {
-    when(event.getOperation()).thenReturn(Operation.REPLACE);
-    when(internalRegion.hasServerProxy()).thenReturn(true);
+    givenReplaceOnClient();
 
     createInstance();
 
@@ -134,7 +436,7 @@ public class RegionMapPutTest {
 
   @Test
   public void replaceOnClientIsFalseIfOperationIsReplaceAndOwnerIsNotClient() {
-    when(event.getOperation()).thenReturn(Operation.REPLACE);
+    givenReplaceOnPeer();
 
     createInstance();
 
@@ -142,6 +444,137 @@ public class RegionMapPutTest {
   }
 
   @Test
+  public void putReturnsNull_ifOnlyExistingAndEntryIsTombstone() {
+    ifOld = true;
+    givenExistingRegionEntry();
+    when(existingRegionEntry.isTombstone()).thenReturn(true);
+
+    RegionEntry result = doPut();
+
+    assertThat(result).isNull();
+  }
+
+  @Test
+  public void putReturnsExistingEntry_ifOnlyExistingAndEntryIsNotTombstone() {
+    ifOld = true;
+    givenExistingRegionEntry();
+    when(existingRegionEntry.isTombstone()).thenReturn(false);
+
+    RegionEntry result = doPut();
+
+    assertThat(result).isSameAs(existingRegionEntry);
+  }
+
+  @Test
+  public void putReturnsNull_ifOnlyExistingAndEntryIsRemoved() {
+    ifOld = true;
+    givenExistingRegionEntry();
+    when(existingRegionEntry.isRemoved()).thenReturn(true);
+    givenReplaceOnPeer();
+
+    RegionEntry result = doPut();
+
+    assertThat(result).isNull();
+  }
+
+  @Test
+  public void 
putReturnsExistingEntry_ifOnlyExistingEntryIsRemovedAndReplaceOnClient() {
+    ifOld = true;
+    givenExistingRegionEntry();
+    when(existingRegionEntry.isRemoved()).thenReturn(true);
+    givenReplaceOnClient();
+
+    RegionEntry result = doPut();
+
+    assertThat(result).isSameAs(existingRegionEntry);
+  }
+
+  @Test
+  public void 
putReturnsExistingEntry_ifReplaceOnClientAndTombstoneButNoVersionTag() {
+    ifOld = true;
+    givenReplaceOnClient();
+    givenExistingRegionEntry();
+    when(existingRegionEntry.isTombstone()).thenReturn(true);
+    when(event.getVersionTag()).thenReturn(null);
+
+    RegionEntry result = doPut();
+
+    assertThat(result).isSameAs(existingRegionEntry);
+  }
+
+  @Test
+  public void putReturnsNull_ifReplaceOnClientAndTombstoneAndVersionTag()
+      throws RegionClearedException {
+    ifOld = true;
+    givenReplaceOnClient();
+    givenExistingRegionEntry();
+    when(existingRegionEntry.isTombstone()).thenReturn(true);
+    
when(existingRegionEntry.getVersionStamp()).thenReturn(mock(VersionStamp.class));
+    when(event.getVersionTag()).thenReturn(mock(VersionTag.class));
+
+    RegionEntry result = doPut();
+
+    assertThat(result).isNull();
+    verify(existingRegionEntry, times(1)).setValue(internalRegion, 
Token.TOMBSTONE);
+    verify(internalRegion, 
times(1)).rescheduleTombstone(same(existingRegionEntry), any());
+  }
+
+  @Test
+  public void 
createWithoutOverwriteDestroyedReturnsNullAndCallsSetOldValueDestroyedToken_ifRegionUninitializedAndCurrentValueIsDestroyed()
 {
+    overwriteDestroyed = false;
+    when(internalRegion.isInitialized()).thenReturn(false);
+    when(createdRegionEntry.getValueAsToken()).thenReturn(Token.DESTROYED);
+
+    RegionEntry result = doPut();
+
+    assertThat(result).isNull();
+    verify(event, times(1)).setOldValueDestroyedToken();
+  }
+
+  @Test
+  public void 
createWithoutOverwriteDestroyedReturnsNullAndCallsSetOldValueDestroyedToken_ifRegionUninitializedAndCurrentValueIsTombstone()
 {
+    overwriteDestroyed = false;
+    when(internalRegion.isInitialized()).thenReturn(false);
+    when(createdRegionEntry.getValueAsToken()).thenReturn(Token.TOMBSTONE);
+
+    RegionEntry result = doPut();
+
+    assertThat(result).isNull();
+    verify(event, times(1)).setOldValueDestroyedToken();
+  }
+
+  @Test
+  public void 
createWithOverwriteDestroyedReturnsCreatedRegionEntry_ifRegionUninitializedAndCurrentValueIsDestroyed()
 {
+    overwriteDestroyed = true;
+    when(internalRegion.isInitialized()).thenReturn(false);
+    when(createdRegionEntry.getValueAsToken()).thenReturn(Token.DESTROYED);
+
+    RegionEntry result = doPut();
+
+    assertThat(result).isSameAs(createdRegionEntry);
+    verify(event, never()).setOldValueDestroyedToken();
+  }
+
+  @Test
+  public void 
putIgnoresRegionClearedException_ifReplaceOnClientAndTombstoneAndVersionTag()
+      throws RegionClearedException {
+    ifOld = true;
+    givenReplaceOnClient();
+    givenExistingRegionEntry();
+    when(existingRegionEntry.isTombstone()).thenReturn(true);
+    
when(existingRegionEntry.getVersionStamp()).thenReturn(mock(VersionStamp.class));
+    when(event.getVersionTag()).thenReturn(mock(VersionTag.class));
+    
doThrow(RegionClearedException.class).when(existingRegionEntry).setValue(internalRegion,
+        Token.TOMBSTONE);
+
+    RegionEntry result = doPut();
+
+    assertThat(result).isNull();
+    verify(existingRegionEntry, times(1)).setValue(internalRegion, 
Token.TOMBSTONE);
+    verify(internalRegion, 
times(1)).rescheduleTombstone(same(existingRegionEntry), any());
+  }
+
+  @Test
   public void onlyExistingDefaultsToFalse() {
     createInstance();
 
@@ -160,8 +593,7 @@ public class RegionMapPutTest {
   @Test
   public void onlyExistingIsFalseIfOldAndReplaceOnClient() {
     ifOld = true;
-    when(event.getOperation()).thenReturn(Operation.REPLACE);
-    when(internalRegion.hasServerProxy()).thenReturn(true);
+    givenReplaceOnClient();
 
     createInstance();
 
@@ -176,7 +608,7 @@ public class RegionMapPutTest {
   }
 
   @Test
-  public void cacheWriteIsFaseIfGenerateCallbacksButNotDistributedEtc() {
+  public void cacheWriteIsFalseIfGenerateCallbacksButNotDistributedEtc() {
     when(event.isGenerateCallbacks()).thenReturn(true);
     createInstance();
 
@@ -230,6 +662,130 @@ public class RegionMapPutTest {
     assertThat(instance.isCacheWrite()).isFalse();
   }
 
+  @Test
+  public void basicPutPart2ToldClearDidNotOccur_ifPutDoneWithoutAClear() 
throws Exception {
+    ifNew = true;
+    when(event.getOperation()).thenReturn(Operation.CREATE);
+
+    doPut();
+
+    verify(internalRegion, times(1)).basicPutPart2(any(), any(), anyBoolean(), 
anyLong(),
+        eq(false));
+  }
+
+  @Test
+  public void basicPutPart2ToldClearDidOccur_ifPutDoneWithAClear() throws 
Exception {
+    ifNew = true;
+    when(event.getOperation()).thenReturn(Operation.CREATE);
+    doThrow(RegionClearedException.class).when(event).putNewEntry(any(), 
any());
+
+    doPut();
+
+    verify(internalRegion, times(1)).basicPutPart2(any(), any(), anyBoolean(), 
anyLong(), eq(true));
+  }
+
+  @Test
+  public void lruUpdateCallbackCalled_ifPutDoneWithoutAClear() throws 
Exception {
+    ifNew = true;
+    when(event.getOperation()).thenReturn(Operation.CREATE);
+
+    doPut();
+
+    verify(focusedRegionMap, times(1)).lruUpdateCallback();
+  }
+
+  @Test
+  public void lruUpdateCallbackNotCalled_ifPutDoneWithAClear() throws 
Exception {
+    ifNew = true;
+    when(event.getOperation()).thenReturn(Operation.CREATE);
+    doThrow(RegionClearedException.class).when(event).putNewEntry(any(), 
any());
+
+    doPut();
+
+    verify(focusedRegionMap, never()).lruUpdateCallback();
+  }
+
+  @Test
+  public void lruEnryCreateCalled_ifCreateDoneWithoutAClear() throws Exception 
{
+    ifNew = true;
+    when(event.getOperation()).thenReturn(Operation.CREATE);
+
+    doPut();
+
+    verify(focusedRegionMap, times(1)).lruEntryCreate(createdRegionEntry);
+  }
+
+  @Test
+  public void lruEnryCreateNotCalled_ifCreateDoneWithAClear() throws Exception 
{
+    ifNew = true;
+    when(event.getOperation()).thenReturn(Operation.CREATE);
+    doThrow(RegionClearedException.class).when(event).putNewEntry(any(), 
any());
+
+    doPut();
+
+    verify(focusedRegionMap, never()).lruEntryCreate(createdRegionEntry);
+  }
+
+  @Test
+  public void lruEnryUpdateCalled_ifUpdateDoneWithoutAClear() throws Exception 
{
+    ifOld = true;
+    RegionEntry existingRegionEntry = mock(RegionEntry.class);
+    when(focusedRegionMap.getEntry(event)).thenReturn(existingRegionEntry);
+
+    doPut();
+
+    verify(focusedRegionMap, times(1)).lruEntryUpdate(existingRegionEntry);
+  }
+
+  @Test
+  public void lruEnryUpdateNotCalled_ifUpdateDoneWithAClear() throws Exception 
{
+    ifOld = true;
+    RegionEntry existingRegionEntry = mock(RegionEntry.class);
+    when(focusedRegionMap.getEntry(event)).thenReturn(existingRegionEntry);
+    doThrow(RegionClearedException.class).when(event).putExistingEntry(any(), 
any(), anyBoolean(),
+        any());
+
+    doPut();
+
+    verify(focusedRegionMap, never()).lruEntryUpdate(existingRegionEntry);
+  }
+
+  @Test
+  public void putThrows_ifCreateDoneWithConcurrentCacheModificationException() 
throws Exception {
+    ifNew = true;
+    when(event.getOperation()).thenReturn(Operation.CREATE);
+    
doThrow(ConcurrentCacheModificationException.class).when(event).putNewEntry(any(),
 any());
+
+    assertThatThrownBy(() -> 
doPut()).isInstanceOf(ConcurrentCacheModificationException.class);
+
+    verify(event, times(1)).getVersionTag();
+  }
+
+  @Test
+  public void 
putInvokesNotifyTimestampsToGateways_ifCreateDoneWithConcurrentCacheModificationException()
+      throws Exception {
+    ifNew = true;
+    when(event.getOperation()).thenReturn(Operation.CREATE);
+    
doThrow(ConcurrentCacheModificationException.class).when(event).putNewEntry(any(),
 any());
+    VersionTag versionTag = mock(VersionTag.class);
+    when(versionTag.isTimeStampUpdated()).thenReturn(true);
+    when(event.getVersionTag()).thenReturn(versionTag);
+
+    assertThatThrownBy(() -> 
doPut()).isInstanceOf(ConcurrentCacheModificationException.class);
+
+    verify(internalRegion, times(1)).notifyTimestampsToGateways(same(event));
+  }
+
+  @Test
+  public void putThrows_ifLruUpdateCallbackThrowsDiskAccessException() throws 
Exception {
+    ifNew = true;
+    when(event.getOperation()).thenReturn(Operation.CREATE);
+    
doThrow(DiskAccessException.class).when(focusedRegionMap).lruUpdateCallback();
+
+    assertThatThrownBy(() -> doPut()).isInstanceOf(DiskAccessException.class);
+
+    verify(internalRegion, times(1)).handleDiskAccessException(any());
+  }
 
   @Test
   public void createOnEmptyMapAddsEntry() throws Exception {
@@ -238,8 +794,8 @@ public class RegionMapPutTest {
 
     RegionEntry result = doPut();
 
-    assertThat(result).isSameAs(regionEntry);
-    verify(event, times(1)).putNewEntry(internalRegion, regionEntry);
+    assertThat(result).isSameAs(createdRegionEntry);
+    verify(event, times(1)).putNewEntry(internalRegion, createdRegionEntry);
     verify(internalRegion, times(1)).basicPutPart2(eq(event), eq(result), 
eq(true), anyLong(),
         eq(false));
     verify(internalRegion, times(1)).basicPutPart3(eq(event), eq(result), 
eq(true), anyLong(),
@@ -247,6 +803,26 @@ public class RegionMapPutTest {
   }
 
   @Test
+  public void putWithTombstoneNewValue_callsBasicPutPart3WithFalse() {
+    when(event.basicGetNewValue()).thenReturn(Token.TOMBSTONE);
+
+    doPut();
+
+    verify(internalRegion, times(1)).basicPutPart3(any(), any(), anyBoolean(), 
anyLong(), eq(false),
+        anyBoolean(), anyBoolean(), any(), anyBoolean());
+  }
+
+  @Test
+  public void putWithNonTombstoneNewValue_callsBasicPutPart3WithTrue() {
+    when(event.basicGetNewValue()).thenReturn("newValue");
+
+    doPut();
+
+    verify(internalRegion, times(1)).basicPutPart3(any(), any(), anyBoolean(), 
anyLong(), eq(true),
+        anyBoolean(), anyBoolean(), any(), anyBoolean());
+  }
+
+  @Test
   public void putOnEmptyMapAddsEntry() throws Exception {
     ifNew = false;
     ifOld = false;
@@ -254,8 +830,8 @@ public class RegionMapPutTest {
 
     RegionEntry result = doPut();
 
-    assertThat(result).isSameAs(regionEntry);
-    verify(event, times(1)).putNewEntry(internalRegion, regionEntry);
+    assertThat(result).isSameAs(createdRegionEntry);
+    verify(event, times(1)).putNewEntry(internalRegion, createdRegionEntry);
     verify(internalRegion, times(1)).basicPutPart2(eq(event), eq(result), 
eq(true), anyLong(),
         eq(false));
     verify(internalRegion, times(1)).basicPutPart3(eq(event), eq(result), 
eq(true), anyLong(),
@@ -298,7 +874,7 @@ public class RegionMapPutTest {
   public void createOnEntryReturnedFromPutIfAbsentDoesNothing() throws 
RegionClearedException {
     ifNew = true;
     when(focusedRegionMap.getEntry(event)).thenReturn(mock(RegionEntry.class));
-    when(focusedRegionMap.putEntryIfAbsent(event.getKey(), regionEntry))
+    when(focusedRegionMap.putEntryIfAbsent(event.getKey(), createdRegionEntry))
         .thenReturn(mock(RegionEntry.class));
     when(event.getOperation()).thenReturn(Operation.CREATE);
 
@@ -318,14 +894,14 @@ public class RegionMapPutTest {
     ifNew = true;
     RegionEntry existingRegionEntry = mock(RegionEntry.class);
     when(existingRegionEntry.isRemovedPhase2()).thenReturn(true);
-    when(focusedRegionMap.putEntryIfAbsent(event.getKey(), regionEntry))
+    when(focusedRegionMap.putEntryIfAbsent(event.getKey(), createdRegionEntry))
         .thenReturn(existingRegionEntry).thenReturn(null);
     when(event.getOperation()).thenReturn(Operation.CREATE);
 
     RegionEntry result = doPut();
 
-    assertThat(result).isSameAs(regionEntry);
-    verify(event, times(1)).putNewEntry(internalRegion, regionEntry);
+    assertThat(result).isSameAs(createdRegionEntry);
+    verify(event, times(1)).putNewEntry(internalRegion, createdRegionEntry);
     verify(internalRegion, times(1)).basicPutPart2(eq(event), eq(result), 
eq(true), anyLong(),
         eq(false));
     verify(internalRegion, times(1)).basicPutPart3(eq(event), eq(result), 
eq(true), anyLong(),
@@ -371,4 +947,44 @@ public class RegionMapPutTest {
         eq(true), eq(ifNew), eq(ifOld), eq(expectedOldValue), 
eq(requireOldValue));
   }
 
+  private void givenAnOperationThatDoesNotGuaranteeOldValue() {
+    when(event.getOperation()).thenReturn(Operation.UPDATE);
+  }
+
+  private void givenAnOperationThatGuaranteesOldValue() {
+    when(event.getOperation()).thenReturn(Operation.PUT_IF_ABSENT);
+  }
+
+  private void givenPutNeedsToDoCacheWrite() {
+    when(event.isGenerateCallbacks()).thenReturn(true);
+    when(internalRegion.getScope()).thenReturn(Scope.DISTRIBUTED_ACK);
+  }
+
+  private void givenPutDoesNotNeedToDoCacheWrite() {
+    when(event.isGenerateCallbacks()).thenReturn(false);
+    when(internalRegion.getScope()).thenReturn(Scope.DISTRIBUTED_ACK);
+  }
+
+  private void givenExistingRegionEntry() {
+    when(focusedRegionMap.getEntry(event)).thenReturn(existingRegionEntry);
+  }
+
+  private void givenReplaceOnClient() {
+    when(event.getOperation()).thenReturn(Operation.REPLACE);
+    when(internalRegion.hasServerProxy()).thenReturn(true);
+  }
+
+  private void givenReplaceOnPeer() {
+    when(event.getOperation()).thenReturn(Operation.REPLACE);
+    when(internalRegion.hasServerProxy()).thenReturn(false);
+  }
+
+  private void givenThatRunWhileEvictionDisabledCallsItsRunnable() {
+    doAnswer(invocation -> {
+      Runnable runnable = invocation.getArgument(0);
+      runnable.run();
+      return null;
+    }).when(focusedRegionMap).runWhileEvictionDisabled(any());
+  }
+
 }

-- 
To stop receiving notification emails like this one, please contact
[email protected].

Reply via email to