/* * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ package com.facebook.imagepipeline.producers; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; import java.util.LinkedList; import java.util.Queue; import android.net.Uri; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.any; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; @RunWith(PowerMockRunner.class) @PrepareForTest({ HttpUrlConnectionNetworkFetcher.class, Uri.class }) public class HttpUrlConnectionNetworkFetcherTest { public static final String INITIAL_TEST_URL = "http://localhost/"; public static final String HTTPS_URL = "https://localhost/"; @Mock private FetchState mMockFetchState; @Mock private ProducerContext mMockProducerContext; @Mock private NetworkFetcher.Callback mMockCallback; private HttpUrlConnectionNetworkFetcher mFetcher; private Queue<HttpURLConnection> mConnectionsQueue; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mFetcher = new HttpUrlConnectionNetworkFetcher(); mConnectionsQueue = new LinkedList<>(); mockUrlConnections(); mockUriParse(); mockFetchState(); } private void mockUrlConnections() throws Exception { URL mockUrl = PowerMockito.mock(URL.class); PowerMockito.whenNew(URL.class).withAnyArguments().thenReturn(mockUrl); PowerMockito.when(mockUrl.openConnection()).then(new Answer<URLConnection>() { @Override public URLConnection answer(InvocationOnMock invocation) throws Throwable { return mConnectionsQueue.poll(); } }); } private void mockUriParse() { PowerMockito.mockStatic(Uri.class); PowerMockito.when(Uri.parse(anyString())).then(new Answer<Uri>() { @Override public Uri answer(InvocationOnMock invocation) throws Throwable { return mockUri((String) invocation.getArguments()[0]); } }); } private Uri mockUri(final String url) { Uri mockUri = mock(Uri.class); when(mockUri.toString()).thenReturn(url); when(mockUri.getScheme()).then(new Answer<String>() { @Override public String answer(InvocationOnMock invocation) throws Throwable { return url.substring(0, url.indexOf(':')); } }); return mockUri; } private void mockFetchState() { when(mMockFetchState.getContext()).thenReturn(mMockProducerContext); when(mMockFetchState.getUri()).then(new Answer<Uri>() { @Override public Uri answer(InvocationOnMock invocation) throws Throwable { return mockUri(INITIAL_TEST_URL); } }); } @Test public void testFetchSendsSuccessToCallback() throws IOException { InputStream mockInputStream = mock(InputStream.class); HttpURLConnection mockConnection = mockSuccessWithStream(mockInputStream); runFetch(); InOrder inOrder = inOrder(mMockCallback, mockConnection); inOrder.verify(mockConnection).getInputStream(); inOrder.verify(mMockCallback).onResponse(mockInputStream, -1); inOrder.verify(mockConnection).disconnect(); verifyNoMoreInteractions(mMockCallback); } @Test public void testFetchSendsErrorToCallbackAfterHttpError() throws IOException { HttpURLConnection mockResponse = mockFailure(); runFetch(); verify(mMockCallback).onFailure(any(IOException.class)); verify(mockResponse).disconnect(); verifyNoMoreInteractions(mMockCallback); } @Test public void testFetchSendsSuccessToCallbackAfterRedirect() throws IOException { HttpURLConnection mockRedirect = mockRedirectTo(HTTPS_URL); InputStream mockInputStream = mock(InputStream.class); HttpURLConnection mockRedirectedConnection = mockSuccessWithStream(mockInputStream); runFetch(); verify(mockRedirect).disconnect(); InOrder inOrder = inOrder(mMockCallback, mockRedirectedConnection); inOrder.verify(mMockCallback).onResponse(mockInputStream, -1); inOrder.verify(mockRedirectedConnection).disconnect(); verifyNoMoreInteractions(mMockCallback); } @Test public void testFetchSendsErrorToCallbackAfterRedirectToSameLocation() throws IOException { HttpURLConnection mockRedirect = mockRedirectTo(INITIAL_TEST_URL); HttpURLConnection mockSuccess = mockSuccess(); runFetch(); verify(mMockCallback).onFailure(any(IOException.class)); verify(mockRedirect).disconnect(); verifyZeroInteractions(mockSuccess); verifyNoMoreInteractions(mMockCallback); } @Test public void testFetchSendsErrorToCallbackAfterTooManyRedirects() throws IOException { mockRedirectTo(HTTPS_URL); mockRedirectTo(INITIAL_TEST_URL); mockRedirectTo(HTTPS_URL); mockRedirectTo(INITIAL_TEST_URL); mockRedirectTo(HTTPS_URL); mockRedirectTo(INITIAL_TEST_URL); HttpURLConnection mockResponseAfterSixRedirects = mockSuccess(); runFetch(); verify(mMockCallback).onFailure(any(IOException.class)); verifyZeroInteractions(mockResponseAfterSixRedirects); verifyNoMoreInteractions(mMockCallback); } private HttpURLConnection mockSuccess() throws IOException { return mockSuccessWithStream(mock(InputStream.class)); } private HttpURLConnection mockSuccessWithStream(InputStream is) throws IOException { HttpURLConnection mockResponse = mock(HttpURLConnection.class); when(mockResponse.getResponseCode()).thenReturn(200); when(mockResponse.getInputStream()).thenReturn(is); queueConnection(mockResponse); return mockResponse; } private HttpURLConnection mockFailure() throws IOException { HttpURLConnection mockResponse = mock(HttpURLConnection.class); when(mockResponse.getResponseCode()).thenReturn(404); queueConnection(mockResponse); return mockResponse; } private HttpURLConnection mockRedirectTo(String redirectUrl) throws IOException { HttpURLConnection mockResponse = mock(HttpURLConnection.class); when(mockResponse.getResponseCode()).thenReturn(301); when(mockResponse.getHeaderField("Location")).thenReturn(redirectUrl); queueConnection(mockResponse); return mockResponse; } private void queueConnection(HttpURLConnection connection) { mConnectionsQueue.add(connection); } private void runFetch() { mFetcher.fetchSync(mMockFetchState, mMockCallback); } }