diff options
author | Ficus Kirkpatrick <ficus@android.com> | 2014-11-29 09:32:54 -0800 |
---|---|---|
committer | Ficus Kirkpatrick <ficus@android.com> | 2014-12-05 15:46:44 -0800 |
commit | b9b8dc3d98fb1a8c3f02c2c2fcc18cbd344c05cb (patch) | |
tree | 1f4a33c24f608d79781de54e5d1ff5f3072fa261 /src/test/java/com | |
parent | 008e0cc8e51ef9e91110d91d4d662d0d86b252a1 (diff) | |
download | volley-b9b8dc3d98fb1a8c3f02c2c2fcc18cbd344c05cb.tar.gz |
Migrate from Gradle to Maven.
- Restructure source to src/{main,test} style
- Add pom.xml and update Android.mk
- Migrate all tests to JUnit4 and Robolectric
- RequestQueueTest is currently @Ignored as fixing it will
involve more extensive refactoring.
- Main library still builds in Gradle; tests do not
Change-Id: I1edc53bb1a54f64d3e806e4572901295ef63e2ca
Diffstat (limited to 'src/test/java/com')
25 files changed, 2478 insertions, 0 deletions
diff --git a/src/test/java/com/android/volley/CacheDispatcherTest.java b/src/test/java/com/android/volley/CacheDispatcherTest.java new file mode 100644 index 0000000..42bdda0 --- /dev/null +++ b/src/test/java/com/android/volley/CacheDispatcherTest.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.volley; + +import com.android.volley.mock.MockCache; +import com.android.volley.mock.MockRequest; +import com.android.volley.mock.MockResponseDelivery; +import com.android.volley.mock.WaitableQueue; +import com.android.volley.utils.CacheTestUtils; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +import static org.junit.Assert.*; + +@RunWith(RobolectricTestRunner.class) +@SuppressWarnings("rawtypes") +public class CacheDispatcherTest { + private CacheDispatcher mDispatcher; + private WaitableQueue mCacheQueue; + private WaitableQueue mNetworkQueue; + private MockCache mCache; + private MockResponseDelivery mDelivery; + private MockRequest mRequest; + + private static final long TIMEOUT_MILLIS = 5000; + + @Before public void setUp() throws Exception { + mCacheQueue = new WaitableQueue(); + mNetworkQueue = new WaitableQueue(); + mCache = new MockCache(); + mDelivery = new MockResponseDelivery(); + + mRequest = new MockRequest(); + + mDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery); + mDispatcher.start(); + } + + @After public void tearDown() throws Exception { + mDispatcher.quit(); + mDispatcher.join(); + } + + // A cancelled request should not be processed at all. + @Test public void cancelledRequest() throws Exception { + mRequest.cancel(); + mCacheQueue.add(mRequest); + mCacheQueue.waitUntilEmpty(TIMEOUT_MILLIS); + assertFalse(mCache.getCalled); + assertFalse(mDelivery.wasEitherResponseCalled()); + } + + // A cache miss does not post a response and puts the request on the network queue. + @Test public void cacheMiss() throws Exception { + mCacheQueue.add(mRequest); + mCacheQueue.waitUntilEmpty(TIMEOUT_MILLIS); + assertFalse(mDelivery.wasEitherResponseCalled()); + assertTrue(mNetworkQueue.size() > 0); + Request request = mNetworkQueue.take(); + assertNull(request.getCacheEntry()); + } + + // A non-expired cache hit posts a response and does not queue to the network. + @Test public void nonExpiredCacheHit() throws Exception { + Cache.Entry entry = CacheTestUtils.makeRandomCacheEntry(null, false, false); + mCache.setEntryToReturn(entry); + mCacheQueue.add(mRequest); + mCacheQueue.waitUntilEmpty(TIMEOUT_MILLIS); + assertTrue(mDelivery.postResponse_called); + assertFalse(mDelivery.postError_called); + } + + // A soft-expired cache hit posts a response and queues to the network. + @Test public void softExpiredCacheHit() throws Exception { + Cache.Entry entry = CacheTestUtils.makeRandomCacheEntry(null, false, true); + mCache.setEntryToReturn(entry); + mCacheQueue.add(mRequest); + mCacheQueue.waitUntilEmpty(TIMEOUT_MILLIS); + assertTrue(mDelivery.postResponse_called); + assertFalse(mDelivery.postError_called); + assertTrue(mNetworkQueue.size() > 0); + Request request = mNetworkQueue.take(); + assertSame(entry, request.getCacheEntry()); + } + + // An expired cache hit does not post a response and queues to the network. + @Test public void expiredCacheHit() throws Exception { + Cache.Entry entry = CacheTestUtils.makeRandomCacheEntry(null, true, true); + mCache.setEntryToReturn(entry); + mCacheQueue.add(mRequest); + mCacheQueue.waitUntilEmpty(TIMEOUT_MILLIS); + assertFalse(mDelivery.wasEitherResponseCalled()); + assertTrue(mNetworkQueue.size() > 0); + Request request = mNetworkQueue.take(); + assertSame(entry, request.getCacheEntry()); + } +} diff --git a/src/test/java/com/android/volley/NetworkDispatcherTest.java b/src/test/java/com/android/volley/NetworkDispatcherTest.java new file mode 100644 index 0000000..c5763bd --- /dev/null +++ b/src/test/java/com/android/volley/NetworkDispatcherTest.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.volley; + +import com.android.volley.mock.MockCache; +import com.android.volley.mock.MockNetwork; +import com.android.volley.mock.MockRequest; +import com.android.volley.mock.MockResponseDelivery; +import com.android.volley.mock.WaitableQueue; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +import java.util.Arrays; + +import static org.junit.Assert.*; + +@RunWith(RobolectricTestRunner.class) +public class NetworkDispatcherTest { + private NetworkDispatcher mDispatcher; + private MockResponseDelivery mDelivery; + private WaitableQueue mNetworkQueue; + private MockNetwork mNetwork; + private MockCache mCache; + private MockRequest mRequest; + + private static final byte[] CANNED_DATA = "Ceci n'est pas une vraie reponse".getBytes(); + private static final long TIMEOUT_MILLIS = 5000; + + @Before public void setUp() throws Exception { + mDelivery = new MockResponseDelivery(); + mNetworkQueue = new WaitableQueue(); + mNetwork = new MockNetwork(); + mCache = new MockCache(); + mRequest = new MockRequest(); + mDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork, mCache, mDelivery); + mDispatcher.start(); + } + + @After public void tearDown() throws Exception { + mDispatcher.quit(); + mDispatcher.join(); + } + + @Test public void successPostsResponse() throws Exception { + mNetwork.setDataToReturn(CANNED_DATA); + mNetwork.setNumExceptionsToThrow(0); + mNetworkQueue.add(mRequest); + mNetworkQueue.waitUntilEmpty(TIMEOUT_MILLIS); + assertFalse(mDelivery.postError_called); + assertTrue(mDelivery.postResponse_called); + Response<?> response = mDelivery.responsePosted; + assertNotNull(response); + assertTrue(response.isSuccess()); + assertTrue(Arrays.equals((byte[])response.result, CANNED_DATA)); + } + + @Test public void exceptionPostsError() throws Exception { + mNetwork.setNumExceptionsToThrow(MockNetwork.ALWAYS_THROW_EXCEPTIONS); + mNetworkQueue.add(mRequest); + mNetworkQueue.waitUntilEmpty(TIMEOUT_MILLIS); + assertFalse(mDelivery.postResponse_called); + assertTrue(mDelivery.postError_called); + } + + @Test public void shouldCacheFalse() throws Exception { + mRequest.setShouldCache(false); + mNetworkQueue.add(mRequest); + mNetworkQueue.waitUntilEmpty(TIMEOUT_MILLIS); + assertFalse(mCache.putCalled); + } + + @Test public void shouldCacheTrue() throws Exception { + mNetwork.setDataToReturn(CANNED_DATA); + mRequest.setShouldCache(true); + mRequest.setCacheKey("bananaphone"); + mNetworkQueue.add(mRequest); + mNetworkQueue.waitUntilEmpty(TIMEOUT_MILLIS); + assertTrue(mCache.putCalled); + assertNotNull(mCache.entryPut); + assertTrue(Arrays.equals(mCache.entryPut.data, CANNED_DATA)); + assertEquals("bananaphone", mCache.keyPut); + } +} diff --git a/src/test/java/com/android/volley/RequestQueueTest.java b/src/test/java/com/android/volley/RequestQueueTest.java new file mode 100644 index 0000000..cc88d3b --- /dev/null +++ b/src/test/java/com/android/volley/RequestQueueTest.java @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.volley; + +import com.android.volley.Request.Priority; +import com.android.volley.mock.MockNetwork; +import com.android.volley.mock.MockRequest; +import com.android.volley.toolbox.NoCache; +import com.android.volley.utils.CacheTestUtils; +import com.android.volley.utils.ImmediateResponseDelivery; + +import android.os.SystemClock; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.annotation.Implements; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.Assert.*; + +// TODO: Resurrect these tests when we have something like a finish() observer. +// They are really gross as-is and depend on a bunch of sleeping and whatnot. +@Ignore +@RunWith(RobolectricTestRunner.class) +public class RequestQueueTest { + private ResponseDelivery mDelivery; + + @Before public void setUp() throws Exception { + mDelivery = new ImmediateResponseDelivery(); + } + + /** + * Make a list of requests with random priorities. + * @param count Number of requests to make + */ + private List<MockRequest> makeRequests(int count) { + Request.Priority[] allPriorities = Request.Priority.values(); + Random random = new Random(); + + List<MockRequest> requests = new ArrayList<MockRequest>(); + for (int i = 0; i < count; i++) { + MockRequest request = new MockRequest(); + Request.Priority priority = allPriorities[random.nextInt(allPriorities.length)]; + request.setCacheKey(String.valueOf(i)); + request.setPriority(priority); + requests.add(request); + } + return requests; + } + + @Test public void add_requestProcessedInCorrectOrder() throws Exception { + int requestsToMake = 100; + + OrderCheckingNetwork network = new OrderCheckingNetwork(); + RequestQueue queue = new RequestQueue(new NoCache(), network, 1, mDelivery); + + for (Request<?> request : makeRequests(requestsToMake)) { + queue.add(request); + } + + network.setExpectedRequests(requestsToMake); + queue.start(); + network.waitUntilExpectedDone(2000); // 2 seconds + queue.stop(); + } + + @Test public void add_dedupeByCacheKey() throws Exception { + OrderCheckingNetwork network = new OrderCheckingNetwork(); + final AtomicInteger parsed = new AtomicInteger(); + final AtomicInteger delivered = new AtomicInteger(); + // Enqueue 2 requests with the same cache key. The first request takes 1.5s. Assert that the + // second request is only handled after the first one has been parsed and delivered. + DelayedRequest req1 = new DelayedRequest(1500, parsed, delivered); + DelayedRequest req2 = new DelayedRequest(0, parsed, delivered) { + @Override + protected Response<Object> parseNetworkResponse(NetworkResponse response) { + assertEquals(1, parsed.get()); // req1 must have been parsed. + assertEquals(1, delivered.get()); // req1 must have been parsed. + return super.parseNetworkResponse(response); + } + }; + network.setExpectedRequests(2); + RequestQueue queue = new RequestQueue(new NoCache(), network, 3, mDelivery); + queue.add(req1); + queue.add(req2); + queue.start(); + network.waitUntilExpectedDone(2000); + queue.stop(); + } + + @Test public void cancelAll_onlyCorrectTag() throws Exception { + MockNetwork network = new MockNetwork(); + RequestQueue queue = new RequestQueue(new NoCache(), network, 3, mDelivery); + Object tagA = new Object(); + Object tagB = new Object(); + MockRequest req1 = new MockRequest(); + req1.setTag(tagA); + MockRequest req2 = new MockRequest(); + req2.setTag(tagB); + MockRequest req3 = new MockRequest(); + req3.setTag(tagA); + MockRequest req4 = new MockRequest(); + req4.setTag(tagA); + + queue.add(req1); // A + queue.add(req2); // B + queue.add(req3); // A + queue.cancelAll(tagA); + queue.add(req4); // A + + assertTrue(req1.cancel_called); // A cancelled + assertFalse(req2.cancel_called); // B not cancelled + assertTrue(req3.cancel_called); // A cancelled + assertFalse(req4.cancel_called); // A added after cancel not cancelled + } + + private class OrderCheckingNetwork implements Network { + private Priority mLastPriority = Priority.IMMEDIATE; + private int mLastSequence = -1; + private Semaphore mSemaphore; + + public void setExpectedRequests(int expectedRequests) { + // Leave one permit available so the waiter can find it. + expectedRequests--; + mSemaphore = new Semaphore(-expectedRequests); + } + + public void waitUntilExpectedDone(long timeout) + throws InterruptedException, TimeoutError { + if (mSemaphore.tryAcquire(timeout, TimeUnit.MILLISECONDS) == false) { + throw new TimeoutError(); + } + } + + @Override + public NetworkResponse performRequest(Request<?> request) { + Priority thisPriority = request.getPriority(); + int thisSequence = request.getSequence(); + + int priorityDiff = thisPriority.compareTo(mLastPriority); + + // Should never experience a higher priority after a lower priority + assertFalse(priorityDiff > 0); + + // If we're not transitioning to a new priority block, check sequence numbers + if (priorityDiff == 0) { + assertTrue(thisSequence > mLastSequence); + } + mLastSequence = thisSequence; + mLastPriority = thisPriority; + + mSemaphore.release(); + return new NetworkResponse(new byte[16]); + } + } + + private class DelayedRequest extends Request<Object> { + private final long mDelayMillis; + private final AtomicInteger mParsedCount; + private final AtomicInteger mDeliveredCount; + + public DelayedRequest(long delayMillis, AtomicInteger parsed, AtomicInteger delivered) { + super(Request.Method.GET, "http://buganizer/", null); + mDelayMillis = delayMillis; + mParsedCount = parsed; + mDeliveredCount = delivered; + } + + @Override + protected Response<Object> parseNetworkResponse(NetworkResponse response) { + mParsedCount.incrementAndGet(); + SystemClock.sleep(mDelayMillis); + return Response.success(new Object(), CacheTestUtils.makeRandomCacheEntry(null)); + } + + @Override + protected void deliverResponse(Object response) { + mDeliveredCount.incrementAndGet(); + } + } + +} diff --git a/src/test/java/com/android/volley/RequestTest.java b/src/test/java/com/android/volley/RequestTest.java new file mode 100644 index 0000000..d5beca5 --- /dev/null +++ b/src/test/java/com/android/volley/RequestTest.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.volley; + +import com.android.volley.Request.Priority; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +import static org.junit.Assert.*; + +@RunWith(RobolectricTestRunner.class) +public class RequestTest { + + @Test public void compareTo() { + int sequence = 0; + TestRequest low = new TestRequest(Priority.LOW); + low.setSequence(sequence++); + TestRequest low2 = new TestRequest(Priority.LOW); + low2.setSequence(sequence++); + TestRequest high = new TestRequest(Priority.HIGH); + high.setSequence(sequence++); + TestRequest immediate = new TestRequest(Priority.IMMEDIATE); + immediate.setSequence(sequence++); + + // "Low" should sort higher because it's really processing order. + assertTrue(low.compareTo(high) > 0); + assertTrue(high.compareTo(low) < 0); + assertTrue(low.compareTo(low2) < 0); + assertTrue(low.compareTo(immediate) > 0); + assertTrue(immediate.compareTo(high) < 0); + } + + private class TestRequest extends Request<Object> { + private Priority mPriority = Priority.NORMAL; + public TestRequest(Priority priority) { + super(Request.Method.GET, "", null); + mPriority = priority; + } + + @Override + public Priority getPriority() { + return mPriority; + } + + @Override + protected void deliverResponse(Object response) { + } + + @Override + protected Response<Object> parseNetworkResponse(NetworkResponse response) { + return null; + } + } + + @Test public void urlParsing() { + UrlParseRequest nullUrl = new UrlParseRequest(null); + assertEquals(0, nullUrl.getTrafficStatsTag()); + UrlParseRequest emptyUrl = new UrlParseRequest(""); + assertEquals(0, emptyUrl.getTrafficStatsTag()); + UrlParseRequest noHost = new UrlParseRequest("http:///"); + assertEquals(0, noHost.getTrafficStatsTag()); + UrlParseRequest badProtocol = new UrlParseRequest("bad:http://foo"); + assertEquals(0, badProtocol.getTrafficStatsTag()); + UrlParseRequest goodProtocol = new UrlParseRequest("http://foo"); + assertFalse(0 == goodProtocol.getTrafficStatsTag()); + } + + private class UrlParseRequest extends Request<Object> { + public UrlParseRequest(String url) { + super(Request.Method.GET, url, null); + } + + @Override + protected void deliverResponse(Object response) { + } + + @Override + protected Response<Object> parseNetworkResponse(NetworkResponse response) { + return null; + } + } +} diff --git a/src/test/java/com/android/volley/ResponseDeliveryTest.java b/src/test/java/com/android/volley/ResponseDeliveryTest.java new file mode 100644 index 0000000..9fadfc3 --- /dev/null +++ b/src/test/java/com/android/volley/ResponseDeliveryTest.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.volley; + +import com.android.volley.mock.MockRequest; +import com.android.volley.utils.CacheTestUtils; +import com.android.volley.utils.ImmediateResponseDelivery; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +import static org.junit.Assert.*; + +@RunWith(RobolectricTestRunner.class) +public class ResponseDeliveryTest { + + private ExecutorDelivery mDelivery; + private MockRequest mRequest; + private Response<byte[]> mSuccessResponse; + + @Before public void setUp() throws Exception { + // Make the delivery just run its posted responses immediately. + mDelivery = new ImmediateResponseDelivery(); + mRequest = new MockRequest(); + mRequest.setSequence(1); + byte[] data = new byte[16]; + Cache.Entry cacheEntry = CacheTestUtils.makeRandomCacheEntry(data); + mSuccessResponse = Response.success(data, cacheEntry); + } + + @Test public void postResponseCallsDeliverResponse() { + mDelivery.postResponse(mRequest, mSuccessResponse); + assertTrue(mRequest.deliverResponse_called); + assertFalse(mRequest.deliverError_called); + } + + @Test public void postResponseSuppressesCanceled() { + mRequest.cancel(); + mDelivery.postResponse(mRequest, mSuccessResponse); + assertFalse(mRequest.deliverResponse_called); + assertFalse(mRequest.deliverError_called); + } + + @Test public void postErrorCallsDeliverError() { + Response<byte[]> errorResponse = Response.error(new ServerError()); + + mDelivery.postResponse(mRequest, errorResponse); + assertTrue(mRequest.deliverError_called); + assertFalse(mRequest.deliverResponse_called); + } +} diff --git a/src/test/java/com/android/volley/mock/MockCache.java b/src/test/java/com/android/volley/mock/MockCache.java new file mode 100644 index 0000000..85a4607 --- /dev/null +++ b/src/test/java/com/android/volley/mock/MockCache.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.volley.mock; + +import com.android.volley.Cache; + +public class MockCache implements Cache { + + public boolean clearCalled = false; + @Override + public void clear() { + clearCalled = true; + } + + public boolean getCalled = false; + private Entry mFakeEntry = null; + + public void setEntryToReturn(Entry entry) { + mFakeEntry = entry; + } + + @Override + public Entry get(String key) { + getCalled = true; + return mFakeEntry; + } + + public boolean putCalled = false; + public String keyPut = null; + public Entry entryPut = null; + + @Override + public void put(String key, Entry entry) { + putCalled = true; + keyPut = key; + entryPut = entry; + } + + @Override + public void invalidate(String key, boolean fullExpire) { + } + + @Override + public void remove(String key) { + } + + @Override + public void initialize() { + } + +} diff --git a/src/test/java/com/android/volley/mock/MockHttpClient.java b/src/test/java/com/android/volley/mock/MockHttpClient.java new file mode 100644 index 0000000..c2a36bc --- /dev/null +++ b/src/test/java/com/android/volley/mock/MockHttpClient.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.volley.mock; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpHost; +import org.apache.http.HttpRequest; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.ProtocolVersion; +import org.apache.http.StatusLine; +import org.apache.http.client.HttpClient; +import org.apache.http.client.ResponseHandler; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.conn.ClientConnectionManager; +import org.apache.http.message.BasicHttpResponse; +import org.apache.http.message.BasicStatusLine; +import org.apache.http.params.HttpParams; +import org.apache.http.protocol.HttpContext; + + +public class MockHttpClient implements HttpClient { + private int mStatusCode = HttpStatus.SC_OK; + private HttpEntity mResponseEntity = null; + + public void setResponseData(HttpEntity entity) { + mStatusCode = HttpStatus.SC_OK; + mResponseEntity = entity; + } + + public void setErrorCode(int statusCode) { + if (statusCode == HttpStatus.SC_OK) { + throw new IllegalArgumentException("statusCode cannot be 200 for an error"); + } + mStatusCode = statusCode; + } + + public HttpUriRequest requestExecuted = null; + + // This is the only one we actually use. + @Override + public HttpResponse execute(HttpUriRequest request, HttpContext context) { + requestExecuted = request; + StatusLine statusLine = new BasicStatusLine( + new ProtocolVersion("HTTP", 1, 1), mStatusCode, ""); + HttpResponse response = new BasicHttpResponse(statusLine); + response.setEntity(mResponseEntity); + + return response; + } + + + // Unimplemented methods ahoy + + @Override + public HttpResponse execute(HttpUriRequest request) { + throw new UnsupportedOperationException(); + } + + @Override + public HttpResponse execute(HttpHost target, HttpRequest request) { + throw new UnsupportedOperationException(); + } + + @Override + public <T> T execute(HttpUriRequest arg0, ResponseHandler<? extends T> arg1) { + throw new UnsupportedOperationException(); + } + + @Override + public HttpResponse execute(HttpHost target, HttpRequest request, HttpContext context) { + throw new UnsupportedOperationException(); + } + + @Override + public <T> T execute(HttpUriRequest arg0, ResponseHandler<? extends T> arg1, HttpContext arg2) { + throw new UnsupportedOperationException(); + } + + @Override + public <T> T execute(HttpHost arg0, HttpRequest arg1, ResponseHandler<? extends T> arg2) { + throw new UnsupportedOperationException(); + } + + @Override + public <T> T execute(HttpHost arg0, HttpRequest arg1, ResponseHandler<? extends T> arg2, + HttpContext arg3) { + throw new UnsupportedOperationException(); + } + + @Override + public ClientConnectionManager getConnectionManager() { + throw new UnsupportedOperationException(); + } + + @Override + public HttpParams getParams() { + throw new UnsupportedOperationException(); + } +} diff --git a/src/test/java/com/android/volley/mock/MockHttpStack.java b/src/test/java/com/android/volley/mock/MockHttpStack.java new file mode 100644 index 0000000..9594fde --- /dev/null +++ b/src/test/java/com/android/volley/mock/MockHttpStack.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.volley.mock; + +import com.android.volley.AuthFailureError; +import com.android.volley.Request; +import com.android.volley.toolbox.HttpStack; + +import org.apache.http.HttpResponse; + +import java.util.HashMap; +import java.util.Map; + +public class MockHttpStack implements HttpStack { + + private HttpResponse mResponseToReturn; + + private String mLastUrl; + + private Map<String, String> mLastHeaders; + + private byte[] mLastPostBody; + + public String getLastUrl() { + return mLastUrl; + } + + public Map<String, String> getLastHeaders() { + return mLastHeaders; + } + + public byte[] getLastPostBody() { + return mLastPostBody; + } + + public void setResponseToReturn(HttpResponse response) { + mResponseToReturn = response; + } + + @Override + public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders) + throws AuthFailureError { + mLastUrl = request.getUrl(); + mLastHeaders = new HashMap<String, String>(); + if (request.getHeaders() != null) { + mLastHeaders.putAll(request.getHeaders()); + } + if (additionalHeaders != null) { + mLastHeaders.putAll(additionalHeaders); + } + try { + mLastPostBody = request.getBody(); + } catch (AuthFailureError e) { + mLastPostBody = null; + } + return mResponseToReturn; + } +} diff --git a/src/test/java/com/android/volley/mock/MockHttpURLConnection.java b/src/test/java/com/android/volley/mock/MockHttpURLConnection.java new file mode 100644 index 0000000..efa3a21 --- /dev/null +++ b/src/test/java/com/android/volley/mock/MockHttpURLConnection.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.volley.mock; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; + +public class MockHttpURLConnection extends HttpURLConnection { + + private boolean mDoOutput; + private String mRequestMethod; + private OutputStream mOutputStream; + + public MockHttpURLConnection() throws MalformedURLException { + super(new URL("http://foo.com")); + mDoOutput = false; + mRequestMethod = "GET"; + mOutputStream = new ByteArrayOutputStream(); + } + + @Override + public void setDoOutput(boolean flag) { + mDoOutput = flag; + } + + @Override + public boolean getDoOutput() { + return mDoOutput; + } + + @Override + public void setRequestMethod(String method) { + mRequestMethod = method; + } + + @Override + public String getRequestMethod() { + return mRequestMethod; + } + + @Override + public OutputStream getOutputStream() { + return mOutputStream; + } + + @Override + public void disconnect() { + } + + @Override + public boolean usingProxy() { + return false; + } + + @Override + public void connect() throws IOException { + } + +} diff --git a/src/test/java/com/android/volley/mock/MockNetwork.java b/src/test/java/com/android/volley/mock/MockNetwork.java new file mode 100644 index 0000000..207ec63 --- /dev/null +++ b/src/test/java/com/android/volley/mock/MockNetwork.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.volley.mock; + +import com.android.volley.Network; +import com.android.volley.NetworkResponse; +import com.android.volley.Request; +import com.android.volley.ServerError; +import com.android.volley.VolleyError; + +public class MockNetwork implements Network { + public final static int ALWAYS_THROW_EXCEPTIONS = -1; + + private int mNumExceptionsToThrow = 0; + private byte[] mDataToReturn = null; + + /** + * @param numExceptionsToThrow number of times to throw an exception or + * {@link #ALWAYS_THROW_EXCEPTIONS} + */ + public void setNumExceptionsToThrow(int numExceptionsToThrow) { + mNumExceptionsToThrow = numExceptionsToThrow; + } + + public void setDataToReturn(byte[] data) { + mDataToReturn = data; + } + + public Request<?> requestHandled = null; + + @Override + public NetworkResponse performRequest(Request<?> request) throws VolleyError { + if (mNumExceptionsToThrow > 0 || mNumExceptionsToThrow == ALWAYS_THROW_EXCEPTIONS) { + if (mNumExceptionsToThrow != ALWAYS_THROW_EXCEPTIONS) { + mNumExceptionsToThrow--; + } + throw new ServerError(); + } + + requestHandled = request; + return new NetworkResponse(mDataToReturn); + } + +} diff --git a/src/test/java/com/android/volley/mock/MockRequest.java b/src/test/java/com/android/volley/mock/MockRequest.java new file mode 100644 index 0000000..9815ea8 --- /dev/null +++ b/src/test/java/com/android/volley/mock/MockRequest.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.volley.mock; + +import com.android.volley.NetworkResponse; +import com.android.volley.Request; +import com.android.volley.Response; +import com.android.volley.Response.ErrorListener; +import com.android.volley.VolleyError; +import com.android.volley.utils.CacheTestUtils; + +import java.util.HashMap; +import java.util.Map; + +public class MockRequest extends Request<byte[]> { + public MockRequest() { + super(Request.Method.GET, "http://foo.com", null); + } + + public MockRequest(String url, ErrorListener listener) { + super(Request.Method.GET, url, listener); + } + + private Map<String, String> mPostParams = new HashMap<String, String>(); + + public void setPostParams(Map<String, String> postParams) { + mPostParams = postParams; + } + + @Override + public Map<String, String> getPostParams() { + return mPostParams; + } + + private String mCacheKey = super.getCacheKey(); + + public void setCacheKey(String cacheKey) { + mCacheKey = cacheKey; + } + + @Override + public String getCacheKey() { + return mCacheKey; + } + + public boolean deliverResponse_called = false; + public boolean parseResponse_called = false; + + @Override + protected void deliverResponse(byte[] response) { + deliverResponse_called = true; + } + + public boolean deliverError_called = false; + + @Override + public void deliverError(VolleyError error) { + super.deliverError(error); + deliverError_called = true; + } + + public boolean cancel_called = false; + + @Override + public void cancel() { + cancel_called = true; + super.cancel(); + } + + private Priority mPriority = super.getPriority(); + + public void setPriority(Priority priority) { + mPriority = priority; + } + + @Override + public Priority getPriority() { + return mPriority; + } + + @Override + protected Response<byte[]> parseNetworkResponse(NetworkResponse response) { + parseResponse_called = true; + return Response.success(response.data, CacheTestUtils.makeRandomCacheEntry(response.data)); + } + +} diff --git a/src/test/java/com/android/volley/mock/MockResponseDelivery.java b/src/test/java/com/android/volley/mock/MockResponseDelivery.java new file mode 100644 index 0000000..4dbfd5c --- /dev/null +++ b/src/test/java/com/android/volley/mock/MockResponseDelivery.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.volley.mock; + +import com.android.volley.Request; +import com.android.volley.Response; +import com.android.volley.ResponseDelivery; +import com.android.volley.VolleyError; + +public class MockResponseDelivery implements ResponseDelivery { + + public boolean postResponse_called = false; + public boolean postError_called = false; + + public boolean wasEitherResponseCalled() { + return postResponse_called || postError_called; + } + + public Response<?> responsePosted = null; + @Override + public void postResponse(Request<?> request, Response<?> response) { + postResponse_called = true; + responsePosted = response; + } + + @Override + public void postResponse(Request<?> request, Response<?> response, Runnable runnable) { + postResponse_called = true; + responsePosted = response; + runnable.run(); + } + + @Override + public void postError(Request<?> request, VolleyError error) { + postError_called = true; + } +} diff --git a/src/test/java/com/android/volley/mock/TestRequest.java b/src/test/java/com/android/volley/mock/TestRequest.java new file mode 100644 index 0000000..dfc4dc1 --- /dev/null +++ b/src/test/java/com/android/volley/mock/TestRequest.java @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.volley.mock; + +import com.android.volley.NetworkResponse; +import com.android.volley.Request; +import com.android.volley.Response; + +import java.util.HashMap; +import java.util.Map; + +public class TestRequest { + private static final String TEST_URL = "http://foo.com"; + + /** Base Request class for testing allowing both the deprecated and new constructor. */ + private static class Base extends Request<byte[]> { + @SuppressWarnings("deprecation") + public Base(String url, Response.ErrorListener listener) { + super(url, listener); + } + + public Base(int method, String url, Response.ErrorListener listener) { + super(method, url, listener); + } + + @Override + protected Response<byte[]> parseNetworkResponse(NetworkResponse response) { + return null; + } + + @Override + protected void deliverResponse(byte[] response) { + } + } + + /** Test example of a GET request in the deprecated style. */ + public static class DeprecatedGet extends Base { + public DeprecatedGet() { + super(TEST_URL, null); + } + } + + /** Test example of a POST request in the deprecated style. */ + public static class DeprecatedPost extends Base { + private Map<String, String> mPostParams; + + public DeprecatedPost() { + super(TEST_URL, null); + mPostParams = new HashMap<String, String>(); + mPostParams.put("requestpost", "foo"); + } + + @Override + protected Map<String, String> getPostParams() { + return mPostParams; + } + } + + /** Test example of a GET request in the new style. */ + public static class Get extends Base { + public Get() { + super(Method.GET, TEST_URL, null); + } + } + + /** + * Test example of a POST request in the new style. In the new style, it is possible + * to have a POST with no body. + */ + public static class Post extends Base { + public Post() { + super(Method.POST, TEST_URL, null); + } + } + + /** Test example of a POST request in the new style with a body. */ + public static class PostWithBody extends Post { + private Map<String, String> mParams; + + public PostWithBody() { + mParams = new HashMap<String, String>(); + mParams.put("testKey", "testValue"); + } + + @Override + public Map<String, String> getParams() { + return mParams; + } + } + + /** + * Test example of a PUT request in the new style. In the new style, it is possible to have a + * PUT with no body. + */ + public static class Put extends Base { + public Put() { + super(Method.PUT, TEST_URL, null); + } + } + + /** Test example of a PUT request in the new style with a body. */ + public static class PutWithBody extends Put { + private Map<String, String> mParams = new HashMap<String, String>(); + + public PutWithBody() { + mParams = new HashMap<String, String>(); + mParams.put("testKey", "testValue"); + } + + @Override + public Map<String, String> getParams() { + return mParams; + } + } + + /** Test example of a DELETE request in the new style. */ + public static class Delete extends Base { + public Delete() { + super(Method.DELETE, TEST_URL, null); + } + } + + /** Test example of a HEAD request in the new style. */ + public static class Head extends Base { + public Head() { + super(Method.HEAD, TEST_URL, null); + } + } + + /** Test example of a OPTIONS request in the new style. */ + public static class Options extends Base { + public Options() { + super(Method.OPTIONS, TEST_URL, null); + } + } + + /** Test example of a TRACE request in the new style. */ + public static class Trace extends Base { + public Trace() { + super(Method.TRACE, TEST_URL, null); + } + } + + /** Test example of a PATCH request in the new style. */ + public static class Patch extends Base { + public Patch() { + super(Method.PATCH, TEST_URL, null); + } + } + + /** Test example of a PATCH request in the new style with a body. */ + public static class PatchWithBody extends Patch { + private Map<String, String> mParams = new HashMap<String, String>(); + + public PatchWithBody() { + mParams = new HashMap<String, String>(); + mParams.put("testKey", "testValue"); + } + + @Override + public Map<String, String> getParams() { + return mParams; + } + } +} diff --git a/src/test/java/com/android/volley/mock/WaitableQueue.java b/src/test/java/com/android/volley/mock/WaitableQueue.java new file mode 100644 index 0000000..079bbf5 --- /dev/null +++ b/src/test/java/com/android/volley/mock/WaitableQueue.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.volley.mock; + +import com.android.volley.NetworkResponse; +import com.android.volley.Request; +import com.android.volley.Response; + +import java.util.concurrent.PriorityBlockingQueue; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +// TODO: the name of this class sucks +@SuppressWarnings("serial") +public class WaitableQueue extends PriorityBlockingQueue<Request<?>> { + private final Request<?> mStopRequest = new MagicStopRequest(); + private final Semaphore mStopEvent = new Semaphore(0); + + // TODO: this isn't really "until empty" it's "until next call to take() after empty" + public void waitUntilEmpty(long timeoutMillis) + throws TimeoutException, InterruptedException { + add(mStopRequest); + if (!mStopEvent.tryAcquire(timeoutMillis, TimeUnit.MILLISECONDS)) { + throw new TimeoutException(); + } + } + + @Override + public Request<?> take() throws InterruptedException { + Request<?> item = super.take(); + if (item == mStopRequest) { + mStopEvent.release(); + return take(); + } + return item; + } + + private static class MagicStopRequest extends Request<Object> { + public MagicStopRequest() { + super(Request.Method.GET, "", null); + } + + @Override + public Priority getPriority() { + return Priority.LOW; + } + + @Override + protected Response<Object> parseNetworkResponse(NetworkResponse response) { + return null; + } + + @Override + protected void deliverResponse(Object response) { + } + } +} diff --git a/src/test/java/com/android/volley/toolbox/BasicNetworkTest.java b/src/test/java/com/android/volley/toolbox/BasicNetworkTest.java new file mode 100644 index 0000000..89718b1 --- /dev/null +++ b/src/test/java/com/android/volley/toolbox/BasicNetworkTest.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.volley.toolbox; + +import com.android.volley.NetworkResponse; +import com.android.volley.Request; +import com.android.volley.Response; +import com.android.volley.mock.MockHttpStack; + +import org.apache.http.ProtocolVersion; +import org.apache.http.entity.StringEntity; +import org.apache.http.message.BasicHttpResponse; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +import static org.junit.Assert.*; + +import java.util.HashMap; +import java.util.Map; + +@RunWith(RobolectricTestRunner.class) +public class BasicNetworkTest { + + @Test public void headersAndPostParams() throws Exception { + MockHttpStack mockHttpStack = new MockHttpStack(); + BasicHttpResponse fakeResponse = new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), + 200, "OK"); + fakeResponse.setEntity(new StringEntity("foobar")); + mockHttpStack.setResponseToReturn(fakeResponse); + BasicNetwork httpNetwork = new BasicNetwork(mockHttpStack); + Request<String> request = new Request<String>(Request.Method.GET, "http://foo", null) { + + @Override + protected Response<String> parseNetworkResponse(NetworkResponse response) { + return null; + } + + @Override + protected void deliverResponse(String response) { + } + + @Override + public Map<String, String> getHeaders() { + Map<String, String> result = new HashMap<String, String>(); + result.put("requestheader", "foo"); + return result; + } + + @Override + public Map<String, String> getParams() { + Map<String, String> result = new HashMap<String, String>(); + result.put("requestpost", "foo"); + return result; + } + }; + httpNetwork.performRequest(request); + assertEquals("foo", mockHttpStack.getLastHeaders().get("requestheader")); + assertEquals("requestpost=foo&", new String(mockHttpStack.getLastPostBody())); + } +} diff --git a/src/test/java/com/android/volley/toolbox/ByteArrayPoolTest.java b/src/test/java/com/android/volley/toolbox/ByteArrayPoolTest.java new file mode 100644 index 0000000..661e994 --- /dev/null +++ b/src/test/java/com/android/volley/toolbox/ByteArrayPoolTest.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.volley.toolbox; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +public class ByteArrayPoolTest { + @Test public void reusesBuffer() { + ByteArrayPool pool = new ByteArrayPool(32); + + byte[] buf1 = pool.getBuf(16); + byte[] buf2 = pool.getBuf(16); + + pool.returnBuf(buf1); + pool.returnBuf(buf2); + + byte[] buf3 = pool.getBuf(16); + byte[] buf4 = pool.getBuf(16); + assertTrue(buf3 == buf1 || buf3 == buf2); + assertTrue(buf4 == buf1 || buf4 == buf2); + assertTrue(buf3 != buf4); + } + + @Test public void obeysSizeLimit() { + ByteArrayPool pool = new ByteArrayPool(32); + + byte[] buf1 = pool.getBuf(16); + byte[] buf2 = pool.getBuf(16); + byte[] buf3 = pool.getBuf(16); + + pool.returnBuf(buf1); + pool.returnBuf(buf2); + pool.returnBuf(buf3); + + byte[] buf4 = pool.getBuf(16); + byte[] buf5 = pool.getBuf(16); + byte[] buf6 = pool.getBuf(16); + + assertTrue(buf4 == buf2 || buf4 == buf3); + assertTrue(buf5 == buf2 || buf5 == buf3); + assertTrue(buf4 != buf5); + assertTrue(buf6 != buf1 && buf6 != buf2 && buf6 != buf3); + } + + @Test public void returnsBufferWithRightSize() { + ByteArrayPool pool = new ByteArrayPool(32); + + byte[] buf1 = pool.getBuf(16); + pool.returnBuf(buf1); + + byte[] buf2 = pool.getBuf(17); + assertNotSame(buf2, buf1); + + byte[] buf3 = pool.getBuf(15); + assertSame(buf3, buf1); + } +} diff --git a/src/test/java/com/android/volley/toolbox/DiskBasedCacheTest.java b/src/test/java/com/android/volley/toolbox/DiskBasedCacheTest.java new file mode 100644 index 0000000..4b2955d --- /dev/null +++ b/src/test/java/com/android/volley/toolbox/DiskBasedCacheTest.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.volley.toolbox; + +import com.android.volley.Cache; +import com.android.volley.toolbox.DiskBasedCache.CacheHeader; +import org.junit.Test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.util.HashMap; +import java.util.Map; + +import static org.junit.Assert.*; + +public class DiskBasedCacheTest { + + // Simple end-to-end serialize/deserialize test. + @Test public void cacheHeaderSerialization() throws Exception { + Cache.Entry e = new Cache.Entry(); + e.data = new byte[8]; + e.serverDate = 1234567L; + e.ttl = 9876543L; + e.softTtl = 8765432L; + e.etag = "etag"; + e.responseHeaders = new HashMap<String, String>(); + e.responseHeaders.put("fruit", "banana"); + + CacheHeader first = new CacheHeader("my-magical-key", e); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + first.writeHeader(baos); + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + CacheHeader second = CacheHeader.readHeader(bais); + + assertEquals(first.key, second.key); + assertEquals(first.serverDate, second.serverDate); + assertEquals(first.ttl, second.ttl); + assertEquals(first.softTtl, second.softTtl); + assertEquals(first.etag, second.etag); + assertEquals(first.responseHeaders, second.responseHeaders); + } + + @Test public void serializeInt() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DiskBasedCache.writeInt(baos, 0); + DiskBasedCache.writeInt(baos, 19791214); + DiskBasedCache.writeInt(baos, -20050711); + DiskBasedCache.writeInt(baos, Integer.MIN_VALUE); + DiskBasedCache.writeInt(baos, Integer.MAX_VALUE); + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + assertEquals(DiskBasedCache.readInt(bais), 0); + assertEquals(DiskBasedCache.readInt(bais), 19791214); + assertEquals(DiskBasedCache.readInt(bais), -20050711); + assertEquals(DiskBasedCache.readInt(bais), Integer.MIN_VALUE); + assertEquals(DiskBasedCache.readInt(bais), Integer.MAX_VALUE); + } + + @Test public void serializeLong() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DiskBasedCache.writeLong(baos, 0); + DiskBasedCache.writeLong(baos, 31337); + DiskBasedCache.writeLong(baos, -4160); + DiskBasedCache.writeLong(baos, 4295032832L); + DiskBasedCache.writeLong(baos, -4314824046L); + DiskBasedCache.writeLong(baos, Long.MIN_VALUE); + DiskBasedCache.writeLong(baos, Long.MAX_VALUE); + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + assertEquals(DiskBasedCache.readLong(bais), 0); + assertEquals(DiskBasedCache.readLong(bais), 31337); + assertEquals(DiskBasedCache.readLong(bais), -4160); + assertEquals(DiskBasedCache.readLong(bais), 4295032832L); + assertEquals(DiskBasedCache.readLong(bais), -4314824046L); + assertEquals(DiskBasedCache.readLong(bais), Long.MIN_VALUE); + assertEquals(DiskBasedCache.readLong(bais), Long.MAX_VALUE); + } + + @Test public void serializeString() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DiskBasedCache.writeString(baos, ""); + DiskBasedCache.writeString(baos, "This is a string."); + DiskBasedCache.writeString(baos, "ファイカス"); + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + assertEquals(DiskBasedCache.readString(bais), ""); + assertEquals(DiskBasedCache.readString(bais), "This is a string."); + assertEquals(DiskBasedCache.readString(bais), "ファイカス"); + } + + @Test public void serializeMap() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + Map<String, String> empty = new HashMap<String, String>(); + DiskBasedCache.writeStringStringMap(empty, baos); + DiskBasedCache.writeStringStringMap(null, baos); + Map<String, String> twoThings = new HashMap<String, String>(); + twoThings.put("first", "thing"); + twoThings.put("second", "item"); + DiskBasedCache.writeStringStringMap(twoThings, baos); + Map<String, String> emptyKey = new HashMap<String, String>(); + emptyKey.put("", "value"); + DiskBasedCache.writeStringStringMap(emptyKey, baos); + Map<String, String> emptyValue = new HashMap<String, String>(); + emptyValue.put("key", ""); + DiskBasedCache.writeStringStringMap(emptyValue, baos); + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + assertEquals(DiskBasedCache.readStringStringMap(bais), empty); + assertEquals(DiskBasedCache.readStringStringMap(bais), empty); // null reads back empty + assertEquals(DiskBasedCache.readStringStringMap(bais), twoThings); + assertEquals(DiskBasedCache.readStringStringMap(bais), emptyKey); + assertEquals(DiskBasedCache.readStringStringMap(bais), emptyValue); + } +} diff --git a/src/test/java/com/android/volley/toolbox/HttpClientStackTest.java b/src/test/java/com/android/volley/toolbox/HttpClientStackTest.java new file mode 100644 index 0000000..0c417d4 --- /dev/null +++ b/src/test/java/com/android/volley/toolbox/HttpClientStackTest.java @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.volley.toolbox; + +import com.android.volley.Request.Method; +import com.android.volley.mock.TestRequest; +import com.android.volley.toolbox.HttpClientStack.HttpPatch; + +import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpHead; +import org.apache.http.client.methods.HttpOptions; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.client.methods.HttpTrace; +import org.apache.http.client.methods.HttpUriRequest; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +import static org.junit.Assert.*; + +@RunWith(RobolectricTestRunner.class) +public class HttpClientStackTest { + + @Test public void createDeprecatedGetRequest() throws Exception { + TestRequest.DeprecatedGet request = new TestRequest.DeprecatedGet(); + assertEquals(request.getMethod(), Method.DEPRECATED_GET_OR_POST); + + HttpUriRequest httpRequest = HttpClientStack.createHttpRequest(request, null); + assertTrue(httpRequest instanceof HttpGet); + } + + @Test public void createDeprecatedPostRequest() throws Exception { + TestRequest.DeprecatedPost request = new TestRequest.DeprecatedPost(); + assertEquals(request.getMethod(), Method.DEPRECATED_GET_OR_POST); + + HttpUriRequest httpRequest = HttpClientStack.createHttpRequest(request, null); + assertTrue(httpRequest instanceof HttpPost); + } + + @Test public void createGetRequest() throws Exception { + TestRequest.Get request = new TestRequest.Get(); + assertEquals(request.getMethod(), Method.GET); + + HttpUriRequest httpRequest = HttpClientStack.createHttpRequest(request, null); + assertTrue(httpRequest instanceof HttpGet); + } + + @Test public void createPostRequest() throws Exception { + TestRequest.Post request = new TestRequest.Post(); + assertEquals(request.getMethod(), Method.POST); + + HttpUriRequest httpRequest = HttpClientStack.createHttpRequest(request, null); + assertTrue(httpRequest instanceof HttpPost); + } + + @Test public void createPostRequestWithBody() throws Exception { + TestRequest.PostWithBody request = new TestRequest.PostWithBody(); + assertEquals(request.getMethod(), Method.POST); + + HttpUriRequest httpRequest = HttpClientStack.createHttpRequest(request, null); + assertTrue(httpRequest instanceof HttpPost); + } + + @Test public void createPutRequest() throws Exception { + TestRequest.Put request = new TestRequest.Put(); + assertEquals(request.getMethod(), Method.PUT); + + HttpUriRequest httpRequest = HttpClientStack.createHttpRequest(request, null); + assertTrue(httpRequest instanceof HttpPut); + } + + @Test public void createPutRequestWithBody() throws Exception { + TestRequest.PutWithBody request = new TestRequest.PutWithBody(); + assertEquals(request.getMethod(), Method.PUT); + + HttpUriRequest httpRequest = HttpClientStack.createHttpRequest(request, null); + assertTrue(httpRequest instanceof HttpPut); + } + + @Test public void createDeleteRequest() throws Exception { + TestRequest.Delete request = new TestRequest.Delete(); + assertEquals(request.getMethod(), Method.DELETE); + + HttpUriRequest httpRequest = HttpClientStack.createHttpRequest(request, null); + assertTrue(httpRequest instanceof HttpDelete); + } + + @Test public void createHeadRequest() throws Exception { + TestRequest.Head request = new TestRequest.Head(); + assertEquals(request.getMethod(), Method.HEAD); + + HttpUriRequest httpRequest = HttpClientStack.createHttpRequest(request, null); + assertTrue(httpRequest instanceof HttpHead); + } + + @Test public void createOptionsRequest() throws Exception { + TestRequest.Options request = new TestRequest.Options(); + assertEquals(request.getMethod(), Method.OPTIONS); + + HttpUriRequest httpRequest = HttpClientStack.createHttpRequest(request, null); + assertTrue(httpRequest instanceof HttpOptions); + } + + @Test public void createTraceRequest() throws Exception { + TestRequest.Trace request = new TestRequest.Trace(); + assertEquals(request.getMethod(), Method.TRACE); + + HttpUriRequest httpRequest = HttpClientStack.createHttpRequest(request, null); + assertTrue(httpRequest instanceof HttpTrace); + } + + @Test public void createPatchRequest() throws Exception { + TestRequest.Patch request = new TestRequest.Patch(); + assertEquals(request.getMethod(), Method.PATCH); + + HttpUriRequest httpRequest = HttpClientStack.createHttpRequest(request, null); + assertTrue(httpRequest instanceof HttpPatch); + } + + @Test public void createPatchRequestWithBody() throws Exception { + TestRequest.PatchWithBody request = new TestRequest.PatchWithBody(); + assertEquals(request.getMethod(), Method.PATCH); + + HttpUriRequest httpRequest = HttpClientStack.createHttpRequest(request, null); + assertTrue(httpRequest instanceof HttpPatch); + } +} diff --git a/src/test/java/com/android/volley/toolbox/HttpHeaderParserTest.java b/src/test/java/com/android/volley/toolbox/HttpHeaderParserTest.java new file mode 100644 index 0000000..b8c4847 --- /dev/null +++ b/src/test/java/com/android/volley/toolbox/HttpHeaderParserTest.java @@ -0,0 +1,226 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.volley.toolbox; + +import com.android.volley.Cache; +import com.android.volley.NetworkResponse; + +import org.apache.http.Header; +import org.apache.http.message.BasicHeader; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +import static org.junit.Assert.*; + +@RunWith(RobolectricTestRunner.class) +public class HttpHeaderParserTest { + + private static long ONE_MINUTE_MILLIS = 1000L * 60; + private static long ONE_HOUR_MILLIS = 1000L * 60 * 60; + + private NetworkResponse response; + private Map<String, String> headers; + + @Before public void setUp() throws Exception { + headers = new HashMap<String, String>(); + response = new NetworkResponse(0, null, headers, false); + } + + @Test public void parseCacheHeaders_noHeaders() { + Cache.Entry entry = HttpHeaderParser.parseCacheHeaders(response); + + assertNotNull(entry); + assertNull(entry.etag); + assertEquals(0, entry.serverDate); + assertEquals(0, entry.ttl); + assertEquals(0, entry.softTtl); + } + + @Test public void parseCacheHeaders_headersSet() { + headers.put("MyCustomHeader", "42"); + + Cache.Entry entry = HttpHeaderParser.parseCacheHeaders(response); + + assertNotNull(entry); + assertNotNull(entry.responseHeaders); + assertEquals(1, entry.responseHeaders.size()); + assertEquals("42", entry.responseHeaders.get("MyCustomHeader")); + } + + @Test public void parseCacheHeaders_etag() { + headers.put("ETag", "Yow!"); + + Cache.Entry entry = HttpHeaderParser.parseCacheHeaders(response); + + assertNotNull(entry); + assertEquals("Yow!", entry.etag); + } + + @Test public void parseCacheHeaders_normalExpire() { + long now = System.currentTimeMillis(); + headers.put("Date", rfc1123Date(now)); + headers.put("Expires", rfc1123Date(now + ONE_HOUR_MILLIS)); + + Cache.Entry entry = HttpHeaderParser.parseCacheHeaders(response); + + assertNotNull(entry); + assertNull(entry.etag); + assertEqualsWithin(entry.serverDate, now, ONE_MINUTE_MILLIS); + assertTrue(entry.softTtl >= (now + ONE_HOUR_MILLIS)); + assertTrue(entry.ttl == entry.softTtl); + } + + @Test public void parseCacheHeaders_expiresInPast() { + long now = System.currentTimeMillis(); + headers.put("Date", rfc1123Date(now)); + headers.put("Expires", rfc1123Date(now - ONE_HOUR_MILLIS)); + + Cache.Entry entry = HttpHeaderParser.parseCacheHeaders(response); + + assertNotNull(entry); + assertNull(entry.etag); + assertEqualsWithin(entry.serverDate, now, ONE_MINUTE_MILLIS); + assertEquals(0, entry.ttl); + assertEquals(0, entry.softTtl); + } + + @Test public void parseCacheHeaders_serverRelative() { + + long now = System.currentTimeMillis(); + // Set "current" date as one hour in the future + headers.put("Date", rfc1123Date(now + ONE_HOUR_MILLIS)); + // TTL four hours in the future, so should be three hours from now + headers.put("Expires", rfc1123Date(now + 4 * ONE_HOUR_MILLIS)); + + Cache.Entry entry = HttpHeaderParser.parseCacheHeaders(response); + + assertEqualsWithin(now + 3 * ONE_HOUR_MILLIS, entry.ttl, ONE_MINUTE_MILLIS); + assertEquals(entry.softTtl, entry.ttl); + } + + @Test public void parseCacheHeaders_cacheControlOverridesExpires() { + long now = System.currentTimeMillis(); + headers.put("Date", rfc1123Date(now)); + headers.put("Expires", rfc1123Date(now + ONE_HOUR_MILLIS)); + headers.put("Cache-Control", "public, max-age=86400"); + + Cache.Entry entry = HttpHeaderParser.parseCacheHeaders(response); + + assertNotNull(entry); + assertNull(entry.etag); + assertEqualsWithin(now + 24 * ONE_HOUR_MILLIS, entry.ttl, ONE_MINUTE_MILLIS); + assertEquals(entry.softTtl, entry.ttl); + } + + @Test public void parseCacheHeaders_cacheControlNoCache() { + long now = System.currentTimeMillis(); + headers.put("Date", rfc1123Date(now)); + headers.put("Expires", rfc1123Date(now + ONE_HOUR_MILLIS)); + headers.put("Cache-Control", "no-cache"); + + Cache.Entry entry = HttpHeaderParser.parseCacheHeaders(response); + + assertNull(entry); + } + + @Test public void parseCacheHeaders_cacheControlMustRevalidate() { + long now = System.currentTimeMillis(); + headers.put("Date", rfc1123Date(now)); + headers.put("Expires", rfc1123Date(now + ONE_HOUR_MILLIS)); + headers.put("Cache-Control", "must-revalidate"); + + Cache.Entry entry = HttpHeaderParser.parseCacheHeaders(response); + + assertNotNull(entry); + assertNull(entry.etag); + assertEqualsWithin(now, entry.ttl, ONE_MINUTE_MILLIS); + assertEquals(entry.softTtl, entry.ttl); + } + + private void assertEqualsWithin(long expected, long value, long fudgeFactor) { + long diff = Math.abs(expected - value); + assertTrue(diff < fudgeFactor); + } + + private static String rfc1123Date(long millis) { + DateFormat df = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.ENGLISH); + return df.format(new Date(millis)); + } + + // -------------------------- + + @Test public void parseCharset() { + // Like the ones we usually see + headers.put("Content-Type", "text/plain; charset=utf-8"); + assertEquals("utf-8", HttpHeaderParser.parseCharset(headers)); + + // Extra whitespace + headers.put("Content-Type", "text/plain; charset=utf-8 "); + assertEquals("utf-8", HttpHeaderParser.parseCharset(headers)); + + // Extra parameters + headers.put("Content-Type", "text/plain; charset=utf-8; frozzle=bar"); + assertEquals("utf-8", HttpHeaderParser.parseCharset(headers)); + + // No Content-Type header + headers.clear(); + assertEquals("ISO-8859-1", HttpHeaderParser.parseCharset(headers)); + + // Empty value + headers.put("Content-Type", "text/plain; charset="); + assertEquals("ISO-8859-1", HttpHeaderParser.parseCharset(headers)); + + // None specified + headers.put("Content-Type", "text/plain"); + assertEquals("ISO-8859-1", HttpHeaderParser.parseCharset(headers)); + + // None specified, extra semicolon + headers.put("Content-Type", "text/plain;"); + assertEquals("ISO-8859-1", HttpHeaderParser.parseCharset(headers)); + } + + @Test public void parseCaseInsensitive() { + + long now = System.currentTimeMillis(); + + Header[] headersArray = new Header[5]; + headersArray[0] = new BasicHeader("eTAG", "Yow!"); + headersArray[1] = new BasicHeader("DATE", rfc1123Date(now)); + headersArray[2] = new BasicHeader("expires", rfc1123Date(now + ONE_HOUR_MILLIS)); + headersArray[3] = new BasicHeader("cache-control", "public, max-age=86400"); + headersArray[4] = new BasicHeader("content-type", "text/plain"); + + Map<String, String> headers = BasicNetwork.convertHeaders(headersArray); + NetworkResponse response = new NetworkResponse(0, null, headers, false); + Cache.Entry entry = HttpHeaderParser.parseCacheHeaders(response); + + assertNotNull(entry); + assertEquals("Yow!", entry.etag); + assertEqualsWithin(now + 24 * ONE_HOUR_MILLIS, entry.ttl, ONE_MINUTE_MILLIS); + assertEquals(entry.softTtl, entry.ttl); + assertEquals("ISO-8859-1", HttpHeaderParser.parseCharset(headers)); + } +} diff --git a/src/test/java/com/android/volley/toolbox/HurlStackTest.java b/src/test/java/com/android/volley/toolbox/HurlStackTest.java new file mode 100644 index 0000000..42aeea8 --- /dev/null +++ b/src/test/java/com/android/volley/toolbox/HurlStackTest.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.volley.toolbox; + +import com.android.volley.Request.Method; +import com.android.volley.mock.MockHttpURLConnection; +import com.android.volley.mock.TestRequest; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +import static org.junit.Assert.*; + +@RunWith(RobolectricTestRunner.class) +public class HurlStackTest { + + private MockHttpURLConnection mMockConnection; + + @Before public void setUp() throws Exception { + mMockConnection = new MockHttpURLConnection(); + } + + @Test public void connectionForDeprecatedGetRequest() throws Exception { + TestRequest.DeprecatedGet request = new TestRequest.DeprecatedGet(); + assertEquals(request.getMethod(), Method.DEPRECATED_GET_OR_POST); + + HurlStack.setConnectionParametersForRequest(mMockConnection, request); + assertEquals("GET", mMockConnection.getRequestMethod()); + assertFalse(mMockConnection.getDoOutput()); + } + + @Test public void connectionForDeprecatedPostRequest() throws Exception { + TestRequest.DeprecatedPost request = new TestRequest.DeprecatedPost(); + assertEquals(request.getMethod(), Method.DEPRECATED_GET_OR_POST); + + HurlStack.setConnectionParametersForRequest(mMockConnection, request); + assertEquals("POST", mMockConnection.getRequestMethod()); + assertTrue(mMockConnection.getDoOutput()); + } + + @Test public void connectionForGetRequest() throws Exception { + TestRequest.Get request = new TestRequest.Get(); + assertEquals(request.getMethod(), Method.GET); + + HurlStack.setConnectionParametersForRequest(mMockConnection, request); + assertEquals("GET", mMockConnection.getRequestMethod()); + assertFalse(mMockConnection.getDoOutput()); + } + + @Test public void connectionForPostRequest() throws Exception { + TestRequest.Post request = new TestRequest.Post(); + assertEquals(request.getMethod(), Method.POST); + + HurlStack.setConnectionParametersForRequest(mMockConnection, request); + assertEquals("POST", mMockConnection.getRequestMethod()); + assertFalse(mMockConnection.getDoOutput()); + } + + @Test public void connectionForPostWithBodyRequest() throws Exception { + TestRequest.PostWithBody request = new TestRequest.PostWithBody(); + assertEquals(request.getMethod(), Method.POST); + + HurlStack.setConnectionParametersForRequest(mMockConnection, request); + assertEquals("POST", mMockConnection.getRequestMethod()); + assertTrue(mMockConnection.getDoOutput()); + } + + @Test public void connectionForPutRequest() throws Exception { + TestRequest.Put request = new TestRequest.Put(); + assertEquals(request.getMethod(), Method.PUT); + + HurlStack.setConnectionParametersForRequest(mMockConnection, request); + assertEquals("PUT", mMockConnection.getRequestMethod()); + assertFalse(mMockConnection.getDoOutput()); + } + + @Test public void connectionForPutWithBodyRequest() throws Exception { + TestRequest.PutWithBody request = new TestRequest.PutWithBody(); + assertEquals(request.getMethod(), Method.PUT); + + HurlStack.setConnectionParametersForRequest(mMockConnection, request); + assertEquals("PUT", mMockConnection.getRequestMethod()); + assertTrue(mMockConnection.getDoOutput()); + } + + @Test public void connectionForDeleteRequest() throws Exception { + TestRequest.Delete request = new TestRequest.Delete(); + assertEquals(request.getMethod(), Method.DELETE); + + HurlStack.setConnectionParametersForRequest(mMockConnection, request); + assertEquals("DELETE", mMockConnection.getRequestMethod()); + assertFalse(mMockConnection.getDoOutput()); + } + + @Test public void connectionForHeadRequest() throws Exception { + TestRequest.Head request = new TestRequest.Head(); + assertEquals(request.getMethod(), Method.HEAD); + + HurlStack.setConnectionParametersForRequest(mMockConnection, request); + assertEquals("HEAD", mMockConnection.getRequestMethod()); + assertFalse(mMockConnection.getDoOutput()); + } + + @Test public void connectionForOptionsRequest() throws Exception { + TestRequest.Options request = new TestRequest.Options(); + assertEquals(request.getMethod(), Method.OPTIONS); + + HurlStack.setConnectionParametersForRequest(mMockConnection, request); + assertEquals("OPTIONS", mMockConnection.getRequestMethod()); + assertFalse(mMockConnection.getDoOutput()); + } + + @Test public void connectionForTraceRequest() throws Exception { + TestRequest.Trace request = new TestRequest.Trace(); + assertEquals(request.getMethod(), Method.TRACE); + + HurlStack.setConnectionParametersForRequest(mMockConnection, request); + assertEquals("TRACE", mMockConnection.getRequestMethod()); + assertFalse(mMockConnection.getDoOutput()); + } + + @Test public void connectionForPatchRequest() throws Exception { + TestRequest.Patch request = new TestRequest.Patch(); + assertEquals(request.getMethod(), Method.PATCH); + + HurlStack.setConnectionParametersForRequest(mMockConnection, request); + assertEquals("PATCH", mMockConnection.getRequestMethod()); + assertFalse(mMockConnection.getDoOutput()); + } + + @Test public void connectionForPatchWithBodyRequest() throws Exception { + TestRequest.PatchWithBody request = new TestRequest.PatchWithBody(); + assertEquals(request.getMethod(), Method.PATCH); + + HurlStack.setConnectionParametersForRequest(mMockConnection, request); + assertEquals("PATCH", mMockConnection.getRequestMethod()); + assertTrue(mMockConnection.getDoOutput()); + } +} diff --git a/src/test/java/com/android/volley/toolbox/ImageRequestTest.java b/src/test/java/com/android/volley/toolbox/ImageRequestTest.java new file mode 100644 index 0000000..2f4495a --- /dev/null +++ b/src/test/java/com/android/volley/toolbox/ImageRequestTest.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.volley.toolbox; + +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; + +import com.android.volley.NetworkResponse; +import com.android.volley.Response; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.shadows.ShadowBitmapFactory; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import static org.junit.Assert.*; + +@RunWith(RobolectricTestRunner.class) +public class ImageRequestTest { + + @Test public void parseNetworkResponse_resizing() throws Exception { + // This is a horrible hack but Robolectric doesn't have a way to provide + // width and height hints for decodeByteArray. It works because the byte array + // "file:fake" is ASCII encodable and thus the name in Robolectric's fake + // bitmap creator survives as-is, and provideWidthAndHeightHints puts + // "file:" + name in its lookaside map. I write all this because it will + // probably break mysteriously at some point and I feel terrible about your + // having to debug it. + byte[] jpegBytes = "file:fake".getBytes(); + ShadowBitmapFactory.provideWidthAndHeightHints("fake", 1024, 500); + NetworkResponse jpeg = new NetworkResponse(jpegBytes); + + // No resize + verifyResize(jpeg, 0, 0, 1024, 500); + + // Exact sizes + verifyResize(jpeg, 512, 250, 512, 250); // exactly half + verifyResize(jpeg, 511, 249, 509, 249); // just under half + verifyResize(jpeg, 1080, 500, 1024, 500); // larger + verifyResize(jpeg, 500, 500, 500, 244); // keep same ratio + + // Specify only width, preserve aspect ratio + verifyResize(jpeg, 512, 0, 512, 250); + verifyResize(jpeg, 800, 0, 800, 390); + verifyResize(jpeg, 1024, 0, 1024, 500); + + // Specify only height, preserve aspect ratio + verifyResize(jpeg, 0, 250, 512, 250); + verifyResize(jpeg, 0, 391, 800, 391); + verifyResize(jpeg, 0, 500, 1024, 500); + } + + private void verifyResize(NetworkResponse networkResponse, int maxWidth, int maxHeight, + int expectedWidth, int expectedHeight) { + ImageRequest request = new ImageRequest( + "", null, maxWidth, maxHeight, Config.RGB_565, null); + Response<Bitmap> response = request.parseNetworkResponse(networkResponse); + assertNotNull(response); + assertTrue(response.isSuccess()); + Bitmap bitmap = response.result; + assertNotNull(bitmap); + assertEquals(expectedWidth, bitmap.getWidth()); + assertEquals(expectedHeight, bitmap.getHeight()); + } + + @Test public void findBestSampleSize() { + // desired == actual == 1 + assertEquals(1, ImageRequest.findBestSampleSize(100, 150, 100, 150)); + + // exactly half == 2 + assertEquals(2, ImageRequest.findBestSampleSize(280, 160, 140, 80)); + + // just over half == 1 + assertEquals(1, ImageRequest.findBestSampleSize(1000, 800, 501, 401)); + + // just under 1/4 == 4 + assertEquals(4, ImageRequest.findBestSampleSize(100, 200, 24, 50)); + } + + private static byte[] readInputStream(InputStream in) throws IOException { + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int count; + while ((count = in.read(buffer)) != -1) { + bytes.write(buffer, 0, count); + } + in.close(); + return bytes.toByteArray(); + } +} diff --git a/src/test/java/com/android/volley/toolbox/NetworkImageViewTest.java b/src/test/java/com/android/volley/toolbox/NetworkImageViewTest.java new file mode 100644 index 0000000..48c81b6 --- /dev/null +++ b/src/test/java/com/android/volley/toolbox/NetworkImageViewTest.java @@ -0,0 +1,53 @@ +package com.android.volley.toolbox; + +import android.view.ViewGroup.LayoutParams; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; + +import static org.junit.Assert.*; + +@RunWith(RobolectricTestRunner.class) +public class NetworkImageViewTest { + private NetworkImageView mNIV; + private MockImageLoader mMockImageLoader; + + @Before public void setUp() throws Exception { + mMockImageLoader = new MockImageLoader(); + mNIV = new NetworkImageView(Robolectric.application); + } + + @Test public void setImageUrl_requestsImage() { + mNIV.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); + mNIV.setImageUrl("http://foo", mMockImageLoader); + assertEquals("http://foo", mMockImageLoader.lastRequestUrl); + assertEquals(0, mMockImageLoader.lastMaxWidth); + assertEquals(0, mMockImageLoader.lastMaxHeight); + } + + // public void testSetImageUrl_setsMaxSize() { + // // TODO: Not sure how to make getWidth() return something from an + // // instrumentation test. Write this test once it's figured out. + // } + + private class MockImageLoader extends ImageLoader { + public MockImageLoader() { + super(null, null); + } + + public String lastRequestUrl; + public int lastMaxWidth; + public int lastMaxHeight; + + public ImageContainer get(String requestUrl, ImageListener imageListener, int maxWidth, + int maxHeight) { + lastRequestUrl = requestUrl; + lastMaxWidth = maxWidth; + lastMaxHeight = maxHeight; + return null; + } + } +} diff --git a/src/test/java/com/android/volley/toolbox/PoolingByteArrayOutputStreamTest.java b/src/test/java/com/android/volley/toolbox/PoolingByteArrayOutputStreamTest.java new file mode 100644 index 0000000..c3bfac7 --- /dev/null +++ b/src/test/java/com/android/volley/toolbox/PoolingByteArrayOutputStreamTest.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.volley.toolbox; + +import java.io.IOException; +import java.util.Arrays; + +import org.junit.Test; + +import static org.junit.Assert.*; + +public class PoolingByteArrayOutputStreamTest { + @Test public void pooledOneBuffer() throws IOException { + ByteArrayPool pool = new ByteArrayPool(32768); + writeOneBuffer(pool); + writeOneBuffer(pool); + writeOneBuffer(pool); + } + + @Test public void pooledIndividualWrites() throws IOException { + ByteArrayPool pool = new ByteArrayPool(32768); + writeBytesIndividually(pool); + writeBytesIndividually(pool); + writeBytesIndividually(pool); + } + + @Test public void unpooled() throws IOException { + ByteArrayPool pool = new ByteArrayPool(0); + writeOneBuffer(pool); + writeOneBuffer(pool); + writeOneBuffer(pool); + } + + @Test public void unpooledIndividualWrites() throws IOException { + ByteArrayPool pool = new ByteArrayPool(0); + writeBytesIndividually(pool); + writeBytesIndividually(pool); + writeBytesIndividually(pool); + } + + private void writeOneBuffer(ByteArrayPool pool) throws IOException { + byte[] data = new byte[16384]; + for (int i = 0; i < data.length; i++) { + data[i] = (byte) (i & 0xff); + } + PoolingByteArrayOutputStream os = new PoolingByteArrayOutputStream(pool); + os.write(data); + + assertTrue(Arrays.equals(data, os.toByteArray())); + } + + private void writeBytesIndividually(ByteArrayPool pool) { + byte[] data = new byte[16384]; + for (int i = 0; i < data.length; i++) { + data[i] = (byte) (i & 0xff); + } + PoolingByteArrayOutputStream os = new PoolingByteArrayOutputStream(pool); + for (int i = 0; i < data.length; i++) { + os.write(data[i]); + } + + assertTrue(Arrays.equals(data, os.toByteArray())); + } +} diff --git a/src/test/java/com/android/volley/utils/CacheTestUtils.java b/src/test/java/com/android/volley/utils/CacheTestUtils.java new file mode 100644 index 0000000..cd2b8e7 --- /dev/null +++ b/src/test/java/com/android/volley/utils/CacheTestUtils.java @@ -0,0 +1,40 @@ +// Copyright 2011 Google Inc. All Rights Reserved. + +package com.android.volley.utils; + +import com.android.volley.Cache; + +import java.util.Random; + +public class CacheTestUtils { + + /** + * Makes a random cache entry. + * @param data Data to use, or null to use random data + * @param isExpired Whether the TTLs should be set such that this entry is expired + * @param needsRefresh Whether the TTLs should be set such that this entry needs refresh + */ + public static Cache.Entry makeRandomCacheEntry( + byte[] data, boolean isExpired, boolean needsRefresh) { + Random random = new Random(); + Cache.Entry entry = new Cache.Entry(); + if (data != null) { + entry.data = data; + } else { + entry.data = new byte[random.nextInt(1024)]; + } + entry.etag = String.valueOf(random.nextLong()); + entry.serverDate = random.nextLong(); + entry.ttl = isExpired ? 0 : Long.MAX_VALUE; + entry.softTtl = needsRefresh ? 0 : Long.MAX_VALUE; + return entry; + } + + /** + * Like {@link #makeRandomCacheEntry(byte[], boolean, boolean)} but + * defaults to an unexpired entry. + */ + public static Cache.Entry makeRandomCacheEntry(byte[] data) { + return makeRandomCacheEntry(data, false, false); + } +} diff --git a/src/test/java/com/android/volley/utils/ImmediateResponseDelivery.java b/src/test/java/com/android/volley/utils/ImmediateResponseDelivery.java new file mode 100644 index 0000000..666e0d0 --- /dev/null +++ b/src/test/java/com/android/volley/utils/ImmediateResponseDelivery.java @@ -0,0 +1,23 @@ +// Copyright 2011 Google Inc. All rights reserved. + +package com.android.volley.utils; + +import com.android.volley.ExecutorDelivery; + +import java.util.concurrent.Executor; + +/** + * A ResponseDelivery for testing that immediately delivers responses + * instead of posting back to the main thread. + */ +public class ImmediateResponseDelivery extends ExecutorDelivery { + + public ImmediateResponseDelivery() { + super(new Executor() { + @Override + public void execute(Runnable command) { + command.run(); + } + }); + } +} |