/* * Copyright (C) 2013 Square, Inc. * * 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.squareup.picasso; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.Handler; import android.os.Message; import com.squareup.picasso.NetworkRequestHandler.ContentLengthException; import java.util.Arrays; import java.util.concurrent.ExecutorService; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.robolectric.RobolectricGradleTestRunner; import static android.content.Context.CONNECTIVITY_SERVICE; import static android.content.Intent.ACTION_AIRPLANE_MODE_CHANGED; import static android.content.pm.PackageManager.PERMISSION_DENIED; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static com.squareup.picasso.Dispatcher.NetworkBroadcastReceiver; import static com.squareup.picasso.Dispatcher.NetworkBroadcastReceiver.EXTRA_AIRPLANE_STATE; import static com.squareup.picasso.Picasso.LoadedFrom.MEMORY; import static com.squareup.picasso.TestUtils.URI_1; import static com.squareup.picasso.TestUtils.URI_2; import static com.squareup.picasso.TestUtils.URI_KEY_1; import static com.squareup.picasso.TestUtils.URI_KEY_2; import static com.squareup.picasso.TestUtils.makeBitmap; import static com.squareup.picasso.TestUtils.mockAction; import static com.squareup.picasso.TestUtils.mockCallback; import static com.squareup.picasso.TestUtils.mockHunter; import static com.squareup.picasso.TestUtils.mockNetworkInfo; import static com.squareup.picasso.TestUtils.mockPicasso; import static com.squareup.picasso.TestUtils.mockTarget; import static org.fest.assertions.api.Assertions.assertThat; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; @RunWith(RobolectricGradleTestRunner.class) public class DispatcherTest { @Mock Context context; @Mock ConnectivityManager connectivityManager; @Mock PicassoExecutorService service; @Mock ExecutorService serviceMock; @Mock Handler mainThreadHandler; @Mock Downloader downloader; @Mock Cache cache; @Mock Stats stats; private Dispatcher dispatcher; final Bitmap bitmap1 = makeBitmap(); final Bitmap bitmap2 = makeBitmap(); @Before public void setUp() { initMocks(this); dispatcher = createDispatcher(); } @Test public void shutdownStopsService() { dispatcher.shutdown(); verify(service).shutdown(); } @Test public void shutdownStopsDownloader() { dispatcher.shutdown(); verify(downloader).shutdown(); } @Test public void shutdownUnregistersReceiver() { dispatcher.shutdown(); verify(context).unregisterReceiver(dispatcher.receiver); } @Test public void performSubmitWithNewRequestQueuesHunter() { Action action = mockAction(URI_KEY_1, URI_1); dispatcher.performSubmit(action); assertThat(dispatcher.hunterMap).hasSize(1); verify(service).submit(any(BitmapHunter.class)); } @Test public void performSubmitWithTwoDifferentRequestsQueuesHunters() { Action action1 = mockAction(URI_KEY_1, URI_1); Action action2 = mockAction(URI_KEY_2, URI_2); dispatcher.performSubmit(action1); dispatcher.performSubmit(action2); assertThat(dispatcher.hunterMap).hasSize(2); verify(service, times(2)).submit(any(BitmapHunter.class)); } @Test public void performSubmitWithExistingRequestAttachesToHunter() { Action action1 = mockAction(URI_KEY_1, URI_1); Action action2 = mockAction(URI_KEY_1, URI_1); dispatcher.performSubmit(action1); dispatcher.performSubmit(action2); assertThat(dispatcher.hunterMap).hasSize(1); verify(service).submit(any(BitmapHunter.class)); } @Test public void performSubmitWithShutdownServiceIgnoresRequest() { when(service.isShutdown()).thenReturn(true); Action action = mockAction(URI_KEY_1, URI_1); dispatcher.performSubmit(action); assertThat(dispatcher.hunterMap).isEmpty(); verify(service, never()).submit(any(BitmapHunter.class)); } @Test public void performSubmitWithShutdownAttachesRequest() { BitmapHunter hunter = mockHunter(URI_KEY_1, bitmap1, false); dispatcher.hunterMap.put(URI_KEY_1, hunter); when(service.isShutdown()).thenReturn(true); Action action = mockAction(URI_KEY_1, URI_1); dispatcher.performSubmit(action); assertThat(dispatcher.hunterMap).hasSize(1); verify(hunter).attach(action); verify(service, never()).submit(any(BitmapHunter.class)); } @Test public void performSubmitWithFetchAction() { String pausedTag = "pausedTag"; dispatcher.pausedTags.add(pausedTag); assertThat(dispatcher.pausedActions).isEmpty(); FetchAction fetchAction1 = new FetchAction(mockPicasso(), new Request.Builder(URI_1).build(), 0, 0, pausedTag, URI_KEY_1, null); FetchAction fetchAction2 = new FetchAction(mockPicasso(), new Request.Builder(URI_1).build(), 0, 0, pausedTag, URI_KEY_1, null); dispatcher.performSubmit(fetchAction1); dispatcher.performSubmit(fetchAction2); assertThat(dispatcher.pausedActions).hasSize(2); } @Test public void performSubmitWithFetchActionWithSuccessCompletionCallback() { String pausedTag = "pausedTag"; Callback callback = mockCallback(); FetchAction fetchAction = new FetchAction(mockPicasso(), new Request.Builder(URI_1).build(), 0, 0, pausedTag, URI_KEY_1, callback); dispatcher.performSubmit(fetchAction); fetchAction.complete(bitmap1, MEMORY); verify(callback).onSuccess(); } @Test public void performSubmitWithFetchActionWithErrorCompletionCallback() { String pausedTag = "pausedTag"; Callback callback = mockCallback(); FetchAction fetchAction = new FetchAction(mockPicasso(), new Request.Builder(URI_1).build(), 0, 0, pausedTag, URI_KEY_1, callback); dispatcher.performSubmit(fetchAction, false); Exception e = new RuntimeException(); fetchAction.error(e); verify(callback).onError(e); } @Test public void performCancelWithFetchActionWithCallback() { String pausedTag = "pausedTag"; dispatcher.pausedTags.add(pausedTag); assertThat(dispatcher.pausedActions).isEmpty(); Callback callback = mockCallback(); FetchAction fetchAction1 = new FetchAction(mockPicasso(), new Request.Builder(URI_1).build(), 0, 0, pausedTag, URI_KEY_1, callback); dispatcher.performCancel(fetchAction1); fetchAction1.cancel(); assertThat(dispatcher.pausedActions).isEmpty(); } @Test public void performCancelDetachesRequestAndCleansUp() { Target target = mockTarget(); Action action = mockAction(URI_KEY_1, URI_1, target); BitmapHunter hunter = mockHunter(URI_KEY_1, bitmap1, false); hunter.attach(action); when(hunter.cancel()).thenReturn(true); dispatcher.hunterMap.put(URI_KEY_1, hunter); dispatcher.failedActions.put(target, action); dispatcher.performCancel(action); verify(hunter).detach(action); verify(hunter).cancel(); assertThat(dispatcher.hunterMap).isEmpty(); assertThat(dispatcher.failedActions).isEmpty(); } @Test public void performCancelMultipleRequestsDetachesOnly() { Action action1 = mockAction(URI_KEY_1, URI_1); Action action2 = mockAction(URI_KEY_1, URI_1); BitmapHunter hunter = mockHunter(URI_KEY_1, bitmap1, false); hunter.attach(action1); hunter.attach(action2); dispatcher.hunterMap.put(URI_KEY_1, hunter); dispatcher.performCancel(action1); verify(hunter).detach(action1); verify(hunter).cancel(); assertThat(dispatcher.hunterMap).hasSize(1); } @Test public void performCancelUnqueuesAndDetachesPausedRequest() { Action action = mockAction(URI_KEY_1, URI_1, mockTarget(), "tag"); BitmapHunter hunter = mockHunter(URI_KEY_1, bitmap1, false, action); dispatcher.hunterMap.put(URI_KEY_1, hunter); dispatcher.pausedTags.add("tag"); dispatcher.pausedActions.put(action.getTarget(), action); dispatcher.performCancel(action); assertThat(dispatcher.pausedTags).hasSize(1).contains("tag"); assertThat(dispatcher.pausedActions).isEmpty(); verify(hunter).detach(action); } @Test public void performCompleteSetsResultInCache() { BitmapHunter hunter = mockHunter(URI_KEY_1, bitmap1, 0, null); dispatcher.performComplete(hunter); verify(cache).set(hunter.getKey(), hunter.getResult()); } @Test public void performCompleteWithNoStoreMemoryPolicy() { int memoryPolicy = 0; memoryPolicy |= MemoryPolicy.NO_STORE.index; BitmapHunter hunter = mockHunter(URI_KEY_1, bitmap1, memoryPolicy, null); dispatcher.performComplete(hunter); assertThat(dispatcher.hunterMap).isEmpty(); verifyZeroInteractions(cache); } @Test public void performCompleteCleansUpAndAddsToBatch() { BitmapHunter hunter = mockHunter(URI_KEY_1, bitmap1, false); dispatcher.performComplete(hunter); assertThat(dispatcher.hunterMap).isEmpty(); assertThat(dispatcher.batch).hasSize(1); } @Test public void performCompleteCleansUpAndDoesNotAddToBatchIfCancelled() { BitmapHunter hunter = mockHunter(URI_KEY_1, bitmap1, false); when(hunter.isCancelled()).thenReturn(true); dispatcher.performComplete(hunter); assertThat(dispatcher.hunterMap).isEmpty(); assertThat(dispatcher.batch).isEmpty(); } @Test public void performErrorCleansUpAndAddsToBatch() { BitmapHunter hunter = mockHunter(URI_KEY_1, bitmap1, false); dispatcher.hunterMap.put(hunter.getKey(), hunter); dispatcher.performError(hunter, false); assertThat(dispatcher.hunterMap).isEmpty(); assertThat(dispatcher.batch).hasSize(1); } @Test public void performErrorCleansUpAndDoesNotAddToBatchIfCancelled() { BitmapHunter hunter = mockHunter(URI_KEY_1, bitmap1, false); when(hunter.isCancelled()).thenReturn(true); dispatcher.hunterMap.put(hunter.getKey(), hunter); dispatcher.performError(hunter, false); assertThat(dispatcher.hunterMap).isEmpty(); assertThat(dispatcher.batch).isEmpty(); } @Test public void performBatchCompleteFlushesHunters() { BitmapHunter hunter1 = mockHunter(URI_KEY_2, bitmap1, false); BitmapHunter hunter2 = mockHunter(URI_KEY_2, bitmap2, false); dispatcher.batch.add(hunter1); dispatcher.batch.add(hunter2); dispatcher.performBatchComplete(); assertThat(dispatcher.batch).isEmpty(); } @Test public void performRetrySkipsIfHunterIsCancelled() { BitmapHunter hunter = mockHunter(URI_KEY_2, bitmap1, false); when(hunter.isCancelled()).thenReturn(true); dispatcher.performRetry(hunter); verifyZeroInteractions(service); assertThat(dispatcher.hunterMap).isEmpty(); assertThat(dispatcher.failedActions).isEmpty(); } @Test public void performRetryForContentLengthResetsNetworkPolicy() { NetworkInfo networkInfo = mockNetworkInfo(true); BitmapHunter hunter = mockHunter(URI_KEY_2, bitmap1, false); when(hunter.shouldRetry(anyBoolean(), any(NetworkInfo.class))).thenReturn(true); when(hunter.getException()).thenReturn(new ContentLengthException("304 error")); when(connectivityManager.getActiveNetworkInfo()).thenReturn(networkInfo); dispatcher.performRetry(hunter); assertThat(NetworkPolicy.shouldReadFromDiskCache(hunter.networkPolicy)).isFalse(); } @Test public void performRetryDoesNotMarkForReplayIfNotSupported() { NetworkInfo networkInfo = mockNetworkInfo(true); BitmapHunter hunter = mockHunter(URI_KEY_1, bitmap1, false, mockAction(URI_KEY_1, URI_1)); when(hunter.supportsReplay()).thenReturn(false); when(hunter.shouldRetry(anyBoolean(), any(NetworkInfo.class))).thenReturn(false); when(connectivityManager.getActiveNetworkInfo()).thenReturn(networkInfo); dispatcher.performRetry(hunter); assertThat(dispatcher.hunterMap).isEmpty(); assertThat(dispatcher.failedActions).isEmpty(); verify(service, never()).submit(hunter); } @Test public void performRetryDoesNotMarkForReplayIfNoNetworkScanning() { BitmapHunter hunter = mockHunter(URI_KEY_1, bitmap1, false, mockAction(URI_KEY_1, URI_1)); when(hunter.shouldRetry(anyBoolean(), any(NetworkInfo.class))).thenReturn(false); when(hunter.supportsReplay()).thenReturn(true); Dispatcher dispatcher = createDispatcher(false); dispatcher.performRetry(hunter); assertThat(dispatcher.hunterMap).isEmpty(); assertThat(dispatcher.failedActions).isEmpty(); verify(service, never()).submit(hunter); } @Test public void performRetryMarksForReplayIfSupportedScansNetworkChangesAndShouldNotRetry() { NetworkInfo networkInfo = mockNetworkInfo(true); Action action = mockAction(URI_KEY_1, URI_1, mockTarget()); BitmapHunter hunter = mockHunter(URI_KEY_1, bitmap1, false, action); when(hunter.supportsReplay()).thenReturn(true); when(hunter.shouldRetry(anyBoolean(), any(NetworkInfo.class))).thenReturn(false); when(connectivityManager.getActiveNetworkInfo()).thenReturn(networkInfo); dispatcher.performRetry(hunter); assertThat(dispatcher.hunterMap).isEmpty(); assertThat(dispatcher.failedActions).hasSize(1); verify(service, never()).submit(hunter); } @Test public void performRetryRetriesIfNoNetworkScanning() { BitmapHunter hunter = mockHunter(URI_KEY_1, bitmap1, false, mockAction(URI_KEY_1, URI_1)); when(hunter.shouldRetry(anyBoolean(), any(NetworkInfo.class))).thenReturn(true); Dispatcher dispatcher = createDispatcher(false); dispatcher.performRetry(hunter); assertThat(dispatcher.hunterMap).isEmpty(); assertThat(dispatcher.failedActions).isEmpty(); verify(service).submit(hunter); } @Test public void performRetryMarksForReplayIfSupportsReplayAndShouldNotRetry() { Action action = mockAction(URI_KEY_1, URI_1, mockTarget()); BitmapHunter hunter = mockHunter(URI_KEY_1, bitmap1, false, action); when(hunter.shouldRetry(anyBoolean(), any(NetworkInfo.class))).thenReturn(false); when(hunter.supportsReplay()).thenReturn(true); dispatcher.performRetry(hunter); assertThat(dispatcher.hunterMap).isEmpty(); assertThat(dispatcher.failedActions).hasSize(1); verify(service, never()).submit(hunter); } @Test public void performRetryRetriesIfShouldRetry() { Action action = mockAction(URI_KEY_1, URI_1, mockTarget()); BitmapHunter hunter = mockHunter(URI_KEY_1, bitmap1, false, action); when(hunter.shouldRetry(anyBoolean(), any(NetworkInfo.class))).thenReturn(true); dispatcher.performRetry(hunter); assertThat(dispatcher.hunterMap).isEmpty(); assertThat(dispatcher.failedActions).isEmpty(); verify(service).submit(hunter); } @Test public void performRetrySkipIfServiceShutdown() { when(service.isShutdown()).thenReturn(true); BitmapHunter hunter = mockHunter(URI_KEY_1, bitmap1, false); dispatcher.performRetry(hunter); verify(service, never()).submit(hunter); assertThat(dispatcher.hunterMap).isEmpty(); assertThat(dispatcher.failedActions).isEmpty(); } @Test public void performAirplaneModeChange() { assertThat(dispatcher.airplaneMode).isFalse(); dispatcher.performAirplaneModeChange(true); assertThat(dispatcher.airplaneMode).isTrue(); dispatcher.performAirplaneModeChange(false); assertThat(dispatcher.airplaneMode).isFalse(); } @Test public void performNetworkStateChangeWithNullInfo() { dispatcher.performNetworkStateChange(null); verify(service, times(1)).adjustThreadCount(null); } @Test public void performNetworkStateChangeWithDisconnectedInfo() { NetworkInfo info = mockNetworkInfo(); when(info.isConnectedOrConnecting()).thenReturn(false); dispatcher.performNetworkStateChange(info); verify(service, times(1)).adjustThreadCount(info); } @Test public void performNetworkStateChangeWithConnectedInfoDifferentInstance() { NetworkInfo info = mockNetworkInfo(true); dispatcher.performNetworkStateChange(info); verify(service, times(1)).adjustThreadCount(info); } @Test public void performNetworkStateChangeWithNullInfoIgnores() { Dispatcher dispatcher = createDispatcher(serviceMock); dispatcher.performNetworkStateChange(null); verifyZeroInteractions(service); } @Test public void performNetworkStateChangeWithDisconnectedInfoIgnores() { Dispatcher dispatcher = createDispatcher(serviceMock); NetworkInfo info = mockNetworkInfo(); when(info.isConnectedOrConnecting()).thenReturn(false); dispatcher.performNetworkStateChange(info); verifyZeroInteractions(service); } @Test public void performNetworkStateChangeWithConnectedInfoDifferentInstanceIgnores() { Dispatcher dispatcher = createDispatcher(serviceMock); NetworkInfo info = mockNetworkInfo(true); dispatcher.performNetworkStateChange(info); verifyZeroInteractions(service); } @Test public void performPauseAndResumeUpdatesListOfPausedTags() { dispatcher.performPauseTag("tag"); assertThat(dispatcher.pausedTags).hasSize(1).contains("tag"); dispatcher.performResumeTag("tag"); assertThat(dispatcher.pausedTags).isEmpty(); } @Test public void performPauseTagIsIdempotent() { Action action = mockAction(URI_KEY_1, URI_1, mockTarget(), "tag"); BitmapHunter hunter = mockHunter(URI_KEY_1, bitmap1, false, action); dispatcher.hunterMap.put(URI_KEY_1, hunter); dispatcher.pausedTags.add("tag"); dispatcher.performPauseTag("tag"); verify(hunter, never()).getAction(); } @Test public void performPauseTagQueuesNewRequestDoesNotSubmit() { dispatcher.performPauseTag("tag"); Action action = mockAction(URI_KEY_1, URI_1, "tag"); dispatcher.performSubmit(action); assertThat(dispatcher.hunterMap).isEmpty(); assertThat(dispatcher.pausedActions).hasSize(1).containsValue(action); verify(service, never()).submit(any(BitmapHunter.class)); } @Test public void performPauseTagDoesNotQueueUnrelatedRequest() { dispatcher.performPauseTag("tag"); Action action = mockAction(URI_KEY_1, URI_1, "anothertag"); dispatcher.performSubmit(action); assertThat(dispatcher.hunterMap).hasSize(1); assertThat(dispatcher.pausedActions).isEmpty(); verify(service).submit(any(BitmapHunter.class)); } @Test public void performPauseDetachesRequestAndCancelsHunter() { Action action = mockAction(URI_KEY_1, URI_1, "tag"); BitmapHunter hunter = mockHunter(URI_KEY_1, bitmap1, false, action); when(hunter.cancel()).thenReturn(true); dispatcher.hunterMap.put(URI_KEY_1, hunter); dispatcher.performPauseTag("tag"); assertThat(dispatcher.hunterMap).isEmpty(); assertThat(dispatcher.pausedActions).hasSize(1).containsValue(action); verify(hunter).detach(action); verify(hunter).cancel(); } @Test public void performPauseOnlyDetachesPausedRequest() { Action action1 = mockAction(URI_KEY_1, URI_1, mockTarget(), "tag1"); Action action2 = mockAction(URI_KEY_1, URI_1, mockTarget(), "tag2"); BitmapHunter hunter = mockHunter(URI_KEY_1, bitmap1, false); when(hunter.getActions()).thenReturn(Arrays.asList(action1, action2)); dispatcher.hunterMap.put(URI_KEY_1, hunter); dispatcher.performPauseTag("tag1"); assertThat(dispatcher.hunterMap).hasSize(1).containsValue(hunter); assertThat(dispatcher.pausedActions).hasSize(1).containsValue(action1); verify(hunter).detach(action1); verify(hunter, never()).detach(action2); } @Test public void performResumeTagIsIdempotent() { dispatcher.performResumeTag("tag"); verify(mainThreadHandler, never()).sendMessage(any(Message.class)); } @Test public void performNetworkStateChangeWithConnectedInfoAndPicassoExecutorServiceAdjustsThreads() { PicassoExecutorService service = mock(PicassoExecutorService.class); NetworkInfo info = mockNetworkInfo(true); Dispatcher dispatcher = createDispatcher(service); dispatcher.performNetworkStateChange(info); verify(service).adjustThreadCount(info); verifyZeroInteractions(service); } @Test public void performNetworkStateChangeFlushesFailedHunters() { PicassoExecutorService service = mock(PicassoExecutorService.class); NetworkInfo info = mockNetworkInfo(true); Dispatcher dispatcher = createDispatcher(service); Action failedAction1 = mockAction(URI_KEY_1, URI_1); Action failedAction2 = mockAction(URI_KEY_2, URI_2); dispatcher.failedActions.put(URI_KEY_1, failedAction1); dispatcher.failedActions.put(URI_KEY_2, failedAction2); dispatcher.performNetworkStateChange(info); verify(service, times(2)).submit(any(BitmapHunter.class)); assertThat(dispatcher.failedActions).isEmpty(); } @Test public void nullIntentOnReceiveDoesNothing() { Dispatcher dispatcher = mock(Dispatcher.class); NetworkBroadcastReceiver receiver = new NetworkBroadcastReceiver(dispatcher); receiver.onReceive(context, null); verifyZeroInteractions(dispatcher); } @Test public void nullExtrasOnReceiveConnectivityAreOk() { ConnectivityManager connectivityManager = mock(ConnectivityManager.class); NetworkInfo networkInfo = mockNetworkInfo(); when(connectivityManager.getActiveNetworkInfo()).thenReturn(networkInfo); when(context.getSystemService(CONNECTIVITY_SERVICE)).thenReturn(connectivityManager); Dispatcher dispatcher = mock(Dispatcher.class); NetworkBroadcastReceiver receiver = new NetworkBroadcastReceiver(dispatcher); receiver.onReceive(context, new Intent(CONNECTIVITY_ACTION)); verify(dispatcher).dispatchNetworkStateChange(networkInfo); } @Test public void nullExtrasOnReceiveAirplaneDoesNothing() { Dispatcher dispatcher = mock(Dispatcher.class); NetworkBroadcastReceiver receiver = new NetworkBroadcastReceiver(dispatcher); receiver.onReceive(context, new Intent(ACTION_AIRPLANE_MODE_CHANGED)); verifyZeroInteractions(dispatcher); } @Test public void correctExtrasOnReceiveAirplaneDispatches() { setAndVerifyAirplaneMode(false); setAndVerifyAirplaneMode(true); } private void setAndVerifyAirplaneMode(boolean airplaneOn) { Dispatcher dispatcher = mock(Dispatcher.class); NetworkBroadcastReceiver receiver = new NetworkBroadcastReceiver(dispatcher); final Intent intent = new Intent(ACTION_AIRPLANE_MODE_CHANGED); intent.putExtra(EXTRA_AIRPLANE_STATE, airplaneOn); receiver.onReceive(context, intent); verify(dispatcher).dispatchAirplaneModeChange(airplaneOn); } private Dispatcher createDispatcher() { return createDispatcher(service); } private Dispatcher createDispatcher(boolean scansNetworkChanges) { return createDispatcher(service, scansNetworkChanges); } private Dispatcher createDispatcher(ExecutorService service) { return createDispatcher(service, true); } private Dispatcher createDispatcher(ExecutorService service, boolean scansNetworkChanges) { when(context.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(connectivityManager); when(context.checkCallingOrSelfPermission(anyString())).thenReturn( scansNetworkChanges ? PERMISSION_GRANTED : PERMISSION_DENIED); return new Dispatcher(context, service, mainThreadHandler, downloader, cache, stats); } }