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