blob: 6794af82f5696a079121a3d7e81d2e61a0e3874c [file] [log] [blame]
package com.android.volley.toolbox;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import com.android.volley.Request;
import com.android.volley.RetryPolicy;
import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.http.Header;
import org.apache.http.HttpRequest;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpUriRequest;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.robolectric.RobolectricTestRunner;
/** Tests to validate that HttpStack implementations conform with expected behavior. */
@RunWith(RobolectricTestRunner.class)
public class HttpStackConformanceTest {
@Mock private RetryPolicy mMockRetryPolicy;
@Mock private Request mMockRequest;
@Mock private HttpURLConnection mMockConnection;
@Mock private OutputStream mMockOutputStream;
@Spy private HurlStack mHurlStack = new HurlStack();
@Mock private HttpClient mMockHttpClient;
private HttpClientStack mHttpClientStack;
private final TestCase[] mTestCases =
new TestCase[] {
// TestCase for HurlStack.
new TestCase() {
@Override
public HttpStack getStack() {
return mHurlStack;
}
@Override
public void setOutputHeaderMap(final Map<String, String> outputHeaderMap) {
doAnswer(
new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) {
outputHeaderMap.put(
invocation.<String>getArgument(0),
invocation.<String>getArgument(1));
return null;
}
})
.when(mMockConnection)
.setRequestProperty(anyString(), anyString());
doAnswer(
new Answer<Map<String, List<String>>>() {
@Override
public Map<String, List<String>> answer(
InvocationOnMock invocation) {
Map<String, List<String>> result = new HashMap<>();
for (Map.Entry<String, String> entry :
outputHeaderMap.entrySet()) {
result.put(
entry.getKey(),
Collections.singletonList(
entry.getValue()));
}
return result;
}
})
.when(mMockConnection)
.getRequestProperties();
}
},
// TestCase for HttpClientStack.
new TestCase() {
@Override
public HttpStack getStack() {
return mHttpClientStack;
}
@Override
public void setOutputHeaderMap(final Map<String, String> outputHeaderMap) {
try {
doAnswer(
new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation)
throws Throwable {
HttpRequest request = invocation.getArgument(0);
for (Header header : request.getAllHeaders()) {
if (outputHeaderMap.containsKey(
header.getName())) {
fail(
"Multiple values for header "
+ header.getName());
}
outputHeaderMap.put(
header.getName(),
header.getValue());
}
return null;
}
})
.when(mMockHttpClient)
.execute(any(HttpUriRequest.class));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
};
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mHttpClientStack = spy(new HttpClientStack(mMockHttpClient));
doReturn(mMockConnection).when(mHurlStack).createConnection(any(URL.class));
doReturn(mMockOutputStream).when(mMockConnection).getOutputStream();
when(mMockRequest.getUrl()).thenReturn("http://127.0.0.1");
when(mMockRequest.getRetryPolicy()).thenReturn(mMockRetryPolicy);
}
@Test
public void headerPrecedence() throws Exception {
Map<String, String> additionalHeaders = new HashMap<>();
additionalHeaders.put("A", "AddlA");
additionalHeaders.put("B", "AddlB");
Map<String, String> requestHeaders = new HashMap<>();
requestHeaders.put("A", "RequestA");
requestHeaders.put("C", "RequestC");
when(mMockRequest.getHeaders()).thenReturn(requestHeaders);
when(mMockRequest.getMethod()).thenReturn(Request.Method.POST);
when(mMockRequest.getBody()).thenReturn(new byte[0]);
when(mMockRequest.getBodyContentType()).thenReturn("BodyContentType");
for (TestCase testCase : mTestCases) {
// Test once without a Content-Type header in getHeaders().
Map<String, String> combinedHeaders = new HashMap<>();
testCase.setOutputHeaderMap(combinedHeaders);
testCase.getStack().performRequest(mMockRequest, additionalHeaders);
Map<String, String> expectedHeaders = new HashMap<>();
expectedHeaders.put("A", "RequestA");
expectedHeaders.put("B", "AddlB");
expectedHeaders.put("C", "RequestC");
expectedHeaders.put(HttpHeaderParser.HEADER_CONTENT_TYPE, "BodyContentType");
assertEquals(expectedHeaders, combinedHeaders);
// Reset and test again with a Content-Type header in getHeaders().
combinedHeaders.clear();
requestHeaders.put(HttpHeaderParser.HEADER_CONTENT_TYPE, "RequestContentType");
expectedHeaders.put(HttpHeaderParser.HEADER_CONTENT_TYPE, "RequestContentType");
testCase.getStack().performRequest(mMockRequest, additionalHeaders);
assertEquals(expectedHeaders, combinedHeaders);
// Clear the Content-Type header for the next TestCase.
requestHeaders.remove(HttpHeaderParser.HEADER_CONTENT_TYPE);
}
}
private interface TestCase {
HttpStack getStack();
void setOutputHeaderMap(Map<String, String> outputHeaderMap);
}
}