1/* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.volley; 18 19import com.android.volley.Request.Priority; 20import com.android.volley.mock.MockNetwork; 21import com.android.volley.mock.MockRequest; 22import com.android.volley.toolbox.NoCache; 23import com.android.volley.utils.CacheTestUtils; 24import com.android.volley.utils.ImmediateResponseDelivery; 25 26import android.os.SystemClock; 27import android.test.InstrumentationTestCase; 28import android.test.UiThreadTest; 29import android.test.suitebuilder.annotation.LargeTest; 30 31import java.util.ArrayList; 32import java.util.List; 33import java.util.Random; 34import java.util.concurrent.Semaphore; 35import java.util.concurrent.TimeUnit; 36import java.util.concurrent.atomic.AtomicInteger; 37 38@LargeTest 39public class RequestQueueTest extends InstrumentationTestCase { 40 private ResponseDelivery mDelivery; 41 42 @Override 43 protected void setUp() throws Exception { 44 super.setUp(); 45 46 mDelivery = new ImmediateResponseDelivery(); 47 } 48 49 /** 50 * Make a list of requests with random priorities. 51 * @param count Number of requests to make 52 */ 53 private List<MockRequest> makeRequests(int count) { 54 Request.Priority[] allPriorities = Request.Priority.values(); 55 Random random = new Random(); 56 57 List<MockRequest> requests = new ArrayList<MockRequest>(); 58 for (int i = 0; i < count; i++) { 59 MockRequest request = new MockRequest(); 60 Request.Priority priority = allPriorities[random.nextInt(allPriorities.length)]; 61 request.setCacheKey(String.valueOf(i)); 62 request.setPriority(priority); 63 requests.add(request); 64 } 65 return requests; 66 } 67 68 @UiThreadTest 69 public void testAdd_requestProcessedInCorrectOrder() throws Exception { 70 int requestsToMake = 100; 71 72 OrderCheckingNetwork network = new OrderCheckingNetwork(); 73 RequestQueue queue = new RequestQueue(new NoCache(), network, 1, mDelivery); 74 75 for (Request<?> request : makeRequests(requestsToMake)) { 76 queue.add(request); 77 } 78 79 network.setExpectedRequests(requestsToMake); 80 queue.start(); 81 network.waitUntilExpectedDone(2000); // 2 seconds 82 queue.stop(); 83 } 84 85 public void testAdd_dedupeByCacheKey() throws Exception { 86 OrderCheckingNetwork network = new OrderCheckingNetwork(); 87 final AtomicInteger parsed = new AtomicInteger(); 88 final AtomicInteger delivered = new AtomicInteger(); 89 // Enqueue 2 requests with the same cache key. The first request takes 1.5s. Assert that the 90 // second request is only handled after the first one has been parsed and delivered. 91 DelayedRequest req1 = new DelayedRequest(1500, parsed, delivered); 92 DelayedRequest req2 = new DelayedRequest(0, parsed, delivered) { 93 @Override 94 protected Response<Object> parseNetworkResponse(NetworkResponse response) { 95 assertEquals(1, parsed.get()); // req1 must have been parsed. 96 assertEquals(1, delivered.get()); // req1 must have been parsed. 97 return super.parseNetworkResponse(response); 98 } 99 }; 100 network.setExpectedRequests(2); 101 RequestQueue queue = new RequestQueue(new NoCache(), network, 3, mDelivery); 102 queue.add(req1); 103 queue.add(req2); 104 queue.start(); 105 network.waitUntilExpectedDone(2000); 106 queue.stop(); 107 } 108 109 public void testCancelAll_onlyCorrectTag() throws Exception { 110 MockNetwork network = new MockNetwork(); 111 RequestQueue queue = new RequestQueue(new NoCache(), network, 3, mDelivery); 112 Object tagA = new Object(); 113 Object tagB = new Object(); 114 MockRequest req1 = new MockRequest(); 115 req1.setTag(tagA); 116 MockRequest req2 = new MockRequest(); 117 req2.setTag(tagB); 118 MockRequest req3 = new MockRequest(); 119 req3.setTag(tagA); 120 MockRequest req4 = new MockRequest(); 121 req4.setTag(tagA); 122 123 queue.add(req1); // A 124 queue.add(req2); // B 125 queue.add(req3); // A 126 queue.cancelAll(tagA); 127 queue.add(req4); // A 128 129 assertTrue(req1.cancel_called); // A cancelled 130 assertFalse(req2.cancel_called); // B not cancelled 131 assertTrue(req3.cancel_called); // A cancelled 132 assertFalse(req4.cancel_called); // A added after cancel not cancelled 133 } 134 135 private class OrderCheckingNetwork implements Network { 136 private Priority mLastPriority = Priority.IMMEDIATE; 137 private int mLastSequence = -1; 138 private Semaphore mSemaphore; 139 140 public void setExpectedRequests(int expectedRequests) { 141 // Leave one permit available so the waiter can find it. 142 expectedRequests--; 143 mSemaphore = new Semaphore(-expectedRequests); 144 } 145 146 public void waitUntilExpectedDone(long timeout) 147 throws InterruptedException, TimeoutError { 148 if (mSemaphore.tryAcquire(timeout, TimeUnit.MILLISECONDS) == false) { 149 throw new TimeoutError(); 150 } 151 } 152 153 @Override 154 public NetworkResponse performRequest(Request<?> request) { 155 Priority thisPriority = request.getPriority(); 156 int thisSequence = request.getSequence(); 157 158 int priorityDiff = thisPriority.compareTo(mLastPriority); 159 160 // Should never experience a higher priority after a lower priority 161 assertFalse(priorityDiff > 0); 162 163 // If we're not transitioning to a new priority block, check sequence numbers 164 if (priorityDiff == 0) { 165 assertTrue(thisSequence > mLastSequence); 166 } 167 mLastSequence = thisSequence; 168 mLastPriority = thisPriority; 169 170 mSemaphore.release(); 171 return new NetworkResponse(new byte[16]); 172 } 173 } 174 175 @UiThreadTest 176 @SuppressWarnings("deprecation") 177 public void testDrain_suppressesEarlierResponses() { 178 MockNetwork network = new MockNetwork(); 179 network.setDataToReturn(new byte[128]); 180 RequestQueue queue = new RequestQueue(new NoCache(), network, 4, mDelivery); 181 182 int requestsBefore = 40; 183 AtomicInteger parsedBefore = new AtomicInteger(); 184 AtomicInteger deliveredBefore = new AtomicInteger(); 185 186 // Start the queue and add a bunch of requests to it. 187 queue.start(); 188 for (int i = 0; i < requestsBefore; i++) { 189 // Make each request take 50 ms. 190 DelayedRequest request = new DelayedRequest(50, parsedBefore, deliveredBefore); 191 queue.add(request); 192 } 193 // Sleep for a jiffy to let some of the slow requests be scheduled. 194 SystemClock.sleep(5); 195 196 // Drain the queue. None of the original requests should be delivered 197 // because they are still waiting to parse. 198 queue.drain(); 199 200 // Add some requests afterward to make sure not only that the ones before 201 // the drain were not delivered, but that ones afterward are. 202 int requestsAfter = 10; 203 AtomicInteger parsedAfter = new AtomicInteger(); 204 AtomicInteger deliveredAfter = new AtomicInteger(); 205 for (int i = 0; i < requestsAfter; i++) { 206 // These ones are much faster... 207 DelayedRequest request = new DelayedRequest(1, parsedAfter, deliveredAfter); 208 queue.add(request); 209 } 210 211 // Wait up to 1 second to finish 212 for (int i = 0; i < 10; i++) { 213 if (deliveredAfter.get() != requestsAfter) { 214 SystemClock.sleep(100); 215 continue; 216 } 217 218 assertEquals(deliveredAfter.get(), requestsAfter); 219 // Make sure that all "after" requests were parsed 220 assertEquals(parsedAfter.get(), requestsAfter); 221 222 // Make sure that at least one of the "before" requests was parsed 223 assertTrue(parsedBefore.get() > 0); 224 // And that none of them are delivered 225 assertEquals(0, deliveredBefore.get()); 226 queue.stop(); 227 return; 228 } 229 230 fail("Timed out waiting for requests to complete"); 231 } 232 233 @UiThreadTest 234 @SuppressWarnings("deprecation") 235 public void testDrain_preservesUndrainable() { 236 MockNetwork network = new MockNetwork(); 237 network.setDataToReturn(new byte[128]); 238 RequestQueue queue = new RequestQueue(new NoCache(), network, 4, mDelivery); 239 240 // Make a bunch of requests 241 List<MockRequest> requests = makeRequests(50); 242 243 // Pick one to be invincible. 244 MockRequest invincible = requests.get(new Random().nextInt(requests.size())); 245 invincible.setDrainable(false); 246 247 // Add them all to the queue. 248 for (MockRequest request : requests) { 249 queue.add(request); 250 } 251 252 // Drain the queue. 253 queue.drain(); 254 255 // Start the queue. 256 queue.start(); 257 258 // Wait up to 1 second to finish 259 for (int i = 0; i < 10; i++) { 260 if (!invincible.parseResponse_called) { 261 SystemClock.sleep(100); 262 continue; 263 } 264 // If parseResponse got called, that's good enough 265 queue.stop(); 266 return; 267 } 268 269 fail("Undrainable request should have been parsed"); 270 } 271 272 private class DelayedRequest extends Request<Object> { 273 private final long mDelayMillis; 274 private final AtomicInteger mParsedCount; 275 private final AtomicInteger mDeliveredCount; 276 277 public DelayedRequest(long delayMillis, AtomicInteger parsed, AtomicInteger delivered) { 278 super("http://buganizer/", null); 279 mDelayMillis = delayMillis; 280 mParsedCount = parsed; 281 mDeliveredCount = delivered; 282 } 283 284 @Override 285 protected Response<Object> parseNetworkResponse(NetworkResponse response) { 286 mParsedCount.incrementAndGet(); 287 SystemClock.sleep(mDelayMillis); 288 return Response.success(new Object(), CacheTestUtils.makeRandomCacheEntry(null)); 289 } 290 291 @Override 292 protected void deliverResponse(Object response) { 293 mDeliveredCount.incrementAndGet(); 294 } 295 } 296 297} 298