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