private void doClientServerTest(final String regionName, boolean createPR) throws Exception {
    // create region on the server
    final Host host = Host.getHost(0);
    final VM server = host.getVM(0);
    final VM client = host.getVM(1);
    final Object bigKey = -1;
    final Object smallKey = -2;

    final int port = startCacheServer(server, 0f, 90f, regionName, createPR, false, 0);
    startClient(client, server, port, regionName);
    doPuts(client, regionName, false/* catchServerException */, false/* catchLowMemoryException */);
    doPutAlls(client, regionName, false/* catchServerException */,
        false/* catchLowMemoryException */, Range.DEFAULT);


    // make the region sick in the server
    final long bytesUsedAfterSmallKey = (long) server.invoke(new SerializableCallable() {
      @Override
      public Object call() throws Exception {
        InternalResourceManager irm = getCache().getInternalResourceManager();
        final OffHeapMemoryMonitor ohm = irm.getOffHeapMonitor();
        assertTrue(ohm.getState().isNormal());
        getCache().getLogger().fine(addExpectedExString);
        final LocalRegion r = (LocalRegion) getRootRegion().getSubregion(regionName);
        final long bytesUsedAfterSmallKey;
        {
          OffHeapMemoryMonitorObserverImpl _testHook = new OffHeapMemoryMonitorObserverImpl();
          ohm.testHook = _testHook;
          try {
            r.put(smallKey, "1234567890");
            bytesUsedAfterSmallKey = _testHook.verifyBeginUpdateMemoryUsed(false);
          } finally {
            ohm.testHook = null;
          }
        }
        {
          final OffHeapMemoryMonitorObserverImpl th = new OffHeapMemoryMonitorObserverImpl();
          ohm.testHook = th;
          try {
            r.put(bigKey, new byte[943720]);
            th.verifyBeginUpdateMemoryUsed(bytesUsedAfterSmallKey + 943720 + 8, true);
            WaitCriterion waitForCritical = new WaitCriterion() {
              @Override
              public boolean done() {
                return th.checkUpdateStateAndSendEventBeforeProcess(
                    bytesUsedAfterSmallKey + 943720 + 8, MemoryState.EVICTION_DISABLED_CRITICAL);
              }

              @Override
              public String description() {
                return null;
              }
            };
            Wait.waitForCriterion(waitForCritical, 30 * 1000, 9, false);
            th.validateUpdateStateAndSendEventBeforeProcess(bytesUsedAfterSmallKey + 943720 + 8,
                MemoryState.EVICTION_DISABLED_CRITICAL);
          } finally {
            ohm.testHook = null;
          }
        }
        WaitCriterion wc;
        if (r instanceof PartitionedRegion) {
          final PartitionedRegion pr = (PartitionedRegion) r;
          final int bucketId = PartitionedRegionHelper.getHashKey(pr, null, bigKey, null, null);
          wc = new WaitCriterion() {
            @Override
            public String description() {
              return "Expected to go critical: isCritical=" + ohm.getState().isCritical();
            }

            @Override
            public boolean done() {
              if (!ohm.getState().isCritical()) {
                return false;
              }
              // Only done once the bucket has been marked sick
              try {
                pr.getRegionAdvisor().checkIfBucketSick(bucketId, bigKey);
                return false;
              } catch (LowMemoryException ignore) {
                return true;
              }
            }
          };
        } else {
          wc = new WaitCriterion() {
            @Override
            public String description() {
              return "Expected to go critical: isCritical=" + ohm.getState().isCritical()
                  + " memoryThresholdReached=" + r.isMemoryThresholdReached();
            }

            @Override
            public boolean done() {
              return ohm.getState().isCritical() && r.isMemoryThresholdReached();
            }
          };
        }
        Wait.waitForCriterion(wc, 30000, 9, true);
        getCache().getLogger().fine(removeExpectedExString);
        return bytesUsedAfterSmallKey;
      }
    });

    // make sure client puts are rejected
    doPuts(client, regionName, true/* catchServerException */, false/* catchLowMemoryException */);
    doPutAlls(client, regionName, true/* catchServerException */,
        false/* catchLowMemoryException */, new Range(Range.DEFAULT, Range.DEFAULT.width() + 1));

    // make the region healthy in the server
    server.invoke(new SerializableRunnable() {
      @Override
      public void run() {
        InternalResourceManager irm = getCache().getInternalResourceManager();
        final OffHeapMemoryMonitor ohm = irm.getOffHeapMonitor();
        assertTrue(ohm.getState().isCritical());
        getCache().getLogger().fine(addExpectedBelow);
        OffHeapMemoryMonitorObserverImpl _testHook = new OffHeapMemoryMonitorObserverImpl();
        ohm.testHook = _testHook;
        try {
          getRootRegion().getSubregion(regionName).destroy(bigKey);
          _testHook.verifyBeginUpdateMemoryUsed(bytesUsedAfterSmallKey, true);
        } finally {
          ohm.testHook = null;
        }
        WaitCriterion wc = new WaitCriterion() {
          @Override
          public String description() {
            return "Expected to go normal";
          }

          @Override
          public boolean done() {
            return ohm.getState().isNormal();
          }
        };
        Wait.waitForCriterion(wc, 30000, 9, true);
        getCache().getLogger().fine(removeExpectedBelow);
        return;
      }
    });
  }
Create a list of integers consisting of the ranges defined by the provided argument e.g..
createRanges(1, 4, 10, 12) means create ranges 1 through 4 and 10 through 12 and should yield
the list: 1, 2, 3, 4, 10, 11, 12
geode-core/src/distributedTest/java/org/apache/geode/cache/management/MemoryThresholdsOffHeapDUnitTest.java