use of dev.failsafe.event.ExecutionCompletedEvent in project failsafe by jhalterman.
the class OkHttpTesting method test.
private <T> void test(FailsafeExecutor<Response> failsafe, Call when, Then<Response> then, int expectedStatus, T expectedResult, Class<? extends Throwable>[] expectedExceptions) {
AtomicReference<CompletableFuture<Response>> futureRef = new AtomicReference<>();
AtomicReference<ExecutionCompletedEvent<Response>> completedEventRef = new AtomicReference<>();
Waiter completionListenerWaiter = new Waiter();
EventListener<ExecutionCompletedEvent<Response>> 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);
failsafe.onComplete(setCompletedEventFn);
Runnable postTestFn = () -> {
ignoreExceptions(() -> completionListenerWaiter.await(5000));
ExecutionCompletedEvent<Response> completedEvent = completedEventRef.get();
if (expectedExceptions == null) {
assertEquals(completedEvent.getResult().code(), expectedStatus);
assertNull(completedEvent.getException());
} else {
assertNull(completedEvent.getResult());
assertMatches(completedEvent.getException(), expectedExceptions);
}
if (then != null)
then.accept(futureRef.get(), completedEvent);
};
Consumer<Response> assertResult = response -> {
String result = unwrapExceptions(() -> response.body().string());
assertEquals(result, expectedResult);
assertEquals(response.code(), expectedStatus);
};
// Run sync test and assert result
System.out.println("\nRunning sync test");
FailsafeCall failsafeCall = FailsafeCall.with(failsafe).compose(when);
if (expectedExceptions == null) {
assertResult.accept(unwrapExceptions(failsafeCall::execute));
} else {
assertThrows(failsafeCall::execute, expectedExceptions);
}
postTestFn.run();
if (expectedExInner.length > 0)
expected.add(0, ExecutionException.class);
// Run async test and assert result
System.out.println("\nRunning async test");
failsafeCall = failsafeCall.clone();
CompletableFuture<Response> future = failsafeCall.executeAsync();
futureRef.set(future);
if (expectedExInner.length == 0) {
assertResult.accept(unwrapExceptions(future::get));
} else {
assertThrowsSup(future::get, expected);
}
postTestFn.run();
}
use of dev.failsafe.event.ExecutionCompletedEvent 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));
}
use of dev.failsafe.event.ExecutionCompletedEvent in project failsafe by jhalterman.
the class FutureCancellationTest method testCancelWithNestedRetries.
/**
* Asserts that cancelling a FailsafeFuture causes both retry policies to stop.
*/
public void testCancelWithNestedRetries() throws Throwable {
// Given
Stats outerRetryStats = new Stats();
Stats innerRetryStats = new Stats();
RetryPolicy<Object> outerRetryPolicy = withStatsAndLogs(RetryPolicy.builder(), outerRetryStats).build();
RetryPolicy<Object> innerRetryPolicy = withStatsAndLogs(RetryPolicy.builder().withMaxRetries(3).withDelay(Duration.ofMillis(100)), innerRetryStats).build();
AtomicReference<Future<Void>> futureRef = new AtomicReference<>();
AtomicReference<ExecutionCompletedEvent<Object>> completedRef = new AtomicReference<>();
Waiter waiter = new Waiter();
// When
futureRef.set(Failsafe.with(outerRetryPolicy, innerRetryPolicy).onComplete(e -> {
completedRef.set(e);
waiter.resume();
}).runAsync(ctx -> {
if (ctx.isFirstAttempt())
throw new IllegalStateException();
else
futureRef.get().cancel(false);
}));
// Then
assertThrows(() -> futureRef.get().get(1, TimeUnit.SECONDS), CancellationException.class);
waiter.await(1000);
assertNull(completedRef.get().getResult());
assertTrue(completedRef.get().getException() instanceof CancellationException);
assertEquals(outerRetryStats.failedAttemptCount, 0);
assertEquals(innerRetryStats.failedAttemptCount, 1);
}
use of dev.failsafe.event.ExecutionCompletedEvent in project failsafe by jhalterman.
the class RetrofitTesting method test.
private <T> void test(FailsafeExecutor<Response<T>> failsafe, Call<T> when, Then<Response<T>> then, int expectedStatus, T expectedResult, Class<? extends Throwable>[] expectedExceptions) {
AtomicReference<CompletableFuture<Response<T>>> futureRef = new AtomicReference<>();
AtomicReference<ExecutionCompletedEvent<Response<T>>> completedEventRef = new AtomicReference<>();
Waiter completionListenerWaiter = new Waiter();
EventListener<ExecutionCompletedEvent<Response<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);
failsafe.onComplete(setCompletedEventFn);
Runnable postTestFn = () -> {
ignoreExceptions(() -> completionListenerWaiter.await(5000));
ExecutionCompletedEvent<Response<T>> completedEvent = completedEventRef.get();
if (expectedExceptions == null) {
assertEquals(completedEvent.getResult().code(), expectedStatus);
assertNull(completedEvent.getException());
} else {
assertNull(completedEvent.getResult());
assertMatches(completedEvent.getException(), expectedExceptions);
}
if (then != null)
then.accept(futureRef.get(), completedEvent);
};
Consumer<Response<T>> assertResult = response -> {
T result = unwrapExceptions(response::body);
assertEquals(result, expectedResult);
assertEquals(response.code(), expectedStatus);
};
// Run sync test and assert result
System.out.println("\nRunning sync test");
FailsafeCall<T> failsafeCall = FailsafeCall.with(failsafe).compose(when);
if (expectedExceptions == null) {
assertResult.accept(unwrapExceptions(failsafeCall::execute));
} else {
assertThrows(failsafeCall::execute, expectedExceptions);
}
postTestFn.run();
if (expectedExInner.length > 0)
expected.add(0, ExecutionException.class);
// Run async test and assert result
System.out.println("\nRunning async test");
failsafeCall = failsafeCall.clone();
CompletableFuture<Response<T>> future = failsafeCall.executeAsync();
futureRef.set(future);
if (expectedExInner.length == 0) {
assertResult.accept(unwrapExceptions(future::get));
} else {
assertThrowsSup(future::get, expected);
}
postTestFn.run();
}
Aggregations