geode-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From sai_boorlaga...@apache.org
Subject [geode] branch develop updated: GEODE-5223 Add unit tests for AbstractRegionMap.txApplyDestroy (#1988)
Date Fri, 01 Jun 2018 17:27:15 GMT
This is an automated email from the ASF dual-hosted git repository.

sai_boorlagadda 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 af07c3c  GEODE-5223 Add unit tests for AbstractRegionMap.txApplyDestroy (#1988)
af07c3c is described below

commit af07c3c674559ff562f902b58ad5e295282555b6
Author: Dale Emery <dale@dhemery.com>
AuthorDate: Fri Jun 1 10:26:19 2018 -0700

    GEODE-5223 Add unit tests for AbstractRegionMap.txApplyDestroy (#1988)
---
 .../geode/internal/cache/AbstractRegionMap.java    |   66 +-
 .../apache/geode/internal/cache/BucketRegion.java  |    1 +
 .../geode/internal/cache/InternalRegion.java       |    3 +
 .../geode/internal/cache/ProxyRegionMap.java       |    6 +-
 .../apache/geode/internal/cache/RegionEntry.java   |    2 +-
 .../cache/AbstractRegionMapTxApplyDestroyTest.java | 1480 ++++++++++++++++++++
 6 files changed, 1522 insertions(+), 36 deletions(-)

diff --git a/geode-core/src/main/java/org/apache/geode/internal/cache/AbstractRegionMap.java b/geode-core/src/main/java/org/apache/geode/internal/cache/AbstractRegionMap.java
index 50b5371..ef4f2eb 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/cache/AbstractRegionMap.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/cache/AbstractRegionMap.java
@@ -922,7 +922,7 @@ public abstract class AbstractRegionMap
                     done = true;
                   } finally {
                     if (event != null) {
-                      event.release();
+                      releaseEvent(event);
                       event = null;
                     }
                   }
@@ -974,7 +974,7 @@ public abstract class AbstractRegionMap
                 done = true;
               } finally {
                 if (event != null) {
-                  event.release();
+                  releaseEvent(event);
                   event = null;
                 }
               }
@@ -998,7 +998,7 @@ public abstract class AbstractRegionMap
         } // synchronized
       } finally {
         if (event != null)
-          event.release();
+          releaseEvent(event);
         OffHeapHelper.release(oldValue);
       }
     } catch (RegionClearedException rce) {
@@ -1048,7 +1048,7 @@ public abstract class AbstractRegionMap
     final LocalRegion owner = _getOwner();
 
     final boolean isRegionReady = !inTokenMode;
-    final boolean hasRemoteOrigin = !((TXId) txId).getMemberId().equals(owner.getMyId());
+    final boolean hasRemoteOrigin = !txId.getMemberId().equals(owner.getMyId());
     boolean callbackEventAddedToPending = false;
     IndexManager oqlIndexManager = owner.getIndexManager();
     try {
@@ -1068,9 +1068,8 @@ public abstract class AbstractRegionMap
               // Create an entry event only if the calling context is
               // a receipt of a TXCommitMessage AND there are callbacks installed
               // for this region
-              boolean invokeCallbacks = shouldCreateCallbackEvent(owner, isRegionReady || inRI);
               @Released
-              EntryEventImpl callbackEvent = createCallbackEvent(owner, op, key, null, txId,
+              final EntryEventImpl callbackEvent = createCallbackEvent(owner, op, key, null, txId,
                   txEvent, eventId, aCallbackArgument, filterRoutingInfo, bridgeContext,
                   txEntryState, versionTag, tailKey);
               try {
@@ -1099,7 +1098,7 @@ public abstract class AbstractRegionMap
                   } else {
                     if (!re.isTombstone()) {
                       {
-                        if (shouldPerformConcurrencyChecks(owner, callbackEvent)
+                        if (owner.getConcurrencyChecksEnabled()
                             && callbackEvent.getVersionTag() != null) {
                           re.makeTombstone(owner, callbackEvent.getVersionTag());
                         } else {
@@ -1119,6 +1118,7 @@ public abstract class AbstractRegionMap
                 }
                 owner.txApplyDestroyPart2(re, re.getKey(), inTokenMode,
                     clearOccured /* Clear Conflciting with the operation */);
+                boolean invokeCallbacks = shouldInvokeCallbacks(owner, isRegionReady || inRI);
                 if (invokeCallbacks) {
                   switchEventOwnerAndOriginRemote(callbackEvent, hasRemoteOrigin);
                   pendingCallbacks.add(callbackEvent);
@@ -1127,13 +1127,12 @@ public abstract class AbstractRegionMap
                 if (!clearOccured) {
                   lruEntryDestroy(re);
                 }
-                if (owner.getConcurrencyChecksEnabled() && txEntryState != null
-                    && callbackEvent != null) {
+                if (owner.getConcurrencyChecksEnabled() && txEntryState != null) {
                   txEntryState.setVersionTag(callbackEvent.getVersionTag());
                 }
               } finally {
                 if (!callbackEventAddedToPending)
-                  callbackEvent.release();
+                  releaseEvent(callbackEvent);
               }
             }
           }
@@ -1165,8 +1164,7 @@ public abstract class AbstractRegionMap
                   oldRe = putEntryIfAbsent(key, newRe);
                 } else {
                   try {
-                    boolean invokeCallbacks =
-                        shouldCreateCallbackEvent(owner, isRegionReady || inRI);
+                    boolean invokeCallbacks = shouldInvokeCallbacks(owner, isRegionReady || inRI);
                     callbackEvent = createCallbackEvent(owner, op, key, null, txId, txEvent,
                         eventId, aCallbackArgument, filterRoutingInfo, bridgeContext, txEntryState,
                         versionTag, tailKey);
@@ -1192,6 +1190,8 @@ public abstract class AbstractRegionMap
                           oldSize = owner.calculateRegionEntryValueSize(oldRe);
                         }
                       }
+                      // TODO: Token.DESTROYED should only be used if "inTokenMode".
+                      // Otherwise this should be a TOMBSTONE
                       oldRe.setValue(owner, Token.DESTROYED);
                       EntryLogger.logTXDestroy(_getOwnerObject(), key);
                       if (wasTombstone) {
@@ -1203,13 +1203,13 @@ public abstract class AbstractRegionMap
                       lruEntryDestroy(oldRe);
                     } finally {
                       if (!callbackEventAddedToPending)
-                        callbackEvent.release();
+                        releaseEvent(callbackEvent);
                     }
                   } catch (RegionClearedException rce) {
                     owner.txApplyDestroyPart2(oldRe, oldRe.getKey(), inTokenMode,
                         true /* Clear Conflicting with the operation */);
                   }
-                  if (shouldPerformConcurrencyChecks(owner, callbackEvent)
+                  if (owner.getConcurrencyChecksEnabled()
                       && callbackEvent.getVersionTag() != null) {
                     oldRe.makeTombstone(owner, callbackEvent.getVersionTag());
                   } else if (!inTokenMode) {
@@ -1225,7 +1225,7 @@ public abstract class AbstractRegionMap
             if (!opCompleted) {
               // already has value set to Token.DESTROYED
               opCompleted = true;
-              boolean invokeCallbacks = shouldCreateCallbackEvent(owner, isRegionReady || inRI);
+              boolean invokeCallbacks = shouldInvokeCallbacks(owner, isRegionReady || inRI);
               callbackEvent = createCallbackEvent(owner, op, key, null, txId, txEvent, eventId,
                   aCallbackArgument, filterRoutingInfo, bridgeContext, txEntryState, versionTag,
                   tailKey);
@@ -1246,8 +1246,7 @@ public abstract class AbstractRegionMap
                 }
                 EntryLogger.logTXDestroy(_getOwnerObject(), key);
                 owner.updateSizeOnCreate(newRe.getKey(), 0);
-                if (shouldPerformConcurrencyChecks(owner, callbackEvent)
-                    && callbackEvent.getVersionTag() != null) {
+                if (owner.getConcurrencyChecksEnabled() && callbackEvent.getVersionTag() != null) {
                   newRe.makeTombstone(owner, callbackEvent.getVersionTag());
                 } else if (!inTokenMode) {
                   // only remove for NORMAL regions if they do not generate versions see 51781
@@ -1261,11 +1260,10 @@ public abstract class AbstractRegionMap
                 // and will be removed when gii completes
               } finally {
                 if (!callbackEventAddedToPending)
-                  callbackEvent.release();
+                  releaseEvent(callbackEvent);
               }
             }
-            if (owner.getConcurrencyChecksEnabled() && txEntryState != null
-                && callbackEvent != null) {
+            if (owner.getConcurrencyChecksEnabled() && txEntryState != null) {
               txEntryState.setVersionTag(callbackEvent.getVersionTag());
             }
           }
@@ -1276,7 +1274,7 @@ public abstract class AbstractRegionMap
             oqlIndexManager.countDownIndexUpdaters();
           }
         }
-      } else if (re == null) {
+      } else { // re == null
         // Fix bug#43594
         // In cases where bucket region is re-created, it may so happen that
         // the destroy is already applied on the Initial image provider, thus
@@ -1295,7 +1293,7 @@ public abstract class AbstractRegionMap
           callbackEventAddedToPending = true;
         } finally {
           if (!callbackEventAddedToPending)
-            callbackEvent.release();
+            releaseEvent(callbackEvent);
         }
       }
     } catch (DiskAccessException dae) {
@@ -1304,6 +1302,10 @@ public abstract class AbstractRegionMap
     }
   }
 
