1 /*
2  * Copyright (C) 2012 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 
17 package com.android.volley.toolbox;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.fail;
21 import static org.mockito.ArgumentMatchers.anyString;
22 import static org.mockito.Mockito.never;
23 import static org.mockito.Mockito.verify;
24 import static org.mockito.Mockito.when;
25 
26 import com.android.volley.Header;
27 import com.android.volley.Request.Method;
28 import com.android.volley.mock.TestRequest;
29 import java.io.ByteArrayInputStream;
30 import java.io.ByteArrayOutputStream;
31 import java.io.IOException;
32 import java.net.HttpURLConnection;
33 import java.net.SocketTimeoutException;
34 import java.net.URL;
35 import java.nio.charset.StandardCharsets;
36 import java.util.ArrayList;
37 import java.util.Collections;
38 import java.util.HashMap;
39 import java.util.List;
40 import java.util.Map;
41 import org.junit.Before;
42 import org.junit.Test;
43 import org.junit.runner.RunWith;
44 import org.mockito.Mock;
45 import org.mockito.MockitoAnnotations;
46 import org.robolectric.RobolectricTestRunner;
47 
48 @RunWith(RobolectricTestRunner.class)
49 public class HurlStackTest {
50 
51     @Mock private HttpURLConnection mMockConnection;
52     private HurlStack mHurlStack;
53 
54     @Before
setUp()55     public void setUp() throws Exception {
56         MockitoAnnotations.initMocks(this);
57         when(mMockConnection.getOutputStream()).thenReturn(new ByteArrayOutputStream());
58 
59         mHurlStack =
60                 new HurlStack() {
61                     @Override
62                     protected HttpURLConnection createConnection(URL url) {
63                         return mMockConnection;
64                     }
65                 };
66     }
67 
68     @Test
connectionForDeprecatedGetRequest()69     public void connectionForDeprecatedGetRequest() throws Exception {
70         TestRequest.DeprecatedGet request = new TestRequest.DeprecatedGet();
71         assertEquals(request.getMethod(), Method.DEPRECATED_GET_OR_POST);
72 
73         HurlStack.setConnectionParametersForRequest(mMockConnection, request);
74         verify(mMockConnection, never()).setRequestMethod(anyString());
75         verify(mMockConnection, never()).setDoOutput(true);
76     }
77 
78     @Test
connectionForDeprecatedPostRequest()79     public void connectionForDeprecatedPostRequest() throws Exception {
80         TestRequest.DeprecatedPost request = new TestRequest.DeprecatedPost();
81         assertEquals(request.getMethod(), Method.DEPRECATED_GET_OR_POST);
82 
83         HurlStack.setConnectionParametersForRequest(mMockConnection, request);
84         verify(mMockConnection).setRequestMethod("POST");
85         verify(mMockConnection).setDoOutput(true);
86     }
87 
88     @Test
connectionForGetRequest()89     public void connectionForGetRequest() throws Exception {
90         TestRequest.Get request = new TestRequest.Get();
91         assertEquals(request.getMethod(), Method.GET);
92 
93         HurlStack.setConnectionParametersForRequest(mMockConnection, request);
94         verify(mMockConnection).setRequestMethod("GET");
95         verify(mMockConnection, never()).setDoOutput(true);
96     }
97 
98     @Test
connectionForPostRequest()99     public void connectionForPostRequest() throws Exception {
100         TestRequest.Post request = new TestRequest.Post();
101         assertEquals(request.getMethod(), Method.POST);
102 
103         HurlStack.setConnectionParametersForRequest(mMockConnection, request);
104         verify(mMockConnection).setRequestMethod("POST");
105         verify(mMockConnection, never()).setDoOutput(true);
106     }
107 
108     @Test
connectionForPostWithBodyRequest()109     public void connectionForPostWithBodyRequest() throws Exception {
110         TestRequest.PostWithBody request = new TestRequest.PostWithBody();
111         assertEquals(request.getMethod(), Method.POST);
112 
113         HurlStack.setConnectionParametersForRequest(mMockConnection, request);
114         verify(mMockConnection).setRequestMethod("POST");
115         verify(mMockConnection).setDoOutput(true);
116     }
117 
118     @Test
connectionForPutRequest()119     public void connectionForPutRequest() throws Exception {
120         TestRequest.Put request = new TestRequest.Put();
121         assertEquals(request.getMethod(), Method.PUT);
122 
123         HurlStack.setConnectionParametersForRequest(mMockConnection, request);
124         verify(mMockConnection).setRequestMethod("PUT");
125         verify(mMockConnection, never()).setDoOutput(true);
126     }
127 
128     @Test
connectionForPutWithBodyRequest()129     public void connectionForPutWithBodyRequest() throws Exception {
130         TestRequest.PutWithBody request = new TestRequest.PutWithBody();
131         assertEquals(request.getMethod(), Method.PUT);
132 
133         HurlStack.setConnectionParametersForRequest(mMockConnection, request);
134         verify(mMockConnection).setRequestMethod("PUT");
135         verify(mMockConnection).setDoOutput(true);
136     }
137 
138     @Test
connectionForDeleteRequest()139     public void connectionForDeleteRequest() throws Exception {
140         TestRequest.Delete request = new TestRequest.Delete();
141         assertEquals(request.getMethod(), Method.DELETE);
142 
143         HurlStack.setConnectionParametersForRequest(mMockConnection, request);
144         verify(mMockConnection).setRequestMethod("DELETE");
145         verify(mMockConnection, never()).setDoOutput(true);
146     }
147 
148     @Test
connectionForHeadRequest()149     public void connectionForHeadRequest() throws Exception {
150         TestRequest.Head request = new TestRequest.Head();
151         assertEquals(request.getMethod(), Method.HEAD);
152 
153         HurlStack.setConnectionParametersForRequest(mMockConnection, request);
154         verify(mMockConnection).setRequestMethod("HEAD");
155         verify(mMockConnection, never()).setDoOutput(true);
156     }
157 
158     @Test
connectionForOptionsRequest()159     public void connectionForOptionsRequest() throws Exception {
160         TestRequest.Options request = new TestRequest.Options();
161         assertEquals(request.getMethod(), Method.OPTIONS);
162 
163         HurlStack.setConnectionParametersForRequest(mMockConnection, request);
164         verify(mMockConnection).setRequestMethod("OPTIONS");
165         verify(mMockConnection, never()).setDoOutput(true);
166     }
167 
168     @Test
connectionForTraceRequest()169     public void connectionForTraceRequest() throws Exception {
170         TestRequest.Trace request = new TestRequest.Trace();
171         assertEquals(request.getMethod(), Method.TRACE);
172 
173         HurlStack.setConnectionParametersForRequest(mMockConnection, request);
174         verify(mMockConnection).setRequestMethod("TRACE");
175         verify(mMockConnection, never()).setDoOutput(true);
176     }
177 
178     @Test
connectionForPatchRequest()179     public void connectionForPatchRequest() throws Exception {
180         TestRequest.Patch request = new TestRequest.Patch();
181         assertEquals(request.getMethod(), Method.PATCH);
182 
183         HurlStack.setConnectionParametersForRequest(mMockConnection, request);
184         verify(mMockConnection).setRequestMethod("PATCH");
185         verify(mMockConnection, never()).setDoOutput(true);
186     }
187 
188     @Test
connectionForPatchWithBodyRequest()189     public void connectionForPatchWithBodyRequest() throws Exception {
190         TestRequest.PatchWithBody request = new TestRequest.PatchWithBody();
191         assertEquals(request.getMethod(), Method.PATCH);
192 
193         HurlStack.setConnectionParametersForRequest(mMockConnection, request);
194         verify(mMockConnection).setRequestMethod("PATCH");
195         verify(mMockConnection).setDoOutput(true);
196     }
197 
198     @Test
executeRequestClosesConnection_connectionError()199     public void executeRequestClosesConnection_connectionError() throws Exception {
200         when(mMockConnection.getResponseCode()).thenThrow(new SocketTimeoutException());
201         try {
202             mHurlStack.executeRequest(
203                     new TestRequest.Get(), Collections.<String, String>emptyMap());
204             fail("Should have thrown exception");
205         } catch (IOException e) {
206             verify(mMockConnection).disconnect();
207         }
208     }
209 
210     @Test
executeRequestClosesConnection_invalidResponseCode()211     public void executeRequestClosesConnection_invalidResponseCode() throws Exception {
212         when(mMockConnection.getResponseCode()).thenReturn(-1);
213         try {
214             mHurlStack.executeRequest(
215                     new TestRequest.Get(), Collections.<String, String>emptyMap());
216             fail("Should have thrown exception");
217         } catch (IOException e) {
218             verify(mMockConnection).disconnect();
219         }
220     }
221 
222     @Test
executeRequestClosesConnection_noResponseBody()223     public void executeRequestClosesConnection_noResponseBody() throws Exception {
224         when(mMockConnection.getResponseCode()).thenReturn(HttpURLConnection.HTTP_NO_CONTENT);
225         mHurlStack.executeRequest(new TestRequest.Get(), Collections.<String, String>emptyMap());
226         verify(mMockConnection).disconnect();
227     }
228 
229     @Test
executeRequestClosesConnection_hasResponseBody()230     public void executeRequestClosesConnection_hasResponseBody() throws Exception {
231         when(mMockConnection.getResponseCode()).thenReturn(HttpURLConnection.HTTP_OK);
232         when(mMockConnection.getInputStream())
233                 .thenReturn(new ByteArrayInputStream("hello".getBytes(StandardCharsets.UTF_8)));
234         HttpResponse response =
235                 mHurlStack.executeRequest(
236                         new TestRequest.Get(), Collections.<String, String>emptyMap());
237         // Shouldn't be disconnected until the stream is consumed.
238         verify(mMockConnection, never()).disconnect();
239         response.getContent().close();
240         verify(mMockConnection).disconnect();
241     }
242 
243     @Test
convertHeaders()244     public void convertHeaders() {
245         Map<String, List<String>> headers = new HashMap<>();
246         headers.put(null, Collections.singletonList("Ignored"));
247         headers.put("HeaderA", Collections.singletonList("ValueA"));
248         List<String> values = new ArrayList<>();
249         values.add("ValueB_1");
250         values.add("ValueB_2");
251         headers.put("HeaderB", values);
252         List<Header> result = HurlStack.convertHeaders(headers);
253         List<Header> expected = new ArrayList<>();
254         expected.add(new Header("HeaderA", "ValueA"));
255         expected.add(new Header("HeaderB", "ValueB_1"));
256         expected.add(new Header("HeaderB", "ValueB_2"));
257         assertEquals(expected, result);
258     }
259 }
260