@Test
public void testChainAsync() throws Exception {

    GridFutureAdapter<String> fut0 = new GridFutureAdapter<>();
    IgniteFuture<String> fut = createFuture(fut0);
    C1<IgniteFuture<String>, Integer> chainClos = new C1<IgniteFuture<String>, Integer>() {
        @Override public Integer apply(IgniteFuture<String> fut) {
            assertEquals(CUSTOM_THREAD_NAME, Thread.currentThread().getName());
            return Integer.valueOf(fut.get());
        }
    };
    IgniteFuture<Integer> chained1 = fut.chainAsync(chainClos, customExec);
    assertFalse(chained1.isDone());
    final CountDownLatch latch = new CountDownLatch(1);
    class TestClosure implements CI1<IgniteFuture<Integer>> {
        private final CountDownLatch latch;
        private TestClosure(CountDownLatch latch) {
            this.latch = latch;
        }
        @Override public void apply(IgniteFuture<Integer> fut) {
            assertEquals(CUSTOM_THREAD_NAME, Thread.currentThread().getName());
            assertEquals(10, (int)fut.get());
            latch.countDown();
        }
    }
    chained1.listen(new TestClosure(latch));
    fut0.onDone("10");
    // Chained future will be completed asynchronously.
    chained1.get(100, TimeUnit.MILLISECONDS);
    assertTrue(chained1.isDone());
    assertEquals(10, (int)chained1.get());
    assert latch.await(100, TimeUnit.MILLISECONDS);
    assertTrue(fut.isDone());
    assertEquals("10", fut.get());
    // Test finished future
    GridFinishedFuture<String> ffut0 = new GridFinishedFuture<>("10");
    CountDownLatch latch1 = new CountDownLatch(1);
    IgniteFuture<Integer> chained2 = createFuture(ffut0).chainAsync(chainClos, customExec);
    chained2.listen(new TestClosure(latch1));
    chained2.get(100, TimeUnit.MILLISECONDS);
    assertTrue(chained2.isDone());
    assertEquals(10, (int)chained2.get());
    assert latch1.await(100, TimeUnit.MILLISECONDS);
}