@Test
  public void testOffline() throws Exception, InterruptedException {
    // Table should be after group table name so it gets assigned later.
    final TableName failoverTable = TableName.valueOf(getNameWithoutIndex(name.getMethodName()));
    TEST_UTIL.createTable(failoverTable, Bytes.toBytes("f"));
    final HRegionServer killRS = ((SingleProcessHBaseCluster) cluster).getRegionServer(0);
    final HRegionServer groupRS = ((SingleProcessHBaseCluster) cluster).getRegionServer(1);
    final HRegionServer failoverRS = ((SingleProcessHBaseCluster) cluster).getRegionServer(2);
    String newGroup = "my_group";
    Admin admin = TEST_UTIL.getAdmin();
    admin.addRSGroup(newGroup);
    if (
      master.getAssignmentManager().getRegionStates().getRegionAssignments()
        .containsValue(failoverRS.getServerName())
    ) {
      for (RegionInfo regionInfo : hbaseAdmin.getRegions(failoverRS.getServerName())) {
        hbaseAdmin.move(regionInfo.getEncodedNameAsBytes(), failoverRS.getServerName());
      }
      LOG.info("Waiting for region unassignments on failover RS...");
      TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
        @Override
        public boolean evaluate() throws Exception {
          return !master.getServerManager().getLoad(failoverRS.getServerName()).getRegionMetrics()
            .isEmpty();
        }
      });
    }

    // Move server to group and make sure all tables are assigned.
    admin.moveServersToRSGroup(Sets.newHashSet(groupRS.getServerName().getAddress()), newGroup);
    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
      @Override
      public boolean evaluate() throws Exception {
        return groupRS.getNumberOfOnlineRegions() < 1
          && master.getAssignmentManager().getRegionStates().getRegionsInTransitionCount() < 1;
      }
    });
    // Move table to group and wait.
    admin.setRSGroup(Sets.newHashSet(RSGroupInfoManagerImpl.RSGROUP_TABLE_NAME), newGroup);
    LOG.info("Waiting for move table...");
    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
      @Override
      public boolean evaluate() throws Exception {
        return groupRS.getNumberOfOnlineRegions() == 1;
      }
    });

    groupRS.stop("die");
    // Race condition here.
    TEST_UTIL.getHBaseCluster().getMaster().stopMaster();
    LOG.info("Waiting for offline mode...");
    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
      @Override
      public boolean evaluate() throws Exception {
        return TEST_UTIL.getHBaseCluster().getMaster() != null
          && TEST_UTIL.getHBaseCluster().getMaster().isActiveMaster()
          && TEST_UTIL.getHBaseCluster().getMaster().isInitialized()
          && TEST_UTIL.getHBaseCluster().getMaster().getServerManager().getOnlineServers().size()
              <= 3;
      }
    });

    // Get groupInfoManager from the new active master.
    RSGroupInfoManager groupMgr =
      ((SingleProcessHBaseCluster) cluster).getMaster().getRSGroupInfoManager();
    // Make sure balancer is in offline mode, since this is what we're testing.
    assertFalse(groupMgr.isOnline());
    // Kill final regionserver to see the failover happens for all tables except GROUP table since
    // it's group does not have any online RS.
    killRS.stop("die");
    master = TEST_UTIL.getHBaseCluster().getMaster();
    LOG.info("Waiting for new table assignment...");
    TEST_UTIL.waitFor(WAIT_TIMEOUT, new Waiter.Predicate<Exception>() {
      @Override
      public boolean evaluate() throws Exception {
        return failoverRS.getRegions(failoverTable).size() >= 1;
      }
    });
    assertEquals(0, failoverRS.getRegions(RSGroupInfoManagerImpl.RSGROUP_TABLE_NAME).size());

    // Need this for minicluster to shutdown cleanly.
    master.stopMaster();
  }
This tests that GroupBasedBalancer will use data in zk to do balancing during master startup.
This does not test retain assignment.
<p/>
The tests brings up 3 RS, creates a new RS group 'my_group', moves 1 RS to 'my_group', assigns
'hbase:rsgroup' to 'my_group', and kill the only server in that group so that 'hbase:rsgroup'
table isn't available. It then kills the active master and waits for backup master to come
online. In new master, RSGroupInfoManagerImpl gets the data from zk and waits for the expected
assignment with a timeout.
hbase-server/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsOfflineMode.java