Merge "Refactor smoke tests to enable one-app-per-testcase runs"

This commit is contained in:
Omari Stephens
2012-03-12 16:38:35 -07:00
committed by Android (Google) Code Review
3 changed files with 193 additions and 53 deletions

View File

@@ -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>

View File

@@ -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) {

View File

@@ -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;
}
}