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