+  void releaseEvent(final EntryEventImpl event) {
+    event.release();
+  }
+
   /**
    * If true then invalidates that throw EntryNotFoundException or that are already invalid will
    * first call afterInvalidate on CacheListeners. The old value on the event passed to
@@ -1860,7 +1862,7 @@ public abstract class AbstractRegionMap
                   // a receipt of a TXCommitMessage AND there are callbacks
                   // installed
                   // for this region
-                  boolean invokeCallbacks = shouldCreateCallbackEvent(owner, owner.isInitialized());
+                  boolean invokeCallbacks = shouldInvokeCallbacks(owner, owner.isInitialized());
                   boolean callbackEventInPending = false;
                   callbackEvent = createCallbackEvent(owner,
                       localOp ? Operation.LOCAL_INVALIDATE : Operation.INVALIDATE, key, newValue,
@@ -1910,13 +1912,13 @@ public abstract class AbstractRegionMap
                     }
                   } finally {
                     if (!callbackEventInPending)
-                      callbackEvent.release();
+                      releaseEvent(callbackEvent);
                   }
                 }
               }
             }
             if (!opCompleted) {
-              boolean invokeCallbacks = shouldCreateCallbackEvent(owner, owner.isInitialized());
+              boolean invokeCallbacks = shouldInvokeCallbacks(owner, owner.isInitialized());
               boolean callbackEventInPending = false;
               callbackEvent = createCallbackEvent(owner,
                   localOp ? Operation.LOCAL_INVALIDATE : Operation.INVALIDATE, key, newValue, txId,
@@ -1953,7 +1955,7 @@ public abstract class AbstractRegionMap
                 }
               } finally {
                 if (!callbackEventInPending)
-                  callbackEvent.release();
+                  releaseEvent(callbackEvent);
               }
             }
           } finally {
@@ -1974,7 +1976,7 @@ public abstract class AbstractRegionMap
               // a receipt of a TXCommitMessage AND there are callbacks
               // installed
               // for this region
-              boolean invokeCallbacks = shouldCreateCallbackEvent(owner, owner.isInitialized());
+              boolean invokeCallbacks = shouldInvokeCallbacks(owner, owner.isInitialized());
               boolean callbackEventInPending = false;
               callbackEvent = createCallbackEvent(owner,
                   localOp ? Operation.LOCAL_INVALIDATE : Operation.INVALIDATE, key, newValue, txId,
@@ -2015,7 +2017,7 @@ public abstract class AbstractRegionMap
                 }
               } finally {
                 if (!callbackEventInPending)
-                  callbackEvent.release();
+                  releaseEvent(callbackEvent);
               }
               return;
             }
@@ -2038,7 +2040,7 @@ public abstract class AbstractRegionMap
             callbackEventInPending = true;
           } finally {
             if (!callbackEventInPending)
-              callbackEvent.release();
+              releaseEvent(callbackEvent);
           }
         }
       }
@@ -2126,7 +2128,7 @@ public abstract class AbstractRegionMap
 
   private void txHandleWANEvent(final LocalRegion owner, EntryEventImpl callbackEvent,
       TXEntryState txEntryState) {
-    ((BucketRegion) owner).handleWANEvent(callbackEvent);
+    owner.handleWANEvent(callbackEvent);
     if (txEntryState != null) {
       txEntryState.setTailKey(callbackEvent.getTailKey());
     }
@@ -2219,7 +2221,7 @@ public abstract class AbstractRegionMap
     }
   }
 
-  static boolean shouldCreateCallbackEvent(final LocalRegion owner, final boolean isInitialized) {
+  static boolean shouldInvokeCallbacks(final LocalRegion owner, final boolean isInitialized) {
     LocalRegion lr = owner;
     boolean isPartitioned = lr.isUsedForPartitionedRegionBucket();
 
@@ -2253,7 +2255,7 @@ public abstract class AbstractRegionMap
     DistributedMember originator = null;
     // txId should not be null even on localOrigin
     Assert.assertTrue(txId != null);
-    originator = ((TXId) txId).getMemberId();
+    originator = txId.getMemberId();
 
     InternalRegion eventRegion = internalRegion;
     if (eventRegion.isUsedForPartitionedRegionBucket()) {
diff --git a/geode-core/src/main/java/org/apache/geode/internal/cache/BucketRegion.java b/geode-core/src/main/java/org/apache/geode/internal/cache/BucketRegion.java
index 07dc319..0fb745d 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/cache/BucketRegion.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/cache/BucketRegion.java
@@ -569,6 +569,7 @@ public class BucketRegion extends DistributedRegion implements Bucket {
     return eventSeqNum.get();
   }
 
+  @Override
   public void handleWANEvent(EntryEventImpl event) {
     if (this.eventSeqNum == null) {
       if (logger.isDebugEnabled()) {
diff --git a/geode-core/src/main/java/org/apache/geode/internal/cache/InternalRegion.java b/geode-core/src/main/java/org/apache/geode/internal/cache/InternalRegion.java
index 38d517f..8b887ef 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/cache/InternalRegion.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/cache/InternalRegion.java
@@ -405,4 +405,7 @@ public interface InternalRegion extends Region, HasCachePerfStats, RegionEntryCo
   }
 
   EvictionController getEvictionController();
+
+  default void handleWANEvent(EntryEventImpl event) {}
+
 }
diff --git a/geode-core/src/main/java/org/apache/geode/internal/cache/ProxyRegionMap.java b/geode-core/src/main/java/org/apache/geode/internal/cache/ProxyRegionMap.java
index 26d95a6..6eb4479 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/cache/ProxyRegionMap.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/cache/ProxyRegionMap.java
@@ -271,7 +271,7 @@ class ProxyRegionMap implements RegionMap {
       if (event != null) {
         event.addDestroy(this.owner, markerEntry, key, aCallbackArgument);
       }
-      if (AbstractRegionMap.shouldCreateCallbackEvent(this.owner, !inTokenMode)) {
+      if (AbstractRegionMap.shouldInvokeCallbacks(this.owner, !inTokenMode)) {
         // fix for bug 39526
         @Released
         EntryEventImpl e = AbstractRegionMap.createCallbackEvent(this.owner, op, key, null,
@@ -294,7 +294,7 @@ class ProxyRegionMap implements RegionMap {
       if (event != null) {
         event.addInvalidate(this.owner, markerEntry, key, newValue, aCallbackArgument);
       }
-      if (AbstractRegionMap.shouldCreateCallbackEvent(this.owner, this.owner.isInitialized())) {
+      if (AbstractRegionMap.shouldInvokeCallbacks(this.owner, this.owner.isInitialized())) {
         // fix for bug 39526
         @Released
         EntryEventImpl e = AbstractRegionMap.createCallbackEvent(this.owner,
@@ -320,7 +320,7 @@ class ProxyRegionMap implements RegionMap {
       if (event != null) {
         event.addPut(putOperation, this.owner, markerEntry, key, newValue, aCallbackArgument);
       }
-      if (AbstractRegionMap.shouldCreateCallbackEvent(this.owner, this.owner.isInitialized())) {
+      if (AbstractRegionMap.shouldInvokeCallbacks(this.owner, this.owner.isInitialized())) {
         // fix for bug 39526
         @Released
         EntryEventImpl e = AbstractRegionMap.createCallbackEvent(this.owner, putOperation, key,
diff --git a/geode-core/src/main/java/org/apache/geode/internal/cache/RegionEntry.java b/geode-core/src/main/java/org/apache/geode/internal/cache/RegionEntry.java
index 24cb359..c3ab74c 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/cache/RegionEntry.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/cache/RegionEntry.java
@@ -146,7 +146,7 @@ public interface RegionEntry {
 
   /**
    * Returns true if this entry does not exist. This is true if removal has started (value ==
-   * Token.REMOVED_PHASE1) or has completed (value == Token.REMOVED_PHASE2).
+   * Token.REMOVED_PHASE1) or has completed (value == Token.REMOVED_PHASE2) or is a TOMBSTONE.
    */
   boolean isRemoved();
 
