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.logging; 17 18 import static com.google.common.truth.Truth.assertThat; 19 import static com.google.common.util.concurrent.Futures.immediateVoidFuture; 20 import static org.mockito.ArgumentMatchers.any; 21 import static org.mockito.Mockito.times; 22 import static org.mockito.Mockito.verify; 23 import static org.mockito.Mockito.when; 24 25 import android.content.Context; 26 import android.net.Uri; 27 import androidx.test.core.app.ApplicationProvider; 28 import com.google.mobiledatadownload.internal.MetadataProto.DataFile; 29 import com.google.mobiledatadownload.internal.MetadataProto.DataFileGroupInternal; 30 import com.google.mobiledatadownload.internal.MetadataProto.GroupKey; 31 import com.google.mobiledatadownload.internal.MetadataProto.NewFileKey; 32 import com.google.android.libraries.mobiledatadownload.SilentFeedback; 33 import com.google.android.libraries.mobiledatadownload.file.SynchronousFileStorage; 34 import com.google.android.libraries.mobiledatadownload.file.spi.Backend; 35 import com.google.android.libraries.mobiledatadownload.internal.FileGroupsMetadata; 36 import com.google.android.libraries.mobiledatadownload.internal.MddConstants; 37 import com.google.android.libraries.mobiledatadownload.internal.MddTestUtil; 38 import com.google.android.libraries.mobiledatadownload.internal.SharedFileManager; 39 import com.google.android.libraries.mobiledatadownload.internal.SharedFilesMetadata; 40 import com.google.android.libraries.mobiledatadownload.internal.collect.GroupKeyAndGroup; 41 import com.google.android.libraries.mobiledatadownload.internal.logging.StorageLogger.GroupStorage; 42 import com.google.android.libraries.mobiledatadownload.internal.util.DirectoryUtil; 43 import com.google.android.libraries.mobiledatadownload.testing.TestFlags; 44 import com.google.auto.value.AutoValue; 45 import com.google.common.base.Optional; 46 import com.google.common.util.concurrent.AsyncCallable; 47 import com.google.common.util.concurrent.Futures; 48 import com.google.common.util.concurrent.MoreExecutors; 49 import com.google.mobiledatadownload.LogProto.DataDownloadFileGroupStats; 50 import com.google.mobiledatadownload.LogProto.MddStorageStats; 51 import java.io.IOException; 52 import java.util.ArrayList; 53 import java.util.Arrays; 54 import java.util.List; 55 import org.junit.Before; 56 import org.junit.Rule; 57 import org.junit.Test; 58 import org.junit.runner.RunWith; 59 import org.mockito.ArgumentCaptor; 60 import org.mockito.Captor; 61 import org.mockito.Mock; 62 import org.mockito.junit.MockitoJUnit; 63 import org.mockito.junit.MockitoRule; 64 import org.robolectric.RobolectricTestRunner; 65 66 @RunWith(RobolectricTestRunner.class) 67 public class StorageLoggerTest { 68 private static final String GROUP_1 = "group1"; 69 private static final String GROUP_2 = "group2"; 70 private static final String PACKAGE_1 = "package1"; 71 private static final String PACKAGE_2 = "package2"; 72 private static final int FILE_GROUP_VERSION_NUMBER_1 = 10; 73 private static final int FILE_GROUP_VERSION_NUMBER_2 = 20; 74 75 private static final long BUILD_ID_1 = 10; 76 private static final long BUILD_ID_2 = 20; 77 private static final String VARIANT_ID = "test-variant"; 78 79 // Note: We can't make those android uris static variable since the Uri.parse will fail 80 // with initialization. 81 private final Uri androidUri1 = 82 Uri.parse("android://com.google.android.gms/files/datadownload/shared/public/file_1"); 83 private static final long FILE_SIZE_1 = 1; 84 85 private final Uri androidUri2 = 86 Uri.parse("android://com.google.android.gms/files/datadownload/shared/public/file_2"); 87 private static final long FILE_SIZE_2 = 2; 88 89 private final Uri androidUri3 = 90 Uri.parse("android://com.google.android.gms/files/datadownload/shared/public/file_3"); 91 private static final long FILE_SIZE_3 = 4; 92 93 private final Uri androidUri4 = 94 Uri.parse("android://com.google.android.gms/files/datadownload/shared/public/file_4"); 95 private static final long FILE_SIZE_4 = 8; 96 97 private final Uri androidUri5 = 98 Uri.parse("android://com.google.android.gms/files/datadownload/shared/public/file_5"); 99 private static final long FILE_SIZE_5 = 16; 100 101 private final Uri androidUri6 = 102 Uri.parse("android://com.google.android.gms/files/datadownload/shared/public/file_6"); 103 private static final long FILE_SIZE_6 = 32; 104 105 private final Uri inlineUri1 = 106 Uri.parse("android://com.google.android.gms/files/datadownload/shared/public/inline_file_1"); 107 private static final long INLINE_FILE_SIZE_1 = 64; 108 109 private static final long MDD_DIRECTORY_SIZE = 110 FILE_SIZE_1 111 + FILE_SIZE_2 112 + FILE_SIZE_3 113 + FILE_SIZE_4 114 + FILE_SIZE_5 115 + FILE_SIZE_6 116 + INLINE_FILE_SIZE_1; 117 118 // These files will belong to 2 groups 119 private static final DataFile DATA_FILE_1 = MddTestUtil.createDataFile("file1", 1); 120 private static final DataFile DATA_FILE_2 = MddTestUtil.createDataFile("file2", 2); 121 private static final DataFile DATA_FILE_3 = MddTestUtil.createDataFile("file3", 3); 122 private static final DataFile DATA_FILE_4 = MddTestUtil.createDataFile("file4", 4); 123 private static final DataFile DATA_FILE_5 = MddTestUtil.createDataFile("file5", 5); 124 private static final DataFile DATA_FILE_6 = MddTestUtil.createDataFile("file6", 6); 125 private static final DataFile INLINE_DATA_FILE_1 = 126 DataFile.newBuilder() 127 .setFileId("inlineFile1") 128 .setUrlToDownload("inlinefile:sha1:inlinefile1") 129 .setChecksum("inlinefile1") 130 .setByteSize((int) INLINE_FILE_SIZE_1) 131 .build(); 132 133 private SynchronousFileStorage fileStorage; 134 135 private final Context context = ApplicationProvider.getApplicationContext(); 136 137 @Rule public final MockitoRule mocks = MockitoJUnit.rule(); 138 139 @Mock EventLogger mockEventLogger; 140 @Mock FileGroupsMetadata mockFileGroupsMetadata; 141 @Mock SharedFileManager mockSharedFileManager; 142 @Mock Backend mockBackend; 143 @Mock SilentFeedback mockSilentFeedback; 144 145 @Captor ArgumentCaptor<AsyncCallable<MddStorageStats>> mddStorageStatsCallableArgumentCaptor; 146 147 private final TestFlags flags = new TestFlags(); 148 149 @Before setUp()150 public void setUp() throws Exception { 151 152 setUpFileMock(androidUri1, FILE_SIZE_1); 153 setUpFileMock(androidUri2, FILE_SIZE_2); 154 setUpFileMock(androidUri3, FILE_SIZE_3); 155 setUpFileMock(androidUri4, FILE_SIZE_4); 156 setUpFileMock(androidUri5, FILE_SIZE_5); 157 setUpFileMock(androidUri6, FILE_SIZE_6); 158 setUpFileMock(inlineUri1, INLINE_FILE_SIZE_1); 159 160 Uri downloadDirUri = DirectoryUtil.getBaseDownloadDirectory(context, Optional.absent()); 161 setUpDirectoryMock( 162 downloadDirUri, 163 Arrays.asList( 164 androidUri1, 165 androidUri2, 166 androidUri3, 167 androidUri4, 168 androidUri5, 169 androidUri6, 170 inlineUri1)); 171 172 when(mockBackend.name()).thenReturn("android"); 173 fileStorage = new SynchronousFileStorage(Arrays.asList(mockBackend)); 174 175 flags.storageStatsLoggingSampleInterval = Optional.of(1); 176 } 177 178 // TODO(b/115659980): consider moving this to a public utility class in the File Library setUpFileMock(Uri uri, long size)179 private void setUpFileMock(Uri uri, long size) throws IOException { 180 when(mockBackend.exists(uri)).thenReturn(true); 181 when(mockBackend.isDirectory(uri)).thenReturn(false); 182 when(mockBackend.fileSize(uri)).thenReturn(size); 183 } 184 185 // TODO(b/115659980): consider moving this to a public utility class in the File Library setUpDirectoryMock(Uri uri, List<Uri> children)186 private void setUpDirectoryMock(Uri uri, List<Uri> children) throws IOException { 187 when(mockBackend.exists(uri)).thenReturn(true); 188 when(mockBackend.isDirectory(uri)).thenReturn(true); 189 when(mockBackend.children(uri)).thenReturn(children); 190 } 191 192 @Test testLogMddStorageStats()193 public void testLogMddStorageStats() throws Exception { 194 // Setup Group1 that has 3 FileDataGroups: 195 // - Stale group has DATA_FILE_1, DATA_FILE_2. 196 // - Downloaded group has DATA_FILE_2, DATA_FILE_3. 197 // - Pending group has DATA_FILE_3, DATA_FILE_4. 198 DataFileGroupInternal group1Stale = 199 createDataFileGroupWithFiles( 200 GROUP_1, 201 PACKAGE_1, 202 Arrays.asList(DATA_FILE_1, DATA_FILE_2), 203 Arrays.asList(androidUri1, androidUri2)); 204 DataFileGroupInternal group1Downloaded = 205 createDataFileGroupWithFiles( 206 GROUP_1, 207 PACKAGE_1, 208 Arrays.asList(DATA_FILE_2, DATA_FILE_3), 209 Arrays.asList(androidUri2, androidUri3)) 210 .toBuilder() 211 .setFileGroupVersionNumber(FILE_GROUP_VERSION_NUMBER_1) 212 .setBuildId(BUILD_ID_1) 213 .setVariantId(VARIANT_ID) 214 .build(); 215 DataFileGroupInternal group1Pending = 216 createDataFileGroupWithFiles( 217 GROUP_1, 218 PACKAGE_1, 219 Arrays.asList(DATA_FILE_3, DATA_FILE_4), 220 Arrays.asList(androidUri3, androidUri4)); 221 222 // Setup Group2 that has 2 FileDataGroups: 223 // - Downloaded group has DATA_FILE_5. 224 // - Pending group has DATA_FILE_6. 225 DataFileGroupInternal group2Downloaded = 226 createDataFileGroupWithFiles( 227 GROUP_2, PACKAGE_2, Arrays.asList(DATA_FILE_5), Arrays.asList(androidUri5)) 228 .toBuilder() 229 .setFileGroupVersionNumber(FILE_GROUP_VERSION_NUMBER_2) 230 .setBuildId(BUILD_ID_2) 231 .setVariantId(VARIANT_ID) 232 .build(); 233 DataFileGroupInternal group2Pending = 234 createDataFileGroupWithFiles( 235 GROUP_2, PACKAGE_2, Arrays.asList(DATA_FILE_6), Arrays.asList(androidUri6)); 236 237 List<GroupKeyAndGroup> groups = new ArrayList<>(); 238 groups.add(createGroupKeyAndGroup(group1Downloaded, true /*downloaded*/)); 239 groups.add(createGroupKeyAndGroup(group1Pending, false /*downloaded*/)); 240 groups.add(createGroupKeyAndGroup(group2Downloaded, true /*downloaded*/)); 241 groups.add(createGroupKeyAndGroup(group2Pending, false /*downloaded*/)); 242 when(mockFileGroupsMetadata.getAllFreshGroups()).thenReturn(Futures.immediateFuture(groups)); 243 244 when(mockFileGroupsMetadata.getAllStaleGroups()) 245 .thenReturn(Futures.immediateFuture(Arrays.asList(group1Stale))); 246 247 verifyStorageStats( 248 /* totalMddBytesUsed= */ FILE_SIZE_1 249 + FILE_SIZE_2 250 + FILE_SIZE_3 251 + FILE_SIZE_4 252 + FILE_SIZE_5 253 + FILE_SIZE_6, 254 ExpectedFileGroupStorageStats.create( 255 GROUP_1, 256 PACKAGE_1, 257 BUILD_ID_1, 258 VARIANT_ID, 259 FILE_GROUP_VERSION_NUMBER_1, 260 createGroupStorage( 261 /* totalBytesUsed= */ FILE_SIZE_1 + FILE_SIZE_2 + FILE_SIZE_3 + FILE_SIZE_4, 262 /* totalInlineBytesUsed= */ 0, 263 /* downloadedGroupBytesUsed= */ FILE_SIZE_2 + FILE_SIZE_3, 264 /* downloadedGroupInlineBytesUsed= */ 0, 265 /* totalFileCount= */ 2, 266 /* totalInlineFileCount= */ 0)), 267 ExpectedFileGroupStorageStats.create( 268 GROUP_2, 269 PACKAGE_2, 270 BUILD_ID_2, 271 VARIANT_ID, 272 FILE_GROUP_VERSION_NUMBER_2, 273 createGroupStorage( 274 /* totalBytesUsed= */ FILE_SIZE_5 + FILE_SIZE_6, 275 /* totalInlineBytesUsed= */ 0, 276 /* downloadedGroupBytesUsed= */ FILE_SIZE_5, 277 /* downloadedGroupInlineBytesUsed= */ 0, 278 /* totalFileCount= */ 1, 279 /* totalInlineFileCount= */ 0))); 280 } 281 282 @Test testLogMddStorageStats_noDownloadedInGroup2()283 public void testLogMddStorageStats_noDownloadedInGroup2() throws Exception { 284 // Setup Group1 that has 3 FileDataGroups: 285 // - Stale group has DATA_FILE_1, DATA_FILE_2. 286 // - Downloaded group has DATA_FILE_2, DATA_FILE_3. 287 // - Pending group has DATA_FILE_3, DATA_FILE_4. 288 DataFileGroupInternal group1Stale = 289 createDataFileGroupWithFiles( 290 GROUP_1, 291 PACKAGE_1, 292 Arrays.asList(DATA_FILE_1, DATA_FILE_2), 293 Arrays.asList(androidUri1, androidUri2)); 294 DataFileGroupInternal group1Downloaded = 295 createDataFileGroupWithFiles( 296 GROUP_1, 297 PACKAGE_1, 298 Arrays.asList(DATA_FILE_2, DATA_FILE_3), 299 Arrays.asList(androidUri2, androidUri3)) 300 .toBuilder() 301 .setFileGroupVersionNumber(FILE_GROUP_VERSION_NUMBER_1) 302 .setBuildId(BUILD_ID_1) 303 .setVariantId(VARIANT_ID) 304 .build(); 305 DataFileGroupInternal group1Pending = 306 createDataFileGroupWithFiles( 307 GROUP_1, 308 PACKAGE_1, 309 Arrays.asList(DATA_FILE_3, DATA_FILE_4), 310 Arrays.asList(androidUri3, androidUri4)); 311 312 // Setup Group2 that has 2 FileDataGroups (no downloaded) 313 // - Stale group has DATA_FILE_5. 314 // - Pending group has DATA_FILE_6. 315 DataFileGroupInternal group2Stale = 316 createDataFileGroupWithFiles( 317 GROUP_2, PACKAGE_2, Arrays.asList(DATA_FILE_5), Arrays.asList(androidUri5)); 318 DataFileGroupInternal group2Pending = 319 createDataFileGroupWithFiles( 320 GROUP_2, PACKAGE_2, Arrays.asList(DATA_FILE_6), Arrays.asList(androidUri6)); 321 322 List<GroupKeyAndGroup> groups = new ArrayList<>(); 323 groups.add(createGroupKeyAndGroup(group1Downloaded, true /*downloaded*/)); 324 groups.add(createGroupKeyAndGroup(group1Pending, false /*downloaded*/)); 325 groups.add(createGroupKeyAndGroup(group2Pending, false /*downloaded*/)); 326 when(mockFileGroupsMetadata.getAllFreshGroups()).thenReturn(Futures.immediateFuture(groups)); 327 328 when(mockFileGroupsMetadata.getAllStaleGroups()) 329 .thenReturn(Futures.immediateFuture(Arrays.asList(group1Stale, group2Stale))); 330 331 verifyStorageStats( 332 /* totalMddBytesUsed= */ FILE_SIZE_1 333 + FILE_SIZE_2 334 + FILE_SIZE_3 335 + FILE_SIZE_4 336 + FILE_SIZE_5 337 + FILE_SIZE_6, 338 ExpectedFileGroupStorageStats.create( 339 GROUP_1, 340 PACKAGE_1, 341 BUILD_ID_1, 342 VARIANT_ID, 343 FILE_GROUP_VERSION_NUMBER_1, 344 createGroupStorage( 345 /* totalBytesUsed= */ FILE_SIZE_1 + FILE_SIZE_2 + FILE_SIZE_3 + FILE_SIZE_4, 346 /* totalInlineBytesUsed= */ 0, 347 /* downloadedGroupBytesUsed= */ FILE_SIZE_2 + FILE_SIZE_3, 348 /* downloadedGroupInlineBytesUsed= */ 0, 349 /* totalFileCount= */ 2, 350 /* totalInlineFileCount= */ 0)), 351 ExpectedFileGroupStorageStats.create( 352 GROUP_2, 353 PACKAGE_2, 354 /* buildId= */ 0, 355 /* variantId= */ "", 356 /* fileGroupVersionNumber= */ -1, 357 createGroupStorage( 358 /* totalBytesUsed= */ FILE_SIZE_5 + FILE_SIZE_6, 359 /* totalInlineBytesUsed= */ 0, 360 /* downloadedGroupBytesUsed= */ 0, 361 /* downloadedGroupInlineBytesUsed= */ 0, 362 /* totalFileCount= */ 1, 363 /* totalInlineFileCount= */ 0))); 364 } 365 366 @Test testLogMddStorageStats_commonFilesBetweenGroups()367 public void testLogMddStorageStats_commonFilesBetweenGroups() throws Exception { 368 // Setup Group1 that has 3 FileDataGroups: 369 // - Stale group has DATA_FILE_1, DATA_FILE_2. 370 // - Downloaded group has DATA_FILE_2, DATA_FILE_3. 371 // - Pending group has DATA_FILE_3, DATA_FILE_4. 372 DataFileGroupInternal group1Stale = 373 createDataFileGroupWithFiles( 374 GROUP_1, 375 PACKAGE_1, 376 Arrays.asList(DATA_FILE_1, DATA_FILE_2), 377 Arrays.asList(androidUri1, androidUri2)); 378 DataFileGroupInternal group1Downloaded = 379 createDataFileGroupWithFiles( 380 GROUP_1, 381 PACKAGE_1, 382 Arrays.asList(DATA_FILE_2, DATA_FILE_3), 383 Arrays.asList(androidUri2, androidUri3)) 384 .toBuilder() 385 .setFileGroupVersionNumber(FILE_GROUP_VERSION_NUMBER_1) 386 .setBuildId(BUILD_ID_1) 387 .setVariantId(VARIANT_ID) 388 .build(); 389 DataFileGroupInternal group1Pending = 390 createDataFileGroupWithFiles( 391 GROUP_1, 392 PACKAGE_1, 393 Arrays.asList(DATA_FILE_3, DATA_FILE_4), 394 Arrays.asList(androidUri3, androidUri4)); 395 396 // Setup Group2 that has 3 FileDataGroups: 397 // - Stale group has DATA_FILE_1, DATA_FILE_3. 398 // - Downloaded group has DATA_FILE_4, DATA_FILE_5. 399 // - Pending group has DATA_FILE_6. 400 DataFileGroupInternal group2Stale = 401 createDataFileGroupWithFiles( 402 GROUP_2, 403 PACKAGE_2, 404 Arrays.asList(DATA_FILE_1, DATA_FILE_3), 405 Arrays.asList(androidUri1, androidUri3)); 406 DataFileGroupInternal group2Downloaded = 407 createDataFileGroupWithFiles( 408 GROUP_2, 409 PACKAGE_2, 410 Arrays.asList(DATA_FILE_4, DATA_FILE_5), 411 Arrays.asList(androidUri4, androidUri5)) 412 .toBuilder() 413 .setFileGroupVersionNumber(FILE_GROUP_VERSION_NUMBER_2) 414 .setBuildId(BUILD_ID_2) 415 .setVariantId(VARIANT_ID) 416 .build(); 417 DataFileGroupInternal group2Pending = 418 createDataFileGroupWithFiles( 419 GROUP_2, PACKAGE_2, Arrays.asList(DATA_FILE_6), Arrays.asList(androidUri6)); 420 421 List<GroupKeyAndGroup> groups = new ArrayList<>(); 422 groups.add(createGroupKeyAndGroup(group1Downloaded, true /*downloaded*/)); 423 groups.add(createGroupKeyAndGroup(group1Pending, false /*downloaded*/)); 424 groups.add(createGroupKeyAndGroup(group2Downloaded, true /*downloaded*/)); 425 groups.add(createGroupKeyAndGroup(group2Pending, false /*downloaded*/)); 426 when(mockFileGroupsMetadata.getAllFreshGroups()).thenReturn(Futures.immediateFuture(groups)); 427 428 when(mockFileGroupsMetadata.getAllStaleGroups()) 429 .thenReturn(Futures.immediateFuture(Arrays.asList(group1Stale, group2Stale))); 430 431 verifyStorageStats( 432 /* totalMddBytesUsed= */ FILE_SIZE_1 433 + FILE_SIZE_2 434 + FILE_SIZE_3 435 + FILE_SIZE_4 436 + FILE_SIZE_5 437 + FILE_SIZE_6, 438 ExpectedFileGroupStorageStats.create( 439 GROUP_1, 440 PACKAGE_1, 441 BUILD_ID_1, 442 VARIANT_ID, 443 FILE_GROUP_VERSION_NUMBER_1, 444 createGroupStorage( 445 /* totalBytesUsed= */ FILE_SIZE_1 + FILE_SIZE_2 + FILE_SIZE_3 + FILE_SIZE_4, 446 /* totalInlineBytesUsed= */ 0, 447 /* downloadedGroupBytesUsed= */ FILE_SIZE_2 + FILE_SIZE_3, 448 /* downloadedGroupInlineBytesUsed= */ 0, 449 /* totalFileCount= */ 2, 450 /* totalInlineFileCount= */ 0)), 451 ExpectedFileGroupStorageStats.create( 452 GROUP_2, 453 PACKAGE_2, 454 BUILD_ID_2, 455 VARIANT_ID, 456 FILE_GROUP_VERSION_NUMBER_2, 457 createGroupStorage( 458 /* totalBytesUsed= */ FILE_SIZE_1 459 + FILE_SIZE_3 460 + FILE_SIZE_4 461 + FILE_SIZE_5 462 + FILE_SIZE_6, 463 /* totalInlineBytesUsed= */ 0, 464 /* downloadedGroupBytesUsed= */ FILE_SIZE_4 + FILE_SIZE_5, 465 /* downloadedGroupInlineBytesUsed= */ 0, 466 /* totalFileCount= */ 2, 467 /* totalInlineFileCount= */ 0))); 468 } 469 470 @Test testLogMddStorageStats_emptyDownloadedGroup()471 public void testLogMddStorageStats_emptyDownloadedGroup() throws Exception { 472 // Setup Group1 that has 3 FileDataGroups: 473 // - Stale group has DATA_FILE_1, DATA_FILE_2. 474 // - Downloaded group has DATA_FILE_2, DATA_FILE_3. 475 // - Pending group has DATA_FILE_3, DATA_FILE_4. 476 DataFileGroupInternal group1Stale = 477 createDataFileGroupWithFiles( 478 GROUP_1, 479 PACKAGE_1, 480 Arrays.asList(DATA_FILE_1, DATA_FILE_2), 481 Arrays.asList(androidUri1, androidUri2)); 482 DataFileGroupInternal group1Downloaded = 483 createDataFileGroupWithFiles( 484 GROUP_1, 485 PACKAGE_1, 486 Arrays.asList(DATA_FILE_2, DATA_FILE_3), 487 Arrays.asList(androidUri2, androidUri3)) 488 .toBuilder() 489 .setFileGroupVersionNumber(FILE_GROUP_VERSION_NUMBER_1) 490 .setBuildId(BUILD_ID_1) 491 .setVariantId(VARIANT_ID) 492 .build(); 493 DataFileGroupInternal group1Pending = 494 createDataFileGroupWithFiles( 495 GROUP_1, 496 PACKAGE_1, 497 Arrays.asList(DATA_FILE_3, DATA_FILE_4), 498 Arrays.asList(androidUri3, androidUri4)); 499 500 // Downloaded Group2 is empty (no file). This could happen when we send an empty FileGroup to 501 // clear old config. 502 DataFileGroupInternal group2Downloaded = 503 createDataFileGroupWithFiles( 504 GROUP_2, PACKAGE_2, new ArrayList<>() /*dataFiles*/, new ArrayList<>() /*fileUris*/) 505 .toBuilder() 506 .setFileGroupVersionNumber(FILE_GROUP_VERSION_NUMBER_2) 507 .setBuildId(BUILD_ID_2) 508 .setVariantId(VARIANT_ID) 509 .build(); 510 511 List<GroupKeyAndGroup> groups = new ArrayList<>(); 512 groups.add(createGroupKeyAndGroup(group1Downloaded, true /*downloaded*/)); 513 groups.add(createGroupKeyAndGroup(group1Pending, false /*downloaded*/)); 514 groups.add(createGroupKeyAndGroup(group2Downloaded, true /*downloaded*/)); 515 when(mockFileGroupsMetadata.getAllFreshGroups()).thenReturn(Futures.immediateFuture(groups)); 516 517 when(mockFileGroupsMetadata.getAllStaleGroups()) 518 .thenReturn(Futures.immediateFuture(Arrays.asList(group1Stale))); 519 520 verifyStorageStats( 521 /* totalMddBytesUsed= */ FILE_SIZE_1 + FILE_SIZE_2 + FILE_SIZE_3 + FILE_SIZE_4, 522 ExpectedFileGroupStorageStats.create( 523 GROUP_1, 524 PACKAGE_1, 525 BUILD_ID_1, 526 VARIANT_ID, 527 FILE_GROUP_VERSION_NUMBER_1, 528 createGroupStorage( 529 /* totalBytesUsed= */ FILE_SIZE_1 + FILE_SIZE_2 + FILE_SIZE_3 + FILE_SIZE_4, 530 /* totalInlineBytesUsed= */ 0, 531 /* downloadedGroupBytesUsed= */ FILE_SIZE_2 + FILE_SIZE_3, 532 /* downloadedGroupInlineBytesUsed= */ 0, 533 /* totalFileCount= */ 2, 534 /* totalInlineFileCount= */ 0)), 535 ExpectedFileGroupStorageStats.create( 536 GROUP_2, 537 PACKAGE_2, 538 BUILD_ID_2, 539 VARIANT_ID, 540 FILE_GROUP_VERSION_NUMBER_2, 541 createGroupStorage( 542 /* totalBytesUsed= */ 0, 543 /* totalInlineBytesUsed= */ 0, 544 /* downloadedGroupBytesUsed= */ 0, 545 /* downloadedGroupInlineBytesUsed= */ 0, 546 /* totalFileCount= */ 0, 547 /* totalInlineFileCount= */ 0))); 548 } 549 550 @Test testLogMddStorageStats_mddDirectoryNotExists()551 public void testLogMddStorageStats_mddDirectoryNotExists() throws Exception { 552 when(mockFileGroupsMetadata.getAllFreshGroups()) 553 .thenReturn(Futures.immediateFuture(new ArrayList<>())); 554 when(mockFileGroupsMetadata.getAllStaleGroups()) 555 .thenReturn(Futures.immediateFuture(new ArrayList<>())); 556 when(mockBackend.exists(DirectoryUtil.getBaseDownloadDirectory(context, Optional.absent()))) 557 .thenReturn(false); 558 559 StorageLogger storageLogger = 560 new StorageLogger( 561 context, 562 mockFileGroupsMetadata, 563 mockSharedFileManager, 564 fileStorage, 565 mockEventLogger, 566 mockSilentFeedback, 567 Optional.absent(), 568 MoreExecutors.directExecutor()); 569 570 when(mockEventLogger.logMddStorageStats(any())).thenReturn(immediateVoidFuture()); 571 572 storageLogger.logStorageStats(/* daysSinceLastLog= */ 1).get(); 573 574 verify(mockEventLogger, times(1)) 575 .logMddStorageStats(mddStorageStatsCallableArgumentCaptor.capture()); 576 AsyncCallable<MddStorageStats> mddStorageStatsCallable = 577 mddStorageStatsCallableArgumentCaptor.getValue(); 578 579 MddStorageStats mddStorageStats = mddStorageStatsCallable.call().get(); 580 assertThat(mddStorageStats.getTotalMddBytesUsed()).isEqualTo(0); 581 assertThat(mddStorageStats.getTotalMddDirectoryBytesUsed()).isEqualTo(0); 582 583 assertThat(mddStorageStats.getDataDownloadFileGroupStatsList()).isEmpty(); 584 assertThat(mddStorageStats.getTotalBytesUsedList()).isEmpty(); 585 assertThat(mddStorageStats.getDownloadedGroupBytesUsedList()).isEmpty(); 586 } 587 588 @Test testMddStorageStats_includesDaysSinceLastLog()589 public void testMddStorageStats_includesDaysSinceLastLog() throws Exception { 590 when(mockFileGroupsMetadata.getAllFreshGroups()) 591 .thenReturn(Futures.immediateFuture(new ArrayList<>())); 592 when(mockFileGroupsMetadata.getAllStaleGroups()) 593 .thenReturn(Futures.immediateFuture(new ArrayList<>())); 594 when(mockBackend.exists(DirectoryUtil.getBaseDownloadDirectory(context, Optional.absent()))) 595 .thenReturn(false); 596 597 StorageLogger storageLogger = 598 new StorageLogger( 599 context, 600 mockFileGroupsMetadata, 601 mockSharedFileManager, 602 fileStorage, 603 mockEventLogger, 604 mockSilentFeedback, 605 Optional.absent(), 606 MoreExecutors.directExecutor()); 607 608 when(mockEventLogger.logMddStorageStats(any())).thenReturn(immediateVoidFuture()); 609 610 storageLogger.logStorageStats(/* daysSinceLastLog= */ -1).get(); 611 612 verify(mockEventLogger, times(1)) 613 .logMddStorageStats(mddStorageStatsCallableArgumentCaptor.capture()); 614 615 AsyncCallable<MddStorageStats> mddStorageStatsCallable = 616 mddStorageStatsCallableArgumentCaptor.getValue(); 617 MddStorageStats mddStorageStats = mddStorageStatsCallable.call().get(); 618 619 assertThat(mddStorageStats.getDaysSinceLastLog()).isEqualTo(-1); 620 } 621 622 @Test testLogMddStorageStats_groupWithInlineFiles()623 public void testLogMddStorageStats_groupWithInlineFiles() throws Exception { 624 // Setup Group1 that has 3 FileDataGroups: 625 // - Stale group has DATA_FILE_1, DATA_FILE_2. 626 // - Downloaded group has DATA_FILE_2, INLINE_FILE_1, 627 // - Pending group has DATA_FILE_3, INLINE_FILE_1, 628 DataFileGroupInternal group1Stale = 629 createDataFileGroupWithFiles( 630 GROUP_1, 631 PACKAGE_1, 632 Arrays.asList(DATA_FILE_1, DATA_FILE_2), 633 Arrays.asList(androidUri1, androidUri2)) 634 .toBuilder() 635 .setFileGroupVersionNumber(FILE_GROUP_VERSION_NUMBER_1) 636 .setBuildId(BUILD_ID_1) 637 .setVariantId(VARIANT_ID) 638 .build(); 639 DataFileGroupInternal group1Downloaded = 640 createDataFileGroupWithFiles( 641 GROUP_1, 642 PACKAGE_1, 643 Arrays.asList(DATA_FILE_2, INLINE_DATA_FILE_1), 644 Arrays.asList(androidUri2, inlineUri1)) 645 .toBuilder() 646 .setFileGroupVersionNumber(FILE_GROUP_VERSION_NUMBER_1) 647 .setBuildId(BUILD_ID_1) 648 .setVariantId(VARIANT_ID) 649 .build(); 650 DataFileGroupInternal group1Pending = 651 createDataFileGroupWithFiles( 652 GROUP_1, 653 PACKAGE_1, 654 Arrays.asList(DATA_FILE_3, INLINE_DATA_FILE_1), 655 Arrays.asList(androidUri3, inlineUri1)) 656 .toBuilder() 657 .setFileGroupVersionNumber(FILE_GROUP_VERSION_NUMBER_1) 658 .setBuildId(BUILD_ID_1) 659 .setVariantId(VARIANT_ID) 660 .build(); 661 662 List<GroupKeyAndGroup> groups = new ArrayList<>(); 663 groups.add(createGroupKeyAndGroup(group1Downloaded, true /*downloaded*/)); 664 groups.add(createGroupKeyAndGroup(group1Pending, false /*downloaded*/)); 665 when(mockFileGroupsMetadata.getAllFreshGroups()).thenReturn(Futures.immediateFuture(groups)); 666 667 when(mockFileGroupsMetadata.getAllStaleGroups()) 668 .thenReturn(Futures.immediateFuture(Arrays.asList(group1Stale))); 669 670 verifyStorageStats( 671 /* totalMddBytesUsed= */ FILE_SIZE_1 + FILE_SIZE_2 + FILE_SIZE_3 + INLINE_FILE_SIZE_1, 672 ExpectedFileGroupStorageStats.create( 673 GROUP_1, 674 PACKAGE_1, 675 BUILD_ID_1, 676 VARIANT_ID, 677 FILE_GROUP_VERSION_NUMBER_1, 678 createGroupStorage( 679 /* totalBytesUsed= */ FILE_SIZE_1 + FILE_SIZE_2 + FILE_SIZE_3 + INLINE_FILE_SIZE_1, 680 /* totalInlineBytesUsed= */ INLINE_FILE_SIZE_1, 681 /* downloadedGroupBytesUsed= */ FILE_SIZE_2 + INLINE_FILE_SIZE_1, 682 /* downloadedGroupInlineBytesUsed= */ INLINE_FILE_SIZE_1, 683 /* totalFileCount= */ 2, 684 /* totalInlineFileCount= */ 1))); 685 } 686 verifyStorageStats( long totalMddBytesUsed, ExpectedFileGroupStorageStats... expectedStatsList)687 private void verifyStorageStats( 688 long totalMddBytesUsed, ExpectedFileGroupStorageStats... expectedStatsList) throws Exception { 689 StorageLogger storageLogger = 690 new StorageLogger( 691 context, 692 mockFileGroupsMetadata, 693 mockSharedFileManager, 694 fileStorage, 695 mockEventLogger, 696 mockSilentFeedback, 697 Optional.absent(), 698 MoreExecutors.directExecutor()); 699 when(mockEventLogger.logMddStorageStats(any())).thenReturn(immediateVoidFuture()); 700 storageLogger.logStorageStats(/* daysSinceLastLog= */ 1).get(); 701 702 verify(mockEventLogger, times(1)) 703 .logMddStorageStats(mddStorageStatsCallableArgumentCaptor.capture()); 704 705 AsyncCallable<MddStorageStats> mddStorageStatsCallable = 706 mddStorageStatsCallableArgumentCaptor.getValue(); 707 MddStorageStats mddStorageStats = mddStorageStatsCallable.call().get(); 708 709 assertThat(mddStorageStats.getTotalMddBytesUsed()).isEqualTo(totalMddBytesUsed); 710 assertThat(mddStorageStats.getTotalMddDirectoryBytesUsed()).isEqualTo(MDD_DIRECTORY_SIZE); 711 712 assertThat(mddStorageStats.getDataDownloadFileGroupStatsCount()) 713 .isEqualTo(expectedStatsList.length); 714 assertThat(mddStorageStats.getTotalBytesUsedCount()).isEqualTo(expectedStatsList.length); 715 assertThat(mddStorageStats.getDownloadedGroupBytesUsedCount()) 716 .isEqualTo(expectedStatsList.length); 717 718 for (int i = 0; i < expectedStatsList.length; i++) { 719 DataDownloadFileGroupStats fileGroupStats = 720 mddStorageStats.getDataDownloadFileGroupStatsList().get(i); 721 long totalBytesUsed = mddStorageStats.getTotalBytesUsed(i); 722 long totalInlineBytesUsed = mddStorageStats.getTotalInlineBytesUsed(i); 723 long downloadedGroupBytesUsed = mddStorageStats.getDownloadedGroupBytesUsed(i); 724 long downloadedGroupInlineBytesUsed = mddStorageStats.getDownloadedGroupInlineBytesUsed(i); 725 726 ExpectedFileGroupStorageStats expectedStats = 727 getExpectedStatsForName(fileGroupStats.getFileGroupName(), expectedStatsList); 728 GroupStorage expectedGroupStorage = expectedStats.groupStorage(); 729 730 assertThat(fileGroupStats.getOwnerPackage()).isEqualTo(expectedStats.packageName()); 731 assertThat(fileGroupStats.getFileGroupVersionNumber()) 732 .isEqualTo(expectedStats.fileGroupVersionNumber()); 733 assertThat(fileGroupStats.getVariantId()).isEqualTo(expectedStats.variantId()); 734 assertThat(fileGroupStats.getBuildId()).isEqualTo(expectedStats.buildId()); 735 assertThat(totalBytesUsed).isEqualTo(expectedGroupStorage.totalBytesUsed); 736 assertThat(totalInlineBytesUsed).isEqualTo(expectedGroupStorage.totalInlineBytesUsed); 737 assertThat(downloadedGroupBytesUsed).isEqualTo(expectedGroupStorage.downloadedGroupBytesUsed); 738 assertThat(downloadedGroupInlineBytesUsed) 739 .isEqualTo(expectedGroupStorage.downloadedGroupInlineBytesUsed); 740 assertThat(fileGroupStats.getFileCount()).isEqualTo(expectedGroupStorage.totalFileCount); 741 assertThat(fileGroupStats.getInlineFileCount()) 742 .isEqualTo(expectedGroupStorage.totalInlineFileCount); 743 } 744 } 745 746 /** Find the expected stats for a given group name. */ getExpectedStatsForName( String groupName, ExpectedFileGroupStorageStats[] expectedStatsList)747 private ExpectedFileGroupStorageStats getExpectedStatsForName( 748 String groupName, ExpectedFileGroupStorageStats[] expectedStatsList) { 749 for (int i = 0; i < expectedStatsList.length; i++) { 750 if (groupName.equals(expectedStatsList[i].groupName())) { 751 return expectedStatsList[i]; 752 } 753 } 754 755 throw new AssertionError(String.format("Couldn't find group for name: %s", groupName)); 756 } 757 758 /** Creates a data file group with the given list of files. */ createDataFileGroupWithFiles( String fileGroupName, String ownerPackage, List<DataFile> dataFiles, List<Uri> fileUris)759 private DataFileGroupInternal createDataFileGroupWithFiles( 760 String fileGroupName, String ownerPackage, List<DataFile> dataFiles, List<Uri> fileUris) { 761 DataFileGroupInternal.Builder dataFileGroup = 762 DataFileGroupInternal.newBuilder() 763 .setGroupName(fileGroupName) 764 .setOwnerPackage(ownerPackage); 765 766 for (int i = 0; i < dataFiles.size(); ++i) { 767 DataFile file = dataFiles.get(i); 768 NewFileKey newFileKey = 769 SharedFilesMetadata.createKeyFromDataFile(file, dataFileGroup.getAllowedReadersEnum()); 770 dataFileGroup.addFile(file); 771 when(mockSharedFileManager.getOnDeviceUri(newFileKey)) 772 .thenReturn(Futures.immediateFuture(fileUris.get(i))); 773 } 774 return dataFileGroup.build(); 775 } 776 createGroupKeyAndGroup( DataFileGroupInternal fileGroup, boolean downloaded)777 private static GroupKeyAndGroup createGroupKeyAndGroup( 778 DataFileGroupInternal fileGroup, boolean downloaded) { 779 GroupKey groupKey = createGroupKey(fileGroup, downloaded); 780 return GroupKeyAndGroup.create(groupKey, fileGroup); 781 } 782 createGroupKey(DataFileGroupInternal fileGroup, boolean downloaded)783 private static GroupKey createGroupKey(DataFileGroupInternal fileGroup, boolean downloaded) { 784 GroupKey.Builder groupKey = GroupKey.newBuilder().setGroupName(fileGroup.getGroupName()); 785 786 if (fileGroup.getOwnerPackage().isEmpty()) { 787 groupKey.setOwnerPackage(MddConstants.GMS_PACKAGE); 788 } else { 789 groupKey.setOwnerPackage(fileGroup.getOwnerPackage()); 790 } 791 groupKey.setDownloaded(downloaded); 792 793 return groupKey.build(); 794 } 795 createGroupStorage( long totalBytesUsed, long totalInlineBytesUsed, long downloadedGroupBytesUsed, long downloadedGroupInlineBytesUsed, int totalFileCount, int totalInlineFileCount)796 private static GroupStorage createGroupStorage( 797 long totalBytesUsed, 798 long totalInlineBytesUsed, 799 long downloadedGroupBytesUsed, 800 long downloadedGroupInlineBytesUsed, 801 int totalFileCount, 802 int totalInlineFileCount) { 803 GroupStorage groupStorage = new GroupStorage(); 804 groupStorage.totalBytesUsed = totalBytesUsed; 805 groupStorage.totalInlineBytesUsed = totalInlineBytesUsed; 806 groupStorage.downloadedGroupBytesUsed = downloadedGroupBytesUsed; 807 groupStorage.downloadedGroupInlineBytesUsed = downloadedGroupInlineBytesUsed; 808 groupStorage.totalFileCount = totalFileCount; 809 groupStorage.totalInlineFileCount = totalInlineFileCount; 810 return groupStorage; 811 } 812 813 @AutoValue 814 abstract static class ExpectedFileGroupStorageStats { groupName()815 abstract String groupName(); 816 packageName()817 abstract String packageName(); 818 buildId()819 abstract long buildId(); 820 variantId()821 abstract String variantId(); 822 fileGroupVersionNumber()823 abstract int fileGroupVersionNumber(); 824 groupStorage()825 abstract GroupStorage groupStorage(); 826 create( String groupName, String packageName, long buildId, String variantId, int fileGroupVersionNumber, GroupStorage groupStorage)827 static ExpectedFileGroupStorageStats create( 828 String groupName, 829 String packageName, 830 long buildId, 831 String variantId, 832 int fileGroupVersionNumber, 833 GroupStorage groupStorage) { 834 return new AutoValue_StorageLoggerTest_ExpectedFileGroupStorageStats( 835 groupName, packageName, buildId, variantId, fileGroupVersionNumber, groupStorage); 836 } 837 } 838 } 839