• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2022 Google LLC
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.google.android.libraries.mobiledatadownload.internal;
17 
18 import static com.google.common.truth.Truth.assertThat;
19 import static org.mockito.ArgumentMatchers.any;
20 import static org.mockito.Mockito.doThrow;
21 import static org.mockito.Mockito.never;
22 import static org.mockito.Mockito.times;
23 import static org.mockito.Mockito.verify;
24 import static org.mockito.Mockito.verifyNoMoreInteractions;
25 import static org.mockito.Mockito.when;
26 
27 import android.content.Context;
28 import android.net.Uri;
29 import androidx.test.core.app.ApplicationProvider;
30 import com.google.mobiledatadownload.internal.MetadataProto.DataFile;
31 import com.google.mobiledatadownload.internal.MetadataProto.DataFileGroupBookkeeping;
32 import com.google.mobiledatadownload.internal.MetadataProto.DataFileGroupInternal;
33 import com.google.mobiledatadownload.internal.MetadataProto.DataFileGroupInternal.AllowedReaders;
34 import com.google.mobiledatadownload.internal.MetadataProto.FileStatus;
35 import com.google.mobiledatadownload.internal.MetadataProto.GroupKey;
36 import com.google.mobiledatadownload.internal.MetadataProto.NewFileKey;
37 import com.google.mobiledatadownload.internal.MetadataProto.SharedFile;
38 import com.google.android.libraries.mobiledatadownload.SilentFeedback;
39 import com.google.android.libraries.mobiledatadownload.delta.DeltaDecoder;
40 import com.google.android.libraries.mobiledatadownload.file.SynchronousFileStorage;
41 import com.google.android.libraries.mobiledatadownload.file.spi.Backend;
42 import com.google.android.libraries.mobiledatadownload.internal.Migrations.FileKeyVersion;
43 import com.google.android.libraries.mobiledatadownload.internal.collect.GroupKeyAndGroup;
44 import com.google.android.libraries.mobiledatadownload.internal.downloader.MddFileDownloader;
45 import com.google.android.libraries.mobiledatadownload.internal.logging.EventLogger;
46 import com.google.android.libraries.mobiledatadownload.internal.util.DirectoryUtil;
47 import com.google.android.libraries.mobiledatadownload.monitor.DownloadProgressMonitor;
48 import com.google.android.libraries.mobiledatadownload.testing.FakeTimeSource;
49 import com.google.android.libraries.mobiledatadownload.testing.TestFlags;
50 import com.google.common.base.Optional;
51 import com.google.common.collect.ImmutableList;
52 import com.google.common.util.concurrent.Futures;
53 import com.google.common.util.concurrent.MoreExecutors;
54 import com.google.mobiledatadownload.LogEnumsProto.MddClientEvent;
55 import java.io.IOException;
56 import java.util.ArrayList;
57 import java.util.Arrays;
58 import java.util.Calendar;
59 import java.util.Collections;
60 import java.util.List;
61 import java.util.concurrent.atomic.AtomicReference;
62 import org.junit.Before;
63 import org.junit.Rule;
64 import org.junit.Test;
65 import org.junit.runner.RunWith;
66 import org.mockito.Mock;
67 import org.mockito.invocation.InvocationOnMock;
68 import org.mockito.junit.MockitoJUnit;
69 import org.mockito.junit.MockitoRule;
70 import org.robolectric.RobolectricTestRunner;
71 
72 @RunWith(RobolectricTestRunner.class)
73 public final class ExpirationHandlerTest {
74 
75   @Mock SharedFileManager mockSharedFileManager;
76   @Mock SharedFilesMetadata mockSharedFilesMetadata;
77   @Mock FileGroupsMetadata mockFileGroupsMetadata;
78   @Mock EventLogger mockEventLogger;
79   @Mock SilentFeedback mockSilentFeedback;
80 
81   @Mock Backend mockBackend;
82   @Mock Backend mockBlobStoreBackend;
83   @Mock MddFileDownloader mockDownloader;
84   @Mock DownloadProgressMonitor mockDownloadMonitor;
85 
86   // Allows mockFileGroupsMetadata to correctly respond to writeStaleGroups and getAllStaleGroups.
87   AtomicReference<ImmutableList<DataFileGroupInternal>> fileGroupsMetadataStaleGroups =
88       new AtomicReference<>(ImmutableList.of());
89 
90   private SynchronousFileStorage fileStorage;
91   private Context context;
92   private ExpirationHandler expirationHandler;
93   private ExpirationHandler expirationHandlerNoMocks;
94   private FakeTimeSource testClock;
95   private Uri baseDownloadDirectoryUri;
96   private Uri baseDownloadSymlinkDirectoryUri;
97   private FileGroupsMetadata fileGroupsMetadata;
98   private SharedFilesMetadata sharedFilesMetadata;
99   private SharedFileManager sharedFileManager;
100 
101   private static final String TEST_GROUP_1 = "test-group-1";
102   private static final GroupKey TEST_KEY_1 = GroupKey.getDefaultInstance();
103 
104   private static final String TEST_GROUP_2 = "test-group-2";
105   private static final GroupKey TEST_KEY_2 = GroupKey.getDefaultInstance();
106 
107   private final Uri testUri1 =
108       Uri.parse(
109           "android://com.google.android.libraries.mobiledatadownload.internal/files/datadownload/shared/public/file_1");
110 
111   private final Uri testUri2 =
112       Uri.parse(
113           "android://com.google.android.libraries.mobiledatadownload.internal/files/datadownload/shared/public/file_2");
114 
115   private final Uri tempTestUri2 =
116       Uri.parse(
117           "android://com.google.android.libraries.mobiledatadownload.internal/files/datadownload/shared/public/file_2_temp");
118 
119   private final Uri testUri3 =
120       Uri.parse(
121           "android://com.google.android.libraries.mobiledatadownload.internal/files/datadownload/shared/public/file_3");
122 
123   private final Uri testUri4 =
124       Uri.parse(
125           "android://com.google.android.libraries.mobiledatadownload.internal/files/datadownload/shared/public/file_4");
126 
127   // MDD file URI could be a folder which is unzipped from zip folder download transform
128   private final Uri testDirUri1 =
129       Uri.parse(
130           "android://com.google.android.libraries.mobiledatadownload.internal/files/datadownload/shared/public/dir_1");
131 
132   private final Uri testDirFileUri1 =
133       Uri.parse(
134           "android://com.google.android.libraries.mobiledatadownload.internal/files/datadownload/shared/public/dir_1/file_1");
135 
136   private final Uri testDirFileUri2 =
137       Uri.parse(
138           "android://com.google.android.libraries.mobiledatadownload.internal/files/datadownload/shared/public/dir_1/file_2");
139 
140   private final Uri dirForAll =
141       Uri.parse(
142           "android://com.google.android.libraries.mobiledatadownload.internal/files/datadownload/shared/public_3p");
143   private final Uri dirFor1p =
144       Uri.parse(
145           "android://com.google.android.libraries.mobiledatadownload.internal/files/datadownload/shared/public");
146   private final Uri dirFor0p =
147       Uri.parse(
148           "android://com.google.android.libraries.mobiledatadownload.internal/files/datadownload/shared/private");
149 
150   private final Uri symlinkDirForGroup1 =
151       Uri.parse(
152           "android://com.google.android.libraries.mobiledatadownload.internal/files/datadownload/shared/links/public/test-group-1");
153   private final Uri symlinkForUri1 =
154       Uri.parse(
155           "android://com.google.android.libraries.mobiledatadownload.internal/files/datadownload/shared/links/public/test-group-1/test-group-1_0");
156 
157   private final Uri symlinkDirForGroup2 =
158       Uri.parse(
159           "android://com.google.android.libraries.mobiledatadownload.internal/files/datadownload/shared/links/public/test-group-2");
160   private final Uri symlinkForUri2 =
161       Uri.parse(
162           "android://com.google.android.libraries.mobiledatadownload.internal/files/datadownload/shared/links/public/test-group-2/test-group-2_0");
163 
164   private final TestFlags flags = new TestFlags();
165 
166   @Rule public final MockitoRule mocks = MockitoJUnit.rule();
167 
168   @Before
setUp()169   public void setUp() throws Exception {
170 
171     context = ApplicationProvider.getApplicationContext();
172 
173     testClock = new FakeTimeSource();
174 
175     baseDownloadDirectoryUri = DirectoryUtil.getBaseDownloadDirectory(context, Optional.absent());
176     baseDownloadSymlinkDirectoryUri =
177         DirectoryUtil.getBaseDownloadSymlinkDirectory(context, Optional.absent());
178     when(mockBackend.name()).thenReturn("android");
179     when(mockBlobStoreBackend.name()).thenReturn("blobstore");
180     setUpDirectoryMock(baseDownloadDirectoryUri, Arrays.asList(dirForAll, dirFor1p, dirFor0p));
181     setUpDirectoryMock(dirForAll, ImmutableList.of());
182     setUpDirectoryMock(dirFor0p, ImmutableList.of());
183     setUpDirectoryMock(dirFor1p, ImmutableList.of());
184     setUpDirectoryMock(testDirUri1, ImmutableList.of());
185     fileStorage = new SynchronousFileStorage(Arrays.asList(mockBackend, mockBlobStoreBackend));
186 
187     expirationHandler =
188         new ExpirationHandler(
189             context,
190             mockFileGroupsMetadata,
191             mockSharedFileManager,
192             mockSharedFilesMetadata,
193             mockEventLogger,
194             testClock,
195             fileStorage,
196             Optional.absent(),
197             mockSilentFeedback,
198             MoreExecutors.directExecutor(),
199             flags);
200 
201     // By default, mocks will return empty lists
202     when(mockFileGroupsMetadata.getAllFreshGroups())
203         .thenReturn(Futures.immediateFuture(ImmutableList.of()));
204     when(mockFileGroupsMetadata.removeAllGroupsWithKeys(any()))
205         .thenReturn(Futures.immediateFuture(true));
206     when(mockFileGroupsMetadata.removeAllStaleGroups()).thenReturn(Futures.immediateVoidFuture());
207     when(mockSharedFileManager.removeFileEntry(any())).thenReturn(Futures.immediateFuture(true));
208     when(mockSharedFilesMetadata.read(any()))
209         .thenReturn(Futures.immediateFuture(SharedFile.getDefaultInstance()));
210 
211     // Calls to mockFileGroupsMetadata.writeStaleGroups() are reflected by getAllStaleGroups().
212     when(mockFileGroupsMetadata.getAllStaleGroups())
213         .thenAnswer(invocation -> Futures.immediateFuture(fileGroupsMetadataStaleGroups.get()));
214     when(mockFileGroupsMetadata.writeStaleGroups(any()))
215         .thenAnswer(
216             (InvocationOnMock invocation) -> {
217               List<DataFileGroupInternal> request = invocation.getArgument(0);
218               fileGroupsMetadataStaleGroups.set(ImmutableList.copyOf(request));
219               return Futures.immediateFuture(true);
220             });
221   }
222 
setupForAndroidShared()223   private void setupForAndroidShared() {
224     // Construct an expiration handler without mocking the main classes
225     fileGroupsMetadata =
226         new SharedPreferencesFileGroupsMetadata(
227             context,
228             testClock,
229             mockSilentFeedback,
230             Optional.absent(),
231             MoreExecutors.directExecutor());
232     Optional<DeltaDecoder> deltaDecoder = Optional.absent();
233     sharedFilesMetadata =
234         new SharedPreferencesSharedFilesMetadata(
235             context, mockSilentFeedback, Optional.absent(), flags);
236     sharedFileManager =
237         new SharedFileManager(
238             context,
239             mockSilentFeedback,
240             sharedFilesMetadata,
241             fileStorage,
242             mockDownloader,
243             deltaDecoder,
244             Optional.of(mockDownloadMonitor),
245             mockEventLogger,
246             flags,
247             fileGroupsMetadata,
248             Optional.absent(),
249             MoreExecutors.directExecutor());
250 
251     expirationHandlerNoMocks =
252         new ExpirationHandler(
253             context,
254             fileGroupsMetadata,
255             sharedFileManager,
256             sharedFilesMetadata,
257             mockEventLogger,
258             testClock,
259             fileStorage,
260             Optional.absent(),
261             mockSilentFeedback,
262             MoreExecutors.directExecutor(),
263             flags);
264   }
265 
266   @Test
updateExpiration_noGroups()267   public void updateExpiration_noGroups() throws Exception {
268     when(mockFileGroupsMetadata.getAllFreshGroups())
269         .thenReturn(Futures.immediateFuture(ImmutableList.of()));
270     when(mockSharedFilesMetadata.getAllFileKeys())
271         .thenReturn(Futures.immediateFuture(ImmutableList.of()));
272 
273     expirationHandler.updateExpiration().get();
274 
275     verify(mockFileGroupsMetadata, times(3)).getAllFreshGroups();
276     verify(mockFileGroupsMetadata, times(2)).getAllStaleGroups();
277     verify(mockFileGroupsMetadata).removeAllGroupsWithKeys(ImmutableList.of());
278     verify(mockFileGroupsMetadata).removeAllStaleGroups();
279     verify(mockFileGroupsMetadata).writeStaleGroups(ImmutableList.of());
280     verifyNoMoreInteractions(mockFileGroupsMetadata);
281     verify(mockSharedFilesMetadata).getAllFileKeys();
282     verify(mockBackend).exists(baseDownloadDirectoryUri);
283     verify(mockBackend).children(baseDownloadDirectoryUri);
284     verify(mockBackend, never()).deleteFile(any());
285     verifyNoMoreInteractions(mockSharedFileManager);
286     verifyNoMoreInteractions(mockEventLogger);
287   }
288 
289   @Test
updateExpiration_noExpiredGroups_noExpirationDates()290   public void updateExpiration_noExpiredGroups_noExpirationDates() throws Exception {
291     DataFileGroupInternal dataFileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP_1, 2);
292     NewFileKey[] fileKeys = MddTestUtil.createFileKeysForDataFileGroupInternal(dataFileGroup);
293 
294     List<GroupKeyAndGroup> groups =
295         Arrays.asList(GroupKeyAndGroup.create(TEST_KEY_1, dataFileGroup));
296     when(mockFileGroupsMetadata.getAllFreshGroups()).thenReturn(Futures.immediateFuture(groups));
297     when(mockSharedFileManager.getFileStatus(fileKeys[0]))
298         .thenReturn(Futures.immediateFuture(FileStatus.DOWNLOAD_COMPLETE));
299     when(mockSharedFileManager.getOnDeviceUri(fileKeys[0]))
300         .thenReturn(Futures.immediateFuture(testUri1));
301     when(mockSharedFileManager.getFileStatus(fileKeys[1]))
302         .thenReturn(Futures.immediateFuture(FileStatus.DOWNLOAD_COMPLETE));
303     when(mockSharedFileManager.getOnDeviceUri(fileKeys[1]))
304         .thenReturn(Futures.immediateFuture(testUri2));
305 
306     when(mockSharedFilesMetadata.getAllFileKeys())
307         .thenReturn(Futures.immediateFuture(Arrays.asList(fileKeys)));
308     when(mockBackend.children(dirFor0p)).thenReturn(Arrays.asList(testUri1, testUri2));
309 
310     expirationHandler.updateExpiration().get();
311 
312     verify(mockFileGroupsMetadata, times(3)).getAllFreshGroups();
313     verify(mockFileGroupsMetadata, times(2)).getAllStaleGroups();
314     verify(mockFileGroupsMetadata).removeAllGroupsWithKeys(ImmutableList.of());
315     verify(mockFileGroupsMetadata).removeAllStaleGroups();
316     verify(mockFileGroupsMetadata).writeStaleGroups(ImmutableList.of());
317     verifyNoMoreInteractions(mockFileGroupsMetadata);
318     verify(mockSharedFilesMetadata).getAllFileKeys();
319     verify(mockSharedFileManager).getOnDeviceUri(fileKeys[0]);
320     verify(mockSharedFileManager).getOnDeviceUri(fileKeys[1]);
321     verify(mockBackend).exists(baseDownloadDirectoryUri);
322     verify(mockBackend).children(baseDownloadDirectoryUri);
323     verify(mockBackend).isDirectory(dirForAll);
324     verify(mockBackend).isDirectory(dirFor1p);
325     verify(mockBackend, never()).deleteFile(any());
326     verifyNoMoreInteractions(mockSharedFileManager);
327     verifyNoMoreInteractions(mockEventLogger);
328   }
329 
330   @Test
updateExpiration_noExpiredGroups_expirationDates()331   public void updateExpiration_noExpiredGroups_expirationDates() throws Exception {
332     Calendar now = new Calendar.Builder().setDate(2018, Calendar.MARCH, 20).build();
333     testClock.set(now.getTimeInMillis());
334 
335     DataFileGroupInternal dataFileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP_1, 2);
336     Calendar later = new Calendar.Builder().setDate(2018, Calendar.APRIL, 20).build();
337     long laterTimeSecs = later.getTimeInMillis() / 1000;
338     dataFileGroup = dataFileGroup.toBuilder().setExpirationDateSecs(laterTimeSecs).build();
339 
340     NewFileKey[] fileKeys = MddTestUtil.createFileKeysForDataFileGroupInternal(dataFileGroup);
341 
342     List<GroupKeyAndGroup> groups =
343         Arrays.asList(GroupKeyAndGroup.create(TEST_KEY_1, dataFileGroup));
344     when(mockFileGroupsMetadata.getAllFreshGroups()).thenReturn(Futures.immediateFuture(groups));
345     when(mockSharedFileManager.getFileStatus(fileKeys[0]))
346         .thenReturn(Futures.immediateFuture(FileStatus.DOWNLOAD_COMPLETE));
347     when(mockSharedFileManager.getOnDeviceUri(fileKeys[0]))
348         .thenReturn(Futures.immediateFuture(testUri1));
349     when(mockSharedFileManager.getFileStatus(fileKeys[1]))
350         .thenReturn(Futures.immediateFuture(FileStatus.DOWNLOAD_COMPLETE));
351     when(mockSharedFileManager.getOnDeviceUri(fileKeys[1]))
352         .thenReturn(Futures.immediateFuture(testUri2));
353 
354     when(mockSharedFilesMetadata.getAllFileKeys())
355         .thenReturn(Futures.immediateFuture(Arrays.asList(fileKeys)));
356     when(mockBackend.children(dirFor0p)).thenReturn(Arrays.asList(testUri1, testUri2));
357 
358     expirationHandler.updateExpiration().get();
359 
360     verify(mockFileGroupsMetadata, times(3)).getAllFreshGroups();
361     verify(mockFileGroupsMetadata, times(2)).getAllStaleGroups();
362     verify(mockFileGroupsMetadata).removeAllGroupsWithKeys(ImmutableList.of());
363     verify(mockFileGroupsMetadata).removeAllStaleGroups();
364     verify(mockFileGroupsMetadata).writeStaleGroups(ImmutableList.of());
365     verifyNoMoreInteractions(mockFileGroupsMetadata);
366     verify(mockSharedFilesMetadata).getAllFileKeys();
367     verify(mockSharedFileManager).getOnDeviceUri(fileKeys[0]);
368     verify(mockSharedFileManager).getOnDeviceUri(fileKeys[1]);
369     verify(mockBackend).exists(baseDownloadDirectoryUri);
370     verify(mockBackend).children(baseDownloadDirectoryUri);
371     verify(mockBackend).isDirectory(dirForAll);
372     verify(mockBackend).isDirectory(dirFor1p);
373     verify(mockBackend, never()).deleteFile(any());
374     verifyNoMoreInteractions(mockSharedFileManager);
375     verifyNoMoreInteractions(mockEventLogger);
376   }
377 
378   @Test
updateExpiration_expiredGroups()379   public void updateExpiration_expiredGroups() throws Exception {
380     // Current time
381     Calendar now = new Calendar.Builder().setDate(2020, Calendar.MARCH, 20).build();
382     testClock.set(now.getTimeInMillis());
383 
384     DataFileGroupInternal dataFileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP_1, 5);
385     // Time when the group expires
386     Calendar earlier = new Calendar.Builder().setDate(2018, Calendar.MARCH, 20).build();
387     long earlierTimeSecs = earlier.getTimeInMillis() / 1000;
388     dataFileGroup = dataFileGroup.toBuilder().setExpirationDateSecs(earlierTimeSecs).build();
389 
390     NewFileKey[] fileKeys = MddTestUtil.createFileKeysForDataFileGroupInternal(dataFileGroup);
391 
392     List<GroupKeyAndGroup> groups =
393         Arrays.asList(GroupKeyAndGroup.create(TEST_KEY_1, dataFileGroup));
394     when(mockFileGroupsMetadata.getAllFreshGroups())
395         .thenReturn(Futures.immediateFuture(groups))
396         .thenReturn(Futures.immediateFuture(new ArrayList<>()));
397     when(mockSharedFileManager.getFileStatus(fileKeys[0]))
398         .thenReturn(Futures.immediateFuture(FileStatus.DOWNLOAD_COMPLETE));
399     when(mockSharedFileManager.getOnDeviceUri(fileKeys[0]))
400         .thenReturn(Futures.immediateFuture(testUri1));
401     when(mockSharedFileManager.getFileStatus(fileKeys[1]))
402         .thenReturn(Futures.immediateFuture(FileStatus.DOWNLOAD_FAILED));
403     when(mockSharedFileManager.getOnDeviceUri(fileKeys[1]))
404         .thenReturn(Futures.immediateFuture(testUri2));
405     when(mockSharedFileManager.getFileStatus(fileKeys[2]))
406         .thenReturn(Futures.immediateFuture(FileStatus.DOWNLOAD_IN_PROGRESS));
407     when(mockSharedFileManager.getOnDeviceUri(fileKeys[2]))
408         .thenReturn(Futures.immediateFuture(testUri3));
409     when(mockSharedFileManager.getFileStatus(fileKeys[3]))
410         .thenReturn(Futures.immediateFuture(FileStatus.SUBSCRIBED));
411     when(mockSharedFileManager.getOnDeviceUri(fileKeys[3]))
412         .thenReturn(Futures.immediateFuture(testUri4));
413     when(mockSharedFileManager.getFileStatus(fileKeys[4]))
414         .thenReturn(Futures.immediateFuture(FileStatus.NONE));
415 
416     when(mockSharedFilesMetadata.getAllFileKeys())
417         .thenReturn(Futures.immediateFuture(Arrays.asList(fileKeys)));
418     when(mockBackend.children(dirFor0p))
419         .thenReturn(Arrays.asList(testUri1, testUri2, testUri3, testUri4));
420     expirationHandler.updateExpiration().get();
421 
422     verify(mockFileGroupsMetadata, times(3)).getAllFreshGroups();
423     verify(mockFileGroupsMetadata, times(2)).getAllStaleGroups();
424     verify(mockFileGroupsMetadata).removeAllGroupsWithKeys(Arrays.asList(TEST_KEY_1));
425     verify(mockFileGroupsMetadata).removeAllStaleGroups();
426     verify(mockFileGroupsMetadata).writeStaleGroups(ImmutableList.of());
427     verifyNoMoreInteractions(mockFileGroupsMetadata);
428     verify(mockSharedFilesMetadata).getAllFileKeys();
429     verify(mockSharedFileManager).removeFileEntry(fileKeys[0]);
430     verify(mockSharedFileManager).removeFileEntry(fileKeys[1]);
431     verify(mockSharedFileManager).removeFileEntry(fileKeys[2]);
432     verify(mockSharedFileManager).removeFileEntry(fileKeys[3]);
433     verify(mockSharedFileManager).removeFileEntry(fileKeys[4]);
434     verify(mockBackend).exists(baseDownloadDirectoryUri);
435     verify(mockBackend).children(baseDownloadDirectoryUri);
436     verify(mockBackend).isDirectory(testUri1);
437     verify(mockBackend).isDirectory(testUri2);
438     verify(mockBackend).isDirectory(testUri3);
439     verify(mockBackend).isDirectory(testUri4);
440     verify(mockBackend).deleteFile(testUri1);
441     verify(mockBackend).deleteFile(testUri2);
442     verify(mockBackend).deleteFile(testUri3);
443     verify(mockBackend).deleteFile(testUri4);
444     verifyNoMoreInteractions(mockSharedFileManager);
445 
446     verify(mockEventLogger)
447         .logEventSampled(
448             MddClientEvent.Code.EVENT_CODE_UNSPECIFIED,
449             dataFileGroup.getGroupName(),
450             dataFileGroup.getFileGroupVersionNumber(),
451             dataFileGroup.getBuildId(),
452             dataFileGroup.getVariantId());
453     verify(mockEventLogger).logMddDataDownloadFileExpirationEvent(0, 4);
454     verify(mockEventLogger).logMddDataDownloadFileExpirationEvent(0, 5);
455     verifyNoMoreInteractions(mockEventLogger);
456   }
457 
458   @Test
updateExpiration_noExpiredGroups_pendingGroup()459   public void updateExpiration_noExpiredGroups_pendingGroup() throws Exception {
460     Calendar now = new Calendar.Builder().setDate(2018, Calendar.MARCH, 20).build();
461     testClock.set(now.getTimeInMillis());
462 
463     DataFileGroupInternal dataFileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP_1, 2);
464     Calendar later = new Calendar.Builder().setDate(2018, Calendar.APRIL, 20).build();
465     long laterTimeSecs = later.getTimeInMillis() / 1000;
466     dataFileGroup = dataFileGroup.toBuilder().setExpirationDateSecs(laterTimeSecs).build();
467 
468     NewFileKey[] fileKeys = MddTestUtil.createFileKeysForDataFileGroupInternal(dataFileGroup);
469 
470     List<GroupKeyAndGroup> groups =
471         Arrays.asList(GroupKeyAndGroup.create(TEST_KEY_1, dataFileGroup));
472     when(mockFileGroupsMetadata.getAllFreshGroups()).thenReturn(Futures.immediateFuture(groups));
473     when(mockSharedFileManager.getFileStatus(fileKeys[0]))
474         .thenReturn(Futures.immediateFuture(FileStatus.DOWNLOAD_COMPLETE));
475     when(mockSharedFileManager.getOnDeviceUri(fileKeys[0]))
476         .thenReturn(Futures.immediateFuture(testUri1));
477     // The second file has not been downloaded.
478     when(mockSharedFileManager.getFileStatus(fileKeys[1]))
479         .thenReturn(Futures.immediateFuture(FileStatus.NONE));
480     when(mockSharedFileManager.getOnDeviceUri(fileKeys[1]))
481         .thenReturn(Futures.immediateFuture(testUri2));
482     when(mockSharedFilesMetadata.getAllFileKeys())
483         .thenReturn(Futures.immediateFuture(Arrays.asList(fileKeys)));
484     when(mockBackend.children(dirFor0p)).thenReturn(Arrays.asList(testUri1, testUri2));
485 
486     expirationHandler.updateExpiration().get();
487 
488     verify(mockFileGroupsMetadata, times(3)).getAllFreshGroups();
489     verify(mockFileGroupsMetadata, times(2)).getAllStaleGroups();
490     verify(mockFileGroupsMetadata).removeAllGroupsWithKeys(ImmutableList.of());
491     verify(mockFileGroupsMetadata).removeAllStaleGroups();
492     verify(mockFileGroupsMetadata).writeStaleGroups(ImmutableList.of());
493     verifyNoMoreInteractions(mockFileGroupsMetadata);
494     verify(mockSharedFilesMetadata).getAllFileKeys();
495     verify(mockSharedFileManager).getOnDeviceUri(fileKeys[0]);
496     verify(mockSharedFileManager).getOnDeviceUri(fileKeys[1]);
497     verify(mockBackend).exists(baseDownloadDirectoryUri);
498     verify(mockBackend).children(baseDownloadDirectoryUri);
499     verify(mockBackend).isDirectory(dirForAll);
500     verify(mockBackend).isDirectory(dirFor1p);
501     verify(mockBackend, never()).deleteFile(any());
502     verifyNoMoreInteractions(mockSharedFileManager);
503     verifyNoMoreInteractions(mockEventLogger);
504   }
505 
506   @Test
updateExpiration_notDeleteInternalFiles()507   public void updateExpiration_notDeleteInternalFiles() throws Exception {
508     Migrations.setCurrentVersion(context, FileKeyVersion.USE_CHECKSUM_ONLY);
509     Calendar now = new Calendar.Builder().setDate(2018, Calendar.MARCH, 20).build();
510     testClock.set(now.getTimeInMillis());
511 
512     DataFileGroupInternal dataFileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP_1, 2);
513     Calendar later = new Calendar.Builder().setDate(2018, Calendar.APRIL, 20).build();
514     long laterTimeSecs = later.getTimeInMillis() / 1000;
515     dataFileGroup = dataFileGroup.toBuilder().setExpirationDateSecs(laterTimeSecs).build();
516 
517     NewFileKey[] fileKeys = createFileKeysUseChecksumOnly(dataFileGroup);
518 
519     List<GroupKeyAndGroup> groups =
520         Arrays.asList(GroupKeyAndGroup.create(TEST_KEY_1, dataFileGroup));
521     when(mockFileGroupsMetadata.getAllFreshGroups()).thenReturn(Futures.immediateFuture(groups));
522     when(mockSharedFileManager.getFileStatus(fileKeys[0]))
523         .thenReturn(Futures.immediateFuture(FileStatus.DOWNLOAD_COMPLETE));
524     when(mockSharedFileManager.getOnDeviceUri(fileKeys[0]))
525         .thenReturn(Futures.immediateFuture(testUri1));
526     // The second file has not been downloaded.
527     when(mockSharedFileManager.getFileStatus(fileKeys[1]))
528         .thenReturn(Futures.immediateFuture(FileStatus.NONE));
529     when(mockSharedFileManager.getOnDeviceUri(fileKeys[1]))
530         .thenReturn(Futures.immediateFuture(testUri2));
531     when(mockSharedFilesMetadata.getAllFileKeys())
532         .thenReturn(Futures.immediateFuture(Arrays.asList(fileKeys)));
533     when(mockBackend.children(dirFor0p))
534         .thenReturn(Arrays.asList(testUri1, testUri2, tempTestUri2));
535 
536     expirationHandler.updateExpiration().get();
537 
538     verify(mockFileGroupsMetadata, times(3)).getAllFreshGroups();
539     verify(mockFileGroupsMetadata, times(2)).getAllStaleGroups();
540     verify(mockFileGroupsMetadata).removeAllGroupsWithKeys(ImmutableList.of());
541     verify(mockFileGroupsMetadata).removeAllStaleGroups();
542     verify(mockFileGroupsMetadata).writeStaleGroups(ImmutableList.of());
543     verifyNoMoreInteractions(mockFileGroupsMetadata);
544     verify(mockSharedFilesMetadata).getAllFileKeys();
545     verify(mockSharedFileManager).getOnDeviceUri(fileKeys[0]);
546     verify(mockSharedFileManager).getOnDeviceUri(fileKeys[1]);
547     verify(mockBackend).exists(baseDownloadDirectoryUri);
548     verify(mockBackend).children(baseDownloadDirectoryUri);
549     verify(mockBackend).isDirectory(dirForAll);
550     verify(mockBackend).isDirectory(dirFor1p);
551     verify(mockBackend).isDirectory(dirFor0p);
552     verify(mockBackend, never()).deleteFile(any());
553     verifyNoMoreInteractions(mockSharedFileManager);
554     verifyNoMoreInteractions(mockEventLogger);
555   }
556 
557   @Test
updateExpiration_deleteInternalFilesWithExipiredAccountedFile()558   public void updateExpiration_deleteInternalFilesWithExipiredAccountedFile() throws Exception {
559     Migrations.setCurrentVersion(context, FileKeyVersion.USE_CHECKSUM_ONLY);
560     Calendar now = new Calendar.Builder().setDate(2018, Calendar.MARCH, 20).build();
561     testClock.set(now.getTimeInMillis());
562 
563     DataFileGroupInternal dataFileGroup =
564         MddTestUtil.createDataFileGroupInternal(TEST_GROUP_1, 1).toBuilder()
565             .setBuildId(10)
566             .setVariantId("testVariant")
567             .build();
568     long nowTimeSecs = now.getTimeInMillis() / 1000;
569     dataFileGroup = dataFileGroup.toBuilder().setExpirationDateSecs(nowTimeSecs).build();
570 
571     NewFileKey[] fileKeys = createFileKeysUseChecksumOnly(dataFileGroup);
572 
573     List<GroupKeyAndGroup> groups =
574         Arrays.asList(GroupKeyAndGroup.create(TEST_KEY_1, dataFileGroup));
575     when(mockFileGroupsMetadata.getAllFreshGroups())
576         .thenReturn(Futures.immediateFuture(groups))
577         .thenReturn(Futures.immediateFuture(new ArrayList<>()));
578     when(mockSharedFileManager.getFileStatus(fileKeys[0]))
579         .thenReturn(Futures.immediateFuture(FileStatus.DOWNLOAD_COMPLETE));
580     when(mockSharedFileManager.getOnDeviceUri(fileKeys[0]))
581         .thenReturn(Futures.immediateFuture(testUri2));
582 
583     when(mockSharedFilesMetadata.getAllFileKeys())
584         .thenReturn(Futures.immediateFuture(Arrays.asList(fileKeys)));
585     when(mockBackend.children(dirFor0p)).thenReturn(Arrays.asList(testUri2, tempTestUri2));
586     expirationHandler.updateExpiration().get();
587 
588     verify(mockFileGroupsMetadata, times(3)).getAllFreshGroups();
589     verify(mockFileGroupsMetadata, times(2)).getAllStaleGroups();
590     verify(mockFileGroupsMetadata).removeAllGroupsWithKeys(Arrays.asList(TEST_KEY_1));
591     verify(mockFileGroupsMetadata).removeAllStaleGroups();
592     verify(mockFileGroupsMetadata).writeStaleGroups(ImmutableList.of());
593     verifyNoMoreInteractions(mockFileGroupsMetadata);
594     verify(mockSharedFilesMetadata).getAllFileKeys();
595     verify(mockSharedFileManager).removeFileEntry(fileKeys[0]);
596 
597     verify(mockBackend).exists(baseDownloadDirectoryUri);
598     verify(mockBackend).children(baseDownloadDirectoryUri);
599     verify(mockBackend).isDirectory(testUri2);
600     verify(mockBackend).isDirectory(tempTestUri2);
601     verify(mockBackend).deleteFile(testUri2);
602     verify(mockBackend).deleteFile(tempTestUri2);
603     verifyNoMoreInteractions(mockSharedFileManager);
604 
605     verify(mockEventLogger)
606         .logEventSampled(
607             MddClientEvent.Code.EVENT_CODE_UNSPECIFIED,
608             dataFileGroup.getGroupName(),
609             dataFileGroup.getFileGroupVersionNumber(),
610             dataFileGroup.getBuildId(),
611             dataFileGroup.getVariantId());
612     verify(mockEventLogger).logMddDataDownloadFileExpirationEvent(0, 2);
613     verify(mockEventLogger).logMddDataDownloadFileExpirationEvent(0, 1);
614     verifyNoMoreInteractions(mockEventLogger);
615   }
616 
617   @Test
updateExpiration_sharedFiles_noExpiration()618   public void updateExpiration_sharedFiles_noExpiration() throws Exception {
619     Calendar now = new Calendar.Builder().setDate(2018, Calendar.MARCH, 20).build();
620     testClock.set(now.getTimeInMillis());
621 
622     DataFile dataFile = MddTestUtil.createDataFile("file", 0);
623     NewFileKey fileKey =
624         SharedFilesMetadata.createKeyFromDataFile(
625             dataFile, AllowedReaders.ONLY_GOOGLE_PLAY_SERVICES);
626 
627     // The first group expires 30 days from now.
628     Calendar later = new Calendar.Builder().setDate(2018, Calendar.APRIL, 20).build();
629     long laterTimeSecs = later.getTimeInMillis() / 1000;
630     DataFileGroupInternal firstGroup =
631         DataFileGroupInternal.newBuilder()
632             .setGroupName(TEST_GROUP_1)
633             .addFile(dataFile)
634             .setAllowedReadersEnum(AllowedReaders.ONLY_GOOGLE_PLAY_SERVICES)
635             .setExpirationDateSecs(laterTimeSecs)
636             .build();
637 
638     // The second group never expires
639     DataFileGroupInternal secondGroup =
640         DataFileGroupInternal.newBuilder()
641             .setGroupName(TEST_GROUP_2)
642             .addFile(dataFile)
643             .setAllowedReadersEnum(AllowedReaders.ONLY_GOOGLE_PLAY_SERVICES)
644             .build();
645 
646     List<GroupKeyAndGroup> groups =
647         Arrays.asList(
648             GroupKeyAndGroup.create(TEST_KEY_1, firstGroup),
649             GroupKeyAndGroup.create(TEST_KEY_2, secondGroup));
650     when(mockFileGroupsMetadata.getAllFreshGroups()).thenReturn(Futures.immediateFuture(groups));
651     when(mockSharedFileManager.getFileStatus(fileKey))
652         .thenReturn(Futures.immediateFuture(FileStatus.DOWNLOAD_COMPLETE));
653     when(mockSharedFileManager.getOnDeviceUri(fileKey))
654         .thenReturn(Futures.immediateFuture(testUri1));
655     when(mockSharedFilesMetadata.getAllFileKeys())
656         .thenReturn(Futures.immediateFuture(Collections.singletonList(fileKey)));
657     when(mockBackend.children(dirFor0p)).thenReturn(Arrays.asList(testUri1));
658 
659     expirationHandler.updateExpiration().get();
660 
661     verify(mockFileGroupsMetadata, times(3)).getAllFreshGroups();
662     verify(mockFileGroupsMetadata, times(2)).getAllStaleGroups();
663     verify(mockFileGroupsMetadata).removeAllGroupsWithKeys(ImmutableList.of());
664     verify(mockFileGroupsMetadata).removeAllStaleGroups();
665     verify(mockFileGroupsMetadata).writeStaleGroups(ImmutableList.of());
666     verifyNoMoreInteractions(mockFileGroupsMetadata);
667     verify(mockSharedFilesMetadata).getAllFileKeys();
668     verify(mockSharedFileManager).getOnDeviceUri(fileKey);
669     verify(mockBackend).exists(baseDownloadDirectoryUri);
670     verify(mockBackend).children(baseDownloadDirectoryUri);
671     verify(mockBackend).isDirectory(dirForAll);
672     verify(mockBackend, never()).deleteFile(any());
673     verifyNoMoreInteractions(mockSharedFileManager);
674     verifyNoMoreInteractions(mockEventLogger);
675   }
676 
677   @Test
updateExpiration_sharedFiles_expiration()678   public void updateExpiration_sharedFiles_expiration() throws Exception {
679     Calendar now = new Calendar.Builder().setDate(2018, Calendar.MARCH, 20).build();
680     testClock.set(now.getTimeInMillis());
681 
682     DataFile dataFile = MddTestUtil.createDataFile("file", 0);
683     NewFileKey fileKey =
684         SharedFilesMetadata.createKeyFromDataFile(
685             dataFile, AllowedReaders.ONLY_GOOGLE_PLAY_SERVICES);
686 
687     // The first group expires 30 days from now.
688     Calendar later = new Calendar.Builder().setDate(2018, Calendar.APRIL, 20).build();
689     long laterTimeSecs = later.getTimeInMillis() / 1000;
690     DataFileGroupInternal firstGroup =
691         DataFileGroupInternal.newBuilder()
692             .setGroupName(TEST_GROUP_1)
693             .addFile(dataFile)
694             .setAllowedReadersEnum(AllowedReaders.ONLY_GOOGLE_PLAY_SERVICES)
695             .setExpirationDateSecs(laterTimeSecs)
696             .build();
697 
698     // The second group expires 15 days
699     Calendar sooner = new Calendar.Builder().setDate(2018, Calendar.APRIL, 5).build();
700     DataFileGroupInternal secondGroup =
701         DataFileGroupInternal.newBuilder()
702             .setGroupName(TEST_GROUP_2)
703             .addFile(dataFile)
704             .setAllowedReadersEnum(AllowedReaders.ONLY_GOOGLE_PLAY_SERVICES)
705             .setExpirationDateSecs(sooner.getTimeInMillis() / 1000)
706             .build();
707 
708     List<GroupKeyAndGroup> groups =
709         Arrays.asList(
710             GroupKeyAndGroup.create(TEST_KEY_1, firstGroup),
711             GroupKeyAndGroup.create(TEST_KEY_2, secondGroup));
712     when(mockFileGroupsMetadata.getAllFreshGroups()).thenReturn(Futures.immediateFuture(groups));
713     when(mockSharedFileManager.getFileStatus(fileKey))
714         .thenReturn(Futures.immediateFuture(FileStatus.DOWNLOAD_COMPLETE));
715     when(mockSharedFileManager.getOnDeviceUri(fileKey))
716         .thenReturn(Futures.immediateFuture(testUri1));
717     when(mockSharedFilesMetadata.getAllFileKeys())
718         .thenReturn(Futures.immediateFuture(Collections.singletonList(fileKey)));
719     when(mockBackend.children(dirFor0p)).thenReturn(Arrays.asList(testUri1));
720 
721     expirationHandler.updateExpiration().get();
722 
723     verify(mockFileGroupsMetadata, times(3)).getAllFreshGroups();
724     verify(mockFileGroupsMetadata, times(2)).getAllStaleGroups();
725     verify(mockFileGroupsMetadata).removeAllGroupsWithKeys(ImmutableList.of());
726     verify(mockFileGroupsMetadata).removeAllStaleGroups();
727     verify(mockFileGroupsMetadata).writeStaleGroups(ImmutableList.of());
728     verifyNoMoreInteractions(mockFileGroupsMetadata);
729     verify(mockSharedFilesMetadata).getAllFileKeys();
730     verify(mockSharedFileManager).getOnDeviceUri(fileKey);
731     verify(mockBackend).exists(baseDownloadDirectoryUri);
732     verify(mockBackend).children(baseDownloadDirectoryUri);
733     verify(mockBackend).isDirectory(dirForAll);
734     verify(mockBackend, never()).deleteFile(any());
735     verifyNoMoreInteractions(mockSharedFileManager);
736     verifyNoMoreInteractions(mockEventLogger);
737   }
738 
739   @Test
updateExpiration_noExpiredStaleGroups()740   public void updateExpiration_noExpiredStaleGroups() throws Exception {
741     Calendar now = new Calendar.Builder().setDate(2018, Calendar.MARCH, 20).build();
742     testClock.set(now.getTimeInMillis());
743 
744     Calendar later = new Calendar.Builder().setDate(2018, Calendar.MARCH, 22).build();
745     Long laterTimeSecs = later.getTimeInMillis() / 1000;
746     ;
747     DataFileGroupInternal dataFileGroup =
748         MddTestUtil.createDataFileGroupInternal(TEST_GROUP_1, 2).toBuilder()
749             .setStaleLifetimeSecs(laterTimeSecs - (now.getTimeInMillis() / 1000))
750             .setBookkeeping(
751                 DataFileGroupBookkeeping.newBuilder().setStaleExpirationDate(laterTimeSecs).build())
752             .build();
753     NewFileKey[] fileKeys = MddTestUtil.createFileKeysForDataFileGroupInternal(dataFileGroup);
754 
755     fileGroupsMetadataStaleGroups.set(ImmutableList.of(dataFileGroup));
756     when(mockSharedFileManager.getFileStatus(fileKeys[0]))
757         .thenReturn(Futures.immediateFuture(FileStatus.DOWNLOAD_COMPLETE));
758     when(mockSharedFileManager.getOnDeviceUri(fileKeys[0]))
759         .thenReturn(Futures.immediateFuture(testUri1));
760     when(mockSharedFileManager.getFileStatus(fileKeys[1]))
761         .thenReturn(Futures.immediateFuture(FileStatus.DOWNLOAD_COMPLETE));
762     when(mockSharedFileManager.getOnDeviceUri(fileKeys[1]))
763         .thenReturn(Futures.immediateFuture(testUri2));
764 
765     when(mockSharedFilesMetadata.getAllFileKeys())
766         .thenReturn(Futures.immediateFuture(Arrays.asList(fileKeys)));
767     when(mockBackend.children(dirFor0p)).thenReturn(Arrays.asList(testUri1, testUri2));
768     expirationHandler.updateExpiration().get();
769 
770     verify(mockFileGroupsMetadata, times(3)).getAllFreshGroups();
771     verify(mockFileGroupsMetadata, times(2)).getAllStaleGroups();
772     verify(mockFileGroupsMetadata).removeAllGroupsWithKeys(ImmutableList.of());
773     verify(mockFileGroupsMetadata).removeAllStaleGroups();
774     verify(mockFileGroupsMetadata).writeStaleGroups(ImmutableList.of(dataFileGroup));
775     verifyNoMoreInteractions(mockFileGroupsMetadata);
776     verify(mockSharedFilesMetadata).getAllFileKeys();
777     verify(mockSharedFileManager).getOnDeviceUri(fileKeys[0]);
778     verify(mockSharedFileManager).getOnDeviceUri(fileKeys[1]);
779     verify(mockBackend).exists(baseDownloadDirectoryUri);
780     verify(mockBackend).children(baseDownloadDirectoryUri);
781     verify(mockBackend).isDirectory(dirForAll);
782     verify(mockBackend).isDirectory(dirFor1p);
783     verify(mockBackend, never()).deleteFile(any());
784     verifyNoMoreInteractions(mockSharedFileManager);
785     verifyNoMoreInteractions(mockEventLogger);
786   }
787 
788   @Test
updateExpiration_noExpiredStaleGroups_notDeleteDir()789   public void updateExpiration_noExpiredStaleGroups_notDeleteDir() throws Exception {
790     Calendar now = new Calendar.Builder().setDate(2018, Calendar.MARCH, 20).build();
791     testClock.set(now.getTimeInMillis());
792 
793     Calendar later = new Calendar.Builder().setDate(2018, Calendar.MARCH, 22).build();
794     long laterTimeSecs = later.getTimeInMillis() / 1000;
795     DataFileGroupInternal dataFileGroup =
796         MddTestUtil.createDataFileGroupInternal(TEST_GROUP_1, 2).toBuilder()
797             .setStaleLifetimeSecs(laterTimeSecs - (now.getTimeInMillis() / 1000))
798             .setBookkeeping(
799                 DataFileGroupBookkeeping.newBuilder().setStaleExpirationDate(laterTimeSecs).build())
800             .build();
801     NewFileKey[] fileKeys = MddTestUtil.createFileKeysForDataFileGroupInternal(dataFileGroup);
802 
803     fileGroupsMetadataStaleGroups.set(ImmutableList.of(dataFileGroup));
804     when(mockSharedFileManager.getFileStatus(fileKeys[0]))
805         .thenReturn(Futures.immediateFuture(FileStatus.DOWNLOAD_COMPLETE));
806     when(mockSharedFileManager.getOnDeviceUri(fileKeys[0]))
807         .thenReturn(Futures.immediateFuture(testDirUri1));
808     when(mockBackend.children(testDirUri1))
809         .thenReturn(Arrays.asList(testDirFileUri1, testDirFileUri2));
810     when(mockSharedFileManager.getFileStatus(fileKeys[1]))
811         .thenReturn(Futures.immediateFuture(FileStatus.DOWNLOAD_COMPLETE));
812     when(mockSharedFileManager.getOnDeviceUri(fileKeys[1]))
813         .thenReturn(Futures.immediateFuture(testUri2));
814 
815     when(mockSharedFilesMetadata.getAllFileKeys())
816         .thenReturn(Futures.immediateFuture(Arrays.asList(fileKeys)));
817     when(mockBackend.children(dirFor0p)).thenReturn(Arrays.asList(testDirUri1, testUri2));
818     expirationHandler.updateExpiration().get();
819 
820     verify(mockFileGroupsMetadata, times(3)).getAllFreshGroups();
821     verify(mockFileGroupsMetadata, times(2)).getAllStaleGroups();
822     verify(mockFileGroupsMetadata).removeAllGroupsWithKeys(ImmutableList.of());
823     verify(mockFileGroupsMetadata).removeAllStaleGroups();
824     verify(mockFileGroupsMetadata).writeStaleGroups(ImmutableList.of(dataFileGroup));
825     verifyNoMoreInteractions(mockFileGroupsMetadata);
826     verify(mockSharedFilesMetadata).getAllFileKeys();
827     verify(mockSharedFileManager).getOnDeviceUri(fileKeys[0]);
828     verify(mockSharedFileManager).getOnDeviceUri(fileKeys[1]);
829     verify(mockBackend).exists(baseDownloadDirectoryUri);
830     verify(mockBackend).children(baseDownloadDirectoryUri);
831     verify(mockBackend).isDirectory(dirForAll);
832     verify(mockBackend).isDirectory(dirFor1p);
833     verify(mockBackend, never()).deleteFile(any());
834     verifyNoMoreInteractions(mockSharedFileManager);
835     verifyNoMoreInteractions(mockEventLogger);
836   }
837 
838   @Test
updateExpiration_expiredStaleGroups_shorterStaleExpirationDate()839   public void updateExpiration_expiredStaleGroups_shorterStaleExpirationDate() throws Exception {
840     Calendar now = new Calendar.Builder().setDate(2018, Calendar.MARCH, 20).build();
841     Calendar later = new Calendar.Builder().setDate(2018, Calendar.MARCH, 22).build();
842     testClock.set(now.getTimeInMillis());
843 
844     Long nowTimeSecs = now.getTimeInMillis() / 1000;
845     DataFileGroupInternal dataFileGroup =
846         MddTestUtil.createDataFileGroupInternal(TEST_GROUP_1, 2).toBuilder()
847             .setStaleLifetimeSecs(0)
848             .setBookkeeping(
849                 DataFileGroupBookkeeping.newBuilder().setStaleExpirationDate(nowTimeSecs).build())
850             .setExpirationDateSecs(later.getTimeInMillis() / 1000)
851             .setBuildId(10)
852             .setVariantId("testVariant")
853             .build();
854     NewFileKey[] fileKeys = MddTestUtil.createFileKeysForDataFileGroupInternal(dataFileGroup);
855 
856     fileGroupsMetadataStaleGroups.set(ImmutableList.of(dataFileGroup));
857     when(mockSharedFileManager.getFileStatus(fileKeys[0]))
858         .thenReturn(Futures.immediateFuture(FileStatus.DOWNLOAD_COMPLETE));
859     when(mockSharedFileManager.getOnDeviceUri(fileKeys[0]))
860         .thenReturn(Futures.immediateFuture(testUri1));
861     when(mockSharedFileManager.getFileStatus(fileKeys[1]))
862         .thenReturn(Futures.immediateFuture(FileStatus.DOWNLOAD_COMPLETE));
863     when(mockSharedFileManager.getOnDeviceUri(fileKeys[1]))
864         .thenReturn(Futures.immediateFuture(testUri2));
865     when(mockSharedFilesMetadata.getAllFileKeys())
866         .thenReturn(Futures.immediateFuture(Arrays.asList(fileKeys)));
867     when(mockBackend.children(dirFor0p)).thenReturn(Arrays.asList(testUri1, testUri2));
868 
869     expirationHandler.updateExpiration().get();
870 
871     verify(mockFileGroupsMetadata, times(3)).getAllFreshGroups();
872     verify(mockFileGroupsMetadata, times(2)).getAllStaleGroups();
873     verify(mockFileGroupsMetadata).removeAllGroupsWithKeys(ImmutableList.of());
874     verify(mockFileGroupsMetadata).removeAllStaleGroups();
875     verify(mockFileGroupsMetadata).writeStaleGroups(ImmutableList.of());
876     verifyNoMoreInteractions(mockFileGroupsMetadata);
877     verify(mockSharedFilesMetadata).getAllFileKeys();
878     verify(mockSharedFileManager).removeFileEntry(fileKeys[0]);
879     verify(mockSharedFileManager).removeFileEntry(fileKeys[1]);
880     verifyNoMoreInteractions(mockSharedFileManager);
881     verify(mockBackend).exists(baseDownloadDirectoryUri);
882     verify(mockBackend).children(baseDownloadDirectoryUri);
883     verify(mockBackend).isDirectory(testUri1);
884     verify(mockBackend).isDirectory(testUri2);
885     verify(mockBackend).deleteFile(testUri1);
886     verify(mockBackend).deleteFile(testUri2);
887     verify(mockEventLogger)
888         .logEventSampled(
889             MddClientEvent.Code.EVENT_CODE_UNSPECIFIED,
890             dataFileGroup.getGroupName(),
891             dataFileGroup.getFileGroupVersionNumber(),
892             dataFileGroup.getBuildId(),
893             dataFileGroup.getVariantId());
894     verify(mockEventLogger).logMddDataDownloadFileExpirationEvent(0, 2);
895     verify(mockEventLogger).logMddDataDownloadFileExpirationEvent(0, 2);
896     verifyNoMoreInteractions(mockEventLogger);
897   }
898 
899   @Test
updateExpiration_expiredStaleGroups_shorterExpirationDate()900   public void updateExpiration_expiredStaleGroups_shorterExpirationDate() throws Exception {
901     Calendar now = new Calendar.Builder().setDate(2018, Calendar.MARCH, 20).build();
902     Calendar later = new Calendar.Builder().setDate(2018, Calendar.MARCH, 22).build();
903     testClock.set(now.getTimeInMillis());
904 
905     Long nowTimeSecs = now.getTimeInMillis() / 1000;
906     DataFileGroupInternal dataFileGroup =
907         MddTestUtil.createDataFileGroupInternal(TEST_GROUP_1, 2).toBuilder()
908             .setBookkeeping(
909                 DataFileGroupBookkeeping.newBuilder()
910                     .setStaleExpirationDate(later.getTimeInMillis() / 1000)
911                     .build())
912             .setExpirationDateSecs(nowTimeSecs)
913             .setBuildId(10)
914             .setVariantId("testVariant")
915             .build();
916     NewFileKey[] fileKeys = MddTestUtil.createFileKeysForDataFileGroupInternal(dataFileGroup);
917 
918     fileGroupsMetadataStaleGroups.set(ImmutableList.of(dataFileGroup));
919 
920     when(mockSharedFileManager.getFileStatus(fileKeys[0]))
921         .thenReturn(Futures.immediateFuture(FileStatus.DOWNLOAD_COMPLETE));
922     when(mockSharedFileManager.getOnDeviceUri(fileKeys[0]))
923         .thenReturn(Futures.immediateFuture(testUri1));
924     when(mockSharedFileManager.getFileStatus(fileKeys[1]))
925         .thenReturn(Futures.immediateFuture(FileStatus.DOWNLOAD_COMPLETE));
926     when(mockSharedFileManager.getOnDeviceUri(fileKeys[1]))
927         .thenReturn(Futures.immediateFuture(testUri2));
928     when(mockSharedFilesMetadata.getAllFileKeys())
929         .thenReturn(Futures.immediateFuture(Arrays.asList(fileKeys)));
930     when(mockBackend.children(dirFor0p)).thenReturn(Arrays.asList(testUri1, testUri2));
931 
932     expirationHandler.updateExpiration().get();
933 
934     verify(mockFileGroupsMetadata, times(3)).getAllFreshGroups();
935     verify(mockFileGroupsMetadata, times(2)).getAllStaleGroups();
936     verify(mockFileGroupsMetadata).removeAllGroupsWithKeys(ImmutableList.of());
937     verify(mockFileGroupsMetadata).removeAllStaleGroups();
938     verify(mockFileGroupsMetadata).writeStaleGroups(ImmutableList.of());
939     verifyNoMoreInteractions(mockFileGroupsMetadata);
940     verify(mockSharedFilesMetadata).getAllFileKeys();
941     verify(mockSharedFileManager).removeFileEntry(fileKeys[0]);
942     verify(mockSharedFileManager).removeFileEntry(fileKeys[1]);
943     verifyNoMoreInteractions(mockSharedFileManager);
944     verify(mockBackend).exists(baseDownloadDirectoryUri);
945     verify(mockBackend).children(baseDownloadDirectoryUri);
946     verify(mockBackend).isDirectory(testUri1);
947     verify(mockBackend).isDirectory(testUri2);
948     verify(mockBackend).deleteFile(testUri1);
949     verify(mockBackend).deleteFile(testUri2);
950     verify(mockEventLogger)
951         .logEventSampled(
952             MddClientEvent.Code.EVENT_CODE_UNSPECIFIED,
953             dataFileGroup.getGroupName(),
954             dataFileGroup.getFileGroupVersionNumber(),
955             dataFileGroup.getBuildId(),
956             dataFileGroup.getVariantId());
957     verify(mockEventLogger).logMddDataDownloadFileExpirationEvent(0, 2);
958     verify(mockEventLogger).logMddDataDownloadFileExpirationEvent(0, 2);
959     verifyNoMoreInteractions(mockEventLogger);
960   }
961 
962   @Test
updateExpiration_expiredStaleGroups_deleteExpiredDir()963   public void updateExpiration_expiredStaleGroups_deleteExpiredDir() throws Exception {
964     Calendar now = new Calendar.Builder().setDate(2018, Calendar.MARCH, 20).build();
965     Calendar later = new Calendar.Builder().setDate(2018, Calendar.MARCH, 22).build();
966     testClock.set(now.getTimeInMillis());
967     long nowTimeSecs = now.getTimeInMillis() / 1000;
968     DataFileGroupInternal dataFileGroup =
969         MddTestUtil.createDataFileGroupInternal(TEST_GROUP_1, 2).toBuilder()
970             .setBookkeeping(
971                 DataFileGroupBookkeeping.newBuilder()
972                     .setStaleExpirationDate(later.getTimeInMillis() / 1000)
973                     .build())
974             .setExpirationDateSecs(nowTimeSecs)
975             .build();
976     NewFileKey[] fileKeys = MddTestUtil.createFileKeysForDataFileGroupInternal(dataFileGroup);
977 
978     fileGroupsMetadataStaleGroups.set(ImmutableList.of(dataFileGroup));
979 
980     when(mockSharedFileManager.getFileStatus(fileKeys[0]))
981         .thenReturn(Futures.immediateFuture(FileStatus.DOWNLOAD_COMPLETE));
982     when(mockSharedFileManager.getOnDeviceUri(fileKeys[0]))
983         .thenReturn(Futures.immediateFuture(testDirUri1));
984     when(mockSharedFileManager.getFileStatus(fileKeys[1]))
985         .thenReturn(Futures.immediateFuture(FileStatus.DOWNLOAD_COMPLETE));
986     when(mockSharedFileManager.getOnDeviceUri(fileKeys[1]))
987         .thenReturn(Futures.immediateFuture(testUri2));
988     when(mockSharedFilesMetadata.getAllFileKeys())
989         .thenReturn(Futures.immediateFuture(Arrays.asList(fileKeys)));
990     when(mockBackend.children(dirFor1p)).thenReturn(Arrays.asList(testDirUri1, testUri2));
991     when(mockBackend.children(testDirUri1))
992         .thenReturn(Arrays.asList(testDirFileUri1, testDirFileUri2));
993 
994     expirationHandler.updateExpiration().get();
995 
996     verify(mockFileGroupsMetadata, times(3)).getAllFreshGroups();
997     verify(mockFileGroupsMetadata, times(2)).getAllStaleGroups();
998     verify(mockFileGroupsMetadata).removeAllGroupsWithKeys(ImmutableList.of());
999     verify(mockFileGroupsMetadata).removeAllStaleGroups();
1000     verify(mockFileGroupsMetadata).writeStaleGroups(ImmutableList.of());
1001     verifyNoMoreInteractions(mockFileGroupsMetadata);
1002     verify(mockSharedFilesMetadata).getAllFileKeys();
1003     verify(mockSharedFileManager).removeFileEntry(fileKeys[0]);
1004     verify(mockSharedFileManager).removeFileEntry(fileKeys[1]);
1005     verifyNoMoreInteractions(mockSharedFileManager);
1006     verify(mockBackend).exists(baseDownloadDirectoryUri);
1007     verify(mockBackend).children(baseDownloadDirectoryUri);
1008     verify(mockBackend).isDirectory(testDirUri1);
1009     verify(mockBackend).isDirectory(testDirFileUri1);
1010     verify(mockBackend).isDirectory(testDirFileUri2);
1011     verify(mockBackend).isDirectory(testUri2);
1012     verify(mockBackend).deleteFile(testDirFileUri1);
1013     verify(mockBackend).deleteFile(testDirFileUri2);
1014     verify(mockBackend).deleteFile(testUri2);
1015     verify(mockEventLogger)
1016         .logEventSampled(
1017             MddClientEvent.Code.EVENT_CODE_UNSPECIFIED,
1018             dataFileGroup.getGroupName(),
1019             dataFileGroup.getFileGroupVersionNumber(),
1020             dataFileGroup.getBuildId(),
1021             dataFileGroup.getVariantId());
1022     verify(mockEventLogger).logMddDataDownloadFileExpirationEvent(0, 3);
1023     verify(mockEventLogger).logMddDataDownloadFileExpirationEvent(0, 2);
1024     verifyNoMoreInteractions(mockEventLogger);
1025   }
1026 
1027   @Test
updateExpiration_sharedFiles_staleGroupSoonerExpiration_activeGroupLaterExpiration()1028   public void updateExpiration_sharedFiles_staleGroupSoonerExpiration_activeGroupLaterExpiration()
1029       throws Exception {
1030     Calendar now = new Calendar.Builder().setDate(2018, Calendar.MARCH, 20).build();
1031     testClock.set(now.getTimeInMillis());
1032 
1033     DataFile dataFile = MddTestUtil.createDataFile("file", 0);
1034     NewFileKey fileKey =
1035         SharedFilesMetadata.createKeyFromDataFile(
1036             dataFile, AllowedReaders.ONLY_GOOGLE_PLAY_SERVICES);
1037 
1038     // The active group expires 30 days from now.
1039     Calendar later = new Calendar.Builder().setDate(2018, Calendar.APRIL, 20).build();
1040     long laterTimeSecs = later.getTimeInMillis() / 1000;
1041     DataFileGroupInternal activeGroup =
1042         DataFileGroupInternal.newBuilder()
1043             .setGroupName(TEST_GROUP_1)
1044             .addFile(dataFile)
1045             .setAllowedReadersEnum(AllowedReaders.ONLY_GOOGLE_PLAY_SERVICES)
1046             .setExpirationDateSecs(laterTimeSecs)
1047             .build();
1048 
1049     // The stale group expires 2 days from now.
1050     Calendar sooner = new Calendar.Builder().setDate(2018, Calendar.MARCH, 22).build();
1051     DataFileGroupInternal staleGroup =
1052         DataFileGroupInternal.newBuilder()
1053             .setGroupName(TEST_GROUP_2)
1054             .addFile(dataFile)
1055             .setAllowedReadersEnum(AllowedReaders.ONLY_GOOGLE_PLAY_SERVICES)
1056             .setBookkeeping(
1057                 DataFileGroupBookkeeping.newBuilder()
1058                     .setStaleExpirationDate(sooner.getTimeInMillis() / 1000)
1059                     .build())
1060             .setStaleLifetimeSecs((sooner.getTimeInMillis() - now.getTimeInMillis()) / 1000)
1061             .build();
1062 
1063     List<GroupKeyAndGroup> groups = Arrays.asList(GroupKeyAndGroup.create(TEST_KEY_1, activeGroup));
1064     when(mockFileGroupsMetadata.getAllFreshGroups()).thenReturn(Futures.immediateFuture(groups));
1065 
1066     fileGroupsMetadataStaleGroups.set(ImmutableList.of(staleGroup));
1067 
1068     when(mockSharedFileManager.getFileStatus(fileKey))
1069         .thenReturn(Futures.immediateFuture(FileStatus.DOWNLOAD_COMPLETE));
1070     when(mockSharedFileManager.getOnDeviceUri(fileKey))
1071         .thenReturn(Futures.immediateFuture(testUri1));
1072     when(mockSharedFilesMetadata.getAllFileKeys())
1073         .thenReturn(Futures.immediateFuture(Collections.singletonList(fileKey)));
1074     setUpDirectoryMock(dirFor1p, Arrays.asList(testUri1));
1075     setUpFileMock(testUri1, 100);
1076 
1077     expirationHandler.updateExpiration().get();
1078 
1079     verify(mockFileGroupsMetadata, times(3)).getAllFreshGroups();
1080     verify(mockFileGroupsMetadata, times(2)).getAllStaleGroups();
1081     verify(mockFileGroupsMetadata).removeAllGroupsWithKeys(ImmutableList.of());
1082     verify(mockFileGroupsMetadata).removeAllStaleGroups();
1083     verify(mockFileGroupsMetadata).writeStaleGroups(ImmutableList.of(staleGroup));
1084     verifyNoMoreInteractions(mockFileGroupsMetadata);
1085     verify(mockSharedFilesMetadata).getAllFileKeys();
1086     verify(mockSharedFileManager).getOnDeviceUri(fileKey);
1087     verifyNoMoreInteractions(mockSharedFileManager);
1088     verifyNoMoreInteractions(mockEventLogger);
1089     verify(mockBackend).exists(baseDownloadDirectoryUri);
1090     verify(mockBackend).children(baseDownloadDirectoryUri);
1091     verify(mockBackend).isDirectory(dirForAll);
1092     verify(mockBackend, never()).deleteFile(any());
1093   }
1094 
1095   @Test
updateExpiration_sharedFiles_staleGroupLaterExpiration_activeGroupSoonerExpiration()1096   public void updateExpiration_sharedFiles_staleGroupLaterExpiration_activeGroupSoonerExpiration()
1097       throws Exception {
1098     Calendar now = new Calendar.Builder().setDate(2018, Calendar.MARCH, 20).build();
1099     testClock.set(now.getTimeInMillis());
1100 
1101     DataFile dataFile = MddTestUtil.createDataFile("file", 0);
1102     NewFileKey fileKey =
1103         SharedFilesMetadata.createKeyFromDataFile(
1104             dataFile, AllowedReaders.ONLY_GOOGLE_PLAY_SERVICES);
1105 
1106     // The active group expires 1 day from now.
1107     Calendar sooner = new Calendar.Builder().setDate(2018, Calendar.MARCH, 21).build();
1108     DataFileGroupInternal activeGroup =
1109         DataFileGroupInternal.newBuilder()
1110             .setGroupName(TEST_GROUP_1)
1111             .addFile(dataFile)
1112             .setAllowedReadersEnum(AllowedReaders.ONLY_GOOGLE_PLAY_SERVICES)
1113             .setExpirationDateSecs(sooner.getTimeInMillis() / 1000)
1114             .build();
1115 
1116     // The stale group expires 2 days from now.
1117     Calendar later = new Calendar.Builder().setDate(2018, Calendar.MARCH, 22).build();
1118     long laterTimeSecs = later.getTimeInMillis() / 1000;
1119     DataFileGroupInternal staleGroup =
1120         DataFileGroupInternal.newBuilder()
1121             .setGroupName(TEST_GROUP_2)
1122             .addFile(dataFile)
1123             .setAllowedReadersEnum(AllowedReaders.ONLY_GOOGLE_PLAY_SERVICES)
1124             .setBookkeeping(
1125                 DataFileGroupBookkeeping.newBuilder().setStaleExpirationDate(laterTimeSecs).build())
1126             .setStaleLifetimeSecs(laterTimeSecs - now.getTimeInMillis() / 1000)
1127             .build();
1128 
1129     List<GroupKeyAndGroup> groups = Arrays.asList(GroupKeyAndGroup.create(TEST_KEY_1, activeGroup));
1130     when(mockFileGroupsMetadata.getAllFreshGroups()).thenReturn(Futures.immediateFuture(groups));
1131 
1132     fileGroupsMetadataStaleGroups.set(ImmutableList.of(staleGroup));
1133 
1134     when(mockSharedFileManager.getFileStatus(fileKey))
1135         .thenReturn(Futures.immediateFuture(FileStatus.DOWNLOAD_COMPLETE));
1136     when(mockSharedFileManager.getOnDeviceUri(fileKey))
1137         .thenReturn(Futures.immediateFuture(testUri1));
1138     when(mockSharedFilesMetadata.getAllFileKeys())
1139         .thenReturn(Futures.immediateFuture(Collections.singletonList(fileKey)));
1140     when(mockBackend.children(dirFor0p)).thenReturn(Arrays.asList(testUri1));
1141 
1142     expirationHandler.updateExpiration().get();
1143 
1144     verify(mockFileGroupsMetadata, times(3)).getAllFreshGroups();
1145     verify(mockFileGroupsMetadata, times(2)).getAllStaleGroups();
1146     verify(mockFileGroupsMetadata).removeAllGroupsWithKeys(ImmutableList.of());
1147     verify(mockFileGroupsMetadata).removeAllStaleGroups();
1148     verify(mockFileGroupsMetadata).writeStaleGroups(ImmutableList.of(staleGroup));
1149     verifyNoMoreInteractions(mockFileGroupsMetadata);
1150     verify(mockSharedFilesMetadata).getAllFileKeys();
1151     verify(mockSharedFileManager).getOnDeviceUri(fileKey);
1152     verifyNoMoreInteractions(mockSharedFileManager);
1153     verifyNoMoreInteractions(mockEventLogger);
1154     verify(mockBackend).exists(baseDownloadDirectoryUri);
1155     verify(mockBackend).children(baseDownloadDirectoryUri);
1156     verify(mockBackend).isDirectory(dirForAll);
1157     verify(mockBackend, never()).deleteFile(any());
1158   }
1159 
1160   @Test
updateExpiration_sharedFiles_staleGroup_activeGroupNoExpiration()1161   public void updateExpiration_sharedFiles_staleGroup_activeGroupNoExpiration() throws Exception {
1162     Calendar now = new Calendar.Builder().setDate(2018, Calendar.MARCH, 20).build();
1163     testClock.set(now.getTimeInMillis());
1164 
1165     DataFile dataFile = MddTestUtil.createDataFile("file", 0);
1166     NewFileKey fileKey =
1167         SharedFilesMetadata.createKeyFromDataFile(
1168             dataFile, AllowedReaders.ONLY_GOOGLE_PLAY_SERVICES);
1169 
1170     // The active group never expires.
1171     DataFileGroupInternal activeGroup =
1172         DataFileGroupInternal.newBuilder()
1173             .setGroupName(TEST_GROUP_1)
1174             .addFile(dataFile)
1175             .setAllowedReadersEnum(AllowedReaders.ONLY_GOOGLE_PLAY_SERVICES)
1176             .build();
1177 
1178     // The stale group expires 2 days from now.
1179     Calendar sooner = new Calendar.Builder().setDate(2018, Calendar.MARCH, 22).build();
1180     DataFileGroupInternal staleGroup =
1181         DataFileGroupInternal.newBuilder()
1182             .setGroupName(TEST_GROUP_2)
1183             .addFile(dataFile)
1184             .setAllowedReadersEnum(AllowedReaders.ONLY_GOOGLE_PLAY_SERVICES)
1185             .setBookkeeping(
1186                 DataFileGroupBookkeeping.newBuilder()
1187                     .setStaleExpirationDate(sooner.getTimeInMillis() / 1000)
1188                     .build())
1189             .setStaleLifetimeSecs((sooner.getTimeInMillis() - now.getTimeInMillis()) / 1000)
1190             .build();
1191 
1192     List<GroupKeyAndGroup> groups = Arrays.asList(GroupKeyAndGroup.create(TEST_KEY_1, activeGroup));
1193     when(mockFileGroupsMetadata.getAllFreshGroups()).thenReturn(Futures.immediateFuture(groups));
1194 
1195     fileGroupsMetadataStaleGroups.set(ImmutableList.of(staleGroup));
1196 
1197     when(mockSharedFileManager.getFileStatus(fileKey))
1198         .thenReturn(Futures.immediateFuture(FileStatus.DOWNLOAD_COMPLETE));
1199     when(mockSharedFileManager.getOnDeviceUri(fileKey))
1200         .thenReturn(Futures.immediateFuture(testUri1));
1201     when(mockSharedFilesMetadata.getAllFileKeys())
1202         .thenReturn(Futures.immediateFuture(Collections.singletonList(fileKey)));
1203     when(mockBackend.children(dirFor0p)).thenReturn(Arrays.asList(testUri1));
1204 
1205     expirationHandler.updateExpiration().get();
1206 
1207     verify(mockFileGroupsMetadata, times(3)).getAllFreshGroups();
1208     verify(mockFileGroupsMetadata, times(2)).getAllStaleGroups();
1209     verify(mockFileGroupsMetadata).removeAllGroupsWithKeys(ImmutableList.of());
1210     verify(mockFileGroupsMetadata).removeAllStaleGroups();
1211     verify(mockFileGroupsMetadata).writeStaleGroups(ImmutableList.of(staleGroup));
1212     verifyNoMoreInteractions(mockFileGroupsMetadata);
1213     verify(mockSharedFilesMetadata).getAllFileKeys();
1214     verify(mockSharedFileManager).getOnDeviceUri(fileKey);
1215     verifyNoMoreInteractions(mockSharedFileManager);
1216     verifyNoMoreInteractions(mockEventLogger);
1217     verify(mockBackend).exists(baseDownloadDirectoryUri);
1218     verify(mockBackend).children(baseDownloadDirectoryUri);
1219     verify(mockBackend).isDirectory(dirForAll);
1220     verify(mockBackend, never()).deleteFile(any());
1221   }
1222 
1223   @Test
updateExpiration_sharedFiles_staleGroupNonExpired_activeGroupExpired()1224   public void updateExpiration_sharedFiles_staleGroupNonExpired_activeGroupExpired()
1225       throws Exception {
1226     Calendar now = new Calendar.Builder().setDate(2018, Calendar.MARCH, 20).build();
1227     testClock.set(now.getTimeInMillis());
1228 
1229     DataFile dataFile = MddTestUtil.createDataFile("file", 0);
1230     NewFileKey fileKey =
1231         SharedFilesMetadata.createKeyFromDataFile(
1232             dataFile, AllowedReaders.ONLY_GOOGLE_PLAY_SERVICES);
1233 
1234     // The active group is expired.
1235     Calendar sooner = new Calendar.Builder().setDate(2018, Calendar.MARCH, 19).build();
1236     DataFileGroupInternal activeGroup =
1237         DataFileGroupInternal.newBuilder()
1238             .setGroupName(TEST_GROUP_1)
1239             .addFile(dataFile)
1240             .setAllowedReadersEnum(AllowedReaders.ONLY_GOOGLE_PLAY_SERVICES)
1241             .setExpirationDateSecs(sooner.getTimeInMillis() / 1000)
1242             .build();
1243 
1244     // The stale group expires 2 days from now.
1245     Calendar later = new Calendar.Builder().setDate(2018, Calendar.MARCH, 22).build();
1246     long laterTimeSecs = later.getTimeInMillis() / 1000;
1247     DataFileGroupInternal staleGroup =
1248         DataFileGroupInternal.newBuilder()
1249             .setGroupName(TEST_GROUP_2)
1250             .addFile(dataFile)
1251             .setAllowedReadersEnum(AllowedReaders.ONLY_GOOGLE_PLAY_SERVICES)
1252             .setBookkeeping(
1253                 DataFileGroupBookkeeping.newBuilder().setStaleExpirationDate(laterTimeSecs).build())
1254             .setStaleLifetimeSecs(laterTimeSecs - now.getTimeInMillis() / 1000)
1255             .build();
1256 
1257     List<GroupKeyAndGroup> groups = Arrays.asList(GroupKeyAndGroup.create(TEST_KEY_1, activeGroup));
1258     when(mockFileGroupsMetadata.getAllFreshGroups()).thenReturn(Futures.immediateFuture(groups));
1259 
1260     fileGroupsMetadataStaleGroups.set(ImmutableList.of(staleGroup));
1261 
1262     when(mockSharedFileManager.getFileStatus(fileKey))
1263         .thenReturn(Futures.immediateFuture(FileStatus.DOWNLOAD_COMPLETE));
1264     when(mockSharedFileManager.getOnDeviceUri(fileKey))
1265         .thenReturn(Futures.immediateFuture(testUri1));
1266     when(mockSharedFilesMetadata.getAllFileKeys())
1267         .thenReturn(Futures.immediateFuture(Collections.singletonList(fileKey)));
1268     when(mockBackend.children(dirFor0p)).thenReturn(Arrays.asList(testUri1));
1269 
1270     expirationHandler.updateExpiration().get();
1271 
1272     verify(mockFileGroupsMetadata, times(3)).getAllFreshGroups();
1273     verify(mockFileGroupsMetadata, times(2)).getAllStaleGroups();
1274     verify(mockFileGroupsMetadata).removeAllGroupsWithKeys(Arrays.asList(TEST_KEY_1));
1275     verify(mockFileGroupsMetadata).removeAllStaleGroups();
1276     verify(mockFileGroupsMetadata).writeStaleGroups(ImmutableList.of(staleGroup));
1277     verifyNoMoreInteractions(mockFileGroupsMetadata);
1278     verify(mockSharedFilesMetadata).getAllFileKeys();
1279     verify(mockSharedFileManager).getOnDeviceUri(fileKey);
1280     verifyNoMoreInteractions(mockSharedFileManager);
1281     verify(mockEventLogger)
1282         .logEventSampled(
1283             MddClientEvent.Code.EVENT_CODE_UNSPECIFIED,
1284             activeGroup.getGroupName(),
1285             activeGroup.getFileGroupVersionNumber(),
1286             activeGroup.getBuildId(),
1287             activeGroup.getVariantId());
1288     verifyNoMoreInteractions(mockEventLogger);
1289     verify(mockBackend).exists(baseDownloadDirectoryUri);
1290     verify(mockBackend).children(baseDownloadDirectoryUri);
1291     verify(mockBackend).isDirectory(dirForAll);
1292     verify(mockBackend, never()).deleteFile(any());
1293   }
1294 
1295   @Test
updateExpiration_multipleExpiredGroups()1296   public void updateExpiration_multipleExpiredGroups() throws Exception {
1297     Calendar now = new Calendar.Builder().setDate(2018, Calendar.MARCH, 20).build();
1298     testClock.set(now.getTimeInMillis());
1299 
1300     DataFile dataFile = MddTestUtil.createDataFile("file", 0);
1301     NewFileKey fileKey =
1302         SharedFilesMetadata.createKeyFromDataFile(
1303             dataFile, AllowedReaders.ONLY_GOOGLE_PLAY_SERVICES);
1304 
1305     // DAY 0 : The firstGroup is active and will expire in 30 days.
1306     Calendar earlier = new Calendar.Builder().setDate(2018, Calendar.MARCH, 19).build();
1307     Long earlierSecs = earlier.getTimeInMillis() / 1000;
1308     DataFileGroupInternal firstGroup =
1309         DataFileGroupInternal.newBuilder()
1310             .setGroupName(TEST_GROUP_1)
1311             .addFile(dataFile)
1312             .setAllowedReadersEnum(AllowedReaders.ONLY_GOOGLE_PLAY_SERVICES)
1313             .setExpirationDateSecs(earlierSecs)
1314             .build();
1315 
1316     Calendar earliest = new Calendar.Builder().setDate(2018, Calendar.MARCH, 18).build();
1317     Long earliestSecs = earliest.getTimeInMillis() / 1000;
1318     DataFileGroupInternal secondGroup =
1319         DataFileGroupInternal.newBuilder()
1320             .setGroupName(TEST_GROUP_2)
1321             .addFile(dataFile)
1322             .setAllowedReadersEnum(AllowedReaders.ONLY_GOOGLE_PLAY_SERVICES)
1323             .setExpirationDateSecs(earliestSecs)
1324             .build();
1325 
1326     List<GroupKeyAndGroup> groups = Arrays.asList(GroupKeyAndGroup.create(TEST_KEY_1, firstGroup));
1327     when(mockFileGroupsMetadata.getAllFreshGroups())
1328         .thenReturn(Futures.immediateFuture(groups))
1329         .thenReturn(Futures.immediateFuture(ImmutableList.of()));
1330 
1331     fileGroupsMetadataStaleGroups.set(ImmutableList.of(secondGroup));
1332 
1333     when(mockSharedFileManager.getFileStatus(fileKey))
1334         .thenReturn(Futures.immediateFuture(FileStatus.DOWNLOAD_COMPLETE));
1335     when(mockSharedFileManager.getOnDeviceUri(fileKey))
1336         .thenReturn(Futures.immediateFuture(testUri1));
1337     when(mockSharedFilesMetadata.getAllFileKeys())
1338         .thenReturn(Futures.immediateFuture(Collections.singletonList(fileKey)));
1339     when(mockBackend.children(dirFor0p))
1340         .thenReturn(Arrays.asList(testUri1, testUri3 /*an old file left on device somehow*/));
1341 
1342     expirationHandler.updateExpiration().get();
1343 
1344     verify(mockFileGroupsMetadata, times(3)).getAllFreshGroups();
1345     verify(mockFileGroupsMetadata, times(2)).getAllStaleGroups();
1346     verify(mockFileGroupsMetadata).removeAllGroupsWithKeys(Arrays.asList(TEST_KEY_1));
1347     verify(mockFileGroupsMetadata).removeAllStaleGroups();
1348     verify(mockFileGroupsMetadata).writeStaleGroups(ImmutableList.of());
1349     verifyNoMoreInteractions(mockFileGroupsMetadata);
1350     verify(mockSharedFilesMetadata).getAllFileKeys();
1351     // unsubscribe should only be called once even though two groups referencing fileKey have both
1352     // expired.
1353     verify(mockSharedFileManager).removeFileEntry(fileKey);
1354     verifyNoMoreInteractions(mockSharedFileManager);
1355     verify(mockBackend).exists(baseDownloadDirectoryUri);
1356     verify(mockBackend).children(baseDownloadDirectoryUri);
1357     verify(mockBackend).isDirectory(testUri1);
1358     verify(mockBackend).deleteFile(testUri1);
1359     verify(mockBackend).isDirectory(testUri3);
1360     verify(mockBackend).deleteFile(testUri3);
1361   }
1362 
1363   @Test
updateExpiration_multipleTimes_withGroupTransitions()1364   public void updateExpiration_multipleTimes_withGroupTransitions() throws Exception {
1365     Calendar now = new Calendar.Builder().setDate(2018, Calendar.MARCH, 20).build();
1366     testClock.set(now.getTimeInMillis());
1367 
1368     DataFile dataFile = MddTestUtil.createDataFile("file", 0);
1369     NewFileKey fileKey =
1370         SharedFilesMetadata.createKeyFromDataFile(
1371             dataFile, AllowedReaders.ONLY_GOOGLE_PLAY_SERVICES);
1372 
1373     // DAY 0 : The firstGroup is active and will expire in 30 days.
1374     Calendar firstExpiration = new Calendar.Builder().setDate(2018, Calendar.APRIL, 20).build();
1375     Long firstExpirationSecs = firstExpiration.getTimeInMillis() / 1000;
1376     DataFileGroupInternal.Builder firstGroup =
1377         DataFileGroupInternal.newBuilder()
1378             .setGroupName(TEST_GROUP_1)
1379             .addFile(dataFile)
1380             .setAllowedReadersEnum(AllowedReaders.ONLY_GOOGLE_PLAY_SERVICES)
1381             .setExpirationDateSecs(firstExpirationSecs);
1382 
1383     List<GroupKeyAndGroup> groups =
1384         Arrays.asList(GroupKeyAndGroup.create(TEST_KEY_1, firstGroup.build()));
1385     when(mockFileGroupsMetadata.getAllFreshGroups()).thenReturn(Futures.immediateFuture(groups));
1386 
1387     when(mockSharedFileManager.getFileStatus(fileKey))
1388         .thenReturn(Futures.immediateFuture(FileStatus.DOWNLOAD_COMPLETE));
1389     when(mockSharedFileManager.getOnDeviceUri(fileKey))
1390         .thenReturn(Futures.immediateFuture(testUri1));
1391     when(mockSharedFilesMetadata.getAllFileKeys())
1392         .thenReturn(Futures.immediateFuture(Collections.singletonList(fileKey)));
1393     when(mockBackend.children(dirFor0p)).thenReturn(Arrays.asList(testUri1));
1394 
1395     expirationHandler.updateExpiration().get();
1396 
1397     verify(mockFileGroupsMetadata, times(3)).getAllFreshGroups();
1398     verify(mockFileGroupsMetadata, times(2)).getAllStaleGroups();
1399     verify(mockFileGroupsMetadata).removeAllGroupsWithKeys(ImmutableList.of());
1400     verify(mockFileGroupsMetadata).removeAllStaleGroups();
1401     verify(mockFileGroupsMetadata).writeStaleGroups(ImmutableList.of());
1402     verifyNoMoreInteractions(mockFileGroupsMetadata);
1403     verify(mockSharedFilesMetadata).getAllFileKeys();
1404     verify(mockSharedFileManager).getOnDeviceUri(fileKey);
1405     verify(mockBackend).exists(baseDownloadDirectoryUri);
1406     verify(mockBackend).children(baseDownloadDirectoryUri);
1407     verify(mockBackend).isDirectory(dirForAll);
1408     verify(mockBackend, never()).deleteFile(any());
1409     verifyNoMoreInteractions(mockSharedFileManager);
1410 
1411     // DAY 1 : firstGroup becomes stale and should expire in 2 days.
1412     now = new Calendar.Builder().setDate(2018, Calendar.MARCH, 21).build();
1413     testClock.set(now.getTimeInMillis());
1414 
1415     Calendar firstStaleExpiration =
1416         new Calendar.Builder().setDate(2018, Calendar.MARCH, 23).build();
1417     long firstStaleExpirationSecs = firstStaleExpiration.getTimeInMillis() / 1000;
1418     firstGroup
1419         .setBookkeeping(
1420             DataFileGroupBookkeeping.newBuilder()
1421                 .setStaleExpirationDate(firstStaleExpirationSecs)
1422                 .build())
1423         .setStaleLifetimeSecs(firstStaleExpirationSecs - (now.getTimeInMillis() / 1000));
1424 
1425     when(mockFileGroupsMetadata.getAllFreshGroups())
1426         .thenReturn(Futures.immediateFuture(ImmutableList.of()));
1427 
1428     fileGroupsMetadataStaleGroups.set(ImmutableList.of(firstGroup.build()));
1429 
1430     expirationHandler.updateExpiration().get();
1431 
1432     verify(mockFileGroupsMetadata, times(6)).getAllFreshGroups();
1433     verify(mockFileGroupsMetadata, times(4)).getAllStaleGroups();
1434     verify(mockFileGroupsMetadata, times(2)).removeAllGroupsWithKeys(ImmutableList.of());
1435     verify(mockFileGroupsMetadata, times(2)).removeAllStaleGroups();
1436     verify(mockFileGroupsMetadata).writeStaleGroups(ImmutableList.of(firstGroup.build()));
1437     verifyNoMoreInteractions(mockFileGroupsMetadata);
1438     verify(mockSharedFileManager, times(2)).getOnDeviceUri(fileKey);
1439     verify(mockSharedFilesMetadata, times(2)).getAllFileKeys();
1440     verifyNoMoreInteractions(mockSharedFileManager);
1441     verify(mockBackend, times(2)).exists(baseDownloadDirectoryUri);
1442     verify(mockBackend, times(2)).children(baseDownloadDirectoryUri);
1443     verify(mockBackend, times(2)).isDirectory(dirFor1p);
1444     verify(mockBackend, never()).deleteFile(any());
1445 
1446     // DAY 2 : secondGroup arrives and requests the shared file.
1447     now = new Calendar.Builder().setDate(2018, Calendar.MARCH, 22).build();
1448     testClock.set(now.getTimeInMillis());
1449 
1450     Calendar secondExpiration = new Calendar.Builder().setDate(2018, Calendar.APRIL, 22).build();
1451     long secondExpirationSecs = secondExpiration.getTimeInMillis() / 1000;
1452     DataFileGroupInternal secondGroup =
1453         DataFileGroupInternal.newBuilder()
1454             .setGroupName(TEST_GROUP_2)
1455             .addFile(dataFile)
1456             .setAllowedReadersEnum(AllowedReaders.ONLY_GOOGLE_PLAY_SERVICES)
1457             .setExpirationDateSecs(secondExpirationSecs)
1458             .build();
1459 
1460     groups = Arrays.asList(GroupKeyAndGroup.create(TEST_KEY_2, secondGroup));
1461     when(mockFileGroupsMetadata.getAllFreshGroups()).thenReturn(Futures.immediateFuture(groups));
1462 
1463     expirationHandler.updateExpiration().get();
1464 
1465     verify(mockFileGroupsMetadata, times(9)).getAllFreshGroups();
1466     verify(mockFileGroupsMetadata, times(6)).getAllStaleGroups();
1467     verify(mockFileGroupsMetadata, times(3)).removeAllGroupsWithKeys(ImmutableList.of());
1468     verify(mockFileGroupsMetadata, times(3)).removeAllStaleGroups();
1469     verify(mockFileGroupsMetadata, times(2)).writeStaleGroups(ImmutableList.of(firstGroup.build()));
1470     verifyNoMoreInteractions(mockFileGroupsMetadata);
1471     verify(mockSharedFileManager, times(3)).getOnDeviceUri(fileKey);
1472     verify(mockSharedFilesMetadata, times(3)).getAllFileKeys();
1473     verifyNoMoreInteractions(mockSharedFileManager);
1474     verify(mockBackend, times(3)).exists(baseDownloadDirectoryUri);
1475     verify(mockBackend, times(3)).children(baseDownloadDirectoryUri);
1476     verify(mockBackend, times(3)).isDirectory(dirFor0p);
1477     verify(mockBackend, never()).deleteFile(any());
1478 
1479     // DAY 3 : the firstGroup expires
1480     now = new Calendar.Builder().setDate(2018, Calendar.MARCH, 22).build();
1481     testClock.set(now.getTimeInMillis());
1482 
1483     expirationHandler.updateExpiration().get();
1484 
1485     verify(mockFileGroupsMetadata, times(12)).getAllFreshGroups();
1486     verify(mockFileGroupsMetadata, times(8)).getAllStaleGroups();
1487     verify(mockFileGroupsMetadata, times(4)).removeAllGroupsWithKeys(ImmutableList.of());
1488     verify(mockFileGroupsMetadata, times(4)).removeAllStaleGroups();
1489     verify(mockFileGroupsMetadata, times(3)).writeStaleGroups(ImmutableList.of(firstGroup.build()));
1490     verifyNoMoreInteractions(mockFileGroupsMetadata);
1491     verify(mockSharedFileManager, times(4)).getOnDeviceUri(fileKey);
1492     verify(mockSharedFilesMetadata, times(4)).getAllFileKeys();
1493     verifyNoMoreInteractions(mockSharedFileManager);
1494     verifyNoMoreInteractions(mockEventLogger);
1495     verify(mockBackend, times(4)).exists(baseDownloadDirectoryUri);
1496     verify(mockBackend, times(4)).children(baseDownloadDirectoryUri);
1497     verify(mockBackend, times(4)).isDirectory(dirForAll);
1498     verify(mockBackend, never()).deleteFile(any());
1499   }
1500 
1501   @Test
updateExpiration_expiredGroups_withAndroidSharedFile()1502   public void updateExpiration_expiredGroups_withAndroidSharedFile() throws Exception {
1503     setupForAndroidShared();
1504     // Current time
1505     Calendar now = new Calendar.Builder().setDate(2020, Calendar.MARCH, 20).build();
1506     testClock.set(now.getTimeInMillis());
1507 
1508     DataFileGroupInternal dataFileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP_1, 1);
1509     // Time when the group expires
1510     Calendar earlier = new Calendar.Builder().setDate(2018, Calendar.MARCH, 20).build();
1511     long nowTimeSecs = earlier.getTimeInMillis() / 1000;
1512     dataFileGroup = dataFileGroup.toBuilder().setExpirationDateSecs(nowTimeSecs).build();
1513     NewFileKey[] fileKeys = MddTestUtil.createFileKeysForDataFileGroupInternal(dataFileGroup);
1514     String androidSharingChecksum = "sha256_" + dataFileGroup.getFile(0).getChecksum();
1515     Uri blobUri = DirectoryUtil.getBlobUri(context, androidSharingChecksum);
1516 
1517     assertThat(sharedFileManager.reserveFileEntry(fileKeys[0]).get()).isTrue();
1518     assertThat(
1519             sharedFileManager
1520                 .setAndroidSharedDownloadedFileEntry(
1521                     fileKeys[0], androidSharingChecksum, nowTimeSecs)
1522                 .get())
1523         .isTrue();
1524     assertThat(fileGroupsMetadata.write(TEST_KEY_1, dataFileGroup).get()).isTrue();
1525 
1526     expirationHandlerNoMocks.updateExpiration().get();
1527 
1528     verify(mockBlobStoreBackend).deleteFile(blobUri);
1529     assertThat(sharedFilesMetadata.read(fileKeys[0]).get()).isNull();
1530     assertThat(fileGroupsMetadata.read(TEST_KEY_1).get()).isNull();
1531     verify(mockEventLogger)
1532         .logEventSampled(
1533             MddClientEvent.Code.EVENT_CODE_UNSPECIFIED,
1534             dataFileGroup.getGroupName(),
1535             dataFileGroup.getFileGroupVersionNumber(),
1536             dataFileGroup.getBuildId(),
1537             dataFileGroup.getVariantId());
1538     verify(mockEventLogger).logEventSampled(MddClientEvent.Code.EVENT_CODE_UNSPECIFIED);
1539     verify(mockEventLogger).logMddDataDownloadFileExpirationEvent(0, 1);
1540     verify(mockEventLogger).logMddDataDownloadFileExpirationEvent(0, 1);
1541     verifyNoMoreInteractions(mockEventLogger);
1542   }
1543 
1544   @Test
updateExpiration_expiredGroups_withAndroidSharedFile_releaseLeaseFails()1545   public void updateExpiration_expiredGroups_withAndroidSharedFile_releaseLeaseFails()
1546       throws Exception {
1547     setupForAndroidShared();
1548     // Current time
1549     Calendar now = new Calendar.Builder().setDate(2020, Calendar.MARCH, 20).build();
1550     testClock.set(now.getTimeInMillis());
1551 
1552     DataFileGroupInternal dataFileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP_1, 1);
1553     // Time when the group expires
1554     Calendar earlier = new Calendar.Builder().setDate(2018, Calendar.MARCH, 20).build();
1555     long nowTimeSecs = earlier.getTimeInMillis() / 1000;
1556     dataFileGroup = dataFileGroup.toBuilder().setExpirationDateSecs(nowTimeSecs).build();
1557     NewFileKey[] fileKeys = MddTestUtil.createFileKeysForDataFileGroupInternal(dataFileGroup);
1558     String androidSharingChecksum = "sha256_" + dataFileGroup.getFile(0).getChecksum();
1559     Uri blobUri = DirectoryUtil.getBlobUri(context, androidSharingChecksum);
1560 
1561     doThrow(new IOException()).when(mockBlobStoreBackend).deleteFile(blobUri);
1562 
1563     assertThat(sharedFileManager.reserveFileEntry(fileKeys[0]).get()).isTrue();
1564     assertThat(
1565             sharedFileManager
1566                 .setAndroidSharedDownloadedFileEntry(
1567                     fileKeys[0], androidSharingChecksum, nowTimeSecs)
1568                 .get())
1569         .isTrue();
1570     assertThat(fileGroupsMetadata.write(TEST_KEY_1, dataFileGroup).get()).isTrue();
1571 
1572     expirationHandlerNoMocks.updateExpiration().get();
1573 
1574     verify(mockBlobStoreBackend).deleteFile(blobUri);
1575     assertThat(sharedFilesMetadata.read(fileKeys[0]).get()).isNull();
1576     assertThat(fileGroupsMetadata.read(TEST_KEY_1).get()).isNull();
1577     verify(mockEventLogger)
1578         .logEventSampled(
1579             MddClientEvent.Code.EVENT_CODE_UNSPECIFIED,
1580             dataFileGroup.getGroupName(),
1581             dataFileGroup.getFileGroupVersionNumber(),
1582             dataFileGroup.getBuildId(),
1583             dataFileGroup.getVariantId());
1584     verify(mockEventLogger).logEventSampled(MddClientEvent.Code.EVENT_CODE_UNSPECIFIED);
1585     verify(mockEventLogger).logMddDataDownloadFileExpirationEvent(0, 1);
1586     verifyNoMoreInteractions(mockEventLogger);
1587   }
1588 
1589   @Test
updateExpiration_expiredGroups_withAndroidSharedAndNotAndroidSharedFiles()1590   public void updateExpiration_expiredGroups_withAndroidSharedAndNotAndroidSharedFiles()
1591       throws Exception {
1592     setupForAndroidShared();
1593     Calendar now = new Calendar.Builder().setDate(2018, Calendar.MARCH, 20).build();
1594     testClock.set(now.getTimeInMillis());
1595 
1596     DataFileGroupInternal dataFileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP_1, 2);
1597     long nowTimeSecs = now.getTimeInMillis() / 1000;
1598     dataFileGroup = dataFileGroup.toBuilder().setExpirationDateSecs(nowTimeSecs).build();
1599     NewFileKey[] fileKeys = MddTestUtil.createFileKeysForDataFileGroupInternal(dataFileGroup);
1600     String androidSharingChecksum = "sha256_" + dataFileGroup.getFile(0).getChecksum();
1601     Uri blobUri = DirectoryUtil.getBlobUri(context, androidSharingChecksum);
1602 
1603     assertThat(sharedFileManager.reserveFileEntry(fileKeys[0]).get()).isTrue();
1604     assertThat(sharedFileManager.reserveFileEntry(fileKeys[1]).get()).isTrue();
1605     assertThat(
1606             sharedFileManager
1607                 .setAndroidSharedDownloadedFileEntry(
1608                     fileKeys[0], androidSharingChecksum, nowTimeSecs)
1609                 .get())
1610         .isTrue();
1611     assertThat(fileGroupsMetadata.write(TEST_KEY_1, dataFileGroup).get()).isTrue();
1612 
1613     when(mockBackend.children(dirFor0p)).thenReturn(Arrays.asList(testUri2));
1614 
1615     expirationHandlerNoMocks.updateExpiration().get();
1616 
1617     verify(mockBackend).deleteFile(testUri2);
1618     verify(mockBlobStoreBackend).deleteFile(blobUri);
1619     assertThat(sharedFilesMetadata.read(fileKeys[0]).get()).isNull();
1620     assertThat(sharedFilesMetadata.read(fileKeys[1]).get()).isNull();
1621     assertThat(fileGroupsMetadata.read(TEST_KEY_1).get()).isNull();
1622 
1623     verify(mockEventLogger)
1624         .logEventSampled(
1625             MddClientEvent.Code.EVENT_CODE_UNSPECIFIED,
1626             dataFileGroup.getGroupName(),
1627             dataFileGroup.getFileGroupVersionNumber(),
1628             dataFileGroup.getBuildId(),
1629             dataFileGroup.getVariantId());
1630     verify(mockEventLogger).logEventSampled(MddClientEvent.Code.EVENT_CODE_UNSPECIFIED);
1631     verify(mockEventLogger).logMddDataDownloadFileExpirationEvent(0, 1);
1632     verify(mockEventLogger).logMddDataDownloadFileExpirationEvent(0, 1);
1633     verify(mockEventLogger).logMddDataDownloadFileExpirationEvent(0, 2);
1634     verifyNoMoreInteractions(mockEventLogger);
1635   }
1636 
1637   @Test
updateExpiration_noExpiredAndroidSharedGroup_withUnaccountedFile()1638   public void updateExpiration_noExpiredAndroidSharedGroup_withUnaccountedFile() throws Exception {
1639     setupForAndroidShared();
1640     Calendar now = new Calendar.Builder().setDate(2018, Calendar.MARCH, 20).build();
1641     testClock.set(now.getTimeInMillis());
1642 
1643     DataFileGroupInternal dataFileGroup =
1644         MddTestUtil.createSharedDataFileGroupInternal(TEST_GROUP_1, 1);
1645     // No changes to dataFileGroup
1646     NewFileKey[] fileKeys = MddTestUtil.createFileKeysForDataFileGroupInternal(dataFileGroup);
1647     String androidSharingChecksum = dataFileGroup.getFile(0).getAndroidSharingChecksum();
1648     Uri blobUri = DirectoryUtil.getBlobUri(context, androidSharingChecksum);
1649 
1650     assertThat(sharedFileManager.reserveFileEntry(fileKeys[0]).get()).isTrue();
1651     assertThat(
1652             sharedFileManager
1653                 .setAndroidSharedDownloadedFileEntry(fileKeys[0], androidSharingChecksum, 0)
1654                 .get())
1655         .isTrue();
1656     assertThat(fileGroupsMetadata.write(TEST_KEY_1, dataFileGroup).get()).isTrue();
1657 
1658     // Unaccounted file
1659     when(mockBackend.children(dirFor0p)).thenReturn(Arrays.asList(tempTestUri2));
1660 
1661     expirationHandlerNoMocks.updateExpiration().get();
1662 
1663     assertThat(fileGroupsMetadata.read(TEST_KEY_1).get()).isNotNull();
1664     verify(mockBlobStoreBackend, never()).deleteFile(blobUri);
1665     verify(mockBackend).deleteFile(tempTestUri2);
1666     verify(mockEventLogger).logMddDataDownloadFileExpirationEvent(0, 1);
1667     verifyNoMoreInteractions(mockEventLogger);
1668   }
1669 
1670   @Test
updateExpiration_expiredGroups_withIsolatedStructure()1671   public void updateExpiration_expiredGroups_withIsolatedStructure() throws Exception {
1672     setupIsolatedSymlinkStructure();
1673 
1674     // Current time
1675     Calendar now = new Calendar.Builder().setDate(2020, Calendar.MARCH, 20).build();
1676     testClock.set(now.getTimeInMillis());
1677 
1678     DataFileGroupInternal dataFileGroup = MddTestUtil.createDataFileGroupInternal(TEST_GROUP_1, 1);
1679     // Time when the group expires
1680     Calendar earlier = new Calendar.Builder().setDate(2018, Calendar.MARCH, 20).build();
1681     long earlierTimeSecs = earlier.getTimeInMillis() / 1000;
1682     dataFileGroup =
1683         dataFileGroup.toBuilder()
1684             .setExpirationDateSecs(earlierTimeSecs)
1685             .setPreserveFilenamesAndIsolateFiles(true)
1686             .build();
1687 
1688     NewFileKey[] fileKeys = MddTestUtil.createFileKeysForDataFileGroupInternal(dataFileGroup);
1689 
1690     List<GroupKeyAndGroup> groups =
1691         Arrays.asList(GroupKeyAndGroup.create(TEST_KEY_1, dataFileGroup));
1692     when(mockFileGroupsMetadata.getAllFreshGroups())
1693         .thenReturn(Futures.immediateFuture(groups))
1694         .thenReturn(Futures.immediateFuture(new ArrayList<>()));
1695     when(mockSharedFileManager.getFileStatus(fileKeys[0]))
1696         .thenReturn(Futures.immediateFuture(FileStatus.DOWNLOAD_COMPLETE));
1697     when(mockSharedFileManager.getOnDeviceUri(fileKeys[0]))
1698         .thenReturn(Futures.immediateFuture(testUri1));
1699 
1700     when(mockSharedFilesMetadata.getAllFileKeys())
1701         .thenReturn(Futures.immediateFuture(Arrays.asList(fileKeys)));
1702     when(mockBackend.children(dirFor0p)).thenReturn(Arrays.asList(testUri1));
1703     expirationHandler.updateExpiration().get();
1704 
1705     verify(mockFileGroupsMetadata, times(3)).getAllFreshGroups();
1706     verify(mockFileGroupsMetadata, times(2)).getAllStaleGroups();
1707     verify(mockFileGroupsMetadata).removeAllGroupsWithKeys(Arrays.asList(TEST_KEY_1));
1708     verify(mockFileGroupsMetadata).removeAllStaleGroups();
1709     verify(mockFileGroupsMetadata).writeStaleGroups(ImmutableList.of());
1710     verifyNoMoreInteractions(mockFileGroupsMetadata);
1711     verify(mockSharedFilesMetadata).getAllFileKeys();
1712     verify(mockSharedFileManager).removeFileEntry(fileKeys[0]);
1713     verify(mockBackend).exists(baseDownloadDirectoryUri);
1714     verify(mockBackend).children(baseDownloadDirectoryUri);
1715     verify(mockBackend).isDirectory(testUri1);
1716     verify(mockBackend).deleteFile(testUri1);
1717     verify(mockBackend, times(2)).exists(symlinkDirForGroup1);
1718     verify(mockBackend, times(2)).isDirectory(symlinkDirForGroup1);
1719     verify(mockBackend).deleteDirectory(symlinkDirForGroup1);
1720     verifyNoMoreInteractions(mockSharedFileManager);
1721   }
1722 
1723   @Test
updateExpiration_noExpiredGroups_doesNotRemoveIsolatedStructure()1724   public void updateExpiration_noExpiredGroups_doesNotRemoveIsolatedStructure() throws Exception {
1725     // Create group that has isolated structure
1726     DataFileGroupInternal dataFileGroup =
1727         MddTestUtil.createDataFileGroupInternal(TEST_GROUP_1, 1).toBuilder()
1728             .setPreserveFilenamesAndIsolateFiles(true)
1729             .build();
1730 
1731     NewFileKey[] fileKeys = MddTestUtil.createFileKeysForDataFileGroupInternal(dataFileGroup);
1732 
1733     // Setup mocks to return our fresh group
1734     List<GroupKeyAndGroup> groups =
1735         Arrays.asList(GroupKeyAndGroup.create(TEST_KEY_1, dataFileGroup));
1736     when(mockFileGroupsMetadata.getAllFreshGroups()).thenReturn(Futures.immediateFuture(groups));
1737     when(mockSharedFileManager.getFileStatus(fileKeys[0]))
1738         .thenReturn(Futures.immediateFuture(FileStatus.DOWNLOAD_COMPLETE));
1739     when(mockSharedFileManager.getOnDeviceUri(fileKeys[0]))
1740         .thenReturn(Futures.immediateFuture(testUri1));
1741     when(mockSharedFilesMetadata.getAllFileKeys())
1742         .thenReturn(Futures.immediateFuture(Arrays.asList(fileKeys)));
1743 
1744     expirationHandler.updateExpiration().get();
1745 
1746     // Verify file is not deleted
1747     verify(mockBackend, never()).deleteFile(testUri1);
1748 
1749     // Verify symlinks are not considered for deletion:
1750     verify(mockBackend, never()).exists(symlinkDirForGroup1);
1751     verify(mockBackend, never()).isDirectory(symlinkDirForGroup1);
1752     verify(mockBackend, never()).deleteDirectory(symlinkDirForGroup1);
1753     verify(mockBackend, never()).exists(symlinkForUri1);
1754     verify(mockBackend, never()).isDirectory(symlinkForUri1);
1755     verify(mockBackend, never()).deleteFile(symlinkForUri1);
1756   }
1757 
1758   @Test
updateExpiration_expiredStaleGroup_withIsolatedStructure_deletesFiles()1759   public void updateExpiration_expiredStaleGroup_withIsolatedStructure_deletesFiles()
1760       throws Exception {
1761     setupIsolatedSymlinkStructure();
1762 
1763     Calendar now = new Calendar.Builder().setDate(2018, Calendar.MARCH, 20).build();
1764     Calendar later = new Calendar.Builder().setDate(2018, Calendar.MARCH, 22).build();
1765     testClock.set(now.getTimeInMillis());
1766     long nowTimeSecs = now.getTimeInMillis() / 1000;
1767     DataFileGroupInternal dataFileGroup =
1768         MddTestUtil.createDataFileGroupInternal(TEST_GROUP_1, 1).toBuilder()
1769             .setBookkeeping(
1770                 DataFileGroupBookkeeping.newBuilder()
1771                     .setStaleExpirationDate(later.getTimeInMillis() / 1000)
1772                     .build())
1773             .setExpirationDateSecs(nowTimeSecs)
1774             .setPreserveFilenamesAndIsolateFiles(true)
1775             .build();
1776     NewFileKey[] fileKeys = MddTestUtil.createFileKeysForDataFileGroupInternal(dataFileGroup);
1777 
1778     fileGroupsMetadataStaleGroups.set(ImmutableList.of(dataFileGroup));
1779 
1780     when(mockSharedFileManager.getFileStatus(fileKeys[0]))
1781         .thenReturn(Futures.immediateFuture(FileStatus.DOWNLOAD_COMPLETE));
1782     when(mockSharedFileManager.getOnDeviceUri(fileKeys[0]))
1783         .thenReturn(Futures.immediateFuture(testDirUri1));
1784     when(mockSharedFilesMetadata.getAllFileKeys())
1785         .thenReturn(Futures.immediateFuture(Arrays.asList(fileKeys)));
1786     when(mockBackend.children(dirFor1p)).thenReturn(Arrays.asList(testDirUri1, testUri2));
1787     when(mockBackend.children(testDirUri1))
1788         .thenReturn(Arrays.asList(testDirFileUri1, testDirFileUri2));
1789 
1790     expirationHandler.updateExpiration().get();
1791 
1792     verify(mockFileGroupsMetadata, times(3)).getAllFreshGroups();
1793     verify(mockFileGroupsMetadata, times(2)).getAllStaleGroups();
1794     verify(mockFileGroupsMetadata).removeAllGroupsWithKeys(ImmutableList.of());
1795     verify(mockFileGroupsMetadata).removeAllStaleGroups();
1796     verify(mockFileGroupsMetadata).writeStaleGroups(ImmutableList.of());
1797     verifyNoMoreInteractions(mockFileGroupsMetadata);
1798     verify(mockSharedFilesMetadata).getAllFileKeys();
1799     verify(mockSharedFileManager).removeFileEntry(fileKeys[0]);
1800     verifyNoMoreInteractions(mockSharedFileManager);
1801     verify(mockBackend).exists(baseDownloadDirectoryUri);
1802     verify(mockBackend).children(baseDownloadDirectoryUri);
1803     verify(mockBackend).isDirectory(testDirUri1);
1804     verify(mockBackend).isDirectory(testDirFileUri1);
1805     verify(mockBackend).isDirectory(testDirFileUri2);
1806     verify(mockBackend, times(2)).exists(symlinkDirForGroup1);
1807     verify(mockBackend, times(2)).isDirectory(symlinkDirForGroup1);
1808     verify(mockBackend).deleteDirectory(symlinkDirForGroup1);
1809     verifyNoMoreInteractions(mockSharedFileManager);
1810   }
1811 
1812   @Test
updateExpiration_noExpiredGroups_removesUnaccountedIsolatedFileUri()1813   public void updateExpiration_noExpiredGroups_removesUnaccountedIsolatedFileUri()
1814       throws Exception {
1815     setupIsolatedSymlinkStructure();
1816 
1817     DataFileGroupInternal isolatedGroup1 =
1818         MddTestUtil.createDataFileGroupInternal(TEST_GROUP_1, 1).toBuilder()
1819             .setPreserveFilenamesAndIsolateFiles(true)
1820             .build();
1821     NewFileKey[] fileKeys = MddTestUtil.createFileKeysForDataFileGroupInternal(isolatedGroup1);
1822 
1823     List<GroupKeyAndGroup> groups =
1824         Arrays.asList(GroupKeyAndGroup.create(TEST_KEY_1, isolatedGroup1));
1825     when(mockFileGroupsMetadata.getAllFreshGroups()).thenReturn(Futures.immediateFuture(groups));
1826     when(mockSharedFileManager.getFileStatus(fileKeys[0]))
1827         .thenReturn(Futures.immediateFuture(FileStatus.DOWNLOAD_COMPLETE));
1828     when(mockSharedFileManager.getOnDeviceUri(fileKeys[0]))
1829         .thenReturn(Futures.immediateFuture(testUri1));
1830 
1831     when(mockSharedFilesMetadata.getAllFileKeys())
1832         .thenReturn(Futures.immediateFuture(Arrays.asList(fileKeys)));
1833 
1834     expirationHandler.updateExpiration().get();
1835 
1836     // Verify only the unaccounted isolated file uri is deleted.
1837     verify(mockBackend).deleteFile(symlinkForUri2);
1838     verify(mockBackend, never()).deleteFile(symlinkForUri1);
1839   }
1840 
1841   // TODO(b/115659980): consider moving this to a public utility class in the File Library
setUpFileMock(Uri uri, long size)1842   private void setUpFileMock(Uri uri, long size) throws Exception {
1843     when(mockBackend.exists(uri)).thenReturn(true);
1844     when(mockBackend.isDirectory(uri)).thenReturn(false);
1845     when(mockBackend.fileSize(uri)).thenReturn(size);
1846   }
1847 
1848   // TODO(b/115659980): consider moving this to a public utility class in the File Library
setUpDirectoryMock(Uri uri, List<Uri> children)1849   private void setUpDirectoryMock(Uri uri, List<Uri> children) throws Exception {
1850     when(mockBackend.exists(uri)).thenReturn(true);
1851     when(mockBackend.isDirectory(uri)).thenReturn(true);
1852     when(mockBackend.children(uri)).thenReturn(children);
1853   }
1854 
createFileKeysUseChecksumOnly(DataFileGroupInternal group)1855   private NewFileKey[] createFileKeysUseChecksumOnly(DataFileGroupInternal group) {
1856     NewFileKey[] newFileKeys = new NewFileKey[group.getFileCount()];
1857     for (int i = 0; i < group.getFileCount(); ++i) {
1858       newFileKeys[i] =
1859           SharedFilesMetadata.createKeyFromDataFileForCurrentVersion(
1860               context, group.getFile(i), group.getAllowedReadersEnum(), mockSilentFeedback);
1861     }
1862     return newFileKeys;
1863   }
1864 
setupIsolatedSymlinkStructure()1865   private void setupIsolatedSymlinkStructure() throws Exception {
1866     setUpDirectoryMock(
1867         baseDownloadDirectoryUri,
1868         Arrays.asList(dirForAll, dirFor1p, dirFor0p, baseDownloadSymlinkDirectoryUri));
1869     setUpDirectoryMock(
1870         baseDownloadSymlinkDirectoryUri,
1871         ImmutableList.of(symlinkDirForGroup1, symlinkDirForGroup2));
1872     setUpDirectoryMock(symlinkDirForGroup1, ImmutableList.of(symlinkForUri1));
1873     setUpDirectoryMock(symlinkDirForGroup2, ImmutableList.of(symlinkForUri2));
1874   }
1875 }
1876