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; 18 19 import static com.google.common.truth.Truth.assertThat; 20 import static org.mockito.Mockito.when; 21 22 import android.os.LocaleList; 23 import androidx.test.core.app.ApplicationProvider; 24 import androidx.test.ext.junit.runners.AndroidJUnit4; 25 import androidx.test.filters.SmallTest; 26 import com.android.textclassifier.ModelFileManager.ModelFile; 27 import com.android.textclassifier.common.logging.ResultIdUtils.ModelInfo; 28 import com.google.common.base.Optional; 29 import com.google.common.collect.ImmutableList; 30 import java.io.File; 31 import java.io.IOException; 32 import java.util.Collections; 33 import java.util.List; 34 import java.util.Locale; 35 import java.util.function.Supplier; 36 import java.util.stream.Collectors; 37 import org.junit.After; 38 import org.junit.Before; 39 import org.junit.Test; 40 import org.junit.runner.RunWith; 41 import org.mockito.Mock; 42 import org.mockito.MockitoAnnotations; 43 44 @SmallTest 45 @RunWith(AndroidJUnit4.class) 46 public class ModelFileManagerTest { 47 private static final Locale DEFAULT_LOCALE = Locale.forLanguageTag("en-US"); 48 @Mock private Supplier<ImmutableList<ModelFile>> modelFileSupplier; 49 private ModelFileManager.ModelFileSupplierImpl modelFileSupplierImpl; 50 private ModelFileManager modelFileManager; 51 private File rootTestDir; 52 private File factoryModelDir; 53 private File updatedModelFile; 54 55 @Before setup()56 public void setup() { 57 MockitoAnnotations.initMocks(this); 58 modelFileManager = new ModelFileManager(modelFileSupplier); 59 rootTestDir = ApplicationProvider.getApplicationContext().getCacheDir(); 60 factoryModelDir = new File(rootTestDir, "factory"); 61 updatedModelFile = new File(rootTestDir, "updated.model"); 62 63 modelFileSupplierImpl = 64 new ModelFileManager.ModelFileSupplierImpl( 65 factoryModelDir, 66 "test\\d.model", 67 updatedModelFile, 68 fd -> 1, 69 fd -> ModelFileManager.ModelFile.LANGUAGE_INDEPENDENT); 70 71 rootTestDir.mkdirs(); 72 factoryModelDir.mkdirs(); 73 74 Locale.setDefault(DEFAULT_LOCALE); 75 } 76 77 @After removeTestDir()78 public void removeTestDir() { 79 recursiveDelete(rootTestDir); 80 } 81 82 @Test get()83 public void get() { 84 ModelFileManager.ModelFile modelFile = 85 new ModelFileManager.ModelFile(new File("/path/a"), 1, ImmutableList.of(), "", true); 86 when(modelFileSupplier.get()).thenReturn(ImmutableList.of(modelFile)); 87 88 List<ModelFileManager.ModelFile> modelFiles = modelFileManager.listModelFiles(); 89 90 assertThat(modelFiles).hasSize(1); 91 assertThat(modelFiles.get(0)).isEqualTo(modelFile); 92 } 93 94 @Test findBestModel_versionCode()95 public void findBestModel_versionCode() { 96 ModelFileManager.ModelFile olderModelFile = 97 new ModelFileManager.ModelFile(new File("/path/a"), 1, ImmutableList.of(), "", true); 98 99 ModelFileManager.ModelFile newerModelFile = 100 new ModelFileManager.ModelFile(new File("/path/b"), 2, ImmutableList.of(), "", true); 101 when(modelFileSupplier.get()).thenReturn(ImmutableList.of(olderModelFile, newerModelFile)); 102 103 ModelFileManager.ModelFile bestModelFile = 104 modelFileManager.findBestModelFile(LocaleList.getEmptyLocaleList()); 105 106 assertThat(bestModelFile).isEqualTo(newerModelFile); 107 } 108 109 @Test findBestModel_languageDependentModelIsPreferred()110 public void findBestModel_languageDependentModelIsPreferred() { 111 Locale locale = Locale.forLanguageTag("ja"); 112 ModelFileManager.ModelFile languageIndependentModelFile = 113 new ModelFileManager.ModelFile(new File("/path/a"), 1, ImmutableList.of(), "", true); 114 115 ModelFileManager.ModelFile languageDependentModelFile = 116 new ModelFileManager.ModelFile( 117 new File("/path/b"), 118 1, 119 Collections.singletonList(locale), 120 locale.toLanguageTag(), 121 false); 122 when(modelFileSupplier.get()) 123 .thenReturn(ImmutableList.of(languageIndependentModelFile, languageDependentModelFile)); 124 125 ModelFileManager.ModelFile bestModelFile = 126 modelFileManager.findBestModelFile(LocaleList.forLanguageTags(locale.toLanguageTag())); 127 assertThat(bestModelFile).isEqualTo(languageDependentModelFile); 128 } 129 130 @Test findBestModel_noMatchedLanguageModel()131 public void findBestModel_noMatchedLanguageModel() { 132 Locale locale = Locale.forLanguageTag("ja"); 133 ModelFileManager.ModelFile languageIndependentModelFile = 134 new ModelFileManager.ModelFile(new File("/path/a"), 1, Collections.emptyList(), "", true); 135 136 ModelFileManager.ModelFile languageDependentModelFile = 137 new ModelFileManager.ModelFile( 138 new File("/path/b"), 139 1, 140 Collections.singletonList(locale), 141 locale.toLanguageTag(), 142 false); 143 144 when(modelFileSupplier.get()) 145 .thenReturn(ImmutableList.of(languageIndependentModelFile, languageDependentModelFile)); 146 147 ModelFileManager.ModelFile bestModelFile = 148 modelFileManager.findBestModelFile(LocaleList.forLanguageTags("zh-hk")); 149 assertThat(bestModelFile).isEqualTo(languageIndependentModelFile); 150 } 151 152 @Test findBestModel_noMatchedLanguageModel_defaultLocaleModelExists()153 public void findBestModel_noMatchedLanguageModel_defaultLocaleModelExists() { 154 ModelFileManager.ModelFile languageIndependentModelFile = 155 new ModelFileManager.ModelFile(new File("/path/a"), 1, ImmutableList.of(), "", true); 156 157 ModelFileManager.ModelFile languageDependentModelFile = 158 new ModelFileManager.ModelFile( 159 new File("/path/b"), 160 1, 161 Collections.singletonList(DEFAULT_LOCALE), 162 DEFAULT_LOCALE.toLanguageTag(), 163 false); 164 165 when(modelFileSupplier.get()) 166 .thenReturn(ImmutableList.of(languageIndependentModelFile, languageDependentModelFile)); 167 168 ModelFileManager.ModelFile bestModelFile = 169 modelFileManager.findBestModelFile(LocaleList.forLanguageTags("zh-hk")); 170 assertThat(bestModelFile).isEqualTo(languageIndependentModelFile); 171 } 172 173 @Test findBestModel_languageIsMoreImportantThanVersion()174 public void findBestModel_languageIsMoreImportantThanVersion() { 175 ModelFileManager.ModelFile matchButOlderModel = 176 new ModelFileManager.ModelFile( 177 new File("/path/a"), 178 1, 179 Collections.singletonList(Locale.forLanguageTag("fr")), 180 "fr", 181 false); 182 183 ModelFileManager.ModelFile mismatchButNewerModel = 184 new ModelFileManager.ModelFile( 185 new File("/path/b"), 186 2, 187 Collections.singletonList(Locale.forLanguageTag("ja")), 188 "ja", 189 false); 190 191 when(modelFileSupplier.get()) 192 .thenReturn(ImmutableList.of(matchButOlderModel, mismatchButNewerModel)); 193 194 ModelFileManager.ModelFile bestModelFile = 195 modelFileManager.findBestModelFile(LocaleList.forLanguageTags("fr")); 196 assertThat(bestModelFile).isEqualTo(matchButOlderModel); 197 } 198 199 @Test findBestModel_languageIsMoreImportantThanVersion_bestModelComesFirst()200 public void findBestModel_languageIsMoreImportantThanVersion_bestModelComesFirst() { 201 ModelFileManager.ModelFile matchLocaleModel = 202 new ModelFileManager.ModelFile( 203 new File("/path/b"), 204 1, 205 Collections.singletonList(Locale.forLanguageTag("ja")), 206 "ja", 207 false); 208 209 ModelFileManager.ModelFile languageIndependentModel = 210 new ModelFileManager.ModelFile(new File("/path/a"), 2, ImmutableList.of(), "", true); 211 when(modelFileSupplier.get()) 212 .thenReturn(ImmutableList.of(matchLocaleModel, languageIndependentModel)); 213 214 ModelFileManager.ModelFile bestModelFile = 215 modelFileManager.findBestModelFile(LocaleList.forLanguageTags("ja")); 216 217 assertThat(bestModelFile).isEqualTo(matchLocaleModel); 218 } 219 220 @Test modelFileEquals()221 public void modelFileEquals() { 222 ModelFileManager.ModelFile modelA = 223 new ModelFileManager.ModelFile( 224 new File("/path/a"), 225 1, 226 Collections.singletonList(Locale.forLanguageTag("ja")), 227 "ja", 228 false); 229 230 ModelFileManager.ModelFile modelB = 231 new ModelFileManager.ModelFile( 232 new File("/path/a"), 233 1, 234 Collections.singletonList(Locale.forLanguageTag("ja")), 235 "ja", 236 false); 237 238 assertThat(modelA).isEqualTo(modelB); 239 } 240 241 @Test modelFile_different()242 public void modelFile_different() { 243 ModelFileManager.ModelFile modelA = 244 new ModelFileManager.ModelFile( 245 new File("/path/a"), 246 1, 247 Collections.singletonList(Locale.forLanguageTag("ja")), 248 "ja", 249 false); 250 251 ModelFileManager.ModelFile modelB = 252 new ModelFileManager.ModelFile( 253 new File("/path/b"), 254 1, 255 Collections.singletonList(Locale.forLanguageTag("ja")), 256 "ja", 257 false); 258 259 assertThat(modelA).isNotEqualTo(modelB); 260 } 261 262 @Test modelFile_getPath()263 public void modelFile_getPath() { 264 ModelFileManager.ModelFile modelA = 265 new ModelFileManager.ModelFile( 266 new File("/path/a"), 267 1, 268 Collections.singletonList(Locale.forLanguageTag("ja")), 269 "ja", 270 false); 271 272 assertThat(modelA.getPath()).isEqualTo("/path/a"); 273 } 274 275 @Test modelFile_getName()276 public void modelFile_getName() { 277 ModelFileManager.ModelFile modelA = 278 new ModelFileManager.ModelFile( 279 new File("/path/a"), 280 1, 281 Collections.singletonList(Locale.forLanguageTag("ja")), 282 "ja", 283 false); 284 285 assertThat(modelA.getName()).isEqualTo("a"); 286 } 287 288 @Test modelFile_isPreferredTo_languageDependentIsBetter()289 public void modelFile_isPreferredTo_languageDependentIsBetter() { 290 ModelFileManager.ModelFile modelA = 291 new ModelFileManager.ModelFile( 292 new File("/path/a"), 293 1, 294 Collections.singletonList(Locale.forLanguageTag("ja")), 295 "ja", 296 false); 297 298 ModelFileManager.ModelFile modelB = 299 new ModelFileManager.ModelFile(new File("/path/b"), 2, ImmutableList.of(), "", true); 300 301 assertThat(modelA.isPreferredTo(modelB)).isTrue(); 302 } 303 304 @Test modelFile_isPreferredTo_version()305 public void modelFile_isPreferredTo_version() { 306 ModelFileManager.ModelFile modelA = 307 new ModelFileManager.ModelFile( 308 new File("/path/a"), 309 2, 310 Collections.singletonList(Locale.forLanguageTag("ja")), 311 "ja", 312 false); 313 314 ModelFileManager.ModelFile modelB = 315 new ModelFileManager.ModelFile(new File("/path/b"), 1, Collections.emptyList(), "", false); 316 317 assertThat(modelA.isPreferredTo(modelB)).isTrue(); 318 } 319 320 @Test modelFile_toModelInfo()321 public void modelFile_toModelInfo() { 322 ModelFileManager.ModelFile modelFile = 323 new ModelFileManager.ModelFile( 324 new File("/path/a"), 2, ImmutableList.of(Locale.JAPANESE), "ja", false); 325 326 ModelInfo modelInfo = modelFile.toModelInfo(); 327 328 assertThat(modelInfo.toModelName()).isEqualTo("ja_v2"); 329 } 330 331 @Test modelFile_toModelInfos()332 public void modelFile_toModelInfos() { 333 ModelFile englishModelFile = 334 new ModelFile(new File("/path/a"), 1, ImmutableList.of(Locale.ENGLISH), "en", false); 335 ModelFile japaneseModelFile = 336 new ModelFile(new File("/path/a"), 2, ImmutableList.of(Locale.JAPANESE), "ja", false); 337 338 ImmutableList<Optional<ModelInfo>> modelInfos = 339 ModelFileManager.ModelFile.toModelInfos( 340 Optional.of(englishModelFile), Optional.of(japaneseModelFile)); 341 342 assertThat( 343 modelInfos.stream() 344 .map(modelFile -> modelFile.transform(ModelInfo::toModelName).or("")) 345 .collect(Collectors.toList())) 346 .containsExactly("en_v1", "ja_v2") 347 .inOrder(); 348 } 349 350 @Test testFileSupplierImpl_updatedFileOnly()351 public void testFileSupplierImpl_updatedFileOnly() throws IOException { 352 updatedModelFile.createNewFile(); 353 File model1 = new File(factoryModelDir, "test1.model"); 354 model1.createNewFile(); 355 File model2 = new File(factoryModelDir, "test2.model"); 356 model2.createNewFile(); 357 new File(factoryModelDir, "not_match_regex.model").createNewFile(); 358 359 List<ModelFileManager.ModelFile> modelFiles = modelFileSupplierImpl.get(); 360 List<String> modelFilePaths = 361 modelFiles.stream().map(modelFile -> modelFile.getPath()).collect(Collectors.toList()); 362 363 assertThat(modelFiles).hasSize(3); 364 assertThat(modelFilePaths) 365 .containsExactly( 366 updatedModelFile.getAbsolutePath(), model1.getAbsolutePath(), model2.getAbsolutePath()); 367 } 368 369 @Test testFileSupplierImpl_empty()370 public void testFileSupplierImpl_empty() { 371 factoryModelDir.delete(); 372 List<ModelFileManager.ModelFile> modelFiles = modelFileSupplierImpl.get(); 373 374 assertThat(modelFiles).hasSize(0); 375 } 376 recursiveDelete(File f)377 private static void recursiveDelete(File f) { 378 if (f.isDirectory()) { 379 for (File innerFile : f.listFiles()) { 380 recursiveDelete(innerFile); 381 } 382 } 383 f.delete(); 384 } 385 } 386