@Test
  public void testScanDoesNotCleanRegionsWithExistingParents() throws Exception {
    TableDescriptor td = createTableDescriptorForCurrentMethod();
    // Create regions: aaa->{lastEndKey}, aaa->ccc, aaa->bbb, bbb->ccc, etc.

    // Parent
    RegionInfo parent =
      createRegionInfo(td.getTableName(), Bytes.toBytes("aaa"), HConstants.EMPTY_BYTE_ARRAY, true);
    // Sleep a second else the encoded name on these regions comes out
    // same for all with same start key and made in same second.
    Thread.sleep(1001);

    // Daughter a
    RegionInfo splita =
      createRegionInfo(td.getTableName(), Bytes.toBytes("aaa"), Bytes.toBytes("ccc"), true);
    Thread.sleep(1001);

    // Make daughters of daughter a; splitaa and splitab.
    RegionInfo splitaa =
      createRegionInfo(td.getTableName(), Bytes.toBytes("aaa"), Bytes.toBytes("bbb"), false);
    RegionInfo splitab =
      createRegionInfo(td.getTableName(), Bytes.toBytes("bbb"), Bytes.toBytes("ccc"), false);

    // Daughter b
    RegionInfo splitb =
      createRegionInfo(td.getTableName(), Bytes.toBytes("ccc"), HConstants.EMPTY_BYTE_ARRAY);
    Thread.sleep(1001);

    // Parent has daughters splita and splitb. Splita has daughters splitaa and splitab.
    final Map<RegionInfo, Result> splitParents = new TreeMap<>(new SplitParentFirstComparator());
    splitParents.put(parent, createResult(parent, splita, splitb));
    // simulate that splita goes offline when it is split
    splita = RegionInfoBuilder.newBuilder(splita).setOffline(true).build();
    splitParents.put(splita, createResult(splita, splitaa, splitab));

    final Map<RegionInfo, Result> mergedRegions = new TreeMap<>();
    CatalogJanitor spy = spy(this.janitor);

    CatalogJanitorReport report = new CatalogJanitorReport();
    report.count = 10;
    report.mergedRegions.putAll(mergedRegions);
    report.splitParents.putAll(splitParents);

    doReturn(report).when(spy).scanForReport();

    // Create ref from splita to parent
    LOG.info("parent=" + parent.getShortNameToLog() + ", splita=" + splita.getShortNameToLog());
    Path splitaRef =
      createReferences(this.masterServices, td, parent, splita, Bytes.toBytes("ccc"), false);
    LOG.info("Created reference " + splitaRef);

    // Parent and splita should not be removed because a reference from splita to parent.
    int gcs = spy.scan();
    assertEquals(0, gcs);

    // Now delete the ref
    FileSystem fs = FileSystem.get(HTU.getConfiguration());
    assertTrue(fs.delete(splitaRef, true));

    // now, both parent, and splita can be deleted
    gcs = spy.scan();
    assertEquals(2, gcs);
  }
CatalogJanitor.scan() should not clean parent regions if their own parents are still
referencing them. This ensures that grandparent regions do not point to deleted parent regions.
hbase-server/src/test/java/org/apache/hadoop/hbase/master/janitor/TestCatalogJanitor.java