package org.threadly.concurrent.future; import static org.junit.Assert.*; import static org.threadly.TestConstants.*; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Optional; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; import org.junit.Test; import org.threadly.concurrent.SingleThreadScheduler; import org.threadly.test.concurrent.AsyncVerifier; import org.threadly.test.concurrent.TestableScheduler; import org.threadly.util.StringUtils; import org.threadly.util.SuppressedStackRuntimeException; @SuppressWarnings("javadoc") public class FutureUtilsTest { private static <T> List<ListenableFuture<? extends T>> makeFutures(int count, int errorIndex) { List<ListenableFuture<? extends T>> result = new ArrayList<>(count + 1); for (int i = 0; i < count; i++) { if (i == errorIndex) { result.add(FutureUtils.<T>immediateFailureFuture(null)); } else { result.add(FutureUtils.<T>immediateResultFuture(null)); } } return result; } @Test public void blockTillAllCompleteNullTest() throws InterruptedException { FutureUtils.blockTillAllComplete(null); // should return immediately } @Test public void blockTillAllCompleteTest() throws InterruptedException { List<ListenableFuture<?>> futures = makeFutures(TEST_QTY, -1); FutureUtils.blockTillAllComplete(futures); Iterator<ListenableFuture<?>> it = futures.iterator(); while (it.hasNext()) { assertTrue(it.next().isDone()); } } @Test public void blockTillAllCompleteErrorTest() throws InterruptedException { int errorIndex = TEST_QTY / 2; List<ListenableFuture<?>> futures = makeFutures(TEST_QTY, errorIndex); FutureUtils.blockTillAllComplete(futures); Iterator<ListenableFuture<?>> it = futures.iterator(); while (it.hasNext()) { assertTrue(it.next().isDone()); } } @Test public void blockTillAllCompleteWithTimeoutNullTest() throws InterruptedException, TimeoutException { FutureUtils.blockTillAllComplete(null, 1); // should return immediately } @Test public void blockTillAllCompleteWithTimeoutTest() throws InterruptedException, TimeoutException { List<ListenableFuture<?>> futures = makeFutures(TEST_QTY, -1); FutureUtils.blockTillAllComplete(futures, 1000 * 10); Iterator<ListenableFuture<?>> it = futures.iterator(); while (it.hasNext()) { assertTrue(it.next().isDone()); } } @Test public void blockTillAllCompleteWithTimeoutErrorTest() throws InterruptedException, TimeoutException { int errorIndex = TEST_QTY / 2; List<ListenableFuture<?>> futures = makeFutures(TEST_QTY, errorIndex); FutureUtils.blockTillAllComplete(futures, 1000 * 10); Iterator<ListenableFuture<?>> it = futures.iterator(); while (it.hasNext()) { assertTrue(it.next().isDone()); } } @Test (expected = TimeoutException.class) public void blockTillAllCompleteWithTimeoutTimeoutTest() throws InterruptedException, TimeoutException { List<? extends ListenableFuture<?>> futures = Collections.singletonList(new SettableListenableFuture<>()); FutureUtils.blockTillAllComplete(futures, 100); fail("Exception should have thrown"); } @Test (expected = TimeoutException.class) public void blockTillAllCompleteWithTimeoutZeroTimeoutTest() throws InterruptedException, TimeoutException { List<? extends ListenableFuture<?>> futures = Collections.singletonList(new SettableListenableFuture<>()); FutureUtils.blockTillAllComplete(futures, 0); fail("Exception should have thrown"); } @Test public void blockTillAllCompleteOrFirstErrorNullTest() throws InterruptedException, ExecutionException { FutureUtils.blockTillAllCompleteOrFirstError(null); // should return immediately } @Test public void blockTillAllCompleteOrFirstErrorTest() throws InterruptedException, ExecutionException { List<ListenableFuture<?>> futures = makeFutures(TEST_QTY, -1); FutureUtils.blockTillAllCompleteOrFirstError(futures); Iterator<ListenableFuture<?>> it = futures.iterator(); while (it.hasNext()) { assertTrue(it.next().isDone()); } } @Test public void blockTillAllCompleteOrFirstErrorErrorTest() throws InterruptedException { int errorIndex = TEST_QTY / 2; List<ListenableFuture<?>> futures = makeFutures(TEST_QTY, errorIndex); FutureUtils.blockTillAllComplete(futures); Iterator<ListenableFuture<?>> it = futures.iterator(); for (int i = 0; i <= errorIndex; i++) { Future<?> f = it.next(); if (i < errorIndex) { assertTrue(f.isDone()); } else if (i == errorIndex) { try { f.get(); fail("Exception should have thrown"); } catch (ExecutionException e) { // expected } } } } @Test public void blockTillAllCompleteOrFirstErrorWithTimeoutNullTest() throws InterruptedException, ExecutionException, TimeoutException { FutureUtils.blockTillAllCompleteOrFirstError(null, 10); // should return immediately } @Test public void blockTillAllCompleteOrFirstErrorWithTimeoutTest() throws InterruptedException, ExecutionException, TimeoutException { List<ListenableFuture<?>> futures = makeFutures(TEST_QTY, -1); FutureUtils.blockTillAllCompleteOrFirstError(futures, 1000 * 10); Iterator<ListenableFuture<?>> it = futures.iterator(); while (it.hasNext()) { assertTrue(it.next().isDone()); } } @Test public void blockTillAllCompleteOrFirstErrorWithTimeoutErrorTest() throws InterruptedException, TimeoutException { int errorIndex = TEST_QTY / 2; List<ListenableFuture<?>> futures = makeFutures(TEST_QTY, errorIndex); FutureUtils.blockTillAllComplete(futures, 1000 * 10); Iterator<ListenableFuture<?>> it = futures.iterator(); for (int i = 0; i <= errorIndex; i++) { Future<?> f = it.next(); if (i < errorIndex) { assertTrue(f.isDone()); } else if (i == errorIndex) { try { f.get(); fail("Exception should have thrown"); } catch (ExecutionException e) { // expected } } } } @Test (expected = TimeoutException.class) public void blockTillAllCompleteOrFirstErrorWithTimeoutTimeoutTest() throws InterruptedException, TimeoutException, ExecutionException { List<? extends ListenableFuture<?>> futures = Collections.singletonList(new SettableListenableFuture<>()); FutureUtils.blockTillAllCompleteOrFirstError(futures, 100); fail("Exception should have thrown"); } @Test (expected = TimeoutException.class) public void blockTillAllCompleteOrFirstErrorWithTimeoutZeroTimeoutTest() throws InterruptedException, TimeoutException, ExecutionException { List<? extends ListenableFuture<?>> futures = Collections.singletonList(new SettableListenableFuture<>()); FutureUtils.blockTillAllCompleteOrFirstError(futures, 0); fail("Exception should have thrown"); } @Test public void countFuturesWithResultTest() throws InterruptedException { List<ListenableFuture<Boolean>> futures = new ArrayList<>(TEST_QTY * 2); for (int i = 0; i < TEST_QTY * 2; i++) { futures.add(FutureUtils.immediateResultFuture(i % 2 == 1)); } assertEquals(TEST_QTY, FutureUtils.countFuturesWithResult(futures, false)); } @Test public void countFuturesWithResultWithTimeoutTest() throws InterruptedException, TimeoutException { List<ListenableFuture<Boolean>> futures = new ArrayList<>(TEST_QTY * 2); for (int i = 0; i < TEST_QTY * 2; i++) { futures.add(FutureUtils.immediateResultFuture(i % 2 == 1)); } assertEquals(TEST_QTY, FutureUtils.countFuturesWithResult(futures, false, 100)); } @Test (expected = TimeoutException.class) public void countFuturesWithResultWithTimeoutTimeoutTest() throws InterruptedException, TimeoutException { List<? extends ListenableFuture<?>> futures = Collections.singletonList(new SettableListenableFuture<>()); assertEquals(TEST_QTY, FutureUtils.countFuturesWithResult(futures, false, 100)); fail("Exception should have thrown"); } @Test (expected = TimeoutException.class) public void countFuturesWithResultWithTimeoutZeroTimeoutTest() throws InterruptedException, TimeoutException { List<? extends ListenableFuture<?>> futures = Collections.singletonList(new SettableListenableFuture<>()); assertEquals(TEST_QTY, FutureUtils.countFuturesWithResult(futures, false, 0)); fail("Exception should have thrown"); } private static void verifyCompleteFuture(final ListenableFuture<?> f, final List<ListenableFuture<?>> futures) throws InterruptedException, TimeoutException { final AsyncVerifier av = new AsyncVerifier(); f.addListener(new Runnable() { @Override public void run() { av.assertTrue(f.isDone()); Iterator<ListenableFuture<?>> it = futures.iterator(); while (it.hasNext()) { av.assertTrue(it.next().isDone()); } av.signalComplete(); } }); av.waitForTest(); } @Test public void makeCompleteFutureNullTest() { ListenableFuture<?> f = FutureUtils.makeCompleteFuture(null); assertTrue(f.isDone()); } @Test public void makeCompleteFutureEmptyListTest() { List<ListenableFuture<?>> futures = Collections.emptyList(); ListenableFuture<?> f = FutureUtils.makeCompleteFuture(futures); assertTrue(f.isDone()); } @Test public void makeCompleteFutureAlreadyDoneFuturesTest() { List<ListenableFuture<?>> futures = new ArrayList<>(TEST_QTY); for (int i = 0; i < TEST_QTY; i++) { SettableListenableFuture<?> future = new SettableListenableFuture<>(); future.setResult(null); futures.add(future); } ListenableFuture<?> f = FutureUtils.makeCompleteFuture(futures); assertTrue(f.isDone()); } @Test public void makeCompleteFutureTest() throws InterruptedException, TimeoutException, ExecutionException { makeCompleteFutureTest(-1); } @Test public void makeCompleteFutureWithErrorTest() throws InterruptedException, TimeoutException, ExecutionException { makeCompleteFutureTest(TEST_QTY / 2); } private static void makeCompleteFutureTest(int errorIndex) throws InterruptedException, TimeoutException, ExecutionException { List<ListenableFuture<?>> futures = makeFutures(TEST_QTY, errorIndex); ListenableFuture<?> f = FutureUtils.makeCompleteFuture(futures); verifyCompleteFuture(f, futures); assertNull(f.get()); } @Test public void makeCompleteFutureCancelTest() { SettableListenableFuture<?> slf = new SettableListenableFuture<>(); assertTrue(FutureUtils.makeCompleteFuture(Collections.singletonList(slf)).cancel(true)); assertTrue(slf.isCancelled()); } @Test public void makeCompleteFutureWithResultNullTest() throws InterruptedException, ExecutionException { String result = StringUtils.makeRandomString(5); ListenableFuture<String> f = FutureUtils.makeCompleteFuture(null, result); assertTrue(f.isDone()); assertEquals(result, f.get()); } @Test public void makeCompleteFutureWithResultEmptyListTest() throws InterruptedException, ExecutionException { String result = StringUtils.makeRandomString(5); List<ListenableFuture<?>> futures = Collections.emptyList(); ListenableFuture<String> f = FutureUtils.makeCompleteFuture(futures, result); assertTrue(f.isDone()); assertEquals(result, f.get()); } @Test public void makeCompleteFutureWithResultTest() throws InterruptedException, TimeoutException, ExecutionException { String result = StringUtils.makeRandomString(5); List<ListenableFuture<?>> futures = makeFutures(TEST_QTY, -1); ListenableFuture<String> f = FutureUtils.makeCompleteFuture(futures, result); verifyCompleteFuture(f, futures); assertEquals(result, f.get()); } @Test public void makeCompleteFutureWithNullResultTest() throws InterruptedException, TimeoutException, ExecutionException { List<ListenableFuture<?>> futures = makeFutures(TEST_QTY, -1); ListenableFuture<?> f = FutureUtils.makeCompleteFuture(futures, null); verifyCompleteFuture(f, futures); assertNull(f.get()); } @Test public void makeCompleteFutureWithResultCancelTest() { SettableListenableFuture<?> slf = new SettableListenableFuture<>(); assertTrue(FutureUtils.makeCompleteFuture(Collections.singletonList(slf), null).cancel(true)); assertTrue(slf.isCancelled()); } @Test public void makeFailurePropagatingCompleteFutureNullTest() { ListenableFuture<?> f = FutureUtils.makeFailurePropagatingCompleteFuture(null); assertTrue(f.isDone()); } @Test public void makeFailurePropagatingCompleteFutureEmptyListTest() { List<ListenableFuture<?>> futures = Collections.emptyList(); ListenableFuture<?> f = FutureUtils.makeFailurePropagatingCompleteFuture(futures); assertTrue(f.isDone()); } @Test public void makeFailurePropagatingCompleteFutureAlreadyDoneFuturesTest() { List<ListenableFuture<?>> futures = new ArrayList<>(TEST_QTY); for (int i = 0; i < TEST_QTY; i++) { SettableListenableFuture<?> future = new SettableListenableFuture<>(); future.setResult(null); futures.add(future); } ListenableFuture<?> f = FutureUtils.makeFailurePropagatingCompleteFuture(futures); assertTrue(f.isDone()); } @Test public void makeFailurePropagatingCompleteFutureTest() throws InterruptedException, TimeoutException, ExecutionException { List<ListenableFuture<?>> futures = makeFutures(TEST_QTY, -1); ListenableFuture<?> f = FutureUtils.makeFailurePropagatingCompleteFuture(futures); verifyCompleteFuture(f, futures); assertNull(f.get()); } @Test public void makeFailurePropagatingCompleteFuturePropagateFailureTest() { List<ListenableFuture<?>> futures = makeFutures(TEST_QTY, TEST_QTY / 2); ListenableFuture<?> f = FutureUtils.makeFailurePropagatingCompleteFuture(futures); try { f.get(); fail("Exception should have thrown"); } catch (ExecutionException e) { // expected } catch (InterruptedException e) { fail("Interrupted?"); } } @Test public void makeFailurePropagatingCompleteFuturePropagateCancelTest() { SettableListenableFuture<?> slf = new SettableListenableFuture<>(); assertTrue(slf.cancel(false)); ListenableFuture<?> f = FutureUtils.makeFailurePropagatingCompleteFuture(Collections.singletonList(slf)); assertTrue(f.isDone()); assertTrue(f.isCancelled()); } @Test public void makeFailurePropagatingCompleteFutureCancelTest() { SettableListenableFuture<?> slf = new SettableListenableFuture<>(); assertTrue(FutureUtils.makeFailurePropagatingCompleteFuture(Collections.singletonList(slf)).cancel(true)); assertTrue(slf.isCancelled()); } @Test public void makeFailurePropagatingCompleteFutureWithResultNullTest() throws InterruptedException, ExecutionException { String result = StringUtils.makeRandomString(5); ListenableFuture<String> f = FutureUtils.makeFailurePropagatingCompleteFuture(null, result); assertTrue(f.isDone()); assertEquals(result, f.get()); } @Test public void makeFailurePropagatingCompleteFutureWithResultEmptyListTest() throws InterruptedException, ExecutionException { String result = StringUtils.makeRandomString(5); List<ListenableFuture<?>> futures = Collections.emptyList(); ListenableFuture<String> f = FutureUtils.makeFailurePropagatingCompleteFuture(futures, result); assertTrue(f.isDone()); assertEquals(result, f.get()); } @Test public void makeFailurePropagatingCompleteFutureWithResultTest() throws InterruptedException, TimeoutException, ExecutionException { String result = StringUtils.makeRandomString(5); List<ListenableFuture<?>> futures = makeFutures(TEST_QTY, -1); ListenableFuture<String> f = FutureUtils.makeFailurePropagatingCompleteFuture(futures, result); verifyCompleteFuture(f, futures); assertEquals(result, f.get()); } @Test public void makeFailurePropagatingCompleteFutureWithNullResultTest() throws InterruptedException, TimeoutException, ExecutionException { List<ListenableFuture<?>> futures = makeFutures(TEST_QTY, -1); ListenableFuture<?> f = FutureUtils.makeFailurePropagatingCompleteFuture(futures, null); verifyCompleteFuture(f, futures); assertNull(f.get()); } @Test public void makeFailurePropagatingCompleteFutureWithResultCancelTest() { SettableListenableFuture<?> slf = new SettableListenableFuture<>(); assertTrue(FutureUtils.makeFailurePropagatingCompleteFuture(Collections.singletonList(slf), null).cancel(true)); assertTrue(slf.isCancelled()); } @Test public void makeCompleteListFutureNullTest() { ListenableFuture<List<ListenableFuture<?>>> f = FutureUtils.makeCompleteListFuture(null); assertTrue(f.isDone()); } @Test public void makeCompleteListFutureEmptyListTest() { List<ListenableFuture<?>> futures = Collections.emptyList(); ListenableFuture<List<ListenableFuture<?>>> f = FutureUtils.makeCompleteListFuture(futures); assertTrue(f.isDone()); } @Test public void makeCompleteListFutureAlreadyDoneFuturesTest() throws InterruptedException, ExecutionException { List<ListenableFuture<?>> futures = new ArrayList<>(TEST_QTY); for (int i = 0; i < TEST_QTY; i++) { SettableListenableFuture<?> future = new SettableListenableFuture<>(); if (i == TEST_QTY / 2) { future.setFailure(null); } else { future.setResult(null); } futures.add(future); } ListenableFuture<List<ListenableFuture<?>>> f = FutureUtils.makeCompleteListFuture(futures); assertTrue(f.isDone()); verifyAllIncluded(futures, f.get(), null); } private static void verifyAllIncluded(List<ListenableFuture<?>> expected, List<ListenableFuture<?>> result, ListenableFuture<?> excludedFuture) { Iterator<ListenableFuture<?>> it = expected.iterator(); while (it.hasNext()) { ListenableFuture<?> f = it.next(); if (f != excludedFuture) { assertTrue(result.contains(f)); } } assertFalse(result.contains(excludedFuture)); } @Test public void makeCompleteListFutureTest() throws InterruptedException, TimeoutException, ExecutionException { makeCompleteListFutureTest(-1); } @Test public void makeCompleteListFutureWithErrorTest() throws InterruptedException, TimeoutException, ExecutionException { makeCompleteListFutureTest(TEST_QTY / 2); } private static void makeCompleteListFutureTest(int errorIndex) throws InterruptedException, TimeoutException, ExecutionException { List<ListenableFuture<?>> futures = makeFutures(TEST_QTY, errorIndex); ListenableFuture<List<ListenableFuture<?>>> f = FutureUtils.makeCompleteListFuture(futures); verifyCompleteFuture(f, futures); verifyAllIncluded(futures, f.get(), null); } @Test public void makeCompleteListFutureCancelTest() { SettableListenableFuture<?> slf = new SettableListenableFuture<>(); assertTrue(FutureUtils.makeCompleteListFuture(Collections.singletonList(slf)).cancel(true)); assertTrue(slf.isCancelled()); } @Test public void makeSuccessListFutureNullTest() { ListenableFuture<List<ListenableFuture<?>>> f = FutureUtils.makeSuccessListFuture(null); assertTrue(f.isDone()); } @Test public void makeSuccessListFutureEmptyListTest() { List<ListenableFuture<?>> futures = Collections.emptyList(); ListenableFuture<List<ListenableFuture<?>>> f = FutureUtils.makeSuccessListFuture(futures); assertTrue(f.isDone()); } @Test public void makeSuccessListFutureAlreadyDoneFuturesTest() throws InterruptedException, ExecutionException { List<ListenableFuture<?>> futures = new ArrayList<>(TEST_QTY); ListenableFuture<?> failureFuture = null; for (int i = 0; i < TEST_QTY; i++) { SettableListenableFuture<?> future = new SettableListenableFuture<>(); if (i == TEST_QTY / 2) { failureFuture = future; future.setFailure(null); } else { future.setResult(null); } futures.add(future); } ListenableFuture<List<ListenableFuture<?>>> f = FutureUtils.makeSuccessListFuture(futures); assertTrue(f.isDone()); verifyAllIncluded(futures, f.get(), failureFuture); } @Test public void makeSuccessListFutureWithErrorTest() throws ExecutionException, InterruptedException, TimeoutException { List<ListenableFuture<?>> futures = makeFutures(TEST_QTY, -1); SettableListenableFuture<?> failureFuture = new SettableListenableFuture<>(); failureFuture.setFailure(null); futures.add(failureFuture); ListenableFuture<List<ListenableFuture<?>>> f = FutureUtils.makeSuccessListFuture(futures); verifyCompleteFuture(f, futures); verifyAllIncluded(futures, f.get(), failureFuture); } @Test public void makeSuccessListFutureWithCancelErrorTest() throws ExecutionException, InterruptedException, TimeoutException { List<ListenableFuture<?>> futures = makeFutures(TEST_QTY, -1); SettableListenableFuture<?> cancelFuture = new SettableListenableFuture<>(); cancelFuture.cancel(false); futures.add(cancelFuture); ListenableFuture<List<ListenableFuture<?>>> f = FutureUtils.makeSuccessListFuture(futures); verifyCompleteFuture(f, futures); verifyAllIncluded(futures, f.get(), cancelFuture); } @Test public void makeSuccessListFutureCancelTest() { SettableListenableFuture<?> slf = new SettableListenableFuture<>(); assertTrue(FutureUtils.makeSuccessListFuture(Collections.singletonList(slf)).cancel(true)); assertTrue(slf.isCancelled()); } @Test public void makeFailureListFutureNullTest() { ListenableFuture<List<ListenableFuture<?>>> f = FutureUtils.makeFailureListFuture(null); assertTrue(f.isDone()); } @Test public void makeFailureListFutureEmptyListTest() { List<ListenableFuture<?>> futures = Collections.emptyList(); ListenableFuture<List<ListenableFuture<?>>> f = FutureUtils.makeFailureListFuture(futures); assertTrue(f.isDone()); } @Test public void makeFailureListFutureAlreadyDoneFuturesTest() throws InterruptedException, ExecutionException { List<ListenableFuture<?>> futures = new ArrayList<>(TEST_QTY); ListenableFuture<?> failureFuture = null; for (int i = 0; i < TEST_QTY; i++) { SettableListenableFuture<?> future = new SettableListenableFuture<>(); if (i == TEST_QTY / 2) { failureFuture = future; future.setFailure(null); } else { future.setResult(null); } futures.add(future); } ListenableFuture<List<ListenableFuture<?>>> f = FutureUtils.makeFailureListFuture(futures); assertTrue(f.isDone()); verifyNoneIncluded(futures, f.get(), failureFuture); } private static void verifyNoneIncluded(List<ListenableFuture<?>> exempt, List<ListenableFuture<?>> result, ListenableFuture<?> includedFuture) { Iterator<ListenableFuture<?>> it = exempt.iterator(); while (it.hasNext()) { ListenableFuture<?> f = it.next(); if (f != includedFuture) { assertFalse(result.contains(f)); } } assertTrue(result.contains(includedFuture)); } @Test public void makeFailureListFutureWithErrorTest() throws InterruptedException, TimeoutException, ExecutionException { List<ListenableFuture<?>> futures = makeFutures(TEST_QTY, -1); SettableListenableFuture<?> failureFuture = new SettableListenableFuture<>(); failureFuture.setFailure(null); futures.add(failureFuture); ListenableFuture<List<ListenableFuture<?>>> f = FutureUtils.makeFailureListFuture(futures); verifyCompleteFuture(f, futures); verifyNoneIncluded(futures, f.get(), failureFuture); } @Test public void makeFailureListFutureWithCancelErrorTest() throws InterruptedException, TimeoutException, ExecutionException { List<ListenableFuture<?>> futures = makeFutures(TEST_QTY, -1); SettableListenableFuture<?> cancelFuture = new SettableListenableFuture<>(); cancelFuture.cancel(false); futures.add(cancelFuture); ListenableFuture<List<ListenableFuture<?>>> f = FutureUtils.makeFailureListFuture(futures); verifyCompleteFuture(f, futures); verifyNoneIncluded(futures, f.get(), cancelFuture); } @Test public void makeFailureListFutureCancelTest() { SettableListenableFuture<?> slf = new SettableListenableFuture<>(); assertTrue(FutureUtils.makeFailureListFuture(Collections.singletonList(slf)).cancel(true)); assertTrue(slf.isCancelled()); } @Test public void makeResultListFutureNullFuturesTest() throws InterruptedException, ExecutionException { ListenableFuture<List<String>> resultFuture = FutureUtils.makeResultListFuture(null, false); assertTrue(resultFuture.isDone()); assertNotNull(resultFuture.get()); assertTrue(resultFuture.get().isEmpty()); } @Test public void makeResultListFutureAlreadyDoneFuturesTest() throws InterruptedException, ExecutionException { List<ListenableFuture<? extends String>> futures = makeFutures(TEST_QTY, -1); ListenableFuture<List<String>> resultFuture = FutureUtils.makeResultListFuture(futures, false); assertTrue(resultFuture.isDone()); assertEquals(TEST_QTY, resultFuture.get().size()); } @Test public void makeResultListFutureIgnoreFailureTest() throws InterruptedException, ExecutionException { List<ListenableFuture<? extends String>> futures = makeFutures(TEST_QTY, TEST_QTY / 2); ListenableFuture<List<String>> resultFuture = FutureUtils.makeResultListFuture(futures, true); assertTrue(resultFuture.isDone()); assertEquals(TEST_QTY - 1, resultFuture.get().size()); } @Test (expected = ExecutionException.class) public void makeResultListFutureWithFailureTest() throws InterruptedException, ExecutionException { List<ListenableFuture<? extends String>> futures = makeFutures(TEST_QTY, TEST_QTY / 2); ListenableFuture<List<String>> resultFuture = FutureUtils.makeResultListFuture(futures, false); assertTrue(resultFuture.isDone()); resultFuture.get(); fail("Exception should have thrown"); } @Test public void makeResultListFutureCancelTest() { SettableListenableFuture<?> slf = new SettableListenableFuture<>(); assertTrue(FutureUtils.makeResultListFuture(Collections.singletonList(slf), true).cancel(true)); assertTrue(slf.isCancelled()); } @Test public void makeResultListFutureResultsTest() throws InterruptedException, ExecutionException { List<String> expectedResults = new ArrayList<>(TEST_QTY); List<ListenableFuture<String>> futures = new ArrayList<>(TEST_QTY); for (int i = 0; i < TEST_QTY; i++) { String result = StringUtils.makeRandomString(5); expectedResults.add(result); futures.add(FutureUtils.immediateResultFuture(result)); } List<String> actualResults = FutureUtils.makeResultListFuture(futures, false).get(); assertTrue(actualResults.containsAll(expectedResults)); assertTrue(expectedResults.containsAll(actualResults)); } @Test public void cancelIncompleteFuturesTest() throws InterruptedException, ExecutionException { List<SettableListenableFuture<?>> futures = new ArrayList<>(TEST_QTY); for (int i = 0; i < TEST_QTY; i++) { SettableListenableFuture<?> slf = new SettableListenableFuture<>(); futures.add(slf); if (i % 2 == 0) { slf.setResult(null); } } FutureUtils.cancelIncompleteFutures(futures, false); for (int i = 0; i < TEST_QTY; i++) { SettableListenableFuture<?> slf = futures.get(i); assertTrue(slf.isDone()); if (i % 2 == 0) { slf.get(); // should not throw as was completed normally } else { assertTrue(slf.isCancelled()); } } } @Test public void cancelIncompleteFuturesIfAnyFailTest() throws InterruptedException, ExecutionException { List<SettableListenableFuture<?>> futures = new ArrayList<>(TEST_QTY); for (int i = 0; i < TEST_QTY; i++) { SettableListenableFuture<?> slf = new SettableListenableFuture<>(); futures.add(slf); if (i % 2 == 0) { slf.setResult(null); } } FutureUtils.cancelIncompleteFuturesIfAnyFail(false, futures, false); // fail one future futures.get(1).setFailure(null); for (int i = 0; i < TEST_QTY; i++) { SettableListenableFuture<?> slf = futures.get(i); assertTrue(slf.isDone()); if (i % 2 == 0) { slf.get(); // should not throw as was completed normally } else if (i != 1) { // skip manually failed future assertTrue(slf.isCancelled()); } } } @Test public void cancelIncompleteFuturesIfAnyFailCancelTest() { List<SettableListenableFuture<?>> futures = new ArrayList<>(TEST_QTY); for (int i = 0; i < TEST_QTY; i++) { futures.add(new SettableListenableFuture<>()); } FutureUtils.cancelIncompleteFuturesIfAnyFail(false, futures, false); // cancel one future assertTrue(futures.get(1).cancel(false)); for (int i = 0; i < TEST_QTY; i++) { assertTrue(futures.get(i).isCancelled()); } } @Test public void cancelIncompleteFuturesIfAnyFailCopyTest() { List<SettableListenableFuture<?>> futures = new ArrayList<>(TEST_QTY); for (int i = 0; i < TEST_QTY; i++) { futures.add(new SettableListenableFuture<>()); } FutureUtils.cancelIncompleteFuturesIfAnyFail(true, futures, false); // copy and clear futures List<SettableListenableFuture<?>> futuresCopy = new ArrayList<>(futures); futures.clear(); // cancel one future assertTrue(futuresCopy.get(1).cancel(false)); for (int i = 0; i < TEST_QTY; i++) { assertTrue(futuresCopy.get(i).isCancelled()); } } @Test public void immediateResultFutureNullResultTest() throws InterruptedException, ExecutionException, TimeoutException { ListenableFuture<?> testFuture = FutureUtils.immediateResultFuture(null); ImmediateListenableFutureTest.resultTest(testFuture, null); assertTrue(testFuture == ImmediateResultListenableFuture.NULL_RESULT); } @Test public void immediateResultFutureTest() throws InterruptedException, ExecutionException, TimeoutException { Object result = new Object(); ListenableFuture<?> testFuture = FutureUtils.immediateResultFuture(result); ImmediateListenableFutureTest.resultTest(testFuture, result); } @Test public void immediateResultFutureCancelTest() { ListenableFuture<?> testFuture = FutureUtils.immediateResultFuture(null); ImmediateListenableFutureTest.cancelTest(testFuture); } @Test public void immediateResultFutureAddListenerTest() { ListenableFuture<?> testFuture = FutureUtils.immediateResultFuture(null); ImmediateListenableFutureTest.addListenerTest(testFuture); } @Test public void immediateResultFutureAddCallbackTest() { Object result = new Object(); ListenableFuture<?> testFuture = FutureUtils.immediateResultFuture(result); ImmediateListenableFutureTest.resultAddCallbackTest(testFuture, result); } @Test public void immediateFailureFutureTest() { Exception failure = new Exception(); ListenableFuture<?> testFuture = FutureUtils.immediateFailureFuture(failure); ImmediateListenableFutureTest.failureTest(testFuture, failure); } @Test public void immediateFailureFutureCancelTest() { ListenableFuture<?> testFuture = FutureUtils.immediateFailureFuture(null); ImmediateListenableFutureTest.cancelTest(testFuture); } @Test public void immediateFailureFutureAddListenerTest() { ListenableFuture<?> testFuture = FutureUtils.immediateFailureFuture(null); ImmediateListenableFutureTest.addListenerTest(testFuture); } @Test public void immediateFailureFutureAddCallbackTest() { Throwable failure = new Exception(); ListenableFuture<?> testFuture = FutureUtils.immediateFailureFuture(failure); ImmediateListenableFutureTest.failureAddCallbackTest(testFuture, failure); } @Test public void scheduleWhileTaskResultNullFirstRunInThreadTest() throws Exception { int scheduleDelayMillis = 10; TestableScheduler scheduler = new TestableScheduler(); Object result = new Object(); ListenableFuture<?> f = FutureUtils.scheduleWhileTaskResultNull(scheduler, scheduleDelayMillis, false, new Callable<Object>() { private boolean first = true; @Override public Object call() throws Exception { if (first) { first = false; return null; } else { return result; } } }); assertFalse(f.isDone()); assertEquals(1, scheduler.advance(scheduleDelayMillis)); assertTrue(f.isDone()); assertTrue(result == f.get()); } @Test public void scheduleWhileTaskResultNullFirstRunOnSchedulerTest() throws Exception { int scheduleDelayMillis = 10; TestableScheduler scheduler = new TestableScheduler(); Object result = new Object(); ListenableFuture<?> f = FutureUtils.scheduleWhileTaskResultNull(scheduler, scheduleDelayMillis, true, new Callable<Object>() { private boolean first = true; @Override public Object call() throws Exception { if (first) { first = false; return null; } else { return result; } } }); assertFalse(f.isDone()); assertEquals(1, scheduler.tick()); // first run async assertFalse(f.isDone()); assertEquals(1, scheduler.advance(scheduleDelayMillis)); assertTrue(f.isDone()); assertTrue(result == f.get()); } @Test public void scheduleWhileTaskResultNullTaskFailureInThreadTest() throws InterruptedException { TestableScheduler scheduler = new TestableScheduler(); RuntimeException failure = new SuppressedStackRuntimeException(); ListenableFuture<?> f = FutureUtils.scheduleWhileTaskResultNull(scheduler, 10, false, () -> { throw failure; }); assertTrue(f.isDone()); try { f.get(); fail("Exception should have thrown"); } catch (ExecutionException e) { assertTrue(e.getCause() == failure); } } @Test public void scheduleWhileTaskResultNullTaskFailureOnSchedulerTest() throws InterruptedException { TestableScheduler scheduler = new TestableScheduler(); RuntimeException failure = new SuppressedStackRuntimeException(); ListenableFuture<?> f = FutureUtils.scheduleWhileTaskResultNull(scheduler, 10, true, () -> { throw failure; }); assertFalse(f.isDone()); assertEquals(1, scheduler.tick()); // first run async assertTrue(f.isDone()); try { f.get(); fail("Exception should have thrown"); } catch (ExecutionException e) { assertTrue(e.getCause() == failure); } } @Test public void scheduleWhileTaskResultNullTimeoutTest() throws Exception { SingleThreadScheduler scheduler = new SingleThreadScheduler(); try { ListenableFuture<?> f = FutureUtils.scheduleWhileTaskResultNull(scheduler, 2, true, () -> null, DELAY_TIME); assertNull(f.get(DELAY_TIME + 1_000, TimeUnit.MILLISECONDS)); } finally { scheduler.shutdownNow(); } } @Test public void scheduleWhileTaskResultNullCancelReturnedFutureTest() { TestableScheduler scheduler = new TestableScheduler(); AtomicInteger runCount = new AtomicInteger(); ListenableFuture<?> f = FutureUtils.scheduleWhileTaskResultNull(scheduler, 1, false, () -> { runCount.incrementAndGet(); return null; }); assertEquals(1, scheduler.advance(1)); int startCount = runCount.get(); f.cancel(false); assertEquals(1, scheduler.advance(1)); // should be task realizing it was canceled // verify task did not run assertEquals(startCount, runCount.get()); assertEquals(0, scheduler.advance(100)); // should never run again } @Test public void scheduleWhileFirstRunInThreadTest() throws Exception { int scheduleDelayMillis = 10; TestableScheduler scheduler = new TestableScheduler(); Optional<Object> result = Optional.of(new Object()); ListenableFuture<?> f = FutureUtils.scheduleWhile(scheduler, scheduleDelayMillis, false, new Callable<Optional<Object>>() { private boolean first = true; @Override public Optional<Object> call() throws Exception { if (first) { first = false; return Optional.empty(); } else { return result; } } }, (o) -> ! o.isPresent()); assertFalse(f.isDone()); assertEquals(1, scheduler.advance(scheduleDelayMillis)); assertTrue(f.isDone()); assertTrue(result == f.get()); } @Test public void scheduleWhileFirstRunOnSchedulerTest() throws Exception { int scheduleDelayMillis = 10; TestableScheduler scheduler = new TestableScheduler(); Optional<Object> result = Optional.of(new Object()); ListenableFuture<?> f = FutureUtils.scheduleWhile(scheduler, scheduleDelayMillis, true, new Callable<Optional<Object>>() { private boolean first = true; @Override public Optional<Object> call() throws Exception { if (first) { first = false; return Optional.empty(); } else { return result; } } }, (o) -> ! o.isPresent()); assertFalse(f.isDone()); assertEquals(1, scheduler.tick()); // first run async assertFalse(f.isDone()); assertEquals(1, scheduler.advance(scheduleDelayMillis)); assertTrue(f.isDone()); assertTrue(result == f.get()); } @Test public void scheduleWhileTaskFailureInThreadTest() throws InterruptedException { TestableScheduler scheduler = new TestableScheduler(); RuntimeException failure = new SuppressedStackRuntimeException(); ListenableFuture<?> f = FutureUtils.scheduleWhile(scheduler, 10, false, () -> { throw failure; }, (o) -> false); assertTrue(f.isDone()); try { f.get(); fail("Exception should have thrown"); } catch (ExecutionException e) { assertTrue(e.getCause() == failure); } } @Test public void scheduleWhileTaskFailureOnSchedulerTest() throws InterruptedException { TestableScheduler scheduler = new TestableScheduler(); RuntimeException failure = new SuppressedStackRuntimeException(); ListenableFuture<?> f = FutureUtils.scheduleWhile(scheduler, 10, true, () -> { throw failure; }, (o) -> false); assertFalse(f.isDone()); assertEquals(1, scheduler.tick()); // first run async assertTrue(f.isDone()); try { f.get(); fail("Exception should have thrown"); } catch (ExecutionException e) { assertTrue(e.getCause() == failure); } } @Test public void scheduleWhileTimeoutTest() throws Exception { SingleThreadScheduler scheduler = new SingleThreadScheduler(); try { ListenableFuture<Optional<?>> f = FutureUtils.scheduleWhile(scheduler, 2, true, () -> Optional.empty(), (o) -> ! o.isPresent(), DELAY_TIME); assertFalse(f.get(DELAY_TIME + 1_000, TimeUnit.MILLISECONDS).isPresent()); } finally { scheduler.shutdownNow(); } } @Test public void scheduleWhilePredicateThrowsInThreadTest() throws InterruptedException { TestableScheduler scheduler = new TestableScheduler(); RuntimeException failure = new SuppressedStackRuntimeException(); ListenableFuture<Object> f = FutureUtils.scheduleWhile(scheduler, 2, false, () -> null, (o) -> { throw failure; }); assertTrue(f.isDone()); try { f.get(); fail("Exception should have thrown"); } catch (ExecutionException e) { assertTrue(e.getCause() == failure); } } @Test public void scheduleWhilePredicateThrowsOnSchedulerTest() throws InterruptedException { TestableScheduler scheduler = new TestableScheduler(); RuntimeException failure = new SuppressedStackRuntimeException(); ListenableFuture<Object> f = FutureUtils.scheduleWhile(scheduler, 2, true, () -> null, (o) -> { throw failure; }); assertFalse(f.isDone()); assertEquals(1, scheduler.tick()); assertTrue(f.isDone()); try { f.get(); fail("Exception should have thrown"); } catch (ExecutionException e) { assertTrue(e.getCause() == failure); } } @Test public void scheduleWhileAlreadyDoneWithFinalResultTest() throws Exception { TestableScheduler scheduler = new TestableScheduler(); Object result = new Object(); ListenableFuture<Object> f = FutureUtils.scheduleWhile(scheduler, 2, FutureUtils.immediateResultFuture(result), () -> result, (o) -> false); assertTrue(f.isDone()); assertTrue(result == f.get()); } @Test public void scheduleWhileAlreadyDoneWithFailureTest() throws InterruptedException { TestableScheduler scheduler = new TestableScheduler(); RuntimeException failure = new SuppressedStackRuntimeException(); ListenableFuture<Object> f = FutureUtils.scheduleWhile(scheduler, 2, FutureUtils.immediateFailureFuture(failure), () -> null, (o) -> false); assertTrue(f.isDone()); try { f.get(); fail("Exception should have thrown"); } catch (ExecutionException e) { assertTrue(e.getCause() == failure); } } @Test public void scheduleWhileAlreadyDoneCanceledTest() { TestableScheduler scheduler = new TestableScheduler(); SettableListenableFuture<?> startingFuture = new SettableListenableFuture<>(); startingFuture.cancel(false); ListenableFuture<Object> f = FutureUtils.scheduleWhile(scheduler, 2, startingFuture, () -> null, (o) -> false); assertTrue(f.isDone()); assertTrue(f.isCancelled()); } @Test public void scheduleWhileCancelReturnedFutureTest() { TestableScheduler scheduler = new TestableScheduler(); AtomicInteger runCount = new AtomicInteger(); ListenableFuture<?> f = FutureUtils.scheduleWhile(scheduler, 1, false, () -> { runCount.incrementAndGet(); return null; }, (o) -> true); assertEquals(1, scheduler.advance(1)); int startCount = runCount.get(); f.cancel(false); assertEquals(1, scheduler.advance(1)); // should be task realizing it was canceled // verify task did not run assertEquals(startCount, runCount.get()); assertEquals(0, scheduler.advance(100)); // should never run again } }