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.android.libraries.mobiledatadownload.internal.MddTestUtil.writeSharedFiles; 19 import static com.google.common.truth.Truth.assertThat; 20 import static com.google.common.truth.Truth.assertWithMessage; 21 import static org.junit.Assert.assertThrows; 22 import static org.mockito.ArgumentMatchers.any; 23 import static org.mockito.ArgumentMatchers.anyInt; 24 import static org.mockito.ArgumentMatchers.anyList; 25 import static org.mockito.ArgumentMatchers.anyLong; 26 import static org.mockito.ArgumentMatchers.eq; 27 import static org.mockito.ArgumentMatchers.isA; 28 import static org.mockito.Mockito.never; 29 import static org.mockito.Mockito.reset; 30 import static org.mockito.Mockito.times; 31 import static org.mockito.Mockito.verify; 32 import static org.mockito.Mockito.verifyNoInteractions; 33 import static org.mockito.Mockito.verifyNoMoreInteractions; 34 import static org.mockito.Mockito.when; 35 36 import android.accounts.Account; 37 import android.content.Context; 38 import android.content.pm.PackageInfo; 39 import android.content.pm.PackageManager; 40 import android.net.Uri; 41 import android.os.Build; 42 import androidx.test.core.app.ApplicationProvider; 43 import com.google.mobiledatadownload.internal.MetadataProto.DataFile; 44 import com.google.mobiledatadownload.internal.MetadataProto.DataFileGroupBookkeeping; 45 import com.google.mobiledatadownload.internal.MetadataProto.DataFileGroupInternal; 46 import com.google.mobiledatadownload.internal.MetadataProto.DataFileGroupInternal.AllowedReaders; 47 import com.google.mobiledatadownload.internal.MetadataProto.DownloadConditions; 48 import com.google.mobiledatadownload.internal.MetadataProto.DownloadConditions.ActivatingCondition; 49 import com.google.mobiledatadownload.internal.MetadataProto.DownloadConditions.DeviceNetworkPolicy; 50 import com.google.mobiledatadownload.internal.MetadataProto.DownloadConditions.DeviceStoragePolicy; 51 import com.google.mobiledatadownload.internal.MetadataProto.ExtraHttpHeader; 52 import com.google.mobiledatadownload.internal.MetadataProto.FileStatus; 53 import com.google.mobiledatadownload.internal.MetadataProto.GroupKey; 54 import com.google.mobiledatadownload.internal.MetadataProto.NewFileKey; 55 import com.google.mobiledatadownload.internal.MetadataProto.SharedFile; 56 import com.google.android.libraries.mobiledatadownload.AccountSource; 57 import com.google.android.libraries.mobiledatadownload.AggregateException; 58 import com.google.android.libraries.mobiledatadownload.DownloadException; 59 import com.google.android.libraries.mobiledatadownload.DownloadException.DownloadResultCode; 60 import com.google.android.libraries.mobiledatadownload.FileSource; 61 import com.google.android.libraries.mobiledatadownload.SilentFeedback; 62 import com.google.android.libraries.mobiledatadownload.account.AccountUtil; 63 import com.google.android.libraries.mobiledatadownload.file.SynchronousFileStorage; 64 import com.google.android.libraries.mobiledatadownload.file.backends.AndroidFileBackend; 65 import com.google.android.libraries.mobiledatadownload.file.common.LimitExceededException; 66 import com.google.android.libraries.mobiledatadownload.file.spi.Backend; 67 import com.google.android.libraries.mobiledatadownload.internal.FileGroupManager.GroupDownloadStatus; 68 import com.google.android.libraries.mobiledatadownload.internal.collect.GroupKeyAndGroup; 69 import com.google.android.libraries.mobiledatadownload.internal.downloader.DownloaderCallbackImpl; 70 import com.google.android.libraries.mobiledatadownload.internal.downloader.MddFileDownloader; 71 import com.google.android.libraries.mobiledatadownload.internal.experimentation.DownloadStageManager; 72 import com.google.android.libraries.mobiledatadownload.internal.experimentation.NoOpDownloadStageManager; 73 import com.google.android.libraries.mobiledatadownload.internal.logging.DownloadStateLogger; 74 import com.google.android.libraries.mobiledatadownload.internal.logging.EventLogger; 75 import com.google.android.libraries.mobiledatadownload.internal.logging.testing.FakeEventLogger; 76 import com.google.android.libraries.mobiledatadownload.internal.util.DirectoryUtil; 77 import com.google.android.libraries.mobiledatadownload.internal.util.FileGroupUtil; 78 import com.google.android.libraries.mobiledatadownload.monitor.DownloadProgressMonitor; 79 import com.google.android.libraries.mobiledatadownload.testing.FakeTimeSource; 80 import com.google.android.libraries.mobiledatadownload.testing.TestFlags; 81 import com.google.common.base.Optional; 82 import com.google.common.collect.ArrayListMultimap; 83 import com.google.common.collect.ImmutableList; 84 import com.google.common.collect.ImmutableMap; 85 import com.google.common.collect.Lists; 86 import com.google.common.labs.concurrent.LabsFutures; 87 import com.google.common.truth.Correspondence; 88 import com.google.common.util.concurrent.AsyncFunction; 89 import com.google.common.util.concurrent.Futures; 90 import com.google.common.util.concurrent.ListenableFuture; 91 import com.google.common.util.concurrent.MoreExecutors; 92 import com.google.mobiledatadownload.LogEnumsProto.MddClientEvent; 93 import com.google.mobiledatadownload.LogEnumsProto.MddDownloadResult; 94 import com.google.mobiledatadownload.LogProto.DataDownloadFileGroupStats; 95 import com.google.protobuf.Any; 96 import com.google.protobuf.ByteString; 97 import com.google.protobuf.ExtensionRegistryLite; 98 import com.google.protobuf.StringValue; 99 import java.io.Closeable; 100 import java.io.File; 101 import java.io.FileOutputStream; 102 import java.io.IOException; 103 import java.util.ArrayList; 104 import java.util.Arrays; 105 import java.util.Calendar; 106 import java.util.List; 107 import java.util.concurrent.ExecutionException; 108 import java.util.concurrent.Executor; 109 import java.util.concurrent.Executors; 110 import java.util.concurrent.TimeUnit; 111 import org.junit.Before; 112 import org.junit.Rule; 113 import org.junit.Test; 114 import org.junit.rules.TemporaryFolder; 115 import org.junit.runner.RunWith; 116 import org.mockito.ArgumentCaptor; 117 import org.mockito.Captor; 118 import org.mockito.Mock; 119 import org.mockito.invocation.InvocationOnMock; 120 import org.mockito.junit.MockitoJUnit; 121 import org.mockito.junit.MockitoRule; 122 import org.mockito.stubbing.Answer; 123 import org.robolectric.RobolectricTestRunner; 124 import org.robolectric.Shadows; 125 import org.robolectric.util.ReflectionHelpers; 126 127 @RunWith(RobolectricTestRunner.class) 128 public class FileGroupManagerTest { 129 130 private static final long CURRENT_TIMESTAMP = 1000; 131 132 private static final int TRAFFIC_TAG = 1000; 133 134 private static final Executor SEQUENTIAL_CONTROL_EXECUTOR = 135 Executors.newSingleThreadScheduledExecutor(); 136 137 private static final String TEST_GROUP = "test-group"; 138 private static final String TEST_GROUP_2 = "test-group-2"; 139 private static final String TEST_GROUP_3 = "test-group-3"; 140 private static final String TEST_GROUP_4 = "test-group-4"; 141 private static final long FILE_GROUP_EXPIRATION_DATE_SECS = 10; 142 private static final String HOST_APP_LOG_SOURCE = "HOST_APP_LOG_SOURCE"; 143 private static final String HOST_APP_PRIMES_LOG_SOURCE = "HOST_APP_PRIMES_LOG_SOURCE"; 144 145 private static final Correspondence<GroupKey, String> GROUP_KEY_TO_VARIANT = 146 Correspondence.transforming(GroupKey::getVariantId, "using variant"); 147 private static final Correspondence<GroupKeyAndGroup, String> KEY_GROUP_PAIR_TO_VARIANT = 148 Correspondence.transforming( 149 keyGroupPair -> { 150 assertThat(keyGroupPair.groupKey().getVariantId()) 151 .isEqualTo(keyGroupPair.dataFileGroup().getVariantId()); 152 return keyGroupPair.dataFileGroup().getVariantId(); 153 }, 154 "using variant from group key and file group"); 155 156 private static GroupKey testKey; 157 private static GroupKey testKey2; 158 private static GroupKey testKey3; 159 private static GroupKey testKey4; 160 161 private Context context; 162 private FileGroupManager fileGroupManager; 163 private FileGroupsMetadata fileGroupsMetadata; 164 private SharedFileManager sharedFileManager; 165 private SharedFilesMetadata sharedFilesMetadata; 166 private FakeTimeSource testClock; 167 private SynchronousFileStorage fileStorage; 168 public File publicDirectory; 169 private final TestFlags flags = new TestFlags(); 170 171 @Rule(order = 2) 172 public TemporaryFolder folder = new TemporaryFolder(); 173 174 @Rule(order = 3) 175 public final MockitoRule mocks = MockitoJUnit.rule(); 176 177 @Mock EventLogger mockLogger; 178 @Mock SilentFeedback mockSilentFeedback; 179 @Mock MddFileDownloader mockDownloader; 180 @Mock SharedFileManager mockSharedFileManager; 181 @Mock FileGroupsMetadata mockFileGroupsMetadata; 182 @Mock DownloadProgressMonitor mockDownloadMonitor; 183 @Mock AccountSource mockAccountSource; 184 @Mock Backend mockBackend; 185 @Mock Closeable closeable; 186 187 @Captor ArgumentCaptor<FileSource> fileSourceCaptor; 188 @Captor ArgumentCaptor<GroupKey> groupKeyCaptor; 189 @Captor ArgumentCaptor<List<GroupKey>> groupKeysCaptor; 190 191 private DownloadStageManager downloadStageManager; 192 193 @Before setUp()194 public void setUp() throws Exception { 195 context = ApplicationProvider.getApplicationContext(); 196 197 when(mockBackend.name()).thenReturn("blobstore"); 198 fileStorage = 199 new SynchronousFileStorage( 200 Arrays.asList(AndroidFileBackend.builder(context).build(), mockBackend)); 201 202 testClock = new FakeTimeSource().set(CURRENT_TIMESTAMP); 203 204 testKey = 205 GroupKey.newBuilder() 206 .setGroupName(TEST_GROUP) 207 .setOwnerPackage(context.getPackageName()) 208 .build(); 209 testKey2 = 210 GroupKey.newBuilder() 211 .setGroupName(TEST_GROUP_2) 212 .setOwnerPackage(context.getPackageName()) 213 .build(); 214 testKey3 = 215 GroupKey.newBuilder() 216 .setGroupName(TEST_GROUP_3) 217 .setOwnerPackage(context.getPackageName()) 218 .build(); 219 testKey4 = 220 GroupKey.newBuilder() 221 .setGroupName(TEST_GROUP_4) 222 .setOwnerPackage(context.getPackageName()) 223 .build(); 224 225 fileGroupsMetadata = 226 new SharedPreferencesFileGroupsMetadata( 227 context, 228 testClock, 229 mockSilentFeedback, 230 Optional.absent(), 231 MoreExecutors.directExecutor()); 232 sharedFilesMetadata = 233 new SharedPreferencesSharedFilesMetadata( 234 context, mockSilentFeedback, Optional.absent(), flags); 235 sharedFileManager = 236 new SharedFileManager( 237 context, 238 mockSilentFeedback, 239 sharedFilesMetadata, 240 fileStorage, 241 mockDownloader, 242 Optional.absent(), 243 Optional.of(mockDownloadMonitor), 244 mockLogger, 245 flags, 246 fileGroupsMetadata, 247 Optional.absent(), 248 MoreExecutors.directExecutor()); 249 250 downloadStageManager = new NoOpDownloadStageManager(); 251 252 fileGroupManager = 253 new FileGroupManager( 254 context, 255 mockLogger, 256 mockSilentFeedback, 257 fileGroupsMetadata, 258 sharedFileManager, 259 testClock, 260 Optional.of(mockAccountSource), 261 SEQUENTIAL_CONTROL_EXECUTOR, 262 Optional.absent(), 263 fileStorage, 264 downloadStageManager, 265 flags); 266 // TODO(b/117571083): Replace with fileStorage API. 267 File downloadDirectory = 268 new File(context.getFilesDir(), DirectoryUtil.MDD_STORAGE_MODULE + "/" + "shared"); 269 publicDirectory = new File(downloadDirectory, DirectoryUtil.MDD_STORAGE_ALL_GOOGLE_APPS); 270 publicDirectory.mkdirs(); 271 272 // file sharing is available for SDK R+ 273 ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.R); 274 } 275 assertLoggedNewConfigs( FakeEventLogger fakeEventLogger, DataDownloadFileGroupStats fileGroupStats, Void newConfigReceivedInfo)276 private void assertLoggedNewConfigs( 277 FakeEventLogger fakeEventLogger, 278 DataDownloadFileGroupStats fileGroupStats, 279 Void newConfigReceivedInfo) { 280 ArrayListMultimap<DataDownloadFileGroupStats, Void> loggedConfigs = 281 fakeEventLogger.getLoggedNewConfigReceived(); 282 assertThat(loggedConfigs).hasSize(1); 283 assertThat(loggedConfigs.get(fileGroupStats)).containsExactly(newConfigReceivedInfo); 284 } 285 286 @Test testAddGroupForDownload()287 public void testAddGroupForDownload() throws Exception { 288 FakeEventLogger fakeEventLogger = new FakeEventLogger(); 289 290 resetFileGroupManager(fakeEventLogger, fileGroupsMetadata, sharedFileManager); 291 292 DataFileGroupInternal dataFileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2); 293 NewFileKey[] groupKeys = MddTestUtil.createFileKeysForDataFileGroupInternal(dataFileGroup); 294 295 assertThat(fileGroupManager.addGroupForDownload(testKey, dataFileGroup).get()).isTrue(); 296 verifyAddGroupForDownloadWritesMetadata(testKey, dataFileGroup, CURRENT_TIMESTAMP); 297 298 // Check that downloaded file groups doesn't contain this file group. 299 assertThat(readDownloadedFileGroup(testKey)).isNull(); 300 301 assertThat(sharedFileManager.getSharedFile(groupKeys[0]).get()).isNotNull(); 302 assertThat(sharedFileManager.getSharedFile(groupKeys[1]).get()).isNotNull(); 303 304 assertLoggedNewConfigs( 305 fakeEventLogger, createFileGroupDetails(dataFileGroup).clearFileCount().build(), null); 306 } 307 308 @Test testAddGroupForDownload_correctlyPopulatesBuildIdAndVariantId()309 public void testAddGroupForDownload_correctlyPopulatesBuildIdAndVariantId() throws Exception { 310 FakeEventLogger fakeEventLogger = new FakeEventLogger(); 311 resetFileGroupManager(fakeEventLogger, fileGroupsMetadata, sharedFileManager); 312 313 DataFileGroupInternal dataFileGroup = 314 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2).toBuilder() 315 .setBuildId(10) 316 .setVariantId("testVariant") 317 .build(); 318 NewFileKey[] groupKeys = MddTestUtil.createFileKeysForDataFileGroupInternal(dataFileGroup); 319 320 assertThat(fileGroupManager.addGroupForDownload(testKey, dataFileGroup).get()).isTrue(); 321 verifyAddGroupForDownloadWritesMetadata(testKey, dataFileGroup, CURRENT_TIMESTAMP); 322 323 // Check that downloaded file groups doesn't contain this file group. 324 assertThat(readDownloadedFileGroup(testKey)).isNull(); 325 326 assertThat(sharedFileManager.getSharedFile(groupKeys[0]).get()).isNotNull(); 327 assertThat(sharedFileManager.getSharedFile(groupKeys[1]).get()).isNotNull(); 328 329 assertLoggedNewConfigs( 330 fakeEventLogger, createFileGroupDetails(dataFileGroup).clearFileCount().build(), null); 331 } 332 333 @Test testAddGroupForDownload_groupUpdated()334 public void testAddGroupForDownload_groupUpdated() throws Exception { 335 FakeEventLogger fakeEventLogger = new FakeEventLogger(); 336 resetFileGroupManager(fakeEventLogger, fileGroupsMetadata, sharedFileManager); 337 338 DataFileGroupInternal dataFileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2); 339 assertThat(fileGroupManager.addGroupForDownload(testKey, dataFileGroup).get()).isTrue(); 340 verifyAddGroupForDownloadWritesMetadata(testKey, dataFileGroup, CURRENT_TIMESTAMP); 341 342 assertLoggedNewConfigs( 343 fakeEventLogger, createFileGroupDetails(dataFileGroup).clearFileCount().build(), null); 344 fakeEventLogger.reset(); 345 346 // Update the file id and see that the group gets updated in the pending groups list. 347 dataFileGroup = 348 dataFileGroup.toBuilder() 349 .setFile(0, dataFileGroup.getFile(0).toBuilder().setFileId("file2")) 350 .setDownloadConditions(DownloadConditions.getDefaultInstance()) 351 .build(); 352 assertThat(fileGroupManager.addGroupForDownload(testKey, dataFileGroup).get()).isTrue(); 353 verifyAddGroupForDownloadWritesMetadata(testKey, dataFileGroup, CURRENT_TIMESTAMP); 354 355 assertLoggedNewConfigs( 356 fakeEventLogger, createFileGroupDetails(dataFileGroup).clearFileCount().build(), null); 357 fakeEventLogger.reset(); 358 359 // Update other parameters and check that we successfully add the group. 360 dataFileGroup = dataFileGroup.toBuilder().setFileGroupVersionNumber(2).build(); 361 assertThat(fileGroupManager.addGroupForDownload(testKey, dataFileGroup).get()).isTrue(); 362 verifyAddGroupForDownloadWritesMetadata(testKey, dataFileGroup, CURRENT_TIMESTAMP); 363 364 assertLoggedNewConfigs( 365 fakeEventLogger, createFileGroupDetails(dataFileGroup).clearFileCount().build(), null); 366 fakeEventLogger.reset(); 367 368 dataFileGroup = dataFileGroup.toBuilder().setStaleLifetimeSecs(50).build(); 369 assertThat(fileGroupManager.addGroupForDownload(testKey, dataFileGroup).get()).isTrue(); 370 verifyAddGroupForDownloadWritesMetadata(testKey, dataFileGroup, CURRENT_TIMESTAMP); 371 372 assertLoggedNewConfigs( 373 fakeEventLogger, createFileGroupDetails(dataFileGroup).clearFileCount().build(), null); 374 fakeEventLogger.reset(); 375 376 dataFileGroup = 377 dataFileGroup.toBuilder() 378 .setDownloadConditions( 379 DownloadConditions.newBuilder() 380 .setDeviceNetworkPolicy( 381 DeviceNetworkPolicy.DOWNLOAD_FIRST_ON_WIFI_THEN_ON_ANY_NETWORK)) 382 .build(); 383 assertThat(fileGroupManager.addGroupForDownload(testKey, dataFileGroup).get()).isTrue(); 384 verifyAddGroupForDownloadWritesMetadata(testKey, dataFileGroup, CURRENT_TIMESTAMP); 385 386 assertLoggedNewConfigs( 387 fakeEventLogger, createFileGroupDetails(dataFileGroup).clearFileCount().build(), null); 388 fakeEventLogger.reset(); 389 390 DownloadConditions downloadConditions = 391 DownloadConditions.newBuilder() 392 .setDeviceStoragePolicy(DeviceStoragePolicy.BLOCK_DOWNLOAD_LOWER_THRESHOLD) 393 .build(); 394 dataFileGroup = dataFileGroup.toBuilder().setDownloadConditions(downloadConditions).build(); 395 assertThat(fileGroupManager.addGroupForDownload(testKey, dataFileGroup).get()).isTrue(); 396 verifyAddGroupForDownloadWritesMetadata(testKey, dataFileGroup, CURRENT_TIMESTAMP); 397 398 assertLoggedNewConfigs( 399 fakeEventLogger, createFileGroupDetails(dataFileGroup).clearFileCount().build(), null); 400 fakeEventLogger.reset(); 401 402 dataFileGroup = 403 dataFileGroup.toBuilder() 404 .setAllowedReadersEnum(AllowedReaders.ONLY_GOOGLE_PLAY_SERVICES) 405 .build(); 406 assertThat(fileGroupManager.addGroupForDownload(testKey, dataFileGroup).get()).isTrue(); 407 verifyAddGroupForDownloadWritesMetadata(testKey, dataFileGroup, CURRENT_TIMESTAMP); 408 409 assertLoggedNewConfigs( 410 fakeEventLogger, createFileGroupDetails(dataFileGroup).clearFileCount().build(), null); 411 } 412 413 @Test testAddGroupForDownload_groupUpdated_whenBuildChanges()414 public void testAddGroupForDownload_groupUpdated_whenBuildChanges() throws Exception { 415 FakeEventLogger fakeEventLogger = new FakeEventLogger(); 416 resetFileGroupManager(fakeEventLogger, fileGroupsMetadata, sharedFileManager); 417 418 DataFileGroupInternal dataFileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2); 419 assertThat(fileGroupManager.addGroupForDownload(testKey, dataFileGroup).get()).isTrue(); 420 verifyAddGroupForDownloadWritesMetadata(testKey, dataFileGroup, CURRENT_TIMESTAMP); 421 422 // Reset to clear events before next add group call 423 fakeEventLogger.reset(); 424 425 // Update the file id and see that the group gets updated in the pending groups list. 426 dataFileGroup = dataFileGroup.toBuilder().setBuildId(123456789L).build(); 427 assertThat(fileGroupManager.addGroupForDownload(testKey, dataFileGroup).get()).isTrue(); 428 verifyAddGroupForDownloadWritesMetadata(testKey, dataFileGroup, CURRENT_TIMESTAMP); 429 430 assertLoggedNewConfigs( 431 fakeEventLogger, createFileGroupDetails(dataFileGroup).clearFileCount().build(), null); 432 } 433 434 @Test testAddGroupForDownload_groupUpdated_whenVariantChanges()435 public void testAddGroupForDownload_groupUpdated_whenVariantChanges() throws Exception { 436 FakeEventLogger fakeEventLogger = new FakeEventLogger(); 437 resetFileGroupManager(fakeEventLogger, fileGroupsMetadata, sharedFileManager); 438 439 DataFileGroupInternal dataFileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2); 440 assertThat(fileGroupManager.addGroupForDownload(testKey, dataFileGroup).get()).isTrue(); 441 verifyAddGroupForDownloadWritesMetadata(testKey, dataFileGroup, CURRENT_TIMESTAMP); 442 443 // Reset to clear events before next add group call 444 fakeEventLogger.reset(); 445 446 // Update the file id and see that the group gets updated in the pending groups list. 447 dataFileGroup = dataFileGroup.toBuilder().setVariantId("some-different-variant").build(); 448 assertThat(fileGroupManager.addGroupForDownload(testKey, dataFileGroup).get()).isTrue(); 449 verifyAddGroupForDownloadWritesMetadata(testKey, dataFileGroup, CURRENT_TIMESTAMP); 450 451 assertLoggedNewConfigs( 452 fakeEventLogger, createFileGroupDetails(dataFileGroup).clearFileCount().build(), null); 453 } 454 455 @Test testAddGroupForDownloadWithSyncId_failedToUpdateMetadataNoScheduleViaSpe()456 public void testAddGroupForDownloadWithSyncId_failedToUpdateMetadataNoScheduleViaSpe() 457 throws Exception { 458 // Mock FileGroupsMetadata and SharedFileManager to test failure scenario. 459 resetFileGroupManager(mockFileGroupsMetadata, mockSharedFileManager); 460 DataFileGroupInternal dataFileGroup = 461 MddTestUtil.createDataFileGroupInternalWithDownloadId(TEST_GROUP, 2); 462 NewFileKey[] groupKeys = MddTestUtil.createFileKeysForDataFileGroupInternal(dataFileGroup); 463 464 when(mockSharedFileManager.reserveFileEntry(any(NewFileKey.class))) 465 .thenReturn(Futures.immediateFuture(true)); 466 467 // Failed to write to Metadata, no task will be scheduled via SPE. 468 when(mockFileGroupsMetadata.write(any(GroupKey.class), any(DataFileGroupInternal.class))) 469 .thenReturn(Futures.immediateFuture(false)); 470 when(mockFileGroupsMetadata.read(any(GroupKey.class))) 471 .thenReturn(Futures.immediateFuture(null)); 472 473 ListenableFuture<Boolean> addGroupFuture = 474 fileGroupManager.addGroupForDownload(testKey, dataFileGroup); 475 assertThrows(ExecutionException.class, addGroupFuture::get); 476 IOException e = LabsFutures.getFailureCauseAs(addGroupFuture, IOException.class); 477 assertThat(e).hasMessageThat().contains("Failed to commit new group metadata to disk."); 478 479 // Check that downloaded file groups doesn't contain this file group. 480 GroupKey downloadedkey = testKey.toBuilder().setDownloaded(true).build(); 481 assertWithMessage(String.format("Expected that key %s should not exist.", downloadedkey)) 482 .that(mockFileGroupsMetadata.read(downloadedkey).get()) 483 .isNull(); 484 // Check that the get method doesn't return this file group. 485 assertThat(fileGroupManager.getFileGroup(testKey, true).get()).isNull(); 486 487 verify(mockSharedFileManager).reserveFileEntry(groupKeys[0]); 488 verify(mockSharedFileManager).reserveFileEntry(groupKeys[1]); 489 } 490 491 @Test testAddGroupForDownload_duplicatePendingGroup()492 public void testAddGroupForDownload_duplicatePendingGroup() throws Exception { 493 DataFileGroupInternal dataFileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2); 494 495 assertThat(fileGroupManager.addGroupForDownload(testKey, dataFileGroup).get()).isTrue(); 496 verifyAddGroupForDownloadWritesMetadata(testKey, dataFileGroup, CURRENT_TIMESTAMP); 497 498 // Send the exact same group again, and check that it is considered duplicate. 499 assertThat(fileGroupManager.addGroupForDownload(testKey, dataFileGroup).get()).isFalse(); 500 } 501 502 @Test testAddGroupForDownload_duplicateDownloadedGroup()503 public void testAddGroupForDownload_duplicateDownloadedGroup() throws Exception { 504 DataFileGroupInternal dataFileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2); 505 writeDownloadedFileGroup(testKey, dataFileGroup); 506 507 // Send the exact same group as the downloaded group, and check that it is considered duplicate. 508 assertThat(fileGroupManager.addGroupForDownload(testKey, dataFileGroup).get()).isFalse(); 509 510 verifyNoInteractions(mockLogger); 511 } 512 513 @Test testAddGroupForDownload_filePropertiesUpdated()514 public void testAddGroupForDownload_filePropertiesUpdated() throws Exception { 515 DataFileGroupInternal dataFileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2); 516 writePendingFileGroup(testKey, dataFileGroup); 517 518 dataFileGroup = 519 dataFileGroup.toBuilder() 520 .setFile(0, dataFileGroup.getFile(0).toBuilder().setUrlToDownload("https://file2")) 521 .build(); 522 // Send the same group with different property, and check that it is NOT duplicate. 523 assertThat(fileGroupManager.addGroupForDownload(testKey, dataFileGroup).get()).isTrue(); 524 525 dataFileGroup = 526 dataFileGroup.toBuilder() 527 .setFile(0, dataFileGroup.getFile(0).toBuilder().setUrlToDownload("https://file3")) 528 .build(); 529 assertThat(fileGroupManager.addGroupForDownload(testKey, dataFileGroup).get()).isTrue(); 530 } 531 532 @Test testAddGroupForDownload_differentPendingGroup_duplicateDownloadedGroup()533 public void testAddGroupForDownload_differentPendingGroup_duplicateDownloadedGroup() 534 throws Exception { 535 536 DataFileGroupInternal firstGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2); 537 assertThat(fileGroupManager.addGroupForDownload(testKey, firstGroup).get()).isTrue(); 538 verifyAddGroupForDownloadWritesMetadata(testKey, firstGroup, CURRENT_TIMESTAMP); 539 540 verify(mockLogger) 541 .logNewConfigReceived(createFileGroupDetails(firstGroup).clearFileCount().build(), null); 542 reset(mockLogger); 543 544 // Create a second group that is identical except for one different file id. 545 DataFileGroupInternal.Builder secondGroup = 546 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2).toBuilder(); 547 secondGroup.setFile(0, secondGroup.getFile(0).toBuilder().setFileId("file2")); 548 writeDownloadedFileGroup(testKey, secondGroup.build()); 549 550 // Send the updated group, and check that it is not considered duplicate. 551 assertThat(fileGroupManager.addGroupForDownload(testKey, secondGroup.build()).get()).isTrue(); 552 verifyAddGroupForDownloadWritesMetadata(testKey, secondGroup.build(), CURRENT_TIMESTAMP); 553 verify(mockLogger) 554 .logNewConfigReceived(createFileGroupDetails(firstGroup).clearFileCount().build(), null); 555 } 556 557 @Test testAddGroupForDownload_subscribeFailed()558 public void testAddGroupForDownload_subscribeFailed() throws Exception { 559 // Mock SharedFileManager to test failure scenario. 560 resetFileGroupManager(fileGroupsMetadata, mockSharedFileManager); 561 DataFileGroupInternal dataFileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 3); 562 NewFileKey[] groupKeys = MddTestUtil.createFileKeysForDataFileGroupInternal(dataFileGroup); 563 564 ArgumentCaptor<NewFileKey> fileCaptor = ArgumentCaptor.forClass(NewFileKey.class); 565 when(mockSharedFileManager.reserveFileEntry(fileCaptor.capture())) 566 .thenReturn( 567 Futures.immediateFuture(true), 568 Futures.immediateFuture(false), 569 Futures.immediateFuture(true)); 570 ExecutionException ex = 571 assertThrows( 572 ExecutionException.class, 573 fileGroupManager.addGroupForDownload(testKey, dataFileGroup)::get); 574 assertThat(ex).hasCauseThat().isInstanceOf(IOException.class); 575 576 // Verify that we tried to subscribe to only the first 2 files. 577 assertThat(fileCaptor.getAllValues()).containsExactly(groupKeys[0], groupKeys[1]); 578 579 verify(mockLogger) 580 .logNewConfigReceived(createFileGroupDetails(dataFileGroup).clearFileCount().build(), null); 581 } 582 583 @Test testAddGroupForDownload_subscribeFailed_firstFile()584 public void testAddGroupForDownload_subscribeFailed_firstFile() throws Exception { 585 // Mock SharedFileManager to test failure scenario. 586 resetFileGroupManager(fileGroupsMetadata, mockSharedFileManager); 587 DataFileGroupInternal dataFileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 3); 588 NewFileKey[] groupKeys = MddTestUtil.createFileKeysForDataFileGroupInternal(dataFileGroup); 589 590 ArgumentCaptor<NewFileKey> fileCaptor = ArgumentCaptor.forClass(NewFileKey.class); 591 when(mockSharedFileManager.reserveFileEntry(fileCaptor.capture())) 592 .thenReturn( 593 Futures.immediateFuture(false), 594 Futures.immediateFuture(true), 595 Futures.immediateFuture(true)); 596 ExecutionException ex = 597 assertThrows( 598 ExecutionException.class, 599 fileGroupManager.addGroupForDownload(testKey, dataFileGroup)::get); 600 assertThat(ex).hasCauseThat().isInstanceOf(IOException.class); 601 602 // Verify that we tried to subscribe to only the first file. 603 assertThat(fileCaptor.getAllValues()).containsExactly(groupKeys[0]); 604 605 verify(mockLogger) 606 .logNewConfigReceived(createFileGroupDetails(dataFileGroup).clearFileCount().build(), null); 607 } 608 609 @Test testAddGroupForDownload_alreadyDownloadedGroup()610 public void testAddGroupForDownload_alreadyDownloadedGroup() throws Exception { 611 // Write a group to the pending shared prefs. 612 DataFileGroupInternal pendingGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2); 613 writePendingFileGroup(testKey, pendingGroup); 614 615 DataFileGroupInternal oldDownloadedGroup = 616 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1); 617 writeDownloadedFileGroup(testKey, oldDownloadedGroup); 618 619 // Add a newer version of that group 620 DataFileGroupInternal receivedGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 3); 621 622 assertThat(fileGroupManager.addGroupForDownload(testKey, receivedGroup).get()).isTrue(); 623 624 // The new added group should be the pending group. 625 verifyAddGroupForDownloadWritesMetadata(testKey, receivedGroup, CURRENT_TIMESTAMP); 626 assertThat(oldDownloadedGroup).isEqualTo(readDownloadedFileGroup(testKey)); 627 } 628 629 @Test testAddGroupForDownload_addEmptyGroup()630 public void testAddGroupForDownload_addEmptyGroup() throws Exception { 631 // Write a group to the pending shared prefs. 632 DataFileGroupInternal pendingGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2); 633 writePendingFileGroup(testKey, pendingGroup); 634 635 DataFileGroupInternal emptyGroup = 636 DataFileGroupInternal.newBuilder().setGroupName(TEST_GROUP).build(); 637 assertThat(fileGroupManager.addGroupForDownload(testKey, emptyGroup).get()).isTrue(); 638 verifyAddGroupForDownloadWritesMetadata(testKey, emptyGroup, CURRENT_TIMESTAMP); 639 } 640 641 @Test testAddGroupForDownload_addGroupForUninstalledApp()642 public void testAddGroupForDownload_addGroupForUninstalledApp() throws Exception { 643 DataFileGroupInternal dataFileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2); 644 GroupKey uninstalledAppKey = 645 GroupKey.newBuilder().setGroupName(TEST_GROUP).setOwnerPackage("not.installed.app").build(); 646 647 // Send a group with an owner package that is not installed. Ensure that this group is rejected. 648 assertThrows( 649 UninstalledAppException.class, 650 () -> fileGroupManager.addGroupForDownload(uninstalledAppKey, dataFileGroup)); 651 verify(mockLogger) 652 .logEventSampled( 653 MddClientEvent.Code.EVENT_CODE_UNSPECIFIED, 654 TEST_GROUP, 655 /* fileGroupVersionNumber= */ 0, 656 /* buildId= */ 0, 657 /* variantId= */ ""); 658 verifyNoMoreInteractions(mockLogger); 659 } 660 661 @Test testAddGroupForDownload_expiredGroup()662 public void testAddGroupForDownload_expiredGroup() throws Exception { 663 Calendar date = new Calendar.Builder().setDate(1970, Calendar.JANUARY, 2).build(); 664 DataFileGroupInternal dataFileGroup = 665 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2).toBuilder() 666 .setExpirationDateSecs(date.getTimeInMillis() / 1000) 667 .build(); 668 669 testClock.set(System.currentTimeMillis()); 670 671 // Send a group with an expiration date that has already passed. 672 assertThrows( 673 ExpiredFileGroupException.class, 674 () -> fileGroupManager.addGroupForDownload(testKey, dataFileGroup)); 675 verify(mockLogger) 676 .logEventSampled( 677 MddClientEvent.Code.EVENT_CODE_UNSPECIFIED, 678 TEST_GROUP, 679 /* fileGroupVersionNumber= */ 0, 680 /* buildId= */ 0, 681 /* variantId= */ ""); 682 verifyNoMoreInteractions(mockLogger); 683 } 684 685 @Test testAddGroupForDownload_justExpiredGroup()686 public void testAddGroupForDownload_justExpiredGroup() throws Exception { 687 long oneHourAgo = (System.currentTimeMillis() - TimeUnit.HOURS.toMillis(1)) / 1000; 688 DataFileGroupInternal dataFileGroup = 689 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2).toBuilder() 690 .setExpirationDateSecs(oneHourAgo) 691 .build(); 692 693 testClock.set(System.currentTimeMillis()); 694 695 // Send a group with an expiration date that has already passed. 696 assertThrows( 697 ExpiredFileGroupException.class, 698 () -> fileGroupManager.addGroupForDownload(testKey, dataFileGroup)); 699 verify(mockLogger) 700 .logEventSampled( 701 MddClientEvent.Code.EVENT_CODE_UNSPECIFIED, 702 TEST_GROUP, 703 /* fileGroupVersionNumber= */ 0, 704 /* buildId= */ 0, 705 /* variantId= */ ""); 706 verifyNoMoreInteractions(mockLogger); 707 } 708 709 @Test testAddGroupForDownload_nonexpiredGroup()710 public void testAddGroupForDownload_nonexpiredGroup() throws Exception { 711 DataFileGroupInternal dataFileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2); 712 NewFileKey[] groupKeys = MddTestUtil.createFileKeysForDataFileGroupInternal(dataFileGroup); 713 714 long tenDaysFromNow = (System.currentTimeMillis() + TimeUnit.DAYS.toMillis(10)) / 1000; 715 dataFileGroup = dataFileGroup.toBuilder().setExpirationDateSecs(tenDaysFromNow).build(); 716 717 testClock.set(System.currentTimeMillis()); 718 719 assertThat(fileGroupManager.addGroupForDownload(testKey, dataFileGroup).get()).isTrue(); 720 verifyAddGroupForDownloadWritesMetadata(testKey, dataFileGroup, testClock.currentTimeMillis()); 721 722 // Check that downloaded file groups doesn't contain this file group. 723 assertThat(readDownloadedFileGroup(testKey)).isNull(); 724 // Check that the get method doesn't return this file group. 725 assertThat(fileGroupManager.getFileGroup(testKey, true).get()).isNull(); 726 727 assertThat(sharedFileManager.getSharedFile(groupKeys[0]).get()).isNotNull(); 728 assertThat(sharedFileManager.getSharedFile(groupKeys[1]).get()).isNotNull(); 729 verify(mockLogger) 730 .logNewConfigReceived(createFileGroupDetails(dataFileGroup).clearFileCount().build(), null); 731 } 732 733 @Test testAddGroupForDownload_nonexpiredGroupNoExpiration()734 public void testAddGroupForDownload_nonexpiredGroupNoExpiration() throws Exception { 735 DataFileGroupInternal.Builder dataFileGroup = 736 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2).toBuilder(); 737 NewFileKey[] groupKeys = 738 MddTestUtil.createFileKeysForDataFileGroupInternal(dataFileGroup.build()); 739 740 dataFileGroup.setExpirationDateSecs(0); // 0 means don't expire 741 742 testClock.set(System.currentTimeMillis()); 743 744 assertThat(fileGroupManager.addGroupForDownload(testKey, dataFileGroup.build()).get()).isTrue(); 745 verifyAddGroupForDownloadWritesMetadata( 746 testKey, dataFileGroup.build(), testClock.currentTimeMillis()); 747 748 // Check that downloaded file groups doesn't contain this file group. 749 assertThat(readDownloadedFileGroup(testKey)).isNull(); 750 // Check that the get method doesn't return this file group. 751 assertThat(fileGroupManager.getFileGroup(testKey, true).get()).isNull(); 752 753 assertThat(sharedFileManager.getSharedFile(groupKeys[0]).get()).isNotNull(); 754 assertThat(sharedFileManager.getSharedFile(groupKeys[1]).get()).isNotNull(); 755 verify(mockLogger) 756 .logNewConfigReceived( 757 createFileGroupDetails(dataFileGroup.build()).clearFileCount().build(), null); 758 } 759 760 @Test testAddGroupForDownload_extendExpiration()761 public void testAddGroupForDownload_extendExpiration() throws Exception { 762 DataFileGroupInternal.Builder dataFileGroup = 763 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2).toBuilder(); 764 long tenDaysFromNow = (System.currentTimeMillis() + TimeUnit.DAYS.toMillis(10)) / 1000; 765 dataFileGroup.setExpirationDateSecs(tenDaysFromNow); 766 767 assertThat(fileGroupManager.addGroupForDownload(testKey, dataFileGroup.build()).get()).isTrue(); 768 verifyAddGroupForDownloadWritesMetadata(testKey, dataFileGroup.build(), CURRENT_TIMESTAMP); 769 770 // Now send the group again with a longer expiration. 771 long twentyDaysFromNow = tenDaysFromNow + TimeUnit.DAYS.toSeconds(10); 772 dataFileGroup = dataFileGroup.setExpirationDateSecs(twentyDaysFromNow); 773 774 assertThat(fileGroupManager.addGroupForDownload(testKey, dataFileGroup.build()).get()).isTrue(); 775 verifyAddGroupForDownloadWritesMetadata(testKey, dataFileGroup.build(), CURRENT_TIMESTAMP); 776 } 777 778 @Test testAddGroupForDownload_reduceExpiration()779 public void testAddGroupForDownload_reduceExpiration() throws Exception { 780 long tenDaysFromNow = (System.currentTimeMillis() + TimeUnit.DAYS.toMillis(10)) / 1000; 781 DataFileGroupInternal dataFileGroup = 782 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2).toBuilder() 783 .setExpirationDateSecs(tenDaysFromNow) 784 .build(); 785 786 assertThat(fileGroupManager.addGroupForDownload(testKey, dataFileGroup).get()).isTrue(); 787 verifyAddGroupForDownloadWritesMetadata(testKey, dataFileGroup, CURRENT_TIMESTAMP); 788 789 // Now send the group again with a longer expiration. 790 long fiveDaysFromNow = tenDaysFromNow - TimeUnit.DAYS.toSeconds(5); 791 dataFileGroup = dataFileGroup.toBuilder().setExpirationDateSecs(fiveDaysFromNow).build(); 792 793 assertThat(fileGroupManager.addGroupForDownload(testKey, dataFileGroup).get()).isTrue(); 794 verifyAddGroupForDownloadWritesMetadata(testKey, dataFileGroup, CURRENT_TIMESTAMP); 795 } 796 797 @Test testAddGroupForDownload_delayedDownload()798 public void testAddGroupForDownload_delayedDownload() throws Exception { 799 flags.enableDelayedDownload = Optional.of(true); 800 801 // Create 2 groups, one of which requires device side activation. 802 DataFileGroupInternal fileGroup1 = 803 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2).toBuilder() 804 .setDownloadConditions( 805 DownloadConditions.newBuilder() 806 .setActivatingCondition(ActivatingCondition.DEVICE_ACTIVATED)) 807 .build(); 808 809 DataFileGroupInternal fileGroup2 = MddTestUtil.createDataFileGroupInternal(TEST_GROUP_2, 3); 810 811 // Assert that adding the first group throws an exception. 812 ExecutionException ex = 813 assertThrows( 814 ExecutionException.class, 815 fileGroupManager.addGroupForDownload(testKey, fileGroup1)::get); 816 assertThat(ex).hasCauseThat().isInstanceOf(ActivationRequiredForGroupException.class); 817 assertThat(fileGroupManager.addGroupForDownload(testKey2, fileGroup2).get()).isTrue(); 818 819 // Now activate the group and verify that we are able to add the first group. 820 assertThat(fileGroupManager.setGroupActivation(testKey, true).get()).isTrue(); 821 assertThat(fileGroupManager.addGroupForDownload(testKey, fileGroup1).get()).isTrue(); 822 823 // Deactivate the group again and verify that we should no longer be able to add it. 824 assertThat(fileGroupManager.setGroupActivation(testKey, false).get()).isTrue(); 825 ex = 826 assertThrows( 827 ExecutionException.class, 828 fileGroupManager.addGroupForDownload(testKey, fileGroup1)::get); 829 assertThat(ex).hasCauseThat().isInstanceOf(ActivationRequiredForGroupException.class); 830 } 831 832 @Test testAddGroupForDownload_onWifiFirst()833 public void testAddGroupForDownload_onWifiFirst() throws Exception { 834 int elapsedTime = 1000; 835 DataFileGroupInternal.Builder dataFileGroup = 836 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2).toBuilder(); 837 838 { 839 testClock.set(elapsedTime); 840 assertThat(fileGroupManager.addGroupForDownload(testKey, dataFileGroup.build()).get()) 841 .isTrue(); 842 // The wifi only download timestamp is set correctly. 843 verifyAddGroupForDownloadWritesMetadata(testKey, dataFileGroup.build(), elapsedTime); 844 } 845 846 { 847 // Update metadata does not change the wifi only download timestamp. 848 long tenDaysFromNow = (System.currentTimeMillis() + TimeUnit.DAYS.toMillis(10)) / 1000; 849 dataFileGroup.setExpirationDateSecs(tenDaysFromNow); 850 assertThat(fileGroupManager.addGroupForDownload(testKey, dataFileGroup.build()).get()) 851 .isTrue(); 852 verifyAddGroupForDownloadWritesMetadata(testKey, dataFileGroup.build(), elapsedTime); 853 } 854 855 { 856 // Change another metadata field does not change the wifi only download timestamp. 857 dataFileGroup.setFileGroupVersionNumber(2); 858 assertThat(fileGroupManager.addGroupForDownload(testKey, dataFileGroup.build()).get()) 859 .isTrue(); 860 // The wifi only download timestamp does not change. 861 verifyAddGroupForDownloadWritesMetadata(testKey, dataFileGroup.build(), elapsedTime); 862 } 863 864 { 865 // Update the file's urlToDownload will reset the wifi only download timestamp. 866 elapsedTime = 2000; 867 testClock.set(elapsedTime); 868 dataFileGroup.setFile( 869 0, dataFileGroup.getFile(0).toBuilder().setUrlToDownload("https://new_url")); 870 assertThat(fileGroupManager.addGroupForDownload(testKey, dataFileGroup.build()).get()) 871 .isTrue(); 872 // The wifi only download timestamp change since we change the urlToDownload 873 verifyAddGroupForDownloadWritesMetadata(testKey, dataFileGroup.build(), elapsedTime); 874 } 875 876 { 877 // Update the file's byteSize will reset the wifi only download timestamp. 878 elapsedTime = 3000; 879 testClock.set(elapsedTime); 880 dataFileGroup.setFile(1, dataFileGroup.getFile(1).toBuilder().setByteSize(5001)); 881 assertThat(fileGroupManager.addGroupForDownload(testKey, dataFileGroup.build()).get()) 882 .isTrue(); 883 // The wifi only download timestamp change since we change the urlToDownload 884 verifyAddGroupForDownloadWritesMetadata(testKey, dataFileGroup.build(), elapsedTime); 885 } 886 887 { 888 // Update the file's checksum will reset the wifi only download timestamp. 889 elapsedTime = 4000; 890 testClock.set(elapsedTime); 891 dataFileGroup.setFile(1, dataFileGroup.getFile(1).toBuilder().setChecksum("new check sum")); 892 assertThat(fileGroupManager.addGroupForDownload(testKey, dataFileGroup.build()).get()) 893 .isTrue(); 894 // The wifi only download timestamp change since we change the urlToDownload 895 verifyAddGroupForDownloadWritesMetadata(testKey, dataFileGroup.build(), elapsedTime); 896 } 897 } 898 899 @Test testAddGroupForDownload_addsSideloadedGroup()900 public void testAddGroupForDownload_addsSideloadedGroup() throws Exception { 901 // Create sideloaded group 902 DataFileGroupInternal sideloadedGroup = 903 DataFileGroupInternal.newBuilder() 904 .setGroupName(TEST_GROUP) 905 .addFile( 906 DataFile.newBuilder() 907 .setFileId("sideloaded_file") 908 .setUrlToDownload("file:/test") 909 .setChecksumType(DataFile.ChecksumType.NONE) 910 .build()) 911 .build(); 912 913 assertThat(fileGroupManager.addGroupForDownload(testKey, sideloadedGroup).get()).isTrue(); 914 915 verifyAddGroupForDownloadWritesMetadata(testKey, sideloadedGroup, 1000L); 916 } 917 918 @Test testAddGroupForDownload_multipleVariants()919 public void testAddGroupForDownload_multipleVariants() throws Exception { 920 // Create 3 group keys of the same group, but with different variants 921 GroupKey defaultGroupKey = 922 GroupKey.newBuilder() 923 .setGroupName(TEST_GROUP) 924 .setOwnerPackage(context.getPackageName()) 925 .build(); 926 GroupKey enGroupKey = defaultGroupKey.toBuilder().setVariantId("en").build(); 927 GroupKey frGroupKey = defaultGroupKey.toBuilder().setVariantId("fr").build(); 928 929 DataFileGroupInternal defaultFileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1); 930 DataFileGroupInternal enFileGroup = defaultFileGroup.toBuilder().setVariantId("en").build(); 931 DataFileGroupInternal frFileGroup = defaultFileGroup.toBuilder().setVariantId("fr").build(); 932 933 assertThat(fileGroupManager.addGroupForDownload(defaultGroupKey, defaultFileGroup).get()) 934 .isTrue(); 935 assertThat(fileGroupManager.addGroupForDownload(enGroupKey, enFileGroup).get()).isTrue(); 936 assertThat(fileGroupManager.addGroupForDownload(frGroupKey, frFileGroup).get()).isTrue(); 937 938 assertThat(fileGroupsMetadata.getAllGroupKeys().get()) 939 .comparingElementsUsing(GROUP_KEY_TO_VARIANT) 940 .containsExactly("", "en", "fr"); 941 } 942 943 @Test removeFileGroup_noVersionExists()944 public void removeFileGroup_noVersionExists() throws Exception { 945 // No record for both pending key and downloaded key. 946 GroupKey groupKey = 947 GroupKey.newBuilder() 948 .setGroupName(TEST_GROUP) 949 .setOwnerPackage(context.getPackageName()) 950 .build(); 951 952 fileGroupManager.removeFileGroup(groupKey, /* pendingOnly= */ false).get(); 953 954 assertThat(fileGroupsMetadata.getAllStaleGroups().get()).isEmpty(); 955 assertThat(fileGroupsMetadata.getAllGroupKeys().get()).isEmpty(); 956 957 // There is no pending file group, so no call to clearSyncReasons. 958 verifyNoInteractions(mockLogger); 959 } 960 961 @Test removeFileGroup_pendingVersionExists()962 public void removeFileGroup_pendingVersionExists() throws Exception { 963 DataFile dataFile1 = DataFile.newBuilder().setFileId("file1").setChecksum("checksum1").build(); 964 DataFile dataFile2 = DataFile.newBuilder().setFileId("file2").setChecksum("checksum2").build(); 965 966 NewFileKey newFileKey1 = 967 SharedFilesMetadata.createKeyFromDataFile(dataFile1, AllowedReaders.ALL_GOOGLE_APPS); 968 NewFileKey newFileKey2 = 969 SharedFilesMetadata.createKeyFromDataFile(dataFile2, AllowedReaders.ALL_GOOGLE_APPS); 970 971 DataFileGroupInternal pendingFileGroup = 972 DataFileGroupInternal.newBuilder() 973 .setGroupName(TEST_GROUP) 974 .setFileGroupVersionNumber(1) 975 .setAllowedReadersEnum(AllowedReaders.ALL_GOOGLE_APPS) 976 .addAllFile(Lists.newArrayList(dataFile1, dataFile2)) 977 .build(); 978 979 GroupKey groupKey = 980 GroupKey.newBuilder() 981 .setGroupName(TEST_GROUP) 982 .setOwnerPackage(context.getPackageName()) 983 .build(); 984 GroupKey pendingGroupKey = groupKey.toBuilder().setDownloaded(false).build(); 985 GroupKey downloadedGroupKey = groupKey.toBuilder().setDownloaded(true).build(); 986 987 writePendingFileGroup(pendingGroupKey, pendingFileGroup); 988 writeSharedFiles( 989 sharedFilesMetadata, 990 pendingFileGroup, 991 ImmutableList.of(FileStatus.DOWNLOAD_IN_PROGRESS, FileStatus.DOWNLOAD_IN_PROGRESS)); 992 993 fileGroupManager.removeFileGroup(groupKey, /* pendingOnly= */ false).get(); 994 995 assertThat(readPendingFileGroup(pendingGroupKey)).isNull(); 996 assertThat(readPendingFileGroup(downloadedGroupKey)).isNull(); 997 assertThat(fileGroupsMetadata.getAllGroupKeys().get()).isEmpty(); 998 999 Uri pendingFileUri1 = 1000 DirectoryUtil.getOnDeviceUri( 1001 context, 1002 newFileKey1.getAllowedReaders(), 1003 dataFile1.getFileId(), 1004 newFileKey1.getChecksum(), 1005 mockSilentFeedback, 1006 /* instanceId= */ Optional.absent(), 1007 /* androidShared= */ false); 1008 Uri pendingFileUri2 = 1009 DirectoryUtil.getOnDeviceUri( 1010 context, 1011 newFileKey2.getAllowedReaders(), 1012 dataFile2.getFileId(), 1013 newFileKey2.getChecksum(), 1014 mockSilentFeedback, 1015 /* instanceId= */ Optional.absent(), 1016 /* androidShared= */ false); 1017 1018 verify(mockDownloader).stopDownloading(newFileKey1.getChecksum(), pendingFileUri1); 1019 verify(mockDownloader).stopDownloading(newFileKey2.getChecksum(), pendingFileUri2); 1020 1021 verifyNoInteractions(mockLogger); 1022 } 1023 1024 @Test removeFileGroup_downloadedVersionExists()1025 public void removeFileGroup_downloadedVersionExists() throws Exception { 1026 DataFile dataFile1 = DataFile.newBuilder().setFileId("file1").setChecksum("checksum1").build(); 1027 DataFile dataFile2 = DataFile.newBuilder().setFileId("file2").setChecksum("checksum2").build(); 1028 1029 DataFileGroupInternal downloadedFileGroup = 1030 DataFileGroupInternal.newBuilder() 1031 .setGroupName(TEST_GROUP) 1032 .setFileGroupVersionNumber(0) 1033 .setBuildId(0) 1034 .setVariantId("") 1035 .setAllowedReadersEnum(AllowedReaders.ALL_GOOGLE_APPS) 1036 .addAllFile(Lists.newArrayList(dataFile1, dataFile2)) 1037 .build(); 1038 1039 GroupKey groupKey = 1040 GroupKey.newBuilder() 1041 .setGroupName(TEST_GROUP) 1042 .setOwnerPackage(context.getPackageName()) 1043 .build(); 1044 GroupKey pendingGroupKey = groupKey.toBuilder().setDownloaded(false).build(); 1045 GroupKey downloadedGroupKey = groupKey.toBuilder().setDownloaded(true).build(); 1046 1047 writeDownloadedFileGroup(downloadedGroupKey, downloadedFileGroup); 1048 writeSharedFiles( 1049 sharedFilesMetadata, 1050 downloadedFileGroup, 1051 ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE, FileStatus.DOWNLOAD_COMPLETE)); 1052 1053 fileGroupManager.removeFileGroup(groupKey, /* pendingOnly= */ false).get(); 1054 1055 assertThat(readPendingFileGroup(pendingGroupKey)).isNull(); 1056 assertThat(readPendingFileGroup(downloadedGroupKey)).isNull(); 1057 assertThat(fileGroupsMetadata.getAllStaleGroups().get()) 1058 .containsExactly( 1059 downloadedFileGroup.toBuilder() 1060 .setBookkeeping( 1061 downloadedFileGroup.getBookkeeping().toBuilder() 1062 .setStaleExpirationDate(1) 1063 .build()) 1064 .build()); 1065 1066 verify(mockDownloader, never()).stopDownloading(any(String.class), any(Uri.class)); 1067 1068 verifyNoInteractions(mockLogger); 1069 } 1070 1071 @Test removeFileGroup_bothVersionsExist()1072 public void removeFileGroup_bothVersionsExist() throws Exception { 1073 DataFile registeredFile = 1074 DataFile.newBuilder().setFileId("file").setChecksum("registered").build(); 1075 DataFile downloadedFile = 1076 DataFile.newBuilder().setFileId("file").setChecksum("downloaded").build(); 1077 1078 NewFileKey registeredFileKey = 1079 SharedFilesMetadata.createKeyFromDataFile(registeredFile, AllowedReaders.ALL_GOOGLE_APPS); 1080 1081 DataFileGroupInternal pendingFileGroup = 1082 DataFileGroupInternal.newBuilder() 1083 .setGroupName(TEST_GROUP) 1084 .setFileGroupVersionNumber(1) 1085 .setAllowedReadersEnum(AllowedReaders.ALL_GOOGLE_APPS) 1086 .addFile(registeredFile) 1087 .build(); 1088 DataFileGroupInternal downloadedFileGroup = 1089 DataFileGroupInternal.newBuilder() 1090 .setGroupName(TEST_GROUP) 1091 .setFileGroupVersionNumber(0) 1092 .setBuildId(0) 1093 .setVariantId("") 1094 .setAllowedReadersEnum(AllowedReaders.ALL_GOOGLE_APPS) 1095 .addFile(downloadedFile) 1096 .build(); 1097 1098 GroupKey groupKey = 1099 GroupKey.newBuilder() 1100 .setGroupName(TEST_GROUP) 1101 .setOwnerPackage(context.getPackageName()) 1102 .build(); 1103 GroupKey pendingGroupKey = groupKey.toBuilder().setDownloaded(false).build(); 1104 GroupKey downloadedGroupKey = groupKey.toBuilder().setDownloaded(true).build(); 1105 1106 writePendingFileGroup(pendingGroupKey, pendingFileGroup); 1107 writeDownloadedFileGroup(downloadedGroupKey, downloadedFileGroup); 1108 writeSharedFiles( 1109 sharedFilesMetadata, pendingFileGroup, ImmutableList.of(FileStatus.DOWNLOAD_IN_PROGRESS)); 1110 writeSharedFiles( 1111 sharedFilesMetadata, downloadedFileGroup, ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE)); 1112 1113 fileGroupManager.removeFileGroup(groupKey, /* pendingOnly= */ false).get(); 1114 1115 assertThat(readPendingFileGroup(pendingGroupKey)).isNull(); 1116 assertThat(readPendingFileGroup(downloadedGroupKey)).isNull(); 1117 assertThat(fileGroupsMetadata.getAllStaleGroups().get()) 1118 .containsExactly( 1119 downloadedFileGroup.toBuilder() 1120 .setBookkeeping( 1121 downloadedFileGroup.getBookkeeping().toBuilder() 1122 .setStaleExpirationDate(1) 1123 .build()) 1124 .build()); 1125 1126 Uri pendingFileUri = 1127 DirectoryUtil.getOnDeviceUri( 1128 context, 1129 registeredFileKey.getAllowedReaders(), 1130 registeredFile.getFileId(), 1131 registeredFileKey.getChecksum(), 1132 mockSilentFeedback, 1133 /* instanceId= */ Optional.absent(), 1134 /* androidShared= */ false); 1135 1136 // Only called once to stop download of pending file. 1137 verify(mockDownloader).stopDownloading(registeredFileKey.getChecksum(), pendingFileUri); 1138 1139 verifyNoInteractions(mockLogger); 1140 } 1141 1142 @Test removeFileGroup_bothVersionsExist_onlyRemovePending()1143 public void removeFileGroup_bothVersionsExist_onlyRemovePending() throws Exception { 1144 DataFile registeredFile = 1145 DataFile.newBuilder().setFileId("file").setChecksum("registered").build(); 1146 DataFile downloadedFile = 1147 DataFile.newBuilder().setFileId("file").setChecksum("downloaded").build(); 1148 1149 NewFileKey registeredFileKey = 1150 SharedFilesMetadata.createKeyFromDataFile(registeredFile, AllowedReaders.ALL_GOOGLE_APPS); 1151 1152 DataFileGroupInternal pendingFileGroup = 1153 DataFileGroupInternal.newBuilder() 1154 .setGroupName(TEST_GROUP) 1155 .setFileGroupVersionNumber(1) 1156 .setAllowedReadersEnum(AllowedReaders.ALL_GOOGLE_APPS) 1157 .addFile(registeredFile) 1158 .build(); 1159 DataFileGroupInternal downloadedFileGroup = 1160 DataFileGroupInternal.newBuilder() 1161 .setGroupName(TEST_GROUP) 1162 .setFileGroupVersionNumber(0) 1163 .setBuildId(0) 1164 .setVariantId("") 1165 .setAllowedReadersEnum(AllowedReaders.ALL_GOOGLE_APPS) 1166 .addFile(downloadedFile) 1167 .build(); 1168 1169 GroupKey groupKey = 1170 GroupKey.newBuilder() 1171 .setGroupName(TEST_GROUP) 1172 .setOwnerPackage(context.getPackageName()) 1173 .build(); 1174 GroupKey pendingGroupKey = groupKey.toBuilder().setDownloaded(false).build(); 1175 GroupKey downloadedGroupKey = groupKey.toBuilder().setDownloaded(true).build(); 1176 1177 writePendingFileGroup(pendingGroupKey, pendingFileGroup); 1178 writeDownloadedFileGroup(downloadedGroupKey, downloadedFileGroup); 1179 writeSharedFiles( 1180 sharedFilesMetadata, pendingFileGroup, ImmutableList.of(FileStatus.DOWNLOAD_IN_PROGRESS)); 1181 writeSharedFiles( 1182 sharedFilesMetadata, downloadedFileGroup, ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE)); 1183 1184 fileGroupManager.removeFileGroup(groupKey, /* pendingOnly= */ true).get(); 1185 1186 assertThat(readPendingFileGroup(pendingGroupKey)).isNull(); 1187 assertThat(readPendingFileGroup(downloadedGroupKey)).isNull(); 1188 1189 // Pending group was just removed, and downloaded was not added to stale groups. 1190 assertThat(fileGroupsMetadata.getAllStaleGroups().get()).isEmpty(); 1191 // Downloaded group is still available. 1192 assertThat(fileGroupsMetadata.getAllFreshGroups().get()) 1193 .containsExactly(GroupKeyAndGroup.create(downloadedGroupKey, downloadedFileGroup)); 1194 1195 Uri pendingFileUri = 1196 DirectoryUtil.getOnDeviceUri( 1197 context, 1198 registeredFileKey.getAllowedReaders(), 1199 registeredFile.getFileId(), 1200 registeredFileKey.getChecksum(), 1201 mockSilentFeedback, 1202 /* instanceId= */ Optional.absent(), 1203 /* androidShared= */ false); 1204 1205 // Only called once to stop download of pending file. 1206 verify(mockDownloader).stopDownloading(registeredFileKey.getChecksum(), pendingFileUri); 1207 1208 verifyNoInteractions(mockLogger); 1209 } 1210 1211 @Test removeFileGroup_fileReferencedByOtherFileGroup_willNotCancelDownload()1212 public void removeFileGroup_fileReferencedByOtherFileGroup_willNotCancelDownload() 1213 throws Exception { 1214 DataFile dataFile1 = DataFile.newBuilder().setFileId("file1").setChecksum("checksum1").build(); 1215 DataFile dataFile2 = DataFile.newBuilder().setFileId("file2").setChecksum("checksum2").build(); 1216 1217 DataFileGroupInternal pendingFileGroup = 1218 DataFileGroupInternal.newBuilder() 1219 .setGroupName(TEST_GROUP) 1220 .setFileGroupVersionNumber(1) 1221 .setAllowedReadersEnum(AllowedReaders.ALL_GOOGLE_APPS) 1222 .addAllFile(Lists.newArrayList(dataFile1, dataFile2)) 1223 .build(); 1224 1225 DataFileGroupInternal pendingFileGroup2 = 1226 DataFileGroupInternal.newBuilder() 1227 .setGroupName(TEST_GROUP_2) 1228 .setFileGroupVersionNumber(1) 1229 .setAllowedReadersEnum(AllowedReaders.ALL_GOOGLE_APPS) 1230 .addAllFile(Lists.newArrayList(dataFile1, dataFile2)) 1231 .build(); 1232 1233 GroupKey groupKey = 1234 GroupKey.newBuilder() 1235 .setGroupName(TEST_GROUP) 1236 .setOwnerPackage(context.getPackageName()) 1237 .build(); 1238 GroupKey pendingGroupKey = groupKey.toBuilder().setDownloaded(false).build(); 1239 GroupKey downloadedGroupKey = groupKey.toBuilder().setDownloaded(true).build(); 1240 1241 GroupKey groupKey2 = 1242 GroupKey.newBuilder() 1243 .setGroupName(TEST_GROUP_2) 1244 .setOwnerPackage(context.getPackageName()) 1245 .build(); 1246 GroupKey pendingGroupKey2 = groupKey2.toBuilder().setDownloaded(false).build(); 1247 1248 writePendingFileGroup(pendingGroupKey, pendingFileGroup); 1249 writePendingFileGroup(pendingGroupKey2, pendingFileGroup2); 1250 writeSharedFiles( 1251 sharedFilesMetadata, 1252 pendingFileGroup, 1253 ImmutableList.of(FileStatus.DOWNLOAD_IN_PROGRESS, FileStatus.DOWNLOAD_IN_PROGRESS)); 1254 writeSharedFiles( 1255 sharedFilesMetadata, 1256 pendingFileGroup2, 1257 ImmutableList.of(FileStatus.DOWNLOAD_IN_PROGRESS, FileStatus.DOWNLOAD_IN_PROGRESS)); 1258 assertThat(fileGroupsMetadata.getAllFreshGroups().get()) 1259 .containsExactly( 1260 GroupKeyAndGroup.create(pendingGroupKey, pendingFileGroup), 1261 GroupKeyAndGroup.create(pendingGroupKey2, pendingFileGroup2)); 1262 1263 fileGroupManager.removeFileGroup(groupKey, /* pendingOnly= */ false).get(); 1264 1265 assertThat(readPendingFileGroup(pendingGroupKey)).isNull(); 1266 assertThat(readPendingFileGroup(pendingGroupKey2)).isNotNull(); 1267 assertThat(readPendingFileGroup(downloadedGroupKey)).isNull(); 1268 assertThat(fileGroupsMetadata.getAllStaleGroups().get()).isEmpty(); 1269 assertThat(fileGroupsMetadata.getAllFreshGroups().get()) 1270 .containsExactly(GroupKeyAndGroup.create(pendingGroupKey2, pendingFileGroup2)); 1271 1272 verify(mockDownloader, never()).stopDownloading(any(String.class), any(Uri.class)); 1273 1274 verifyNoInteractions(mockLogger); 1275 } 1276 1277 @Test removeFileGroup_onFailure()1278 public void removeFileGroup_onFailure() throws Exception { 1279 // Mock FileGroupsMetadata to test failure scenario. 1280 resetFileGroupManager(mockFileGroupsMetadata, sharedFileManager); 1281 DataFileGroupInternal pendingFileGroup = 1282 DataFileGroupInternal.newBuilder() 1283 .setGroupName(TEST_GROUP) 1284 .setFileGroupVersionNumber(1) 1285 .build(); 1286 DataFileGroupInternal downloadedFileGroup = 1287 DataFileGroupInternal.newBuilder() 1288 .setGroupName(TEST_GROUP) 1289 .setFileGroupVersionNumber(0) 1290 .setBuildId(0) 1291 .setVariantId("") 1292 .build(); 1293 1294 GroupKey groupKey = 1295 GroupKey.newBuilder() 1296 .setGroupName(TEST_GROUP) 1297 .setOwnerPackage(context.getPackageName()) 1298 .build(); 1299 GroupKey pendingGroupKey = groupKey.toBuilder().setDownloaded(false).build(); 1300 GroupKey downloadedGroupKey = groupKey.toBuilder().setDownloaded(true).build(); 1301 1302 when(mockFileGroupsMetadata.read(pendingGroupKey)) 1303 .thenReturn(Futures.immediateFuture(pendingFileGroup)); 1304 when(mockFileGroupsMetadata.read(downloadedGroupKey)) 1305 .thenReturn(Futures.immediateFuture(downloadedFileGroup)); 1306 when(mockFileGroupsMetadata.remove(pendingGroupKey)).thenReturn(Futures.immediateFuture(true)); 1307 when(mockFileGroupsMetadata.remove(downloadedGroupKey)) 1308 .thenReturn(Futures.immediateFuture(false)); 1309 1310 // Exception should be thrown when fileGroupManager attempts to remove downloadedGroupKey. 1311 ExecutionException expected = 1312 assertThrows( 1313 ExecutionException.class, 1314 () -> fileGroupManager.removeFileGroup(groupKey, /* pendingOnly= */ false).get()); 1315 assertThat(expected).hasCauseThat().isInstanceOf(IOException.class); 1316 1317 verify(mockFileGroupsMetadata).remove(pendingGroupKey); 1318 verify(mockFileGroupsMetadata).remove(downloadedGroupKey); 1319 verify(mockFileGroupsMetadata, never()).addStaleGroup(any(DataFileGroupInternal.class)); 1320 1321 verify(mockLogger).logEventSampled(MddClientEvent.Code.EVENT_CODE_UNSPECIFIED); 1322 } 1323 1324 @Test removeFileGroup_removesSideloadedGroup()1325 public void removeFileGroup_removesSideloadedGroup() throws Exception { 1326 // Create sideloaded group 1327 DataFileGroupInternal sideloadedGroup = 1328 DataFileGroupInternal.newBuilder() 1329 .setGroupName(TEST_GROUP) 1330 .addFile( 1331 DataFile.newBuilder() 1332 .setFileId("sideloaded_file") 1333 .setUrlToDownload("file:/test") 1334 .setChecksumType(DataFile.ChecksumType.NONE) 1335 .build()) 1336 .build(); 1337 1338 writePendingFileGroup(testKey, sideloadedGroup); 1339 writeDownloadedFileGroup(testKey, sideloadedGroup); 1340 1341 fileGroupManager.removeFileGroup(testKey, /* pendingOnly= */ false).get(); 1342 1343 assertThat(readPendingFileGroup(testKey)).isNull(); 1344 assertThat(readDownloadedFileGroup(testKey)).isNull(); 1345 } 1346 1347 @Test 1348 public void removeFileGroup_whenMultipleVariantsExist_whenNoVariantSpecified_removesEmptyVariantGroup()1349 removeFileGroup_whenMultipleVariantsExist_whenNoVariantSpecified_removesEmptyVariantGroup() 1350 throws Exception { 1351 // Create 3 variants of a group (default (no variant), en, fr) and have them all added. When 1352 // removeFileGroups is called and the group key given does not include a variant, ensure that 1353 // the default group is removed. 1354 1355 GroupKey defaultGroupKey = 1356 GroupKey.newBuilder() 1357 .setGroupName(TEST_GROUP) 1358 .setOwnerPackage(context.getPackageName()) 1359 .build(); 1360 GroupKey enGroupKey = defaultGroupKey.toBuilder().setVariantId("en").build(); 1361 GroupKey frGroupKey = defaultGroupKey.toBuilder().setVariantId("fr").build(); 1362 1363 DataFileGroupInternal defaultFileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1); 1364 DataFileGroupInternal enFileGroup = defaultFileGroup.toBuilder().setVariantId("en").build(); 1365 DataFileGroupInternal frFileGroup = defaultFileGroup.toBuilder().setVariantId("fr").build(); 1366 1367 writePendingFileGroup(getPendingKey(defaultGroupKey), defaultFileGroup); 1368 writePendingFileGroup(getPendingKey(enGroupKey), enFileGroup); 1369 writePendingFileGroup(getPendingKey(frGroupKey), frFileGroup); 1370 1371 writeSharedFiles( 1372 sharedFilesMetadata, defaultFileGroup, ImmutableList.of(FileStatus.DOWNLOAD_IN_PROGRESS)); 1373 writeSharedFiles( 1374 sharedFilesMetadata, enFileGroup, ImmutableList.of(FileStatus.DOWNLOAD_IN_PROGRESS)); 1375 writeSharedFiles( 1376 sharedFilesMetadata, frFileGroup, ImmutableList.of(FileStatus.DOWNLOAD_IN_PROGRESS)); 1377 1378 // Assert that all file groups share the same file even through the variants are different 1379 assertThat(sharedFilesMetadata.getAllFileKeys().get()).hasSize(1); 1380 1381 { 1382 // Perfrom removal once and check that the default group gets removed 1383 fileGroupManager.removeFileGroup(defaultGroupKey, /* pendingOnly= */ false).get(); 1384 1385 assertThat(fileGroupsMetadata.getAllGroupKeys().get()) 1386 .comparingElementsUsing(GROUP_KEY_TO_VARIANT) 1387 .containsExactly("en", "fr"); 1388 assertThat(fileGroupsMetadata.getAllFreshGroups().get()) 1389 .comparingElementsUsing(KEY_GROUP_PAIR_TO_VARIANT) 1390 .containsExactly("en", "fr"); 1391 1392 assertThat(sharedFilesMetadata.getAllFileKeys().get()).hasSize(1); 1393 } 1394 1395 { 1396 // Perform remove again and verify that there is no change in state 1397 fileGroupManager.removeFileGroup(defaultGroupKey, /* pendingOnly= */ false).get(); 1398 1399 assertThat(fileGroupsMetadata.getAllGroupKeys().get()) 1400 .comparingElementsUsing(GROUP_KEY_TO_VARIANT) 1401 .containsExactly("en", "fr"); 1402 assertThat(fileGroupsMetadata.getAllFreshGroups().get()) 1403 .comparingElementsUsing(KEY_GROUP_PAIR_TO_VARIANT) 1404 .containsExactly("en", "fr"); 1405 1406 assertThat(sharedFilesMetadata.getAllFileKeys().get()).hasSize(1); 1407 } 1408 } 1409 1410 @Test removeFileGroup_whenMultipleVariantsExist_whenVariantSpecified_removesVariantGroup()1411 public void removeFileGroup_whenMultipleVariantsExist_whenVariantSpecified_removesVariantGroup() 1412 throws Exception { 1413 // Create 3 variants of a group (default (no variant), en, fr) and have them all added. When 1414 // removeFileGroups is called and the group key given includes a variant, ensure that only 1415 // the group with that variant is removed. 1416 1417 GroupKey defaultGroupKey = 1418 GroupKey.newBuilder() 1419 .setGroupName(TEST_GROUP) 1420 .setOwnerPackage(context.getPackageName()) 1421 .build(); 1422 GroupKey enGroupKey = defaultGroupKey.toBuilder().setVariantId("en").build(); 1423 GroupKey frGroupKey = defaultGroupKey.toBuilder().setVariantId("fr").build(); 1424 1425 DataFileGroupInternal defaultFileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1); 1426 DataFileGroupInternal enFileGroup = defaultFileGroup.toBuilder().setVariantId("en").build(); 1427 DataFileGroupInternal frFileGroup = defaultFileGroup.toBuilder().setVariantId("fr").build(); 1428 1429 writePendingFileGroup(getPendingKey(defaultGroupKey), defaultFileGroup); 1430 writePendingFileGroup(getPendingKey(enGroupKey), enFileGroup); 1431 writePendingFileGroup(getPendingKey(frGroupKey), frFileGroup); 1432 1433 writeSharedFiles( 1434 sharedFilesMetadata, defaultFileGroup, ImmutableList.of(FileStatus.DOWNLOAD_IN_PROGRESS)); 1435 writeSharedFiles( 1436 sharedFilesMetadata, enFileGroup, ImmutableList.of(FileStatus.DOWNLOAD_IN_PROGRESS)); 1437 writeSharedFiles( 1438 sharedFilesMetadata, frFileGroup, ImmutableList.of(FileStatus.DOWNLOAD_IN_PROGRESS)); 1439 1440 // Assert that all file groups share the same file even through the variants are different 1441 assertThat(sharedFilesMetadata.getAllFileKeys().get()).hasSize(1); 1442 1443 { 1444 // Perfrom removal once and check that the en group gets removed 1445 fileGroupManager.removeFileGroup(enGroupKey, /* pendingOnly= */ false).get(); 1446 1447 assertThat(fileGroupsMetadata.getAllGroupKeys().get()) 1448 .comparingElementsUsing(GROUP_KEY_TO_VARIANT) 1449 .containsExactly("", "fr"); 1450 assertThat(fileGroupsMetadata.getAllFreshGroups().get()) 1451 .comparingElementsUsing(KEY_GROUP_PAIR_TO_VARIANT) 1452 .containsExactly("", "fr"); 1453 1454 assertThat(sharedFilesMetadata.getAllFileKeys().get()).hasSize(1); 1455 } 1456 1457 { 1458 // Perform remove again and verify that there is no change in state 1459 fileGroupManager.removeFileGroup(enGroupKey, /* pendingOnly= */ false).get(); 1460 1461 assertThat(fileGroupsMetadata.getAllGroupKeys().get()) 1462 .comparingElementsUsing(GROUP_KEY_TO_VARIANT) 1463 .containsExactly("", "fr"); 1464 assertThat(fileGroupsMetadata.getAllFreshGroups().get()) 1465 .comparingElementsUsing(KEY_GROUP_PAIR_TO_VARIANT) 1466 .containsExactly("", "fr"); 1467 1468 assertThat(sharedFilesMetadata.getAllFileKeys().get()).hasSize(1); 1469 } 1470 } 1471 1472 @Test testRemoveFileGroups_whenNoGroupsExist_performsNoRemovals()1473 public void testRemoveFileGroups_whenNoGroupsExist_performsNoRemovals() throws Exception { 1474 GroupKey groupKey1 = 1475 GroupKey.newBuilder() 1476 .setGroupName(TEST_GROUP) 1477 .setOwnerPackage(context.getPackageName()) 1478 .build(); 1479 GroupKey groupKey2 = 1480 GroupKey.newBuilder() 1481 .setGroupName(TEST_GROUP_2) 1482 .setOwnerPackage(context.getPackageName()) 1483 .build(); 1484 1485 fileGroupManager.removeFileGroups(ImmutableList.of(groupKey1, groupKey2)).get(); 1486 1487 assertThat(fileGroupsMetadata.getAllStaleGroups().get()).isEmpty(); 1488 assertThat(fileGroupsMetadata.getAllGroupKeys().get()).isEmpty(); 1489 } 1490 1491 @Test testRemoveFileGroups_whenNoMatchingKeysExist_performsNoRemovals()1492 public void testRemoveFileGroups_whenNoMatchingKeysExist_performsNoRemovals() throws Exception { 1493 // Create a pending and downloaded version of a file group 1494 // Pending group includes 2 files: 1 that is shared with downloaded group and one that will be 1495 // marked pending 1496 DataFileGroupInternal pendingFileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2); 1497 DataFileGroupInternal downloadedFileGroup = 1498 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1); 1499 1500 GroupKey.Builder groupKeyBuilder = 1501 GroupKey.newBuilder().setGroupName(TEST_GROUP).setOwnerPackage(context.getPackageName()); 1502 GroupKey pendingGroupKey = groupKeyBuilder.setDownloaded(false).build(); 1503 GroupKey downloadedGroupKey = groupKeyBuilder.setDownloaded(true).build(); 1504 GroupKey nonMatchingGroupKey1 = groupKeyBuilder.setGroupName(TEST_GROUP_2).build(); 1505 GroupKey nonMatchingGroupKey2 = groupKeyBuilder.setGroupName(TEST_GROUP_3).build(); 1506 1507 // Write file group and shared file metadata 1508 // NOTE: pending group contains all files in downloaded group, so we only need to write shared 1509 // file state once. 1510 writePendingFileGroup(pendingGroupKey, pendingFileGroup); 1511 writeDownloadedFileGroup(downloadedGroupKey, downloadedFileGroup); 1512 writeSharedFiles( 1513 sharedFilesMetadata, 1514 pendingFileGroup, 1515 ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE, FileStatus.DOWNLOAD_IN_PROGRESS)); 1516 1517 fileGroupManager 1518 .removeFileGroups(ImmutableList.of(nonMatchingGroupKey1, nonMatchingGroupKey2)) 1519 .get(); 1520 1521 assertThat(readPendingFileGroup(pendingGroupKey)).isNotNull(); 1522 assertThat(readDownloadedFileGroup(downloadedGroupKey)).isNotNull(); 1523 assertThat(fileGroupsMetadata.getAllFreshGroups().get()).hasSize(2); 1524 assertThat(fileGroupsMetadata.getAllStaleGroups().get()).isEmpty(); 1525 1526 verify(mockDownloader, times(0)).stopDownloading(any(), any()); 1527 } 1528 1529 @Test testRemoveFileGroups_whenMatchingPendingGroups_performsRemove()1530 public void testRemoveFileGroups_whenMatchingPendingGroups_performsRemove() throws Exception { 1531 // Create 2 pending groups that will be removed, 1 pending group that shouldn't be removed, and 1532 // 1 downloaded group that shouldn't be removed 1533 DataFileGroupInternal pendingGroupToRemove1 = 1534 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1).toBuilder().build(); 1535 DataFileGroupInternal pendingGroupToRemove2 = 1536 MddTestUtil.createDataFileGroupInternal(TEST_GROUP_2, 1); 1537 DataFileGroupInternal pendingGroupToKeep = 1538 MddTestUtil.createDataFileGroupInternal(TEST_GROUP_3, 1).toBuilder().build(); 1539 DataFileGroupInternal downloadedGroupToKeep = 1540 MddTestUtil.createDataFileGroupInternal(TEST_GROUP_4, 1); 1541 1542 GroupKey.Builder groupKeyBuilder = 1543 GroupKey.newBuilder().setOwnerPackage(context.getPackageName()).setDownloaded(false); 1544 GroupKey pendingGroupKeyToRemove1 = groupKeyBuilder.setGroupName(TEST_GROUP).build(); 1545 GroupKey pendingGroupKeyToRemove2 = groupKeyBuilder.setGroupName(TEST_GROUP_2).build(); 1546 GroupKey pendingGroupKeyToKeep = groupKeyBuilder.setGroupName(TEST_GROUP_3).build(); 1547 GroupKey downloadedGroupKeyToKeep = 1548 groupKeyBuilder.setGroupName(TEST_GROUP_4).setDownloaded(true).build(); 1549 1550 writePendingFileGroup(pendingGroupKeyToRemove1, pendingGroupToRemove1); 1551 writePendingFileGroup(pendingGroupKeyToRemove2, pendingGroupToRemove2); 1552 writePendingFileGroup(pendingGroupKeyToKeep, pendingGroupToKeep); 1553 writeDownloadedFileGroup(downloadedGroupKeyToKeep, downloadedGroupToKeep); 1554 1555 writeSharedFiles( 1556 sharedFilesMetadata, 1557 pendingGroupToRemove1, 1558 ImmutableList.of(FileStatus.DOWNLOAD_IN_PROGRESS)); 1559 writeSharedFiles( 1560 sharedFilesMetadata, 1561 pendingGroupToRemove2, 1562 ImmutableList.of(FileStatus.DOWNLOAD_IN_PROGRESS)); 1563 writeSharedFiles( 1564 sharedFilesMetadata, pendingGroupToKeep, ImmutableList.of(FileStatus.DOWNLOAD_IN_PROGRESS)); 1565 writeSharedFiles( 1566 sharedFilesMetadata, downloadedGroupToKeep, ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE)); 1567 1568 fileGroupManager 1569 .removeFileGroups(ImmutableList.of(pendingGroupKeyToRemove1, pendingGroupKeyToRemove2)) 1570 .get(); 1571 1572 // Construct Pending File Uris to check which downloads were stopped 1573 NewFileKey pendingFileKey1 = 1574 MddTestUtil.createFileKeysForDataFileGroupInternal(pendingGroupToRemove1)[0]; 1575 NewFileKey pendingFileKey2 = 1576 MddTestUtil.createFileKeysForDataFileGroupInternal(pendingGroupToRemove2)[0]; 1577 NewFileKey pendingFileKey3 = 1578 MddTestUtil.createFileKeysForDataFileGroupInternal(pendingGroupToKeep)[0]; 1579 Uri pendingFileUri1 = 1580 DirectoryUtil.getOnDeviceUri( 1581 context, 1582 pendingFileKey1.getAllowedReaders(), 1583 pendingGroupToRemove1.getFile(0).getFileId(), 1584 pendingFileKey1.getChecksum(), 1585 mockSilentFeedback, 1586 /* instanceId= */ Optional.absent(), 1587 /* androidShared= */ false); 1588 Uri pendingFileUri2 = 1589 DirectoryUtil.getOnDeviceUri( 1590 context, 1591 pendingFileKey2.getAllowedReaders(), 1592 pendingGroupToRemove2.getFile(0).getFileId(), 1593 pendingFileKey2.getChecksum(), 1594 mockSilentFeedback, 1595 /* instanceId= */ Optional.absent(), 1596 /* androidShared= */ false); 1597 Uri pendingFileUri3 = 1598 DirectoryUtil.getOnDeviceUri( 1599 context, 1600 pendingFileKey3.getAllowedReaders(), 1601 pendingGroupToKeep.getFile(0).getFileId(), 1602 pendingFileKey3.getChecksum(), 1603 mockSilentFeedback, 1604 /* instanceId= */ Optional.absent(), 1605 /* androidShared= */ false); 1606 1607 // Assert that matching pending groups are removed 1608 assertThat(readPendingFileGroup(pendingGroupKeyToRemove1)).isNull(); 1609 assertThat(readPendingFileGroup(pendingGroupKeyToRemove2)).isNull(); 1610 assertThat(readPendingFileGroup(pendingGroupKeyToKeep)).isNotNull(); 1611 assertThat(readDownloadedFileGroup(downloadedGroupKeyToKeep)).isNotNull(); 1612 assertThat(fileGroupsMetadata.getAllFreshGroups().get()).hasSize(2); 1613 assertThat(fileGroupsMetadata.getAllStaleGroups().get()).isEmpty(); 1614 1615 verify(mockDownloader).stopDownloading(pendingFileKey1.getChecksum(), pendingFileUri1); 1616 verify(mockDownloader).stopDownloading(pendingFileKey2.getChecksum(), pendingFileUri2); 1617 verify(mockDownloader, times(0)) 1618 .stopDownloading(pendingFileKey3.getChecksum(), pendingFileUri3); 1619 } 1620 1621 @Test testRemoveFileGroups_whenMatchingDownloadedGroups_performsRemove()1622 public void testRemoveFileGroups_whenMatchingDownloadedGroups_performsRemove() throws Exception { 1623 // Create 2 downloaded groups that will be removed, 1 downloaded group that shouldn't be 1624 // removed, and 1 pending group that shouldn't be removed 1625 DataFileGroupInternal downloadedGroupToRemove1 = 1626 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1); 1627 DataFileGroupInternal downloadedGroupToRemove2 = 1628 MddTestUtil.createDataFileGroupInternal(TEST_GROUP_2, 1); 1629 DataFileGroupInternal downloadedGroupToKeep = 1630 MddTestUtil.createDataFileGroupInternal(TEST_GROUP_3, 1); 1631 DataFileGroupInternal pendingGroupToKeep = 1632 MddTestUtil.createDataFileGroupInternal(TEST_GROUP_4, 1).toBuilder().build(); 1633 1634 GroupKey.Builder groupKeyBuilder = 1635 GroupKey.newBuilder().setOwnerPackage(context.getPackageName()).setDownloaded(true); 1636 GroupKey downloadedGroupKeyToRemove1 = groupKeyBuilder.setGroupName(TEST_GROUP).build(); 1637 GroupKey downloadedGroupKeyToRemove2 = groupKeyBuilder.setGroupName(TEST_GROUP_2).build(); 1638 GroupKey downloadedGroupKeyToKeep = groupKeyBuilder.setGroupName(TEST_GROUP_3).build(); 1639 GroupKey pendingGroupKeyToKeep = 1640 groupKeyBuilder.setGroupName(TEST_GROUP_4).setDownloaded(false).build(); 1641 1642 writeDownloadedFileGroup(downloadedGroupKeyToRemove1, downloadedGroupToRemove1); 1643 writeDownloadedFileGroup(downloadedGroupKeyToRemove2, downloadedGroupToRemove2); 1644 writeDownloadedFileGroup(downloadedGroupKeyToKeep, downloadedGroupToKeep); 1645 writePendingFileGroup(pendingGroupKeyToKeep, pendingGroupToKeep); 1646 1647 writeSharedFiles( 1648 sharedFilesMetadata, 1649 downloadedGroupToRemove1, 1650 ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE)); 1651 writeSharedFiles( 1652 sharedFilesMetadata, 1653 downloadedGroupToRemove2, 1654 ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE)); 1655 writeSharedFiles( 1656 sharedFilesMetadata, downloadedGroupToKeep, ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE)); 1657 writeSharedFiles( 1658 sharedFilesMetadata, pendingGroupToKeep, ImmutableList.of(FileStatus.DOWNLOAD_IN_PROGRESS)); 1659 1660 fileGroupManager 1661 .removeFileGroups( 1662 ImmutableList.of(downloadedGroupKeyToRemove1, downloadedGroupKeyToRemove2)) 1663 .get(); 1664 1665 // Construct Pending File Uri to check that it isn't cancelled 1666 NewFileKey pendingFileKey1 = 1667 MddTestUtil.createFileKeysForDataFileGroupInternal(pendingGroupToKeep)[0]; 1668 Uri pendingFileUri1 = 1669 DirectoryUtil.getOnDeviceUri( 1670 context, 1671 pendingFileKey1.getAllowedReaders(), 1672 pendingGroupToKeep.getFile(0).getFileId(), 1673 pendingFileKey1.getChecksum(), 1674 mockSilentFeedback, 1675 /* instanceId= */ Optional.absent(), 1676 /* androidShared= */ false); 1677 1678 // Assert that matching pending groups are removed 1679 assertThat(readDownloadedFileGroup(downloadedGroupKeyToRemove1)).isNull(); 1680 assertThat(readDownloadedFileGroup(downloadedGroupKeyToRemove2)).isNull(); 1681 assertThat(readDownloadedFileGroup(downloadedGroupKeyToKeep)).isNotNull(); 1682 assertThat(readPendingFileGroup(pendingGroupKeyToKeep)).isNotNull(); 1683 1684 assertThat(fileGroupsMetadata.getAllFreshGroups().get()) 1685 .containsExactly( 1686 GroupKeyAndGroup.create(downloadedGroupKeyToKeep, downloadedGroupToKeep), 1687 GroupKeyAndGroup.create(pendingGroupKeyToKeep, pendingGroupToKeep)); 1688 assertThat(fileGroupsMetadata.getAllStaleGroups().get()) 1689 .containsExactly( 1690 downloadedGroupToRemove1.toBuilder() 1691 .setBookkeeping( 1692 downloadedGroupToRemove1.getBookkeeping().toBuilder() 1693 .setStaleExpirationDate(1) 1694 .build()) 1695 .build(), 1696 downloadedGroupToRemove2.toBuilder() 1697 .setBookkeeping( 1698 downloadedGroupToRemove2.getBookkeeping().toBuilder() 1699 .setStaleExpirationDate(1) 1700 .build()) 1701 .build()); 1702 1703 verify(mockDownloader, times(0)) 1704 .stopDownloading(pendingFileKey1.getChecksum(), pendingFileUri1); 1705 } 1706 1707 @Test testRemoveFileGroups_whenMatchingBothVersions_performsRemove()1708 public void testRemoveFileGroups_whenMatchingBothVersions_performsRemove() throws Exception { 1709 // Create 2 file groups, each with 2 versions (downloaded and pending) 1710 DataFileGroupInternal downloadedGroupToRemove1 = 1711 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1); 1712 DataFileGroupInternal downloadedGroupToRemove2 = 1713 MddTestUtil.createDataFileGroupInternal(TEST_GROUP_2, 1); 1714 DataFileGroupInternal pendingGroupToRemove1 = 1715 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1).toBuilder().build(); 1716 DataFileGroupInternal pendingGroupToRemove2 = 1717 MddTestUtil.createDataFileGroupInternal(TEST_GROUP_2, 1); 1718 1719 GroupKey.Builder groupKeyBuilder = 1720 GroupKey.newBuilder().setOwnerPackage(context.getPackageName()).setDownloaded(true); 1721 GroupKey downloadedGroupKeyToRemove1 = groupKeyBuilder.setGroupName(TEST_GROUP).build(); 1722 GroupKey downloadedGroupKeyToRemove2 = groupKeyBuilder.setGroupName(TEST_GROUP_2).build(); 1723 GroupKey pendingGroupKeyToRemove1 = 1724 groupKeyBuilder.setGroupName(TEST_GROUP).setDownloaded(false).build(); 1725 GroupKey pendingGroupKeyToRemove2 = 1726 groupKeyBuilder.setGroupName(TEST_GROUP_2).setDownloaded(false).build(); 1727 1728 writeDownloadedFileGroup(downloadedGroupKeyToRemove1, downloadedGroupToRemove1); 1729 writeDownloadedFileGroup(downloadedGroupKeyToRemove2, downloadedGroupToRemove2); 1730 writePendingFileGroup(pendingGroupKeyToRemove1, pendingGroupToRemove1); 1731 writePendingFileGroup(pendingGroupKeyToRemove2, pendingGroupToRemove2); 1732 1733 writeSharedFiles( 1734 sharedFilesMetadata, 1735 downloadedGroupToRemove1, 1736 ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE)); 1737 writeSharedFiles( 1738 sharedFilesMetadata, 1739 downloadedGroupToRemove2, 1740 ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE)); 1741 writeSharedFiles( 1742 sharedFilesMetadata, 1743 pendingGroupToRemove1, 1744 ImmutableList.of(FileStatus.DOWNLOAD_IN_PROGRESS)); 1745 writeSharedFiles( 1746 sharedFilesMetadata, 1747 pendingGroupToRemove2, 1748 ImmutableList.of(FileStatus.DOWNLOAD_IN_PROGRESS)); 1749 1750 // NOTE: the downloaded version of keys are used in this call, but this shouldn't be relevant; 1751 // both downloaded and pending versions of group keys should be checked for removal. 1752 fileGroupManager 1753 .removeFileGroups( 1754 ImmutableList.of(downloadedGroupKeyToRemove1, downloadedGroupKeyToRemove2)) 1755 .get(); 1756 1757 // Construct Pending File Uri to check that its download was cancelled 1758 NewFileKey pendingFileKey1 = 1759 MddTestUtil.createFileKeysForDataFileGroupInternal(pendingGroupToRemove1)[0]; 1760 NewFileKey pendingFileKey2 = 1761 MddTestUtil.createFileKeysForDataFileGroupInternal(pendingGroupToRemove2)[0]; 1762 Uri pendingFileUri1 = 1763 DirectoryUtil.getOnDeviceUri( 1764 context, 1765 pendingFileKey1.getAllowedReaders(), 1766 pendingGroupToRemove1.getFile(0).getFileId(), 1767 pendingFileKey1.getChecksum(), 1768 mockSilentFeedback, 1769 /* instanceId= */ Optional.absent(), 1770 /* androidShared= */ false); 1771 Uri pendingFileUri2 = 1772 DirectoryUtil.getOnDeviceUri( 1773 context, 1774 pendingFileKey2.getAllowedReaders(), 1775 pendingGroupToRemove2.getFile(0).getFileId(), 1776 pendingFileKey2.getChecksum(), 1777 mockSilentFeedback, 1778 /* instanceId= */ Optional.absent(), 1779 /* androidShared= */ false); 1780 1781 // Assert that matching pending groups are removed 1782 assertThat(readDownloadedFileGroup(downloadedGroupKeyToRemove1)).isNull(); 1783 assertThat(readDownloadedFileGroup(downloadedGroupKeyToRemove2)).isNull(); 1784 assertThat(readPendingFileGroup(pendingGroupKeyToRemove1)).isNull(); 1785 assertThat(readPendingFileGroup(pendingGroupKeyToRemove2)).isNull(); 1786 1787 assertThat(fileGroupsMetadata.getAllFreshGroups().get()).isEmpty(); 1788 assertThat(fileGroupsMetadata.getAllStaleGroups().get()) 1789 .containsExactly( 1790 downloadedGroupToRemove1.toBuilder() 1791 .setBookkeeping( 1792 downloadedGroupToRemove1.getBookkeeping().toBuilder() 1793 .setStaleExpirationDate(1) 1794 .build()) 1795 .build(), 1796 downloadedGroupToRemove2.toBuilder() 1797 .setBookkeeping( 1798 downloadedGroupToRemove2.getBookkeeping().toBuilder() 1799 .setStaleExpirationDate(1) 1800 .build()) 1801 .build()); 1802 1803 verify(mockDownloader, times(1)) 1804 .stopDownloading(pendingFileKey1.getChecksum(), pendingFileUri1); 1805 verify(mockDownloader, times(1)) 1806 .stopDownloading(pendingFileKey2.getChecksum(), pendingFileUri2); 1807 } 1808 1809 @Test testRemoveFileGroups_whenFilesAreReferencedByOtherGroups_doesNotCancelDownloads()1810 public void testRemoveFileGroups_whenFilesAreReferencedByOtherGroups_doesNotCancelDownloads() 1811 throws Exception { 1812 // Setup 2 pending groups to remove that each contain a file referenced by a 3rd pending group 1813 // that doesn't get removed. The pending file downloads referenced by the 3rd group should not 1814 // be cancelled. 1815 DataFileGroupInternal pendingGroupToKeep = 1816 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2); 1817 DataFileGroupInternal pendingGroupToRemove1 = 1818 MddTestUtil.createDataFileGroupInternal(TEST_GROUP_2, 1).toBuilder() 1819 .addFile(pendingGroupToKeep.getFile(0)) 1820 .build(); 1821 DataFileGroupInternal pendingGroupToRemove2 = 1822 MddTestUtil.createDataFileGroupInternal(TEST_GROUP_3, 1).toBuilder() 1823 .addFile(pendingGroupToKeep.getFile(1)) 1824 .build(); 1825 1826 GroupKey.Builder groupKeyBuilder = 1827 GroupKey.newBuilder().setOwnerPackage(context.getPackageName()).setDownloaded(false); 1828 GroupKey pendingGroupKeyToKeep = groupKeyBuilder.setGroupName(TEST_GROUP).build(); 1829 GroupKey pendingGroupKeyToRemove1 = groupKeyBuilder.setGroupName(TEST_GROUP_2).build(); 1830 GroupKey pendingGroupKeyToRemove2 = groupKeyBuilder.setGroupName(TEST_GROUP_3).build(); 1831 1832 writePendingFileGroup(pendingGroupKeyToKeep, pendingGroupToKeep); 1833 writePendingFileGroup(pendingGroupKeyToRemove1, pendingGroupToRemove1); 1834 writePendingFileGroup(pendingGroupKeyToRemove2, pendingGroupToRemove2); 1835 1836 writeSharedFiles( 1837 sharedFilesMetadata, 1838 pendingGroupToKeep, 1839 ImmutableList.of(FileStatus.DOWNLOAD_IN_PROGRESS, FileStatus.DOWNLOAD_IN_PROGRESS)); 1840 writeSharedFiles( 1841 sharedFilesMetadata, 1842 pendingGroupToRemove1, 1843 ImmutableList.of(FileStatus.DOWNLOAD_IN_PROGRESS, FileStatus.DOWNLOAD_IN_PROGRESS)); 1844 writeSharedFiles( 1845 sharedFilesMetadata, 1846 pendingGroupToRemove2, 1847 ImmutableList.of(FileStatus.DOWNLOAD_IN_PROGRESS, FileStatus.DOWNLOAD_IN_PROGRESS)); 1848 1849 fileGroupManager 1850 .removeFileGroups(ImmutableList.of(pendingGroupKeyToRemove1, pendingGroupKeyToRemove2)) 1851 .get(); 1852 1853 assertThat(readPendingFileGroup(pendingGroupKeyToRemove1)).isNull(); 1854 assertThat(readPendingFileGroup(pendingGroupKeyToRemove2)).isNull(); 1855 assertThat(readPendingFileGroup(pendingGroupKeyToKeep)).isNotNull(); 1856 assertThat(fileGroupsMetadata.getAllFreshGroups().get()) 1857 .containsExactly(GroupKeyAndGroup.create(pendingGroupKeyToKeep, pendingGroupToKeep)); 1858 1859 // Get On Device Uris to check if file downloads were cancelled 1860 List<Uri> uncancelledFileUris = getOnDeviceUrisForFileGroup(pendingGroupToKeep); 1861 verify(mockDownloader, times(0)).stopDownloading(any(), eq(uncancelledFileUris.get(0))); 1862 verify(mockDownloader, times(0)).stopDownloading(any(), eq(uncancelledFileUris.get(1))); 1863 1864 verify(mockDownloader, times(1)) 1865 .stopDownloading( 1866 pendingGroupToRemove1.getFile(0).getChecksum(), 1867 getOnDeviceUrisForFileGroup(pendingGroupToRemove1).get(0)); 1868 verify(mockDownloader, times(1)) 1869 .stopDownloading( 1870 pendingGroupToRemove2.getFile(0).getChecksum(), 1871 getOnDeviceUrisForFileGroup(pendingGroupToRemove2).get(0)); 1872 } 1873 1874 @Test testRemoveFileGroups_whenRemovePendingGroupFails_doesNotContinue()1875 public void testRemoveFileGroups_whenRemovePendingGroupFails_doesNotContinue() throws Exception { 1876 // Use Mocks to simulate failure scenario 1877 resetFileGroupManager(mockFileGroupsMetadata, mockSharedFileManager); 1878 1879 DataFileGroupInternal pendingGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1); 1880 1881 GroupKey.Builder groupKeyBuilder = 1882 GroupKey.newBuilder().setOwnerPackage(context.getPackageName()); 1883 GroupKey pendingGroupKey = 1884 groupKeyBuilder.setGroupName(TEST_GROUP).setDownloaded(false).build(); 1885 GroupKey downloadedGroupKey = 1886 groupKeyBuilder.setGroupName(TEST_GROUP_2).setDownloaded(true).build(); 1887 1888 when(mockFileGroupsMetadata.read(pendingGroupKey)) 1889 .thenReturn(Futures.immediateFuture(pendingGroup)); 1890 when(mockFileGroupsMetadata.read(getPendingKey(downloadedGroupKey))) 1891 .thenReturn(Futures.immediateFuture(null)); 1892 when(mockFileGroupsMetadata.removeAllGroupsWithKeys(groupKeysCaptor.capture())) 1893 .thenReturn(Futures.immediateFuture(false)); 1894 1895 ExecutionException ex = 1896 assertThrows( 1897 ExecutionException.class, 1898 () -> 1899 fileGroupManager 1900 .removeFileGroups(ImmutableList.of(pendingGroupKey, downloadedGroupKey)) 1901 .get()); 1902 assertThat(ex).hasCauseThat().isInstanceOf(IOException.class); 1903 1904 verify(mockFileGroupsMetadata, times(0)).addStaleGroup(any()); 1905 verify(mockSharedFileManager, times(0)).cancelDownload(any()); 1906 verify(mockLogger, times(1)).logEventSampled(MddClientEvent.Code.EVENT_CODE_UNSPECIFIED); 1907 verify(mockFileGroupsMetadata, times(1)).removeAllGroupsWithKeys(any()); 1908 List<GroupKey> attemptedRemoveKeys = groupKeysCaptor.getValue(); 1909 assertThat(attemptedRemoveKeys).containsExactly(pendingGroupKey); 1910 } 1911 1912 @Test testRemoveFileGroups_whenRemoveDownloadedGroupFails_doesNotContinue()1913 public void testRemoveFileGroups_whenRemoveDownloadedGroupFails_doesNotContinue() 1914 throws Exception { 1915 // Use Mock FileGroupsMetadata to simulate failure scenario 1916 resetFileGroupManager(mockFileGroupsMetadata, mockSharedFileManager); 1917 1918 DataFileGroupInternal pendingGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1); 1919 DataFileGroupInternal downloadedGroup = 1920 MddTestUtil.createDataFileGroupInternal(TEST_GROUP_2, 1); 1921 1922 GroupKey.Builder groupKeyBuilder = 1923 GroupKey.newBuilder().setOwnerPackage(context.getPackageName()); 1924 GroupKey pendingGroupKey = 1925 groupKeyBuilder.setGroupName(TEST_GROUP).setDownloaded(false).build(); 1926 GroupKey downloadedGroupKey = 1927 groupKeyBuilder.setGroupName(TEST_GROUP_2).setDownloaded(true).build(); 1928 1929 // Mock variations of group key reads 1930 when(mockFileGroupsMetadata.read(pendingGroupKey)) 1931 .thenReturn(Futures.immediateFuture(pendingGroup)); 1932 when(mockFileGroupsMetadata.read(getPendingKey(downloadedGroupKey))) 1933 .thenReturn(Futures.immediateFuture(null)); 1934 when(mockFileGroupsMetadata.read(downloadedGroupKey)) 1935 .thenReturn(Futures.immediateFuture(downloadedGroup)); 1936 when(mockFileGroupsMetadata.read(getDownloadedKey(pendingGroupKey))) 1937 .thenReturn(Futures.immediateFuture(null)); 1938 1939 // Return true for pending groups removed, but false for downloaded groups 1940 when(mockFileGroupsMetadata.removeAllGroupsWithKeys(groupKeysCaptor.capture())) 1941 .thenReturn(Futures.immediateFuture(true)) 1942 .thenReturn(Futures.immediateFuture(false)); 1943 1944 ExecutionException ex = 1945 assertThrows( 1946 ExecutionException.class, 1947 () -> 1948 fileGroupManager 1949 .removeFileGroups(ImmutableList.of(pendingGroupKey, downloadedGroupKey)) 1950 .get()); 1951 assertThat(ex).hasCauseThat().isInstanceOf(IOException.class); 1952 1953 verify(mockFileGroupsMetadata, times(0)).addStaleGroup(any()); 1954 verify(mockSharedFileManager, times(0)).cancelDownload(any()); 1955 verify(mockLogger, times(1)).logEventSampled(MddClientEvent.Code.EVENT_CODE_UNSPECIFIED); 1956 verify(mockFileGroupsMetadata, times(2)).removeAllGroupsWithKeys(any()); 1957 List<List<GroupKey>> removeCallInvocations = groupKeysCaptor.getAllValues(); 1958 assertThat(removeCallInvocations.get(0)).containsExactly(pendingGroupKey); 1959 assertThat(removeCallInvocations.get(1)).containsExactly(downloadedGroupKey); 1960 } 1961 1962 @Test testRemoveFileGroups_whenAddingStaleGroupFails_doesNotContinue()1963 public void testRemoveFileGroups_whenAddingStaleGroupFails_doesNotContinue() throws Exception { 1964 // Use Mock FileGroupsMetadata to simulate failure scenario 1965 resetFileGroupManager(mockFileGroupsMetadata, mockSharedFileManager); 1966 1967 DataFileGroupInternal pendingGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1); 1968 DataFileGroupInternal downloadedGroup = 1969 MddTestUtil.createDataFileGroupInternal(TEST_GROUP_2, 1); 1970 1971 GroupKey.Builder groupKeyBuilder = 1972 GroupKey.newBuilder().setOwnerPackage(context.getPackageName()); 1973 GroupKey pendingGroupKey = 1974 groupKeyBuilder.setGroupName(TEST_GROUP).setDownloaded(false).build(); 1975 GroupKey downloadedGroupKey = 1976 groupKeyBuilder.setGroupName(TEST_GROUP_2).setDownloaded(true).build(); 1977 1978 // Mock read group key variations 1979 when(mockFileGroupsMetadata.read(pendingGroupKey)) 1980 .thenReturn(Futures.immediateFuture(pendingGroup)); 1981 when(mockFileGroupsMetadata.read(getPendingKey(downloadedGroupKey))) 1982 .thenReturn(Futures.immediateFuture(null)); 1983 when(mockFileGroupsMetadata.read(downloadedGroupKey)) 1984 .thenReturn(Futures.immediateFuture(downloadedGroup)); 1985 when(mockFileGroupsMetadata.read(getDownloadedKey(pendingGroupKey))) 1986 .thenReturn(Futures.immediateFuture(null)); 1987 1988 // Always return true for remove calls 1989 when(mockFileGroupsMetadata.removeAllGroupsWithKeys(groupKeysCaptor.capture())) 1990 .thenReturn(Futures.immediateFuture(true)); 1991 1992 // Fail when attempting to add a stale group 1993 when(mockFileGroupsMetadata.addStaleGroup(downloadedGroup)) 1994 .thenReturn(Futures.immediateFuture(false)); 1995 1996 ExecutionException ex = 1997 assertThrows( 1998 ExecutionException.class, 1999 () -> 2000 fileGroupManager 2001 .removeFileGroups(ImmutableList.of(pendingGroupKey, downloadedGroupKey)) 2002 .get()); 2003 assertThat(ex).hasCauseThat().isInstanceOf(AggregateException.class); 2004 2005 verify(mockFileGroupsMetadata, times(1)).addStaleGroup(downloadedGroup); 2006 verify(mockSharedFileManager, times(0)).cancelDownload(any()); 2007 verify(mockLogger, times(1)).logEventSampled(MddClientEvent.Code.EVENT_CODE_UNSPECIFIED); 2008 verify(mockFileGroupsMetadata, times(2)).removeAllGroupsWithKeys(any()); 2009 List<List<GroupKey>> removeCallInvocations = groupKeysCaptor.getAllValues(); 2010 assertThat(removeCallInvocations.get(0)).containsExactly(pendingGroupKey); 2011 assertThat(removeCallInvocations.get(1)).containsExactly(downloadedGroupKey); 2012 } 2013 2014 @Test testRemoveFileGroups_whenCancellingPendingDownloadFails_doesNotContinue()2015 public void testRemoveFileGroups_whenCancellingPendingDownloadFails_doesNotContinue() 2016 throws Exception { 2017 // Use Mock FileGroupsMetadata to simulate failure scenario 2018 resetFileGroupManager(fileGroupsMetadata, mockSharedFileManager); 2019 2020 DataFileGroupInternal pendingGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1); 2021 DataFileGroupInternal downloadedGroup = 2022 MddTestUtil.createDataFileGroupInternal(TEST_GROUP_2, 1); 2023 2024 GroupKey.Builder groupKeyBuilder = 2025 GroupKey.newBuilder().setOwnerPackage(context.getPackageName()); 2026 GroupKey pendingGroupKey = 2027 groupKeyBuilder.setGroupName(TEST_GROUP).setDownloaded(false).build(); 2028 GroupKey downloadedGroupKey = 2029 groupKeyBuilder.setGroupName(TEST_GROUP_2).setDownloaded(true).build(); 2030 2031 writePendingFileGroup(pendingGroupKey, pendingGroup); 2032 writeDownloadedFileGroup(downloadedGroupKey, downloadedGroup); 2033 writeSharedFiles( 2034 sharedFilesMetadata, pendingGroup, ImmutableList.of(FileStatus.DOWNLOAD_IN_PROGRESS)); 2035 writeSharedFiles( 2036 sharedFilesMetadata, downloadedGroup, ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE)); 2037 2038 // Fail when cancelling download 2039 NewFileKey[] pendingFileKeys = MddTestUtil.createFileKeysForDataFileGroupInternal(pendingGroup); 2040 when(mockSharedFileManager.cancelDownload(pendingFileKeys[0])) 2041 .thenReturn(Futures.immediateFailedFuture(new Exception("Test failure"))); 2042 2043 ExecutionException ex = 2044 assertThrows( 2045 ExecutionException.class, 2046 () -> 2047 fileGroupManager 2048 .removeFileGroups(ImmutableList.of(pendingGroupKey, downloadedGroupKey)) 2049 .get()); 2050 assertThat(ex).hasCauseThat().isInstanceOf(AggregateException.class); 2051 2052 assertThat(readPendingFileGroup(pendingGroupKey)).isNull(); 2053 assertThat(readDownloadedFileGroup(downloadedGroupKey)).isNull(); 2054 assertThat(fileGroupsMetadata.getAllFreshGroups().get()).isEmpty(); 2055 assertThat(fileGroupsMetadata.getAllStaleGroups().get()) 2056 .containsExactly( 2057 downloadedGroup.toBuilder() 2058 .setBookkeeping( 2059 downloadedGroup.getBookkeeping().toBuilder().setStaleExpirationDate(1).build()) 2060 .build()); 2061 } 2062 2063 @Test testRemoveFileGroups_whenMultipleVariantsExists_removesVariantsSpecified()2064 public void testRemoveFileGroups_whenMultipleVariantsExists_removesVariantsSpecified() 2065 throws Exception { 2066 // Create multiple variants of a group (default (empty), en, fr) and remove the default (empty) 2067 // variant and en keys. Ensure that only the fr group remains. 2068 GroupKey defaultGroupKey = 2069 GroupKey.newBuilder() 2070 .setGroupName(TEST_GROUP) 2071 .setOwnerPackage(context.getPackageName()) 2072 .build(); 2073 GroupKey enGroupKey = defaultGroupKey.toBuilder().setVariantId("en").build(); 2074 GroupKey frGroupKey = defaultGroupKey.toBuilder().setVariantId("fr").build(); 2075 2076 DataFileGroupInternal defaultFileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1); 2077 DataFileGroupInternal enFileGroup = defaultFileGroup.toBuilder().setVariantId("en").build(); 2078 DataFileGroupInternal frFileGroup = defaultFileGroup.toBuilder().setVariantId("fr").build(); 2079 2080 writePendingFileGroup(getPendingKey(defaultGroupKey), defaultFileGroup); 2081 writePendingFileGroup(getPendingKey(enGroupKey), enFileGroup); 2082 writePendingFileGroup(getPendingKey(frGroupKey), frFileGroup); 2083 2084 writeSharedFiles( 2085 sharedFilesMetadata, defaultFileGroup, ImmutableList.of(FileStatus.DOWNLOAD_IN_PROGRESS)); 2086 writeSharedFiles( 2087 sharedFilesMetadata, enFileGroup, ImmutableList.of(FileStatus.DOWNLOAD_IN_PROGRESS)); 2088 writeSharedFiles( 2089 sharedFilesMetadata, frFileGroup, ImmutableList.of(FileStatus.DOWNLOAD_IN_PROGRESS)); 2090 2091 // Assert that all file groups share the same file even through the variants are different 2092 assertThat(sharedFilesMetadata.getAllFileKeys().get()).hasSize(1); 2093 2094 { 2095 // Perfrom removal once and check that the correct groups get removed 2096 fileGroupManager.removeFileGroups(ImmutableList.of(defaultGroupKey, enGroupKey)).get(); 2097 2098 assertThat(fileGroupsMetadata.getAllGroupKeys().get()) 2099 .comparingElementsUsing(GROUP_KEY_TO_VARIANT) 2100 .containsExactly("fr"); 2101 assertThat(fileGroupsMetadata.getAllFreshGroups().get()) 2102 .comparingElementsUsing(KEY_GROUP_PAIR_TO_VARIANT) 2103 .containsExactly("fr"); 2104 2105 assertThat(sharedFilesMetadata.getAllFileKeys().get()).hasSize(1); 2106 } 2107 2108 { 2109 // Perform remove again and verify that there is no change in state 2110 fileGroupManager.removeFileGroups(ImmutableList.of(defaultGroupKey, enGroupKey)).get(); 2111 2112 assertThat(fileGroupsMetadata.getAllGroupKeys().get()) 2113 .comparingElementsUsing(GROUP_KEY_TO_VARIANT) 2114 .containsExactly("fr"); 2115 assertThat(fileGroupsMetadata.getAllFreshGroups().get()) 2116 .comparingElementsUsing(KEY_GROUP_PAIR_TO_VARIANT) 2117 .containsExactly("fr"); 2118 2119 assertThat(sharedFilesMetadata.getAllFileKeys().get()).hasSize(1); 2120 } 2121 } 2122 2123 @Test testGetDownloadedGroup()2124 public void testGetDownloadedGroup() throws Exception { 2125 assertThat(fileGroupManager.getFileGroup(testKey, true).get()).isNull(); 2126 2127 DataFileGroupInternal dataFileGroup = 2128 MddTestUtil.createDownloadedDataFileGroupInternal(TEST_GROUP, 2); 2129 writeDownloadedFileGroup(testKey, dataFileGroup); 2130 2131 DataFileGroupInternal downloadedGroup = fileGroupManager.getFileGroup(testKey, true).get(); 2132 MddTestUtil.assertMessageEquals(dataFileGroup, downloadedGroup); 2133 } 2134 2135 @Test testGetDownloadedGroup_whenMultipleVariantsExists_getsCorrectGroup()2136 public void testGetDownloadedGroup_whenMultipleVariantsExists_getsCorrectGroup() 2137 throws Exception { 2138 GroupKey defaultGroupKey = 2139 GroupKey.newBuilder() 2140 .setGroupName(TEST_GROUP) 2141 .setOwnerPackage(context.getPackageName()) 2142 .build(); 2143 GroupKey enGroupKey = defaultGroupKey.toBuilder().setVariantId("en").build(); 2144 2145 // Initially, assert that groups don't exist 2146 assertThat(fileGroupManager.getFileGroup(defaultGroupKey, true).get()).isNull(); 2147 assertThat(fileGroupManager.getFileGroup(enGroupKey, true).get()).isNull(); 2148 2149 // Create groups and write them 2150 DataFileGroupInternal defaultFileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1); 2151 DataFileGroupInternal enFileGroup = defaultFileGroup.toBuilder().setVariantId("en").build(); 2152 2153 writeDownloadedFileGroup(getDownloadedKey(defaultGroupKey), defaultFileGroup); 2154 writeDownloadedFileGroup(getDownloadedKey(enGroupKey), enFileGroup); 2155 2156 // Assert the correct group is returned for each key 2157 DataFileGroupInternal groupForDefaultKey = 2158 fileGroupManager.getFileGroup(defaultGroupKey, true).get(); 2159 MddTestUtil.assertMessageEquals(defaultFileGroup, groupForDefaultKey); 2160 2161 DataFileGroupInternal groupForEnKey = fileGroupManager.getFileGroup(enGroupKey, true).get(); 2162 MddTestUtil.assertMessageEquals(enFileGroup, groupForEnKey); 2163 } 2164 2165 @Test testGetPendingGroup()2166 public void testGetPendingGroup() throws Exception { 2167 assertThat(fileGroupManager.getFileGroup(testKey, false).get()).isNull(); 2168 2169 DataFileGroupInternal dataFileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2); 2170 writePendingFileGroup(testKey, dataFileGroup); 2171 2172 DataFileGroupInternal pendingGroup = fileGroupManager.getFileGroup(testKey, false).get(); 2173 MddTestUtil.assertMessageEquals(dataFileGroup, pendingGroup); 2174 } 2175 2176 @Test testGetPendingGroup_whenMultipleVariantsExists_getsCorrectGroup()2177 public void testGetPendingGroup_whenMultipleVariantsExists_getsCorrectGroup() throws Exception { 2178 GroupKey defaultGroupKey = 2179 GroupKey.newBuilder() 2180 .setGroupName(TEST_GROUP) 2181 .setOwnerPackage(context.getPackageName()) 2182 .build(); 2183 GroupKey enGroupKey = defaultGroupKey.toBuilder().setVariantId("en").build(); 2184 2185 // Initially, assert that groups don't exist 2186 assertThat(fileGroupManager.getFileGroup(defaultGroupKey, false).get()).isNull(); 2187 assertThat(fileGroupManager.getFileGroup(enGroupKey, false).get()).isNull(); 2188 2189 // Create groups and write them 2190 DataFileGroupInternal defaultFileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1); 2191 DataFileGroupInternal enFileGroup = defaultFileGroup.toBuilder().setVariantId("en").build(); 2192 2193 writePendingFileGroup(getPendingKey(defaultGroupKey), defaultFileGroup); 2194 writePendingFileGroup(getPendingKey(enGroupKey), enFileGroup); 2195 2196 // Assert the correct group is returned for each key 2197 DataFileGroupInternal groupForDefaultKey = 2198 fileGroupManager.getFileGroup(defaultGroupKey, false).get(); 2199 MddTestUtil.assertMessageEquals(defaultFileGroup, groupForDefaultKey); 2200 2201 DataFileGroupInternal groupForEnKey = fileGroupManager.getFileGroup(enGroupKey, false).get(); 2202 MddTestUtil.assertMessageEquals(enFileGroup, groupForEnKey); 2203 } 2204 2205 @Test testSetGroupActivation_deactivationRemovesGroupsRequiringActivation()2206 public void testSetGroupActivation_deactivationRemovesGroupsRequiringActivation() 2207 throws Exception { 2208 flags.enableDelayedDownload = Optional.of(true); 2209 2210 // Create 2 groups, one of which requires device side activation. 2211 DataFileGroupInternal.Builder fileGroup1 = 2212 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2).toBuilder(); 2213 DownloadConditions.Builder downloadConditions = 2214 DownloadConditions.newBuilder() 2215 .setActivatingCondition(ActivatingCondition.DEVICE_ACTIVATED); 2216 fileGroup1.setDownloadConditions(downloadConditions); 2217 2218 DataFileGroupInternal fileGroup2 = MddTestUtil.createDataFileGroupInternal(TEST_GROUP_2, 3); 2219 2220 // Activate both group keys and add groups to FileGroupManager. 2221 assertThat(fileGroupManager.setGroupActivation(testKey, true).get()).isTrue(); 2222 2223 assertThat(fileGroupManager.addGroupForDownload(testKey, fileGroup1.build()).get()).isTrue(); 2224 2225 assertThat(fileGroupManager.setGroupActivation(testKey2, true).get()).isTrue(); 2226 2227 assertThat(fileGroupManager.addGroupForDownload(testKey2, fileGroup2).get()).isTrue(); 2228 2229 // Add a downloaded version of the second group, that requires device side activation. 2230 DataFileGroupInternal downloadedfileGroup2 = 2231 MddTestUtil.createDownloadedDataFileGroupInternal(TEST_GROUP_2, 1); 2232 downloadConditions = DownloadConditions.newBuilder(); 2233 downloadedfileGroup2 = 2234 downloadedfileGroup2.toBuilder() 2235 .setDownloadConditions( 2236 downloadConditions.setActivatingCondition(ActivatingCondition.DEVICE_ACTIVATED)) 2237 .build(); 2238 writeDownloadedFileGroup(testKey2, downloadedfileGroup2); 2239 2240 // Deactivate both group keys, and check that the groups that required activation are deleted. 2241 assertThat(fileGroupManager.setGroupActivation(testKey, false).get()).isTrue(); 2242 // Setting group activation to false will only remove groups that have 2243 // ActivatingCondition.DEVICE_ACTIVATED. So the pending version will remain, while the 2244 // downloaded one is removed. 2245 assertThat(fileGroupManager.setGroupActivation(testKey2, false).get()).isTrue(); 2246 2247 assertThat(readPendingFileGroup(testKey)).isNull(); 2248 assertThat(readPendingFileGroup(testKey2)).isNotNull(); 2249 assertThat(readDownloadedFileGroup(testKey2)).isNull(); 2250 } 2251 2252 @Test testImportFilesIntoFileGroup_whenExistingGroupDoesNotExist_fails()2253 public void testImportFilesIntoFileGroup_whenExistingGroupDoesNotExist_fails() throws Exception { 2254 DataFile inlineFile = 2255 DataFile.newBuilder() 2256 .setFileId("inline-file") 2257 .setChecksum("abc") 2258 .setUrlToDownload("inlinefile:sha1:abc") 2259 .build(); 2260 ImmutableList<DataFile> updatedDataFileList = ImmutableList.of(inlineFile); 2261 2262 GroupKey groupKey = GroupKey.newBuilder().setGroupName("non-existing-group").build(); 2263 2264 ExecutionException ex = 2265 assertThrows( 2266 ExecutionException.class, 2267 () -> 2268 fileGroupManager 2269 .importFilesIntoFileGroup( 2270 groupKey, 2271 /* buildId= */ 0, 2272 /* variantId= */ "", 2273 updatedDataFileList, 2274 /* inlineFileMap= */ ImmutableMap.of(), 2275 /* customPropertyOptional= */ Optional.absent(), 2276 noCustomValidation()) 2277 .get()); 2278 2279 assertThat(ex).hasCauseThat().isInstanceOf(DownloadException.class); 2280 DownloadException dex = (DownloadException) ex.getCause(); 2281 assertThat(dex.getDownloadResultCode()).isEqualTo(DownloadResultCode.GROUP_NOT_FOUND_ERROR); 2282 } 2283 2284 @Test testImportFilesIntoFileGroup_whenExistingPendingGroupDoesNotMatchIdentifiers_fails()2285 public void testImportFilesIntoFileGroup_whenExistingPendingGroupDoesNotMatchIdentifiers_fails() 2286 throws Exception { 2287 // Set up existing pending file group 2288 DataFileGroupInternal existingFileGroup = 2289 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 0).toBuilder() 2290 .addFile( 2291 DataFile.newBuilder() 2292 .setFileId("inline-file") 2293 .setChecksum("abc") 2294 .setUrlToDownload("inlinefile:sha1:abc") 2295 .build()) 2296 .build(); 2297 GroupKey groupKey = GroupKey.newBuilder().setGroupName(TEST_GROUP).build(); 2298 2299 writePendingFileGroup(getPendingKey(groupKey), existingFileGroup); 2300 writeSharedFiles( 2301 sharedFilesMetadata, existingFileGroup, ImmutableList.of(FileStatus.DOWNLOAD_IN_PROGRESS)); 2302 2303 ExecutionException ex = 2304 assertThrows( 2305 ExecutionException.class, 2306 () -> 2307 fileGroupManager 2308 .importFilesIntoFileGroup( 2309 groupKey, 2310 /* buildId= */ 1, 2311 /* variantId= */ "", 2312 /* updatedDataFileList= */ ImmutableList.of(), 2313 /* inlineFileMap= */ ImmutableMap.of(), 2314 /* customPropertyOptional= */ Optional.absent(), 2315 noCustomValidation()) 2316 .get()); 2317 2318 assertThat(ex).hasCauseThat().isInstanceOf(DownloadException.class); 2319 DownloadException dex = (DownloadException) ex.getCause(); 2320 assertThat(dex.getDownloadResultCode()).isEqualTo(DownloadResultCode.GROUP_NOT_FOUND_ERROR); 2321 } 2322 2323 @Test 2324 public void testImportFilesIntoFileGroup_whenExistingDownloadedGroupDoesNotMatchIdentifiers_fails()2325 testImportFilesIntoFileGroup_whenExistingDownloadedGroupDoesNotMatchIdentifiers_fails() 2326 throws Exception { 2327 // Set up existing downloaded file group 2328 DataFileGroupInternal existingFileGroup = 2329 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 0).toBuilder() 2330 .addFile( 2331 DataFile.newBuilder() 2332 .setFileId("inline-file") 2333 .setChecksum("abc") 2334 .setUrlToDownload("inlinefile:sha1:abc") 2335 .build()) 2336 .build(); 2337 GroupKey groupKey = GroupKey.newBuilder().setGroupName(TEST_GROUP).build(); 2338 2339 writeDownloadedFileGroup(getDownloadedKey(groupKey), existingFileGroup); 2340 writeSharedFiles( 2341 sharedFilesMetadata, existingFileGroup, ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE)); 2342 2343 ExecutionException ex = 2344 assertThrows( 2345 ExecutionException.class, 2346 () -> 2347 fileGroupManager 2348 .importFilesIntoFileGroup( 2349 groupKey, 2350 /* buildId= */ 0, 2351 /* variantId= */ "testvariant", 2352 /* updatedDataFileList= */ ImmutableList.of(), 2353 /* inlineFileMap= */ ImmutableMap.of(), 2354 /* customPropertyOptional= */ Optional.absent(), 2355 noCustomValidation()) 2356 .get()); 2357 2358 assertThat(ex).hasCauseThat().isInstanceOf(DownloadException.class); 2359 DownloadException dex = (DownloadException) ex.getCause(); 2360 assertThat(dex.getDownloadResultCode()).isEqualTo(DownloadResultCode.GROUP_NOT_FOUND_ERROR); 2361 } 2362 2363 @Test testImportFilesIntoFileGroup_whenBuildIdDoesNotMatch_fails()2364 public void testImportFilesIntoFileGroup_whenBuildIdDoesNotMatch_fails() throws Exception { 2365 // Set up existing pending/downloaded groups and check that they do not match due to build ID 2366 // differences. 2367 2368 // Any can pack proto messages only, so use StringValue. 2369 Any customProperty = 2370 Any.parseFrom( 2371 StringValue.of("testCustomProperty").toByteString(), 2372 ExtensionRegistryLite.getEmptyRegistry()); 2373 DataFileGroupInternal existingDownloadedFileGroup = 2374 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 0).toBuilder() 2375 .setBuildId(1) 2376 .setVariantId("testvariant") 2377 .setCustomProperty(customProperty) 2378 .build(); 2379 DataFileGroupInternal existingPendingFileGroup = 2380 existingDownloadedFileGroup.toBuilder().setBuildId(2).build(); 2381 2382 GroupKey groupKey = GroupKey.newBuilder().setGroupName(TEST_GROUP).build(); 2383 2384 writeDownloadedFileGroup(getDownloadedKey(groupKey), existingDownloadedFileGroup); 2385 writePendingFileGroup(getPendingKey(groupKey), existingPendingFileGroup); 2386 2387 ExecutionException ex = 2388 assertThrows( 2389 ExecutionException.class, 2390 () -> 2391 fileGroupManager 2392 .importFilesIntoFileGroup( 2393 groupKey, 2394 /* buildId= */ 3, 2395 /* variantId= */ "testvariant", 2396 /* updatedDataFileList= */ ImmutableList.of(), 2397 /* inlineFileMap= */ ImmutableMap.of(), 2398 /* customPropertyOptional= */ Optional.of(customProperty), 2399 noCustomValidation()) 2400 .get()); 2401 2402 assertThat(ex).hasCauseThat().isInstanceOf(DownloadException.class); 2403 DownloadException dex = (DownloadException) ex.getCause(); 2404 assertThat(dex.getDownloadResultCode()).isEqualTo(DownloadResultCode.GROUP_NOT_FOUND_ERROR); 2405 } 2406 2407 @Test testImportFilesIntoFileGroup_whenVariantIdDoesNotMatch_fails()2408 public void testImportFilesIntoFileGroup_whenVariantIdDoesNotMatch_fails() throws Exception { 2409 // Set up existing pending/downloaded groups and check that they do not match due to variant ID 2410 // differences. 2411 2412 // Any can pack proto messages only, so use StringValue. 2413 Any customProperty = 2414 Any.parseFrom( 2415 StringValue.of("testCustomProperty").toByteString(), 2416 ExtensionRegistryLite.getEmptyRegistry()); 2417 DataFileGroupInternal existingDownloadedFileGroup = 2418 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 0).toBuilder() 2419 .setBuildId(1) 2420 .setVariantId("testvariant") 2421 .setCustomProperty(customProperty) 2422 .build(); 2423 DataFileGroupInternal existingPendingFileGroup = 2424 existingDownloadedFileGroup.toBuilder().setVariantId("testvariant2").build(); 2425 2426 GroupKey groupKey = GroupKey.newBuilder().setGroupName(TEST_GROUP).build(); 2427 2428 writeDownloadedFileGroup(getDownloadedKey(groupKey), existingDownloadedFileGroup); 2429 writePendingFileGroup(getPendingKey(groupKey), existingPendingFileGroup); 2430 2431 ExecutionException ex = 2432 assertThrows( 2433 ExecutionException.class, 2434 () -> 2435 fileGroupManager 2436 .importFilesIntoFileGroup( 2437 groupKey, 2438 /* buildId= */ 1, 2439 /* variantId= */ "testvariant3", 2440 /* updatedDataFileList= */ ImmutableList.of(), 2441 /* inlineFileMap= */ ImmutableMap.of(), 2442 /* customPropertyOptional= */ Optional.of(customProperty), 2443 noCustomValidation()) 2444 .get()); 2445 2446 assertThat(ex).hasCauseThat().isInstanceOf(DownloadException.class); 2447 DownloadException dex = (DownloadException) ex.getCause(); 2448 assertThat(dex.getDownloadResultCode()).isEqualTo(DownloadResultCode.GROUP_NOT_FOUND_ERROR); 2449 } 2450 2451 @Test testImportFilesIntoFileGroup_whenCustomPropertyDoesNotMatch_whenDueToMismatch_fails()2452 public void testImportFilesIntoFileGroup_whenCustomPropertyDoesNotMatch_whenDueToMismatch_fails() 2453 throws Exception { 2454 // Set up existing pending/downloaded groups and check that they do not match due to custom 2455 // property differences. 2456 2457 // Any can pack proto messages only, so use StringValue. 2458 Any downloadedCustomProperty = 2459 Any.parseFrom( 2460 StringValue.of("testDownloadedCustomProperty").toByteString(), 2461 ExtensionRegistryLite.getEmptyRegistry()); 2462 Any pendingCustomProperty = 2463 Any.parseFrom( 2464 StringValue.of("testPendingCustomProperty").toByteString(), 2465 ExtensionRegistryLite.getEmptyRegistry()); 2466 Any mismatchedCustomProperty = 2467 Any.parseFrom( 2468 StringValue.of("testMismatcheCustomProperty").toByteString(), 2469 ExtensionRegistryLite.getEmptyRegistry()); 2470 2471 DataFileGroupInternal existingDownloadedFileGroup = 2472 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 0).toBuilder() 2473 .setBuildId(1) 2474 .setVariantId("testvariant") 2475 .setCustomProperty(downloadedCustomProperty) 2476 .build(); 2477 DataFileGroupInternal existingPendingFileGroup = 2478 existingDownloadedFileGroup.toBuilder().setCustomProperty(pendingCustomProperty).build(); 2479 2480 GroupKey groupKey = GroupKey.newBuilder().setGroupName(TEST_GROUP).build(); 2481 2482 writeDownloadedFileGroup(getDownloadedKey(groupKey), existingDownloadedFileGroup); 2483 writePendingFileGroup(getPendingKey(groupKey), existingPendingFileGroup); 2484 2485 ExecutionException ex = 2486 assertThrows( 2487 ExecutionException.class, 2488 () -> 2489 fileGroupManager 2490 .importFilesIntoFileGroup( 2491 groupKey, 2492 /* buildId= */ 1, 2493 /* variantId= */ "testvariant", 2494 /* updatedDataFileList= */ ImmutableList.of(), 2495 /* inlineFileMap= */ ImmutableMap.of(), 2496 /* customPropertyOptional= */ Optional.of(mismatchedCustomProperty), 2497 noCustomValidation()) 2498 .get()); 2499 2500 assertThat(ex).hasCauseThat().isInstanceOf(DownloadException.class); 2501 DownloadException dex = (DownloadException) ex.getCause(); 2502 assertThat(dex.getDownloadResultCode()).isEqualTo(DownloadResultCode.GROUP_NOT_FOUND_ERROR); 2503 } 2504 2505 @Test 2506 public void testImportFilesIntoFileGroup_whenCustomPropertyDoesNotMatch_whenDueToBeingAbsent_fails()2507 testImportFilesIntoFileGroup_whenCustomPropertyDoesNotMatch_whenDueToBeingAbsent_fails() 2508 throws Exception { 2509 // Set up existing pending/downloaded groups and check that they do not match due to custom 2510 // property differences. 2511 2512 // Any can pack proto messages only, so use StringValue. 2513 Any downloadedCustomProperty = 2514 Any.parseFrom( 2515 StringValue.of("testDownloadedCustomProperty").toByteString(), 2516 ExtensionRegistryLite.getEmptyRegistry()); 2517 Any pendingCustomProperty = 2518 Any.parseFrom( 2519 StringValue.of("testPendingCustomProperty").toByteString(), 2520 ExtensionRegistryLite.getEmptyRegistry()); 2521 2522 DataFileGroupInternal existingDownloadedFileGroup = 2523 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 0).toBuilder() 2524 .setBuildId(1) 2525 .setVariantId("testvariant") 2526 .setCustomProperty(downloadedCustomProperty) 2527 .build(); 2528 DataFileGroupInternal existingPendingFileGroup = 2529 existingDownloadedFileGroup.toBuilder().setCustomProperty(pendingCustomProperty).build(); 2530 2531 GroupKey groupKey = GroupKey.newBuilder().setGroupName(TEST_GROUP).build(); 2532 2533 writeDownloadedFileGroup(getDownloadedKey(groupKey), existingDownloadedFileGroup); 2534 writePendingFileGroup(getPendingKey(groupKey), existingPendingFileGroup); 2535 2536 ExecutionException ex = 2537 assertThrows( 2538 ExecutionException.class, 2539 () -> 2540 fileGroupManager 2541 .importFilesIntoFileGroup( 2542 groupKey, 2543 /* buildId= */ 1, 2544 /* variantId= */ "testvariant", 2545 /* updatedDataFileList= */ ImmutableList.of(), 2546 /* inlineFileMap= */ ImmutableMap.of(), 2547 /* customPropertyOptional= */ Optional.absent(), 2548 noCustomValidation()) 2549 .get()); 2550 2551 assertThat(ex).hasCauseThat().isInstanceOf(DownloadException.class); 2552 DownloadException dex = (DownloadException) ex.getCause(); 2553 assertThat(dex.getDownloadResultCode()).isEqualTo(DownloadResultCode.GROUP_NOT_FOUND_ERROR); 2554 } 2555 2556 @Test testImportFilesIntoFileGroup_whenUnableToReserveNewFiles_fails()2557 public void testImportFilesIntoFileGroup_whenUnableToReserveNewFiles_fails() throws Exception { 2558 // Reset with mock SharedFileManager to force failures 2559 resetFileGroupManager(fileGroupsMetadata, mockSharedFileManager); 2560 2561 // Create existing file group and a new file group that will add an inline file (which needs to 2562 // be reserved in SharedFileManager). 2563 DataFileGroupInternal existingFileGroup = 2564 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1); 2565 ImmutableList<DataFile> updatedDataFileList = 2566 ImmutableList.of( 2567 DataFile.newBuilder() 2568 .setFileId("inline-file") 2569 .setChecksum("abc") 2570 .setUrlToDownload("inlinefile:sha1:abc") 2571 .build()); 2572 2573 GroupKey groupKey = GroupKey.newBuilder().setGroupName(TEST_GROUP).build(); 2574 2575 writeDownloadedFileGroup(getDownloadedKey(groupKey), existingFileGroup); 2576 writeSharedFiles( 2577 sharedFilesMetadata, existingFileGroup, ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE)); 2578 2579 NewFileKey newInlineFileKey = 2580 SharedFilesMetadata.createKeyFromDataFile( 2581 updatedDataFileList.get(0), existingFileGroup.getAllowedReadersEnum()); 2582 NewFileKey existingFileKey = 2583 SharedFilesMetadata.createKeyFromDataFile( 2584 existingFileGroup.getFile(0), existingFileGroup.getAllowedReadersEnum()); 2585 2586 when(mockSharedFileManager.reserveFileEntry(newInlineFileKey)) 2587 .thenReturn(Futures.immediateFuture(false)); 2588 when(mockSharedFileManager.reserveFileEntry(existingFileKey)) 2589 .thenReturn(Futures.immediateFuture(true)); 2590 2591 ExecutionException ex = 2592 assertThrows( 2593 ExecutionException.class, 2594 () -> 2595 fileGroupManager 2596 .importFilesIntoFileGroup( 2597 groupKey, 2598 /* buildId= */ 0, 2599 /* variantId= */ "", 2600 /* updatedDataFileList= */ updatedDataFileList, 2601 /* inlineFileMap= */ ImmutableMap.of(), 2602 /* customPropertyOptional= */ Optional.absent(), 2603 noCustomValidation()) 2604 .get()); 2605 assertThat(ex).hasCauseThat().isInstanceOf(DownloadException.class); 2606 DownloadException dex = (DownloadException) ex.getCause(); 2607 assertThat(dex.getDownloadResultCode()) 2608 .isEqualTo(DownloadResultCode.UNABLE_TO_RESERVE_FILE_ENTRY); 2609 } 2610 2611 @Test 2612 public void testImportFilesIntoFileGroup_whenNoNewInlineFilesSpecifiedAndFilesDownloaded_completes()2613 testImportFilesIntoFileGroup_whenNoNewInlineFilesSpecifiedAndFilesDownloaded_completes() 2614 throws Exception { 2615 // Create a group that has 1 standard file and 1 inline file, both downloaded 2616 DataFileGroupInternal dataFileGroup = 2617 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1).toBuilder() 2618 .addFile( 2619 DataFile.newBuilder() 2620 .setFileId("inline-file") 2621 .setChecksum("abc") 2622 .setUrlToDownload("inlinefile:sha1:abc") 2623 .build()) 2624 .build(); 2625 ImmutableMap<String, FileSource> inlineFileMap = 2626 ImmutableMap.of( 2627 "inline-file", FileSource.ofByteString(ByteString.copyFromUtf8("TEST_CONTENT"))); 2628 2629 GroupKey groupKey = GroupKey.newBuilder().setGroupName(TEST_GROUP).build(); 2630 2631 writeDownloadedFileGroup(getDownloadedKey(groupKey), dataFileGroup); 2632 writeSharedFiles( 2633 sharedFilesMetadata, 2634 dataFileGroup, 2635 ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE, FileStatus.DOWNLOAD_COMPLETE)); 2636 2637 fileGroupManager 2638 .importFilesIntoFileGroup( 2639 groupKey, 2640 /* buildId= */ 0, 2641 /* variantId= */ "", 2642 /* updatedDataFileList= */ ImmutableList.of(), 2643 inlineFileMap, 2644 /* customPropertyOptional= */ Optional.absent(), 2645 noCustomValidation()) 2646 .get(); 2647 2648 // Since no new files were specified, the group should remain the same (downloaded). 2649 DataFileGroupInternal downloadedFileGroup = 2650 fileGroupsMetadata.read(getDownloadedKey(groupKey)).get(); 2651 assertThat(downloadedFileGroup.getGroupName()).isEqualTo(dataFileGroup.getGroupName()); 2652 assertThat(downloadedFileGroup.getBuildId()).isEqualTo(dataFileGroup.getBuildId()); 2653 assertThat(downloadedFileGroup.getVariantId()).isEqualTo(dataFileGroup.getVariantId()); 2654 assertThat(downloadedFileGroup.getFileList()) 2655 .containsExactlyElementsIn(dataFileGroup.getFileList()); 2656 } 2657 2658 @Test testImportFilesIntoFileGroup_whenNoNewFilesSpecifiedAndFilesPending_completes()2659 public void testImportFilesIntoFileGroup_whenNoNewFilesSpecifiedAndFilesPending_completes() 2660 throws Exception { 2661 DataFileGroupInternal dataFileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1); 2662 2663 GroupKey groupKey = GroupKey.newBuilder().setGroupName(TEST_GROUP).build(); 2664 2665 writePendingFileGroup(getPendingKey(groupKey), dataFileGroup); 2666 writeSharedFiles( 2667 sharedFilesMetadata, dataFileGroup, ImmutableList.of(FileStatus.DOWNLOAD_IN_PROGRESS)); 2668 2669 fileGroupManager 2670 .importFilesIntoFileGroup( 2671 groupKey, 2672 /* buildId= */ 0, 2673 /* variantId= */ "", 2674 /* updatedDataFileList= */ ImmutableList.of(), 2675 /* inlineFileMap= */ ImmutableMap.of(), 2676 /* customPropertyOptional= */ Optional.absent(), 2677 noCustomValidation()) 2678 .get(); 2679 2680 // Since no new files were specified, the group should remain the same (pending). 2681 DataFileGroupInternal pendingFileGroup = fileGroupsMetadata.read(getPendingKey(groupKey)).get(); 2682 assertThat(pendingFileGroup.getGroupName()).isEqualTo(dataFileGroup.getGroupName()); 2683 assertThat(pendingFileGroup.getBuildId()).isEqualTo(dataFileGroup.getBuildId()); 2684 assertThat(pendingFileGroup.getVariantId()).isEqualTo(dataFileGroup.getVariantId()); 2685 assertThat(pendingFileGroup.getFileList()).isEqualTo(dataFileGroup.getFileList()); 2686 } 2687 2688 @Test testImportFilesIntoFileGroup_whenImportingInlineFileAndPending_mergesGroup()2689 public void testImportFilesIntoFileGroup_whenImportingInlineFileAndPending_mergesGroup() 2690 throws Exception { 2691 // Set up an existing pending file group and a new inline file to merge 2692 DataFileGroupInternal existingPendingFileGroup = 2693 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1); 2694 ImmutableList<DataFile> updatedDataFileList = 2695 ImmutableList.of( 2696 DataFile.newBuilder() 2697 .setFileId("inline-file") 2698 .setChecksum("abc") 2699 .setUrlToDownload("inlinefile:sha1:abc") 2700 .build()); 2701 ImmutableMap<String, FileSource> inlineFileMap = 2702 ImmutableMap.of( 2703 "inline-file", FileSource.ofByteString(ByteString.copyFromUtf8("TEST_CONTENT"))); 2704 2705 GroupKey groupKey = GroupKey.newBuilder().setGroupName(TEST_GROUP).build(); 2706 2707 writePendingFileGroup(getPendingKey(groupKey), existingPendingFileGroup); 2708 writeSharedFiles( 2709 sharedFilesMetadata, 2710 existingPendingFileGroup, 2711 ImmutableList.of(FileStatus.DOWNLOAD_IN_PROGRESS)); 2712 2713 // TODO: remove once SFM can perform import 2714 // write inline file as downloaded so FGM can find it 2715 NewFileKey newInlineFileKey = 2716 SharedFilesMetadata.createKeyFromDataFile( 2717 updatedDataFileList.get(0), existingPendingFileGroup.getAllowedReadersEnum()); 2718 SharedFile newInlineSharedFile = 2719 SharedFile.newBuilder() 2720 .setFileName(updatedDataFileList.get(0).getFileId()) 2721 .setFileStatus(FileStatus.DOWNLOAD_COMPLETE) 2722 .build(); 2723 sharedFilesMetadata.write(newInlineFileKey, newInlineSharedFile).get(); 2724 2725 fileGroupManager 2726 .importFilesIntoFileGroup( 2727 groupKey, 2728 /* buildId= */ 0, 2729 /* variantId= */ "", 2730 updatedDataFileList, 2731 inlineFileMap, 2732 /* customPropertyOptional= */ Optional.absent(), 2733 noCustomValidation()) 2734 .get(); 2735 2736 // Check that resulting file group remains pending, but should have both files merged together 2737 DataFileGroupInternal pendingFileGroupAfterImport = 2738 fileGroupsMetadata.read(getPendingKey(groupKey)).get(); 2739 assertThat(pendingFileGroupAfterImport.getFileCount()).isEqualTo(2); 2740 assertThat(pendingFileGroupAfterImport.getFileList()) 2741 .containsExactly(existingPendingFileGroup.getFile(0), updatedDataFileList.get(0)); 2742 } 2743 2744 @Test testImportFilesIntoFileGroup_whenImportingInlineFileAndDownloaded_mergesGroup()2745 public void testImportFilesIntoFileGroup_whenImportingInlineFileAndDownloaded_mergesGroup() 2746 throws Exception { 2747 // Set up an existing downloaded file group and a new file group with an inline file to merge 2748 DataFileGroupInternal existingDownloadedFileGroup = 2749 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1); 2750 ImmutableList<DataFile> updatedDataFileList = 2751 ImmutableList.of( 2752 DataFile.newBuilder() 2753 .setFileId("inline-file") 2754 .setChecksum("abc") 2755 .setUrlToDownload("inlinefile:sha1:abc") 2756 .build()); 2757 ImmutableMap<String, FileSource> inlineFileMap = 2758 ImmutableMap.of( 2759 "inline-file", FileSource.ofByteString(ByteString.copyFromUtf8("TEST_CONTENT"))); 2760 2761 GroupKey groupKey = GroupKey.newBuilder().setGroupName(TEST_GROUP).build(); 2762 2763 writeDownloadedFileGroup(getDownloadedKey(groupKey), existingDownloadedFileGroup); 2764 writeSharedFiles( 2765 sharedFilesMetadata, 2766 existingDownloadedFileGroup, 2767 ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE)); 2768 2769 // TODO: remove once SFM can perform import 2770 // write inline file as downloaded so FGM can find it 2771 NewFileKey newInlineFileKey = 2772 SharedFilesMetadata.createKeyFromDataFile( 2773 updatedDataFileList.get(0), existingDownloadedFileGroup.getAllowedReadersEnum()); 2774 SharedFile newInlineSharedFile = 2775 SharedFile.newBuilder() 2776 .setFileName(updatedDataFileList.get(0).getFileId()) 2777 .setFileStatus(FileStatus.DOWNLOAD_COMPLETE) 2778 .build(); 2779 sharedFilesMetadata.write(newInlineFileKey, newInlineSharedFile).get(); 2780 2781 fileGroupManager 2782 .importFilesIntoFileGroup( 2783 groupKey, 2784 /* buildId= */ 0, 2785 /* variantId= */ "", 2786 updatedDataFileList, 2787 inlineFileMap, 2788 /* customPropertyOptional= */ Optional.absent(), 2789 noCustomValidation()) 2790 .get(); 2791 2792 // Check that resulting file group is downloaded, but should have both files merged together 2793 DataFileGroupInternal downloadedFileGroupAfterImport = 2794 fileGroupsMetadata.read(getDownloadedKey(groupKey)).get(); 2795 assertThat(downloadedFileGroupAfterImport.getFileCount()).isEqualTo(2); 2796 assertThat(downloadedFileGroupAfterImport.getFileList()) 2797 .containsExactly(existingDownloadedFileGroup.getFile(0), updatedDataFileList.get(0)); 2798 } 2799 2800 @Test testImportFilesIntoFileGroup_whenMatchesDownloadedButNotPending_importsToDownloaded()2801 public void testImportFilesIntoFileGroup_whenMatchesDownloadedButNotPending_importsToDownloaded() 2802 throws Exception { 2803 // Set up an existing pending file group, an existing downloaded file group and a new file 2804 // group that matches the downloaded file group 2805 DataFileGroupInternal existingPendingFileGroup = 2806 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1); 2807 DataFileGroupInternal existingDownloadedFileGroup = 2808 existingPendingFileGroup.toBuilder() 2809 .clearFile() 2810 .addFile(MddTestUtil.createDataFile("downloaded-file", 0)) 2811 .setBuildId(10) 2812 .build(); 2813 ImmutableList<DataFile> updatedDataFileList = 2814 ImmutableList.of( 2815 DataFile.newBuilder() 2816 .setFileId("inline-file") 2817 .setChecksum("abc") 2818 .setUrlToDownload("inlinefile:abc") 2819 .build()); 2820 ImmutableMap<String, FileSource> inlineFileMap = 2821 ImmutableMap.of( 2822 "inline-file", FileSource.ofByteString(ByteString.copyFromUtf8("TEST_CONTENT"))); 2823 2824 GroupKey groupKey = GroupKey.newBuilder().setGroupName(TEST_GROUP).build(); 2825 2826 writePendingFileGroup(getPendingKey(groupKey), existingPendingFileGroup); 2827 writeDownloadedFileGroup(getDownloadedKey(groupKey), existingDownloadedFileGroup); 2828 2829 writeSharedFiles( 2830 sharedFilesMetadata, 2831 existingPendingFileGroup, 2832 ImmutableList.of(FileStatus.DOWNLOAD_IN_PROGRESS)); 2833 writeSharedFiles( 2834 sharedFilesMetadata, 2835 existingDownloadedFileGroup, 2836 ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE)); 2837 2838 // TODO: remove once SFM can perform import 2839 // write inline file as downloaded so FGM can find it 2840 NewFileKey newInlineFileKey = 2841 SharedFilesMetadata.createKeyFromDataFile( 2842 updatedDataFileList.get(0), existingDownloadedFileGroup.getAllowedReadersEnum()); 2843 SharedFile newInlineSharedFile = 2844 SharedFile.newBuilder() 2845 .setFileName(updatedDataFileList.get(0).getFileId()) 2846 .setFileStatus(FileStatus.DOWNLOAD_COMPLETE) 2847 .build(); 2848 sharedFilesMetadata.write(newInlineFileKey, newInlineSharedFile).get(); 2849 2850 fileGroupManager 2851 .importFilesIntoFileGroup( 2852 groupKey, 2853 /* buildId= */ 10, 2854 /* variantId= */ "", 2855 updatedDataFileList, 2856 inlineFileMap, 2857 /* customPropertyOptional= */ Optional.absent(), 2858 noCustomValidation()) 2859 .get(); 2860 2861 // Check that downloaded file group now contains the merged file group and pending group remains 2862 // the same. 2863 DataFileGroupInternal downloadedFileGroupAfterImport = 2864 fileGroupsMetadata.read(getDownloadedKey(groupKey)).get(); 2865 assertThat(downloadedFileGroupAfterImport.getBuildId()) 2866 .isEqualTo(existingDownloadedFileGroup.getBuildId()); 2867 assertThat(downloadedFileGroupAfterImport.getFileCount()).isEqualTo(2); 2868 assertThat(downloadedFileGroupAfterImport.getFileList()) 2869 .containsExactly(existingDownloadedFileGroup.getFile(0), updatedDataFileList.get(0)); 2870 assertThat(fileGroupsMetadata.read(getPendingKey(groupKey)).get()) 2871 .isEqualTo(existingPendingFileGroup); 2872 } 2873 2874 @Test testImportFilesIntoFileGroup_whenMatchesPendingButNotDownloaded_importsToPending()2875 public void testImportFilesIntoFileGroup_whenMatchesPendingButNotDownloaded_importsToPending() 2876 throws Exception { 2877 // Set up an existing pending file group, an existing downloaded file group and a new file 2878 // group that matches the pending file group 2879 DataFileGroupInternal existingPendingFileGroup = 2880 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1); 2881 DataFileGroupInternal existingDownloadedFileGroup = 2882 existingPendingFileGroup.toBuilder() 2883 .clearFile() 2884 .addFile(MddTestUtil.createDataFile("downloaded-file", 0)) 2885 .setBuildId(10) 2886 .build(); 2887 ImmutableList<DataFile> updatedDataFileList = 2888 ImmutableList.of( 2889 DataFile.newBuilder() 2890 .setFileId("inline-file") 2891 .setChecksum("abc") 2892 .setUrlToDownload("inlinefile:abc") 2893 .build()); 2894 ImmutableMap<String, FileSource> inlineFileMap = 2895 ImmutableMap.of( 2896 "inline-file", FileSource.ofByteString(ByteString.copyFromUtf8("TEST_CONTENT"))); 2897 2898 GroupKey groupKey = GroupKey.newBuilder().setGroupName(TEST_GROUP).build(); 2899 2900 writePendingFileGroup(getPendingKey(groupKey), existingPendingFileGroup); 2901 writeDownloadedFileGroup(getDownloadedKey(groupKey), existingDownloadedFileGroup); 2902 2903 writeSharedFiles( 2904 sharedFilesMetadata, 2905 existingPendingFileGroup, 2906 ImmutableList.of(FileStatus.DOWNLOAD_IN_PROGRESS)); 2907 writeSharedFiles( 2908 sharedFilesMetadata, 2909 existingDownloadedFileGroup, 2910 ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE)); 2911 2912 // TODO: remove once SFM can perform import 2913 // write inline file as downloaded so FGM can find it 2914 NewFileKey newInlineFileKey = 2915 SharedFilesMetadata.createKeyFromDataFile( 2916 updatedDataFileList.get(0), existingPendingFileGroup.getAllowedReadersEnum()); 2917 SharedFile newInlineSharedFile = 2918 SharedFile.newBuilder() 2919 .setFileName(updatedDataFileList.get(0).getFileId()) 2920 .setFileStatus(FileStatus.DOWNLOAD_COMPLETE) 2921 .build(); 2922 sharedFilesMetadata.write(newInlineFileKey, newInlineSharedFile).get(); 2923 2924 fileGroupManager 2925 .importFilesIntoFileGroup( 2926 groupKey, 2927 /* buildId= */ 0, 2928 /* variantId= */ "", 2929 updatedDataFileList, 2930 inlineFileMap, 2931 /* customPropertyOptional= */ Optional.absent(), 2932 noCustomValidation()) 2933 .get(); 2934 2935 // Check that pending file group now contains the merged file group and downloaded group remains 2936 // the same. 2937 DataFileGroupInternal pendingFileGroupAfterImport = 2938 fileGroupsMetadata.read(getPendingKey(groupKey)).get(); 2939 assertThat(pendingFileGroupAfterImport.getBuildId()) 2940 .isEqualTo(existingPendingFileGroup.getBuildId()); 2941 assertThat(pendingFileGroupAfterImport.getFileCount()).isEqualTo(2); 2942 assertThat(pendingFileGroupAfterImport.getFileList()) 2943 .containsExactly(existingPendingFileGroup.getFile(0), updatedDataFileList.get(0)); 2944 assertThat(fileGroupsMetadata.read(getDownloadedKey(groupKey)).get()) 2945 .isEqualTo(existingDownloadedFileGroup); 2946 } 2947 2948 @Test testImportFilesIntoFileGroup_whenPerformingImport_choosesFileSourceById()2949 public void testImportFilesIntoFileGroup_whenPerformingImport_choosesFileSourceById() 2950 throws Exception { 2951 // Use mockSharedFileManager to check startImport call 2952 resetFileGroupManager(fileGroupsMetadata, mockSharedFileManager); 2953 2954 FileSource testFileSource = 2955 FileSource.ofByteString(ByteString.copyFromUtf8("TEST_FILE_SOURCE")); 2956 2957 // Setup file group with inline file to import 2958 DataFileGroupInternal fileGroup = 2959 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 0).toBuilder() 2960 .addFile( 2961 DataFile.newBuilder() 2962 .setFileId("inline-file") 2963 .setChecksum("abc") 2964 .setUrlToDownload("inlinefile:sha1:abc") 2965 .build()) 2966 .build(); 2967 2968 GroupKey groupKey = GroupKey.newBuilder().setGroupName(TEST_GROUP).build(); 2969 NewFileKey inlineFileKey = 2970 SharedFilesMetadata.createKeyFromDataFile( 2971 fileGroup.getFile(0), fileGroup.getAllowedReadersEnum()); 2972 2973 writePendingFileGroup(getPendingKey(groupKey), fileGroup); 2974 2975 // Setup mock SFM with successful calls 2976 when(mockSharedFileManager.reserveFileEntry(inlineFileKey)) 2977 .thenReturn(Futures.immediateFuture(true)); 2978 when(mockSharedFileManager.getFileStatus(inlineFileKey)) 2979 .thenReturn(Futures.immediateFuture(FileStatus.DOWNLOAD_IN_PROGRESS)); 2980 when(mockSharedFileManager.startImport( 2981 groupKeyCaptor.capture(), 2982 eq(fileGroup.getFile(0)), 2983 eq(inlineFileKey), 2984 any(), 2985 fileSourceCaptor.capture())) 2986 .thenReturn(Futures.immediateVoidFuture()); 2987 2988 fileGroupManager 2989 .importFilesIntoFileGroup( 2990 groupKey, 2991 /* buildId= */ 0, 2992 /* variantId= */ "", 2993 /* updatedDataFileList= */ ImmutableList.of(), 2994 /* inlineFileMap= */ ImmutableMap.of("inline-file", testFileSource), 2995 /* customPropertyOptional= */ Optional.absent(), 2996 noCustomValidation()) 2997 .get(); 2998 2999 // Check that SFM startImport was called with expected inputs 3000 verify(mockSharedFileManager, times(1)).startImport(any(), any(), any(), any(), any()); 3001 assertThat(groupKeyCaptor.getValue().getGroupName()).isEqualTo(TEST_GROUP); 3002 assertThat(fileSourceCaptor.getValue()).isEqualTo(testFileSource); 3003 } 3004 3005 @Test testImportFilesIntoFileGroup_whenFileAlreadyDownloaded_doesNotAttemptToImport()3006 public void testImportFilesIntoFileGroup_whenFileAlreadyDownloaded_doesNotAttemptToImport() 3007 throws Exception { 3008 // Use mockSharedFileManager to check startImport call 3009 resetFileGroupManager(fileGroupsMetadata, mockSharedFileManager); 3010 3011 FileSource testFileSource = 3012 FileSource.ofByteString(ByteString.copyFromUtf8("TEST_FILE_SOURCE")); 3013 3014 // Create an existing downloaded file group and attempt to import again with a given source. 3015 // Since the file is already marked DOWNLOAD_COMPLETE, the import should not be invoked. 3016 DataFileGroupInternal existingDownloadedFileGroup = 3017 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 0).toBuilder() 3018 .addFile( 3019 DataFile.newBuilder() 3020 .setFileId("inline-file") 3021 .setChecksum("abc") 3022 .setUrlToDownload("inlinefile:abc") 3023 .build()) 3024 .build(); 3025 3026 GroupKey groupKey = GroupKey.newBuilder().setGroupName(TEST_GROUP).build(); 3027 NewFileKey inlineFileKey = 3028 SharedFilesMetadata.createKeyFromDataFile( 3029 existingDownloadedFileGroup.getFile(0), 3030 existingDownloadedFileGroup.getAllowedReadersEnum()); 3031 3032 writeDownloadedFileGroup(getDownloadedKey(groupKey), existingDownloadedFileGroup); 3033 3034 // Setup mock SFM with successful calls 3035 when(mockSharedFileManager.reserveFileEntry(inlineFileKey)) 3036 .thenReturn(Futures.immediateFuture(true)); 3037 when(mockSharedFileManager.getFileStatus(inlineFileKey)) 3038 .thenReturn(Futures.immediateFuture(FileStatus.DOWNLOAD_COMPLETE)); 3039 3040 fileGroupManager 3041 .importFilesIntoFileGroup( 3042 groupKey, 3043 /* buildId= */ 0, 3044 /* variantId= */ "", 3045 /* updatedDataFileList= */ ImmutableList.of(), 3046 /* inlineFileMap= */ ImmutableMap.of("inline-file", testFileSource), 3047 /* customPropertyOptional= */ Optional.absent(), 3048 noCustomValidation()) 3049 .get(); 3050 3051 // Check that SFM startImport was not called 3052 verify(mockSharedFileManager, times(0)).startImport(any(), any(), any(), any(), any()); 3053 } 3054 3055 @Test testImportFilesIntoFileGroups_whenFileSourceNotProvided_fails()3056 public void testImportFilesIntoFileGroups_whenFileSourceNotProvided_fails() throws Exception { 3057 // create a file group added to MDD with an inline file and check that import call fails if 3058 // source is not provided. 3059 DataFileGroupInternal existingFileGroup = 3060 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 0).toBuilder() 3061 .addFile( 3062 DataFile.newBuilder() 3063 .setFileId("inline-file") 3064 .setChecksum("abc") 3065 .setUrlToDownload("inlinefile:abc") 3066 .build()) 3067 .build(); 3068 3069 GroupKey groupKey = GroupKey.newBuilder().setGroupName(TEST_GROUP).build(); 3070 3071 writePendingFileGroup(getPendingKey(groupKey), existingFileGroup); 3072 3073 writeSharedFiles( 3074 sharedFilesMetadata, existingFileGroup, ImmutableList.of(FileStatus.DOWNLOAD_IN_PROGRESS)); 3075 3076 ExecutionException ex = 3077 assertThrows( 3078 ExecutionException.class, 3079 () -> 3080 fileGroupManager 3081 .importFilesIntoFileGroup( 3082 groupKey, 3083 /* buildId= */ 0, 3084 /* variantId= */ "", 3085 /* updatedDataFileList= */ ImmutableList.of(), 3086 /* inlineFileMap= */ ImmutableMap.of(), 3087 /* customPropertyOptional= */ Optional.absent(), 3088 noCustomValidation()) 3089 .get()); 3090 assertThat(ex).hasCauseThat().isInstanceOf(AggregateException.class); 3091 AggregateException aex = (AggregateException) ex.getCause(); 3092 assertThat(aex.getFailures()).hasSize(1); 3093 assertThat(aex.getFailures().get(0)).isInstanceOf(DownloadException.class); 3094 DownloadException dex = (DownloadException) aex.getFailures().get(0); 3095 assertThat(dex.getDownloadResultCode()) 3096 .isEqualTo(DownloadResultCode.MISSING_INLINE_FILE_SOURCE); 3097 } 3098 3099 @Test testImportFilesIntoFileGroup_whenImportFails_preventsMetadataUpdate()3100 public void testImportFilesIntoFileGroup_whenImportFails_preventsMetadataUpdate() 3101 throws Exception { 3102 // Use mockSharedFileManager to mock a failure for an import 3103 resetFileGroupManager(fileGroupsMetadata, mockSharedFileManager); 3104 3105 FileSource testFileSource1 = 3106 FileSource.ofByteString(ByteString.copyFromUtf8("TEST_FILE_SOURCE_1")); 3107 FileSource testFileSource2 = 3108 FileSource.ofByteString(ByteString.copyFromUtf8("TEST_FILE_SOURCE_2")); 3109 3110 // Setup empty file group 3111 DataFileGroupInternal fileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 0); 3112 3113 // Setup list of files that should be imported 3114 ImmutableList<DataFile> updatedDataFileList = 3115 ImmutableList.of( 3116 DataFile.newBuilder() 3117 .setFileId("inline-file-1") 3118 .setChecksum("abc") 3119 .setUrlToDownload("inlinefile:sha1:abc") 3120 .build(), 3121 DataFile.newBuilder() 3122 .setFileId("inline-file-2") 3123 .setChecksum("def") 3124 .setUrlToDownload("inlinefile:sha1:def") 3125 .build()); 3126 3127 GroupKey groupKey = GroupKey.newBuilder().setGroupName(TEST_GROUP).build(); 3128 NewFileKey inlineFileKey1 = 3129 SharedFilesMetadata.createKeyFromDataFile( 3130 updatedDataFileList.get(0), fileGroup.getAllowedReadersEnum()); 3131 NewFileKey inlineFileKey2 = 3132 SharedFilesMetadata.createKeyFromDataFile( 3133 updatedDataFileList.get(1), fileGroup.getAllowedReadersEnum()); 3134 3135 writeDownloadedFileGroup(getDownloadedKey(groupKey), fileGroup); 3136 3137 // Setup mock calls to SFM 3138 when(mockSharedFileManager.reserveFileEntry(any())).thenReturn(Futures.immediateFuture(true)); 3139 3140 // Mock that inline file 1 completed, but inline file 2 failed 3141 when(mockSharedFileManager.getFileStatus(inlineFileKey1)) 3142 .thenReturn(Futures.immediateFuture(FileStatus.DOWNLOAD_IN_PROGRESS)); 3143 when(mockSharedFileManager.getFileStatus(inlineFileKey2)) 3144 .thenReturn(Futures.immediateFuture(FileStatus.DOWNLOAD_FAILED)); 3145 when(mockSharedFileManager.startImport( 3146 any(), eq(updatedDataFileList.get(0)), eq(inlineFileKey1), any(), any())) 3147 .thenReturn(Futures.immediateVoidFuture()); 3148 when(mockSharedFileManager.startImport( 3149 any(), eq(updatedDataFileList.get(1)), eq(inlineFileKey2), any(), any())) 3150 .thenReturn( 3151 Futures.immediateFailedFuture( 3152 DownloadException.builder() 3153 .setDownloadResultCode(DownloadResultCode.DOWNLOAD_TRANSFORM_IO_ERROR) 3154 .build())); 3155 3156 ExecutionException ex = 3157 assertThrows( 3158 ExecutionException.class, 3159 () -> 3160 fileGroupManager 3161 .importFilesIntoFileGroup( 3162 groupKey, 3163 /* buildId= */ 0, 3164 /* variantId= */ "", 3165 updatedDataFileList, 3166 /* inlineFileMap= */ ImmutableMap.of( 3167 "inline-file-1", testFileSource1, "inline-file-2", testFileSource2), 3168 /* customPropertyOptional= */ Optional.absent(), 3169 noCustomValidation()) 3170 .get()); 3171 3172 // Check for expected cause 3173 assertThat(ex).hasCauseThat().isInstanceOf(AggregateException.class); 3174 AggregateException aex = (AggregateException) ex.getCause(); 3175 assertThat(aex.getFailures()).hasSize(1); 3176 assertThat(aex.getFailures().get(0)).isInstanceOf(DownloadException.class); 3177 DownloadException dex = (DownloadException) aex.getFailures().get(0); 3178 assertThat(dex.getDownloadResultCode()) 3179 .isEqualTo(DownloadResultCode.DOWNLOAD_TRANSFORM_IO_ERROR); 3180 3181 // Check that existing (empty) group remains in metadata. iow, the files from 3182 // updatedDataFileList were not added since the import failed. 3183 DataFileGroupInternal existingFileGroup = 3184 fileGroupsMetadata.read(getDownloadedKey(groupKey)).get(); 3185 assertThat(existingFileGroup.getFileList()).isEmpty(); 3186 3187 // Check that SFM startImport was called with expected inputs 3188 verify(mockSharedFileManager, times(2)).startImport(any(), any(), any(), any(), any()); 3189 } 3190 3191 @Test testImportFilesIntoFileGroup_skipsSideloadedFile()3192 public void testImportFilesIntoFileGroup_skipsSideloadedFile() throws Exception { 3193 // Create sideloaded group with inline file 3194 DataFileGroupInternal sideloadedGroup = 3195 DataFileGroupInternal.newBuilder() 3196 .setGroupName(TEST_GROUP) 3197 .addFile( 3198 DataFile.newBuilder() 3199 .setFileId("sideloaded_file") 3200 .setUrlToDownload("file:/test") 3201 .setChecksumType(DataFile.ChecksumType.NONE) 3202 .build()) 3203 .addFile( 3204 DataFile.newBuilder() 3205 .setFileId("inline_file") 3206 .setUrlToDownload("inlinefile:sha1:checksum") 3207 .setChecksum("checksum") 3208 .build()) 3209 .build(); 3210 ImmutableMap<String, FileSource> inlineFileMap = 3211 ImmutableMap.of( 3212 "inline_file", FileSource.ofByteString(ByteString.copyFromUtf8("TEST_CONTENT"))); 3213 NewFileKey inlineFileKey = 3214 SharedFilesMetadata.createKeyFromDataFile( 3215 sideloadedGroup.getFile(1), sideloadedGroup.getAllowedReadersEnum()); 3216 3217 // Write group as pending since we are waiting on inline file 3218 writePendingFileGroup(testKey, sideloadedGroup); 3219 3220 // Write inline file as succeeded so we skip SFM's import call 3221 SharedFile inlineSharedFile = 3222 SharedFile.newBuilder() 3223 .setFileName(sideloadedGroup.getFile(1).getFileId()) 3224 .setFileStatus(FileStatus.DOWNLOAD_COMPLETE) 3225 .build(); 3226 sharedFilesMetadata.write(inlineFileKey, inlineSharedFile).get(); 3227 3228 fileGroupManager 3229 .importFilesIntoFileGroup( 3230 testKey, 3231 sideloadedGroup.getBuildId(), 3232 sideloadedGroup.getVariantId(), 3233 /* updatedDataFileList= */ ImmutableList.of(), 3234 inlineFileMap, 3235 /* customPropertyOptional= */ Optional.absent(), 3236 noCustomValidation()) 3237 .get(); 3238 3239 assertThat(readPendingFileGroup(testKey)).isNull(); 3240 assertThat(readDownloadedFileGroup(testKey)).isNotNull(); 3241 } 3242 3243 @Test testDownloadPendingGroup_success()3244 public void testDownloadPendingGroup_success() throws Exception { 3245 // Write 1 group to the pending shared prefs. 3246 DataFileGroupInternal fileGroup = 3247 createDataFileGroup( 3248 TEST_GROUP, 3249 /* fileCount= */ 2, 3250 /* downloadAttemptCount= */ 3, 3251 /* newFilesReceivedTimestamp= */ testClock.currentTimeMillis() - 500L); 3252 ExtraHttpHeader extraHttpHeader = 3253 ExtraHttpHeader.newBuilder().setKey("user-agent").setValue("mdd-downloader").build(); 3254 3255 fileGroup = 3256 fileGroup.toBuilder() 3257 .setOwnerPackage(context.getPackageName()) 3258 .setDownloadConditions(DownloadConditions.getDefaultInstance()) 3259 .setTrafficTag(TRAFFIC_TAG) 3260 .addGroupExtraHttpHeaders(extraHttpHeader) 3261 .build(); 3262 writePendingFileGroup(testKey, fileGroup); 3263 3264 writeSharedFiles( 3265 sharedFilesMetadata, 3266 fileGroup, 3267 ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE, FileStatus.DOWNLOAD_COMPLETE)); 3268 3269 fileGroupManager 3270 .downloadFileGroup(testKey, DownloadConditions.getDefaultInstance(), noCustomValidation()) 3271 .get(); 3272 3273 // Verify that pending key is removed if download is complete. 3274 assertThat(readPendingFileGroup(testKey)).isNull(); 3275 3276 // Verify that downloaded key is written into metadata if download is complete. 3277 assertThat(readDownloadedFileGroup(testKey)).isNotNull(); 3278 verify(mockLogger) 3279 .logEventSampled( 3280 MddClientEvent.Code.EVENT_CODE_UNSPECIFIED, 3281 TEST_GROUP, 3282 0, 3283 /* buildId= */ 0, 3284 /* variantId= */ ""); 3285 verify(mockLogger) 3286 .logMddDownloadResult( 3287 MddDownloadResult.Code.SUCCESS, 3288 createFileGroupDetails(fileGroup) 3289 .setOwnerPackage(context.getPackageName()) 3290 .clearFileCount() 3291 .build()); 3292 verify(mockLogger) 3293 .logMddDownloadLatency( 3294 createFileGroupDetails(fileGroup).build(), 3295 createMddDownloadLatency( 3296 /* downloadAttemptCount= */ 4, 3297 /* downloadLatencyMs= */ 0L, 3298 /* totalLatencyMs= */ 500L)); 3299 } 3300 3301 @Test testDownloadPendingGroup_withFailingCustomValidator()3302 public void testDownloadPendingGroup_withFailingCustomValidator() throws Exception { 3303 // Write 1 group to the pending shared prefs. 3304 DataFileGroupInternal fileGroup = 3305 createDataFileGroup( 3306 TEST_GROUP, 3307 /* fileCount= */ 2, 3308 /* downloadAttemptCount= */ 3, 3309 /* newFilesReceivedTimestamp= */ testClock.currentTimeMillis() - 500L); 3310 ExtraHttpHeader extraHttpHeader = 3311 ExtraHttpHeader.newBuilder().setKey("user-agent").setValue("mdd-downloader").build(); 3312 3313 fileGroup = 3314 fileGroup.toBuilder() 3315 .setOwnerPackage(context.getPackageName()) 3316 .setDownloadConditions(DownloadConditions.getDefaultInstance()) 3317 .setTrafficTag(TRAFFIC_TAG) 3318 .addGroupExtraHttpHeaders(extraHttpHeader) 3319 .build(); 3320 writePendingFileGroup(testKey, fileGroup); 3321 3322 writeSharedFiles( 3323 sharedFilesMetadata, 3324 fileGroup, 3325 ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE, FileStatus.DOWNLOAD_COMPLETE)); 3326 3327 AsyncFunction<DataFileGroupInternal, Boolean> failingValidator = 3328 unused -> Futures.immediateFuture(false); 3329 ListenableFuture<DataFileGroupInternal> downloadFuture = 3330 fileGroupManager.downloadFileGroup( 3331 testKey, DownloadConditions.getDefaultInstance(), failingValidator); 3332 3333 ExecutionException exception = assertThrows(ExecutionException.class, downloadFuture::get); 3334 assertThat(exception).hasCauseThat().isInstanceOf(DownloadException.class); 3335 DownloadException cause = (DownloadException) exception.getCause(); 3336 assertThat(cause).isNotNull(); 3337 assertThat(cause).hasMessageThat().contains("CUSTOM_FILEGROUP_VALIDATION_FAILED"); 3338 3339 // Verify that pending key was removed. This will ensure the files are eligible for garbage 3340 // collection. 3341 assertThat(readPendingFileGroup(testKey)).isNull(); 3342 3343 verify(mockLogger) 3344 .logEventSampled( 3345 MddClientEvent.Code.EVENT_CODE_UNSPECIFIED, 3346 TEST_GROUP, 3347 /* fileGroupVersionNumber= */ 0, 3348 /* buildId= */ 0, 3349 /* variantId= */ ""); 3350 3351 ArgumentCaptor<MddDownloadResult.Code> resultCodeCaptor = 3352 ArgumentCaptor.forClass(MddDownloadResult.Code.class); 3353 ArgumentCaptor<DataDownloadFileGroupStats> groupDetailsCaptor = 3354 ArgumentCaptor.forClass(DataDownloadFileGroupStats.class); 3355 verify(mockLogger) 3356 .logMddDownloadResult(resultCodeCaptor.capture(), groupDetailsCaptor.capture()); 3357 3358 // Also clearing the file group version number becaused it ends up not being attached since 3359 // the pending group was removed. 3360 DataDownloadFileGroupStats expectedGroupDetails = 3361 createFileGroupDetails(fileGroup).clearFileCount().clearFileGroupVersionNumber().build(); 3362 3363 assertThat(resultCodeCaptor.getAllValues()) 3364 .containsExactly(MddDownloadResult.Code.CUSTOM_FILEGROUP_VALIDATION_FAILED); 3365 assertThat(groupDetailsCaptor.getAllValues()).containsExactly(expectedGroupDetails); 3366 } 3367 3368 @Test testDownloadFileGroup_failed()3369 public void testDownloadFileGroup_failed() throws Exception { 3370 // Write 1 group to the pending shared prefs. 3371 DataFileGroupInternal fileGroup = 3372 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2).toBuilder() 3373 .setVariantId("test-variant") 3374 .setBuildId(10) 3375 .build(); 3376 NewFileKey[] keys = MddTestUtil.createFileKeysForDataFileGroupInternal(fileGroup); 3377 fileGroup = 3378 fileGroup.toBuilder() 3379 .setOwnerPackage(context.getPackageName()) 3380 .setDownloadConditions(DownloadConditions.getDefaultInstance()) 3381 .build(); 3382 writePendingFileGroup(testKey, fileGroup); 3383 3384 // Not all files are downloaded. 3385 writeSharedFiles( 3386 sharedFilesMetadata, 3387 fileGroup, 3388 ImmutableList.of(FileStatus.SUBSCRIBED, FileStatus.SUBSCRIBED)); 3389 3390 // First file failed. 3391 Uri failingFileUri = 3392 DirectoryUtil.getOnDeviceUri( 3393 context, 3394 keys[0].getAllowedReaders(), 3395 fileGroup.getFile(0).getFileId(), 3396 fileGroup.getFile(0).getChecksum(), 3397 mockSilentFeedback, 3398 /* instanceId= */ Optional.absent(), 3399 false); 3400 fileDownloadFails(keys[0], failingFileUri, DownloadResultCode.LOW_DISK_ERROR); 3401 3402 // Second file succeeded. 3403 Uri succeedingFileUri = 3404 DirectoryUtil.getOnDeviceUri( 3405 context, 3406 keys[1].getAllowedReaders(), 3407 fileGroup.getFile(1).getFileId(), 3408 fileGroup.getFile(1).getChecksum(), 3409 mockSilentFeedback, 3410 /* instanceId= */ Optional.absent(), 3411 false); 3412 fileDownloadSucceeds(keys[1], succeedingFileUri); 3413 3414 ListenableFuture<DataFileGroupInternal> downloadFuture = 3415 fileGroupManager.downloadFileGroup( 3416 testKey, DownloadConditions.getDefaultInstance(), noCustomValidation()); 3417 3418 ExecutionException exception = assertThrows(ExecutionException.class, downloadFuture::get); 3419 assertThat(exception).hasCauseThat().isInstanceOf(AggregateException.class); 3420 AggregateException cause = (AggregateException) exception.getCause(); 3421 assertThat(cause).isNotNull(); 3422 ImmutableList<Throwable> failures = cause.getFailures(); 3423 assertThat(failures).hasSize(1); 3424 assertThat(failures.get(0)).isInstanceOf(DownloadException.class); 3425 assertThat(failures.get(0)).hasMessageThat().contains("LOW_DISK_ERROR"); 3426 3427 // Verify that the pending group is still part of pending groups prefs. 3428 assertThat(readPendingFileGroup(testKey)).isNotNull(); 3429 3430 // Verify that the pending group is not changed from pending to downloaded. 3431 assertThat(readDownloadedFileGroup(testKey)).isNull(); 3432 3433 verify(mockLogger) 3434 .logEventSampled( 3435 MddClientEvent.Code.EVENT_CODE_UNSPECIFIED, 3436 TEST_GROUP, 3437 /* fileGroupVersionNumber= */ 0, 3438 /* buildId= */ 10, 3439 /* variantId= */ "test-variant"); 3440 3441 ArgumentCaptor<MddDownloadResult.Code> resultCodeCaptor = 3442 ArgumentCaptor.forClass(MddDownloadResult.Code.class); 3443 ArgumentCaptor<DataDownloadFileGroupStats> groupDetailsCaptor = 3444 ArgumentCaptor.forClass(DataDownloadFileGroupStats.class); 3445 verify(mockLogger) 3446 .logMddDownloadResult(resultCodeCaptor.capture(), groupDetailsCaptor.capture()); 3447 3448 DataDownloadFileGroupStats expectedGroupDetails = 3449 createFileGroupDetails(fileGroup).clearFileCount().build(); 3450 3451 assertThat(resultCodeCaptor.getAllValues()) 3452 .containsExactly(MddDownloadResult.Code.LOW_DISK_ERROR); 3453 assertThat(groupDetailsCaptor.getAllValues()).containsExactly(expectedGroupDetails); 3454 } 3455 3456 @Test testDownloadFileGroup_failedWithMultipleExceptions()3457 public void testDownloadFileGroup_failedWithMultipleExceptions() throws Exception { 3458 // Write 1 group to the pending shared prefs. 3459 DataFileGroupInternal fileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 3); 3460 NewFileKey[] keys = MddTestUtil.createFileKeysForDataFileGroupInternal(fileGroup); 3461 fileGroup = 3462 fileGroup.toBuilder() 3463 .setOwnerPackage(context.getPackageName()) 3464 .setDownloadConditions(DownloadConditions.getDefaultInstance()) 3465 .build(); 3466 writePendingFileGroup(testKey, fileGroup); 3467 3468 // Not all files are downloaded. 3469 writeSharedFiles( 3470 sharedFilesMetadata, 3471 fileGroup, 3472 ImmutableList.of(FileStatus.SUBSCRIBED, FileStatus.SUBSCRIBED, FileStatus.SUBSCRIBED)); 3473 3474 // First file succeeded. 3475 Uri succeedingFileUri = 3476 DirectoryUtil.getOnDeviceUri( 3477 context, 3478 keys[0].getAllowedReaders(), 3479 fileGroup.getFile(0).getFileId(), 3480 fileGroup.getFile(0).getChecksum(), 3481 mockSilentFeedback, 3482 /* instanceId= */ Optional.absent(), 3483 false); 3484 fileDownloadSucceeds(keys[0], succeedingFileUri); 3485 3486 // Second file failed with download transform I/O error. 3487 Uri failingFileUri1 = 3488 DirectoryUtil.getOnDeviceUri( 3489 context, 3490 keys[1].getAllowedReaders(), 3491 fileGroup.getFile(1).getFileId(), 3492 fileGroup.getFile(1).getChecksum(), 3493 mockSilentFeedback, 3494 /* instanceId= */ Optional.absent(), 3495 false); 3496 fileDownloadFails(keys[1], failingFileUri1, DownloadResultCode.DOWNLOAD_TRANSFORM_IO_ERROR); 3497 3498 // Third file failed with android downloader http error. 3499 Uri failingFileUri2 = 3500 DirectoryUtil.getOnDeviceUri( 3501 context, 3502 keys[2].getAllowedReaders(), 3503 fileGroup.getFile(2).getFileId(), 3504 fileGroup.getFile(2).getChecksum(), 3505 mockSilentFeedback, 3506 /* instanceId= */ Optional.absent(), 3507 false); 3508 fileDownloadFails(keys[2], failingFileUri2, DownloadResultCode.ANDROID_DOWNLOADER_HTTP_ERROR); 3509 3510 ListenableFuture<DataFileGroupInternal> downloadFuture = 3511 fileGroupManager.downloadFileGroup( 3512 testKey, DownloadConditions.getDefaultInstance(), noCustomValidation()); 3513 3514 // Ensure that all exceptions are aggregated. 3515 ExecutionException exception = assertThrows(ExecutionException.class, downloadFuture::get); 3516 assertThat(exception).hasCauseThat().isInstanceOf(AggregateException.class); 3517 AggregateException cause = (AggregateException) exception.getCause(); 3518 assertThat(cause).isNotNull(); 3519 ImmutableList<Throwable> failures = cause.getFailures(); 3520 assertThat(failures).hasSize(2); 3521 assertThat(failures.get(0)).isInstanceOf(DownloadException.class); 3522 assertThat(failures.get(0)).hasMessageThat().contains("DOWNLOAD_TRANSFORM_IO_ERROR"); 3523 assertThat(failures.get(1)).isInstanceOf(DownloadException.class); 3524 assertThat(failures.get(1)).hasMessageThat().contains("ANDROID_DOWNLOADER_HTTP_ERROR"); 3525 3526 // Verify that the pending group is still part of pending groups prefs. 3527 assertThat(readPendingFileGroup(testKey)).isNotNull(); 3528 3529 // Verify that the pending group is not changed from pending to downloaded. 3530 assertThat(readDownloadedFileGroup(testKey)).isNull(); 3531 3532 verify(mockLogger) 3533 .logEventSampled( 3534 MddClientEvent.Code.EVENT_CODE_UNSPECIFIED, 3535 TEST_GROUP, 3536 /* fileGroupVersionNumber= */ 0, 3537 /* buildId= */ 0, 3538 /* variantId= */ ""); 3539 3540 ArgumentCaptor<MddDownloadResult.Code> resultCodeCaptor = 3541 ArgumentCaptor.forClass(MddDownloadResult.Code.class); 3542 ArgumentCaptor<DataDownloadFileGroupStats> groupDetailsCaptor = 3543 ArgumentCaptor.forClass(DataDownloadFileGroupStats.class); 3544 verify(mockLogger, times(2)) 3545 .logMddDownloadResult(resultCodeCaptor.capture(), groupDetailsCaptor.capture()); 3546 3547 DataDownloadFileGroupStats expectedGroupDetails = 3548 createFileGroupDetails(fileGroup).clearFileCount().build(); 3549 3550 assertThat(resultCodeCaptor.getAllValues()) 3551 .containsExactly( 3552 MddDownloadResult.Code.DOWNLOAD_TRANSFORM_IO_ERROR, 3553 MddDownloadResult.Code.ANDROID_DOWNLOADER_HTTP_ERROR); 3554 assertThat(groupDetailsCaptor.getAllValues()) 3555 .containsExactly(expectedGroupDetails, expectedGroupDetails); 3556 } 3557 3558 @Test testDownloadFileGroup_failedWithUnknownError()3559 public void testDownloadFileGroup_failedWithUnknownError() throws Exception { 3560 // Write 1 group to the pending shared prefs. 3561 DataFileGroupInternal fileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2); 3562 NewFileKey[] keys = MddTestUtil.createFileKeysForDataFileGroupInternal(fileGroup); 3563 fileGroup = 3564 fileGroup.toBuilder() 3565 .setOwnerPackage(context.getPackageName()) 3566 .setDownloadConditions(DownloadConditions.getDefaultInstance()) 3567 .build(); 3568 writePendingFileGroup(testKey, fileGroup); 3569 3570 writeSharedFiles( 3571 sharedFilesMetadata, 3572 fileGroup, 3573 ImmutableList.of(FileStatus.SUBSCRIBED, FileStatus.DOWNLOAD_COMPLETE)); 3574 3575 // First file failed. 3576 Uri failingFileUri = 3577 DirectoryUtil.getOnDeviceUri( 3578 context, 3579 keys[0].getAllowedReaders(), 3580 fileGroup.getFile(0).getFileId(), 3581 fileGroup.getFile(0).getChecksum(), 3582 mockSilentFeedback, 3583 /* instanceId= */ Optional.absent(), 3584 false); 3585 // The file status is set to DOWNLOAD_FAILED but the downloader returns an immediateVoidFuture. 3586 // An UNKNOWN_ERROR is logged. 3587 fileDownloadFails(keys[0], failingFileUri, /* failureCode= */ null); 3588 3589 ListenableFuture<DataFileGroupInternal> downloadFuture = 3590 fileGroupManager.downloadFileGroup( 3591 testKey, DownloadConditions.getDefaultInstance(), noCustomValidation()); 3592 3593 ExecutionException exception = assertThrows(ExecutionException.class, downloadFuture::get); 3594 assertThat(exception).hasMessageThat().contains("UNKNOWN_ERROR"); 3595 3596 // Verify that the pending group is still part of pending groups prefs. 3597 assertThat(readPendingFileGroup(testKey)).isNotNull(); 3598 3599 // Verify that the pending group is not changed from pending to downloaded. 3600 assertThat(readDownloadedFileGroup(testKey)).isNull(); 3601 3602 verify(mockLogger) 3603 .logEventSampled( 3604 MddClientEvent.Code.EVENT_CODE_UNSPECIFIED, 3605 TEST_GROUP, 3606 /* fileGroupVersionNumber= */ 0, 3607 /* buildId= */ 0, 3608 /* variantId= */ ""); 3609 } 3610 3611 @Test testDownloadFileGroup_pending()3612 public void testDownloadFileGroup_pending() throws Exception { 3613 // Write 1 group to the pending shared prefs. 3614 DataFileGroupInternal fileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2); 3615 fileGroup = 3616 fileGroup.toBuilder() 3617 .setOwnerPackage(context.getPackageName()) 3618 .setDownloadConditions(DownloadConditions.getDefaultInstance()) 3619 .build(); 3620 writePendingFileGroup(testKey, fileGroup); 3621 3622 // Not all files are downloaded. 3623 writeSharedFiles( 3624 sharedFilesMetadata, 3625 fileGroup, 3626 ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE, FileStatus.SUBSCRIBED)); 3627 3628 when(mockDownloader.startDownloading( 3629 any(String.class), 3630 any(GroupKey.class), 3631 anyInt(), 3632 anyLong(), 3633 any(String.class), 3634 any(Uri.class), 3635 any(String.class), 3636 anyInt(), 3637 any(DownloadConditions.class), 3638 isA(DownloaderCallbackImpl.class), 3639 anyInt(), 3640 anyList())) 3641 .thenReturn(Futures.immediateVoidFuture()); 3642 3643 ListenableFuture<DataFileGroupInternal> downloadFuture = 3644 fileGroupManager.downloadFileGroup( 3645 testKey, DownloadConditions.getDefaultInstance(), noCustomValidation()); 3646 3647 ExecutionException exception = assertThrows(ExecutionException.class, downloadFuture::get); 3648 assertThat(exception).hasMessageThat().contains("UNKNOWN_ERROR"); 3649 3650 // Verify that the pending group is still part of pending groups prefs. 3651 assertThat(readPendingFileGroup(testKey)).isNotNull(); 3652 3653 // Verify that the pending group is not changed from pending to downloaded. 3654 assertThat(readDownloadedFileGroup(testKey)).isNull(); 3655 3656 verify(mockLogger) 3657 .logEventSampled( 3658 MddClientEvent.Code.EVENT_CODE_UNSPECIFIED, 3659 TEST_GROUP, 3660 /* fileGroupVersionNumber= */ 0, 3661 /* buildId= */ 0, 3662 /* variantId= */ ""); 3663 verify(mockLogger) 3664 .logMddDownloadResult( 3665 MddDownloadResult.Code.UNKNOWN_ERROR, 3666 createFileGroupDetails(fileGroup) 3667 .setOwnerPackage(context.getPackageName()) 3668 .clearFileCount() 3669 .build()); 3670 } 3671 3672 @Test testDownloadFileGroup_alreadyDownloaded()3673 public void testDownloadFileGroup_alreadyDownloaded() throws Exception { 3674 // Write 1 group to the downloaded shared prefs. 3675 DataFileGroupInternal fileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2); 3676 fileGroup = 3677 fileGroup.toBuilder() 3678 .setOwnerPackage(context.getPackageName()) 3679 .setDownloadConditions(DownloadConditions.getDefaultInstance()) 3680 .build(); 3681 writeDownloadedFileGroup(testKey, fileGroup); 3682 3683 List<GroupKey> originalKeys = fileGroupsMetadata.getAllGroupKeys().get(); 3684 3685 ListenableFuture<DataFileGroupInternal> downloadFuture = 3686 fileGroupManager.downloadFileGroup( 3687 testKey, DownloadConditions.getDefaultInstance(), noCustomValidation()); 3688 3689 assertThat(downloadFuture.get()).isEqualTo(fileGroup); 3690 3691 // Verify that the downloaded group is still part of downloaded groups prefs. 3692 DataFileGroupInternal downloadedGroup = readDownloadedFileGroup(testKey); 3693 assertThat(downloadedGroup).isEqualTo(fileGroup); 3694 3695 // Verify that no group metadata is written or removed. 3696 assertThat(originalKeys).isEqualTo(fileGroupsMetadata.getAllGroupKeys().get()); 3697 } 3698 3699 @Test testDownloadFileGroup_nullDownloadCondition()3700 public void testDownloadFileGroup_nullDownloadCondition() throws Exception { 3701 DownloadConditions downloadConditions = 3702 DownloadConditions.newBuilder() 3703 .setDeviceNetworkPolicy(DeviceNetworkPolicy.DOWNLOAD_ONLY_ON_WIFI) 3704 .setDeviceStoragePolicy(DeviceStoragePolicy.BLOCK_DOWNLOAD_IN_LOW_STORAGE) 3705 .build(); 3706 3707 // Write 1 group to the pending shared prefs. 3708 DataFileGroupInternal fileGroup = 3709 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2).toBuilder() 3710 .setDownloadConditions(downloadConditions) 3711 .build(); 3712 writePendingFileGroup(testKey, fileGroup); 3713 3714 writeSharedFiles( 3715 sharedFilesMetadata, 3716 fileGroup, 3717 ImmutableList.of(FileStatus.SUBSCRIBED, FileStatus.SUBSCRIBED)); 3718 3719 ArgumentCaptor<DownloadConditions> downloadConditionsCaptor = 3720 ArgumentCaptor.forClass(DownloadConditions.class); 3721 when(mockDownloader.startDownloading( 3722 any(String.class), 3723 any(GroupKey.class), 3724 anyInt(), 3725 anyLong(), 3726 any(String.class), 3727 any(Uri.class), 3728 any(String.class), 3729 anyInt(), 3730 downloadConditionsCaptor.capture(), 3731 isA(DownloaderCallbackImpl.class), 3732 anyInt(), 3733 anyList())) 3734 .then( 3735 new Answer<ListenableFuture<Void>>() { 3736 @Override 3737 public ListenableFuture<Void> answer(InvocationOnMock invocation) throws Throwable { 3738 writeSharedFiles( 3739 sharedFilesMetadata, 3740 fileGroup, 3741 ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE, FileStatus.DOWNLOAD_COMPLETE)); 3742 return Futures.immediateVoidFuture(); 3743 } 3744 }); 3745 3746 DataFileGroupInternal updatedFileGroup = 3747 fileGroup.toBuilder() 3748 .setBookkeeping( 3749 DataFileGroupBookkeeping.newBuilder() 3750 .setDownloadStartedCount(1) 3751 .setGroupDownloadStartedTimestampInMillis(1000L)) 3752 .build(); 3753 3754 // Calling with DownloadConditions = null will use the config from server. 3755 assertThat( 3756 fileGroupManager 3757 .downloadFileGroup(testKey, null /*downloadConditions*/, noCustomValidation()) 3758 .get()) 3759 .isEqualTo(updatedFileGroup); 3760 assertThat(downloadConditionsCaptor.getValue()).isEqualTo(downloadConditions); 3761 } 3762 3763 @Test testDownloadFileGroup_nonNullDownloadCondition()3764 public void testDownloadFileGroup_nonNullDownloadCondition() throws Exception { 3765 DownloadConditions downloadConditions = 3766 DownloadConditions.newBuilder() 3767 .setDeviceNetworkPolicy(DeviceNetworkPolicy.DOWNLOAD_ONLY_ON_WIFI) 3768 .setDeviceStoragePolicy(DeviceStoragePolicy.BLOCK_DOWNLOAD_IN_LOW_STORAGE) 3769 .build(); 3770 3771 // Write 1 group to the pending shared prefs. 3772 DataFileGroupInternal fileGroup = 3773 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2).toBuilder() 3774 .setDownloadConditions(downloadConditions) 3775 .build(); 3776 writePendingFileGroup(testKey, fileGroup); 3777 3778 writeSharedFiles( 3779 sharedFilesMetadata, 3780 fileGroup, 3781 ImmutableList.of(FileStatus.SUBSCRIBED, FileStatus.SUBSCRIBED)); 3782 3783 ArgumentCaptor<DownloadConditions> downloadConditionsCaptor = 3784 ArgumentCaptor.forClass(DownloadConditions.class); 3785 when(mockDownloader.startDownloading( 3786 any(String.class), 3787 any(GroupKey.class), 3788 anyInt(), 3789 anyLong(), 3790 any(String.class), 3791 any(Uri.class), 3792 any(String.class), 3793 anyInt(), 3794 downloadConditionsCaptor.capture(), 3795 isA(DownloaderCallbackImpl.class), 3796 anyInt(), 3797 anyList())) 3798 .then( 3799 new Answer<ListenableFuture<Void>>() { 3800 @Override 3801 public ListenableFuture<Void> answer(InvocationOnMock invocation) throws Throwable { 3802 writeSharedFiles( 3803 sharedFilesMetadata, 3804 fileGroup, 3805 ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE, FileStatus.DOWNLOAD_COMPLETE)); 3806 return Futures.immediateVoidFuture(); 3807 } 3808 }); 3809 3810 DownloadConditions downloadConditions2 = 3811 DownloadConditions.newBuilder() 3812 .setDeviceNetworkPolicy(DeviceNetworkPolicy.DOWNLOAD_ON_ANY_NETWORK) 3813 .setDeviceStoragePolicy(DeviceStoragePolicy.BLOCK_DOWNLOAD_IN_LOW_STORAGE) 3814 .build(); 3815 3816 DataFileGroupInternal updatedFileGroup = 3817 fileGroup.toBuilder() 3818 .setBookkeeping( 3819 DataFileGroupBookkeeping.newBuilder() 3820 .setDownloadStartedCount(1) 3821 .setGroupDownloadStartedTimestampInMillis(1000L)) 3822 .build(); 3823 3824 // downloadConditions2 will override the pendingGroup.downloadConditions 3825 assertThat( 3826 fileGroupManager 3827 .downloadFileGroup(testKey, downloadConditions2, noCustomValidation()) 3828 .get()) 3829 .isEqualTo(updatedFileGroup); 3830 assertThat(downloadConditionsCaptor.getValue()).isEqualTo(downloadConditions2); 3831 } 3832 3833 @Test testDownloadFileGroup_notFoundGroup()3834 public void testDownloadFileGroup_notFoundGroup() throws Exception { 3835 // Mock FileGroupsMetadata to test failure scenario. 3836 resetFileGroupManager(mockFileGroupsMetadata, sharedFileManager); 3837 // Can't find the group. 3838 ArgumentCaptor<GroupKey> groupKeyCaptor = ArgumentCaptor.forClass(GroupKey.class); 3839 when(mockFileGroupsMetadata.read(groupKeyCaptor.capture())) 3840 .thenReturn(Futures.immediateFuture(null)); 3841 3842 // Download not-found group will lead to failed future. 3843 ExecutionException exception = 3844 assertThrows( 3845 ExecutionException.class, 3846 () -> 3847 fileGroupManager 3848 .downloadFileGroup(testKey, null /*downloadConditions*/, noCustomValidation()) 3849 .get()); 3850 assertThat(exception).hasCauseThat().isInstanceOf(DownloadException.class); 3851 3852 // Make sure that file group manager attempted to read both pending key and downloaded key. 3853 assertThat(groupKeyCaptor.getAllValues()) 3854 .containsAtLeast(getPendingKey(testKey), getDownloadedKey(testKey)); 3855 } 3856 3857 @Test testDownloadFileGroup_downloadStartedTimestampAbsent()3858 public void testDownloadFileGroup_downloadStartedTimestampAbsent() throws Exception { 3859 // Write 1 group to the pending shared prefs. 3860 DataFileGroupInternal fileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2); 3861 fileGroup = 3862 fileGroup.toBuilder() 3863 .setOwnerPackage(context.getPackageName()) 3864 .setDownloadConditions(DownloadConditions.getDefaultInstance()) 3865 .setTrafficTag(TRAFFIC_TAG) 3866 .build(); 3867 writePendingFileGroup(testKey, fileGroup); 3868 3869 writeSharedFiles( 3870 sharedFilesMetadata, 3871 fileGroup, 3872 ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE, FileStatus.DOWNLOAD_COMPLETE)); 3873 3874 fileGroupManager 3875 .downloadFileGroup(testKey, DownloadConditions.getDefaultInstance(), noCustomValidation()) 3876 .get(); 3877 3878 DataFileGroupBookkeeping bookkeeping = readDownloadedFileGroup(testKey).getBookkeeping(); 3879 assertThat(bookkeeping.hasGroupDownloadStartedTimestampInMillis()).isTrue(); 3880 // Make sure that the download started timestamp is set to current time. 3881 assertThat(bookkeeping.getGroupDownloadStartedTimestampInMillis()) 3882 .isEqualTo(testClock.currentTimeMillis()); 3883 // Make sure that the download started count is accumulated. 3884 assertThat(bookkeeping.getDownloadStartedCount()).isEqualTo(1); 3885 3886 verify(mockLogger) 3887 .logEventSampled( 3888 MddClientEvent.Code.EVENT_CODE_UNSPECIFIED, 3889 fileGroup.getGroupName(), 3890 fileGroup.getFileGroupVersionNumber(), 3891 /* buildId= */ 0, 3892 /* variantId= */ ""); 3893 } 3894 3895 @Test testDownloadFileGroup_downloadStartedTimestampPresent()3896 public void testDownloadFileGroup_downloadStartedTimestampPresent() throws Exception { 3897 // Write 1 group to the pending shared prefs. 3898 DataFileGroupInternal fileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2); 3899 3900 fileGroup = 3901 fileGroup.toBuilder() 3902 .setOwnerPackage(context.getPackageName()) 3903 .setDownloadConditions(DownloadConditions.getDefaultInstance()) 3904 .setTrafficTag(TRAFFIC_TAG) 3905 .setBookkeeping( 3906 DataFileGroupBookkeeping.newBuilder() 3907 .setGroupDownloadStartedTimestampInMillis(123456) 3908 .setDownloadStartedCount(2)) 3909 .build(); 3910 writePendingFileGroup(testKey, fileGroup); 3911 3912 writeSharedFiles( 3913 sharedFilesMetadata, 3914 fileGroup, 3915 ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE, FileStatus.DOWNLOAD_COMPLETE)); 3916 3917 fileGroupManager 3918 .downloadFileGroup(testKey, DownloadConditions.getDefaultInstance(), noCustomValidation()) 3919 .get(); 3920 3921 DataFileGroupBookkeeping bookkeeping = readDownloadedFileGroup(testKey).getBookkeeping(); 3922 assertThat(bookkeeping.hasGroupDownloadStartedTimestampInMillis()).isTrue(); 3923 // Make sure that the download started timestamp is not changed. 3924 assertThat(bookkeeping.getGroupDownloadStartedTimestampInMillis()).isEqualTo(123456); 3925 // Make sure that the download started count is accumulated. 3926 assertThat(bookkeeping.getDownloadStartedCount()).isEqualTo(3); 3927 3928 verify(mockLogger, never()) 3929 .logEventSampled( 3930 eq(MddClientEvent.Code.EVENT_CODE_UNSPECIFIED), 3931 any(String.class), 3932 anyInt(), 3933 anyLong(), 3934 any(String.class)); 3935 } 3936 3937 @Test testDownloadFileGroup_updateBookkeepingOnDownloadFailed()3938 public void testDownloadFileGroup_updateBookkeepingOnDownloadFailed() throws Exception { 3939 // Mock FileGroupsMetadata to test failure scenario. 3940 resetFileGroupManager(mockFileGroupsMetadata, sharedFileManager); 3941 // Write 1 group to the pending shared prefs. 3942 DataFileGroupInternal fileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2); 3943 fileGroup = 3944 fileGroup.toBuilder() 3945 .setOwnerPackage(context.getPackageName()) 3946 .setDownloadConditions(DownloadConditions.getDefaultInstance()) 3947 .setTrafficTag(TRAFFIC_TAG) 3948 .build(); 3949 GroupKey pendingKey = testKey.toBuilder().setDownloaded(false).build(); 3950 when(mockFileGroupsMetadata.read(pendingKey)).thenReturn(Futures.immediateFuture(fileGroup)); 3951 3952 // All files are downloaded. 3953 writeSharedFiles( 3954 sharedFilesMetadata, 3955 fileGroup, 3956 ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE, FileStatus.DOWNLOAD_COMPLETE)); 3957 3958 ArgumentCaptor<DataFileGroupInternal> fileGroupCaptor = 3959 ArgumentCaptor.forClass(DataFileGroupInternal.class); 3960 when(mockFileGroupsMetadata.write(eq(pendingKey), fileGroupCaptor.capture())) 3961 .thenReturn(Futures.immediateFuture(false)); 3962 3963 ExecutionException executionException = 3964 assertThrows( 3965 ExecutionException.class, 3966 () -> 3967 fileGroupManager 3968 .downloadFileGroup( 3969 testKey, DownloadConditions.getDefaultInstance(), noCustomValidation()) 3970 .get()); 3971 assertThat(executionException).hasCauseThat().isInstanceOf(DownloadException.class); 3972 DownloadException downloadException = (DownloadException) executionException.getCause(); 3973 assertThat(downloadException).hasCauseThat().isInstanceOf(IOException.class); 3974 assertThat(downloadException.getDownloadResultCode()) 3975 .isEqualTo(DownloadResultCode.UNABLE_TO_UPDATE_GROUP_METADATA_ERROR); 3976 3977 DataFileGroupBookkeeping bookkeeping = fileGroupCaptor.getValue().getBookkeeping(); 3978 assertThat(bookkeeping.hasGroupDownloadStartedTimestampInMillis()).isTrue(); 3979 assertThat(bookkeeping.getGroupDownloadStartedTimestampInMillis()) 3980 .isEqualTo(testClock.currentTimeMillis()); 3981 3982 verify(mockLogger, never()) 3983 .logEventSampled( 3984 eq(MddClientEvent.Code.EVENT_CODE_UNSPECIFIED), 3985 any(String.class), 3986 anyInt(), 3987 anyLong(), 3988 any(String.class)); 3989 verify(mockLogger).logEventSampled(MddClientEvent.Code.EVENT_CODE_UNSPECIFIED); 3990 } 3991 3992 @Test testDownloadToBeSharedPendingGroup_success_lowSdk_notShared()3993 public void testDownloadToBeSharedPendingGroup_success_lowSdk_notShared() throws Exception { 3994 ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.R - 1); 3995 // Write 1 group to the pending shared prefs. 3996 DataFileGroupInternal fileGroup = 3997 createDataFileGroup( 3998 TEST_GROUP, 3999 /* fileCount= */ 0, 4000 /* downloadAttemptCount= */ 3, 4001 /* newFilesReceivedTimestamp= */ testClock.currentTimeMillis() - 500L); 4002 ExtraHttpHeader extraHttpHeader = 4003 ExtraHttpHeader.newBuilder().setKey("user-agent").setValue("mdd-downloader").build(); 4004 4005 fileGroup = 4006 fileGroup.toBuilder() 4007 .setOwnerPackage(context.getPackageName()) 4008 .setDownloadConditions(DownloadConditions.getDefaultInstance()) 4009 .setTrafficTag(TRAFFIC_TAG) 4010 .addGroupExtraHttpHeaders(extraHttpHeader) 4011 .addFile(0, MddTestUtil.createSharedDataFile(TEST_GROUP, 0)) 4012 .addFile(1, MddTestUtil.createDataFile(TEST_GROUP, 1)) 4013 .build(); 4014 writePendingFileGroup(testKey, fileGroup); 4015 4016 writeSharedFiles( 4017 sharedFilesMetadata, 4018 fileGroup, 4019 ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE, FileStatus.DOWNLOAD_COMPLETE)); 4020 4021 fileGroupManager 4022 .downloadFileGroup(testKey, DownloadConditions.getDefaultInstance(), noCustomValidation()) 4023 .get(); 4024 4025 // Verify that pending key is removed if download is complete. 4026 assertThat(readPendingFileGroup(testKey)).isNull(); 4027 4028 // Verify that downloaded key is written into metadata if download is complete. 4029 assertThat(readDownloadedFileGroup(testKey)).isNotNull(); 4030 verify(mockLogger) 4031 .logEventSampled( 4032 MddClientEvent.Code.EVENT_CODE_UNSPECIFIED, 4033 TEST_GROUP, 4034 /* fileGroupVersionNumber= */ 0, 4035 /* buildId= */ 0, 4036 /* variantId= */ ""); 4037 verify(mockLogger) 4038 .logMddDownloadResult( 4039 MddDownloadResult.Code.SUCCESS, 4040 createFileGroupDetails(fileGroup) 4041 .setOwnerPackage(context.getPackageName()) 4042 .clearFileCount() 4043 .build()); 4044 4045 verify(mockLogger) 4046 .logMddDownloadLatency( 4047 createFileGroupDetails(fileGroup).build(), 4048 createMddDownloadLatency( 4049 /* downloadAttemptCount= */ 4, 4050 /* downloadLatencyMs= */ 0L, 4051 /* totalLatencyMs= */ 500L)); 4052 4053 // exists only called once in tryToShareBeforeDownload 4054 verify(mockBackend, never()).exists(any()); 4055 // openForWrite is called in tryToShareBeforeDownload for copying the file and acquiring the 4056 // lease. 4057 verify(mockBackend, never()).openForWrite(any()); 4058 } 4059 4060 @Test testDownloadFileGroup_success_oneFileAndroidSharedAndDownloaded()4061 public void testDownloadFileGroup_success_oneFileAndroidSharedAndDownloaded() throws Exception { 4062 // Write 1 group to the pending shared prefs. 4063 DataFileGroupInternal fileGroup = 4064 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2).toBuilder() 4065 .setVariantId("test-variant") 4066 .setBuildId(10) 4067 .build(); 4068 NewFileKey[] keys = MddTestUtil.createFileKeysForDataFileGroupInternal(fileGroup); 4069 ExtraHttpHeader extraHttpHeader = 4070 ExtraHttpHeader.newBuilder().setKey("user-agent").setValue("mdd-downloader").build(); 4071 4072 fileGroup = 4073 fileGroup.toBuilder() 4074 .setOwnerPackage(context.getPackageName()) 4075 .setDownloadConditions(DownloadConditions.getDefaultInstance()) 4076 .setTrafficTag(TRAFFIC_TAG) 4077 .addGroupExtraHttpHeaders(extraHttpHeader) 4078 .build(); 4079 writePendingFileGroup(testKey, fileGroup); 4080 4081 writeSharedFiles( 4082 sharedFilesMetadata, 4083 fileGroup, 4084 ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE, FileStatus.DOWNLOAD_COMPLETE), 4085 /* androidShared */ ImmutableList.of(true, false)); 4086 4087 SharedFile file0 = sharedFileManager.getSharedFile(keys[0]).get(); 4088 SharedFile file1 = sharedFileManager.getSharedFile(keys[1]).get(); 4089 Uri blobUri = DirectoryUtil.getBlobUri(context, file0.getAndroidSharingChecksum()); 4090 Uri leaseUri = 4091 DirectoryUtil.getBlobStoreLeaseUri(context, file0.getAndroidSharingChecksum(), 0); 4092 4093 fileGroupManager 4094 .downloadFileGroup(testKey, DownloadConditions.getDefaultInstance(), noCustomValidation()) 4095 .get(); 4096 4097 // Verify that pending key is removed if download is complete. 4098 assertThat(readPendingFileGroup(testKey)).isNull(); 4099 4100 // Verify that downloaded key is written into metadata if download is complete. 4101 assertThat(readDownloadedFileGroup(testKey)).isNotNull(); 4102 verify(mockLogger) 4103 .logEventSampled( 4104 MddClientEvent.Code.EVENT_CODE_UNSPECIFIED, 4105 TEST_GROUP, 4106 /* fileGroupVersionNumber= */ 0, 4107 /* buildId= */ 10, 4108 /* variantId= */ "test-variant"); 4109 4110 verify(mockBackend, never()).exists(blobUri); 4111 verify(mockBackend, never()).openForWrite(blobUri); 4112 verify(mockBackend, never()).openForWrite(leaseUri); 4113 4114 assertThat(sharedFileManager.getSharedFile(keys[0]).get()).isEqualTo(file0); 4115 assertThat(sharedFileManager.getSharedFile(keys[1]).get()).isEqualTo(file1); 4116 4117 ArgumentCaptor<Void> mddAndroidSharingLogArgumentCaptor = ArgumentCaptor.forClass(Void.class); 4118 4119 verify(mockLogger).logMddAndroidSharingLog(mddAndroidSharingLogArgumentCaptor.capture()); 4120 Void mddAndroidSharingLog = null; 4121 Void expectedLog = null; 4122 assertThat(mddAndroidSharingLog).isEqualTo(expectedLog); 4123 } 4124 4125 @Test testDownloadFileGroup_pending_oneBlobExistsBeforeDownload()4126 public void testDownloadFileGroup_pending_oneBlobExistsBeforeDownload() throws Exception { 4127 // Write 1 group to the pending shared prefs. 4128 DataFileGroupInternal fileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 0); 4129 ExtraHttpHeader extraHttpHeader = 4130 ExtraHttpHeader.newBuilder().setKey("user-agent").setValue("mdd-downloader").build(); 4131 4132 fileGroup = 4133 fileGroup.toBuilder() 4134 .setOwnerPackage(context.getPackageName()) 4135 .setDownloadConditions(DownloadConditions.getDefaultInstance()) 4136 .setExpirationDateSecs(FILE_GROUP_EXPIRATION_DATE_SECS) 4137 .setTrafficTag(TRAFFIC_TAG) 4138 .addGroupExtraHttpHeaders(extraHttpHeader) 4139 .addFile(0, MddTestUtil.createSharedDataFile(TEST_GROUP, 0)) 4140 .addFile(1, MddTestUtil.createDataFile(TEST_GROUP, 1)) 4141 .build(); 4142 4143 NewFileKey[] keys = MddTestUtil.createFileKeysForDataFileGroupInternal(fileGroup); 4144 writePendingFileGroup(testKey, fileGroup); 4145 4146 writeSharedFiles( 4147 sharedFilesMetadata, 4148 fileGroup, 4149 ImmutableList.of(FileStatus.SUBSCRIBED, FileStatus.SUBSCRIBED)); 4150 4151 // File that can be shared 4152 DataFile file = fileGroup.getFile(0); 4153 Uri blobUri = DirectoryUtil.getBlobUri(context, file.getAndroidSharingChecksum()); 4154 Uri leaseUri = 4155 DirectoryUtil.getBlobStoreLeaseUri( 4156 context, file.getAndroidSharingChecksum(), FILE_GROUP_EXPIRATION_DATE_SECS); 4157 // The file is available in the blob storage 4158 when(mockBackend.exists(blobUri)).thenReturn(true); 4159 4160 // First file's download succeeds 4161 Uri onDeviceuri = 4162 DirectoryUtil.getOnDeviceUri( 4163 context, 4164 keys[0].getAllowedReaders(), 4165 file.getFileId(), 4166 keys[0].getChecksum(), 4167 mockSilentFeedback, 4168 /* instanceId= */ Optional.absent(), 4169 /* androidShared= */ false); 4170 fileDownloadSucceeds(keys[0], onDeviceuri); 4171 4172 // Second file's download succeeds 4173 onDeviceuri = 4174 DirectoryUtil.getOnDeviceUri( 4175 context, 4176 keys[1].getAllowedReaders(), 4177 fileGroup.getFile(1).getFileId(), 4178 keys[1].getChecksum(), 4179 mockSilentFeedback, 4180 /* instanceId= */ Optional.absent(), 4181 /* androidShared= */ false); 4182 fileDownloadSucceeds(keys[1], onDeviceuri); 4183 4184 fileGroupManager 4185 .downloadFileGroup(testKey, DownloadConditions.getDefaultInstance(), noCustomValidation()) 4186 .get(); 4187 4188 // Verify that the pending group is not part of pending groups prefs. 4189 assertThat(readPendingFileGroup(testKey)).isNull(); 4190 4191 // Verify that the downloaded group is still part of downloaded groups prefs. 4192 assertThat(readDownloadedFileGroup(testKey)).isNotNull(); 4193 4194 verify(mockLogger) 4195 .logEventSampled( 4196 MddClientEvent.Code.EVENT_CODE_UNSPECIFIED, 4197 TEST_GROUP, 4198 /* fileGroupVersionNumber= */ 0, 4199 /* buildId= */ 0, 4200 /* variantId= */ ""); 4201 4202 verify(mockBackend).exists(blobUri); 4203 // openForWrite is called only once in tryToShareBeforeDownload for acquiring the lease. 4204 verify(mockBackend, never()).openForWrite(blobUri); 4205 verify(mockBackend).openForWrite(leaseUri); 4206 4207 SharedFile expectedSharedFile0 = 4208 SharedFile.newBuilder() 4209 .setFileName("android_shared_sha256_1230") 4210 .setFileStatus(FileStatus.DOWNLOAD_COMPLETE) 4211 .setAndroidShared(true) 4212 .setAndroidSharingChecksum("sha256_1230") 4213 .setMaxExpirationDateSecs(FILE_GROUP_EXPIRATION_DATE_SECS) 4214 .build(); 4215 SharedFile expectedSharedFile1 = 4216 SharedFile.newBuilder() 4217 .setFileName(fileGroup.getFile(1).getFileId()) 4218 .setFileStatus(FileStatus.DOWNLOAD_COMPLETE) 4219 .setMaxExpirationDateSecs(FILE_GROUP_EXPIRATION_DATE_SECS) 4220 .build(); 4221 assertThat(sharedFileManager.getSharedFile(keys[0]).get()).isEqualTo(expectedSharedFile0); 4222 assertThat(sharedFileManager.getSharedFile(keys[1]).get()).isEqualTo(expectedSharedFile1); 4223 4224 ArgumentCaptor<Void> mddAndroidSharingLogArgumentCaptor = ArgumentCaptor.forClass(Void.class); 4225 4226 verify(mockLogger).logMddAndroidSharingLog(mddAndroidSharingLogArgumentCaptor.capture()); 4227 Void mddAndroidSharingLog = null; 4228 Void expectedLog = null; 4229 assertThat(mddAndroidSharingLog).isEqualTo(expectedLog); 4230 } 4231 4232 @Test testDownloadFileGroup_pending_oneBlobExistsAfterDownload()4233 public void testDownloadFileGroup_pending_oneBlobExistsAfterDownload() throws Exception { 4234 // Write 1 group to the pending shared prefs. 4235 DataFileGroupInternal tmpFileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 0); 4236 ExtraHttpHeader extraHttpHeader = 4237 ExtraHttpHeader.newBuilder().setKey("user-agent").setValue("mdd-downloader").build(); 4238 4239 final DataFileGroupInternal fileGroup = 4240 tmpFileGroup.toBuilder() 4241 .setOwnerPackage(context.getPackageName()) 4242 .setDownloadConditions(DownloadConditions.getDefaultInstance()) 4243 .setExpirationDateSecs(FILE_GROUP_EXPIRATION_DATE_SECS) 4244 .setTrafficTag(TRAFFIC_TAG) 4245 .addGroupExtraHttpHeaders(extraHttpHeader) 4246 .addFile(0, MddTestUtil.createSharedDataFile(TEST_GROUP, 0)) 4247 .addFile(1, MddTestUtil.createDataFile(TEST_GROUP, 1)) 4248 .build(); 4249 4250 NewFileKey[] keys = MddTestUtil.createFileKeysForDataFileGroupInternal(fileGroup); 4251 writePendingFileGroup(testKey, fileGroup); 4252 4253 writeSharedFiles( 4254 sharedFilesMetadata, 4255 fileGroup, 4256 ImmutableList.of(FileStatus.SUBSCRIBED, FileStatus.DOWNLOAD_COMPLETE)); 4257 4258 // File that can be shared 4259 DataFile file = fileGroup.getFile(0); 4260 Uri blobUri = DirectoryUtil.getBlobUri(context, file.getAndroidSharingChecksum()); 4261 Uri leaseUri = 4262 DirectoryUtil.getBlobStoreLeaseUri( 4263 context, file.getAndroidSharingChecksum(), FILE_GROUP_EXPIRATION_DATE_SECS); 4264 4265 // The file isn't available in the blob storage when tryToShareBeforeDownload is called 4266 when(mockBackend.exists(blobUri)).thenReturn(false); 4267 4268 simulateDownload(file, file.getFileId()); 4269 Uri onDeviceuri = 4270 DirectoryUtil.getOnDeviceUri( 4271 context, 4272 keys[0].getAllowedReaders(), 4273 file.getFileId(), 4274 keys[0].getChecksum(), 4275 mockSilentFeedback, 4276 /* instanceId= */ Optional.absent(), 4277 /* androidShared= */ false); 4278 assertThat(fileStorage.exists(onDeviceuri)).isTrue(); 4279 4280 when(mockDownloader.startDownloading( 4281 any(String.class), 4282 any(GroupKey.class), 4283 anyInt(), 4284 anyLong(), 4285 any(String.class), 4286 eq(onDeviceuri), 4287 any(String.class), 4288 anyInt(), 4289 any(DownloadConditions.class), 4290 isA(DownloaderCallbackImpl.class), 4291 anyInt(), 4292 anyList())) 4293 .then( 4294 new Answer<ListenableFuture<Void>>() { 4295 @Override 4296 public ListenableFuture<Void> answer(InvocationOnMock invocation) throws Throwable { 4297 // The file now exists in the shared storage 4298 when(mockBackend.exists(blobUri)).thenReturn(true); 4299 writeSharedFiles( 4300 sharedFilesMetadata, 4301 fileGroup, 4302 ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE, FileStatus.DOWNLOAD_COMPLETE)); 4303 return Futures.immediateVoidFuture(); 4304 } 4305 }); 4306 4307 fileGroupManager 4308 .downloadFileGroup(testKey, DownloadConditions.getDefaultInstance(), noCustomValidation()) 4309 .get(); 4310 4311 // Verify that pending key is removed if download is complete. 4312 assertThat(readPendingFileGroup(testKey)).isNull(); 4313 4314 // Verify that downloaded key is written into metadata if download is complete. 4315 assertThat(readDownloadedFileGroup(testKey)).isNotNull(); 4316 verify(mockLogger) 4317 .logEventSampled( 4318 MddClientEvent.Code.EVENT_CODE_UNSPECIFIED, 4319 TEST_GROUP, 4320 /* fileGroupVersionNumber= */ 0, 4321 /* buildId= */ 0, 4322 /* variantId= */ ""); 4323 4324 // exists called once in tryToShareBeforeDownload and once in tryToShareAfterDownload 4325 verify(mockBackend, times(2)).exists(blobUri); 4326 // openForWrite is called only once in tryToShareAfterDownload for acquiring the lease. 4327 verify(mockBackend, never()).openForWrite(blobUri); 4328 verify(mockBackend).openForWrite(leaseUri); 4329 4330 SharedFile expectedSharedFile0 = 4331 SharedFile.newBuilder() 4332 .setFileName("android_shared_sha256_1230") 4333 .setFileStatus(FileStatus.DOWNLOAD_COMPLETE) 4334 .setAndroidShared(true) 4335 .setAndroidSharingChecksum("sha256_1230") 4336 .setMaxExpirationDateSecs(FILE_GROUP_EXPIRATION_DATE_SECS) 4337 .build(); 4338 SharedFile expectedSharedFile1 = 4339 SharedFile.newBuilder() 4340 .setFileName(fileGroup.getFile(1).getFileId()) 4341 .setFileStatus(FileStatus.DOWNLOAD_COMPLETE) 4342 .setMaxExpirationDateSecs(FILE_GROUP_EXPIRATION_DATE_SECS) 4343 .build(); 4344 assertThat(sharedFileManager.getSharedFile(keys[0]).get()).isEqualTo(expectedSharedFile0); 4345 assertThat(sharedFileManager.getSharedFile(keys[1]).get()).isEqualTo(expectedSharedFile1); 4346 4347 // Local copy has not been deleted. 4348 assertThat(fileStorage.exists(onDeviceuri)).isTrue(); 4349 4350 ArgumentCaptor<Void> mddAndroidSharingLogArgumentCaptor = ArgumentCaptor.forClass(Void.class); 4351 4352 verify(mockLogger).logMddAndroidSharingLog(mddAndroidSharingLogArgumentCaptor.capture()); 4353 Void mddAndroidSharingLog = null; 4354 Void expectedLog = null; 4355 assertThat(mddAndroidSharingLog).isEqualTo(expectedLog); 4356 } 4357 4358 @Test testDownloadFileGroup_success_oneFileCanBeCopiedBeforeDownload()4359 public void testDownloadFileGroup_success_oneFileCanBeCopiedBeforeDownload() throws Exception { 4360 File tempFile = folder.newFile("blobFile"); 4361 // Write 1 group to the pending shared prefs. 4362 DataFileGroupInternal fileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 0); 4363 ExtraHttpHeader extraHttpHeader = 4364 ExtraHttpHeader.newBuilder().setKey("user-agent").setValue("mdd-downloader").build(); 4365 4366 fileGroup = 4367 fileGroup.toBuilder() 4368 .setOwnerPackage(context.getPackageName()) 4369 .setDownloadConditions(DownloadConditions.getDefaultInstance()) 4370 .setTrafficTag(TRAFFIC_TAG) 4371 .addGroupExtraHttpHeaders(extraHttpHeader) 4372 .addFile(0, MddTestUtil.createSharedDataFile(TEST_GROUP, 0)) 4373 .addFile(1, MddTestUtil.createDataFile(TEST_GROUP, 1)) 4374 .build(); 4375 NewFileKey[] keys = MddTestUtil.createFileKeysForDataFileGroupInternal(fileGroup); 4376 writePendingFileGroup(testKey, fileGroup); 4377 4378 // All files are downloaded. 4379 writeSharedFiles( 4380 sharedFilesMetadata, 4381 fileGroup, 4382 ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE, FileStatus.DOWNLOAD_COMPLETE)); 4383 4384 DataFile file = fileGroup.getFile(0); 4385 Uri blobUri = DirectoryUtil.getBlobUri(context, file.getAndroidSharingChecksum()); 4386 Uri leaseUri = DirectoryUtil.getBlobStoreLeaseUri(context, file.getAndroidSharingChecksum(), 0); 4387 // The file isn't available yet in the blob storage 4388 when(mockBackend.exists(blobUri)).thenReturn(false); 4389 // File that can be copied to the blob storage 4390 when(mockBackend.openForWrite(blobUri)).thenReturn(new FileOutputStream(tempFile)); 4391 4392 File onDeviceFile = simulateDownload(file, fileGroup.getFile(0).getFileId()); 4393 Uri onDeviceuri = 4394 DirectoryUtil.getOnDeviceUri( 4395 context, 4396 keys[0].getAllowedReaders(), 4397 fileGroup.getFile(0).getFileId(), 4398 keys[0].getChecksum(), 4399 mockSilentFeedback, 4400 /* instanceId= */ Optional.absent(), 4401 /* androidShared= */ false); 4402 assertThat(fileStorage.exists(onDeviceuri)).isTrue(); 4403 4404 fileGroupManager 4405 .downloadFileGroup(testKey, DownloadConditions.getDefaultInstance(), noCustomValidation()) 4406 .get(); 4407 4408 // Verify that pending key is removed if download is complete. 4409 assertThat(readPendingFileGroup(testKey)).isNull(); 4410 4411 // Verify that downloaded key is written into metadata if download is complete. 4412 assertThat(readDownloadedFileGroup(testKey)).isNotNull(); 4413 verify(mockLogger) 4414 .logEventSampled( 4415 MddClientEvent.Code.EVENT_CODE_UNSPECIFIED, 4416 TEST_GROUP, 4417 /* fileGroupVersionNumber= */ 0, 4418 /* buildId= */ 0, 4419 /* variantId= */ ""); 4420 4421 // exists only called once in tryToShareBeforeDownload 4422 verify(mockBackend).exists(blobUri); 4423 // openForWrite is called in tryToShareBeforeDownload for copying the file and acquiring the 4424 // lease. 4425 verify(mockBackend).openForWrite(blobUri); 4426 verify(mockBackend).openForWrite(leaseUri); 4427 4428 SharedFile expectedSharedFile0 = 4429 SharedFile.newBuilder() 4430 .setFileName("android_shared_sha256_1230") 4431 .setFileStatus(FileStatus.DOWNLOAD_COMPLETE) 4432 .setAndroidShared(true) 4433 .setAndroidSharingChecksum("sha256_1230") 4434 .setMaxExpirationDateSecs(0) 4435 .build(); 4436 SharedFile expectedSharedFile1 = 4437 SharedFile.newBuilder() 4438 .setFileName(fileGroup.getFile(1).getFileId()) 4439 .setFileStatus(FileStatus.DOWNLOAD_COMPLETE) 4440 .build(); 4441 assertThat(sharedFileManager.getSharedFile(keys[0]).get()).isEqualTo(expectedSharedFile0); 4442 assertThat(sharedFileManager.getSharedFile(keys[1]).get()).isEqualTo(expectedSharedFile1); 4443 4444 assertThat(fileStorage.exists(onDeviceuri)).isTrue(); 4445 onDeviceFile.delete(); 4446 4447 ArgumentCaptor<Void> mddAndroidSharingLogArgumentCaptor = ArgumentCaptor.forClass(Void.class); 4448 4449 verify(mockLogger).logMddAndroidSharingLog(mddAndroidSharingLogArgumentCaptor.capture()); 4450 Void mddAndroidSharingLog = null; 4451 Void expectedLog = null; 4452 assertThat(mddAndroidSharingLog).isEqualTo(expectedLog); 4453 } 4454 4455 @Test testDownloadFileGroup_oneFileCanBeCopiedAfterDownload()4456 public void testDownloadFileGroup_oneFileCanBeCopiedAfterDownload() throws Exception { 4457 File tempFile = folder.newFile("blobFile"); 4458 // Write 1 group to the pending shared prefs. 4459 DataFileGroupInternal tmpFfileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 0); 4460 ExtraHttpHeader extraHttpHeader = 4461 ExtraHttpHeader.newBuilder().setKey("user-agent").setValue("mdd-downloader").build(); 4462 4463 final DataFileGroupInternal fileGroup = 4464 tmpFfileGroup.toBuilder() 4465 .setOwnerPackage(context.getPackageName()) 4466 .setDownloadConditions(DownloadConditions.getDefaultInstance()) 4467 .setTrafficTag(TRAFFIC_TAG) 4468 .addGroupExtraHttpHeaders(extraHttpHeader) 4469 .addFile(0, MddTestUtil.createSharedDataFile(TEST_GROUP, 0)) 4470 .addFile(1, MddTestUtil.createDataFile(TEST_GROUP, 1)) 4471 .build(); 4472 NewFileKey[] keys = MddTestUtil.createFileKeysForDataFileGroupInternal(fileGroup); 4473 writePendingFileGroup(testKey, fileGroup); 4474 4475 writeSharedFiles( 4476 sharedFilesMetadata, 4477 fileGroup, 4478 ImmutableList.of(FileStatus.SUBSCRIBED, FileStatus.DOWNLOAD_COMPLETE)); 4479 4480 // File that can be copied to the blob storage 4481 DataFile file = fileGroup.getFile(0); 4482 Uri blobUri = DirectoryUtil.getBlobUri(context, file.getAndroidSharingChecksum()); 4483 Uri leaseUri = DirectoryUtil.getBlobStoreLeaseUri(context, file.getAndroidSharingChecksum(), 0); 4484 // The file is available in the blob storage 4485 when(mockBackend.exists(blobUri)).thenReturn(false); 4486 4487 simulateDownload(file, file.getFileId()); 4488 Uri onDeviceuri = 4489 DirectoryUtil.getOnDeviceUri( 4490 context, 4491 keys[0].getAllowedReaders(), 4492 file.getFileId(), 4493 keys[0].getChecksum(), 4494 mockSilentFeedback, 4495 /* instanceId= */ Optional.absent(), 4496 /* androidShared= */ false); 4497 assertThat(fileStorage.exists(onDeviceuri)).isTrue(); 4498 4499 when(mockDownloader.startDownloading( 4500 any(String.class), 4501 any(GroupKey.class), 4502 anyInt(), 4503 anyLong(), 4504 any(String.class), 4505 eq(onDeviceuri), 4506 any(String.class), 4507 anyInt(), 4508 any(DownloadConditions.class), 4509 isA(DownloaderCallbackImpl.class), 4510 anyInt(), 4511 anyList())) 4512 .then( 4513 new Answer<ListenableFuture<Void>>() { 4514 @Override 4515 public ListenableFuture<Void> answer(InvocationOnMock invocation) throws Throwable { 4516 // The file will be copied in tryToShareAfterDownload 4517 when(mockBackend.openForWrite(blobUri)).thenReturn(new FileOutputStream(tempFile)); 4518 writeSharedFiles( 4519 sharedFilesMetadata, 4520 fileGroup, 4521 ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE, FileStatus.DOWNLOAD_COMPLETE)); 4522 return Futures.immediateVoidFuture(); 4523 } 4524 }); 4525 4526 fileGroupManager 4527 .downloadFileGroup(testKey, DownloadConditions.getDefaultInstance(), noCustomValidation()) 4528 .get(); 4529 4530 // Verify that pending key is removed if download is complete. 4531 assertThat(readPendingFileGroup(testKey)).isNull(); 4532 4533 // Verify that downloaded key is written into metadata if download is complete. 4534 assertThat(readDownloadedFileGroup(testKey)).isNotNull(); 4535 verify(mockLogger) 4536 .logEventSampled( 4537 MddClientEvent.Code.EVENT_CODE_UNSPECIFIED, 4538 TEST_GROUP, 4539 /* fileGroupVersionNumber= */ 0, 4540 /* buildId= */ 0, 4541 /* variantId= */ ""); 4542 4543 // exists only called once in tryToShareBeforeDownload, once in tryToShareAfterDownload 4544 verify(mockBackend, times(2)).exists(blobUri); 4545 // File copied once in tryToShareAfterDownload 4546 verify(mockBackend).openForWrite(blobUri); 4547 verify(mockBackend).openForWrite(leaseUri); 4548 4549 SharedFile expectedSharedFile0 = 4550 SharedFile.newBuilder() 4551 .setFileName("android_shared_sha256_1230") 4552 .setFileStatus(FileStatus.DOWNLOAD_COMPLETE) 4553 .setAndroidShared(true) 4554 .setAndroidSharingChecksum("sha256_1230") 4555 .setMaxExpirationDateSecs(0) 4556 .build(); 4557 SharedFile expectedSharedFile1 = 4558 SharedFile.newBuilder() 4559 .setFileName(fileGroup.getFile(1).getFileId()) 4560 .setFileStatus(FileStatus.DOWNLOAD_COMPLETE) 4561 .build(); 4562 assertThat(sharedFileManager.getSharedFile(keys[0]).get()).isEqualTo(expectedSharedFile0); 4563 assertThat(sharedFileManager.getSharedFile(keys[1]).get()).isEqualTo(expectedSharedFile1); 4564 4565 // Local copy has not been deleted. 4566 assertThat(fileStorage.exists(onDeviceuri)).isTrue(); 4567 4568 ArgumentCaptor<Void> mddAndroidSharingLogArgumentCaptor = ArgumentCaptor.forClass(Void.class); 4569 4570 verify(mockLogger).logMddAndroidSharingLog(mddAndroidSharingLogArgumentCaptor.capture()); 4571 Void mddAndroidSharingLog = null; 4572 Void expectedLog = null; 4573 assertThat(mddAndroidSharingLog).isEqualTo(expectedLog); 4574 } 4575 4576 @Test testDownloadFileGroup_nonToBeSharedFile_neverShared()4577 public void testDownloadFileGroup_nonToBeSharedFile_neverShared() throws Exception { 4578 // Write 1 group to the pending shared prefs. 4579 DataFileGroupInternal tmpFileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1); 4580 ExtraHttpHeader extraHttpHeader = 4581 ExtraHttpHeader.newBuilder().setKey("user-agent").setValue("mdd-downloader").build(); 4582 4583 final DataFileGroupInternal fileGroup = 4584 tmpFileGroup.toBuilder() 4585 .setOwnerPackage(context.getPackageName()) 4586 .setDownloadConditions(DownloadConditions.getDefaultInstance()) 4587 .setExpirationDateSecs(FILE_GROUP_EXPIRATION_DATE_SECS) 4588 .setTrafficTag(TRAFFIC_TAG) 4589 .addGroupExtraHttpHeaders(extraHttpHeader) 4590 .build(); 4591 NewFileKey[] keys = MddTestUtil.createFileKeysForDataFileGroupInternal(fileGroup); 4592 writePendingFileGroup(testKey, fileGroup); 4593 4594 writeSharedFiles(sharedFilesMetadata, fileGroup, ImmutableList.of(FileStatus.SUBSCRIBED)); 4595 4596 DataFile file = fileGroup.getFile(0); 4597 File onDeviceFile = simulateDownload(file, file.getFileId()); 4598 Uri onDeviceuri = 4599 DirectoryUtil.getOnDeviceUri( 4600 context, 4601 keys[0].getAllowedReaders(), 4602 file.getFileId(), 4603 keys[0].getChecksum(), 4604 mockSilentFeedback, 4605 /* instanceId= */ Optional.absent(), 4606 /* androidShared= */ false); 4607 assertThat(fileStorage.exists(onDeviceuri)).isTrue(); 4608 4609 fileDownloadSucceeds(keys[0], onDeviceuri); 4610 4611 fileGroupManager 4612 .downloadFileGroup(testKey, DownloadConditions.getDefaultInstance(), noCustomValidation()) 4613 .get(); 4614 4615 // Verify that pending key is removed if download is complete. 4616 assertThat(readPendingFileGroup(testKey)).isNull(); 4617 4618 // Verify that downloaded key is written into metadata if download is complete. 4619 assertThat(readDownloadedFileGroup(testKey)).isNotNull(); 4620 verify(mockLogger) 4621 .logEventSampled( 4622 MddClientEvent.Code.EVENT_CODE_UNSPECIFIED, 4623 TEST_GROUP, 4624 /* fileGroupVersionNumber= */ 0, 4625 /* buildId= */ 0, 4626 /* variantId= */ ""); 4627 4628 verify(mockBackend, never()).exists(any()); 4629 verify(mockBackend, never()).openForWrite(any()); 4630 4631 SharedFile expectedSharedFile = 4632 SharedFile.newBuilder() 4633 .setFileName(file.getFileId()) 4634 .setFileStatus(FileStatus.DOWNLOAD_COMPLETE) 4635 .setMaxExpirationDateSecs(FILE_GROUP_EXPIRATION_DATE_SECS) 4636 .build(); 4637 assertThat(sharedFileManager.getSharedFile(keys[0]).get()).isEqualTo(expectedSharedFile); 4638 4639 assertThat(fileStorage.exists(onDeviceuri)).isTrue(); 4640 onDeviceFile.delete(); 4641 } 4642 4643 @Test testDownloadFileGroup_androidSharingFails()4644 public void testDownloadFileGroup_androidSharingFails() throws Exception { 4645 // Write 1 group to the pending shared prefs. 4646 DataFileGroupInternal tmpFileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 0); 4647 ExtraHttpHeader extraHttpHeader = 4648 ExtraHttpHeader.newBuilder().setKey("user-agent").setValue("mdd-downloader").build(); 4649 4650 final DataFileGroupInternal fileGroup = 4651 tmpFileGroup.toBuilder() 4652 .setOwnerPackage(context.getPackageName()) 4653 .setDownloadConditions(DownloadConditions.getDefaultInstance()) 4654 .setTrafficTag(TRAFFIC_TAG) 4655 .addGroupExtraHttpHeaders(extraHttpHeader) 4656 .addFile(0, MddTestUtil.createDataFile(TEST_GROUP, 0)) 4657 .addFile(1, MddTestUtil.createSharedDataFile(TEST_GROUP, 1)) 4658 .build(); 4659 NewFileKey[] keys = MddTestUtil.createFileKeysForDataFileGroupInternal(fileGroup); 4660 writePendingFileGroup(testKey, fileGroup); 4661 4662 writeSharedFiles( 4663 sharedFilesMetadata, 4664 fileGroup, 4665 ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE, FileStatus.DOWNLOAD_COMPLETE)); 4666 4667 // Second file fails with file storage I/O exception when called from tryToShareBeforeDownload 4668 // and tryToShareAfterDownload. 4669 DataFile file = fileGroup.getFile(1); 4670 Uri blobUri = DirectoryUtil.getBlobUri(context, file.getAndroidSharingChecksum()); 4671 when(mockBackend.exists(blobUri)).thenThrow(new IOException()); 4672 4673 // Any error during sharing doesn't stop the download: the file will be stored locally. 4674 fileGroupManager 4675 .downloadFileGroup(testKey, DownloadConditions.getDefaultInstance(), noCustomValidation()) 4676 .get(); 4677 4678 // Verify that pending key is removed if download is complete. 4679 assertThat(readPendingFileGroup(testKey)).isNull(); 4680 4681 // Verify that downloaded key is written into metadata if download is complete. 4682 assertThat(readDownloadedFileGroup(testKey)).isNotNull(); 4683 4684 verify(mockBackend, times(2)).exists(blobUri); 4685 verify(mockBackend, never()).openForWrite(any()); 4686 4687 SharedFile expectedSharedFile0 = 4688 SharedFile.newBuilder() 4689 .setFileName(fileGroup.getFile(0).getFileId()) 4690 .setFileStatus(FileStatus.DOWNLOAD_COMPLETE) 4691 .build(); 4692 SharedFile expectedSharedFile1 = 4693 expectedSharedFile0.toBuilder().setFileName(fileGroup.getFile(1).getFileId()).build(); 4694 assertThat(sharedFileManager.getSharedFile(keys[0]).get()).isEqualTo(expectedSharedFile0); 4695 assertThat(sharedFileManager.getSharedFile(keys[1]).get()).isEqualTo(expectedSharedFile1); 4696 4697 verify(mockLogger) 4698 .logEventSampled( 4699 MddClientEvent.Code.EVENT_CODE_UNSPECIFIED, 4700 TEST_GROUP, 4701 /* fileGroupVersionNumber= */ 0, 4702 /* buildId= */ 0, 4703 /* variantId= */ ""); 4704 verify(mockLogger) 4705 .logMddDownloadResult( 4706 MddDownloadResult.Code.SUCCESS, 4707 DataDownloadFileGroupStats.newBuilder() 4708 .setFileGroupName(TEST_GROUP) 4709 .setOwnerPackage(context.getPackageName()) 4710 .setFileGroupVersionNumber(0) 4711 .setBuildId(0) 4712 .setVariantId("") 4713 .build()); 4714 4715 ArgumentCaptor<Void> mddAndroidSharingLogArgumentCaptor = ArgumentCaptor.forClass(Void.class); 4716 4717 verify(mockLogger, times(2)) 4718 .logMddAndroidSharingLog(mddAndroidSharingLogArgumentCaptor.capture()); 4719 Void mddAndroidSharingLogBeforeAndAfterDownload = null; 4720 assertThat(mddAndroidSharingLogArgumentCaptor.getAllValues()) 4721 .containsExactly( 4722 mddAndroidSharingLogBeforeAndAfterDownload, mddAndroidSharingLogBeforeAndAfterDownload); 4723 } 4724 4725 @Test testDownloadFileGroup_skipsSideloadedFiles()4726 public void testDownloadFileGroup_skipsSideloadedFiles() throws Exception { 4727 // Create sideloaded group with normal file 4728 DataFileGroupInternal sideloadedGroup = 4729 DataFileGroupInternal.newBuilder() 4730 .setGroupName(TEST_GROUP) 4731 .addFile( 4732 DataFile.newBuilder() 4733 .setFileId("sideloaded_file") 4734 .setUrlToDownload("file:/test") 4735 .setChecksumType(DataFile.ChecksumType.NONE) 4736 .build()) 4737 .addFile( 4738 DataFile.newBuilder() 4739 .setFileId("normal_file") 4740 .setUrlToDownload("https://url.to.download") 4741 .setChecksumType(DataFile.ChecksumType.NONE) 4742 .build()) 4743 .build(); 4744 NewFileKey normalFileKey = 4745 SharedFilesMetadata.createKeyFromDataFile( 4746 sideloadedGroup.getFile(1), sideloadedGroup.getAllowedReadersEnum()); 4747 4748 // Write group as pending since we are waiting on normal file 4749 writePendingFileGroup(testKey, sideloadedGroup); 4750 SharedFile normalSharedFile = 4751 SharedFile.newBuilder() 4752 .setFileName(sideloadedGroup.getFile(1).getFileId()) 4753 .setFileStatus(FileStatus.SUBSCRIBED) 4754 .build(); 4755 sharedFilesMetadata.write(normalFileKey, normalSharedFile).get(); 4756 4757 // Mock that download of normal file succeeds 4758 Uri normalFileUri = 4759 DirectoryUtil.getOnDeviceUri( 4760 context, 4761 normalFileKey.getAllowedReaders(), 4762 sideloadedGroup.getFile(1).getFileId(), 4763 sideloadedGroup.getFile(1).getChecksum(), 4764 mockSilentFeedback, 4765 /* instanceId= */ Optional.absent(), 4766 false); 4767 fileDownloadSucceeds(normalFileKey, normalFileUri); 4768 4769 fileGroupManager 4770 .downloadFileGroup(testKey, DownloadConditions.getDefaultInstance(), noCustomValidation()) 4771 .get(); 4772 4773 assertThat(readPendingFileGroup(testKey)).isNull(); 4774 assertThat(readDownloadedFileGroup(testKey)).isNotNull(); 4775 4776 verify(mockDownloader) 4777 .startDownloading( 4778 eq(sideloadedGroup.getFile(1).getChecksum()), 4779 eq(testKey), 4780 anyInt(), 4781 anyLong(), 4782 any(String.class), 4783 eq(normalFileUri), 4784 eq(sideloadedGroup.getFile(1).getUrlToDownload()), 4785 anyInt(), 4786 any(), 4787 any(), 4788 anyInt(), 4789 anyList()); 4790 } 4791 4792 @Test testDownloadFileGroup_whenMultipleVariantsExist_downloadsSpecifiedVariant()4793 public void testDownloadFileGroup_whenMultipleVariantsExist_downloadsSpecifiedVariant() 4794 throws Exception { 4795 GroupKey defaultGroupKey = 4796 GroupKey.newBuilder() 4797 .setGroupName(TEST_GROUP) 4798 .setOwnerPackage(context.getPackageName()) 4799 .build(); 4800 GroupKey enGroupKey = defaultGroupKey.toBuilder().setVariantId("en").build(); 4801 4802 DataFileGroupInternal defaultFileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1); 4803 // Create EN with custom file ids so it doesn't overlap with the default file group. 4804 DataFileGroupInternal enFileGroup = 4805 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 0).toBuilder() 4806 .addFile(MddTestUtil.createDataFile("en", 0)) 4807 .addFile(MddTestUtil.createDataFile("en", 1)) 4808 .build(); 4809 4810 writePendingFileGroup(getPendingKey(defaultGroupKey), defaultFileGroup); 4811 writePendingFileGroup(getPendingKey(enGroupKey), enFileGroup); 4812 4813 writeSharedFiles( 4814 sharedFilesMetadata, defaultFileGroup, ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE)); 4815 4816 fileGroupManager 4817 .downloadFileGroup( 4818 defaultGroupKey, DownloadConditions.getDefaultInstance(), noCustomValidation()) 4819 .get(); 4820 4821 // Verify that correct group was downloaded 4822 assertThat(readPendingFileGroup(defaultGroupKey)).isNull(); 4823 assertThat(readDownloadedFileGroup(defaultGroupKey)).isNotNull(); 4824 4825 assertThat(readPendingFileGroup(enGroupKey)).isNotNull(); 4826 assertThat(readDownloadedFileGroup(enGroupKey)).isNull(); 4827 4828 // Attempt to download en group and check that it is now downloaded 4829 writeSharedFiles( 4830 sharedFilesMetadata, 4831 enFileGroup, 4832 ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE, FileStatus.DOWNLOAD_COMPLETE)); 4833 4834 fileGroupManager 4835 .downloadFileGroup( 4836 enGroupKey, DownloadConditions.getDefaultInstance(), noCustomValidation()) 4837 .get(); 4838 4839 // Verify that correct group was downloaded 4840 assertThat(readPendingFileGroup(defaultGroupKey)).isNull(); 4841 assertThat(readDownloadedFileGroup(defaultGroupKey)).isNotNull(); 4842 4843 assertThat(readPendingFileGroup(enGroupKey)).isNull(); 4844 assertThat(readDownloadedFileGroup(enGroupKey)).isNotNull(); 4845 } 4846 4847 @Test testDownloadAllPendingGroups_onWifi()4848 public void testDownloadAllPendingGroups_onWifi() throws Exception { 4849 // Write 3 groups to the pending shared prefs. 4850 // MDD successfully downloaded filegroup1, partially downloaded filegroup2 and failed to 4851 // download filegroup3. 4852 DataFileGroupInternal fileGroup1 = 4853 createDataFileGroup( 4854 TEST_GROUP, 4855 /* fileCount= */ 2, 4856 /* downloadAttemptCount= */ 7, 4857 /* newFilesReceivedTimestamp= */ testClock.currentTimeMillis() - 500L); 4858 fileGroup1 = 4859 fileGroup1.toBuilder() 4860 .setDownloadConditions(DownloadConditions.getDefaultInstance()) 4861 .build(); 4862 writePendingFileGroup(testKey, fileGroup1); 4863 // All files are downloaded. 4864 writeSharedFiles( 4865 sharedFilesMetadata, 4866 fileGroup1, 4867 ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE, FileStatus.DOWNLOAD_COMPLETE)); 4868 4869 DataFileGroupInternal fileGroup2 = MddTestUtil.createDataFileGroupInternal(TEST_GROUP_2, 2); 4870 fileGroup2 = 4871 fileGroup2.toBuilder() 4872 .setDownloadConditions(DownloadConditions.getDefaultInstance()) 4873 .build(); 4874 writePendingFileGroup(testKey2, fileGroup2); 4875 // Not all files are downloaded. 4876 writeSharedFiles( 4877 sharedFilesMetadata, 4878 fileGroup2, 4879 ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE, FileStatus.DOWNLOAD_IN_PROGRESS)); 4880 4881 GroupKey expectedKey2 = testKey2.toBuilder().setDownloaded(false).build(); 4882 // The file status isn't changed to DOWNLOAD_COMPLETE, it remains DOWNLOAD_IN_PROGRESS. 4883 // An UNKNOWN_ERROR is logged. 4884 when(mockDownloader.getInProgressFuture(any(String.class), any(Uri.class))) 4885 .thenReturn(Futures.immediateFuture(Optional.of(Futures.immediateVoidFuture()))); 4886 4887 DataFileGroupInternal tmpFileGroup3 = MddTestUtil.createDataFileGroupInternal(TEST_GROUP_3, 2); 4888 final DataFileGroupInternal fileGroup3 = 4889 tmpFileGroup3.toBuilder() 4890 .setDownloadConditions( 4891 DownloadConditions.newBuilder().setDownloadFirstOnWifiPeriodSecs(1000000)) 4892 .build(); 4893 writePendingFileGroup(testKey3, fileGroup3); 4894 // Not all files are downloaded. 4895 writeSharedFiles( 4896 sharedFilesMetadata, 4897 fileGroup3, 4898 ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE, FileStatus.DOWNLOAD_FAILED)); 4899 4900 GroupKey expectedKey3 = testKey3.toBuilder().setDownloaded(false).build(); 4901 // One file fails, status is DOWNLOAD_FAILED but the downloader returns an 4902 // immediateVoidFuture. An UNKNOWN_ERROR is logged. 4903 when(mockDownloader.startDownloading( 4904 any(String.class), 4905 eq(expectedKey3), 4906 anyInt(), 4907 anyLong(), 4908 any(String.class), 4909 any(Uri.class), 4910 any(String.class), 4911 anyInt(), 4912 any(DownloadConditions.class), 4913 isA(DownloaderCallbackImpl.class), 4914 anyInt(), 4915 anyList())) 4916 .then( 4917 new Answer<ListenableFuture<Void>>() { 4918 @Override 4919 public ListenableFuture<Void> answer(InvocationOnMock invocation) throws Throwable { 4920 writeSharedFiles( 4921 sharedFilesMetadata, 4922 fileGroup3, 4923 ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE, FileStatus.DOWNLOAD_FAILED)); 4924 return Futures.immediateVoidFuture(); 4925 } 4926 }); 4927 4928 fileGroupManager.scheduleAllPendingGroupsForDownload(true, noCustomValidation()).get(); 4929 4930 verify(mockLogger) 4931 .logMddDownloadLatency( 4932 createFileGroupDetails(fileGroup1).build(), 4933 createMddDownloadLatency( 4934 /* downloadAttemptCount= */ 8, 4935 /* downloadLatencyMs= */ 0L, 4936 /* totalLatencyMs= */ 500L)); 4937 verify(mockLogger) 4938 .logEventSampled( 4939 MddClientEvent.Code.EVENT_CODE_UNSPECIFIED, 4940 TEST_GROUP_2, 4941 /* fileGroupVersionNumber= */ 0, 4942 /* buildId= */ 0, 4943 /* variantId= */ ""); 4944 verify(mockLogger) 4945 .logEventSampled( 4946 MddClientEvent.Code.EVENT_CODE_UNSPECIFIED, 4947 TEST_GROUP_3, 4948 /* fileGroupVersionNumber= */ 0, 4949 /* buildId= */ 0, 4950 /* variantId= */ ""); 4951 4952 // Make sure that the successful download of fileGroup1, the failed downloads of fileGroup2 and 4953 // fileGroup3 are all logged to clearcut. 4954 verify(mockLogger) 4955 .logMddDownloadResult( 4956 MddDownloadResult.Code.SUCCESS, 4957 createFileGroupDetails(fileGroup1) 4958 .setOwnerPackage(context.getPackageName()) 4959 .clearFileCount() 4960 .build()); 4961 verify(mockLogger) 4962 .logMddDownloadResult( 4963 MddDownloadResult.Code.UNKNOWN_ERROR, 4964 createFileGroupDetails(fileGroup2) 4965 .setOwnerPackage(context.getPackageName()) 4966 .clearFileCount() 4967 .build()); 4968 4969 verify(mockLogger) 4970 .logMddDownloadResult( 4971 MddDownloadResult.Code.UNKNOWN_ERROR, 4972 createFileGroupDetails(fileGroup3) 4973 .setOwnerPackage(context.getPackageName()) 4974 .clearFileCount() 4975 .build()); 4976 } 4977 4978 @Test testDownloadAllPendingGroups_withoutWifi()4979 public void testDownloadAllPendingGroups_withoutWifi() throws Exception { 4980 // Write 2 groups to the pending shared prefs. 4981 DataFileGroupInternal fileGroup1 = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2); 4982 fileGroup1 = 4983 fileGroup1.toBuilder() 4984 .setDownloadConditions( 4985 DownloadConditions.newBuilder() 4986 .setDeviceNetworkPolicy(DeviceNetworkPolicy.DOWNLOAD_ON_ANY_NETWORK)) 4987 .build(); 4988 writePendingFileGroup(testKey, fileGroup1); 4989 writeSharedFiles( 4990 sharedFilesMetadata, 4991 fileGroup1, 4992 ImmutableList.of(FileStatus.DOWNLOAD_IN_PROGRESS, FileStatus.DOWNLOAD_IN_PROGRESS)); 4993 4994 DataFileGroupInternal fileGroup2 = MddTestUtil.createDataFileGroupInternal(TEST_GROUP_2, 3); 4995 writePendingFileGroup(testKey2, fileGroup2); 4996 writeSharedFiles( 4997 sharedFilesMetadata, 4998 fileGroup2, 4999 ImmutableList.of( 5000 FileStatus.DOWNLOAD_IN_PROGRESS, 5001 FileStatus.DOWNLOAD_IN_PROGRESS, 5002 FileStatus.DOWNLOAD_IN_PROGRESS)); 5003 5004 fileGroupManager.scheduleAllPendingGroupsForDownload(false, noCustomValidation()).get(); 5005 5006 // Only the files in the first group will be downloaded. 5007 verify(mockDownloader, times(2)).getInProgressFuture(any(String.class), any(Uri.class)); 5008 5009 verifyNoMoreInteractions(mockDownloader); 5010 } 5011 5012 @Test testDownloadAllPendingGroups_wifiFirst_without_Wifi()5013 public void testDownloadAllPendingGroups_wifiFirst_without_Wifi() throws Exception { 5014 DataFileGroupInternal dataFileGroup = 5015 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2).toBuilder() 5016 .setDownloadConditions( 5017 DownloadConditions.newBuilder() 5018 .setDeviceNetworkPolicy( 5019 DeviceNetworkPolicy.DOWNLOAD_FIRST_ON_WIFI_THEN_ON_ANY_NETWORK) 5020 .setDownloadFirstOnWifiPeriodSecs(10)) 5021 .build(); 5022 5023 testClock.set(1000); 5024 5025 { 5026 // Check that pending groups contain the added file group. 5027 assertThat(fileGroupManager.addGroupForDownload(testKey, dataFileGroup).get()).isTrue(); 5028 verifyAddGroupForDownloadWritesMetadata(testKey, dataFileGroup, 1000); 5029 } 5030 5031 { 5032 // Set time so that it has not passed the wifi only period. 5033 testClock.set(2000); 5034 fileGroupManager.scheduleAllPendingGroupsForDownload(false, noCustomValidation()).get(); 5035 } 5036 5037 { 5038 // Set time so that it has passed the wifi only period. 5039 testClock.set(2000 + 10 * 1000); 5040 ArgumentCaptor<DownloadConditions> downloadConditionCaptor = 5041 ArgumentCaptor.forClass(DownloadConditions.class); 5042 when(mockDownloader.startDownloading( 5043 any(String.class), 5044 any(GroupKey.class), 5045 anyInt(), 5046 anyLong(), 5047 any(String.class), 5048 any(Uri.class), 5049 any(String.class), 5050 anyInt(), 5051 downloadConditionCaptor.capture(), 5052 isA(DownloaderCallbackImpl.class), 5053 anyInt(), 5054 anyList())) 5055 .thenReturn(Futures.immediateVoidFuture()); 5056 5057 fileGroupManager.scheduleAllPendingGroupsForDownload(false, noCustomValidation()).get(); 5058 5059 // verify that the group's DeviceNetworkPolicy changes to 5060 // DeviceNetworkPolicy.DOWNLOAD_ON_ANY_NETWORK 5061 assertThat(downloadConditionCaptor.getValue().getDeviceNetworkPolicy()) 5062 .isEqualTo(DeviceNetworkPolicy.DOWNLOAD_ON_ANY_NETWORK); 5063 } 5064 } 5065 5066 @Test testDownloadAllPendingGroups_startDownloadFails()5067 public void testDownloadAllPendingGroups_startDownloadFails() throws Exception { 5068 // Write 2 groups to the pending shared prefs. 5069 DataFileGroupInternal fileGroup1 = 5070 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2).toBuilder() 5071 .setDownloadConditions(DownloadConditions.getDefaultInstance()) 5072 .build(); 5073 writePendingFileGroup(testKey, fileGroup1); 5074 writeSharedFiles( 5075 sharedFilesMetadata, 5076 fileGroup1, 5077 ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE, FileStatus.SUBSCRIBED)); 5078 NewFileKey[] keys1 = MddTestUtil.createFileKeysForDataFileGroupInternal(fileGroup1); 5079 5080 DataFileGroupInternal fileGroup2 = MddTestUtil.createDataFileGroupInternal(TEST_GROUP_2, 1); 5081 writePendingFileGroup(testKey2, fileGroup2); 5082 writeSharedFiles( 5083 sharedFilesMetadata, fileGroup2, ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE)); 5084 5085 // Make the download call fail for one of the files in first group. 5086 Uri failingFileUri = 5087 DirectoryUtil.getOnDeviceUri( 5088 context, 5089 keys1[1].getAllowedReaders(), 5090 fileGroup1.getFile(1).getFileId(), 5091 fileGroup1.getFile(1).getChecksum(), 5092 mockSilentFeedback, 5093 /* instanceId= */ Optional.absent(), 5094 false); 5095 fileDownloadFails(keys1[1], failingFileUri, DownloadResultCode.LOW_DISK_ERROR); 5096 5097 fileGroupManager.scheduleAllPendingGroupsForDownload(true, noCustomValidation()).get(); 5098 5099 verify(mockLogger) 5100 .logEventSampled( 5101 MddClientEvent.Code.EVENT_CODE_UNSPECIFIED, 5102 TEST_GROUP, 5103 /* fileGroupVersionNumber= */ 0, 5104 /* buildId= */ 0, 5105 /* variantId= */ ""); 5106 verify(mockLogger) 5107 .logEventSampled( 5108 MddClientEvent.Code.EVENT_CODE_UNSPECIFIED, 5109 TEST_GROUP_2, 5110 /* fileGroupVersionNumber= */ 0, 5111 /* buildId= */ 0, 5112 /* variantId= */ ""); 5113 5114 verify(mockLogger) 5115 .logMddDownloadResult( 5116 MddDownloadResult.Code.LOW_DISK_ERROR, 5117 createFileGroupDetails(fileGroup1) 5118 .clearFileCount() 5119 .setOwnerPackage(context.getPackageName()) 5120 .build()); 5121 5122 verify(mockLogger) 5123 .logMddDownloadResult( 5124 MddDownloadResult.Code.SUCCESS, 5125 createFileGroupDetails(fileGroup2) 5126 .clearFileCount() 5127 .setOwnerPackage(context.getPackageName()) 5128 .build()); 5129 } 5130 5131 // case 1: the file is already shared in the blob storage. 5132 @Test tryToShareBeforeDownload_alreadyShared()5133 public void tryToShareBeforeDownload_alreadyShared() throws Exception { 5134 DataFileGroupInternal fileGroup = 5135 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2).toBuilder() 5136 .setDownloadConditions(DownloadConditions.getDefaultInstance()) 5137 .build(); 5138 5139 DataFile file = MddTestUtil.createSharedDataFile("fileId", 0); 5140 NewFileKey newFileKey = 5141 SharedFilesMetadata.createKeyFromDataFile(file, AllowedReaders.ALL_GOOGLE_APPS); 5142 // Set the file metadata as already downloaded and shared 5143 SharedFile existingDownloadedSharedFile = 5144 SharedFile.newBuilder() 5145 .setFileStatus(FileStatus.DOWNLOAD_COMPLETE) 5146 .setFileName("") 5147 .setAndroidShared(true) 5148 .setAndroidSharingChecksum(file.getAndroidSharingChecksum()) 5149 .setMaxExpirationDateSecs(FILE_GROUP_EXPIRATION_DATE_SECS) 5150 .build(); 5151 sharedFilesMetadata.write(newFileKey, existingDownloadedSharedFile).get(); 5152 5153 fileGroupManager.tryToShareBeforeDownload(fileGroup, file, newFileKey).get(); 5154 5155 verify(mockBackend, never()).exists(any()); 5156 // openForWrite isn't called to update the lease because the current fileGroup's expiration date 5157 // is < maxExpirationDate. 5158 verify(mockBackend, never()).openForWrite(any()); 5159 5160 assertThat(sharedFileManager.getSharedFile(newFileKey).get()) 5161 .isEqualTo(existingDownloadedSharedFile); 5162 5163 ArgumentCaptor<Void> mddAndroidSharingLogArgumentCaptor = ArgumentCaptor.forClass(Void.class); 5164 5165 verify(mockLogger).logMddAndroidSharingLog(mddAndroidSharingLogArgumentCaptor.capture()); 5166 Void mddAndroidSharingLog = null; 5167 assertThat(mddAndroidSharingLogArgumentCaptor.getAllValues()) 5168 .containsExactly(mddAndroidSharingLog); 5169 verifyNoMoreInteractions(mockLogger); 5170 } 5171 5172 // case 2a: the to-be-shared file is available in the blob storage. 5173 @Test tryToShareBeforeDownload_toBeSharedFile_blobExists()5174 public void tryToShareBeforeDownload_toBeSharedFile_blobExists() throws Exception { 5175 // Create a file group with expiration date smaller than the expiration date of the existing 5176 // SharedFile. 5177 DataFileGroupInternal fileGroup = 5178 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2).toBuilder() 5179 .setDownloadConditions(DownloadConditions.getDefaultInstance()) 5180 .setExpirationDateSecs(FILE_GROUP_EXPIRATION_DATE_SECS - 1) 5181 .build(); 5182 5183 // Create a to-be-shared file 5184 DataFile file = MddTestUtil.createSharedDataFile("fileId", 0); 5185 NewFileKey newFileKey = 5186 SharedFilesMetadata.createKeyFromDataFile(file, AllowedReaders.ALL_GOOGLE_APPS); 5187 // Set the file metadata as download pending and non shared 5188 SharedFile existingSharedFile = 5189 SharedFile.newBuilder() 5190 .setFileStatus(FileStatus.DOWNLOAD_IN_PROGRESS) 5191 .setFileName("fileName") 5192 .setAndroidShared(false) 5193 .setMaxExpirationDateSecs(FILE_GROUP_EXPIRATION_DATE_SECS) 5194 .build(); 5195 5196 sharedFilesMetadata.write(newFileKey, existingSharedFile).get(); 5197 5198 Uri blobUri = DirectoryUtil.getBlobUri(context, file.getAndroidSharingChecksum()); 5199 Uri leaseUri = 5200 DirectoryUtil.getBlobStoreLeaseUri( 5201 context, file.getAndroidSharingChecksum(), FILE_GROUP_EXPIRATION_DATE_SECS); 5202 // The file is available in the blob storage 5203 when(mockBackend.exists(blobUri)).thenReturn(true); 5204 5205 fileGroupManager.tryToShareBeforeDownload(fileGroup, file, newFileKey).get(); 5206 5207 // openForWrite is called only once for acquiring the lease. 5208 verify(mockBackend, never()).openForWrite(blobUri); 5209 verify(mockBackend).openForWrite(leaseUri); 5210 SharedFile sharedFile = sharedFileManager.getSharedFile(newFileKey).get(); 5211 // Verify that the SharedFile retains the longest expiration date after the download. 5212 assertThat(sharedFile.getMaxExpirationDateSecs()).isEqualTo(FILE_GROUP_EXPIRATION_DATE_SECS); 5213 assertThat(sharedFile.getAndroidShared()).isTrue(); 5214 assertThat(sharedFileManager.getOnDeviceUri(newFileKey).get()).isEqualTo(blobUri); 5215 5216 ArgumentCaptor<Void> mddAndroidSharingLogArgumentCaptor = ArgumentCaptor.forClass(Void.class); 5217 5218 verify(mockLogger).logMddAndroidSharingLog(mddAndroidSharingLogArgumentCaptor.capture()); 5219 Void mddAndroidSharingLog = null; 5220 assertThat(mddAndroidSharingLogArgumentCaptor.getAllValues()) 5221 .containsExactly(mddAndroidSharingLog); 5222 verifyNoMoreInteractions(mockLogger); 5223 } 5224 5225 // case 3: the to-be-shared file is available in the local storage. 5226 @Test tryToShareBeforeDownload_toBeSharedFile_canBeCopied()5227 public void tryToShareBeforeDownload_toBeSharedFile_canBeCopied() throws Exception { 5228 File tempFile = folder.newFile("blobFile"); 5229 // Create a file group with expiration date bigger than the expiration date of the existing 5230 // SharedFile. 5231 DataFileGroupInternal fileGroup = 5232 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2).toBuilder() 5233 .setDownloadConditions(DownloadConditions.getDefaultInstance()) 5234 .setExpirationDateSecs(FILE_GROUP_EXPIRATION_DATE_SECS + 1) 5235 .build(); 5236 5237 // Create a to-be-shared file 5238 DataFile file = MddTestUtil.createSharedDataFile("fileId", 0); 5239 NewFileKey newFileKey = 5240 SharedFilesMetadata.createKeyFromDataFile(file, AllowedReaders.ALL_GOOGLE_APPS); 5241 // Set the file metadata as downloaded and non shared 5242 SharedFile existingSharedFile = 5243 SharedFile.newBuilder() 5244 .setFileStatus(FileStatus.DOWNLOAD_COMPLETE) 5245 .setFileName("fileName") 5246 .setAndroidShared(false) 5247 .setMaxExpirationDateSecs(FILE_GROUP_EXPIRATION_DATE_SECS) 5248 .build(); 5249 sharedFilesMetadata.write(newFileKey, existingSharedFile).get(); 5250 5251 Uri blobUri = DirectoryUtil.getBlobUri(context, file.getAndroidSharingChecksum()); 5252 Uri leaseUri = 5253 DirectoryUtil.getBlobStoreLeaseUri( 5254 context, file.getAndroidSharingChecksum(), FILE_GROUP_EXPIRATION_DATE_SECS + 1); 5255 // The file isn't available in the blob storage 5256 when(mockBackend.exists(blobUri)).thenReturn(false); 5257 when(mockBackend.openForWrite(blobUri)).thenReturn(new FileOutputStream(tempFile)); 5258 5259 File onDeviceFile = simulateDownload(file, existingSharedFile.getFileName()); 5260 Uri onDeviceuri = 5261 DirectoryUtil.getOnDeviceUri( 5262 context, 5263 newFileKey.getAllowedReaders(), 5264 existingSharedFile.getFileName(), 5265 newFileKey.getChecksum(), 5266 mockSilentFeedback, 5267 /* instanceId= */ Optional.absent(), 5268 /* androidShared= */ false); 5269 assertThat(fileStorage.exists(onDeviceuri)).isTrue(); 5270 5271 fileGroupManager.tryToShareBeforeDownload(fileGroup, file, newFileKey).get(); 5272 5273 // openForWrite is called once for writing the blob, once for acquiring the lease. 5274 verify(mockBackend).openForWrite(blobUri); 5275 verify(mockBackend).openForWrite(leaseUri); 5276 5277 SharedFile sharedFile = sharedFileManager.getSharedFile(newFileKey).get(); 5278 // Verify that the SharedFile has updated its expiration date after the download. 5279 assertThat(sharedFile.getMaxExpirationDateSecs()) 5280 .isEqualTo(FILE_GROUP_EXPIRATION_DATE_SECS + 1); 5281 assertThat(sharedFile.getAndroidShared()).isTrue(); 5282 assertThat(sharedFileManager.getOnDeviceUri(newFileKey).get()).isEqualTo(blobUri); 5283 5284 // The local copy will be deleted in daily maintance 5285 assertThat(fileStorage.exists(onDeviceuri)).isTrue(); 5286 onDeviceFile.delete(); 5287 5288 ArgumentCaptor<Void> mddAndroidSharingLogArgumentCaptor = ArgumentCaptor.forClass(Void.class); 5289 5290 verify(mockLogger).logMddAndroidSharingLog(mddAndroidSharingLogArgumentCaptor.capture()); 5291 5292 Void mddAndroidSharingLog = null; 5293 assertThat(mddAndroidSharingLogArgumentCaptor.getAllValues()) 5294 .containsExactly(mddAndroidSharingLog); 5295 verifyNoMoreInteractions(mockLogger); 5296 } 5297 5298 // The file can't be shared and isn't available locally. 5299 @Test tryToShareBeforeDownload_toBeSharedFile_cannotBeShared_neverDownloaded()5300 public void tryToShareBeforeDownload_toBeSharedFile_cannotBeShared_neverDownloaded() 5301 throws Exception { 5302 DataFileGroupInternal fileGroup = 5303 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2).toBuilder() 5304 .setDownloadConditions(DownloadConditions.getDefaultInstance()) 5305 .build(); 5306 5307 DataFile file = MddTestUtil.createSharedDataFile("fileId", 0); 5308 NewFileKey newFileKey = 5309 SharedFilesMetadata.createKeyFromDataFile(file, AllowedReaders.ALL_GOOGLE_APPS); 5310 SharedFile existingSharedFile = 5311 SharedFile.newBuilder() 5312 .setFileStatus(FileStatus.DOWNLOAD_IN_PROGRESS) 5313 .setFileName("fileName") 5314 .setAndroidShared(false) 5315 .build(); 5316 sharedFilesMetadata.write(newFileKey, existingSharedFile).get(); 5317 5318 Uri blobUri = DirectoryUtil.getBlobUri(context, file.getAndroidSharingChecksum()); 5319 // The file isn't available in the blob storage 5320 when(mockBackend.exists(blobUri)).thenReturn(false); 5321 5322 fileGroupManager.tryToShareBeforeDownload(fileGroup, file, newFileKey).get(); 5323 5324 // We never acquire the lease nor update the max expiration date. 5325 verify(mockBackend).exists(blobUri); 5326 verify(mockBackend, never()).openForWrite(any()); 5327 5328 SharedFile sharedFile = sharedFileManager.getSharedFile(newFileKey).get(); 5329 assertThat(sharedFile).isEqualTo(existingSharedFile); 5330 5331 verifyNoInteractions(mockLogger); 5332 } 5333 5334 // case 4: the non-to-be-shared file can't be shared and is available in the local storage. 5335 @Test tryToShareBeforeDownload_nonToBeSharedFile_alreadyDownloaded_cannotBeShared()5336 public void tryToShareBeforeDownload_nonToBeSharedFile_alreadyDownloaded_cannotBeShared() 5337 throws Exception { 5338 DataFileGroupInternal fileGroup = 5339 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2).toBuilder() 5340 .setDownloadConditions(DownloadConditions.getDefaultInstance()) 5341 .build(); 5342 // non-to-be-shared file with ChecksumType SHA1 5343 DataFile file = MddTestUtil.createDataFile("fileId", 0); 5344 NewFileKey newFileKey = 5345 SharedFilesMetadata.createKeyFromDataFile(file, AllowedReaders.ALL_GOOGLE_APPS); 5346 // Set the file metadata downloaded and non shared 5347 SharedFile existingSharedFile = 5348 SharedFile.newBuilder() 5349 .setFileStatus(FileStatus.DOWNLOAD_COMPLETE) 5350 .setFileName("fileName") 5351 .setAndroidShared(false) 5352 .setMaxExpirationDateSecs(FILE_GROUP_EXPIRATION_DATE_SECS) 5353 .build(); 5354 sharedFilesMetadata.write(newFileKey, existingSharedFile).get(); 5355 5356 fileGroupManager.tryToShareBeforeDownload(fileGroup, file, newFileKey).get(); 5357 5358 verify(mockBackend, never()).exists(any()); 5359 // We never acquire the lease since the file can't be shared. 5360 verify(mockBackend, never()).openForWrite(any()); 5361 5362 SharedFile sharedFile = sharedFileManager.getSharedFile(newFileKey).get(); 5363 assertThat(sharedFile).isEqualTo(existingSharedFile); 5364 5365 verify(mockSharedFileManager, never()) 5366 .setAndroidSharedDownloadedFileEntry(any(), any(), anyLong()); 5367 5368 verifyNoInteractions(mockLogger); 5369 } 5370 5371 @Test tryToShareBeforeDownload_blobUriNotSupported()5372 public void tryToShareBeforeDownload_blobUriNotSupported() throws Exception { 5373 // FileStorage without BlobStoreBackend 5374 fileStorage = 5375 new SynchronousFileStorage(Arrays.asList(AndroidFileBackend.builder(context).build())); 5376 fileGroupManager = 5377 new FileGroupManager( 5378 context, 5379 mockLogger, 5380 mockSilentFeedback, 5381 fileGroupsMetadata, 5382 sharedFileManager, 5383 testClock, 5384 Optional.of(mockAccountSource), 5385 SEQUENTIAL_CONTROL_EXECUTOR, 5386 Optional.absent(), 5387 fileStorage, 5388 downloadStageManager, 5389 flags); 5390 5391 DataFileGroupInternal fileGroup = 5392 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 0).toBuilder() 5393 .setDownloadConditions(DownloadConditions.getDefaultInstance()) 5394 .build(); 5395 5396 // Create a to-be-shared file 5397 DataFile file = MddTestUtil.createSharedDataFile("fileId", 0); 5398 NewFileKey newFileKey = 5399 SharedFilesMetadata.createKeyFromDataFile(file, AllowedReaders.ALL_GOOGLE_APPS); 5400 // Set the file metadata as download completed and non shared 5401 SharedFile existingSharedFile = 5402 SharedFile.newBuilder() 5403 .setFileStatus(FileStatus.DOWNLOAD_COMPLETE) 5404 .setFileName("fileName") 5405 .setAndroidShared(false) 5406 .setMaxExpirationDateSecs(FILE_GROUP_EXPIRATION_DATE_SECS) 5407 .build(); 5408 5409 sharedFilesMetadata.write(newFileKey, existingSharedFile).get(); 5410 5411 fileGroupManager.tryToShareBeforeDownload(fileGroup, file, newFileKey).get(); 5412 5413 verify(mockBackend, never()).openForWrite(any()); 5414 5415 SharedFile sharedFile = sharedFileManager.getSharedFile(newFileKey).get(); 5416 assertThat(sharedFile).isEqualTo(existingSharedFile); 5417 5418 ArgumentCaptor<Void> mddAndroidSharingLogArgumentCaptor = ArgumentCaptor.forClass(Void.class); 5419 verify(mockLogger).logMddAndroidSharingLog(mddAndroidSharingLogArgumentCaptor.capture()); 5420 5421 Void mddAndroidSharingLog = null; 5422 assertThat(mddAndroidSharingLogArgumentCaptor.getAllValues()) 5423 .containsExactly(mddAndroidSharingLog); 5424 verifyNoMoreInteractions(mockLogger); 5425 } 5426 5427 @Test tryToShareBeforeDownload_setAndroidSharedDownloadedFileEntryReturnsFalse()5428 public void tryToShareBeforeDownload_setAndroidSharedDownloadedFileEntryReturnsFalse() 5429 throws Exception { 5430 // Mock SharedFileManager to test failure scenario. 5431 resetFileGroupManager(fileGroupsMetadata, mockSharedFileManager); 5432 DataFileGroupInternal fileGroup = 5433 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2).toBuilder() 5434 .setDownloadConditions(DownloadConditions.getDefaultInstance()) 5435 .build(); 5436 5437 // Create a to-be-shared file 5438 DataFile file = MddTestUtil.createSharedDataFile("fileId", 0); 5439 NewFileKey newFileKey = 5440 SharedFilesMetadata.createKeyFromDataFile(file, AllowedReaders.ALL_GOOGLE_APPS); 5441 5442 SharedFile existingSharedFile = 5443 SharedFile.newBuilder() 5444 .setFileStatus(FileStatus.DOWNLOAD_IN_PROGRESS) 5445 .setFileName("fileName") 5446 .setAndroidShared(false) 5447 .setMaxExpirationDateSecs(FILE_GROUP_EXPIRATION_DATE_SECS) 5448 .build(); 5449 5450 when(mockSharedFileManager.getSharedFile(newFileKey)) 5451 .thenReturn(Futures.immediateFuture(existingSharedFile)); 5452 Uri blobUri = DirectoryUtil.getBlobUri(context, file.getAndroidSharingChecksum()); 5453 Uri leaseUri = 5454 DirectoryUtil.getBlobStoreLeaseUri( 5455 context, file.getAndroidSharingChecksum(), FILE_GROUP_EXPIRATION_DATE_SECS); 5456 // The file is available in the blob storage 5457 when(mockBackend.exists(blobUri)).thenReturn(true); 5458 5459 // Last operation fails 5460 when(mockSharedFileManager.setAndroidSharedDownloadedFileEntry( 5461 newFileKey, file.getAndroidSharingChecksum(), FILE_GROUP_EXPIRATION_DATE_SECS)) 5462 .thenReturn(Futures.immediateFuture(false)); 5463 5464 fileGroupManager.tryToShareBeforeDownload(fileGroup, file, newFileKey).get(); 5465 5466 // openForWrite is called only once for acquiring the lease. 5467 verify(mockBackend, never()).openForWrite(blobUri); 5468 verify(mockBackend).openForWrite(leaseUri); 5469 5470 ArgumentCaptor<Void> mddAndroidSharingLogArgumentCaptor = ArgumentCaptor.forClass(Void.class); 5471 5472 verify(mockLogger).logMddAndroidSharingLog(mddAndroidSharingLogArgumentCaptor.capture()); 5473 Void mddAndroidSharingLog = null; 5474 assertThat(mddAndroidSharingLogArgumentCaptor.getAllValues()) 5475 .containsExactly(mddAndroidSharingLog); 5476 verifyNoMoreInteractions(mockLogger); 5477 } 5478 5479 @Test tryToShareBeforeDownload_blobExistsThrowsIOException()5480 public void tryToShareBeforeDownload_blobExistsThrowsIOException() throws Exception { 5481 DataFileGroupInternal fileGroup = 5482 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2).toBuilder() 5483 .setDownloadConditions(DownloadConditions.getDefaultInstance()) 5484 .build(); 5485 5486 // Create a to-be-shared file 5487 DataFile file = MddTestUtil.createSharedDataFile("fileId", 0); 5488 NewFileKey newFileKey = 5489 SharedFilesMetadata.createKeyFromDataFile(file, AllowedReaders.ALL_GOOGLE_APPS); 5490 5491 SharedFile existingSharedFile = 5492 SharedFile.newBuilder() 5493 .setFileStatus(FileStatus.DOWNLOAD_IN_PROGRESS) 5494 .setFileName("fileName") 5495 .setAndroidShared(false) 5496 .setMaxExpirationDateSecs(FILE_GROUP_EXPIRATION_DATE_SECS) 5497 .build(); 5498 sharedFilesMetadata.write(newFileKey, existingSharedFile).get(); 5499 5500 Uri blobUri = DirectoryUtil.getBlobUri(context, file.getAndroidSharingChecksum()); 5501 Uri leaseUri = 5502 DirectoryUtil.getBlobStoreLeaseUri( 5503 context, file.getAndroidSharingChecksum(), FILE_GROUP_EXPIRATION_DATE_SECS); 5504 5505 when(mockBackend.exists(blobUri)).thenReturn(true); 5506 when(mockBackend.openForWrite(leaseUri)).thenThrow(new IOException()); 5507 5508 fileGroupManager.tryToShareBeforeDownload(fileGroup, file, newFileKey).get(); 5509 5510 SharedFile sharedFile = sharedFileManager.getSharedFile(newFileKey).get(); 5511 assertThat(sharedFile).isEqualTo(existingSharedFile); 5512 5513 ArgumentCaptor<Void> mddAndroidSharingLogArgumentCaptor = ArgumentCaptor.forClass(Void.class); 5514 5515 verify(mockLogger).logMddAndroidSharingLog(mddAndroidSharingLogArgumentCaptor.capture()); 5516 5517 Void mddAndroidSharingLog = null; 5518 assertThat(mddAndroidSharingLogArgumentCaptor.getAllValues()) 5519 .containsExactly(mddAndroidSharingLog); 5520 verifyNoMoreInteractions(mockLogger); 5521 } 5522 5523 @Test tryToShareBeforeDownload_fileStorageThrowsLimitExceededException()5524 public void tryToShareBeforeDownload_fileStorageThrowsLimitExceededException() throws Exception { 5525 // Create a file group with expiration date bigger than the expiration date of the existing 5526 // SharedFile. 5527 DataFileGroupInternal fileGroup = 5528 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2).toBuilder() 5529 .setDownloadConditions(DownloadConditions.getDefaultInstance()) 5530 .setExpirationDateSecs(FILE_GROUP_EXPIRATION_DATE_SECS + 1) 5531 .build(); 5532 5533 // Create a to-be-shared file 5534 DataFile file = MddTestUtil.createSharedDataFile("fileId", 0); 5535 NewFileKey newFileKey = 5536 SharedFilesMetadata.createKeyFromDataFile(file, AllowedReaders.ALL_GOOGLE_APPS); 5537 5538 SharedFile existingSharedFile = 5539 SharedFile.newBuilder() 5540 .setFileStatus(FileStatus.DOWNLOAD_IN_PROGRESS) 5541 .setFileName("fileName") 5542 .setAndroidShared(false) 5543 .setMaxExpirationDateSecs(FILE_GROUP_EXPIRATION_DATE_SECS) 5544 .build(); 5545 sharedFilesMetadata.write(newFileKey, existingSharedFile).get(); 5546 5547 Uri blobUri = DirectoryUtil.getBlobUri(context, file.getAndroidSharingChecksum()); 5548 Uri leaseUri = 5549 DirectoryUtil.getBlobStoreLeaseUri( 5550 context, file.getAndroidSharingChecksum(), FILE_GROUP_EXPIRATION_DATE_SECS + 1); 5551 // The file is available in the blob storage 5552 when(mockBackend.exists(blobUri)).thenReturn(true); 5553 // Writing the lease throws an exception 5554 when(mockBackend.openForWrite(leaseUri)).thenThrow(new LimitExceededException()); 5555 5556 fileGroupManager.tryToShareBeforeDownload(fileGroup, file, newFileKey).get(); 5557 5558 verify(mockBackend, never()).openForWrite(blobUri); 5559 verify(mockBackend).openForWrite(leaseUri); 5560 5561 SharedFile sharedFile = sharedFileManager.getSharedFile(newFileKey).get(); 5562 // Since there was an exception, the existing shared file didn't update the expiration date. 5563 assertThat(sharedFile).isEqualTo(existingSharedFile); 5564 5565 ArgumentCaptor<Void> mddAndroidSharingLogArgumentCaptor = ArgumentCaptor.forClass(Void.class); 5566 verify(mockLogger).logMddAndroidSharingLog(mddAndroidSharingLogArgumentCaptor.capture()); 5567 5568 Void mddAndroidSharingLog = null; 5569 assertThat(mddAndroidSharingLogArgumentCaptor.getAllValues()) 5570 .containsExactly(mddAndroidSharingLog); 5571 verifyNoMoreInteractions(mockLogger); 5572 } 5573 5574 @Test tryToShareAfterDownload_alreadyShared_sameFileGroup()5575 public void tryToShareAfterDownload_alreadyShared_sameFileGroup() throws Exception { 5576 DataFileGroupInternal fileGroup = 5577 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1).toBuilder() 5578 .setDownloadConditions(DownloadConditions.getDefaultInstance()) 5579 .build(); 5580 5581 DataFile file = MddTestUtil.createSharedDataFile("fileId", 0); 5582 NewFileKey newFileKey = 5583 SharedFilesMetadata.createKeyFromDataFile(file, AllowedReaders.ALL_GOOGLE_APPS); 5584 SharedFile existingSharedFile = 5585 SharedFile.newBuilder() 5586 .setFileStatus(FileStatus.DOWNLOAD_COMPLETE) 5587 .setFileName("fileName") 5588 .setAndroidShared(true) 5589 .setAndroidSharingChecksum(file.getAndroidSharingChecksum()) 5590 .setMaxExpirationDateSecs(FILE_GROUP_EXPIRATION_DATE_SECS) 5591 .build(); 5592 sharedFilesMetadata.write(newFileKey, existingSharedFile).get(); 5593 5594 fileGroupManager.tryToShareAfterDownload(fileGroup, file, newFileKey).get(); 5595 5596 verify(mockBackend, never()).exists(any()); 5597 verify(mockBackend, never()).openForWrite(any()); 5598 5599 SharedFile sharedFile = sharedFileManager.getSharedFile(newFileKey).get(); 5600 assertThat(sharedFile).isEqualTo(existingSharedFile); 5601 5602 verifyNoInteractions(mockLogger); 5603 } 5604 5605 @Test tryToShareAfterDownload_alreadyShared_differentFileGroup()5606 public void tryToShareAfterDownload_alreadyShared_differentFileGroup() throws Exception { 5607 // Create a file group with expiration date bigger than the expiration date of the existing 5608 // SharedFile. 5609 DataFileGroupInternal fileGroup = 5610 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1).toBuilder() 5611 .setExpirationDateSecs(FILE_GROUP_EXPIRATION_DATE_SECS) 5612 .setDownloadConditions(DownloadConditions.getDefaultInstance()) 5613 .build(); 5614 5615 DataFile file = MddTestUtil.createSharedDataFile("fileId", 0); 5616 NewFileKey newFileKey = 5617 SharedFilesMetadata.createKeyFromDataFile(file, AllowedReaders.ALL_GOOGLE_APPS); 5618 SharedFile existingSharedFile = 5619 SharedFile.newBuilder() 5620 .setFileStatus(FileStatus.DOWNLOAD_COMPLETE) 5621 .setFileName("fileName") 5622 .setAndroidShared(true) 5623 .setAndroidSharingChecksum(file.getAndroidSharingChecksum()) 5624 .setMaxExpirationDateSecs(FILE_GROUP_EXPIRATION_DATE_SECS - 1) 5625 .build(); 5626 sharedFilesMetadata.write(newFileKey, existingSharedFile).get(); 5627 5628 Uri blobUri = DirectoryUtil.getBlobUri(context, file.getAndroidSharingChecksum()); 5629 Uri leaseUri = 5630 DirectoryUtil.getBlobStoreLeaseUri( 5631 context, file.getAndroidSharingChecksum(), FILE_GROUP_EXPIRATION_DATE_SECS); 5632 5633 fileGroupManager.tryToShareAfterDownload(fileGroup, file, newFileKey).get(); 5634 5635 // openForWrite is called only once for acquiring the lease. 5636 verify(mockBackend, never()).exists(any()); 5637 verify(mockBackend).openForWrite(leaseUri); 5638 5639 SharedFile sharedFile = sharedFileManager.getSharedFile(newFileKey).get(); 5640 // Verify that the SharedFile has updated its expiration date after the download. 5641 assertThat(sharedFile.getMaxExpirationDateSecs()).isEqualTo(FILE_GROUP_EXPIRATION_DATE_SECS); 5642 assertThat(sharedFile.getAndroidShared()).isTrue(); 5643 assertThat(sharedFileManager.getOnDeviceUri(newFileKey).get()).isEqualTo(blobUri); 5644 5645 ArgumentCaptor<Void> mddAndroidSharingLogArgumentCaptor = ArgumentCaptor.forClass(Void.class); 5646 5647 verify(mockLogger).logMddAndroidSharingLog(mddAndroidSharingLogArgumentCaptor.capture()); 5648 Void mddAndroidSharingLog = null; 5649 assertThat(mddAndroidSharingLogArgumentCaptor.getAllValues()) 5650 .containsExactly(mddAndroidSharingLog); 5651 verifyNoMoreInteractions(mockLogger); 5652 } 5653 5654 @Test tryToShareAfterDownload_toBeSharedFile_blobExists()5655 public void tryToShareAfterDownload_toBeSharedFile_blobExists() throws Exception { 5656 // Create a file group with expiration date bigger than the expiration date of the existing 5657 // SharedFile. 5658 DataFileGroupInternal fileGroup = 5659 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1).toBuilder() 5660 .setExpirationDateSecs(FILE_GROUP_EXPIRATION_DATE_SECS) 5661 .setDownloadConditions(DownloadConditions.getDefaultInstance()) 5662 .build(); 5663 5664 DataFile file = MddTestUtil.createSharedDataFile("fileId", 0); 5665 NewFileKey newFileKey = 5666 SharedFilesMetadata.createKeyFromDataFile(file, AllowedReaders.ALL_GOOGLE_APPS); 5667 SharedFile existingSharedFile = 5668 SharedFile.newBuilder() 5669 .setFileStatus(FileStatus.DOWNLOAD_COMPLETE) 5670 .setFileName("fileName") 5671 .setAndroidShared(false) 5672 .build(); 5673 sharedFilesMetadata.write(newFileKey, existingSharedFile).get(); 5674 5675 Uri blobUri = DirectoryUtil.getBlobUri(context, file.getAndroidSharingChecksum()); 5676 Uri leaseUri = 5677 DirectoryUtil.getBlobStoreLeaseUri( 5678 context, file.getAndroidSharingChecksum(), FILE_GROUP_EXPIRATION_DATE_SECS); 5679 // The file is available in the blob storage 5680 when(mockBackend.exists(blobUri)).thenReturn(true); 5681 5682 simulateDownload(file, existingSharedFile.getFileName()); 5683 Uri onDeviceuri = 5684 DirectoryUtil.getOnDeviceUri( 5685 context, 5686 newFileKey.getAllowedReaders(), 5687 existingSharedFile.getFileName(), 5688 newFileKey.getChecksum(), 5689 mockSilentFeedback, 5690 /* instanceId= */ Optional.absent(), 5691 false); 5692 assertThat(fileStorage.exists(onDeviceuri)).isTrue(); 5693 5694 fileGroupManager.tryToShareAfterDownload(fileGroup, file, newFileKey).get(); 5695 5696 verify(mockBackend).exists(blobUri); 5697 verify(mockBackend).openForWrite(leaseUri); 5698 5699 SharedFile sharedFile = sharedFileManager.getSharedFile(newFileKey).get(); 5700 // Verify that the SharedFile has updated its expiration date after the download. 5701 assertThat(sharedFile.getMaxExpirationDateSecs()).isEqualTo(FILE_GROUP_EXPIRATION_DATE_SECS); 5702 assertThat(sharedFile.getAndroidShared()).isTrue(); 5703 assertThat(sharedFileManager.getOnDeviceUri(newFileKey).get()).isEqualTo(blobUri); 5704 5705 // Local copy has not been deleted. 5706 assertThat(fileStorage.exists(onDeviceuri)).isTrue(); 5707 5708 ArgumentCaptor<Void> mddAndroidSharingLogArgumentCaptor = ArgumentCaptor.forClass(Void.class); 5709 5710 verify(mockLogger).logMddAndroidSharingLog(mddAndroidSharingLogArgumentCaptor.capture()); 5711 Void mddAndroidSharingLog = null; 5712 assertThat(mddAndroidSharingLogArgumentCaptor.getAllValues()) 5713 .containsExactly(mddAndroidSharingLog); 5714 verifyNoMoreInteractions(mockLogger); 5715 } 5716 5717 @Test tryToShareAfterDownload_toBeSharedFile_canBeCopied()5718 public void tryToShareAfterDownload_toBeSharedFile_canBeCopied() throws Exception { 5719 // Create a file group with expiration date bigger than the expiration date of the existing 5720 // SharedFile. 5721 File tempFile = folder.newFile("blobFile"); 5722 DataFileGroupInternal fileGroup = 5723 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1).toBuilder() 5724 .setExpirationDateSecs(FILE_GROUP_EXPIRATION_DATE_SECS) 5725 .setDownloadConditions(DownloadConditions.getDefaultInstance()) 5726 .build(); 5727 5728 DataFile file = MddTestUtil.createSharedDataFile("fileId", 0); 5729 NewFileKey newFileKey = 5730 SharedFilesMetadata.createKeyFromDataFile(file, AllowedReaders.ALL_GOOGLE_APPS); 5731 SharedFile existingSharedFile = 5732 SharedFile.newBuilder() 5733 .setFileStatus(FileStatus.DOWNLOAD_COMPLETE) 5734 .setFileName("fileName") 5735 .setAndroidShared(false) 5736 .build(); 5737 sharedFilesMetadata.write(newFileKey, existingSharedFile).get(); 5738 5739 Uri blobUri = DirectoryUtil.getBlobUri(context, file.getAndroidSharingChecksum()); 5740 Uri leaseUri = 5741 DirectoryUtil.getBlobStoreLeaseUri( 5742 context, file.getAndroidSharingChecksum(), FILE_GROUP_EXPIRATION_DATE_SECS); 5743 // The file isn't available yet in the blob storage 5744 when(mockBackend.exists(blobUri)).thenReturn(false); 5745 when(mockBackend.openForWrite(blobUri)).thenReturn(new FileOutputStream(tempFile)); 5746 5747 simulateDownload(file, existingSharedFile.getFileName()); 5748 Uri onDeviceuri = 5749 DirectoryUtil.getOnDeviceUri( 5750 context, 5751 newFileKey.getAllowedReaders(), 5752 existingSharedFile.getFileName(), 5753 newFileKey.getChecksum(), 5754 mockSilentFeedback, 5755 /* instanceId= */ Optional.absent(), 5756 false); 5757 assertThat(fileStorage.exists(onDeviceuri)).isTrue(); 5758 5759 fileGroupManager.tryToShareAfterDownload(fileGroup, file, newFileKey).get(); 5760 5761 // openForWrite is called once for writing the blob, once for acquiring the lease. 5762 verify(mockBackend).openForWrite(blobUri); 5763 verify(mockBackend).openForWrite(leaseUri); 5764 5765 SharedFile sharedFile = sharedFileManager.getSharedFile(newFileKey).get(); 5766 // Verify that the SharedFile has updated its expiration date after the download. 5767 assertThat(sharedFile.getMaxExpirationDateSecs()).isEqualTo(FILE_GROUP_EXPIRATION_DATE_SECS); 5768 assertThat(sharedFile.getAndroidShared()).isTrue(); 5769 assertThat(sharedFileManager.getOnDeviceUri(newFileKey).get()).isEqualTo(blobUri); 5770 5771 // Local copy has not been deleted. 5772 assertThat(fileStorage.exists(onDeviceuri)).isTrue(); 5773 5774 ArgumentCaptor<Void> mddAndroidSharingLogArgumentCaptor = ArgumentCaptor.forClass(Void.class); 5775 5776 verify(mockLogger).logMddAndroidSharingLog(mddAndroidSharingLogArgumentCaptor.capture()); 5777 Void mddAndroidSharingLog = null; 5778 assertThat(mddAndroidSharingLogArgumentCaptor.getAllValues()) 5779 .containsExactly(mddAndroidSharingLog); 5780 verifyNoMoreInteractions(mockLogger); 5781 } 5782 5783 @Test tryToShareAfterDownload_nonToBeSharedFile_neverShared()5784 public void tryToShareAfterDownload_nonToBeSharedFile_neverShared() throws Exception { 5785 // Create a file group with expiration date bigger than the expiration date of the existing 5786 // SharedFile. 5787 DataFileGroupInternal fileGroup = 5788 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1).toBuilder() 5789 .setExpirationDateSecs(FILE_GROUP_EXPIRATION_DATE_SECS) 5790 .setDownloadConditions(DownloadConditions.getDefaultInstance()) 5791 .build(); 5792 5793 DataFile file = MddTestUtil.createDataFile("fileId", 0); 5794 NewFileKey newFileKey = 5795 SharedFilesMetadata.createKeyFromDataFile(file, AllowedReaders.ALL_GOOGLE_APPS); 5796 SharedFile existingSharedFile = 5797 SharedFile.newBuilder() 5798 .setFileStatus(FileStatus.DOWNLOAD_COMPLETE) 5799 .setFileName("fileName") 5800 .setAndroidShared(false) 5801 .build(); 5802 sharedFilesMetadata.write(newFileKey, existingSharedFile).get(); 5803 5804 File onDeviceFile = simulateDownload(file, existingSharedFile.getFileName()); 5805 Uri onDeviceuri = 5806 DirectoryUtil.getOnDeviceUri( 5807 context, 5808 newFileKey.getAllowedReaders(), 5809 existingSharedFile.getFileName(), 5810 newFileKey.getChecksum(), 5811 mockSilentFeedback, 5812 /* instanceId= */ Optional.absent(), 5813 false); 5814 assertThat(fileStorage.exists(onDeviceuri)).isTrue(); 5815 5816 fileGroupManager.tryToShareAfterDownload(fileGroup, file, newFileKey).get(); 5817 5818 verify(mockBackend, never()).exists(any()); 5819 verify(mockBackend, never()).openForWrite(any()); 5820 5821 SharedFile sharedFile = sharedFileManager.getSharedFile(newFileKey).get(); 5822 // Verify that the SharedFile has updated its expiration date after the download. 5823 SharedFile expectedSharedFile = 5824 existingSharedFile.toBuilder() 5825 .setMaxExpirationDateSecs(FILE_GROUP_EXPIRATION_DATE_SECS) 5826 .build(); 5827 assertThat(sharedFile).isEqualTo(expectedSharedFile); 5828 5829 // Local copy still available. 5830 assertThat(fileStorage.exists(onDeviceuri)).isTrue(); 5831 onDeviceFile.delete(); 5832 5833 verifyNoInteractions(mockLogger); 5834 } 5835 5836 @Test tryToShareAfterDownload_toBeSharedFile_neverShared()5837 public void tryToShareAfterDownload_toBeSharedFile_neverShared() throws Exception { 5838 // Create a file group with expiration date bigger than the expiration date of the existing 5839 // SharedFile. 5840 DataFileGroupInternal fileGroup = 5841 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1).toBuilder() 5842 .setExpirationDateSecs(FILE_GROUP_EXPIRATION_DATE_SECS) 5843 .setDownloadConditions(DownloadConditions.getDefaultInstance()) 5844 .build(); 5845 5846 DataFile file = MddTestUtil.createSharedDataFile("fileId", 0); 5847 // This should never happened in a real scenario. 5848 file = file.toBuilder().setAndroidSharingChecksum("").build(); 5849 5850 NewFileKey newFileKey = 5851 SharedFilesMetadata.createKeyFromDataFile(file, AllowedReaders.ALL_GOOGLE_APPS); 5852 SharedFile existingSharedFile = 5853 SharedFile.newBuilder() 5854 .setFileStatus(FileStatus.DOWNLOAD_COMPLETE) 5855 .setFileName("fileName") 5856 .setAndroidShared(false) 5857 .build(); 5858 sharedFilesMetadata.write(newFileKey, existingSharedFile).get(); 5859 5860 File onDeviceFile = simulateDownload(file, existingSharedFile.getFileName()); 5861 Uri onDeviceuri = 5862 DirectoryUtil.getOnDeviceUri( 5863 context, 5864 newFileKey.getAllowedReaders(), 5865 existingSharedFile.getFileName(), 5866 newFileKey.getChecksum(), 5867 mockSilentFeedback, 5868 /* instanceId= */ Optional.absent(), 5869 false); 5870 assertThat(fileStorage.exists(onDeviceuri)).isTrue(); 5871 5872 fileGroupManager.tryToShareAfterDownload(fileGroup, file, newFileKey).get(); 5873 5874 verify(mockBackend, never()).exists(any()); 5875 verify(mockBackend, never()).openForWrite(any()); 5876 5877 SharedFile sharedFile = sharedFileManager.getSharedFile(newFileKey).get(); 5878 // Verify that the SharedFile has updated its expiration date after the download. 5879 SharedFile expectedSharedFile = 5880 existingSharedFile.toBuilder() 5881 .setMaxExpirationDateSecs(FILE_GROUP_EXPIRATION_DATE_SECS) 5882 .build(); 5883 assertThat(sharedFile).isEqualTo(expectedSharedFile); 5884 5885 // Local copy still available. 5886 assertThat(fileStorage.exists(onDeviceuri)).isTrue(); 5887 onDeviceFile.delete(); 5888 5889 ArgumentCaptor<Void> mddAndroidSharingLogArgumentCaptor = ArgumentCaptor.forClass(Void.class); 5890 verify(mockLogger).logMddAndroidSharingLog(mddAndroidSharingLogArgumentCaptor.capture()); 5891 5892 Void mddAndroidSharingLog = null; 5893 assertThat(mddAndroidSharingLogArgumentCaptor.getAllValues()) 5894 .containsExactly(mddAndroidSharingLog); 5895 5896 verifyNoMoreInteractions(mockLogger); 5897 } 5898 5899 @Test tryToShareAfterDownload_blobUriNotSupported()5900 public void tryToShareAfterDownload_blobUriNotSupported() throws Exception { 5901 // FileStorage without BlobStoreBackend 5902 fileStorage = 5903 new SynchronousFileStorage(Arrays.asList(AndroidFileBackend.builder(context).build())); 5904 fileGroupManager = 5905 new FileGroupManager( 5906 context, 5907 mockLogger, 5908 mockSilentFeedback, 5909 fileGroupsMetadata, 5910 sharedFileManager, 5911 testClock, 5912 Optional.of(mockAccountSource), 5913 SEQUENTIAL_CONTROL_EXECUTOR, 5914 Optional.absent(), 5915 fileStorage, 5916 downloadStageManager, 5917 flags); 5918 5919 DataFileGroupInternal fileGroup = 5920 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 0).toBuilder() 5921 .setDownloadConditions(DownloadConditions.getDefaultInstance()) 5922 .build(); 5923 5924 // Create a to-be-shared file 5925 DataFile file = MddTestUtil.createSharedDataFile("fileId", 0); 5926 NewFileKey newFileKey = 5927 SharedFilesMetadata.createKeyFromDataFile(file, AllowedReaders.ALL_GOOGLE_APPS); 5928 // Set the file metadata as download completed and non shared 5929 SharedFile existingSharedFile = 5930 SharedFile.newBuilder() 5931 .setFileStatus(FileStatus.DOWNLOAD_COMPLETE) 5932 .setFileName("fileName") 5933 .setAndroidShared(false) 5934 .setMaxExpirationDateSecs(FILE_GROUP_EXPIRATION_DATE_SECS) 5935 .build(); 5936 sharedFilesMetadata.write(newFileKey, existingSharedFile).get(); 5937 5938 fileGroupManager.tryToShareAfterDownload(fileGroup, file, newFileKey).get(); 5939 5940 verify(mockBackend, never()).openForWrite(any()); 5941 5942 SharedFile sharedFile = sharedFileManager.getSharedFile(newFileKey).get(); 5943 assertThat(sharedFile).isEqualTo(existingSharedFile); 5944 5945 ArgumentCaptor<Void> mddAndroidSharingLogArgumentCaptor = ArgumentCaptor.forClass(Void.class); 5946 verify(mockLogger).logMddAndroidSharingLog(mddAndroidSharingLogArgumentCaptor.capture()); 5947 5948 Void mddAndroidSharingLog = null; 5949 assertThat(mddAndroidSharingLogArgumentCaptor.getAllValues()) 5950 .containsExactly(mddAndroidSharingLog); 5951 verifyNoMoreInteractions(mockLogger); 5952 } 5953 5954 @Test tryToShareAfterDownload_nonExistentFile()5955 public void tryToShareAfterDownload_nonExistentFile() throws Exception { 5956 DataFileGroupInternal fileGroup = 5957 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1).toBuilder() 5958 .setDownloadConditions(DownloadConditions.getDefaultInstance()) 5959 .build(); 5960 5961 DataFile file = MddTestUtil.createSharedDataFile("fileId", 0); 5962 NewFileKey newFileKey = 5963 SharedFilesMetadata.createKeyFromDataFile(file, AllowedReaders.ALL_GOOGLE_APPS); 5964 5965 ListenableFuture<Void> tryToShareFuture = 5966 fileGroupManager.tryToShareAfterDownload(fileGroup, file, newFileKey); 5967 5968 ExecutionException exception = assertThrows(ExecutionException.class, tryToShareFuture::get); 5969 assertThat(exception).hasCauseThat().isInstanceOf(SharedFileMissingException.class); 5970 5971 ArgumentCaptor<Void> mddAndroidSharingLogArgumentCaptor = ArgumentCaptor.forClass(Void.class); 5972 verify(mockLogger).logMddAndroidSharingLog(mddAndroidSharingLogArgumentCaptor.capture()); 5973 5974 Void mddAndroidSharingLog = null; 5975 assertThat(mddAndroidSharingLogArgumentCaptor.getAllValues()) 5976 .containsExactly(mddAndroidSharingLog); 5977 verifyNoMoreInteractions(mockLogger); 5978 5979 verify(mockBackend, never()).exists(any()); 5980 verify(mockBackend, never()).openForWrite(any()); 5981 verify(mockSharedFileManager, never()) 5982 .setAndroidSharedDownloadedFileEntry(any(), any(), anyLong()); 5983 verify(mockSharedFileManager, never()).updateMaxExpirationDateSecs(newFileKey, 0); 5984 } 5985 5986 @Test tryToShareAfterDownload_updateMaxExpirationDateSecsReturnsFalse()5987 public void tryToShareAfterDownload_updateMaxExpirationDateSecsReturnsFalse() throws Exception { 5988 // Mock SharedFileManager to test failure scenario. 5989 resetFileGroupManager(fileGroupsMetadata, mockSharedFileManager); 5990 // Create a file group with expiration date bigger than the expiration date of the existing 5991 // SharedFile. 5992 DataFileGroupInternal fileGroup = 5993 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 1).toBuilder() 5994 .setExpirationDateSecs(FILE_GROUP_EXPIRATION_DATE_SECS) 5995 .setDownloadConditions(DownloadConditions.getDefaultInstance()) 5996 .build(); 5997 5998 DataFile file = MddTestUtil.createDataFile("fileId", 0); 5999 NewFileKey newFileKey = 6000 SharedFilesMetadata.createKeyFromDataFile(file, AllowedReaders.ALL_GOOGLE_APPS); 6001 SharedFile existingSharedFile = 6002 SharedFile.newBuilder() 6003 .setFileStatus(FileStatus.DOWNLOAD_COMPLETE) 6004 .setFileName("fileName") 6005 .setAndroidShared(false) 6006 .build(); 6007 6008 when(mockSharedFileManager.getSharedFile(newFileKey)) 6009 .thenReturn(Futures.immediateFuture(existingSharedFile)); 6010 when(mockSharedFileManager.updateMaxExpirationDateSecs( 6011 newFileKey, FILE_GROUP_EXPIRATION_DATE_SECS)) 6012 .thenReturn(Futures.immediateFuture(false)); 6013 6014 File onDeviceFile = simulateDownload(file, existingSharedFile.getFileName()); 6015 Uri onDeviceuri = 6016 DirectoryUtil.getOnDeviceUri( 6017 context, 6018 newFileKey.getAllowedReaders(), 6019 existingSharedFile.getFileName(), 6020 newFileKey.getChecksum(), 6021 mockSilentFeedback, 6022 /* instanceId= */ Optional.absent(), 6023 false); 6024 assertThat(fileStorage.exists(onDeviceuri)).isTrue(); 6025 6026 fileGroupManager.tryToShareAfterDownload(fileGroup, file, newFileKey).get(); 6027 6028 verify(mockBackend, never()).exists(any()); 6029 verify(mockBackend, never()).openForWrite(any()); 6030 verify(mockSharedFileManager, never()) 6031 .setAndroidSharedDownloadedFileEntry(any(), any(), anyLong()); 6032 verify(mockSharedFileManager) 6033 .updateMaxExpirationDateSecs(newFileKey, FILE_GROUP_EXPIRATION_DATE_SECS); 6034 assertThat(fileStorage.exists(onDeviceuri)).isTrue(); 6035 onDeviceFile.delete(); 6036 6037 ArgumentCaptor<Void> mddAndroidSharingLogArgumentCaptor = ArgumentCaptor.forClass(Void.class); 6038 verify(mockLogger).logMddAndroidSharingLog(mddAndroidSharingLogArgumentCaptor.capture()); 6039 Void mddAndroidSharingLog = null; 6040 assertThat(mddAndroidSharingLogArgumentCaptor.getAllValues()) 6041 .containsExactly(mddAndroidSharingLog); 6042 verifyNoMoreInteractions(mockLogger); 6043 } 6044 6045 @Test tryToShareAfterDownload_setAndroidSharedDownloadedFileEntryReturnsFalse()6046 public void tryToShareAfterDownload_setAndroidSharedDownloadedFileEntryReturnsFalse() 6047 throws Exception { 6048 // Mock SharedFileManager to test failure scenario. 6049 resetFileGroupManager(fileGroupsMetadata, mockSharedFileManager); 6050 DataFileGroupInternal fileGroup = 6051 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2).toBuilder() 6052 .setDownloadConditions(DownloadConditions.getDefaultInstance()) 6053 .build(); 6054 6055 // Create a to-be-shared file 6056 DataFile file = MddTestUtil.createSharedDataFile("fileId", 0); 6057 NewFileKey newFileKey = 6058 SharedFilesMetadata.createKeyFromDataFile(file, AllowedReaders.ALL_GOOGLE_APPS); 6059 6060 SharedFile existingSharedFile = 6061 SharedFile.newBuilder() 6062 .setFileStatus(FileStatus.DOWNLOAD_COMPLETE) 6063 .setFileName("fileName") 6064 .setAndroidShared(false) 6065 .setMaxExpirationDateSecs(FILE_GROUP_EXPIRATION_DATE_SECS) 6066 .build(); 6067 6068 when(mockSharedFileManager.getSharedFile(newFileKey)) 6069 .thenReturn(Futures.immediateFuture(existingSharedFile)); 6070 Uri blobUri = DirectoryUtil.getBlobUri(context, file.getAndroidSharingChecksum()); 6071 Uri leaseUri = 6072 DirectoryUtil.getBlobStoreLeaseUri( 6073 context, file.getAndroidSharingChecksum(), FILE_GROUP_EXPIRATION_DATE_SECS); 6074 // The file is available in the blob storage 6075 when(mockBackend.exists(blobUri)).thenReturn(true); 6076 6077 // Last operation fails 6078 when(mockSharedFileManager.setAndroidSharedDownloadedFileEntry( 6079 newFileKey, file.getAndroidSharingChecksum(), FILE_GROUP_EXPIRATION_DATE_SECS)) 6080 .thenReturn(Futures.immediateFuture(false)); 6081 when(mockSharedFileManager.updateMaxExpirationDateSecs(newFileKey, 0)) 6082 .thenReturn(Futures.immediateFuture(true)); 6083 6084 File onDeviceFile = simulateDownload(file, existingSharedFile.getFileName()); 6085 Uri onDeviceuri = 6086 DirectoryUtil.getOnDeviceUri( 6087 context, 6088 newFileKey.getAllowedReaders(), 6089 existingSharedFile.getFileName(), 6090 newFileKey.getChecksum(), 6091 mockSilentFeedback, 6092 /* instanceId= */ Optional.absent(), 6093 false); 6094 assertThat(fileStorage.exists(onDeviceuri)).isTrue(); 6095 6096 fileGroupManager.tryToShareAfterDownload(fileGroup, file, newFileKey).get(); 6097 6098 verify(mockBackend).exists(blobUri); 6099 // openForWrite is called only once for acquiring the lease. 6100 verify(mockBackend, never()).openForWrite(blobUri); 6101 verify(mockBackend).openForWrite(leaseUri); 6102 verify(mockSharedFileManager).updateMaxExpirationDateSecs(newFileKey, 0); 6103 assertThat(fileStorage.exists(onDeviceuri)).isTrue(); 6104 onDeviceFile.delete(); 6105 6106 ArgumentCaptor<Void> mddAndroidSharingLogArgumentCaptor = ArgumentCaptor.forClass(Void.class); 6107 verify(mockLogger).logMddAndroidSharingLog(mddAndroidSharingLogArgumentCaptor.capture()); 6108 6109 Void mddAndroidSharingLog = null; 6110 assertThat(mddAndroidSharingLogArgumentCaptor.getAllValues()) 6111 .containsExactly(mddAndroidSharingLog); 6112 verifyNoMoreInteractions(mockLogger); 6113 } 6114 6115 @Test tryToShareAfterDownload_copyBlobThrowsIOException()6116 public void tryToShareAfterDownload_copyBlobThrowsIOException() throws Exception { 6117 DataFileGroupInternal fileGroup = 6118 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2).toBuilder() 6119 .setDownloadConditions(DownloadConditions.getDefaultInstance()) 6120 .build(); 6121 6122 // Create a to-be-shared file 6123 DataFile file = MddTestUtil.createSharedDataFile("fileId", 0); 6124 NewFileKey newFileKey = 6125 SharedFilesMetadata.createKeyFromDataFile(file, AllowedReaders.ALL_GOOGLE_APPS); 6126 6127 SharedFile existingSharedFile = 6128 SharedFile.newBuilder() 6129 .setFileStatus(FileStatus.DOWNLOAD_COMPLETE) 6130 .setFileName("fileName") 6131 .setAndroidShared(false) 6132 .setMaxExpirationDateSecs(FILE_GROUP_EXPIRATION_DATE_SECS) 6133 .build(); 6134 sharedFilesMetadata.write(newFileKey, existingSharedFile).get(); 6135 6136 Uri blobUri = DirectoryUtil.getBlobUri(context, file.getAndroidSharingChecksum()); 6137 Uri leaseUri = 6138 DirectoryUtil.getBlobStoreLeaseUri( 6139 context, file.getAndroidSharingChecksum(), FILE_GROUP_EXPIRATION_DATE_SECS); 6140 // The file isn't available in the blob storage 6141 when(mockBackend.exists(blobUri)).thenReturn(false); 6142 // Copying the blob throws an exception 6143 when(mockBackend.openForWrite(blobUri)).thenThrow(new IOException()); 6144 6145 File onDeviceFile = simulateDownload(file, existingSharedFile.getFileName()); 6146 Uri onDeviceuri = 6147 DirectoryUtil.getOnDeviceUri( 6148 context, 6149 newFileKey.getAllowedReaders(), 6150 existingSharedFile.getFileName(), 6151 newFileKey.getChecksum(), 6152 mockSilentFeedback, 6153 /* instanceId= */ Optional.absent(), 6154 false); 6155 assertThat(fileStorage.exists(onDeviceuri)).isTrue(); 6156 6157 fileGroupManager.tryToShareAfterDownload(fileGroup, file, newFileKey).get(); 6158 6159 verify(mockBackend, never()).openForWrite(leaseUri); 6160 6161 SharedFile sharedFile = sharedFileManager.getSharedFile(newFileKey).get(); 6162 assertThat(sharedFile).isEqualTo(existingSharedFile); 6163 6164 // Local copy still available. 6165 assertThat(fileStorage.exists(onDeviceuri)).isTrue(); 6166 onDeviceFile.delete(); 6167 6168 ArgumentCaptor<Void> mddAndroidSharingLogArgumentCaptor = ArgumentCaptor.forClass(Void.class); 6169 verify(mockLogger).logMddAndroidSharingLog(mddAndroidSharingLogArgumentCaptor.capture()); 6170 6171 Void mddAndroidSharingLog = null; 6172 assertThat(mddAndroidSharingLogArgumentCaptor.getAllValues()) 6173 .containsExactly(mddAndroidSharingLog); 6174 verifyNoMoreInteractions(mockLogger); 6175 } 6176 6177 @Test tryToShareAfterDownload_fileStorageThrowsLimitExceededException()6178 public void tryToShareAfterDownload_fileStorageThrowsLimitExceededException() throws Exception { 6179 // Create a file group with expiration date bigger than the expiration date of the existing 6180 // SharedFile. 6181 DataFileGroupInternal fileGroup = 6182 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2).toBuilder() 6183 .setDownloadConditions(DownloadConditions.getDefaultInstance()) 6184 .setExpirationDateSecs(FILE_GROUP_EXPIRATION_DATE_SECS + 1) 6185 .build(); 6186 6187 // Create a to-be-shared file 6188 DataFile file = MddTestUtil.createSharedDataFile("fileId", 0); 6189 NewFileKey newFileKey = 6190 SharedFilesMetadata.createKeyFromDataFile(file, AllowedReaders.ALL_GOOGLE_APPS); 6191 6192 SharedFile existingSharedFile = 6193 SharedFile.newBuilder() 6194 .setFileStatus(FileStatus.DOWNLOAD_COMPLETE) 6195 .setFileName("fileName") 6196 .setAndroidShared(false) 6197 .setMaxExpirationDateSecs(FILE_GROUP_EXPIRATION_DATE_SECS) 6198 .build(); 6199 sharedFilesMetadata.write(newFileKey, existingSharedFile).get(); 6200 6201 Uri blobUri = DirectoryUtil.getBlobUri(context, file.getAndroidSharingChecksum()); 6202 Uri leaseUri = 6203 DirectoryUtil.getBlobStoreLeaseUri( 6204 context, file.getAndroidSharingChecksum(), FILE_GROUP_EXPIRATION_DATE_SECS + 1); 6205 // The file is available in the blob storage 6206 when(mockBackend.exists(blobUri)).thenReturn(true); 6207 6208 File onDeviceFile = simulateDownload(file, existingSharedFile.getFileName()); 6209 Uri onDeviceuri = 6210 DirectoryUtil.getOnDeviceUri( 6211 context, 6212 newFileKey.getAllowedReaders(), 6213 existingSharedFile.getFileName(), 6214 newFileKey.getChecksum(), 6215 mockSilentFeedback, 6216 /* instanceId= */ Optional.absent(), 6217 false); 6218 assertThat(fileStorage.exists(onDeviceuri)).isTrue(); 6219 6220 // Writing the lease throws an exception 6221 when(mockBackend.openForWrite(leaseUri)).thenThrow(new LimitExceededException()); 6222 6223 fileGroupManager.tryToShareAfterDownload(fileGroup, file, newFileKey).get(); 6224 6225 verify(mockBackend, never()).openForWrite(blobUri); 6226 verify(mockBackend).openForWrite(leaseUri); 6227 6228 SharedFile sharedFile = sharedFileManager.getSharedFile(newFileKey).get(); 6229 // Even if there was an exception, the SharedFile has updated its expiration date after the 6230 // download. 6231 SharedFile expectedSharedFile = 6232 existingSharedFile.toBuilder() 6233 .setMaxExpirationDateSecs(FILE_GROUP_EXPIRATION_DATE_SECS + 1) 6234 .build(); 6235 assertThat(sharedFile).isEqualTo(expectedSharedFile); 6236 6237 // Local copy still available. 6238 assertThat(fileStorage.exists(onDeviceuri)).isTrue(); 6239 onDeviceFile.delete(); 6240 6241 ArgumentCaptor<Void> mddAndroidSharingLogArgumentCaptor = ArgumentCaptor.forClass(Void.class); 6242 verify(mockLogger).logMddAndroidSharingLog(mddAndroidSharingLogArgumentCaptor.capture()); 6243 6244 Void mddAndroidSharingLog = null; 6245 assertThat(mddAndroidSharingLogArgumentCaptor.getAllValues()) 6246 .containsExactly(mddAndroidSharingLog); 6247 verifyNoMoreInteractions(mockLogger); 6248 } 6249 6250 @Test testVerifyPendingGroupDownloaded()6251 public void testVerifyPendingGroupDownloaded() throws Exception { 6252 // Write 2 groups to the pending shared prefs. 6253 DataFileGroupInternal fileGroup1 = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2); 6254 writePendingFileGroup(testKey, fileGroup1); 6255 DataFileGroupInternal fileGroup2 = MddTestUtil.createDataFileGroupInternal(TEST_GROUP_2, 2); 6256 writePendingFileGroup(testKey2, fileGroup2); 6257 6258 // Make the verify download call fail for one file in the first group. 6259 writeSharedFiles( 6260 sharedFilesMetadata, 6261 fileGroup1, 6262 ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE, FileStatus.DOWNLOAD_IN_PROGRESS)); 6263 writeSharedFiles( 6264 sharedFilesMetadata, 6265 fileGroup2, 6266 ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE, FileStatus.DOWNLOAD_COMPLETE)); 6267 6268 testClock.set(/* millis */ 1000); 6269 6270 assertThat( 6271 fileGroupManager 6272 .verifyGroupDownloaded( 6273 testKey, 6274 fileGroup1, 6275 /* removePendingVersion= */ true, 6276 noCustomValidation(), 6277 DownloadStateLogger.forDownload(mockLogger)) 6278 .get()) 6279 .isEqualTo(GroupDownloadStatus.PENDING); 6280 assertThat( 6281 fileGroupManager 6282 .verifyGroupDownloaded( 6283 testKey2, 6284 fileGroup2, 6285 /* removePendingVersion= */ true, 6286 noCustomValidation(), 6287 DownloadStateLogger.forDownload(mockLogger)) 6288 .get()) 6289 .isEqualTo(GroupDownloadStatus.DOWNLOADED); 6290 6291 // Verify that the pending group is still part of pending groups prefs. 6292 DataFileGroupInternal pendingGroup1 = readPendingFileGroup(testKey); 6293 assertThat(pendingGroup1).isEqualTo(fileGroup1); 6294 6295 // Verify that the pending group is not written into metadata. 6296 assertThat(readDownloadedFileGroup(testKey)).isNull(); 6297 6298 fileGroup2 = FileGroupUtil.setDownloadedTimestampInMillis(fileGroup2, 1000); 6299 6300 // Verify that the completely downloaded group is written into metadata. 6301 DataFileGroupInternal downloadedGroup2 = readDownloadedFileGroup(testKey2); 6302 assertThat(downloadedGroup2).isEqualTo(fileGroup2); 6303 6304 verify(mockLogger) 6305 .logEventSampled( 6306 MddClientEvent.Code.EVENT_CODE_UNSPECIFIED, 6307 TEST_GROUP, 6308 /* fileGroupVersionNumber= */ 0, 6309 /* buildId= */ 0, 6310 /* variantId= */ ""); 6311 verify(mockLogger) 6312 .logEventSampled( 6313 MddClientEvent.Code.EVENT_CODE_UNSPECIFIED, 6314 TEST_GROUP_2, 6315 /* fileGroupVersionNumber= */ 0, 6316 /* buildId= */ 0, 6317 /* variantId= */ ""); 6318 } 6319 6320 @Test testVerifyAllPendingGroupsDownloaded()6321 public void testVerifyAllPendingGroupsDownloaded() throws Exception { 6322 // Write 2 groups to the pending shared prefs. 6323 DataFileGroupInternal fileGroup1 = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2); 6324 writePendingFileGroup(testKey, fileGroup1); 6325 DataFileGroupInternal fileGroup2 = MddTestUtil.createDataFileGroupInternal(TEST_GROUP_2, 2); 6326 writePendingFileGroup(testKey2, fileGroup2); 6327 6328 // Make the verify download call fail for one file in the first group. 6329 writeSharedFiles( 6330 sharedFilesMetadata, 6331 fileGroup1, 6332 ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE, FileStatus.DOWNLOAD_IN_PROGRESS)); 6333 writeSharedFiles( 6334 sharedFilesMetadata, 6335 fileGroup2, 6336 ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE, FileStatus.DOWNLOAD_COMPLETE)); 6337 6338 testClock.set(/* millis */ 1000); 6339 fileGroupManager.verifyAllPendingGroupsDownloaded(noCustomValidation()).get(); 6340 6341 // Verify that the pending group is still part of pending groups prefs. 6342 DataFileGroupInternal pendingGroup1 = readPendingFileGroup(testKey); 6343 MddTestUtil.assertMessageEquals(fileGroup1, pendingGroup1); 6344 6345 // Verify that the pending group is not written into metadata. 6346 assertThat(readDownloadedFileGroup(testKey)).isNull(); 6347 6348 fileGroup2 = FileGroupUtil.setDownloadedTimestampInMillis(fileGroup2, 1000); 6349 6350 // Verify that the completely downloaded group is written into metadata. 6351 DataFileGroupInternal downloadedGroup2 = readDownloadedFileGroup(testKey2); 6352 assertThat(downloadedGroup2).isEqualTo(fileGroup2); 6353 6354 verify(mockLogger) 6355 .logEventSampled( 6356 MddClientEvent.Code.EVENT_CODE_UNSPECIFIED, 6357 TEST_GROUP, 6358 /* fileGroupVersionNumber= */ 0, 6359 /* buildId= */ 0, 6360 /* variantId= */ ""); 6361 verify(mockLogger) 6362 .logEventSampled( 6363 MddClientEvent.Code.EVENT_CODE_UNSPECIFIED, 6364 TEST_GROUP_2, 6365 /* fileGroupVersionNumber= */ 0, 6366 /* buildId= */ 0, 6367 /* variantId= */ ""); 6368 } 6369 6370 @Test testVerifyAllPendingGroupsDownloaded_existingDownloadedGroup()6371 public void testVerifyAllPendingGroupsDownloaded_existingDownloadedGroup() throws Exception { 6372 // Write 2 groups to the pending shared prefs. 6373 DataFileGroupInternal fileGroup1 = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2); 6374 writePendingFileGroup(testKey, fileGroup1); 6375 DataFileGroupInternal fileGroup2 = MddTestUtil.createDataFileGroupInternal(TEST_GROUP_2, 2); 6376 writePendingFileGroup(testKey2, fileGroup2); 6377 6378 // Also write 2 groups to the downloaded shared prefs. 6379 // fileGroup3 is the downloaded version if fileGroup1. 6380 DataFileGroupInternal fileGroup3 = 6381 MddTestUtil.createDownloadedDataFileGroupInternal(TEST_GROUP, 1); 6382 writeDownloadedFileGroup(testKey, fileGroup3); 6383 DataFileGroupInternal fileGroup4 = 6384 MddTestUtil.createDownloadedDataFileGroupInternal(TEST_GROUP_3, 2); 6385 writeDownloadedFileGroup(testKey3, fileGroup4); 6386 6387 // All file are downloaded. 6388 writeSharedFiles( 6389 sharedFilesMetadata, 6390 fileGroup1, 6391 ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE, FileStatus.DOWNLOAD_COMPLETE)); 6392 writeSharedFiles( 6393 sharedFilesMetadata, 6394 fileGroup2, 6395 ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE, FileStatus.DOWNLOAD_COMPLETE)); 6396 writeSharedFiles( 6397 sharedFilesMetadata, fileGroup3, ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE)); 6398 writeSharedFiles( 6399 sharedFilesMetadata, 6400 fileGroup4, 6401 ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE, FileStatus.DOWNLOAD_COMPLETE)); 6402 6403 testClock.set(/* millis */ 1000); 6404 fileGroupManager.verifyAllPendingGroupsDownloaded(noCustomValidation()).get(); 6405 6406 // Verify that pending key is removed if the group is downloaded. 6407 assertThat(readPendingFileGroup(testKey)).isNull(); 6408 assertThat(readPendingFileGroup(testKey2)).isNull(); 6409 assertThat(readPendingFileGroup(testKey3)).isNull(); 6410 6411 fileGroup1 = FileGroupUtil.setDownloadedTimestampInMillis(fileGroup1, 1000); 6412 fileGroup2 = FileGroupUtil.setDownloadedTimestampInMillis(fileGroup2, 1000); 6413 6414 // Verify that pending group is marked as downloaded group. 6415 DataFileGroupInternal downloadedGroup1 = readDownloadedFileGroup(testKey); 6416 assertThat(downloadedGroup1).isEqualTo(fileGroup1); 6417 DataFileGroupInternal downloadedGroup2 = readDownloadedFileGroup(testKey2); 6418 assertThat(downloadedGroup2).isEqualTo(fileGroup2); 6419 DataFileGroupInternal downloadedGroup4 = readDownloadedFileGroup(testKey3); 6420 assertThat(downloadedGroup4).isEqualTo(fileGroup4); 6421 6422 verify(mockLogger) 6423 .logEventSampled( 6424 MddClientEvent.Code.EVENT_CODE_UNSPECIFIED, 6425 TEST_GROUP, 6426 /* fileGroupVersionNumber= */ 0, 6427 /* buildId= */ 0, 6428 /* variantId= */ ""); 6429 verify(mockLogger) 6430 .logEventSampled( 6431 MddClientEvent.Code.EVENT_CODE_UNSPECIFIED, 6432 TEST_GROUP_2, 6433 /* fileGroupVersionNumber= */ 0, 6434 /* buildId= */ 0, 6435 /* variantId= */ ""); 6436 6437 // fileGroup3 should have been scheduled for deletion. 6438 fileGroup3 = 6439 fileGroup3.toBuilder() 6440 .setBookkeeping(DataFileGroupBookkeeping.newBuilder().setStaleExpirationDate(1).build()) 6441 .build(); 6442 assertThat(fileGroupsMetadata.getAllStaleGroups().get()).containsExactly(fileGroup3); 6443 } 6444 6445 @Test testGroupDownloadFailed()6446 public void testGroupDownloadFailed() throws Exception { 6447 // Write 2 groups to the pending shared prefs. 6448 DataFileGroupInternal fileGroup1 = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2); 6449 writePendingFileGroup(testKey, fileGroup1); 6450 DataFileGroupInternal fileGroup2 = MddTestUtil.createDataFileGroupInternal(TEST_GROUP_2, 2); 6451 writePendingFileGroup(testKey2, fileGroup2); 6452 6453 // Make the second file of the first group fail. 6454 writeSharedFiles( 6455 sharedFilesMetadata, 6456 fileGroup1, 6457 ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE, FileStatus.DOWNLOAD_FAILED)); 6458 writeSharedFiles( 6459 sharedFilesMetadata, 6460 fileGroup2, 6461 ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE, FileStatus.DOWNLOAD_COMPLETE)); 6462 6463 fileGroupManager.verifyAllPendingGroupsDownloaded(noCustomValidation()).get(); 6464 6465 // Verify that pending key is removed if download is complete. 6466 assertThat(readPendingFileGroup(testKey)).isEqualTo(fileGroup1); 6467 assertThat(readPendingFileGroup(testKey2)).isNull(); 6468 6469 // Verify that downloaded key is written into metadata if download is complete. 6470 fileGroup2 = FileGroupUtil.setDownloadedTimestampInMillis(fileGroup2, 1000); 6471 DataFileGroupInternal downloadedGroup2 = readDownloadedFileGroup(testKey2); 6472 assertThat(downloadedGroup2).isEqualTo(fileGroup2); 6473 6474 verify(mockLogger) 6475 .logEventSampled( 6476 MddClientEvent.Code.EVENT_CODE_UNSPECIFIED, 6477 TEST_GROUP, 6478 /* fileGroupVersionNumber= */ 0, 6479 /* buildId= */ 0, 6480 /* variantId= */ ""); 6481 verify(mockLogger) 6482 .logEventSampled( 6483 MddClientEvent.Code.EVENT_CODE_UNSPECIFIED, 6484 TEST_GROUP_2, 6485 /* fileGroupVersionNumber= */ 0, 6486 /* buildId= */ 0, 6487 /* variantId= */ ""); 6488 } 6489 6490 @Test testDeleteUninstalledAppGroups_noUninstalledApps()6491 public void testDeleteUninstalledAppGroups_noUninstalledApps() throws Exception { 6492 PackageManager packageManager = context.getPackageManager(); 6493 final PackageInfo packageInfo = new PackageInfo(); 6494 packageInfo.packageName = context.getPackageName(); 6495 packageInfo.lastUpdateTime = System.currentTimeMillis(); 6496 Shadows.shadowOf(packageManager).addPackage(packageInfo); 6497 6498 DataFileGroupInternal fileGroup1 = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2); 6499 writePendingFileGroup(testKey, fileGroup1); 6500 6501 DataFileGroupInternal fileGroup2 = MddTestUtil.createDataFileGroupInternal(TEST_GROUP_2, 2); 6502 writePendingFileGroup(testKey2, fileGroup2); 6503 6504 fileGroupManager.deleteUninstalledAppGroups().get(); 6505 6506 assertThat(readPendingFileGroup(testKey)).isEqualTo(fileGroup1); 6507 assertThat(readPendingFileGroup(testKey2)).isEqualTo(fileGroup2); 6508 } 6509 6510 @Test testDeleteUninstalledAppGroups_uninstalledApp()6511 public void testDeleteUninstalledAppGroups_uninstalledApp() throws Exception { 6512 PackageManager packageManager = context.getPackageManager(); 6513 final PackageInfo packageInfo = new PackageInfo(); 6514 packageInfo.packageName = context.getPackageName(); 6515 packageInfo.lastUpdateTime = System.currentTimeMillis(); 6516 Shadows.shadowOf(packageManager).addPackage(packageInfo); 6517 6518 DataFileGroupInternal fileGroup1 = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2); 6519 writePendingFileGroup(testKey, fileGroup1); 6520 DataFileGroupInternal fileGroup2 = MddTestUtil.createDataFileGroupInternal(TEST_GROUP_2, 2); 6521 GroupKey uninstalledAppKey = 6522 GroupKey.newBuilder().setGroupName(TEST_GROUP_2).setOwnerPackage("uninstalled.app").build(); 6523 writeDownloadedFileGroup(uninstalledAppKey, fileGroup2); 6524 6525 assertThat(readPendingFileGroup(testKey)).isEqualTo(fileGroup1); 6526 assertThat(readDownloadedFileGroup(uninstalledAppKey)).isEqualTo(fileGroup2); 6527 6528 fileGroupManager.deleteUninstalledAppGroups().get(); 6529 6530 assertThat(readPendingFileGroup(testKey)).isEqualTo(fileGroup1); 6531 assertThat(readDownloadedFileGroup(uninstalledAppKey)).isNull(); 6532 } 6533 6534 @Test testDeleteRemovedAccountGroups_noRemovedAccounts()6535 public void testDeleteRemovedAccountGroups_noRemovedAccounts() throws Exception { 6536 Account account1 = new Account("name1", "type1"); 6537 Account account2 = new Account("name2", "type2"); 6538 6539 when(mockAccountSource.getAllAccounts()).thenReturn(ImmutableList.of(account1, account2)); 6540 6541 DataFileGroupInternal fileGroup1 = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2); 6542 GroupKey key1 = 6543 GroupKey.newBuilder() 6544 .setGroupName(TEST_GROUP) 6545 .setOwnerPackage(context.getPackageName()) 6546 .setAccount(AccountUtil.serialize(account1)) 6547 .build(); 6548 6549 DataFileGroupInternal fileGroup2 = MddTestUtil.createDataFileGroupInternal(TEST_GROUP_2, 2); 6550 GroupKey key2 = 6551 GroupKey.newBuilder() 6552 .setGroupName(TEST_GROUP_2) 6553 .setOwnerPackage(context.getPackageName()) 6554 .setAccount(AccountUtil.serialize(account2)) 6555 .build(); 6556 6557 DataFileGroupInternal fileGroup3 = MddTestUtil.createDataFileGroupInternal(TEST_GROUP_3, 2); 6558 GroupKey key3 = 6559 GroupKey.newBuilder() 6560 .setGroupName(TEST_GROUP_3) 6561 .setOwnerPackage(context.getPackageName()) 6562 .build(); 6563 6564 writeDownloadedFileGroup(key1, fileGroup1); 6565 writeDownloadedFileGroup(key2, fileGroup2); 6566 writeDownloadedFileGroup(key3, fileGroup3); 6567 6568 fileGroupManager.deleteRemovedAccountGroups().get(); 6569 6570 assertThat(fileGroupsMetadata.getAllGroupKeys().get()) 6571 .containsExactly(getDownloadedKey(key1), getDownloadedKey(key2), getDownloadedKey(key3)); 6572 6573 verifyNoInteractions(mockLogger); 6574 } 6575 6576 @Test testDeleteRemovedAccountGroups_removedAccounts()6577 public void testDeleteRemovedAccountGroups_removedAccounts() throws Exception { 6578 Account account1 = new Account("name1", "type1"); 6579 Account account2 = new Account("name2", "type2"); 6580 6581 when(mockAccountSource.getAllAccounts()).thenReturn(ImmutableList.of(account1)); 6582 6583 DataFileGroupInternal fileGroup1 = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2); 6584 GroupKey key1 = 6585 GroupKey.newBuilder() 6586 .setGroupName(TEST_GROUP) 6587 .setOwnerPackage(context.getPackageName()) 6588 .setAccount(AccountUtil.serialize(account1)) 6589 .build(); 6590 6591 DataFileGroupInternal fileGroup2 = MddTestUtil.createDataFileGroupInternal(TEST_GROUP_2, 2); 6592 GroupKey key2 = 6593 GroupKey.newBuilder() 6594 .setGroupName(TEST_GROUP_2) 6595 .setOwnerPackage(context.getPackageName()) 6596 .setAccount(AccountUtil.serialize(account2)) 6597 .build(); 6598 6599 DataFileGroupInternal fileGroup3 = MddTestUtil.createDataFileGroupInternal(TEST_GROUP_3, 2); 6600 GroupKey key3 = 6601 GroupKey.newBuilder() 6602 .setGroupName(TEST_GROUP_3) 6603 .setOwnerPackage(context.getPackageName()) 6604 .build(); 6605 6606 writeDownloadedFileGroup(key1, fileGroup1); 6607 writeDownloadedFileGroup(key2, fileGroup2); 6608 writeDownloadedFileGroup(key3, fileGroup3); 6609 6610 fileGroupManager.deleteRemovedAccountGroups().get(); 6611 6612 assertThat(fileGroupsMetadata.getAllGroupKeys().get()) 6613 .containsExactly(getDownloadedKey(key1), getDownloadedKey(key3)); 6614 6615 verify(mockLogger) 6616 .logEventSampled( 6617 MddClientEvent.Code.EVENT_CODE_UNSPECIFIED, 6618 TEST_GROUP_2, 6619 /* fileGroupVersionNumber= */ 0, 6620 /* buildId= */ 0, 6621 /* variantId= */ ""); 6622 verifyNoMoreInteractions(mockLogger); 6623 } 6624 6625 @Test testLogAndDeleteForMissingSharedFiles()6626 public void testLogAndDeleteForMissingSharedFiles() throws Exception { 6627 resetFileGroupManager(fileGroupsMetadata, mockSharedFileManager); 6628 6629 GroupKey downloadedGroupKeyWithFileMissing = 6630 GroupKey.newBuilder() 6631 .setGroupName(TEST_GROUP) 6632 .setOwnerPackage(context.getPackageName()) 6633 .setDownloaded(true) 6634 .build(); 6635 DataFileGroupInternal fileGroup1 = MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2); 6636 writeDownloadedFileGroup(downloadedGroupKeyWithFileMissing, fileGroup1); 6637 NewFileKey[] keys = MddTestUtil.createFileKeysForDataFileGroupInternal(fileGroup1); 6638 when(mockSharedFileManager.reVerifyFile(eq(keys[0]), eq(fileGroup1.getFile(0)))) 6639 .thenReturn(Futures.immediateFailedFuture(new SharedFileMissingException())); 6640 when(mockSharedFileManager.reVerifyFile(eq(keys[1]), eq(fileGroup1.getFile(1)))) 6641 .thenReturn(Futures.immediateFailedFuture(new SharedFileMissingException())); 6642 6643 GroupKey pendingGroupKeyWithFileMissing = 6644 GroupKey.newBuilder() 6645 .setGroupName(TEST_GROUP_2) 6646 .setOwnerPackage(context.getPackageName()) 6647 .setDownloaded(false) 6648 .build(); 6649 DataFileGroupInternal fileGroup2 = MddTestUtil.createDataFileGroupInternal(TEST_GROUP_2, 2); 6650 writePendingFileGroup(pendingGroupKeyWithFileMissing, fileGroup2); 6651 // Write only the first file metadata. 6652 NewFileKey[] keys2 = MddTestUtil.createFileKeysForDataFileGroupInternal(fileGroup2); 6653 when(mockSharedFileManager.reVerifyFile(eq(keys2[0]), eq(fileGroup2.getFile(0)))) 6654 .thenReturn(Futures.immediateFailedFuture(new SharedFileMissingException())); 6655 // mockSharedFileManager returns "OK" when verifying second file. 6656 6657 GroupKey groupKeyWithNoFileMissing = 6658 GroupKey.newBuilder() 6659 .setGroupName(TEST_GROUP_3) 6660 .setOwnerPackage(context.getPackageName()) 6661 .setDownloaded(true) 6662 .build(); 6663 DataFileGroupInternal fileGroup3 = MddTestUtil.createDataFileGroupInternal(TEST_GROUP_3, 2); 6664 writeDownloadedFileGroup(groupKeyWithNoFileMissing, fileGroup3); 6665 // mockSharedFileManager always returns "OK" when verifying files. 6666 6667 fileGroupManager.logAndDeleteForMissingSharedFiles().get(); 6668 6669 if (flags.deleteFileGroupsWithFilesMissing()) { 6670 assertThat(fileGroupsMetadata.getAllGroupKeys().get()) 6671 .containsExactly(groupKeyWithNoFileMissing); 6672 } else { 6673 assertThat(fileGroupsMetadata.getAllGroupKeys().get()) 6674 .containsExactly( 6675 downloadedGroupKeyWithFileMissing, 6676 pendingGroupKeyWithFileMissing, 6677 groupKeyWithNoFileMissing); 6678 } 6679 6680 verify(mockLogger, times(2)) 6681 .logEventSampled( 6682 MddClientEvent.Code.EVENT_CODE_UNSPECIFIED, 6683 TEST_GROUP, 6684 /* fileGroupVersionNumber= */ 0, 6685 /* buildId= */ 0, 6686 /* variantId= */ ""); 6687 verify(mockLogger) 6688 .logEventSampled( 6689 MddClientEvent.Code.EVENT_CODE_UNSPECIFIED, 6690 TEST_GROUP_2, 6691 /* fileGroupVersionNumber= */ 0, 6692 /* buildId= */ 0, 6693 /* variantId= */ ""); 6694 verifyNoMoreInteractions(mockLogger); 6695 } 6696 6697 @Test getOnDeviceUri_shortcutsForSideloadedFiles_delegatesToSharedFileManagerOtherwise()6698 public void getOnDeviceUri_shortcutsForSideloadedFiles_delegatesToSharedFileManagerOtherwise() 6699 throws Exception { 6700 // Ensure that sideloading is turned off 6701 flags.enableSideloading = Optional.of(true); 6702 6703 // Create mixed group 6704 DataFileGroupInternal sideloadedGroup = 6705 DataFileGroupInternal.newBuilder() 6706 .setGroupName(TEST_GROUP) 6707 .addFile( 6708 DataFile.newBuilder() 6709 .setFileId("sideloaded_file") 6710 .setUrlToDownload("file:/test") 6711 .setChecksumType(DataFile.ChecksumType.NONE) 6712 .build()) 6713 .addFile( 6714 DataFile.newBuilder() 6715 .setFileId("standard_file") 6716 .setUrlToDownload("https://url.to.download") 6717 .setChecksumType(DataFile.ChecksumType.NONE) 6718 .build()) 6719 .addFile( 6720 DataFile.newBuilder() 6721 .setFileId("inline_file") 6722 .setUrlToDownload("inlinefile:sha1:checksum") 6723 .setChecksum("checksum") 6724 .build()) 6725 .build(); 6726 6727 // Write shared files so shared file manager can get uris 6728 NewFileKey[] newFileKeys = MddTestUtil.createFileKeysForDataFileGroupInternal(sideloadedGroup); 6729 6730 sharedFilesMetadata 6731 .write( 6732 newFileKeys[1], 6733 SharedFile.newBuilder() 6734 .setFileName(sideloadedGroup.getFile(1).getFileId()) 6735 .setFileStatus(FileStatus.DOWNLOAD_COMPLETE) 6736 .build()) 6737 .get(); 6738 sharedFilesMetadata 6739 .write( 6740 newFileKeys[2], 6741 SharedFile.newBuilder() 6742 .setFileName(sideloadedGroup.getFile(2).getFileId()) 6743 .setFileStatus(FileStatus.DOWNLOAD_COMPLETE) 6744 .build()) 6745 .get(); 6746 6747 assertThat( 6748 fileGroupManager 6749 .getOnDeviceUri(sideloadedGroup.getFile(0), sideloadedGroup) 6750 .get() 6751 .getScheme()) 6752 .isEqualTo("file"); 6753 assertThat( 6754 fileGroupManager 6755 .getOnDeviceUri(sideloadedGroup.getFile(1), sideloadedGroup) 6756 .get() 6757 .getScheme()) 6758 .isEqualTo("android"); 6759 assertThat( 6760 fileGroupManager 6761 .getOnDeviceUri(sideloadedGroup.getFile(2), sideloadedGroup) 6762 .get() 6763 .getScheme()) 6764 .isEqualTo("android"); 6765 } 6766 6767 @Test testAddGroupForDownload_withExperimentationConfig()6768 public void testAddGroupForDownload_withExperimentationConfig() throws Exception { 6769 flags.enableDownloadStageExperimentIdPropagation = Optional.of(true); 6770 6771 Long buildId = 999L; 6772 Integer experimentId = 12345; 6773 DataFileGroupInternal dataFileGroup = 6774 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2).toBuilder() 6775 .setBuildId(buildId) 6776 .build(); 6777 6778 assertThat(fileGroupManager.addGroupForDownload(testKey, dataFileGroup).get()).isTrue(); 6779 } 6780 6781 @Test testAddGroupForDownload_withExperimentationConfig_overwritesPendingExperimentIds()6782 public void testAddGroupForDownload_withExperimentationConfig_overwritesPendingExperimentIds() 6783 throws Exception { 6784 flags.enableDownloadStageExperimentIdPropagation = Optional.of(true); 6785 6786 long buildId = 999L; 6787 long buildId2 = 100L; 6788 int experimentId = 12345; 6789 int experimentId2 = 23456; 6790 6791 DataFileGroupInternal dataFileGroup = 6792 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2).toBuilder() 6793 .setBuildId(buildId) 6794 .build(); 6795 6796 DataFileGroupInternal dataFileGroup2 = 6797 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2).toBuilder() 6798 .setBuildId(buildId2) 6799 .build(); 6800 6801 assertThat(fileGroupManager.addGroupForDownload(testKey, dataFileGroup).get()).isTrue(); 6802 // Overwrite the group. The old experiment id should be deleted and the new experiment id should 6803 // be populated. 6804 assertThat(fileGroupManager.addGroupForDownload(testKey, dataFileGroup2).get()).isTrue(); 6805 } 6806 6807 @Test testDownloadPendingGroup_withExperimentationConfig_updatesExperimentIdToDownloaded()6808 public void testDownloadPendingGroup_withExperimentationConfig_updatesExperimentIdToDownloaded() 6809 throws Exception { 6810 flags.enableDownloadStageExperimentIdPropagation = Optional.of(true); 6811 6812 int experimentIdDownloading = 12345; 6813 int experimentIdDownloaded = 23456; 6814 long buildId = 999L; 6815 6816 ExtraHttpHeader extraHttpHeader = 6817 ExtraHttpHeader.newBuilder().setKey("user-agent").setValue("mdd-downloader").build(); 6818 6819 // Write 1 group to the pending shared prefs. 6820 DataFileGroupInternal fileGroup = 6821 createDataFileGroup( 6822 TEST_GROUP, 6823 /* fileCount= */ 2, 6824 /* downloadAttemptCount= */ 3, 6825 /* newFilesReceivedTimestamp= */ testClock.currentTimeMillis() - 500L) 6826 .toBuilder() 6827 .setBuildId(buildId) 6828 .setOwnerPackage(context.getPackageName()) 6829 .setDownloadConditions(DownloadConditions.getDefaultInstance()) 6830 .setTrafficTag(TRAFFIC_TAG) 6831 .addGroupExtraHttpHeaders(extraHttpHeader) 6832 .build(); 6833 6834 writePendingFileGroup(testKey, fileGroup); 6835 6836 writeSharedFiles( 6837 sharedFilesMetadata, 6838 fileGroup, 6839 ImmutableList.of(FileStatus.DOWNLOAD_COMPLETE, FileStatus.DOWNLOAD_COMPLETE)); 6840 6841 fileGroupManager 6842 .downloadFileGroup(testKey, DownloadConditions.getDefaultInstance(), noCustomValidation()) 6843 .get(); 6844 } 6845 6846 @Test testRemoveFileGroup_withExperimentationConfig_removesExperimentIds()6847 public void testRemoveFileGroup_withExperimentationConfig_removesExperimentIds() 6848 throws Exception { 6849 flags.enableDownloadStageExperimentIdPropagation = Optional.of(true); 6850 6851 long buildId = 999L; 6852 int experimentId = 12345; 6853 DataFileGroupInternal dataFileGroup = 6854 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2).toBuilder() 6855 .setBuildId(buildId) 6856 .build(); 6857 6858 assertThat(fileGroupManager.addGroupForDownload(testKey, dataFileGroup).get()).isTrue(); 6859 fileGroupManager.removeFileGroup(testKey, /* pendingOnly= */ false).get(); 6860 } 6861 6862 @Test testRemoveFileGroups_withExperimentationConfig_removesExperimentIds()6863 public void testRemoveFileGroups_withExperimentationConfig_removesExperimentIds() 6864 throws Exception { 6865 flags.enableDownloadStageExperimentIdPropagation = Optional.of(true); 6866 6867 long buildId = 999L; 6868 int experimentId = 12345; 6869 DataFileGroupInternal dataFileGroup = 6870 MddTestUtil.createDataFileGroupInternal(TEST_GROUP, 2).toBuilder() 6871 .setBuildId(buildId) 6872 .build(); 6873 6874 assertThat(fileGroupManager.addGroupForDownload(testKey, dataFileGroup).get()).isTrue(); 6875 fileGroupManager.removeFileGroups(ImmutableList.of(testKey)).get(); 6876 } 6877 6878 /** 6879 * Re-instantiates {@code fileGroupManager} with the injected parameters. 6880 * 6881 * <p>It can be used to work with the mocks for FileGroupsMetadata and/or SharedFileManager. 6882 */ resetFileGroupManager( FileGroupsMetadata fileGroupsMetadata, SharedFileManager sharedFileManager)6883 private void resetFileGroupManager( 6884 FileGroupsMetadata fileGroupsMetadata, SharedFileManager sharedFileManager) throws Exception { 6885 resetFileGroupManager(this.mockLogger, fileGroupsMetadata, sharedFileManager); 6886 } 6887 resetFileGroupManager( EventLogger eventLogger, FileGroupsMetadata fileGroupsMetadata, SharedFileManager sharedFileManager)6888 private void resetFileGroupManager( 6889 EventLogger eventLogger, 6890 FileGroupsMetadata fileGroupsMetadata, 6891 SharedFileManager sharedFileManager) 6892 throws Exception { 6893 fileGroupManager = 6894 new FileGroupManager( 6895 context, 6896 eventLogger, 6897 mockSilentFeedback, 6898 fileGroupsMetadata, 6899 sharedFileManager, 6900 testClock, 6901 Optional.of(mockAccountSource), 6902 SEQUENTIAL_CONTROL_EXECUTOR, 6903 Optional.absent(), 6904 fileStorage, 6905 downloadStageManager, 6906 flags); 6907 } 6908 createFileGroupDetails( DataFileGroupInternal fileGroup)6909 private static DataDownloadFileGroupStats.Builder createFileGroupDetails( 6910 DataFileGroupInternal fileGroup) { 6911 return DataDownloadFileGroupStats.newBuilder() 6912 .setOwnerPackage(fileGroup.getOwnerPackage()) 6913 .setFileGroupName(fileGroup.getGroupName()) 6914 .setFileGroupVersionNumber(fileGroup.getFileGroupVersionNumber()) 6915 .setBuildId(fileGroup.getBuildId()) 6916 .setVariantId(fileGroup.getVariantId()) 6917 .setFileCount(fileGroup.getFileCount()); 6918 } 6919 createMddDownloadLatency( int downloadAttemptCount, long downloadLatencyMs, long totalLatencyMs)6920 private static Void createMddDownloadLatency( 6921 int downloadAttemptCount, long downloadLatencyMs, long totalLatencyMs) { 6922 return null; 6923 } 6924 createDataFileGroup( String groupName, int fileCount, int downloadAttemptCount, long newFilesReceivedTimestamp)6925 private static DataFileGroupInternal createDataFileGroup( 6926 String groupName, int fileCount, int downloadAttemptCount, long newFilesReceivedTimestamp) { 6927 return MddTestUtil.createDataFileGroupInternal(groupName, fileCount).toBuilder() 6928 .setBookkeeping( 6929 DataFileGroupBookkeeping.newBuilder() 6930 .setDownloadStartedCount(downloadAttemptCount) 6931 .setGroupNewFilesReceivedTimestamp(newFilesReceivedTimestamp)) 6932 .build(); 6933 } 6934 6935 /** The file download succeeds so the new file status is DOWNLOAD_COMPLETE. */ fileDownloadSucceeds(NewFileKey key, Uri fileUri)6936 private void fileDownloadSucceeds(NewFileKey key, Uri fileUri) { 6937 when(mockDownloader.startDownloading( 6938 any(String.class), 6939 any(GroupKey.class), 6940 anyInt(), 6941 anyLong(), 6942 any(String.class), 6943 eq(fileUri), 6944 any(String.class), 6945 anyInt(), 6946 any(DownloadConditions.class), 6947 isA(DownloaderCallbackImpl.class), 6948 anyInt(), 6949 anyList())) 6950 .then( 6951 new Answer<ListenableFuture<Void>>() { 6952 @Override 6953 public ListenableFuture<Void> answer(InvocationOnMock invocation) throws Throwable { 6954 SharedFile sharedFile = 6955 sharedFileManager.getSharedFile(key).get().toBuilder() 6956 .setFileStatus(FileStatus.DOWNLOAD_COMPLETE) 6957 .build(); 6958 sharedFilesMetadata.write(key, sharedFile).get(); 6959 return Futures.immediateVoidFuture(); 6960 } 6961 }); 6962 } 6963 6964 /** 6965 * The file download fails so the new file status is DOWNLOAD_FAILED. If failureCode is not null, 6966 * the downloader returns a immediateFailedFuture; otherwise it returns an immediateVoidFuture. 6967 */ fileDownloadFails(NewFileKey key, Uri fileUri, DownloadResultCode failureCode)6968 private void fileDownloadFails(NewFileKey key, Uri fileUri, DownloadResultCode failureCode) { 6969 when(mockDownloader.startDownloading( 6970 any(String.class), 6971 any(GroupKey.class), 6972 anyInt(), 6973 anyLong(), 6974 any(String.class), 6975 eq(fileUri), 6976 any(String.class), 6977 anyInt(), 6978 any(DownloadConditions.class), 6979 isA(DownloaderCallbackImpl.class), 6980 anyInt(), 6981 anyList())) 6982 .then( 6983 new Answer<ListenableFuture<Void>>() { 6984 @Override 6985 public ListenableFuture<Void> answer(InvocationOnMock invocation) throws Throwable { 6986 SharedFile sharedFile = 6987 sharedFileManager.getSharedFile(key).get().toBuilder() 6988 .setFileStatus(FileStatus.DOWNLOAD_FAILED) 6989 .build(); 6990 sharedFilesMetadata.write(key, sharedFile).get(); 6991 if (failureCode == null) { 6992 return Futures.immediateVoidFuture(); 6993 } 6994 return Futures.immediateFailedFuture( 6995 DownloadException.builder().setDownloadResultCode(failureCode).build()); 6996 } 6997 }); 6998 } 6999 readPendingFileGroup(GroupKey key)7000 private DataFileGroupInternal readPendingFileGroup(GroupKey key) throws Exception { 7001 GroupKey duplicateGroupKey = key.toBuilder().setDownloaded(false).build(); 7002 return fileGroupsMetadata.read(duplicateGroupKey).get(); 7003 } 7004 readDownloadedFileGroup(GroupKey key)7005 private DataFileGroupInternal readDownloadedFileGroup(GroupKey key) throws Exception { 7006 GroupKey duplicateGroupKey = key.toBuilder().setDownloaded(true).build(); 7007 return fileGroupsMetadata.read(duplicateGroupKey).get(); 7008 } 7009 writePendingFileGroup(GroupKey key, DataFileGroupInternal group)7010 private void writePendingFileGroup(GroupKey key, DataFileGroupInternal group) throws Exception { 7011 GroupKey duplicateGroupKey = key.toBuilder().setDownloaded(false).build(); 7012 fileGroupsMetadata.write(duplicateGroupKey, group).get(); 7013 } 7014 writeDownloadedFileGroup(GroupKey key, DataFileGroupInternal group)7015 private void writeDownloadedFileGroup(GroupKey key, DataFileGroupInternal group) 7016 throws Exception { 7017 GroupKey duplicateGroupKey = key.toBuilder().setDownloaded(true).build(); 7018 fileGroupsMetadata.write(duplicateGroupKey, group).get(); 7019 } 7020 verifyAddGroupForDownloadWritesMetadata( GroupKey key, DataFileGroupInternal group, long expectedTimestamp)7021 private void verifyAddGroupForDownloadWritesMetadata( 7022 GroupKey key, DataFileGroupInternal group, long expectedTimestamp) throws Exception { 7023 GroupKey duplicateGroupKey = key.toBuilder().setDownloaded(false).build(); 7024 7025 DataFileGroupInternal updatedFileGroup = 7026 setReceivedTimeStampWithFeatureOn(group, expectedTimestamp); 7027 assertThat(fileGroupsMetadata.read(duplicateGroupKey).get()).isEqualTo(updatedFileGroup); 7028 } 7029 getPendingKey(GroupKey key)7030 private static GroupKey getPendingKey(GroupKey key) { 7031 return key.toBuilder().setDownloaded(false).build(); 7032 } 7033 getDownloadedKey(GroupKey key)7034 private static GroupKey getDownloadedKey(GroupKey key) { 7035 return key.toBuilder().setDownloaded(true).build(); 7036 } 7037 setReceivedTimeStampWithFeatureOn( DataFileGroupInternal dataFileGroup, long elapsedTime)7038 private static DataFileGroupInternal setReceivedTimeStampWithFeatureOn( 7039 DataFileGroupInternal dataFileGroup, long elapsedTime) { 7040 DataFileGroupBookkeeping bookkeeping = 7041 dataFileGroup.getBookkeeping().toBuilder() 7042 .setGroupNewFilesReceivedTimestamp(elapsedTime) 7043 .build(); 7044 return dataFileGroup.toBuilder().setBookkeeping(bookkeeping).build(); 7045 } 7046 7047 /** 7048 * Simulates the download of the file {@code dataFile} by writing a file with name {@code 7049 * fileName}. 7050 */ simulateDownload(DataFile dataFile, String fileName)7051 private File simulateDownload(DataFile dataFile, String fileName) throws IOException { 7052 File onDeviceFile = new File(publicDirectory, fileName); 7053 byte[] bytes = new byte[dataFile.getByteSize()]; 7054 try (FileOutputStream writer = new FileOutputStream(onDeviceFile)) { 7055 writer.write(bytes); 7056 } 7057 return onDeviceFile; 7058 } 7059 getOnDeviceUrisForFileGroup(DataFileGroupInternal fileGroup)7060 private List<Uri> getOnDeviceUrisForFileGroup(DataFileGroupInternal fileGroup) { 7061 ArrayList<Uri> uriList = new ArrayList<>(fileGroup.getFileCount()); 7062 NewFileKey[] newFileKeys = MddTestUtil.createFileKeysForDataFileGroupInternal(fileGroup); 7063 7064 for (int i = 0; i < newFileKeys.length; i++) { 7065 NewFileKey newFileKey = newFileKeys[i]; 7066 DataFile dataFile = fileGroup.getFile(i); 7067 uriList.add( 7068 DirectoryUtil.getOnDeviceUri( 7069 context, 7070 newFileKey.getAllowedReaders(), 7071 dataFile.getFileId(), 7072 newFileKey.getChecksum(), 7073 mockSilentFeedback, 7074 /* instanceId= */ Optional.absent(), 7075 /* androidShared= */ false)); 7076 } 7077 return uriList; 7078 } 7079 noCustomValidation()7080 private AsyncFunction<DataFileGroupInternal, Boolean> noCustomValidation() { 7081 return unused -> Futures.immediateFuture(true); 7082 } 7083 } 7084