651 lines
27 KiB
Java
651 lines
27 KiB
Java
/*
|
|
* Copyright 2022 Google LLC
|
|
*
|
|
* 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.google.android.libraries.mobiledatadownload;
|
|
|
|
import static com.google.android.libraries.mobiledatadownload.DownloadException.DownloadResultCode.ANDROID_DOWNLOADER_UNKNOWN;
|
|
import static com.google.common.truth.Truth.assertThat;
|
|
import static org.junit.Assert.assertThrows;
|
|
import static org.junit.Assert.assertTrue;
|
|
import static org.mockito.ArgumentMatchers.any;
|
|
import static org.mockito.Mockito.times;
|
|
import static org.mockito.Mockito.verify;
|
|
import static org.mockito.Mockito.when;
|
|
|
|
import android.content.Context;
|
|
import android.net.Uri;
|
|
import android.util.Log;
|
|
import androidx.test.core.app.ApplicationProvider;
|
|
import com.google.android.libraries.mobiledatadownload.downloader.DownloadConstraints;
|
|
import com.google.android.libraries.mobiledatadownload.downloader.DownloadRequest;
|
|
import com.google.android.libraries.mobiledatadownload.downloader.FileDownloader;
|
|
import com.google.android.libraries.mobiledatadownload.file.SynchronousFileStorage;
|
|
import com.google.android.libraries.mobiledatadownload.file.backends.AndroidFileBackend;
|
|
import com.google.android.libraries.mobiledatadownload.foreground.ForegroundDownloadKey;
|
|
import com.google.android.libraries.mobiledatadownload.internal.logging.LogUtil;
|
|
import com.google.android.libraries.mobiledatadownload.monitor.DownloadProgressMonitor;
|
|
import com.google.android.libraries.mobiledatadownload.monitor.NetworkUsageMonitor;
|
|
import com.google.android.libraries.mobiledatadownload.testing.BlockingFileDownloader;
|
|
import com.google.android.libraries.mobiledatadownload.testing.FakeTimeSource;
|
|
import com.google.android.libraries.mobiledatadownload.testing.TestFlags;
|
|
import com.google.common.base.Optional;
|
|
import com.google.common.base.Supplier;
|
|
import com.google.common.base.Suppliers;
|
|
import com.google.common.collect.ImmutableList;
|
|
import com.google.common.labs.concurrent.LabsFutures;
|
|
import com.google.common.util.concurrent.Futures;
|
|
import com.google.common.util.concurrent.ListenableFuture;
|
|
import com.google.common.util.concurrent.ListeningExecutorService;
|
|
import com.google.common.util.concurrent.MoreExecutors;
|
|
import java.util.concurrent.ExecutionException;
|
|
import java.util.concurrent.Executors;
|
|
import org.junit.After;
|
|
import org.junit.Before;
|
|
import org.junit.Rule;
|
|
import org.junit.Test;
|
|
import org.junit.runner.RunWith;
|
|
import org.mockito.ArgumentCaptor;
|
|
import org.mockito.Captor;
|
|
import org.mockito.Mock;
|
|
import org.mockito.junit.MockitoJUnit;
|
|
import org.mockito.junit.MockitoRule;
|
|
import org.robolectric.RobolectricTestRunner;
|
|
import org.robolectric.shadows.ShadowLog;
|
|
|
|
@RunWith(RobolectricTestRunner.class)
|
|
public final class DownloadFileTest {
|
|
@Rule public final MockitoRule mocks = MockitoJUnit.rule();
|
|
|
|
// 1MB file.
|
|
private static final String FILE_URL =
|
|
"https://www.gstatic.com/icing/idd/sample_group/sample_file_3_1519240701";
|
|
private static final Uri DESTINATION_FILE_URI =
|
|
Uri.parse(
|
|
"android://com.google.android.libraries.mobiledatadownload/files/datadownload/shared/public/file_1");
|
|
|
|
private static final Context context = ApplicationProvider.getApplicationContext();
|
|
|
|
private final TestFlags flags = new TestFlags();
|
|
private final FakeTimeSource clock = new FakeTimeSource();
|
|
|
|
private DownloadProgressMonitor downloadProgressMonitor;
|
|
private SynchronousFileStorage fileStorage;
|
|
|
|
@Mock private SingleFileDownloadListener mockDownloadListener;
|
|
@Mock private NetworkUsageMonitor mockNetworkUsageMonitor;
|
|
@Mock private FileDownloader mockFileDownloader;
|
|
@Mock private DownloadProgressMonitor mockDownloadMonitor;
|
|
|
|
private BlockingFileDownloader blockingFileDownloader;
|
|
private SingleFileDownloadRequest singleFileDownloadRequest;
|
|
private MobileDataDownload mobileDataDownload;
|
|
|
|
@Captor ArgumentCaptor<DownloadListener> downloadListenerCaptor;
|
|
|
|
@Captor
|
|
ArgumentCaptor<com.google.android.libraries.mobiledatadownload.lite.DownloadListener>
|
|
liteDownloadListenerCaptor;
|
|
|
|
@Captor ArgumentCaptor<DownloadRequest> singleFileDownloadRequestCaptor;
|
|
|
|
ListeningExecutorService controlExecutor =
|
|
MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());
|
|
|
|
@Before
|
|
public void setUp() {
|
|
ShadowLog.setLoggable(LogUtil.TAG, Log.DEBUG);
|
|
|
|
downloadProgressMonitor = new DownloadProgressMonitor(clock, controlExecutor);
|
|
|
|
fileStorage =
|
|
new SynchronousFileStorage(
|
|
/* backends= */ ImmutableList.of(AndroidFileBackend.builder(context).build()),
|
|
/* transforms= */ ImmutableList.of(),
|
|
/* monitors= */ ImmutableList.of(downloadProgressMonitor));
|
|
|
|
singleFileDownloadRequest =
|
|
SingleFileDownloadRequest.newBuilder()
|
|
.setDestinationFileUri(DESTINATION_FILE_URI)
|
|
.setUrlToDownload(FILE_URL)
|
|
.setDownloadConstraints(DownloadConstraints.NETWORK_CONNECTED)
|
|
.setNotificationContentTitle("File url: " + FILE_URL)
|
|
.build();
|
|
|
|
blockingFileDownloader = new BlockingFileDownloader(controlExecutor);
|
|
|
|
when(mockDownloadListener.onComplete()).thenReturn(Futures.immediateVoidFuture());
|
|
when(mockFileDownloader.startDownloading(singleFileDownloadRequestCaptor.capture()))
|
|
.thenReturn(Futures.immediateVoidFuture());
|
|
}
|
|
|
|
@After
|
|
public void tearDown() {
|
|
// Reset state of blockingFileDownloader to prevent deadlocks
|
|
blockingFileDownloader.resetState();
|
|
}
|
|
|
|
@Test
|
|
public void downloadFile_whenRequestAlreadyMade_dedups() throws Exception {
|
|
// Use BlockingFileDownloader to ensure first download is in progress.
|
|
mobileDataDownload = getMobileDataDownload(() -> blockingFileDownloader);
|
|
|
|
ListenableFuture<Void> downloadFuture1 =
|
|
mobileDataDownload.downloadFile(singleFileDownloadRequest);
|
|
ListenableFuture<Void> downloadFuture2 =
|
|
mobileDataDownload.downloadFile(singleFileDownloadRequest);
|
|
|
|
// Allow blocking download to finish
|
|
blockingFileDownloader.finishDownloading();
|
|
|
|
// Finish future 2 and assert that future 1 has completed as well
|
|
downloadFuture2.get();
|
|
|
|
awaitAllExecutorsIdle();
|
|
|
|
assertThat(downloadFuture1.isDone()).isTrue();
|
|
}
|
|
|
|
@Test
|
|
public void downloadFile_whenRequestAlreadyMadeUsingForegroundService_dedups() throws Exception {
|
|
// Use BlockingFileDownloader to ensure first download is in progress.
|
|
mobileDataDownload = getMobileDataDownload(() -> blockingFileDownloader);
|
|
|
|
ListenableFuture<Void> downloadFuture1 =
|
|
mobileDataDownload.downloadFileWithForegroundService(singleFileDownloadRequest);
|
|
ListenableFuture<Void> downloadFuture2 =
|
|
mobileDataDownload.downloadFile(singleFileDownloadRequest);
|
|
|
|
// Allow blocking download to finish
|
|
blockingFileDownloader.finishDownloading();
|
|
|
|
// Finish future 2 and assert that future 1 has completed as well
|
|
downloadFuture2.get();
|
|
|
|
awaitAllExecutorsIdle();
|
|
assertThat(downloadFuture1.isDone()).isTrue();
|
|
}
|
|
|
|
@Test
|
|
public void downloadFile_beginsDownload() throws Exception {
|
|
mobileDataDownload =
|
|
getMobileDataDownload(
|
|
() -> mockFileDownloader,
|
|
/* foregroundDownloadServiceClassOptional= */ Optional.absent(),
|
|
Optional.of(mockDownloadMonitor));
|
|
|
|
singleFileDownloadRequest =
|
|
SingleFileDownloadRequest.newBuilder()
|
|
.setDestinationFileUri(DESTINATION_FILE_URI)
|
|
.setUrlToDownload(FILE_URL)
|
|
.setDownloadConstraints(DownloadConstraints.NETWORK_CONNECTED)
|
|
.setListenerOptional(Optional.of(mockDownloadListener))
|
|
.build();
|
|
|
|
ListenableFuture<Void> downloadFuture =
|
|
mobileDataDownload.downloadFile(singleFileDownloadRequest);
|
|
downloadFuture.get();
|
|
|
|
awaitAllExecutorsIdle();
|
|
|
|
// Verify that correct DownloadRequest is sent to underlying FileDownloader
|
|
DownloadRequest actualDownloadRequest = singleFileDownloadRequestCaptor.getValue();
|
|
assertThat(actualDownloadRequest.fileUri()).isEqualTo(DESTINATION_FILE_URI);
|
|
assertThat(actualDownloadRequest.urlToDownload()).isEqualTo(FILE_URL);
|
|
assertThat(actualDownloadRequest.downloadConstraints())
|
|
.isEqualTo(DownloadConstraints.NETWORK_CONNECTED);
|
|
|
|
// Verify that downloadMonitor adds the listener
|
|
verify(mockDownloadMonitor).addDownloadListener(any(), liteDownloadListenerCaptor.capture());
|
|
verify(mockFileDownloader).startDownloading(any());
|
|
|
|
verify(mockDownloadMonitor).removeDownloadListener(DESTINATION_FILE_URI);
|
|
verify(mockDownloadListener).onComplete();
|
|
|
|
// Ensure that given download listener is the same one passed to download monitor
|
|
com.google.android.libraries.mobiledatadownload.lite.DownloadListener capturedDownloadListener =
|
|
liteDownloadListenerCaptor.getValue();
|
|
DownloadException testException =
|
|
DownloadException.builder().setDownloadResultCode(ANDROID_DOWNLOADER_UNKNOWN).build();
|
|
|
|
capturedDownloadListener.onProgress(10);
|
|
capturedDownloadListener.onFailure(testException);
|
|
capturedDownloadListener.onPausedForConnectivity();
|
|
|
|
verify(mockDownloadListener).onProgress(10);
|
|
verify(mockDownloadListener).onFailure(testException);
|
|
verify(mockDownloadListener).onPausedForConnectivity();
|
|
}
|
|
|
|
@Test
|
|
public void download_whenListenerProvided_handlesOnCompleteFailed() throws Exception {
|
|
Exception failureException = new Exception("test failure");
|
|
when(mockDownloadListener.onComplete())
|
|
.thenReturn(Futures.immediateFailedFuture(failureException));
|
|
mobileDataDownload =
|
|
getMobileDataDownload(
|
|
createSuccessfulFileDownloaderSupplier(),
|
|
/* foregroundDownloadServiceClassOptional= */ Optional.absent(),
|
|
Optional.of(downloadProgressMonitor));
|
|
|
|
singleFileDownloadRequest =
|
|
SingleFileDownloadRequest.newBuilder()
|
|
.setDestinationFileUri(DESTINATION_FILE_URI)
|
|
.setUrlToDownload(FILE_URL)
|
|
.setDownloadConstraints(DownloadConstraints.NETWORK_CONNECTED)
|
|
.setListenerOptional(Optional.of(mockDownloadListener))
|
|
.build();
|
|
|
|
mobileDataDownload.downloadFile(singleFileDownloadRequest).get();
|
|
|
|
awaitAllExecutorsIdle();
|
|
|
|
// Verify the DownloadListeners onComplete was invoked
|
|
verify(mockDownloadListener).onComplete();
|
|
}
|
|
|
|
@Test
|
|
public void downloadFile_whenDownloadFails_reportsFailure() throws Exception {
|
|
DownloadException downloadException =
|
|
DownloadException.builder().setDownloadResultCode(ANDROID_DOWNLOADER_UNKNOWN).build();
|
|
|
|
mobileDataDownload =
|
|
getMobileDataDownload(
|
|
createFailingFileDownloaderSupplier(downloadException),
|
|
/* foregroundDownloadServiceClassOptional= */ Optional.absent(),
|
|
Optional.of(downloadProgressMonitor));
|
|
|
|
singleFileDownloadRequest =
|
|
SingleFileDownloadRequest.newBuilder()
|
|
.setDestinationFileUri(DESTINATION_FILE_URI)
|
|
.setUrlToDownload(FILE_URL)
|
|
.setDownloadConstraints(DownloadConstraints.NETWORK_CONNECTED)
|
|
.setListenerOptional(Optional.of(mockDownloadListener))
|
|
.build();
|
|
|
|
ListenableFuture<Void> downloadFuture =
|
|
mobileDataDownload.downloadFile(singleFileDownloadRequest);
|
|
assertThrows(ExecutionException.class, downloadFuture::get);
|
|
DownloadException e = LabsFutures.getFailureCauseAs(downloadFuture, DownloadException.class);
|
|
assertThat(e.getDownloadResultCode()).isEqualTo(ANDROID_DOWNLOADER_UNKNOWN);
|
|
|
|
// Verify that DownloadListener.onFailure was invoked with failure
|
|
verify(mockDownloadListener).onFailure(downloadException);
|
|
verify(mockDownloadListener, times(0)).onComplete();
|
|
}
|
|
|
|
@Test
|
|
public void downloadFile_whenReturnedFutureIsCanceled_cancelsDownload() throws Exception {
|
|
// Wrap mock around BlockingFileDownloader to simulate long download
|
|
mobileDataDownload = getMobileDataDownload(() -> blockingFileDownloader);
|
|
|
|
ListenableFuture<Void> downloadFuture =
|
|
mobileDataDownload.downloadFile(singleFileDownloadRequest);
|
|
|
|
// Wait for download to start and confirm download future is still running.
|
|
blockingFileDownloader.waitForDownloadStarted();
|
|
assertThat(downloadFuture.isDone()).isFalse();
|
|
|
|
// Cancel download future.
|
|
downloadFuture.cancel(true);
|
|
|
|
// Check that future is now cancelled.
|
|
assertThat(downloadFuture.isCancelled()).isTrue();
|
|
}
|
|
|
|
@Test
|
|
public void downloadFile_whenMonitorNotProvided_whenDownloadFails_reportsFailure()
|
|
throws Exception {
|
|
DownloadException downloadException =
|
|
DownloadException.builder().setDownloadResultCode(ANDROID_DOWNLOADER_UNKNOWN).build();
|
|
|
|
mobileDataDownload =
|
|
getMobileDataDownload(createFailingFileDownloaderSupplier(downloadException));
|
|
|
|
singleFileDownloadRequest =
|
|
SingleFileDownloadRequest.newBuilder()
|
|
.setDestinationFileUri(DESTINATION_FILE_URI)
|
|
.setUrlToDownload(FILE_URL)
|
|
.setDownloadConstraints(DownloadConstraints.NETWORK_CONNECTED)
|
|
.setListenerOptional(Optional.of(mockDownloadListener))
|
|
.build();
|
|
|
|
ListenableFuture<Void> downloadFuture =
|
|
mobileDataDownload.downloadFile(singleFileDownloadRequest);
|
|
assertThrows(ExecutionException.class, downloadFuture::get);
|
|
DownloadException e = LabsFutures.getFailureCauseAs(downloadFuture, DownloadException.class);
|
|
assertThat(e.getDownloadResultCode()).isEqualTo(ANDROID_DOWNLOADER_UNKNOWN);
|
|
|
|
// Verify that DownloadListener.onFailure was invoked with failure
|
|
verify(mockDownloadListener).onFailure(downloadException);
|
|
verify(mockDownloadListener, times(0)).onComplete();
|
|
}
|
|
|
|
@Test
|
|
public void downloadFileWithWithForegroundService_requiresForegroundDownloadService()
|
|
throws Exception {
|
|
// Create downloader without providing foreground service
|
|
mobileDataDownload =
|
|
getMobileDataDownload(
|
|
() -> mockFileDownloader,
|
|
/* foregroundDownloadServiceClassOptional= */ Optional.absent(),
|
|
Optional.of(downloadProgressMonitor));
|
|
|
|
// Without foreground service, download call should fail with IllegalStateException
|
|
ListenableFuture<Void> downloadFuture =
|
|
mobileDataDownload.downloadFileWithForegroundService(singleFileDownloadRequest);
|
|
ExecutionException e = assertThrows(ExecutionException.class, downloadFuture::get);
|
|
assertThat(e).hasCauseThat().isInstanceOf(IllegalStateException.class);
|
|
|
|
// Verify that underlying download is not started
|
|
verify(mockFileDownloader, times(0)).startDownloading(any());
|
|
}
|
|
|
|
@Test
|
|
public void downloadFileWithForegroundService_requiresDownloadMonitor() throws Exception {
|
|
// Create downloader without providing DownloadMonitor
|
|
mobileDataDownload =
|
|
getMobileDataDownload(
|
|
() -> mockFileDownloader,
|
|
Optional.of(this.getClass()),
|
|
/* downloadProgressMonitorOptional= */ Optional.absent());
|
|
|
|
// Without monitor, download call should fail with IllegalStateException
|
|
ListenableFuture<Void> downloadFuture =
|
|
mobileDataDownload.downloadFileWithForegroundService(singleFileDownloadRequest);
|
|
ExecutionException e = assertThrows(ExecutionException.class, downloadFuture::get);
|
|
assertThat(e).hasCauseThat().isInstanceOf(IllegalStateException.class);
|
|
|
|
// Verify that underlying download is not started
|
|
verify(mockFileDownloader, times(0)).startDownloading(any());
|
|
}
|
|
|
|
@Test
|
|
public void downloadFileWithForegroundService_whenRequestAlreadyMade_dedups() throws Exception {
|
|
// Use BlockingFileDownloader to control when the download will finish.
|
|
mobileDataDownload = getMobileDataDownload(() -> blockingFileDownloader);
|
|
|
|
ListenableFuture<Void> downloadFuture1 =
|
|
mobileDataDownload.downloadFileWithForegroundService(singleFileDownloadRequest);
|
|
ListenableFuture<Void> downloadFuture2 =
|
|
mobileDataDownload.downloadFileWithForegroundService(singleFileDownloadRequest);
|
|
|
|
// Now we let the 2 futures downloadFuture1 downloadFuture2 to run by opening the latch.
|
|
blockingFileDownloader.finishDownloading();
|
|
|
|
// Now finish future 2, future 1 should finish too and the cache clears the future.
|
|
downloadFuture2.get();
|
|
|
|
awaitAllExecutorsIdle();
|
|
|
|
assertThat(downloadFuture1.isDone()).isTrue();
|
|
}
|
|
|
|
@Test
|
|
public void
|
|
downloadFileWithForegroundService_whenRequestAlreadyMadeWithoutForegroundService_dedups()
|
|
throws Exception {
|
|
// Use BlockingFileDownloader to control when the download will finish.
|
|
mobileDataDownload =
|
|
getMobileDataDownload(
|
|
() -> blockingFileDownloader,
|
|
Optional.of(this.getClass()),
|
|
Optional.of(downloadProgressMonitor));
|
|
|
|
ListenableFuture<Void> downloadFuture1 =
|
|
mobileDataDownload.downloadFile(singleFileDownloadRequest);
|
|
ListenableFuture<Void> downloadFuture2 =
|
|
mobileDataDownload.downloadFileWithForegroundService(singleFileDownloadRequest);
|
|
|
|
// Now we let the 2 futures downloadFuture1 downloadFuture2 to run by opening the latch.
|
|
blockingFileDownloader.finishDownloading();
|
|
|
|
// Now finish future 2, future 1 should finish too and the cache clears the future.
|
|
downloadFuture2.get();
|
|
|
|
awaitAllExecutorsIdle();
|
|
|
|
assertThat(downloadFuture1.isDone()).isTrue();
|
|
}
|
|
|
|
@Test
|
|
public void downloadFileWithForegroundService() throws Exception {
|
|
when(mockFileDownloader.startDownloading(singleFileDownloadRequestCaptor.capture()))
|
|
.thenReturn(Futures.immediateVoidFuture());
|
|
mobileDataDownload =
|
|
getMobileDataDownload(
|
|
() -> mockFileDownloader,
|
|
Optional.of(this.getClass()),
|
|
Optional.of(mockDownloadMonitor));
|
|
|
|
singleFileDownloadRequest =
|
|
SingleFileDownloadRequest.newBuilder()
|
|
.setDestinationFileUri(DESTINATION_FILE_URI)
|
|
.setUrlToDownload(FILE_URL)
|
|
.setDownloadConstraints(DownloadConstraints.NETWORK_CONNECTED)
|
|
.setNotificationContentTitle("File url: " + FILE_URL)
|
|
.setListenerOptional(Optional.of(mockDownloadListener))
|
|
.build();
|
|
|
|
ListenableFuture<Void> downloadFuture =
|
|
mobileDataDownload.downloadFileWithForegroundService(singleFileDownloadRequest);
|
|
downloadFuture.get();
|
|
|
|
awaitAllExecutorsIdle();
|
|
|
|
// Verify that the correct DownloadRequest is sent to underderlying FileDownloader.
|
|
DownloadRequest actualDownloadRequest = singleFileDownloadRequestCaptor.getValue();
|
|
assertThat(actualDownloadRequest.fileUri()).isEqualTo(DESTINATION_FILE_URI);
|
|
assertThat(actualDownloadRequest.urlToDownload()).isEqualTo(FILE_URL);
|
|
assertThat(actualDownloadRequest.downloadConstraints())
|
|
.isEqualTo(DownloadConstraints.NETWORK_CONNECTED);
|
|
|
|
// Verify that downloadMonitor will add a DownloadListener.
|
|
verify(mockDownloadMonitor).addDownloadListener(any(), liteDownloadListenerCaptor.capture());
|
|
verify(mockFileDownloader).startDownloading(any());
|
|
|
|
verify(mockDownloadMonitor).removeDownloadListener(DESTINATION_FILE_URI);
|
|
|
|
verify(mockDownloadListener).onComplete();
|
|
|
|
com.google.android.libraries.mobiledatadownload.lite.DownloadListener capturedListener =
|
|
liteDownloadListenerCaptor.getValue();
|
|
|
|
// Now simulate other DownloadListener's callbacks:
|
|
capturedListener.onProgress(10);
|
|
capturedListener.onPausedForConnectivity();
|
|
DownloadException downloadException =
|
|
DownloadException.builder().setDownloadResultCode(ANDROID_DOWNLOADER_UNKNOWN).build();
|
|
capturedListener.onFailure(downloadException);
|
|
|
|
awaitAllExecutorsIdle();
|
|
|
|
verify(mockDownloadListener).onProgress(10);
|
|
verify(mockDownloadListener).onPausedForConnectivity();
|
|
verify(mockDownloadListener).onFailure(downloadException);
|
|
}
|
|
|
|
@Test
|
|
public void downloadFileWithForegroundService_clientOnCompleteFailed() throws Exception {
|
|
Exception failureException = new Exception("test failure");
|
|
|
|
when(mockFileDownloader.startDownloading(singleFileDownloadRequestCaptor.capture()))
|
|
.thenReturn(Futures.immediateVoidFuture());
|
|
mobileDataDownload =
|
|
getMobileDataDownload(
|
|
() -> mockFileDownloader,
|
|
Optional.of(this.getClass()),
|
|
Optional.of(downloadProgressMonitor));
|
|
|
|
// Client's provided DownloadListener.onComplete failed.
|
|
when(mockDownloadListener.onComplete())
|
|
.thenReturn(Futures.immediateFailedFuture(failureException));
|
|
|
|
singleFileDownloadRequest =
|
|
SingleFileDownloadRequest.newBuilder()
|
|
.setDestinationFileUri(DESTINATION_FILE_URI)
|
|
.setUrlToDownload(FILE_URL)
|
|
.setDownloadConstraints(DownloadConstraints.NETWORK_CONNECTED)
|
|
.setNotificationContentTitle("File url: " + FILE_URL)
|
|
.setListenerOptional(Optional.of(mockDownloadListener))
|
|
.build();
|
|
|
|
ListenableFuture<Void> downloadFuture =
|
|
mobileDataDownload.downloadFileWithForegroundService(singleFileDownloadRequest);
|
|
downloadFuture.get();
|
|
|
|
awaitAllExecutorsIdle();
|
|
|
|
// Verify that the correct DownloadRequest is sent to underderlying FileDownloader.
|
|
DownloadRequest actualDownloadRequest = singleFileDownloadRequestCaptor.getValue();
|
|
assertThat(actualDownloadRequest.fileUri()).isEqualTo(DESTINATION_FILE_URI);
|
|
assertThat(actualDownloadRequest.urlToDownload()).isEqualTo(FILE_URL);
|
|
assertThat(actualDownloadRequest.downloadConstraints())
|
|
.isEqualTo(DownloadConstraints.NETWORK_CONNECTED);
|
|
|
|
verify(mockFileDownloader).startDownloading(any());
|
|
verify(mockDownloadListener).onComplete();
|
|
}
|
|
|
|
@Test
|
|
public void downloadFileWithForegroundService_failure() throws Exception {
|
|
DownloadException downloadException =
|
|
DownloadException.builder().setDownloadResultCode(ANDROID_DOWNLOADER_UNKNOWN).build();
|
|
|
|
when(mockFileDownloader.startDownloading(singleFileDownloadRequestCaptor.capture()))
|
|
.thenReturn(Futures.immediateFailedFuture(downloadException));
|
|
|
|
mobileDataDownload =
|
|
getMobileDataDownload(
|
|
() -> mockFileDownloader,
|
|
Optional.of(this.getClass()),
|
|
Optional.of(downloadProgressMonitor));
|
|
|
|
singleFileDownloadRequest =
|
|
SingleFileDownloadRequest.newBuilder()
|
|
.setDestinationFileUri(DESTINATION_FILE_URI)
|
|
.setUrlToDownload(FILE_URL)
|
|
.setDownloadConstraints(DownloadConstraints.NETWORK_CONNECTED)
|
|
.setNotificationContentTitle("File url: " + FILE_URL)
|
|
.setListenerOptional(Optional.of(mockDownloadListener))
|
|
.build();
|
|
|
|
ListenableFuture<Void> downloadFuture =
|
|
mobileDataDownload.downloadFileWithForegroundService(singleFileDownloadRequest);
|
|
assertThrows(ExecutionException.class, downloadFuture::get);
|
|
DownloadException e = LabsFutures.getFailureCauseAs(downloadFuture, DownloadException.class);
|
|
assertThat(e.getDownloadResultCode()).isEqualTo(ANDROID_DOWNLOADER_UNKNOWN);
|
|
|
|
awaitAllExecutorsIdle();
|
|
|
|
// Verify that the correct DownloadRequest is sent to underderlying FileDownloader.
|
|
DownloadRequest actualDownloadRequest = singleFileDownloadRequestCaptor.getValue();
|
|
assertThat(actualDownloadRequest.fileUri()).isEqualTo(DESTINATION_FILE_URI);
|
|
assertThat(actualDownloadRequest.urlToDownload()).isEqualTo(FILE_URL);
|
|
assertThat(actualDownloadRequest.downloadConstraints())
|
|
.isEqualTo(DownloadConstraints.NETWORK_CONNECTED);
|
|
|
|
// Since the download failed, onComplete will not be called but onFailure.
|
|
verify(mockDownloadListener, times(0)).onComplete();
|
|
verify(mockDownloadListener).onFailure(downloadException);
|
|
}
|
|
|
|
@Test
|
|
public void cancelDownloadFileWithForegroundService() throws Exception {
|
|
// Use BlockingFileDownloader to control when the download will finish.
|
|
mobileDataDownload = getMobileDataDownload(() -> blockingFileDownloader);
|
|
|
|
ForegroundDownloadKey foregroundDownloadKey =
|
|
ForegroundDownloadKey.ofSingleFile(DESTINATION_FILE_URI);
|
|
|
|
ListenableFuture<Void> downloadFuture =
|
|
mobileDataDownload.downloadFileWithForegroundService(singleFileDownloadRequest);
|
|
|
|
blockingFileDownloader.waitForDownloadStarted();
|
|
|
|
mobileDataDownload.cancelForegroundDownload(foregroundDownloadKey.toString());
|
|
|
|
awaitAllExecutorsIdle();
|
|
|
|
assertTrue(downloadFuture.isCancelled());
|
|
}
|
|
|
|
@Test
|
|
public void cancelListenableFuture() throws Exception {
|
|
// Use BlockingFileDownloader to control when the download will finish.
|
|
mobileDataDownload = getMobileDataDownload(() -> blockingFileDownloader);
|
|
|
|
ListenableFuture<Void> downloadFuture =
|
|
mobileDataDownload.downloadFileWithForegroundService(singleFileDownloadRequest);
|
|
|
|
// Wait for download to start and confirm download future is still running.
|
|
blockingFileDownloader.waitForDownloadStarted();
|
|
assertThat(downloadFuture.isDone()).isFalse();
|
|
|
|
// Cancel download future.
|
|
downloadFuture.cancel(true);
|
|
|
|
// Check that future is now cancelled.
|
|
assertThat(downloadFuture.isCancelled()).isTrue();
|
|
}
|
|
|
|
private Supplier<FileDownloader> createFailingFileDownloaderSupplier(Throwable throwable) {
|
|
return Suppliers.ofInstance(
|
|
new FileDownloader() {
|
|
@Override
|
|
public ListenableFuture<Void> startDownloading(DownloadRequest request) {
|
|
return Futures.immediateFailedFuture(throwable);
|
|
}
|
|
});
|
|
}
|
|
|
|
private Supplier<FileDownloader> createSuccessfulFileDownloaderSupplier() {
|
|
return Suppliers.ofInstance(
|
|
new FileDownloader() {
|
|
@Override
|
|
public ListenableFuture<Void> startDownloading(DownloadRequest request) {
|
|
return Futures.immediateVoidFuture();
|
|
}
|
|
});
|
|
}
|
|
|
|
private MobileDataDownload getMobileDataDownload(
|
|
Supplier<FileDownloader> fileDownloaderSupplier) {
|
|
return getMobileDataDownload(
|
|
fileDownloaderSupplier, Optional.of(this.getClass()), Optional.of(downloadProgressMonitor));
|
|
}
|
|
|
|
private MobileDataDownload getMobileDataDownload(
|
|
Supplier<FileDownloader> fileDownloaderSupplier,
|
|
Optional<Class<?>> foregroundDownloadServiceClassOptional,
|
|
Optional<DownloadProgressMonitor> downloadProgressMonitorOptional) {
|
|
return MobileDataDownloadBuilder.newBuilder()
|
|
.setContext(context)
|
|
.setControlExecutor(controlExecutor)
|
|
.setFileDownloaderSupplier(fileDownloaderSupplier)
|
|
.setFileStorage(fileStorage)
|
|
.setDownloadMonitorOptional(downloadProgressMonitorOptional)
|
|
.setNetworkUsageMonitor(mockNetworkUsageMonitor)
|
|
.setForegroundDownloadServiceOptional(foregroundDownloadServiceClassOptional)
|
|
.setFlagsOptional(Optional.of(flags))
|
|
.build();
|
|
}
|
|
|
|
/** Waits long enough for async operations to finish running. */
|
|
// TODO(b/217551873): investigate ways to make this more robust
|
|
private static void awaitAllExecutorsIdle() throws Exception {
|
|
Thread.sleep(/* millis= */ 100);
|
|
}
|
|
}
|