1/*
2 * Copyright (C) 2015 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.RequestQueue.RequestFinishedListener;
21import com.android.volley.mock.MockRequest;
22import com.android.volley.mock.ShadowSystemClock;
23import com.android.volley.toolbox.NoCache;
24import com.android.volley.utils.ImmediateResponseDelivery;
25import org.junit.Before;
26import org.junit.Test;
27import org.junit.runner.RunWith;
28import org.mockito.Mock;
29import org.mockito.invocation.InvocationOnMock;
30import org.mockito.stubbing.Answer;
31import org.robolectric.RobolectricTestRunner;
32import org.robolectric.annotation.Config;
33
34import java.util.ArrayList;
35import java.util.List;
36import java.util.Random;
37import java.util.concurrent.Semaphore;
38import java.util.concurrent.TimeUnit;
39
40import static org.junit.Assert.*;
41import static org.mockito.Mockito.*;
42import static org.mockito.MockitoAnnotations.initMocks;
43
44
45/**
46 * Integration tests for {@link RequestQueue}, that verify its behavior in conjunction with real dispatcher, queues and
47 * Requests. Network is mocked out
48 */
49@RunWith(RobolectricTestRunner.class)
50@Config(shadows = {ShadowSystemClock.class})
51public class RequestQueueIntegrationTest {
52
53    private ResponseDelivery mDelivery;
54    @Mock private Network mMockNetwork;
55
56    @Before public void setUp() throws Exception {
57        mDelivery = new ImmediateResponseDelivery();
58        initMocks(this);
59    }
60
61    @Test public void add_requestProcessedInCorrectOrder() throws Exception {
62        // Enqueue 2 requests with different cache keys, and different priorities. The second, higher priority request
63        // takes 20ms.
64        // Assert that first request is only handled after the first one has been parsed and delivered.
65        MockRequest lowerPriorityReq = new MockRequest();
66        MockRequest higherPriorityReq = new MockRequest();
67        lowerPriorityReq.setCacheKey("1");
68        higherPriorityReq.setCacheKey("2");
69        lowerPriorityReq.setPriority(Priority.LOW);
70        higherPriorityReq.setPriority(Priority.HIGH);
71
72        RequestFinishedListener listener = mock(RequestFinishedListener.class);
73        Answer<NetworkResponse> delayAnswer = new Answer<NetworkResponse>() {
74            @Override
75            public NetworkResponse answer(InvocationOnMock invocationOnMock) throws Throwable {
76                Thread.sleep(20);
77                return mock(NetworkResponse.class);
78            }
79        };
80        //delay only for higher request
81        when(mMockNetwork.performRequest(higherPriorityReq)).thenAnswer(delayAnswer);
82        when(mMockNetwork.performRequest(lowerPriorityReq)).thenReturn(mock(NetworkResponse.class));
83
84        RequestQueue queue = new RequestQueue(new NoCache(), mMockNetwork, 1, mDelivery);
85        queue.addRequestFinishedListener(listener);
86        queue.add(lowerPriorityReq);
87        queue.add(higherPriorityReq);
88        queue.start();
89
90        // you cannot do strict order verification in combination with timeouts with mockito 1.9.5 :(
91        // as an alternative, first verify no requests have finished, while higherPriorityReq should be processing
92        verifyNoMoreInteractions(listener);
93        // verify higherPriorityReq goes through first
94        verify(listener, timeout(100)).onRequestFinished(higherPriorityReq);
95        // verify lowerPriorityReq goes last
96        verify(listener, timeout(10)).onRequestFinished(lowerPriorityReq);
97        queue.stop();
98    }
99
100    /**
101     * Asserts that requests with same cache key are processed in order.
102     *
103     * Needs to be an integration test because relies on complex interations between various queues
104     */
105    @Test public void add_dedupeByCacheKey() throws Exception {
106        // Enqueue 2 requests with the same cache key. The first request takes 20ms. Assert that the
107        // second request is only handled after the first one has been parsed and delivered.
108        Request req1 = new MockRequest();
109        Request req2 = new MockRequest();
110        RequestFinishedListener listener = mock(RequestFinishedListener.class);
111        Answer<NetworkResponse> delayAnswer = new Answer<NetworkResponse>() {
112            @Override
113            public NetworkResponse answer(InvocationOnMock invocationOnMock) throws Throwable {
114                Thread.sleep(20);
115                return mock(NetworkResponse.class);
116            }
117        };
118        //delay only for first
119        when(mMockNetwork.performRequest(req1)).thenAnswer(delayAnswer);
120        when(mMockNetwork.performRequest(req2)).thenReturn(mock(NetworkResponse.class));
121
122        RequestQueue queue = new RequestQueue(new NoCache(), mMockNetwork, 3, mDelivery);
123        queue.addRequestFinishedListener(listener);
124        queue.add(req1);
125        queue.add(req2);
126        queue.start();
127
128        // you cannot do strict order verification with mockito 1.9.5 :(
129        // as an alternative, first verify no requests have finished, then verify req1 goes through
130        verifyNoMoreInteractions(listener);
131        verify(listener, timeout(100)).onRequestFinished(req1);
132        verify(listener, timeout(10)).onRequestFinished(req2);
133        queue.stop();
134    }
135
136    /**
137     * Verify RequestFinishedListeners are informed when requests are canceled
138     *
139     * Needs to be an integration test because relies on Request -> dispatcher -> RequestQueue interaction
140     */
141    @Test public void add_requestFinishedListenerCanceled() throws Exception {
142        RequestFinishedListener listener = mock(RequestFinishedListener.class);
143        Request request = new MockRequest();
144        Answer<NetworkResponse> delayAnswer = new Answer<NetworkResponse>() {
145            @Override
146            public NetworkResponse answer(InvocationOnMock invocationOnMock) throws Throwable {
147                Thread.sleep(200);
148                return mock(NetworkResponse.class);
149            }
150        };
151        RequestQueue queue = new RequestQueue(new NoCache(), mMockNetwork, 1, mDelivery);
152
153        when(mMockNetwork.performRequest(request)).thenAnswer(delayAnswer);
154
155        queue.addRequestFinishedListener(listener);
156        queue.start();
157        queue.add(request);
158
159        request.cancel();
160        verify(listener, timeout(100)).onRequestFinished(request);
161        queue.stop();
162    }
163
164    /**
165     * Verify RequestFinishedListeners are informed when requests are successfully delivered
166     *
167     * Needs to be an integration test because relies on Request -> dispatcher -> RequestQueue interaction
168     */
169    @Test public void add_requestFinishedListenerSuccess() throws Exception {
170        NetworkResponse response = mock(NetworkResponse.class);
171        Request request = new MockRequest();
172        RequestFinishedListener listener = mock(RequestFinishedListener.class);
173        RequestFinishedListener listener2 = mock(RequestFinishedListener.class);
174        RequestQueue queue = new RequestQueue(new NoCache(), mMockNetwork, 1, mDelivery);
175
176        queue.addRequestFinishedListener(listener);
177        queue.addRequestFinishedListener(listener2);
178        queue.start();
179        queue.add(request);
180
181        verify(listener, timeout(100)).onRequestFinished(request);
182        verify(listener2, timeout(100)).onRequestFinished(request);
183
184        queue.stop();
185    }
186
187    /**
188     * Verify RequestFinishedListeners are informed when request errors
189     *
190     * Needs to be an integration test because relies on Request -> dispatcher -> RequestQueue interaction
191     */
192    @Test public void add_requestFinishedListenerError() throws Exception {
193        RequestFinishedListener listener = mock(RequestFinishedListener.class);
194        Request request = new MockRequest();
195        RequestQueue queue = new RequestQueue(new NoCache(), mMockNetwork, 1, mDelivery);
196
197        when(mMockNetwork.performRequest(request)).thenThrow(new VolleyError());
198
199        queue.addRequestFinishedListener(listener);
200        queue.start();
201        queue.add(request);
202
203        verify(listener, timeout(100)).onRequestFinished(request);
204        queue.stop();
205    }
206
207}
208