summaryrefslogtreecommitdiff
path: root/services/robotests
diff options
context:
space:
mode:
authorAl Sutton <alsutton@google.com>2020-01-16 12:38:45 +0000
committerAl Sutton <alsutton@google.com>2020-01-21 14:00:03 +0000
commit7ab8dc312867a060f5536c71c20abd9512aacb2c (patch)
tree64222f623ba26bc05eb85d4fde99afeff137b3ef /services/robotests
parentb8429e8051ba507940d879c75172c8c3d4c97a99 (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.java94
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 ->