1package com.squareup.okhttp; 2 3import java.util.ArrayList; 4import java.util.Arrays; 5import java.util.Iterator; 6import java.util.List; 7import java.util.concurrent.AbstractExecutorService; 8import java.util.concurrent.TimeUnit; 9import org.junit.Before; 10import org.junit.Test; 11 12import static org.junit.Assert.assertEquals; 13import static org.junit.Assert.fail; 14 15public final class DispatcherTest { 16 RecordingExecutor executor = new RecordingExecutor(); 17 RecordingReceiver receiver = new RecordingReceiver(); 18 Dispatcher dispatcher = new Dispatcher(executor); 19 OkHttpClient client = new OkHttpClient().setDispatcher(dispatcher); 20 21 @Before public void setUp() throws Exception { 22 dispatcher.setMaxRequests(20); 23 dispatcher.setMaxRequestsPerHost(10); 24 } 25 26 @Test public void maxRequestsZero() throws Exception { 27 try { 28 dispatcher.setMaxRequests(0); 29 fail(); 30 } catch (IllegalArgumentException expected) { 31 } 32 } 33 34 @Test public void maxPerHostZero() throws Exception { 35 try { 36 dispatcher.setMaxRequestsPerHost(0); 37 fail(); 38 } catch (IllegalArgumentException expected) { 39 } 40 } 41 42 @Test public void enqueuedJobsRunImmediately() throws Exception { 43 client.enqueue(newRequest("http://a/1"), receiver); 44 executor.assertJobs("http://a/1"); 45 } 46 47 @Test public void maxRequestsEnforced() throws Exception { 48 dispatcher.setMaxRequests(3); 49 client.enqueue(newRequest("http://a/1"), receiver); 50 client.enqueue(newRequest("http://a/2"), receiver); 51 client.enqueue(newRequest("http://b/1"), receiver); 52 client.enqueue(newRequest("http://b/2"), receiver); 53 executor.assertJobs("http://a/1", "http://a/2", "http://b/1"); 54 } 55 56 @Test public void maxPerHostEnforced() throws Exception { 57 dispatcher.setMaxRequestsPerHost(2); 58 client.enqueue(newRequest("http://a/1"), receiver); 59 client.enqueue(newRequest("http://a/2"), receiver); 60 client.enqueue(newRequest("http://a/3"), receiver); 61 executor.assertJobs("http://a/1", "http://a/2"); 62 } 63 64 @Test public void increasingMaxRequestsPromotesJobsImmediately() throws Exception { 65 dispatcher.setMaxRequests(2); 66 client.enqueue(newRequest("http://a/1"), receiver); 67 client.enqueue(newRequest("http://b/1"), receiver); 68 client.enqueue(newRequest("http://c/1"), receiver); 69 client.enqueue(newRequest("http://a/2"), receiver); 70 client.enqueue(newRequest("http://b/2"), receiver); 71 dispatcher.setMaxRequests(4); 72 executor.assertJobs("http://a/1", "http://b/1", "http://c/1", "http://a/2"); 73 } 74 75 @Test public void increasingMaxPerHostPromotesJobsImmediately() throws Exception { 76 dispatcher.setMaxRequestsPerHost(2); 77 client.enqueue(newRequest("http://a/1"), receiver); 78 client.enqueue(newRequest("http://a/2"), receiver); 79 client.enqueue(newRequest("http://a/3"), receiver); 80 client.enqueue(newRequest("http://a/4"), receiver); 81 client.enqueue(newRequest("http://a/5"), receiver); 82 dispatcher.setMaxRequestsPerHost(4); 83 executor.assertJobs("http://a/1", "http://a/2", "http://a/3", "http://a/4"); 84 } 85 86 @Test public void oldJobFinishesNewJobCanRunDifferentHost() throws Exception { 87 dispatcher.setMaxRequests(1); 88 client.enqueue(newRequest("http://a/1"), receiver); 89 client.enqueue(newRequest("http://b/1"), receiver); 90 executor.finishJob("http://a/1"); 91 executor.assertJobs("http://b/1"); 92 } 93 94 @Test public void oldJobFinishesNewJobWithSameHostStarts() throws Exception { 95 dispatcher.setMaxRequests(2); 96 dispatcher.setMaxRequestsPerHost(1); 97 client.enqueue(newRequest("http://a/1"), receiver); 98 client.enqueue(newRequest("http://b/1"), receiver); 99 client.enqueue(newRequest("http://b/2"), receiver); 100 client.enqueue(newRequest("http://a/2"), receiver); 101 executor.finishJob("http://a/1"); 102 executor.assertJobs("http://b/1", "http://a/2"); 103 } 104 105 @Test public void oldJobFinishesNewJobCantRunDueToHostLimit() throws Exception { 106 dispatcher.setMaxRequestsPerHost(1); 107 client.enqueue(newRequest("http://a/1"), receiver); 108 client.enqueue(newRequest("http://b/1"), receiver); 109 client.enqueue(newRequest("http://a/2"), receiver); 110 executor.finishJob("http://b/1"); 111 executor.assertJobs("http://a/1"); 112 } 113 114 @Test public void cancelingReadyJobPreventsItFromStarting() throws Exception { 115 dispatcher.setMaxRequestsPerHost(1); 116 client.enqueue(newRequest("http://a/1"), receiver); 117 client.enqueue(newRequest("http://a/2", "tag1"), receiver); 118 dispatcher.cancel("tag1"); 119 executor.finishJob("http://a/1"); 120 executor.assertJobs(); 121 } 122 123 @Test public void cancelingRunningJobTakesNoEffectUntilJobFinishes() throws Exception { 124 dispatcher.setMaxRequests(1); 125 client.enqueue(newRequest("http://a/1", "tag1"), receiver); 126 client.enqueue(newRequest("http://a/2"), receiver); 127 dispatcher.cancel("tag1"); 128 executor.assertJobs("http://a/1"); 129 executor.finishJob("http://a/1"); 130 executor.assertJobs("http://a/2"); 131 } 132 133 class RecordingExecutor extends AbstractExecutorService { 134 private List<Job> jobs = new ArrayList<Job>(); 135 136 @Override public void execute(Runnable command) { 137 jobs.add((Job) command); 138 } 139 140 public void assertJobs(String... expectedUrls) { 141 List<String> actualUrls = new ArrayList<String>(); 142 for (Job job : jobs) { 143 actualUrls.add(job.request().urlString()); 144 } 145 assertEquals(Arrays.asList(expectedUrls), actualUrls); 146 } 147 148 public void finishJob(String url) { 149 for (Iterator<Job> i = jobs.iterator(); i.hasNext(); ) { 150 Job job = i.next(); 151 if (job.request().urlString().equals(url)) { 152 i.remove(); 153 dispatcher.finished(job); 154 return; 155 } 156 } 157 throw new AssertionError("No such job: " + url); 158 } 159 160 @Override public void shutdown() { 161 throw new UnsupportedOperationException(); 162 } 163 164 @Override public List<Runnable> shutdownNow() { 165 throw new UnsupportedOperationException(); 166 } 167 168 @Override public boolean isShutdown() { 169 throw new UnsupportedOperationException(); 170 } 171 172 @Override public boolean isTerminated() { 173 throw new UnsupportedOperationException(); 174 } 175 176 @Override public boolean awaitTermination(long timeout, TimeUnit unit) 177 throws InterruptedException { 178 throw new UnsupportedOperationException(); 179 } 180 } 181 182 private Request newRequest(String url) { 183 return new Request.Builder().url(url).build(); 184 } 185 186 private Request newRequest(String url, String tag) { 187 return new Request.Builder().url(url).tag(tag).build(); 188 } 189} 190