@VisibleForTesting
  @InterfaceAudience.Private
  ShutdownHookManager() {
  }
@Test
  public void shutdownHookManager() {
    assertNotNull("No ShutdownHookManager", mgr);
    assertEquals(0, mgr.getShutdownHooksInOrder().size());
    Hook hook1 = new Hook("hook1", 0, false);
    Hook hook2 = new Hook("hook2", 0, false);
    Hook hook3 = new Hook("hook3", 1000, false);
    Hook hook4 = new Hook("hook4", 25000, true);
    Hook hook5 = new Hook("hook5",
        (SERVICE_SHUTDOWN_TIMEOUT_DEFAULT + 1) * 1000, true);

    mgr.addShutdownHook(hook1, 0);
    assertTrue(mgr.hasShutdownHook(hook1));
    assertEquals(1, mgr.getShutdownHooksInOrder().size());
    assertEquals(hook1, mgr.getShutdownHooksInOrder().get(0).getHook());
    assertTrue(mgr.removeShutdownHook(hook1));
    assertFalse(mgr.hasShutdownHook(hook1));
    assertFalse(mgr.removeShutdownHook(hook1));

    mgr.addShutdownHook(hook1, 0);
    assertTrue(mgr.hasShutdownHook(hook1));
    assertEquals(1, mgr.getShutdownHooksInOrder().size());
    assertEquals(SERVICE_SHUTDOWN_TIMEOUT_DEFAULT,
        mgr.getShutdownHooksInOrder().get(0).getTimeout());

    mgr.addShutdownHook(hook2, 1);
    assertTrue(mgr.hasShutdownHook(hook1));
    assertTrue(mgr.hasShutdownHook(hook2));
    assertEquals(2, mgr.getShutdownHooksInOrder().size());
    assertEquals(hook2, mgr.getShutdownHooksInOrder().get(0).getHook());
    assertEquals(hook1, mgr.getShutdownHooksInOrder().get(1).getHook());

    // Test hook finish without timeout
    mgr.addShutdownHook(hook3, 2, 4, TimeUnit.SECONDS);
    assertTrue(mgr.hasShutdownHook(hook3));
    assertEquals(hook3, mgr.getShutdownHooksInOrder().get(0).getHook());
    assertEquals(4, mgr.getShutdownHooksInOrder().get(0).getTimeout());

    // Test hook finish with timeout; highest priority
    int hook4timeout = 2;
    mgr.addShutdownHook(hook4, 3, hook4timeout, TimeUnit.SECONDS);
    assertTrue(mgr.hasShutdownHook(hook4));
    assertEquals(hook4, mgr.getShutdownHooksInOrder().get(0).getHook());
    assertEquals(2, mgr.getShutdownHooksInOrder().get(0).getTimeout());

    // a default timeout hook and verify it gets the default timeout
    mgr.addShutdownHook(hook5, 5);
    ShutdownHookManager.HookEntry hookEntry5 = mgr.getShutdownHooksInOrder()
        .get(0);
    assertEquals(hook5, hookEntry5.getHook());
    assertEquals("default timeout not used",
        ShutdownHookManager.getShutdownTimeout(new Configuration()),
        hookEntry5.getTimeout());
    assertEquals("hook priority", 5, hookEntry5.getPriority());
    // remove this to avoid a longer sleep in the test run
    assertTrue("failed to remove " + hook5,
        mgr.removeShutdownHook(hook5));


    // now execute the hook shutdown sequence
    INVOCATION_COUNT.set(0);
    LOG.info("invoking executeShutdown()");
    int timeouts = mgr.executeShutdown();
    LOG.info("Shutdown completed");
    assertEquals("Number of timed out hooks", 1, timeouts);

    List<ShutdownHookManager.HookEntry> hooks
        = mgr.getShutdownHooksInOrder();

    // analyze the hooks
    for (ShutdownHookManager.HookEntry entry : hooks) {
      Hook hook = (Hook) entry.getHook();
      assertTrue("Was not invoked " + hook, hook.invoked);
      // did any hook raise an exception?
      hook.maybeThrowAssertion();
    }

    // check the state of some of the invoked hooks
    // hook4 was invoked first, but it timed out.
    assertEquals("Expected to be invoked first " + hook4,
        1, hook4.invokedOrder);
    assertFalse("Expected to time out " + hook4, hook4.completed);


    // hook1 completed, but in order after the others, so its start time
    // is the longest.
    assertTrue("Expected to complete " + hook1, hook1.completed);
    long invocationInterval = hook1.startTime - hook4.startTime;
    assertTrue("invocation difference too short " + invocationInterval,
        invocationInterval >= hook4timeout * 1000);
    assertTrue("sleeping hook4 blocked other threads for " + invocationInterval,
        invocationInterval < hook4.sleepTime);

    // finally, clear the hooks
    mgr.clearShutdownHooks();
    // and verify that the hooks are empty
    assertFalse(mgr.hasShutdownHook(hook1));
    assertEquals("shutdown hook list is not empty",
        0,
        mgr.getShutdownHooksInOrder().size());
  }