/*
* Copyright (C) 2015 Square, Inc.
*
* 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 retrofit2;
import com.google.common.reflect.TypeToken;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.List;
import java.util.concurrent.Executor;
import okhttp3.Request;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
@SuppressWarnings("unchecked")
public final class ExecutorCallAdapterFactoryTest {
private static final Annotation[] NO_ANNOTATIONS = new Annotation[0];
private final Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://localhost:1")
.build();
private final Callback<String> callback = mock(Callback.class);
private final Executor callbackExecutor = spy(new Executor() {
@Override public void execute(Runnable runnable) {
runnable.run();
}
});
private final CallAdapter.Factory factory = new ExecutorCallAdapterFactory(callbackExecutor);
@Test public void rawTypeThrows() {
try {
factory.get(Call.class, NO_ANNOTATIONS, retrofit);
fail();
} catch (IllegalArgumentException e) {
assertThat(e).hasMessage("Call return type must be parameterized as Call<Foo> or Call<? extends Foo>");
}
}
@Test public void responseType() {
Type classType = new TypeToken<Call<String>>() {}.getType();
assertThat(factory.get(classType, NO_ANNOTATIONS, retrofit).responseType())
.isEqualTo(String.class);
Type wilcardType = new TypeToken<Call<? extends String>>() {}.getType();
assertThat(factory.get(wilcardType, NO_ANNOTATIONS, retrofit).responseType())
.isEqualTo(String.class);
Type genericType = new TypeToken<Call<List<String>>>() {}.getType();
assertThat(factory.get(genericType, NO_ANNOTATIONS, retrofit).responseType())
.isEqualTo(new TypeToken<List<String>>() {}.getType());
}
@Test public void adaptedCallExecute() throws IOException {
Type returnType = new TypeToken<Call<String>>() {}.getType();
CallAdapter<String, Call<String>> adapter =
(CallAdapter<String, Call<String>>) factory.get(returnType, NO_ANNOTATIONS, retrofit);
final Response<String> response = Response.success("Hi");
Call<String> call = adapter.adapt(new EmptyCall() {
@Override public Response<String> execute() throws IOException {
return response;
}
});
assertThat(call.execute()).isSameAs(response);
}
@Test public void adaptedCallEnqueueUsesExecutorForSuccessCallback() {
Type returnType = new TypeToken<Call<String>>() {}.getType();
CallAdapter<String, Call<String>> adapter =
(CallAdapter<String, Call<String>>) factory.get(returnType, NO_ANNOTATIONS, retrofit);
final Response<String> response = Response.success("Hi");
EmptyCall originalCall = new EmptyCall() {
@Override public void enqueue(Callback<String> callback) {
callback.onResponse(this, response);
}
};
Call<String> call = adapter.adapt(originalCall);
call.enqueue(callback);
verify(callbackExecutor).execute(any(Runnable.class));
verify(callback).onResponse(call, response);
}
@Test public void adaptedCallEnqueueUsesExecutorForFailureCallback() {
Type returnType = new TypeToken<Call<String>>() {}.getType();
CallAdapter<String, Call<String>> adapter =
(CallAdapter<String, Call<String>>) factory.get(returnType, NO_ANNOTATIONS, retrofit);
final Throwable throwable = new IOException();
EmptyCall originalCall = new EmptyCall() {
@Override public void enqueue(Callback<String> callback) {
callback.onFailure(this, throwable);
}
};
Call<String> call = adapter.adapt(originalCall);
call.enqueue(callback);
verify(callbackExecutor).execute(any(Runnable.class));
verifyNoMoreInteractions(callbackExecutor);
verify(callback).onFailure(call, throwable);
verifyNoMoreInteractions(callback);
}
@Test public void adaptedCallCloneDeepCopy() {
Type returnType = new TypeToken<Call<String>>() {}.getType();
CallAdapter<String, Call<String>> adapter =
(CallAdapter<String, Call<String>>) factory.get(returnType, NO_ANNOTATIONS, retrofit);
Call<String> delegate = mock(Call.class);
Call<String> call = adapter.adapt(delegate);
Call<String> cloned = call.clone();
assertThat(cloned).isNotSameAs(call);
verify(delegate).clone();
verifyNoMoreInteractions(delegate);
}
@Test public void adaptedCallCancel() {
Type returnType = new TypeToken<Call<String>>() {}.getType();
CallAdapter<String, Call<String>> adapter =
(CallAdapter<String, Call<String>>) factory.get(returnType, NO_ANNOTATIONS, retrofit);
Call<String> delegate = mock(Call.class);
Call<String> call = adapter.adapt(delegate);
call.cancel();
verify(delegate).cancel();
verifyNoMoreInteractions(delegate);
}
static class EmptyCall implements Call<String> {
@Override public void enqueue(Callback<String> callback) {
throw new UnsupportedOperationException();
}
@Override public boolean isExecuted() {
return false;
}
@Override public Response<String> execute() throws IOException {
throw new UnsupportedOperationException();
}
@Override public void cancel() {
throw new UnsupportedOperationException();
}
@Override public boolean isCanceled() {
return false;
}
@Override public Call<String> clone() {
throw new UnsupportedOperationException();
}
@Override public Request request() {
throw new UnsupportedOperationException();
}
}
}