From d9c78463d92cc1872a00e44ede91f15e9ed4355e Mon Sep 17 00:00:00 2001 From: Oli Lan Date: Mon, 30 Sep 2019 10:18:48 +0100 Subject: [PATCH] Add unit tests for RollbackStore. This adds a test covering the saving and loading of rollback data as JSON performed by the RollbackStore class. A small change to RollbackStore is made to allow for easier testing. This will allow future changes to the structure of Rollback and to RollbackStore to be made with greater confidence that the persistence format has not been changed inadvertently. Bug: 141915404 Test: atest RollbackStoreTest (new test) Change-Id: I97b9428f66d0c7ec2f1fd1c7a34afbb4d13bc896 --- .../server/rollback/RollbackStore.java | 23 +- .../server/rollback/RollbackStoreTest.java | 318 ++++++++++++++++++ 2 files changed, 333 insertions(+), 8 deletions(-) create mode 100644 services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java index a9331aa5648fc..5b226245a892f 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.Slog; import android.util.SparseLongArray; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import libcore.io.IoUtils; @@ -288,19 +289,25 @@ class RollbackStore { 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 0000000000000..ee3b15a97f26f --- /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 VER_PKG_CORR = + new Correspondence() { + @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 + RESTORE_INFO_CORR = + new Correspondence() { + @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 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()); + } + +}