You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by st...@apache.org on 2020/03/11 04:26:05 UTC

[hbase] branch branch-2 updated: HBASE-23963 Split TestFromClientSide; it takes too long to complete timing out

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

stack pushed a commit to branch branch-2
in repository https://gitbox.apache.org/repos/asf/hbase.git


The following commit(s) were added to refs/heads/branch-2 by this push:
     new ee0ea33  HBASE-23963 Split TestFromClientSide; it takes too long to complete timing out
ee0ea33 is described below

commit ee0ea33ad0bddbe4110ac2b293fa7a89a34ad1b4
Author: stack <st...@apache.org>
AuthorDate: Tue Mar 10 14:55:50 2020 -0700

    HBASE-23963 Split TestFromClientSide; it takes too long to complete timing out
    
    Split TestGetFromClientSide. Means have to also split the superclass
    TestFromClientSideWithCoprocessor.
    
    Signed-off-by: Duo Zhang <zh...@apache.org>
---
 .../hadoop/hbase/client/FromClientSideBase.java    | 1086 ++++
 .../hadoop/hbase/client/TestFromClientSide.java    | 5561 +-------------------
 .../hadoop/hbase/client/TestFromClientSide4.java   | 1354 +++++
 .../hadoop/hbase/client/TestFromClientSide5.java   | 2596 +++++++++
 .../client/TestFromClientSideWithCoprocessor.java  |   13 +-
 ...ava => TestFromClientSideWithCoprocessor4.java} |   16 +-
 ...ava => TestFromClientSideWithCoprocessor5.java} |   14 +-
 7 files changed, 5322 insertions(+), 5318 deletions(-)

diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/FromClientSideBase.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/FromClientSideBase.java
new file mode 100644
index 0000000..6787a11
--- /dev/null
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/FromClientSideBase.java
@@ -0,0 +1,1086 @@
+/*
+ * 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, 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, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.hbase.client;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.Cell;
+import org.apache.hadoop.hbase.CellUtil;
+import org.apache.hadoop.hbase.CompareOperator;
+import org.apache.hadoop.hbase.HBaseCommonTestingUtility;
+import org.apache.hadoop.hbase.HBaseTestingUtility;
+import org.apache.hadoop.hbase.HConstants;
+import org.apache.hadoop.hbase.HRegionLocation;
+import org.apache.hadoop.hbase.StartMiniClusterOption;
+import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
+import org.apache.hadoop.hbase.filter.BinaryComparator;
+import org.apache.hadoop.hbase.filter.Filter;
+import org.apache.hadoop.hbase.filter.FilterList;
+import org.apache.hadoop.hbase.filter.PrefixFilter;
+import org.apache.hadoop.hbase.filter.RowFilter;
+import org.apache.hadoop.hbase.filter.SingleColumnValueFilter;
+import org.apache.hadoop.hbase.filter.WhileMatchFilter;
+import org.apache.hadoop.hbase.testclassification.ClientTests;
+import org.apache.hadoop.hbase.testclassification.LargeTests;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
+import org.apache.hadoop.hbase.util.NonRepeatedEnvironmentEdge;
+import org.apache.hadoop.hbase.util.TableDescriptorChecker;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.hbase.thirdparty.com.google.common.base.Preconditions;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Base for TestFromClientSide* classes.
+ * Has common defines and utility used by all.
+ */
+@Category({LargeTests.class, ClientTests.class})
+@SuppressWarnings ("deprecation")
+@RunWith(Parameterized.class)
+class FromClientSideBase {
+  private static final Logger LOG = LoggerFactory.getLogger(FromClientSideBase.class);
+  static HBaseTestingUtility TEST_UTIL;
+  static byte [] ROW = Bytes.toBytes("testRow");
+  static byte [] FAMILY = Bytes.toBytes("testFamily");
+  static final byte[] INVALID_FAMILY = Bytes.toBytes("invalidTestFamily");
+  static byte [] QUALIFIER = Bytes.toBytes("testQualifier");
+  static byte [] VALUE = Bytes.toBytes("testValue");
+  static int SLAVES = 1;
+
+  // To keep the child classes happy.
+  FromClientSideBase() {}
+
+  /**
+   * JUnit does not provide an easy way to run a hook after each parameterized run. Without that
+   * there is no easy way to restart the test cluster after each parameterized run. Annotation
+   * BeforeParam does not work either because it runs before parameterization and hence does not
+   * have access to the test parameters (which is weird).
+   *
+   * This *hack* checks if the current instance of test cluster configuration has the passed
+   * parameterized configs. In such a case, we can just reuse the cluster for test and do not need
+   * to initialize from scratch. While this is a hack, it saves a ton of time for the full
+   * test and de-flakes it.
+   */
+  protected static boolean isSameParameterizedCluster(Class registryImpl, int numHedgedReqs) {
+    if (TEST_UTIL == null) {
+      return false;
+    }
+    Configuration conf = TEST_UTIL.getConfiguration();
+    Class confClass = conf.getClass(
+        HConstants.CLIENT_CONNECTION_REGISTRY_IMPL_CONF_KEY, ZKConnectionRegistry.class);
+    int hedgedReqConfig = conf.getInt(HConstants.HBASE_RPCS_HEDGED_REQS_FANOUT_KEY,
+        HConstants.HBASE_RPCS_HEDGED_REQS_FANOUT_DEFAULT);
+    return confClass.getName().equals(registryImpl.getName()) && numHedgedReqs == hedgedReqConfig;
+  }
+
+  protected static final void initialize(Class registryImpl, int numHedgedReqs, Class<?>... cps)
+      throws Exception {
+    // initialize() is called for every unit test, however we only want to reset the cluster state
+    // at the end of every parameterized run.
+    if (isSameParameterizedCluster(registryImpl, numHedgedReqs)) {
+      return;
+    }
+    // Uncomment the following lines if more verbosity is needed for
+    // debugging (see HBASE-12285 for details).
+    // ((Log4JLogger)RpcServer.LOG).getLogger().setLevel(Level.ALL);
+    // ((Log4JLogger)RpcClient.LOG).getLogger().setLevel(Level.ALL);
+    // ((Log4JLogger)ScannerCallable.LOG).getLogger().setLevel(Level.ALL);
+    // make sure that we do not get the same ts twice, see HBASE-19731 for more details.
+    EnvironmentEdgeManager.injectEdge(new NonRepeatedEnvironmentEdge());
+    if (TEST_UTIL != null) {
+      // We reached end of a parameterized run, clean up.
+      TEST_UTIL.shutdownMiniCluster();
+    }
+    TEST_UTIL = new HBaseTestingUtility();
+    Configuration conf = TEST_UTIL.getConfiguration();
+    conf.setStrings(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY,
+      Arrays.stream(cps).map(Class::getName).toArray(String[]::new));
+    conf.setBoolean(TableDescriptorChecker.TABLE_SANITY_CHECKS, true); // enable for below tests
+    conf.setClass(HConstants.CLIENT_CONNECTION_REGISTRY_IMPL_CONF_KEY, registryImpl,
+        ConnectionRegistry.class);
+    if (numHedgedReqs == 1) {
+      conf.setBoolean(HConstants.MASTER_REGISTRY_ENABLE_HEDGED_READS_KEY, false);
+    } else {
+      Preconditions.checkArgument(numHedgedReqs > 1);
+      conf.setBoolean(HConstants.MASTER_REGISTRY_ENABLE_HEDGED_READS_KEY, true);
+    }
+    conf.setInt(HConstants.HBASE_RPCS_HEDGED_REQS_FANOUT_KEY, numHedgedReqs);
+    StartMiniClusterOption.Builder builder = StartMiniClusterOption.builder();
+    // Multiple masters needed only when hedged reads for master registry are enabled.
+    builder.numMasters(numHedgedReqs > 1 ? 3 : 1).numRegionServers(SLAVES);
+    TEST_UTIL.startMiniCluster(builder.build());
+  }
+
+  protected static void afterClass() throws Exception {
+    if (TEST_UTIL != null) {
+      TEST_UTIL.shutdownMiniCluster();
+    }
+  }
+
+  protected void deleteColumns(Table ht, String value, String keyPrefix)
+    throws IOException {
+    ResultScanner scanner = buildScanner(keyPrefix, value, ht);
+    Iterator<Result> it = scanner.iterator();
+    int count = 0;
+    while (it.hasNext()) {
+      Result result = it.next();
+      Delete delete = new Delete(result.getRow());
+      delete.addColumn(Bytes.toBytes("trans-tags"), Bytes.toBytes("qual2"));
+      ht.delete(delete);
+      count++;
+    }
+    assertEquals("Did not perform correct number of deletes", 3, count);
+  }
+
+  protected int getNumberOfRows(String keyPrefix, String value, Table ht)
+    throws Exception {
+    ResultScanner resultScanner = buildScanner(keyPrefix, value, ht);
+    Iterator<Result> scanner = resultScanner.iterator();
+    int numberOfResults = 0;
+    while (scanner.hasNext()) {
+      Result result = scanner.next();
+      System.out.println("Got back key: " + Bytes.toString(result.getRow()));
+      for (Cell kv : result.rawCells()) {
+        System.out.println("kv=" + kv.toString() + ", "
+          + Bytes.toString(CellUtil.cloneValue(kv)));
+      }
+      numberOfResults++;
+    }
+    return numberOfResults;
+  }
+
+  protected ResultScanner buildScanner(String keyPrefix, String value, Table ht)
+    throws IOException {
+    // OurFilterList allFilters = new OurFilterList();
+    FilterList allFilters = new FilterList(/* FilterList.Operator.MUST_PASS_ALL */);
+    allFilters.addFilter(new PrefixFilter(Bytes.toBytes(keyPrefix)));
+    SingleColumnValueFilter filter = new SingleColumnValueFilter(Bytes
+      .toBytes("trans-tags"), Bytes.toBytes("qual2"), CompareOperator.EQUAL, Bytes
+      .toBytes(value));
+    filter.setFilterIfMissing(true);
+    allFilters.addFilter(filter);
+
+    // allFilters.addFilter(new
+    // RowExcludingSingleColumnValueFilter(Bytes.toBytes("trans-tags"),
+    // Bytes.toBytes("qual2"), CompareOp.EQUAL, Bytes.toBytes(value)));
+
+    Scan scan = new Scan();
+    scan.addFamily(Bytes.toBytes("trans-blob"));
+    scan.addFamily(Bytes.toBytes("trans-type"));
+    scan.addFamily(Bytes.toBytes("trans-date"));
+    scan.addFamily(Bytes.toBytes("trans-tags"));
+    scan.addFamily(Bytes.toBytes("trans-group"));
+    scan.setFilter(allFilters);
+
+    return ht.getScanner(scan);
+  }
+
+  protected void putRows(Table ht, int numRows, String value, String key)
+    throws IOException {
+    for (int i = 0; i < numRows; i++) {
+      String row = key + "_" + HBaseCommonTestingUtility.getRandomUUID().toString();
+      System.out.println(String.format("Saving row: %s, with value %s", row,
+        value));
+      Put put = new Put(Bytes.toBytes(row));
+      put.setDurability(Durability.SKIP_WAL);
+      put.addColumn(Bytes.toBytes("trans-blob"), null, Bytes
+        .toBytes("value for blob"));
+      put.addColumn(Bytes.toBytes("trans-type"), null, Bytes.toBytes("statement"));
+      put.addColumn(Bytes.toBytes("trans-date"), null, Bytes
+        .toBytes("20090921010101999"));
+      put.addColumn(Bytes.toBytes("trans-tags"), Bytes.toBytes("qual2"), Bytes
+        .toBytes(value));
+      put.addColumn(Bytes.toBytes("trans-group"), null, Bytes
+        .toBytes("adhocTransactionGroupId"));
+      ht.put(put);
+    }
+  }
+
+  protected void assertRowCount(final Table t, final int expected) throws IOException {
+    assertEquals(expected, TEST_UTIL.countRows(t));
+  }
+
+  /*
+   * @param key
+   * @return Scan with RowFilter that does LESS than passed key.
+   */
+  protected Scan createScanWithRowFilter(final byte [] key) {
+    return createScanWithRowFilter(key, null, CompareOperator.LESS);
+  }
+
+  /*
+   * @param key
+   * @param op
+   * @param startRow
+   * @return Scan with RowFilter that does CompareOp op on passed key.
+   */
+  protected Scan createScanWithRowFilter(final byte [] key,
+    final byte [] startRow, CompareOperator op) {
+    // Make sure key is of some substance... non-null and > than first key.
+    assertTrue(key != null && key.length > 0 &&
+      Bytes.BYTES_COMPARATOR.compare(key, new byte [] {'a', 'a', 'a'}) >= 0);
+    LOG.info("Key=" + Bytes.toString(key));
+    Scan s = startRow == null? new Scan(): new Scan(startRow);
+    Filter f = new RowFilter(op, new BinaryComparator(key));
+    f = new WhileMatchFilter(f);
+    s.setFilter(f);
+    return s;
+  }
+
+  /**
+   * Split table into multiple regions.
+   * @param t Table to split.
+   * @return Map of regions to servers.
+   */
+  protected List<HRegionLocation> splitTable(final Table t) throws IOException {
+    // Split this table in two.
+    Admin admin = TEST_UTIL.getAdmin();
+    admin.split(t.getName());
+    // Is it right closing this admin?
+    admin.close();
+    List<HRegionLocation> regions = waitOnSplit(t);
+    assertTrue(regions.size() > 1);
+    return regions;
+  }
+
+  /*
+   * Wait on table split.  May return because we waited long enough on the split
+   * and it didn't happen.  Caller should check.
+   * @param t
+   * @return Map of table regions; caller needs to check table actually split.
+   */
+  private List<HRegionLocation> waitOnSplit(final Table t) throws IOException {
+    try (RegionLocator locator = TEST_UTIL.getConnection().getRegionLocator(t.getName())) {
+      List<HRegionLocation> regions = locator.getAllRegionLocations();
+      int originalCount = regions.size();
+      for (int i = 0; i < TEST_UTIL.getConfiguration().getInt("hbase.test.retries", 30); i++) {
+        try {
+          Thread.sleep(1000);
+        } catch (InterruptedException e) {
+          e.printStackTrace();
+        }
+        regions = locator.getAllRegionLocations();
+        if (regions.size() > originalCount) {
+          break;
+        }
+      }
+      return regions;
+    }
+  }
+
+  protected Result getSingleScanResult(Table ht, Scan scan) throws IOException {
+    ResultScanner scanner = ht.getScanner(scan);
+    Result result = scanner.next();
+    scanner.close();
+    return result;
+  }
+
+  protected byte [][] makeNAscii(byte [] base, int n) {
+    if(n > 256) {
+      return makeNBig(base, n);
+    }
+    byte [][] ret = new byte[n][];
+    for(int i=0;i<n;i++) {
+      byte [] tail = Bytes.toBytes(Integer.toString(i));
+      ret[i] = Bytes.add(base, tail);
+    }
+    return ret;
+  }
+
+  protected byte [][] makeN(byte [] base, int n) {
+    if (n > 256) {
+      return makeNBig(base, n);
+    }
+    byte [][] ret = new byte[n][];
+    for(int i=0;i<n;i++) {
+      ret[i] = Bytes.add(base, new byte[]{(byte)i});
+    }
+    return ret;
+  }
+
+  protected byte [][] makeNBig(byte [] base, int n) {
+    byte [][] ret = new byte[n][];
+    for(int i = 0; i < n; i++) {
+      int byteA = (i % 256);
+      int byteB = (i >> 8);
+      ret[i] = Bytes.add(base, new byte[]{(byte)byteB,(byte)byteA});
+    }
+    return ret;
+  }
+
+  protected long [] makeStamps(int n) {
+    long [] stamps = new long[n];
+    for (int i = 0; i < n; i++) {
+      stamps[i] = i+1L;
+    }
+    return stamps;
+  }
+
+  protected static boolean equals(byte [] left, byte [] right) {
+    if (left == null && right == null) {
+      return true;
+    }
+    if (left == null && right.length == 0) {
+      return true;
+    }
+    if (right == null && left.length == 0) {
+      return true;
+    }
+    return Bytes.equals(left, right);
+  }
+
+  protected void assertKey(Cell key, byte [] row, byte [] family, byte [] qualifier,
+      byte [] value) {
+    assertTrue("Expected row [" + Bytes.toString(row) + "] " +
+        "Got row [" + Bytes.toString(CellUtil.cloneRow(key)) +"]",
+      equals(row, CellUtil.cloneRow(key)));
+    assertTrue("Expected family [" + Bytes.toString(family) + "] " +
+        "Got family [" + Bytes.toString(CellUtil.cloneFamily(key)) + "]",
+      equals(family, CellUtil.cloneFamily(key)));
+    assertTrue("Expected qualifier [" + Bytes.toString(qualifier) + "] " +
+        "Got qualifier [" + Bytes.toString(CellUtil.cloneQualifier(key)) + "]",
+      equals(qualifier, CellUtil.cloneQualifier(key)));
+    assertTrue("Expected value [" + Bytes.toString(value) + "] " +
+        "Got value [" + Bytes.toString(CellUtil.cloneValue(key)) + "]",
+      equals(value, CellUtil.cloneValue(key)));
+  }
+
+  protected static void assertIncrementKey(Cell key, byte [] row, byte [] family,
+    byte [] qualifier, long value) {
+    assertTrue("Expected row [" + Bytes.toString(row) + "] " +
+        "Got row [" + Bytes.toString(CellUtil.cloneRow(key)) +"]",
+      equals(row, CellUtil.cloneRow(key)));
+    assertTrue("Expected family [" + Bytes.toString(family) + "] " +
+        "Got family [" + Bytes.toString(CellUtil.cloneFamily(key)) + "]",
+      equals(family, CellUtil.cloneFamily(key)));
+    assertTrue("Expected qualifier [" + Bytes.toString(qualifier) + "] " +
+        "Got qualifier [" + Bytes.toString(CellUtil.cloneQualifier(key)) + "]",
+      equals(qualifier, CellUtil.cloneQualifier(key)));
+    assertEquals(
+      "Expected value [" + value + "] " + "Got value [" + Bytes.toLong(CellUtil.cloneValue(key))
+        + "]", Bytes.toLong(CellUtil.cloneValue(key)), value);
+  }
+
+  protected void assertNumKeys(Result result, int n) throws Exception {
+    assertEquals("Expected " + n + " keys but got " + result.size(), result.size(), n);
+  }
+
+  protected void assertNResult(Result result, byte [] row,
+    byte [][] families, byte [][] qualifiers, byte [][] values, int [][] idxs) {
+    assertTrue("Expected row [" + Bytes.toString(row) + "] " +
+        "Got row [" + Bytes.toString(result.getRow()) +"]",
+      equals(row, result.getRow()));
+    assertEquals("Expected " + idxs.length + " keys but result contains " + result.size(),
+      result.size(), idxs.length);
+
+    Cell [] keys = result.rawCells();
+
+    for(int i=0;i<keys.length;i++) {
+      byte [] family = families[idxs[i][0]];
+      byte [] qualifier = qualifiers[idxs[i][1]];
+      byte [] value = values[idxs[i][2]];
+      Cell key = keys[i];
+
+      byte[] famb = CellUtil.cloneFamily(key);
+      byte[] qualb = CellUtil.cloneQualifier(key);
+      byte[] valb = CellUtil.cloneValue(key);
+      assertTrue("(" + i + ") Expected family [" + Bytes.toString(family)
+          + "] " + "Got family [" + Bytes.toString(famb) + "]",
+        equals(family, famb));
+      assertTrue("(" + i + ") Expected qualifier [" + Bytes.toString(qualifier)
+          + "] " + "Got qualifier [" + Bytes.toString(qualb) + "]",
+        equals(qualifier, qualb));
+      assertTrue("(" + i + ") Expected value [" + Bytes.toString(value) + "] "
+          + "Got value [" + Bytes.toString(valb) + "]",
+        equals(value, valb));
+    }
+  }
+
+  protected void assertNResult(Result result, byte [] row,
+    byte [] family, byte [] qualifier, long [] stamps, byte [][] values,
+    int start, int end) {
+    assertTrue("Expected row [" + Bytes.toString(row) + "] " +
+        "Got row [" + Bytes.toString(result.getRow()) +"]",
+      equals(row, result.getRow()));
+    int expectedResults = end - start + 1;
+    assertEquals(expectedResults, result.size());
+
+    Cell[] keys = result.rawCells();
+
+    for (int i=0; i<keys.length; i++) {
+      byte [] value = values[end-i];
+      long ts = stamps[end-i];
+      Cell key = keys[i];
+
+      assertTrue("(" + i + ") Expected family [" + Bytes.toString(family)
+          + "] " + "Got family [" + Bytes.toString(CellUtil.cloneFamily(key)) + "]",
+        CellUtil.matchingFamily(key, family));
+      assertTrue("(" + i + ") Expected qualifier [" + Bytes.toString(qualifier)
+          + "] " + "Got qualifier [" + Bytes.toString(CellUtil.cloneQualifier(key))+ "]",
+        CellUtil.matchingQualifier(key, qualifier));
+      assertEquals("Expected ts [" + ts + "] " + "Got ts [" + key.getTimestamp() + "]", ts,
+        key.getTimestamp());
+      assertTrue("(" + i + ") Expected value [" + Bytes.toString(value) + "] "
+          + "Got value [" + Bytes.toString(CellUtil.cloneValue(key)) + "]",
+        CellUtil.matchingValue(key,  value));
+    }
+  }
+
+  /**
+   * Validate that result contains two specified keys, exactly.
+   * It is assumed key A sorts before key B.
+   */
+  protected void assertDoubleResult(Result result, byte [] row,
+    byte [] familyA, byte [] qualifierA, byte [] valueA,
+    byte [] familyB, byte [] qualifierB, byte [] valueB) {
+    assertTrue("Expected row [" + Bytes.toString(row) + "] " +
+        "Got row [" + Bytes.toString(result.getRow()) +"]",
+      equals(row, result.getRow()));
+    assertEquals("Expected two keys but result contains " + result.size(),
+      2, result.size());
+    Cell [] kv = result.rawCells();
+    Cell kvA = kv[0];
+    assertTrue("(A) Expected family [" + Bytes.toString(familyA) + "] " +
+        "Got family [" + Bytes.toString(CellUtil.cloneFamily(kvA)) + "]",
+      equals(familyA, CellUtil.cloneFamily(kvA)));
+    assertTrue("(A) Expected qualifier [" + Bytes.toString(qualifierA) + "] " +
+        "Got qualifier [" + Bytes.toString(CellUtil.cloneQualifier(kvA)) + "]",
+      equals(qualifierA, CellUtil.cloneQualifier(kvA)));
+    assertTrue("(A) Expected value [" + Bytes.toString(valueA) + "] " +
+        "Got value [" + Bytes.toString(CellUtil.cloneValue(kvA)) + "]",
+      equals(valueA, CellUtil.cloneValue(kvA)));
+    Cell kvB = kv[1];
+    assertTrue("(B) Expected family [" + Bytes.toString(familyB) + "] " +
+        "Got family [" + Bytes.toString(CellUtil.cloneFamily(kvB)) + "]",
+      equals(familyB, CellUtil.cloneFamily(kvB)));
+    assertTrue("(B) Expected qualifier [" + Bytes.toString(qualifierB) + "] " +
+        "Got qualifier [" + Bytes.toString(CellUtil.cloneQualifier(kvB)) + "]",
+      equals(qualifierB, CellUtil.cloneQualifier(kvB)));
+    assertTrue("(B) Expected value [" + Bytes.toString(valueB) + "] " +
+        "Got value [" + Bytes.toString(CellUtil.cloneValue(kvB)) + "]",
+      equals(valueB, CellUtil.cloneValue(kvB)));
+  }
+
+  protected void assertSingleResult(Result result, byte [] row, byte [] family,
+    byte [] qualifier, byte [] value) {
+    assertTrue("Expected row [" + Bytes.toString(row) + "] " +
+        "Got row [" + Bytes.toString(result.getRow()) +"]",
+      equals(row, result.getRow()));
+    assertEquals("Expected a single key but result contains " + result.size(), 1, result.size());
+    Cell kv = result.rawCells()[0];
+    assertTrue("Expected family [" + Bytes.toString(family) + "] " +
+        "Got family [" + Bytes.toString(CellUtil.cloneFamily(kv)) + "]",
+      equals(family, CellUtil.cloneFamily(kv)));
+    assertTrue("Expected qualifier [" + Bytes.toString(qualifier) + "] " +
+        "Got qualifier [" + Bytes.toString(CellUtil.cloneQualifier(kv)) + "]",
+      equals(qualifier, CellUtil.cloneQualifier(kv)));
+    assertTrue("Expected value [" + Bytes.toString(value) + "] " +
+        "Got value [" + Bytes.toString(CellUtil.cloneValue(kv)) + "]",
+      equals(value, CellUtil.cloneValue(kv)));
+  }
+
+  protected void assertSingleResult(Result result, byte[] row, byte[] family, byte[] qualifier,
+    long value) {
+    assertTrue(
+      "Expected row [" + Bytes.toString(row) + "] " + "Got row [" + Bytes.toString(result.getRow())
+        + "]", equals(row, result.getRow()));
+    assertEquals("Expected a single key but result contains " + result.size(), 1, result.size());
+    Cell kv = result.rawCells()[0];
+    assertTrue(
+      "Expected family [" + Bytes.toString(family) + "] " + "Got family ["
+        + Bytes.toString(CellUtil.cloneFamily(kv)) + "]",
+      equals(family, CellUtil.cloneFamily(kv)));
+    assertTrue("Expected qualifier [" + Bytes.toString(qualifier) + "] " + "Got qualifier ["
+        + Bytes.toString(CellUtil.cloneQualifier(kv)) + "]",
+      equals(qualifier, CellUtil.cloneQualifier(kv)));
+    assertEquals(
+      "Expected value [" + value + "] " + "Got value [" + Bytes.toLong(CellUtil.cloneValue(kv))
+        + "]", value, Bytes.toLong(CellUtil.cloneValue(kv)));
+  }
+
+  protected void assertSingleResult(Result result, byte [] row, byte [] family,
+    byte [] qualifier, long ts, byte [] value) {
+    assertTrue("Expected row [" + Bytes.toString(row) + "] " +
+        "Got row [" + Bytes.toString(result.getRow()) +"]",
+      equals(row, result.getRow()));
+    assertEquals("Expected a single key but result contains " + result.size(), 1, result.size());
+    Cell kv = result.rawCells()[0];
+    assertTrue("Expected family [" + Bytes.toString(family) + "] " +
+        "Got family [" + Bytes.toString(CellUtil.cloneFamily(kv)) + "]",
+      equals(family, CellUtil.cloneFamily(kv)));
+    assertTrue("Expected qualifier [" + Bytes.toString(qualifier) + "] " +
+        "Got qualifier [" + Bytes.toString(CellUtil.cloneQualifier(kv)) + "]",
+      equals(qualifier, CellUtil.cloneQualifier(kv)));
+    assertEquals("Expected ts [" + ts + "] " + "Got ts [" + kv.getTimestamp() + "]", ts,
+      kv.getTimestamp());
+    assertTrue("Expected value [" + Bytes.toString(value) + "] " +
+        "Got value [" + Bytes.toString(CellUtil.cloneValue(kv)) + "]",
+      equals(value, CellUtil.cloneValue(kv)));
+  }
+
+  protected void assertEmptyResult(Result result) throws Exception {
+    assertTrue("expected an empty result but result contains " +
+      result.size() + " keys", result.isEmpty());
+  }
+
+  protected void assertNullResult(Result result) throws Exception {
+    assertNull("expected null result but received a non-null result", result);
+  }
+
+  protected void getVersionRangeAndVerifyGreaterThan(Table ht, byte [] row,
+    byte [] family, byte [] qualifier, long [] stamps, byte [][] values,
+    int start, int end) throws IOException {
+    Get get = new Get(row);
+    get.addColumn(family, qualifier);
+    get.readVersions(Integer.MAX_VALUE);
+    get.setTimeRange(stamps[start+1], Long.MAX_VALUE);
+    Result result = ht.get(get);
+    assertNResult(result, row, family, qualifier, stamps, values, start+1, end);
+  }
+
+  protected void getVersionRangeAndVerify(Table ht, byte [] row, byte [] family,
+    byte [] qualifier, long [] stamps, byte [][] values, int start, int end) throws IOException {
+    Get get = new Get(row);
+    get.addColumn(family, qualifier);
+    get.readVersions(Integer.MAX_VALUE);
+    get.setTimeRange(stamps[start], stamps[end]+1);
+    Result result = ht.get(get);
+    assertNResult(result, row, family, qualifier, stamps, values, start, end);
+  }
+
+  protected void getAllVersionsAndVerify(Table ht, byte [] row, byte [] family,
+    byte [] qualifier, long [] stamps, byte [][] values, int start, int end) throws IOException {
+    Get get = new Get(row);
+    get.addColumn(family, qualifier);
+    get.readVersions(Integer.MAX_VALUE);
+    Result result = ht.get(get);
+    assertNResult(result, row, family, qualifier, stamps, values, start, end);
+  }
+
+  protected void scanVersionRangeAndVerifyGreaterThan(Table ht, byte [] row,
+    byte [] family, byte [] qualifier, long [] stamps, byte [][] values,
+    int start, int end) throws IOException {
+    Scan scan = new Scan(row);
+    scan.addColumn(family, qualifier);
+    scan.setMaxVersions(Integer.MAX_VALUE);
+    scan.setTimeRange(stamps[start+1], Long.MAX_VALUE);
+    Result result = getSingleScanResult(ht, scan);
+    assertNResult(result, row, family, qualifier, stamps, values, start+1, end);
+  }
+
+  protected void scanVersionRangeAndVerify(Table ht, byte [] row, byte [] family,
+    byte [] qualifier, long [] stamps, byte [][] values, int start, int end) throws IOException {
+    Scan scan = new Scan(row);
+    scan.addColumn(family, qualifier);
+    scan.setMaxVersions(Integer.MAX_VALUE);
+    scan.setTimeRange(stamps[start], stamps[end]+1);
+    Result result = getSingleScanResult(ht, scan);
+    assertNResult(result, row, family, qualifier, stamps, values, start, end);
+  }
+
+  protected void scanAllVersionsAndVerify(Table ht, byte [] row, byte [] family,
+    byte [] qualifier, long [] stamps, byte [][] values, int start, int end) throws IOException {
+    Scan scan = new Scan(row);
+    scan.addColumn(family, qualifier);
+    scan.setMaxVersions(Integer.MAX_VALUE);
+    Result result = getSingleScanResult(ht, scan);
+    assertNResult(result, row, family, qualifier, stamps, values, start, end);
+  }
+
+  protected void getVersionAndVerify(Table ht, byte [] row, byte [] family,
+    byte [] qualifier, long stamp, byte [] value) throws Exception {
+    Get get = new Get(row);
+    get.addColumn(family, qualifier);
+    get.setTimestamp(stamp);
+    get.readVersions(Integer.MAX_VALUE);
+    Result result = ht.get(get);
+    assertSingleResult(result, row, family, qualifier, stamp, value);
+  }
+
+  protected void getVersionAndVerifyMissing(Table ht, byte [] row, byte [] family,
+    byte [] qualifier, long stamp) throws Exception {
+    Get get = new Get(row);
+    get.addColumn(family, qualifier);
+    get.setTimestamp(stamp);
+    get.readVersions(Integer.MAX_VALUE);
+    Result result = ht.get(get);
+    assertEmptyResult(result);
+  }
+
+  protected void scanVersionAndVerify(Table ht, byte [] row, byte [] family,
+    byte [] qualifier, long stamp, byte [] value) throws Exception {
+    Scan scan = new Scan(row);
+    scan.addColumn(family, qualifier);
+    scan.setTimestamp(stamp);
+    scan.setMaxVersions(Integer.MAX_VALUE);
+    Result result = getSingleScanResult(ht, scan);
+    assertSingleResult(result, row, family, qualifier, stamp, value);
+  }
+
+  protected void scanVersionAndVerifyMissing(Table ht, byte [] row,
+    byte [] family, byte [] qualifier, long stamp) throws Exception {
+    Scan scan = new Scan(row);
+    scan.addColumn(family, qualifier);
+    scan.setTimestamp(stamp);
+    scan.setMaxVersions(Integer.MAX_VALUE);
+    Result result = getSingleScanResult(ht, scan);
+    assertNullResult(result);
+  }
+
+  protected void getTestNull(Table ht, byte [] row, byte [] family, byte [] value)
+      throws Exception {
+    Get get = new Get(row);
+    get.addColumn(family, null);
+    Result result = ht.get(get);
+    assertSingleResult(result, row, family, null, value);
+
+    get = new Get(row);
+    get.addColumn(family, HConstants.EMPTY_BYTE_ARRAY);
+    result = ht.get(get);
+    assertSingleResult(result, row, family, HConstants.EMPTY_BYTE_ARRAY, value);
+
+    get = new Get(row);
+    get.addFamily(family);
+    result = ht.get(get);
+    assertSingleResult(result, row, family, HConstants.EMPTY_BYTE_ARRAY, value);
+
+    get = new Get(row);
+    result = ht.get(get);
+    assertSingleResult(result, row, family, HConstants.EMPTY_BYTE_ARRAY, value);
+
+  }
+
+  protected void getTestNull(Table ht, byte[] row, byte[] family, long value) throws Exception {
+    Get get = new Get(row);
+    get.addColumn(family, null);
+    Result result = ht.get(get);
+    assertSingleResult(result, row, family, null, value);
+
+    get = new Get(row);
+    get.addColumn(family, HConstants.EMPTY_BYTE_ARRAY);
+    result = ht.get(get);
+    assertSingleResult(result, row, family, HConstants.EMPTY_BYTE_ARRAY, value);
+
+    get = new Get(row);
+    get.addFamily(family);
+    result = ht.get(get);
+    assertSingleResult(result, row, family, HConstants.EMPTY_BYTE_ARRAY, value);
+
+    get = new Get(row);
+    result = ht.get(get);
+    assertSingleResult(result, row, family, HConstants.EMPTY_BYTE_ARRAY, value);
+  }
+
+  protected void scanTestNull(Table ht, byte[] row, byte[] family, byte[] value)
+    throws Exception {
+    scanTestNull(ht, row, family, value, false);
+  }
+
+  protected void scanTestNull(Table ht, byte[] row, byte[] family, byte[] value,
+    boolean isReversedScan) throws Exception {
+
+    Scan scan = new Scan();
+    scan.setReversed(isReversedScan);
+    scan.addColumn(family, null);
+    Result result = getSingleScanResult(ht, scan);
+    assertSingleResult(result, row, family, HConstants.EMPTY_BYTE_ARRAY, value);
+
+    scan = new Scan();
+    scan.setReversed(isReversedScan);
+    scan.addColumn(family, HConstants.EMPTY_BYTE_ARRAY);
+    result = getSingleScanResult(ht, scan);
+    assertSingleResult(result, row, family, HConstants.EMPTY_BYTE_ARRAY, value);
+
+    scan = new Scan();
+    scan.setReversed(isReversedScan);
+    scan.addFamily(family);
+    result = getSingleScanResult(ht, scan);
+    assertSingleResult(result, row, family, HConstants.EMPTY_BYTE_ARRAY, value);
+
+    scan = new Scan();
+    scan.setReversed(isReversedScan);
+    result = getSingleScanResult(ht, scan);
+    assertSingleResult(result, row, family, HConstants.EMPTY_BYTE_ARRAY, value);
+
+  }
+
+  protected void singleRowGetTest(Table ht, byte [][] ROWS, byte [][] FAMILIES,
+    byte [][] QUALIFIERS, byte [][] VALUES) throws Exception {
+    // Single column from memstore
+    Get get = new Get(ROWS[0]);
+    get.addColumn(FAMILIES[4], QUALIFIERS[0]);
+    Result result = ht.get(get);
+    assertSingleResult(result, ROWS[0], FAMILIES[4], QUALIFIERS[0], VALUES[0]);
+
+    // Single column from storefile
+    get = new Get(ROWS[0]);
+    get.addColumn(FAMILIES[2], QUALIFIERS[2]);
+    result = ht.get(get);
+    assertSingleResult(result, ROWS[0], FAMILIES[2], QUALIFIERS[2], VALUES[2]);
+
+    // Single column from storefile, family match
+    get = new Get(ROWS[0]);
+    get.addFamily(FAMILIES[7]);
+    result = ht.get(get);
+    assertSingleResult(result, ROWS[0], FAMILIES[7], QUALIFIERS[7], VALUES[7]);
+
+    // Two columns, one from memstore one from storefile, same family,
+    // wildcard match
+    get = new Get(ROWS[0]);
+    get.addFamily(FAMILIES[4]);
+    result = ht.get(get);
+    assertDoubleResult(result, ROWS[0], FAMILIES[4], QUALIFIERS[0], VALUES[0],
+      FAMILIES[4], QUALIFIERS[4], VALUES[4]);
+
+    // Two columns, one from memstore one from storefile, same family,
+    // explicit match
+    get = new Get(ROWS[0]);
+    get.addColumn(FAMILIES[4], QUALIFIERS[0]);
+    get.addColumn(FAMILIES[4], QUALIFIERS[4]);
+    result = ht.get(get);
+    assertDoubleResult(result, ROWS[0], FAMILIES[4], QUALIFIERS[0], VALUES[0],
+      FAMILIES[4], QUALIFIERS[4], VALUES[4]);
+
+    // Three column, one from memstore two from storefile, different families,
+    // wildcard match
+    get = new Get(ROWS[0]);
+    get.addFamily(FAMILIES[4]);
+    get.addFamily(FAMILIES[7]);
+    result = ht.get(get);
+    assertNResult(result, ROWS[0], FAMILIES, QUALIFIERS, VALUES,
+      new int [][] { {4, 0, 0}, {4, 4, 4}, {7, 7, 7} });
+
+    // Multiple columns from everywhere storefile, many family, wildcard
+    get = new Get(ROWS[0]);
+    get.addFamily(FAMILIES[2]);
+    get.addFamily(FAMILIES[4]);
+    get.addFamily(FAMILIES[6]);
+    get.addFamily(FAMILIES[7]);
+    result = ht.get(get);
+    assertNResult(result, ROWS[0], FAMILIES, QUALIFIERS, VALUES,
+      new int [][] {
+        {2, 2, 2}, {2, 4, 4}, {4, 0, 0}, {4, 4, 4}, {6, 6, 6}, {6, 7, 7}, {7, 7, 7}
+      });
+
+    // Multiple columns from everywhere storefile, many family, wildcard
+    get = new Get(ROWS[0]);
+    get.addColumn(FAMILIES[2], QUALIFIERS[2]);
+    get.addColumn(FAMILIES[2], QUALIFIERS[4]);
+    get.addColumn(FAMILIES[4], QUALIFIERS[0]);
+    get.addColumn(FAMILIES[4], QUALIFIERS[4]);
+    get.addColumn(FAMILIES[6], QUALIFIERS[6]);
+    get.addColumn(FAMILIES[6], QUALIFIERS[7]);
+    get.addColumn(FAMILIES[7], QUALIFIERS[7]);
+    get.addColumn(FAMILIES[7], QUALIFIERS[8]);
+    result = ht.get(get);
+    assertNResult(result, ROWS[0], FAMILIES, QUALIFIERS, VALUES,
+      new int [][] {
+        {2, 2, 2}, {2, 4, 4}, {4, 0, 0}, {4, 4, 4}, {6, 6, 6}, {6, 7, 7}, {7, 7, 7}
+      });
+
+    // Everything
+    get = new Get(ROWS[0]);
+    result = ht.get(get);
+    assertNResult(result, ROWS[0], FAMILIES, QUALIFIERS, VALUES,
+      new int [][] {
+        {2, 2, 2}, {2, 4, 4}, {4, 0, 0}, {4, 4, 4}, {6, 6, 6}, {6, 7, 7}, {7, 7, 7}, {9, 0, 0}
+      });
+
+    // Get around inserted columns
+
+    get = new Get(ROWS[1]);
+    result = ht.get(get);
+    assertEmptyResult(result);
+
+    get = new Get(ROWS[0]);
+    get.addColumn(FAMILIES[4], QUALIFIERS[3]);
+    get.addColumn(FAMILIES[2], QUALIFIERS[3]);
+    result = ht.get(get);
+    assertEmptyResult(result);
+
+  }
+
+  protected void singleRowScanTest(Table ht, byte [][] ROWS, byte [][] FAMILIES,
+    byte [][] QUALIFIERS, byte [][] VALUES) throws Exception {
+    // Single column from memstore
+    Scan scan = new Scan();
+    scan.addColumn(FAMILIES[4], QUALIFIERS[0]);
+    Result result = getSingleScanResult(ht, scan);
+    assertSingleResult(result, ROWS[0], FAMILIES[4], QUALIFIERS[0], VALUES[0]);
+
+    // Single column from storefile
+    scan = new Scan();
+    scan.addColumn(FAMILIES[2], QUALIFIERS[2]);
+    result = getSingleScanResult(ht, scan);
+    assertSingleResult(result, ROWS[0], FAMILIES[2], QUALIFIERS[2], VALUES[2]);
+
+    // Single column from storefile, family match
+    scan = new Scan();
+    scan.addFamily(FAMILIES[7]);
+    result = getSingleScanResult(ht, scan);
+    assertSingleResult(result, ROWS[0], FAMILIES[7], QUALIFIERS[7], VALUES[7]);
+
+    // Two columns, one from memstore one from storefile, same family,
+    // wildcard match
+    scan = new Scan();
+    scan.addFamily(FAMILIES[4]);
+    result = getSingleScanResult(ht, scan);
+    assertDoubleResult(result, ROWS[0], FAMILIES[4], QUALIFIERS[0], VALUES[0],
+      FAMILIES[4], QUALIFIERS[4], VALUES[4]);
+
+    // Two columns, one from memstore one from storefile, same family,
+    // explicit match
+    scan = new Scan();
+    scan.addColumn(FAMILIES[4], QUALIFIERS[0]);
+    scan.addColumn(FAMILIES[4], QUALIFIERS[4]);
+    result = getSingleScanResult(ht, scan);
+    assertDoubleResult(result, ROWS[0], FAMILIES[4], QUALIFIERS[0], VALUES[0],
+      FAMILIES[4], QUALIFIERS[4], VALUES[4]);
+
+    // Three column, one from memstore two from storefile, different families,
+    // wildcard match
+    scan = new Scan();
+    scan.addFamily(FAMILIES[4]);
+    scan.addFamily(FAMILIES[7]);
+    result = getSingleScanResult(ht, scan);
+    assertNResult(result, ROWS[0], FAMILIES, QUALIFIERS, VALUES,
+      new int [][] { {4, 0, 0}, {4, 4, 4}, {7, 7, 7} });
+
+    // Multiple columns from everywhere storefile, many family, wildcard
+    scan = new Scan();
+    scan.addFamily(FAMILIES[2]);
+    scan.addFamily(FAMILIES[4]);
+    scan.addFamily(FAMILIES[6]);
+    scan.addFamily(FAMILIES[7]);
+    result = getSingleScanResult(ht, scan);
+    assertNResult(result, ROWS[0], FAMILIES, QUALIFIERS, VALUES,
+      new int [][] {
+        {2, 2, 2}, {2, 4, 4}, {4, 0, 0}, {4, 4, 4}, {6, 6, 6}, {6, 7, 7}, {7, 7, 7}
+      });
+
+    // Multiple columns from everywhere storefile, many family, wildcard
+    scan = new Scan();
+    scan.addColumn(FAMILIES[2], QUALIFIERS[2]);
+    scan.addColumn(FAMILIES[2], QUALIFIERS[4]);
+    scan.addColumn(FAMILIES[4], QUALIFIERS[0]);
+    scan.addColumn(FAMILIES[4], QUALIFIERS[4]);
+    scan.addColumn(FAMILIES[6], QUALIFIERS[6]);
+    scan.addColumn(FAMILIES[6], QUALIFIERS[7]);
+    scan.addColumn(FAMILIES[7], QUALIFIERS[7]);
+    scan.addColumn(FAMILIES[7], QUALIFIERS[8]);
+    result = getSingleScanResult(ht, scan);
+    assertNResult(result, ROWS[0], FAMILIES, QUALIFIERS, VALUES,
+      new int [][] {
+        {2, 2, 2}, {2, 4, 4}, {4, 0, 0}, {4, 4, 4}, {6, 6, 6}, {6, 7, 7}, {7, 7, 7}
+      });
+
+    // Everything
+    scan = new Scan();
+    result = getSingleScanResult(ht, scan);
+    assertNResult(result, ROWS[0], FAMILIES, QUALIFIERS, VALUES,
+      new int [][] {
+        {2, 2, 2}, {2, 4, 4}, {4, 0, 0}, {4, 4, 4}, {6, 6, 6}, {6, 7, 7}, {7, 7, 7}, {9, 0, 0}
+      });
+
+    // Scan around inserted columns
+
+    scan = new Scan(ROWS[1]);
+    result = getSingleScanResult(ht, scan);
+    assertNullResult(result);
+
+    scan = new Scan();
+    scan.addColumn(FAMILIES[4], QUALIFIERS[3]);
+    scan.addColumn(FAMILIES[2], QUALIFIERS[3]);
+    result = getSingleScanResult(ht, scan);
+    assertNullResult(result);
+  }
+
+  /**
+   * Verify a single column using gets.
+   * Expects family and qualifier arrays to be valid for at least
+   * the range:  idx-2 < idx < idx+2
+   */
+  protected void getVerifySingleColumn(Table ht, byte [][] ROWS, int ROWIDX, byte [][] FAMILIES,
+    int FAMILYIDX, byte [][] QUALIFIERS, int QUALIFIERIDX, byte [][] VALUES, int VALUEIDX)
+    throws Exception {
+    Get get = new Get(ROWS[ROWIDX]);
+    Result result = ht.get(get);
+    assertSingleResult(result, ROWS[ROWIDX], FAMILIES[FAMILYIDX],
+      QUALIFIERS[QUALIFIERIDX], VALUES[VALUEIDX]);
+
+    get = new Get(ROWS[ROWIDX]);
+    get.addFamily(FAMILIES[FAMILYIDX]);
+    result = ht.get(get);
+    assertSingleResult(result, ROWS[ROWIDX], FAMILIES[FAMILYIDX],
+      QUALIFIERS[QUALIFIERIDX], VALUES[VALUEIDX]);
+
+    get = new Get(ROWS[ROWIDX]);
+    get.addFamily(FAMILIES[FAMILYIDX-2]);
+    get.addFamily(FAMILIES[FAMILYIDX]);
+    get.addFamily(FAMILIES[FAMILYIDX+2]);
+    result = ht.get(get);
+    assertSingleResult(result, ROWS[ROWIDX], FAMILIES[FAMILYIDX],
+      QUALIFIERS[QUALIFIERIDX], VALUES[VALUEIDX]);
+
+    get = new Get(ROWS[ROWIDX]);
+    get.addColumn(FAMILIES[FAMILYIDX], QUALIFIERS[0]);
+    result = ht.get(get);
+    assertSingleResult(result, ROWS[ROWIDX], FAMILIES[FAMILYIDX],
+      QUALIFIERS[QUALIFIERIDX], VALUES[VALUEIDX]);
+
+    get = new Get(ROWS[ROWIDX]);
+    get.addColumn(FAMILIES[FAMILYIDX], QUALIFIERS[1]);
+    get.addFamily(FAMILIES[FAMILYIDX]);
+    result = ht.get(get);
+    assertSingleResult(result, ROWS[ROWIDX], FAMILIES[FAMILYIDX],
+      QUALIFIERS[QUALIFIERIDX], VALUES[VALUEIDX]);
+
+    get = new Get(ROWS[ROWIDX]);
+    get.addFamily(FAMILIES[FAMILYIDX]);
+    get.addColumn(FAMILIES[FAMILYIDX+1], QUALIFIERS[1]);
+    get.addColumn(FAMILIES[FAMILYIDX-2], QUALIFIERS[1]);
+    get.addFamily(FAMILIES[FAMILYIDX-1]);
+    get.addFamily(FAMILIES[FAMILYIDX+2]);
+    result = ht.get(get);
+    assertSingleResult(result, ROWS[ROWIDX], FAMILIES[FAMILYIDX],
+      QUALIFIERS[QUALIFIERIDX], VALUES[VALUEIDX]);
+
+  }
+
+
+  /**
+   * Verify a single column using scanners.
+   * Expects family and qualifier arrays to be valid for at least
+   * the range:  idx-2 to idx+2
+   * Expects row array to be valid for at least idx to idx+2
+   */
+  protected void scanVerifySingleColumn(Table ht, byte [][] ROWS, int ROWIDX, byte [][] FAMILIES,
+    int FAMILYIDX, byte [][] QUALIFIERS, int QUALIFIERIDX, byte [][] VALUES, int VALUEIDX)
+    throws Exception {
+    Scan scan = new Scan();
+    Result result = getSingleScanResult(ht, scan);
+    assertSingleResult(result, ROWS[ROWIDX], FAMILIES[FAMILYIDX],
+      QUALIFIERS[QUALIFIERIDX], VALUES[VALUEIDX]);
+
+    scan = new Scan(ROWS[ROWIDX]);
+    result = getSingleScanResult(ht, scan);
+    assertSingleResult(result, ROWS[ROWIDX], FAMILIES[FAMILYIDX],
+      QUALIFIERS[QUALIFIERIDX], VALUES[VALUEIDX]);
+
+    scan = new Scan(ROWS[ROWIDX], ROWS[ROWIDX+1]);
+    result = getSingleScanResult(ht, scan);
+    assertSingleResult(result, ROWS[ROWIDX], FAMILIES[FAMILYIDX],
+      QUALIFIERS[QUALIFIERIDX], VALUES[VALUEIDX]);
+
+    scan = new Scan(HConstants.EMPTY_START_ROW, ROWS[ROWIDX+1]);
+    result = getSingleScanResult(ht, scan);
+    assertSingleResult(result, ROWS[ROWIDX], FAMILIES[FAMILYIDX],
+      QUALIFIERS[QUALIFIERIDX], VALUES[VALUEIDX]);
+
+    scan = new Scan();
+    scan.addFamily(FAMILIES[FAMILYIDX]);
+    result = getSingleScanResult(ht, scan);
+    assertSingleResult(result, ROWS[ROWIDX], FAMILIES[FAMILYIDX],
+      QUALIFIERS[QUALIFIERIDX], VALUES[VALUEIDX]);
+
+    scan = new Scan();
+    scan.addColumn(FAMILIES[FAMILYIDX], QUALIFIERS[QUALIFIERIDX]);
+    result = getSingleScanResult(ht, scan);
+    assertSingleResult(result, ROWS[ROWIDX], FAMILIES[FAMILYIDX],
+      QUALIFIERS[QUALIFIERIDX], VALUES[VALUEIDX]);
+
+    scan = new Scan();
+    scan.addColumn(FAMILIES[FAMILYIDX], QUALIFIERS[QUALIFIERIDX+1]);
+    scan.addFamily(FAMILIES[FAMILYIDX]);
+    result = getSingleScanResult(ht, scan);
+    assertSingleResult(result, ROWS[ROWIDX], FAMILIES[FAMILYIDX],
+      QUALIFIERS[QUALIFIERIDX], VALUES[VALUEIDX]);
+
+    scan = new Scan();
+    scan.addColumn(FAMILIES[FAMILYIDX-1], QUALIFIERS[QUALIFIERIDX+1]);
+    scan.addColumn(FAMILIES[FAMILYIDX], QUALIFIERS[QUALIFIERIDX]);
+    scan.addFamily(FAMILIES[FAMILYIDX+1]);
+    result = getSingleScanResult(ht, scan);
+    assertSingleResult(result, ROWS[ROWIDX], FAMILIES[FAMILYIDX],
+      QUALIFIERS[QUALIFIERIDX], VALUES[VALUEIDX]);
+
+  }
+
+  /**
+   * Verify we do not read any values by accident around a single column
+   * Same requirements as getVerifySingleColumn
+   */
+  protected void getVerifySingleEmpty(Table ht, byte [][] ROWS, int ROWIDX, byte [][] FAMILIES,
+    int FAMILYIDX, byte [][] QUALIFIERS, int QUALIFIERIDX) throws Exception {
+    Get get = new Get(ROWS[ROWIDX]);
+    get.addFamily(FAMILIES[4]);
+    get.addColumn(FAMILIES[4], QUALIFIERS[1]);
+    Result result = ht.get(get);
+    assertEmptyResult(result);
+
+    get = new Get(ROWS[ROWIDX]);
+    get.addFamily(FAMILIES[4]);
+    get.addColumn(FAMILIES[4], QUALIFIERS[2]);
+    result = ht.get(get);
+    assertEmptyResult(result);
+
+    get = new Get(ROWS[ROWIDX]);
+    get.addFamily(FAMILIES[3]);
+    get.addColumn(FAMILIES[4], QUALIFIERS[2]);
+    get.addFamily(FAMILIES[5]);
+    result = ht.get(get);
+    assertEmptyResult(result);
+
+    get = new Get(ROWS[ROWIDX+1]);
+    result = ht.get(get);
+    assertEmptyResult(result);
+
+  }
+
+  protected void scanVerifySingleEmpty(Table ht, byte [][] ROWS, int ROWIDX, byte [][] FAMILIES,
+    int FAMILYIDX, byte [][] QUALIFIERS, int QUALIFIERIDX) throws Exception {
+    Scan scan = new Scan(ROWS[ROWIDX+1]);
+    Result result = getSingleScanResult(ht, scan);
+    assertNullResult(result);
+
+    scan = new Scan(ROWS[ROWIDX+1],ROWS[ROWIDX+2]);
+    result = getSingleScanResult(ht, scan);
+    assertNullResult(result);
+
+    scan = new Scan(HConstants.EMPTY_START_ROW, ROWS[ROWIDX]);
+    result = getSingleScanResult(ht, scan);
+    assertNullResult(result);
+
+    scan = new Scan();
+    scan.addColumn(FAMILIES[FAMILYIDX], QUALIFIERS[QUALIFIERIDX+1]);
+    scan.addFamily(FAMILIES[FAMILYIDX-1]);
+    result = getSingleScanResult(ht, scan);
+    assertNullResult(result);
+
+  }
+}
+
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestFromClientSide.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestFromClientSide.java
index 67fe82d..4a83527 100644
--- a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestFromClientSide.java
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestFromClientSide.java
@@ -19,8 +19,6 @@ package org.apache.hadoop.hbase.client;
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
@@ -29,88 +27,35 @@ import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.EnumSet;
 import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
-import java.util.NavigableMap;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.atomic.AtomicReference;
-import org.apache.commons.lang3.ArrayUtils;
 import org.apache.hadoop.conf.Configuration;
-import org.apache.hadoop.fs.Path;
 import org.apache.hadoop.hbase.Cell;
-import org.apache.hadoop.hbase.CellScanner;
 import org.apache.hadoop.hbase.CellUtil;
-import org.apache.hadoop.hbase.ClusterMetrics.Option;
 import org.apache.hadoop.hbase.CompareOperator;
-import org.apache.hadoop.hbase.DoNotRetryIOException;
 import org.apache.hadoop.hbase.HBaseClassTestRule;
 import org.apache.hadoop.hbase.HBaseTestingUtility;
 import org.apache.hadoop.hbase.HColumnDescriptor;
 import org.apache.hadoop.hbase.HConstants;
-import org.apache.hadoop.hbase.HRegionInfo;
 import org.apache.hadoop.hbase.HRegionLocation;
 import org.apache.hadoop.hbase.HTableDescriptor;
 import org.apache.hadoop.hbase.KeepDeletedCells;
-import org.apache.hadoop.hbase.KeyValue;
-import org.apache.hadoop.hbase.MiniHBaseCluster;
 import org.apache.hadoop.hbase.PrivateCellUtil;
-import org.apache.hadoop.hbase.RegionLocations;
-import org.apache.hadoop.hbase.ServerName;
-import org.apache.hadoop.hbase.StartMiniClusterOption;
 import org.apache.hadoop.hbase.TableName;
 import org.apache.hadoop.hbase.TableNameTestRule;
-import org.apache.hadoop.hbase.Waiter;
-import org.apache.hadoop.hbase.client.metrics.ScanMetrics;
-import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
 import org.apache.hadoop.hbase.coprocessor.MultiRowMutationEndpoint;
-import org.apache.hadoop.hbase.filter.BinaryComparator;
 import org.apache.hadoop.hbase.filter.Filter;
-import org.apache.hadoop.hbase.filter.FilterList;
-import org.apache.hadoop.hbase.filter.FirstKeyOnlyFilter;
-import org.apache.hadoop.hbase.filter.InclusiveStopFilter;
 import org.apache.hadoop.hbase.filter.KeyOnlyFilter;
 import org.apache.hadoop.hbase.filter.LongComparator;
-import org.apache.hadoop.hbase.filter.PrefixFilter;
 import org.apache.hadoop.hbase.filter.QualifierFilter;
 import org.apache.hadoop.hbase.filter.RegexStringComparator;
-import org.apache.hadoop.hbase.filter.RowFilter;
 import org.apache.hadoop.hbase.filter.SingleColumnValueFilter;
-import org.apache.hadoop.hbase.filter.SubstringComparator;
-import org.apache.hadoop.hbase.filter.ValueFilter;
-import org.apache.hadoop.hbase.filter.WhileMatchFilter;
-import org.apache.hadoop.hbase.io.TimeRange;
-import org.apache.hadoop.hbase.io.hfile.BlockCache;
-import org.apache.hadoop.hbase.io.hfile.CacheConfig;
-import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel;
-import org.apache.hadoop.hbase.master.LoadBalancer;
-import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
-import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.MutationProto;
-import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.MutationProto.MutationType;
-import org.apache.hadoop.hbase.protobuf.generated.MultiRowMutationProtos.MultiRowMutationService;
-import org.apache.hadoop.hbase.protobuf.generated.MultiRowMutationProtos.MutateRowsRequest;
-import org.apache.hadoop.hbase.regionserver.HRegion;
-import org.apache.hadoop.hbase.regionserver.HRegionServer;
-import org.apache.hadoop.hbase.regionserver.HStore;
-import org.apache.hadoop.hbase.regionserver.NoSuchColumnFamilyException;
 import org.apache.hadoop.hbase.testclassification.ClientTests;
 import org.apache.hadoop.hbase.testclassification.LargeTests;
 import org.apache.hadoop.hbase.util.Bytes;
-import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
-import org.apache.hadoop.hbase.util.FSUtils;
-import org.apache.hadoop.hbase.util.NonRepeatedEnvironmentEdge;
-import org.apache.hadoop.hbase.util.Pair;
-import org.apache.hadoop.hbase.util.TableDescriptorChecker;
 import org.junit.AfterClass;
-import org.junit.Assume;
 import org.junit.ClassRule;
-import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
@@ -118,7 +63,6 @@ import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.apache.hbase.thirdparty.com.google.common.base.Preconditions;
 
 /**
  * Run tests that use the HBase clients; {@link Table}.
@@ -126,117 +70,45 @@ import org.apache.hbase.thirdparty.com.google.common.base.Preconditions;
  * Each creates a table named for the method and does its stuff against that.
  *
  * Parameterized to run with different registry implementations.
+ *
+ * This class was split in three because it got too big when parameterized. Other classes
+ * are below.
+ *
+ * @see TestFromClientSide4
+ * @see TestFromClientSide5
  */
+// NOTE: Increment tests were moved to their own class, TestIncrementsFromClientSide.
 @Category({LargeTests.class, ClientTests.class})
 @SuppressWarnings ("deprecation")
 @RunWith(Parameterized.class)
-public class TestFromClientSide {
-
-  @ClassRule
-  public static final HBaseClassTestRule CLASS_RULE =
-      HBaseClassTestRule.forClass(TestFromClientSide.class);
-
-  // NOTE: Increment tests were moved to their own class, TestIncrementsFromClientSide.
+public class TestFromClientSide extends FromClientSideBase {
   private static final Logger LOG = LoggerFactory.getLogger(TestFromClientSide.class);
-  protected static HBaseTestingUtility TEST_UTIL;
-  private static byte [] ROW = Bytes.toBytes("testRow");
-  private static byte [] FAMILY = Bytes.toBytes("testFamily");
-  private static final byte[] INVALID_FAMILY = Bytes.toBytes("invalidTestFamily");
-  private static byte [] QUALIFIER = Bytes.toBytes("testQualifier");
-  private static byte [] VALUE = Bytes.toBytes("testValue");
-  protected static int SLAVES = 3;
 
+  @ClassRule public static final HBaseClassTestRule CLASS_RULE =
+    HBaseClassTestRule.forClass(TestFromClientSide.class);
   @Rule public TableNameTestRule name = new TableNameTestRule();
 
   // To keep the child classes happy.
-  TestFromClientSide() {}
+  TestFromClientSide() {
+  }
 
   public TestFromClientSide(Class registry, int numHedgedReqs) throws Exception {
     initialize(registry, numHedgedReqs, MultiRowMutationEndpoint.class);
   }
 
-  @Parameterized.Parameters
-  public static Collection parameters() {
-    return Arrays.asList(new Object[][] {
-        { MasterRegistry.class, 1},
-        { MasterRegistry.class, 2},
-        { ZKConnectionRegistry.class, 1}
-    });
-  }
-
-  /**
-   * JUnit does not provide an easy way to run a hook after each parameterized run. Without that
-   * there is no easy way to restart the test cluster after each parameterized run. Annotation
-   * BeforeParam does not work either because it runs before parameterization and hence does not
-   * have access to the test parameters (which is weird).
-   *
-   * This *hack* checks if the current instance of test cluster configuration has the passed
-   * parameterized configs. In such a case, we can just reuse the cluster for test and do not need
-   * to initialize from scratch. While this is a hack, it saves a ton of time for the full
-   * test and de-flakes it.
-   */
-  private static boolean isSameParameterizedCluster(Class registryImpl, int numHedgedReqs) {
-    if (TEST_UTIL == null) {
-      return false;
-    }
-    Configuration conf = TEST_UTIL.getConfiguration();
-    Class confClass = conf.getClass(
-        HConstants.CLIENT_CONNECTION_REGISTRY_IMPL_CONF_KEY, ZKConnectionRegistry.class);
-    int hedgedReqConfig = conf.getInt(HConstants.HBASE_RPCS_HEDGED_REQS_FANOUT_KEY,
-        HConstants.HBASE_RPCS_HEDGED_REQS_FANOUT_DEFAULT);
-    return confClass.getName().equals(registryImpl.getName()) && numHedgedReqs == hedgedReqConfig;
-  }
-
-  protected static final void initialize(Class registryImpl, int numHedgedReqs, Class<?>... cps)
-      throws Exception {
-    // initialize() is called for every unit test, however we only want to reset the cluster state
-    // at the end of every parameterized run.
-    if (isSameParameterizedCluster(registryImpl, numHedgedReqs)) {
-      return;
-    }
-    // Uncomment the following lines if more verbosity is needed for
-    // debugging (see HBASE-12285 for details).
-    // ((Log4JLogger)RpcServer.LOG).getLogger().setLevel(Level.ALL);
-    // ((Log4JLogger)RpcClient.LOG).getLogger().setLevel(Level.ALL);
-    // ((Log4JLogger)ScannerCallable.LOG).getLogger().setLevel(Level.ALL);
-    // make sure that we do not get the same ts twice, see HBASE-19731 for more details.
-    EnvironmentEdgeManager.injectEdge(new NonRepeatedEnvironmentEdge());
-    if (TEST_UTIL != null) {
-      // We reached end of a parameterized run, clean up.
-      TEST_UTIL.shutdownMiniCluster();
-    }
-    TEST_UTIL = new HBaseTestingUtility();
-    Configuration conf = TEST_UTIL.getConfiguration();
-    conf.setStrings(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY,
-      Arrays.stream(cps).map(Class::getName).toArray(String[]::new));
-    conf.setBoolean(TableDescriptorChecker.TABLE_SANITY_CHECKS, true); // enable for below tests
-    conf.setClass(HConstants.CLIENT_CONNECTION_REGISTRY_IMPL_CONF_KEY, registryImpl,
-        ConnectionRegistry.class);
-    if (numHedgedReqs == 1) {
-      conf.setBoolean(HConstants.MASTER_REGISTRY_ENABLE_HEDGED_READS_KEY, false);
-    } else {
-      Preconditions.checkArgument(numHedgedReqs > 1);
-      conf.setBoolean(HConstants.MASTER_REGISTRY_ENABLE_HEDGED_READS_KEY, true);
-    }
-    conf.setInt(HConstants.HBASE_RPCS_HEDGED_REQS_FANOUT_KEY, numHedgedReqs);
-    StartMiniClusterOption.Builder builder = StartMiniClusterOption.builder();
-    // Multiple masters needed only when hedged reads for master registry are enabled.
-    builder.numMasters(numHedgedReqs > 1 ? 3 : 1).numRegionServers(SLAVES);
-    TEST_UTIL.startMiniCluster(builder.build());
+  @Parameterized.Parameters public static Collection parameters() {
+    return Arrays.asList(new Object[][] { { MasterRegistry.class, 1 }, { MasterRegistry.class, 2 },
+      { ZKConnectionRegistry.class, 1 } });
   }
 
-  @AfterClass
-  public static void tearDownAfterClass() throws Exception {
-    if (TEST_UTIL != null) {
-      TEST_UTIL.shutdownMiniCluster();
-    }
+  @AfterClass public static void tearDownAfterClass() throws Exception {
+    afterClass();
   }
 
   /**
    * Test append result when there are duplicate rpc request.
    */
-  @Test
-  public void testDuplicateAppend() throws Exception {
+  @Test public void testDuplicateAppend() throws Exception {
     HTableDescriptor hdt = TEST_UTIL.createTableDescriptor(name.getTableName());
     Map<String, String> kvs = new HashMap<>();
     kvs.put(HConnectionTestingUtility.SleepAtFirstRpcCall.SLEEP_TIME_CONF_KEY, "2000");
@@ -276,8 +148,7 @@ public class TestFromClientSide {
   /**
    * Basic client side validation of HBASE-4536
    */
-  @Test
-  public void testKeepDeletedCells() throws Exception {
+  @Test public void testKeepDeletedCells() throws Exception {
     final TableName tableName = name.getTableName();
     final byte[] FAMILY = Bytes.toBytes("family");
     final byte[] C0 = Bytes.toBytes("c0");
@@ -286,7 +157,7 @@ public class TestFromClientSide {
     final byte[] T2 = Bytes.toBytes("T2");
     final byte[] T3 = Bytes.toBytes("T3");
     HColumnDescriptor hcd =
-        new HColumnDescriptor(FAMILY).setKeepDeletedCells(KeepDeletedCells.TRUE).setMaxVersions(3);
+      new HColumnDescriptor(FAMILY).setKeepDeletedCells(KeepDeletedCells.TRUE).setMaxVersions(3);
 
     HTableDescriptor desc = new HTableDescriptor(tableName);
     desc.addFamily(hcd);
@@ -342,8 +213,7 @@ public class TestFromClientSide {
   /**
    * Basic client side validation of HBASE-10118
    */
-  @Test
-  public void testPurgeFutureDeletes() throws Exception {
+  @Test public void testPurgeFutureDeletes() throws Exception {
     final TableName tableName = name.getTableName();
     final byte[] ROW = Bytes.toBytes("row");
     final byte[] FAMILY = Bytes.toBytes("family");
@@ -374,12 +244,8 @@ public class TestFromClientSide {
       TEST_UTIL.getAdmin().majorCompact(tableName);
 
       // waiting for the major compaction to complete
-      TEST_UTIL.waitFor(6000, new Waiter.Predicate<IOException>() {
-        @Override
-        public boolean evaluate() throws IOException {
-          return TEST_UTIL.getAdmin().getCompactionState(tableName) == CompactionState.NONE;
-        }
-      });
+      TEST_UTIL.waitFor(6000,
+        () -> TEST_UTIL.getAdmin().getCompactionState(tableName) == CompactionState.NONE);
 
       put = new Put(ROW, ts);
       put.addColumn(FAMILY, COLUMN, VALUE);
@@ -395,8 +261,7 @@ public class TestFromClientSide {
    * Verifies that getConfiguration returns the same Configuration object used
    * to create the HTable instance.
    */
-  @Test
-  public void testGetConfiguration() throws Exception {
+  @Test public void testGetConfiguration() throws Exception {
     final TableName tableName = name.getTableName();
     byte[][] FAMILIES = new byte[][] { Bytes.toBytes("foo") };
     Configuration conf = TEST_UTIL.getConfiguration();
@@ -409,18 +274,16 @@ public class TestFromClientSide {
    * Test from client side of an involved filter against a multi family that
    * involves deletes.
    */
-  @Test
-  public void testWeirdCacheBehaviour() throws Exception {
+  @Test public void testWeirdCacheBehaviour() throws Exception {
     final TableName tableName = name.getTableName();
-    byte [][] FAMILIES = new byte[][] { Bytes.toBytes("trans-blob"),
-        Bytes.toBytes("trans-type"), Bytes.toBytes("trans-date"),
-        Bytes.toBytes("trans-tags"), Bytes.toBytes("trans-group") };
+    byte[][] FAMILIES = new byte[][] { Bytes.toBytes("trans-blob"), Bytes.toBytes("trans-type"),
+      Bytes.toBytes("trans-date"), Bytes.toBytes("trans-tags"), Bytes.toBytes("trans-group") };
     try (Table ht = TEST_UTIL.createTable(tableName, FAMILIES)) {
       String value = "this is the value";
       String value2 = "this is some other value";
-      String keyPrefix1 = TEST_UTIL.getRandomUUID().toString();
-      String keyPrefix2 = TEST_UTIL.getRandomUUID().toString();
-      String keyPrefix3 = TEST_UTIL.getRandomUUID().toString();
+      String keyPrefix1 = HBaseTestingUtility.getRandomUUID().toString();
+      String keyPrefix2 = HBaseTestingUtility.getRandomUUID().toString();
+      String keyPrefix3 = HBaseTestingUtility.getRandomUUID().toString();
       putRows(ht, 3, value, keyPrefix1);
       putRows(ht, 3, value, keyPrefix2);
       putRows(ht, 3, value, keyPrefix3);
@@ -430,103 +293,24 @@ public class TestFromClientSide {
       try (Table table = TEST_UTIL.getConnection().getTable(tableName)) {
         System.out.println("Checking values for key: " + keyPrefix1);
         assertEquals("Got back incorrect number of rows from scan", 3,
-                getNumberOfRows(keyPrefix1, value2, table));
+          getNumberOfRows(keyPrefix1, value2, table));
         System.out.println("Checking values for key: " + keyPrefix2);
         assertEquals("Got back incorrect number of rows from scan", 3,
-                getNumberOfRows(keyPrefix2, value2, table));
+          getNumberOfRows(keyPrefix2, value2, table));
         System.out.println("Checking values for key: " + keyPrefix3);
         assertEquals("Got back incorrect number of rows from scan", 3,
-                getNumberOfRows(keyPrefix3, value2, table));
+          getNumberOfRows(keyPrefix3, value2, table));
         deleteColumns(ht, value2, keyPrefix1);
         deleteColumns(ht, value2, keyPrefix2);
         deleteColumns(ht, value2, keyPrefix3);
         System.out.println("Starting important checks.....");
-        assertEquals("Got back incorrect number of rows from scan: " + keyPrefix1,
-                0, getNumberOfRows(keyPrefix1, value2, table));
-        assertEquals("Got back incorrect number of rows from scan: " + keyPrefix2,
-                0, getNumberOfRows(keyPrefix2, value2, table));
-        assertEquals("Got back incorrect number of rows from scan: " + keyPrefix3,
-                0, getNumberOfRows(keyPrefix3, value2, table));
-      }
-    }
-  }
-
-  private void deleteColumns(Table ht, String value, String keyPrefix)
-      throws IOException {
-    ResultScanner scanner = buildScanner(keyPrefix, value, ht);
-    Iterator<Result> it = scanner.iterator();
-    int count = 0;
-    while (it.hasNext()) {
-      Result result = it.next();
-      Delete delete = new Delete(result.getRow());
-      delete.addColumn(Bytes.toBytes("trans-tags"), Bytes.toBytes("qual2"));
-      ht.delete(delete);
-      count++;
-    }
-    assertEquals("Did not perform correct number of deletes", 3, count);
-  }
-
-  private int getNumberOfRows(String keyPrefix, String value, Table ht)
-      throws Exception {
-    ResultScanner resultScanner = buildScanner(keyPrefix, value, ht);
-    Iterator<Result> scanner = resultScanner.iterator();
-    int numberOfResults = 0;
-    while (scanner.hasNext()) {
-      Result result = scanner.next();
-      System.out.println("Got back key: " + Bytes.toString(result.getRow()));
-      for (Cell kv : result.rawCells()) {
-        System.out.println("kv=" + kv.toString() + ", "
-            + Bytes.toString(CellUtil.cloneValue(kv)));
+        assertEquals("Got back incorrect number of rows from scan: " + keyPrefix1, 0,
+          getNumberOfRows(keyPrefix1, value2, table));
+        assertEquals("Got back incorrect number of rows from scan: " + keyPrefix2, 0,
+          getNumberOfRows(keyPrefix2, value2, table));
+        assertEquals("Got back incorrect number of rows from scan: " + keyPrefix3, 0,
+          getNumberOfRows(keyPrefix3, value2, table));
       }
-      numberOfResults++;
-    }
-    return numberOfResults;
-  }
-
-  private ResultScanner buildScanner(String keyPrefix, String value, Table ht)
-      throws IOException {
-    // OurFilterList allFilters = new OurFilterList();
-    FilterList allFilters = new FilterList(/* FilterList.Operator.MUST_PASS_ALL */);
-    allFilters.addFilter(new PrefixFilter(Bytes.toBytes(keyPrefix)));
-    SingleColumnValueFilter filter = new SingleColumnValueFilter(Bytes
-        .toBytes("trans-tags"), Bytes.toBytes("qual2"), CompareOperator.EQUAL, Bytes
-        .toBytes(value));
-    filter.setFilterIfMissing(true);
-    allFilters.addFilter(filter);
-
-    // allFilters.addFilter(new
-    // RowExcludingSingleColumnValueFilter(Bytes.toBytes("trans-tags"),
-    // Bytes.toBytes("qual2"), CompareOp.EQUAL, Bytes.toBytes(value)));
-
-    Scan scan = new Scan();
-    scan.addFamily(Bytes.toBytes("trans-blob"));
-    scan.addFamily(Bytes.toBytes("trans-type"));
-    scan.addFamily(Bytes.toBytes("trans-date"));
-    scan.addFamily(Bytes.toBytes("trans-tags"));
-    scan.addFamily(Bytes.toBytes("trans-group"));
-    scan.setFilter(allFilters);
-
-    return ht.getScanner(scan);
-  }
-
-  private void putRows(Table ht, int numRows, String value, String key)
-      throws IOException {
-    for (int i = 0; i < numRows; i++) {
-      String row = key + "_" + TEST_UTIL.getRandomUUID().toString();
-      System.out.println(String.format("Saving row: %s, with value %s", row,
-          value));
-      Put put = new Put(Bytes.toBytes(row));
-      put.setDurability(Durability.SKIP_WAL);
-      put.addColumn(Bytes.toBytes("trans-blob"), null, Bytes
-          .toBytes("value for blob"));
-      put.addColumn(Bytes.toBytes("trans-type"), null, Bytes.toBytes("statement"));
-      put.addColumn(Bytes.toBytes("trans-date"), null, Bytes
-          .toBytes("20090921010101999"));
-      put.addColumn(Bytes.toBytes("trans-tags"), Bytes.toBytes("qual2"), Bytes
-          .toBytes(value));
-      put.addColumn(Bytes.toBytes("trans-group"), null, Bytes
-          .toBytes("adhocTransactionGroupId"));
-      ht.put(put);
     }
   }
 
@@ -535,9 +319,7 @@ public class TestFromClientSide {
    * logs to ensure that we're not scanning more regions that we're supposed to.
    * Related to the TestFilterAcrossRegions over in the o.a.h.h.filter package.
    */
-  @Test
-  public void testFilterAcrossMultipleRegions()
-      throws IOException, InterruptedException {
+  @Test public void testFilterAcrossMultipleRegions() throws IOException {
     final TableName tableName = name.getTableName();
     try (Table t = TEST_UTIL.createTable(tableName, FAMILY)) {
       int rowCount = TEST_UTIL.loadTable(t, FAMILY, false);
@@ -560,111 +342,36 @@ public class TestFromClientSide {
 
       // New test.  Make it so scan goes into next region by one and then two.
       // Make sure count comes out right.
-      byte[] key = new byte[]{endKey[0], endKey[1], (byte) (endKey[2] + 1)};
+      byte[] key = new byte[] { endKey[0], endKey[1], (byte) (endKey[2] + 1) };
       int plusOneCount = TEST_UTIL.countRows(t, createScanWithRowFilter(key));
       assertEquals(endKeyCount + 1, plusOneCount);
-      key = new byte[]{endKey[0], endKey[1], (byte) (endKey[2] + 2)};
+      key = new byte[] { endKey[0], endKey[1], (byte) (endKey[2] + 2) };
       int plusTwoCount = TEST_UTIL.countRows(t, createScanWithRowFilter(key));
       assertEquals(endKeyCount + 2, plusTwoCount);
 
       // New test.  Make it so I scan one less than endkey.
-      key = new byte[]{endKey[0], endKey[1], (byte) (endKey[2] - 1)};
+      key = new byte[] { endKey[0], endKey[1], (byte) (endKey[2] - 1) };
       int minusOneCount = TEST_UTIL.countRows(t, createScanWithRowFilter(key));
       assertEquals(endKeyCount - 1, minusOneCount);
       // For above test... study logs.  Make sure we do "Finished with scanning.."
       // in first region and that we do not fall into the next region.
 
-      key = new byte[]{'a', 'a', 'a'};
-      int countBBB = TEST_UTIL.countRows(t,
-              createScanWithRowFilter(key, null, CompareOperator.EQUAL));
+      key = new byte[] { 'a', 'a', 'a' };
+      int countBBB = TEST_UTIL.countRows(t, createScanWithRowFilter(key, null,
+        CompareOperator.EQUAL));
       assertEquals(1, countBBB);
 
       int countGreater = TEST_UTIL.countRows(t, createScanWithRowFilter(endKey, null,
-              CompareOperator.GREATER_OR_EQUAL));
+        CompareOperator.GREATER_OR_EQUAL));
       // Because started at start of table.
       assertEquals(0, countGreater);
       countGreater = TEST_UTIL.countRows(t, createScanWithRowFilter(endKey, endKey,
-              CompareOperator.GREATER_OR_EQUAL));
+        CompareOperator.GREATER_OR_EQUAL));
       assertEquals(rowCount - endKeyCount, countGreater);
     }
   }
 
-  /*
-   * @param key
-   * @return Scan with RowFilter that does LESS than passed key.
-   */
-  private Scan createScanWithRowFilter(final byte [] key) {
-    return createScanWithRowFilter(key, null, CompareOperator.LESS);
-  }
-
-  /*
-   * @param key
-   * @param op
-   * @param startRow
-   * @return Scan with RowFilter that does CompareOp op on passed key.
-   */
-  private Scan createScanWithRowFilter(final byte [] key,
-      final byte [] startRow, CompareOperator op) {
-    // Make sure key is of some substance... non-null and > than first key.
-    assertTrue(key != null && key.length > 0 &&
-      Bytes.BYTES_COMPARATOR.compare(key, new byte [] {'a', 'a', 'a'}) >= 0);
-    LOG.info("Key=" + Bytes.toString(key));
-    Scan s = startRow == null? new Scan(): new Scan(startRow);
-    Filter f = new RowFilter(op, new BinaryComparator(key));
-    f = new WhileMatchFilter(f);
-    s.setFilter(f);
-    return s;
-  }
-
-  private void assertRowCount(final Table t, final int expected)
-  throws IOException {
-    assertEquals(expected, TEST_UTIL.countRows(t, new Scan()));
-  }
-
-  /*
-   * Split table into multiple regions.
-   * @param t Table to split.
-   * @return Map of regions to servers.
-   * @throws IOException
-   */
-  private List<HRegionLocation> splitTable(final Table t) throws IOException {
-    // Split this table in two.
-    try (Admin admin = TEST_UTIL.getAdmin()) {
-      admin.split(t.getName());
-    }
-    List<HRegionLocation> regions = waitOnSplit(t);
-    assertTrue(regions.size() > 1);
-    return regions;
-  }
-
-  /*
-   * Wait on table split.  May return because we waited long enough on the split
-   * and it didn't happen.  Caller should check.
-   * @param t
-   * @return Map of table regions; caller needs to check table actually split.
-   */
-  private List<HRegionLocation> waitOnSplit(final Table t) throws IOException {
-    try (RegionLocator locator = TEST_UTIL.getConnection().getRegionLocator(t.getName())) {
-      List<HRegionLocation> regions = locator.getAllRegionLocations();
-      int originalCount = regions.size();
-      for (int i = 0; i < TEST_UTIL.getConfiguration().getInt("hbase.test.retries", 30); i++) {
-        Thread.currentThread();
-        try {
-          Thread.sleep(1000);
-        } catch (InterruptedException e) {
-          e.printStackTrace();
-        }
-        regions = locator.getAllRegionLocations();
-        if (regions.size() > originalCount) {
-          break;
-        }
-      }
-      return regions;
-    }
-  }
-
-  @Test
-  public void testSuperSimple() throws Exception {
+  @Test public void testSuperSimple() throws Exception {
     final TableName tableName = name.getTableName();
     try (Table ht = TEST_UTIL.createTable(tableName, FAMILY)) {
       Put put = new Put(ROW);
@@ -674,13 +381,12 @@ public class TestFromClientSide {
       scan.addColumn(FAMILY, tableName.toBytes());
       try (ResultScanner scanner = ht.getScanner(scan)) {
         Result result = scanner.next();
-        assertTrue("Expected null result", result == null);
+        assertNull("Expected null result", result);
       }
     }
   }
 
-  @Test
-  public void testMaxKeyValueSize() throws Exception {
+  @Test public void testMaxKeyValueSize() throws Exception {
     final TableName tableName = name.getTableName();
     Configuration conf = TEST_UTIL.getConfiguration();
     String oldMaxSize = conf.get(ConnectionConfiguration.MAX_KEYVALUE_SIZE_KEY);
@@ -691,11 +397,11 @@ public class TestFromClientSide {
       ht.put(put);
 
       try {
-        TEST_UTIL.getConfiguration().setInt(
-                ConnectionConfiguration.MAX_KEYVALUE_SIZE_KEY, 2 * 1024 * 1024);
+        TEST_UTIL.getConfiguration()
+          .setInt(ConnectionConfiguration.MAX_KEYVALUE_SIZE_KEY, 2 * 1024 * 1024);
         // Create new table so we pick up the change in Configuration.
         try (Connection connection =
-                     ConnectionFactory.createConnection(TEST_UTIL.getConfiguration())) {
+            ConnectionFactory.createConnection(TEST_UTIL.getConfiguration())) {
           try (Table t = connection.getTable(TableName.valueOf(FAMILY))) {
             put = new Put(ROW);
             put.addColumn(FAMILY, QUALIFIER, value);
@@ -703,24 +409,22 @@ public class TestFromClientSide {
           }
         }
         fail("Inserting a too large KeyValue worked, should throw exception");
-      } catch (Exception e) {
+      } catch (Exception ignored) {
       }
     }
     conf.set(ConnectionConfiguration.MAX_KEYVALUE_SIZE_KEY, oldMaxSize);
   }
 
-  @Test
-  public void testFilters() throws Exception {
+  @Test public void testFilters() throws Exception {
     final TableName tableName = name.getTableName();
     try (Table ht = TEST_UTIL.createTable(tableName, FAMILY)) {
       byte[][] ROWS = makeN(ROW, 10);
-      byte[][] QUALIFIERS = {
-              Bytes.toBytes("col0-<d2v1>-<d3v2>"), Bytes.toBytes("col1-<d2v1>-<d3v2>"),
-              Bytes.toBytes("col2-<d2v1>-<d3v2>"), Bytes.toBytes("col3-<d2v1>-<d3v2>"),
-              Bytes.toBytes("col4-<d2v1>-<d3v2>"), Bytes.toBytes("col5-<d2v1>-<d3v2>"),
-              Bytes.toBytes("col6-<d2v1>-<d3v2>"), Bytes.toBytes("col7-<d2v1>-<d3v2>"),
-              Bytes.toBytes("col8-<d2v1>-<d3v2>"), Bytes.toBytes("col9-<d2v1>-<d3v2>")
-      };
+      byte[][] QUALIFIERS =
+        { Bytes.toBytes("col0-<d2v1>-<d3v2>"), Bytes.toBytes("col1-<d2v1>-<d3v2>"),
+          Bytes.toBytes("col2-<d2v1>-<d3v2>"), Bytes.toBytes("col3-<d2v1>-<d3v2>"),
+          Bytes.toBytes("col4-<d2v1>-<d3v2>"), Bytes.toBytes("col5-<d2v1>-<d3v2>"),
+          Bytes.toBytes("col6-<d2v1>-<d3v2>"), Bytes.toBytes("col7-<d2v1>-<d3v2>"),
+          Bytes.toBytes("col8-<d2v1>-<d3v2>"), Bytes.toBytes("col9-<d2v1>-<d3v2>") };
       for (int i = 0; i < 10; i++) {
         Put put = new Put(ROWS[i]);
         put.setDurability(Durability.SKIP_WAL);
@@ -730,7 +434,7 @@ public class TestFromClientSide {
       Scan scan = new Scan();
       scan.addFamily(FAMILY);
       Filter filter = new QualifierFilter(CompareOperator.EQUAL,
-              new RegexStringComparator("col[1-5]"));
+        new RegexStringComparator("col[1-5]"));
       scan.setFilter(filter);
       try (ResultScanner scanner = ht.getScanner(scan)) {
         int expectedIndex = 1;
@@ -738,7 +442,7 @@ public class TestFromClientSide {
           assertEquals(1, result.size());
           assertTrue(Bytes.equals(CellUtil.cloneRow(result.rawCells()[0]), ROWS[expectedIndex]));
           assertTrue(Bytes.equals(CellUtil.cloneQualifier(result.rawCells()[0]),
-                  QUALIFIERS[expectedIndex]));
+            QUALIFIERS[expectedIndex]));
           expectedIndex++;
         }
         assertEquals(6, expectedIndex);
@@ -746,8 +450,7 @@ public class TestFromClientSide {
     }
   }
 
-  @Test
-  public void testFilterWithLongCompartor() throws Exception {
+  @Test public void testFilterWithLongCompartor() throws Exception {
     final TableName tableName = name.getTableName();
     try (Table ht = TEST_UTIL.createTable(tableName, FAMILY)) {
       byte[][] ROWS = makeN(ROW, 10);
@@ -764,7 +467,7 @@ public class TestFromClientSide {
       Scan scan = new Scan();
       scan.addFamily(FAMILY);
       Filter filter = new SingleColumnValueFilter(FAMILY, QUALIFIER, CompareOperator.GREATER,
-              new LongComparator(500));
+        new LongComparator(500));
       scan.setFilter(filter);
       try (ResultScanner scanner = ht.getScanner(scan)) {
         int expectedIndex = 0;
@@ -778,18 +481,16 @@ public class TestFromClientSide {
     }
   }
 
-  @Test
-  public void testKeyOnlyFilter() throws Exception {
+  @Test public void testKeyOnlyFilter() throws Exception {
     final TableName tableName = name.getTableName();
     try (Table ht = TEST_UTIL.createTable(tableName, FAMILY)) {
       byte[][] ROWS = makeN(ROW, 10);
-      byte[][] QUALIFIERS = {
-              Bytes.toBytes("col0-<d2v1>-<d3v2>"), Bytes.toBytes("col1-<d2v1>-<d3v2>"),
-              Bytes.toBytes("col2-<d2v1>-<d3v2>"), Bytes.toBytes("col3-<d2v1>-<d3v2>"),
-              Bytes.toBytes("col4-<d2v1>-<d3v2>"), Bytes.toBytes("col5-<d2v1>-<d3v2>"),
-              Bytes.toBytes("col6-<d2v1>-<d3v2>"), Bytes.toBytes("col7-<d2v1>-<d3v2>"),
-              Bytes.toBytes("col8-<d2v1>-<d3v2>"), Bytes.toBytes("col9-<d2v1>-<d3v2>")
-      };
+      byte[][] QUALIFIERS =
+        { Bytes.toBytes("col0-<d2v1>-<d3v2>"), Bytes.toBytes("col1-<d2v1>-<d3v2>"),
+          Bytes.toBytes("col2-<d2v1>-<d3v2>"), Bytes.toBytes("col3-<d2v1>-<d3v2>"),
+          Bytes.toBytes("col4-<d2v1>-<d3v2>"), Bytes.toBytes("col5-<d2v1>-<d3v2>"),
+          Bytes.toBytes("col6-<d2v1>-<d3v2>"), Bytes.toBytes("col7-<d2v1>-<d3v2>"),
+          Bytes.toBytes("col8-<d2v1>-<d3v2>"), Bytes.toBytes("col9-<d2v1>-<d3v2>") };
       for (int i = 0; i < 10; i++) {
         Put put = new Put(ROWS[i]);
         put.setDurability(Durability.SKIP_WAL);
@@ -816,8 +517,7 @@ public class TestFromClientSide {
   /**
    * Test simple table and non-existent row cases.
    */
-  @Test
-  public void testSimpleMissing() throws Exception {
+  @Test public void testSimpleMissing() throws Exception {
     final TableName tableName = name.getTableName();
     try (Table ht = TEST_UTIL.createTable(tableName, FAMILY)) {
       byte[][] ROWS = makeN(ROW, 4);
@@ -927,27 +627,20 @@ public class TestFromClientSide {
    * Test basic puts, gets, scans, and deletes for a single row
    * in a multiple family table.
    */
-  @Test
-  public void testSingleRowMultipleFamily() throws Exception {
+  @SuppressWarnings("checkstyle:MethodLength") @Test public void testSingleRowMultipleFamily()
+    throws Exception {
     final TableName tableName = name.getTableName();
-    byte [][] ROWS = makeN(ROW, 3);
-    byte [][] FAMILIES = makeNAscii(FAMILY, 10);
-    byte [][] QUALIFIERS = makeN(QUALIFIER, 10);
-    byte [][] VALUES = makeN(VALUE, 10);
+    byte[][] ROWS = makeN(ROW, 3);
+    byte[][] FAMILIES = makeNAscii(FAMILY, 10);
+    byte[][] QUALIFIERS = makeN(QUALIFIER, 10);
+    byte[][] VALUES = makeN(VALUE, 10);
 
     try (Table ht = TEST_UTIL.createTable(tableName, FAMILIES)) {
-
-      Get get;
-      Scan scan;
-      Delete delete;
-      Put put;
-      Result result;
-
       ////////////////////////////////////////////////////////////////////////////
       // Insert one column to one family
       ////////////////////////////////////////////////////////////////////////////
 
-      put = new Put(ROWS[0]);
+      Put put = new Put(ROWS[0]);
       put.addColumn(FAMILIES[4], QUALIFIERS[0], VALUES[0]);
       ht.put(put);
 
@@ -1017,18 +710,18 @@ public class TestFromClientSide {
       ////////////////////////////////////////////////////////////////////////////
       // Delete a storefile column
       ////////////////////////////////////////////////////////////////////////////
-      delete = new Delete(ROWS[0]);
+      Delete delete = new Delete(ROWS[0]);
       delete.addColumns(FAMILIES[6], QUALIFIERS[7]);
       ht.delete(delete);
 
       // Try to get deleted column
-      get = new Get(ROWS[0]);
+      Get get = new Get(ROWS[0]);
       get.addColumn(FAMILIES[6], QUALIFIERS[7]);
-      result = ht.get(get);
+      Result result = ht.get(get);
       assertEmptyResult(result);
 
       // Try to scan deleted column
-      scan = new Scan();
+      Scan scan = new Scan();
       scan.addColumn(FAMILIES[6], QUALIFIERS[7]);
       result = getSingleScanResult(ht, scan);
       assertNullResult(result);
@@ -1169,83 +862,55 @@ public class TestFromClientSide {
       TEST_UTIL.flush();
 
       // Try to get storefile column in deleted family
-      get = new Get(ROWS[0]);
-      get.addColumn(FAMILIES[4], QUALIFIERS[4]);
-      result = ht.get(get);
-      assertEmptyResult(result);
+      assertEmptyResult(ht.get(new Get(ROWS[0]).addColumn(FAMILIES[4], QUALIFIERS[4])));
 
       // Try to get memstore column in deleted family
-      get = new Get(ROWS[0]);
-      get.addColumn(FAMILIES[4], QUALIFIERS[3]);
-      result = ht.get(get);
-      assertEmptyResult(result);
+      assertEmptyResult(ht.get(new Get(ROWS[0]).addColumn(FAMILIES[4], QUALIFIERS[3])));
 
       // Try to get deleted family
-      get = new Get(ROWS[0]);
-      get.addFamily(FAMILIES[4]);
-      result = ht.get(get);
-      assertEmptyResult(result);
+      assertEmptyResult(ht.get(new Get(ROWS[0]).addFamily(FAMILIES[4])));
 
       // Try to scan storefile column in deleted family
-      scan = new Scan();
-      scan.addColumn(FAMILIES[4], QUALIFIERS[4]);
-      result = getSingleScanResult(ht, scan);
-      assertNullResult(result);
+      assertNullResult(getSingleScanResult(ht, new Scan().addColumn(FAMILIES[4], QUALIFIERS[4])));
 
       // Try to scan memstore column in deleted family
-      scan = new Scan();
-      scan.addColumn(FAMILIES[4], QUALIFIERS[3]);
-      result = getSingleScanResult(ht, scan);
-      assertNullResult(result);
+      assertNullResult(getSingleScanResult(ht, new Scan().addColumn(FAMILIES[4], QUALIFIERS[3])));
 
       // Try to scan deleted family
-      scan = new Scan();
-      scan.addFamily(FAMILIES[4]);
-      result = getSingleScanResult(ht, scan);
-      assertNullResult(result);
+      assertNullResult(getSingleScanResult(ht, new Scan().addFamily(FAMILIES[4])));
 
       // Make sure we can still get another family
-      get = new Get(ROWS[0]);
-      get.addColumn(FAMILIES[2], QUALIFIERS[2]);
-      result = ht.get(get);
-      assertSingleResult(result, ROWS[0], FAMILIES[2], QUALIFIERS[2], VALUES[2]);
+      assertSingleResult(ht.get(new Get(ROWS[0]).addColumn(FAMILIES[2], QUALIFIERS[2])), ROWS[0],
+        FAMILIES[2], QUALIFIERS[2], VALUES[2]);
 
-      get = new Get(ROWS[0]);
-      get.addColumn(FAMILIES[6], QUALIFIERS[9]);
-      result = ht.get(get);
-      assertSingleResult(result, ROWS[0], FAMILIES[6], QUALIFIERS[9], VALUES[9]);
+      assertSingleResult(ht.get(new Get(ROWS[0]).addColumn(FAMILIES[6], QUALIFIERS[9])), ROWS[0],
+        FAMILIES[6], QUALIFIERS[9], VALUES[9]);
 
       // Make sure we can still scan another family
-      scan = new Scan();
-      scan.addColumn(FAMILIES[6], QUALIFIERS[6]);
-      result = getSingleScanResult(ht, scan);
-      assertSingleResult(result, ROWS[0], FAMILIES[6], QUALIFIERS[6], VALUES[6]);
+      assertSingleResult(getSingleScanResult(ht, new Scan().addColumn(FAMILIES[6], QUALIFIERS[6])),
+        ROWS[0], FAMILIES[6], QUALIFIERS[6], VALUES[6]);
 
-      scan = new Scan();
-      scan.addColumn(FAMILIES[6], QUALIFIERS[9]);
-      result = getSingleScanResult(ht, scan);
-      assertSingleResult(result, ROWS[0], FAMILIES[6], QUALIFIERS[9], VALUES[9]);
+      assertSingleResult(getSingleScanResult(ht, new Scan().addColumn(FAMILIES[6], QUALIFIERS[9])),
+        ROWS[0], FAMILIES[6], QUALIFIERS[9], VALUES[9]);
     }
   }
 
-  @Test(expected = NullPointerException.class)
-  public void testNullTableName() throws IOException {
+  @Test(expected = NullPointerException.class) public void testNullTableName() throws IOException {
     // Null table name (should NOT work)
-    TEST_UTIL.createTable((TableName)null, FAMILY);
+    TEST_UTIL.createTable(null, FAMILY);
     fail("Creating a table with null name passed, should have failed");
   }
 
-  @Test(expected = IllegalArgumentException.class)
-  public void testNullFamilyName() throws IOException {
+  @Test(expected = IllegalArgumentException.class) public void testNullFamilyName()
+    throws IOException {
     final TableName tableName = name.getTableName();
 
     // Null family (should NOT work)
-    TEST_UTIL.createTable(tableName, new byte[][]{null});
+    TEST_UTIL.createTable(tableName, new byte[][] { null });
     fail("Creating a table with a null family passed, should fail");
   }
 
-  @Test
-  public void testNullRowAndQualifier() throws Exception {
+  @Test public void testNullRowAndQualifier() throws Exception {
     final TableName tableName = name.getTableName();
 
     try (Table ht = TEST_UTIL.createTable(tableName, FAMILY)) {
@@ -1256,7 +921,7 @@ public class TestFromClientSide {
         put.addColumn(FAMILY, QUALIFIER, VALUE);
         ht.put(put);
         fail("Inserting a null row worked, should throw exception");
-      } catch (Exception e) {
+      } catch (Exception ignored) {
       }
 
       // Null qualifier (should work)
@@ -1280,8 +945,7 @@ public class TestFromClientSide {
     }
   }
 
-  @Test
-  public void testNullEmptyQualifier() throws Exception {
+  @Test public void testNullEmptyQualifier() throws Exception {
     final TableName tableName = name.getTableName();
 
     try (Table ht = TEST_UTIL.createTable(tableName, FAMILY)) {
@@ -1318,8 +982,7 @@ public class TestFromClientSide {
     }
   }
 
-  @Test
-  public void testNullValue() throws IOException {
+  @Test public void testNullValue() throws IOException {
     final TableName tableName = name.getTableName();
 
     try (Table ht = TEST_UTIL.createTable(tableName, FAMILY)) {
@@ -1353,8 +1016,7 @@ public class TestFromClientSide {
     }
   }
 
-  @Test
-  public void testNullQualifier() throws Exception {
+  @Test public void testNullQualifier() throws Exception {
     final TableName tableName = name.getTableName();
     try (Table table = TEST_UTIL.createTable(tableName, FAMILY)) {
 
@@ -1406,17 +1068,16 @@ public class TestFromClientSide {
 
       delete = new Delete(ROW);
       delete.addColumns(FAMILY, null);
-      table.checkAndMutate(ROW, FAMILY).ifEquals(Bytes.toBytes("checkAndMutate"))
-              .thenDelete(delete);
+      table.checkAndMutate(ROW, FAMILY).
+        ifEquals(Bytes.toBytes("checkAndMutate")).thenDelete(delete);
     }
   }
 
-  @Test
-  public void testVersions() throws Exception {
+  @Test @SuppressWarnings("checkstyle:MethodLength") public void testVersions() throws Exception {
     final TableName tableName = name.getTableName();
 
-    long [] STAMPS = makeStamps(20);
-    byte [][] VALUES = makeNAscii(VALUE, 20);
+    long[] STAMPS = makeStamps(20);
+    byte[][] VALUES = makeNAscii(VALUE, 20);
 
     try (Table ht = TEST_UTIL.createTable(tableName, FAMILY, 10)) {
 
@@ -1451,19 +1112,15 @@ public class TestFromClientSide {
       get.addColumn(FAMILY, QUALIFIER);
       get.setMaxVersions(2);
       Result result = ht.get(get);
-      assertNResult(result, ROW, FAMILY, QUALIFIER,
-          new long [] {STAMPS[4], STAMPS[5]},
-          new byte[][] {VALUES[4], VALUES[5]},
-          0, 1);
+      assertNResult(result, ROW, FAMILY, QUALIFIER, new long[] { STAMPS[4], STAMPS[5] },
+        new byte[][] { VALUES[4], VALUES[5] }, 0, 1);
 
       Scan scan = new Scan(ROW);
       scan.addColumn(FAMILY, QUALIFIER);
       scan.setMaxVersions(2);
       result = getSingleScanResult(ht, scan);
-      assertNResult(result, ROW, FAMILY, QUALIFIER,
-              new long[]{STAMPS[4], STAMPS[5]},
-              new byte[][]{VALUES[4], VALUES[5]},
-              0, 1);
+      assertNResult(result, ROW, FAMILY, QUALIFIER, new long[] { STAMPS[4], STAMPS[5] },
+        new byte[][] { VALUES[4], VALUES[5] }, 0, 1);
 
       // Flush and redo
 
@@ -1492,20 +1149,15 @@ public class TestFromClientSide {
       get.addColumn(FAMILY, QUALIFIER);
       get.setMaxVersions(2);
       result = ht.get(get);
-      assertNResult(result, ROW, FAMILY, QUALIFIER,
-          new long [] {STAMPS[4], STAMPS[5]},
-          new byte[][] {VALUES[4], VALUES[5]},
-          0, 1);
+      assertNResult(result, ROW, FAMILY, QUALIFIER, new long[] { STAMPS[4], STAMPS[5] },
+        new byte[][] { VALUES[4], VALUES[5] }, 0, 1);
 
       scan = new Scan(ROW);
       scan.addColumn(FAMILY, QUALIFIER);
       scan.setMaxVersions(2);
       result = getSingleScanResult(ht, scan);
-      assertNResult(result, ROW, FAMILY, QUALIFIER,
-              new long[]{STAMPS[4], STAMPS[5]},
-              new byte[][]{VALUES[4], VALUES[5]},
-              0, 1);
-
+      assertNResult(result, ROW, FAMILY, QUALIFIER, new long[] { STAMPS[4], STAMPS[5] },
+        new byte[][] { VALUES[4], VALUES[5] }, 0, 1);
 
       // Add some memstore and retest
 
@@ -1523,40 +1175,38 @@ public class TestFromClientSide {
       get.setMaxVersions();
       result = ht.get(get);
       assertNResult(result, ROW, FAMILY, QUALIFIER,
-          new long [] {STAMPS[1], STAMPS[2], STAMPS[3], STAMPS[4], STAMPS[5], STAMPS[6], STAMPS[7],
-                  STAMPS[8]},
-          new byte[][] {VALUES[1], VALUES[2], VALUES[3], VALUES[4], VALUES[5], VALUES[6], VALUES[7],
-                  VALUES[8]},
-          0, 7);
+        new long[] { STAMPS[1], STAMPS[2], STAMPS[3], STAMPS[4], STAMPS[5], STAMPS[6], STAMPS[7],
+          STAMPS[8] },
+        new byte[][] { VALUES[1], VALUES[2], VALUES[3], VALUES[4], VALUES[5], VALUES[6], VALUES[7],
+          VALUES[8] }, 0, 7);
 
       scan = new Scan(ROW);
       scan.addColumn(FAMILY, QUALIFIER);
       scan.setMaxVersions();
       result = getSingleScanResult(ht, scan);
       assertNResult(result, ROW, FAMILY, QUALIFIER,
-          new long[]{STAMPS[1], STAMPS[2], STAMPS[3], STAMPS[4], STAMPS[5], STAMPS[6], STAMPS[7],
-                  STAMPS[8]},
-          new byte[][]{VALUES[1], VALUES[2], VALUES[3], VALUES[4], VALUES[5], VALUES[6], VALUES[7],
-                  VALUES[8]},0, 7);
+        new long[] { STAMPS[1], STAMPS[2], STAMPS[3], STAMPS[4], STAMPS[5], STAMPS[6], STAMPS[7],
+          STAMPS[8] },
+        new byte[][] { VALUES[1], VALUES[2], VALUES[3], VALUES[4], VALUES[5], VALUES[6], VALUES[7],
+          VALUES[8] }, 0, 7);
 
       get = new Get(ROW);
       get.setMaxVersions();
       result = ht.get(get);
       assertNResult(result, ROW, FAMILY, QUALIFIER,
-          new long [] {STAMPS[1], STAMPS[2], STAMPS[3], STAMPS[4], STAMPS[5], STAMPS[6], STAMPS[7],
-                  STAMPS[8]},
-          new byte[][] {VALUES[1], VALUES[2], VALUES[3], VALUES[4], VALUES[5], VALUES[6], VALUES[7],
-                  VALUES[8]},
-          0, 7);
+        new long[] { STAMPS[1], STAMPS[2], STAMPS[3], STAMPS[4], STAMPS[5], STAMPS[6], STAMPS[7],
+          STAMPS[8] },
+        new byte[][] { VALUES[1], VALUES[2], VALUES[3], VALUES[4], VALUES[5], VALUES[6], VALUES[7],
+          VALUES[8] }, 0, 7);
 
       scan = new Scan(ROW);
       scan.setMaxVersions();
       result = getSingleScanResult(ht, scan);
       assertNResult(result, ROW, FAMILY, QUALIFIER,
-          new long[]{STAMPS[1], STAMPS[2], STAMPS[3], STAMPS[4], STAMPS[5], STAMPS[6], STAMPS[7],
-                  STAMPS[8]},
-          new byte[][]{VALUES[1], VALUES[2], VALUES[3], VALUES[4], VALUES[5], VALUES[6], VALUES[7],
-                  VALUES[8]},0, 7);
+        new long[] { STAMPS[1], STAMPS[2], STAMPS[3], STAMPS[4], STAMPS[5], STAMPS[6], STAMPS[7],
+          STAMPS[8] },
+        new byte[][] { VALUES[1], VALUES[2], VALUES[3], VALUES[4], VALUES[5], VALUES[6], VALUES[7],
+          VALUES[8] }, 0, 7);
 
       // Verify we can get each one properly
       getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[1], VALUES[1]);
@@ -1591,21 +1241,20 @@ public class TestFromClientSide {
       get.setMaxVersions(Integer.MAX_VALUE);
       result = ht.get(get);
       assertNResult(result, ROW, FAMILY, QUALIFIER,
-          new long [] {STAMPS[3], STAMPS[4], STAMPS[5], STAMPS[6], STAMPS[7], STAMPS[8], STAMPS[9],
-                  STAMPS[11], STAMPS[13], STAMPS[15]},
-          new byte[][] {VALUES[3], VALUES[4], VALUES[5], VALUES[6], VALUES[7], VALUES[8], VALUES[9],
-                  VALUES[11], VALUES[13], VALUES[15]},
-          0, 9);
+        new long[] { STAMPS[3], STAMPS[4], STAMPS[5], STAMPS[6], STAMPS[7], STAMPS[8], STAMPS[9],
+          STAMPS[11], STAMPS[13], STAMPS[15] },
+        new byte[][] { VALUES[3], VALUES[4], VALUES[5], VALUES[6], VALUES[7], VALUES[8], VALUES[9],
+          VALUES[11], VALUES[13], VALUES[15] }, 0, 9);
 
       scan = new Scan(ROW);
       scan.addColumn(FAMILY, QUALIFIER);
       scan.setMaxVersions(Integer.MAX_VALUE);
       result = getSingleScanResult(ht, scan);
       assertNResult(result, ROW, FAMILY, QUALIFIER,
-          new long[]{STAMPS[3], STAMPS[4], STAMPS[5], STAMPS[6], STAMPS[7], STAMPS[8], STAMPS[9],
-                  STAMPS[11], STAMPS[13], STAMPS[15]},
-          new byte[][]{VALUES[3], VALUES[4], VALUES[5], VALUES[6], VALUES[7], VALUES[8], VALUES[9],
-                  VALUES[11], VALUES[13], VALUES[15]},0, 9);
+        new long[] { STAMPS[3], STAMPS[4], STAMPS[5], STAMPS[6], STAMPS[7], STAMPS[8], STAMPS[9],
+          STAMPS[11], STAMPS[13], STAMPS[15] },
+        new byte[][] { VALUES[3], VALUES[4], VALUES[5], VALUES[6], VALUES[7], VALUES[8], VALUES[9],
+          VALUES[11], VALUES[13], VALUES[15] }, 0, 9);
 
       // Delete a version in the memstore and a version in a storefile
       Delete delete = new Delete(ROW);
@@ -1619,32 +1268,30 @@ public class TestFromClientSide {
       get.setMaxVersions(Integer.MAX_VALUE);
       result = ht.get(get);
       assertNResult(result, ROW, FAMILY, QUALIFIER,
-          new long [] {STAMPS[1], STAMPS[2], STAMPS[3], STAMPS[4], STAMPS[5], STAMPS[6], STAMPS[8],
-                  STAMPS[9], STAMPS[13], STAMPS[15]},
-          new byte[][] {VALUES[1], VALUES[2], VALUES[3], VALUES[4], VALUES[5], VALUES[6], VALUES[8],
-                  VALUES[9], VALUES[13], VALUES[15]},
-          0, 9);
+        new long[] { STAMPS[1], STAMPS[2], STAMPS[3], STAMPS[4], STAMPS[5], STAMPS[6], STAMPS[8],
+          STAMPS[9], STAMPS[13], STAMPS[15] },
+        new byte[][] { VALUES[1], VALUES[2], VALUES[3], VALUES[4], VALUES[5], VALUES[6], VALUES[8],
+          VALUES[9], VALUES[13], VALUES[15] }, 0, 9);
 
       scan = new Scan(ROW);
       scan.addColumn(FAMILY, QUALIFIER);
       scan.setMaxVersions(Integer.MAX_VALUE);
       result = getSingleScanResult(ht, scan);
       assertNResult(result, ROW, FAMILY, QUALIFIER,
-          new long[]{STAMPS[1], STAMPS[2], STAMPS[3], STAMPS[4], STAMPS[5], STAMPS[6], STAMPS[8],
-                  STAMPS[9], STAMPS[13], STAMPS[15]},
-          new byte[][]{VALUES[1], VALUES[2], VALUES[3], VALUES[4], VALUES[5], VALUES[6], VALUES[8],
-                  VALUES[9], VALUES[13], VALUES[15]},0, 9);
+        new long[] { STAMPS[1], STAMPS[2], STAMPS[3], STAMPS[4], STAMPS[5], STAMPS[6], STAMPS[8],
+          STAMPS[9], STAMPS[13], STAMPS[15] },
+        new byte[][] { VALUES[1], VALUES[2], VALUES[3], VALUES[4], VALUES[5], VALUES[6], VALUES[8],
+          VALUES[9], VALUES[13], VALUES[15] }, 0, 9);
     }
   }
 
-  @Test
-  @SuppressWarnings("checkstyle:MethodLength")
-  public void testVersionLimits() throws Exception {
+  @Test @SuppressWarnings("checkstyle:MethodLength") public void testVersionLimits()
+    throws Exception {
     final TableName tableName = name.getTableName();
-    byte [][] FAMILIES = makeNAscii(FAMILY, 3);
-    int [] LIMITS = {1,3,5};
-    long [] STAMPS = makeStamps(10);
-    byte [][] VALUES = makeNAscii(VALUE, 10);
+    byte[][] FAMILIES = makeNAscii(FAMILY, 3);
+    int[] LIMITS = { 1, 3, 5 };
+    long[] STAMPS = makeStamps(10);
+    byte[][] VALUES = makeNAscii(VALUE, 10);
     try (Table ht = TEST_UTIL.createTable(tableName, FAMILIES, LIMITS)) {
 
       // Insert limit + 1 on each family
@@ -1672,37 +1319,29 @@ public class TestFromClientSide {
       get.addColumn(FAMILIES[0], QUALIFIER);
       get.setMaxVersions(Integer.MAX_VALUE);
       Result result = ht.get(get);
-      assertNResult(result, ROW, FAMILIES[0], QUALIFIER,
-          new long [] {STAMPS[1]},
-          new byte[][] {VALUES[1]},
-          0, 0);
+      assertNResult(result, ROW, FAMILIES[0], QUALIFIER, new long[] { STAMPS[1] },
+        new byte[][] { VALUES[1] }, 0, 0);
 
       get = new Get(ROW);
       get.addFamily(FAMILIES[0]);
       get.setMaxVersions(Integer.MAX_VALUE);
       result = ht.get(get);
-      assertNResult(result, ROW, FAMILIES[0], QUALIFIER,
-          new long [] {STAMPS[1]},
-          new byte[][] {VALUES[1]},
-          0, 0);
+      assertNResult(result, ROW, FAMILIES[0], QUALIFIER, new long[] { STAMPS[1] },
+        new byte[][] { VALUES[1] }, 0, 0);
 
       Scan scan = new Scan(ROW);
       scan.addColumn(FAMILIES[0], QUALIFIER);
       scan.setMaxVersions(Integer.MAX_VALUE);
       result = getSingleScanResult(ht, scan);
-      assertNResult(result, ROW, FAMILIES[0], QUALIFIER,
-              new long[]{STAMPS[1]},
-              new byte[][]{VALUES[1]},
-              0, 0);
+      assertNResult(result, ROW, FAMILIES[0], QUALIFIER, new long[] { STAMPS[1] },
+        new byte[][] { VALUES[1] }, 0, 0);
 
       scan = new Scan(ROW);
       scan.addFamily(FAMILIES[0]);
       scan.setMaxVersions(Integer.MAX_VALUE);
       result = getSingleScanResult(ht, scan);
-      assertNResult(result, ROW, FAMILIES[0], QUALIFIER,
-              new long[]{STAMPS[1]},
-              new byte[][]{VALUES[1]},
-              0, 0);
+      assertNResult(result, ROW, FAMILIES[0], QUALIFIER, new long[] { STAMPS[1] },
+        new byte[][] { VALUES[1] }, 0, 0);
 
       // Family1
 
@@ -1711,36 +1350,32 @@ public class TestFromClientSide {
       get.setMaxVersions(Integer.MAX_VALUE);
       result = ht.get(get);
       assertNResult(result, ROW, FAMILIES[1], QUALIFIER,
-          new long [] {STAMPS[1], STAMPS[2], STAMPS[3]},
-          new byte[][] {VALUES[1], VALUES[2], VALUES[3]},
-          0, 2);
+        new long[] { STAMPS[1], STAMPS[2], STAMPS[3] },
+        new byte[][] { VALUES[1], VALUES[2], VALUES[3] }, 0, 2);
 
       get = new Get(ROW);
       get.addFamily(FAMILIES[1]);
       get.setMaxVersions(Integer.MAX_VALUE);
       result = ht.get(get);
       assertNResult(result, ROW, FAMILIES[1], QUALIFIER,
-          new long [] {STAMPS[1], STAMPS[2], STAMPS[3]},
-          new byte[][] {VALUES[1], VALUES[2], VALUES[3]},
-          0, 2);
+        new long[] { STAMPS[1], STAMPS[2], STAMPS[3] },
+        new byte[][] { VALUES[1], VALUES[2], VALUES[3] }, 0, 2);
 
       scan = new Scan(ROW);
       scan.addColumn(FAMILIES[1], QUALIFIER);
       scan.setMaxVersions(Integer.MAX_VALUE);
       result = getSingleScanResult(ht, scan);
       assertNResult(result, ROW, FAMILIES[1], QUALIFIER,
-              new long[]{STAMPS[1], STAMPS[2], STAMPS[3]},
-              new byte[][]{VALUES[1], VALUES[2], VALUES[3]},
-              0, 2);
+        new long[] { STAMPS[1], STAMPS[2], STAMPS[3] },
+        new byte[][] { VALUES[1], VALUES[2], VALUES[3] }, 0, 2);
 
       scan = new Scan(ROW);
       scan.addFamily(FAMILIES[1]);
       scan.setMaxVersions(Integer.MAX_VALUE);
       result = getSingleScanResult(ht, scan);
       assertNResult(result, ROW, FAMILIES[1], QUALIFIER,
-              new long[]{STAMPS[1], STAMPS[2], STAMPS[3]},
-              new byte[][]{VALUES[1], VALUES[2], VALUES[3]},
-              0, 2);
+        new long[] { STAMPS[1], STAMPS[2], STAMPS[3] },
+        new byte[][] { VALUES[1], VALUES[2], VALUES[3] }, 0, 2);
 
       // Family2
 
@@ -1749,44 +1384,39 @@ public class TestFromClientSide {
       get.setMaxVersions(Integer.MAX_VALUE);
       result = ht.get(get);
       assertNResult(result, ROW, FAMILIES[2], QUALIFIER,
-          new long [] {STAMPS[2], STAMPS[3], STAMPS[4], STAMPS[5], STAMPS[6]},
-          new byte[][] {VALUES[2], VALUES[3], VALUES[4], VALUES[5], VALUES[6]},
-          0, 4);
+        new long[] { STAMPS[2], STAMPS[3], STAMPS[4], STAMPS[5], STAMPS[6] },
+        new byte[][] { VALUES[2], VALUES[3], VALUES[4], VALUES[5], VALUES[6] }, 0, 4);
 
       get = new Get(ROW);
       get.addFamily(FAMILIES[2]);
       get.setMaxVersions(Integer.MAX_VALUE);
       result = ht.get(get);
       assertNResult(result, ROW, FAMILIES[2], QUALIFIER,
-          new long [] {STAMPS[2], STAMPS[3], STAMPS[4], STAMPS[5], STAMPS[6]},
-          new byte[][] {VALUES[2], VALUES[3], VALUES[4], VALUES[5], VALUES[6]},
-          0, 4);
+        new long[] { STAMPS[2], STAMPS[3], STAMPS[4], STAMPS[5], STAMPS[6] },
+        new byte[][] { VALUES[2], VALUES[3], VALUES[4], VALUES[5], VALUES[6] }, 0, 4);
 
       scan = new Scan(ROW);
       scan.addColumn(FAMILIES[2], QUALIFIER);
       scan.setMaxVersions(Integer.MAX_VALUE);
       result = getSingleScanResult(ht, scan);
       assertNResult(result, ROW, FAMILIES[2], QUALIFIER,
-              new long[]{STAMPS[2], STAMPS[3], STAMPS[4], STAMPS[5], STAMPS[6]},
-              new byte[][]{VALUES[2], VALUES[3], VALUES[4], VALUES[5], VALUES[6]},
-              0, 4);
+        new long[] { STAMPS[2], STAMPS[3], STAMPS[4], STAMPS[5], STAMPS[6] },
+        new byte[][] { VALUES[2], VALUES[3], VALUES[4], VALUES[5], VALUES[6] }, 0, 4);
 
       scan = new Scan(ROW);
       scan.addFamily(FAMILIES[2]);
       scan.setMaxVersions(Integer.MAX_VALUE);
       result = getSingleScanResult(ht, scan);
       assertNResult(result, ROW, FAMILIES[2], QUALIFIER,
-              new long[]{STAMPS[2], STAMPS[3], STAMPS[4], STAMPS[5], STAMPS[6]},
-              new byte[][]{VALUES[2], VALUES[3], VALUES[4], VALUES[5], VALUES[6]},
-              0, 4);
+        new long[] { STAMPS[2], STAMPS[3], STAMPS[4], STAMPS[5], STAMPS[6] },
+        new byte[][] { VALUES[2], VALUES[3], VALUES[4], VALUES[5], VALUES[6] }, 0, 4);
 
       // Try all families
 
       get = new Get(ROW);
       get.setMaxVersions(Integer.MAX_VALUE);
       result = ht.get(get);
-      assertTrue("Expected 9 keys but received " + result.size(),
-          result.size() == 9);
+      assertEquals("Expected 9 keys but received " + result.size(), 9, result.size());
 
       get = new Get(ROW);
       get.addFamily(FAMILIES[0]);
@@ -1794,8 +1424,7 @@ public class TestFromClientSide {
       get.addFamily(FAMILIES[2]);
       get.setMaxVersions(Integer.MAX_VALUE);
       result = ht.get(get);
-      assertTrue("Expected 9 keys but received " + result.size(),
-          result.size() == 9);
+      assertEquals("Expected 9 keys but received " + result.size(), 9, result.size());
 
       get = new Get(ROW);
       get.addColumn(FAMILIES[0], QUALIFIER);
@@ -1803,14 +1432,12 @@ public class TestFromClientSide {
       get.addColumn(FAMILIES[2], QUALIFIER);
       get.setMaxVersions(Integer.MAX_VALUE);
       result = ht.get(get);
-      assertTrue("Expected 9 keys but received " + result.size(),
-          result.size() == 9);
+      assertEquals("Expected 9 keys but received " + result.size(), 9, result.size());
 
       scan = new Scan(ROW);
       scan.setMaxVersions(Integer.MAX_VALUE);
       result = getSingleScanResult(ht, scan);
-      assertTrue("Expected 9 keys but received " + result.size(),
-              result.size() == 9);
+      assertEquals("Expected 9 keys but received " + result.size(), 9, result.size());
 
       scan = new Scan(ROW);
       scan.setMaxVersions(Integer.MAX_VALUE);
@@ -1818,8 +1445,7 @@ public class TestFromClientSide {
       scan.addFamily(FAMILIES[1]);
       scan.addFamily(FAMILIES[2]);
       result = getSingleScanResult(ht, scan);
-      assertTrue("Expected 9 keys but received " + result.size(),
-              result.size() == 9);
+      assertEquals("Expected 9 keys but received " + result.size(), 9, result.size());
 
       scan = new Scan(ROW);
       scan.setMaxVersions(Integer.MAX_VALUE);
@@ -1827,19 +1453,17 @@ public class TestFromClientSide {
       scan.addColumn(FAMILIES[1], QUALIFIER);
       scan.addColumn(FAMILIES[2], QUALIFIER);
       result = getSingleScanResult(ht, scan);
-      assertTrue("Expected 9 keys but received " + result.size(),
-              result.size() == 9);
+      assertEquals("Expected 9 keys but received " + result.size(), 9, result.size());
     }
   }
 
-  @Test
-  public void testDeleteFamilyVersion() throws Exception {
+  @Test public void testDeleteFamilyVersion() throws Exception {
     try (Admin admin = TEST_UTIL.getAdmin()) {
       final TableName tableName = name.getTableName();
 
       byte[][] QUALIFIERS = makeNAscii(QUALIFIER, 1);
       byte[][] VALUES = makeN(VALUE, 5);
-      long[] ts = {1000, 2000, 3000, 4000, 5000};
+      long[] ts = { 1000, 2000, 3000, 4000, 5000 };
 
       try (Table ht = TEST_UTIL.createTable(tableName, FAMILY, 5)) {
 
@@ -1864,26 +1488,23 @@ public class TestFromClientSide {
           get.setMaxVersions(Integer.MAX_VALUE);
           Result result = ht.get(get);
           // verify version '1000'/'3000'/'5000' remains for all columns
-          assertNResult(result, ROW, FAMILY, QUALIFIERS[i],
-                  new long[]{ts[0], ts[2], ts[4]},
-                  new byte[][]{VALUES[0], VALUES[2], VALUES[4]},
-                  0, 2);
+          assertNResult(result, ROW, FAMILY, QUALIFIERS[i], new long[] { ts[0], ts[2], ts[4] },
+            new byte[][] { VALUES[0], VALUES[2], VALUES[4] }, 0, 2);
         }
       }
     }
   }
 
-  @Test
-  public void testDeleteFamilyVersionWithOtherDeletes() throws Exception {
+  @Test public void testDeleteFamilyVersionWithOtherDeletes() throws Exception {
     final TableName tableName = name.getTableName();
 
-    byte [][] QUALIFIERS = makeNAscii(QUALIFIER, 5);
-    byte [][] VALUES = makeN(VALUE, 5);
-    long [] ts = {1000, 2000, 3000, 4000, 5000};
+    byte[][] QUALIFIERS = makeNAscii(QUALIFIER, 5);
+    byte[][] VALUES = makeN(VALUE, 5);
+    long[] ts = { 1000, 2000, 3000, 4000, 5000 };
 
-    try (Admin admin = TEST_UTIL.getAdmin();
-        Table ht = TEST_UTIL.createTable(tableName, FAMILY, 5)) {
-      Put put = null;
+    try (Admin admin = TEST_UTIL.getAdmin(); Table ht = TEST_UTIL.createTable(tableName, FAMILY,
+      5)) {
+      Put put;
       Result result = null;
       Get get = null;
       Delete delete = null;
@@ -1938,19 +1559,15 @@ public class TestFromClientSide {
       get.addColumn(FAMILY, QUALIFIERS[0]);
       get.setMaxVersions(Integer.MAX_VALUE);
       result = ht.get(get);
-      assertNResult(result, ROW, FAMILY, QUALIFIERS[0],
-              new long[]{ts[4]},
-              new byte[][]{VALUES[4]},
-              0, 0);
+      assertNResult(result, ROW, FAMILY, QUALIFIERS[0], new long[] { ts[4] },
+        new byte[][] { VALUES[4] }, 0, 0);
 
       get = new Get(ROW);
       get.addColumn(FAMILY, QUALIFIERS[1]);
       get.setMaxVersions(Integer.MAX_VALUE);
       result = ht.get(get);
-      assertNResult(result, ROW, FAMILY, QUALIFIERS[1],
-              new long[]{ts[2], ts[4]},
-              new byte[][]{VALUES[2], VALUES[4]},
-              0, 1);
+      assertNResult(result, ROW, FAMILY, QUALIFIERS[1], new long[] { ts[2], ts[4] },
+        new byte[][] { VALUES[2], VALUES[4] }, 0, 1);
 
       get = new Get(ROW);
       get.addColumn(FAMILY, QUALIFIERS[2]);
@@ -1962,19 +1579,15 @@ public class TestFromClientSide {
       get.addColumn(FAMILY, QUALIFIERS[3]);
       get.setMaxVersions(Integer.MAX_VALUE);
       result = ht.get(get);
-      assertNResult(result, ROW, FAMILY, QUALIFIERS[3],
-              new long[]{ts[2], ts[4]},
-              new byte[][]{VALUES[2], VALUES[4]},
-              0, 1);
+      assertNResult(result, ROW, FAMILY, QUALIFIERS[3], new long[] { ts[2], ts[4] },
+        new byte[][] { VALUES[2], VALUES[4] }, 0, 1);
 
       get = new Get(ROW);
       get.addColumn(FAMILY, QUALIFIERS[4]);
       get.setMaxVersions(Integer.MAX_VALUE);
       result = ht.get(get);
-      assertNResult(result, ROW, FAMILY, QUALIFIERS[4],
-              new long[]{ts[2]},
-              new byte[][]{VALUES[2]},
-              0, 0);
+      assertNResult(result, ROW, FAMILY, QUALIFIERS[4], new long[] { ts[2] },
+        new byte[][] { VALUES[2] }, 0, 0);
 
       // 6. check ROWS[0]
       for (int i = 0; i < 5; i++) {
@@ -1983,21 +1596,18 @@ public class TestFromClientSide {
         get.readVersions(Integer.MAX_VALUE);
         result = ht.get(get);
         // verify version '1000'/'3000'/'5000' remains for all columns
-        assertNResult(result, ROW2, FAMILY, QUALIFIERS[i],
-                new long[]{ts[0], ts[2], ts[4]},
-                new byte[][]{VALUES[0], VALUES[2], VALUES[4]},
-                0, 2);
+        assertNResult(result, ROW2, FAMILY, QUALIFIERS[i], new long[] { ts[0], ts[2], ts[4] },
+          new byte[][] { VALUES[0], VALUES[2], VALUES[4] }, 0, 2);
       }
     }
   }
 
-  @Test
-  public void testDeleteWithFailed() throws Exception {
+  @Test public void testDeleteWithFailed() throws Exception {
     final TableName tableName = name.getTableName();
 
-    byte [][] FAMILIES = makeNAscii(FAMILY, 3);
-    byte [][] VALUES = makeN(VALUE, 5);
-    long [] ts = {1000, 2000, 3000, 4000, 5000};
+    byte[][] FAMILIES = makeNAscii(FAMILY, 3);
+    byte[][] VALUES = makeN(VALUE, 5);
+    long[] ts = { 1000, 2000, 3000, 4000, 5000 };
 
     try (Table ht = TEST_UTIL.createTable(tableName, FAMILIES, 3)) {
       Put put = new Put(ROW);
@@ -2017,14 +1627,13 @@ public class TestFromClientSide {
     }
   }
 
-  @Test
-  public void testDeletes() throws Exception {
+  @Test @SuppressWarnings("checkstyle:MethodLength") public void testDeletes() throws Exception {
     final TableName tableName = name.getTableName();
 
-    byte [][] ROWS = makeNAscii(ROW, 6);
-    byte [][] FAMILIES = makeNAscii(FAMILY, 3);
-    byte [][] VALUES = makeN(VALUE, 5);
-    long [] ts = {1000, 2000, 3000, 4000, 5000};
+    byte[][] ROWS = makeNAscii(ROW, 6);
+    byte[][] FAMILIES = makeNAscii(FAMILY, 3);
+    byte[][] VALUES = makeN(VALUE, 5);
+    long[] ts = { 1000, 2000, 3000, 4000, 5000 };
 
     try (Table ht = TEST_UTIL.createTable(tableName, FAMILIES, 3)) {
 
@@ -2041,19 +1650,15 @@ public class TestFromClientSide {
       get.addFamily(FAMILIES[0]);
       get.setMaxVersions(Integer.MAX_VALUE);
       Result result = ht.get(get);
-      assertNResult(result, ROW, FAMILIES[0], QUALIFIER,
-          new long [] {ts[1]},
-          new byte[][] {VALUES[1]},
-          0, 0);
+      assertNResult(result, ROW, FAMILIES[0], QUALIFIER, new long[] { ts[1] },
+        new byte[][] { VALUES[1] }, 0, 0);
 
       Scan scan = new Scan(ROW);
       scan.addFamily(FAMILIES[0]);
       scan.setMaxVersions(Integer.MAX_VALUE);
       result = getSingleScanResult(ht, scan);
-      assertNResult(result, ROW, FAMILIES[0], QUALIFIER,
-              new long[]{ts[1]},
-              new byte[][]{VALUES[1]},
-              0, 0);
+      assertNResult(result, ROW, FAMILIES[0], QUALIFIER, new long[] { ts[1] },
+        new byte[][] { VALUES[1] }, 0, 0);
 
       // Test delete latest version
       put = new Put(ROW);
@@ -2073,19 +1678,15 @@ public class TestFromClientSide {
       get.addColumn(FAMILIES[0], QUALIFIER);
       get.setMaxVersions(Integer.MAX_VALUE);
       result = ht.get(get);
-      assertNResult(result, ROW, FAMILIES[0], QUALIFIER,
-          new long [] {ts[1], ts[2], ts[3]},
-          new byte[][] {VALUES[1], VALUES[2], VALUES[3]},
-          0, 2);
+      assertNResult(result, ROW, FAMILIES[0], QUALIFIER, new long[] { ts[1], ts[2], ts[3] },
+        new byte[][] { VALUES[1], VALUES[2], VALUES[3] }, 0, 2);
 
       scan = new Scan(ROW);
       scan.addColumn(FAMILIES[0], QUALIFIER);
       scan.setMaxVersions(Integer.MAX_VALUE);
       result = getSingleScanResult(ht, scan);
-      assertNResult(result, ROW, FAMILIES[0], QUALIFIER,
-              new long[]{ts[1], ts[2], ts[3]},
-              new byte[][]{VALUES[1], VALUES[2], VALUES[3]},
-              0, 2);
+      assertNResult(result, ROW, FAMILIES[0], QUALIFIER, new long[] { ts[1], ts[2], ts[3] },
+        new byte[][] { VALUES[1], VALUES[2], VALUES[3] }, 0, 2);
 
       // Test for HBASE-1847
       delete = new Delete(ROW);
@@ -2105,7 +1706,6 @@ public class TestFromClientSide {
       put.addColumn(FAMILIES[0], QUALIFIER, ts[4], VALUES[4]); // 5000
       ht.put(put);
 
-
       // It used to be due to the internal implementation of Get, that
       // the Get() call would return ts[4] UNLIKE the Scan below. With
       // the switch to using Scan for Get this is no longer the case.
@@ -2113,10 +1713,8 @@ public class TestFromClientSide {
       get.addFamily(FAMILIES[0]);
       get.setMaxVersions(Integer.MAX_VALUE);
       result = ht.get(get);
-      assertNResult(result, ROW, FAMILIES[0], QUALIFIER,
-          new long [] {ts[1], ts[2], ts[3]},
-          new byte[][] {VALUES[1], VALUES[2], VALUES[3]},
-          0, 2);
+      assertNResult(result, ROW, FAMILIES[0], QUALIFIER, new long[] { ts[1], ts[2], ts[3] },
+        new byte[][] { VALUES[1], VALUES[2], VALUES[3] }, 0, 2);
 
       // The Scanner returns the previous values, the expected-naive-unexpected behavior
 
@@ -2124,10 +1722,8 @@ public class TestFromClientSide {
       scan.addFamily(FAMILIES[0]);
       scan.setMaxVersions(Integer.MAX_VALUE);
       result = getSingleScanResult(ht, scan);
-      assertNResult(result, ROW, FAMILIES[0], QUALIFIER,
-              new long[]{ts[1], ts[2], ts[3]},
-              new byte[][]{VALUES[1], VALUES[2], VALUES[3]},
-              0, 2);
+      assertNResult(result, ROW, FAMILIES[0], QUALIFIER, new long[] { ts[1], ts[2], ts[3] },
+        new byte[][] { VALUES[1], VALUES[2], VALUES[3] }, 0, 2);
 
       // Test deleting an entire family from one row but not the other various ways
 
@@ -2158,8 +1754,8 @@ public class TestFromClientSide {
       get.addFamily(FAMILIES[2]);
       get.setMaxVersions(Integer.MAX_VALUE);
       result = ht.get(get);
-      assertTrue("Expected 4 key but received " + result.size() + ": " + result,
-          result.size() == 4);
+      assertEquals("Expected 4 key but received " + result.size() + ": " + result, 4,
+        result.size());
 
       delete = new Delete(ROWS[0]);
       delete.addFamily(FAMILIES[2]);
@@ -2180,40 +1776,32 @@ public class TestFromClientSide {
       get.addFamily(FAMILIES[2]);
       get.setMaxVersions(Integer.MAX_VALUE);
       result = ht.get(get);
-      assertTrue("Expected 2 keys but received " + result.size(),
-          result.size() == 2);
-      assertNResult(result, ROWS[0], FAMILIES[1], QUALIFIER,
-          new long [] {ts[0], ts[1]},
-          new byte[][] {VALUES[0], VALUES[1]},
-          0, 1);
+      assertEquals("Expected 2 keys but received " + result.size(), 2, result.size());
+      assertNResult(result, ROWS[0], FAMILIES[1], QUALIFIER, new long[] { ts[0], ts[1] },
+        new byte[][] { VALUES[0], VALUES[1] }, 0, 1);
 
       scan = new Scan(ROWS[0]);
       scan.addFamily(FAMILIES[1]);
       scan.addFamily(FAMILIES[2]);
       scan.setMaxVersions(Integer.MAX_VALUE);
       result = getSingleScanResult(ht, scan);
-      assertTrue("Expected 2 keys but received " + result.size(),
-              result.size() == 2);
-      assertNResult(result, ROWS[0], FAMILIES[1], QUALIFIER,
-              new long[]{ts[0], ts[1]},
-              new byte[][]{VALUES[0], VALUES[1]},
-              0, 1);
+      assertEquals("Expected 2 keys but received " + result.size(), 2, result.size());
+      assertNResult(result, ROWS[0], FAMILIES[1], QUALIFIER, new long[] { ts[0], ts[1] },
+        new byte[][] { VALUES[0], VALUES[1] }, 0, 1);
 
       get = new Get(ROWS[1]);
       get.addFamily(FAMILIES[1]);
       get.addFamily(FAMILIES[2]);
       get.setMaxVersions(Integer.MAX_VALUE);
       result = ht.get(get);
-      assertTrue("Expected 2 keys but received " + result.size(),
-          result.size() == 2);
+      assertEquals("Expected 2 keys but received " + result.size(), 2, result.size());
 
       scan = new Scan(ROWS[1]);
       scan.addFamily(FAMILIES[1]);
       scan.addFamily(FAMILIES[2]);
       scan.setMaxVersions(Integer.MAX_VALUE);
       result = getSingleScanResult(ht, scan);
-      assertTrue("Expected 2 keys but received " + result.size(),
-              result.size() == 2);
+      assertEquals("Expected 2 keys but received " + result.size(), 2, result.size());
 
       get = new Get(ROWS[2]);
       get.addFamily(FAMILIES[1]);
@@ -2221,10 +1809,8 @@ public class TestFromClientSide {
       get.setMaxVersions(Integer.MAX_VALUE);
       result = ht.get(get);
       assertEquals(1, result.size());
-      assertNResult(result, ROWS[2], FAMILIES[2], QUALIFIER,
-          new long [] {ts[2]},
-          new byte[][] {VALUES[2]},
-          0, 0);
+      assertNResult(result, ROWS[2], FAMILIES[2], QUALIFIER, new long[] { ts[2] },
+        new byte[][] { VALUES[2] }, 0, 0);
 
       scan = new Scan(ROWS[2]);
       scan.addFamily(FAMILIES[1]);
@@ -2232,10 +1818,8 @@ public class TestFromClientSide {
       scan.setMaxVersions(Integer.MAX_VALUE);
       result = getSingleScanResult(ht, scan);
       assertEquals(1, result.size());
-      assertNResult(result, ROWS[2], FAMILIES[2], QUALIFIER,
-              new long[]{ts[2]},
-              new byte[][]{VALUES[2]},
-              0, 0);
+      assertNResult(result, ROWS[2], FAMILIES[2], QUALIFIER, new long[] { ts[2] },
+        new byte[][] { VALUES[2] }, 0, 0);
 
       // Test if we delete the family first in one row (HBASE-1541)
 
@@ -2257,16 +1841,14 @@ public class TestFromClientSide {
       get.addFamily(FAMILIES[2]);
       get.setMaxVersions(Integer.MAX_VALUE);
       result = ht.get(get);
-      assertTrue("Expected 1 key but received " + result.size(),
-          result.size() == 1);
+      assertEquals("Expected 1 key but received " + result.size(), 1, result.size());
 
       get = new Get(ROWS[4]);
       get.addFamily(FAMILIES[1]);
       get.addFamily(FAMILIES[2]);
       get.setMaxVersions(Integer.MAX_VALUE);
       result = ht.get(get);
-      assertTrue("Expected 2 keys but received " + result.size(),
-          result.size() == 2);
+      assertEquals("Expected 2 keys but received " + result.size(), 2, result.size());
 
       scan = new Scan(ROWS[3]);
       scan.addFamily(FAMILIES[1]);
@@ -2274,13 +1856,11 @@ public class TestFromClientSide {
       scan.setMaxVersions(Integer.MAX_VALUE);
       try (ResultScanner scanner = ht.getScanner(scan)) {
         result = scanner.next();
-        assertTrue("Expected 1 key but received " + result.size(),
-                result.size() == 1);
+        assertEquals("Expected 1 key but received " + result.size(), 1, result.size());
         assertTrue(Bytes.equals(CellUtil.cloneRow(result.rawCells()[0]), ROWS[3]));
         assertTrue(Bytes.equals(CellUtil.cloneValue(result.rawCells()[0]), VALUES[0]));
         result = scanner.next();
-        assertTrue("Expected 2 keys but received " + result.size(),
-                result.size() == 2);
+        assertEquals("Expected 2 keys but received " + result.size(), 2, result.size());
         assertTrue(Bytes.equals(CellUtil.cloneRow(result.rawCells()[0]), ROWS[4]));
         assertTrue(Bytes.equals(CellUtil.cloneRow(result.rawCells()[1]), ROWS[4]));
         assertTrue(Bytes.equals(CellUtil.cloneValue(result.rawCells()[0]), VALUES[1]));
@@ -2300,7 +1880,7 @@ public class TestFromClientSide {
         get = new Get(bytes);
         get.addFamily(FAMILIES[0]);
         result = ht.get(get);
-        assertTrue(result.size() == 1);
+        assertEquals(1, result.size());
       }
       ArrayList<Delete> deletes = new ArrayList<>();
       for (int i = 0; i < 10; i++) {
@@ -2319,4642 +1899,5 @@ public class TestFromClientSide {
       }
     }
   }
+}
 
-  /**
-   * Test batch operations with combination of valid and invalid args
-   */
-  @Test
-  public void testBatchOperationsWithErrors() throws Exception {
-    final TableName tableName = name.getTableName();
-    try (Table foo = TEST_UTIL.createTable(tableName, new byte[][] {FAMILY}, 10)) {
-
-      int NUM_OPS = 100;
-      int FAILED_OPS = 50;
-
-      RetriesExhaustedWithDetailsException expectedException = null;
-      IllegalArgumentException iae = null;
-
-      // 1.1 Put with no column families (local validation, runtime exception)
-      List<Put> puts = new ArrayList<Put>(NUM_OPS);
-      for (int i = 0; i != NUM_OPS; i++) {
-        Put put = new Put(Bytes.toBytes(i));
-        puts.add(put);
-      }
-
-      try {
-        foo.put(puts);
-      } catch (IllegalArgumentException e) {
-        iae = e;
-      }
-      assertNotNull(iae);
-      assertEquals(NUM_OPS, puts.size());
-
-      // 1.2 Put with invalid column family
-      iae = null;
-      puts.clear();
-      for (int i = 0; i != NUM_OPS; i++) {
-        Put put = new Put(Bytes.toBytes(i));
-        put.addColumn((i % 2) == 0 ? FAMILY : INVALID_FAMILY, FAMILY, Bytes.toBytes(i));
-        puts.add(put);
-      }
-
-      try {
-        foo.put(puts);
-      } catch (RetriesExhaustedWithDetailsException e) {
-        expectedException = e;
-      }
-      assertNotNull(expectedException);
-      assertEquals(FAILED_OPS, expectedException.exceptions.size());
-      assertTrue(expectedException.actions.contains(puts.get(1)));
-
-      // 2.1 Get non-existent rows
-      List<Get> gets = new ArrayList<>(NUM_OPS);
-      for (int i = 0; i < NUM_OPS; i++) {
-        Get get = new Get(Bytes.toBytes(i));
-        // get.addColumn(FAMILY, FAMILY);
-        gets.add(get);
-      }
-      Result[] getsResult = foo.get(gets);
-
-      assertNotNull(getsResult);
-      assertEquals(NUM_OPS, getsResult.length);
-      assertNull(getsResult[1].getRow());
-
-      // 2.2 Get with invalid column family
-      gets.clear();
-      getsResult = null;
-      expectedException = null;
-      for (int i = 0; i < NUM_OPS; i++) {
-        Get get = new Get(Bytes.toBytes(i));
-        get.addColumn((i % 2) == 0 ? FAMILY : INVALID_FAMILY, FAMILY);
-        gets.add(get);
-      }
-      try {
-        getsResult = foo.get(gets);
-      } catch (RetriesExhaustedWithDetailsException e) {
-        expectedException = e;
-      }
-      assertNull(getsResult);
-      assertNotNull(expectedException);
-      assertEquals(FAILED_OPS, expectedException.exceptions.size());
-      assertTrue(expectedException.actions.contains(gets.get(1)));
-
-      // 3.1 Delete with invalid column family
-      expectedException = null;
-      List<Delete> deletes = new ArrayList<>(NUM_OPS);
-      for (int i = 0; i < NUM_OPS; i++) {
-        Delete delete = new Delete(Bytes.toBytes(i));
-        delete.addColumn((i % 2) == 0 ? FAMILY : INVALID_FAMILY, FAMILY);
-        deletes.add(delete);
-      }
-      try {
-        foo.delete(deletes);
-      } catch (RetriesExhaustedWithDetailsException e) {
-        expectedException = e;
-      }
-      assertEquals((NUM_OPS - FAILED_OPS), deletes.size());
-      assertNotNull(expectedException);
-      assertEquals(FAILED_OPS, expectedException.exceptions.size());
-      assertTrue(expectedException.actions.contains(deletes.get(1)));
-
-
-      // 3.2 Delete non-existent rows
-      deletes.clear();
-      for (int i = 0; i < NUM_OPS; i++) {
-        Delete delete = new Delete(Bytes.toBytes(i));
-        deletes.add(delete);
-      }
-      foo.delete(deletes);
-
-      assertTrue(deletes.isEmpty());
-    }
-  }
-
-  /*
-   * Baseline "scalability" test.
-   *
-   * Tests one hundred families, one million columns, one million versions
-   */
-  @Ignore @Test
-  public void testMillions() throws Exception {
-
-    // 100 families
-
-    // millions of columns
-
-    // millions of versions
-
-  }
-
-  @Ignore @Test
-  public void testMultipleRegionsAndBatchPuts() throws Exception {
-    // Two family table
-
-    // Insert lots of rows
-
-    // Insert to the same row with batched puts
-
-    // Insert to multiple rows with batched puts
-
-    // Split the table
-
-    // Get row from first region
-
-    // Get row from second region
-
-    // Scan all rows
-
-    // Insert to multiple regions with batched puts
-
-    // Get row from first region
-
-    // Get row from second region
-
-    // Scan all rows
-
-
-  }
-
-  @Ignore @Test
-  public void testMultipleRowMultipleFamily() throws Exception {
-
-  }
-
-  //
-  // JIRA Testers
-  //
-
-  /**
-   * HBASE-867
-   *    If millions of columns in a column family, hbase scanner won't come up
-   *
-   *    Test will create numRows rows, each with numColsPerRow columns
-   *    (1 version each), and attempt to scan them all.
-   *
-   *    To test at scale, up numColsPerRow to the millions
-   *    (have not gotten that to work running as junit though)
-   */
-  @Test
-  public void testJiraTest867() throws Exception {
-    int numRows = 10;
-    int numColsPerRow = 2000;
-
-    final TableName tableName = name.getTableName();
-
-    byte [][] ROWS = makeN(ROW, numRows);
-    byte [][] QUALIFIERS = makeN(QUALIFIER, numColsPerRow);
-
-    try (Table ht = TEST_UTIL.createTable(tableName, FAMILY)) {
-
-      // Insert rows
-
-      for (int i = 0; i < numRows; i++) {
-        Put put = new Put(ROWS[i]);
-        put.setDurability(Durability.SKIP_WAL);
-        for (int j = 0; j < numColsPerRow; j++) {
-          put.addColumn(FAMILY, QUALIFIERS[j], QUALIFIERS[j]);
-        }
-        assertTrue("Put expected to contain " + numColsPerRow + " columns but " +
-                "only contains " + put.size(), put.size() == numColsPerRow);
-        ht.put(put);
-      }
-
-      // Get a row
-      Get get = new Get(ROWS[numRows - 1]);
-      Result result = ht.get(get);
-      assertNumKeys(result, numColsPerRow);
-      Cell[] keys = result.rawCells();
-      for (int i = 0; i < result.size(); i++) {
-        assertKey(keys[i], ROWS[numRows - 1], FAMILY, QUALIFIERS[i], QUALIFIERS[i]);
-      }
-
-      // Scan the rows
-      Scan scan = new Scan();
-      try (ResultScanner scanner = ht.getScanner(scan)) {
-        int rowCount = 0;
-        while ((result = scanner.next()) != null) {
-          assertNumKeys(result, numColsPerRow);
-          Cell[] kvs = result.rawCells();
-          for (int i = 0; i < numColsPerRow; i++) {
-            assertKey(kvs[i], ROWS[rowCount], FAMILY, QUALIFIERS[i], QUALIFIERS[i]);
-          }
-          rowCount++;
-        }
-        assertTrue("Expected to scan " + numRows + " rows but actually scanned "
-                + rowCount + " rows", rowCount == numRows);
-      }
-
-      // flush and try again
-
-      TEST_UTIL.flush();
-
-      // Get a row
-      get = new Get(ROWS[numRows - 1]);
-      result = ht.get(get);
-      assertNumKeys(result, numColsPerRow);
-      keys = result.rawCells();
-      for (int i = 0; i < result.size(); i++) {
-        assertKey(keys[i], ROWS[numRows - 1], FAMILY, QUALIFIERS[i], QUALIFIERS[i]);
-      }
-
-      // Scan the rows
-      scan = new Scan();
-      try (ResultScanner scanner = ht.getScanner(scan)) {
-        int rowCount = 0;
-        while ((result = scanner.next()) != null) {
-          assertNumKeys(result, numColsPerRow);
-          Cell[] kvs = result.rawCells();
-          for (int i = 0; i < numColsPerRow; i++) {
-            assertKey(kvs[i], ROWS[rowCount], FAMILY, QUALIFIERS[i], QUALIFIERS[i]);
-          }
-          rowCount++;
-        }
-        assertTrue("Expected to scan " + numRows + " rows but actually scanned "
-                + rowCount + " rows", rowCount == numRows);
-      }
-    }
-  }
-
-  /**
-   * HBASE-861
-   *    get with timestamp will return a value if there is a version with an
-   *    earlier timestamp
-   */
-  @Test
-  public void testJiraTest861() throws Exception {
-    final TableName tableName = name.getTableName();
-    byte [][] VALUES = makeNAscii(VALUE, 7);
-    long [] STAMPS = makeStamps(7);
-
-    try (Table ht = TEST_UTIL.createTable(tableName, FAMILY, 10)) {
-
-      // Insert three versions
-
-      Put put = new Put(ROW);
-      put.addColumn(FAMILY, QUALIFIER, STAMPS[3], VALUES[3]);
-      put.addColumn(FAMILY, QUALIFIER, STAMPS[2], VALUES[2]);
-      put.addColumn(FAMILY, QUALIFIER, STAMPS[4], VALUES[4]);
-      ht.put(put);
-
-      // Get the middle value
-      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[2], VALUES[2]);
-
-      // Try to get one version before (expect fail)
-      getVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[1]);
-
-      // Try to get one version after (expect fail)
-      getVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[5]);
-
-      // Try same from storefile
-      TEST_UTIL.flush();
-      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[2], VALUES[2]);
-      getVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[1]);
-      getVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[5]);
-
-      // Insert two more versions surrounding others, into memstore
-      put = new Put(ROW);
-      put.addColumn(FAMILY, QUALIFIER, STAMPS[0], VALUES[0]);
-      put.addColumn(FAMILY, QUALIFIER, STAMPS[6], VALUES[6]);
-      ht.put(put);
-
-      // Check we can get everything we should and can't get what we shouldn't
-      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[0], VALUES[0]);
-      getVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[1]);
-      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[2], VALUES[2]);
-      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[3], VALUES[3]);
-      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[4], VALUES[4]);
-      getVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[5]);
-      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[6], VALUES[6]);
-
-      // Try same from two storefiles
-      TEST_UTIL.flush();
-      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[0], VALUES[0]);
-      getVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[1]);
-      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[2], VALUES[2]);
-      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[3], VALUES[3]);
-      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[4], VALUES[4]);
-      getVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[5]);
-      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[6], VALUES[6]);
-    }
-  }
-
-  /**
-   * HBASE-33
-   *    Add a HTable get/obtainScanner method that retrieves all versions of a
-   *    particular column and row between two timestamps
-   */
-  @Test
-  public void testJiraTest33() throws Exception {
-    final TableName tableName = name.getTableName();
-    byte [][] VALUES = makeNAscii(VALUE, 7);
-    long [] STAMPS = makeStamps(7);
-
-    try (Table ht = TEST_UTIL.createTable(tableName, FAMILY, 10)) {
-
-      // Insert lots versions
-
-      Put put = new Put(ROW);
-      put.addColumn(FAMILY, QUALIFIER, STAMPS[0], VALUES[0]);
-      put.addColumn(FAMILY, QUALIFIER, STAMPS[1], VALUES[1]);
-      put.addColumn(FAMILY, QUALIFIER, STAMPS[2], VALUES[2]);
-      put.addColumn(FAMILY, QUALIFIER, STAMPS[3], VALUES[3]);
-      put.addColumn(FAMILY, QUALIFIER, STAMPS[4], VALUES[4]);
-      put.addColumn(FAMILY, QUALIFIER, STAMPS[5], VALUES[5]);
-      ht.put(put);
-
-      getVersionRangeAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 0, 5);
-      getVersionRangeAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 0, 2);
-      getVersionRangeAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 4, 5);
-      getVersionRangeAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 2, 3);
-
-      scanVersionRangeAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 0, 5);
-      scanVersionRangeAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 0, 2);
-      scanVersionRangeAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 4, 5);
-      scanVersionRangeAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 2, 3);
-
-      // Try same from storefile
-      TEST_UTIL.flush();
-
-      getVersionRangeAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 0, 5);
-      getVersionRangeAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 0, 2);
-      getVersionRangeAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 4, 5);
-      getVersionRangeAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 2, 3);
-
-      scanVersionRangeAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 0, 5);
-      scanVersionRangeAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 0, 2);
-      scanVersionRangeAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 4, 5);
-      scanVersionRangeAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 2, 3);
-    }
-  }
-
-  /**
-   * HBASE-1014
-   *    commit(BatchUpdate) method should return timestamp
-   */
-  @Test
-  public void testJiraTest1014() throws Exception {
-    final TableName tableName = name.getTableName();
-
-    try (Table ht = TEST_UTIL.createTable(tableName, FAMILY, 10)) {
-
-      long manualStamp = 12345;
-
-      // Insert lots versions
-
-      Put put = new Put(ROW);
-      put.addColumn(FAMILY, QUALIFIER, manualStamp, VALUE);
-      ht.put(put);
-
-      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, manualStamp, VALUE);
-      getVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, manualStamp - 1);
-      getVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, manualStamp + 1);
-    }
-  }
-
-  /**
-   * HBASE-1182
-   *    Scan for columns > some timestamp
-   */
-  @Test
-  public void testJiraTest1182() throws Exception {
-    final TableName tableName = name.getTableName();
-    byte [][] VALUES = makeNAscii(VALUE, 7);
-    long [] STAMPS = makeStamps(7);
-
-    try (Table ht = TEST_UTIL.createTable(tableName, FAMILY, 10)) {
-
-      // Insert lots versions
-
-      Put put = new Put(ROW);
-      put.addColumn(FAMILY, QUALIFIER, STAMPS[0], VALUES[0]);
-      put.addColumn(FAMILY, QUALIFIER, STAMPS[1], VALUES[1]);
-      put.addColumn(FAMILY, QUALIFIER, STAMPS[2], VALUES[2]);
-      put.addColumn(FAMILY, QUALIFIER, STAMPS[3], VALUES[3]);
-      put.addColumn(FAMILY, QUALIFIER, STAMPS[4], VALUES[4]);
-      put.addColumn(FAMILY, QUALIFIER, STAMPS[5], VALUES[5]);
-      ht.put(put);
-
-      getVersionRangeAndVerifyGreaterThan(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 0, 5);
-      getVersionRangeAndVerifyGreaterThan(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 2, 5);
-      getVersionRangeAndVerifyGreaterThan(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 4, 5);
-
-      scanVersionRangeAndVerifyGreaterThan(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 0, 5);
-      scanVersionRangeAndVerifyGreaterThan(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 2, 5);
-      scanVersionRangeAndVerifyGreaterThan(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 4, 5);
-
-      // Try same from storefile
-      TEST_UTIL.flush();
-
-      getVersionRangeAndVerifyGreaterThan(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 0, 5);
-      getVersionRangeAndVerifyGreaterThan(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 2, 5);
-      getVersionRangeAndVerifyGreaterThan(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 4, 5);
-
-      scanVersionRangeAndVerifyGreaterThan(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 0, 5);
-      scanVersionRangeAndVerifyGreaterThan(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 2, 5);
-      scanVersionRangeAndVerifyGreaterThan(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 4, 5);
-    }
-  }
-
-  /**
-   * HBASE-52
-   *    Add a means of scanning over all versions
-   */
-  @Test
-  public void testJiraTest52() throws Exception {
-    final TableName tableName = name.getTableName();
-    byte [][] VALUES = makeNAscii(VALUE, 7);
-    long [] STAMPS = makeStamps(7);
-
-    try (Table ht = TEST_UTIL.createTable(tableName, FAMILY, 10)) {
-
-      // Insert lots versions
-
-      Put put = new Put(ROW);
-      put.addColumn(FAMILY, QUALIFIER, STAMPS[0], VALUES[0]);
-      put.addColumn(FAMILY, QUALIFIER, STAMPS[1], VALUES[1]);
-      put.addColumn(FAMILY, QUALIFIER, STAMPS[2], VALUES[2]);
-      put.addColumn(FAMILY, QUALIFIER, STAMPS[3], VALUES[3]);
-      put.addColumn(FAMILY, QUALIFIER, STAMPS[4], VALUES[4]);
-      put.addColumn(FAMILY, QUALIFIER, STAMPS[5], VALUES[5]);
-      ht.put(put);
-
-      getAllVersionsAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 0, 5);
-
-      scanAllVersionsAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 0, 5);
-
-      // Try same from storefile
-      TEST_UTIL.flush();
-
-      getAllVersionsAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 0, 5);
-
-      scanAllVersionsAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 0, 5);
-    }
-  }
-
-  //
-  // Bulk Testers
-  //
-
-  private void getVersionRangeAndVerifyGreaterThan(Table ht, byte [] row,
-      byte [] family, byte [] qualifier, long [] stamps, byte [][] values,
-      int start, int end) throws IOException {
-    Get get = new Get(row);
-    get.addColumn(family, qualifier);
-    get.setMaxVersions(Integer.MAX_VALUE);
-    get.setTimeRange(stamps[start+1], Long.MAX_VALUE);
-    Result result = ht.get(get);
-    assertNResult(result, row, family, qualifier, stamps, values, start+1, end);
-  }
-
-  private void getVersionRangeAndVerify(Table ht, byte [] row, byte [] family,
-      byte [] qualifier, long [] stamps, byte [][] values, int start, int end) throws IOException {
-    Get get = new Get(row);
-    get.addColumn(family, qualifier);
-    get.setMaxVersions(Integer.MAX_VALUE);
-    get.setTimeRange(stamps[start], stamps[end]+1);
-    Result result = ht.get(get);
-    assertNResult(result, row, family, qualifier, stamps, values, start, end);
-  }
-
-  private void getAllVersionsAndVerify(Table ht, byte [] row, byte [] family,
-      byte [] qualifier, long [] stamps, byte [][] values, int start, int end) throws IOException {
-    Get get = new Get(row);
-    get.addColumn(family, qualifier);
-    get.setMaxVersions(Integer.MAX_VALUE);
-    Result result = ht.get(get);
-    assertNResult(result, row, family, qualifier, stamps, values, start, end);
-  }
-
-  private void scanVersionRangeAndVerifyGreaterThan(Table ht, byte [] row,
-      byte [] family, byte [] qualifier, long [] stamps, byte [][] values,
-      int start, int end) throws IOException {
-    Scan scan = new Scan(row);
-    scan.addColumn(family, qualifier);
-    scan.setMaxVersions(Integer.MAX_VALUE);
-    scan.setTimeRange(stamps[start+1], Long.MAX_VALUE);
-    Result result = getSingleScanResult(ht, scan);
-    assertNResult(result, row, family, qualifier, stamps, values, start+1, end);
-  }
-
-  private void scanVersionRangeAndVerify(Table ht, byte [] row, byte [] family,
-      byte [] qualifier, long [] stamps, byte [][] values, int start, int end) throws IOException {
-    Scan scan = new Scan(row);
-    scan.addColumn(family, qualifier);
-    scan.setMaxVersions(Integer.MAX_VALUE);
-    scan.setTimeRange(stamps[start], stamps[end]+1);
-    Result result = getSingleScanResult(ht, scan);
-    assertNResult(result, row, family, qualifier, stamps, values, start, end);
-  }
-
-  private void scanAllVersionsAndVerify(Table ht, byte [] row, byte [] family,
-      byte [] qualifier, long [] stamps, byte [][] values, int start, int end) throws IOException {
-    Scan scan = new Scan(row);
-    scan.addColumn(family, qualifier);
-    scan.setMaxVersions(Integer.MAX_VALUE);
-    Result result = getSingleScanResult(ht, scan);
-    assertNResult(result, row, family, qualifier, stamps, values, start, end);
-  }
-
-  private void getVersionAndVerify(Table ht, byte [] row, byte [] family,
-      byte [] qualifier, long stamp, byte [] value) throws Exception {
-    Get get = new Get(row);
-    get.addColumn(family, qualifier);
-    get.setTimestamp(stamp);
-    get.setMaxVersions(Integer.MAX_VALUE);
-    Result result = ht.get(get);
-    assertSingleResult(result, row, family, qualifier, stamp, value);
-  }
-
-  private void getVersionAndVerifyMissing(Table ht, byte [] row, byte [] family,
-      byte [] qualifier, long stamp) throws Exception {
-    Get get = new Get(row);
-    get.addColumn(family, qualifier);
-    get.setTimestamp(stamp);
-    get.setMaxVersions(Integer.MAX_VALUE);
-    Result result = ht.get(get);
-    assertEmptyResult(result);
-  }
-
-  private void scanVersionAndVerify(Table ht, byte [] row, byte [] family,
-      byte [] qualifier, long stamp, byte [] value) throws Exception {
-    Scan scan = new Scan(row);
-    scan.addColumn(family, qualifier);
-    scan.setTimestamp(stamp);
-    scan.setMaxVersions(Integer.MAX_VALUE);
-    Result result = getSingleScanResult(ht, scan);
-    assertSingleResult(result, row, family, qualifier, stamp, value);
-  }
-
-  private void scanVersionAndVerifyMissing(Table ht, byte [] row,
-      byte [] family, byte [] qualifier, long stamp) throws Exception {
-    Scan scan = new Scan(row);
-    scan.addColumn(family, qualifier);
-    scan.setTimestamp(stamp);
-    scan.setMaxVersions(Integer.MAX_VALUE);
-    Result result = getSingleScanResult(ht, scan);
-    assertNullResult(result);
-  }
-
-  private void getTestNull(Table ht, byte [] row, byte [] family, byte [] value) throws Exception {
-    Get get = new Get(row);
-    get.addColumn(family, null);
-    Result result = ht.get(get);
-    assertSingleResult(result, row, family, null, value);
-
-    get = new Get(row);
-    get.addColumn(family, HConstants.EMPTY_BYTE_ARRAY);
-    result = ht.get(get);
-    assertSingleResult(result, row, family, HConstants.EMPTY_BYTE_ARRAY, value);
-
-    get = new Get(row);
-    get.addFamily(family);
-    result = ht.get(get);
-    assertSingleResult(result, row, family, HConstants.EMPTY_BYTE_ARRAY, value);
-
-    get = new Get(row);
-    result = ht.get(get);
-    assertSingleResult(result, row, family, HConstants.EMPTY_BYTE_ARRAY, value);
-
-  }
-
-  private void getTestNull(Table ht, byte[] row, byte[] family, long value) throws Exception {
-    Get get = new Get(row);
-    get.addColumn(family, null);
-    Result result = ht.get(get);
-    assertSingleResult(result, row, family, null, value);
-
-    get = new Get(row);
-    get.addColumn(family, HConstants.EMPTY_BYTE_ARRAY);
-    result = ht.get(get);
-    assertSingleResult(result, row, family, HConstants.EMPTY_BYTE_ARRAY, value);
-
-    get = new Get(row);
-    get.addFamily(family);
-    result = ht.get(get);
-    assertSingleResult(result, row, family, HConstants.EMPTY_BYTE_ARRAY, value);
-
-    get = new Get(row);
-    result = ht.get(get);
-    assertSingleResult(result, row, family, HConstants.EMPTY_BYTE_ARRAY, value);
-  }
-
-  private void scanTestNull(Table ht, byte[] row, byte[] family, byte[] value)
-      throws Exception {
-    scanTestNull(ht, row, family, value, false);
-  }
-
-  private void scanTestNull(Table ht, byte[] row, byte[] family, byte[] value,
-      boolean isReversedScan) throws Exception {
-
-    Scan scan = new Scan();
-    scan.setReversed(isReversedScan);
-    scan.addColumn(family, null);
-    Result result = getSingleScanResult(ht, scan);
-    assertSingleResult(result, row, family, HConstants.EMPTY_BYTE_ARRAY, value);
-
-    scan = new Scan();
-    scan.setReversed(isReversedScan);
-    scan.addColumn(family, HConstants.EMPTY_BYTE_ARRAY);
-    result = getSingleScanResult(ht, scan);
-    assertSingleResult(result, row, family, HConstants.EMPTY_BYTE_ARRAY, value);
-
-    scan = new Scan();
-    scan.setReversed(isReversedScan);
-    scan.addFamily(family);
-    result = getSingleScanResult(ht, scan);
-    assertSingleResult(result, row, family, HConstants.EMPTY_BYTE_ARRAY, value);
-
-    scan = new Scan();
-    scan.setReversed(isReversedScan);
-    result = getSingleScanResult(ht, scan);
-    assertSingleResult(result, row, family, HConstants.EMPTY_BYTE_ARRAY, value);
-
-  }
-
-  private void singleRowGetTest(Table ht, byte [][] ROWS, byte [][] FAMILIES,
-      byte [][] QUALIFIERS, byte [][] VALUES) throws Exception {
-    // Single column from memstore
-    Get get = new Get(ROWS[0]);
-    get.addColumn(FAMILIES[4], QUALIFIERS[0]);
-    Result result = ht.get(get);
-    assertSingleResult(result, ROWS[0], FAMILIES[4], QUALIFIERS[0], VALUES[0]);
-
-    // Single column from storefile
-    get = new Get(ROWS[0]);
-    get.addColumn(FAMILIES[2], QUALIFIERS[2]);
-    result = ht.get(get);
-    assertSingleResult(result, ROWS[0], FAMILIES[2], QUALIFIERS[2], VALUES[2]);
-
-    // Single column from storefile, family match
-    get = new Get(ROWS[0]);
-    get.addFamily(FAMILIES[7]);
-    result = ht.get(get);
-    assertSingleResult(result, ROWS[0], FAMILIES[7], QUALIFIERS[7], VALUES[7]);
-
-    // Two columns, one from memstore one from storefile, same family,
-    // wildcard match
-    get = new Get(ROWS[0]);
-    get.addFamily(FAMILIES[4]);
-    result = ht.get(get);
-    assertDoubleResult(result, ROWS[0], FAMILIES[4], QUALIFIERS[0], VALUES[0],
-        FAMILIES[4], QUALIFIERS[4], VALUES[4]);
-
-    // Two columns, one from memstore one from storefile, same family,
-    // explicit match
-    get = new Get(ROWS[0]);
-    get.addColumn(FAMILIES[4], QUALIFIERS[0]);
-    get.addColumn(FAMILIES[4], QUALIFIERS[4]);
-    result = ht.get(get);
-    assertDoubleResult(result, ROWS[0], FAMILIES[4], QUALIFIERS[0], VALUES[0],
-        FAMILIES[4], QUALIFIERS[4], VALUES[4]);
-
-    // Three column, one from memstore two from storefile, different families,
-    // wildcard match
-    get = new Get(ROWS[0]);
-    get.addFamily(FAMILIES[4]);
-    get.addFamily(FAMILIES[7]);
-    result = ht.get(get);
-    assertNResult(result, ROWS[0], FAMILIES, QUALIFIERS, VALUES,
-        new int [][] { {4, 0, 0}, {4, 4, 4}, {7, 7, 7} });
-
-    // Multiple columns from everywhere storefile, many family, wildcard
-    get = new Get(ROWS[0]);
-    get.addFamily(FAMILIES[2]);
-    get.addFamily(FAMILIES[4]);
-    get.addFamily(FAMILIES[6]);
-    get.addFamily(FAMILIES[7]);
-    result = ht.get(get);
-    assertNResult(result, ROWS[0], FAMILIES, QUALIFIERS, VALUES,
-        new int [][] {
-          {2, 2, 2}, {2, 4, 4}, {4, 0, 0}, {4, 4, 4}, {6, 6, 6}, {6, 7, 7}, {7, 7, 7}
-        });
-
-    // Multiple columns from everywhere storefile, many family, wildcard
-    get = new Get(ROWS[0]);
-    get.addColumn(FAMILIES[2], QUALIFIERS[2]);
-    get.addColumn(FAMILIES[2], QUALIFIERS[4]);
-    get.addColumn(FAMILIES[4], QUALIFIERS[0]);
-    get.addColumn(FAMILIES[4], QUALIFIERS[4]);
-    get.addColumn(FAMILIES[6], QUALIFIERS[6]);
-    get.addColumn(FAMILIES[6], QUALIFIERS[7]);
-    get.addColumn(FAMILIES[7], QUALIFIERS[7]);
-    get.addColumn(FAMILIES[7], QUALIFIERS[8]);
-    result = ht.get(get);
-    assertNResult(result, ROWS[0], FAMILIES, QUALIFIERS, VALUES,
-        new int [][] {
-          {2, 2, 2}, {2, 4, 4}, {4, 0, 0}, {4, 4, 4}, {6, 6, 6}, {6, 7, 7}, {7, 7, 7}
-        });
-
-    // Everything
-    get = new Get(ROWS[0]);
-    result = ht.get(get);
-    assertNResult(result, ROWS[0], FAMILIES, QUALIFIERS, VALUES,
-        new int [][] {
-          {2, 2, 2}, {2, 4, 4}, {4, 0, 0}, {4, 4, 4}, {6, 6, 6}, {6, 7, 7}, {7, 7, 7}, {9, 0, 0}
-        });
-
-    // Get around inserted columns
-
-    get = new Get(ROWS[1]);
-    result = ht.get(get);
-    assertEmptyResult(result);
-
-    get = new Get(ROWS[0]);
-    get.addColumn(FAMILIES[4], QUALIFIERS[3]);
-    get.addColumn(FAMILIES[2], QUALIFIERS[3]);
-    result = ht.get(get);
-    assertEmptyResult(result);
-
-  }
-
-  private void singleRowScanTest(Table ht, byte [][] ROWS, byte [][] FAMILIES,
-      byte [][] QUALIFIERS, byte [][] VALUES) throws Exception {
-    // Single column from memstore
-    Scan scan = new Scan();
-    scan.addColumn(FAMILIES[4], QUALIFIERS[0]);
-    Result result = getSingleScanResult(ht, scan);
-    assertSingleResult(result, ROWS[0], FAMILIES[4], QUALIFIERS[0], VALUES[0]);
-
-    // Single column from storefile
-    scan = new Scan();
-    scan.addColumn(FAMILIES[2], QUALIFIERS[2]);
-    result = getSingleScanResult(ht, scan);
-    assertSingleResult(result, ROWS[0], FAMILIES[2], QUALIFIERS[2], VALUES[2]);
-
-    // Single column from storefile, family match
-    scan = new Scan();
-    scan.addFamily(FAMILIES[7]);
-    result = getSingleScanResult(ht, scan);
-    assertSingleResult(result, ROWS[0], FAMILIES[7], QUALIFIERS[7], VALUES[7]);
-
-    // Two columns, one from memstore one from storefile, same family,
-    // wildcard match
-    scan = new Scan();
-    scan.addFamily(FAMILIES[4]);
-    result = getSingleScanResult(ht, scan);
-    assertDoubleResult(result, ROWS[0], FAMILIES[4], QUALIFIERS[0], VALUES[0],
-        FAMILIES[4], QUALIFIERS[4], VALUES[4]);
-
-    // Two columns, one from memstore one from storefile, same family,
-    // explicit match
-    scan = new Scan();
-    scan.addColumn(FAMILIES[4], QUALIFIERS[0]);
-    scan.addColumn(FAMILIES[4], QUALIFIERS[4]);
-    result = getSingleScanResult(ht, scan);
-    assertDoubleResult(result, ROWS[0], FAMILIES[4], QUALIFIERS[0], VALUES[0],
-        FAMILIES[4], QUALIFIERS[4], VALUES[4]);
-
-    // Three column, one from memstore two from storefile, different families,
-    // wildcard match
-    scan = new Scan();
-    scan.addFamily(FAMILIES[4]);
-    scan.addFamily(FAMILIES[7]);
-    result = getSingleScanResult(ht, scan);
-    assertNResult(result, ROWS[0], FAMILIES, QUALIFIERS, VALUES,
-        new int [][] { {4, 0, 0}, {4, 4, 4}, {7, 7, 7} });
-
-    // Multiple columns from everywhere storefile, many family, wildcard
-    scan = new Scan();
-    scan.addFamily(FAMILIES[2]);
-    scan.addFamily(FAMILIES[4]);
-    scan.addFamily(FAMILIES[6]);
-    scan.addFamily(FAMILIES[7]);
-    result = getSingleScanResult(ht, scan);
-    assertNResult(result, ROWS[0], FAMILIES, QUALIFIERS, VALUES,
-        new int [][] {
-          {2, 2, 2}, {2, 4, 4}, {4, 0, 0}, {4, 4, 4}, {6, 6, 6}, {6, 7, 7}, {7, 7, 7}
-        });
-
-    // Multiple columns from everywhere storefile, many family, wildcard
-    scan = new Scan();
-    scan.addColumn(FAMILIES[2], QUALIFIERS[2]);
-    scan.addColumn(FAMILIES[2], QUALIFIERS[4]);
-    scan.addColumn(FAMILIES[4], QUALIFIERS[0]);
-    scan.addColumn(FAMILIES[4], QUALIFIERS[4]);
-    scan.addColumn(FAMILIES[6], QUALIFIERS[6]);
-    scan.addColumn(FAMILIES[6], QUALIFIERS[7]);
-    scan.addColumn(FAMILIES[7], QUALIFIERS[7]);
-    scan.addColumn(FAMILIES[7], QUALIFIERS[8]);
-    result = getSingleScanResult(ht, scan);
-    assertNResult(result, ROWS[0], FAMILIES, QUALIFIERS, VALUES,
-        new int [][] {
-          {2, 2, 2}, {2, 4, 4}, {4, 0, 0}, {4, 4, 4}, {6, 6, 6}, {6, 7, 7}, {7, 7, 7}
-        });
-
-    // Everything
-    scan = new Scan();
-    result = getSingleScanResult(ht, scan);
-    assertNResult(result, ROWS[0], FAMILIES, QUALIFIERS, VALUES,
-        new int [][] {
-          {2, 2, 2}, {2, 4, 4}, {4, 0, 0}, {4, 4, 4}, {6, 6, 6}, {6, 7, 7}, {7, 7, 7}, {9, 0, 0}
-        });
-
-    // Scan around inserted columns
-
-    scan = new Scan(ROWS[1]);
-    result = getSingleScanResult(ht, scan);
-    assertNullResult(result);
-
-    scan = new Scan();
-    scan.addColumn(FAMILIES[4], QUALIFIERS[3]);
-    scan.addColumn(FAMILIES[2], QUALIFIERS[3]);
-    result = getSingleScanResult(ht, scan);
-    assertNullResult(result);
-  }
-
-  /**
-   * Verify a single column using gets.
-   * Expects family and qualifier arrays to be valid for at least
-   * the range:  idx-2 < idx < idx+2
-   */
-  private void getVerifySingleColumn(Table ht, byte [][] ROWS, int ROWIDX, byte [][] FAMILIES,
-      int FAMILYIDX, byte [][] QUALIFIERS, int QUALIFIERIDX, byte [][] VALUES, int VALUEIDX)
-      throws Exception {
-    Get get = new Get(ROWS[ROWIDX]);
-    Result result = ht.get(get);
-    assertSingleResult(result, ROWS[ROWIDX], FAMILIES[FAMILYIDX],
-        QUALIFIERS[QUALIFIERIDX], VALUES[VALUEIDX]);
-
-    get = new Get(ROWS[ROWIDX]);
-    get.addFamily(FAMILIES[FAMILYIDX]);
-    result = ht.get(get);
-    assertSingleResult(result, ROWS[ROWIDX], FAMILIES[FAMILYIDX],
-        QUALIFIERS[QUALIFIERIDX], VALUES[VALUEIDX]);
-
-    get = new Get(ROWS[ROWIDX]);
-    get.addFamily(FAMILIES[FAMILYIDX-2]);
-    get.addFamily(FAMILIES[FAMILYIDX]);
-    get.addFamily(FAMILIES[FAMILYIDX+2]);
-    result = ht.get(get);
-    assertSingleResult(result, ROWS[ROWIDX], FAMILIES[FAMILYIDX],
-        QUALIFIERS[QUALIFIERIDX], VALUES[VALUEIDX]);
-
-    get = new Get(ROWS[ROWIDX]);
-    get.addColumn(FAMILIES[FAMILYIDX], QUALIFIERS[0]);
-    result = ht.get(get);
-    assertSingleResult(result, ROWS[ROWIDX], FAMILIES[FAMILYIDX],
-        QUALIFIERS[QUALIFIERIDX], VALUES[VALUEIDX]);
-
-    get = new Get(ROWS[ROWIDX]);
-    get.addColumn(FAMILIES[FAMILYIDX], QUALIFIERS[1]);
-    get.addFamily(FAMILIES[FAMILYIDX]);
-    result = ht.get(get);
-    assertSingleResult(result, ROWS[ROWIDX], FAMILIES[FAMILYIDX],
-        QUALIFIERS[QUALIFIERIDX], VALUES[VALUEIDX]);
-
-    get = new Get(ROWS[ROWIDX]);
-    get.addFamily(FAMILIES[FAMILYIDX]);
-    get.addColumn(FAMILIES[FAMILYIDX+1], QUALIFIERS[1]);
-    get.addColumn(FAMILIES[FAMILYIDX-2], QUALIFIERS[1]);
-    get.addFamily(FAMILIES[FAMILYIDX-1]);
-    get.addFamily(FAMILIES[FAMILYIDX+2]);
-    result = ht.get(get);
-    assertSingleResult(result, ROWS[ROWIDX], FAMILIES[FAMILYIDX],
-        QUALIFIERS[QUALIFIERIDX], VALUES[VALUEIDX]);
-
-  }
-
-
-  /**
-   * Verify a single column using scanners.
-   * Expects family and qualifier arrays to be valid for at least
-   * the range:  idx-2 to idx+2
-   * Expects row array to be valid for at least idx to idx+2
-   */
-  private void scanVerifySingleColumn(Table ht, byte [][] ROWS, int ROWIDX, byte [][] FAMILIES,
-      int FAMILYIDX, byte [][] QUALIFIERS, int QUALIFIERIDX, byte [][] VALUES, int VALUEIDX)
-      throws Exception {
-    Scan scan = new Scan();
-    Result result = getSingleScanResult(ht, scan);
-    assertSingleResult(result, ROWS[ROWIDX], FAMILIES[FAMILYIDX],
-        QUALIFIERS[QUALIFIERIDX], VALUES[VALUEIDX]);
-
-    scan = new Scan(ROWS[ROWIDX]);
-    result = getSingleScanResult(ht, scan);
-    assertSingleResult(result, ROWS[ROWIDX], FAMILIES[FAMILYIDX],
-        QUALIFIERS[QUALIFIERIDX], VALUES[VALUEIDX]);
-
-    scan = new Scan(ROWS[ROWIDX], ROWS[ROWIDX+1]);
-    result = getSingleScanResult(ht, scan);
-    assertSingleResult(result, ROWS[ROWIDX], FAMILIES[FAMILYIDX],
-        QUALIFIERS[QUALIFIERIDX], VALUES[VALUEIDX]);
-
-    scan = new Scan(HConstants.EMPTY_START_ROW, ROWS[ROWIDX+1]);
-    result = getSingleScanResult(ht, scan);
-    assertSingleResult(result, ROWS[ROWIDX], FAMILIES[FAMILYIDX],
-        QUALIFIERS[QUALIFIERIDX], VALUES[VALUEIDX]);
-
-    scan = new Scan();
-    scan.addFamily(FAMILIES[FAMILYIDX]);
-    result = getSingleScanResult(ht, scan);
-    assertSingleResult(result, ROWS[ROWIDX], FAMILIES[FAMILYIDX],
-        QUALIFIERS[QUALIFIERIDX], VALUES[VALUEIDX]);
-
-    scan = new Scan();
-    scan.addColumn(FAMILIES[FAMILYIDX], QUALIFIERS[QUALIFIERIDX]);
-    result = getSingleScanResult(ht, scan);
-    assertSingleResult(result, ROWS[ROWIDX], FAMILIES[FAMILYIDX],
-        QUALIFIERS[QUALIFIERIDX], VALUES[VALUEIDX]);
-
-    scan = new Scan();
-    scan.addColumn(FAMILIES[FAMILYIDX], QUALIFIERS[QUALIFIERIDX+1]);
-    scan.addFamily(FAMILIES[FAMILYIDX]);
-    result = getSingleScanResult(ht, scan);
-    assertSingleResult(result, ROWS[ROWIDX], FAMILIES[FAMILYIDX],
-        QUALIFIERS[QUALIFIERIDX], VALUES[VALUEIDX]);
-
-    scan = new Scan();
-    scan.addColumn(FAMILIES[FAMILYIDX-1], QUALIFIERS[QUALIFIERIDX+1]);
-    scan.addColumn(FAMILIES[FAMILYIDX], QUALIFIERS[QUALIFIERIDX]);
-    scan.addFamily(FAMILIES[FAMILYIDX+1]);
-    result = getSingleScanResult(ht, scan);
-    assertSingleResult(result, ROWS[ROWIDX], FAMILIES[FAMILYIDX],
-        QUALIFIERS[QUALIFIERIDX], VALUES[VALUEIDX]);
-
-  }
-
-  /**
-   * Verify we do not read any values by accident around a single column
-   * Same requirements as getVerifySingleColumn
-   */
-  private void getVerifySingleEmpty(Table ht, byte [][] ROWS, int ROWIDX, byte [][] FAMILIES,
-      int FAMILYIDX, byte [][] QUALIFIERS, int QUALIFIERIDX) throws Exception {
-    Get get = new Get(ROWS[ROWIDX]);
-    get.addFamily(FAMILIES[4]);
-    get.addColumn(FAMILIES[4], QUALIFIERS[1]);
-    Result result = ht.get(get);
-    assertEmptyResult(result);
-
-    get = new Get(ROWS[ROWIDX]);
-    get.addFamily(FAMILIES[4]);
-    get.addColumn(FAMILIES[4], QUALIFIERS[2]);
-    result = ht.get(get);
-    assertEmptyResult(result);
-
-    get = new Get(ROWS[ROWIDX]);
-    get.addFamily(FAMILIES[3]);
-    get.addColumn(FAMILIES[4], QUALIFIERS[2]);
-    get.addFamily(FAMILIES[5]);
-    result = ht.get(get);
-    assertEmptyResult(result);
-
-    get = new Get(ROWS[ROWIDX+1]);
-    result = ht.get(get);
-    assertEmptyResult(result);
-
-  }
-
-  private void scanVerifySingleEmpty(Table ht, byte [][] ROWS, int ROWIDX, byte [][] FAMILIES,
-      int FAMILYIDX, byte [][] QUALIFIERS, int QUALIFIERIDX) throws Exception {
-    Scan scan = new Scan(ROWS[ROWIDX+1]);
-    Result result = getSingleScanResult(ht, scan);
-    assertNullResult(result);
-
-    scan = new Scan(ROWS[ROWIDX+1],ROWS[ROWIDX+2]);
-    result = getSingleScanResult(ht, scan);
-    assertNullResult(result);
-
-    scan = new Scan(HConstants.EMPTY_START_ROW, ROWS[ROWIDX]);
-    result = getSingleScanResult(ht, scan);
-    assertNullResult(result);
-
-    scan = new Scan();
-    scan.addColumn(FAMILIES[FAMILYIDX], QUALIFIERS[QUALIFIERIDX+1]);
-    scan.addFamily(FAMILIES[FAMILYIDX-1]);
-    result = getSingleScanResult(ht, scan);
-    assertNullResult(result);
-
-  }
-
-  //
-  // Verifiers
-  //
-
-  private void assertKey(Cell key, byte [] row, byte [] family, byte [] qualifier, byte [] value) {
-    assertTrue("Expected row [" + Bytes.toString(row) + "] " +
-        "Got row [" + Bytes.toString(CellUtil.cloneRow(key)) +"]",
-        equals(row, CellUtil.cloneRow(key)));
-    assertTrue("Expected family [" + Bytes.toString(family) + "] " +
-        "Got family [" + Bytes.toString(CellUtil.cloneFamily(key)) + "]",
-        equals(family, CellUtil.cloneFamily(key)));
-    assertTrue("Expected qualifier [" + Bytes.toString(qualifier) + "] " +
-        "Got qualifier [" + Bytes.toString(CellUtil.cloneQualifier(key)) + "]",
-        equals(qualifier, CellUtil.cloneQualifier(key)));
-    assertTrue("Expected value [" + Bytes.toString(value) + "] " +
-        "Got value [" + Bytes.toString(CellUtil.cloneValue(key)) + "]",
-        equals(value, CellUtil.cloneValue(key)));
-  }
-
-  static void assertIncrementKey(Cell key, byte [] row, byte [] family,
-      byte [] qualifier, long value) {
-    assertTrue("Expected row [" + Bytes.toString(row) + "] " +
-        "Got row [" + Bytes.toString(CellUtil.cloneRow(key)) +"]",
-        equals(row, CellUtil.cloneRow(key)));
-    assertTrue("Expected family [" + Bytes.toString(family) + "] " +
-        "Got family [" + Bytes.toString(CellUtil.cloneFamily(key)) + "]",
-        equals(family, CellUtil.cloneFamily(key)));
-    assertTrue("Expected qualifier [" + Bytes.toString(qualifier) + "] " +
-        "Got qualifier [" + Bytes.toString(CellUtil.cloneQualifier(key)) + "]",
-        equals(qualifier, CellUtil.cloneQualifier(key)));
-    assertTrue("Expected value [" + value + "] " +
-        "Got value [" + Bytes.toLong(CellUtil.cloneValue(key)) + "]",
-        Bytes.toLong(CellUtil.cloneValue(key)) == value);
-  }
-
-  private void assertNumKeys(Result result, int n) throws Exception {
-    assertTrue("Expected " + n + " keys but got " + result.size(),
-        result.size() == n);
-  }
-
-  private void assertNResult(Result result, byte [] row,
-      byte [][] families, byte [][] qualifiers, byte [][] values, int [][] idxs) {
-    assertTrue("Expected row [" + Bytes.toString(row) + "] " +
-        "Got row [" + Bytes.toString(result.getRow()) +"]",
-        equals(row, result.getRow()));
-    assertTrue("Expected " + idxs.length + " keys but result contains "
-        + result.size(), result.size() == idxs.length);
-
-    Cell [] keys = result.rawCells();
-
-    for(int i=0;i<keys.length;i++) {
-      byte [] family = families[idxs[i][0]];
-      byte [] qualifier = qualifiers[idxs[i][1]];
-      byte [] value = values[idxs[i][2]];
-      Cell key = keys[i];
-
-      byte[] famb = CellUtil.cloneFamily(key);
-      byte[] qualb = CellUtil.cloneQualifier(key);
-      byte[] valb = CellUtil.cloneValue(key);
-      assertTrue("(" + i + ") Expected family [" + Bytes.toString(family)
-          + "] " + "Got family [" + Bytes.toString(famb) + "]",
-          equals(family, famb));
-      assertTrue("(" + i + ") Expected qualifier [" + Bytes.toString(qualifier)
-          + "] " + "Got qualifier [" + Bytes.toString(qualb) + "]",
-          equals(qualifier, qualb));
-      assertTrue("(" + i + ") Expected value [" + Bytes.toString(value) + "] "
-          + "Got value [" + Bytes.toString(valb) + "]",
-          equals(value, valb));
-    }
-  }
-
-  private void assertNResult(Result result, byte [] row,
-      byte [] family, byte [] qualifier, long [] stamps, byte [][] values,
-      int start, int end) {
-    assertTrue("Expected row [" + Bytes.toString(row) + "] " +
-        "Got row [" + Bytes.toString(result.getRow()) +"]",
-        equals(row, result.getRow()));
-    int expectedResults = end - start + 1;
-    assertEquals(expectedResults, result.size());
-
-    Cell[] keys = result.rawCells();
-
-    for (int i=0; i<keys.length; i++) {
-      byte [] value = values[end-i];
-      long ts = stamps[end-i];
-      Cell key = keys[i];
-
-      assertTrue("(" + i + ") Expected family [" + Bytes.toString(family)
-          + "] " + "Got family [" + Bytes.toString(CellUtil.cloneFamily(key)) + "]",
-          CellUtil.matchingFamily(key, family));
-      assertTrue("(" + i + ") Expected qualifier [" + Bytes.toString(qualifier)
-          + "] " + "Got qualifier [" + Bytes.toString(CellUtil.cloneQualifier(key))+ "]",
-          CellUtil.matchingQualifier(key, qualifier));
-      assertTrue("Expected ts [" + ts + "] " +
-          "Got ts [" + key.getTimestamp() + "]", ts == key.getTimestamp());
-      assertTrue("(" + i + ") Expected value [" + Bytes.toString(value) + "] "
-          + "Got value [" + Bytes.toString(CellUtil.cloneValue(key)) + "]",
-          CellUtil.matchingValue(key,  value));
-    }
-  }
-
-  /**
-   * Validate that result contains two specified keys, exactly.
-   * It is assumed key A sorts before key B.
-   */
-  private void assertDoubleResult(Result result, byte [] row,
-      byte [] familyA, byte [] qualifierA, byte [] valueA,
-      byte [] familyB, byte [] qualifierB, byte [] valueB) {
-    assertTrue("Expected row [" + Bytes.toString(row) + "] " +
-        "Got row [" + Bytes.toString(result.getRow()) +"]",
-        equals(row, result.getRow()));
-    assertTrue("Expected two keys but result contains " + result.size(),
-        result.size() == 2);
-    Cell [] kv = result.rawCells();
-    Cell kvA = kv[0];
-    assertTrue("(A) Expected family [" + Bytes.toString(familyA) + "] " +
-        "Got family [" + Bytes.toString(CellUtil.cloneFamily(kvA)) + "]",
-        equals(familyA, CellUtil.cloneFamily(kvA)));
-    assertTrue("(A) Expected qualifier [" + Bytes.toString(qualifierA) + "] " +
-        "Got qualifier [" + Bytes.toString(CellUtil.cloneQualifier(kvA)) + "]",
-        equals(qualifierA, CellUtil.cloneQualifier(kvA)));
-    assertTrue("(A) Expected value [" + Bytes.toString(valueA) + "] " +
-        "Got value [" + Bytes.toString(CellUtil.cloneValue(kvA)) + "]",
-        equals(valueA, CellUtil.cloneValue(kvA)));
-    Cell kvB = kv[1];
-    assertTrue("(B) Expected family [" + Bytes.toString(familyB) + "] " +
-        "Got family [" + Bytes.toString(CellUtil.cloneFamily(kvB)) + "]",
-        equals(familyB, CellUtil.cloneFamily(kvB)));
-    assertTrue("(B) Expected qualifier [" + Bytes.toString(qualifierB) + "] " +
-        "Got qualifier [" + Bytes.toString(CellUtil.cloneQualifier(kvB)) + "]",
-        equals(qualifierB, CellUtil.cloneQualifier(kvB)));
-    assertTrue("(B) Expected value [" + Bytes.toString(valueB) + "] " +
-        "Got value [" + Bytes.toString(CellUtil.cloneValue(kvB)) + "]",
-        equals(valueB, CellUtil.cloneValue(kvB)));
-  }
-
-  private void assertSingleResult(Result result, byte [] row, byte [] family,
-      byte [] qualifier, byte [] value) {
-    assertTrue("Expected row [" + Bytes.toString(row) + "] " +
-        "Got row [" + Bytes.toString(result.getRow()) +"]",
-        equals(row, result.getRow()));
-    assertTrue("Expected a single key but result contains " + result.size(),
-        result.size() == 1);
-    Cell kv = result.rawCells()[0];
-    assertTrue("Expected family [" + Bytes.toString(family) + "] " +
-        "Got family [" + Bytes.toString(CellUtil.cloneFamily(kv)) + "]",
-        equals(family, CellUtil.cloneFamily(kv)));
-    assertTrue("Expected qualifier [" + Bytes.toString(qualifier) + "] " +
-        "Got qualifier [" + Bytes.toString(CellUtil.cloneQualifier(kv)) + "]",
-        equals(qualifier, CellUtil.cloneQualifier(kv)));
-    assertTrue("Expected value [" + Bytes.toString(value) + "] " +
-        "Got value [" + Bytes.toString(CellUtil.cloneValue(kv)) + "]",
-        equals(value, CellUtil.cloneValue(kv)));
-  }
-
-  private void assertSingleResult(Result result, byte[] row, byte[] family, byte[] qualifier,
-      long value) throws Exception {
-    assertTrue(
-      "Expected row [" + Bytes.toString(row) + "] " + "Got row [" + Bytes.toString(result.getRow())
-          + "]", equals(row, result.getRow()));
-    assertTrue("Expected a single key but result contains " + result.size(), result.size() == 1);
-    Cell kv = result.rawCells()[0];
-    assertTrue(
-      "Expected family [" + Bytes.toString(family) + "] " + "Got family ["
-          + Bytes.toString(CellUtil.cloneFamily(kv)) + "]",
-      equals(family, CellUtil.cloneFamily(kv)));
-    assertTrue("Expected qualifier [" + Bytes.toString(qualifier) + "] " + "Got qualifier ["
-        + Bytes.toString(CellUtil.cloneQualifier(kv)) + "]",
-      equals(qualifier, CellUtil.cloneQualifier(kv)));
-    assertTrue(
-      "Expected value [" + value + "] " + "Got value [" + Bytes.toLong(CellUtil.cloneValue(kv))
-          + "]", value == Bytes.toLong(CellUtil.cloneValue(kv)));
-  }
-
-  private void assertSingleResult(Result result, byte [] row, byte [] family,
-      byte [] qualifier, long ts, byte [] value) {
-    assertTrue("Expected row [" + Bytes.toString(row) + "] " +
-        "Got row [" + Bytes.toString(result.getRow()) +"]",
-        equals(row, result.getRow()));
-    assertTrue("Expected a single key but result contains " + result.size(),
-        result.size() == 1);
-    Cell kv = result.rawCells()[0];
-    assertTrue("Expected family [" + Bytes.toString(family) + "] " +
-        "Got family [" + Bytes.toString(CellUtil.cloneFamily(kv)) + "]",
-        equals(family, CellUtil.cloneFamily(kv)));
-    assertTrue("Expected qualifier [" + Bytes.toString(qualifier) + "] " +
-        "Got qualifier [" + Bytes.toString(CellUtil.cloneQualifier(kv)) + "]",
-        equals(qualifier, CellUtil.cloneQualifier(kv)));
-    assertTrue("Expected ts [" + ts + "] " +
-        "Got ts [" + kv.getTimestamp() + "]", ts == kv.getTimestamp());
-    assertTrue("Expected value [" + Bytes.toString(value) + "] " +
-        "Got value [" + Bytes.toString(CellUtil.cloneValue(kv)) + "]",
-        equals(value, CellUtil.cloneValue(kv)));
-  }
-
-  private void assertEmptyResult(Result result) throws Exception {
-    assertTrue("expected an empty result but result contains " +
-        result.size() + " keys", result.isEmpty());
-  }
-
-  private void assertNullResult(Result result) throws Exception {
-    assertTrue("expected null result but received a non-null result",
-        result == null);
-  }
-
-  //
-  // Helpers
-  //
-
-  private Result getSingleScanResult(Table ht, Scan scan) throws IOException {
-    try (ResultScanner scanner = ht.getScanner(scan)) {
-      Result result = scanner.next();
-      return result;
-    }
-  }
-
-  private byte [][] makeNAscii(byte [] base, int n) {
-    if(n > 256) {
-      return makeNBig(base, n);
-    }
-    byte [][] ret = new byte[n][];
-    for(int i=0;i<n;i++) {
-      byte [] tail = Bytes.toBytes(Integer.toString(i));
-      ret[i] = Bytes.add(base, tail);
-    }
-    return ret;
-  }
-
-  private byte [][] makeN(byte [] base, int n) {
-    if (n > 256) {
-      return makeNBig(base, n);
-    }
-    byte [][] ret = new byte[n][];
-    for(int i=0;i<n;i++) {
-      ret[i] = Bytes.add(base, new byte[]{(byte)i});
-    }
-    return ret;
-  }
-
-  private byte [][] makeNBig(byte [] base, int n) {
-    byte [][] ret = new byte[n][];
-    for(int i=0;i<n;i++) {
-      int byteA = (i % 256);
-      int byteB = (i >> 8);
-      ret[i] = Bytes.add(base, new byte[]{(byte)byteB,(byte)byteA});
-    }
-    return ret;
-  }
-
-  private long [] makeStamps(int n) {
-    long [] stamps = new long[n];
-    for (int i = 0; i < n; i++) {
-      stamps[i] = i+1L;
-    }
-    return stamps;
-  }
-
-  static boolean equals(byte [] left, byte [] right) {
-    if (left == null && right == null) return true;
-    if (left == null && right.length == 0) return true;
-    if (right == null && left.length == 0) return true;
-    return Bytes.equals(left, right);
-  }
-
-  @Test
-  public void testDuplicateVersions() throws Exception {
-    final TableName tableName = name.getTableName();
-
-    long [] STAMPS = makeStamps(20);
-    byte [][] VALUES = makeNAscii(VALUE, 20);
-
-    try (Table ht = TEST_UTIL.createTable(tableName, FAMILY, 10)) {
-
-      // Insert 4 versions of same column
-      Put put = new Put(ROW);
-      put.addColumn(FAMILY, QUALIFIER, STAMPS[1], VALUES[1]);
-      put.addColumn(FAMILY, QUALIFIER, STAMPS[2], VALUES[2]);
-      put.addColumn(FAMILY, QUALIFIER, STAMPS[4], VALUES[4]);
-      put.addColumn(FAMILY, QUALIFIER, STAMPS[5], VALUES[5]);
-      ht.put(put);
-
-      // Verify we can get each one properly
-      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[1], VALUES[1]);
-      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[2], VALUES[2]);
-      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[4], VALUES[4]);
-      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[5], VALUES[5]);
-      scanVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[1], VALUES[1]);
-      scanVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[2], VALUES[2]);
-      scanVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[4], VALUES[4]);
-      scanVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[5], VALUES[5]);
-
-      // Verify we don't accidentally get others
-      getVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[0]);
-      getVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[3]);
-      getVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[6]);
-      scanVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[0]);
-      scanVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[3]);
-      scanVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[6]);
-
-      // Ensure maxVersions in query is respected
-      Get get = new Get(ROW);
-      get.addColumn(FAMILY, QUALIFIER);
-      get.setMaxVersions(2);
-      Result result = ht.get(get);
-      assertNResult(result, ROW, FAMILY, QUALIFIER,
-          new long [] {STAMPS[4], STAMPS[5]},
-          new byte[][] {VALUES[4], VALUES[5]},
-          0, 1);
-
-      Scan scan = new Scan(ROW);
-      scan.addColumn(FAMILY, QUALIFIER);
-      scan.setMaxVersions(2);
-      result = getSingleScanResult(ht, scan);
-      assertNResult(result, ROW, FAMILY, QUALIFIER,
-              new long[]{STAMPS[4], STAMPS[5]},
-              new byte[][]{VALUES[4], VALUES[5]},
-              0, 1);
-
-      // Flush and redo
-
-      TEST_UTIL.flush();
-
-      // Verify we can get each one properly
-      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[1], VALUES[1]);
-      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[2], VALUES[2]);
-      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[4], VALUES[4]);
-      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[5], VALUES[5]);
-      scanVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[1], VALUES[1]);
-      scanVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[2], VALUES[2]);
-      scanVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[4], VALUES[4]);
-      scanVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[5], VALUES[5]);
-
-      // Verify we don't accidentally get others
-      getVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[0]);
-      getVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[3]);
-      getVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[6]);
-      scanVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[0]);
-      scanVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[3]);
-      scanVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[6]);
-
-      // Ensure maxVersions in query is respected
-      get = new Get(ROW);
-      get.addColumn(FAMILY, QUALIFIER);
-      get.setMaxVersions(2);
-      result = ht.get(get);
-      assertNResult(result, ROW, FAMILY, QUALIFIER,
-          new long [] {STAMPS[4], STAMPS[5]},
-          new byte[][] {VALUES[4], VALUES[5]},
-          0, 1);
-
-      scan = new Scan(ROW);
-      scan.addColumn(FAMILY, QUALIFIER);
-      scan.setMaxVersions(2);
-      result = getSingleScanResult(ht, scan);
-      assertNResult(result, ROW, FAMILY, QUALIFIER,
-              new long[]{STAMPS[4], STAMPS[5]},
-              new byte[][]{VALUES[4], VALUES[5]},
-              0, 1);
-
-
-      // Add some memstore and retest
-
-      // Insert 4 more versions of same column and a dupe
-      put = new Put(ROW);
-      put.addColumn(FAMILY, QUALIFIER, STAMPS[3], VALUES[3]);
-      put.addColumn(FAMILY, QUALIFIER, STAMPS[4], VALUES[14]);
-      put.addColumn(FAMILY, QUALIFIER, STAMPS[6], VALUES[6]);
-      put.addColumn(FAMILY, QUALIFIER, STAMPS[7], VALUES[7]);
-      put.addColumn(FAMILY, QUALIFIER, STAMPS[8], VALUES[8]);
-      ht.put(put);
-
-      // Ensure maxVersions in query is respected
-      get = new Get(ROW);
-      get.addColumn(FAMILY, QUALIFIER);
-      get.setMaxVersions(7);
-      result = ht.get(get);
-      assertNResult(result, ROW, FAMILY, QUALIFIER,
-          new long [] {STAMPS[2], STAMPS[3], STAMPS[4], STAMPS[5], STAMPS[6], STAMPS[7], STAMPS[8]},
-          new byte[][] {VALUES[2], VALUES[3], VALUES[14], VALUES[5], VALUES[6], VALUES[7],
-                  VALUES[8]},
-          0, 6);
-
-      scan = new Scan(ROW);
-      scan.addColumn(FAMILY, QUALIFIER);
-      scan.setMaxVersions(7);
-      result = getSingleScanResult(ht, scan);
-      assertNResult(result, ROW, FAMILY, QUALIFIER,
-        new long[]{STAMPS[2], STAMPS[3], STAMPS[4], STAMPS[5], STAMPS[6], STAMPS[7], STAMPS[8]},
-        new byte[][]{VALUES[2], VALUES[3], VALUES[14], VALUES[5], VALUES[6], VALUES[7], VALUES[8]},
-        0, 6);
-
-      get = new Get(ROW);
-      get.setMaxVersions(7);
-      result = ht.get(get);
-      assertNResult(result, ROW, FAMILY, QUALIFIER,
-          new long [] {STAMPS[2], STAMPS[3], STAMPS[4], STAMPS[5], STAMPS[6], STAMPS[7], STAMPS[8]},
-          new byte[][] {VALUES[2], VALUES[3], VALUES[14], VALUES[5], VALUES[6], VALUES[7],
-                  VALUES[8]},
-          0, 6);
-
-      scan = new Scan(ROW);
-      scan.setMaxVersions(7);
-      result = getSingleScanResult(ht, scan);
-      assertNResult(result, ROW, FAMILY, QUALIFIER,
-        new long[]{STAMPS[2], STAMPS[3], STAMPS[4], STAMPS[5], STAMPS[6], STAMPS[7], STAMPS[8]},
-        new byte[][]{VALUES[2], VALUES[3], VALUES[14], VALUES[5], VALUES[6], VALUES[7], VALUES[8]},
-        0, 6);
-
-      // Verify we can get each one properly
-      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[1], VALUES[1]);
-      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[2], VALUES[2]);
-      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[4], VALUES[14]);
-      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[7], VALUES[7]);
-      scanVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[1], VALUES[1]);
-      scanVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[2], VALUES[2]);
-      scanVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[4], VALUES[14]);
-      scanVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[7], VALUES[7]);
-
-      // Verify we don't accidentally get others
-      getVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[0]);
-      getVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[9]);
-      scanVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[0]);
-      scanVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[9]);
-
-      // Ensure maxVersions of table is respected
-
-      TEST_UTIL.flush();
-
-      // Insert 4 more versions of same column and a dupe
-      put = new Put(ROW);
-      put.addColumn(FAMILY, QUALIFIER, STAMPS[9], VALUES[9]);
-      put.addColumn(FAMILY, QUALIFIER, STAMPS[11], VALUES[11]);
-      put.addColumn(FAMILY, QUALIFIER, STAMPS[13], VALUES[13]);
-      put.addColumn(FAMILY, QUALIFIER, STAMPS[15], VALUES[15]);
-      ht.put(put);
-
-      get = new Get(ROW);
-      get.addColumn(FAMILY, QUALIFIER);
-      get.setMaxVersions(Integer.MAX_VALUE);
-      result = ht.get(get);
-      assertNResult(result, ROW, FAMILY, QUALIFIER,
-          new long [] {STAMPS[3], STAMPS[4], STAMPS[5], STAMPS[6], STAMPS[7], STAMPS[8], STAMPS[9],
-                  STAMPS[11], STAMPS[13], STAMPS[15]},
-          new byte[][] {VALUES[3], VALUES[14], VALUES[5], VALUES[6], VALUES[7], VALUES[8],
-                  VALUES[9], VALUES[11], VALUES[13], VALUES[15]},
-          0, 9);
-
-      scan = new Scan(ROW);
-      scan.addColumn(FAMILY, QUALIFIER);
-      scan.setMaxVersions(Integer.MAX_VALUE);
-      result = getSingleScanResult(ht, scan);
-      assertNResult(result, ROW, FAMILY, QUALIFIER,
-          new long[]{STAMPS[3], STAMPS[4], STAMPS[5], STAMPS[6], STAMPS[7], STAMPS[8], STAMPS[9],
-                  STAMPS[11], STAMPS[13], STAMPS[15]},
-          new byte[][]{VALUES[3], VALUES[14], VALUES[5], VALUES[6], VALUES[7], VALUES[8], VALUES[9],
-                  VALUES[11], VALUES[13], VALUES[15]},0, 9);
-
-      // Delete a version in the memstore and a version in a storefile
-      Delete delete = new Delete(ROW);
-      delete.addColumn(FAMILY, QUALIFIER, STAMPS[11]);
-      delete.addColumn(FAMILY, QUALIFIER, STAMPS[7]);
-      ht.delete(delete);
-
-      // Test that it's gone
-      get = new Get(ROW);
-      get.addColumn(FAMILY, QUALIFIER);
-      get.setMaxVersions(Integer.MAX_VALUE);
-      result = ht.get(get);
-      assertNResult(result, ROW, FAMILY, QUALIFIER,
-          new long [] {STAMPS[1], STAMPS[2], STAMPS[3], STAMPS[4], STAMPS[5], STAMPS[6], STAMPS[8],
-                  STAMPS[9], STAMPS[13], STAMPS[15]},
-          new byte[][] {VALUES[1], VALUES[2], VALUES[3], VALUES[14], VALUES[5], VALUES[6],
-                  VALUES[8], VALUES[9], VALUES[13], VALUES[15]},
-          0, 9);
-
-      scan = new Scan(ROW);
-      scan.addColumn(FAMILY, QUALIFIER);
-      scan.setMaxVersions(Integer.MAX_VALUE);
-      result = getSingleScanResult(ht, scan);
-      assertNResult(result, ROW, FAMILY, QUALIFIER,
-          new long[]{STAMPS[1], STAMPS[2], STAMPS[3], STAMPS[4], STAMPS[5], STAMPS[6], STAMPS[8],
-                  STAMPS[9], STAMPS[13], STAMPS[15]},
-          new byte[][]{VALUES[1], VALUES[2], VALUES[3], VALUES[14], VALUES[5], VALUES[6], VALUES[8],
-                  VALUES[9], VALUES[13], VALUES[15]},0,9);
-    }
-  }
-
-  @Test
-  public void testUpdates() throws Exception {
-    final TableName tableName = name.getTableName();
-    try (Table hTable = TEST_UTIL.createTable(tableName, FAMILY, 10)) {
-
-      // Write a column with values at timestamp 1, 2 and 3
-      byte[] row = Bytes.toBytes("row1");
-      byte[] qualifier = Bytes.toBytes("myCol");
-      Put put = new Put(row);
-      put.addColumn(FAMILY, qualifier, 1L, Bytes.toBytes("AAA"));
-      hTable.put(put);
-
-      put = new Put(row);
-      put.addColumn(FAMILY, qualifier, 2L, Bytes.toBytes("BBB"));
-      hTable.put(put);
-
-      put = new Put(row);
-      put.addColumn(FAMILY, qualifier, 3L, Bytes.toBytes("EEE"));
-      hTable.put(put);
-
-      Get get = new Get(row);
-      get.addColumn(FAMILY, qualifier);
-      get.setMaxVersions();
-
-      // Check that the column indeed has the right values at timestamps 1 and
-      // 2
-      Result result = hTable.get(get);
-      NavigableMap<Long, byte[]> navigableMap =
-              result.getMap().get(FAMILY).get(qualifier);
-      assertEquals("AAA", Bytes.toString(navigableMap.get(1L)));
-      assertEquals("BBB", Bytes.toString(navigableMap.get(2L)));
-
-      // Update the value at timestamp 1
-      put = new Put(row);
-      put.addColumn(FAMILY, qualifier, 1L, Bytes.toBytes("CCC"));
-      hTable.put(put);
-
-      // Update the value at timestamp 2
-      put = new Put(row);
-      put.addColumn(FAMILY, qualifier, 2L, Bytes.toBytes("DDD"));
-      hTable.put(put);
-
-      // Check that the values at timestamp 2 and 1 got updated
-      result = hTable.get(get);
-      navigableMap = result.getMap().get(FAMILY).get(qualifier);
-      assertEquals("CCC", Bytes.toString(navigableMap.get(1L)));
-      assertEquals("DDD", Bytes.toString(navigableMap.get(2L)));
-    }
-  }
-
-  @Test
-  public void testUpdatesWithMajorCompaction() throws Exception {
-    final TableName tableName = name.getTableName();
-    try (Table hTable = TEST_UTIL.createTable(tableName, FAMILY, 10);
-        Admin admin = TEST_UTIL.getAdmin()) {
-
-      // Write a column with values at timestamp 1, 2 and 3
-      byte[] row = Bytes.toBytes("row2");
-      byte[] qualifier = Bytes.toBytes("myCol");
-      Put put = new Put(row);
-      put.addColumn(FAMILY, qualifier, 1L, Bytes.toBytes("AAA"));
-      hTable.put(put);
-
-      put = new Put(row);
-      put.addColumn(FAMILY, qualifier, 2L, Bytes.toBytes("BBB"));
-      hTable.put(put);
-
-      put = new Put(row);
-      put.addColumn(FAMILY, qualifier, 3L, Bytes.toBytes("EEE"));
-      hTable.put(put);
-
-      Get get = new Get(row);
-      get.addColumn(FAMILY, qualifier);
-      get.setMaxVersions();
-
-      // Check that the column indeed has the right values at timestamps 1 and
-      // 2
-      Result result = hTable.get(get);
-      NavigableMap<Long, byte[]> navigableMap =
-              result.getMap().get(FAMILY).get(qualifier);
-      assertEquals("AAA", Bytes.toString(navigableMap.get(1L)));
-      assertEquals("BBB", Bytes.toString(navigableMap.get(2L)));
-
-      // Trigger a major compaction
-      admin.flush(tableName);
-      admin.majorCompact(tableName);
-      Thread.sleep(6000);
-
-      // Update the value at timestamp 1
-      put = new Put(row);
-      put.addColumn(FAMILY, qualifier, 1L, Bytes.toBytes("CCC"));
-      hTable.put(put);
-
-      // Update the value at timestamp 2
-      put = new Put(row);
-      put.addColumn(FAMILY, qualifier, 2L, Bytes.toBytes("DDD"));
-      hTable.put(put);
-
-      // Trigger a major compaction
-      admin.flush(tableName);
-      admin.majorCompact(tableName);
-      Thread.sleep(6000);
-
-      // Check that the values at timestamp 2 and 1 got updated
-      result = hTable.get(get);
-      navigableMap = result.getMap().get(FAMILY).get(qualifier);
-      assertEquals("CCC", Bytes.toString(navigableMap.get(1L)));
-      assertEquals("DDD", Bytes.toString(navigableMap.get(2L)));
-    }
-  }
-
-  @Test
-  public void testMajorCompactionBetweenTwoUpdates() throws Exception {
-    final TableName tableName = name.getTableName();
-    try (Table hTable = TEST_UTIL.createTable(tableName, FAMILY, 10);
-        Admin admin = TEST_UTIL.getAdmin()) {
-
-      // Write a column with values at timestamp 1, 2 and 3
-      byte[] row = Bytes.toBytes("row3");
-      byte[] qualifier = Bytes.toBytes("myCol");
-      Put put = new Put(row);
-      put.addColumn(FAMILY, qualifier, 1L, Bytes.toBytes("AAA"));
-      hTable.put(put);
-
-      put = new Put(row);
-      put.addColumn(FAMILY, qualifier, 2L, Bytes.toBytes("BBB"));
-      hTable.put(put);
-
-      put = new Put(row);
-      put.addColumn(FAMILY, qualifier, 3L, Bytes.toBytes("EEE"));
-      hTable.put(put);
-
-      Get get = new Get(row);
-      get.addColumn(FAMILY, qualifier);
-      get.setMaxVersions();
-
-      // Check that the column indeed has the right values at timestamps 1 and
-      // 2
-      Result result = hTable.get(get);
-      NavigableMap<Long, byte[]> navigableMap =
-              result.getMap().get(FAMILY).get(qualifier);
-      assertEquals("AAA", Bytes.toString(navigableMap.get(1L)));
-      assertEquals("BBB", Bytes.toString(navigableMap.get(2L)));
-
-      // Trigger a major compaction
-      admin.flush(tableName);
-      admin.majorCompact(tableName);
-      Thread.sleep(6000);
-
-      // Update the value at timestamp 1
-      put = new Put(row);
-      put.addColumn(FAMILY, qualifier, 1L, Bytes.toBytes("CCC"));
-      hTable.put(put);
-
-      // Trigger a major compaction
-      admin.flush(tableName);
-      admin.majorCompact(tableName);
-      Thread.sleep(6000);
-
-      // Update the value at timestamp 2
-      put = new Put(row);
-      put.addColumn(FAMILY, qualifier, 2L, Bytes.toBytes("DDD"));
-      hTable.put(put);
-
-      // Trigger a major compaction
-      admin.flush(tableName);
-      admin.majorCompact(tableName);
-      Thread.sleep(6000);
-
-      // Check that the values at timestamp 2 and 1 got updated
-      result = hTable.get(get);
-      navigableMap = result.getMap().get(FAMILY).get(qualifier);
-
-      assertEquals("CCC", Bytes.toString(navigableMap.get(1L)));
-      assertEquals("DDD", Bytes.toString(navigableMap.get(2L)));
-    }
-  }
-
-  @Test
-  public void testGet_EmptyTable() throws IOException {
-    try (Table table = TEST_UTIL.createTable(name.getTableName(), FAMILY)) {
-      Get get = new Get(ROW);
-      get.addFamily(FAMILY);
-      Result r = table.get(get);
-      assertTrue(r.isEmpty());
-    }
-  }
-
-  @Test
-  public void testGet_NullQualifier() throws IOException {
-    try (Table table = TEST_UTIL.createTable(name.getTableName(), FAMILY)) {
-      Put put = new Put(ROW);
-      put.addColumn(FAMILY, QUALIFIER, VALUE);
-      table.put(put);
-
-      put = new Put(ROW);
-      put.addColumn(FAMILY, null, VALUE);
-      table.put(put);
-      LOG.info("Row put");
-
-      Get get = new Get(ROW);
-      get.addColumn(FAMILY, null);
-      Result r = table.get(get);
-      assertEquals(1, r.size());
-
-      get = new Get(ROW);
-      get.addFamily(FAMILY);
-      r = table.get(get);
-      assertEquals(2, r.size());
-    }
-  }
-
-  @Test
-  public void testGet_NonExistentRow() throws IOException {
-    try (Table table = TEST_UTIL.createTable(name.getTableName(), FAMILY)) {
-      Put put = new Put(ROW);
-      put.addColumn(FAMILY, QUALIFIER, VALUE);
-      table.put(put);
-      LOG.info("Row put");
-
-      Get get = new Get(ROW);
-      get.addFamily(FAMILY);
-      Result r = table.get(get);
-      assertFalse(r.isEmpty());
-      System.out.println("Row retrieved successfully");
-
-      byte[] missingrow = Bytes.toBytes("missingrow");
-      get = new Get(missingrow);
-      get.addFamily(FAMILY);
-      r = table.get(get);
-      assertTrue(r.isEmpty());
-      LOG.info("Row missing as it should be");
-    }
-  }
-
-  @Test
-  public void testPut() throws IOException {
-    final byte [] CONTENTS_FAMILY = Bytes.toBytes("contents");
-    final byte [] SMALL_FAMILY = Bytes.toBytes("smallfam");
-    final byte [] row1 = Bytes.toBytes("row1");
-    final byte [] row2 = Bytes.toBytes("row2");
-    final byte [] value = Bytes.toBytes("abcd");
-    try (Table table = TEST_UTIL.createTable(name.getTableName(),
-        new byte[][] { CONTENTS_FAMILY, SMALL_FAMILY })) {
-      Put put = new Put(row1);
-      put.addColumn(CONTENTS_FAMILY, null, value);
-      table.put(put);
-
-      put = new Put(row2);
-      put.addColumn(CONTENTS_FAMILY, null, value);
-
-      assertEquals(1, put.size());
-      assertEquals(1, put.getFamilyCellMap().get(CONTENTS_FAMILY).size());
-
-      // KeyValue v1 expectation.  Cast for now until we go all Cell all the time. TODO
-      KeyValue kv = (KeyValue) put.getFamilyCellMap().get(CONTENTS_FAMILY).get(0);
-
-      assertTrue(Bytes.equals(CellUtil.cloneFamily(kv), CONTENTS_FAMILY));
-      // will it return null or an empty byte array?
-      assertTrue(Bytes.equals(CellUtil.cloneQualifier(kv), new byte[0]));
-
-      assertTrue(Bytes.equals(CellUtil.cloneValue(kv), value));
-
-      table.put(put);
-
-      Scan scan = new Scan();
-      scan.addColumn(CONTENTS_FAMILY, null);
-      try (ResultScanner scanner = table.getScanner(scan)) {
-        for (Result r : scanner) {
-          for (Cell key : r.rawCells()) {
-            System.out.println(Bytes.toString(r.getRow()) + ": " + key.toString());
-          }
-        }
-      }
-    }
-  }
-
-  @Test
-  public void testPutNoCF() throws IOException {
-    final byte[] BAD_FAM = Bytes.toBytes("BAD_CF");
-    final byte[] VAL = Bytes.toBytes(100);
-    try (Table table = TEST_UTIL.createTable(name.getTableName(), FAMILY)) {
-      boolean caughtNSCFE = false;
-
-      try {
-        Put p = new Put(ROW);
-        p.addColumn(BAD_FAM, QUALIFIER, VAL);
-        table.put(p);
-      } catch (Exception e) {
-        caughtNSCFE = e instanceof NoSuchColumnFamilyException;
-      }
-      assertTrue("Should throw NoSuchColumnFamilyException", caughtNSCFE);
-    }
-  }
-
-  @Test
-  public void testRowsPut() throws IOException {
-    final byte[] CONTENTS_FAMILY = Bytes.toBytes("contents");
-    final byte[] SMALL_FAMILY = Bytes.toBytes("smallfam");
-    final int NB_BATCH_ROWS = 10;
-    final byte[] value = Bytes.toBytes("abcd");
-    try (Table table = TEST_UTIL.createTable(name.getTableName(),
-      new byte[][] {CONTENTS_FAMILY, SMALL_FAMILY })) {
-      ArrayList<Put> rowsUpdate = new ArrayList<Put>();
-      for (int i = 0; i < NB_BATCH_ROWS; i++) {
-        byte[] row = Bytes.toBytes("row" + i);
-        Put put = new Put(row);
-        put.setDurability(Durability.SKIP_WAL);
-        put.addColumn(CONTENTS_FAMILY, null, value);
-        rowsUpdate.add(put);
-      }
-      table.put(rowsUpdate);
-      Scan scan = new Scan();
-      scan.addFamily(CONTENTS_FAMILY);
-      try (ResultScanner scanner = table.getScanner(scan)) {
-        int nbRows = 0;
-        for (@SuppressWarnings("unused")
-                Result row : scanner) {
-          nbRows++;
-        }
-        assertEquals(NB_BATCH_ROWS, nbRows);
-      }
-    }
-  }
-
-  @Test
-  public void testRowsPutBufferedManyManyFlushes() throws IOException {
-    final byte[] CONTENTS_FAMILY = Bytes.toBytes("contents");
-    final byte[] SMALL_FAMILY = Bytes.toBytes("smallfam");
-    final byte[] value = Bytes.toBytes("abcd");
-    final int NB_BATCH_ROWS = 10;
-    try (Table table = TEST_UTIL.createTable(name.getTableName(),
-        new byte[][] { CONTENTS_FAMILY, SMALL_FAMILY })) {
-      ArrayList<Put> rowsUpdate = new ArrayList<Put>();
-      for (int i = 0; i < NB_BATCH_ROWS * 10; i++) {
-        byte[] row = Bytes.toBytes("row" + i);
-        Put put = new Put(row);
-        put.setDurability(Durability.SKIP_WAL);
-        put.addColumn(CONTENTS_FAMILY, null, value);
-        rowsUpdate.add(put);
-      }
-      table.put(rowsUpdate);
-
-      Scan scan = new Scan();
-      scan.addFamily(CONTENTS_FAMILY);
-      try (ResultScanner scanner = table.getScanner(scan)) {
-        int nbRows = 0;
-        for (@SuppressWarnings("unused")
-                Result row : scanner) {
-          nbRows++;
-        }
-        assertEquals(NB_BATCH_ROWS * 10, nbRows);
-      }
-    }
-  }
-
-  @Test
-  public void testAddKeyValue() throws IOException {
-    final byte[] CONTENTS_FAMILY = Bytes.toBytes("contents");
-    final byte[] value = Bytes.toBytes("abcd");
-    final byte[] row1 = Bytes.toBytes("row1");
-    final byte[] row2 = Bytes.toBytes("row2");
-    byte[] qualifier = Bytes.toBytes("qf1");
-    Put put = new Put(row1);
-
-    // Adding KeyValue with the same row
-    KeyValue kv = new KeyValue(row1, CONTENTS_FAMILY, qualifier, value);
-    boolean ok = true;
-    try {
-      put.add(kv);
-    } catch (IOException e) {
-      ok = false;
-    }
-    assertEquals(true, ok);
-
-    // Adding KeyValue with the different row
-    kv = new KeyValue(row2, CONTENTS_FAMILY, qualifier, value);
-    ok = false;
-    try {
-      put.add(kv);
-    } catch (IOException e) {
-      ok = true;
-    }
-    assertEquals(true, ok);
-  }
-
-  /**
-   * test for HBASE-737
-   */
-  @Test
-  public void testHBase737 () throws IOException {
-    final byte [] FAM1 = Bytes.toBytes("fam1");
-    final byte [] FAM2 = Bytes.toBytes("fam2");
-    // Open table
-    try (Table table = TEST_UTIL.createTable(name.getTableName(),
-      new byte [][] {FAM1, FAM2})) {
-      // Insert some values
-      Put put = new Put(ROW);
-      put.addColumn(FAM1, Bytes.toBytes("letters"), Bytes.toBytes("abcdefg"));
-      table.put(put);
-      try {
-        Thread.sleep(1000);
-      } catch (InterruptedException i) {
-        //ignore
-      }
-
-      put = new Put(ROW);
-      put.addColumn(FAM1, Bytes.toBytes("numbers"), Bytes.toBytes("123456"));
-      table.put(put);
-
-      try {
-        Thread.sleep(1000);
-      } catch (InterruptedException i) {
-        //ignore
-      }
-
-      put = new Put(ROW);
-      put.addColumn(FAM2, Bytes.toBytes("letters"), Bytes.toBytes("hijklmnop"));
-      table.put(put);
-
-      long[] times = new long[3];
-
-      // First scan the memstore
-
-      Scan scan = new Scan();
-      scan.addFamily(FAM1);
-      scan.addFamily(FAM2);
-      try (ResultScanner s = table.getScanner(scan)) {
-        int index = 0;
-        Result r = null;
-        while ((r = s.next()) != null) {
-          for (Cell key : r.rawCells()) {
-            times[index++] = key.getTimestamp();
-          }
-        }
-      }
-      for (int i = 0; i < times.length - 1; i++) {
-        for (int j = i + 1; j < times.length; j++) {
-          assertTrue(times[j] > times[i]);
-        }
-      }
-
-      // Flush data to disk and try again
-      TEST_UTIL.flush();
-
-      // Reset times
-      for (int i = 0; i < times.length; i++) {
-        times[i] = 0;
-      }
-
-      try {
-        Thread.sleep(1000);
-      } catch (InterruptedException i) {
-        //ignore
-      }
-      scan = new Scan();
-      scan.addFamily(FAM1);
-      scan.addFamily(FAM2);
-      try (ResultScanner s = table.getScanner(scan)) {
-        int index = 0;
-        Result r = null;
-        while ((r = s.next()) != null) {
-          for (Cell key : r.rawCells()) {
-            times[index++] = key.getTimestamp();
-          }
-        }
-        for (int i = 0; i < times.length - 1; i++) {
-          for (int j = i + 1; j < times.length; j++) {
-            assertTrue(times[j] > times[i]);
-          }
-        }
-      }
-    }
-  }
-
-  @Test
-  public void testListTables() throws IOException, InterruptedException {
-    final String testTableName = name.getTableName().toString();
-    final TableName tableName1 = TableName.valueOf(testTableName + "1");
-    final TableName tableName2 = TableName.valueOf(testTableName + "2");
-    final TableName tableName3 = TableName.valueOf(testTableName + "3");
-    TableName [] tables = new TableName[] { tableName1, tableName2, tableName3 };
-    for (int i = 0; i < tables.length; i++) {
-      TEST_UTIL.createTable(tables[i], FAMILY);
-    }
-    try (Admin admin = TEST_UTIL.getAdmin()) {
-      List<TableDescriptor> ts = admin.listTableDescriptors();
-      HashSet<TableDescriptor> result = new HashSet<>(ts);
-      int size = result.size();
-      assertTrue(size >= tables.length);
-      for (int i = 0; i < tables.length && i < size; i++) {
-        boolean found = false;
-        for (int j = 0; j < ts.size(); j++) {
-          if (ts.get(j).getTableName().equals(tables[i])) {
-            found = true;
-            break;
-          }
-        }
-        assertTrue("Not found: " + tables[i], found);
-      }
-    }
-  }
-
-  /**
-   * simple test that just executes parts of the client
-   * API that accept a pre-created Connection instance
-   */
-  @Test
-  public void testUnmanagedHConnection() throws IOException {
-    final TableName tableName = name.getTableName();
-    TEST_UTIL.createTable(tableName, HConstants.CATALOG_FAMILY);
-    try (Connection conn = ConnectionFactory.createConnection(TEST_UTIL.getConfiguration());
-        Table t = conn.getTable(tableName);
-        Admin admin = conn.getAdmin()) {
-      assertTrue(admin.tableExists(tableName));
-      assertTrue(t.get(new Get(ROW)).isEmpty());
-    }
-  }
-
-  /**
-   * test of that unmanaged HConnections are able to reconnect
-   * properly (see HBASE-5058)
-   */
-  @Test
-  public void testUnmanagedHConnectionReconnect() throws Exception {
-    Configuration conf = TEST_UTIL.getConfiguration();
-    Class registryImpl = conf.getClass(
-        HConstants.CLIENT_CONNECTION_REGISTRY_IMPL_CONF_KEY, ZKConnectionRegistry.class);
-    // This test does not make sense for MasterRegistry since it stops the only master in the
-    // cluster and starts a new master without populating the underlying config for the connection.
-    Assume.assumeFalse(registryImpl.equals(MasterRegistry.class));
-    final TableName tableName = name.getTableName();
-    TEST_UTIL.createTable(tableName, HConstants.CATALOG_FAMILY);
-    try (Connection conn = ConnectionFactory.createConnection(TEST_UTIL.getConfiguration())) {
-      try (Table t = conn.getTable(tableName);
-           Admin admin = conn.getAdmin()) {
-        assertTrue(admin.tableExists(tableName));
-        assertTrue(t.get(new Get(ROW)).isEmpty());
-      }
-
-      // stop the master
-      MiniHBaseCluster cluster = TEST_UTIL.getHBaseCluster();
-      cluster.stopMaster(0, false);
-      cluster.waitOnMaster(0);
-
-      // start up a new master
-      cluster.startMaster();
-      assertTrue(cluster.waitForActiveAndReadyMaster());
-
-      // test that the same unmanaged connection works with a new
-      // Admin and can connect to the new master;
-      boolean tablesOnMaster = LoadBalancer.isTablesOnMaster(TEST_UTIL.getConfiguration());
-      try (Admin admin = conn.getAdmin()) {
-        assertTrue(admin.tableExists(tableName));
-        assertTrue(admin.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS))
-                .getLiveServerMetrics().size() == SLAVES + (tablesOnMaster ? 1 : 0));
-      }
-    }
-  }
-
-  @Test
-  public void testMiscHTableStuff() throws IOException {
-    final String testTableName = name.getTableName().toString();
-    final TableName tableAname = TableName.valueOf(testTableName + "A");
-    final TableName tableBname = TableName.valueOf(testTableName + "B");
-    final byte[] attrName = Bytes.toBytes("TESTATTR");
-    final byte[] attrValue = Bytes.toBytes("somevalue");
-    byte[] value = Bytes.toBytes("value");
-
-    try (Table a = TEST_UTIL.createTable(tableAname, HConstants.CATALOG_FAMILY);
-        Table b = TEST_UTIL.createTable(tableBname, HConstants.CATALOG_FAMILY)) {
-      Put put = new Put(ROW);
-      put.addColumn(HConstants.CATALOG_FAMILY, null, value);
-      a.put(put);
-
-      // open a new connection to A and a connection to b
-      try (Table newA = TEST_UTIL.getConnection().getTable(tableAname)) {
-
-        // copy data from A to B
-        Scan scan = new Scan();
-        scan.addFamily(HConstants.CATALOG_FAMILY);
-        try (ResultScanner s = newA.getScanner(scan)) {
-          for (Result r : s) {
-            put = new Put(r.getRow());
-            put.setDurability(Durability.SKIP_WAL);
-            for (Cell kv : r.rawCells()) {
-              put.add(kv);
-            }
-            b.put(put);
-          }
-        }
-      }
-
-      // Opening a new connection to A will cause the tables to be reloaded
-      try (Table anotherA = TEST_UTIL.getConnection().getTable(tableAname)) {
-        Get get = new Get(ROW);
-        get.addFamily(HConstants.CATALOG_FAMILY);
-        anotherA.get(get);
-      }
-
-      // We can still access A through newA because it has the table information
-      // cached. And if it needs to recalibrate, that will cause the information
-      // to be reloaded.
-
-      // Test user metadata
-      try (Admin admin = TEST_UTIL.getAdmin()) {
-        // make a modifiable descriptor
-        HTableDescriptor desc = new HTableDescriptor(a.getTableDescriptor());
-        // offline the table
-        admin.disableTable(tableAname);
-        // add a user attribute to HTD
-        desc.setValue(attrName, attrValue);
-        // add a user attribute to HCD
-        for (HColumnDescriptor c : desc.getFamilies()) {
-          c.setValue(attrName, attrValue);
-        }
-        // update metadata for all regions of this table
-        admin.modifyTable(desc);
-        // enable the table
-        admin.enableTable(tableAname);
-      }
-      // Test that attribute changes were applied
-      HTableDescriptor desc = a.getTableDescriptor();
-      assertEquals("wrong table descriptor returned", desc.getTableName(), tableAname);
-      // check HTD attribute
-      value = desc.getValue(attrName);
-      assertFalse("missing HTD attribute value", value == null);
-      assertFalse("HTD attribute value is incorrect",
-              Bytes.compareTo(value, attrValue) != 0);
-      // check HCD attribute
-      for (HColumnDescriptor c : desc.getFamilies()) {
-        value = c.getValue(attrName);
-        assertFalse("missing HCD attribute value", value == null);
-        assertFalse("HCD attribute value is incorrect",
-                Bytes.compareTo(value, attrValue) != 0);
-      }
-    }
-  }
-
-  @Test
-  public void testGetClosestRowBefore() throws IOException, InterruptedException {
-    final TableName tableName = name.getTableName();
-    final byte[] firstRow = Bytes.toBytes("row111");
-    final byte[] secondRow = Bytes.toBytes("row222");
-    final byte[] thirdRow = Bytes.toBytes("row333");
-    final byte[] forthRow = Bytes.toBytes("row444");
-    final byte[] beforeFirstRow = Bytes.toBytes("row");
-    final byte[] beforeSecondRow = Bytes.toBytes("row22");
-    final byte[] beforeThirdRow = Bytes.toBytes("row33");
-    final byte[] beforeForthRow = Bytes.toBytes("row44");
-
-    try (Table table =
-        TEST_UTIL.createTable(tableName,
-          new byte[][] { HConstants.CATALOG_FAMILY, Bytes.toBytes("info2") }, 1, 1024);
-      RegionLocator locator = TEST_UTIL.getConnection().getRegionLocator(tableName)) {
-
-      // set block size to 64 to making 2 kvs into one block, bypassing the walkForwardInSingleRow
-      // in Store.rowAtOrBeforeFromStoreFile
-      String regionName = locator.getAllRegionLocations().get(0).getRegionInfo().getEncodedName();
-      HRegion region = TEST_UTIL.getRSForFirstRegionInTable(tableName).getRegion(regionName);
-      Put put1 = new Put(firstRow);
-      Put put2 = new Put(secondRow);
-      Put put3 = new Put(thirdRow);
-      Put put4 = new Put(forthRow);
-      byte[] one = new byte[] { 1 };
-      byte[] two = new byte[] { 2 };
-      byte[] three = new byte[] { 3 };
-      byte[] four = new byte[] { 4 };
-
-      put1.addColumn(HConstants.CATALOG_FAMILY, null, one);
-      put2.addColumn(HConstants.CATALOG_FAMILY, null, two);
-      put3.addColumn(HConstants.CATALOG_FAMILY, null, three);
-      put4.addColumn(HConstants.CATALOG_FAMILY, null, four);
-      table.put(put1);
-      table.put(put2);
-      table.put(put3);
-      table.put(put4);
-      region.flush(true);
-
-      Result result;
-
-      // Test before first that null is returned
-      result = getReverseScanResult(table, beforeFirstRow,
-        HConstants.CATALOG_FAMILY);
-      assertNull(result);
-
-      // Test at first that first is returned
-      result = getReverseScanResult(table, firstRow, HConstants.CATALOG_FAMILY);
-      assertTrue(result.containsColumn(HConstants.CATALOG_FAMILY, null));
-      assertTrue(Bytes.equals(result.getRow(), firstRow));
-      assertTrue(Bytes.equals(result.getValue(HConstants.CATALOG_FAMILY, null), one));
-
-      // Test in between first and second that first is returned
-      result = getReverseScanResult(table, beforeSecondRow, HConstants.CATALOG_FAMILY);
-      assertTrue(result.containsColumn(HConstants.CATALOG_FAMILY, null));
-      assertTrue(Bytes.equals(result.getRow(), firstRow));
-      assertTrue(Bytes.equals(result.getValue(HConstants.CATALOG_FAMILY, null), one));
-
-      // Test at second make sure second is returned
-      result = getReverseScanResult(table, secondRow, HConstants.CATALOG_FAMILY);
-      assertTrue(result.containsColumn(HConstants.CATALOG_FAMILY, null));
-      assertTrue(Bytes.equals(result.getRow(), secondRow));
-      assertTrue(Bytes.equals(result.getValue(HConstants.CATALOG_FAMILY, null), two));
-
-      // Test in second and third, make sure second is returned
-      result = getReverseScanResult(table, beforeThirdRow, HConstants.CATALOG_FAMILY);
-      assertTrue(result.containsColumn(HConstants.CATALOG_FAMILY, null));
-      assertTrue(Bytes.equals(result.getRow(), secondRow));
-      assertTrue(Bytes.equals(result.getValue(HConstants.CATALOG_FAMILY, null), two));
-
-      // Test at third make sure third is returned
-      result = getReverseScanResult(table, thirdRow, HConstants.CATALOG_FAMILY);
-      assertTrue(result.containsColumn(HConstants.CATALOG_FAMILY, null));
-      assertTrue(Bytes.equals(result.getRow(), thirdRow));
-      assertTrue(Bytes.equals(result.getValue(HConstants.CATALOG_FAMILY, null), three));
-
-      // Test in third and forth, make sure third is returned
-      result = getReverseScanResult(table, beforeForthRow, HConstants.CATALOG_FAMILY);
-      assertTrue(result.containsColumn(HConstants.CATALOG_FAMILY, null));
-      assertTrue(Bytes.equals(result.getRow(), thirdRow));
-      assertTrue(Bytes.equals(result.getValue(HConstants.CATALOG_FAMILY, null), three));
-
-      // Test at forth make sure forth is returned
-      result = getReverseScanResult(table, forthRow, HConstants.CATALOG_FAMILY);
-      assertTrue(result.containsColumn(HConstants.CATALOG_FAMILY, null));
-      assertTrue(Bytes.equals(result.getRow(), forthRow));
-      assertTrue(Bytes.equals(result.getValue(HConstants.CATALOG_FAMILY, null), four));
-
-      // Test after forth make sure forth is returned
-      result = getReverseScanResult(table, Bytes.add(forthRow, one), HConstants.CATALOG_FAMILY);
-      assertTrue(result.containsColumn(HConstants.CATALOG_FAMILY, null));
-      assertTrue(Bytes.equals(result.getRow(), forthRow));
-      assertTrue(Bytes.equals(result.getValue(HConstants.CATALOG_FAMILY, null), four));
-    }
-  }
-
-  private Result getReverseScanResult(Table table, byte[] row, byte[] fam) throws IOException {
-    Scan scan = new Scan(row);
-    scan.setSmall(true);
-    scan.setReversed(true);
-    scan.setCaching(1);
-    scan.addFamily(fam);
-    try (ResultScanner scanner = table.getScanner(scan)) {
-      return scanner.next();
-    }
-  }
-
-  /**
-   * For HBASE-2156
-   */
-  @Test
-  public void testScanVariableReuse() throws Exception {
-    Scan scan = new Scan();
-    scan.addFamily(FAMILY);
-    scan.addColumn(FAMILY, ROW);
-
-    assertTrue(scan.getFamilyMap().get(FAMILY).size() == 1);
-
-    scan = new Scan();
-    scan.addFamily(FAMILY);
-
-    assertTrue(scan.getFamilyMap().get(FAMILY) == null);
-    assertTrue(scan.getFamilyMap().containsKey(FAMILY));
-  }
-
-  @Test
-  public void testMultiRowMutation() throws Exception {
-    LOG.info("Starting testMultiRowMutation");
-    final TableName tableName = name.getTableName();
-    final byte [] ROW1 = Bytes.toBytes("testRow1");
-
-    try (Table t = TEST_UTIL.createTable(tableName, FAMILY)) {
-      Put p = new Put(ROW);
-      p.addColumn(FAMILY, QUALIFIER, VALUE);
-      MutationProto m1 = ProtobufUtil.toMutation(MutationType.PUT, p);
-
-      p = new Put(ROW1);
-      p.addColumn(FAMILY, QUALIFIER, VALUE);
-      MutationProto m2 = ProtobufUtil.toMutation(MutationType.PUT, p);
-
-      MutateRowsRequest.Builder mrmBuilder = MutateRowsRequest.newBuilder();
-      mrmBuilder.addMutationRequest(m1);
-      mrmBuilder.addMutationRequest(m2);
-      MutateRowsRequest mrm = mrmBuilder.build();
-      CoprocessorRpcChannel channel = t.coprocessorService(ROW);
-      MultiRowMutationService.BlockingInterface service =
-              MultiRowMutationService.newBlockingStub(channel);
-      service.mutateRows(null, mrm);
-      Get g = new Get(ROW);
-      Result r = t.get(g);
-      assertEquals(0, Bytes.compareTo(VALUE, r.getValue(FAMILY, QUALIFIER)));
-      g = new Get(ROW1);
-      r = t.get(g);
-      assertEquals(0, Bytes.compareTo(VALUE, r.getValue(FAMILY, QUALIFIER)));
-    }
-  }
-
-  @Test
-  public void testRowMutation() throws Exception {
-    LOG.info("Starting testRowMutation");
-    final TableName tableName = name.getTableName();
-    try (Table t = TEST_UTIL.createTable(tableName, FAMILY)) {
-      byte[][] QUALIFIERS = new byte[][]{
-              Bytes.toBytes("a"), Bytes.toBytes("b")
-      };
-      RowMutations arm = new RowMutations(ROW);
-      Put p = new Put(ROW);
-      p.addColumn(FAMILY, QUALIFIERS[0], VALUE);
-      arm.add(p);
-      t.mutateRow(arm);
-
-      Get g = new Get(ROW);
-      Result r = t.get(g);
-      assertEquals(0, Bytes.compareTo(VALUE, r.getValue(FAMILY, QUALIFIERS[0])));
-
-      arm = new RowMutations(ROW);
-      p = new Put(ROW);
-      p.addColumn(FAMILY, QUALIFIERS[1], VALUE);
-      arm.add(p);
-      Delete d = new Delete(ROW);
-      d.addColumns(FAMILY, QUALIFIERS[0]);
-      arm.add(d);
-      // TODO: Trying mutateRow again.  The batch was failing with a one try only.
-      t.mutateRow(arm);
-      r = t.get(g);
-      assertEquals(0, Bytes.compareTo(VALUE, r.getValue(FAMILY, QUALIFIERS[1])));
-      assertNull(r.getValue(FAMILY, QUALIFIERS[0]));
-
-      //Test that we get a region level exception
-      try {
-        arm = new RowMutations(ROW);
-        p = new Put(ROW);
-        p.addColumn(new byte[]{'b', 'o', 'g', 'u', 's'}, QUALIFIERS[0], VALUE);
-        arm.add(p);
-        t.mutateRow(arm);
-        fail("Expected NoSuchColumnFamilyException");
-      } catch (RetriesExhaustedWithDetailsException e) {
-        for (Throwable rootCause : e.getCauses()) {
-          if (rootCause instanceof NoSuchColumnFamilyException) {
-            return;
-          }
-        }
-        throw e;
-      }
-    }
-  }
-
-  @Test
-  public void testBatchAppendWithReturnResultFalse() throws Exception {
-    LOG.info("Starting testBatchAppendWithReturnResultFalse");
-    final TableName tableName = name.getTableName();
-    try (Table table = TEST_UTIL.createTable(tableName, FAMILY)) {
-      Append append1 = new Append(Bytes.toBytes("row1"));
-      append1.setReturnResults(false);
-      append1.addColumn(FAMILY, Bytes.toBytes("f1"), Bytes.toBytes("value1"));
-      Append append2 = new Append(Bytes.toBytes("row1"));
-      append2.setReturnResults(false);
-      append2.addColumn(FAMILY, Bytes.toBytes("f1"), Bytes.toBytes("value2"));
-      List<Append> appends = new ArrayList<>();
-      appends.add(append1);
-      appends.add(append2);
-      Object[] results = new Object[2];
-      table.batch(appends, results);
-      assertTrue(results.length == 2);
-      for (Object r : results) {
-        Result result = (Result) r;
-        assertTrue(result.isEmpty());
-      }
-    }
-  }
-
-  @Test
-  public void testAppend() throws Exception {
-    LOG.info("Starting testAppend");
-    final TableName tableName = name.getTableName();
-    try (Table t = TEST_UTIL.createTable(tableName, FAMILY)) {
-      byte[] v1 = Bytes.toBytes("42");
-      byte[] v2 = Bytes.toBytes("23");
-      byte[][] QUALIFIERS = new byte[][]{
-              Bytes.toBytes("b"), Bytes.toBytes("a"), Bytes.toBytes("c")
-      };
-      Append a = new Append(ROW);
-      a.addColumn(FAMILY, QUALIFIERS[0], v1);
-      a.addColumn(FAMILY, QUALIFIERS[1], v2);
-      a.setReturnResults(false);
-      assertEmptyResult(t.append(a));
-
-      a = new Append(ROW);
-      a.addColumn(FAMILY, QUALIFIERS[0], v2);
-      a.addColumn(FAMILY, QUALIFIERS[1], v1);
-      a.addColumn(FAMILY, QUALIFIERS[2], v2);
-      Result r = t.append(a);
-      assertEquals(0, Bytes.compareTo(Bytes.add(v1, v2), r.getValue(FAMILY, QUALIFIERS[0])));
-      assertEquals(0, Bytes.compareTo(Bytes.add(v2, v1), r.getValue(FAMILY, QUALIFIERS[1])));
-      // QUALIFIERS[2] previously not exist, verify both value and timestamp are correct
-      assertEquals(0, Bytes.compareTo(v2, r.getValue(FAMILY, QUALIFIERS[2])));
-      assertEquals(r.getColumnLatestCell(FAMILY, QUALIFIERS[0]).getTimestamp(),
-              r.getColumnLatestCell(FAMILY, QUALIFIERS[2]).getTimestamp());
-    }
-  }
-  private List<Result> doAppend(final boolean walUsed) throws IOException {
-    LOG.info("Starting testAppend, walUsed is " + walUsed);
-    final TableName TABLENAME =
-            TableName.valueOf(walUsed ? "testAppendWithWAL" : "testAppendWithoutWAL");
-    try (Table t = TEST_UTIL.createTable(TABLENAME, FAMILY)) {
-      final byte[] row1 = Bytes.toBytes("c");
-      final byte[] row2 = Bytes.toBytes("b");
-      final byte[] row3 = Bytes.toBytes("a");
-      final byte[] qual = Bytes.toBytes("qual");
-      Put put_0 = new Put(row2);
-      put_0.addColumn(FAMILY, qual, Bytes.toBytes("put"));
-      Put put_1 = new Put(row3);
-      put_1.addColumn(FAMILY, qual, Bytes.toBytes("put"));
-      Append append_0 = new Append(row1);
-      append_0.addColumn(FAMILY, qual, Bytes.toBytes("i"));
-      Append append_1 = new Append(row1);
-      append_1.addColumn(FAMILY, qual, Bytes.toBytes("k"));
-      Append append_2 = new Append(row1);
-      append_2.addColumn(FAMILY, qual, Bytes.toBytes("e"));
-      if (!walUsed) {
-        append_2.setDurability(Durability.SKIP_WAL);
-      }
-      Append append_3 = new Append(row1);
-      append_3.addColumn(FAMILY, qual, Bytes.toBytes("a"));
-      Scan s = new Scan();
-      s.setCaching(1);
-      t.append(append_0);
-      t.put(put_0);
-      t.put(put_1);
-      List<Result> results = new LinkedList<>();
-      try (ResultScanner scanner = t.getScanner(s)) {
-        t.append(append_1);
-        t.append(append_2);
-        t.append(append_3);
-        for (Result r : scanner) {
-          results.add(r);
-        }
-      }
-      TEST_UTIL.deleteTable(TABLENAME);
-      return results;
-    }
-  }
-
-  @Test
-  public void testAppendWithoutWAL() throws Exception {
-    List<Result> resultsWithWal = doAppend(true);
-    List<Result> resultsWithoutWal = doAppend(false);
-    assertEquals(resultsWithWal.size(), resultsWithoutWal.size());
-    for (int i = 0; i != resultsWithWal.size(); ++i) {
-      Result resultWithWal = resultsWithWal.get(i);
-      Result resultWithoutWal = resultsWithoutWal.get(i);
-      assertEquals(resultWithWal.rawCells().length, resultWithoutWal.rawCells().length);
-      for (int j = 0; j != resultWithWal.rawCells().length; ++j) {
-        Cell cellWithWal = resultWithWal.rawCells()[j];
-        Cell cellWithoutWal = resultWithoutWal.rawCells()[j];
-        assertTrue(Bytes.equals(CellUtil.cloneRow(cellWithWal),
-                CellUtil.cloneRow(cellWithoutWal)));
-        assertTrue(Bytes.equals(CellUtil.cloneFamily(cellWithWal),
-                CellUtil.cloneFamily(cellWithoutWal)));
-        assertTrue(Bytes.equals(CellUtil.cloneQualifier(cellWithWal),
-                CellUtil.cloneQualifier(cellWithoutWal)));
-        assertTrue(Bytes.equals(CellUtil.cloneValue(cellWithWal),
-                CellUtil.cloneValue(cellWithoutWal)));
-      }
-    }
-  }
-
-  @Test
-  public void testClientPoolRoundRobin() throws IOException {
-    final TableName tableName = name.getTableName();
-
-    int poolSize = 3;
-    int numVersions = poolSize * 2;
-    Configuration conf = TEST_UTIL.getConfiguration();
-    conf.set(HConstants.HBASE_CLIENT_IPC_POOL_TYPE, "round-robin");
-    conf.setInt(HConstants.HBASE_CLIENT_IPC_POOL_SIZE, poolSize);
-
-    try (Table table =
-                 TEST_UTIL.createTable(tableName, new byte[][] { FAMILY }, Integer.MAX_VALUE)) {
-
-      final long ts = EnvironmentEdgeManager.currentTime();
-      Get get = new Get(ROW);
-      get.addColumn(FAMILY, QUALIFIER);
-      get.setMaxVersions();
-
-      for (int versions = 1; versions <= numVersions; versions++) {
-        Put put = new Put(ROW);
-        put.addColumn(FAMILY, QUALIFIER, ts + versions, VALUE);
-        table.put(put);
-
-        Result result = table.get(get);
-        NavigableMap<Long, byte[]> navigableMap = result.getMap().get(FAMILY)
-                .get(QUALIFIER);
-
-        assertEquals("The number of versions of '" + Bytes.toString(FAMILY) + ":"
-                + Bytes.toString(QUALIFIER) + " did not match", versions, navigableMap.size());
-        for (Map.Entry<Long, byte[]> entry : navigableMap.entrySet()) {
-          assertTrue("The value at time " + entry.getKey()
-                          + " did not match what was put",
-                  Bytes.equals(VALUE, entry.getValue()));
-        }
-      }
-    }
-  }
-
-  @Ignore ("Flakey: HBASE-8989") @Test
-  public void testClientPoolThreadLocal() throws IOException {
-    final TableName tableName = name.getTableName();
-
-    int poolSize = Integer.MAX_VALUE;
-    int numVersions = 3;
-    Configuration conf = TEST_UTIL.getConfiguration();
-    conf.set(HConstants.HBASE_CLIENT_IPC_POOL_TYPE, "thread-local");
-    conf.setInt(HConstants.HBASE_CLIENT_IPC_POOL_SIZE, poolSize);
-
-    try (final Table table = TEST_UTIL.createTable(tableName, new byte[][] { FAMILY },  3)) {
-
-      final long ts = EnvironmentEdgeManager.currentTime();
-      final Get get = new Get(ROW);
-      get.addColumn(FAMILY, QUALIFIER);
-      get.setMaxVersions();
-
-      for (int versions = 1; versions <= numVersions; versions++) {
-        Put put = new Put(ROW);
-        put.addColumn(FAMILY, QUALIFIER, ts + versions, VALUE);
-        table.put(put);
-
-        Result result = table.get(get);
-        NavigableMap<Long, byte[]> navigableMap = result.getMap().get(FAMILY)
-                .get(QUALIFIER);
-
-        assertEquals("The number of versions of '" + Bytes.toString(FAMILY) + ":"
-                + Bytes.toString(QUALIFIER) + " did not match", versions, navigableMap.size());
-        for (Map.Entry<Long, byte[]> entry : navigableMap.entrySet()) {
-          assertTrue("The value at time " + entry.getKey()
-                          + " did not match what was put",
-                  Bytes.equals(VALUE, entry.getValue()));
-        }
-      }
-
-      final Object waitLock = new Object();
-      ExecutorService executorService = Executors.newFixedThreadPool(numVersions);
-      final AtomicReference<AssertionError> error = new AtomicReference<>(null);
-      for (int versions = numVersions; versions < numVersions * 2; versions++) {
-        final int versionsCopy = versions;
-        executorService.submit(new Callable<Void>() {
-          @Override
-          public Void call() {
-            try {
-              Put put = new Put(ROW);
-              put.addColumn(FAMILY, QUALIFIER, ts + versionsCopy, VALUE);
-              table.put(put);
-
-              Result result = table.get(get);
-              NavigableMap<Long, byte[]> navigableMap = result.getMap()
-                      .get(FAMILY).get(QUALIFIER);
-
-              assertEquals("The number of versions of '" + Bytes.toString(FAMILY) + ":"
-                      + Bytes.toString(QUALIFIER) + " did not match " + versionsCopy, versionsCopy,
-                      navigableMap.size());
-              for (Map.Entry<Long, byte[]> entry : navigableMap.entrySet()) {
-                assertTrue("The value at time " + entry.getKey()
-                                + " did not match what was put",
-                        Bytes.equals(VALUE, entry.getValue()));
-              }
-              synchronized (waitLock) {
-                waitLock.wait();
-              }
-            } catch (Exception e) {
-            } catch (AssertionError e) {
-              // the error happens in a thread, it won't fail the test,
-              // need to pass it to the caller for proper handling.
-              error.set(e);
-              LOG.error(e.toString(), e);
-            }
-
-            return null;
-          }
-        });
-      }
-      synchronized (waitLock) {
-        waitLock.notifyAll();
-      }
-      executorService.shutdownNow();
-      assertNull(error.get());
-    }
-  }
-
-  @Test
-  public void testCheckAndPut() throws IOException {
-    final byte [] anotherrow = Bytes.toBytes("anotherrow");
-    final byte [] value2 = Bytes.toBytes("abcd");
-
-    try (Table table = TEST_UTIL.createTable(name.getTableName(), FAMILY)) {
-      Put put1 = new Put(ROW);
-      put1.addColumn(FAMILY, QUALIFIER, VALUE);
-
-      // row doesn't exist, so using non-null value should be considered "not match".
-      boolean ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .ifEquals(VALUE).thenPut(put1);
-      assertFalse(ok);
-
-      // row doesn't exist, so using "ifNotExists" should be considered "match".
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER).ifNotExists().thenPut(put1);
-      assertTrue(ok);
-
-      // row now exists, so using "ifNotExists" should be considered "not match".
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER).ifNotExists().thenPut(put1);
-      assertFalse(ok);
-
-      Put put2 = new Put(ROW);
-      put2.addColumn(FAMILY, QUALIFIER, value2);
-
-      // row now exists, use the matching value to check
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER).ifEquals(VALUE).thenPut(put2);
-      assertTrue(ok);
-
-      Put put3 = new Put(anotherrow);
-      put3.addColumn(FAMILY, QUALIFIER, VALUE);
-
-      // try to do CheckAndPut on different rows
-      try {
-        table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER).ifEquals(value2).thenPut(put3);
-        fail("trying to check and modify different rows should have failed.");
-      } catch (Exception e) {
-      }
-    }
-  }
-
-  @Test
-  public void testCheckAndMutateWithTimeRange() throws IOException {
-    try (Table table = TEST_UTIL.createTable(name.getTableName(), FAMILY)) {
-      final long ts = System.currentTimeMillis() / 2;
-      Put put = new Put(ROW);
-      put.addColumn(FAMILY, QUALIFIER, ts, VALUE);
-
-      boolean ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .ifNotExists()
-              .thenPut(put);
-      assertTrue(ok);
-
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .timeRange(TimeRange.at(ts + 10000))
-              .ifEquals(VALUE)
-              .thenPut(put);
-      assertFalse(ok);
-
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .timeRange(TimeRange.from(ts + 10000))
-              .ifEquals(VALUE)
-              .thenPut(put);
-      assertFalse(ok);
-
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .timeRange(TimeRange.between(ts + 10000, ts + 20000))
-              .ifEquals(VALUE)
-              .thenPut(put);
-      assertFalse(ok);
-
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .timeRange(TimeRange.until(ts))
-              .ifEquals(VALUE)
-              .thenPut(put);
-      assertFalse(ok);
-
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .timeRange(TimeRange.at(ts))
-              .ifEquals(VALUE)
-              .thenPut(put);
-      assertTrue(ok);
-
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .timeRange(TimeRange.from(ts))
-              .ifEquals(VALUE)
-              .thenPut(put);
-      assertTrue(ok);
-
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .timeRange(TimeRange.between(ts, ts + 20000))
-              .ifEquals(VALUE)
-              .thenPut(put);
-      assertTrue(ok);
-
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .timeRange(TimeRange.until(ts + 10000))
-              .ifEquals(VALUE)
-              .thenPut(put);
-      assertTrue(ok);
-
-      RowMutations rm = new RowMutations(ROW)
-              .add((Mutation) put);
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .timeRange(TimeRange.at(ts + 10000))
-              .ifEquals(VALUE)
-              .thenMutate(rm);
-      assertFalse(ok);
-
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .timeRange(TimeRange.at(ts))
-              .ifEquals(VALUE)
-              .thenMutate(rm);
-      assertTrue(ok);
-
-      Delete delete = new Delete(ROW)
-              .addColumn(FAMILY, QUALIFIER);
-
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .timeRange(TimeRange.at(ts + 10000))
-              .ifEquals(VALUE)
-              .thenDelete(delete);
-      assertFalse(ok);
-
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .timeRange(TimeRange.at(ts))
-              .ifEquals(VALUE)
-              .thenDelete(delete);
-      assertTrue(ok);
-    }
-  }
-
-  @Test
-  public void testCheckAndPutWithCompareOp() throws IOException {
-    final byte [] value1 = Bytes.toBytes("aaaa");
-    final byte [] value2 = Bytes.toBytes("bbbb");
-    final byte [] value3 = Bytes.toBytes("cccc");
-    final byte [] value4 = Bytes.toBytes("dddd");
-
-    try (Table table = TEST_UTIL.createTable(name.getTableName(), FAMILY)) {
-
-      Put put2 = new Put(ROW);
-      put2.addColumn(FAMILY, QUALIFIER, value2);
-
-      Put put3 = new Put(ROW);
-      put3.addColumn(FAMILY, QUALIFIER, value3);
-
-      // row doesn't exist, so using "ifNotExists" should be considered "match".
-      boolean ok =
-              table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER).ifNotExists().thenPut(put2);
-      assertTrue(ok);
-
-      // cell = "bbbb", using "aaaa" to compare only LESS/LESS_OR_EQUAL/NOT_EQUAL
-      // turns out "match"
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .ifMatches(CompareOperator.GREATER, value1).thenPut(put2);
-      assertFalse(ok);
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .ifMatches(CompareOperator.EQUAL, value1).thenPut(put2);
-      assertFalse(ok);
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .ifMatches(CompareOperator.GREATER_OR_EQUAL, value1).thenPut(put2);
-      assertFalse(ok);
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .ifMatches(CompareOperator.LESS, value1).thenPut(put2);
-      assertTrue(ok);
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .ifMatches(CompareOperator.LESS_OR_EQUAL, value1).thenPut(put2);
-      assertTrue(ok);
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .ifMatches(CompareOperator.NOT_EQUAL, value1).thenPut(put3);
-      assertTrue(ok);
-
-      // cell = "cccc", using "dddd" to compare only LARGER/LARGER_OR_EQUAL/NOT_EQUAL
-      // turns out "match"
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .ifMatches(CompareOperator.LESS, value4).thenPut(put3);
-      assertFalse(ok);
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .ifMatches(CompareOperator.LESS_OR_EQUAL, value4).thenPut(put3);
-      assertFalse(ok);
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .ifMatches(CompareOperator.EQUAL, value4).thenPut(put3);
-      assertFalse(ok);
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .ifMatches(CompareOperator.GREATER, value4).thenPut(put3);
-      assertTrue(ok);
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .ifMatches(CompareOperator.GREATER_OR_EQUAL, value4).thenPut(put3);
-      assertTrue(ok);
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .ifMatches(CompareOperator.NOT_EQUAL, value4).thenPut(put2);
-      assertTrue(ok);
-
-      // cell = "bbbb", using "bbbb" to compare only GREATER_OR_EQUAL/LESS_OR_EQUAL/EQUAL
-      // turns out "match"
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .ifMatches(CompareOperator.GREATER, value2).thenPut(put2);
-      assertFalse(ok);
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .ifMatches(CompareOperator.NOT_EQUAL, value2).thenPut(put2);
-      assertFalse(ok);
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .ifMatches(CompareOperator.LESS, value2).thenPut(put2);
-      assertFalse(ok);
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .ifMatches(CompareOperator.GREATER_OR_EQUAL, value2).thenPut(put2);
-      assertTrue(ok);
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .ifMatches(CompareOperator.LESS_OR_EQUAL, value2).thenPut(put2);
-      assertTrue(ok);
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .ifMatches(CompareOperator.EQUAL, value2).thenPut(put3);
-      assertTrue(ok);
-    }
-  }
-
-  @Test
-  public void testCheckAndDelete() throws IOException {
-    final byte [] value1 = Bytes.toBytes("aaaa");
-
-    try (Table table = TEST_UTIL.createTable(name.getTableName(),
-        FAMILY)) {
-
-      Put put = new Put(ROW);
-      put.addColumn(FAMILY, QUALIFIER, value1);
-      table.put(put);
-
-      Delete delete = new Delete(ROW);
-      delete.addColumns(FAMILY, QUALIFIER);
-
-      boolean ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .ifEquals(value1).thenDelete(delete);
-      assertTrue(ok);
-    }
-  }
-
-  @Test
-  public void testCheckAndDeleteWithCompareOp() throws IOException {
-    final byte [] value1 = Bytes.toBytes("aaaa");
-    final byte [] value2 = Bytes.toBytes("bbbb");
-    final byte [] value3 = Bytes.toBytes("cccc");
-    final byte [] value4 = Bytes.toBytes("dddd");
-
-    try (Table table = TEST_UTIL.createTable(name.getTableName(),
-        FAMILY)) {
-
-      Put put2 = new Put(ROW);
-      put2.addColumn(FAMILY, QUALIFIER, value2);
-      table.put(put2);
-
-      Put put3 = new Put(ROW);
-      put3.addColumn(FAMILY, QUALIFIER, value3);
-
-      Delete delete = new Delete(ROW);
-      delete.addColumns(FAMILY, QUALIFIER);
-
-      // cell = "bbbb", using "aaaa" to compare only LESS/LESS_OR_EQUAL/NOT_EQUAL
-      // turns out "match"
-      boolean ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .ifMatches(CompareOperator.GREATER, value1).thenDelete(delete);
-      assertFalse(ok);
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .ifMatches(CompareOperator.EQUAL, value1).thenDelete(delete);
-      assertFalse(ok);
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .ifMatches(CompareOperator.GREATER_OR_EQUAL, value1).thenDelete(delete);
-      assertFalse(ok);
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .ifMatches(CompareOperator.LESS, value1).thenDelete(delete);
-      assertTrue(ok);
-      table.put(put2);
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .ifMatches(CompareOperator.LESS_OR_EQUAL, value1).thenDelete(delete);
-      assertTrue(ok);
-      table.put(put2);
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .ifMatches(CompareOperator.NOT_EQUAL, value1).thenDelete(delete);
-      assertTrue(ok);
-
-      // cell = "cccc", using "dddd" to compare only LARGER/LARGER_OR_EQUAL/NOT_EQUAL
-      // turns out "match"
-      table.put(put3);
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .ifMatches(CompareOperator.LESS, value4).thenDelete(delete);
-      assertFalse(ok);
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .ifMatches(CompareOperator.LESS_OR_EQUAL, value4).thenDelete(delete);
-      assertFalse(ok);
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .ifMatches(CompareOperator.EQUAL, value4).thenDelete(delete);
-      assertFalse(ok);
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .ifMatches(CompareOperator.GREATER, value4).thenDelete(delete);
-      assertTrue(ok);
-      table.put(put3);
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .ifMatches(CompareOperator.GREATER_OR_EQUAL, value4).thenDelete(delete);
-      assertTrue(ok);
-      table.put(put3);
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .ifMatches(CompareOperator.NOT_EQUAL, value4).thenDelete(delete);
-      assertTrue(ok);
-
-      // cell = "bbbb", using "bbbb" to compare only GREATER_OR_EQUAL/LESS_OR_EQUAL/EQUAL
-      // turns out "match"
-      table.put(put2);
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .ifMatches(CompareOperator.GREATER, value2).thenDelete(delete);
-      assertFalse(ok);
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .ifMatches(CompareOperator.NOT_EQUAL, value2).thenDelete(delete);
-      assertFalse(ok);
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .ifMatches(CompareOperator.LESS, value2).thenDelete(delete);
-      assertFalse(ok);
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .ifMatches(CompareOperator.GREATER_OR_EQUAL, value2).thenDelete(delete);
-      assertTrue(ok);
-      table.put(put2);
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .ifMatches(CompareOperator.LESS_OR_EQUAL, value2).thenDelete(delete);
-      assertTrue(ok);
-      table.put(put2);
-      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
-              .ifMatches(CompareOperator.EQUAL, value2).thenDelete(delete);
-      assertTrue(ok);
-    }
-  }
-
-  /**
-  * Test ScanMetrics
-  */
-  @Test
-  @SuppressWarnings({"unused", "checkstyle:EmptyBlock"})
-  public void testScanMetrics() throws Exception {
-    final TableName tableName = name.getTableName();
-
-    // Set up test table:
-    // Create table:
-    try (Table ht = TEST_UTIL.createMultiRegionTable(tableName, FAMILY)) {
-      int numOfRegions = -1;
-      try (RegionLocator r = TEST_UTIL.getConnection().getRegionLocator(tableName)) {
-        numOfRegions = r.getStartKeys().length;
-      }
-      // Create 3 rows in the table, with rowkeys starting with "zzz*" so that
-      // scan are forced to hit all the regions.
-      Put put1 = new Put(Bytes.toBytes("zzz1"));
-      put1.addColumn(FAMILY, QUALIFIER, VALUE);
-      Put put2 = new Put(Bytes.toBytes("zzz2"));
-      put2.addColumn(FAMILY, QUALIFIER, VALUE);
-      Put put3 = new Put(Bytes.toBytes("zzz3"));
-      put3.addColumn(FAMILY, QUALIFIER, VALUE);
-      ht.put(Arrays.asList(put1, put2, put3));
-
-      Scan scan1 = new Scan();
-      int numRecords = 0;
-      try (ResultScanner scanner = ht.getScanner(scan1)) {
-        for (Result result : scanner) {
-          numRecords++;
-        }
-
-        LOG.info("test data has " + numRecords + " records.");
-
-        // by default, scan metrics collection is turned off
-        assertEquals(null, scanner.getScanMetrics());
-      }
-
-      // turn on scan metrics
-      Scan scan2 = new Scan();
-      scan2.setScanMetricsEnabled(true);
-      scan2.setCaching(numRecords + 1);
-      try (ResultScanner scanner = ht.getScanner(scan2)) {
-        for (Result result : scanner.next(numRecords - 1)) {
-        }
-      }
-      // closing the scanner will set the metrics.
-      assertNotNull(scan2.getScanMetrics());
-
-      // set caching to 1, because metrics are collected in each roundtrip only
-      scan2 = new Scan();
-      scan2.setScanMetricsEnabled(true);
-      scan2.setCaching(1);
-      try (ResultScanner scanner = ht.getScanner(scan2)) {
-        // per HBASE-5717, this should still collect even if you don't run all the way to
-        // the end of the scanner. So this is asking for 2 of the 3 rows we inserted.
-        for (Result result : scanner.next(numRecords - 1)) {
-        }
-        ScanMetrics scanMetrics = scanner.getScanMetrics();
-        assertEquals("Did not access all the regions in the table", numOfRegions,
-                scanMetrics.countOfRegions.get());
-      }
-      // check byte counters
-      scan2 = new Scan();
-      scan2.setScanMetricsEnabled(true);
-      scan2.setCaching(1);
-      try (ResultScanner scanner = ht.getScanner(scan2)) {
-        int numBytes = 0;
-        for (Result result : scanner.next(1)) {
-          for (Cell cell : result.listCells()) {
-            numBytes += PrivateCellUtil.estimatedSerializedSizeOf(cell);
-          }
-        }
-        ScanMetrics scanMetrics = scanner.getScanMetrics();
-        assertEquals("Did not count the result bytes", numBytes,
-                scanMetrics.countOfBytesInResults.get());
-      }
-
-      // check byte counters on a small scan
-      scan2 = new Scan();
-      scan2.setScanMetricsEnabled(true);
-      scan2.setCaching(1);
-      scan2.setSmall(true);
-      try (ResultScanner scanner = ht.getScanner(scan2)) {
-        int numBytes = 0;
-        for (Result result : scanner.next(1)) {
-          for (Cell cell : result.listCells()) {
-            numBytes += PrivateCellUtil.estimatedSerializedSizeOf(cell);
-          }
-        }
-        ScanMetrics scanMetrics = scanner.getScanMetrics();
-        assertEquals("Did not count the result bytes", numBytes,
-                scanMetrics.countOfBytesInResults.get());
-      }
-
-      // now, test that the metrics are still collected even if you don't call close, but do
-      // run past the end of all the records
-      /** There seems to be a timing issue here.  Comment out for now. Fix when time.
-       Scan scanWithoutClose = new Scan();
-       scanWithoutClose.setCaching(1);
-       scanWithoutClose.setScanMetricsEnabled(true);
-       ResultScanner scannerWithoutClose = ht.getScanner(scanWithoutClose);
-       for (Result result : scannerWithoutClose.next(numRecords + 1)) {
-       }
-       ScanMetrics scanMetricsWithoutClose = getScanMetrics(scanWithoutClose);
-       assertEquals("Did not access all the regions in the table", numOfRegions,
-       scanMetricsWithoutClose.countOfRegions.get());
-       */
-
-      // finally,
-      // test that the metrics are collected correctly if you both run past all the records,
-      // AND close the scanner
-      Scan scanWithClose = new Scan();
-      // make sure we can set caching up to the number of a scanned values
-      scanWithClose.setCaching(numRecords);
-      scanWithClose.setScanMetricsEnabled(true);
-      try (ResultScanner scannerWithClose = ht.getScanner(scanWithClose)) {
-        for (Result result : scannerWithClose.next(numRecords + 1)) {
-        }
-      }
-      ScanMetrics scanMetricsWithClose = scanWithClose.getScanMetrics();
-      assertEquals("Did not access all the regions in the table", numOfRegions,
-              scanMetricsWithClose.countOfRegions.get());
-    }
-  }
-
-  private ScanMetrics getScanMetrics(Scan scan) throws Exception {
-    byte[] serializedMetrics = scan.getAttribute(Scan.SCAN_ATTRIBUTES_METRICS_DATA);
-    assertTrue("Serialized metrics were not found.", serializedMetrics != null);
-
-    ScanMetrics scanMetrics = ProtobufUtil.toScanMetrics(serializedMetrics);
-
-    return scanMetrics;
-  }
-
-  /**
-   * Tests that cache on write works all the way up from the client-side.
-   *
-   * Performs inserts, flushes, and compactions, verifying changes in the block
-   * cache along the way.
-   */
-  @Test
-  public void testCacheOnWriteEvictOnClose() throws Exception {
-    final TableName tableName = name.getTableName();
-    byte [] data = Bytes.toBytes("data");
-    try (Table table = TEST_UTIL.createTable(tableName, FAMILY)) {
-      try (RegionLocator locator = TEST_UTIL.getConnection().getRegionLocator(tableName)) {
-        // get the block cache and region
-        String regionName = locator.getAllRegionLocations().get(0).getRegionInfo().getEncodedName();
-
-        HRegion region = TEST_UTIL.getRSForFirstRegionInTable(tableName)
-                .getRegion(regionName);
-        HStore store = region.getStores().iterator().next();
-        CacheConfig cacheConf = store.getCacheConfig();
-        cacheConf.setCacheDataOnWrite(true);
-        cacheConf.setEvictOnClose(true);
-        BlockCache cache = cacheConf.getBlockCache().get();
-
-        // establish baseline stats
-        long startBlockCount = cache.getBlockCount();
-        long startBlockHits = cache.getStats().getHitCount();
-        long startBlockMiss = cache.getStats().getMissCount();
-
-        // wait till baseline is stable, (minimal 500 ms)
-        for (int i = 0; i < 5; i++) {
-          Thread.sleep(100);
-          if (startBlockCount != cache.getBlockCount()
-                  || startBlockHits != cache.getStats().getHitCount()
-                  || startBlockMiss != cache.getStats().getMissCount()) {
-            startBlockCount = cache.getBlockCount();
-            startBlockHits = cache.getStats().getHitCount();
-            startBlockMiss = cache.getStats().getMissCount();
-            i = -1;
-          }
-        }
-
-        // insert data
-        Put put = new Put(ROW);
-        put.addColumn(FAMILY, QUALIFIER, data);
-        table.put(put);
-        assertTrue(Bytes.equals(table.get(new Get(ROW)).value(), data));
-
-        // data was in memstore so don't expect any changes
-        assertEquals(startBlockCount, cache.getBlockCount());
-        assertEquals(startBlockHits, cache.getStats().getHitCount());
-        assertEquals(startBlockMiss, cache.getStats().getMissCount());
-
-        // flush the data
-        LOG.debug("Flushing cache");
-        region.flush(true);
-
-        // expect two more blocks in cache - DATA and ROOT_INDEX
-        // , no change in hits/misses
-        long expectedBlockCount = startBlockCount + 2;
-        long expectedBlockHits = startBlockHits;
-        long expectedBlockMiss = startBlockMiss;
-        assertEquals(expectedBlockCount, cache.getBlockCount());
-        assertEquals(expectedBlockHits, cache.getStats().getHitCount());
-        assertEquals(expectedBlockMiss, cache.getStats().getMissCount());
-        // read the data and expect same blocks, one new hit, no misses
-        assertTrue(Bytes.equals(table.get(new Get(ROW)).value(), data));
-        assertEquals(expectedBlockCount, cache.getBlockCount());
-        assertEquals(++expectedBlockHits, cache.getStats().getHitCount());
-        assertEquals(expectedBlockMiss, cache.getStats().getMissCount());
-        // insert a second column, read the row, no new blocks, one new hit
-        byte[] QUALIFIER2 = Bytes.add(QUALIFIER, QUALIFIER);
-        byte[] data2 = Bytes.add(data, data);
-        put = new Put(ROW);
-        put.addColumn(FAMILY, QUALIFIER2, data2);
-        table.put(put);
-        Result r = table.get(new Get(ROW));
-        assertTrue(Bytes.equals(r.getValue(FAMILY, QUALIFIER), data));
-        assertTrue(Bytes.equals(r.getValue(FAMILY, QUALIFIER2), data2));
-        assertEquals(expectedBlockCount, cache.getBlockCount());
-        assertEquals(++expectedBlockHits, cache.getStats().getHitCount());
-        assertEquals(expectedBlockMiss, cache.getStats().getMissCount());
-        // flush, one new block
-        System.out.println("Flushing cache");
-        region.flush(true);
-
-        // + 1 for Index Block, +1 for data block
-        expectedBlockCount += 2;
-        assertEquals(expectedBlockCount, cache.getBlockCount());
-        assertEquals(expectedBlockHits, cache.getStats().getHitCount());
-        assertEquals(expectedBlockMiss, cache.getStats().getMissCount());
-        // compact, net minus two blocks, two hits, no misses
-        System.out.println("Compacting");
-        assertEquals(2, store.getStorefilesCount());
-        store.triggerMajorCompaction();
-        region.compact(true);
-        store.closeAndArchiveCompactedFiles();
-        waitForStoreFileCount(store, 1, 10000); // wait 10 seconds max
-        assertEquals(1, store.getStorefilesCount());
-        // evicted two data blocks and two index blocks and compaction does not cache new blocks
-        expectedBlockCount = 0;
-        assertEquals(expectedBlockCount, cache.getBlockCount());
-        expectedBlockHits += 2;
-        assertEquals(expectedBlockMiss, cache.getStats().getMissCount());
-        assertEquals(expectedBlockHits, cache.getStats().getHitCount());
-        // read the row, this should be a cache miss because we don't cache data
-        // blocks on compaction
-        r = table.get(new Get(ROW));
-        assertTrue(Bytes.equals(r.getValue(FAMILY, QUALIFIER), data));
-        assertTrue(Bytes.equals(r.getValue(FAMILY, QUALIFIER2), data2));
-        expectedBlockCount += 1; // cached one data block
-        assertEquals(expectedBlockCount, cache.getBlockCount());
-        assertEquals(expectedBlockHits, cache.getStats().getHitCount());
-        assertEquals(++expectedBlockMiss, cache.getStats().getMissCount());
-      }
-    }
-  }
-
-  private void waitForStoreFileCount(HStore store, int count, int timeout)
-      throws InterruptedException {
-    long start = System.currentTimeMillis();
-    while (start + timeout > System.currentTimeMillis() && store.getStorefilesCount() != count) {
-      Thread.sleep(100);
-    }
-    System.out.println("start=" + start + ", now=" + System.currentTimeMillis() + ", cur=" +
-        store.getStorefilesCount());
-    assertEquals(count, store.getStorefilesCount());
-  }
-
-  @Test
-  /**
-   * Tests the non cached version of getRegionLocator by moving a region.
-   */
-  public void testNonCachedGetRegionLocation() throws Exception {
-    // Test Initialization.
-    final TableName tableName = name.getTableName();
-    byte [] family1 = Bytes.toBytes("f1");
-    byte [] family2 = Bytes.toBytes("f2");
-    try (Table table = TEST_UTIL.createTable(tableName, new byte[][] {family1, family2}, 10);
-        Admin admin = TEST_UTIL.getAdmin();
-        RegionLocator locator = TEST_UTIL.getConnection().getRegionLocator(tableName)) {
-      List<HRegionLocation> allRegionLocations = locator.getAllRegionLocations();
-      assertEquals(1, allRegionLocations.size());
-      HRegionInfo regionInfo = allRegionLocations.get(0).getRegionInfo();
-      ServerName addrBefore = allRegionLocations.get(0).getServerName();
-      // Verify region location before move.
-      HRegionLocation addrCache = locator.getRegionLocation(regionInfo.getStartKey(), false);
-      HRegionLocation addrNoCache = locator.getRegionLocation(regionInfo.getStartKey(),  true);
-
-      assertEquals(addrBefore.getPort(), addrCache.getPort());
-      assertEquals(addrBefore.getPort(), addrNoCache.getPort());
-
-      ServerName addrAfter = null;
-      // Now move the region to a different server.
-      for (int i = 0; i < SLAVES; i++) {
-        HRegionServer regionServer = TEST_UTIL.getHBaseCluster().getRegionServer(i);
-        ServerName addr = regionServer.getServerName();
-        if (addr.getPort() != addrBefore.getPort()) {
-          admin.move(regionInfo.getEncodedNameAsBytes(), addr);
-          // Wait for the region to move.
-          Thread.sleep(5000);
-          addrAfter = addr;
-          break;
-        }
-      }
-
-      // Verify the region was moved.
-      addrCache = locator.getRegionLocation(regionInfo.getStartKey(), false);
-      addrNoCache = locator.getRegionLocation(regionInfo.getStartKey(), true);
-      assertNotNull(addrAfter);
-      assertTrue(addrAfter.getPort() != addrCache.getPort());
-      assertEquals(addrAfter.getPort(), addrNoCache.getPort());
-    }
-  }
-
-  @Test
-  /**
-   * Tests getRegionsInRange by creating some regions over which a range of
-   * keys spans; then changing the key range.
-   */
-  public void testGetRegionsInRange() throws Exception {
-    // Test Initialization.
-    byte [] startKey = Bytes.toBytes("ddc");
-    byte [] endKey = Bytes.toBytes("mmm");
-    TableName tableName = name.getTableName();
-    TEST_UTIL.createMultiRegionTable(tableName, new byte[][] { FAMILY }, 10);
-
-    int numOfRegions = -1;
-    try (RegionLocator r = TEST_UTIL.getConnection().getRegionLocator(tableName)) {
-      numOfRegions = r.getStartKeys().length;
-    }
-    assertEquals(26, numOfRegions);
-
-    // Get the regions in this range
-    List<HRegionLocation> regionsList = getRegionsInRange(tableName, startKey, endKey);
-    assertEquals(10, regionsList.size());
-
-    // Change the start key
-    startKey = Bytes.toBytes("fff");
-    regionsList = getRegionsInRange(tableName, startKey, endKey);
-    assertEquals(7, regionsList.size());
-
-    // Change the end key
-    endKey = Bytes.toBytes("nnn");
-    regionsList = getRegionsInRange(tableName, startKey, endKey);
-    assertEquals(8, regionsList.size());
-
-    // Empty start key
-    regionsList = getRegionsInRange(tableName, HConstants.EMPTY_START_ROW, endKey);
-    assertEquals(13, regionsList.size());
-
-    // Empty end key
-    regionsList = getRegionsInRange(tableName, startKey, HConstants.EMPTY_END_ROW);
-    assertEquals(21, regionsList.size());
-
-    // Both start and end keys empty
-    regionsList = getRegionsInRange(tableName, HConstants.EMPTY_START_ROW,
-        HConstants.EMPTY_END_ROW);
-    assertEquals(26, regionsList.size());
-
-    // Change the end key to somewhere in the last block
-    endKey = Bytes.toBytes("zzz1");
-    regionsList = getRegionsInRange(tableName, startKey, endKey);
-    assertEquals(21, regionsList.size());
-
-    // Change the start key to somewhere in the first block
-    startKey = Bytes.toBytes("aac");
-    regionsList = getRegionsInRange(tableName, startKey, endKey);
-    assertEquals(26, regionsList.size());
-
-    // Make start and end key the same
-    startKey = endKey = Bytes.toBytes("ccc");
-    regionsList = getRegionsInRange(tableName, startKey, endKey);
-    assertEquals(1, regionsList.size());
-  }
-
-  private List<HRegionLocation> getRegionsInRange(TableName tableName, byte[] startKey,
-      byte[] endKey) throws IOException {
-    List<HRegionLocation> regionsInRange = new ArrayList<>();
-    byte[] currentKey = startKey;
-    final boolean endKeyIsEndOfTable = Bytes.equals(endKey, HConstants.EMPTY_END_ROW);
-    try (RegionLocator r = TEST_UTIL.getConnection().getRegionLocator(tableName)) {
-      do {
-        HRegionLocation regionLocation = r.getRegionLocation(currentKey);
-        regionsInRange.add(regionLocation);
-        currentKey = regionLocation.getRegionInfo().getEndKey();
-      } while (!Bytes.equals(currentKey, HConstants.EMPTY_END_ROW)
-          && (endKeyIsEndOfTable || Bytes.compareTo(currentKey, endKey) < 0));
-      return regionsInRange;
-    }
-  }
-
-  @Test
-  public void testJira6912() throws Exception {
-    final TableName tableName = name.getTableName();
-    try (Table foo = TEST_UTIL.createTable(tableName, new byte[][] {FAMILY}, 10)) {
-
-      List<Put> puts = new ArrayList<Put>();
-      for (int i = 0; i != 100; i++) {
-        Put put = new Put(Bytes.toBytes(i));
-        put.addColumn(FAMILY, FAMILY, Bytes.toBytes(i));
-        puts.add(put);
-      }
-      foo.put(puts);
-      // If i comment this out it works
-      TEST_UTIL.flush();
-
-      Scan scan = new Scan();
-      scan.setStartRow(Bytes.toBytes(1));
-      scan.setStopRow(Bytes.toBytes(3));
-      scan.addColumn(FAMILY, FAMILY);
-      scan.setFilter(new RowFilter(CompareOperator.NOT_EQUAL,
-              new BinaryComparator(Bytes.toBytes(1))));
-
-      try (ResultScanner scanner = foo.getScanner(scan)) {
-        Result[] bar = scanner.next(100);
-        assertEquals(1, bar.length);
-      }
-    }
-  }
-
-  @Test
-  public void testScan_NullQualifier() throws IOException {
-    try (Table table = TEST_UTIL.createTable(name.getTableName(), FAMILY)) {
-      Put put = new Put(ROW);
-      put.addColumn(FAMILY, QUALIFIER, VALUE);
-      table.put(put);
-
-      put = new Put(ROW);
-      put.addColumn(FAMILY, null, VALUE);
-      table.put(put);
-      LOG.info("Row put");
-
-      Scan scan = new Scan();
-      scan.addColumn(FAMILY, null);
-
-      ResultScanner scanner = table.getScanner(scan);
-      Result[] bar = scanner.next(100);
-      assertEquals(1, bar.length);
-      assertEquals(1, bar[0].size());
-
-      scan = new Scan();
-      scan.addFamily(FAMILY);
-
-      scanner = table.getScanner(scan);
-      bar = scanner.next(100);
-      assertEquals(1, bar.length);
-      assertEquals(2, bar[0].size());
-    }
-  }
-
-  @Test
-  public void testNegativeTimestamp() throws IOException {
-    try (Table table = TEST_UTIL.createTable(name.getTableName(), FAMILY)) {
-
-      try {
-        Put put = new Put(ROW, -1);
-        put.addColumn(FAMILY, QUALIFIER, VALUE);
-        table.put(put);
-        fail("Negative timestamps should not have been allowed");
-      } catch (IllegalArgumentException ex) {
-        assertTrue(ex.getMessage().contains("negative"));
-      }
-
-      try {
-        Put put = new Put(ROW);
-        long ts = -1;
-        put.addColumn(FAMILY, QUALIFIER, ts, VALUE);
-        table.put(put);
-        fail("Negative timestamps should not have been allowed");
-      } catch (IllegalArgumentException ex) {
-        assertTrue(ex.getMessage().contains("negative"));
-      }
-
-      try {
-        Delete delete = new Delete(ROW, -1);
-        table.delete(delete);
-        fail("Negative timestamps should not have been allowed");
-      } catch (IllegalArgumentException ex) {
-        assertTrue(ex.getMessage().contains("negative"));
-      }
-
-      try {
-        Delete delete = new Delete(ROW);
-        delete.addFamily(FAMILY, -1);
-        table.delete(delete);
-        fail("Negative timestamps should not have been allowed");
-      } catch (IllegalArgumentException ex) {
-        assertTrue(ex.getMessage().contains("negative"));
-      }
-
-      try {
-        Scan scan = new Scan();
-        scan.setTimeRange(-1, 1);
-        table.getScanner(scan);
-        fail("Negative timestamps should not have been allowed");
-      } catch (IllegalArgumentException ex) {
-        assertTrue(ex.getMessage().contains("negative"));
-      }
-
-      // KeyValue should allow negative timestamps for backwards compat. Otherwise, if the user
-      // already has negative timestamps in cluster data, HBase won't be able to handle that
-      try {
-        new KeyValue(Bytes.toBytes(42), Bytes.toBytes(42), Bytes.toBytes(42), -1,
-                Bytes.toBytes(42));
-      } catch (IllegalArgumentException ex) {
-        fail("KeyValue SHOULD allow negative timestamps");
-      }
-
-    }
-  }
-
-  @Test
-  public void testRawScanRespectsVersions() throws Exception {
-    final TableName tableName = name.getTableName();
-    try (Table table = TEST_UTIL.createTable(tableName, FAMILY)) {
-      byte[] row = Bytes.toBytes("row");
-
-      // put the same row 4 times, with different values
-      Put p = new Put(row);
-      p.addColumn(FAMILY, QUALIFIER, 10, VALUE);
-      table.put(p);
-      p = new Put(row);
-      p.addColumn(FAMILY, QUALIFIER, 11, ArrayUtils.add(VALUE, (byte) 2));
-      table.put(p);
-
-      p = new Put(row);
-      p.addColumn(FAMILY, QUALIFIER, 12, ArrayUtils.add(VALUE, (byte) 3));
-      table.put(p);
-
-      p = new Put(row);
-      p.addColumn(FAMILY, QUALIFIER, 13, ArrayUtils.add(VALUE, (byte) 4));
-      table.put(p);
-
-      int versions = 4;
-      Scan s = new Scan(row);
-      // get all the possible versions
-      s.setMaxVersions();
-      s.setRaw(true);
-
-      try (ResultScanner scanner = table.getScanner(s)) {
-        int count = 0;
-        for (Result r : scanner) {
-          assertEquals("Found an unexpected number of results for the row!", versions,
-                  r.listCells().size());
-          count++;
-        }
-        assertEquals("Found more than a single row when raw scanning the table with a single row!",
-                1, count);
-      }
-
-      // then if we decrease the number of versions, but keep the scan raw, we should see exactly
-      // that number of versions
-      versions = 2;
-      s.setMaxVersions(versions);
-      try (ResultScanner scanner = table.getScanner(s)) {
-        int count = 0;
-        for (Result r : scanner) {
-          assertEquals("Found an unexpected number of results for the row!", versions,
-                  r.listCells().size());
-          count++;
-        }
-        assertEquals("Found more than a single row when raw scanning the table with a single row!",
-                1, count);
-      }
-
-      // finally, if we turn off raw scanning, but max out the number of versions, we should go back
-      // to seeing just three
-      versions = 3;
-      s.setMaxVersions(versions);
-      try (ResultScanner scanner = table.getScanner(s)) {
-        int count = 0;
-        for (Result r : scanner) {
-          assertEquals("Found an unexpected number of results for the row!", versions,
-                  r.listCells().size());
-          count++;
-        }
-        assertEquals("Found more than a single row when raw scanning the table with a single row!",
-                1, count);
-      }
-
-    }
-    TEST_UTIL.deleteTable(tableName);
-  }
-
-  @Test
-  public void testEmptyFilterList() throws Exception {
-    // Test Initialization.
-    final TableName tableName = name.getTableName();
-    try (Table table = TEST_UTIL.createTable(tableName, FAMILY)) {
-
-      // Insert one row each region
-      Put put = new Put(Bytes.toBytes("row"));
-      put.addColumn(FAMILY, QUALIFIER, VALUE);
-      table.put(put);
-
-      List<Result> scanResults = new LinkedList<>();
-      Scan scan = new Scan();
-      scan.setFilter(new FilterList());
-      try (ResultScanner scanner = table.getScanner(scan)) {
-        for (Result r : scanner) {
-          scanResults.add(r);
-        }
-      }
-      assertEquals(1, scanResults.size());
-      Get g = new Get(Bytes.toBytes("row"));
-      g.setFilter(new FilterList());
-      Result getResult = table.get(g);
-      Result scanResult = scanResults.get(0);
-      assertEquals(scanResult.rawCells().length, getResult.rawCells().length);
-      for (int i = 0; i != scanResult.rawCells().length; ++i) {
-        Cell scanCell = scanResult.rawCells()[i];
-        Cell getCell = getResult.rawCells()[i];
-        assertEquals(0, Bytes.compareTo(CellUtil.cloneRow(scanCell),
-                CellUtil.cloneRow(getCell)));
-        assertEquals(0, Bytes.compareTo(CellUtil.cloneFamily(scanCell),
-                CellUtil.cloneFamily(getCell)));
-        assertEquals(0, Bytes.compareTo(CellUtil.cloneQualifier(scanCell),
-                CellUtil.cloneQualifier(getCell)));
-        assertEquals(0, Bytes.compareTo(CellUtil.cloneValue(scanCell),
-                CellUtil.cloneValue(getCell)));
-      }
-    }
-  }
-
-  @Test
-  public void testSmallScan() throws Exception {
-    // Test Initialization.
-    final TableName tableName = name.getTableName();
-    try (Table table = TEST_UTIL.createTable(tableName, FAMILY)) {
-
-      // Insert one row each region
-      int insertNum = 10;
-      for (int i = 0; i < 10; i++) {
-        Put put = new Put(Bytes.toBytes("row" + String.format("%03d", i)));
-        put.addColumn(FAMILY, QUALIFIER, VALUE);
-        table.put(put);
-      }
-
-      // normal scan
-      try (ResultScanner scanner = table.getScanner(new Scan())) {
-        int count = 0;
-        for (Result r : scanner) {
-          assertTrue(!r.isEmpty());
-          count++;
-        }
-        assertEquals(insertNum, count);
-      }
-
-      // small scan
-      Scan scan = new Scan(HConstants.EMPTY_START_ROW, HConstants.EMPTY_END_ROW);
-      scan.setSmall(true);
-      scan.setCaching(2);
-      try (ResultScanner scanner = table.getScanner(scan)) {
-        int count = 0;
-        for (Result r : scanner) {
-          assertTrue(!r.isEmpty());
-          count++;
-        }
-        assertEquals(insertNum, count);
-      }
-    }
-  }
-
-  @Test
-  public void testSuperSimpleWithReverseScan() throws Exception {
-    final TableName tableName = name.getTableName();
-    try (Table ht = TEST_UTIL.createTable(tableName, FAMILY)) {
-      Put put = new Put(Bytes.toBytes("0-b11111-0000000000000000000"));
-      put.addColumn(FAMILY, QUALIFIER, VALUE);
-      ht.put(put);
-      put = new Put(Bytes.toBytes("0-b11111-0000000000000000002"));
-      put.addColumn(FAMILY, QUALIFIER, VALUE);
-      ht.put(put);
-      put = new Put(Bytes.toBytes("0-b11111-0000000000000000004"));
-      put.addColumn(FAMILY, QUALIFIER, VALUE);
-      ht.put(put);
-      put = new Put(Bytes.toBytes("0-b11111-0000000000000000006"));
-      put.addColumn(FAMILY, QUALIFIER, VALUE);
-      ht.put(put);
-      put = new Put(Bytes.toBytes("0-b11111-0000000000000000008"));
-      put.addColumn(FAMILY, QUALIFIER, VALUE);
-      ht.put(put);
-      put = new Put(Bytes.toBytes("0-b22222-0000000000000000001"));
-      put.addColumn(FAMILY, QUALIFIER, VALUE);
-      ht.put(put);
-      put = new Put(Bytes.toBytes("0-b22222-0000000000000000003"));
-      put.addColumn(FAMILY, QUALIFIER, VALUE);
-      ht.put(put);
-      put = new Put(Bytes.toBytes("0-b22222-0000000000000000005"));
-      put.addColumn(FAMILY, QUALIFIER, VALUE);
-      ht.put(put);
-      put = new Put(Bytes.toBytes("0-b22222-0000000000000000007"));
-      put.addColumn(FAMILY, QUALIFIER, VALUE);
-      ht.put(put);
-      put = new Put(Bytes.toBytes("0-b22222-0000000000000000009"));
-      put.addColumn(FAMILY, QUALIFIER, VALUE);
-      ht.put(put);
-      Scan scan = new Scan(Bytes.toBytes("0-b11111-9223372036854775807"),
-              Bytes.toBytes("0-b11111-0000000000000000000"));
-      scan.setReversed(true);
-      try (ResultScanner scanner = ht.getScanner(scan)) {
-        Result result = scanner.next();
-        assertTrue(Bytes.equals(result.getRow(),
-                Bytes.toBytes("0-b11111-0000000000000000008")));
-      }
-    }
-  }
-
-  @Test
-  public void testFiltersWithReverseScan() throws Exception {
-    final TableName tableName = name.getTableName();
-    try (Table ht = TEST_UTIL.createTable(tableName, FAMILY)) {
-      byte[][] ROWS = makeN(ROW, 10);
-      byte[][] QUALIFIERS = {Bytes.toBytes("col0-<d2v1>-<d3v2>"),
-              Bytes.toBytes("col1-<d2v1>-<d3v2>"),
-              Bytes.toBytes("col2-<d2v1>-<d3v2>"),
-              Bytes.toBytes("col3-<d2v1>-<d3v2>"),
-              Bytes.toBytes("col4-<d2v1>-<d3v2>"),
-              Bytes.toBytes("col5-<d2v1>-<d3v2>"),
-              Bytes.toBytes("col6-<d2v1>-<d3v2>"),
-              Bytes.toBytes("col7-<d2v1>-<d3v2>"),
-              Bytes.toBytes("col8-<d2v1>-<d3v2>"),
-              Bytes.toBytes("col9-<d2v1>-<d3v2>")};
-      for (int i = 0; i < 10; i++) {
-        Put put = new Put(ROWS[i]);
-        put.addColumn(FAMILY, QUALIFIERS[i], VALUE);
-        ht.put(put);
-      }
-      Scan scan = new Scan();
-      scan.setReversed(true);
-      scan.addFamily(FAMILY);
-      Filter filter = new QualifierFilter(CompareOperator.EQUAL,
-              new RegexStringComparator("col[1-5]"));
-      scan.setFilter(filter);
-      try (ResultScanner scanner = ht.getScanner(scan)) {
-        int expectedIndex = 5;
-        for (Result result : scanner) {
-          assertEquals(1, result.size());
-          Cell c = result.rawCells()[0];
-          assertTrue(Bytes.equals(c.getRowArray(), c.getRowOffset(), c.getRowLength(),
-                  ROWS[expectedIndex], 0, ROWS[expectedIndex].length));
-          assertTrue(Bytes.equals(c.getQualifierArray(), c.getQualifierOffset(),
-                  c.getQualifierLength(), QUALIFIERS[expectedIndex], 0,
-                  QUALIFIERS[expectedIndex].length));
-          expectedIndex--;
-        }
-        assertEquals(0, expectedIndex);
-      }
-    }
-  }
-
-  @Test
-  public void testKeyOnlyFilterWithReverseScan() throws Exception {
-    final TableName tableName = name.getTableName();
-    try (Table ht = TEST_UTIL.createTable(tableName, FAMILY)) {
-      byte[][] ROWS = makeN(ROW, 10);
-      byte[][] QUALIFIERS = {Bytes.toBytes("col0-<d2v1>-<d3v2>"),
-              Bytes.toBytes("col1-<d2v1>-<d3v2>"),
-              Bytes.toBytes("col2-<d2v1>-<d3v2>"),
-              Bytes.toBytes("col3-<d2v1>-<d3v2>"),
-              Bytes.toBytes("col4-<d2v1>-<d3v2>"),
-              Bytes.toBytes("col5-<d2v1>-<d3v2>"),
-              Bytes.toBytes("col6-<d2v1>-<d3v2>"),
-              Bytes.toBytes("col7-<d2v1>-<d3v2>"),
-              Bytes.toBytes("col8-<d2v1>-<d3v2>"),
-              Bytes.toBytes("col9-<d2v1>-<d3v2>")};
-      for (int i = 0; i < 10; i++) {
-        Put put = new Put(ROWS[i]);
-        put.addColumn(FAMILY, QUALIFIERS[i], VALUE);
-        ht.put(put);
-      }
-      Scan scan = new Scan();
-      scan.setReversed(true);
-      scan.addFamily(FAMILY);
-      Filter filter = new KeyOnlyFilter(true);
-      scan.setFilter(filter);
-      try (ResultScanner scanner = ht.getScanner(scan)) {
-        int count = 0;
-        for (Result result : ht.getScanner(scan)) {
-          assertEquals(1, result.size());
-          assertEquals(Bytes.SIZEOF_INT, result.rawCells()[0].getValueLength());
-          assertEquals(VALUE.length, Bytes.toInt(CellUtil.cloneValue(result.rawCells()[0])));
-          count++;
-        }
-        assertEquals(10, count);
-      }
-    }
-  }
-
-  /**
-   * Test simple table and non-existent row cases.
-   */
-  @Test
-  public void testSimpleMissingWithReverseScan() throws Exception {
-    final TableName tableName = name.getTableName();
-    try (Table ht = TEST_UTIL.createTable(tableName, FAMILY)) {
-      byte[][] ROWS = makeN(ROW, 4);
-
-      // Try to get a row on an empty table
-      Scan scan = new Scan();
-      scan.setReversed(true);
-      Result result = getSingleScanResult(ht, scan);
-      assertNullResult(result);
-
-      scan = new Scan(ROWS[0]);
-      scan.setReversed(true);
-      result = getSingleScanResult(ht, scan);
-      assertNullResult(result);
-
-      scan = new Scan(ROWS[0], ROWS[1]);
-      scan.setReversed(true);
-      result = getSingleScanResult(ht, scan);
-      assertNullResult(result);
-
-      scan = new Scan();
-      scan.setReversed(true);
-      scan.addFamily(FAMILY);
-      result = getSingleScanResult(ht, scan);
-      assertNullResult(result);
-
-      scan = new Scan();
-      scan.setReversed(true);
-      scan.addColumn(FAMILY, QUALIFIER);
-      result = getSingleScanResult(ht, scan);
-      assertNullResult(result);
-
-      // Insert a row
-
-      Put put = new Put(ROWS[2]);
-      put.addColumn(FAMILY, QUALIFIER, VALUE);
-      ht.put(put);
-
-      // Make sure we can scan the row
-      scan = new Scan();
-      scan.setReversed(true);
-      result = getSingleScanResult(ht, scan);
-      assertSingleResult(result, ROWS[2], FAMILY, QUALIFIER, VALUE);
-
-      scan = new Scan(ROWS[3], ROWS[0]);
-      scan.setReversed(true);
-      result = getSingleScanResult(ht, scan);
-      assertSingleResult(result, ROWS[2], FAMILY, QUALIFIER, VALUE);
-
-      scan = new Scan(ROWS[2], ROWS[1]);
-      scan.setReversed(true);
-      result = getSingleScanResult(ht, scan);
-      assertSingleResult(result, ROWS[2], FAMILY, QUALIFIER, VALUE);
-
-      // Try to scan empty rows around it
-      // Introduced MemStore#shouldSeekForReverseScan to fix the following
-      scan = new Scan(ROWS[1]);
-      scan.setReversed(true);
-      result = getSingleScanResult(ht, scan);
-      assertNullResult(result);
-    }
-  }
-
-  @Test
-  public void testNullWithReverseScan() throws Exception {
-    final TableName tableName = name.getTableName();
-    try (Table ht = TEST_UTIL.createTable(tableName, FAMILY)) {
-      // Null qualifier (should work)
-      Put put = new Put(ROW);
-      put.addColumn(FAMILY, null, VALUE);
-      ht.put(put);
-      scanTestNull(ht, ROW, FAMILY, VALUE, true);
-      Delete delete = new Delete(ROW);
-      delete.addColumns(FAMILY, null);
-      ht.delete(delete);
-    }
-
-    // Use a new table
-    try (Table ht =
-       TEST_UTIL.createTable(TableName.valueOf(name.getTableName().toString() + "2"), FAMILY)) {
-      // Empty qualifier, byte[0] instead of null (should work)
-      Put put = new Put(ROW);
-      put.addColumn(FAMILY, HConstants.EMPTY_BYTE_ARRAY, VALUE);
-      ht.put(put);
-      scanTestNull(ht, ROW, FAMILY, VALUE, true);
-      TEST_UTIL.flush();
-      scanTestNull(ht, ROW, FAMILY, VALUE, true);
-      Delete delete = new Delete(ROW);
-      delete.addColumns(FAMILY, HConstants.EMPTY_BYTE_ARRAY);
-      ht.delete(delete);
-      // Null value
-      put = new Put(ROW);
-      put.addColumn(FAMILY, QUALIFIER, null);
-      ht.put(put);
-      Scan scan = new Scan();
-      scan.setReversed(true);
-      scan.addColumn(FAMILY, QUALIFIER);
-      Result result = getSingleScanResult(ht, scan);
-      assertSingleResult(result, ROW, FAMILY, QUALIFIER, null);
-    }
-  }
-
-  @Test
-  @SuppressWarnings("checkstyle:MethodLength")
-  public void testDeletesWithReverseScan() throws Exception {
-    final TableName tableName = name.getTableName();
-    byte[][] ROWS = makeNAscii(ROW, 6);
-    byte[][] FAMILIES = makeNAscii(FAMILY, 3);
-    byte[][] VALUES = makeN(VALUE, 5);
-    long[] ts = { 1000, 2000, 3000, 4000, 5000 };
-    try (Table ht = TEST_UTIL.createTable(tableName, FAMILIES, 3)) {
-
-      Put put = new Put(ROW);
-      put.addColumn(FAMILIES[0], QUALIFIER, ts[0], VALUES[0]);
-      put.addColumn(FAMILIES[0], QUALIFIER, ts[1], VALUES[1]);
-      ht.put(put);
-
-      Delete delete = new Delete(ROW);
-      delete.addFamily(FAMILIES[0], ts[0]);
-      ht.delete(delete);
-
-      Scan scan = new Scan(ROW);
-      scan.setReversed(true);
-      scan.addFamily(FAMILIES[0]);
-      scan.setMaxVersions(Integer.MAX_VALUE);
-      Result result = getSingleScanResult(ht, scan);
-      assertNResult(result, ROW, FAMILIES[0], QUALIFIER, new long[]{ts[1]},
-              new byte[][]{VALUES[1]}, 0, 0);
-
-      // Test delete latest version
-      put = new Put(ROW);
-      put.addColumn(FAMILIES[0], QUALIFIER, ts[4], VALUES[4]);
-      put.addColumn(FAMILIES[0], QUALIFIER, ts[2], VALUES[2]);
-      put.addColumn(FAMILIES[0], QUALIFIER, ts[3], VALUES[3]);
-      put.addColumn(FAMILIES[0], null, ts[4], VALUES[4]);
-      put.addColumn(FAMILIES[0], null, ts[2], VALUES[2]);
-      put.addColumn(FAMILIES[0], null, ts[3], VALUES[3]);
-      ht.put(put);
-
-      delete = new Delete(ROW);
-      delete.addColumn(FAMILIES[0], QUALIFIER); // ts[4]
-      ht.delete(delete);
-
-      scan = new Scan(ROW);
-      scan.setReversed(true);
-      scan.addColumn(FAMILIES[0], QUALIFIER);
-      scan.setMaxVersions(Integer.MAX_VALUE);
-      result = getSingleScanResult(ht, scan);
-      assertNResult(result, ROW, FAMILIES[0], QUALIFIER, new long[]{ts[1],
-              ts[2], ts[3]}, new byte[][]{VALUES[1], VALUES[2], VALUES[3]}, 0, 2);
-
-      // Test for HBASE-1847
-      delete = new Delete(ROW);
-      delete.addColumn(FAMILIES[0], null);
-      ht.delete(delete);
-
-      // Cleanup null qualifier
-      delete = new Delete(ROW);
-      delete.addColumns(FAMILIES[0], null);
-      ht.delete(delete);
-
-      // Expected client behavior might be that you can re-put deleted values
-      // But alas, this is not to be. We can't put them back in either case.
-
-      put = new Put(ROW);
-      put.addColumn(FAMILIES[0], QUALIFIER, ts[0], VALUES[0]);
-      put.addColumn(FAMILIES[0], QUALIFIER, ts[4], VALUES[4]);
-      ht.put(put);
-
-      // The Scanner returns the previous values, the expected-naive-unexpected
-      // behavior
-
-      scan = new Scan(ROW);
-      scan.setReversed(true);
-      scan.addFamily(FAMILIES[0]);
-      scan.setMaxVersions(Integer.MAX_VALUE);
-      result = getSingleScanResult(ht, scan);
-      assertNResult(result, ROW, FAMILIES[0], QUALIFIER, new long[]{ts[1],
-              ts[2], ts[3]}, new byte[][]{VALUES[1], VALUES[2], VALUES[3]}, 0, 2);
-
-      // Test deleting an entire family from one row but not the other various
-      // ways
-
-      put = new Put(ROWS[0]);
-      put.addColumn(FAMILIES[1], QUALIFIER, ts[0], VALUES[0]);
-      put.addColumn(FAMILIES[1], QUALIFIER, ts[1], VALUES[1]);
-      put.addColumn(FAMILIES[2], QUALIFIER, ts[2], VALUES[2]);
-      put.addColumn(FAMILIES[2], QUALIFIER, ts[3], VALUES[3]);
-      ht.put(put);
-
-      put = new Put(ROWS[1]);
-      put.addColumn(FAMILIES[1], QUALIFIER, ts[0], VALUES[0]);
-      put.addColumn(FAMILIES[1], QUALIFIER, ts[1], VALUES[1]);
-      put.addColumn(FAMILIES[2], QUALIFIER, ts[2], VALUES[2]);
-      put.addColumn(FAMILIES[2], QUALIFIER, ts[3], VALUES[3]);
-      ht.put(put);
-
-      put = new Put(ROWS[2]);
-      put.addColumn(FAMILIES[1], QUALIFIER, ts[0], VALUES[0]);
-      put.addColumn(FAMILIES[1], QUALIFIER, ts[1], VALUES[1]);
-      put.addColumn(FAMILIES[2], QUALIFIER, ts[2], VALUES[2]);
-      put.addColumn(FAMILIES[2], QUALIFIER, ts[3], VALUES[3]);
-      ht.put(put);
-
-      delete = new Delete(ROWS[0]);
-      delete.addFamily(FAMILIES[2]);
-      ht.delete(delete);
-
-      delete = new Delete(ROWS[1]);
-      delete.addColumns(FAMILIES[1], QUALIFIER);
-      ht.delete(delete);
-
-      delete = new Delete(ROWS[2]);
-      delete.addColumn(FAMILIES[1], QUALIFIER);
-      delete.addColumn(FAMILIES[1], QUALIFIER);
-      delete.addColumn(FAMILIES[2], QUALIFIER);
-      ht.delete(delete);
-
-      scan = new Scan(ROWS[0]);
-      scan.setReversed(true);
-      scan.addFamily(FAMILIES[1]);
-      scan.addFamily(FAMILIES[2]);
-      scan.setMaxVersions(Integer.MAX_VALUE);
-      result = getSingleScanResult(ht, scan);
-      assertTrue("Expected 2 keys but received " + result.size(),
-              result.size() == 2);
-      assertNResult(result, ROWS[0], FAMILIES[1], QUALIFIER, new long[]{ts[0],
-              ts[1]}, new byte[][]{VALUES[0], VALUES[1]}, 0, 1);
-
-      scan = new Scan(ROWS[1]);
-      scan.setReversed(true);
-      scan.addFamily(FAMILIES[1]);
-      scan.addFamily(FAMILIES[2]);
-      scan.setMaxVersions(Integer.MAX_VALUE);
-      result = getSingleScanResult(ht, scan);
-      assertTrue("Expected 2 keys but received " + result.size(),
-              result.size() == 2);
-
-      scan = new Scan(ROWS[2]);
-      scan.setReversed(true);
-      scan.addFamily(FAMILIES[1]);
-      scan.addFamily(FAMILIES[2]);
-      scan.setMaxVersions(Integer.MAX_VALUE);
-      result = getSingleScanResult(ht, scan);
-      assertEquals(1, result.size());
-      assertNResult(result, ROWS[2], FAMILIES[2], QUALIFIER,
-              new long[]{ts[2]}, new byte[][]{VALUES[2]}, 0, 0);
-
-      // Test if we delete the family first in one row (HBASE-1541)
-
-      delete = new Delete(ROWS[3]);
-      delete.addFamily(FAMILIES[1]);
-      ht.delete(delete);
-
-      put = new Put(ROWS[3]);
-      put.addColumn(FAMILIES[2], QUALIFIER, VALUES[0]);
-      ht.put(put);
-
-      put = new Put(ROWS[4]);
-      put.addColumn(FAMILIES[1], QUALIFIER, VALUES[1]);
-      put.addColumn(FAMILIES[2], QUALIFIER, VALUES[2]);
-      ht.put(put);
-
-      scan = new Scan(ROWS[4]);
-      scan.setReversed(true);
-      scan.addFamily(FAMILIES[1]);
-      scan.addFamily(FAMILIES[2]);
-      scan.setMaxVersions(Integer.MAX_VALUE);
-      try (ResultScanner scanner = ht.getScanner(scan)) {
-        result = scanner.next();
-        assertTrue("Expected 2 keys but received " + result.size(),
-                result.size() == 2);
-        assertTrue(Bytes.equals(CellUtil.cloneRow(result.rawCells()[0]), ROWS[4]));
-        assertTrue(Bytes.equals(CellUtil.cloneRow(result.rawCells()[1]), ROWS[4]));
-        assertTrue(Bytes.equals(CellUtil.cloneValue(result.rawCells()[0]), VALUES[1]));
-        assertTrue(Bytes.equals(CellUtil.cloneValue(result.rawCells()[1]), VALUES[2]));
-        result = scanner.next();
-        assertTrue("Expected 1 key but received " + result.size(),
-                result.size() == 1);
-        assertTrue(Bytes.equals(CellUtil.cloneRow(result.rawCells()[0]), ROWS[3]));
-        assertTrue(Bytes.equals(CellUtil.cloneValue(result.rawCells()[0]), VALUES[0]));
-      }
-    }
-  }
-
-  /**
-   * Tests reversed scan under multi regions
-   */
-  @Test
-  public void testReversedScanUnderMultiRegions() throws Exception {
-    // Test Initialization.
-    final TableName tableName = name.getTableName();
-    byte[] maxByteArray = ConnectionUtils.MAX_BYTE_ARRAY;
-    byte[][] splitRows = new byte[][] { Bytes.toBytes("005"),
-        Bytes.add(Bytes.toBytes("005"), Bytes.multiple(maxByteArray, 16)),
-        Bytes.toBytes("006"),
-        Bytes.add(Bytes.toBytes("006"), Bytes.multiple(maxByteArray, 8)),
-        Bytes.toBytes("007"),
-        Bytes.add(Bytes.toBytes("007"), Bytes.multiple(maxByteArray, 4)),
-        Bytes.toBytes("008"), Bytes.multiple(maxByteArray, 2) };
-    try (Table table = TEST_UTIL.createTable(tableName, FAMILY, splitRows)) {
-      TEST_UTIL.waitUntilAllRegionsAssigned(table.getName());
-
-      try (RegionLocator l = TEST_UTIL.getConnection().getRegionLocator(tableName)) {
-        assertEquals(splitRows.length + 1, l.getAllRegionLocations().size());
-      }
-      // Insert one row each region
-      int insertNum = splitRows.length;
-      for (int i = 0; i < insertNum; i++) {
-        Put put = new Put(splitRows[i]);
-        put.addColumn(FAMILY, QUALIFIER, VALUE);
-        table.put(put);
-      }
-
-      // scan forward
-      try (ResultScanner scanner = table.getScanner(new Scan())) {
-        int count = 0;
-        for (Result r : scanner) {
-          assertTrue(!r.isEmpty());
-          count++;
-        }
-        assertEquals(insertNum, count);
-      }
-
-      // scan backward
-      Scan scan = new Scan();
-      scan.setReversed(true);
-      try (ResultScanner scanner = table.getScanner(scan)) {
-        int count = 0;
-        byte[] lastRow = null;
-        for (Result r : scanner) {
-          assertTrue(!r.isEmpty());
-          count++;
-          byte[] thisRow = r.getRow();
-          if (lastRow != null) {
-            assertTrue("Error scan order, last row= " + Bytes.toString(lastRow)
-                            + ",this row=" + Bytes.toString(thisRow),
-                    Bytes.compareTo(thisRow, lastRow) < 0);
-          }
-          lastRow = thisRow;
-        }
-        assertEquals(insertNum, count);
-      }
-    }
-  }
-
-  /**
-   * Tests reversed scan under multi regions
-   */
-  @Test
-  public void testSmallReversedScanUnderMultiRegions() throws Exception {
-    // Test Initialization.
-    final TableName tableName = name.getTableName();
-    byte[][] splitRows = new byte[][]{
-        Bytes.toBytes("000"), Bytes.toBytes("002"), Bytes.toBytes("004"),
-        Bytes.toBytes("006"), Bytes.toBytes("008"), Bytes.toBytes("010")};
-    try (Table table = TEST_UTIL.createTable(tableName, FAMILY, splitRows)) {
-      TEST_UTIL.waitUntilAllRegionsAssigned(table.getName());
-
-      try (RegionLocator l = TEST_UTIL.getConnection().getRegionLocator(tableName)) {
-        assertEquals(splitRows.length + 1, l.getAllRegionLocations().size());
-      }
-      for (byte[] splitRow : splitRows) {
-        Put put = new Put(splitRow);
-        put.addColumn(FAMILY, QUALIFIER, VALUE);
-        table.put(put);
-
-        byte[] nextRow = Bytes.copy(splitRow);
-        nextRow[nextRow.length - 1]++;
-
-        put = new Put(nextRow);
-        put.addColumn(FAMILY, QUALIFIER, VALUE);
-        table.put(put);
-      }
-
-      // scan forward
-      try (ResultScanner scanner = table.getScanner(new Scan())) {
-        int count = 0;
-        for (Result r : scanner) {
-          assertTrue(!r.isEmpty());
-          count++;
-        }
-        assertEquals(12, count);
-      }
-
-      reverseScanTest(table, false);
-      reverseScanTest(table, true);
-    }
-  }
-
-  private void reverseScanTest(Table table, boolean small) throws IOException {
-    // scan backward
-    Scan scan = new Scan();
-    scan.setReversed(true);
-    try (ResultScanner scanner = table.getScanner(scan)) {
-      int count = 0;
-      byte[] lastRow = null;
-      for (Result r : scanner) {
-        assertTrue(!r.isEmpty());
-        count++;
-        byte[] thisRow = r.getRow();
-        if (lastRow != null) {
-          assertTrue("Error scan order, last row= " + Bytes.toString(lastRow)
-                          + ",this row=" + Bytes.toString(thisRow),
-                  Bytes.compareTo(thisRow, lastRow) < 0);
-        }
-        lastRow = thisRow;
-      }
-      assertEquals(12, count);
-    }
-
-    scan = new Scan();
-    scan.setSmall(small);
-    scan.setReversed(true);
-    scan.setStartRow(Bytes.toBytes("002"));
-    try (ResultScanner scanner = table.getScanner(scan)) {
-      int count = 0;
-      byte[] lastRow = null;
-      for (Result r : scanner) {
-        assertTrue(!r.isEmpty());
-        count++;
-        byte[] thisRow = r.getRow();
-        if (lastRow != null) {
-          assertTrue("Error scan order, last row= " + Bytes.toString(lastRow)
-                          + ",this row=" + Bytes.toString(thisRow),
-                  Bytes.compareTo(thisRow, lastRow) < 0);
-        }
-        lastRow = thisRow;
-      }
-      assertEquals(3, count); // 000 001 002
-    }
-
-    scan = new Scan();
-    scan.setSmall(small);
-    scan.setReversed(true);
-    scan.setStartRow(Bytes.toBytes("002"));
-    scan.setStopRow(Bytes.toBytes("000"));
-    try (ResultScanner scanner = table.getScanner(scan)) {
-      int count = 0;
-      byte[] lastRow = null;
-      for (Result r : scanner) {
-        assertTrue(!r.isEmpty());
-        count++;
-        byte[] thisRow = r.getRow();
-        if (lastRow != null) {
-          assertTrue("Error scan order, last row= " + Bytes.toString(lastRow)
-                          + ",this row=" + Bytes.toString(thisRow),
-                  Bytes.compareTo(thisRow, lastRow) < 0);
-        }
-        lastRow = thisRow;
-      }
-      assertEquals(2, count); // 001 002
-    }
-
-    scan = new Scan();
-    scan.setSmall(small);
-    scan.setReversed(true);
-    scan.setStartRow(Bytes.toBytes("001"));
-    try (ResultScanner scanner = table.getScanner(scan)) {
-      int count = 0;
-      byte[] lastRow = null;
-      for (Result r : scanner) {
-        assertTrue(!r.isEmpty());
-        count++;
-        byte[] thisRow = r.getRow();
-        if (lastRow != null) {
-          assertTrue("Error scan order, last row= " + Bytes.toString(lastRow)
-                          + ",this row=" + Bytes.toString(thisRow),
-                  Bytes.compareTo(thisRow, lastRow) < 0);
-        }
-        lastRow = thisRow;
-      }
-      assertEquals(2, count); // 000 001
-    }
-
-    scan = new Scan();
-    scan.setSmall(small);
-    scan.setReversed(true);
-    scan.setStartRow(Bytes.toBytes("000"));
-    try (ResultScanner scanner = table.getScanner(scan)) {
-      int count = 0;
-      byte[] lastRow = null;
-      for (Result r : scanner) {
-        assertTrue(!r.isEmpty());
-        count++;
-        byte[] thisRow = r.getRow();
-        if (lastRow != null) {
-          assertTrue("Error scan order, last row= " + Bytes.toString(lastRow)
-                          + ",this row=" + Bytes.toString(thisRow),
-                  Bytes.compareTo(thisRow, lastRow) < 0);
-        }
-        lastRow = thisRow;
-      }
-      assertEquals(1, count); // 000
-    }
-
-    scan = new Scan();
-    scan.setSmall(small);
-    scan.setReversed(true);
-    scan.setStartRow(Bytes.toBytes("006"));
-    scan.setStopRow(Bytes.toBytes("002"));
-    try (ResultScanner scanner = table.getScanner(scan)) {
-      int count = 0;
-      byte[] lastRow = null;
-      for (Result r : scanner) {
-        assertTrue(!r.isEmpty());
-        count++;
-        byte[] thisRow = r.getRow();
-        if (lastRow != null) {
-          assertTrue("Error scan order, last row= " + Bytes.toString(lastRow)
-                          + ",this row=" + Bytes.toString(thisRow),
-                  Bytes.compareTo(thisRow, lastRow) < 0);
-        }
-        lastRow = thisRow;
-      }
-      assertEquals(4, count); // 003 004 005 006
-    }
-  }
-
-  private static Pair<byte[][], byte[][]> getStartEndKeys(List<RegionLocations> regions) {
-    final byte[][] startKeyList = new byte[regions.size()][];
-    final byte[][] endKeyList = new byte[regions.size()][];
-
-    for (int i = 0; i < regions.size(); i++) {
-      RegionInfo region = regions.get(i).getRegionLocation().getRegion();
-      startKeyList[i] = region.getStartKey();
-      endKeyList[i] = region.getEndKey();
-    }
-
-    return new Pair<>(startKeyList, endKeyList);
-  }
-
-  @Test
-  public void testFilterAllRecords() throws IOException {
-    Scan scan = new Scan();
-    scan.setBatch(1);
-    scan.setCaching(1);
-    // Filter out any records
-    scan.setFilter(new FilterList(new FirstKeyOnlyFilter(), new InclusiveStopFilter(new byte[0])));
-    try (Table table = TEST_UTIL.getConnection().getTable(TableName.NAMESPACE_TABLE_NAME)) {
-      try (ResultScanner s = table.getScanner(scan)) {
-        assertNull(s.next());
-      }
-    }
-  }
-
-  @Test
-  public void testRegionCache() throws IOException {
-    HTableDescriptor htd = new HTableDescriptor(name.getTableName());
-    HColumnDescriptor fam = new HColumnDescriptor(FAMILY);
-    htd.addFamily(fam);
-    byte[][] KEYS = HBaseTestingUtility.KEYS_FOR_HBA_CREATE_TABLE;
-    try (Admin admin = TEST_UTIL.getAdmin()) {
-      admin.createTable(htd, KEYS);
-      HRegionLocator locator =
-              (HRegionLocator) admin.getConnection().getRegionLocator(htd.getTableName());
-      List<HRegionLocation> results = locator.getAllRegionLocations();
-      int number = ((ConnectionImplementation) admin.getConnection())
-              .getNumberOfCachedRegionLocations(htd.getTableName());
-      assertEquals(results.size(), number);
-      ConnectionImplementation conn = ((ConnectionImplementation) admin.getConnection());
-      assertNotNull("Can't get cached location for row aaa",
-              conn.getCachedLocation(htd.getTableName(), Bytes.toBytes("aaa")));
-      for (byte[] startKey : HBaseTestingUtility.KEYS_FOR_HBA_CREATE_TABLE) {
-        assertNotNull("Can't get cached location for row " +
-                Bytes.toString(startKey), (conn.getCachedLocation(htd.getTableName(), startKey)));
-      }
-    }
-  }
-
-  @Test
-  public void testCellSizeLimit() throws IOException {
-    final TableName tableName = TableName.valueOf("testCellSizeLimit");
-    HTableDescriptor htd = new HTableDescriptor(tableName);
-    htd.setConfiguration(HRegion.HBASE_MAX_CELL_SIZE_KEY, Integer.toString(10 * 1024)); // 10K
-    HColumnDescriptor fam = new HColumnDescriptor(FAMILY);
-    htd.addFamily(fam);
-    try (Admin admin = TEST_UTIL.getAdmin()) {
-      admin.createTable(htd);
-    }
-    // Will succeed
-    try (Table t = TEST_UTIL.getConnection().getTable(tableName)) {
-      t.put(new Put(ROW).addColumn(FAMILY, QUALIFIER, Bytes.toBytes(0L)));
-      t.increment(new Increment(ROW).addColumn(FAMILY, QUALIFIER, 1L));
-    }
-    // Will succeed
-    try (Table t = TEST_UTIL.getConnection().getTable(tableName)) {
-      t.put(new Put(ROW).addColumn(FAMILY, QUALIFIER, new byte[9*1024]));
-    }
-    // Will fail
-    try (Table t = TEST_UTIL.getConnection().getTable(tableName)) {
-      try {
-        t.put(new Put(ROW).addColumn(FAMILY, QUALIFIER, new byte[10 * 1024]));
-        fail("Oversize cell failed to trigger exception");
-      } catch (IOException e) {
-        // expected
-      }
-      try {
-        t.append(new Append(ROW).addColumn(FAMILY, QUALIFIER, new byte[2 * 1024]));
-        fail("Oversize cell failed to trigger exception");
-      } catch (IOException e) {
-        // expected
-      }
-    }
-  }
-
-  @Test
-  public void testDeleteSpecifiedVersionOfSpecifiedColumn() throws Exception {
-    try (Admin admin = TEST_UTIL.getAdmin()) {
-      final TableName tableName = name.getTableName();
-
-      byte[][] VALUES = makeN(VALUE, 5);
-      long[] ts = {1000, 2000, 3000, 4000, 5000};
-
-      try (Table ht = TEST_UTIL.createTable(tableName, FAMILY, 5)) {
-
-        Put put = new Put(ROW);
-        // Put version 1000,2000,3000,4000 of column FAMILY:QUALIFIER
-        for (int t = 0; t < 4; t++) {
-          put.addColumn(FAMILY, QUALIFIER, ts[t], VALUES[t]);
-        }
-        ht.put(put);
-
-        Delete delete = new Delete(ROW);
-        // Delete version 3000 of column FAMILY:QUALIFIER
-        delete.addColumn(FAMILY, QUALIFIER, ts[2]);
-        ht.delete(delete);
-
-        Get get = new Get(ROW);
-        get.addColumn(FAMILY, QUALIFIER);
-        get.setMaxVersions(Integer.MAX_VALUE);
-        Result result = ht.get(get);
-        // verify version 1000,2000,4000 remains for column FAMILY:QUALIFIER
-        assertNResult(result, ROW, FAMILY, QUALIFIER, new long[]{ts[0], ts[1], ts[3]}, new byte[][]{
-                VALUES[0], VALUES[1], VALUES[3]}, 0, 2);
-
-        delete = new Delete(ROW);
-        // Delete a version 5000 of column FAMILY:QUALIFIER which didn't exist
-        delete.addColumn(FAMILY, QUALIFIER, ts[4]);
-        ht.delete(delete);
-
-        get = new Get(ROW);
-        get.addColumn(FAMILY, QUALIFIER);
-        get.setMaxVersions(Integer.MAX_VALUE);
-        result = ht.get(get);
-        // verify version 1000,2000,4000 remains for column FAMILY:QUALIFIER
-        assertNResult(result, ROW, FAMILY, QUALIFIER, new long[]{ts[0], ts[1], ts[3]}, new byte[][]{
-                VALUES[0], VALUES[1], VALUES[3]}, 0, 2);
-      }
-    }
-  }
-
-  @Test
-  public void testDeleteLatestVersionOfSpecifiedColumn() throws Exception {
-    try (Admin admin = TEST_UTIL.getAdmin()) {
-      final TableName tableName = name.getTableName();
-
-      byte[][] VALUES = makeN(VALUE, 5);
-      long[] ts = {1000, 2000, 3000, 4000, 5000};
-
-      try (Table ht = TEST_UTIL.createTable(tableName, FAMILY, 5)) {
-
-        Put put = new Put(ROW);
-        // Put version 1000,2000,3000,4000 of column FAMILY:QUALIFIER
-        for (int t = 0; t < 4; t++) {
-          put.addColumn(FAMILY, QUALIFIER, ts[t], VALUES[t]);
-        }
-        ht.put(put);
-
-        Delete delete = new Delete(ROW);
-        // Delete latest version of column FAMILY:QUALIFIER
-        delete.addColumn(FAMILY, QUALIFIER);
-        ht.delete(delete);
-
-        Get get = new Get(ROW);
-        get.addColumn(FAMILY, QUALIFIER);
-        get.setMaxVersions(Integer.MAX_VALUE);
-        Result result = ht.get(get);
-        // verify version 1000,2000,3000 remains for column FAMILY:QUALIFIER
-        assertNResult(result, ROW, FAMILY, QUALIFIER, new long[]{ts[0], ts[1], ts[2]}, new byte[][]{
-                VALUES[0], VALUES[1], VALUES[2]}, 0, 2);
-
-        delete = new Delete(ROW);
-        // Delete two latest version of column FAMILY:QUALIFIER
-        delete.addColumn(FAMILY, QUALIFIER);
-        delete.addColumn(FAMILY, QUALIFIER);
-        ht.delete(delete);
-
-        get = new Get(ROW);
-        get.addColumn(FAMILY, QUALIFIER);
-        get.setMaxVersions(Integer.MAX_VALUE);
-        result = ht.get(get);
-        // verify version 1000 remains for column FAMILY:QUALIFIER
-        assertNResult(result, ROW, FAMILY, QUALIFIER, new long[]{ts[0]}, new byte[][]{VALUES[0]},
-                0, 0);
-
-        put = new Put(ROW);
-        // Put a version 5000 of column FAMILY:QUALIFIER
-        put.addColumn(FAMILY, QUALIFIER, ts[4], VALUES[4]);
-        ht.put(put);
-
-        get = new Get(ROW);
-        get.addColumn(FAMILY, QUALIFIER);
-        get.setMaxVersions(Integer.MAX_VALUE);
-        result = ht.get(get);
-        // verify version 1000,5000 remains for column FAMILY:QUALIFIER
-        assertNResult(result, ROW, FAMILY, QUALIFIER, new long[]{ts[0], ts[4]}, new byte[][]{
-                VALUES[0], VALUES[4]}, 0, 1);
-      }
-    }
-  }
-
-  /**
-   * Test for HBASE-17125
-   */
-  @Test
-  public void testReadWithFilter() throws Exception {
-    try (Admin admin = TEST_UTIL.getAdmin()) {
-      final TableName tableName = name.getTableName();
-      try (Table table = TEST_UTIL.createTable(tableName, FAMILY, 3)) {
-
-        byte[] VALUEA = Bytes.toBytes("value-a");
-        byte[] VALUEB = Bytes.toBytes("value-b");
-        long[] ts = {1000, 2000, 3000, 4000};
-
-        Put put = new Put(ROW);
-        // Put version 1000,2000,3000,4000 of column FAMILY:QUALIFIER
-        for (int t = 0; t <= 3; t++) {
-          if (t <= 1) {
-            put.addColumn(FAMILY, QUALIFIER, ts[t], VALUEA);
-          } else {
-            put.addColumn(FAMILY, QUALIFIER, ts[t], VALUEB);
-          }
-        }
-        table.put(put);
-
-        Scan scan =
-                new Scan().setFilter(new ValueFilter(CompareOperator.EQUAL,
-                        new SubstringComparator("value-a")))
-                        .setMaxVersions(3);
-        ResultScanner scanner = table.getScanner(scan);
-        Result result = scanner.next();
-        // ts[0] has gone from user view. Only read ts[2] which value is less or equal to 3
-        assertNResult(result, ROW, FAMILY, QUALIFIER, new long[]{ts[1]}, new byte[][]{VALUEA}, 0,
-                0);
-
-        Get get =
-                new Get(ROW)
-                        .setFilter(new ValueFilter(CompareOperator.EQUAL,
-                                new SubstringComparator("value-a")))
-                        .setMaxVersions(3);
-        result = table.get(get);
-        // ts[0] has gone from user view. Only read ts[2] which value is less or equal to 3
-        assertNResult(result, ROW, FAMILY, QUALIFIER, new long[]{ts[1]}, new byte[][]{VALUEA}, 0,
-                0);
-
-        // Test with max versions 1, it should still read ts[1]
-        scan =
-                new Scan().setFilter(new ValueFilter(CompareOperator.EQUAL,
-                        new SubstringComparator("value-a")))
-                        .setMaxVersions(1);
-        scanner = table.getScanner(scan);
-        result = scanner.next();
-        // ts[0] has gone from user view. Only read ts[2] which value is less or equal to 3
-        assertNResult(result, ROW, FAMILY, QUALIFIER, new long[]{ts[1]}, new byte[][]{VALUEA}, 0,
-                0);
-
-        // Test with max versions 1, it should still read ts[1]
-        get =
-                new Get(ROW)
-                        .setFilter(new ValueFilter(CompareOperator.EQUAL,
-                                new SubstringComparator("value-a")))
-                        .setMaxVersions(1);
-        result = table.get(get);
-        // ts[0] has gone from user view. Only read ts[2] which value is less or equal to 3
-        assertNResult(result, ROW, FAMILY, QUALIFIER, new long[]{ts[1]}, new byte[][]{VALUEA}, 0,
-                0);
-
-        // Test with max versions 5, it should still read ts[1]
-        scan =
-                new Scan().setFilter(new ValueFilter(CompareOperator.EQUAL,
-                        new SubstringComparator("value-a")))
-                        .setMaxVersions(5);
-        scanner = table.getScanner(scan);
-        result = scanner.next();
-        // ts[0] has gone from user view. Only read ts[2] which value is less or equal to 3
-        assertNResult(result, ROW, FAMILY, QUALIFIER, new long[]{ts[1]}, new byte[][]{VALUEA}, 0,
-                0);
-
-        // Test with max versions 5, it should still read ts[1]
-        get =
-                new Get(ROW)
-                        .setFilter(new ValueFilter(CompareOperator.EQUAL,
-                                new SubstringComparator("value-a")))
-                        .setMaxVersions(5);
-        result = table.get(get);
-        // ts[0] has gone from user view. Only read ts[2] which value is less or equal to 3
-        assertNResult(result, ROW, FAMILY, QUALIFIER, new long[]{ts[1]}, new byte[][]{VALUEA}, 0,
-                0);
-      }
-    }
-  }
-
-  @Test
-  public void testCellUtilTypeMethods() throws IOException {
-    final TableName tableName = name.getTableName();
-    try (Table table = TEST_UTIL.createTable(tableName, FAMILY)) {
-
-      final byte[] row = Bytes.toBytes("p");
-      Put p = new Put(row);
-      p.addColumn(FAMILY, QUALIFIER, VALUE);
-      table.put(p);
-
-      try (ResultScanner scanner = table.getScanner(new Scan())) {
-        Result result = scanner.next();
-        assertNotNull(result);
-        CellScanner cs = result.cellScanner();
-        assertTrue(cs.advance());
-        Cell c = cs.current();
-        assertTrue(CellUtil.isPut(c));
-        assertFalse(CellUtil.isDelete(c));
-        assertFalse(cs.advance());
-        assertNull(scanner.next());
-      }
-
-      Delete d = new Delete(row);
-      d.addColumn(FAMILY, QUALIFIER);
-      table.delete(d);
-
-      Scan scan = new Scan();
-      scan.setRaw(true);
-      try (ResultScanner scanner = table.getScanner(scan)) {
-        Result result = scanner.next();
-        assertNotNull(result);
-        CellScanner cs = result.cellScanner();
-        assertTrue(cs.advance());
-
-        // First cell should be the delete (masking the Put)
-        Cell c = cs.current();
-        assertTrue("Cell should be a Delete: " + c, CellUtil.isDelete(c));
-        assertFalse("Cell should not be a Put: " + c, CellUtil.isPut(c));
-
-        // Second cell should be the original Put
-        assertTrue(cs.advance());
-        c = cs.current();
-        assertFalse("Cell should not be a Delete: " + c, CellUtil.isDelete(c));
-        assertTrue("Cell should be a Put: " + c, CellUtil.isPut(c));
-
-        // No more cells in this row
-        assertFalse(cs.advance());
-
-        // No more results in this scan
-        assertNull(scanner.next());
-      }
-    }
-  }
-
-  @Test(expected = DoNotRetryIOException.class)
-  public void testCreateTableWithZeroRegionReplicas() throws Exception {
-    TableName tableName = name.getTableName();
-    TableDescriptor desc = TableDescriptorBuilder.newBuilder(tableName)
-        .setColumnFamily(ColumnFamilyDescriptorBuilder.of(Bytes.toBytes("cf")))
-        .setRegionReplication(0)
-        .build();
-
-    TEST_UTIL.getAdmin().createTable(desc);
-  }
-
-  @Test(expected = DoNotRetryIOException.class)
-  public void testModifyTableWithZeroRegionReplicas() throws Exception {
-    TableName tableName = name.getTableName();
-    TableDescriptor desc = TableDescriptorBuilder.newBuilder(tableName)
-        .setColumnFamily(ColumnFamilyDescriptorBuilder.of(Bytes.toBytes("cf")))
-        .build();
-
-    TEST_UTIL.getAdmin().createTable(desc);
-    TableDescriptor newDesc = TableDescriptorBuilder.newBuilder(desc)
-        .setRegionReplication(0)
-        .build();
-
-    TEST_UTIL.getAdmin().modifyTable(newDesc);
-  }
-
-  @Test(timeout = 60000)
-  public void testModifyTableWithMemstoreData() throws Exception {
-    TableName tableName = name.getTableName();
-    createTableAndValidateTableSchemaModification(tableName, true);
-  }
-
-  @Test(timeout = 60000)
-  public void testDeleteCFWithMemstoreData() throws Exception {
-    TableName tableName = name.getTableName();
-    createTableAndValidateTableSchemaModification(tableName, false);
-  }
-
-  /**
-   * Create table and validate online schema modification
-   * @param tableName Table name
-   * @param modifyTable Modify table if true otherwise delete column family
-   * @throws IOException in case of failures
-   */
-  private void createTableAndValidateTableSchemaModification(TableName tableName,
-      boolean modifyTable) throws Exception {
-    Admin admin = TEST_UTIL.getAdmin();
-    // Create table with two Cfs
-    byte[] cf1 = Bytes.toBytes("cf1");
-    byte[] cf2 = Bytes.toBytes("cf2");
-    TableDescriptor tableDesc = TableDescriptorBuilder.newBuilder(tableName)
-        .setColumnFamily(ColumnFamilyDescriptorBuilder.of(cf1))
-        .setColumnFamily(ColumnFamilyDescriptorBuilder.of(cf2)).build();
-    admin.createTable(tableDesc);
-
-    Table t = TEST_UTIL.getConnection().getTable(tableName);
-    // Insert few records and flush the table
-    t.put(new Put(ROW).addColumn(cf1, QUALIFIER, Bytes.toBytes("val1")));
-    t.put(new Put(ROW).addColumn(cf2, QUALIFIER, Bytes.toBytes("val2")));
-    admin.flush(tableName);
-    Path tableDir = FSUtils.getTableDir(TEST_UTIL.getDefaultRootDirPath(), tableName);
-    List<Path> regionDirs = FSUtils.getRegionDirs(TEST_UTIL.getTestFileSystem(), tableDir);
-    assertTrue(regionDirs.size() == 1);
-    List<Path> familyDirs = FSUtils.getFamilyDirs(TEST_UTIL.getTestFileSystem(), regionDirs.get(0));
-    assertTrue(familyDirs.size() == 2);
-
-    // Insert record but dont flush the table
-    t.put(new Put(ROW).addColumn(cf1, QUALIFIER, Bytes.toBytes("val2")));
-    t.put(new Put(ROW).addColumn(cf2, QUALIFIER, Bytes.toBytes("val2")));
-
-    if (modifyTable) {
-      tableDesc = TableDescriptorBuilder.newBuilder(tableDesc).removeColumnFamily(cf2).build();
-      admin.modifyTable(tableDesc);
-    } else {
-      admin.deleteColumnFamily(tableName, cf2);
-    }
-    // After table modification or delete family there should be only one CF in FS
-    familyDirs = FSUtils.getFamilyDirs(TEST_UTIL.getTestFileSystem(), regionDirs.get(0));
-    assertTrue("CF dir count should be 1, but was " + familyDirs.size(), familyDirs.size() == 1);
-  }
-}
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestFromClientSide4.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestFromClientSide4.java
new file mode 100644
index 0000000..98ae322
--- /dev/null
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestFromClientSide4.java
@@ -0,0 +1,1354 @@
+/*
+ * 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, 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, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.hbase.client;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.List;
+import java.util.NavigableMap;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.Cell;
+import org.apache.hadoop.hbase.CellUtil;
+import org.apache.hadoop.hbase.ClusterMetrics.Option;
+import org.apache.hadoop.hbase.HBaseClassTestRule;
+import org.apache.hadoop.hbase.HColumnDescriptor;
+import org.apache.hadoop.hbase.HConstants;
+import org.apache.hadoop.hbase.HTableDescriptor;
+import org.apache.hadoop.hbase.KeyValue;
+import org.apache.hadoop.hbase.MiniHBaseCluster;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.TableNameTestRule;
+import org.apache.hadoop.hbase.coprocessor.MultiRowMutationEndpoint;
+import org.apache.hadoop.hbase.master.LoadBalancer;
+import org.apache.hadoop.hbase.regionserver.NoSuchColumnFamilyException;
+import org.apache.hadoop.hbase.testclassification.ClientTests;
+import org.apache.hadoop.hbase.testclassification.LargeTests;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.junit.AfterClass;
+import org.junit.Assume;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Run tests that use the HBase clients; {@link Table}.
+ * Sets up the HBase mini cluster once at start and runs through all client tests.
+ * Each creates a table named for the method and does its stuff against that.
+ *
+ * Parameterized to run with different registry implementations.
+ */
+@Category({LargeTests.class, ClientTests.class})
+@SuppressWarnings ("deprecation")
+@RunWith(Parameterized.class)
+public class TestFromClientSide4 extends FromClientSideBase {
+  private static final Logger LOG = LoggerFactory.getLogger(TestFromClientSide4.class);
+  @ClassRule
+  public static final HBaseClassTestRule CLASS_RULE =
+    HBaseClassTestRule.forClass(TestFromClientSide4.class);
+  @Rule
+  public TableNameTestRule name = new TableNameTestRule();
+
+  // To keep the child classes happy.
+  TestFromClientSide4() {
+  }
+
+  public TestFromClientSide4(Class registry, int numHedgedReqs) throws Exception {
+    initialize(registry, numHedgedReqs, MultiRowMutationEndpoint.class);
+  }
+
+  @Parameterized.Parameters
+  public static Collection parameters() {
+    return Arrays.asList(new Object[][] { { MasterRegistry.class, 1 }, { MasterRegistry.class, 2 },
+      { ZKConnectionRegistry.class, 1 } });
+  }
+
+  @AfterClass
+  public static void tearDownAfterClass() throws Exception {
+    afterClass();
+  }
+
+  /**
+   * Test batch operations with combination of valid and invalid args
+   */
+  @Test public void testBatchOperationsWithErrors() throws Exception {
+    final TableName tableName = name.getTableName();
+    try (Table foo = TEST_UTIL.createTable(tableName, new byte[][] { FAMILY }, 10)) {
+
+      int NUM_OPS = 100;
+
+      // 1.1 Put with no column families (local validation, runtime exception)
+      List<Put> puts = new ArrayList<>(NUM_OPS);
+      for (int i = 0; i != NUM_OPS; i++) {
+        Put put = new Put(Bytes.toBytes(i));
+        puts.add(put);
+      }
+
+      try {
+        foo.put(puts);
+        fail();
+      } catch (IllegalArgumentException e) {
+        // expected
+        assertEquals(NUM_OPS, puts.size());
+      }
+
+      // 1.2 Put with invalid column family
+      puts.clear();
+      for (int i = 0; i < NUM_OPS; i++) {
+        Put put = new Put(Bytes.toBytes(i));
+        put.addColumn((i % 2) == 0 ? FAMILY : INVALID_FAMILY, FAMILY, Bytes.toBytes(i));
+        puts.add(put);
+      }
+
+      try {
+        foo.put(puts);
+        fail();
+      } catch (RetriesExhaustedException e) {
+        if (e instanceof RetriesExhaustedWithDetailsException) {
+          assertThat(((RetriesExhaustedWithDetailsException)e).exceptions.get(0),
+            instanceOf(NoSuchColumnFamilyException.class));
+        } else {
+          assertThat(e.getCause(), instanceOf(NoSuchColumnFamilyException.class));
+        }
+      }
+
+      // 2.1 Get non-existent rows
+      List<Get> gets = new ArrayList<>(NUM_OPS);
+      for (int i = 0; i < NUM_OPS; i++) {
+        Get get = new Get(Bytes.toBytes(i));
+        gets.add(get);
+      }
+      Result[] getsResult = foo.get(gets);
+      assertNotNull(getsResult);
+      assertEquals(NUM_OPS, getsResult.length);
+      for (int i = 0; i < NUM_OPS; i++) {
+        Result getResult = getsResult[i];
+        if (i % 2 == 0) {
+          assertFalse(getResult.isEmpty());
+        } else {
+          assertTrue(getResult.isEmpty());
+        }
+      }
+
+      // 2.2 Get with invalid column family
+      gets.clear();
+      for (int i = 0; i < NUM_OPS; i++) {
+        Get get = new Get(Bytes.toBytes(i));
+        get.addColumn((i % 2) == 0 ? FAMILY : INVALID_FAMILY, FAMILY);
+        gets.add(get);
+      }
+      try {
+        foo.get(gets);
+        fail();
+      } catch (RetriesExhaustedException e) {
+        if (e instanceof RetriesExhaustedWithDetailsException) {
+          assertThat(((RetriesExhaustedWithDetailsException)e).exceptions.get(0),
+            instanceOf(NoSuchColumnFamilyException.class));
+        } else {
+          assertThat(e.getCause(), instanceOf(NoSuchColumnFamilyException.class));
+        }
+      }
+
+      // 3.1 Delete with invalid column family
+      List<Delete> deletes = new ArrayList<>(NUM_OPS);
+      for (int i = 0; i < NUM_OPS; i++) {
+        Delete delete = new Delete(Bytes.toBytes(i));
+        delete.addColumn((i % 2) == 0 ? FAMILY : INVALID_FAMILY, FAMILY);
+        deletes.add(delete);
+      }
+      try {
+        foo.delete(deletes);
+        fail();
+      } catch (RetriesExhaustedException e) {
+        if (e instanceof RetriesExhaustedWithDetailsException) {
+          assertThat(((RetriesExhaustedWithDetailsException)e).exceptions.get(0),
+            instanceOf(NoSuchColumnFamilyException.class));
+        } else {
+          assertThat(e.getCause(), instanceOf(NoSuchColumnFamilyException.class));
+        }
+      }
+
+      // all valid rows should have been deleted
+      gets.clear();
+      for (int i = 0; i < NUM_OPS; i++) {
+        Get get = new Get(Bytes.toBytes(i));
+        gets.add(get);
+      }
+      getsResult = foo.get(gets);
+      assertNotNull(getsResult);
+      assertEquals(NUM_OPS, getsResult.length);
+      for (Result getResult : getsResult) {
+        assertTrue(getResult.isEmpty());
+      }
+
+      // 3.2 Delete non-existent rows
+      deletes.clear();
+      for (int i = 0; i < NUM_OPS; i++) {
+        Delete delete = new Delete(Bytes.toBytes(i));
+        deletes.add(delete);
+      }
+      foo.delete(deletes);
+    }
+  }
+
+  //
+  // JIRA Testers
+  //
+
+  /**
+   * HBASE-867
+   * If millions of columns in a column family, hbase scanner won't come up
+   * Test will create numRows rows, each with numColsPerRow columns
+   * (1 version each), and attempt to scan them all.
+   * To test at scale, up numColsPerRow to the millions
+   * (have not gotten that to work running as junit though)
+   */
+  @Test public void testJiraTest867() throws Exception {
+    int numRows = 10;
+    int numColsPerRow = 2000;
+
+    final TableName tableName = name.getTableName();
+
+    byte[][] ROWS = makeN(ROW, numRows);
+    byte[][] QUALIFIERS = makeN(QUALIFIER, numColsPerRow);
+
+    try (Table ht = TEST_UTIL.createTable(tableName, FAMILY)) {
+
+      // Insert rows
+
+      for (int i = 0; i < numRows; i++) {
+        Put put = new Put(ROWS[i]);
+        put.setDurability(Durability.SKIP_WAL);
+        for (int j = 0; j < numColsPerRow; j++) {
+          put.addColumn(FAMILY, QUALIFIERS[j], QUALIFIERS[j]);
+        }
+        assertEquals(
+          "Put expected to contain " + numColsPerRow + " columns but " + "only contains " + put
+            .size(), put.size(), numColsPerRow);
+        ht.put(put);
+      }
+
+      // Get a row
+      Get get = new Get(ROWS[numRows - 1]);
+      Result result = ht.get(get);
+      assertNumKeys(result, numColsPerRow);
+      Cell[] keys = result.rawCells();
+      for (int i = 0; i < result.size(); i++) {
+        assertKey(keys[i], ROWS[numRows - 1], FAMILY, QUALIFIERS[i], QUALIFIERS[i]);
+      }
+
+      // Scan the rows
+      Scan scan = new Scan();
+      try (ResultScanner scanner = ht.getScanner(scan)) {
+        int rowCount = 0;
+        while ((result = scanner.next()) != null) {
+          assertNumKeys(result, numColsPerRow);
+          Cell[] kvs = result.rawCells();
+          for (int i = 0; i < numColsPerRow; i++) {
+            assertKey(kvs[i], ROWS[rowCount], FAMILY, QUALIFIERS[i], QUALIFIERS[i]);
+          }
+          rowCount++;
+        }
+        assertEquals(
+          "Expected to scan " + numRows + " rows but actually scanned " + rowCount + " rows",
+          rowCount, numRows);
+      }
+
+      // flush and try again
+
+      TEST_UTIL.flush();
+
+      // Get a row
+      get = new Get(ROWS[numRows - 1]);
+      result = ht.get(get);
+      assertNumKeys(result, numColsPerRow);
+      keys = result.rawCells();
+      for (int i = 0; i < result.size(); i++) {
+        assertKey(keys[i], ROWS[numRows - 1], FAMILY, QUALIFIERS[i], QUALIFIERS[i]);
+      }
+
+      // Scan the rows
+      scan = new Scan();
+      try (ResultScanner scanner = ht.getScanner(scan)) {
+        int rowCount = 0;
+        while ((result = scanner.next()) != null) {
+          assertNumKeys(result, numColsPerRow);
+          Cell[] kvs = result.rawCells();
+          for (int i = 0; i < numColsPerRow; i++) {
+            assertKey(kvs[i], ROWS[rowCount], FAMILY, QUALIFIERS[i], QUALIFIERS[i]);
+          }
+          rowCount++;
+        }
+        assertEquals(
+          "Expected to scan " + numRows + " rows but actually scanned " + rowCount + " rows",
+          rowCount, numRows);
+      }
+    }
+  }
+
+  /**
+   * HBASE-861
+   * get with timestamp will return a value if there is a version with an
+   * earlier timestamp
+   */
+  @Test public void testJiraTest861() throws Exception {
+    final TableName tableName = name.getTableName();
+    byte[][] VALUES = makeNAscii(VALUE, 7);
+    long[] STAMPS = makeStamps(7);
+
+    try (Table ht = TEST_UTIL.createTable(tableName, FAMILY, 10)) {
+
+      // Insert three versions
+
+      Put put = new Put(ROW);
+      put.addColumn(FAMILY, QUALIFIER, STAMPS[3], VALUES[3]);
+      put.addColumn(FAMILY, QUALIFIER, STAMPS[2], VALUES[2]);
+      put.addColumn(FAMILY, QUALIFIER, STAMPS[4], VALUES[4]);
+      ht.put(put);
+
+      // Get the middle value
+      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[2], VALUES[2]);
+
+      // Try to get one version before (expect fail)
+      getVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[1]);
+
+      // Try to get one version after (expect fail)
+      getVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[5]);
+
+      // Try same from storefile
+      TEST_UTIL.flush();
+      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[2], VALUES[2]);
+      getVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[1]);
+      getVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[5]);
+
+      // Insert two more versions surrounding others, into memstore
+      put = new Put(ROW);
+      put.addColumn(FAMILY, QUALIFIER, STAMPS[0], VALUES[0]);
+      put.addColumn(FAMILY, QUALIFIER, STAMPS[6], VALUES[6]);
+      ht.put(put);
+
+      // Check we can get everything we should and can't get what we shouldn't
+      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[0], VALUES[0]);
+      getVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[1]);
+      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[2], VALUES[2]);
+      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[3], VALUES[3]);
+      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[4], VALUES[4]);
+      getVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[5]);
+      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[6], VALUES[6]);
+
+      // Try same from two storefiles
+      TEST_UTIL.flush();
+      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[0], VALUES[0]);
+      getVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[1]);
+      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[2], VALUES[2]);
+      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[3], VALUES[3]);
+      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[4], VALUES[4]);
+      getVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[5]);
+      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[6], VALUES[6]);
+    }
+  }
+
+  /**
+   * HBASE-33
+   * Add a HTable get/obtainScanner method that retrieves all versions of a
+   * particular column and row between two timestamps
+   */
+  @Test public void testJiraTest33() throws Exception {
+    final TableName tableName = name.getTableName();
+    byte[][] VALUES = makeNAscii(VALUE, 7);
+    long[] STAMPS = makeStamps(7);
+
+    try (Table ht = TEST_UTIL.createTable(tableName, FAMILY, 10)) {
+
+      // Insert lots versions
+
+      Put put = new Put(ROW);
+      put.addColumn(FAMILY, QUALIFIER, STAMPS[0], VALUES[0]);
+      put.addColumn(FAMILY, QUALIFIER, STAMPS[1], VALUES[1]);
+      put.addColumn(FAMILY, QUALIFIER, STAMPS[2], VALUES[2]);
+      put.addColumn(FAMILY, QUALIFIER, STAMPS[3], VALUES[3]);
+      put.addColumn(FAMILY, QUALIFIER, STAMPS[4], VALUES[4]);
+      put.addColumn(FAMILY, QUALIFIER, STAMPS[5], VALUES[5]);
+      ht.put(put);
+
+      getVersionRangeAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 0, 5);
+      getVersionRangeAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 0, 2);
+      getVersionRangeAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 4, 5);
+      getVersionRangeAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 2, 3);
+
+      scanVersionRangeAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 0, 5);
+      scanVersionRangeAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 0, 2);
+      scanVersionRangeAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 4, 5);
+      scanVersionRangeAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 2, 3);
+
+      // Try same from storefile
+      TEST_UTIL.flush();
+
+      getVersionRangeAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 0, 5);
+      getVersionRangeAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 0, 2);
+      getVersionRangeAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 4, 5);
+      getVersionRangeAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 2, 3);
+
+      scanVersionRangeAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 0, 5);
+      scanVersionRangeAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 0, 2);
+      scanVersionRangeAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 4, 5);
+      scanVersionRangeAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 2, 3);
+    }
+  }
+
+  /**
+   * HBASE-1014
+   * commit(BatchUpdate) method should return timestamp
+   */
+  @Test public void testJiraTest1014() throws Exception {
+    final TableName tableName = name.getTableName();
+
+    try (Table ht = TEST_UTIL.createTable(tableName, FAMILY, 10)) {
+
+      long manualStamp = 12345;
+
+      // Insert lots versions
+
+      Put put = new Put(ROW);
+      put.addColumn(FAMILY, QUALIFIER, manualStamp, VALUE);
+      ht.put(put);
+
+      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, manualStamp, VALUE);
+      getVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, manualStamp - 1);
+      getVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, manualStamp + 1);
+    }
+  }
+
+  /**
+   * HBASE-1182
+   * Scan for columns > some timestamp
+   */
+  @Test public void testJiraTest1182() throws Exception {
+    final TableName tableName = name.getTableName();
+    byte[][] VALUES = makeNAscii(VALUE, 7);
+    long[] STAMPS = makeStamps(7);
+
+    try (Table ht = TEST_UTIL.createTable(tableName, FAMILY, 10)) {
+
+      // Insert lots versions
+
+      Put put = new Put(ROW);
+      put.addColumn(FAMILY, QUALIFIER, STAMPS[0], VALUES[0]);
+      put.addColumn(FAMILY, QUALIFIER, STAMPS[1], VALUES[1]);
+      put.addColumn(FAMILY, QUALIFIER, STAMPS[2], VALUES[2]);
+      put.addColumn(FAMILY, QUALIFIER, STAMPS[3], VALUES[3]);
+      put.addColumn(FAMILY, QUALIFIER, STAMPS[4], VALUES[4]);
+      put.addColumn(FAMILY, QUALIFIER, STAMPS[5], VALUES[5]);
+      ht.put(put);
+
+      getVersionRangeAndVerifyGreaterThan(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 0, 5);
+      getVersionRangeAndVerifyGreaterThan(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 2, 5);
+      getVersionRangeAndVerifyGreaterThan(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 4, 5);
+
+      scanVersionRangeAndVerifyGreaterThan(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 0, 5);
+      scanVersionRangeAndVerifyGreaterThan(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 2, 5);
+      scanVersionRangeAndVerifyGreaterThan(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 4, 5);
+
+      // Try same from storefile
+      TEST_UTIL.flush();
+
+      getVersionRangeAndVerifyGreaterThan(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 0, 5);
+      getVersionRangeAndVerifyGreaterThan(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 2, 5);
+      getVersionRangeAndVerifyGreaterThan(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 4, 5);
+
+      scanVersionRangeAndVerifyGreaterThan(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 0, 5);
+      scanVersionRangeAndVerifyGreaterThan(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 2, 5);
+      scanVersionRangeAndVerifyGreaterThan(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 4, 5);
+    }
+  }
+
+  /**
+   * HBASE-52
+   * Add a means of scanning over all versions
+   */
+  @Test public void testJiraTest52() throws Exception {
+    final TableName tableName = name.getTableName();
+    byte[][] VALUES = makeNAscii(VALUE, 7);
+    long[] STAMPS = makeStamps(7);
+
+    try (Table ht = TEST_UTIL.createTable(tableName, FAMILY, 10)) {
+
+      // Insert lots versions
+
+      Put put = new Put(ROW);
+      put.addColumn(FAMILY, QUALIFIER, STAMPS[0], VALUES[0]);
+      put.addColumn(FAMILY, QUALIFIER, STAMPS[1], VALUES[1]);
+      put.addColumn(FAMILY, QUALIFIER, STAMPS[2], VALUES[2]);
+      put.addColumn(FAMILY, QUALIFIER, STAMPS[3], VALUES[3]);
+      put.addColumn(FAMILY, QUALIFIER, STAMPS[4], VALUES[4]);
+      put.addColumn(FAMILY, QUALIFIER, STAMPS[5], VALUES[5]);
+      ht.put(put);
+
+      getAllVersionsAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 0, 5);
+
+      scanAllVersionsAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 0, 5);
+
+      // Try same from storefile
+      TEST_UTIL.flush();
+
+      getAllVersionsAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 0, 5);
+
+      scanAllVersionsAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS, VALUES, 0, 5);
+    }
+  }
+
+  @Test
+  @SuppressWarnings("checkstyle:MethodLength")
+  public void testDuplicateVersions() throws Exception {
+    final TableName tableName = name.getTableName();
+
+    long[] STAMPS = makeStamps(20);
+    byte[][] VALUES = makeNAscii(VALUE, 20);
+
+    try (Table ht = TEST_UTIL.createTable(tableName, FAMILY, 10)) {
+
+      // Insert 4 versions of same column
+      Put put = new Put(ROW);
+      put.addColumn(FAMILY, QUALIFIER, STAMPS[1], VALUES[1]);
+      put.addColumn(FAMILY, QUALIFIER, STAMPS[2], VALUES[2]);
+      put.addColumn(FAMILY, QUALIFIER, STAMPS[4], VALUES[4]);
+      put.addColumn(FAMILY, QUALIFIER, STAMPS[5], VALUES[5]);
+      ht.put(put);
+
+      // Verify we can get each one properly
+      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[1], VALUES[1]);
+      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[2], VALUES[2]);
+      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[4], VALUES[4]);
+      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[5], VALUES[5]);
+      scanVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[1], VALUES[1]);
+      scanVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[2], VALUES[2]);
+      scanVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[4], VALUES[4]);
+      scanVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[5], VALUES[5]);
+
+      // Verify we don't accidentally get others
+      getVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[0]);
+      getVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[3]);
+      getVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[6]);
+      scanVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[0]);
+      scanVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[3]);
+      scanVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[6]);
+
+      // Ensure maxVersions in query is respected
+      Get get = new Get(ROW);
+      get.addColumn(FAMILY, QUALIFIER);
+      get.readVersions(2);
+      Result result = ht.get(get);
+      assertNResult(result, ROW, FAMILY, QUALIFIER, new long[] { STAMPS[4], STAMPS[5] },
+        new byte[][] { VALUES[4], VALUES[5] }, 0, 1);
+
+      Scan scan = new Scan(ROW);
+      scan.addColumn(FAMILY, QUALIFIER);
+      scan.setMaxVersions(2);
+      result = getSingleScanResult(ht, scan);
+      assertNResult(result, ROW, FAMILY, QUALIFIER, new long[] { STAMPS[4], STAMPS[5] },
+        new byte[][] { VALUES[4], VALUES[5] }, 0, 1);
+
+      // Flush and redo
+
+      TEST_UTIL.flush();
+
+      // Verify we can get each one properly
+      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[1], VALUES[1]);
+      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[2], VALUES[2]);
+      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[4], VALUES[4]);
+      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[5], VALUES[5]);
+      scanVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[1], VALUES[1]);
+      scanVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[2], VALUES[2]);
+      scanVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[4], VALUES[4]);
+      scanVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[5], VALUES[5]);
+
+      // Verify we don't accidentally get others
+      getVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[0]);
+      getVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[3]);
+      getVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[6]);
+      scanVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[0]);
+      scanVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[3]);
+      scanVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[6]);
+
+      // Ensure maxVersions in query is respected
+      get = new Get(ROW);
+      get.addColumn(FAMILY, QUALIFIER);
+      get.readVersions(2);
+      result = ht.get(get);
+      assertNResult(result, ROW, FAMILY, QUALIFIER, new long[] { STAMPS[4], STAMPS[5] },
+        new byte[][] { VALUES[4], VALUES[5] }, 0, 1);
+
+      scan = new Scan(ROW);
+      scan.addColumn(FAMILY, QUALIFIER);
+      scan.setMaxVersions(2);
+      result = getSingleScanResult(ht, scan);
+      assertNResult(result, ROW, FAMILY, QUALIFIER, new long[] { STAMPS[4], STAMPS[5] },
+        new byte[][] { VALUES[4], VALUES[5] }, 0, 1);
+
+      // Add some memstore and retest
+
+      // Insert 4 more versions of same column and a dupe
+      put = new Put(ROW);
+      put.addColumn(FAMILY, QUALIFIER, STAMPS[3], VALUES[3]);
+      put.addColumn(FAMILY, QUALIFIER, STAMPS[4], VALUES[14]);
+      put.addColumn(FAMILY, QUALIFIER, STAMPS[6], VALUES[6]);
+      put.addColumn(FAMILY, QUALIFIER, STAMPS[7], VALUES[7]);
+      put.addColumn(FAMILY, QUALIFIER, STAMPS[8], VALUES[8]);
+      ht.put(put);
+
+      // Ensure maxVersions in query is respected
+      get = new Get(ROW);
+      get.addColumn(FAMILY, QUALIFIER);
+      get.readVersions(7);
+      result = ht.get(get);
+      assertNResult(result, ROW, FAMILY, QUALIFIER,
+        new long[] { STAMPS[2], STAMPS[3], STAMPS[4], STAMPS[5], STAMPS[6], STAMPS[7], STAMPS[8] },
+        new byte[][] { VALUES[2], VALUES[3], VALUES[14], VALUES[5], VALUES[6], VALUES[7],
+          VALUES[8] }, 0, 6);
+
+      scan = new Scan(ROW);
+      scan.addColumn(FAMILY, QUALIFIER);
+      scan.setMaxVersions(7);
+      result = getSingleScanResult(ht, scan);
+      assertNResult(result, ROW, FAMILY, QUALIFIER,
+        new long[] { STAMPS[2], STAMPS[3], STAMPS[4], STAMPS[5], STAMPS[6], STAMPS[7], STAMPS[8] },
+        new byte[][] { VALUES[2], VALUES[3], VALUES[14], VALUES[5], VALUES[6], VALUES[7],
+          VALUES[8] }, 0, 6);
+
+      get = new Get(ROW);
+      get.readVersions(7);
+      result = ht.get(get);
+      assertNResult(result, ROW, FAMILY, QUALIFIER,
+        new long[] { STAMPS[2], STAMPS[3], STAMPS[4], STAMPS[5], STAMPS[6], STAMPS[7], STAMPS[8] },
+        new byte[][] { VALUES[2], VALUES[3], VALUES[14], VALUES[5], VALUES[6], VALUES[7],
+          VALUES[8] }, 0, 6);
+
+      scan = new Scan(ROW);
+      scan.setMaxVersions(7);
+      result = getSingleScanResult(ht, scan);
+      assertNResult(result, ROW, FAMILY, QUALIFIER,
+        new long[] { STAMPS[2], STAMPS[3], STAMPS[4], STAMPS[5], STAMPS[6], STAMPS[7], STAMPS[8] },
+        new byte[][] { VALUES[2], VALUES[3], VALUES[14], VALUES[5], VALUES[6], VALUES[7],
+          VALUES[8] }, 0, 6);
+
+      // Verify we can get each one properly
+      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[1], VALUES[1]);
+      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[2], VALUES[2]);
+      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[4], VALUES[14]);
+      getVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[7], VALUES[7]);
+      scanVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[1], VALUES[1]);
+      scanVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[2], VALUES[2]);
+      scanVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[4], VALUES[14]);
+      scanVersionAndVerify(ht, ROW, FAMILY, QUALIFIER, STAMPS[7], VALUES[7]);
+
+      // Verify we don't accidentally get others
+      getVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[0]);
+      getVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[9]);
+      scanVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[0]);
+      scanVersionAndVerifyMissing(ht, ROW, FAMILY, QUALIFIER, STAMPS[9]);
+
+      // Ensure maxVersions of table is respected
+
+      TEST_UTIL.flush();
+
+      // Insert 4 more versions of same column and a dupe
+      put = new Put(ROW);
+      put.addColumn(FAMILY, QUALIFIER, STAMPS[9], VALUES[9]);
+      put.addColumn(FAMILY, QUALIFIER, STAMPS[11], VALUES[11]);
+      put.addColumn(FAMILY, QUALIFIER, STAMPS[13], VALUES[13]);
+      put.addColumn(FAMILY, QUALIFIER, STAMPS[15], VALUES[15]);
+      ht.put(put);
+
+      get = new Get(ROW);
+      get.addColumn(FAMILY, QUALIFIER);
+      get.readVersions(Integer.MAX_VALUE);
+      result = ht.get(get);
+      assertNResult(result, ROW, FAMILY, QUALIFIER,
+        new long[] { STAMPS[3], STAMPS[4], STAMPS[5], STAMPS[6], STAMPS[7], STAMPS[8], STAMPS[9],
+          STAMPS[11], STAMPS[13], STAMPS[15] },
+        new byte[][] { VALUES[3], VALUES[14], VALUES[5], VALUES[6], VALUES[7], VALUES[8], VALUES[9],
+          VALUES[11], VALUES[13], VALUES[15] }, 0, 9);
+
+      scan = new Scan(ROW);
+      scan.addColumn(FAMILY, QUALIFIER);
+      scan.setMaxVersions(Integer.MAX_VALUE);
+      result = getSingleScanResult(ht, scan);
+      assertNResult(result, ROW, FAMILY, QUALIFIER,
+        new long[] { STAMPS[3], STAMPS[4], STAMPS[5], STAMPS[6], STAMPS[7], STAMPS[8], STAMPS[9],
+          STAMPS[11], STAMPS[13], STAMPS[15] },
+        new byte[][] { VALUES[3], VALUES[14], VALUES[5], VALUES[6], VALUES[7], VALUES[8], VALUES[9],
+          VALUES[11], VALUES[13], VALUES[15] }, 0, 9);
+
+      // Delete a version in the memstore and a version in a storefile
+      Delete delete = new Delete(ROW);
+      delete.addColumn(FAMILY, QUALIFIER, STAMPS[11]);
+      delete.addColumn(FAMILY, QUALIFIER, STAMPS[7]);
+      ht.delete(delete);
+
+      // Test that it's gone
+      get = new Get(ROW);
+      get.addColumn(FAMILY, QUALIFIER);
+      get.readVersions(Integer.MAX_VALUE);
+      result = ht.get(get);
+      assertNResult(result, ROW, FAMILY, QUALIFIER,
+        new long[] { STAMPS[1], STAMPS[2], STAMPS[3], STAMPS[4], STAMPS[5], STAMPS[6], STAMPS[8],
+          STAMPS[9], STAMPS[13], STAMPS[15] },
+        new byte[][] { VALUES[1], VALUES[2], VALUES[3], VALUES[14], VALUES[5], VALUES[6], VALUES[8],
+          VALUES[9], VALUES[13], VALUES[15] }, 0, 9);
+
+      scan = new Scan(ROW);
+      scan.addColumn(FAMILY, QUALIFIER);
+      scan.setMaxVersions(Integer.MAX_VALUE);
+      result = getSingleScanResult(ht, scan);
+      assertNResult(result, ROW, FAMILY, QUALIFIER,
+        new long[] { STAMPS[1], STAMPS[2], STAMPS[3], STAMPS[4], STAMPS[5], STAMPS[6], STAMPS[8],
+          STAMPS[9], STAMPS[13], STAMPS[15] },
+        new byte[][] { VALUES[1], VALUES[2], VALUES[3], VALUES[14], VALUES[5], VALUES[6], VALUES[8],
+          VALUES[9], VALUES[13], VALUES[15] }, 0, 9);
+    }
+  }
+
+  @Test public void testUpdates() throws Exception {
+    final TableName tableName = name.getTableName();
+    try (Table hTable = TEST_UTIL.createTable(tableName, FAMILY, 10)) {
+
+      // Write a column with values at timestamp 1, 2 and 3
+      byte[] row = Bytes.toBytes("row1");
+      byte[] qualifier = Bytes.toBytes("myCol");
+      Put put = new Put(row);
+      put.addColumn(FAMILY, qualifier, 1L, Bytes.toBytes("AAA"));
+      hTable.put(put);
+
+      put = new Put(row);
+      put.addColumn(FAMILY, qualifier, 2L, Bytes.toBytes("BBB"));
+      hTable.put(put);
+
+      put = new Put(row);
+      put.addColumn(FAMILY, qualifier, 3L, Bytes.toBytes("EEE"));
+      hTable.put(put);
+
+      Get get = new Get(row);
+      get.addColumn(FAMILY, qualifier);
+      get.readAllVersions();
+
+      // Check that the column indeed has the right values at timestamps 1 and
+      // 2
+      Result result = hTable.get(get);
+      NavigableMap<Long, byte[]> navigableMap = result.getMap().get(FAMILY).get(qualifier);
+      assertEquals("AAA", Bytes.toString(navigableMap.get(1L)));
+      assertEquals("BBB", Bytes.toString(navigableMap.get(2L)));
+
+      // Update the value at timestamp 1
+      put = new Put(row);
+      put.addColumn(FAMILY, qualifier, 1L, Bytes.toBytes("CCC"));
+      hTable.put(put);
+
+      // Update the value at timestamp 2
+      put = new Put(row);
+      put.addColumn(FAMILY, qualifier, 2L, Bytes.toBytes("DDD"));
+      hTable.put(put);
+
+      // Check that the values at timestamp 2 and 1 got updated
+      result = hTable.get(get);
+      navigableMap = result.getMap().get(FAMILY).get(qualifier);
+      assertEquals("CCC", Bytes.toString(navigableMap.get(1L)));
+      assertEquals("DDD", Bytes.toString(navigableMap.get(2L)));
+    }
+  }
+
+  @Test public void testUpdatesWithMajorCompaction() throws Exception {
+    final TableName tableName = name.getTableName();
+    try (Table hTable = TEST_UTIL.createTable(tableName, FAMILY, 10);
+        Admin admin = TEST_UTIL.getAdmin()) {
+
+      // Write a column with values at timestamp 1, 2 and 3
+      byte[] row = Bytes.toBytes("row2");
+      byte[] qualifier = Bytes.toBytes("myCol");
+      Put put = new Put(row);
+      put.addColumn(FAMILY, qualifier, 1L, Bytes.toBytes("AAA"));
+      hTable.put(put);
+
+      put = new Put(row);
+      put.addColumn(FAMILY, qualifier, 2L, Bytes.toBytes("BBB"));
+      hTable.put(put);
+
+      put = new Put(row);
+      put.addColumn(FAMILY, qualifier, 3L, Bytes.toBytes("EEE"));
+      hTable.put(put);
+
+      Get get = new Get(row);
+      get.addColumn(FAMILY, qualifier);
+      get.readAllVersions();
+
+      // Check that the column indeed has the right values at timestamps 1 and
+      // 2
+      Result result = hTable.get(get);
+      NavigableMap<Long, byte[]> navigableMap = result.getMap().get(FAMILY).get(qualifier);
+      assertEquals("AAA", Bytes.toString(navigableMap.get(1L)));
+      assertEquals("BBB", Bytes.toString(navigableMap.get(2L)));
+
+      // Trigger a major compaction
+      admin.flush(tableName);
+      admin.majorCompact(tableName);
+      Thread.sleep(6000);
+
+      // Update the value at timestamp 1
+      put = new Put(row);
+      put.addColumn(FAMILY, qualifier, 1L, Bytes.toBytes("CCC"));
+      hTable.put(put);
+
+      // Update the value at timestamp 2
+      put = new Put(row);
+      put.addColumn(FAMILY, qualifier, 2L, Bytes.toBytes("DDD"));
+      hTable.put(put);
+
+      // Trigger a major compaction
+      admin.flush(tableName);
+      admin.majorCompact(tableName);
+      Thread.sleep(6000);
+
+      // Check that the values at timestamp 2 and 1 got updated
+      result = hTable.get(get);
+      navigableMap = result.getMap().get(FAMILY).get(qualifier);
+      assertEquals("CCC", Bytes.toString(navigableMap.get(1L)));
+      assertEquals("DDD", Bytes.toString(navigableMap.get(2L)));
+    }
+  }
+
+  @Test public void testMajorCompactionBetweenTwoUpdates() throws Exception {
+    final TableName tableName = name.getTableName();
+    try (Table hTable = TEST_UTIL.createTable(tableName, FAMILY, 10);
+        Admin admin = TEST_UTIL.getAdmin()) {
+
+      // Write a column with values at timestamp 1, 2 and 3
+      byte[] row = Bytes.toBytes("row3");
+      byte[] qualifier = Bytes.toBytes("myCol");
+      Put put = new Put(row);
+      put.addColumn(FAMILY, qualifier, 1L, Bytes.toBytes("AAA"));
+      hTable.put(put);
+
+      put = new Put(row);
+      put.addColumn(FAMILY, qualifier, 2L, Bytes.toBytes("BBB"));
+      hTable.put(put);
+
+      put = new Put(row);
+      put.addColumn(FAMILY, qualifier, 3L, Bytes.toBytes("EEE"));
+      hTable.put(put);
+
+      Get get = new Get(row);
+      get.addColumn(FAMILY, qualifier);
+      get.readAllVersions();
+
+      // Check that the column indeed has the right values at timestamps 1 and
+      // 2
+      Result result = hTable.get(get);
+      NavigableMap<Long, byte[]> navigableMap = result.getMap().get(FAMILY).get(qualifier);
+      assertEquals("AAA", Bytes.toString(navigableMap.get(1L)));
+      assertEquals("BBB", Bytes.toString(navigableMap.get(2L)));
+
+      // Trigger a major compaction
+      admin.flush(tableName);
+      admin.majorCompact(tableName);
+      Thread.sleep(6000);
+
+      // Update the value at timestamp 1
+      put = new Put(row);
+      put.addColumn(FAMILY, qualifier, 1L, Bytes.toBytes("CCC"));
+      hTable.put(put);
+
+      // Trigger a major compaction
+      admin.flush(tableName);
+      admin.majorCompact(tableName);
+      Thread.sleep(6000);
+
+      // Update the value at timestamp 2
+      put = new Put(row);
+      put.addColumn(FAMILY, qualifier, 2L, Bytes.toBytes("DDD"));
+      hTable.put(put);
+
+      // Trigger a major compaction
+      admin.flush(tableName);
+      admin.majorCompact(tableName);
+      Thread.sleep(6000);
+
+      // Check that the values at timestamp 2 and 1 got updated
+      result = hTable.get(get);
+      navigableMap = result.getMap().get(FAMILY).get(qualifier);
+
+      assertEquals("CCC", Bytes.toString(navigableMap.get(1L)));
+      assertEquals("DDD", Bytes.toString(navigableMap.get(2L)));
+    }
+  }
+
+  @Test public void testGet_EmptyTable() throws IOException {
+    try (Table table = TEST_UTIL.createTable(name.getTableName(), FAMILY)) {
+      Get get = new Get(ROW);
+      get.addFamily(FAMILY);
+      Result r = table.get(get);
+      assertTrue(r.isEmpty());
+    }
+  }
+
+  @Test public void testGet_NullQualifier() throws IOException {
+    try (Table table = TEST_UTIL.createTable(name.getTableName(), FAMILY)) {
+      Put put = new Put(ROW);
+      put.addColumn(FAMILY, QUALIFIER, VALUE);
+      table.put(put);
+
+      put = new Put(ROW);
+      put.addColumn(FAMILY, null, VALUE);
+      table.put(put);
+      LOG.info("Row put");
+
+      Get get = new Get(ROW);
+      get.addColumn(FAMILY, null);
+      Result r = table.get(get);
+      assertEquals(1, r.size());
+
+      get = new Get(ROW);
+      get.addFamily(FAMILY);
+      r = table.get(get);
+      assertEquals(2, r.size());
+    }
+  }
+
+  @Test public void testGet_NonExistentRow() throws IOException {
+    try (Table table = TEST_UTIL.createTable(name.getTableName(), FAMILY)) {
+      Put put = new Put(ROW);
+      put.addColumn(FAMILY, QUALIFIER, VALUE);
+      table.put(put);
+      LOG.info("Row put");
+
+      Get get = new Get(ROW);
+      get.addFamily(FAMILY);
+      Result r = table.get(get);
+      assertFalse(r.isEmpty());
+      System.out.println("Row retrieved successfully");
+
+      byte[] missingrow = Bytes.toBytes("missingrow");
+      get = new Get(missingrow);
+      get.addFamily(FAMILY);
+      r = table.get(get);
+      assertTrue(r.isEmpty());
+      LOG.info("Row missing as it should be");
+    }
+  }
+
+  @Test public void testPut() throws IOException {
+    final byte[] CONTENTS_FAMILY = Bytes.toBytes("contents");
+    final byte[] SMALL_FAMILY = Bytes.toBytes("smallfam");
+    final byte[] row1 = Bytes.toBytes("row1");
+    final byte[] row2 = Bytes.toBytes("row2");
+    final byte[] value = Bytes.toBytes("abcd");
+    try (Table table = TEST_UTIL
+      .createTable(name.getTableName(), new byte[][] { CONTENTS_FAMILY, SMALL_FAMILY })) {
+      Put put = new Put(row1);
+      put.addColumn(CONTENTS_FAMILY, null, value);
+      table.put(put);
+
+      put = new Put(row2);
+      put.addColumn(CONTENTS_FAMILY, null, value);
+
+      assertEquals(1, put.size());
+      assertEquals(1, put.getFamilyCellMap().get(CONTENTS_FAMILY).size());
+
+      // KeyValue v1 expectation.  Cast for now until we go all Cell all the time. TODO
+      KeyValue kv = (KeyValue) put.getFamilyCellMap().get(CONTENTS_FAMILY).get(0);
+
+      assertTrue(Bytes.equals(CellUtil.cloneFamily(kv), CONTENTS_FAMILY));
+      // will it return null or an empty byte array?
+      assertTrue(Bytes.equals(CellUtil.cloneQualifier(kv), new byte[0]));
+
+      assertTrue(Bytes.equals(CellUtil.cloneValue(kv), value));
+
+      table.put(put);
+
+      Scan scan = new Scan();
+      scan.addColumn(CONTENTS_FAMILY, null);
+      try (ResultScanner scanner = table.getScanner(scan)) {
+        for (Result r : scanner) {
+          for (Cell key : r.rawCells()) {
+            System.out.println(Bytes.toString(r.getRow()) + ": " + key.toString());
+          }
+        }
+      }
+    }
+  }
+
+  @Test public void testPutNoCF() throws IOException {
+    final byte[] BAD_FAM = Bytes.toBytes("BAD_CF");
+    final byte[] VAL = Bytes.toBytes(100);
+    try (Table table = TEST_UTIL.createTable(name.getTableName(), FAMILY)) {
+      boolean caughtNSCFE = false;
+
+      try {
+        Put p = new Put(ROW);
+        p.addColumn(BAD_FAM, QUALIFIER, VAL);
+        table.put(p);
+      } catch (Exception e) {
+        caughtNSCFE = e instanceof NoSuchColumnFamilyException;
+      }
+      assertTrue("Should throw NoSuchColumnFamilyException", caughtNSCFE);
+    }
+  }
+
+  @Test public void testRowsPut() throws IOException {
+    final byte[] CONTENTS_FAMILY = Bytes.toBytes("contents");
+    final byte[] SMALL_FAMILY = Bytes.toBytes("smallfam");
+    final int NB_BATCH_ROWS = 10;
+    final byte[] value = Bytes.toBytes("abcd");
+    try (Table table = TEST_UTIL
+      .createTable(name.getTableName(), new byte[][] { CONTENTS_FAMILY, SMALL_FAMILY })) {
+      ArrayList<Put> rowsUpdate = new ArrayList<>();
+      for (int i = 0; i < NB_BATCH_ROWS; i++) {
+        byte[] row = Bytes.toBytes("row" + i);
+        Put put = new Put(row);
+        put.setDurability(Durability.SKIP_WAL);
+        put.addColumn(CONTENTS_FAMILY, null, value);
+        rowsUpdate.add(put);
+      }
+      table.put(rowsUpdate);
+      Scan scan = new Scan();
+      scan.addFamily(CONTENTS_FAMILY);
+      try (ResultScanner scanner = table.getScanner(scan)) {
+        int nbRows = 0;
+        for (@SuppressWarnings("unused") Result row : scanner) {
+          nbRows++;
+        }
+        assertEquals(NB_BATCH_ROWS, nbRows);
+      }
+    }
+  }
+
+  @Test public void testRowsPutBufferedManyManyFlushes() throws IOException {
+    final byte[] CONTENTS_FAMILY = Bytes.toBytes("contents");
+    final byte[] SMALL_FAMILY = Bytes.toBytes("smallfam");
+    final byte[] value = Bytes.toBytes("abcd");
+    final int NB_BATCH_ROWS = 10;
+    try (Table table = TEST_UTIL
+      .createTable(name.getTableName(), new byte[][] { CONTENTS_FAMILY, SMALL_FAMILY })) {
+      ArrayList<Put> rowsUpdate = new ArrayList<>();
+      for (int i = 0; i < NB_BATCH_ROWS * 10; i++) {
+        byte[] row = Bytes.toBytes("row" + i);
+        Put put = new Put(row);
+        put.setDurability(Durability.SKIP_WAL);
+        put.addColumn(CONTENTS_FAMILY, null, value);
+        rowsUpdate.add(put);
+      }
+      table.put(rowsUpdate);
+
+      Scan scan = new Scan();
+      scan.addFamily(CONTENTS_FAMILY);
+      try (ResultScanner scanner = table.getScanner(scan)) {
+        int nbRows = 0;
+        for (@SuppressWarnings("unused") Result row : scanner) {
+          nbRows++;
+        }
+        assertEquals(NB_BATCH_ROWS * 10, nbRows);
+      }
+    }
+  }
+
+  @Test public void testAddKeyValue() {
+    final byte[] CONTENTS_FAMILY = Bytes.toBytes("contents");
+    final byte[] value = Bytes.toBytes("abcd");
+    final byte[] row1 = Bytes.toBytes("row1");
+    final byte[] row2 = Bytes.toBytes("row2");
+    byte[] qualifier = Bytes.toBytes("qf1");
+    Put put = new Put(row1);
+
+    // Adding KeyValue with the same row
+    KeyValue kv = new KeyValue(row1, CONTENTS_FAMILY, qualifier, value);
+    boolean ok = true;
+    try {
+      put.add(kv);
+    } catch (IOException e) {
+      ok = false;
+    }
+    assertTrue(ok);
+
+    // Adding KeyValue with the different row
+    kv = new KeyValue(row2, CONTENTS_FAMILY, qualifier, value);
+    ok = false;
+    try {
+      put.add(kv);
+    } catch (IOException e) {
+      ok = true;
+    }
+    assertTrue(ok);
+  }
+
+  /**
+   * test for HBASE-737
+   */
+  @Test public void testHBase737() throws IOException {
+    final byte[] FAM1 = Bytes.toBytes("fam1");
+    final byte[] FAM2 = Bytes.toBytes("fam2");
+    // Open table
+    try (Table table = TEST_UTIL.createTable(name.getTableName(), new byte[][] { FAM1, FAM2 })) {
+      // Insert some values
+      Put put = new Put(ROW);
+      put.addColumn(FAM1, Bytes.toBytes("letters"), Bytes.toBytes("abcdefg"));
+      table.put(put);
+      try {
+        Thread.sleep(1000);
+      } catch (InterruptedException i) {
+        //ignore
+      }
+
+      put = new Put(ROW);
+      put.addColumn(FAM1, Bytes.toBytes("numbers"), Bytes.toBytes("123456"));
+      table.put(put);
+
+      try {
+        Thread.sleep(1000);
+      } catch (InterruptedException i) {
+        //ignore
+      }
+
+      put = new Put(ROW);
+      put.addColumn(FAM2, Bytes.toBytes("letters"), Bytes.toBytes("hijklmnop"));
+      table.put(put);
+
+      long[] times = new long[3];
+
+      // First scan the memstore
+
+      Scan scan = new Scan();
+      scan.addFamily(FAM1);
+      scan.addFamily(FAM2);
+      try (ResultScanner s = table.getScanner(scan)) {
+        int index = 0;
+        Result r;
+        while ((r = s.next()) != null) {
+          for (Cell key : r.rawCells()) {
+            times[index++] = key.getTimestamp();
+          }
+        }
+      }
+      for (int i = 0; i < times.length - 1; i++) {
+        for (int j = i + 1; j < times.length; j++) {
+          assertTrue(times[j] > times[i]);
+        }
+      }
+
+      // Flush data to disk and try again
+      TEST_UTIL.flush();
+
+      // Reset times
+      Arrays.fill(times, 0);
+
+      try {
+        Thread.sleep(1000);
+      } catch (InterruptedException i) {
+        //ignore
+      }
+      scan = new Scan();
+      scan.addFamily(FAM1);
+      scan.addFamily(FAM2);
+      try (ResultScanner s = table.getScanner(scan)) {
+        int index = 0;
+        Result r = null;
+        while ((r = s.next()) != null) {
+          for (Cell key : r.rawCells()) {
+            times[index++] = key.getTimestamp();
+          }
+        }
+        for (int i = 0; i < times.length - 1; i++) {
+          for (int j = i + 1; j < times.length; j++) {
+            assertTrue(times[j] > times[i]);
+          }
+        }
+      }
+    }
+  }
+
+  @Test public void testListTables() throws IOException {
+    final String testTableName = name.getTableName().toString();
+    final TableName tableName1 = TableName.valueOf(testTableName + "1");
+    final TableName tableName2 = TableName.valueOf(testTableName + "2");
+    final TableName tableName3 = TableName.valueOf(testTableName + "3");
+    TableName[] tables = new TableName[] { tableName1, tableName2, tableName3 };
+    for (TableName table : tables) {
+      TEST_UTIL.createTable(table, FAMILY);
+    }
+    try (Admin admin = TEST_UTIL.getAdmin()) {
+      List<TableDescriptor> ts = admin.listTableDescriptors();
+      HashSet<TableDescriptor> result = new HashSet<>(ts);
+      int size = result.size();
+      assertTrue(size >= tables.length);
+      for (TableName table : tables) {
+        boolean found = false;
+        for (TableDescriptor t : ts) {
+          if (t.getTableName().equals(table)) {
+            found = true;
+            break;
+          }
+        }
+        assertTrue("Not found: " + table, found);
+      }
+    }
+  }
+
+  /**
+   * simple test that just executes parts of the client
+   * API that accept a pre-created Connection instance
+   */
+  @Test public void testUnmanagedHConnection() throws IOException {
+    final TableName tableName = name.getTableName();
+    TEST_UTIL.createTable(tableName, HConstants.CATALOG_FAMILY);
+    try (Connection conn = ConnectionFactory.createConnection(TEST_UTIL.getConfiguration());
+      Table t = conn.getTable(tableName);
+      Admin admin = conn.getAdmin()) {
+      assertTrue(admin.tableExists(tableName));
+      assertTrue(t.get(new Get(ROW)).isEmpty());
+    }
+  }
+
+  /**
+   * test of that unmanaged HConnections are able to reconnect
+   * properly (see HBASE-5058)
+   */
+  @Test public void testUnmanagedHConnectionReconnect() throws Exception {
+    Configuration conf = TEST_UTIL.getConfiguration();
+    Class registryImpl = conf
+      .getClass(HConstants.CLIENT_CONNECTION_REGISTRY_IMPL_CONF_KEY, ZKConnectionRegistry.class);
+    // This test does not make sense for MasterRegistry since it stops the only master in the
+    // cluster and starts a new master without populating the underlying config for the connection.
+    Assume.assumeFalse(registryImpl.equals(MasterRegistry.class));
+    final TableName tableName = name.getTableName();
+    TEST_UTIL.createTable(tableName, HConstants.CATALOG_FAMILY);
+    try (Connection conn = ConnectionFactory.createConnection(TEST_UTIL.getConfiguration())) {
+      try (Table t = conn.getTable(tableName); Admin admin = conn.getAdmin()) {
+        assertTrue(admin.tableExists(tableName));
+        assertTrue(t.get(new Get(ROW)).isEmpty());
+      }
+
+      // stop the master
+      MiniHBaseCluster cluster = TEST_UTIL.getHBaseCluster();
+      cluster.stopMaster(0, false);
+      cluster.waitOnMaster(0);
+
+      // start up a new master
+      cluster.startMaster();
+      assertTrue(cluster.waitForActiveAndReadyMaster());
+
+      // test that the same unmanaged connection works with a new
+      // Admin and can connect to the new master;
+      boolean tablesOnMaster = LoadBalancer.isTablesOnMaster(TEST_UTIL.getConfiguration());
+      try (Admin admin = conn.getAdmin()) {
+        assertTrue(admin.tableExists(tableName));
+        assertEquals(
+          admin.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS)).getLiveServerMetrics().size(),
+          SLAVES + (tablesOnMaster ? 1 : 0));
+      }
+    }
+  }
+
+  @Test public void testMiscHTableStuff() throws IOException {
+    final String testTableName = name.getTableName().toString();
+    final TableName tableAname = TableName.valueOf(testTableName + "A");
+    final TableName tableBname = TableName.valueOf(testTableName + "B");
+    final byte[] attrName = Bytes.toBytes("TESTATTR");
+    final byte[] attrValue = Bytes.toBytes("somevalue");
+    byte[] value = Bytes.toBytes("value");
+
+    try (Table a = TEST_UTIL.createTable(tableAname, HConstants.CATALOG_FAMILY);
+      Table b = TEST_UTIL.createTable(tableBname, HConstants.CATALOG_FAMILY)) {
+      Put put = new Put(ROW);
+      put.addColumn(HConstants.CATALOG_FAMILY, null, value);
+      a.put(put);
+
+      // open a new connection to A and a connection to b
+      try (Table newA = TEST_UTIL.getConnection().getTable(tableAname)) {
+
+        // copy data from A to B
+        Scan scan = new Scan();
+        scan.addFamily(HConstants.CATALOG_FAMILY);
+        try (ResultScanner s = newA.getScanner(scan)) {
+          for (Result r : s) {
+            put = new Put(r.getRow());
+            put.setDurability(Durability.SKIP_WAL);
+            for (Cell kv : r.rawCells()) {
+              put.add(kv);
+            }
+            b.put(put);
+          }
+        }
+      }
+
+      // Opening a new connection to A will cause the tables to be reloaded
+      try (Table anotherA = TEST_UTIL.getConnection().getTable(tableAname)) {
+        Get get = new Get(ROW);
+        get.addFamily(HConstants.CATALOG_FAMILY);
+        anotherA.get(get);
+      }
+
+      // We can still access A through newA because it has the table information
+      // cached. And if it needs to recalibrate, that will cause the information
+      // to be reloaded.
+
+      // Test user metadata
+      Admin admin = TEST_UTIL.getAdmin();
+      // make a modifiable descriptor
+      HTableDescriptor desc = new HTableDescriptor(a.getDescriptor());
+      // offline the table
+      admin.disableTable(tableAname);
+      // add a user attribute to HTD
+      desc.setValue(attrName, attrValue);
+      // add a user attribute to HCD
+      for (HColumnDescriptor c : desc.getFamilies()) {
+        c.setValue(attrName, attrValue);
+      }
+      // update metadata for all regions of this table
+      admin.modifyTable(desc);
+      // enable the table
+      admin.enableTable(tableAname);
+
+      // Test that attribute changes were applied
+      desc = new HTableDescriptor(a.getDescriptor());
+      assertEquals("wrong table descriptor returned", desc.getTableName(), tableAname);
+      // check HTD attribute
+      value = desc.getValue(attrName);
+      assertNotNull("missing HTD attribute value", value);
+      assertFalse("HTD attribute value is incorrect", Bytes.compareTo(value, attrValue) != 0);
+      // check HCD attribute
+      for (HColumnDescriptor c : desc.getFamilies()) {
+        value = c.getValue(attrName);
+        assertNotNull("missing HCD attribute value", value);
+        assertFalse("HCD attribute value is incorrect", Bytes.compareTo(value, attrValue) != 0);
+      }
+    }
+  }
+}
+
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestFromClientSide5.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestFromClientSide5.java
new file mode 100644
index 0000000..f74a288
--- /dev/null
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestFromClientSide5.java
@@ -0,0 +1,2596 @@
+/*
+ * 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, 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, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.hbase.client;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.NavigableMap;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicReference;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.hbase.Cell;
+import org.apache.hadoop.hbase.CellScanner;
+import org.apache.hadoop.hbase.CellUtil;
+import org.apache.hadoop.hbase.CompareOperator;
+import org.apache.hadoop.hbase.DoNotRetryIOException;
+import org.apache.hadoop.hbase.HBaseClassTestRule;
+import org.apache.hadoop.hbase.HConstants;
+import org.apache.hadoop.hbase.HRegionLocation;
+import org.apache.hadoop.hbase.KeyValue;
+import org.apache.hadoop.hbase.PrivateCellUtil;
+import org.apache.hadoop.hbase.ServerName;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.TableNameTestRule;
+import org.apache.hadoop.hbase.Waiter;
+import org.apache.hadoop.hbase.client.metrics.ScanMetrics;
+import org.apache.hadoop.hbase.coprocessor.MultiRowMutationEndpoint;
+import org.apache.hadoop.hbase.filter.BinaryComparator;
+import org.apache.hadoop.hbase.filter.Filter;
+import org.apache.hadoop.hbase.filter.FilterList;
+import org.apache.hadoop.hbase.filter.FirstKeyOnlyFilter;
+import org.apache.hadoop.hbase.filter.InclusiveStopFilter;
+import org.apache.hadoop.hbase.filter.KeyOnlyFilter;
+import org.apache.hadoop.hbase.filter.QualifierFilter;
+import org.apache.hadoop.hbase.filter.RegexStringComparator;
+import org.apache.hadoop.hbase.filter.RowFilter;
+import org.apache.hadoop.hbase.filter.SubstringComparator;
+import org.apache.hadoop.hbase.filter.ValueFilter;
+import org.apache.hadoop.hbase.io.TimeRange;
+import org.apache.hadoop.hbase.io.hfile.BlockCache;
+import org.apache.hadoop.hbase.io.hfile.CacheConfig;
+import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel;
+import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
+import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.MutationProto;
+import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.MutationProto.MutationType;
+import org.apache.hadoop.hbase.protobuf.generated.MultiRowMutationProtos.MultiRowMutationService;
+import org.apache.hadoop.hbase.protobuf.generated.MultiRowMutationProtos.MutateRowsRequest;
+import org.apache.hadoop.hbase.regionserver.HRegion;
+import org.apache.hadoop.hbase.regionserver.HRegionServer;
+import org.apache.hadoop.hbase.regionserver.HStore;
+import org.apache.hadoop.hbase.regionserver.NoSuchColumnFamilyException;
+import org.apache.hadoop.hbase.testclassification.ClientTests;
+import org.apache.hadoop.hbase.testclassification.LargeTests;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
+import org.apache.hadoop.hbase.util.FSUtils;
+import org.junit.AfterClass;
+import org.junit.ClassRule;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Run tests that use the HBase clients; {@link Table}.
+ * Sets up the HBase mini cluster once at start and runs through all client tests.
+ * Each creates a table named for the method and does its stuff against that.
+ *
+ * Parameterized to run with different registry implementations.
+ */
+@Category({LargeTests.class, ClientTests.class})
+@SuppressWarnings ("deprecation")
+@RunWith(Parameterized.class)
+public class TestFromClientSide5 extends FromClientSideBase {
+  private static final Logger LOG = LoggerFactory.getLogger(TestFromClientSide5.class);
+
+  @ClassRule
+  public static final HBaseClassTestRule CLASS_RULE =
+      HBaseClassTestRule.forClass(TestFromClientSide5.class);
+  @Rule
+  public TableNameTestRule name = new TableNameTestRule();
+
+  // To keep the child classes happy.
+  TestFromClientSide5() {}
+
+  public TestFromClientSide5(Class registry, int numHedgedReqs) throws Exception {
+    initialize(registry, numHedgedReqs, MultiRowMutationEndpoint.class);
+  }
+
+  @Parameterized.Parameters
+  public static Collection parameters() {
+    return Arrays.asList(new Object[][] {
+        { MasterRegistry.class, 1},
+        { MasterRegistry.class, 2},
+        { ZKConnectionRegistry.class, 1}
+    });
+  }
+
+  @AfterClass public static void tearDownAfterClass() throws Exception {
+    afterClass();
+  }
+
+  @Test
+  public void testGetClosestRowBefore() throws IOException, InterruptedException {
+    final TableName tableName = name.getTableName();
+    final byte[] firstRow = Bytes.toBytes("row111");
+    final byte[] secondRow = Bytes.toBytes("row222");
+    final byte[] thirdRow = Bytes.toBytes("row333");
+    final byte[] forthRow = Bytes.toBytes("row444");
+    final byte[] beforeFirstRow = Bytes.toBytes("row");
+    final byte[] beforeSecondRow = Bytes.toBytes("row22");
+    final byte[] beforeThirdRow = Bytes.toBytes("row33");
+    final byte[] beforeForthRow = Bytes.toBytes("row44");
+
+    try (Table table =
+        TEST_UTIL.createTable(tableName,
+          new byte[][] { HConstants.CATALOG_FAMILY, Bytes.toBytes("info2") }, 1, 1024);
+      RegionLocator locator = TEST_UTIL.getConnection().getRegionLocator(tableName)) {
+
+      // set block size to 64 to making 2 kvs into one block, bypassing the walkForwardInSingleRow
+      // in Store.rowAtOrBeforeFromStoreFile
+      String regionName = locator.getAllRegionLocations().get(0).getRegion().getEncodedName();
+      HRegion region = TEST_UTIL.getRSForFirstRegionInTable(tableName).getRegion(regionName);
+      Put put1 = new Put(firstRow);
+      Put put2 = new Put(secondRow);
+      Put put3 = new Put(thirdRow);
+      Put put4 = new Put(forthRow);
+      byte[] one = new byte[] { 1 };
+      byte[] two = new byte[] { 2 };
+      byte[] three = new byte[] { 3 };
+      byte[] four = new byte[] { 4 };
+
+      put1.addColumn(HConstants.CATALOG_FAMILY, null, one);
+      put2.addColumn(HConstants.CATALOG_FAMILY, null, two);
+      put3.addColumn(HConstants.CATALOG_FAMILY, null, three);
+      put4.addColumn(HConstants.CATALOG_FAMILY, null, four);
+      table.put(put1);
+      table.put(put2);
+      table.put(put3);
+      table.put(put4);
+      region.flush(true);
+
+      Result result;
+
+      // Test before first that null is returned
+      result = getReverseScanResult(table, beforeFirstRow);
+      assertNull(result);
+
+      // Test at first that first is returned
+      result = getReverseScanResult(table, firstRow);
+      assertTrue(result.containsColumn(HConstants.CATALOG_FAMILY, null));
+      assertTrue(Bytes.equals(result.getRow(), firstRow));
+      assertTrue(Bytes.equals(result.getValue(HConstants.CATALOG_FAMILY, null), one));
+
+      // Test in between first and second that first is returned
+      result = getReverseScanResult(table, beforeSecondRow);
+      assertTrue(result.containsColumn(HConstants.CATALOG_FAMILY, null));
+      assertTrue(Bytes.equals(result.getRow(), firstRow));
+      assertTrue(Bytes.equals(result.getValue(HConstants.CATALOG_FAMILY, null), one));
+
+      // Test at second make sure second is returned
+      result = getReverseScanResult(table, secondRow);
+      assertTrue(result.containsColumn(HConstants.CATALOG_FAMILY, null));
+      assertTrue(Bytes.equals(result.getRow(), secondRow));
+      assertTrue(Bytes.equals(result.getValue(HConstants.CATALOG_FAMILY, null), two));
+
+      // Test in second and third, make sure second is returned
+      result = getReverseScanResult(table, beforeThirdRow);
+      assertTrue(result.containsColumn(HConstants.CATALOG_FAMILY, null));
+      assertTrue(Bytes.equals(result.getRow(), secondRow));
+      assertTrue(Bytes.equals(result.getValue(HConstants.CATALOG_FAMILY, null), two));
+
+      // Test at third make sure third is returned
+      result = getReverseScanResult(table, thirdRow);
+      assertTrue(result.containsColumn(HConstants.CATALOG_FAMILY, null));
+      assertTrue(Bytes.equals(result.getRow(), thirdRow));
+      assertTrue(Bytes.equals(result.getValue(HConstants.CATALOG_FAMILY, null), three));
+
+      // Test in third and forth, make sure third is returned
+      result = getReverseScanResult(table, beforeForthRow);
+      assertTrue(result.containsColumn(HConstants.CATALOG_FAMILY, null));
+      assertTrue(Bytes.equals(result.getRow(), thirdRow));
+      assertTrue(Bytes.equals(result.getValue(HConstants.CATALOG_FAMILY, null), three));
+
+      // Test at forth make sure forth is returned
+      result = getReverseScanResult(table, forthRow);
+      assertTrue(result.containsColumn(HConstants.CATALOG_FAMILY, null));
+      assertTrue(Bytes.equals(result.getRow(), forthRow));
+      assertTrue(Bytes.equals(result.getValue(HConstants.CATALOG_FAMILY, null), four));
+
+      // Test after forth make sure forth is returned
+      result = getReverseScanResult(table, Bytes.add(forthRow, one));
+      assertTrue(result.containsColumn(HConstants.CATALOG_FAMILY, null));
+      assertTrue(Bytes.equals(result.getRow(), forthRow));
+      assertTrue(Bytes.equals(result.getValue(HConstants.CATALOG_FAMILY, null), four));
+    }
+  }
+
+  private Result getReverseScanResult(Table table, byte[] row) throws IOException {
+    Scan scan = new Scan(row);
+    scan.setSmall(true);
+    scan.setReversed(true);
+    scan.setCaching(1);
+    scan.addFamily(HConstants.CATALOG_FAMILY);
+    try (ResultScanner scanner = table.getScanner(scan)) {
+      return scanner.next();
+    }
+  }
+
+  /**
+   * For HBASE-2156
+   */
+  @Test
+  public void testScanVariableReuse() {
+    Scan scan = new Scan();
+    scan.addFamily(FAMILY);
+    scan.addColumn(FAMILY, ROW);
+
+    assertEquals(1, scan.getFamilyMap().get(FAMILY).size());
+
+    scan = new Scan();
+    scan.addFamily(FAMILY);
+
+    assertNull(scan.getFamilyMap().get(FAMILY));
+    assertTrue(scan.getFamilyMap().containsKey(FAMILY));
+  }
+
+  @Test
+  public void testMultiRowMutation() throws Exception {
+    LOG.info("Starting testMultiRowMutation");
+    final TableName tableName = name.getTableName();
+    final byte [] ROW1 = Bytes.toBytes("testRow1");
+
+    try (Table t = TEST_UTIL.createTable(tableName, FAMILY)) {
+      Put p = new Put(ROW);
+      p.addColumn(FAMILY, QUALIFIER, VALUE);
+      MutationProto m1 = ProtobufUtil.toMutation(MutationType.PUT, p);
+
+      p = new Put(ROW1);
+      p.addColumn(FAMILY, QUALIFIER, VALUE);
+      MutationProto m2 = ProtobufUtil.toMutation(MutationType.PUT, p);
+
+      MutateRowsRequest.Builder mrmBuilder = MutateRowsRequest.newBuilder();
+      mrmBuilder.addMutationRequest(m1);
+      mrmBuilder.addMutationRequest(m2);
+      MutateRowsRequest mrm = mrmBuilder.build();
+      CoprocessorRpcChannel channel = t.coprocessorService(ROW);
+      MultiRowMutationService.BlockingInterface service =
+              MultiRowMutationService.newBlockingStub(channel);
+      service.mutateRows(null, mrm);
+      Get g = new Get(ROW);
+      Result r = t.get(g);
+      assertEquals(0, Bytes.compareTo(VALUE, r.getValue(FAMILY, QUALIFIER)));
+      g = new Get(ROW1);
+      r = t.get(g);
+      assertEquals(0, Bytes.compareTo(VALUE, r.getValue(FAMILY, QUALIFIER)));
+    }
+  }
+
+  @Test
+  public void testRowMutation() throws Exception {
+    LOG.info("Starting testRowMutation");
+    final TableName tableName = name.getTableName();
+    try (Table t = TEST_UTIL.createTable(tableName, FAMILY)) {
+      byte[][] QUALIFIERS = new byte[][] { Bytes.toBytes("a"), Bytes.toBytes("b") };
+      RowMutations arm = new RowMutations(ROW);
+      Put p = new Put(ROW);
+      p.addColumn(FAMILY, QUALIFIERS[0], VALUE);
+      arm.add(p);
+      t.mutateRow(arm);
+
+      Get g = new Get(ROW);
+      Result r = t.get(g);
+      assertEquals(0, Bytes.compareTo(VALUE, r.getValue(FAMILY, QUALIFIERS[0])));
+
+      arm = new RowMutations(ROW);
+      p = new Put(ROW);
+      p.addColumn(FAMILY, QUALIFIERS[1], VALUE);
+      arm.add(p);
+      Delete d = new Delete(ROW);
+      d.addColumns(FAMILY, QUALIFIERS[0]);
+      arm.add(d);
+      // TODO: Trying mutateRow again. The batch was failing with a one try only.
+      t.mutateRow(arm);
+      r = t.get(g);
+      assertEquals(0, Bytes.compareTo(VALUE, r.getValue(FAMILY, QUALIFIERS[1])));
+      assertNull(r.getValue(FAMILY, QUALIFIERS[0]));
+
+      // Test that we get a region level exception
+      try {
+        arm = new RowMutations(ROW);
+        p = new Put(ROW);
+        p.addColumn(new byte[] { 'b', 'o', 'g', 'u', 's' }, QUALIFIERS[0], VALUE);
+        arm.add(p);
+        t.mutateRow(arm);
+        fail("Expected NoSuchColumnFamilyException");
+      } catch (NoSuchColumnFamilyException e) {
+        return;
+      } catch (RetriesExhaustedWithDetailsException e) {
+        for (Throwable rootCause : e.getCauses()) {
+          if (rootCause instanceof NoSuchColumnFamilyException) {
+            return;
+          }
+        }
+        throw e;
+      }
+    }
+  }
+
+  @Test
+  public void testBatchAppendWithReturnResultFalse() throws Exception {
+    LOG.info("Starting testBatchAppendWithReturnResultFalse");
+    final TableName tableName = name.getTableName();
+    try (Table table = TEST_UTIL.createTable(tableName, FAMILY)) {
+      Append append1 = new Append(Bytes.toBytes("row1"));
+      append1.setReturnResults(false);
+      append1.addColumn(FAMILY, Bytes.toBytes("f1"), Bytes.toBytes("value1"));
+      Append append2 = new Append(Bytes.toBytes("row1"));
+      append2.setReturnResults(false);
+      append2.addColumn(FAMILY, Bytes.toBytes("f1"), Bytes.toBytes("value2"));
+      List<Append> appends = new ArrayList<>();
+      appends.add(append1);
+      appends.add(append2);
+      Object[] results = new Object[2];
+      table.batch(appends, results);
+      assertEquals(2, results.length);
+      for (Object r : results) {
+        Result result = (Result) r;
+        assertTrue(result.isEmpty());
+      }
+    }
+  }
+
+  @Test
+  public void testAppend() throws Exception {
+    LOG.info("Starting testAppend");
+    final TableName tableName = name.getTableName();
+    try (Table t = TEST_UTIL.createTable(tableName, FAMILY)) {
+      byte[] v1 = Bytes.toBytes("42");
+      byte[] v2 = Bytes.toBytes("23");
+      byte[][] QUALIFIERS = new byte[][]{
+              Bytes.toBytes("b"), Bytes.toBytes("a"), Bytes.toBytes("c")
+      };
+      Append a = new Append(ROW);
+      a.addColumn(FAMILY, QUALIFIERS[0], v1);
+      a.addColumn(FAMILY, QUALIFIERS[1], v2);
+      a.setReturnResults(false);
+      assertEmptyResult(t.append(a));
+
+      a = new Append(ROW);
+      a.addColumn(FAMILY, QUALIFIERS[0], v2);
+      a.addColumn(FAMILY, QUALIFIERS[1], v1);
+      a.addColumn(FAMILY, QUALIFIERS[2], v2);
+      Result r = t.append(a);
+      assertEquals(0, Bytes.compareTo(Bytes.add(v1, v2), r.getValue(FAMILY, QUALIFIERS[0])));
+      assertEquals(0, Bytes.compareTo(Bytes.add(v2, v1), r.getValue(FAMILY, QUALIFIERS[1])));
+      // QUALIFIERS[2] previously not exist, verify both value and timestamp are correct
+      assertEquals(0, Bytes.compareTo(v2, r.getValue(FAMILY, QUALIFIERS[2])));
+      assertEquals(r.getColumnLatestCell(FAMILY, QUALIFIERS[0]).getTimestamp(),
+              r.getColumnLatestCell(FAMILY, QUALIFIERS[2]).getTimestamp());
+    }
+  }
+  private List<Result> doAppend(final boolean walUsed) throws IOException {
+    LOG.info("Starting testAppend, walUsed is " + walUsed);
+    final TableName TABLENAME =
+            TableName.valueOf(walUsed ? "testAppendWithWAL" : "testAppendWithoutWAL");
+    try (Table t = TEST_UTIL.createTable(TABLENAME, FAMILY)) {
+      final byte[] row1 = Bytes.toBytes("c");
+      final byte[] row2 = Bytes.toBytes("b");
+      final byte[] row3 = Bytes.toBytes("a");
+      final byte[] qual = Bytes.toBytes("qual");
+      Put put_0 = new Put(row2);
+      put_0.addColumn(FAMILY, qual, Bytes.toBytes("put"));
+      Put put_1 = new Put(row3);
+      put_1.addColumn(FAMILY, qual, Bytes.toBytes("put"));
+      Append append_0 = new Append(row1);
+      append_0.addColumn(FAMILY, qual, Bytes.toBytes("i"));
+      Append append_1 = new Append(row1);
+      append_1.addColumn(FAMILY, qual, Bytes.toBytes("k"));
+      Append append_2 = new Append(row1);
+      append_2.addColumn(FAMILY, qual, Bytes.toBytes("e"));
+      if (!walUsed) {
+        append_2.setDurability(Durability.SKIP_WAL);
+      }
+      Append append_3 = new Append(row1);
+      append_3.addColumn(FAMILY, qual, Bytes.toBytes("a"));
+      Scan s = new Scan();
+      s.setCaching(1);
+      t.append(append_0);
+      t.put(put_0);
+      t.put(put_1);
+      List<Result> results = new LinkedList<>();
+      try (ResultScanner scanner = t.getScanner(s)) {
+        t.append(append_1);
+        t.append(append_2);
+        t.append(append_3);
+        for (Result r : scanner) {
+          results.add(r);
+        }
+      }
+      TEST_UTIL.deleteTable(TABLENAME);
+      return results;
+    }
+  }
+
+  @Test
+  public void testAppendWithoutWAL() throws Exception {
+    List<Result> resultsWithWal = doAppend(true);
+    List<Result> resultsWithoutWal = doAppend(false);
+    assertEquals(resultsWithWal.size(), resultsWithoutWal.size());
+    for (int i = 0; i != resultsWithWal.size(); ++i) {
+      Result resultWithWal = resultsWithWal.get(i);
+      Result resultWithoutWal = resultsWithoutWal.get(i);
+      assertEquals(resultWithWal.rawCells().length, resultWithoutWal.rawCells().length);
+      for (int j = 0; j != resultWithWal.rawCells().length; ++j) {
+        Cell cellWithWal = resultWithWal.rawCells()[j];
+        Cell cellWithoutWal = resultWithoutWal.rawCells()[j];
+        assertArrayEquals(CellUtil.cloneRow(cellWithWal), CellUtil.cloneRow(cellWithoutWal));
+        assertArrayEquals(CellUtil.cloneFamily(cellWithWal), CellUtil.cloneFamily(cellWithoutWal));
+        assertArrayEquals(CellUtil.cloneQualifier(cellWithWal),
+          CellUtil.cloneQualifier(cellWithoutWal));
+        assertArrayEquals(CellUtil.cloneValue(cellWithWal), CellUtil.cloneValue(cellWithoutWal));
+      }
+    }
+  }
+
+  @Test
+  public void testClientPoolRoundRobin() throws IOException {
+    final TableName tableName = name.getTableName();
+
+    int poolSize = 3;
+    int numVersions = poolSize * 2;
+    Configuration conf = TEST_UTIL.getConfiguration();
+    conf.set(HConstants.HBASE_CLIENT_IPC_POOL_TYPE, "round-robin");
+    conf.setInt(HConstants.HBASE_CLIENT_IPC_POOL_SIZE, poolSize);
+
+    try (Table table =
+                 TEST_UTIL.createTable(tableName, new byte[][] { FAMILY }, Integer.MAX_VALUE)) {
+
+      final long ts = EnvironmentEdgeManager.currentTime();
+      Get get = new Get(ROW);
+      get.addColumn(FAMILY, QUALIFIER);
+      get.readAllVersions();
+
+      for (int versions = 1; versions <= numVersions; versions++) {
+        Put put = new Put(ROW);
+        put.addColumn(FAMILY, QUALIFIER, ts + versions, VALUE);
+        table.put(put);
+
+        Result result = table.get(get);
+        NavigableMap<Long, byte[]> navigableMap = result.getMap().get(FAMILY)
+                .get(QUALIFIER);
+
+        assertEquals("The number of versions of '" + Bytes.toString(FAMILY) + ":"
+                + Bytes.toString(QUALIFIER) + " did not match", versions, navigableMap.size());
+        for (Map.Entry<Long, byte[]> entry : navigableMap.entrySet()) {
+          assertTrue("The value at time " + entry.getKey()
+                          + " did not match what was put",
+                  Bytes.equals(VALUE, entry.getValue()));
+        }
+      }
+    }
+  }
+
+  @Ignore ("Flakey: HBASE-8989") @Test
+  public void testClientPoolThreadLocal() throws IOException {
+    final TableName tableName = name.getTableName();
+
+    int poolSize = Integer.MAX_VALUE;
+    int numVersions = 3;
+    Configuration conf = TEST_UTIL.getConfiguration();
+    conf.set(HConstants.HBASE_CLIENT_IPC_POOL_TYPE, "thread-local");
+    conf.setInt(HConstants.HBASE_CLIENT_IPC_POOL_SIZE, poolSize);
+
+    try (final Table table = TEST_UTIL.createTable(tableName, new byte[][] { FAMILY },  3)) {
+
+      final long ts = EnvironmentEdgeManager.currentTime();
+      final Get get = new Get(ROW);
+      get.addColumn(FAMILY, QUALIFIER);
+      get.readAllVersions();
+
+      for (int versions = 1; versions <= numVersions; versions++) {
+        Put put = new Put(ROW);
+        put.addColumn(FAMILY, QUALIFIER, ts + versions, VALUE);
+        table.put(put);
+
+        Result result = table.get(get);
+        NavigableMap<Long, byte[]> navigableMap = result.getMap().get(FAMILY)
+                .get(QUALIFIER);
+
+        assertEquals("The number of versions of '" + Bytes.toString(FAMILY) + ":"
+                + Bytes.toString(QUALIFIER) + " did not match", versions, navigableMap.size());
+        for (Map.Entry<Long, byte[]> entry : navigableMap.entrySet()) {
+          assertTrue("The value at time " + entry.getKey()
+                          + " did not match what was put",
+                  Bytes.equals(VALUE, entry.getValue()));
+        }
+      }
+
+      final Object waitLock = new Object();
+      ExecutorService executorService = Executors.newFixedThreadPool(numVersions);
+      final AtomicReference<AssertionError> error = new AtomicReference<>(null);
+      for (int versions = numVersions; versions < numVersions * 2; versions++) {
+        final int versionsCopy = versions;
+        executorService.submit((Callable<Void>) () -> {
+          try {
+            Put put = new Put(ROW);
+            put.addColumn(FAMILY, QUALIFIER, ts + versionsCopy, VALUE);
+            table.put(put);
+
+            Result result = table.get(get);
+            NavigableMap<Long, byte[]> navigableMap = result.getMap()
+                    .get(FAMILY).get(QUALIFIER);
+
+            assertEquals("The number of versions of '" + Bytes.toString(FAMILY) + ":"
+                    + Bytes.toString(QUALIFIER) + " did not match " + versionsCopy, versionsCopy,
+                    navigableMap.size());
+            for (Map.Entry<Long, byte[]> entry : navigableMap.entrySet()) {
+              assertTrue("The value at time " + entry.getKey()
+                              + " did not match what was put",
+                      Bytes.equals(VALUE, entry.getValue()));
+            }
+            synchronized (waitLock) {
+              waitLock.wait();
+            }
+          } catch (Exception ignored) {
+          } catch (AssertionError e) {
+            // the error happens in a thread, it won't fail the test,
+            // need to pass it to the caller for proper handling.
+            error.set(e);
+            LOG.error(e.toString(), e);
+          }
+
+          return null;
+        });
+      }
+      synchronized (waitLock) {
+        waitLock.notifyAll();
+      }
+      executorService.shutdownNow();
+      assertNull(error.get());
+    }
+  }
+
+  @Test
+  public void testCheckAndPut() throws IOException {
+    final byte [] anotherrow = Bytes.toBytes("anotherrow");
+    final byte [] value2 = Bytes.toBytes("abcd");
+
+    try (Table table = TEST_UTIL.createTable(name.getTableName(), FAMILY)) {
+      Put put1 = new Put(ROW);
+      put1.addColumn(FAMILY, QUALIFIER, VALUE);
+
+      // row doesn't exist, so using non-null value should be considered "not match".
+      boolean ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .ifEquals(VALUE).thenPut(put1);
+      assertFalse(ok);
+
+      // row doesn't exist, so using "ifNotExists" should be considered "match".
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER).ifNotExists().thenPut(put1);
+      assertTrue(ok);
+
+      // row now exists, so using "ifNotExists" should be considered "not match".
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER).ifNotExists().thenPut(put1);
+      assertFalse(ok);
+
+      Put put2 = new Put(ROW);
+      put2.addColumn(FAMILY, QUALIFIER, value2);
+
+      // row now exists, use the matching value to check
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER).ifEquals(VALUE).thenPut(put2);
+      assertTrue(ok);
+
+      Put put3 = new Put(anotherrow);
+      put3.addColumn(FAMILY, QUALIFIER, VALUE);
+
+      // try to do CheckAndPut on different rows
+      try {
+        table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER).ifEquals(value2).thenPut(put3);
+        fail("trying to check and modify different rows should have failed.");
+      } catch (Exception ignored) {
+      }
+    }
+  }
+
+  @Test
+  public void testCheckAndMutateWithTimeRange() throws IOException {
+    try (Table table = TEST_UTIL.createTable(name.getTableName(), FAMILY)) {
+      final long ts = System.currentTimeMillis() / 2;
+      Put put = new Put(ROW);
+      put.addColumn(FAMILY, QUALIFIER, ts, VALUE);
+
+      boolean ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .ifNotExists()
+              .thenPut(put);
+      assertTrue(ok);
+
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .timeRange(TimeRange.at(ts + 10000))
+              .ifEquals(VALUE)
+              .thenPut(put);
+      assertFalse(ok);
+
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .timeRange(TimeRange.from(ts + 10000))
+              .ifEquals(VALUE)
+              .thenPut(put);
+      assertFalse(ok);
+
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .timeRange(TimeRange.between(ts + 10000, ts + 20000))
+              .ifEquals(VALUE)
+              .thenPut(put);
+      assertFalse(ok);
+
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .timeRange(TimeRange.until(ts))
+              .ifEquals(VALUE)
+              .thenPut(put);
+      assertFalse(ok);
+
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .timeRange(TimeRange.at(ts))
+              .ifEquals(VALUE)
+              .thenPut(put);
+      assertTrue(ok);
+
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .timeRange(TimeRange.from(ts))
+              .ifEquals(VALUE)
+              .thenPut(put);
+      assertTrue(ok);
+
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .timeRange(TimeRange.between(ts, ts + 20000))
+              .ifEquals(VALUE)
+              .thenPut(put);
+      assertTrue(ok);
+
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .timeRange(TimeRange.until(ts + 10000))
+              .ifEquals(VALUE)
+              .thenPut(put);
+      assertTrue(ok);
+
+      RowMutations rm = new RowMutations(ROW)
+              .add((Mutation) put);
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .timeRange(TimeRange.at(ts + 10000))
+              .ifEquals(VALUE)
+              .thenMutate(rm);
+      assertFalse(ok);
+
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .timeRange(TimeRange.at(ts))
+              .ifEquals(VALUE)
+              .thenMutate(rm);
+      assertTrue(ok);
+
+      Delete delete = new Delete(ROW)
+              .addColumn(FAMILY, QUALIFIER);
+
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .timeRange(TimeRange.at(ts + 10000))
+              .ifEquals(VALUE)
+              .thenDelete(delete);
+      assertFalse(ok);
+
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .timeRange(TimeRange.at(ts))
+              .ifEquals(VALUE)
+              .thenDelete(delete);
+      assertTrue(ok);
+    }
+  }
+
+  @Test
+  public void testCheckAndPutWithCompareOp() throws IOException {
+    final byte [] value1 = Bytes.toBytes("aaaa");
+    final byte [] value2 = Bytes.toBytes("bbbb");
+    final byte [] value3 = Bytes.toBytes("cccc");
+    final byte [] value4 = Bytes.toBytes("dddd");
+
+    try (Table table = TEST_UTIL.createTable(name.getTableName(), FAMILY)) {
+
+      Put put2 = new Put(ROW);
+      put2.addColumn(FAMILY, QUALIFIER, value2);
+
+      Put put3 = new Put(ROW);
+      put3.addColumn(FAMILY, QUALIFIER, value3);
+
+      // row doesn't exist, so using "ifNotExists" should be considered "match".
+      boolean ok =
+              table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER).ifNotExists().thenPut(put2);
+      assertTrue(ok);
+
+      // cell = "bbbb", using "aaaa" to compare only LESS/LESS_OR_EQUAL/NOT_EQUAL
+      // turns out "match"
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .ifMatches(CompareOperator.GREATER, value1).thenPut(put2);
+      assertFalse(ok);
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .ifMatches(CompareOperator.EQUAL, value1).thenPut(put2);
+      assertFalse(ok);
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .ifMatches(CompareOperator.GREATER_OR_EQUAL, value1).thenPut(put2);
+      assertFalse(ok);
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .ifMatches(CompareOperator.LESS, value1).thenPut(put2);
+      assertTrue(ok);
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .ifMatches(CompareOperator.LESS_OR_EQUAL, value1).thenPut(put2);
+      assertTrue(ok);
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .ifMatches(CompareOperator.NOT_EQUAL, value1).thenPut(put3);
+      assertTrue(ok);
+
+      // cell = "cccc", using "dddd" to compare only LARGER/LARGER_OR_EQUAL/NOT_EQUAL
+      // turns out "match"
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .ifMatches(CompareOperator.LESS, value4).thenPut(put3);
+      assertFalse(ok);
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .ifMatches(CompareOperator.LESS_OR_EQUAL, value4).thenPut(put3);
+      assertFalse(ok);
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .ifMatches(CompareOperator.EQUAL, value4).thenPut(put3);
+      assertFalse(ok);
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .ifMatches(CompareOperator.GREATER, value4).thenPut(put3);
+      assertTrue(ok);
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .ifMatches(CompareOperator.GREATER_OR_EQUAL, value4).thenPut(put3);
+      assertTrue(ok);
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .ifMatches(CompareOperator.NOT_EQUAL, value4).thenPut(put2);
+      assertTrue(ok);
+
+      // cell = "bbbb", using "bbbb" to compare only GREATER_OR_EQUAL/LESS_OR_EQUAL/EQUAL
+      // turns out "match"
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .ifMatches(CompareOperator.GREATER, value2).thenPut(put2);
+      assertFalse(ok);
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .ifMatches(CompareOperator.NOT_EQUAL, value2).thenPut(put2);
+      assertFalse(ok);
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .ifMatches(CompareOperator.LESS, value2).thenPut(put2);
+      assertFalse(ok);
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .ifMatches(CompareOperator.GREATER_OR_EQUAL, value2).thenPut(put2);
+      assertTrue(ok);
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .ifMatches(CompareOperator.LESS_OR_EQUAL, value2).thenPut(put2);
+      assertTrue(ok);
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .ifMatches(CompareOperator.EQUAL, value2).thenPut(put3);
+      assertTrue(ok);
+    }
+  }
+
+  @Test
+  public void testCheckAndDelete() throws IOException {
+    final byte [] value1 = Bytes.toBytes("aaaa");
+
+    try (Table table = TEST_UTIL.createTable(name.getTableName(),
+        FAMILY)) {
+
+      Put put = new Put(ROW);
+      put.addColumn(FAMILY, QUALIFIER, value1);
+      table.put(put);
+
+      Delete delete = new Delete(ROW);
+      delete.addColumns(FAMILY, QUALIFIER);
+
+      boolean ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .ifEquals(value1).thenDelete(delete);
+      assertTrue(ok);
+    }
+  }
+
+  @Test
+  public void testCheckAndDeleteWithCompareOp() throws IOException {
+    final byte [] value1 = Bytes.toBytes("aaaa");
+    final byte [] value2 = Bytes.toBytes("bbbb");
+    final byte [] value3 = Bytes.toBytes("cccc");
+    final byte [] value4 = Bytes.toBytes("dddd");
+
+    try (Table table = TEST_UTIL.createTable(name.getTableName(),
+        FAMILY)) {
+
+      Put put2 = new Put(ROW);
+      put2.addColumn(FAMILY, QUALIFIER, value2);
+      table.put(put2);
+
+      Put put3 = new Put(ROW);
+      put3.addColumn(FAMILY, QUALIFIER, value3);
+
+      Delete delete = new Delete(ROW);
+      delete.addColumns(FAMILY, QUALIFIER);
+
+      // cell = "bbbb", using "aaaa" to compare only LESS/LESS_OR_EQUAL/NOT_EQUAL
+      // turns out "match"
+      boolean ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .ifMatches(CompareOperator.GREATER, value1).thenDelete(delete);
+      assertFalse(ok);
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .ifMatches(CompareOperator.EQUAL, value1).thenDelete(delete);
+      assertFalse(ok);
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .ifMatches(CompareOperator.GREATER_OR_EQUAL, value1).thenDelete(delete);
+      assertFalse(ok);
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .ifMatches(CompareOperator.LESS, value1).thenDelete(delete);
+      assertTrue(ok);
+      table.put(put2);
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .ifMatches(CompareOperator.LESS_OR_EQUAL, value1).thenDelete(delete);
+      assertTrue(ok);
+      table.put(put2);
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .ifMatches(CompareOperator.NOT_EQUAL, value1).thenDelete(delete);
+      assertTrue(ok);
+
+      // cell = "cccc", using "dddd" to compare only LARGER/LARGER_OR_EQUAL/NOT_EQUAL
+      // turns out "match"
+      table.put(put3);
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .ifMatches(CompareOperator.LESS, value4).thenDelete(delete);
+      assertFalse(ok);
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .ifMatches(CompareOperator.LESS_OR_EQUAL, value4).thenDelete(delete);
+      assertFalse(ok);
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .ifMatches(CompareOperator.EQUAL, value4).thenDelete(delete);
+      assertFalse(ok);
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .ifMatches(CompareOperator.GREATER, value4).thenDelete(delete);
+      assertTrue(ok);
+      table.put(put3);
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .ifMatches(CompareOperator.GREATER_OR_EQUAL, value4).thenDelete(delete);
+      assertTrue(ok);
+      table.put(put3);
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .ifMatches(CompareOperator.NOT_EQUAL, value4).thenDelete(delete);
+      assertTrue(ok);
+
+      // cell = "bbbb", using "bbbb" to compare only GREATER_OR_EQUAL/LESS_OR_EQUAL/EQUAL
+      // turns out "match"
+      table.put(put2);
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .ifMatches(CompareOperator.GREATER, value2).thenDelete(delete);
+      assertFalse(ok);
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .ifMatches(CompareOperator.NOT_EQUAL, value2).thenDelete(delete);
+      assertFalse(ok);
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .ifMatches(CompareOperator.LESS, value2).thenDelete(delete);
+      assertFalse(ok);
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .ifMatches(CompareOperator.GREATER_OR_EQUAL, value2).thenDelete(delete);
+      assertTrue(ok);
+      table.put(put2);
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .ifMatches(CompareOperator.LESS_OR_EQUAL, value2).thenDelete(delete);
+      assertTrue(ok);
+      table.put(put2);
+      ok = table.checkAndMutate(ROW, FAMILY).qualifier(QUALIFIER)
+              .ifMatches(CompareOperator.EQUAL, value2).thenDelete(delete);
+      assertTrue(ok);
+    }
+  }
+
+  /**
+  * Test ScanMetrics
+  */
+  @Test
+  @SuppressWarnings({"unused", "checkstyle:EmptyBlock"})
+  public void testScanMetrics() throws Exception {
+    final TableName tableName = name.getTableName();
+
+    // Set up test table:
+    // Create table:
+    try (Table ht = TEST_UTIL.createMultiRegionTable(tableName, FAMILY)) {
+      int numOfRegions;
+      try (RegionLocator r = TEST_UTIL.getConnection().getRegionLocator(tableName)) {
+        numOfRegions = r.getStartKeys().length;
+      }
+      // Create 3 rows in the table, with rowkeys starting with "zzz*" so that
+      // scan are forced to hit all the regions.
+      Put put1 = new Put(Bytes.toBytes("zzz1"));
+      put1.addColumn(FAMILY, QUALIFIER, VALUE);
+      Put put2 = new Put(Bytes.toBytes("zzz2"));
+      put2.addColumn(FAMILY, QUALIFIER, VALUE);
+      Put put3 = new Put(Bytes.toBytes("zzz3"));
+      put3.addColumn(FAMILY, QUALIFIER, VALUE);
+      ht.put(Arrays.asList(put1, put2, put3));
+
+      Scan scan1 = new Scan();
+      int numRecords = 0;
+      try (ResultScanner scanner = ht.getScanner(scan1)) {
+        for (Result result : scanner) {
+          numRecords++;
+        }
+
+        LOG.info("test data has " + numRecords + " records.");
+
+        // by default, scan metrics collection is turned off
+        assertNull(scanner.getScanMetrics());
+      }
+
+      // turn on scan metrics
+      Scan scan2 = new Scan();
+      scan2.setScanMetricsEnabled(true);
+      scan2.setCaching(numRecords + 1);
+      try (ResultScanner scanner = ht.getScanner(scan2)) {
+        for (Result result : scanner.next(numRecords - 1)) {
+        }
+        scanner.close();
+        // closing the scanner will set the metrics.
+        assertNotNull(scanner.getScanMetrics());
+      }
+
+      // set caching to 1, because metrics are collected in each roundtrip only
+      scan2 = new Scan();
+      scan2.setScanMetricsEnabled(true);
+      scan2.setCaching(1);
+      try (ResultScanner scanner = ht.getScanner(scan2)) {
+        // per HBASE-5717, this should still collect even if you don't run all the way to
+        // the end of the scanner. So this is asking for 2 of the 3 rows we inserted.
+        for (Result result : scanner.next(numRecords - 1)) {
+        }
+        ScanMetrics scanMetrics = scanner.getScanMetrics();
+        assertEquals("Did not access all the regions in the table", numOfRegions,
+                scanMetrics.countOfRegions.get());
+      }
+
+      // check byte counters
+      scan2 = new Scan();
+      scan2.setScanMetricsEnabled(true);
+      scan2.setCaching(1);
+      try (ResultScanner scanner = ht.getScanner(scan2)) {
+        int numBytes = 0;
+        for (Result result : scanner.next(1)) {
+          for (Cell cell : result.listCells()) {
+            numBytes += PrivateCellUtil.estimatedSerializedSizeOf(cell);
+          }
+        }
+        scanner.close();
+        ScanMetrics scanMetrics = scanner.getScanMetrics();
+        assertEquals("Did not count the result bytes", numBytes,
+                scanMetrics.countOfBytesInResults.get());
+      }
+
+      // check byte counters on a small scan
+      scan2 = new Scan();
+      scan2.setScanMetricsEnabled(true);
+      scan2.setCaching(1);
+      scan2.setSmall(true);
+      try (ResultScanner scanner = ht.getScanner(scan2)) {
+        int numBytes = 0;
+        for (Result result : scanner.next(1)) {
+          for (Cell cell : result.listCells()) {
+            numBytes += PrivateCellUtil.estimatedSerializedSizeOf(cell);
+          }
+        }
+        scanner.close();
+        ScanMetrics scanMetrics = scanner.getScanMetrics();
+        assertEquals("Did not count the result bytes", numBytes,
+                scanMetrics.countOfBytesInResults.get());
+      }
+
+      // now, test that the metrics are still collected even if you don't call close, but do
+      // run past the end of all the records
+      /** There seems to be a timing issue here.  Comment out for now. Fix when time.
+       Scan scanWithoutClose = new Scan();
+       scanWithoutClose.setCaching(1);
+       scanWithoutClose.setScanMetricsEnabled(true);
+       ResultScanner scannerWithoutClose = ht.getScanner(scanWithoutClose);
+       for (Result result : scannerWithoutClose.next(numRecords + 1)) {
+       }
+       ScanMetrics scanMetricsWithoutClose = getScanMetrics(scanWithoutClose);
+       assertEquals("Did not access all the regions in the table", numOfRegions,
+       scanMetricsWithoutClose.countOfRegions.get());
+       */
+
+      // finally,
+      // test that the metrics are collected correctly if you both run past all the records,
+      // AND close the scanner
+      Scan scanWithClose = new Scan();
+      // make sure we can set caching up to the number of a scanned values
+      scanWithClose.setCaching(numRecords);
+      scanWithClose.setScanMetricsEnabled(true);
+      try (ResultScanner scannerWithClose = ht.getScanner(scanWithClose)) {
+        for (Result result : scannerWithClose.next(numRecords + 1)) {
+        }
+        scannerWithClose.close();
+        ScanMetrics scanMetricsWithClose = scannerWithClose.getScanMetrics();
+        assertEquals("Did not access all the regions in the table", numOfRegions,
+                scanMetricsWithClose.countOfRegions.get());
+      }
+    }
+  }
+
+  /**
+   * Tests that cache on write works all the way up from the client-side.
+   *
+   * Performs inserts, flushes, and compactions, verifying changes in the block
+   * cache along the way.
+   */
+  @Test
+  public void testCacheOnWriteEvictOnClose() throws Exception {
+    final TableName tableName = name.getTableName();
+    byte [] data = Bytes.toBytes("data");
+    try (Table table = TEST_UTIL.createTable(tableName, FAMILY)) {
+      try (RegionLocator locator = TEST_UTIL.getConnection().getRegionLocator(tableName)) {
+        // get the block cache and region
+        String regionName = locator.getAllRegionLocations().get(0).getRegion().getEncodedName();
+
+        HRegion region = TEST_UTIL.getRSForFirstRegionInTable(tableName)
+                .getRegion(regionName);
+        HStore store = region.getStores().iterator().next();
+        CacheConfig cacheConf = store.getCacheConfig();
+        cacheConf.setCacheDataOnWrite(true);
+        cacheConf.setEvictOnClose(true);
+        BlockCache cache = cacheConf.getBlockCache().get();
+
+        // establish baseline stats
+        long startBlockCount = cache.getBlockCount();
+        long startBlockHits = cache.getStats().getHitCount();
+        long startBlockMiss = cache.getStats().getMissCount();
+
+        // wait till baseline is stable, (minimal 500 ms)
+        for (int i = 0; i < 5; i++) {
+          Thread.sleep(100);
+          if (startBlockCount != cache.getBlockCount()
+                  || startBlockHits != cache.getStats().getHitCount()
+                  || startBlockMiss != cache.getStats().getMissCount()) {
+            startBlockCount = cache.getBlockCount();
+            startBlockHits = cache.getStats().getHitCount();
+            startBlockMiss = cache.getStats().getMissCount();
+            i = -1;
+          }
+        }
+
+        // insert data
+        Put put = new Put(ROW);
+        put.addColumn(FAMILY, QUALIFIER, data);
+        table.put(put);
+        assertTrue(Bytes.equals(table.get(new Get(ROW)).value(), data));
+
+        // data was in memstore so don't expect any changes
+        assertEquals(startBlockCount, cache.getBlockCount());
+        assertEquals(startBlockHits, cache.getStats().getHitCount());
+        assertEquals(startBlockMiss, cache.getStats().getMissCount());
+
+        // flush the data
+        LOG.debug("Flushing cache");
+        region.flush(true);
+
+        // expect two more blocks in cache - DATA and ROOT_INDEX
+        // , no change in hits/misses
+        long expectedBlockCount = startBlockCount + 2;
+        long expectedBlockHits = startBlockHits;
+        long expectedBlockMiss = startBlockMiss;
+        assertEquals(expectedBlockCount, cache.getBlockCount());
+        assertEquals(expectedBlockHits, cache.getStats().getHitCount());
+        assertEquals(expectedBlockMiss, cache.getStats().getMissCount());
+        // read the data and expect same blocks, one new hit, no misses
+        assertTrue(Bytes.equals(table.get(new Get(ROW)).value(), data));
+        assertEquals(expectedBlockCount, cache.getBlockCount());
+        assertEquals(++expectedBlockHits, cache.getStats().getHitCount());
+        assertEquals(expectedBlockMiss, cache.getStats().getMissCount());
+        // insert a second column, read the row, no new blocks, one new hit
+        byte[] QUALIFIER2 = Bytes.add(QUALIFIER, QUALIFIER);
+        byte[] data2 = Bytes.add(data, data);
+        put = new Put(ROW);
+        put.addColumn(FAMILY, QUALIFIER2, data2);
+        table.put(put);
+        Result r = table.get(new Get(ROW));
+        assertTrue(Bytes.equals(r.getValue(FAMILY, QUALIFIER), data));
+        assertTrue(Bytes.equals(r.getValue(FAMILY, QUALIFIER2), data2));
+        assertEquals(expectedBlockCount, cache.getBlockCount());
+        assertEquals(++expectedBlockHits, cache.getStats().getHitCount());
+        assertEquals(expectedBlockMiss, cache.getStats().getMissCount());
+        // flush, one new block
+        System.out.println("Flushing cache");
+        region.flush(true);
+
+        // + 1 for Index Block, +1 for data block
+        expectedBlockCount += 2;
+        assertEquals(expectedBlockCount, cache.getBlockCount());
+        assertEquals(expectedBlockHits, cache.getStats().getHitCount());
+        assertEquals(expectedBlockMiss, cache.getStats().getMissCount());
+        // compact, net minus two blocks, two hits, no misses
+        System.out.println("Compacting");
+        assertEquals(2, store.getStorefilesCount());
+        store.triggerMajorCompaction();
+        region.compact(true);
+        store.closeAndArchiveCompactedFiles();
+        waitForStoreFileCount(store, 1, 10000); // wait 10 seconds max
+        assertEquals(1, store.getStorefilesCount());
+        // evicted two data blocks and two index blocks and compaction does not cache new blocks
+        expectedBlockCount = 0;
+        assertEquals(expectedBlockCount, cache.getBlockCount());
+        expectedBlockHits += 2;
+        assertEquals(expectedBlockMiss, cache.getStats().getMissCount());
+        assertEquals(expectedBlockHits, cache.getStats().getHitCount());
+        // read the row, this should be a cache miss because we don't cache data
+        // blocks on compaction
+        r = table.get(new Get(ROW));
+        assertTrue(Bytes.equals(r.getValue(FAMILY, QUALIFIER), data));
+        assertTrue(Bytes.equals(r.getValue(FAMILY, QUALIFIER2), data2));
+        expectedBlockCount += 1; // cached one data block
+        assertEquals(expectedBlockCount, cache.getBlockCount());
+        assertEquals(expectedBlockHits, cache.getStats().getHitCount());
+        assertEquals(++expectedBlockMiss, cache.getStats().getMissCount());
+      }
+    }
+  }
+
+  private void waitForStoreFileCount(HStore store, int count, int timeout)
+      throws InterruptedException {
+    long start = System.currentTimeMillis();
+    while (start + timeout > System.currentTimeMillis() && store.getStorefilesCount() != count) {
+      Thread.sleep(100);
+    }
+    System.out.println("start=" + start + ", now=" + System.currentTimeMillis() + ", cur=" +
+        store.getStorefilesCount());
+    assertEquals(count, store.getStorefilesCount());
+  }
+
+  /**
+   * Tests the non cached version of getRegionLocator by moving a region.
+   */
+  @Test
+  public void testNonCachedGetRegionLocation() throws Exception {
+    // Test Initialization.
+    final TableName tableName = name.getTableName();
+    byte [] family1 = Bytes.toBytes("f1");
+    byte [] family2 = Bytes.toBytes("f2");
+    try (Table ignored = TEST_UTIL.createTable(tableName, new byte[][] {family1, family2}, 10);
+        Admin admin = TEST_UTIL.getAdmin();
+        RegionLocator locator = TEST_UTIL.getConnection().getRegionLocator(tableName)) {
+      List<HRegionLocation> allRegionLocations = locator.getAllRegionLocations();
+      assertEquals(1, allRegionLocations.size());
+      RegionInfo regionInfo = allRegionLocations.get(0).getRegion();
+      ServerName addrBefore = allRegionLocations.get(0).getServerName();
+      // Verify region location before move.
+      HRegionLocation addrCache = locator.getRegionLocation(regionInfo.getStartKey(), false);
+      HRegionLocation addrNoCache = locator.getRegionLocation(regionInfo.getStartKey(),  true);
+
+      assertEquals(addrBefore.getPort(), addrCache.getPort());
+      assertEquals(addrBefore.getPort(), addrNoCache.getPort());
+
+      // Make sure more than one server.
+      if (TEST_UTIL.getMiniHBaseCluster().getLiveRegionServerThreads().size() <= 1) {
+        TEST_UTIL.getMiniHBaseCluster().startRegionServer();
+        Waiter.waitFor(TEST_UTIL.getConfiguration(), 30000, new Waiter.Predicate<Exception>() {
+          @Override public boolean evaluate() throws Exception {
+            return TEST_UTIL.getMiniHBaseCluster().getLiveRegionServerThreads().size() > 1;
+          }
+        });
+      }
+
+      ServerName addrAfter = null;
+      // Now move the region to a different server.
+      for (int i = 0; i < TEST_UTIL.getMiniHBaseCluster().getLiveRegionServerThreads().size();
+           i++) {
+        HRegionServer regionServer = TEST_UTIL.getHBaseCluster().getRegionServer(i);
+        ServerName addr = regionServer.getServerName();
+        if (addr.getPort() != addrBefore.getPort()) {
+          admin.move(regionInfo.getEncodedNameAsBytes(), addr);
+          // Wait for the region to move.
+          Thread.sleep(5000);
+          addrAfter = addr;
... 1527 lines suppressed ...