1 /* 2 * Copyright 2022 Google LLC 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 package com.google.android.libraries.mobiledatadownload.internal; 17 18 import static com.google.common.truth.Truth.assertThat; 19 import static com.google.common.util.concurrent.Futures.immediateFuture; 20 import static com.google.common.util.concurrent.Futures.immediateVoidFuture; 21 import static java.util.concurrent.TimeUnit.DAYS; 22 import static org.junit.Assert.assertThrows; 23 import static org.junit.Assume.assumeTrue; 24 import static org.mockito.ArgumentMatchers.any; 25 import static org.mockito.ArgumentMatchers.anyBoolean; 26 import static org.mockito.ArgumentMatchers.anyList; 27 import static org.mockito.ArgumentMatchers.anyLong; 28 import static org.mockito.ArgumentMatchers.eq; 29 import static org.mockito.ArgumentMatchers.isA; 30 import static org.mockito.ArgumentMatchers.isNull; 31 import static org.mockito.Mockito.doThrow; 32 import static org.mockito.Mockito.inOrder; 33 import static org.mockito.Mockito.never; 34 import static org.mockito.Mockito.times; 35 import static org.mockito.Mockito.verify; 36 import static org.mockito.Mockito.verifyNoInteractions; 37 import static org.mockito.Mockito.verifyNoMoreInteractions; 38 import static org.mockito.Mockito.when; 39 40 import android.content.Context; 41 import android.content.SharedPreferences; 42 import android.net.Uri; 43 import androidx.test.core.app.ApplicationProvider; 44 import com.google.mobiledatadownload.internal.MetadataProto.DataFile; 45 import com.google.mobiledatadownload.internal.MetadataProto.DataFile.ChecksumType; 46 import com.google.mobiledatadownload.internal.MetadataProto.DataFileGroupInternal; 47 import com.google.mobiledatadownload.internal.MetadataProto.DownloadConditions; 48 import com.google.mobiledatadownload.internal.MetadataProto.DownloadConditions.DeviceNetworkPolicy; 49 import com.google.mobiledatadownload.internal.MetadataProto.DownloadConditions.DeviceStoragePolicy; 50 import com.google.mobiledatadownload.internal.MetadataProto.GroupKey; 51 import com.google.android.libraries.mobiledatadownload.DownloadException; 52 import com.google.android.libraries.mobiledatadownload.DownloadException.DownloadResultCode; 53 import com.google.android.libraries.mobiledatadownload.FileSource; 54 import com.google.android.libraries.mobiledatadownload.SilentFeedback; 55 import com.google.android.libraries.mobiledatadownload.file.common.testing.TemporaryUri; 56 import com.google.android.libraries.mobiledatadownload.internal.FileGroupManager.GroupDownloadStatus; 57 import com.google.android.libraries.mobiledatadownload.internal.Migrations.FileKeyVersion; 58 import com.google.android.libraries.mobiledatadownload.internal.collect.GroupKeyAndGroup; 59 import com.google.android.libraries.mobiledatadownload.internal.experimentation.DownloadStageManager; 60 import com.google.android.libraries.mobiledatadownload.internal.experimentation.NoOpDownloadStageManager; 61 import com.google.android.libraries.mobiledatadownload.internal.logging.EventLogger; 62 import com.google.android.libraries.mobiledatadownload.internal.logging.FileGroupStatsLogger; 63 import com.google.android.libraries.mobiledatadownload.internal.logging.LoggingStateStore; 64 import com.google.android.libraries.mobiledatadownload.internal.logging.NetworkLogger; 65 import com.google.android.libraries.mobiledatadownload.internal.logging.StorageLogger; 66 import com.google.android.libraries.mobiledatadownload.internal.util.FileGroupUtil; 67 import com.google.android.libraries.mobiledatadownload.internal.util.SharedPreferencesUtil; 68 import com.google.android.libraries.mobiledatadownload.testing.FakeTimeSource; 69 import com.google.android.libraries.mobiledatadownload.testing.MddTestDependencies; 70 import com.google.android.libraries.mobiledatadownload.testing.TestFlags; 71 import com.google.common.base.Optional; 72 import com.google.common.collect.ImmutableList; 73 import com.google.common.collect.ImmutableMap; 74 import com.google.common.labs.concurrent.LabsFutures; 75 import com.google.common.util.concurrent.AsyncFunction; 76 import com.google.common.util.concurrent.Futures; 77 import com.google.common.util.concurrent.ListenableFuture; 78 import com.google.mobiledatadownload.LogEnumsProto.MddClientEvent; 79 import com.google.mobiledatadownload.TransformProto.CompressTransform; 80 import com.google.mobiledatadownload.TransformProto.Transform; 81 import com.google.mobiledatadownload.TransformProto.Transforms; 82 import com.google.mobiledatadownload.TransformProto.ZipTransform; 83 import com.google.protobuf.ByteString; 84 import java.io.IOException; 85 import java.util.List; 86 import java.util.Random; 87 import java.util.concurrent.ExecutionException; 88 import java.util.concurrent.Executor; 89 import java.util.concurrent.Executors; 90 import org.junit.After; 91 import org.junit.Before; 92 import org.junit.Rule; 93 import org.junit.Test; 94 import org.junit.runner.RunWith; 95 import org.mockito.ArgumentCaptor; 96 import org.mockito.Captor; 97 import org.mockito.InOrder; 98 import org.mockito.Mock; 99 import org.mockito.junit.MockitoJUnit; 100 import org.mockito.junit.MockitoRule; 101 import org.robolectric.RobolectricTestRunner; 102 import org.robolectric.annotation.LooperMode; 103 104 // The LooperMode Mode.PAUSED fixes buggy behavior in the legacy looper implementation that can lead 105 // to deadlock in some cases. See documentation at: 106 // http://robolectric.org/javadoc/4.3/org/robolectric/annotation/LooperMode.Mode.html for more 107 // information. 108 @RunWith(RobolectricTestRunner.class) 109 @LooperMode(LooperMode.Mode.PAUSED) 110 public class MobileDataDownloadManagerTest { 111 112 private static final String TEST_GROUP = "test-group"; 113 private static final GroupKey TEST_KEY = 114 FileGroupUtil.createGroupKey(TEST_GROUP, "com.google.android.gms"); 115 private static final Executor CONTROL_EXECUTOR = Executors.newCachedThreadPool(); 116 117 private static final int DEFAULT_DAYS_SINCE_LAST_LOG = 1; 118 119 // Note: We can't make those android uris static variable since the Uri.parse will fail 120 // with initialization. 121 private final Uri fileUri1 = Uri.parse(MddTestUtil.FILE_URI + "1"); 122 private final Uri fileUri2 = Uri.parse(MddTestUtil.FILE_URI + "2"); 123 124 private static final String HOST_APP_LOG_SOURCE = "HOST_APP_LOG_SOURCE"; 125 private static final String HOST_APP_PRIMES_LOG_SOURCE = "HOST_APP_PRIMES_LOG_SOURCE"; 126 127 private Context context; 128 private MobileDataDownloadManager mddManager; 129 private final TestFlags flags = new TestFlags(); 130 131 @Rule(order = 2) 132 public final TemporaryUri tmpUri = new TemporaryUri(); 133 134 @Rule(order = 3) 135 public final MockitoRule mocks = MockitoJUnit.rule(); 136 137 @Mock EventLogger mockLogger; 138 @Mock SharedFileManager mockSharedFileManager; 139 @Mock SharedFilesMetadata mockSharedFilesMetadata; 140 @Mock FileGroupManager mockFileGroupManager; 141 @Mock FileGroupsMetadata mockFileGroupsMetadata; 142 @Mock ExpirationHandler mockExpirationHandler; 143 @Mock SilentFeedback mockSilentFeedback; 144 @Mock StorageLogger mockStorageLogger; 145 @Mock FileGroupStatsLogger mockFileGroupStatsLogger; 146 @Mock NetworkLogger mockNetworkLogger; 147 148 private LoggingStateStore loggingStateStore; 149 private DownloadStageManager downloadStageManager; 150 private FakeTimeSource testClock; 151 152 @Captor ArgumentCaptor<List<GroupKey>> groupKeyListCaptor; 153 154 @Before setUp()155 public void setUp() throws Exception { 156 context = ApplicationProvider.getApplicationContext(); 157 this.testClock = new FakeTimeSource(); 158 testClock.advance(1, DAYS); 159 160 loggingStateStore = 161 MddTestDependencies.LoggingStateStoreImpl.SHARED_PREFERENCES.loggingStateStore( 162 context, Optional.absent(), testClock, CONTROL_EXECUTOR, new Random()); 163 164 loggingStateStore.getAndResetDaysSinceLastMaintenance().get(); 165 testClock.advance(1, DAYS); // The next call into logging state store will return 1 166 167 downloadStageManager = new NoOpDownloadStageManager(); 168 169 mddManager = 170 new MobileDataDownloadManager( 171 context, 172 mockLogger, 173 mockSharedFileManager, 174 mockSharedFilesMetadata, 175 mockFileGroupManager, 176 mockFileGroupsMetadata, 177 mockExpirationHandler, 178 mockSilentFeedback, 179 mockStorageLogger, 180 mockFileGroupStatsLogger, 181 mockNetworkLogger, 182 Optional.absent(), 183 CONTROL_EXECUTOR, 184 flags, 185 loggingStateStore, 186 downloadStageManager); 187 188 // Enable migrations so that init doesn't run all migrations before each test. 189 setMigrationState(MobileDataDownloadManager.MDD_MIGRATED_TO_OFFROAD, true); 190 191 when(mockSharedFileManager.init()).thenReturn(Futures.immediateFuture(true)); 192 when(mockSharedFileManager.clear()).thenReturn(Futures.immediateFuture(null)); 193 when(mockSharedFileManager.cancelDownload(any())).thenReturn(Futures.immediateFuture(null)); 194 when(mockSharedFileManager.cancelDownloadAndClear()).thenReturn(Futures.immediateFuture(null)); 195 196 when(mockSharedFilesMetadata.init()).thenReturn(Futures.immediateFuture(true)); 197 when(mockSharedFilesMetadata.clear()).thenReturn(immediateVoidFuture()); 198 199 when(mockFileGroupsMetadata.init()).thenReturn(Futures.immediateFuture(null)); 200 when(mockFileGroupsMetadata.clear()).thenReturn(Futures.immediateFuture(null)); 201 when(mockFileGroupsMetadata.getAllStaleGroups()) 202 .thenReturn(Futures.immediateFuture(ImmutableList.of())); 203 when(mockFileGroupsMetadata.getAllFreshGroups()) 204 .thenReturn(Futures.immediateFuture(ImmutableList.of())); 205 } 206 207 @After tearDown()208 public void tearDown() throws Exception { 209 mddManager.clear().get(); 210 } 211 212 @Test init_offroadDownloaderMigration()213 public void init_offroadDownloaderMigration() throws Exception { 214 setMigrationState(MobileDataDownloadManager.MDD_MIGRATED_TO_OFFROAD, false); 215 216 mddManager.init().get(); 217 218 verify(mockSharedFileManager).clear(); 219 } 220 221 @Test init_offroadDownloaderMigration_onlyOnce()222 public void init_offroadDownloaderMigration_onlyOnce() throws Exception { 223 setMigrationState(MobileDataDownloadManager.MDD_MIGRATED_TO_OFFROAD, false); 224 225 mddManager.init().get(); 226 mddManager.init().get(); 227 228 verify(mockSharedFileManager, times(1)).clear(); 229 } 230 231 @Test initDoesNotClearsIfInternalInitSucceeds()232 public void initDoesNotClearsIfInternalInitSucceeds() throws Exception { 233 when(mockSharedFileManager.init()).thenReturn(Futures.immediateFuture(true)); 234 235 mddManager.init().get(); 236 237 verify(mockSharedFileManager, times(0)).clear(); 238 } 239 240 @Test initClearsIfInternalInitFails()241 public void initClearsIfInternalInitFails() throws Exception { 242 when(mockSharedFileManager.init()).thenReturn(Futures.immediateFuture(false)); 243 244 mddManager.init().get(); 245 246 verify(mockSharedFileManager).clear(); 247 } 248 249 @Test testAddGroupForDownload()250 public void testAddGroupForDownload() throws Exception { 251 // This tests that the default value of {allowed_readers, allowed_readers_enum} is to allow 252 // access to all 1p google apps. 253 DataFileGroupInternal dataFileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1); 254 when(mockFileGroupManager.addGroupForDownload(eq(TEST_KEY), eq(dataFileGroup))) 255 .thenReturn(Futures.immediateFuture(true)); 256 when(mockFileGroupManager.getFileGroup(eq(TEST_KEY), anyBoolean())) 257 .thenReturn(immediateFuture(dataFileGroup)); 258 when(mockFileGroupManager.verifyGroupDownloaded( 259 eq(TEST_KEY), eq(dataFileGroup), anyBoolean(), any(), any())) 260 .thenReturn(Futures.immediateFuture(GroupDownloadStatus.PENDING)); 261 when(mockFileGroupsMetadata.getAllFreshGroups()) 262 .thenReturn( 263 immediateFuture(ImmutableList.of(GroupKeyAndGroup.create(TEST_KEY, dataFileGroup)))); 264 265 assertThat(mddManager.addGroupForDownload(TEST_KEY, dataFileGroup).get()).isTrue(); 266 verify(mockFileGroupManager).addGroupForDownload(TEST_KEY, dataFileGroup); 267 verify(mockFileGroupManager) 268 .verifyGroupDownloaded(eq(TEST_KEY), eq(dataFileGroup), anyBoolean(), any(), any()); 269 verifyNoInteractions(mockLogger); 270 271 assertThat(mddManager.addGroupForDownload(TEST_KEY, dataFileGroup).get()).isTrue(); 272 } 273 274 @Test testAddGroupForDownload_compressedFile()275 public void testAddGroupForDownload_compressedFile() throws Exception { 276 Migrations.setCurrentVersion(context, FileKeyVersion.USE_CHECKSUM_ONLY); 277 DataFileGroupInternal.Builder fileGroupBuilder = 278 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1).toBuilder(); 279 DataFileGroupInternal dataFileGroup = 280 fileGroupBuilder 281 .setFile( 282 0, 283 fileGroupBuilder.getFile(0).toBuilder() 284 .setDownloadedFileChecksum("downloadchecksum") 285 .setDownloadTransforms( 286 Transforms.newBuilder() 287 .addTransform( 288 Transform.newBuilder() 289 .setCompress(CompressTransform.getDefaultInstance())))) 290 .build(); 291 when(mockFileGroupManager.addGroupForDownload(TEST_KEY, dataFileGroup)) 292 .thenReturn(Futures.immediateFuture(true)); 293 when(mockFileGroupManager.getFileGroup(eq(TEST_KEY), anyBoolean())) 294 .thenReturn(immediateFuture(dataFileGroup)); 295 when(mockFileGroupManager.verifyGroupDownloaded( 296 eq(TEST_KEY), eq(dataFileGroup), anyBoolean(), any(), any())) 297 .thenReturn(Futures.immediateFuture(GroupDownloadStatus.PENDING)); 298 when(mockFileGroupsMetadata.getAllFreshGroups()) 299 .thenReturn( 300 immediateFuture(ImmutableList.of(GroupKeyAndGroup.create(TEST_KEY, dataFileGroup)))); 301 302 assertThat(mddManager.addGroupForDownload(TEST_KEY, dataFileGroup).get()).isTrue(); 303 verify(mockFileGroupManager).addGroupForDownload(TEST_KEY, dataFileGroup); 304 verify(mockFileGroupManager) 305 .verifyGroupDownloaded(eq(TEST_KEY), eq(dataFileGroup), anyBoolean(), any(), any()); 306 verifyNoInteractions(mockLogger); 307 308 assertThat(mddManager.addGroupForDownload(TEST_KEY, dataFileGroup).get()).isTrue(); 309 } 310 311 @Test testAddGroupForDownload_deltaFile()312 public void testAddGroupForDownload_deltaFile() throws Exception { 313 Migrations.setCurrentVersion(context, FileKeyVersion.USE_CHECKSUM_ONLY); 314 DataFileGroupInternal dataFileGroup = 315 MddTestUtil.createFileGroupInternalWithDeltaFile(TEST_GROUP); 316 when(mockFileGroupManager.addGroupForDownload(TEST_KEY, dataFileGroup)) 317 .thenReturn(Futures.immediateFuture(true)); 318 when(mockFileGroupManager.getFileGroup(eq(TEST_KEY), anyBoolean())) 319 .thenReturn(immediateFuture(dataFileGroup)); 320 when(mockFileGroupManager.verifyGroupDownloaded( 321 eq(TEST_KEY), eq(dataFileGroup), anyBoolean(), any(), any())) 322 .thenReturn(Futures.immediateFuture(GroupDownloadStatus.PENDING)); 323 when(mockFileGroupsMetadata.getAllFreshGroups()) 324 .thenReturn( 325 immediateFuture(ImmutableList.of(GroupKeyAndGroup.create(TEST_KEY, dataFileGroup)))); 326 327 assertThat(mddManager.addGroupForDownload(TEST_KEY, dataFileGroup).get()).isTrue(); 328 verify(mockFileGroupManager).addGroupForDownload(TEST_KEY, dataFileGroup); 329 verify(mockFileGroupManager) 330 .verifyGroupDownloaded(eq(TEST_KEY), eq(dataFileGroup), anyBoolean(), any(), any()); 331 verifyNoInteractions(mockLogger); 332 333 assertThat(mddManager.addGroupForDownload(TEST_KEY, dataFileGroup).get()).isTrue(); 334 } 335 336 @Test testAddGroupForDownload_downloadImmediate()337 public void testAddGroupForDownload_downloadImmediate() throws Exception { 338 // This tests that the default value of {allowed_readers, allowed_readers_enum} is to allow 339 // access to all 1p google apps. 340 DataFileGroupInternal dataFileGroup = 341 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1).toBuilder() 342 .setVariantId("testVariant") 343 .setBuildId(10) 344 .build(); 345 when(mockFileGroupManager.addGroupForDownload(TEST_KEY, dataFileGroup)) 346 .thenReturn(Futures.immediateFuture(true)); 347 when(mockFileGroupManager.getFileGroup(eq(TEST_KEY), anyBoolean())) 348 .thenReturn(immediateFuture(dataFileGroup)); 349 when(mockFileGroupManager.verifyGroupDownloaded( 350 eq(TEST_KEY), eq(dataFileGroup), anyBoolean(), any(), any())) 351 .thenReturn(Futures.immediateFuture(GroupDownloadStatus.DOWNLOADED)); 352 when(mockFileGroupsMetadata.getAllFreshGroups()) 353 .thenReturn( 354 immediateFuture(ImmutableList.of(GroupKeyAndGroup.create(TEST_KEY, dataFileGroup)))); 355 356 assertThat(mddManager.addGroupForDownload(TEST_KEY, dataFileGroup).get()).isTrue(); 357 verify(mockFileGroupManager).addGroupForDownload(TEST_KEY, dataFileGroup); 358 verify(mockFileGroupManager) 359 .verifyGroupDownloaded(eq(TEST_KEY), eq(dataFileGroup), anyBoolean(), any(), any()); 360 verify(mockLogger) 361 .logEventSampled( 362 MddClientEvent.Code.EVENT_CODE_UNSPECIFIED, 363 TEST_GROUP, 364 /* fileGroupVersionNumber= */ 0, 365 /* buildId= */ dataFileGroup.getBuildId(), 366 /* variantId= */ dataFileGroup.getVariantId()); 367 368 assertThat(mddManager.addGroupForDownload(TEST_KEY, dataFileGroup).get()).isTrue(); 369 } 370 371 @Test testAddGroupForDownload_throwsIOException()372 public void testAddGroupForDownload_throwsIOException() throws Exception { 373 DataFileGroupInternal dataFileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1); 374 when(mockFileGroupManager.addGroupForDownload(TEST_KEY, dataFileGroup)) 375 .thenThrow(new IOException()); 376 377 // assertThat(mddManager.addGroupForDownload(TEST_KEY, dataFileGroup).get()).isFalse(); 378 ExecutionException exception = 379 assertThrows( 380 ExecutionException.class, 381 () -> mddManager.addGroupForDownload(TEST_KEY, dataFileGroup).get()); 382 assertThat(exception).hasCauseThat().isInstanceOf(IOException.class); 383 verify(mockFileGroupManager).addGroupForDownload(TEST_KEY, dataFileGroup); 384 verify(mockSilentFeedback).send(isA(IOException.class), isA(String.class)); 385 verifyNoInteractions(mockLogger); 386 } 387 388 @Test testAddGroupForDownload_throwsUninstalledAppException()389 public void testAddGroupForDownload_throwsUninstalledAppException() throws Exception { 390 DataFileGroupInternal dataFileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1); 391 when(mockFileGroupManager.addGroupForDownload(TEST_KEY, dataFileGroup)) 392 .thenThrow(new UninstalledAppException()); 393 394 // assertThat(mddManager.addGroupForDownload(TEST_KEY, dataFileGroup).get()).isFalse(); 395 ExecutionException exception = 396 assertThrows( 397 ExecutionException.class, 398 () -> mddManager.addGroupForDownload(TEST_KEY, dataFileGroup).get()); 399 assertThat(exception).hasCauseThat().isInstanceOf(UninstalledAppException.class); 400 verify(mockFileGroupManager).addGroupForDownload(TEST_KEY, dataFileGroup); 401 verifyNoInteractions(mockLogger); 402 } 403 404 @Test testAddGroupForDownload_throwsExpiredFileGroupException()405 public void testAddGroupForDownload_throwsExpiredFileGroupException() throws Exception { 406 DataFileGroupInternal dataFileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1); 407 when(mockFileGroupManager.addGroupForDownload(TEST_KEY, dataFileGroup)) 408 .thenThrow(new ExpiredFileGroupException()); 409 410 // assertThat(mddManager.addGroupForDownload(TEST_KEY, dataFileGroup).get()).isFalse(); 411 ExecutionException exception = 412 assertThrows( 413 ExecutionException.class, 414 () -> mddManager.addGroupForDownload(TEST_KEY, dataFileGroup).get()); 415 assertThat(exception).hasCauseThat().isInstanceOf(ExpiredFileGroupException.class); 416 verify(mockFileGroupManager).addGroupForDownload(TEST_KEY, dataFileGroup); 417 verifyNoInteractions(mockLogger); 418 } 419 420 @Test testAddGroupForDownload_multipleCallsSameGroup()421 public void testAddGroupForDownload_multipleCallsSameGroup() throws Exception { 422 DataFileGroupInternal dataFileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1); 423 when(mockFileGroupManager.addGroupForDownload(TEST_KEY, dataFileGroup)) 424 .thenReturn(Futures.immediateFuture(true), Futures.immediateFuture(false)); 425 when(mockFileGroupManager.getFileGroup(eq(TEST_KEY), anyBoolean())) 426 .thenReturn(immediateFuture(dataFileGroup)); 427 when(mockFileGroupManager.verifyGroupDownloaded( 428 eq(TEST_KEY), eq(dataFileGroup), anyBoolean(), any(), any())) 429 .thenReturn(Futures.immediateFuture(GroupDownloadStatus.PENDING)); 430 when(mockFileGroupsMetadata.getAllFreshGroups()) 431 .thenReturn( 432 immediateFuture(ImmutableList.of(GroupKeyAndGroup.create(TEST_KEY, dataFileGroup)))); 433 434 assertThat(mddManager.addGroupForDownload(TEST_KEY, dataFileGroup).get()).isTrue(); 435 assertThat(mddManager.addGroupForDownload(TEST_KEY, dataFileGroup).get()).isTrue(); 436 verify(mockFileGroupManager, times(2)).addGroupForDownload(TEST_KEY, dataFileGroup); 437 verify(mockFileGroupManager, times(1)) 438 .verifyGroupDownloaded(eq(TEST_KEY), eq(dataFileGroup), anyBoolean(), any(), any()); 439 verifyNoInteractions(mockExpirationHandler); 440 verifyNoInteractions(mockLogger); 441 } 442 443 @Test testAddGroupForDownload_isValidGroup()444 public void testAddGroupForDownload_isValidGroup() throws Exception { 445 DataFileGroupInternal dataFileGroup = 446 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1).toBuilder() 447 .setGroupName("") 448 .setVariantId("testVariant") 449 .setBuildId(10) 450 .build(); 451 assertThat(mddManager.addGroupForDownload(TEST_KEY, dataFileGroup).get()).isFalse(); 452 verifyNoInteractions(mockFileGroupManager); 453 454 verify(mockLogger) 455 .logEventSampled( 456 MddClientEvent.Code.EVENT_CODE_UNSPECIFIED, 457 "", 458 /* fileGroupVersionNumber= */ 0, 459 /* buildId= */ dataFileGroup.getBuildId(), 460 /* variantId= */ dataFileGroup.getVariantId()); 461 } 462 463 @Test testAddGroupForDownload_noChecksum()464 public void testAddGroupForDownload_noChecksum() throws Exception { 465 DataFileGroupInternal.Builder fileGroupBuilder = 466 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1).toBuilder(); 467 DataFileGroupInternal dataFileGroup = 468 fileGroupBuilder 469 .setFile( 470 0, 471 fileGroupBuilder.getFile(0).toBuilder() 472 .setChecksumType(ChecksumType.NONE) 473 .setChecksum("")) 474 .build(); 475 476 ArgumentCaptor<DataFileGroupInternal> dataFileGroupCaptor = 477 ArgumentCaptor.forClass(DataFileGroupInternal.class); 478 479 when(mockFileGroupManager.addGroupForDownload(eq(TEST_KEY), dataFileGroupCaptor.capture())) 480 .thenReturn(Futures.immediateFuture(true)); 481 when(mockFileGroupManager.getFileGroup(eq(TEST_KEY), anyBoolean())) 482 .thenReturn(Futures.immediateFuture(dataFileGroup)); 483 when(mockFileGroupManager.verifyGroupDownloaded( 484 eq(TEST_KEY), eq(dataFileGroup), anyBoolean(), any(), any())) 485 .thenReturn(Futures.immediateFuture(GroupDownloadStatus.PENDING)); 486 when(mockFileGroupsMetadata.getAllFreshGroups()) 487 .thenReturn( 488 immediateFuture(ImmutableList.of(GroupKeyAndGroup.create(TEST_KEY, dataFileGroup)))); 489 490 assertThat(mddManager.addGroupForDownload(TEST_KEY, dataFileGroup).get()).isTrue(); 491 verifyNoInteractions(mockLogger); 492 493 DataFileGroupInternal capturedDataFileGroup = dataFileGroupCaptor.getValue(); 494 495 assertThat(capturedDataFileGroup.getFileCount()).isEqualTo(1); 496 DataFile dataFile = capturedDataFileGroup.getFile(0); 497 // Checksum of the Url. 498 assertThat(dataFile.getChecksum()).isEqualTo("0d79849a839d83fbc53e3bfe794ec38a305b7220"); 499 } 500 501 @Test testAddGroupForDownload_noChecksumWithZipTransform()502 public void testAddGroupForDownload_noChecksumWithZipTransform() throws Exception { 503 DataFileGroupInternal.Builder fileGroupBuilder = 504 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1).toBuilder(); 505 DataFileGroupInternal dataFileGroup = 506 fileGroupBuilder 507 .setFile( 508 0, 509 fileGroupBuilder.getFile(0).toBuilder() 510 .setChecksumType(ChecksumType.NONE) 511 .setChecksum("") 512 .setDownloadedFileChecksum("") 513 .setDownloadTransforms( 514 Transforms.newBuilder() 515 .addTransform( 516 Transform.newBuilder() 517 .setZip(ZipTransform.newBuilder().setTarget("*"))))) 518 .build(); 519 520 ArgumentCaptor<DataFileGroupInternal> dataFileGroupCaptor = 521 ArgumentCaptor.forClass(DataFileGroupInternal.class); 522 523 when(mockFileGroupManager.addGroupForDownload(eq(TEST_KEY), dataFileGroupCaptor.capture())) 524 .thenReturn(Futures.immediateFuture(true)); 525 when(mockFileGroupManager.getFileGroup(eq(TEST_KEY), anyBoolean())) 526 .thenReturn(immediateFuture(dataFileGroup)); 527 when(mockFileGroupManager.verifyGroupDownloaded( 528 eq(TEST_KEY), eq(dataFileGroup), anyBoolean(), any(), any())) 529 .thenReturn(Futures.immediateFuture(GroupDownloadStatus.PENDING)); 530 when(mockFileGroupsMetadata.getAllFreshGroups()) 531 .thenReturn( 532 immediateFuture(ImmutableList.of(GroupKeyAndGroup.create(TEST_KEY, dataFileGroup)))); 533 534 assertThat(mddManager.addGroupForDownload(TEST_KEY, dataFileGroup).get()).isTrue(); 535 verifyNoInteractions(mockLogger); 536 537 DataFileGroupInternal capturedDataFileGroup = dataFileGroupCaptor.getValue(); 538 539 assertThat(capturedDataFileGroup.getFileCount()).isEqualTo(1); 540 DataFile dataFile = capturedDataFileGroup.getFile(0); 541 // Checksum of url is propagated to downloaded file checksum if data file has zip transform. 542 assertThat(dataFile.getChecksum()).isEmpty(); 543 assertThat(dataFile.getDownloadedFileChecksum()) 544 .isEqualTo("0d79849a839d83fbc53e3bfe794ec38a305b7220"); 545 } 546 547 @Test testAddGroupForDownload_noChecksumAndNotSetChecksumType()548 public void testAddGroupForDownload_noChecksumAndNotSetChecksumType() throws Exception { 549 DataFileGroupInternal.Builder fileGroupBuilder = 550 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1).toBuilder(); 551 // Not setting ChecksumType.NONE 552 DataFileGroupInternal dataFileGroup = 553 fileGroupBuilder 554 .setFile(0, fileGroupBuilder.getFile(0).toBuilder().setChecksum("")) 555 .build(); 556 557 assertThat(mddManager.addGroupForDownload(TEST_KEY, dataFileGroup).get()).isFalse(); 558 verify(mockLogger) 559 .logEventSampled( 560 MddClientEvent.Code.EVENT_CODE_UNSPECIFIED, 561 TEST_GROUP, 562 /* fileGroupVersionNumber= */ 0, 563 /* buildId= */ 0, 564 /* variantId= */ ""); 565 verifyNoInteractions(mockFileGroupManager); 566 } 567 568 @Test testAddGroupForDownload_sideloadedFile_onlyWhenSideloadingIsEnabled()569 public void testAddGroupForDownload_sideloadedFile_onlyWhenSideloadingIsEnabled() 570 throws Exception { 571 // Create sideloaded group 572 DataFileGroupInternal sideloadedGroup = 573 DataFileGroupInternal.newBuilder() 574 .setGroupName(TEST_GROUP) 575 .addFile( 576 DataFile.newBuilder() 577 .setFileId("sideloaded_file") 578 .setUrlToDownload("file:/test") 579 .setChecksumType(DataFile.ChecksumType.NONE) 580 .build()) 581 .build(); 582 583 when(mockFileGroupManager.addGroupForDownload(eq(TEST_KEY), any())) 584 .thenReturn(Futures.immediateFuture(true)); 585 when(mockFileGroupManager.getFileGroup(eq(TEST_KEY), anyBoolean())) 586 .thenReturn(immediateFuture(sideloadedGroup)); 587 when(mockFileGroupManager.verifyGroupDownloaded( 588 eq(TEST_KEY), eq(sideloadedGroup), anyBoolean(), any(), any())) 589 .thenReturn(Futures.immediateFuture(GroupDownloadStatus.PENDING)); 590 when(mockFileGroupsMetadata.getAllFreshGroups()) 591 .thenReturn( 592 immediateFuture(ImmutableList.of(GroupKeyAndGroup.create(TEST_KEY, sideloadedGroup)))); 593 594 { 595 // Force sideloading off 596 flags.enableSideloading = Optional.of(false); 597 598 assertThat(mddManager.addGroupForDownload(TEST_KEY, sideloadedGroup).get()).isFalse(); 599 } 600 601 { 602 // Force sideloading on 603 flags.enableSideloading = Optional.of(true); 604 605 assertThat(mddManager.addGroupForDownload(TEST_KEY, sideloadedGroup).get()).isTrue(); 606 } 607 } 608 609 @Test testRemoveFileGroup()610 public void testRemoveFileGroup() throws Exception { 611 GroupKey groupKey = 612 GroupKey.newBuilder() 613 .setGroupName(TEST_GROUP) 614 .setOwnerPackage(context.getPackageName()) 615 .build(); 616 when(mockFileGroupManager.removeFileGroup(eq(groupKey), eq(false))) 617 .thenReturn(Futures.immediateFuture(null /* Void */)); 618 619 mddManager.removeFileGroup(groupKey, /* pendingOnly= */ false).get(); 620 621 verify(mockFileGroupManager).removeFileGroup(groupKey, /* pendingOnly= */ false); 622 verifyNoMoreInteractions(mockFileGroupManager); 623 verifyNoInteractions(mockLogger); 624 } 625 626 @Test testRemoveFileGroup_onFailure()627 public void testRemoveFileGroup_onFailure() throws Exception { 628 GroupKey groupKey = 629 GroupKey.newBuilder() 630 .setGroupName(TEST_GROUP) 631 .setOwnerPackage(context.getPackageName()) 632 .build(); 633 doThrow(new IOException()) 634 .when(mockFileGroupManager) 635 .removeFileGroup(groupKey, /* pendingOnly= */ false); 636 637 ExecutionException ex = 638 assertThrows( 639 ExecutionException.class, 640 mddManager.removeFileGroup(groupKey, /* pendingOnly= */ false)::get); 641 assertThat(ex).hasCauseThat().isInstanceOf(IOException.class); 642 643 verify(mockFileGroupManager).removeFileGroup(groupKey, /* pendingOnly= */ false); 644 verifyNoMoreInteractions(mockFileGroupManager); 645 verifyNoInteractions(mockLogger); 646 } 647 648 @Test testRemoveFileGroups()649 public void testRemoveFileGroups() throws Exception { 650 GroupKey groupKey1 = 651 GroupKey.newBuilder() 652 .setGroupName(TEST_GROUP) 653 .setOwnerPackage(context.getPackageName()) 654 .build(); 655 GroupKey groupKey2 = 656 GroupKey.newBuilder() 657 .setGroupName(TEST_GROUP + "_2") 658 .setOwnerPackage(context.getPackageName()) 659 .build(); 660 661 when(mockFileGroupManager.removeFileGroups(groupKeyListCaptor.capture())) 662 .thenReturn(Futures.immediateVoidFuture()); 663 664 mddManager.removeFileGroups(ImmutableList.of(groupKey1, groupKey2)).get(); 665 666 verify(mockFileGroupManager).removeFileGroups(anyList()); 667 List<GroupKey> groupKeyListCapture = groupKeyListCaptor.getValue(); 668 assertThat(groupKeyListCapture).hasSize(2); 669 assertThat(groupKeyListCapture).contains(groupKey1); 670 assertThat(groupKeyListCapture).contains(groupKey2); 671 } 672 673 @Test testRemoveFileGroups_onFailure()674 public void testRemoveFileGroups_onFailure() throws Exception { 675 GroupKey groupKey1 = 676 GroupKey.newBuilder() 677 .setGroupName(TEST_GROUP) 678 .setOwnerPackage(context.getPackageName()) 679 .build(); 680 GroupKey groupKey2 = 681 GroupKey.newBuilder() 682 .setGroupName(TEST_GROUP + "_2") 683 .setOwnerPackage(context.getPackageName()) 684 .build(); 685 686 when(mockFileGroupManager.removeFileGroups(groupKeyListCaptor.capture())) 687 .thenReturn(Futures.immediateFailedFuture(new Exception("Test failure"))); 688 689 ExecutionException ex = 690 assertThrows( 691 ExecutionException.class, 692 () -> mddManager.removeFileGroups(ImmutableList.of(groupKey1, groupKey2)).get()); 693 assertThat(ex).hasMessageThat().contains("Test failure"); 694 695 verify(mockFileGroupManager).removeFileGroups(anyList()); 696 List<GroupKey> groupKeyListCapture = groupKeyListCaptor.getValue(); 697 assertThat(groupKeyListCapture).hasSize(2); 698 assertThat(groupKeyListCapture).contains(groupKey1); 699 assertThat(groupKeyListCapture).contains(groupKey2); 700 } 701 702 @Test testGetDownloadedGroup()703 public void testGetDownloadedGroup() throws Exception { 704 DataFileGroupInternal dataFileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1); 705 when(mockFileGroupManager.getFileGroup(TEST_KEY, true)) 706 .thenReturn(Futures.immediateFuture(dataFileGroup)); 707 708 DataFileGroupInternal completedDataFileGroup = mddManager.getFileGroup(TEST_KEY, true).get(); 709 MddTestUtil.assertMessageEquals(dataFileGroup, completedDataFileGroup); 710 verifyNoInteractions(mockLogger); 711 } 712 713 @Test testGetDataFileUri()714 public void testGetDataFileUri() throws Exception { 715 DataFileGroupInternal dataFileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2); 716 717 when(mockFileGroupManager.getOnDeviceUris(dataFileGroup)) 718 .thenReturn( 719 Futures.immediateFuture( 720 ImmutableMap.of( 721 dataFileGroup.getFile(0), fileUri1, dataFileGroup.getFile(1), fileUri2))); 722 723 assertThat( 724 mddManager 725 .getDataFileUri( 726 dataFileGroup.getFile(0), dataFileGroup, /* verifyIsolatedStructure= */ true) 727 .get()) 728 .isEqualTo(fileUri1); 729 assertThat( 730 mddManager 731 .getDataFileUri( 732 dataFileGroup.getFile(1), dataFileGroup, /* verifyIsolatedStructure= */ true) 733 .get()) 734 .isEqualTo(fileUri2); 735 } 736 737 @Test testGetDataFileUri_readTransform()738 public void testGetDataFileUri_readTransform() throws Exception { 739 DataFileGroupInternal dataFileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2); 740 741 Transforms compressTransform = 742 Transforms.newBuilder() 743 .addTransform( 744 Transform.newBuilder().setCompress(CompressTransform.getDefaultInstance())) 745 .build(); 746 dataFileGroup = 747 dataFileGroup.toBuilder() 748 .setFile(0, dataFileGroup.getFile(0).toBuilder().setReadTransforms(compressTransform)) 749 .build(); 750 751 when(mockFileGroupManager.getOnDeviceUris(dataFileGroup)) 752 .thenReturn( 753 Futures.immediateFuture( 754 ImmutableMap.of( 755 dataFileGroup.getFile(0), fileUri1, dataFileGroup.getFile(1), fileUri2))); 756 757 assertThat( 758 mddManager 759 .getDataFileUri( 760 dataFileGroup.getFile(0), dataFileGroup, /* verifyIsolatedStructure= */ true) 761 .get()) 762 .isEqualTo(fileUri1.buildUpon().encodedFragment("transform=compress").build()); 763 assertThat( 764 mddManager 765 .getDataFileUri( 766 dataFileGroup.getFile(1), dataFileGroup, /* verifyIsolatedStructure= */ true) 767 .get()) 768 .isEqualTo(fileUri2); 769 } 770 771 @Test testGetDataFileUri_relativeFilePaths()772 public void testGetDataFileUri_relativeFilePaths() throws Exception { 773 DataFile relativePathFile = MddTestUtil.createRelativePathDataFile("file", 1, "test"); 774 DataFileGroupInternal testFileGroup = 775 DataFileGroupInternal.newBuilder() 776 .setGroupName(TEST_GROUP) 777 .setPreserveFilenamesAndIsolateFiles(true) 778 .addFile(relativePathFile) 779 .build(); 780 781 Uri symlinkedUri = 782 FileGroupUtil.getIsolatedFileUri( 783 context, Optional.absent(), relativePathFile, testFileGroup); 784 785 when(mockFileGroupManager.getOnDeviceUris(testFileGroup)) 786 .thenReturn(Futures.immediateFuture(ImmutableMap.of(testFileGroup.getFile(0), fileUri1))); 787 when(mockFileGroupManager.getIsolatedFileUris(testFileGroup)) 788 .thenReturn(ImmutableMap.of(testFileGroup.getFile(0), symlinkedUri)); 789 when(mockFileGroupManager.verifyIsolatedFileUris(any(), any())) 790 .thenReturn(ImmutableMap.of(testFileGroup.getFile(0), symlinkedUri)); 791 792 assertThat( 793 mddManager 794 .getDataFileUri( 795 relativePathFile, testFileGroup, /* verifyIsolatedStructure= */ true) 796 .get()) 797 .isEqualTo(symlinkedUri); 798 } 799 800 @Test testGetDataFileUri_whenSymlinkRequiredButNotPresent_returnsNull()801 public void testGetDataFileUri_whenSymlinkRequiredButNotPresent_returnsNull() throws Exception { 802 DataFile relativePathFile = MddTestUtil.createRelativePathDataFile("file", 1, "test"); 803 DataFileGroupInternal testFileGroup = 804 DataFileGroupInternal.newBuilder() 805 .setGroupName(TEST_GROUP) 806 .setPreserveFilenamesAndIsolateFiles(true) 807 .addFile(relativePathFile) 808 .build(); 809 810 when(mockFileGroupManager.getOnDeviceUris(testFileGroup)) 811 .thenReturn(Futures.immediateFuture(ImmutableMap.of(testFileGroup.getFile(0), fileUri1))); 812 when(mockFileGroupManager.getIsolatedFileUris(testFileGroup)).thenReturn(ImmutableMap.of()); 813 when(mockFileGroupManager.verifyIsolatedFileUris(any(), any())).thenReturn(ImmutableMap.of()); 814 815 assertThat( 816 mddManager 817 .getDataFileUri( 818 relativePathFile, testFileGroup, /* verifyIsolatedStructure= */ true) 819 .get()) 820 .isNull(); 821 } 822 823 @Test testImportFiles_failed()824 public void testImportFiles_failed() throws Exception { 825 ImmutableList<DataFile> updatedDataFileList = 826 ImmutableList.of( 827 MddTestUtil.createDataFile("inline-file", 0).toBuilder() 828 .setUrlToDownload("inlinefile:sha1:abcdef") 829 .build()); 830 ImmutableMap<String, FileSource> inlineFileMap = 831 ImmutableMap.of( 832 "inline-file", FileSource.ofByteString(ByteString.copyFromUtf8("TEST_CONTENT"))); 833 when(mockFileGroupManager.importFilesIntoFileGroup( 834 eq(TEST_KEY), anyLong(), any(), any(), any(), any(), any())) 835 .thenReturn(Futures.immediateFailedFuture(new Exception("Test failure"))); 836 837 ExecutionException ex = 838 assertThrows( 839 ExecutionException.class, 840 () -> 841 mddManager 842 .importFiles( 843 TEST_KEY, 844 1, 845 "testvariant", 846 updatedDataFileList, 847 inlineFileMap, 848 Optional.absent(), 849 noCustomValidation()) 850 .get()); 851 852 assertThat(ex).hasMessageThat().contains("Test failure"); 853 verify(mockFileGroupManager) 854 .importFilesIntoFileGroup( 855 eq(TEST_KEY), 856 anyLong(), 857 any(), 858 eq(updatedDataFileList), 859 eq(inlineFileMap), 860 any(), 861 any()); 862 } 863 864 @Test testImportFiles_succeeds()865 public void testImportFiles_succeeds() throws Exception { 866 ImmutableList<DataFile> updatedDataFileList = 867 ImmutableList.of( 868 MddTestUtil.createDataFile("inline-file", 0).toBuilder() 869 .setUrlToDownload("inlinefile:sha1:abcdef") 870 .build()); 871 ImmutableMap<String, FileSource> inlineFileMap = 872 ImmutableMap.of( 873 "inline-file", FileSource.ofByteString(ByteString.copyFromUtf8("TEST_CONTENT"))); 874 875 when(mockFileGroupManager.importFilesIntoFileGroup( 876 eq(TEST_KEY), anyLong(), any(), any(), any(), any(), any())) 877 .thenReturn(immediateVoidFuture()); 878 879 mddManager 880 .importFiles( 881 TEST_KEY, 882 1, 883 "testvariant", 884 updatedDataFileList, 885 inlineFileMap, 886 Optional.absent(), 887 noCustomValidation()) 888 .get(); 889 890 verify(mockFileGroupManager) 891 .importFilesIntoFileGroup( 892 eq(TEST_KEY), 893 anyLong(), 894 any(), 895 eq(updatedDataFileList), 896 eq(inlineFileMap), 897 any(), 898 any()); 899 } 900 901 @Test testDownloadPendingGroup_failed()902 public void testDownloadPendingGroup_failed() { 903 when(mockFileGroupManager.downloadFileGroup(eq(TEST_KEY), isNull(), any())) 904 .thenReturn( 905 Futures.immediateFailedFuture( 906 DownloadException.builder() 907 .setDownloadResultCode(DownloadResultCode.UNKNOWN_ERROR) 908 .setMessage("Fail") 909 .build())); 910 911 ListenableFuture<DataFileGroupInternal> downloadFuture = 912 mddManager.downloadFileGroup(TEST_KEY, Optional.absent(), noCustomValidation()); 913 assertThrows(ExecutionException.class, downloadFuture::get); 914 DownloadException unused = 915 LabsFutures.getFailureCauseAs(downloadFuture, DownloadException.class); 916 917 verify(mockFileGroupManager).downloadFileGroup(eq(TEST_KEY), isNull(), any()); 918 } 919 920 @Test testDownloadPendingGroup_downloadCondition_absent()921 public void testDownloadPendingGroup_downloadCondition_absent() throws Exception { 922 DataFileGroupInternal pendingGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1); 923 924 when(mockFileGroupManager.downloadFileGroup(eq(TEST_KEY), isNull(), any())) 925 .thenReturn(Futures.immediateFuture(pendingGroup)); 926 927 assertThat( 928 mddManager.downloadFileGroup(TEST_KEY, Optional.absent(), noCustomValidation()).get()) 929 .isEqualTo(pendingGroup); 930 931 verify(mockFileGroupManager).downloadFileGroup(eq(TEST_KEY), isNull(), any()); 932 } 933 934 @Test testDownloadPendingGroup_downloadCondition_present()935 public void testDownloadPendingGroup_downloadCondition_present() throws Exception { 936 DataFileGroupInternal pendingGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1); 937 938 Optional<DownloadConditions> downloadConditionsOptional = 939 Optional.of( 940 DownloadConditions.newBuilder() 941 .setDeviceNetworkPolicy(DeviceNetworkPolicy.DOWNLOAD_ON_ANY_NETWORK) 942 .setDeviceStoragePolicy(DeviceStoragePolicy.BLOCK_DOWNLOAD_IN_LOW_STORAGE) 943 .build()); 944 945 when(mockFileGroupManager.downloadFileGroup( 946 eq(TEST_KEY), eq(downloadConditionsOptional.get()), any())) 947 .thenReturn(Futures.immediateFuture(pendingGroup)); 948 949 assertThat( 950 mddManager 951 .downloadFileGroup(TEST_KEY, downloadConditionsOptional, noCustomValidation()) 952 .get()) 953 .isEqualTo(pendingGroup); 954 955 verify(mockFileGroupManager) 956 .downloadFileGroup(eq(TEST_KEY), eq(downloadConditionsOptional.get()), any()); 957 } 958 959 @Test testDownloadAllPendingGroups()960 public void testDownloadAllPendingGroups() throws Exception { 961 when(mockFileGroupManager.scheduleAllPendingGroupsForDownload(eq(true), any())) 962 .thenReturn(Futures.immediateFuture(null)); 963 964 mddManager.downloadAllPendingGroups(true, noCustomValidation()).get(); 965 966 verify(mockLogger).logEventSampled(MddClientEvent.Code.EVENT_CODE_UNSPECIFIED); 967 verify(mockFileGroupManager).scheduleAllPendingGroupsForDownload(eq(true), any()); 968 verifyNoMoreInteractions(mockLogger); 969 } 970 971 @Test testVerifyPendingGroups()972 public void testVerifyPendingGroups() throws Exception { 973 when(mockFileGroupManager.verifyAllPendingGroupsDownloaded(any())) 974 .thenReturn(Futures.immediateFuture(null)); 975 976 mddManager.verifyAllPendingGroups(noCustomValidation()).get(); 977 978 verify(mockFileGroupManager).verifyAllPendingGroupsDownloaded(any()); 979 verify(mockLogger).logEventSampled(MddClientEvent.Code.EVENT_CODE_UNSPECIFIED); 980 verifyNoMoreInteractions(mockLogger); 981 } 982 983 @Test testMaintenance_mddFileExpiration()984 public void testMaintenance_mddFileExpiration() throws Exception { 985 assumeTrue(flags.mddEnableGarbageCollection()); 986 987 setupMaintenanceTasks(); 988 989 mddManager.maintenance().get(); 990 991 verify(mockFileGroupManager).deleteUninstalledAppGroups(); 992 993 verify(mockExpirationHandler).updateExpiration(); 994 995 verify(mockFileGroupStatsLogger).log(DEFAULT_DAYS_SINCE_LAST_LOG); 996 verify(mockLogger).logEventSampled(MddClientEvent.Code.EVENT_CODE_UNSPECIFIED); 997 } 998 999 @Test testMaintenance_gcFlagControlsGcDuringMaintenance()1000 public void testMaintenance_gcFlagControlsGcDuringMaintenance() throws Exception { 1001 setupMaintenanceTasks(); 1002 flags.mddEnableGarbageCollection = Optional.of(false); 1003 1004 mddManager.maintenance().get(); 1005 1006 verify(mockExpirationHandler, never()).updateExpiration(); 1007 } 1008 1009 @Test testMaintenance_logStorage()1010 public void testMaintenance_logStorage() throws Exception { 1011 setupMaintenanceTasks(); 1012 1013 mddManager.maintenance().get(); 1014 1015 verify(mockStorageLogger).logStorageStats(DEFAULT_DAYS_SINCE_LAST_LOG); 1016 } 1017 1018 @Test testMaintenance_logNetwork()1019 public void testMaintenance_logNetwork() throws Exception { 1020 setupMaintenanceTasks(); 1021 1022 mddManager.maintenance().get(); 1023 verify(mockNetworkLogger).log(); 1024 } 1025 1026 @Test maintenance_triggerSync_absentSpe()1027 public void maintenance_triggerSync_absentSpe() throws Exception { 1028 mddManager = 1029 new MobileDataDownloadManager( 1030 context, 1031 mockLogger, 1032 mockSharedFileManager, 1033 mockSharedFilesMetadata, 1034 mockFileGroupManager, 1035 mockFileGroupsMetadata, 1036 mockExpirationHandler, 1037 mockSilentFeedback, 1038 mockStorageLogger, 1039 mockFileGroupStatsLogger, 1040 mockNetworkLogger, 1041 Optional.absent(), 1042 CONTROL_EXECUTOR, 1043 flags, 1044 loggingStateStore, 1045 downloadStageManager); 1046 1047 setupMaintenanceTasks(); 1048 1049 mddManager.maintenance().get(); 1050 1051 // With absent SPE, no triggerSync was called. 1052 verify(mockFileGroupManager, never()).triggerSyncAllPendingGroups(); 1053 } 1054 1055 @Test testMaintenance_deleteRemovedAccountGroups()1056 public void testMaintenance_deleteRemovedAccountGroups() throws Exception { 1057 setupMaintenanceTasks(); 1058 1059 flags.mddDeleteGroupsRemovedAccounts = Optional.of(true); 1060 1061 mddManager.maintenance().get(); 1062 verify(mockFileGroupManager).deleteRemovedAccountGroups(); 1063 } 1064 setupMaintenanceTasks()1065 void setupMaintenanceTasks() { 1066 1067 flags.enableDaysSinceLastMaintenanceTracking = Optional.of(true); 1068 1069 when(mockStorageLogger.logStorageStats(DEFAULT_DAYS_SINCE_LAST_LOG)) 1070 .thenReturn(Futures.immediateVoidFuture()); 1071 when(mockExpirationHandler.updateExpiration()).thenReturn(Futures.immediateVoidFuture()); 1072 when(mockFileGroupStatsLogger.log(DEFAULT_DAYS_SINCE_LAST_LOG)) 1073 .thenReturn(Futures.immediateVoidFuture()); 1074 when(mockNetworkLogger.log()).thenReturn(Futures.immediateVoidFuture()); 1075 when(mockFileGroupManager.logAndDeleteForMissingSharedFiles()) 1076 .thenReturn(Futures.immediateVoidFuture()); 1077 when(mockFileGroupManager.deleteUninstalledAppGroups()) 1078 .thenReturn(Futures.immediateVoidFuture()); 1079 when(mockFileGroupManager.deleteRemovedAccountGroups()) 1080 .thenReturn(Futures.immediateVoidFuture()); 1081 when(mockFileGroupManager.triggerSyncAllPendingGroups()).thenReturn(immediateVoidFuture()); 1082 1083 when(mockFileGroupManager.verifyAndAttemptToRepairIsolatedFiles()) 1084 .thenReturn(immediateVoidFuture()); 1085 } 1086 1087 @Test testRemoveExpiredGroupsAndFiles()1088 public void testRemoveExpiredGroupsAndFiles() throws Exception { 1089 setupMaintenanceTasks(); 1090 1091 mddManager.removeExpiredGroupsAndFiles().get(); 1092 1093 verify(mockExpirationHandler).updateExpiration(); 1094 } 1095 1096 @Test testClear()1097 public void testClear() throws Exception { 1098 mddManager.clear().get(); 1099 1100 verify(mockSharedFileManager).cancelDownloadAndClear(); 1101 verifyNoInteractions(mockLogger); 1102 } 1103 1104 @Test testCheckResetTrigger_resetTrigger_noIncrement()1105 public void testCheckResetTrigger_resetTrigger_noIncrement() throws Exception { 1106 setSavedResetValue(1); 1107 flags.mddResetTrigger = Optional.of(1); 1108 1109 mddManager.checkResetTrigger().get(); 1110 verify(mockSharedFileManager, never()).clear(); 1111 verifyNoInteractions(mockLogger); 1112 // saved reset value should not have changed 1113 checkSavedResetValue(1); 1114 verifyNoInteractions(mockLogger); 1115 } 1116 1117 @Test testCheckResetTrigger_resetTrigger_singleIncrement()1118 public void testCheckResetTrigger_resetTrigger_singleIncrement() throws Exception { 1119 setSavedResetValue(1); 1120 flags.mddResetTrigger = Optional.of(2); 1121 1122 mddManager.checkResetTrigger().get(); 1123 verify(mockSharedFileManager).cancelDownloadAndClear(); 1124 verify(mockLogger).logEventSampled(MddClientEvent.Code.EVENT_CODE_UNSPECIFIED); 1125 // saved reset value should be set to 2 1126 checkSavedResetValue(2); 1127 verifyNoMoreInteractions(mockLogger); 1128 } 1129 1130 @Test testCheckResetTrigger_resetTrigger_singleIncrementMultipleChecks()1131 public void testCheckResetTrigger_resetTrigger_singleIncrementMultipleChecks() throws Exception { 1132 setSavedResetValue(1); 1133 flags.mddResetTrigger = Optional.of(2); 1134 1135 mddManager.checkResetTrigger().get(); 1136 // The second check should have no effect - clear should only be called once. 1137 mddManager.checkResetTrigger().get(); 1138 verify(mockSharedFileManager).cancelDownloadAndClear(); 1139 verify(mockLogger).logEventSampled(MddClientEvent.Code.EVENT_CODE_UNSPECIFIED); 1140 // saved reset value should be set to 2 1141 checkSavedResetValue(2); 1142 verifyNoMoreInteractions(mockLogger); 1143 } 1144 1145 @Test testCheckResetTrigger_resetTrigger_multipleIncrementMultipleChecks()1146 public void testCheckResetTrigger_resetTrigger_multipleIncrementMultipleChecks() 1147 throws Exception { 1148 setSavedResetValue(1); 1149 flags.mddResetTrigger = Optional.of(2); 1150 1151 mddManager.checkResetTrigger().get(); 1152 1153 flags.mddResetTrigger = Optional.of(3); 1154 1155 mddManager.checkResetTrigger().get(); 1156 1157 verify(mockSharedFileManager, times(2)).cancelDownloadAndClear(); 1158 verify(mockLogger, times(2)).logEventSampled(MddClientEvent.Code.EVENT_CODE_UNSPECIFIED); 1159 // saved reset value should be set to 2 1160 checkSavedResetValue(3); 1161 verifyNoMoreInteractions(mockLogger); 1162 } 1163 1164 @Test testClear_resetsExperimentIds()1165 public void testClear_resetsExperimentIds() throws Exception { 1166 flags.enableDownloadStageExperimentIdPropagation = Optional.of(true); 1167 1168 long buildId = 999L; 1169 int experimentId = 12345; 1170 DataFileGroupInternal dataFileGroup = 1171 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2).toBuilder() 1172 .setBuildId(buildId) 1173 .build(); 1174 1175 when(mockFileGroupsMetadata.getAllFreshGroups()) 1176 .thenReturn( 1177 immediateFuture( 1178 ImmutableList.of( 1179 GroupKeyAndGroup.create( 1180 GroupKey.newBuilder().setGroupName(TEST_GROUP).build(), dataFileGroup)))); 1181 1182 when(mockFileGroupsMetadata.getAllStaleGroups()) 1183 .thenReturn(immediateFuture(ImmutableList.of())); 1184 1185 mddManager.clear().get(); 1186 1187 InOrder inOrder = inOrder(mockFileGroupsMetadata); 1188 1189 inOrder.verify(mockFileGroupsMetadata).getAllFreshGroups(); 1190 inOrder.verify(mockFileGroupsMetadata).clear(); 1191 } 1192 setMigrationState(String key, boolean value)1193 private void setMigrationState(String key, boolean value) { 1194 SharedPreferences sharedPreferences = 1195 SharedPreferencesUtil.getSharedPreferences( 1196 context, MobileDataDownloadManager.MDD_MANAGER_METADATA, Optional.absent()); 1197 sharedPreferences.edit().putBoolean(key, value).commit(); 1198 } 1199 setSavedResetValue(int value)1200 private void setSavedResetValue(int value) { 1201 SharedPreferences prefs = 1202 SharedPreferencesUtil.getSharedPreferences( 1203 context, MobileDataDownloadManager.MDD_MANAGER_METADATA, Optional.absent()); 1204 SharedPreferences.Editor editor = prefs.edit(); 1205 editor.putInt(MobileDataDownloadManager.RESET_TRIGGER, value); 1206 editor.commit(); 1207 } 1208 checkSavedResetValue(int expected)1209 private void checkSavedResetValue(int expected) { 1210 SharedPreferences prefs = 1211 SharedPreferencesUtil.getSharedPreferences( 1212 context, MobileDataDownloadManager.MDD_MANAGER_METADATA, Optional.absent()); 1213 assertThat(prefs.getInt(MobileDataDownloadManager.RESET_TRIGGER, expected - 1)) 1214 .isEqualTo(expected); 1215 } 1216 noCustomValidation()1217 private AsyncFunction<DataFileGroupInternal, Boolean> noCustomValidation() { 1218 return unused -> Futures.immediateFuture(true); 1219 } 1220 } 1221