@Test
  public void testVisit() throws Exception {
    final BlockingCountDownLatch latch = new BlockingCountDownLatch(3);

    IFuture<Void> future1 =
        Jobs.getJobManager()
            .schedule(
                new IRunnable() {

                  @Override
                  public void run() throws Exception {
                    latch.countDownAndBlock();
                  }
                },
                Jobs.newInput()
                    .withRunContext(RunContexts.copyCurrent())
                    .withExceptionHandling(null, false));

    IFuture<Void> future2 =
        Jobs.getJobManager()
            .schedule(
                new IRunnable() {

                  @Override
                  public void run() throws Exception {
                    latch.countDownAndBlock();
                  }
                },
                Jobs.newInput()
                    .withRunContext(RunContexts.copyCurrent())
                    .withExceptionHandling(null, false));

    IFuture<Void> future3 =
        Jobs.getJobManager()
            .schedule(
                new IRunnable() {

                  @Override
                  public void run() throws Exception {
                    latch.countDownAndBlock();
                  }
                },
                Jobs.newInput()
                    .withRunContext(RunContexts.copyCurrent())
                    .withExceptionHandling(null, false));

    assertTrue(latch.await());

    // RUN THE TEST
    final Set<IFuture<?>> protocol = new HashSet<>();
    Jobs.getJobManager()
        .visit(
            Jobs.newFutureFilterBuilder().andMatchFuture(future1, future2, future3).toFilter(),
            new IVisitor<IFuture<?>>() {

              @Override
              public boolean visit(IFuture<?> future) {
                protocol.add(future);
                return true;
              }
            });

    // VERIFY
    assertEquals(CollectionUtility.hashSet(future1, future2, future3), protocol);
  }
  @Test
  public void testShutdown() throws Exception {
    final Set<String> protocol =
        Collections.synchronizedSet(
            new HashSet<String>()); // synchronized because modified/read by different threads.

    final BlockingCountDownLatch setupLatch = new BlockingCountDownLatch(3);
    final BlockingCountDownLatch verifyLatch = new BlockingCountDownLatch(3);

    Jobs.getJobManager()
        .schedule(
            new IRunnable() {

              @Override
              public void run() throws Exception {
                try {
                  setupLatch.countDownAndBlock();
                } catch (InterruptedException e) {
                  protocol.add("interrupted-1");
                } finally {
                  verifyLatch.countDown();
                }
              }
            },
            Jobs.newInput()
                .withRunContext(RunContexts.copyCurrent())
                .withExceptionHandling(null, false));

    Jobs.getJobManager()
        .schedule(
            new IRunnable() {

              @Override
              public void run() throws Exception {
                try {
                  setupLatch.countDownAndBlock();
                } catch (InterruptedException e) {
                  protocol.add("interrupted-2");
                } finally {
                  verifyLatch.countDown();
                }
              }
            },
            Jobs.newInput()
                .withRunContext(RunContexts.copyCurrent())
                .withExceptionHandling(null, false));

    Jobs.getJobManager()
        .schedule(
            new IRunnable() {

              @Override
              public void run() throws Exception {
                try {
                  setupLatch.countDownAndBlock();
                } catch (InterruptedException e) {
                  protocol.add("interrupted-3");
                } finally {
                  verifyLatch.countDown();
                }
              }
            },
            Jobs.newInput()
                .withRunContext(RunContexts.copyCurrent())
                .withExceptionHandling(null, false));

    assertTrue(setupLatch.await());

    // RUN THE TEST
    Jobs.getJobManager().shutdown();

    // VERIFY
    assertTrue(verifyLatch.await());

    assertEquals(
        CollectionUtility.hashSet("interrupted-1", "interrupted-2", "interrupted-3"), protocol);

    try {
      Jobs.schedule(mock(IRunnable.class), Jobs.newInput());
      fail("AssertionError expected");
    } catch (AssertionException e) {
      // NOOP
    }
  }