ProcessErrorsTest.java revision bf29121c215b30bed8cb886f1b6c7d71eb36a49d
1/* 2 * Copyright (C) 2008 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 17package com.android.smoketest; 18 19import android.app.ActivityManager; 20import android.app.ActivityManager.ProcessErrorStateInfo; 21import android.content.Context; 22import android.content.ComponentName; 23import android.content.Intent; 24import android.content.pm.PackageManager; 25import android.content.pm.ResolveInfo; 26import android.test.AndroidTestCase; 27import android.util.Log; 28 29import java.util.ArrayList; 30import java.util.Collection; 31import java.util.HashSet; 32import java.util.Iterator; 33import java.util.List; 34import java.util.Set; 35 36/** 37 * This smoke test is designed to quickly sniff for any error conditions 38 * encountered after initial startup. 39 */ 40public class ProcessErrorsTest extends AndroidTestCase { 41 42 private static final String TAG = "ProcessErrorsTest"; 43 44 private final Intent mHomeIntent; 45 46 protected ActivityManager mActivityManager; 47 protected PackageManager mPackageManager; 48 49 public ProcessErrorsTest() { 50 mHomeIntent = new Intent(Intent.ACTION_MAIN); 51 mHomeIntent.addCategory(Intent.CATEGORY_HOME); 52 mHomeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 53 } 54 55 @Override 56 public void setUp() throws Exception { 57 super.setUp(); 58 mActivityManager = (ActivityManager) 59 getContext().getSystemService(Context.ACTIVITY_SERVICE); 60 mPackageManager = getContext().getPackageManager(); 61 } 62 63 public void testSetUpConditions() throws Exception { 64 assertNotNull(mActivityManager); 65 assertNotNull(mPackageManager); 66 } 67 68 public void testNoProcessErrorsAfterBoot() throws Exception { 69 final String reportMsg = checkForProcessErrors(); 70 if (reportMsg != null) { 71 Log.w(TAG, reportMsg); 72 } 73 74 // report a non-empty list back to the test framework 75 assertNull(reportMsg, reportMsg); 76 } 77 78 private String checkForProcessErrors() throws Exception { 79 List<ProcessErrorStateInfo> errList; 80 errList = mActivityManager.getProcessesInErrorState(); 81 82 // note: this contains information about each process that is currently in an error 83 // condition. if the list is empty (null) then "we're good". 84 85 // if the list is non-empty, then it's useful to report the contents of the list 86 final String reportMsg = reportListContents(errList); 87 return reportMsg; 88 } 89 90 /** 91 * A helper function to query the provided {@link PackageManager} for a list of Activities that 92 * can be launched from Launcher. 93 */ 94 static List<ResolveInfo> getLauncherActivities(PackageManager pm) { 95 final Intent launchable = new Intent(Intent.ACTION_MAIN); 96 launchable.addCategory(Intent.CATEGORY_LAUNCHER); 97 final List<ResolveInfo> activities = pm.queryIntentActivities(launchable, 0); 98 return activities; 99 } 100 101 /** 102 * A helper function to create an {@link Intent} to run, given a {@link ResolveInfo} specifying 103 * an activity to be launched. 104 */ 105 static Intent intentForActivity(ResolveInfo app) { 106 // build an Intent to launch the specified app 107 final ComponentName component = new ComponentName(app.activityInfo.packageName, 108 app.activityInfo.name); 109 final Intent intent = new Intent(Intent.ACTION_MAIN); 110 intent.setComponent(component); 111 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 112 intent.addFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 113 return intent; 114 } 115 116 /** 117 * A method to run the specified Activity and return a {@link Collection} of the Activities that 118 * were in an error state, as listed by {@link ActivityManager.getProcessesInErrorState()}. 119 * <p /> 120 * The method will launch the app, wait for 7 seconds, check for apps in the error state, send 121 * the Home intent, wait for 2 seconds, and then return. 122 */ 123 public Collection<ProcessError> runOneActivity(ResolveInfo app) { 124 final long appLaunchWait = 7000; 125 final long homeLaunchWait = 2000; 126 127 Log.i(TAG, String.format("Running activity %s/%s", app.activityInfo.packageName, 128 app.activityInfo.name)); 129 130 // We check for any Crash or ANR dialogs that are already up, and we ignore them. This is 131 // so that we don't report crashes that were caused by prior apps (which those particular 132 // tests should have caught and reported already). Otherwise, test failures would cascade 133 // from the initial broken app to many/all of the tests following that app's launch. 134 final Collection<ProcessError> preErrProcs = 135 ProcessError.fromCollection(mActivityManager.getProcessesInErrorState()); 136 137 // launch app, and wait 7 seconds for it to start/settle 138 final Intent intent = intentForActivity(app); 139 getContext().startActivity(intent); 140 try { 141 Thread.sleep(appLaunchWait); 142 } catch (InterruptedException e) { 143 // ignore 144 } 145 146 // Send the "home" intent and wait 2 seconds for us to get there 147 getContext().startActivity(mHomeIntent); 148 try { 149 Thread.sleep(homeLaunchWait); 150 } catch (InterruptedException e) { 151 // ignore 152 } 153 154 // See if there are any errors. We wait until down here to give ANRs as much time as 155 // possible to occur. 156 final Collection<ProcessError> errProcs = 157 ProcessError.fromCollection(mActivityManager.getProcessesInErrorState()); 158 // Take the difference between the error processes we see now, and the ones that were 159 // present when we started 160 if (errProcs != null && preErrProcs != null) { 161 errProcs.removeAll(preErrProcs); 162 } 163 164 return errProcs; 165 } 166 167 /** 168 * A test that runs all Launcher-launchable activities and verifies that no ANRs or crashes 169 * happened while doing so. 170 */ 171 public void testRunAllActivities() throws Exception { 172 final Set<ProcessError> errSet = new HashSet<ProcessError>(); 173 174 for (ResolveInfo app : getLauncherActivities(mPackageManager)) { 175 final Collection<ProcessError> errProcs = runOneActivity(app); 176 if (errProcs != null) { 177 errSet.addAll(errProcs); 178 } 179 } 180 181 if (!errSet.isEmpty()) { 182 fail(String.format("Got %d errors:\n%s", errSet.size(), 183 reportWrappedListContents(errSet))); 184 } 185 } 186 187 String reportWrappedListContents(Collection<ProcessError> errList) { 188 List<ProcessErrorStateInfo> newList = new ArrayList<ProcessErrorStateInfo>(errList.size()); 189 for (ProcessError err : errList) { 190 newList.add(err.info); 191 } 192 return reportListContents(newList); 193 } 194 195 /** 196 * This helper function will dump the actual error reports. 197 * 198 * @param errList The error report containing one or more error records. 199 * @return Returns a string containing all of the errors. 200 */ 201 private String reportListContents(Collection<ProcessErrorStateInfo> errList) { 202 if (errList == null) return null; 203 204 StringBuilder builder = new StringBuilder(); 205 206 Iterator<ProcessErrorStateInfo> iter = errList.iterator(); 207 while (iter.hasNext()) { 208 ProcessErrorStateInfo entry = iter.next(); 209 210 String condition; 211 switch (entry.condition) { 212 case ActivityManager.ProcessErrorStateInfo.CRASHED: 213 condition = "a CRASH"; 214 break; 215 case ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING: 216 condition = "an ANR"; 217 break; 218 default: 219 condition = "an unknown error"; 220 break; 221 } 222 223 builder.append(String.format("Process %s encountered %s (%s)", entry.processName, 224 condition, entry.shortMsg)); 225 if (entry.condition == ActivityManager.ProcessErrorStateInfo.CRASHED) { 226 builder.append(String.format(" with stack trace:\n%s\n", entry.stackTrace)); 227 } 228 builder.append("\n"); 229 } 230 return builder.toString(); 231 } 232 233 /** 234 * A {@link ProcessErrorStateInfo} wrapper class that hashes how we want (so that equivalent 235 * crashes are considered equal). 236 */ 237 static class ProcessError { 238 public final ProcessErrorStateInfo info; 239 240 public ProcessError(ProcessErrorStateInfo newInfo) { 241 info = newInfo; 242 } 243 244 public static Collection<ProcessError> fromCollection(Collection<ProcessErrorStateInfo> in) 245 { 246 if (in == null) { 247 return null; 248 } 249 250 List<ProcessError> out = new ArrayList<ProcessError>(in.size()); 251 for (ProcessErrorStateInfo info : in) { 252 out.add(new ProcessError(info)); 253 } 254 return out; 255 } 256 257 private boolean strEquals(String a, String b) { 258 if ((a == null) && (b == null)) { 259 return true; 260 } else if ((a == null) || (b == null)) { 261 return false; 262 } else { 263 return a.equals(b); 264 } 265 } 266 267 @Override 268 public boolean equals(Object other) { 269 if (other == null) return false; 270 if (!(other instanceof ProcessError)) return false; 271 ProcessError peOther = (ProcessError) other; 272 273 return (info.condition == peOther.info.condition) 274 && strEquals(info.longMsg, peOther.info.longMsg) 275 && (info.pid == peOther.info.pid) 276 && strEquals(info.processName, peOther.info.processName) 277 && strEquals(info.shortMsg, peOther.info.shortMsg) 278 && strEquals(info.stackTrace, peOther.info.stackTrace) 279 && strEquals(info.tag, peOther.info.tag) 280 && (info.uid == peOther.info.uid); 281 } 282 283 private int hash(Object obj) { 284 if (obj == null) { 285 return 13; 286 } else { 287 return obj.hashCode(); 288 } 289 } 290 291 @Override 292 public int hashCode() { 293 int code = 17; 294 code += info.condition; 295 code *= hash(info.longMsg); 296 code += info.pid; 297 code *= hash(info.processName); 298 code *= hash(info.shortMsg); 299 code *= hash(info.stackTrace); 300 code *= hash(info.tag); 301 code += info.uid; 302 return code; 303 } 304 } 305} 306