Merge "Add unit tests for RollbackStore."
diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java
index eadd09c..425da37 100644
--- a/services/core/java/com/android/server/rollback/RollbackStore.java
+++ b/services/core/java/com/android/server/rollback/RollbackStore.java
@@ -28,6 +28,7 @@
 import android.util.SparseLongArray;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 
 import libcore.io.IoUtils;
 
@@ -288,19 +289,25 @@
             JSONObject dataJson = new JSONObject(
                     IoUtils.readFileAsString(rollbackJsonFile.getAbsolutePath()));
 
-            return new Rollback(
-                    rollbackInfoFromJson(dataJson.getJSONObject("info")),
-                    backupDir,
-                    Instant.parse(dataJson.getString("timestamp")),
-                    dataJson.getInt("stagedSessionId"),
-                    rollbackStateFromString(dataJson.getString("state")),
-                    dataJson.getInt("apkSessionId"),
-                    dataJson.getBoolean("restoreUserDataInProgress"));
+            return rollbackFromJson(dataJson, backupDir);
         } catch (JSONException | DateTimeParseException | ParseException e) {
             throw new IOException(e);
         }
     }
 
+    @VisibleForTesting
+    static Rollback rollbackFromJson(JSONObject dataJson, File backupDir)
+            throws JSONException, ParseException {
+        return new Rollback(
+                rollbackInfoFromJson(dataJson.getJSONObject("info")),
+                backupDir,
+                Instant.parse(dataJson.getString("timestamp")),
+                dataJson.getInt("stagedSessionId"),
+                rollbackStateFromString(dataJson.getString("state")),
+                dataJson.getInt("apkSessionId"),
+                dataJson.getBoolean("restoreUserDataInProgress"));
+    }
+
     private static JSONObject toJson(VersionedPackage pkg) throws JSONException {
         JSONObject json = new JSONObject();
         json.put("packageName", pkg.getPackageName());
diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java
new file mode 100644
index 0000000..ee3b15a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.rollback;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.pm.VersionedPackage;
+import android.content.rollback.PackageRollbackInfo;
+import android.util.IntArray;
+import android.util.SparseLongArray;
+
+import com.google.common.truth.Correspondence;
+
+import org.json.JSONObject;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.File;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+@RunWith(JUnit4.class)
+public class RollbackStoreTest {
+
+    private static final int ID = 123;
+
+    private static final Correspondence<VersionedPackage, VersionedPackage> VER_PKG_CORR =
+            new Correspondence<VersionedPackage, VersionedPackage>() {
+                @Override
+                public boolean compare(VersionedPackage a, VersionedPackage b) {
+                    if (a == null || b == null) {
+                        return a == b;
+                    }
+                    return a.getLongVersionCode() == b.getLongVersionCode()
+                            && Objects.equals(a.getPackageName(), b.getPackageName());
+                }
+
+                @Override
+                public String toString() {
+                    return "is the same as";
+                }
+            };
+
+    private static final Correspondence<PackageRollbackInfo.RestoreInfo,
+            PackageRollbackInfo.RestoreInfo>
+            RESTORE_INFO_CORR =
+            new Correspondence<PackageRollbackInfo.RestoreInfo, PackageRollbackInfo.RestoreInfo>() {
+                @Override
+                public boolean compare(PackageRollbackInfo.RestoreInfo a,
+                        PackageRollbackInfo.RestoreInfo b) {
+                    if (a == null || b == null) {
+                        return a == b;
+                    }
+                    return a.userId == b.userId
+                            && a.appId == b.appId
+                            && Objects.equals(a.seInfo, b.seInfo);
+                }
+
+                @Override
+                public String toString() {
+                    return "is the same as";
+                }
+            };
+
+    private static final String JSON_ROLLBACK = "{'info':{'rollbackId':123,'packages':"
+            + "[{'versionRolledBackFrom':{'packageName':'blah','longVersionCode':55},"
+            + "'versionRolledBackTo':{'packageName':'blah1','longVersionCode':50},'pendingBackups':"
+            + "[59,1245,124544],'pendingRestores':[{'userId':498,'appId':32322,'seInfo':'wombles'},"
+            + "{'userId':-895,'appId':1,'seInfo':'pingu'}],'isApex':false,'installedUsers':"
+            + "[498468432,1111,98464],'ceSnapshotInodes':[{'userId':1,'ceSnapshotInode':-6},"
+            + "{'userId':2222,'ceSnapshotInode':81641654445},{'userId':546546,"
+            + "'ceSnapshotInode':345689375}]},{'versionRolledBackFrom':{'packageName':'chips',"
+            + "'longVersionCode':28},'versionRolledBackTo':{'packageName':'com.chips.test',"
+            + "'longVersionCode':48},'pendingBackups':[5],'pendingRestores':[{'userId':18,"
+            + "'appId':-12,'seInfo':''}],'isApex':false,'installedUsers':[55,79],"
+            + "'ceSnapshotInodes':[]}],'isStaged':false,'causePackages':[{'packageName':'hello',"
+            + "'longVersionCode':23},{'packageName':'something','longVersionCode':999}],"
+            + "'committedSessionId':45654465},'timestamp':'2019-10-01T12:29:08.855Z',"
+            + "'stagedSessionId':-1,'state':'enabling','apkSessionId':-1,"
+            + "'restoreUserDataInProgress':true}";
+
+    @Rule
+    public TemporaryFolder mFolder = new TemporaryFolder();
+
+    private File mRollbackDir;
+
+    private RollbackStore mRollbackStore;
+
+    @Before
+    public void setUp() throws Exception {
+        mRollbackStore = new RollbackStore(mFolder.getRoot());
+        mRollbackDir = mFolder.newFolder(ID + "");
+        mFolder.newFile("rollback.json");
+    }
+
+    @Test
+    public void createNonStaged() {
+        Rollback rollback = mRollbackStore.createNonStagedRollback(ID);
+
+        assertThat(rollback.getBackupDir().getAbsolutePath())
+                .isEqualTo(mFolder.getRoot().getAbsolutePath() + "/" + ID);
+
+        assertThat(rollback.isStaged()).isFalse();
+        assertThat(rollback.info.getRollbackId()).isEqualTo(ID);
+        assertThat(rollback.info.getPackages()).isEmpty();
+        assertThat(rollback.isEnabling()).isTrue();
+    }
+
+    @Test
+    public void createStaged() {
+        Rollback rollback = mRollbackStore.createStagedRollback(ID, 897);
+
+        assertThat(rollback.getBackupDir().getAbsolutePath())
+                .isEqualTo(mFolder.getRoot().getAbsolutePath() + "/" + ID);
+
+        assertThat(rollback.isStaged()).isTrue();
+        assertThat(rollback.getStagedSessionId()).isEqualTo(897);
+
+        assertThat(rollback.info.getRollbackId()).isEqualTo(ID);
+        assertThat(rollback.info.getPackages()).isEmpty();
+        assertThat(rollback.isEnabling()).isTrue();
+    }
+
+    @Test
+    public void saveAndLoadRollback() {
+        Rollback origRb = mRollbackStore.createNonStagedRollback(ID);
+
+        origRb.setRestoreUserDataInProgress(true);
+        origRb.info.getCausePackages().add(new VersionedPackage("com.made.up", 2));
+        origRb.info.getCausePackages().add(new VersionedPackage("com.pack.age", 99));
+        origRb.info.setCommittedSessionId(123456);
+
+        PackageRollbackInfo pkgInfo1 =
+                new PackageRollbackInfo(new VersionedPackage("com.made.up", 18),
+                        new VersionedPackage("com.something.else", 5), new IntArray(),
+                        new ArrayList<>(), false, new IntArray(), new SparseLongArray());
+        pkgInfo1.getPendingBackups().add(8);
+        pkgInfo1.getPendingBackups().add(888);
+        pkgInfo1.getPendingBackups().add(88885);
+        pkgInfo1.getCeSnapshotInodes().put(12, 424);
+        pkgInfo1.getCeSnapshotInodes().put(222772, 10000000000L);
+        pkgInfo1.getCeSnapshotInodes().put(10, -67);
+
+        pkgInfo1.getPendingRestores().add(
+                new PackageRollbackInfo.RestoreInfo(4980, 3442322, "seInfo"));
+        pkgInfo1.getPendingRestores().add(
+                new PackageRollbackInfo.RestoreInfo(-89, 15, "otherSeInfo"));
+
+        pkgInfo1.getSnapshottedUsers().add(11);
+        pkgInfo1.getSnapshottedUsers().add(1);
+        pkgInfo1.getSnapshottedUsers().add(0);
+
+        PackageRollbackInfo pkgInfo2 = new PackageRollbackInfo(
+                new VersionedPackage("another.package", 2),
+                new VersionedPackage("com.test.ing", 48888), new IntArray(), new ArrayList<>(),
+                false, new IntArray(), new SparseLongArray());
+        pkgInfo2.getPendingBackups().add(57);
+
+        pkgInfo2.getPendingRestores().add(
+                new PackageRollbackInfo.RestoreInfo(180, -120, ""));
+
+        origRb.info.getPackages().add(pkgInfo1);
+        origRb.info.getPackages().add(pkgInfo2);
+
+        RollbackStore.saveRollback(origRb);
+
+        List<Rollback> loadedRollbacks = mRollbackStore.loadRollbacks();
+        assertThat(loadedRollbacks).hasSize(1);
+        Rollback loadedRb = loadedRollbacks.get(0);
+
+        assertRollbacksAreEquivalent(loadedRb, origRb);
+    }
+
+    @Test
+    public void loadFromJson() throws Exception {
+        Rollback expectedRb = mRollbackStore.createNonStagedRollback(ID);
+
+        expectedRb.setTimestamp(Instant.parse("2019-10-01T12:29:08.855Z"));
+        expectedRb.setRestoreUserDataInProgress(true);
+        expectedRb.info.getCausePackages().add(new VersionedPackage("hello", 23));
+        expectedRb.info.getCausePackages().add(new VersionedPackage("something", 999));
+        expectedRb.info.setCommittedSessionId(45654465);
+
+        PackageRollbackInfo pkgInfo1 = new PackageRollbackInfo(new VersionedPackage("blah", 55),
+                new VersionedPackage("blah1", 50), new IntArray(), new ArrayList<>(),
+                false, new IntArray(), new SparseLongArray());
+        pkgInfo1.getPendingBackups().add(59);
+        pkgInfo1.getPendingBackups().add(1245);
+        pkgInfo1.getPendingBackups().add(124544);
+        pkgInfo1.getCeSnapshotInodes().put(546546, 345689375);
+        pkgInfo1.getCeSnapshotInodes().put(2222, 81641654445L);
+        pkgInfo1.getCeSnapshotInodes().put(1, -6);
+
+        pkgInfo1.getPendingRestores().add(
+                new PackageRollbackInfo.RestoreInfo(498, 32322, "wombles"));
+        pkgInfo1.getPendingRestores().add(
+                new PackageRollbackInfo.RestoreInfo(-895, 1, "pingu"));
+
+        pkgInfo1.getSnapshottedUsers().add(498468432);
+        pkgInfo1.getSnapshottedUsers().add(1111);
+        pkgInfo1.getSnapshottedUsers().add(98464);
+
+        PackageRollbackInfo pkgInfo2 = new PackageRollbackInfo(new VersionedPackage("chips", 28),
+                new VersionedPackage("com.chips.test", 48), new IntArray(), new ArrayList<>(),
+                false, new IntArray(), new SparseLongArray());
+        pkgInfo2.getPendingBackups().add(5);
+
+        pkgInfo2.getPendingRestores().add(
+                new PackageRollbackInfo.RestoreInfo(18, -12, ""));
+
+        pkgInfo2.getSnapshottedUsers().add(55);
+        pkgInfo2.getSnapshottedUsers().add(79);
+
+        expectedRb.info.getPackages().add(pkgInfo1);
+        expectedRb.info.getPackages().add(pkgInfo2);
+
+        Rollback parsedRb = RollbackStore.rollbackFromJson(
+                new JSONObject(JSON_ROLLBACK), expectedRb.getBackupDir());
+
+        assertRollbacksAreEquivalent(parsedRb, expectedRb);
+    }
+
+    @Test
+    public void saveAndDelete() {
+        Rollback rollback = mRollbackStore.createNonStagedRollback(ID);
+
+        RollbackStore.saveRollback(rollback);
+
+        File expectedFile = new File(mRollbackDir.getAbsolutePath() + "/rollback.json");
+
+        assertThat(expectedFile.exists()).isTrue();
+
+        RollbackStore.deleteRollback(rollback);
+
+        assertThat(expectedFile.exists()).isFalse();
+    }
+
+    private void assertRollbacksAreEquivalent(Rollback b, Rollback a) {
+        assertThat(b.info.getRollbackId()).isEqualTo(ID);
+
+        assertThat(b.getBackupDir()).isEqualTo(a.getBackupDir());
+
+        assertThat(b.isRestoreUserDataInProgress())
+                .isEqualTo(a.isRestoreUserDataInProgress());
+
+        assertThat(b.getTimestamp()).isEqualTo(a.getTimestamp());
+
+        assertThat(b.isEnabling()).isEqualTo(a.isEnabling());
+        assertThat(b.isAvailable()).isEqualTo(a.isAvailable());
+        assertThat(b.isCommitted()).isEqualTo(a.isCommitted());
+
+        assertThat(b.isStaged()).isEqualTo(a.isStaged());
+
+        assertThat(b.getApexPackageNames())
+                .containsExactlyElementsIn(a.getApexPackageNames());
+
+        assertThat(b.getStagedSessionId()).isEqualTo(a.getStagedSessionId());
+
+        assertThat(b.info.getCommittedSessionId()).isEqualTo(a.info.getCommittedSessionId());
+
+        assertThat(b.info.getCausePackages()).comparingElementsUsing(VER_PKG_CORR)
+                .containsExactlyElementsIn(a.info.getCausePackages());
+
+        assertThat(b.info.getPackages()).hasSize(a.info.getPackages().size());
+
+        for (int i = 0; i < b.info.getPackages().size(); i++) {
+            assertPackageRollbacksAreEquivalent(
+                    b.info.getPackages().get(i), a.info.getPackages().get(i));
+        }
+    }
+
+    private void assertPackageRollbacksAreEquivalent(PackageRollbackInfo b, PackageRollbackInfo a) {
+        assertThat(b.getPackageName()).isEqualTo(a.getPackageName());
+
+        assertThat(b.getVersionRolledBackFrom().getLongVersionCode())
+                .isEqualTo(a.getVersionRolledBackFrom().getLongVersionCode());
+        assertThat(b.getVersionRolledBackFrom().getPackageName())
+                .isEqualTo(a.getVersionRolledBackFrom().getPackageName());
+
+        assertThat(b.getVersionRolledBackTo().getLongVersionCode())
+                .isEqualTo(a.getVersionRolledBackTo().getLongVersionCode());
+        assertThat(b.getVersionRolledBackTo().getPackageName())
+                .isEqualTo(a.getVersionRolledBackTo().getPackageName());
+
+        assertThat(b.getPendingBackups().toArray()).isEqualTo(a.getPendingBackups().toArray());
+
+        assertThat(b.getPendingRestores()).comparingElementsUsing(RESTORE_INFO_CORR)
+                .containsExactlyElementsIn(a.getPendingRestores());
+
+        assertThat(b.isApex()).isEqualTo(a.isApex());
+
+        assertThat(b.getSnapshottedUsers().toArray()).isEqualTo(a.getSnapshottedUsers().toArray());
+
+        assertThat(b.getCeSnapshotInodes().toString())
+                .isEqualTo(a.getCeSnapshotInodes().toString());
+    }
+
+}