Merge "Refactor smoke tests to enable one-app-per-testcase runs"
This commit is contained in:
committed by
Android (Google) Code Review
commit
3d441a1c4f
@@ -18,20 +18,30 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.android.smoketest.tests">
|
||||
|
||||
<!-- We add an application tag here just so that we can indicate that
|
||||
this package needs to link against the android.test library,
|
||||
which is needed when building test cases. -->
|
||||
<!--
|
||||
We add an application tag here just so that we can indicate that this package needs to link
|
||||
against the android.test library, which is needed when building test cases.
|
||||
-->
|
||||
<application>
|
||||
<uses-library android:name="android.test.runner" />
|
||||
</application>
|
||||
|
||||
<!--
|
||||
This declares that this app uses the instrumentation test runner targeting
|
||||
the package of com.android.smoketest. To run the tests use the command:
|
||||
"adb shell am instrument -w com.android.smoketest.tests/android.test.InstrumentationTestRunner"
|
||||
This declares that this app uses the instrumentation test runner targeting the package of
|
||||
com.android.smoketest. To run the tests use the command:
|
||||
`adb shell am instrument -w com.android.smoketest.tests/android.test.InstrumentationTestRunner`
|
||||
-->
|
||||
<instrumentation android:name="android.test.InstrumentationTestRunner"
|
||||
android:targetPackage="com.android.smoketest"
|
||||
android:label="System Smoke Tests"/>
|
||||
|
||||
<!--
|
||||
This declares a method to run the instrumentation with a special runner, which will run each
|
||||
app as a separate testcase. To do so, use the command:
|
||||
`adb shell am instrument -w com.android.smoketest.tests/com.android.smoketest.SmokeTestRunner`
|
||||
-->
|
||||
<instrumentation android:name="com.android.smoketest.SmokeTestRunner"
|
||||
android:targetPackage="com.android.smoketest"
|
||||
android:label="System Smoke Tests"/>
|
||||
|
||||
</manifest>
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package com.android.smoketest;
|
||||
|
||||
import com.android.internal.os.RuntimeInit;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityManager.ProcessErrorStateInfo;
|
||||
import android.content.Context;
|
||||
@@ -42,10 +40,18 @@ import java.util.Set;
|
||||
public class ProcessErrorsTest extends AndroidTestCase {
|
||||
|
||||
private static final String TAG = "ProcessErrorsTest";
|
||||
|
||||
|
||||
private final Intent mHomeIntent;
|
||||
|
||||
protected ActivityManager mActivityManager;
|
||||
protected PackageManager mPackageManager;
|
||||
|
||||
public ProcessErrorsTest() {
|
||||
mHomeIntent = new Intent(Intent.ACTION_MAIN);
|
||||
mHomeIntent.addCategory(Intent.CATEGORY_HOME);
|
||||
mHomeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
@@ -59,7 +65,7 @@ public class ProcessErrorsTest extends AndroidTestCase {
|
||||
assertNotNull(mPackageManager);
|
||||
}
|
||||
|
||||
public void testNoProcessErrors() throws Exception {
|
||||
public void testNoProcessErrorsAfterBoot() throws Exception {
|
||||
final String reportMsg = checkForProcessErrors();
|
||||
if (reportMsg != null) {
|
||||
Log.w(TAG, reportMsg);
|
||||
@@ -72,15 +78,79 @@ public class ProcessErrorsTest extends AndroidTestCase {
|
||||
private String checkForProcessErrors() throws Exception {
|
||||
List<ProcessErrorStateInfo> errList;
|
||||
errList = mActivityManager.getProcessesInErrorState();
|
||||
|
||||
|
||||
// note: this contains information about each process that is currently in an error
|
||||
// condition. if the list is empty (null) then "we're good".
|
||||
|
||||
// condition. if the list is empty (null) then "we're good".
|
||||
|
||||
// if the list is non-empty, then it's useful to report the contents of the list
|
||||
final String reportMsg = reportListContents(errList);
|
||||
return reportMsg;
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper function to query the provided {@link PackageManager} for a list of Activities that
|
||||
* can be launched from Launcher.
|
||||
*/
|
||||
static List<ResolveInfo> getLauncherActivities(PackageManager pm) {
|
||||
final Intent launchable = new Intent(Intent.ACTION_MAIN);
|
||||
launchable.addCategory(Intent.CATEGORY_LAUNCHER);
|
||||
final List<ResolveInfo> activities = pm.queryIntentActivities(launchable, 0);
|
||||
return activities;
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper function to create an {@link Intent} to run, given a {@link ResolveInfo} specifying
|
||||
* an activity to be launched.
|
||||
*/
|
||||
static Intent intentForActivity(ResolveInfo app) {
|
||||
// build an Intent to launch the specified app
|
||||
final ComponentName component = new ComponentName(app.activityInfo.packageName,
|
||||
app.activityInfo.name);
|
||||
final Intent intent = new Intent(Intent.ACTION_MAIN);
|
||||
intent.setComponent(component);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
|
||||
return intent;
|
||||
}
|
||||
|
||||
/**
|
||||
* A method to run the specified Activity and return a {@link Collection} of the Activities that
|
||||
* were in an error state, as listed by {@link ActivityManager.getProcessesInErrorState()}.
|
||||
* <p />
|
||||
* The method will launch the app, wait for 7 seconds, check for apps in the error state, send
|
||||
* the Home intent, wait for 2 seconds, and then return.
|
||||
*/
|
||||
public Collection<ProcessErrorStateInfo> runOneActivity(ResolveInfo app) {
|
||||
final long appLaunchWait = 7000;
|
||||
final long homeLaunchWait = 2000;
|
||||
|
||||
Log.i(TAG, String.format("Running activity %s/%s", app.activityInfo.packageName,
|
||||
app.activityInfo.name));
|
||||
|
||||
// launch app, and wait 7 seconds for it to start/settle
|
||||
final Intent intent = intentForActivity(app);
|
||||
getContext().startActivity(intent);
|
||||
try {
|
||||
Thread.sleep(appLaunchWait);
|
||||
} catch (InterruptedException e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
// See if there are any errors
|
||||
final Collection<ProcessErrorStateInfo> errProcs =
|
||||
mActivityManager.getProcessesInErrorState();
|
||||
|
||||
// Send the "home" intent and wait 2 seconds for us to get there
|
||||
getContext().startActivity(mHomeIntent);
|
||||
try {
|
||||
Thread.sleep(homeLaunchWait);
|
||||
} catch (InterruptedException e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
return errProcs;
|
||||
}
|
||||
|
||||
/**
|
||||
* A test that runs all Launcher-launchable activities and verifies that no ANRs or crashes
|
||||
* happened while doing so.
|
||||
@@ -89,46 +159,12 @@ public class ProcessErrorsTest extends AndroidTestCase {
|
||||
* FIXME: first app doesn't go away.
|
||||
*/
|
||||
public void testRunAllActivities() throws Exception {
|
||||
final Intent home = new Intent(Intent.ACTION_MAIN);
|
||||
home.addCategory(Intent.CATEGORY_HOME);
|
||||
home.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
|
||||
final Intent launchable = new Intent(Intent.ACTION_MAIN);
|
||||
launchable.addCategory(Intent.CATEGORY_LAUNCHER);
|
||||
final List<ResolveInfo> activities = mPackageManager.queryIntentActivities(launchable, 0);
|
||||
final Set<ProcessError> errSet = new HashSet<ProcessError>();
|
||||
|
||||
for (ResolveInfo info : activities) {
|
||||
Log.i(TAG, String.format("Got %s/%s", info.activityInfo.packageName,
|
||||
info.activityInfo.name));
|
||||
|
||||
// build an Intent to launch the app
|
||||
final ComponentName component = new ComponentName(info.activityInfo.packageName,
|
||||
info.activityInfo.name);
|
||||
final Intent intent = new Intent(Intent.ACTION_MAIN);
|
||||
intent.setComponent(component);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
|
||||
// launch app, and wait 7 seconds for it to start/settle
|
||||
getContext().startActivity(intent);
|
||||
try {
|
||||
Thread.sleep(7000);
|
||||
} catch (InterruptedException e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
// See if there are any errors
|
||||
Collection<ProcessErrorStateInfo> procs = mActivityManager.getProcessesInErrorState();
|
||||
if (procs != null) {
|
||||
errSet.addAll(ProcessError.fromCollection(procs));
|
||||
}
|
||||
|
||||
// Send the "home" intent and wait 2 seconds for us to get there
|
||||
getContext().startActivity(home);
|
||||
try {
|
||||
Thread.sleep(2000);
|
||||
} catch (InterruptedException e) {
|
||||
// ignore
|
||||
for (ResolveInfo app : getLauncherActivities(mPackageManager)) {
|
||||
final Collection<ProcessErrorStateInfo> errProcs = runOneActivity(app);
|
||||
if (errProcs != null) {
|
||||
errSet.addAll(ProcessError.fromCollection(errProcs));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,7 +174,7 @@ public class ProcessErrorsTest extends AndroidTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
private String reportWrappedListContents(Collection<ProcessError> errList) {
|
||||
String reportWrappedListContents(Collection<ProcessError> errList) {
|
||||
List<ProcessErrorStateInfo> newList = new ArrayList<ProcessErrorStateInfo>(errList.size());
|
||||
for (ProcessError err : errList) {
|
||||
newList.add(err.info);
|
||||
@@ -186,7 +222,7 @@ public class ProcessErrorsTest extends AndroidTestCase {
|
||||
* A {@link ProcessErrorStateInfo} wrapper class that hashes how we want (so that equivalent
|
||||
* crashes are considered equal).
|
||||
*/
|
||||
private static class ProcessError {
|
||||
static class ProcessError {
|
||||
public final ProcessErrorStateInfo info;
|
||||
|
||||
public ProcessError(ProcessErrorStateInfo newInfo) {
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (C) 2012 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.smoketest;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityManager.ProcessErrorStateInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.test.InstrumentationTestRunner;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import junit.framework.TestSuite;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A special test runner which does a test-start for each app in a separate testcase
|
||||
*/
|
||||
public class SmokeTestRunner extends InstrumentationTestRunner {
|
||||
|
||||
private static final String SUITE_NAME = "Smoke Test Suite";
|
||||
|
||||
/**
|
||||
* Returns a single testcase for each app to launch
|
||||
*/
|
||||
@Override
|
||||
public TestSuite getAllTests() {
|
||||
final TestSuite suite = new TestSuite(SUITE_NAME);
|
||||
|
||||
final PackageManager pm = getTargetContext().getPackageManager();
|
||||
final List<ResolveInfo> apps = ProcessErrorsTest.getLauncherActivities(pm);
|
||||
|
||||
// FIXME: figure out some way to control the reported class names for these anonymous
|
||||
// FIXME: class instances.
|
||||
|
||||
final TestCase setupTest = new ProcessErrorsTest() {
|
||||
@Override
|
||||
public void runTest() throws Exception {
|
||||
testSetUpConditions();
|
||||
}
|
||||
};
|
||||
setupTest.setName("testSetUpConditions");
|
||||
suite.addTest(setupTest);
|
||||
|
||||
final TestCase postBootTest = new ProcessErrorsTest() {
|
||||
@Override
|
||||
public void runTest() throws Exception {
|
||||
testNoProcessErrorsAfterBoot();
|
||||
}
|
||||
};
|
||||
postBootTest.setName("testNoProcessErrorsAfterBoot");
|
||||
suite.addTest(postBootTest);
|
||||
|
||||
for (final ResolveInfo app : apps) {
|
||||
final TestCase appTest = new ProcessErrorsTest() {
|
||||
@Override
|
||||
public void runTest() throws Exception {
|
||||
final Set<ProcessError> errSet = new HashSet<ProcessError>();
|
||||
final Collection<ProcessErrorStateInfo> errProcs = runOneActivity(app);
|
||||
if (errProcs != null) {
|
||||
errSet.addAll(ProcessError.fromCollection(errProcs));
|
||||
}
|
||||
|
||||
if (!errSet.isEmpty()) {
|
||||
fail(String.format("Got %d errors: %s", errSet.size(),
|
||||
reportWrappedListContents(errSet)));
|
||||
}
|
||||
}
|
||||
};
|
||||
appTest.setName(app.activityInfo.name);
|
||||
suite.addTest(appTest);
|
||||
}
|
||||
|
||||
return suite;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user