diff --git a/geode-core/src/test/java/org/apache/geode/internal/cache/AbstractRegionMapTxApplyDestroyTest.java b/geode-core/src/test/java/org/apache/geode/internal/cache/AbstractRegionMapTxApplyDestroyTest.java
new file mode 100644
index 0000000..0517591
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/internal/cache/AbstractRegionMapTxApplyDestroyTest.java
@@ -0,0 +1,1480 @@
+/*
+ * 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; private 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; private software distributed under the
+ * License
+ * is distributed on an "AS IS" BASIS; private WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND; private
+ * either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.internal.cache;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.assertj.core.api.SoftAssertions.assertSoftly;
+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.same;
+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 static org.mockito.MockitoAnnotations.initMocks;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+
+import org.apache.geode.cache.DiskAccessException;
+import org.apache.geode.cache.Operation;
+import org.apache.geode.cache.query.internal.index.IndexManager;
+import org.apache.geode.cache.query.internal.index.IndexProtocol;
+import org.apache.geode.distributed.internal.membership.InternalDistributedMember;
+import org.apache.geode.internal.cache.TXEntryState.DistTxThinEntryState;
+import org.apache.geode.internal.cache.tier.sockets.ClientProxyMembershipID;
+import org.apache.geode.internal.cache.versions.VersionStamp;
+import org.apache.geode.internal.cache.versions.VersionTag;
+import org.apache.geode.internal.util.concurrent.ConcurrentMapWithReusableEntries;
+import org.apache.geode.test.junit.categories.UnitTest;
+
+@SuppressWarnings("unchecked")
+@Category(UnitTest.class)
+public class AbstractRegionMapTxApplyDestroyTest {
+  // parameters
+  private Object key = "key";
+  @Mock
+  private TXId txId;
+  private TXRmtEvent txEvent;
+  private boolean inTokenMode;
+  private boolean inRI;
+  private Operation operation = Operation.DESTROY;
+  @Mock
+  private EventID eventId;
+  private Object aCallbackArgument = "aCallbackArgument";
+  private final List<EntryEventImpl> pendingCallbacks = new ArrayList<>();
+  private FilterRoutingInfo filterRoutingInfo = null; // Provide a meaningful value for this?
+  @Mock
+  private ClientProxyMembershipID bridgeContext;
+  private boolean isOriginRemote = false;
+  @Mock
+  private TXEntryState txEntryState;
+  private VersionTag versionTag;
+  private long tailKey = 223L;
+
+  @Mock
+  private InternalDistributedMember myId;
+  @Mock
+  private InternalDistributedMember remoteId;
+  @Mock
+  private KeyInfo keyInfo;
+  @Mock
+  private ConcurrentMapWithReusableEntries entryMap;
+  @Mock
+  private RegionEntryFactory regionEntryFactory;
+  @Mock
+  private RegionEntry existingRegionEntry;
+  @Mock
+  private RegionEntry factoryRegionEntry;
+  @Mock
+  private RegionEntry oldRegionEntry;
+  @Mock
+  private PartitionedRegion partitionedRegion;
+  @Mock
+  private VersionTag existingVersionTag;
+  @Mock
+  private CachePerfStats cachePerfStats;
+
+  private LocalRegion owner;
+  private TestableAbstractRegionMap regionMap;
+
+  @Before
+  public void setup() {
+    initMocks(this);
+    VersionStamp versionStamp = mock(VersionStamp.class);
+    when(versionStamp.asVersionTag()).thenReturn(existingVersionTag);
+    when(existingRegionEntry.getVersionStamp()).thenReturn(versionStamp);
+    when(existingRegionEntry.getKey()).thenReturn(key);
+    when(factoryRegionEntry.getKey()).thenReturn(key);
+    when(oldRegionEntry.getKey()).thenReturn(key);
+    when(keyInfo.getKey()).thenReturn(key);
+    when(txId.getMemberId()).thenReturn(myId);
+  }
+
+  // tests for no region entry.
+  // Each of these tests requires givenNotInTokenMode and givenNoConcurrencyChecks
+
+  @Test
+  public void txApplyDestroySetCorrectPendingCallback_givenNoRegionEntryNotInTokenModeNoConcurrencyChecks() {
+    givenLocalRegion();
+    givenNoRegionEntry();
+    givenNotInTokenMode();
+    givenNoConcurrencyChecks();
+    when(owner.generateEventID()).thenReturn(true);
+    when(keyInfo.getCallbackArg()).thenReturn(aCallbackArgument);
+
+    doTxApplyDestroy();
+    assertThat(pendingCallbacks).hasSize(1);
+    EntryEventImpl callbackEvent = pendingCallbacks.get(0);
+
+    // noinspection Duplicates
+    assertSoftly(softly -> {
+      softly.assertThat(callbackEvent.getRegion()).isSameAs(owner);
+      softly.assertThat(callbackEvent.getOperation()).isSameAs(operation);
+      softly.assertThat(callbackEvent.getKey()).isSameAs(key);
+      softly.assertThat(callbackEvent.getNewValue()).isNull();
+      softly.assertThat(callbackEvent.getTransactionId()).isSameAs(txId);
+      softly.assertThat(callbackEvent.getEventId()).isSameAs(eventId);
+      softly.assertThat(callbackEvent.getCallbackArgument()).isSameAs(aCallbackArgument);
+      softly.assertThat(callbackEvent.getLocalFilterInfo()).isSameAs(filterRoutingInfo);
+      softly.assertThat(callbackEvent.getContext()).isSameAs(bridgeContext);
+      softly.assertThat(callbackEvent.isOriginRemote()).isEqualTo(isOriginRemote);
+      softly.assertThat(callbackEvent.getVersionTag()).isEqualTo(versionTag);
+      softly.assertThat(callbackEvent.getTailKey()).isEqualTo(tailKey);
+    });
+  }
+
+  @Test
+  public void addsCallbackEvent_givenNoRegionEntryNotInTokenModeNoConcurrencyChecks_andBucket() {
+    givenBucketRegion();
+    givenNoRegionEntry();
+    givenNotInTokenMode();
+    givenNoConcurrencyChecks();
+
+    when(partitionedRegion.generateEventID()).thenReturn(true);
+    when(keyInfo.getCallbackArg()).thenReturn(aCallbackArgument);
+
+    doTxApplyDestroy();
+
+    verify(owner, times(1)).handleWANEvent(any());
+    verify(txEntryState, times(1)).setTailKey(tailKey);
+
+    assertThat(pendingCallbacks).hasSize(1);
+    EntryEventImpl callbackEvent = pendingCallbacks.get(0);
+
+    // noinspection Duplicates
+    assertSoftly(softly -> {
+      softly.assertThat(callbackEvent.getRegion()).isSameAs(partitionedRegion);
+      softly.assertThat(callbackEvent.getOperation()).isSameAs(operation);
+      softly.assertThat(callbackEvent.getKey()).isSameAs(key);
+      softly.assertThat(callbackEvent.getNewValue()).isNull();
+      softly.assertThat(callbackEvent.getTransactionId()).isSameAs(txId);
+      softly.assertThat(callbackEvent.getEventId()).isSameAs(eventId);
+      softly.assertThat(callbackEvent.getCallbackArgument()).isSameAs(aCallbackArgument);
+      softly.assertThat(callbackEvent.getLocalFilterInfo()).isSameAs(filterRoutingInfo);
+      softly.assertThat(callbackEvent.getContext()).isSameAs(bridgeContext);
+      softly.assertThat(callbackEvent.isOriginRemote()).isEqualTo(isOriginRemote);
+      softly.assertThat(callbackEvent.getVersionTag()).isEqualTo(versionTag);
+      softly.assertThat(callbackEvent.getTailKey()).isEqualTo(tailKey);
+    });
+  }
+
+  @Test
+  public void txApplyDestroyCallReleaseEvent_givenNoRegionEntryNotInTokenModeNoConcurrencyChecksAndBucket_whenHandleWANEventThrows() {
+    givenBucketRegion();
+    givenNoRegionEntry();
+    givenNotInTokenMode();
+    givenNoConcurrencyChecks();
+    doThrow(RuntimeException.class).when(owner).handleWANEvent(any());
+
+    assertThatThrownBy(this::doTxApplyDestroy).isInstanceOf(RuntimeException.class);
+
+    verify(regionMap, times(1)).releaseEvent(any());
+  }
+
+  // tests for "existingRegionEntry"
+
+  @Test
+  public void txApplyDestroyHasNoPendingCallback_givenExistingRegionEntryThatIsRemoved() {
+    givenLocalRegion();
+    givenExistingRegionEntry();
+    when(existingRegionEntry.isRemoved()).thenReturn(true);
+
+    doTxApplyDestroy();
+
+    assertThat(pendingCallbacks).isEmpty();
+  }
+
+  @Test
+  public void txApplyDestroyInvokesRescheduleTombstone_givenExistingRegionEntryThatIsTombstone() {
+    givenLocalRegion();
+    givenExistingRegionEntry();
+    when(existingRegionEntry.isRemoved()).thenReturn(true);
+    when(existingRegionEntry.isTombstone()).thenReturn(true);
+
+    doTxApplyDestroy();
+
+    verify(owner, times(1)).rescheduleTombstone(same(existingRegionEntry), any());
+  }
+
+  @Test
+  public void doesNotAddCallbackEvent_ifExistingRegionEntryIsTombstone() throws Exception {
+    givenLocalRegion();
+    givenExistingRegionEntry();
+    when(existingRegionEntry.isRemoved()).thenReturn(true);
+    when(existingRegionEntry.isTombstone()).thenReturn(true);
+
+    doTxApplyDestroy();
+
+    assertThat(pendingCallbacks).isEmpty();
+    verify(existingRegionEntry, never()).makeTombstone(any(), any());
+    verify(txEntryState, never()).setVersionTag(any());
+  }
+
+  @Test
+  public void txApplyDestroyHasNoPendingCallback_givenExistingRegionEntryWithInTokenModeAndNotInRI() {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenExistingRegionEntry();
+    this.inTokenMode = true;
+    this.inRI = false;
+
+    doTxApplyDestroy();
+
+    assertThat(pendingCallbacks).isEmpty();
+    verify(owner, times(1)).txApplyDestroyPart2(same(existingRegionEntry), eq(key),
+        eq(this.inTokenMode), eq(false));
+  }
+
+  @Test
+  public void doesNotAddCallbackEvent_givenExistingRegionEntryWithInTokenModeAndNotInRI()
+      throws Exception {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenExistingRegionEntry();
+    this.inTokenMode = true;
+    this.inRI = false;
+
+    doTxApplyDestroy();
+
+    assertThat(pendingCallbacks).isEmpty();
+    verify(owner).generateAndSetVersionTag(any(EntryEventImpl.class), same(existingRegionEntry));
+    verify(existingRegionEntry, never()).makeTombstone(any(), any());
+    verify(txEntryState).setVersionTag(any());
+  }
+
+  @Test
+  public void setsRegionEntryOnEvent_ifExistingRegionEntryIsValid() throws Exception {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenExistingRegionEntry();
+
+    Object oldValue = "oldValue";
+    when(existingRegionEntry.getValueInVM(owner)).thenReturn(oldValue);
+
+    doTxApplyDestroy();
+
+    assertThat(pendingCallbacks).size().isEqualTo(1);
+    EntryEventImpl event = pendingCallbacks.get(0);
+    assertThat(event.getRegionEntry()).isSameAs(existingRegionEntry);
+    assertThat(event.getOldValue()).isSameAs(oldValue);
+
+    verify(owner).generateAndSetVersionTag(any(EntryEventImpl.class), same(existingRegionEntry));
+    verify(existingRegionEntry, never()).makeTombstone(any(), any());
+    verify(txEntryState).setVersionTag(event.getVersionTag());
+  }
+
+  @Test
+  public void txApplyDestroyUpdateIndexes_givenExistingRegionEntryThatIsValid() throws Exception {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenExistingRegionEntry();
+    IndexManager indexManager = mock(IndexManager.class);
+    when(owner.getIndexManager()).thenReturn(indexManager);
+
+    doTxApplyDestroy();
+
+    verify(indexManager, times(1)).updateIndexes(same(existingRegionEntry),
+        eq(IndexManager.REMOVE_ENTRY), eq(IndexProtocol.OTHER_OP));
+  }
+
+  @Test
+  public void txApplyDestroyCallsAddDestroy_givenExistingRegionEntryThatIsValidAndTxRmtEvent() {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenExistingRegionEntry();
+    txEvent = mock(TXRmtEvent.class);
+
+    doTxApplyDestroy();
+
+    verify(txEvent, times(1)).addDestroy(same(owner), same(existingRegionEntry), eq(key),
+        same(aCallbackArgument));
+  }
+
+  @Test
+  public void callsProcessAndGenerateTXVersionTag_givenExistingRegionEntryThatIsValid() {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenExistingRegionEntry();
+
+    doTxApplyDestroy();
+
+    EntryEventImpl callbackEvent = pendingCallbacks.get(0);
+    verify(regionMap, times(1)).processAndGenerateTXVersionTag(same(callbackEvent),
+        same(existingRegionEntry), same(txEntryState));
+    assertThat(callbackEvent.getNextRegionVersion()).isEqualTo(-1L); // Default value
+  }
+
+  @Test
+  public void setsEventNextRegionVersionOnCallbackEvent_givenExistingRegionEntryThatIsValidAndDistTxEntryStateExists() {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenExistingRegionEntry();
+
+    DistTxThinEntryState distTxEntryStates = mock(DistTxThinEntryState.class);
+    when(txEntryState.getDistTxEntryStates()).thenReturn(distTxEntryStates);
+    when(distTxEntryStates.getRegionVersion()).thenReturn(999L);
+
+    doTxApplyDestroy();
+
+    EntryEventImpl callbackEvent = pendingCallbacks.get(0);
+    assertThat(callbackEvent.getNextRegionVersion()).isEqualTo(999L); // Default value
+  }
+
+  @Test
+  public void txApplyDestroySetsValueToDestroyToken_givenExistingRegionEntryThatIsValidWithInTokenMode()
+      throws Exception {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenExistingRegionEntry();
+    inTokenMode = true;
+
+    doTxApplyDestroy();
+
+    verify(existingRegionEntry, times(1)).setValue(same(owner), eq(Token.DESTROYED));
+  }
+
+  @Test
+  public void txApplyDestroyHandlesClear_givenExistingRegionEntryThatIsValidWithInTokenModeAndSetValueThrowsRegionClearedException()
+      throws Exception {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenExistingRegionEntry();
+    inTokenMode = true;
+    doThrow(RegionClearedException.class).when(existingRegionEntry).setValue(same(owner),
+        eq(Token.DESTROYED));
+
+    doTxApplyDestroy();
+
+    verify(owner, times(1)).txApplyDestroyPart2(any(), any(), anyBoolean(), eq(true));
+    verify(regionMap, never()).lruEntryDestroy(any());
+  }
+
+  @Test
+  public void txApplyDestroyHandlesNoClear_givenExistingRegionEntryThatIsValidWithInTokenMode() {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenExistingRegionEntry();
+    inTokenMode = true;
+
+    doTxApplyDestroy();
+
+    verify(owner, times(1)).txApplyDestroyPart2(any(), any(), anyBoolean(), eq(false));
+    verify(regionMap, times(1)).lruEntryDestroy(any());
+  }
+
+  @Test
+  public void txApplyDestroyCallsUnscheduleTombstone_givenExistingRegionEntryThatIsTombstoneWithInTokenMode() {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenExistingRegionEntry();
+    inTokenMode = true;
+    when(existingRegionEntry.getValueInVM(owner)).thenReturn(Token.TOMBSTONE);
+
+    doTxApplyDestroy();
+
+    verify(owner, times(1)).unscheduleTombstone(same(existingRegionEntry));
+  }
+
+  @Test
+  public void txApplyDestroyCallsRemoveEntry_givenExistingRegionEntryThatIsValidWithoutInTokenModeWithoutConcurrencyCheck()
+      throws Exception {
+    givenLocalRegion();
+    givenNoConcurrencyChecks();
+    givenExistingRegionEntry();
+    inTokenMode = false;
+
+    doTxApplyDestroy();
+
+    verify(existingRegionEntry, times(1)).removePhase1(same(owner), eq(false));
+    verify(existingRegionEntry, times(1)).removePhase2();
+    verify(regionMap, times(1)).removeEntry(eq(key), same(existingRegionEntry), eq(false));
+  }
+
+  @Test
+  public void txApplyDestroyCallsRescheduleTombstone_givenExistingRegionEntryThatIsValidWithoutInTokenModeWithConcurrencyCheckButNoVersionTag()
+      throws Exception {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenExistingRegionEntry();
+    inTokenMode = false;
+    versionTag = null;
+
+    doTxApplyDestroy();
+
+    verify(existingRegionEntry, times(1)).removePhase1(same(owner), eq(false));
+    verify(existingRegionEntry, times(1)).removePhase2();
+    verify(regionMap, times(1)).removeEntry(eq(key), same(existingRegionEntry), eq(false));
+  }
+
+  @Test
+  public void txApplyDestroyCallsMakeTombstone_givenExistingRegionEntryThatIsValidWithoutInTokenModeWithConcurrencyCheckAndVersionTag()
+      throws Exception {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenExistingRegionEntry();
+    inTokenMode = false;
+    versionTag = mock(VersionTag.class);
+
+    doTxApplyDestroy();
+
+    verify(existingRegionEntry, times(1)).makeTombstone(same(owner), same(versionTag));
+  }
+
+  @Test
+  public void txApplyDestroyCallsUpdateSizeOnRemove_givenExistingRegionEntryThatIsValid() {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenExistingRegionEntry();
+    int oldSize = 79;
+    when(owner.calculateRegionEntryValueSize(same(existingRegionEntry))).thenReturn(oldSize);
+
+    doTxApplyDestroy();
+
+    verify(owner, times(1)).updateSizeOnRemove(eq(key), eq(oldSize));
+  }
+
+  @Test
+  public void txApplyDestroyCallsReleaseEvent_givenExistingRegionEntry() {
+    givenLocalRegion();
+    givenExistingRegionEntry();
+
+    doTxApplyDestroy();
+
+    verify(regionMap, times(1)).releaseEvent(any());
+  }
+
+  @Test
+  public void txApplyDestroyHasPendingCallback_givenExistingRegionEntryWithoutInTokenModeAndNotInRI() {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenExistingRegionEntry();
+    inTokenMode = false;
+    inRI = false;
+
+    doTxApplyDestroy();
+
+    assertThat(pendingCallbacks).hasSize(1);
+    verify(owner, times(1)).txApplyDestroyPart2(same(existingRegionEntry), eq(key), eq(inTokenMode),
+        eq(false));
+
+  }
+
+  @Test
+  public void txApplyDestroyHasPendingCallback_givenExistingRegionEntryWithoutInTokenModeAndWithInRI() {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenExistingRegionEntry();
+    inTokenMode = false;
+    inRI = true;
+
+    doTxApplyDestroy();
+
+    assertThat(pendingCallbacks).hasSize(1);
+    verify(owner, times(1)).txApplyDestroyPart2(same(existingRegionEntry), eq(key), eq(inTokenMode),
+        eq(false));
+  }
+
+  @Test
+  public void txApplyDestroyHasPendingCallback_givenExistingRegionEntryWithInTokenModeAndInRI() {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenExistingRegionEntry();
+    inTokenMode = true;
+    inRI = true;
+
+    doTxApplyDestroy();
+
+    assertThat(pendingCallbacks).hasSize(1);
+    verify(owner, times(1)).txApplyDestroyPart2(same(existingRegionEntry), eq(key), eq(inTokenMode),
+        eq(false));
+  }
+
+  @Test
+  public void txApplyDestroyHasNoPendingCallback_givenExistingRegionEntryWithPartitionedRegion() {
+    givenBucketRegion();
+    givenExistingRegionEntry();
+
+    doTxApplyDestroy();
+
+    assertThat(pendingCallbacks).isEmpty();
+    verify(owner, times(1)).txApplyDestroyPart2(same(existingRegionEntry), eq(key), eq(inTokenMode),
+        eq(false));
+  }
+
+  @Test
+  public void txApplyDestroyCallsHandleWanEvent_givenExistingRegionEntryWithPartitionedRegion() {
+    givenBucketRegion();
+    givenExistingRegionEntry();
+
+    doTxApplyDestroy();
+
+    verify(owner, times(1)).handleWANEvent(any());
+    verify(txEntryState, times(1)).setTailKey(tailKey);
+  }
+
+  @Test
+  public void txApplyDestroyDoesNotCallSetVersionTag_givenExistingRegionEntryWithPartitionedRegionButNoConcurrencyChecks() {
+    givenBucketRegion();
+    givenExistingRegionEntry();
+    versionTag = mock(VersionTag.class);
+    givenNoConcurrencyChecks();
+
+    doTxApplyDestroy();
+
+    verify(txEntryState, never()).setVersionTag(any());
+  }
+
+  @Test
+  public void txApplyDestroyPreparesAndReleasesIndexManager_givenExistingRegionEntryWithIndexManager() {
+    givenLocalRegion();
+    givenExistingRegionEntry();
+    IndexManager indexManager = mock(IndexManager.class);
+    when(owner.getIndexManager()).thenReturn(indexManager);
+
+    doTxApplyDestroy();
+
+    InOrder inOrder = inOrder(indexManager);
+    inOrder.verify(indexManager, times(1)).waitForIndexInit();
+    inOrder.verify(indexManager, times(1)).countDownIndexUpdaters();
+  }
+
+  @Test
+  public void txApplyDestroyCallsSetVersionTag_givenExistingRegionEntryWithPartitionedRegionAndConcurrencyChecks() {
+    givenBucketRegion();
+    givenExistingRegionEntry();
+    versionTag = mock(VersionTag.class);
+    givenConcurrencyChecks();
+
+    doTxApplyDestroy();
+
+    verify(txEntryState, times(1)).setVersionTag(same(versionTag));
+  }
+
+  @Test
+  public void txApplyDestroyHasNoPendingCallback_givenExistingRegionEntryWithoutConcurrencyChecks() {
+    givenLocalRegion();
+    givenExistingRegionEntry();
+    givenNoConcurrencyChecks();
+
+    doTxApplyDestroy();
+
+    assertThat(pendingCallbacks).isEmpty();
+    verify(owner, times(1)).txApplyDestroyPart2(same(existingRegionEntry), eq(key), eq(inTokenMode),
+        eq(false));
+  }
+
+  @Test
+  public void txApplyDestroyHasPendingCallback_givenExistingRegionEntryWithShouldDispatchListenerEvent() {
+    givenLocalRegion();
+    givenExistingRegionEntry();
+    when(owner.shouldDispatchListenerEvent()).thenReturn(true);
+
+    doTxApplyDestroy();
+
+    assertThat(pendingCallbacks).hasSize(1);
+    verify(owner, times(1)).txApplyDestroyPart2(same(existingRegionEntry), eq(key), eq(inTokenMode),
+        eq(false));
+  }
+
+  @Test
+  public void txApplyDestroyHasPendingCallback_givenExistingRegionEntryWithshouldNotifyBridgeClients() {
+    givenLocalRegion();
+    givenExistingRegionEntry();
+    when(owner.shouldNotifyBridgeClients()).thenReturn(true);
+
+    doTxApplyDestroy();
+
+    assertThat(pendingCallbacks).hasSize(1);
+    verify(owner, times(1)).txApplyDestroyPart2(same(existingRegionEntry), eq(key), eq(inTokenMode),
+        eq(false));
+  }
+
+  @Test
+  public void txApplyDestroyDoesNotCallsTxApplyDestroyPart2_givenExistingRegionEntry() {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenFactoryRegionEntry();
+    versionTag = mock(VersionTag.class);
+
+    doTxApplyDestroy();
+
+    verify(owner, times(1)).txApplyDestroyPart2(same(factoryRegionEntry), eq(key), eq(inTokenMode),
+        eq(false));
+  }
+
+  // tests for "factoryRegionEntry" (that is, no existing region entry and no oldRegionEntry).
+  // All these tests require no existing region entry (that is, not found by getEntry).
+  // All these tests need one of the following: givenConcurrencyChecks OR inTokenMode
+
+  @Test
+  public void txApplyDestroyCallsCreateEntry_givenFactoryRegionEntryAndInTokenMode() {
+    givenLocalRegion();
+    givenFactoryRegionEntry();
+    inTokenMode = true;
+
+    doTxApplyDestroy();
+
+    verify(regionEntryFactory, times(1)).createEntry(same(owner), eq(key), eq(Token.DESTROYED));
+  }
+
+  @Test
+  public void txApplyDestroyCallsCreateEntry_givenFactoryRegionEntryAndConcurrencyChecks() {
+    givenLocalRegion();
+    givenFactoryRegionEntry();
+    givenConcurrencyChecks();
+
+    doTxApplyDestroy();
+
+    verify(regionEntryFactory, times(1)).createEntry(same(owner), eq(key), eq(Token.DESTROYED));
+  }
+
+  @Test
+  public void txApplyDestroyPreparesAndReleasesIndexManager_givenFactoryRegionEntryAndConcurrencyChecksWithIndexManager() {
+    givenLocalRegion();
+    givenFactoryRegionEntry();
+    givenConcurrencyChecks();
+    IndexManager indexManager = mock(IndexManager.class);
+    when(owner.getIndexManager()).thenReturn(indexManager);
+
+    doTxApplyDestroy();
+
+    InOrder inOrder = inOrder(indexManager);
+    inOrder.verify(indexManager, times(1)).waitForIndexInit();
+    inOrder.verify(indexManager, times(1)).countDownIndexUpdaters();
+  }
+
+  @Test
+  public void txApplyDestroyHasNoPendingCallback_givenFactoryRegionEntryWithInTokenModeAndNotInRI() {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenFactoryRegionEntry();
+    inTokenMode = true;
+    inRI = false;
+
+    doTxApplyDestroy();
+
+    assertThat(pendingCallbacks).isEmpty();
+  }
+
+  @Test
+  public void txApplyDestroyHasPendingCallback_givenFactoryRegionEntryWithoutInTokenModeAndNotInRI() {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenFactoryRegionEntry();
+    inTokenMode = false;
+    inRI = false;
+
+    doTxApplyDestroy();
+
+    assertThat(pendingCallbacks).hasSize(1);
+  }
+
+  @Test
+  public void txApplyDestroyHasPendingCallback_givenFactoryRegionEntryWithoutInTokenModeAndWithInRI() {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenFactoryRegionEntry();
+    inTokenMode = false;
+    inRI = true;
+
+    doTxApplyDestroy();
+
+    assertThat(pendingCallbacks).hasSize(1);
+  }
+
+  @Test
+  public void txApplyDestroyHasPendingCallback_givenFactoryRegionEntryWithInTokenModeAndInRI() {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenFactoryRegionEntry();
+    inTokenMode = true;
+    inRI = true;
+
+    doTxApplyDestroy();
+
+    assertThat(pendingCallbacks).hasSize(1);
+  }
+
+  @Test
+  public void txApplyDestroyHasNoPendingCallback_givenFactoryRegionEntryWithPartitionedRegion() {
+    givenBucketRegion();
+    givenConcurrencyChecks();
+    givenFactoryRegionEntry();
+    when(partitionedRegion.getConcurrencyChecksEnabled()).thenReturn(false);
+
+    doTxApplyDestroy();
+
+    assertThat(pendingCallbacks).isEmpty();
+  }
+
+  @Test
+  public void txApplyDestroyHasNoPendingCallback_givenFactoryRegionEntryWithoutConcurrencyChecksInTokenMode() {
+    givenLocalRegion();
+    givenFactoryRegionEntry();
+    givenNoConcurrencyChecks();
+    inTokenMode = true;
+
+    doTxApplyDestroy();
+
+    assertThat(pendingCallbacks).isEmpty();
+  }
+
+  @Test
+  public void txApplyDestroyHasPendingCallback_givenFactoryRegionEntryWithShouldDispatchListenerEvent() {
+    givenLocalRegion();
+    givenNoConcurrencyChecks();
+    givenFactoryRegionEntry();
+    inTokenMode = true;
+    inRI = true;
+    when(owner.shouldDispatchListenerEvent()).thenReturn(true);
+
+    doTxApplyDestroy();
+
+    assertThat(pendingCallbacks).hasSize(1);
+  }
+
+  @Test
+  public void txApplyDestroyHasPendingCallback_givenFactoryRegionEntryWithshouldNotifyBridgeClients() {
+    givenLocalRegion();
+    givenNoConcurrencyChecks();
+    givenFactoryRegionEntry();
+    inTokenMode = true;
+    inRI = true;
+    when(owner.shouldNotifyBridgeClients()).thenReturn(true);
+
+    doTxApplyDestroy();
+
+    assertThat(pendingCallbacks).hasSize(1);
+  }
+
+  @Test
+  public void txApplyDestroySetsRegionEntryOnEvent_givenFactoryRegionEntry() {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenFactoryRegionEntry();
+
+    doTxApplyDestroy();
+
+    EntryEventImpl event = pendingCallbacks.get(0);
+    assertThat(event.getRegionEntry()).isSameAs(factoryRegionEntry);
+  }
+
+  @Test
+  public void txApplyDestroySetsOldValueOnEventToNotAvailable_givenFactoryRegionEntry() {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenFactoryRegionEntry();
+
+    doTxApplyDestroy();
+
+    EntryEventImpl event = pendingCallbacks.get(0);
+    assertThat(event.getRawOldValue()).isSameAs(Token.NOT_AVAILABLE);
+  }
+
+  @Test
+  public void txApplyDestroyCallsHandleWanEvent_givenFactoryRegionEntryWithPartitionedRegion() {
+    givenBucketRegion();
+    givenConcurrencyChecks();
+    givenFactoryRegionEntry();
+
+    doTxApplyDestroy();
+
+    verify(owner, times(1)).handleWANEvent(any());
+    verify(txEntryState, times(1)).setTailKey(tailKey);
+  }
+
+  @Test
+  public void txApplyDestroyCallsProcessAndGenerateTXVersionTag_givenFactoryRegionEntry() {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenFactoryRegionEntry();
+
+    doTxApplyDestroy();
+
+    verify(regionMap, times(1)).processAndGenerateTXVersionTag(any(), same(factoryRegionEntry),
+        same(txEntryState));
+  }
+
+  @Test
+  public void txApplyDestroySetsRemoteOrigin_givenFactoryRegionEntryAndRemoteTxId() {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenFactoryRegionEntry();
+    when(txId.getMemberId()).thenReturn(remoteId);
+
+    doTxApplyDestroy();
+
+    EntryEventImpl event = pendingCallbacks.get(0);
+    assertThat(event.isOriginRemote()).isTrue();
+  }
+
+  @Test
+  public void txApplyDestroySetsRemoteOrigin_givenFactoryRegionEntry() {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenFactoryRegionEntry();
+
+    doTxApplyDestroy();
+
+    EntryEventImpl event = pendingCallbacks.get(0);
+    assertThat(event.isOriginRemote()).isFalse();
+  }
+
+  @Test
+  public void txApplyDestroySetsRegionOnEvent_givenFactoryRegionEntryAndBucket() {
+    givenBucketRegion();
+    givenConcurrencyChecks();
+    givenFactoryRegionEntry();
+
+    doTxApplyDestroy();
+
+    EntryEventImpl event = pendingCallbacks.get(0);
+    assertThat(event.getRegion()).isSameAs(partitionedRegion);
+  }
+
+  @Test
+  public void txApplyDestroyCallUpdateSizeOnCreate_givenFactoryRegionEntry() {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenFactoryRegionEntry();
+
+    doTxApplyDestroy();
+
+    verify(owner, times(1)).updateSizeOnCreate(eq(key), eq(0));
+  }
+
+  @Test
+  public void txApplyDestroyCallsMakeTombstone_givenFactoryRegionEntryWithConcurrencyCheckAndVersionTag()
+      throws Exception {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenFactoryRegionEntry();
+    versionTag = mock(VersionTag.class);
+
+    doTxApplyDestroy();
+
+    verify(factoryRegionEntry, times(1)).makeTombstone(same(owner), same(versionTag));
+  }
+
+  @Test
+  public void txApplyDestroyNeverCallsMakeTombstone_givenFactoryRegionEntryWithConcurrencyCheckButNoVersionTag()
+      throws Exception {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenFactoryRegionEntry();
+
+    doTxApplyDestroy();
+
+    verify(factoryRegionEntry, never()).makeTombstone(any(), any());
+  }
+
+  @Test
+  public void txApplyDestroyNeverCallsMakeTombstone_givenFactoryRegionEntryWithoutConcurrencyCheckButWithInTokenModeAndVersionTag()
+      throws Exception {
+    givenLocalRegion();
+    givenNoConcurrencyChecks();
+    givenFactoryRegionEntry();
+    inTokenMode = true;
+    versionTag = mock(VersionTag.class);
+
+    doTxApplyDestroy();
+
+    verify(factoryRegionEntry, never()).makeTombstone(any(), any());
+  }
+
+  @Test
+  public void txApplyDestroyCallsRemoveEntry_givenFactoryRegionEntryWithConcurrencyCheck()
+      throws Exception {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenFactoryRegionEntry();
+
+    doTxApplyDestroy();
+
+    verify(factoryRegionEntry, times(1)).removePhase1(same(owner), eq(false));
+    verify(factoryRegionEntry, times(1)).removePhase2();
+    verify(regionMap, times(1)).removeEntry(eq(key), same(factoryRegionEntry), eq(false));
+  }
+
+  @Test
+  public void txApplyDestroyCallsReleaseEvent_givenFactoryRegionEntryWithoutConcurrencyChecksInTokenMode() {
+    givenLocalRegion();
+    givenFactoryRegionEntry();
+    givenNoConcurrencyChecks();
+    inTokenMode = true;
+
+    doTxApplyDestroy();
+
+    verify(regionMap, times(1)).releaseEvent(any());
+  }
+
+  @Test
+  public void txApplyDestroyDoesNotCallSetVersionTag_givenFactoryRegionEntryWithPartitionedRegionButNoConcurrencyChecks() {
+    givenBucketRegion();
+    givenFactoryRegionEntry();
+    versionTag = mock(VersionTag.class);
+    givenNoConcurrencyChecks();
+    inTokenMode = true;
+
+    doTxApplyDestroy();
+
+    verify(txEntryState, never()).setVersionTag(any());
+  }
+
+  @Test
+  public void txApplyDestroyDoesNotCallSetVersionTag_givenFactoryRegionEntryWithPartitionedRegionAndConcurrencyChecks() {
+    givenBucketRegion();
+    givenFactoryRegionEntry();
+    versionTag = mock(VersionTag.class);
+    givenConcurrencyChecks();
+
+    doTxApplyDestroy();
+
+    verify(txEntryState, times(1)).setVersionTag(same(versionTag));
+  }
+
+  @Test
+  public void txApplyDestroyDoesNotCallTxApplyDestroyPart2_givenFactoryRegionEntryWithMakeTombstoneThrowingRegionDestroyedException()
+      throws Exception {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenFactoryRegionEntry();
+    versionTag = mock(VersionTag.class);
+    doThrow(RegionClearedException.class).when(factoryRegionEntry).makeTombstone(any(), any());
+
+    doTxApplyDestroy();
+
+    verify(owner, never()).txApplyDestroyPart2(any(), any(), anyBoolean(), anyBoolean());
+  }
+
+  // tests for "oldRegionEntry" (that is, an existing region entry found by putIfAbsent).
+  // All these tests require no existing region entry (that is, not found by getEntry).
+  // All these tests need one of the following: givenConcurrencyChecks OR inTokenMode
+
+  @Test
+  public void txApplyDestroyRetries_givenOldRegionEntryWithRemovedPhase2() {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenOldRegionEntry();
+    inTokenMode = true;
+    when(oldRegionEntry.isRemovedPhase2()).thenReturn(true).thenReturn(false);
+
+    doTxApplyDestroy();
+
+    verify(cachePerfStats, times(1)).incRetries();
+    verify(entryMap, times(1)).remove(eq(key), same(oldRegionEntry));
+  }
+
+  @Test
+  public void txApplyDestroyHasNoPendingCallback_givenOldRegionEntryWithInTokenModeAndNotInRI() {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenOldRegionEntry();
+    inTokenMode = true;
+    inRI = false;
+
+    doTxApplyDestroy();
+
+    assertThat(pendingCallbacks).isEmpty();
+  }
+
+  @Test
+  public void txApplyDestroyHasPendingCallback_givenOldRegionEntrydWithoutInTokenModeAndNotInRI() {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenOldRegionEntry();
+    inTokenMode = false;
+    inRI = false;
+
+    doTxApplyDestroy();
+
+    assertThat(pendingCallbacks).hasSize(1);
+  }
+
+  @Test
+  public void txApplyDestroyHasPendingCallback_givenOldRegionEntryWithoutInTokenModeAndWithInRI() {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenOldRegionEntry();
+    inTokenMode = false;
+    inRI = true;
+
+    doTxApplyDestroy();
+
+    assertThat(pendingCallbacks).hasSize(1);
+  }
+
+  @Test
+  public void addsCallbackEvent_givenOldRegionEntryWithInTokenModeAndInRI() {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenOldRegionEntry();
+    inTokenMode = true;
+    inRI = true;
+
+    when(owner.generateEventID()).thenReturn(true);
+    when(keyInfo.getCallbackArg()).thenReturn(aCallbackArgument);
+
+    doTxApplyDestroy();
+
+    assertThat(pendingCallbacks).hasSize(1);
+    EntryEventImpl callbackEvent = pendingCallbacks.get(0);
+
+    // noinspection Duplicates
+    assertSoftly(softly -> {
+      softly.assertThat(callbackEvent.getRegion()).isSameAs(owner);
+      softly.assertThat(callbackEvent.getOperation()).isSameAs(operation);
+      softly.assertThat(callbackEvent.getKey()).isSameAs(key);
+      softly.assertThat(callbackEvent.getNewValue()).isNull();
+      softly.assertThat(callbackEvent.getTransactionId()).isSameAs(txId);
+      softly.assertThat(callbackEvent.getEventId()).isSameAs(eventId);
+      softly.assertThat(callbackEvent.getCallbackArgument()).isSameAs(aCallbackArgument);
+      softly.assertThat(callbackEvent.getLocalFilterInfo()).isSameAs(filterRoutingInfo);
+      softly.assertThat(callbackEvent.getContext()).isSameAs(bridgeContext);
+      softly.assertThat(callbackEvent.isOriginRemote()).isEqualTo(isOriginRemote);
+      softly.assertThat(callbackEvent.getVersionTag()).isEqualTo(versionTag);
+      softly.assertThat(callbackEvent.getTailKey()).isEqualTo(tailKey);
+      softly.assertThat(callbackEvent.getRegionEntry()).isSameAs(oldRegionEntry);
+      softly.assertThat(callbackEvent.getOldValue()).isNull();
+    });
+  }
+
+  @Test
+  public void txApplyDestroyHasNoPendingCallback_givenOldRegionEntryWithPartitionedRegion() {
+    givenBucketRegion();
+    givenConcurrencyChecks();
+    givenOldRegionEntry();
+    when(partitionedRegion.getConcurrencyChecksEnabled()).thenReturn(false);
+
+    doTxApplyDestroy();
+
+    assertThat(pendingCallbacks).isEmpty();
+  }
+
+  @Test
+  public void txApplyDestroyHasNoPendingCallback_givenOldRegionEntryWithoutConcurrencyChecksInTokenMode() {
+    givenLocalRegion();
+    givenOldRegionEntry();
+    givenNoConcurrencyChecks();
+    inTokenMode = true;
+
+    doTxApplyDestroy();
+
+    assertThat(pendingCallbacks).isEmpty();
+  }
+
+  @Test
+  public void txApplyDestroyHasPendingCallback_givenOldRegionEntryWithShouldDispatchListenerEvent() {
+    givenLocalRegion();
+    givenNoConcurrencyChecks();
+    givenOldRegionEntry();
+    inTokenMode = true;
+    inRI = true;
+    when(owner.shouldDispatchListenerEvent()).thenReturn(true);
+
+    doTxApplyDestroy();
+
+    assertThat(pendingCallbacks).hasSize(1);
+  }
+
+  @Test
+  public void txApplyDestroyHasPendingCallback_givenOldRegionEntryWithshouldNotifyBridgeClients() {
+    givenLocalRegion();
+    givenNoConcurrencyChecks();
+    givenOldRegionEntry();
+    inTokenMode = true;
+    inRI = true;
+    when(owner.shouldNotifyBridgeClients()).thenReturn(true);
+
+    doTxApplyDestroy();
+
+    assertThat(pendingCallbacks).hasSize(1);
+  }
+
+  @Test
+  public void txApplyDestroySetsRegionEntryOnEvent_givenOldRegionEntry() {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenOldRegionEntry();
+
+    doTxApplyDestroy();
+
+    EntryEventImpl event = pendingCallbacks.get(0);
+    assertThat(event.getRegionEntry()).isSameAs(oldRegionEntry);
+  }
+
+  @Test
+  public void txApplyDestroySetsOldValueOnEventToNotAvailable_givenOldRegionEntry() {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenOldRegionEntry();
+
+    doTxApplyDestroy();
+
+    EntryEventImpl event = pendingCallbacks.get(0);
+    assertThat(event.getRawOldValue()).isSameAs(Token.NOT_AVAILABLE);
+  }
+
+  @Test
+  public void txApplyDestroyCallsHandleWanEvent_givenOldRegionEntryWithPartitionedRegion() {
+    givenBucketRegion();
+    givenConcurrencyChecks();
+    givenOldRegionEntry();
+
+    doTxApplyDestroy();
+
+    verify(owner, times(1)).handleWANEvent(any());
+    verify(txEntryState, times(1)).setTailKey(tailKey);
+  }
+
+  @Test
+  public void txApplyDestroyCallsProcessAndGenerateTXVersionTag_givenOldRegionEntry() {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenOldRegionEntry();
+
+    doTxApplyDestroy();
+
+    verify(regionMap, times(1)).processAndGenerateTXVersionTag(any(), same(oldRegionEntry),
+        same(txEntryState));
+  }
+
+  @Test
+  public void txApplyDestroySetsRemoteOrigin_givenOldRegionEntryAndRemoteTxId() {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenOldRegionEntry();
+    when(txId.getMemberId()).thenReturn(remoteId);
+
+    doTxApplyDestroy();
+
+    EntryEventImpl event = pendingCallbacks.get(0);
+    assertThat(event.isOriginRemote()).isTrue();
+  }
+
+  @Test
+  public void txApplyDestroySetsRemoteOrigin_givenOldRegionEntry() {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenOldRegionEntry();
+
+    doTxApplyDestroy();
+
+    EntryEventImpl event = pendingCallbacks.get(0);
+    assertThat(event.isOriginRemote()).isFalse();
+  }
+
+  @Test
+  public void addsCallbackEvent_givenOldRegionEntryAndBucket() {
+    givenBucketRegion();
+    givenConcurrencyChecks();
+    givenOldRegionEntry();
+
+    when(partitionedRegion.generateEventID()).thenReturn(true);
+    when(keyInfo.getCallbackArg()).thenReturn(aCallbackArgument);
+
+    doTxApplyDestroy();
+
+    EntryEventImpl callbackEvent = pendingCallbacks.get(0);
+
+    // noinspection Duplicates
+    assertSoftly(softly -> {
+      softly.assertThat(callbackEvent.getRegion()).isSameAs(partitionedRegion);
+      softly.assertThat(callbackEvent.getOperation()).isSameAs(operation);
+      softly.assertThat(callbackEvent.getKey()).isSameAs(key);
+      softly.assertThat(callbackEvent.getNewValue()).isNull();
+      softly.assertThat(callbackEvent.getTransactionId()).isSameAs(txId);
+      softly.assertThat(callbackEvent.getEventId()).isSameAs(eventId);
+      softly.assertThat(callbackEvent.getCallbackArgument()).isSameAs(aCallbackArgument);
+      softly.assertThat(callbackEvent.getLocalFilterInfo()).isSameAs(filterRoutingInfo);
+      softly.assertThat(callbackEvent.getContext()).isSameAs(bridgeContext);
+      softly.assertThat(callbackEvent.isOriginRemote()).isEqualTo(isOriginRemote);
+      softly.assertThat(callbackEvent.getVersionTag()).isEqualTo(versionTag);
+      softly.assertThat(callbackEvent.getTailKey()).isEqualTo(tailKey);
+      softly.assertThat(callbackEvent.getRegionEntry()).isSameAs(oldRegionEntry);
+      softly.assertThat(callbackEvent.getOldValue()).isNull();
+    });
+  }
+
+  @Test
+  public void txApplyDestroyCallUpdateSizeOnRemoveWithZero_givenOldRegionEntryThatIsTombstone() {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenOldRegionEntry();
+    when(oldRegionEntry.isTombstone()).thenReturn(true);
+
+    doTxApplyDestroy();
+
+    verify(owner, times(1)).updateSizeOnRemove(eq(key), eq(0));
+  }
+
+  @Test
+  public void txApplyDestroyCallUpdateSizeOnRemoveWithOldSize_givenOldRegionEntryThatIsNotTombstone() {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenOldRegionEntry();
+    int oldSize = 79;
+    when(owner.calculateRegionEntryValueSize(oldRegionEntry)).thenReturn(oldSize);
+    when(oldRegionEntry.isTombstone()).thenReturn(false);
+
+    doTxApplyDestroy();
+
+    verify(owner, times(1)).updateSizeOnRemove(eq(key), eq(oldSize));
+  }
+
+  @Test
+  public void txApplyDestroySetsValueToTokenDestroyed_givenOldRegionEntry() throws Exception {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenOldRegionEntry();
+
+    doTxApplyDestroy();
+
+    verify(oldRegionEntry, times(1)).setValue(same(owner), eq(Token.DESTROYED));
+  }
+
+  @Test
+  public void txApplyDestroySetsValueToTokenDestroyed_givenOldRegionEntryAndInTokenMode()
+      throws Exception {
+    givenLocalRegion();
+    givenNoConcurrencyChecks();
+    givenOldRegionEntry();
+    inTokenMode = true;
+
+    doTxApplyDestroy();
+
+    verify(oldRegionEntry, times(1)).setValue(same(owner), eq(Token.DESTROYED));
+  }
+
+  @Test
+  public void txApplyDestroyCallsUnscheduleTombstone_givenOldRegionEntryThatIsTombstone() {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenOldRegionEntry();
+    when(oldRegionEntry.isTombstone()).thenReturn(true);
+
+    doTxApplyDestroy();
+
+    verify(owner, times(1)).unscheduleTombstone(same(oldRegionEntry));
+  }
+
+  @Test
+  public void txApplyDestroyCallsTxApplyDestroyPart2_givenOldRegionEntry() {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenOldRegionEntry();
+
+    doTxApplyDestroy();
+
+    verify(owner, times(1)).txApplyDestroyPart2(same(oldRegionEntry), eq(key), eq(inTokenMode),
+        eq(false));
+  }
+
+  @Test
+  public void txApplyDestroyCallsTxApplyDestroyPart2_givenOldRegionEntryWithInTokenMode() {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenOldRegionEntry();
+    inTokenMode = true;
+
+    doTxApplyDestroy();
+
+    verify(owner, times(1)).txApplyDestroyPart2(same(oldRegionEntry), eq(key), eq(inTokenMode),
+        eq(false));
+  }
+
+  @Test
+  public void txApplyDestroyCallsLruEntryDestroy_givenOldRegionEntry() {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenOldRegionEntry();
+
+    doTxApplyDestroy();
+
+    verify(regionMap, times(1)).lruEntryDestroy(oldRegionEntry);
+  }
+
+  @Test
+  public void txApplyDestroyCallsReleaseEvent_givenOldRegionEntryWithoutConcurrencyChecksInTokenMode() {
+    givenLocalRegion();
+    givenOldRegionEntry();
+    givenNoConcurrencyChecks();
+    inTokenMode = true;
+
+    doTxApplyDestroy();
+
+    verify(regionMap, times(1)).releaseEvent(any());
+  }
+
+  @Test
+  public void txApplyDestroyDoesCallTxApplyDestroyPart2_givenOldRegionEntryWithMakeTombstoneThrowingRegionDestroyedException()
+      throws Exception {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenOldRegionEntry();
+    doThrow(RegionClearedException.class).when(oldRegionEntry).setValue(any(), any());
+
+    doTxApplyDestroy();
+
+    verify(owner, times(1)).txApplyDestroyPart2(same(oldRegionEntry), eq(key), eq(inTokenMode),
+        eq(true));
+  }
+
+  @Test
+  public void txApplyDestroyCallsMakeTombstone_givenOldRegionEntryWithConcurrencyCheckAndVersionTag()
+      throws Exception {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenOldRegionEntry();
+    versionTag = mock(VersionTag.class);
+
+    doTxApplyDestroy();
+
+    verify(oldRegionEntry, times(1)).makeTombstone(same(owner), same(versionTag));
+  }
+
+  @Test
+  public void txApplyDestroyCallsRemoveEntry_givenOldRegionEntry() throws Exception {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenOldRegionEntry();
+
+    doTxApplyDestroy();
+
+    verify(oldRegionEntry, never()).makeTombstone(any(), any());
+    verify(oldRegionEntry, times(1)).removePhase1(same(owner), eq(false));
+    verify(oldRegionEntry, times(1)).removePhase2();
+    verify(regionMap, times(1)).removeEntry(eq(key), same(oldRegionEntry), eq(false));
+  }
+
+  @Test
+  public void txApplyDestroyNeverCallsRemoveEntry_givenOldRegionEntryAndInTokenMode()
+      throws Exception {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenOldRegionEntry();
+    inTokenMode = true;
+
+    doTxApplyDestroy();
+
+    verify(oldRegionEntry, never()).makeTombstone(any(), any());
+    verify(oldRegionEntry, never()).removePhase1(any(), anyBoolean());
+    verify(oldRegionEntry, never()).removePhase2();
+    verify(regionMap, never()).removeEntry(any(), any(), anyBoolean());
+  }
+
+  @Test
+  public void txApplyDestroyDoesCallsHandleDiskAccessException_givenOldRegionEntrySetValueThatThrowsDiskAccessException()
+      throws Exception {
+    givenLocalRegion();
+    givenConcurrencyChecks();
+    givenOldRegionEntry();
+    doThrow(DiskAccessException.class).when(oldRegionEntry).setValue(any(), any());
+
+    assertThatThrownBy(this::doTxApplyDestroy).isInstanceOf(DiskAccessException.class);
+
+    verify(owner, times(1)).handleDiskAccessException(any());
+  }
+
+  // helper methods for the tests
+
+  private void givenNoRegionEntry() {
+    when(entryMap.get(eq(key))).thenReturn(null);
+  }
+
+  private void givenExistingRegionEntry() {
+    when(entryMap.get(eq(key))).thenReturn(existingRegionEntry);
+  }
+
+  private void givenFactoryRegionEntry() {
+    when(entryMap.get(eq(key))).thenReturn(null);
+    when(regionEntryFactory.createEntry(any(), any(), any())).thenReturn(factoryRegionEntry);
+  }
+
+  private void givenOldRegionEntry() {
+    when(entryMap.get(eq(key))).thenReturn(null);
+    when(regionEntryFactory.createEntry(any(), any(), any())).thenReturn(factoryRegionEntry);
+    when(entryMap.putIfAbsent(eq(key), same(factoryRegionEntry))).thenReturn(oldRegionEntry);
+  }
+
+  private void givenNotInTokenMode() {
+    inTokenMode = false;
+  }
+
+  private void givenConcurrencyChecks() {
+    when(owner.getConcurrencyChecksEnabled()).thenReturn(true);
+    when(partitionedRegion.getConcurrencyChecksEnabled()).thenReturn(true);
+  }
+
+  private void givenNoConcurrencyChecks() {
+    when(owner.getConcurrencyChecksEnabled()).thenReturn(false);
+  }
+
+  private void givenLocalRegion() {
+    owner = mock(LocalRegion.class);
+    setupLocalRegion();
+    regionMap = spy(new TestableAbstractRegionMap());
+  }
+
+  private void givenBucketRegion() {
+    BucketRegion bucketRegion = mock(BucketRegion.class);
+    when(bucketRegion.isUsedForPartitionedRegionBucket()).thenReturn(true);
+    when(bucketRegion.getPartitionedRegion()).thenReturn(partitionedRegion);
+    when(bucketRegion.getBucketAdvisor()).thenReturn(mock(BucketAdvisor.class));
+    owner = bucketRegion;
+    setupLocalRegion();
+    regionMap = spy(new TestableAbstractRegionMap());
+  }
+
+  private void setupLocalRegion() {
+    when(owner.getCachePerfStats()).thenReturn(cachePerfStats);
+    when(owner.getCache()).thenReturn(mock(InternalCache.class));
+    when(owner.getMyId()).thenReturn(myId);
+    when(owner.getKeyInfo(any(), any(), any())).thenReturn(keyInfo);
+  }
+
+  private void doTxApplyDestroy() {
+    regionMap.txApplyDestroy(key, txId, txEvent, inTokenMode, inRI, operation, eventId,
+        aCallbackArgument, pendingCallbacks, filterRoutingInfo, bridgeContext, isOriginRemote,
+        txEntryState, versionTag, tailKey);
+  }
+
+  private class TestableAbstractRegionMap extends AbstractRegionMap {
+
+    TestableAbstractRegionMap() {
+      super(null);
+      initialize(owner, new Attributes(), null, false);
+      setEntryMap(entryMap);
+      setEntryFactory(regionEntryFactory);
+    }
+
+  }
+}

-- 
To stop receiving notification emails like this one, please contact
sai_boorlagadda@apache.org.

Mime
View raw message