13c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerpackage com.squareup.okhttp;
23c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
33c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.ArrayList;
43c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.Arrays;
53c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.Iterator;
63c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.List;
73c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.concurrent.AbstractExecutorService;
83c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.concurrent.TimeUnit;
93c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport org.junit.Before;
103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport org.junit.Test;
113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static org.junit.Assert.assertEquals;
133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static org.junit.Assert.fail;
143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerpublic final class DispatcherTest {
163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  RecordingExecutor executor = new RecordingExecutor();
173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  RecordingReceiver receiver = new RecordingReceiver();
183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  Dispatcher dispatcher = new Dispatcher(executor);
193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  OkHttpClient client = new OkHttpClient().setDispatcher(dispatcher);
203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Before public void setUp() throws Exception {
223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    dispatcher.setMaxRequests(20);
233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    dispatcher.setMaxRequestsPerHost(10);
243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void maxRequestsZero() throws Exception {
273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    try {
283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      dispatcher.setMaxRequests(0);
293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      fail();
303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    } catch (IllegalArgumentException expected) {
313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void maxPerHostZero() throws Exception {
353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    try {
363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      dispatcher.setMaxRequestsPerHost(0);
373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      fail();
383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    } catch (IllegalArgumentException expected) {
393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void enqueuedJobsRunImmediately() throws Exception {
433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    client.enqueue(newRequest("http://a/1"), receiver);
443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    executor.assertJobs("http://a/1");
453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void maxRequestsEnforced() throws Exception {
483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    dispatcher.setMaxRequests(3);
493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    client.enqueue(newRequest("http://a/1"), receiver);
503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    client.enqueue(newRequest("http://a/2"), receiver);
513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    client.enqueue(newRequest("http://b/1"), receiver);
523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    client.enqueue(newRequest("http://b/2"), receiver);
533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    executor.assertJobs("http://a/1", "http://a/2", "http://b/1");
543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void maxPerHostEnforced() throws Exception {
573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    dispatcher.setMaxRequestsPerHost(2);
583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    client.enqueue(newRequest("http://a/1"), receiver);
593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    client.enqueue(newRequest("http://a/2"), receiver);
603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    client.enqueue(newRequest("http://a/3"), receiver);
613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    executor.assertJobs("http://a/1", "http://a/2");
623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void increasingMaxRequestsPromotesJobsImmediately() throws Exception {
653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    dispatcher.setMaxRequests(2);
663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    client.enqueue(newRequest("http://a/1"), receiver);
673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    client.enqueue(newRequest("http://b/1"), receiver);
683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    client.enqueue(newRequest("http://c/1"), receiver);
693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    client.enqueue(newRequest("http://a/2"), receiver);
703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    client.enqueue(newRequest("http://b/2"), receiver);
713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    dispatcher.setMaxRequests(4);
723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    executor.assertJobs("http://a/1", "http://b/1", "http://c/1", "http://a/2");
733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void increasingMaxPerHostPromotesJobsImmediately() throws Exception {
763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    dispatcher.setMaxRequestsPerHost(2);
773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    client.enqueue(newRequest("http://a/1"), receiver);
783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    client.enqueue(newRequest("http://a/2"), receiver);
793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    client.enqueue(newRequest("http://a/3"), receiver);
803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    client.enqueue(newRequest("http://a/4"), receiver);
813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    client.enqueue(newRequest("http://a/5"), receiver);
823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    dispatcher.setMaxRequestsPerHost(4);
833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    executor.assertJobs("http://a/1", "http://a/2", "http://a/3", "http://a/4");
843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void oldJobFinishesNewJobCanRunDifferentHost() throws Exception {
873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    dispatcher.setMaxRequests(1);
883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    client.enqueue(newRequest("http://a/1"), receiver);
893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    client.enqueue(newRequest("http://b/1"), receiver);
903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    executor.finishJob("http://a/1");
913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    executor.assertJobs("http://b/1");
923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void oldJobFinishesNewJobWithSameHostStarts() throws Exception {
953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    dispatcher.setMaxRequests(2);
963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    dispatcher.setMaxRequestsPerHost(1);
973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    client.enqueue(newRequest("http://a/1"), receiver);
983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    client.enqueue(newRequest("http://b/1"), receiver);
993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    client.enqueue(newRequest("http://b/2"), receiver);
1003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    client.enqueue(newRequest("http://a/2"), receiver);
1013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    executor.finishJob("http://a/1");
1023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    executor.assertJobs("http://b/1", "http://a/2");
1033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
1043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void oldJobFinishesNewJobCantRunDueToHostLimit() throws Exception {
1063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    dispatcher.setMaxRequestsPerHost(1);
1073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    client.enqueue(newRequest("http://a/1"), receiver);
1083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    client.enqueue(newRequest("http://b/1"), receiver);
1093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    client.enqueue(newRequest("http://a/2"), receiver);
1103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    executor.finishJob("http://b/1");
1113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    executor.assertJobs("http://a/1");
1123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
1133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void cancelingReadyJobPreventsItFromStarting() throws Exception {
1153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    dispatcher.setMaxRequestsPerHost(1);
1163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    client.enqueue(newRequest("http://a/1"), receiver);
1173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    client.enqueue(newRequest("http://a/2", "tag1"), receiver);
1183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    dispatcher.cancel("tag1");
1193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    executor.finishJob("http://a/1");
1203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    executor.assertJobs();
1213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
1223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Test public void cancelingRunningJobTakesNoEffectUntilJobFinishes() throws Exception {
1243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    dispatcher.setMaxRequests(1);
1253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    client.enqueue(newRequest("http://a/1", "tag1"), receiver);
1263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    client.enqueue(newRequest("http://a/2"), receiver);
1273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    dispatcher.cancel("tag1");
1283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    executor.assertJobs("http://a/1");
1293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    executor.finishJob("http://a/1");
1303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    executor.assertJobs("http://a/2");
1313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
1323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  class RecordingExecutor extends AbstractExecutorService {
1343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    private List<Job> jobs = new ArrayList<Job>();
1353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    @Override public void execute(Runnable command) {
1373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      jobs.add((Job) command);
1383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
1393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    public void assertJobs(String... expectedUrls) {
1413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      List<String> actualUrls = new ArrayList<String>();
1423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      for (Job job : jobs) {
1433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        actualUrls.add(job.request().urlString());
1443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
1453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      assertEquals(Arrays.asList(expectedUrls), actualUrls);
1463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
1473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    public void finishJob(String url) {
1493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      for (Iterator<Job> i = jobs.iterator(); i.hasNext(); ) {
1503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        Job job = i.next();
1513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        if (job.request().urlString().equals(url)) {
1523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          i.remove();
1533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          dispatcher.finished(job);
1543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          return;
1553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        }
1563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      }
1573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      throw new AssertionError("No such job: " + url);
1583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
1593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    @Override public void shutdown() {
1613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      throw new UnsupportedOperationException();
1623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
1633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    @Override public List<Runnable> shutdownNow() {
1653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      throw new UnsupportedOperationException();
1663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
1673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    @Override public boolean isShutdown() {
1693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      throw new UnsupportedOperationException();
1703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
1713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    @Override public boolean isTerminated() {
1733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      throw new UnsupportedOperationException();
1743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
1753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    @Override public boolean awaitTermination(long timeout, TimeUnit unit)
1773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        throws InterruptedException {
1783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      throw new UnsupportedOperationException();
1793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
1803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
1813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private Request newRequest(String url) {
1833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return new Request.Builder().url(url).build();
1843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
1853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private Request newRequest(String url, String tag) {
1873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return new Request.Builder().url(url).tag(tag).build();
1883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
1893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller}
190