@Test
  public void testShutdownLeavesJobRunning() throws InterruptedException {
    OutOfBandScheduledExecutor scheduler = new OutOfBandScheduledExecutor();
    ExecutorService worker = Executors.newSingleThreadExecutor();
    try {
      PartitionedScheduledExecutor executor = new PartitionedScheduledExecutor(scheduler, worker);

      final Semaphore semaphore = new Semaphore(0);
      executor.execute(
          new Runnable() {

            @Override
            public void run() {
              semaphore.acquireUninterruptibly();
            }
          });
      executor.shutdown();
      assertThat(executor.awaitTermination(100, MILLISECONDS), is(false));
      assertThat(executor.isShutdown(), is(true));
      assertThat(executor.isTerminated(), is(false));

      semaphore.release();
      assertThat(executor.awaitTermination(2, MINUTES), is(true));
      assertThat(executor.isShutdown(), is(true));
      assertThat(executor.isTerminated(), is(true));

      assertThat(semaphore.availablePermits(), is(0));
    } finally {
      worker.shutdown();
    }
  }
  @Test
  public void testScheduledTasksRunOnDeclaredPool()
      throws InterruptedException, ExecutionException {
    OutOfBandScheduledExecutor scheduler = new OutOfBandScheduledExecutor();
    ExecutorService worker =
        Executors.newSingleThreadExecutor(
            new ThreadFactory() {

              @Override
              public Thread newThread(Runnable r) {
                return new Thread(r, "testScheduledTasksRunOnDeclaredPool");
              }
            });
    try {
      PartitionedScheduledExecutor executor = new PartitionedScheduledExecutor(scheduler, worker);

      ScheduledFuture<Thread> future =
          executor.schedule(
              new Callable<Thread>() {

                @Override
                public Thread call() {
                  return Thread.currentThread();
                }
              },
              0,
              MILLISECONDS);

      assertThat(waitFor(future).getName(), is("testScheduledTasksRunOnDeclaredPool"));
      executor.shutdown();
    } finally {
      worker.shutdown();
    }
  }
  @Test
  public void testTerminationAfterShutdownWaitsForDelayedTask() throws InterruptedException {
    OutOfBandScheduledExecutor scheduler = new OutOfBandScheduledExecutor();
    ExecutorService worker = Executors.newSingleThreadExecutor();
    try {
      PartitionedScheduledExecutor executor = new PartitionedScheduledExecutor(scheduler, worker);

      ScheduledFuture<?> future =
          executor.schedule(
              new Runnable() {

                @Override
                public void run() {
                  // no-op
                }
              },
              200,
              MILLISECONDS);

      executor.shutdown();
      assertThat(executor.awaitTermination(30, SECONDS), is(true));
      assertThat(executor.isShutdown(), is(true));
      assertThat(executor.isTerminated(), is(true));

      assertThat(future.isDone(), is(true));
    } finally {
      worker.shutdown();
    }
  }
  @Test
  public void testFixedDelayPeriodicTaskIsCancelledByShutdown() throws InterruptedException {
    OutOfBandScheduledExecutor scheduler = new OutOfBandScheduledExecutor();
    ExecutorService worker = Executors.newSingleThreadExecutor();
    try {
      PartitionedScheduledExecutor executor = new PartitionedScheduledExecutor(scheduler, worker);

      ScheduledFuture<?> future =
          executor.scheduleWithFixedDelay(
              new Runnable() {

                @Override
                public void run() {
                  Assert.fail("Should not run!");
                }
              },
              2,
              1,
              MINUTES);

      executor.shutdown();
      assertThat(executor.awaitTermination(30, SECONDS), is(true));
      assertThat(executor.isShutdown(), is(true));
      assertThat(executor.isTerminated(), is(true));

      assertThat(future.isCancelled(), is(true));
    } finally {
      worker.shutdown();
    }
  }
 @Test
 public void testShutdownOfIdleExecutor() throws InterruptedException {
   OutOfBandScheduledExecutor scheduler = new OutOfBandScheduledExecutor();
   ExecutorService worker = Executors.newCachedThreadPool();
   PartitionedScheduledExecutor executor = new PartitionedScheduledExecutor(scheduler, worker);
   executor.shutdown();
   assertThat(executor.isShutdown(), is(true));
   assertThat(executor.awaitTermination(2, TimeUnit.MINUTES), is(true));
   assertThat(executor.isTerminated(), is(true));
 }
  @Test
  public void testQueuedJobRunsAfterShutdown() throws InterruptedException {
    OutOfBandScheduledExecutor scheduler = new OutOfBandScheduledExecutor();
    ExecutorService worker = Executors.newSingleThreadExecutor();
    try {
      PartitionedScheduledExecutor executor = new PartitionedScheduledExecutor(scheduler, worker);

      final Semaphore jobSemaphore = new Semaphore(0);
      final Semaphore testSemaphore = new Semaphore(0);

      executor.submit(
          new Callable<Void>() {

            @Override
            public Void call() throws Exception {
              testSemaphore.release();
              jobSemaphore.acquireUninterruptibly();
              return null;
            }
          });
      executor.submit(
          new Callable<Void>() {

            @Override
            public Void call() throws Exception {
              jobSemaphore.acquireUninterruptibly();
              return null;
            }
          });
      testSemaphore.acquireUninterruptibly();
      executor.shutdown();
      assertThat(executor.awaitTermination(100, MILLISECONDS), is(false));
      assertThat(executor.isShutdown(), is(true));
      assertThat(executor.isTerminated(), is(false));

      jobSemaphore.release();
      assertThat(executor.awaitTermination(100, MILLISECONDS), is(false));
      assertThat(executor.isShutdown(), is(true));
      assertThat(executor.isTerminated(), is(false));

      jobSemaphore.release();
      assertThat(executor.awaitTermination(2, MINUTES), is(true));
      assertThat(executor.isShutdown(), is(true));
      assertThat(executor.isTerminated(), is(true));

      assertThat(jobSemaphore.availablePermits(), is(0));
    } finally {
      worker.shutdown();
    }
  }