Oli Lan | d9c7846 | 2019-09-30 10:18:48 +0100 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2019 The Android Open Source Project |
| 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 | |
| 17 | package com.android.server.rollback; |
| 18 | |
| 19 | import static com.google.common.truth.Truth.assertThat; |
| 20 | |
| 21 | import android.content.pm.VersionedPackage; |
| 22 | import android.content.rollback.PackageRollbackInfo; |
| 23 | import android.util.IntArray; |
| 24 | import android.util.SparseLongArray; |
| 25 | |
| 26 | import com.google.common.truth.Correspondence; |
| 27 | |
| 28 | import org.json.JSONObject; |
| 29 | import org.junit.Before; |
| 30 | import org.junit.Rule; |
| 31 | import org.junit.Test; |
| 32 | import org.junit.rules.TemporaryFolder; |
| 33 | import org.junit.runner.RunWith; |
| 34 | import org.junit.runners.JUnit4; |
| 35 | |
| 36 | import java.io.File; |
| 37 | import java.time.Instant; |
| 38 | import java.util.ArrayList; |
| 39 | import java.util.List; |
| 40 | import java.util.Objects; |
| 41 | |
| 42 | @RunWith(JUnit4.class) |
| 43 | public class RollbackStoreTest { |
| 44 | |
| 45 | private static final int ID = 123; |
Gavin Corkery | 0987cb0 | 2019-10-07 10:46:55 +0100 | [diff] [blame] | 46 | private static final int USER = 0; |
| 47 | private static final String INSTALLER = "some.installer"; |
Oli Lan | d9c7846 | 2019-09-30 10:18:48 +0100 | [diff] [blame] | 48 | |
| 49 | private static final Correspondence<VersionedPackage, VersionedPackage> VER_PKG_CORR = |
| 50 | new Correspondence<VersionedPackage, VersionedPackage>() { |
| 51 | @Override |
| 52 | public boolean compare(VersionedPackage a, VersionedPackage b) { |
| 53 | if (a == null || b == null) { |
| 54 | return a == b; |
| 55 | } |
JW Wang | f73f94a | 2020-01-02 09:16:58 +0800 | [diff] [blame] | 56 | return a.equals(b); |
Oli Lan | d9c7846 | 2019-09-30 10:18:48 +0100 | [diff] [blame] | 57 | } |
| 58 | |
| 59 | @Override |
| 60 | public String toString() { |
| 61 | return "is the same as"; |
| 62 | } |
| 63 | }; |
| 64 | |
| 65 | private static final Correspondence<PackageRollbackInfo.RestoreInfo, |
| 66 | PackageRollbackInfo.RestoreInfo> |
| 67 | RESTORE_INFO_CORR = |
| 68 | new Correspondence<PackageRollbackInfo.RestoreInfo, PackageRollbackInfo.RestoreInfo>() { |
| 69 | @Override |
| 70 | public boolean compare(PackageRollbackInfo.RestoreInfo a, |
| 71 | PackageRollbackInfo.RestoreInfo b) { |
| 72 | if (a == null || b == null) { |
| 73 | return a == b; |
| 74 | } |
| 75 | return a.userId == b.userId |
| 76 | && a.appId == b.appId |
| 77 | && Objects.equals(a.seInfo, b.seInfo); |
| 78 | } |
| 79 | |
| 80 | @Override |
| 81 | public String toString() { |
| 82 | return "is the same as"; |
| 83 | } |
| 84 | }; |
| 85 | |
| 86 | private static final String JSON_ROLLBACK = "{'info':{'rollbackId':123,'packages':" |
| 87 | + "[{'versionRolledBackFrom':{'packageName':'blah','longVersionCode':55}," |
| 88 | + "'versionRolledBackTo':{'packageName':'blah1','longVersionCode':50},'pendingBackups':" |
| 89 | + "[59,1245,124544],'pendingRestores':[{'userId':498,'appId':32322,'seInfo':'wombles'}," |
Mohammad Samiul Islam | 3fcecfc | 2019-12-20 17:46:01 +0000 | [diff] [blame] | 90 | + "{'userId':-895,'appId':1,'seInfo':'pingu'}],'isApex':false,'isApkInApex':false," |
| 91 | + "'installedUsers':" |
Oli Lan | d9c7846 | 2019-09-30 10:18:48 +0100 | [diff] [blame] | 92 | + "[498468432,1111,98464],'ceSnapshotInodes':[{'userId':1,'ceSnapshotInode':-6}," |
| 93 | + "{'userId':2222,'ceSnapshotInode':81641654445},{'userId':546546," |
| 94 | + "'ceSnapshotInode':345689375}]},{'versionRolledBackFrom':{'packageName':'chips'," |
| 95 | + "'longVersionCode':28},'versionRolledBackTo':{'packageName':'com.chips.test'," |
| 96 | + "'longVersionCode':48},'pendingBackups':[5],'pendingRestores':[{'userId':18," |
Mohammad Samiul Islam | 3fcecfc | 2019-12-20 17:46:01 +0000 | [diff] [blame] | 97 | + "'appId':-12,'seInfo':''}],'isApex':false,'isApkInApex':false," |
| 98 | + "'installedUsers':[55,79]," |
Oli Lan | d9c7846 | 2019-09-30 10:18:48 +0100 | [diff] [blame] | 99 | + "'ceSnapshotInodes':[]}],'isStaged':false,'causePackages':[{'packageName':'hello'," |
| 100 | + "'longVersionCode':23},{'packageName':'something','longVersionCode':999}]," |
| 101 | + "'committedSessionId':45654465},'timestamp':'2019-10-01T12:29:08.855Z'," |
| 102 | + "'stagedSessionId':-1,'state':'enabling','apkSessionId':-1," |
Gavin Corkery | 0987cb0 | 2019-10-07 10:46:55 +0100 | [diff] [blame] | 103 | + "'restoreUserDataInProgress':true, 'userId':0," |
| 104 | + "'installerPackageName':'some.installer'}"; |
Oli Lan | d9c7846 | 2019-09-30 10:18:48 +0100 | [diff] [blame] | 105 | |
| 106 | @Rule |
| 107 | public TemporaryFolder mFolder = new TemporaryFolder(); |
| 108 | |
| 109 | private File mRollbackDir; |
| 110 | |
| 111 | private RollbackStore mRollbackStore; |
| 112 | |
| 113 | @Before |
| 114 | public void setUp() throws Exception { |
| 115 | mRollbackStore = new RollbackStore(mFolder.getRoot()); |
| 116 | mRollbackDir = mFolder.newFolder(ID + ""); |
| 117 | mFolder.newFile("rollback.json"); |
| 118 | } |
| 119 | |
| 120 | @Test |
| 121 | public void createNonStaged() { |
JW Wang | 1aba59b | 2020-02-03 09:32:52 +0800 | [diff] [blame] | 122 | Rollback rollback = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER, null); |
Oli Lan | d9c7846 | 2019-09-30 10:18:48 +0100 | [diff] [blame] | 123 | |
| 124 | assertThat(rollback.getBackupDir().getAbsolutePath()) |
| 125 | .isEqualTo(mFolder.getRoot().getAbsolutePath() + "/" + ID); |
| 126 | |
| 127 | assertThat(rollback.isStaged()).isFalse(); |
| 128 | assertThat(rollback.info.getRollbackId()).isEqualTo(ID); |
| 129 | assertThat(rollback.info.getPackages()).isEmpty(); |
| 130 | assertThat(rollback.isEnabling()).isTrue(); |
| 131 | } |
| 132 | |
| 133 | @Test |
| 134 | public void createStaged() { |
JW Wang | 1aba59b | 2020-02-03 09:32:52 +0800 | [diff] [blame] | 135 | Rollback rollback = mRollbackStore.createStagedRollback(ID, 897, USER, INSTALLER, null); |
Oli Lan | d9c7846 | 2019-09-30 10:18:48 +0100 | [diff] [blame] | 136 | |
| 137 | assertThat(rollback.getBackupDir().getAbsolutePath()) |
| 138 | .isEqualTo(mFolder.getRoot().getAbsolutePath() + "/" + ID); |
| 139 | |
| 140 | assertThat(rollback.isStaged()).isTrue(); |
| 141 | assertThat(rollback.getStagedSessionId()).isEqualTo(897); |
| 142 | |
| 143 | assertThat(rollback.info.getRollbackId()).isEqualTo(ID); |
| 144 | assertThat(rollback.info.getPackages()).isEmpty(); |
| 145 | assertThat(rollback.isEnabling()).isTrue(); |
| 146 | } |
| 147 | |
| 148 | @Test |
| 149 | public void saveAndLoadRollback() { |
JW Wang | 1aba59b | 2020-02-03 09:32:52 +0800 | [diff] [blame] | 150 | Rollback origRb = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER, null); |
Oli Lan | d9c7846 | 2019-09-30 10:18:48 +0100 | [diff] [blame] | 151 | |
| 152 | origRb.setRestoreUserDataInProgress(true); |
| 153 | origRb.info.getCausePackages().add(new VersionedPackage("com.made.up", 2)); |
| 154 | origRb.info.getCausePackages().add(new VersionedPackage("com.pack.age", 99)); |
| 155 | origRb.info.setCommittedSessionId(123456); |
| 156 | |
| 157 | PackageRollbackInfo pkgInfo1 = |
| 158 | new PackageRollbackInfo(new VersionedPackage("com.made.up", 18), |
| 159 | new VersionedPackage("com.something.else", 5), new IntArray(), |
Mohammad Samiul Islam | 3fcecfc | 2019-12-20 17:46:01 +0000 | [diff] [blame] | 160 | new ArrayList<>(), false, false, new IntArray(), new SparseLongArray()); |
Oli Lan | d9c7846 | 2019-09-30 10:18:48 +0100 | [diff] [blame] | 161 | pkgInfo1.getPendingBackups().add(8); |
| 162 | pkgInfo1.getPendingBackups().add(888); |
| 163 | pkgInfo1.getPendingBackups().add(88885); |
| 164 | pkgInfo1.getCeSnapshotInodes().put(12, 424); |
| 165 | pkgInfo1.getCeSnapshotInodes().put(222772, 10000000000L); |
| 166 | pkgInfo1.getCeSnapshotInodes().put(10, -67); |
| 167 | |
| 168 | pkgInfo1.getPendingRestores().add( |
| 169 | new PackageRollbackInfo.RestoreInfo(4980, 3442322, "seInfo")); |
| 170 | pkgInfo1.getPendingRestores().add( |
| 171 | new PackageRollbackInfo.RestoreInfo(-89, 15, "otherSeInfo")); |
| 172 | |
| 173 | pkgInfo1.getSnapshottedUsers().add(11); |
| 174 | pkgInfo1.getSnapshottedUsers().add(1); |
| 175 | pkgInfo1.getSnapshottedUsers().add(0); |
| 176 | |
| 177 | PackageRollbackInfo pkgInfo2 = new PackageRollbackInfo( |
| 178 | new VersionedPackage("another.package", 2), |
| 179 | new VersionedPackage("com.test.ing", 48888), new IntArray(), new ArrayList<>(), |
Mohammad Samiul Islam | 3fcecfc | 2019-12-20 17:46:01 +0000 | [diff] [blame] | 180 | false, false, new IntArray(), new SparseLongArray()); |
Oli Lan | d9c7846 | 2019-09-30 10:18:48 +0100 | [diff] [blame] | 181 | pkgInfo2.getPendingBackups().add(57); |
| 182 | |
| 183 | pkgInfo2.getPendingRestores().add( |
| 184 | new PackageRollbackInfo.RestoreInfo(180, -120, "")); |
| 185 | |
| 186 | origRb.info.getPackages().add(pkgInfo1); |
| 187 | origRb.info.getPackages().add(pkgInfo2); |
| 188 | |
| 189 | RollbackStore.saveRollback(origRb); |
| 190 | |
| 191 | List<Rollback> loadedRollbacks = mRollbackStore.loadRollbacks(); |
| 192 | assertThat(loadedRollbacks).hasSize(1); |
| 193 | Rollback loadedRb = loadedRollbacks.get(0); |
| 194 | |
| 195 | assertRollbacksAreEquivalent(loadedRb, origRb); |
| 196 | } |
| 197 | |
| 198 | @Test |
| 199 | public void loadFromJson() throws Exception { |
JW Wang | 1aba59b | 2020-02-03 09:32:52 +0800 | [diff] [blame] | 200 | Rollback expectedRb = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER, null); |
Oli Lan | d9c7846 | 2019-09-30 10:18:48 +0100 | [diff] [blame] | 201 | |
| 202 | expectedRb.setTimestamp(Instant.parse("2019-10-01T12:29:08.855Z")); |
| 203 | expectedRb.setRestoreUserDataInProgress(true); |
| 204 | expectedRb.info.getCausePackages().add(new VersionedPackage("hello", 23)); |
| 205 | expectedRb.info.getCausePackages().add(new VersionedPackage("something", 999)); |
| 206 | expectedRb.info.setCommittedSessionId(45654465); |
| 207 | |
| 208 | PackageRollbackInfo pkgInfo1 = new PackageRollbackInfo(new VersionedPackage("blah", 55), |
| 209 | new VersionedPackage("blah1", 50), new IntArray(), new ArrayList<>(), |
Mohammad Samiul Islam | 3fcecfc | 2019-12-20 17:46:01 +0000 | [diff] [blame] | 210 | false, false, new IntArray(), new SparseLongArray()); |
Oli Lan | d9c7846 | 2019-09-30 10:18:48 +0100 | [diff] [blame] | 211 | pkgInfo1.getPendingBackups().add(59); |
| 212 | pkgInfo1.getPendingBackups().add(1245); |
| 213 | pkgInfo1.getPendingBackups().add(124544); |
| 214 | pkgInfo1.getCeSnapshotInodes().put(546546, 345689375); |
| 215 | pkgInfo1.getCeSnapshotInodes().put(2222, 81641654445L); |
| 216 | pkgInfo1.getCeSnapshotInodes().put(1, -6); |
| 217 | |
| 218 | pkgInfo1.getPendingRestores().add( |
| 219 | new PackageRollbackInfo.RestoreInfo(498, 32322, "wombles")); |
| 220 | pkgInfo1.getPendingRestores().add( |
| 221 | new PackageRollbackInfo.RestoreInfo(-895, 1, "pingu")); |
| 222 | |
| 223 | pkgInfo1.getSnapshottedUsers().add(498468432); |
| 224 | pkgInfo1.getSnapshottedUsers().add(1111); |
| 225 | pkgInfo1.getSnapshottedUsers().add(98464); |
| 226 | |
| 227 | PackageRollbackInfo pkgInfo2 = new PackageRollbackInfo(new VersionedPackage("chips", 28), |
| 228 | new VersionedPackage("com.chips.test", 48), new IntArray(), new ArrayList<>(), |
Mohammad Samiul Islam | 3fcecfc | 2019-12-20 17:46:01 +0000 | [diff] [blame] | 229 | false, false, new IntArray(), new SparseLongArray()); |
Oli Lan | d9c7846 | 2019-09-30 10:18:48 +0100 | [diff] [blame] | 230 | pkgInfo2.getPendingBackups().add(5); |
| 231 | |
| 232 | pkgInfo2.getPendingRestores().add( |
| 233 | new PackageRollbackInfo.RestoreInfo(18, -12, "")); |
| 234 | |
| 235 | pkgInfo2.getSnapshottedUsers().add(55); |
| 236 | pkgInfo2.getSnapshottedUsers().add(79); |
| 237 | |
| 238 | expectedRb.info.getPackages().add(pkgInfo1); |
| 239 | expectedRb.info.getPackages().add(pkgInfo2); |
| 240 | |
| 241 | Rollback parsedRb = RollbackStore.rollbackFromJson( |
| 242 | new JSONObject(JSON_ROLLBACK), expectedRb.getBackupDir()); |
| 243 | |
| 244 | assertRollbacksAreEquivalent(parsedRb, expectedRb); |
| 245 | } |
| 246 | |
| 247 | @Test |
| 248 | public void saveAndDelete() { |
JW Wang | 1aba59b | 2020-02-03 09:32:52 +0800 | [diff] [blame] | 249 | Rollback rollback = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER, null); |
Oli Lan | d9c7846 | 2019-09-30 10:18:48 +0100 | [diff] [blame] | 250 | |
| 251 | RollbackStore.saveRollback(rollback); |
| 252 | |
| 253 | File expectedFile = new File(mRollbackDir.getAbsolutePath() + "/rollback.json"); |
| 254 | |
| 255 | assertThat(expectedFile.exists()).isTrue(); |
| 256 | |
| 257 | RollbackStore.deleteRollback(rollback); |
| 258 | |
| 259 | assertThat(expectedFile.exists()).isFalse(); |
| 260 | } |
| 261 | |
| 262 | private void assertRollbacksAreEquivalent(Rollback b, Rollback a) { |
| 263 | assertThat(b.info.getRollbackId()).isEqualTo(ID); |
| 264 | |
| 265 | assertThat(b.getBackupDir()).isEqualTo(a.getBackupDir()); |
| 266 | |
| 267 | assertThat(b.isRestoreUserDataInProgress()) |
| 268 | .isEqualTo(a.isRestoreUserDataInProgress()); |
| 269 | |
| 270 | assertThat(b.getTimestamp()).isEqualTo(a.getTimestamp()); |
| 271 | |
| 272 | assertThat(b.isEnabling()).isEqualTo(a.isEnabling()); |
| 273 | assertThat(b.isAvailable()).isEqualTo(a.isAvailable()); |
| 274 | assertThat(b.isCommitted()).isEqualTo(a.isCommitted()); |
| 275 | |
| 276 | assertThat(b.isStaged()).isEqualTo(a.isStaged()); |
| 277 | |
| 278 | assertThat(b.getApexPackageNames()) |
| 279 | .containsExactlyElementsIn(a.getApexPackageNames()); |
| 280 | |
| 281 | assertThat(b.getStagedSessionId()).isEqualTo(a.getStagedSessionId()); |
| 282 | |
| 283 | assertThat(b.info.getCommittedSessionId()).isEqualTo(a.info.getCommittedSessionId()); |
| 284 | |
| 285 | assertThat(b.info.getCausePackages()).comparingElementsUsing(VER_PKG_CORR) |
| 286 | .containsExactlyElementsIn(a.info.getCausePackages()); |
| 287 | |
| 288 | assertThat(b.info.getPackages()).hasSize(a.info.getPackages().size()); |
| 289 | |
| 290 | for (int i = 0; i < b.info.getPackages().size(); i++) { |
| 291 | assertPackageRollbacksAreEquivalent( |
| 292 | b.info.getPackages().get(i), a.info.getPackages().get(i)); |
| 293 | } |
Gavin Corkery | 0987cb0 | 2019-10-07 10:46:55 +0100 | [diff] [blame] | 294 | |
| 295 | assertThat(a.getUserId()).isEqualTo(b.getUserId()); |
| 296 | assertThat(a.getInstallerPackageName()).isEqualTo(b.getInstallerPackageName()); |
Oli Lan | d9c7846 | 2019-09-30 10:18:48 +0100 | [diff] [blame] | 297 | } |
| 298 | |
| 299 | private void assertPackageRollbacksAreEquivalent(PackageRollbackInfo b, PackageRollbackInfo a) { |
| 300 | assertThat(b.getPackageName()).isEqualTo(a.getPackageName()); |
| 301 | |
JW Wang | f73f94a | 2020-01-02 09:16:58 +0800 | [diff] [blame] | 302 | assertThat(b.getVersionRolledBackFrom()).isEqualTo(a.getVersionRolledBackFrom()); |
| 303 | assertThat(b.getVersionRolledBackTo()).isEqualTo(a.getVersionRolledBackTo()); |
Oli Lan | d9c7846 | 2019-09-30 10:18:48 +0100 | [diff] [blame] | 304 | |
| 305 | assertThat(b.getPendingBackups().toArray()).isEqualTo(a.getPendingBackups().toArray()); |
| 306 | |
| 307 | assertThat(b.getPendingRestores()).comparingElementsUsing(RESTORE_INFO_CORR) |
| 308 | .containsExactlyElementsIn(a.getPendingRestores()); |
| 309 | |
| 310 | assertThat(b.isApex()).isEqualTo(a.isApex()); |
| 311 | |
| 312 | assertThat(b.getSnapshottedUsers().toArray()).isEqualTo(a.getSnapshottedUsers().toArray()); |
| 313 | |
| 314 | assertThat(b.getCeSnapshotInodes().toString()) |
| 315 | .isEqualTo(a.getCeSnapshotInodes().toString()); |
| 316 | } |
| 317 | |
| 318 | } |