1 /* 2 * Copyright (C) 2018 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.textclassifier.downloader; 18 19 import static com.google.common.truth.Truth.assertThat; 20 import static org.mockito.ArgumentMatchers.any; 21 import static org.mockito.Mockito.when; 22 import static org.testng.Assert.expectThrows; 23 24 import androidx.test.core.app.ApplicationProvider; 25 import com.google.android.downloader.DownloadConstraints; 26 import com.google.android.downloader.DownloadRequest; 27 import com.google.android.downloader.DownloadResult; 28 import com.google.android.downloader.Downloader; 29 import com.google.android.downloader.ErrorDetails; 30 import com.google.android.downloader.RequestException; 31 import com.google.android.downloader.SimpleFileDownloadDestination; 32 import com.google.common.util.concurrent.FluentFuture; 33 import com.google.common.util.concurrent.Futures; 34 import com.google.common.util.concurrent.ListenableFuture; 35 import com.google.common.util.concurrent.MoreExecutors; 36 import com.google.common.util.concurrent.SettableFuture; 37 import java.io.File; 38 import java.net.URI; 39 import org.junit.Before; 40 import org.junit.Rule; 41 import org.junit.Test; 42 import org.junit.runner.RunWith; 43 import org.junit.runners.JUnit4; 44 import org.mockito.Mock; 45 import org.mockito.junit.MockitoJUnit; 46 import org.mockito.junit.MockitoRule; 47 48 @RunWith(JUnit4.class) 49 public final class ModelDownloaderServiceImplTest { 50 51 @Rule public final MockitoRule mocks = MockitoJUnit.rule(); 52 53 private static final long BYTES_WRITTEN = 1L; 54 private static final String DOWNLOAD_URI = 55 "https://www.gstatic.com/android/text_classifier/r/v999/en.fb"; 56 private static final int DOWNLOADER_LIB_ERROR_CODE = 500; 57 private static final String ERROR_MESSAGE = "err_msg"; 58 private static final Exception DOWNLOADER_LIB_EXCEPTION = 59 new RequestException( 60 ErrorDetails.builder() 61 .setErrorMessage(ERROR_MESSAGE) 62 .setHttpStatusCode(DOWNLOADER_LIB_ERROR_CODE) 63 .build()); 64 65 @Mock private Downloader downloader; 66 private File targetModelFile; 67 private File targetMetadataFile; 68 private ModelDownloaderServiceImpl modelDownloaderServiceImpl; 69 private TestSuccessCallbackImpl successCallback; 70 private TestFailureCallbackImpl failureCallback; 71 72 @Before setUp()73 public void setUp() { 74 75 this.targetModelFile = 76 new File(ApplicationProvider.getApplicationContext().getCacheDir(), "model.fb"); 77 this.targetMetadataFile = ModelDownloaderServiceImpl.getMetadataFile(targetModelFile); 78 this.modelDownloaderServiceImpl = 79 new ModelDownloaderServiceImpl(MoreExecutors.newDirectExecutorService(), downloader); 80 this.successCallback = new TestSuccessCallbackImpl(); 81 this.failureCallback = new TestFailureCallbackImpl(); 82 83 targetModelFile.deleteOnExit(); 84 targetMetadataFile.deleteOnExit(); 85 when(downloader.newRequestBuilder(any(), any())) 86 .thenReturn( 87 DownloadRequest.newBuilder() 88 .setUri(URI.create(DOWNLOAD_URI)) 89 .setDownloadConstraints(DownloadConstraints.NONE) 90 .setDestination( 91 new SimpleFileDownloadDestination(targetModelFile, targetMetadataFile))); 92 } 93 94 @Test download_succeeded()95 public void download_succeeded() throws Exception { 96 targetModelFile.createNewFile(); 97 targetMetadataFile.createNewFile(); 98 when(downloader.execute(any())) 99 .thenReturn( 100 FluentFuture.from(Futures.immediateFuture(DownloadResult.create(BYTES_WRITTEN)))); 101 modelDownloaderServiceImpl.download( 102 DOWNLOAD_URI, targetModelFile.getAbsolutePath(), successCallback); 103 104 assertThat(successCallback.getBytesWrittenFuture().get()).isEqualTo(BYTES_WRITTEN); 105 assertThat(targetModelFile.exists()).isTrue(); 106 assertThat(targetMetadataFile.exists()).isFalse(); 107 } 108 109 @Test download_failed()110 public void download_failed() throws Exception { 111 targetModelFile.createNewFile(); 112 targetMetadataFile.createNewFile(); 113 when(downloader.execute(any())) 114 .thenReturn(FluentFuture.from(Futures.immediateFailedFuture(DOWNLOADER_LIB_EXCEPTION))); 115 modelDownloaderServiceImpl.download( 116 DOWNLOAD_URI, targetModelFile.getAbsolutePath(), successCallback); 117 118 Throwable t = 119 expectThrows(Throwable.class, () -> successCallback.getBytesWrittenFuture().get()); 120 assertThat(t).hasCauseThat().isInstanceOf(ModelDownloadException.class); 121 ModelDownloadException e = (ModelDownloadException) t.getCause(); 122 assertThat(e.getErrorCode()).isEqualTo(ModelDownloadException.FAILED_TO_DOWNLOAD_OTHER); 123 assertThat(e.getDownloaderLibErrorCode()).isEqualTo(DOWNLOADER_LIB_ERROR_CODE); 124 assertThat(e).hasMessageThat().contains(ERROR_MESSAGE); 125 assertThat(targetModelFile.exists()).isFalse(); 126 assertThat(targetMetadataFile.exists()).isFalse(); 127 } 128 129 @Test download_succeeded_callbackFailed()130 public void download_succeeded_callbackFailed() throws Exception { 131 targetModelFile.createNewFile(); 132 targetMetadataFile.createNewFile(); 133 when(downloader.execute(any())) 134 .thenReturn( 135 FluentFuture.from(Futures.immediateFuture(DownloadResult.create(BYTES_WRITTEN)))); 136 modelDownloaderServiceImpl.download( 137 DOWNLOAD_URI, targetModelFile.getAbsolutePath(), failureCallback); 138 139 assertThat(failureCallback.onSuccessCalled).isTrue(); 140 assertThat(targetModelFile.exists()).isTrue(); 141 assertThat(targetMetadataFile.exists()).isFalse(); 142 } 143 144 @Test download_failed_callbackFailed()145 public void download_failed_callbackFailed() throws Exception { 146 targetModelFile.createNewFile(); 147 targetMetadataFile.createNewFile(); 148 when(downloader.execute(any())) 149 .thenReturn(FluentFuture.from(Futures.immediateFailedFuture(DOWNLOADER_LIB_EXCEPTION))); 150 modelDownloaderServiceImpl.download( 151 DOWNLOAD_URI, targetModelFile.getAbsolutePath(), failureCallback); 152 153 assertThat(failureCallback.onFailureCalled).isTrue(); 154 assertThat(targetModelFile.exists()).isFalse(); 155 assertThat(targetMetadataFile.exists()).isFalse(); 156 } 157 158 // NOTICE: Had some problem mocking this AIDL interface, so created fake impls 159 private static final class TestSuccessCallbackImpl extends IModelDownloaderCallback.Stub { 160 private final SettableFuture<Long> bytesWrittenFuture = SettableFuture.<Long>create(); 161 getBytesWrittenFuture()162 public ListenableFuture<Long> getBytesWrittenFuture() { 163 return bytesWrittenFuture; 164 } 165 166 @Override onSuccess(long bytesWritten)167 public void onSuccess(long bytesWritten) { 168 bytesWrittenFuture.set(bytesWritten); 169 } 170 171 @Override onFailure(int downloaderLibErrorCode, String errorMsg)172 public void onFailure(int downloaderLibErrorCode, String errorMsg) { 173 bytesWrittenFuture.setException( 174 new ModelDownloadException( 175 ModelDownloadException.FAILED_TO_DOWNLOAD_OTHER, downloaderLibErrorCode, errorMsg)); 176 } 177 } 178 179 private static final class TestFailureCallbackImpl extends IModelDownloaderCallback.Stub { 180 public boolean onSuccessCalled = false; 181 public boolean onFailureCalled = false; 182 183 @Override onSuccess(long bytesWritten)184 public void onSuccess(long bytesWritten) { 185 onSuccessCalled = true; 186 throw new RuntimeException(); 187 } 188 189 @Override onFailure(int downloaderLibErrorCode, String errorMsg)190 public void onFailure(int downloaderLibErrorCode, String errorMsg) { 191 onFailureCalled = true; 192 throw new RuntimeException(); 193 } 194 } 195 } 196