package com.squareup.okhttp;
import co.paralleluniverse.fibers.okhttp.test.utils.FiberOkHttpClientTestWrapper;
import co.paralleluniverse.fibers.okhttp.test.utils.original.RecordingCallback;
import com.squareup.okhttp.Call.AsyncCall;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.AbstractExecutorService;
import java.util.concurrent.TimeUnit;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
public final class DispatcherTest {
RecordingExecutor executor = new RecordingExecutor();
RecordingCallback callback = new RecordingCallback();
Dispatcher dispatcher = new Dispatcher(executor);
OkHttpClient client = new FiberOkHttpClientTestWrapper().setDispatcher(dispatcher);
@Before public void setUp() throws Exception {
dispatcher.setMaxRequests(20);
dispatcher.setMaxRequestsPerHost(10);
}
@Test public void maxRequestsZero() throws Exception {
try {
dispatcher.setMaxRequests(0);
fail();
} catch (IllegalArgumentException expected) {
}
}
@Test public void maxPerHostZero() throws Exception {
try {
dispatcher.setMaxRequestsPerHost(0);
fail();
} catch (IllegalArgumentException expected) {
}
}
@Test public void enqueuedJobsRunImmediately() throws Exception {
client.newCall(newRequest("http://a/1")).enqueue(callback);
executor.assertJobs("http://a/1");
}
@Test public void maxRequestsEnforced() throws Exception {
dispatcher.setMaxRequests(3);
client.newCall(newRequest("http://a/1")).enqueue(callback);
client.newCall(newRequest("http://a/2")).enqueue(callback);
client.newCall(newRequest("http://b/1")).enqueue(callback);
client.newCall(newRequest("http://b/2")).enqueue(callback);
executor.assertJobs("http://a/1", "http://a/2", "http://b/1");
}
@Test public void maxPerHostEnforced() throws Exception {
dispatcher.setMaxRequestsPerHost(2);
client.newCall(newRequest("http://a/1")).enqueue(callback);
client.newCall(newRequest("http://a/2")).enqueue(callback);
client.newCall(newRequest("http://a/3")).enqueue(callback);
executor.assertJobs("http://a/1", "http://a/2");
}
@Test public void increasingMaxRequestsPromotesJobsImmediately() throws Exception {
dispatcher.setMaxRequests(2);
client.newCall(newRequest("http://a/1")).enqueue(callback);
client.newCall(newRequest("http://b/1")).enqueue(callback);
client.newCall(newRequest("http://c/1")).enqueue(callback);
client.newCall(newRequest("http://a/2")).enqueue(callback);
client.newCall(newRequest("http://b/2")).enqueue(callback);
dispatcher.setMaxRequests(4);
executor.assertJobs("http://a/1", "http://b/1", "http://c/1", "http://a/2");
}
@Test public void increasingMaxPerHostPromotesJobsImmediately() throws Exception {
dispatcher.setMaxRequestsPerHost(2);
client.newCall(newRequest("http://a/1")).enqueue(callback);
client.newCall(newRequest("http://a/2")).enqueue(callback);
client.newCall(newRequest("http://a/3")).enqueue(callback);
client.newCall(newRequest("http://a/4")).enqueue(callback);
client.newCall(newRequest("http://a/5")).enqueue(callback);
dispatcher.setMaxRequestsPerHost(4);
executor.assertJobs("http://a/1", "http://a/2", "http://a/3", "http://a/4");
}
@Test public void oldJobFinishesNewJobCanRunDifferentHost() throws Exception {
dispatcher.setMaxRequests(1);
client.newCall(newRequest("http://a/1")).enqueue(callback);
client.newCall(newRequest("http://b/1")).enqueue(callback);
executor.finishJob("http://a/1");
executor.assertJobs("http://b/1");
}
@Test public void oldJobFinishesNewJobWithSameHostStarts() throws Exception {
dispatcher.setMaxRequests(2);
dispatcher.setMaxRequestsPerHost(1);
client.newCall(newRequest("http://a/1")).enqueue(callback);
client.newCall(newRequest("http://b/1")).enqueue(callback);
client.newCall(newRequest("http://b/2")).enqueue(callback);
client.newCall(newRequest("http://a/2")).enqueue(callback);
executor.finishJob("http://a/1");
executor.assertJobs("http://b/1", "http://a/2");
}
@Test public void oldJobFinishesNewJobCantRunDueToHostLimit() throws Exception {
dispatcher.setMaxRequestsPerHost(1);
client.newCall(newRequest("http://a/1")).enqueue(callback);
client.newCall(newRequest("http://b/1")).enqueue(callback);
client.newCall(newRequest("http://a/2")).enqueue(callback);
executor.finishJob("http://b/1");
executor.assertJobs("http://a/1");
}
@Test public void cancelingRunningJobTakesNoEffectUntilJobFinishes() throws Exception {
dispatcher.setMaxRequests(1);
client.newCall(newRequest("http://a/1", "tag1")).enqueue(callback);
client.newCall(newRequest("http://a/2")).enqueue(callback);
dispatcher.cancel("tag1");
executor.assertJobs("http://a/1");
executor.finishJob("http://a/1");
executor.assertJobs("http://a/2");
}
class RecordingExecutor extends AbstractExecutorService {
private List<AsyncCall> calls = new ArrayList<>();
@Override public void execute(Runnable command) {
calls.add((AsyncCall) command);
}
public void assertJobs(String... expectedUrls) {
List<String> actualUrls = new ArrayList<>();
for (AsyncCall call : calls) {
actualUrls.add(call.request().urlString());
}
assertEquals(Arrays.asList(expectedUrls), actualUrls);
}
public void finishJob(String url) {
for (Iterator<AsyncCall> i = calls.iterator(); i.hasNext(); ) {
AsyncCall call = i.next();
if (call.request().urlString().equals(url)) {
i.remove();
dispatcher.finished(call);
return;
}
}
throw new AssertionError("No such job: " + url);
}
@Override public void shutdown() {
throw new UnsupportedOperationException();
}
@Override public List<Runnable> shutdownNow() {
throw new UnsupportedOperationException();
}
@Override public boolean isShutdown() {
throw new UnsupportedOperationException();
}
@Override public boolean isTerminated() {
throw new UnsupportedOperationException();
}
@Override public boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException {
throw new UnsupportedOperationException();
}
}
private Request newRequest(String url) {
return new Request.Builder().url(url).build();
}
private Request newRequest(String url, String tag) {
return new Request.Builder().url(url).tag(tag).build();
}
}