Search in sources :

Example 1 with FailsafeException

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));
}
Also used : Lists(dev.failsafe.internal.util.Lists) ExecutionCompletedEvent(dev.failsafe.event.ExecutionCompletedEvent) Arrays(java.util.Arrays) FailsafeExecutor(dev.failsafe.FailsafeExecutor) Assert.assertNull(org.testng.Assert.assertNull) java.util.concurrent(java.util.concurrent) InternalTesting(dev.failsafe.internal.InternalTesting) Assert.assertEquals(org.testng.Assert.assertEquals) FailsafeException(dev.failsafe.FailsafeException) AtomicReference(java.util.concurrent.atomic.AtomicReference) Function(java.util.function.Function) EventListener(dev.failsafe.event.EventListener) Consumer(java.util.function.Consumer) Waiter(net.jodah.concurrentunit.Waiter) List(java.util.List) RetryPolicy(dev.failsafe.RetryPolicy) LinkedList(java.util.LinkedList) Collections(java.util.Collections) dev.failsafe.function(dev.failsafe.function) FailsafeException(dev.failsafe.FailsafeException) AtomicReference(java.util.concurrent.atomic.AtomicReference) LinkedList(java.util.LinkedList) ExecutionCompletedEvent(dev.failsafe.event.ExecutionCompletedEvent) Function(java.util.function.Function) Waiter(net.jodah.concurrentunit.Waiter)

Aggregations

FailsafeException (dev.failsafe.FailsafeException)1 FailsafeExecutor (dev.failsafe.FailsafeExecutor)1 RetryPolicy (dev.failsafe.RetryPolicy)1 EventListener (dev.failsafe.event.EventListener)1 ExecutionCompletedEvent (dev.failsafe.event.ExecutionCompletedEvent)1 dev.failsafe.function (dev.failsafe.function)1 InternalTesting (dev.failsafe.internal.InternalTesting)1 Lists (dev.failsafe.internal.util.Lists)1 Arrays (java.util.Arrays)1 Collections (java.util.Collections)1 LinkedList (java.util.LinkedList)1 List (java.util.List)1 java.util.concurrent (java.util.concurrent)1 AtomicReference (java.util.concurrent.atomic.AtomicReference)1 Consumer (java.util.function.Consumer)1 Function (java.util.function.Function)1 Waiter (net.jodah.concurrentunit.Waiter)1 Assert.assertEquals (org.testng.Assert.assertEquals)1 Assert.assertNull (org.testng.Assert.assertNull)1