@Test
  public void testSimple() throws InterruptedException, ExecutionException {
    Callable<String> mainJob =
        new Callable<String>() {
          public String call() {
            log.info("main job - " + Tasks.current());
            messages.add("main");
            DynamicTasks.queue(sayTask("world"));
            return "bye";
          }
        };
    DynamicSequentialTask<String> t = new DynamicSequentialTask<String>(mainJob);
    // this should be added before anything added when the task is invoked
    t.queue(sayTask("hello"));

    Assert.assertEquals(messages, Lists.newArrayList());
    Assert.assertEquals(t.isBegun(), false);
    Assert.assertEquals(Iterables.size(t.getChildren()), 1);

    ec.submit(t);
    Assert.assertEquals(t.isSubmitted(), true);
    Assert.assertEquals(t.getUnchecked(Duration.ONE_SECOND), "bye");
    long elapsed = t.getEndTimeUtc() - t.getSubmitTimeUtc();
    Assert.assertTrue(
        elapsed < 1000,
        "elapsed time should have been less than 1s but was " + Time.makeTimeString(elapsed, true));
    Assert.assertEquals(Iterables.size(t.getChildren()), 2);
    Assert.assertEquals(messages.size(), 3, "expected 3 entries, but had " + messages);
    // either main or hello can be first, but world should be last
    Assert.assertEquals(messages.get(2), "world");
  }
 @Test
 public void testInessentialChildrenFailureDoesNotAbortSecondaryOrFailPrimary() {
   Task<String> t1 = monitorableTask(null, "1", new FailCallable());
   TaskTags.markInessential(t1);
   Task<String> t =
       Tasks.<String>builder()
           .dynamic(true)
           .body(monitorableJob("main"))
           .add(t1)
           .add(monitorableTask("2"))
           .build();
   ec.submit(t);
   releaseAndWaitForMonitorableJob("1");
   Assert.assertFalse(t.blockUntilEnded(TINY_TIME));
   releaseAndWaitForMonitorableJob("2");
   Assert.assertFalse(t.blockUntilEnded(TINY_TIME));
   releaseMonitorableJob("main");
   Assert.assertTrue(t.blockUntilEnded(TIMEOUT));
   Assert.assertEquals(messages, MutableList.of("1", "2", "main"));
   Assert.assertTrue(
       stopwatch.elapsed(TimeUnit.MILLISECONDS) < TIMEOUT.toMilliseconds(),
       "took too long: " + stopwatch);
   Assert.assertFalse(t.isError());
   Assert.assertTrue(t1.isError());
 }
  @Test
  public void testCancelled() throws InterruptedException, ExecutionException {
    Task<List<?>> t =
        Tasks.sequential(sayTask("1"), sayTask("2a", Duration.THIRTY_SECONDS, "2b"), sayTask("3"));
    ec.submit(t);
    synchronized (messages) {
      while (messages.size() <= 1) messages.wait();
    }
    Assert.assertEquals(messages, Arrays.asList("1", "2a"));
    Time.sleep(Duration.millis(50));
    t.cancel(true);
    Assert.assertTrue(t.isDone());
    // 2 should get cancelled, and invoke the cancellation semaphore
    // 3 should get cancelled and not run at all
    Assert.assertEquals(messages, Arrays.asList("1", "2a"));

    // Need to ensure that 2 has been started; race where we might cancel it before its run method
    // is even begun. Hence doing "2a; pause; 2b" where nothing is interruptable before pause.
    Assert.assertTrue(cancellations.tryAcquire(10, TimeUnit.SECONDS));

    Iterator<Task<?>> ci = ((HasTaskChildren) t).getChildren().iterator();
    Assert.assertEquals(ci.next().get(), "1");
    Task<?> task2 = ci.next();
    Assert.assertTrue(task2.isBegun());
    Assert.assertTrue(task2.isDone());
    Assert.assertTrue(task2.isCancelled());

    Task<?> task3 = ci.next();
    Assert.assertFalse(task3.isBegun());
    Assert.assertTrue(task2.isDone());
    Assert.assertTrue(task2.isCancelled());

    // but we do _not_ get a mutex from task3 as it does not run (is not interrupted)
    Assert.assertEquals(cancellations.availablePermits(), 0);
  }
 @Test
 public void testComplex() throws InterruptedException, ExecutionException {
   Task<List<?>> t =
       Tasks.sequential(
           sayTask("1"), sayTask("2"), Tasks.parallel(sayTask("4"), sayTask("3")), sayTask("5"));
   ec.submit(t);
   Assert.assertEquals(t.get().size(), 4);
   Asserts.assertEqualsIgnoringOrder((List<?>) t.get().get(2), ImmutableSet.of("3", "4"));
   Assert.assertTrue(
       messages.equals(Arrays.asList("1", "2", "3", "4", "5"))
           || messages.equals(Arrays.asList("1", "2", "4", "3", "5")),
       "messages=" + messages);
 }
  @Test
  public void testTaskBuilderUsingAddAllChildren() {
    Task<String> t =
        Tasks.<String>builder()
            .dynamic(true)
            .body(monitorableJob("main"))
            .addAll(ImmutableList.of(monitorableTask("1"), monitorableTask("2")))
            .build();
    ec.submit(t);
    releaseAndWaitForMonitorableJob("1");
    releaseAndWaitForMonitorableJob("2");
    releaseAndWaitForMonitorableJob("main");

    Assert.assertEquals(messages, MutableList.of("1", "2", "main"));
  }
  @Test
  public void testChildrenRunConcurrentlyWithPrimary() {
    Task<String> t =
        Tasks.<String>builder()
            .dynamic(true)
            .body(monitorableJob("main"))
            .add(monitorableTask("1"))
            .add(monitorableTask("2"))
            .build();
    ec.submit(t);
    releaseAndWaitForMonitorableJob("1");
    releaseAndWaitForMonitorableJob("main");
    Assert.assertFalse(t.blockUntilEnded(TINY_TIME));
    releaseMonitorableJob("2");

    Assert.assertTrue(t.blockUntilEnded(TIMEOUT));
    Assert.assertEquals(messages, MutableList.of("1", "main", "2"));
    Assert.assertTrue(
        stopwatch.elapsed(TimeUnit.MILLISECONDS) < TIMEOUT.toMilliseconds(),
        "took too long: " + stopwatch);
    Assert.assertFalse(t.isError());
  }