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 java.util.concurrent.TimeUnit.SECONDS; 21 import static org.testng.Assert.expectThrows; 22 23 import android.content.Context; 24 import androidx.test.core.app.ApplicationProvider; 25 import com.android.textclassifier.downloader.TestModelDownloaderService.DownloadResult; 26 import com.google.common.util.concurrent.ListenableFuture; 27 import com.google.common.util.concurrent.MoreExecutors; 28 import java.io.File; 29 import java.nio.file.Files; 30 import java.util.concurrent.CancellationException; 31 import org.junit.After; 32 import org.junit.Before; 33 import org.junit.Test; 34 import org.junit.runner.RunWith; 35 import org.junit.runners.JUnit4; 36 37 @RunWith(JUnit4.class) 38 public final class ModelDownloaderImplTest { 39 private static final String MANIFEST_URL = "https://manifest.url"; 40 private static final String MODEL_URL = "https://model.url"; 41 private static final byte[] MODEL_CONTENT_BYTES = "content".getBytes(); 42 private static final long MODEL_SIZE_IN_BYTES = 7L; 43 private static final String MODEL_FINGERPRINT = 44 "5406ebea1618e9b73a7290c5d716f0b47b4f1fbc5d8c" 45 + "5e78c9010a3e01c18d8594aa942e3536f7e01574245d34647523"; 46 private static final ModelManifest.Model MODEL_PROTO = 47 ModelManifest.Model.newBuilder() 48 .setUrl(MODEL_URL) 49 .setSizeInBytes(MODEL_SIZE_IN_BYTES) 50 .setFingerprint(MODEL_FINGERPRINT) 51 .build(); 52 private static final ModelManifest MODEL_MANIFEST_PROTO = 53 ModelManifest.newBuilder().addModels(MODEL_PROTO).build(); 54 55 private ModelDownloaderImpl modelDownloaderImpl; 56 private File modelDownloaderDir; 57 58 @Before setUp()59 public void setUp() { 60 Context context = ApplicationProvider.getApplicationContext(); 61 this.modelDownloaderImpl = 62 new ModelDownloaderImpl( 63 context, MoreExecutors.newDirectExecutorService(), TestModelDownloaderService.class); 64 this.modelDownloaderDir = new File(context.getFilesDir(), "downloader"); 65 this.modelDownloaderDir.mkdirs(); 66 67 TestModelDownloaderService.reset(); 68 } 69 70 @After tearDown()71 public void tearDown() { 72 DownloaderTestUtils.deleteRecursively(modelDownloaderDir); 73 } 74 75 @Test downloadManifest_failToBind()76 public void downloadManifest_failToBind() throws Exception { 77 assertThat(TestModelDownloaderService.hasEverBeenBound()).isFalse(); 78 assertThat(TestModelDownloaderService.isBound()).isFalse(); 79 80 TestModelDownloaderService.setBindSucceed(false); 81 ListenableFuture<ModelManifest> manifestFuture = 82 modelDownloaderImpl.downloadManifest(MANIFEST_URL); 83 84 Throwable t = expectThrows(Throwable.class, manifestFuture::get); 85 assertThat(t).hasCauseThat().isInstanceOf(ModelDownloadException.class); 86 ModelDownloadException e = (ModelDownloadException) t.getCause(); 87 assertThat(e.getErrorCode()) 88 .isEqualTo(ModelDownloadException.FAILED_TO_DOWNLOAD_SERVICE_CONN_BROKEN); 89 assertThat(e.getDownloaderLibErrorCode()) 90 .isEqualTo(ModelDownloadException.DEFAULT_DOWNLOADER_LIB_ERROR_CODE); 91 assertThat(TestModelDownloaderService.isBound()).isFalse(); 92 assertThat(TestModelDownloaderService.hasEverBeenBound()).isFalse(); 93 } 94 95 @Test downloadManifest_succeed()96 public void downloadManifest_succeed() throws Exception { 97 assertThat(TestModelDownloaderService.hasEverBeenBound()).isFalse(); 98 assertThat(TestModelDownloaderService.isBound()).isFalse(); 99 100 TestModelDownloaderService.setBindSucceed(true); 101 TestModelDownloaderService.setDownloadResult( 102 MANIFEST_URL, DownloadResult.SUCCEEDED, MODEL_MANIFEST_PROTO.toByteArray()); 103 ListenableFuture<ModelManifest> manifestFuture = 104 modelDownloaderImpl.downloadManifest(MANIFEST_URL); 105 106 assertThat(manifestFuture.get()).isEqualTo(MODEL_MANIFEST_PROTO); // ProtoTruth is not available 107 assertThat(TestModelDownloaderService.getOnUnbindInvokedLatch().await(1L, SECONDS)).isTrue(); 108 assertThat(TestModelDownloaderService.isBound()).isFalse(); 109 assertThat(TestModelDownloaderService.hasEverBeenBound()).isTrue(); 110 } 111 112 @Test downloadManifest_failToDownload()113 public void downloadManifest_failToDownload() throws Exception { 114 assertThat(TestModelDownloaderService.hasEverBeenBound()).isFalse(); 115 assertThat(TestModelDownloaderService.isBound()).isFalse(); 116 117 TestModelDownloaderService.setBindSucceed(true); 118 TestModelDownloaderService.setDownloadResult(MANIFEST_URL, DownloadResult.FAILED, null); 119 ListenableFuture<ModelManifest> manifestFuture = 120 modelDownloaderImpl.downloadManifest(MANIFEST_URL); 121 122 Throwable t = expectThrows(Throwable.class, manifestFuture::get); 123 assertThat(t).hasCauseThat().isInstanceOf(ModelDownloadException.class); 124 ModelDownloadException e = (ModelDownloadException) t.getCause(); 125 assertThat(e.getErrorCode()).isEqualTo(ModelDownloadException.FAILED_TO_DOWNLOAD_OTHER); 126 assertThat(e.getDownloaderLibErrorCode()) 127 .isEqualTo(TestModelDownloaderService.DOWNLOADER_LIB_ERROR_CODE); 128 assertThat(e).hasMessageThat().contains(TestModelDownloaderService.ERROR_MSG); 129 assertThat(TestModelDownloaderService.getOnUnbindInvokedLatch().await(1L, SECONDS)).isTrue(); 130 assertThat(TestModelDownloaderService.isBound()).isFalse(); 131 assertThat(TestModelDownloaderService.hasEverBeenBound()).isTrue(); 132 } 133 134 @Test downloadManifest_failToParse()135 public void downloadManifest_failToParse() throws Exception { 136 assertThat(TestModelDownloaderService.hasEverBeenBound()).isFalse(); 137 assertThat(TestModelDownloaderService.isBound()).isFalse(); 138 139 TestModelDownloaderService.setBindSucceed(true); 140 TestModelDownloaderService.setDownloadResult( 141 MANIFEST_URL, DownloadResult.SUCCEEDED, "randomString".getBytes()); 142 ListenableFuture<ModelManifest> manifestFuture = 143 modelDownloaderImpl.downloadManifest(MANIFEST_URL); 144 145 Throwable t = expectThrows(Throwable.class, manifestFuture::get); 146 assertThat(t).hasCauseThat().isInstanceOf(ModelDownloadException.class); 147 ModelDownloadException e = (ModelDownloadException) t.getCause(); 148 assertThat(e.getErrorCode()).isEqualTo(ModelDownloadException.FAILED_TO_PARSE_MANIFEST); 149 assertThat(e.getDownloaderLibErrorCode()) 150 .isEqualTo(ModelDownloadException.DEFAULT_DOWNLOADER_LIB_ERROR_CODE); 151 assertThat(TestModelDownloaderService.getOnUnbindInvokedLatch().await(1L, SECONDS)).isTrue(); 152 assertThat(TestModelDownloaderService.isBound()).isFalse(); 153 assertThat(TestModelDownloaderService.hasEverBeenBound()).isTrue(); 154 } 155 156 @Test downloadManifest_cancelAndUnbind()157 public void downloadManifest_cancelAndUnbind() throws Exception { 158 assertThat(TestModelDownloaderService.hasEverBeenBound()).isFalse(); 159 assertThat(TestModelDownloaderService.isBound()).isFalse(); 160 161 TestModelDownloaderService.setBindSucceed(true); 162 TestModelDownloaderService.setDownloadResult(MANIFEST_URL, DownloadResult.DO_NOTHING, null); 163 ListenableFuture<ModelManifest> manifestFuture = 164 modelDownloaderImpl.downloadManifest(MANIFEST_URL); 165 166 assertThat(TestModelDownloaderService.getOnBindInvokedLatch().await(1L, SECONDS)).isTrue(); 167 assertThat(TestModelDownloaderService.isBound()).isTrue(); 168 manifestFuture.cancel(true); 169 170 expectThrows(CancellationException.class, manifestFuture::get); 171 assertThat(TestModelDownloaderService.getOnUnbindInvokedLatch().await(1L, SECONDS)).isTrue(); 172 assertThat(TestModelDownloaderService.isBound()).isFalse(); 173 assertThat(TestModelDownloaderService.hasEverBeenBound()).isTrue(); 174 } 175 176 @Test downloadModel_failToBind()177 public void downloadModel_failToBind() throws Exception { 178 assertThat(TestModelDownloaderService.hasEverBeenBound()).isFalse(); 179 assertThat(TestModelDownloaderService.isBound()).isFalse(); 180 181 TestModelDownloaderService.setBindSucceed(false); 182 ListenableFuture<File> modelFuture = 183 modelDownloaderImpl.downloadModel(modelDownloaderDir, MODEL_PROTO); 184 185 Throwable t = expectThrows(Throwable.class, modelFuture::get); 186 assertThat(t).hasCauseThat().isInstanceOf(ModelDownloadException.class); 187 ModelDownloadException e = (ModelDownloadException) t.getCause(); 188 assertThat(e.getErrorCode()) 189 .isEqualTo(ModelDownloadException.FAILED_TO_DOWNLOAD_SERVICE_CONN_BROKEN); 190 assertThat(e.getDownloaderLibErrorCode()) 191 .isEqualTo(ModelDownloadException.DEFAULT_DOWNLOADER_LIB_ERROR_CODE); 192 assertThat(TestModelDownloaderService.isBound()).isFalse(); 193 assertThat(TestModelDownloaderService.hasEverBeenBound()).isFalse(); 194 } 195 196 @Test downloadModel_succeed()197 public void downloadModel_succeed() throws Exception { 198 assertThat(TestModelDownloaderService.hasEverBeenBound()).isFalse(); 199 assertThat(TestModelDownloaderService.isBound()).isFalse(); 200 201 TestModelDownloaderService.setBindSucceed(true); 202 TestModelDownloaderService.setDownloadResult( 203 MODEL_URL, DownloadResult.SUCCEEDED, MODEL_CONTENT_BYTES); 204 ListenableFuture<File> modelFuture = 205 modelDownloaderImpl.downloadModel(modelDownloaderDir, MODEL_PROTO); 206 207 File modelFile = modelFuture.get(); 208 assertThat(modelFile.getParentFile()).isEqualTo(modelDownloaderDir); 209 assertThat(Files.readAllBytes(modelFile.toPath())).isEqualTo(MODEL_CONTENT_BYTES); 210 assertThat(TestModelDownloaderService.getOnUnbindInvokedLatch().await(1L, SECONDS)).isTrue(); 211 assertThat(TestModelDownloaderService.isBound()).isFalse(); 212 assertThat(TestModelDownloaderService.hasEverBeenBound()).isTrue(); 213 } 214 215 @Test downloadModel_failToDownload()216 public void downloadModel_failToDownload() throws Exception { 217 assertThat(TestModelDownloaderService.hasEverBeenBound()).isFalse(); 218 assertThat(TestModelDownloaderService.isBound()).isFalse(); 219 220 TestModelDownloaderService.setBindSucceed(true); 221 TestModelDownloaderService.setDownloadResult(MODEL_URL, DownloadResult.FAILED, null); 222 ListenableFuture<File> modelFuture = 223 modelDownloaderImpl.downloadModel(modelDownloaderDir, MODEL_PROTO); 224 225 Throwable t = expectThrows(Throwable.class, modelFuture::get); 226 assertThat(t).hasCauseThat().isInstanceOf(ModelDownloadException.class); 227 ModelDownloadException e = (ModelDownloadException) t.getCause(); 228 assertThat(e.getErrorCode()).isEqualTo(ModelDownloadException.FAILED_TO_DOWNLOAD_OTHER); 229 assertThat(e.getDownloaderLibErrorCode()) 230 .isEqualTo(TestModelDownloaderService.DOWNLOADER_LIB_ERROR_CODE); 231 assertThat(e).hasMessageThat().contains(TestModelDownloaderService.ERROR_MSG); 232 assertThat(TestModelDownloaderService.getOnUnbindInvokedLatch().await(1L, SECONDS)).isTrue(); 233 assertThat(TestModelDownloaderService.isBound()).isFalse(); 234 assertThat(TestModelDownloaderService.hasEverBeenBound()).isTrue(); 235 } 236 237 @Test downloadModel_failToValidate()238 public void downloadModel_failToValidate() throws Exception { 239 assertThat(TestModelDownloaderService.hasEverBeenBound()).isFalse(); 240 assertThat(TestModelDownloaderService.isBound()).isFalse(); 241 242 TestModelDownloaderService.setBindSucceed(true); 243 TestModelDownloaderService.setDownloadResult( 244 MODEL_URL, DownloadResult.SUCCEEDED, "randomString".getBytes()); 245 ListenableFuture<File> modelFuture = 246 modelDownloaderImpl.downloadModel(modelDownloaderDir, MODEL_PROTO); 247 248 Throwable t = expectThrows(Throwable.class, modelFuture::get); 249 assertThat(t).hasCauseThat().isInstanceOf(ModelDownloadException.class); 250 ModelDownloadException e = (ModelDownloadException) t.getCause(); 251 assertThat(e.getErrorCode()).isEqualTo(ModelDownloadException.FAILED_TO_VALIDATE_MODEL); 252 assertThat(e.getDownloaderLibErrorCode()) 253 .isEqualTo(ModelDownloadException.DEFAULT_DOWNLOADER_LIB_ERROR_CODE); 254 assertThat(TestModelDownloaderService.getOnUnbindInvokedLatch().await(1L, SECONDS)).isTrue(); 255 assertThat(TestModelDownloaderService.isBound()).isFalse(); 256 assertThat(TestModelDownloaderService.hasEverBeenBound()).isTrue(); 257 } 258 259 @Test downloadModel_cancelAndUnbind()260 public void downloadModel_cancelAndUnbind() throws Exception { 261 assertThat(TestModelDownloaderService.hasEverBeenBound()).isFalse(); 262 assertThat(TestModelDownloaderService.isBound()).isFalse(); 263 264 TestModelDownloaderService.setBindSucceed(true); 265 TestModelDownloaderService.setDownloadResult(MODEL_URL, DownloadResult.DO_NOTHING, null); 266 ListenableFuture<File> modelFuture = 267 modelDownloaderImpl.downloadModel(modelDownloaderDir, MODEL_PROTO); 268 269 assertThat(TestModelDownloaderService.getOnBindInvokedLatch().await(1L, SECONDS)).isTrue(); 270 assertThat(TestModelDownloaderService.isBound()).isTrue(); 271 modelFuture.cancel(true); 272 273 expectThrows(CancellationException.class, modelFuture::get); 274 assertThat(TestModelDownloaderService.getOnUnbindInvokedLatch().await(1L, SECONDS)).isTrue(); 275 assertThat(TestModelDownloaderService.isBound()).isFalse(); 276 assertThat(TestModelDownloaderService.hasEverBeenBound()).isTrue(); 277 } 278 } 279