RetryableCalls { public CallAdapter.Factory getFactory() { return factory; }  CallAdapter.Factory getFactory(); void retryAllCalls(); void clearCalls();  }
@Test public void responseOnTheFirstTry() throws InterruptedException { RetryableCalls retryableCalls = new RetryableCalls(); MockWebServer server = new MockWebServer(); Retrofit retrofit = new Retrofit.Builder() .baseUrl(server.url("/")) .addCallAdapterFactory(retryableCalls.getFactory()) .addConverterFactory(new ToStringConverterFactory()) .build(); Service service = retrofit.create(Service.class); final AtomicReference<Response<String>> responseRef = new AtomicReference<>(); final CountDownLatch latch = new CountDownLatch(1); server.enqueue(new MockResponse().setBody("Hi")); RetryableCall<String> call = service.getString(); call.enqueue(new RetryableCallback<String>() { @Override public void onResponse(RetryableCall<String> call, Response<String> response) { responseRef.set(response); latch.countDown(); } @Override public void onFailure(RetryableCall<String> call, Throwable t) { throw new AssertionError(); } @Override public void onFailureButCanRetry(RetryableCall<String> call, IOException e) { throw new AssertionError(); } }); assertThat(latch.await(10, SECONDS)).isTrue(); Response<String> response = responseRef.get(); assertThat(response.isSuccessful()).isTrue(); assertThat(response.body()).isEqualTo("Hi"); }
@Test public void retryableCallIsExecuted() throws InterruptedException { RetryableCalls retryableCalls = new RetryableCalls(); MockWebServer server = new MockWebServer(); Retrofit retrofit = new Retrofit.Builder() .baseUrl(server.url("/")) .addCallAdapterFactory(retryableCalls.getFactory()) .addConverterFactory(new ToStringConverterFactory()) .build(); Service service = retrofit.create(Service.class); final CountDownLatch latch = new CountDownLatch(1); server.enqueue(new MockResponse().setBody("Hi")); RetryableCall<String> call = service.getString(); assertThat(call.isExecuted()).isFalse(); call.enqueue(new RetryableCallback<String>() { @Override public void onResponse(RetryableCall<String> call, Response<String> response) { latch.countDown(); } @Override public void onFailure(RetryableCall<String> call, Throwable t) { throw new AssertionError(); } @Override public void onFailureButCanRetry(RetryableCall<String> call, IOException e) { throw new AssertionError(); } }); assertThat(call.isExecuted()).isTrue(); assertThat(latch.await(10, SECONDS)).isTrue(); }
@Test public void retryableCallIsCanceledBeforeEnqueue() throws InterruptedException { RetryableCalls retryableCalls = new RetryableCalls(); MockWebServer server = new MockWebServer(); Retrofit retrofit = new Retrofit.Builder() .baseUrl(server.url("/")) .addCallAdapterFactory(retryableCalls.getFactory()) .addConverterFactory(new ToStringConverterFactory()) .build(); Service service = retrofit.create(Service.class); final CountDownLatch latch = new CountDownLatch(1); server.enqueue(new MockResponse().setBody("Hi")); RetryableCall<String> call = service.getString(); assertThat(call.isCanceled()).isFalse(); call.cancel(); assertThat(call.isCanceled()).isTrue(); call.enqueue(new RetryableCallback<String>() { @Override public void onResponse(RetryableCall<String> call, Response<String> response) { throw new AssertionError(); } @Override public void onFailure(RetryableCall<String> call, Throwable t) { latch.countDown(); } @Override public void onFailureButCanRetry(RetryableCall<String> call, IOException e) { throw new AssertionError(); } }); assertThat(latch.await(10, SECONDS)).isTrue(); }
@Test public void retryableCallIsCanceledAfterEnqueue() throws InterruptedException { RetryableCalls retryableCalls = new RetryableCalls(); MockWebServer server = new MockWebServer(); Retrofit retrofit = new Retrofit.Builder() .baseUrl(server.url("/")) .addCallAdapterFactory(retryableCalls.getFactory()) .addConverterFactory(new ToStringConverterFactory()) .build(); Service service = retrofit.create(Service.class); final CountDownLatch latch = new CountDownLatch(1); server.enqueue(new MockResponse().setBody("Hi").setBodyDelay(1, SECONDS)); RetryableCall<String> call = service.getString(); assertThat(call.isCanceled()).isFalse(); call.enqueue(new RetryableCallback<String>() { @Override public void onResponse(RetryableCall<String> call, Response<String> response) { throw new AssertionError(); } @Override public void onFailure(RetryableCall<String> call, Throwable t) { latch.countDown(); } @Override public void onFailureButCanRetry(RetryableCall<String> call, IOException e) { throw new AssertionError(); } }); call.cancel(); assertThat(call.isCanceled()).isTrue(); assertThat(latch.await(10, SECONDS)).isTrue(); }
@Test public void retryableCallMustBeParameterized() throws InterruptedException { RetryableCalls retryableCalls = new RetryableCalls(); MockWebServer server = new MockWebServer(); Retrofit retrofit = new Retrofit.Builder() .baseUrl(server.url("/")) .addCallAdapterFactory(retryableCalls.getFactory()) .addConverterFactory(new ToStringConverterFactory()) .build(); UnparameterizedService service = retrofit.create(UnparameterizedService.class); try { service.get(); fail(); } catch (IllegalArgumentException serviceMethodException) { assertThat(serviceMethodException.getCause()).hasMessageThat().isEqualTo( "RetryableCall return type must be parameterized " + "as RetryableCall<Foo> or RetryableCall<? extends Foo>"); } }
@Test public void cancelGoesToOnFailureWhenInFlight() throws InterruptedException { RetryableCalls retryableCalls = new RetryableCalls(); MockWebServer server = new MockWebServer(); Retrofit retrofit = new Retrofit.Builder() .baseUrl(server.url("/")) .addCallAdapterFactory(retryableCalls.getFactory()) .addConverterFactory(new ToStringConverterFactory()) .build(); Service service = retrofit.create(Service.class); final CountDownLatch failureLatch = new CountDownLatch(1); server.enqueue(new MockResponse().setBody("Hi").setBodyDelay(1, SECONDS)); RetryableCall<String> call = service.getString(); call.enqueue(new RetryableCallback<String>() { @Override public void onResponse(RetryableCall<String> call, Response<String> response) { throw new AssertionError(); } @Override public void onFailure(RetryableCall<String> call, Throwable t) { assertThat(t).isInstanceOf(IOException.class); assertThat(call.isCanceled()).isTrue(); failureLatch.countDown(); } @Override public void onFailureButCanRetry(RetryableCall<String> call, IOException e) { throw new AssertionError(); } }); call.cancel(); assertThat(failureLatch.await(10, SECONDS)).isTrue(); }
@Test public void cancelBetweenOnResponseAndExecutorRunGoesToOnFailure() throws InterruptedException { RetryableCalls retryableCalls = new RetryableCalls(); MockWebServer server = new MockWebServer(); Retrofit retrofit = new Retrofit.Builder() .baseUrl(server.url("/")) .callbackExecutor(new Executor() { @Override public void execute(Runnable command) { try { Thread.sleep(SECONDS.toMillis(2)); } catch (InterruptedException e) { throw new AssertionError(e); } command.run(); } }) .addCallAdapterFactory(retryableCalls.getFactory()) .addConverterFactory(new ToStringConverterFactory()) .build(); Service service = retrofit.create(Service.class); final CountDownLatch failureLatch = new CountDownLatch(1); server.enqueue(new MockResponse().setBody("Hi")); RetryableCall<String> call = service.getString(); call.enqueue(new RetryableCallback<String>() { @Override public void onResponse(RetryableCall<String> call, Response<String> response) { throw new AssertionError(); } @Override public void onFailure(RetryableCall<String> call, Throwable t) { assertThat(t).isInstanceOf(IOException.class); assertThat(call.isCanceled()).isTrue(); failureLatch.countDown(); } @Override public void onFailureButCanRetry(RetryableCall<String> call, IOException e) { throw new AssertionError(); } }); Thread.sleep(SECONDS.toMillis(1)); call.cancel(); assertThat(failureLatch.await(10, SECONDS)).isTrue(); }
@Test public void cancelBetweenOnFailureCanRetryAndExecutorRunGoesToOnFailure() throws InterruptedException { RetryableCalls retryableCalls = new RetryableCalls(); MockWebServer server = new MockWebServer(); Retrofit retrofit = new Retrofit.Builder() .baseUrl(server.url("/")) .callbackExecutor(new Executor() { @Override public void execute(Runnable command) { try { Thread.sleep(SECONDS.toMillis(2)); } catch (InterruptedException e) { throw new AssertionError(e); } command.run(); } }) .addCallAdapterFactory(retryableCalls.getFactory()) .addConverterFactory(new ToStringConverterFactory()) .build(); Service service = retrofit.create(Service.class); final CountDownLatch failureLatch = new CountDownLatch(1); server.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.DISCONNECT_AT_START)); RetryableCall<String> call = service.getString(); call.enqueue(new RetryableCallback<String>() { @Override public void onResponse(RetryableCall<String> call, Response<String> response) { throw new AssertionError(); } @Override public void onFailure(RetryableCall<String> call, Throwable t) { assertThat(t).isInstanceOf(IOException.class); assertThat(call.isCanceled()).isTrue(); failureLatch.countDown(); } @Override public void onFailureButCanRetry(RetryableCall<String> call, IOException e) { throw new AssertionError(); } }); Thread.sleep(SECONDS.toMillis(1)); call.cancel(); assertThat(failureLatch.await(10, SECONDS)).isTrue(); }
@Test public void cancelBetweenOnFailureAndExecutorRunDoesNothing() throws InterruptedException { RetryableCalls retryableCalls = new RetryableCalls(); MockWebServer server = new MockWebServer(); Retrofit retrofit = new Retrofit.Builder() .baseUrl(server.url("/")) .callbackExecutor(new Executor() { @Override public void execute(Runnable command) { try { Thread.sleep(SECONDS.toMillis(2)); } catch (InterruptedException e) { throw new AssertionError(e); } command.run(); } }) .addCallAdapterFactory(retryableCalls.getFactory()) .addConverterFactory(new Converter.Factory() { @Override public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) { return new Converter<ResponseBody, Object>() { @Override public Object convert(ResponseBody value) throws IOException { throw new RuntimeException("Broken!"); } }; } }) .build(); Service service = retrofit.create(Service.class); final CountDownLatch failureLatch = new CountDownLatch(1); server.enqueue(new MockResponse().setBody("Hi")); RetryableCall<String> call = service.getString(); call.enqueue(new RetryableCallback<String>() { @Override public void onResponse(RetryableCall<String> call, Response<String> response) { throw new AssertionError(); } @Override public void onFailure(RetryableCall<String> call, Throwable t) { RuntimeException runtimeException = (RuntimeException) t; assertThat(runtimeException).hasMessageThat().isEqualTo("Broken!"); assertThat(call.isCanceled()).isTrue(); failureLatch.countDown(); } @Override public void onFailureButCanRetry(RetryableCall<String> call, IOException e) { throw new AssertionError(); } }); Thread.sleep(SECONDS.toMillis(1)); call.cancel(); assertThat(failureLatch.await(10, SECONDS)).isTrue(); }
@Test public void callbackOnExecutorResponse() throws InterruptedException { RetryableCalls retryableCalls = new RetryableCalls(); MockWebServer server = new MockWebServer(); final AtomicInteger callbackExecutorRuns = new AtomicInteger(); Retrofit retrofit = new Retrofit.Builder() .baseUrl(server.url("/")) .callbackExecutor(new Executor() { @Override public void execute(Runnable command) { callbackExecutorRuns.incrementAndGet(); command.run(); } }) .addCallAdapterFactory(retryableCalls.getFactory()) .addConverterFactory(new ToStringConverterFactory()) .build(); Service service = retrofit.create(Service.class); final CountDownLatch responseLatch = new CountDownLatch(1); server.enqueue(new MockResponse().setBody("Hi")); RetryableCall<String> call = service.getString(); call.enqueue(new RetryableCallback<String>() { @Override public void onResponse(RetryableCall<String> call, Response<String> response) { responseLatch.countDown(); } @Override public void onFailure(RetryableCall<String> call, Throwable t) { throw new AssertionError(); } @Override public void onFailureButCanRetry(RetryableCall<String> call, IOException e) { throw new AssertionError(); } }); assertThat(responseLatch.await(10, SECONDS)).isTrue(); assertThat(callbackExecutorRuns.get()).isEqualTo(1); }
@Test public void callbackHasOriginalCallResponse() throws InterruptedException { RetryableCalls retryableCalls = new RetryableCalls(); MockWebServer server = new MockWebServer(); Retrofit retrofit = new Retrofit.Builder() .baseUrl(server.url("/")) .addCallAdapterFactory(retryableCalls.getFactory()) .addConverterFactory(new ToStringConverterFactory()) .build(); Service service = retrofit.create(Service.class); final CountDownLatch responseLatch = new CountDownLatch(1); server.enqueue(new MockResponse().setBody("Hi")); final AtomicReference<RetryableCall<String>> responseCallRef = new AtomicReference<>(); RetryableCall<String> call = service.getString(); call.enqueue(new RetryableCallback<String>() { @Override public void onResponse(RetryableCall<String> call, Response<String> response) { responseCallRef.set(call); responseLatch.countDown(); } @Override public void onFailure(RetryableCall<String> call, Throwable t) { throw new AssertionError(); } @Override public void onFailureButCanRetry(RetryableCall<String> call, IOException e) { throw new AssertionError(); } }); assertThat(responseLatch.await(10, SECONDS)).isTrue(); assertThat(responseCallRef.get()).isSameAs(call); }
@Test public void retryableCallRequest() throws InterruptedException { RetryableCalls retryableCalls = new RetryableCalls(); MockWebServer server = new MockWebServer(); Retrofit retrofit = new Retrofit.Builder() .baseUrl(server.url("/")) .addCallAdapterFactory(retryableCalls.getFactory()) .addConverterFactory(new ToStringConverterFactory()) .build(); Service service = retrofit.create(Service.class); RetryableCall<String> call = service.getString(); assertThat(call.request()).isNotNull(); }
@Test public void retryableCallClone() throws InterruptedException { RetryableCalls retryableCalls = new RetryableCalls(); MockWebServer server = new MockWebServer(); Retrofit retrofit = new Retrofit.Builder() .baseUrl(server.url("/")) .addCallAdapterFactory(retryableCalls.getFactory()) .addConverterFactory(new ToStringConverterFactory()) .build(); Service service = retrofit.create(Service.class); final CountDownLatch callLatch = new CountDownLatch(1); server.enqueue(new MockResponse().setBody("Hi")); RetryableCall<String> call = service.getString(); call.enqueue(new RetryableCallback<String>() { @Override public void onResponse(RetryableCall<String> call, Response<String> response) { callLatch.countDown(); } @Override public void onFailure(RetryableCall<String> call, Throwable t) { throw new AssertionError(); } @Override public void onFailureButCanRetry(RetryableCall<String> call, IOException e) { throw new AssertionError(); } }); assertThat(callLatch.await(10, SECONDS)).isTrue(); RetryableCall<String> clone = call.clone(); assertThat(clone).isNotSameAs(call); assertThat(clone.isExecuted()).isFalse(); final CountDownLatch cloneLatch = new CountDownLatch(1); server.enqueue(new MockResponse().setBody("Hi")); clone.enqueue(new RetryableCallback<String>() { @Override public void onResponse(RetryableCall<String> call, Response<String> response) { cloneLatch.countDown(); } @Override public void onFailure(RetryableCall<String> call, Throwable t) { throw new AssertionError(); } @Override public void onFailureButCanRetry(RetryableCall<String> call, IOException e) { throw new AssertionError(); } }); assertThat(cloneLatch.await(10, SECONDS)).isTrue(); }