@Test
  public void testHooks() throws Throwable {
    prepareForEachTest();
    final DiskStoreID memberP = getMemberID(P);
    final DiskStoreID memberR = getMemberID(R);

    assertEquals(0, DistributedCacheOperation.SLOW_DISTRIBUTION_MS);
    prepareCommonTestData(6);
    VersionTag expect_tag = getVersionTag(R, "key5");

    final long[] exceptionlist = {4, 5};
    R.invoke(() -> GIIDeltaDUnitTest.slowGII(exceptionlist));
    AsyncInvocation<Void> async1 = doOnePutAsync(R, 4, "key4");
    waitForToVerifyRVV(R, memberR, 4, null, 0); // P's rvv=r4, gc=0

    AsyncInvocation<Void> async2 = doOneDestroyAsync(R, 5, "key5");
    waitForToVerifyRVV(R, memberR, 5, null, 0); // P's rvv=r5, gc=0

    // P should have unfinished ops R4,R5, but they did not show up in exception list
    waitForToVerifyRVV(P, memberR, 3, null, 0); // P's rvv=r3, gc=0


    // define test hooks
    // 1
    R.invoke(new SerializableRunnable() {
      @Override
      public void run() {
        Mycallback myBeforeRequestRVV =
            new Mycallback(GIITestHookType.BeforeRequestRVV, REGION_NAME);
        InitialImageOperation.setGIITestHook(myBeforeRequestRVV);
      }
    });

    // 2
    R.invoke(new SerializableRunnable() {
      @Override
      public void run() {
        Mycallback myAfterRequestRVV = new Mycallback(GIITestHookType.AfterRequestRVV, REGION_NAME);
        InitialImageOperation.setGIITestHook(myAfterRequestRVV);
      }
    });

    // 3
    R.invoke(new SerializableRunnable() {
      @Override
      public void run() {
        Mycallback myAfterCalculatedUnfinishedOps =
            new Mycallback(GIITestHookType.AfterCalculatedUnfinishedOps, REGION_NAME);
        InitialImageOperation.setGIITestHook(myAfterCalculatedUnfinishedOps);
      }
    });

    // 4
    R.invoke(new SerializableRunnable() {
      @Override
      public void run() {
        Mycallback myBeforeSavedReceivedRVV =
            new Mycallback(GIITestHookType.BeforeSavedReceivedRVV, REGION_NAME);
        InitialImageOperation.setGIITestHook(myBeforeSavedReceivedRVV);
      }
    });

    // 5
    R.invoke(new SerializableRunnable() {
      @Override
      public void run() {
        Mycallback myAfterSavedReceivedRVV =
            new Mycallback(GIITestHookType.AfterSavedReceivedRVV, REGION_NAME);
        InitialImageOperation.setGIITestHook(myAfterSavedReceivedRVV);
      }
    });

    // 6
    R.invoke(new SerializableRunnable() {
      @Override
      public void run() {
        Mycallback myAfterSentRequestImage =
            new Mycallback(GIITestHookType.AfterSentRequestImage, REGION_NAME);
        InitialImageOperation.setGIITestHook(myAfterSentRequestImage);
      }
    });

    // 7
    P.invoke(new SerializableRunnable() {
      @Override
      public void run() {
        Mycallback myAfterReceivedRequestImage =
            new Mycallback(GIITestHookType.AfterReceivedRequestImage, REGION_NAME);
        InitialImageOperation.setGIITestHook(myAfterReceivedRequestImage);
      }
    });

    // 8
    P.invoke(new SerializableRunnable() {
      @Override
      public void run() {
        Mycallback myDuringPackingImage =
            new Mycallback(GIITestHookType.DuringPackingImage, REGION_NAME);
        InitialImageOperation.setGIITestHook(myDuringPackingImage);
      }
    });

    // 9
    P.invoke(new SerializableRunnable() {
      @Override
      public void run() {
        Mycallback myAfterSentImageReply =
            new Mycallback(GIITestHookType.AfterSentImageReply, REGION_NAME);
        InitialImageOperation.setGIITestHook(myAfterSentImageReply);
      }
    });

    // 10
    R.invoke(new SerializableRunnable() {
      @Override
      public void run() {
        Mycallback myAfterReceivedImageReply =
            new Mycallback(GIITestHookType.AfterReceivedImageReply, REGION_NAME);
        InitialImageOperation.setGIITestHook(myAfterReceivedImageReply);
      }
    });

    // 11
    R.invoke(new SerializableRunnable() {
      @Override
      public void run() {
        Mycallback myDuringApplyDelta =
            new Mycallback(GIITestHookType.DuringApplyDelta, REGION_NAME);
        InitialImageOperation.setGIITestHook(myDuringApplyDelta);
      }
    });

    // 12
    R.invoke(new SerializableRunnable() {
      @Override
      public void run() {
        Mycallback myBeforeCleanExpiredTombstones =
            new Mycallback(GIITestHookType.BeforeCleanExpiredTombstones, REGION_NAME);
        InitialImageOperation.setGIITestHook(myBeforeCleanExpiredTombstones);
      }
    });

    // 13
    R.invoke(new SerializableRunnable() {
      @Override
      public void run() {
        Mycallback myAfterSavedRVVEnd =
            new Mycallback(GIITestHookType.AfterSavedRVVEnd, REGION_NAME);
        InitialImageOperation.setGIITestHook(myAfterSavedRVVEnd);
      }
    });


    // shutdown R and restart
    waitForToVerifyRVV(R, memberP, 6, null, 0); // R's rvv=p6, gc=0
    waitForToVerifyRVV(R, memberR, 5, null, 0); // R's rvv=r3, gc=0
    byte[] R_rvv_bytes = getRVVByteArray(R, REGION_NAME);
    closeCache(R);

    // let p7 to succeed
    doOnePut(P, 7, "key1");

    waitForToVerifyRVV(P, memberP, 7, null, 0); // P's rvv=p7, gc=0
    waitForToVerifyRVV(P, memberR, 3, null, 0); // P's rvv=r3, gc=0
    // now P's rvv=P7,R3, R's RVV=P6,R5
    checkIfFullGII(P, REGION_NAME, R_rvv_bytes, false);
    AsyncInvocation<Void> async3 = createDistributedRegionAsync(R);

    // 1
    waitForCallbackStarted(R, GIITestHookType.BeforeRequestRVV);
    R.invoke(() -> InitialImageOperation.resetGIITestHook(GIITestHookType.BeforeRequestRVV, true));
    // 2
    waitForCallbackStarted(R, GIITestHookType.AfterRequestRVV);
    R.invoke(() -> InitialImageOperation.resetGIITestHook(GIITestHookType.AfterRequestRVV, true));
    // 3
    waitForCallbackStarted(R, GIITestHookType.AfterCalculatedUnfinishedOps);
    R.invoke(() -> InitialImageOperation
        .resetGIITestHook(GIITestHookType.AfterCalculatedUnfinishedOps, true));
    // 4
    waitForCallbackStarted(R, GIITestHookType.BeforeSavedReceivedRVV);
    R.invoke(
        () -> InitialImageOperation.resetGIITestHook(GIITestHookType.BeforeSavedReceivedRVV, true));
    // 5
    waitForCallbackStarted(R, GIITestHookType.AfterSavedReceivedRVV);
    R.invoke(
        () -> InitialImageOperation.resetGIITestHook(GIITestHookType.AfterSavedReceivedRVV, true));

    // 6
    waitForCallbackStarted(R, GIITestHookType.AfterSentRequestImage);
    R.invoke(
        () -> InitialImageOperation.resetGIITestHook(GIITestHookType.AfterSentRequestImage, true));

    // 7
    waitForCallbackStarted(P, GIITestHookType.AfterReceivedRequestImage);
    P.invoke(() -> InitialImageOperation.resetGIITestHook(GIITestHookType.AfterReceivedRequestImage,
        true));
    // 8
    waitForCallbackStarted(P, GIITestHookType.DuringPackingImage);
    P.invoke(
        () -> InitialImageOperation.resetGIITestHook(GIITestHookType.DuringPackingImage, true));
    // 9
    waitForCallbackStarted(P, GIITestHookType.AfterSentImageReply);
    P.invoke(
        () -> InitialImageOperation.resetGIITestHook(GIITestHookType.AfterSentImageReply, true));

    // 10
    waitForCallbackStarted(R, GIITestHookType.AfterReceivedImageReply);
    R.invoke(() -> InitialImageOperation.resetGIITestHook(GIITestHookType.AfterReceivedImageReply,
        true));
    // 11
    waitForCallbackStarted(R, GIITestHookType.DuringApplyDelta);
    R.invoke(() -> InitialImageOperation.resetGIITestHook(GIITestHookType.DuringApplyDelta, true));
    // 12
    waitForCallbackStarted(R, GIITestHookType.BeforeCleanExpiredTombstones);
    R.invoke(() -> InitialImageOperation
        .resetGIITestHook(GIITestHookType.BeforeCleanExpiredTombstones, true));
    // 13
    waitForCallbackStarted(R, GIITestHookType.AfterSavedRVVEnd);
    R.invoke(() -> InitialImageOperation.resetGIITestHook(GIITestHookType.AfterSavedRVVEnd, true));

    async3.join(MAX_WAIT);

    waitForToVerifyRVV(R, memberP, 7, null, 0); // P's rvv=p7, gc=0
    waitForToVerifyRVV(R, memberR, 3, exceptionlist, 0); // P's rvv=r3, gc=0

    RegionVersionVector p_rvv = getRVV(P);
    RegionVersionVector r_rvv = getRVV(R);
    assertSameRVV(p_rvv, r_rvv); // after gii, rvv should be the same

    // full gii chunk has 4 entries: key1,2(tombstone),3,5
    // delta gii has 2 entry: p7 (key1) and (key5(T) which is unfinished operation)
    verifyDeltaSizeFromStats(R, 2, 1);
    // verify unfinished op for key5 is revoked
    waitToVerifyKey(R, "key5", generateValue(R));
    VersionTag tag = getVersionTag(R, "key5");
    assertTrue(expect_tag.equals(tag));

    // P will receive R6 and have exception R6(3-6)
    doOnePut(R, 6, "key1"); // r6 will pass
    waitForToVerifyRVV(R, memberR, 6, exceptionlist, 0); // R's rvv=r6, gc=0
    waitForToVerifyRVV(P, memberR, 6, exceptionlist, 0); // P's rvv=r6(3-6), gc=0

    P.invoke(InitialImageOperation::resetAllGIITestHooks);
  }
P1, P2, P3 R does GII but wait at BeforeSavedReceivedRVV, so R's RVV=P3R0 P4, P5 R goes on to
save received RVV. R's new RVV=P5(3-6)R0

When image (which contains P4, P5) arrives, it should fill the special exception

This test will let the 2 operations happen before RIM, so the rvv will match between R and P
and no gii will happen. In this way, the chunk will not contain the missing entries.
geode-core/src/distributedTest/java/org/apache/geode/internal/cache/GIIDeltaDUnitTest.java