diff options
author | TreeHugger Robot <treehugger-gerrit@google.com> | 2020-06-22 16:46:58 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2020-06-22 16:46:58 +0000 |
commit | 42a637d4ee4b35f13bfd8424765b1c8ae5e2844e (patch) | |
tree | f26d33b6e5c143ef75b5aa17b6935239a438f329 | |
parent | 720f686fe34374539928033fdc9af53668314aaf (diff) | |
parent | 9cf5156fedbc2938126bfd0f3a5f5786652b9c09 (diff) |
Merge "Verify staged session remain unchanged on system server reboot" into rvc-dev am: 9cf5156fed
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/11838108
Change-Id: Iac224a8b8d586c20deffdfaf93e2d541bc80cefd
6 files changed, 350 insertions, 0 deletions
diff --git a/tests/StagedInstallTest/Android.bp b/tests/StagedInstallTest/Android.bp new file mode 100644 index 000000000000..c3fdd695c2b7 --- /dev/null +++ b/tests/StagedInstallTest/Android.bp @@ -0,0 +1,31 @@ +// Copyright (C) 2020 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. + +android_test_helper_app { + name: "StagedInstallInternalTestApp", + manifest: "app/AndroidManifest.xml", + srcs: ["app/src/**/*.java"], + static_libs: ["androidx.test.rules", "cts-install-lib"], + test_suites: ["general-tests"], +} + +java_test_host { + name: "StagedInstallInternalTest", + srcs: ["src/**/*.java"], + libs: ["tradefed"], + static_libs: ["testng", "compatibility-tradefed"], + test_suites: ["general-tests"], + test_config: "StagedInstallInternalTest.xml", +} + diff --git a/tests/StagedInstallTest/StagedInstallInternalTest.xml b/tests/StagedInstallTest/StagedInstallInternalTest.xml new file mode 100644 index 000000000000..1b8fa672fe38 --- /dev/null +++ b/tests/StagedInstallTest/StagedInstallInternalTest.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 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 internal staged install tests"> + <option name="test-suite-tag" value="StagedInstallTest" /> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="test-file-name" value="StagedInstallInternalTestApp.apk" /> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="run-command" value="pm uninstall com.android.cts.install.lib.testapp.A" /> + <option name="run-command" value="pm uninstall com.android.cts.install.lib.testapp.B" /> + <option name="teardown-command" value="pm uninstall com.android.cts.install.lib.testapp.A" /> + <option name="teardown-command" value="pm uninstall com.android.cts.install.lib.testapp.B" /> + </target_preparer> + <test class="com.android.tradefed.testtype.HostTest" > + <option name="class" + value="com.android.tests.stagedinstallinternal.host.StagedInstallInternalTest" /> + </test> +</configuration> diff --git a/tests/StagedInstallTest/TEST_MAPPING b/tests/StagedInstallTest/TEST_MAPPING new file mode 100644 index 000000000000..5a7a5a766b88 --- /dev/null +++ b/tests/StagedInstallTest/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "StagedInstallInternalTest" + } + ] +} diff --git a/tests/StagedInstallTest/app/AndroidManifest.xml b/tests/StagedInstallTest/app/AndroidManifest.xml new file mode 100644 index 000000000000..a678f1ec3691 --- /dev/null +++ b/tests/StagedInstallTest/app/AndroidManifest.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 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. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.tests.stagedinstallinternal" > + + <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" /> + <application> + <receiver android:name="com.android.cts.install.lib.LocalIntentSender" + android:exported="true" /> + <uses-library android:name="android.test.runner" /> + </application> + + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.tests.stagedinstallinternal" + android:label="StagedInstallInternal Test"/> + +</manifest> diff --git a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java new file mode 100644 index 000000000000..02597d548361 --- /dev/null +++ b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2020 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.stagedinstallinternal; + +import static com.android.cts.install.lib.InstallUtils.getPackageInstaller; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + +import android.Manifest; +import android.content.pm.PackageInstaller; + +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.cts.install.lib.Install; +import com.android.cts.install.lib.InstallUtils; +import com.android.cts.install.lib.TestApp; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.nio.file.Files; +import java.util.List; +import java.util.function.Consumer; + +@RunWith(JUnit4.class) +public class StagedInstallInternalTest { + + private static final String TAG = StagedInstallInternalTest.class.getSimpleName(); + + private File mTestStateFile = new File( + InstrumentationRegistry.getInstrumentation().getContext().getFilesDir(), + "stagedinstall_state"); + + /** + * Adopts common shell permissions needed for staged install tests. + */ + @Before + public void adoptShellPermissions() { + InstallUtils.adoptShellPermissionIdentity( + Manifest.permission.INSTALL_PACKAGES, + Manifest.permission.DELETE_PACKAGES + ); + } + + /** + * Drops shell permissions needed for staged install tests. + */ + @After + public void dropShellPermissions() { + InstallUtils.dropShellPermissionIdentity(); + } + + // This is marked as @Test to take advantage of @Before/@After methods of this class. Actual + // purpose of this method to be called before and after each test case of + // com.android.test.stagedinstall.host.StagedInstallTest to reduce tests flakiness. + @Test + public void cleanUp() throws Exception { + Files.deleteIfExists(mTestStateFile.toPath()); + } + + @Test + public void testSystemServerRestartDoesNotAffectStagedSessions_Commit() throws Exception { + int sessionId = Install.single(TestApp.A1).setStaged().commit(); + assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1); + assertSessionReady(sessionId); + storeSessionId(sessionId); + } + + @Test + public void testSystemServerRestartDoesNotAffectStagedSessions_Verify() throws Exception { + assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1); + int sessionId = retrieveLastSessionId(); + assertSessionReady(sessionId); + } + + private static void assertSessionReady(int sessionId) { + assertSessionState(sessionId, + (session) -> assertThat(session.isStagedSessionReady()).isTrue()); + } + + private static void assertSessionState( + int sessionId, Consumer<PackageInstaller.SessionInfo> assertion) { + PackageInstaller packageInstaller = getPackageInstaller(); + + List<PackageInstaller.SessionInfo> sessions = packageInstaller.getStagedSessions(); + boolean found = false; + for (PackageInstaller.SessionInfo session : sessions) { + if (session.getSessionId() == sessionId) { + assertion.accept(session); + found = true; + } + } + assertWithMessage("Expecting to find session in getStagedSession()") + .that(found).isTrue(); + + // Test also that getSessionInfo correctly returns the session. + PackageInstaller.SessionInfo sessionInfo = packageInstaller.getSessionInfo(sessionId); + assertion.accept(sessionInfo); + } + + private void storeSessionId(int sessionId) throws Exception { + try (BufferedWriter writer = new BufferedWriter(new FileWriter(mTestStateFile))) { + writer.write("" + sessionId); + } + } + + private int retrieveLastSessionId() throws Exception { + try (BufferedReader reader = new BufferedReader(new FileReader(mTestStateFile))) { + return Integer.parseInt(reader.readLine()); + } + } +} diff --git a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java new file mode 100644 index 000000000000..9b432f7d0ca5 --- /dev/null +++ b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2020 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.stagedinstallinternal.host; + +import static com.google.common.truth.Truth.assertThat; + +import static org.junit.Assert.assertTrue; + +import com.android.ddmlib.Log; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; +import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; +import com.android.tradefed.util.ProcessInfo; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(DeviceJUnit4ClassRunner.class) +public class StagedInstallInternalTest extends BaseHostJUnit4Test { + + private static final String TAG = StagedInstallInternalTest.class.getSimpleName(); + private static final long SYSTEM_SERVER_TIMEOUT_MS = 60 * 1000; + private boolean mWasRoot = false; + + /** + * 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("testApkOnlyEnableRollback");</code> + */ + private void runPhase(String phase) throws Exception { + assertTrue(runDeviceTests("com.android.tests.stagedinstallinternal", + "com.android.tests.stagedinstallinternal.StagedInstallInternalTest", + phase)); + } + + // We do not assert the success of cleanup phase since it might fail due to flaky reasons. + private void cleanUp() throws Exception { + try { + runDeviceTests("com.android.tests.stagedinstallinternal", + "com.android.tests.stagedinstallinternal.StagedInstallInternalTest", + "cleanUp"); + } catch (AssertionError e) { + Log.e(TAG, e); + } + } + + @Before + public void setUp() throws Exception { + mWasRoot = getDevice().isAdbRoot(); + if (!mWasRoot) { + getDevice().enableAdbRoot(); + } + cleanUp(); + // Abandon all staged sessions + getDevice().executeShellCommand("pm install-abandon $(pm get-stagedsessions --only-ready " + + "--only-parent --only-sessionid)"); + } + + @After + public void tearDown() throws Exception { + if (!mWasRoot) { + getDevice().disableAdbRoot(); + } + cleanUp(); + } + + @Test + public void testSystemServerRestartDoesNotAffectStagedSessions() throws Exception { + runPhase("testSystemServerRestartDoesNotAffectStagedSessions_Commit"); + restartSystemServer(); + runPhase("testSystemServerRestartDoesNotAffectStagedSessions_Verify"); + } + + private void restartSystemServer() throws Exception { + // Restart the system server + long oldStartTime = getDevice().getProcessByName("system_server").getStartTime(); + assertThat(getDevice().executeShellCommand("am restart")).contains("Restart the system"); + + // Wait for new system server process to start + long start = System.currentTimeMillis(); + long newStartTime = oldStartTime; + while (System.currentTimeMillis() < start + SYSTEM_SERVER_TIMEOUT_MS) { + ProcessInfo newPs = getDevice().getProcessByName("system_server"); + if (newPs != null) { + newStartTime = newPs.getStartTime(); + if (newStartTime != oldStartTime) { + break; + } + } + Thread.sleep(500); + } + assertThat(newStartTime).isNotEqualTo(oldStartTime); + getDevice().waitForDeviceAvailable(); + } +} |