Skip to content

Commit

Permalink
Add support for creating failed test call instances with any exception.
Browse files Browse the repository at this point in the history
  • Loading branch information
Warren Smith authored and JakeWharton committed Apr 16, 2018
1 parent 5c2f505 commit ce787ed
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 6 deletions.
28 changes: 22 additions & 6 deletions retrofit-mock/src/main/java/retrofit2/mock/Calls.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,20 @@ public static <T> Call<T> response(Response<T> response) {
return new FakeCall<>(response, null);
}

/** Creates a failed {@link Call} from {@code failure}. */
public static <T> Call<T> failure(IOException failure) {
// TODO delete this overload in Retrofit 3.0.
return new FakeCall<>(null, failure);
}

/**
* Creates a failed {@link Call} from {@code failure}.
* <p>
* Note: When invoking {@link Call#execute() execute()} on the returned {@link Call}, if
* {@code failure} is a {@link RuntimeException}, {@link Error}, or {@link IOException} subtype
* it is thrown directly. Otherwise it is "sneaky thrown" despite not being declared.
*/
public static <T> Call<T> failure(Throwable failure) {
return new FakeCall<>(null, failure);
}

Expand All @@ -52,11 +65,11 @@ private Calls() {

static final class FakeCall<T> implements Call<T> {
private final Response<T> response;
private final IOException error;
private final Throwable error;
private final AtomicBoolean canceled = new AtomicBoolean();
private final AtomicBoolean executed = new AtomicBoolean();

FakeCall(@Nullable Response<T> response, @Nullable IOException error) {
FakeCall(@Nullable Response<T> response, @Nullable Throwable error) {
if ((response == null) == (error == null)) {
throw new AssertionError("Only one of response or error can be set.");
}
Expand All @@ -74,7 +87,12 @@ static final class FakeCall<T> implements Call<T> {
if (response != null) {
return response;
}
throw error;
throw FakeCall.<Error>sneakyThrow2(error);
}

@SuppressWarnings("unchecked") // Intentionally abusing this feature.
private static <T extends Throwable> T sneakyThrow2(Throwable t) throws T {
throw (T) t;
}

@SuppressWarnings("ConstantConditions") // Guarding public API nullability.
Expand Down Expand Up @@ -131,10 +149,8 @@ private synchronized Call<T> getDelegate() {
if (delegate == null) {
try {
delegate = callable.call();
} catch (IOException e) {
delegate = failure(e);
} catch (Exception e) {
throw new IllegalStateException("Callable threw unrecoverable exception", e);
delegate = failure(e);
}
this.delegate = delegate;
}
Expand Down
31 changes: 31 additions & 0 deletions retrofit-mock/src/test/java/retrofit2/mock/CallsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,16 @@
*/
package retrofit2.mock;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.cert.CertificateException;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.Test;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
Expand Down Expand Up @@ -155,6 +158,19 @@ public final class CallsTest {
assertTrue(taco.isExecuted());
}

@Test public void failureExecuteCheckedException() {
CertificateException failure = new CertificateException("Hey");
Call<Object> taco = Calls.failure(failure);
assertFalse(taco.isExecuted());
try {
taco.execute();
fail();
} catch (Throwable e) {
assertSame(failure, e);
}
assertTrue(taco.isExecuted());
}

@Test public void failureEnqueue() {
IOException failure = new IOException("Hey");
Call<Object> taco = Calls.failure(failure);
Expand Down Expand Up @@ -260,4 +276,19 @@ public final class CallsTest {
});
assertSame(failure, failureRef.get());
}

@Test public void deferredThrowUncheckedExceptionEnqueue() {
final RuntimeException failure = new RuntimeException("Hey");
final AtomicReference<Throwable> failureRef = new AtomicReference<>();
Calls.failure(failure).enqueue(new Callback<Object>() {
@Override public void onResponse(Call<Object> call, Response<Object> response) {
fail();
}

@Override public void onFailure(Call<Object> call, Throwable t) {
failureRef.set(t);
}
});
assertSame(failure, failureRef.get());
}
}

0 comments on commit ce787ed

Please # to comment.