Merge changes Ie125d44c,If9b5d57e,I001427c7

* changes:
  Create basic test for rollback of staged install.
  Stub out code for a host driven StagedRollbackTest.
  Move RollbackTest app code to a new subdirectory.
This commit is contained in:
TreeHugger Robot
2019-02-18 17:49:56 +00:00
committed by Android (Google) Code Review
11 changed files with 410 additions and 52 deletions

View File

@@ -71,7 +71,7 @@ ROLLBACK_TEST_APP_BV2 := $(LOCAL_INSTALLED_MODULE)
# RollbackTest
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_SRC_FILES := $(call all-java-files-under, RollbackTest/src)
LOCAL_PACKAGE_NAME := RollbackTest
LOCAL_MODULE_TAGS := tests
LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
@@ -84,10 +84,21 @@ LOCAL_JAVA_RESOURCE_FILES := \
$(ROLLBACK_TEST_APP_BV1) \
$(ROLLBACK_TEST_APP_BV2) \
$(ROLLBACK_TEST_APEX_V2)
LOCAL_MANIFEST_FILE := RollbackTest/AndroidManifest.xml
LOCAL_SDK_VERSION := system_current
LOCAL_TEST_CONFIG := RollbackTest.xml
include $(BUILD_PACKAGE)
# StagedRollbackTest
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(call all-java-files-under, StagedRollbackTest/src)
LOCAL_MODULE := StagedRollbackTest
LOCAL_MODULE_TAGS := tests
LOCAL_JAVA_LIBRARIES := tradefed
LOCAL_COMPATIBILITY_SUITE := general-tests
LOCAL_TEST_CONFIG := StagedRollbackTest.xml
include $(BUILD_HOST_JAVA_LIBRARY)
# Clean up local variables
ROLLBACK_TEST_APP_AV1 :=
ROLLBACK_TEST_APP_AV2 :=

View File

@@ -0,0 +1,23 @@
This directory contains a test for the rollback manager service.
Directory structure
===================
RollbackTest
- device driven test for rollbacks not involving staged rollbacks.
StagedRollbackTest
- device driven test for staged rollbacks.
TestApp
- source for dummy apks used in testing.
TestApex
- source for dummy apex modules used in testing.
Running the tests
=================
You can manually run the tests as follows:
atest RollbackTest
atest StagedRollbackTest

View File

@@ -21,5 +21,9 @@
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.tests.rollback" />
<option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
<!-- Exclude the StagedRollbackTest tests, which needs to be specially
driven from the StagedRollbackTest host test -->
<option name="exclude-filter" value="com.android.tests.rollback.StagedRollbackTest" />
</test>
</configuration>

View File

