private void splitTest(byte[] splitPoint, byte[][] familyNames, int[] rowCounts, int numVersions,
    int blockSize, boolean async) throws Exception {
    TableName tableName = TableName.valueOf("testForceSplit");
    StringBuilder sb = new StringBuilder();
    // Add tail to String so can see better in logs where a test is running.
    for (int i = 0; i < rowCounts.length; i++) {
      sb.append("_").append(Integer.toString(rowCounts[i]));
    }
    assertFalse(ADMIN.tableExists(tableName));
    try (final Table table = TEST_UTIL.createTable(tableName, familyNames, numVersions, blockSize);
      final RegionLocator locator = TEST_UTIL.getConnection().getRegionLocator(tableName)) {

      int rowCount = 0;
      byte[] q = new byte[0];

      // insert rows into column families. The number of rows that have values
      // in a specific column family is decided by rowCounts[familyIndex]
      for (int index = 0; index < familyNames.length; index++) {
        ArrayList<Put> puts = new ArrayList<>(rowCounts[index]);
        for (int i = 0; i < rowCounts[index]; i++) {
          byte[] k = Bytes.toBytes(i);
          Put put = new Put(k);
          put.addColumn(familyNames[index], q, k);
          puts.add(put);
        }
        table.put(puts);

        if (rowCount < rowCounts[index]) {
          rowCount = rowCounts[index];
        }
      }

      // get the initial layout (should just be one region)
      List<HRegionLocation> m = locator.getAllRegionLocations();
      LOG.info("Initial regions (" + m.size() + "): " + m);
      assertTrue(m.size() == 1);

      // Verify row count
      Scan scan = new Scan();
      int rows;
      try (ResultScanner scanner = table.getScanner(scan)) {
        rows = count(scanner);
      }
      assertEquals(rowCount, rows);

      // Have an outstanding scan going on to make sure we can scan over splits.
      scan = new Scan();
      try (ResultScanner scanner = table.getScanner(scan)) {
        // Scan first row so we are into first region before split happens.
        scanner.next();

        // Split the table
        if (async) {
          if (splitPoint != null) {
            ADMIN.split(tableName, splitPoint);
          } else {
            ADMIN.split(tableName);
          }
          final AtomicInteger count = new AtomicInteger(0);
          Thread t = new Thread("CheckForSplit") {
            @Override
            public void run() {
              for (int i = 0; i < 45; i++) {
                try {
                  sleep(1000);
                } catch (InterruptedException e) {
                  continue;
                }
                // check again
                List<HRegionLocation> regions = null;
                try {
                  regions = locator.getAllRegionLocations();
                } catch (IOException e) {
                  LOG.warn("get location failed", e);
                }
                if (regions == null) {
                  continue;
                }
                count.set(regions.size());
                if (count.get() >= 2) {
                  LOG.info("Found: " + regions);
                  break;
                }
                LOG.debug("Cycle waiting on split");
              }
              LOG.debug("CheckForSplit thread exited, current region count: " + count.get());
            }
          };
          t.setPriority(Thread.NORM_PRIORITY - 2);
          t.start();
          t.join();
        } else {
          // Sync split region, no need to create a thread to check
          ADMIN.splitRegionAsync(m.get(0).getRegion().getRegionName(), splitPoint).get();
        }
        // Verify row count
        rows = 1 + count(scanner); // We counted one row above.
      }
      assertEquals(rowCount, rows);

      List<HRegionLocation> regions = null;
      try {
        regions = locator.getAllRegionLocations();
      } catch (IOException e) {
        e.printStackTrace();
      }
      assertEquals(2, regions.size());
      if (splitPoint != null) {
        // make sure the split point matches our explicit configuration
        assertEquals(Bytes.toString(splitPoint),
          Bytes.toString(regions.get(0).getRegion().getEndKey()));
        assertEquals(Bytes.toString(splitPoint),
          Bytes.toString(regions.get(1).getRegion().getStartKey()));
        LOG.debug("Properly split on " + Bytes.toString(splitPoint));
      } else {
        if (familyNames.length > 1) {
          int splitKey = Bytes.toInt(regions.get(0).getRegion().getEndKey());
          // check if splitKey is based on the largest column family
          // in terms of it store size
          int deltaForLargestFamily = Math.abs(rowCount / 2 - splitKey);
          LOG.debug("SplitKey=" + splitKey + "&deltaForLargestFamily=" + deltaForLargestFamily
            + ", r=" + regions.get(0).getRegion());
          for (int index = 0; index < familyNames.length; index++) {
            int delta = Math.abs(rowCounts[index] / 2 - splitKey);
            if (delta < deltaForLargestFamily) {
              assertTrue("Delta " + delta + " for family " + index + " should be at least "
                + "deltaForLargestFamily " + deltaForLargestFamily, false);
            }
          }
        }
      }
      TEST_UTIL.deleteTable(tableName);
    }
  }
Multi-family scenario. Tests forcing split from client and having scanners successfully ride
over split.
hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestAdmin1.java