diff options
author | Al Sutton <alsutton@google.com> | 2020-01-16 12:38:45 +0000 |
---|---|---|
committer | Al Sutton <alsutton@google.com> | 2020-01-21 14:00:03 +0000 |
commit | 7ab8dc312867a060f5536c71c20abd9512aacb2c (patch) | |
tree | 64222f623ba26bc05eb85d4fde99afeff137b3ef /services/robotests | |
parent | b8429e8051ba507940d879c75172c8c3d4c97a99 (diff) |
Notify transports of empty K/V backups
In order to allow transports to know when a K/V backup would have
been performed, but was not due to no data changes being reported,
we need to introduce some code to find the K/V backup participants,
remove any which are backed up in this sweep or are currently failing
to back up due to an error, and then inform the transport of the
remaing set.
This CL introduces the code to do that.
We use a state file to determine if a package backed up without an
error on the last run, if the backup fails we remove the file for
that package. When all packages with changed data have been backed
up we get a list of all packages which have the file set (i.e. were
successful), remove the set of packages backed up on this run, and
inform the transport the rest had no data changes.
Bug:147481066
Test: m -j RunBackupFrameworksServicesRoboTests
Change-Id: I5c9f94c925096faf7b65307c0be1a7aba48c1cfb
Diffstat (limited to 'services/robotests')
-rw-r--r-- | services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java | 94 |
1 files changed, 94 insertions, 0 deletions
diff --git a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java index 2510b60955d3..ec56e1ebc8e0 100644 --- a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java +++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java @@ -131,6 +131,7 @@ import org.junit.runner.RunWith; import org.mockito.ArgumentMatcher; import org.mockito.InOrder; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; @@ -2339,6 +2340,85 @@ public class KeyValueBackupTaskTest { assertThat(mBackupManagerService.getCurrentToken()).isEqualTo(0L); } + /** Do not inform transport of an empty backup if the app hasn't backed up before */ + @Test + public void testRunTask_whenNoDataToBackupOnFirstBackup_doesNotTellTransportOfBackup() + throws Exception { + TransportMock transportMock = setUpInitializedTransport(mTransport); + mBackupManagerService.setCurrentToken(0L); + when(transportMock.transport.getCurrentRestoreSet()).thenReturn(1234L); + setUpAgent(PACKAGE_1); + KeyValueBackupTask task = createKeyValueBackupTask(transportMock, true, PACKAGE_1); + + runTask(task); + + verify(transportMock.transport, never()) + .performBackup( + argThat(packageInfo(PACKAGE_1)), any(ParcelFileDescriptor.class), anyInt()); + } + + /** Let the transport know if there are no changes for a KV backed-up package. */ + @Test + public void testRunTask_whenBackupHasCompletedAndThenNoDataChanges_transportGetsNotified() + throws Exception { + TransportMock transportMock = setUpInitializedTransport(mTransport); + when(transportMock.transport.getCurrentRestoreSet()).thenReturn(1234L); + when(transportMock.transport.isAppEligibleForBackup( + argThat(packageInfo(PACKAGE_1)), eq(false))) + .thenReturn(true); + when(transportMock.transport.isAppEligibleForBackup( + argThat(packageInfo(PACKAGE_2)), eq(false))) + .thenReturn(true); + setUpAgentWithData(PACKAGE_1); + setUpAgentWithData(PACKAGE_2); + + PackageInfo endSentinel = new PackageInfo(); + endSentinel.packageName = KeyValueBackupTask.NO_DATA_END_SENTINEL; + + // Perform First Backup run, which should backup both packages + KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1, PACKAGE_2); + runTask(task); + InOrder order = Mockito.inOrder(transportMock.transport); + order.verify(transportMock.transport) + .performBackup( + argThat(packageInfo(PACKAGE_1)), + any(), + eq(BackupTransport.FLAG_NON_INCREMENTAL)); + order.verify(transportMock.transport).finishBackup(); + order.verify(transportMock.transport) + .performBackup( + argThat(packageInfo(PACKAGE_2)), + any(), + eq(BackupTransport.FLAG_NON_INCREMENTAL)); + order.verify(transportMock.transport).finishBackup(); + + // Run again with new data for package 1, but nothing new for package 2 + task = createKeyValueBackupTask(transportMock, PACKAGE_1); + runTask(task); + + // Now for the second run we performed one incremental backup (package 1) and + // made one "no change" call (package 2) before sending the end sentinel. + order.verify(transportMock.transport) + .performBackup( + argThat(packageInfo(PACKAGE_1)), + any(), + eq(BackupTransport.FLAG_INCREMENTAL)); + order.verify(transportMock.transport).finishBackup(); + order.verify(transportMock.transport) + .performBackup( + argThat(packageInfo(PACKAGE_2)), + any(), + eq(BackupTransport.FLAG_DATA_NOT_CHANGED)); + order.verify(transportMock.transport).finishBackup(); + order.verify(transportMock.transport) + .performBackup( + argThat(packageInfo(endSentinel)), + any(), + eq(BackupTransport.FLAG_DATA_NOT_CHANGED)); + order.verify(transportMock.transport).finishBackup(); + order.verifyNoMoreInteractions(); + } + private void runTask(KeyValueBackupTask task) { // Pretend we are not on the main-thread to prevent RemoteCall from complaining mShadowMainLooper.setCurrentThread(false); @@ -2576,6 +2656,20 @@ public class KeyValueBackupTaskTest { packageInfo != null && packageData.packageName.equals(packageInfo.packageName); } + /** Matches {@link PackageInfo} whose package name is {@code packageData.packageName}. */ + private static ArgumentMatcher<PackageInfo> packageInfo(PackageInfo packageData) { + // We have to test for packageInfo nulity because of Mockito's own stubbing with argThat(). + // E.g. if you do: + // + // 1. when(object.method(argThat(str -> str.equals("foo")))).thenReturn(0) + // 2. when(object.method(argThat(str -> str.equals("bar")))).thenReturn(2) + // + // The second line will throw NPE because it will call lambda 1 with null, since argThat() + // returns null. So we guard against that by checking for null. + return packageInfo -> + packageInfo != null && packageInfo.packageName.equals(packageInfo.packageName); + } + /** Matches {@link ApplicationInfo} whose package name is {@code packageData.packageName}. */ private static ArgumentMatcher<ApplicationInfo> applicationInfo(PackageData packageData) { return applicationInfo -> |