use of dev.failsafe.FailsafeException in project failsafe by jhalterman.
the class Testing method testGetInternal.
/**
* This method helps ensure behavior is identical between sync and async executions.
* <p>
* Does a .get, .getAsync, .getAsyncExecution, and .getStageAsync against the failsafe, performing pre-test setup and
* post-test assertion checks. {@code expectedResult} and {@code expectedExceptions} are verified against the returned
* result or thrown exceptions _and_ the ExecutionCompletedEvent's result and failure.
*
* @param given The pre-execution setup to perform. Useful for resetting stats and mocks.
* @param failsafe The FailsafeExecutor to execute with
* @param when the Supplier to provide to the FailsafeExecutor
* @param then post-test Assertions that are provided with Future (if any) and ExecutionCompletedEvent
* @param expectedResult The expected result to assert against the actual result
* @param expectedExceptions The expected exceptions to assert against the actual exceptions
* @param runAsyncExecutions Indicates whether to run the AsyncExecution tests, including .getAsyncExecution. These
* may be skipped for tests that involve timeouts, which don't work reliably against AsyncExecutions, since those may
* return immediately.
*/
private static <T> void testGetInternal(boolean runAsyncExecutions, CheckedRunnable given, FailsafeExecutor<T> failsafe, ContextualSupplier<T, T> when, Then<T> then, T expectedResult, Class<? extends Throwable>[] expectedExceptions) {
AtomicReference<CompletableFuture<T>> futureRef = new AtomicReference<>();
AtomicReference<ExecutionCompletedEvent<T>> completedEventRef = new AtomicReference<>();
Waiter completionListenerWaiter = new Waiter();
EventListener<ExecutionCompletedEvent<T>> setCompletedEventFn = e -> {
completedEventRef.set(e);
completionListenerWaiter.resume();
};
List<Class<? extends Throwable>> expected = new LinkedList<>();
Class<? extends Throwable>[] expectedExInner = expectedExceptions == null ? new Class[] {} : expectedExceptions;
Collections.addAll(expected, expectedExInner);
// Assert results by treating COMPLETE_SIGNAL as a null expected result
Consumer<T> resultAssertion = result -> {
if (result == COMPLETE_SIGNAL)
assertNull(expectedResult);
else
assertEquals(result, expectedResult);
};
Runnable postTestFn = () -> {
ignoreExceptions(() -> completionListenerWaiter.await(5000));
ExecutionCompletedEvent<T> completedEvent = completedEventRef.get();
if (expectedExInner.length > 0) {
assertNull(completedEvent.getResult());
assertMatches(completedEvent.getException(), Arrays.asList(expectedExInner));
} else {
resultAssertion.accept(completedEvent.getResult());
assertNull(completedEvent.getException());
}
if (then != null)
then.accept(futureRef.get(), completedEvent);
};
// Run sync test
System.out.println("\nRunning sync test");
if (given != null)
uncheck(given).run();
if (expectedExInner.length == 0) {
resultAssertion.accept(unwrapExceptions(() -> failsafe.onComplete(setCompletedEventFn).get(when)));
} else {
assertThrows(() -> failsafe.onComplete(setCompletedEventFn).get(when), t -> {
// Insert FailsafeException into expected exceptions if needed
if (t instanceof FailsafeException && !FailsafeException.class.equals(expected.get(0)) && !RuntimeException.class.isAssignableFrom(expected.get(0)))
return Lists.of(FailsafeException.class, expectedExInner);
return expected;
});
}
postTestFn.run();
if (expectedExInner.length > 0)
expected.add(0, ExecutionException.class);
// Create async tester
Consumer<Function<FailsafeExecutor<T>, CompletableFuture<T>>> asyncTester = test -> {
if (given != null)
uncheck(given).run();
CompletableFuture<T> future = test.apply(failsafe.onComplete(setCompletedEventFn));
futureRef.set(future);
if (expectedExInner.length == 0)
resultAssertion.accept(unwrapExceptions(future::get));
else
assertThrowsSup(future::get, expected);
postTestFn.run();
};
// Run async test
System.out.println("\nRunning async test");
asyncTester.accept(executor -> executor.getAsync(when));
// Run async execution test
if (runAsyncExecutions) {
System.out.println("\nRunning async execution test");
AsyncRunnable<T> asyncExecutionWhen = exec -> {
// Run supplier in a different thread
runInThread(() -> {
try {
T result = when.get(exec);
if (result == COMPLETE_SIGNAL)
exec.complete();
else
exec.recordResult(result);
} catch (Throwable t) {
exec.recordException(t);
}
});
};
asyncTester.accept(executor -> executor.getAsyncExecution(asyncExecutionWhen));
}
// Run stage async test
System.out.println("\nRunning get stage async test");
ContextualSupplier<T, ? extends CompletionStage<T>> stageAsyncWhen = ctx -> {
CompletableFuture<T> promise = new CompletableFuture<>();
// Run supplier in a different thread
runInThread(() -> {
try {
promise.complete(when.get(ctx));
} catch (Throwable t) {
promise.completeExceptionally(t);
}
});
return promise;
};
asyncTester.accept(executor -> executor.getStageAsync(stageAsyncWhen));
}
Aggregations