/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package net.jodah.failsafe;
import static net.jodah.failsafe.Testing.failures;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.when;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import net.jodah.failsafe.FailsafeConfigTest.ListenerCounter;
@Test
public class AsyncFailsafeConfigTest {
Service service = mock(Service.class);
ScheduledExecutorService executor;
ListenerCounter abort;
ListenerCounter complete;
ListenerCounter failedAttempt;
ListenerCounter failure;
ListenerCounter retriesExceeded;
ListenerCounter retry;
ListenerCounter success;
public interface Service {
boolean connect();
}
@BeforeMethod
void beforeMethod() {
executor = Executors.newScheduledThreadPool(2);
reset(service);
abort = new ListenerCounter();
complete = new ListenerCounter();
failedAttempt = new ListenerCounter();
failure = new ListenerCounter();
retriesExceeded = new ListenerCounter();
retry = new ListenerCounter();
success = new ListenerCounter();
}
@AfterMethod
void afterMethod() throws Throwable {
executor.shutdownNow();
executor.awaitTermination(5, TimeUnit.SECONDS);
}
<T> AsyncFailsafe<T> registerListeners(AsyncFailsafe<T> failsafe) {
failsafe.onAbortAsync(e -> abort.async(1));
failsafe.onAbortAsync((r, e) -> abort.async(2));
failsafe.onAbortAsync((r, e, c) -> abort.async(3));
abort.asyncListeners = 3;
failsafe.onCompleteAsync((e, r) -> complete.async(1));
failsafe.onCompleteAsync((e, r, c) -> complete.async(2));
complete.asyncListeners = 2;
failsafe.onFailedAttemptAsync(e -> failedAttempt.async(1));
failsafe.onFailedAttemptAsync((r, f) -> failedAttempt.async(2));
failsafe.onFailedAttemptAsync((r, f, c) -> failedAttempt.async(3, c));
failedAttempt.asyncListeners = 3;
failsafe.onFailureAsync(e -> failure.async(1));
failsafe.onFailureAsync((r, e) -> failure.async(2));
failsafe.onFailureAsync((r, e, c) -> failure.async(3));
failure.asyncListeners = 3;
failsafe.onRetriesExceededAsync(e -> retriesExceeded.async(1));
failsafe.onRetriesExceededAsync((r, f) -> retriesExceeded.async(2));
retriesExceeded.asyncListeners = 2;
failsafe.onRetryAsync(e -> retry.async(1));
failsafe.onRetryAsync((r, f) -> retry.async(2));
failsafe.onRetryAsync((r, f, c) -> retry.async(3, c));
retry.asyncListeners = 3;
failsafe.onSuccessAsync(r -> success.async(1));
failsafe.onSuccessAsync((r, c) -> success.async(2));
success.asyncListeners = 2;
return failsafe;
}
/**
* Asserts that listeners are called the expected number of times for a successful completion.
*/
public void testListenersForSuccess() throws Throwable {
Callable<Boolean> callable = () -> service.connect();
// Given - Fail twice then succeed
when(service.connect()).thenThrow(failures(2, new IllegalStateException())).thenReturn(false, false, true);
RetryPolicy retryPolicy = new RetryPolicy().retryWhen(false);
// When
registerListeners(Failsafe.with(retryPolicy).with(executor)).get(callable);
// Then
abort.assertEquals(0);
complete.assertEquals(1);
failedAttempt.assertEquals(4);
failure.assertEquals(0);
retriesExceeded.assertEquals(0);
retry.assertEquals(4);
success.assertEquals(1);
}
/**
* Asserts that listeners are called the expected number of times for an unhandled failure.
*/
public void testListenersForUnhandledFailure() throws Throwable {
Callable<Boolean> callable = () -> service.connect();
// Given - Fail 2 times then don't match policy
when(service.connect()).thenThrow(failures(2, new IllegalStateException()))
.thenThrow(IllegalArgumentException.class);
RetryPolicy retryPolicy = new RetryPolicy().retryOn(IllegalStateException.class).withMaxRetries(10);
// When
registerListeners(Failsafe.with(retryPolicy).with(executor)).get(callable);
// Then
abort.assertEquals(0);
complete.assertEquals(1);
failedAttempt.assertEquals(3);
failure.assertEquals(1);
retriesExceeded.assertEquals(0);
retry.assertEquals(2);
success.assertEquals(0);
}
/**
* Asserts that listeners are called the expected number of times when retries are exceeded.
*/
public void testListenersForRetriesExceeded() throws Throwable {
Callable<Boolean> callable = () -> service.connect();
// Given - Fail 4 times and exceed retries
when(service.connect()).thenThrow(failures(10, new IllegalStateException()));
RetryPolicy retryPolicy = new RetryPolicy().retryWhen(false).withMaxRetries(3);
// When
registerListeners(Failsafe.with(retryPolicy).with(executor)).get(callable);
// Then
abort.assertEquals(0);
complete.assertEquals(1);
failedAttempt.assertEquals(4);
failure.assertEquals(1);
retriesExceeded.assertEquals(1);
retry.assertEquals(3);
success.assertEquals(0);
}
/**
* Asserts that listeners are called the expected number of times for an aborted execution.
*/
public void testListenersForAbort() throws Throwable {
Callable<Boolean> callable = () -> service.connect();
// Given - Fail twice then abort
when(service.connect()).thenThrow(failures(3, new IllegalStateException()))
.thenThrow(new IllegalArgumentException());
RetryPolicy retryPolicy = new RetryPolicy().abortOn(IllegalArgumentException.class).withMaxRetries(3);
// When
Asserts.assertThrows(() -> registerListeners(Failsafe.with(retryPolicy).with(executor)).get(callable).get(),
ExecutionException.class, IllegalArgumentException.class);
// Then
abort.assertEquals(1);
complete.assertEquals(0);
failedAttempt.assertEquals(4);
failure.assertEquals(0);
retriesExceeded.assertEquals(0);
retry.assertEquals(3);
success.assertEquals(0);
}
}