@@ -16,6 +16,10 @@
package com.android.tests.rollback;
import static com.android.tests.rollback.RollbackTestUtils.assertPackageRollbackInfoEquals;
import static com.android.tests.rollback.RollbackTestUtils.assertRollbackInfoEquals;
import static com.android.tests.rollback.RollbackTestUtils.getUniqueRollbackInfoForPackage;
import android.Manifest;
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
@@ -24,7 +28,6 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.VersionedPackage;
import android.content.rollback.PackageRollbackInfo;
import android.content.rollback.RollbackInfo;
import android.content.rollback.RollbackManager;
import android.os.Handler;
@@ -42,7 +45,6 @@ import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.SynchronousQueue;
@@ -617,17 +619,6 @@ public class RollbackTest {
}
}
// Helper function to test the value of a PackageRollbackInfo
private void assertPackageRollbackInfoEquals(String packageName,
long versionRolledBackFrom, long versionRolledBackTo,
PackageRollbackInfo info) {
assertEquals(packageName, info.getPackageName());
assertEquals(packageName, info.getVersionRolledBackFrom().getPackageName());
assertEquals(versionRolledBackFrom, info.getVersionRolledBackFrom().getLongVersionCode());
assertEquals(packageName, info.getVersionRolledBackTo().getPackageName());
assertEquals(versionRolledBackTo, info.getVersionRolledBackTo().getLongVersionCode());
}
/**
* Test bad update automatic rollback.
*/
@@ -713,23 +704,6 @@ public class RollbackTest {
}
}
// Helper function to test the value of a RollbackInfo with single package
private void assertRollbackInfoEquals(String packageName,
long versionRolledBackFrom, long versionRolledBackTo,
RollbackInfo info, VersionedPackage... causePackages) {
assertNotNull(info);
assertEquals(1, info.getPackages().size());
assertPackageRollbackInfoEquals(packageName, versionRolledBackFrom, versionRolledBackTo,
info.getPackages().get(0));
assertEquals(causePackages.length, info.getCausePackages().size());
for (int i = 0; i < causePackages.length; ++i) {
assertEquals(causePackages[i].getPackageName(),
info.getCausePackages().get(i).getPackageName());
assertEquals(causePackages[i].getLongVersionCode(),
info.getCausePackages().get(i).getLongVersionCode());
}
}
// Helper function to test that the given rollback info is a rollback for
// the atomic set {A2, B2} -> {A1, B1}.
private void assertRollbackInfoForAandB(RollbackInfo rollback) {
@@ -743,23 +717,4 @@ public class RollbackTest {
assertPackageRollbackInfoEquals(TEST_APP_A, 2, 1, rollback.getPackages().get(1));
}
}
// Helper function to return the RollbackInfo with a given package in the
// list of rollbacks. Throws an assertion failure if there is more than
// one such rollback info. Returns null if there are no such rollback
// infos.
private RollbackInfo getUniqueRollbackInfoForPackage(List<RollbackInfo> rollbacks,
String packageName) {
RollbackInfo found = null;
for (RollbackInfo rollback : rollbacks) {
for (PackageRollbackInfo info : rollback.getPackages()) {
if (packageName.equals(info.getPackageName())) {
assertNull(found);
found = rollback;
break;
}
}
}
return found;
}
}

View File

