@Test
  public void testEntryTtlLocalDestroy() throws Exception {
    assumeThat(getRegionAttributes().getPartitionAttributes())
        .withFailMessage("the region has partition attributes").isNull();

    final boolean mirrored = getRegionAttributes().getDataPolicy().withReplication();
    final boolean partitioned = getRegionAttributes().getPartitionAttributes() != null
        || getRegionAttributes().getDataPolicy().withPartitioning();
    if (!mirrored) {
      // This test fails intermittently because the DSClock we inherit from the existing
      // distributed system is stuck in the "stopped" state.
      // The DSClock is going away when java groups is merged and at that
      // time this following can be removed.
      disconnectAllFromDS();
    }

    final String name = getUniqueName();
    final int timeout = 10; // ms
    final String key = "KEY";
    final String value = "VALUE";

    vm1.invoke("Populate", () -> {
      System.setProperty(LocalRegion.EXPIRY_MS_PROPERTY, "true");
      try {
        createRegion(name);
      } finally {
        System.getProperties().remove(LocalRegion.EXPIRY_MS_PROPERTY);
      }
    });

    vm0.invoke("Create with TTL", () -> {
      RegionFactory<Object, Object> factory =
          getCache().createRegionFactory(getRegionAttributes());
      factory.setStatisticsEnabled(true);
      ExpirationAttributes expire =
          new ExpirationAttributes(timeout, ExpirationAction.LOCAL_DESTROY);
      factory.setEntryTimeToLive(expire);
      if (!mirrored) {
        // make it cached all events so that remote creates will also
        // be created here
        if (!partitioned) {
          factory.setDataPolicy(DataPolicy.NORMAL);
        }
        factory.setSubscriptionAttributes(new SubscriptionAttributes(InterestPolicy.ALL));
        factory.addCacheListener(new CountingDistCacheListener<>());
      }
      // Crank up the expiration so test runs faster. This property only needs to be set while the
      // region is created
      System.setProperty(LocalRegion.EXPIRY_MS_PROPERTY, "true");
      try {
        createRegion(name, factory);
        if (mirrored) {
          fail("Should have thrown an IllegalStateException");
        }
      } catch (IllegalStateException e) {
        if (!mirrored) {
          throw e;
        }
      } finally {
        System.getProperties().remove(LocalRegion.EXPIRY_MS_PROPERTY);
      }
    });
    if (mirrored) {
      return;
    }

    vm1.invoke(() -> {
      Region<Object, Object> region = getRootRegion().getSubregion(name);
      region.put(key, value);
    });

    vm0.invoke("Check local destroy", () -> {
      final Region<Object, Object> region = getRootRegion().getSubregion(name);
      // make sure we created the entry
      CountingDistCacheListener<Object, Object> l =
          (CountingDistCacheListener<Object, Object>) region.getAttributes()
              .getCacheListeners()[0];
      await()
          .untilAsserted(() -> l.assertCount(1, 0, 0, 0));

      // now make sure it expires
      // this should happen really fast since timeout is 10 ms.
      // But it may take longer in some cases because of thread
      // scheduling delays and machine load (see GEODE-410).
      // The previous code would fail after 100ms; now we wait 3000ms.

      try {
        await()
            .until(() -> {
              Region.Entry re = region.getEntry(key);
              if (re != null) {
                EntryExpiryTask eet = getEntryExpiryTask(region);
                if (eet != null) {
                  long stopTime =
                      getCache().getInternalDistributedSystem().getClock().getStopTime();
                  logger.info("DEBUG: waiting for expire destroy expirationTime= "
                      + eet.getExpirationTime() + " now=" + eet.calculateNow() + " stopTime="
                      + stopTime + " currentTimeMillis=" + System.currentTimeMillis());
                } else {
                  logger.info("DEBUG: waiting for expire destroy but expiry task is null");
                }
              }
              return re == null;
            });
      } catch (ConditionTimeoutException timeoutEx) {
        // Catching in order to inform about the final state
        fail(determineExpiryFailureMessage(region), timeoutEx);
      }

      assertThat(region.getEntry(key)).isNull();
    });

    vm1.invoke("Verify local", () -> {
      Region<Object, Object> region = getRootRegion().getSubregion(name);
      Region.Entry entry = region.getEntry(key);
      assertThat(entry.getValue()).isEqualTo(value);
    });
  }
Tests that an entry in a distributed region that expires with a distributed destroy causes an
event in other VM with isExpiration flag set.
geode-dunit/src/main/java/org/apache/geode/cache30/MultiVMRegionTestCase.java