@@ -16,19 +16,31 @@
package com.android.tests.rollback;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.VersionedPackage;
import android.content.rollback.PackageRollbackInfo;
import android.content.rollback.RollbackInfo;
import android.content.rollback.RollbackManager;
import android.support.test.InstrumentationRegistry;
import android.util.Log;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
/**
* Utilities to facilitate testing rollbacks.
@@ -157,20 +169,28 @@ class RollbackTestUtils {
/**
* Installs the apks with the given resource names as an atomic set.
* <p>
* In case of staged installs, this function will return succesfully after
* the staged install has been committed and is ready for the device to
* reboot.
*
* @param staged if the rollback should be staged.
* @param enableRollback if rollback should be enabled.
* @param resourceNames names of the class loader resource for the apks to
* install.
* @throws AssertionError if the installation fails.
*/
static void installMultiPackage(boolean enableRollback, String... resourceNames)
throws InterruptedException, IOException {
private static void install(boolean staged, boolean enableRollback,
String... resourceNames) throws InterruptedException, IOException {
Context context = InstrumentationRegistry.getContext();
PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
PackageInstaller.SessionParams multiPackageParams = new PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
multiPackageParams.setMultiPackage();
if (staged) {
multiPackageParams.setStaged();
}
if (enableRollback) {
// TODO: Do we set this on the parent params, the child params, or
// both?
@@ -183,6 +203,9 @@ class RollbackTestUtils {
PackageInstaller.Session session = null;
PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
if (staged) {
params.setStaged();
}
if (enableRollback) {
params.setEnableRollback();
}
@@ -204,6 +227,36 @@ class RollbackTestUtils {
// Commit the session (this will start the installation workflow).
multiPackage.commit(LocalIntentSender.getIntentSender());
assertStatusSuccess(LocalIntentSender.getIntentSenderResult());
if (staged) {
waitForSessionReady(multiPackageId);
}
}
/**
* Installs the apks with the given resource names as an atomic set.
*
* @param enableRollback if rollback should be enabled.
* @param resourceNames names of the class loader resource for the apks to
* install.
* @throws AssertionError if the installation fails.
*/
static void installMultiPackage(boolean enableRollback, String... resourceNames)
throws InterruptedException, IOException {
install(false, enableRollback, resourceNames);
}
/**
* Installs the apks with the given resource names as a staged atomic set.
*
* @param enableRollback if rollback should be enabled.
* @param resourceNames names of the class loader resource for the apks to
* install.
* @throws AssertionError if the installation fails.
*/
static void installStaged(boolean enableRollback, String... resourceNames)
throws InterruptedException, IOException {
install(true, enableRollback, resourceNames);
}
static void adoptShellPermissionIdentity(String... permissions) {
@@ -219,4 +272,104 @@ class RollbackTestUtils {
.getUiAutomation()
.dropShellPermissionIdentity();
}
/**
* Returns the RollbackInfo with a given package in the list of rollbacks.
* Throws an assertion failure if there is more than one such rollback
* info. Returns null if there are no such rollback infos.
*/
static RollbackInfo getUniqueRollbackInfoForPackage(List<RollbackInfo> rollbacks,
String packageName) {
RollbackInfo found = null;
for (RollbackInfo rollback : rollbacks) {
for (PackageRollbackInfo info : rollback.getPackages()) {
if (packageName.equals(info.getPackageName())) {
assertNull(found);
found = rollback;
break;
}
}
}
return found;
}
/**
* Asserts that the given PackageRollbackInfo has the expected package
* name and versions.
*/
static void assertPackageRollbackInfoEquals(String packageName,
long versionRolledBackFrom, long versionRolledBackTo,
PackageRollbackInfo info) {
assertEquals(packageName, info.getPackageName());
assertEquals(packageName, info.getVersionRolledBackFrom().getPackageName());
assertEquals(versionRolledBackFrom, info.getVersionRolledBackFrom().getLongVersionCode());
assertEquals(packageName, info.getVersionRolledBackTo().getPackageName());
assertEquals(versionRolledBackTo, info.getVersionRolledBackTo().getLongVersionCode());
}
/**
* Asserts that the given RollbackInfo has a single package with expected
* package name and versions.
*/
static void assertRollbackInfoEquals(String packageName,
long versionRolledBackFrom, long versionRolledBackTo,
RollbackInfo info, VersionedPackage... causePackages) {
assertNotNull(info);
assertEquals(1, info.getPackages().size());
assertPackageRollbackInfoEquals(packageName, versionRolledBackFrom, versionRolledBackTo,
info.getPackages().get(0));
assertEquals(causePackages.length, info.getCausePackages().size());
for (int i = 0; i < causePackages.length; ++i) {
assertEquals(causePackages[i].getPackageName(),
info.getCausePackages().get(i).getPackageName());
assertEquals(causePackages[i].getLongVersionCode(),
info.getCausePackages().get(i).getLongVersionCode());
}
}
/**
* Waits for the given session to be marked as ready.
* Throws an assertion if the session fails.
*/
static void waitForSessionReady(int sessionId) {
BlockingQueue<PackageInstaller.SessionInfo> sessionStatus = new LinkedBlockingQueue<>();
BroadcastReceiver sessionUpdatedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
PackageInstaller.SessionInfo info =
intent.getParcelableExtra(PackageInstaller.EXTRA_SESSION);
if (info != null && info.getSessionId() == sessionId) {
if (info.isSessionReady() || info.isSessionFailed()) {
try {
sessionStatus.put(info);
} catch (InterruptedException e) {
Log.e(TAG, "Failed to put session info.", e);
}
}
}
}
};
IntentFilter sessionUpdatedFilter =
new IntentFilter(PackageInstaller.ACTION_SESSION_UPDATED);
Context context = InstrumentationRegistry.getContext();
context.registerReceiver(sessionUpdatedReceiver, sessionUpdatedFilter);
PackageInstaller installer = context.getPackageManager().getPackageInstaller();
PackageInstaller.SessionInfo info = installer.getSessionInfo(sessionId);
try {
if (info.isSessionReady() || info.isSessionFailed()) {
sessionStatus.put(info);
}
info = sessionStatus.take();
context.unregisterReceiver(sessionUpdatedReceiver);
if (info.isSessionFailed()) {
throw new AssertionError(info.getStagedSessionErrorMessage());
}
} catch (InterruptedException e) {
throw new AssertionError(e);
}
}
}

View File

@@ -0,0 +1,131 @@
/*
* 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.tests.rollback;
import static com.android.tests.rollback.RollbackTestUtils.assertRollbackInfoEquals;
import static com.android.tests.rollback.RollbackTestUtils.getUniqueRollbackInfoForPackage;
import android.Manifest;
import android.content.rollback.RollbackInfo;
import android.content.rollback.RollbackManager;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/**
* Tests for rollback of staged installs.
* <p>
* Note: These tests require reboot in between test phases. They are run
* specially so that the testFooEnableRollback, testFooCommitRollback, and
* testFooConfirmRollback phases of each test are run in order with reboots in
* between them.
*/
@RunWith(JUnit4.class)
public class StagedRollbackTest {
private static final String TAG = "RollbackTest";
private static final String TEST_APP_A = "com.android.tests.rollback.testapp.A";
/**
* Adopts common shell permissions needed for rollback tests.
*/
@Before
public void adoptShellPermissions() {
RollbackTestUtils.adoptShellPermissionIdentity(
Manifest.permission.INSTALL_PACKAGES,
Manifest.permission.DELETE_PACKAGES,
Manifest.permission.MANAGE_ROLLBACKS);
}
/**
* Drops shell permissions needed for rollback tests.
*/
@After
public void dropShellPermissions() {
RollbackTestUtils.dropShellPermissionIdentity();
}
/**
* Test basic rollbacks. Enable rollback phase.
*/
@Test
public void testBasicEnableRollback() throws Exception {
RollbackTestUtils.uninstall(TEST_APP_A);
assertEquals(-1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
RollbackTestUtils.installStaged(true, "RollbackTestAppAv2.apk");
// At this point, the host test driver will reboot the device and run
// testBasicCommitRollback().
}
/**
* Test basic rollbacks. Commit rollback phase.
*/
@Test
public void testBasicCommitRollback() throws Exception {
assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
RollbackManager rm = RollbackTestUtils.getRollbackManager();
RollbackInfo rollback = getUniqueRollbackInfoForPackage(
rm.getAvailableRollbacks(), TEST_APP_A);
assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback);
assertTrue(rollback.isStaged());
RollbackTestUtils.rollback(rollback.getRollbackId());
rollback = getUniqueRollbackInfoForPackage(
rm.getRecentlyCommittedRollbacks(), TEST_APP_A);
assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback);
assertTrue(rollback.isStaged());
assertNotEquals(-1, rollback.getCommittedSessionId());
RollbackTestUtils.waitForSessionReady(rollback.getCommittedSessionId());
// The app should not be rolled back until after reboot.
assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
// At this point, the host test driver will reboot the device and run
// testBasicConfirmRollback().
}
/**
* Test basic rollbacks. Confirm rollback phase.
*/
@Test
public void testBasicConfirmRollback() throws Exception {
assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A));
RollbackManager rm = RollbackTestUtils.getRollbackManager();
RollbackInfo rollback = getUniqueRollbackInfoForPackage(
rm.getRecentlyCommittedRollbacks(), TEST_APP_A);
assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback);
assertTrue(rollback.isStaged());
assertNotEquals(-1, rollback.getCommittedSessionId());
}
}

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<configuration description="Runs the staged rollback tests">
<option name="test-suite-tag" value="StagedRollbackTest" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="RollbackTest.apk" />
</target_preparer>
<test class="com.android.tradefed.testtype.HostTest" >
<option name="class" value="com.android.tests.rollback.host.StagedRollbackTest" />
</test>
</configuration>

View File

@@ -0,0 +1,56 @@
/*
* 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.tests.rollback.host;
import static org.junit.Assert.assertTrue;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* Runs the staged rollback tests.
*/
@RunWith(DeviceJUnit4ClassRunner.class)
public class StagedRollbackTest extends BaseHostJUnit4Test {
/**
* Runs the given phase of a test by calling into the device.
* Throws an exception if the test phase fails.
* <p>
* For example, <code>runPhase("testBasicEnableRollback");</code>
*/
private void runPhase(String phase) throws Exception {
assertTrue(runDeviceTests("com.android.tests.rollback",
"com.android.tests.rollback.StagedRollbackTest",
phase));
}
/**
* Tests staged rollbacks.
*/
@Test
public void testBasic() throws Exception {
runPhase("testBasicEnableRollback");
getDevice().reboot();
runPhase("testBasicCommitRollback");
getDevice().reboot();
runPhase("testBasicConfirmRollback");
}
}