ProcessErrorsTest.java revision 5ae3dfe59d22457fb232853990ba4131385ff3f6
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<ProcessErrorStateInfo> 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 // launch app, and wait 7 seconds for it to start/settle 131 final Intent intent = intentForActivity(app); 132 getContext().startActivity(intent); 133 try { 134 Thread.sleep(appLaunchWait); 135 } catch (InterruptedException e) { 136 // ignore 137 } 138 139 // See if there are any errors 140 final Collection<ProcessErrorStateInfo> errProcs = 141 mActivityManager.getProcessesInErrorState(); 142 143 // Send the "home" intent and wait 2 seconds for us to get there 144 getContext().startActivity(mHomeIntent); 145 try { 146 Thread.sleep(homeLaunchWait); 147 } catch (InterruptedException e) { 148 // ignore 149 } 150 151 return errProcs; 152 } 153 154 /** 155 * A test that runs all Launcher-launchable activities and verifies that no ANRs or crashes 156 * happened while doing so. 157 * <p /> 158 * FIXME: Doesn't detect multiple crashing apps properly, since the crash dialog for the 159 * FIXME: first app doesn't go away. 160 */ 161 public void testRunAllActivities() throws Exception { 162 final Set<ProcessError> errSet = new HashSet<ProcessError>(); 163 164 for (ResolveInfo app : getLauncherActivities(mPackageManager)) { 165 final Collection<ProcessErrorStateInfo> errProcs = runOneActivity(app); 166 if (errProcs != null) { 167 errSet.addAll(ProcessError.fromCollection(errProcs)); 168 } 169 } 170 171 if (!errSet.isEmpty()) { 172 fail(String.format("Got %d errors: %s", errSet.size(), 173 reportWrappedListContents(errSet))); 174 } 175 } 176 177 String reportWrappedListContents(Collection<ProcessError> errList) { 178 List<ProcessErrorStateInfo> newList = new ArrayList<ProcessErrorStateInfo>(errList.size()); 179 for (ProcessError err : errList) { 180 newList.add(err.info); 181 } 182 return reportListContents(newList); 183 } 184 185 /** 186 * This helper function will dump the actual error reports. 187 * 188 * @param errList The error report containing one or more error records. 189 * @return Returns a string containing all of the errors. 190 */ 191 private String reportListContents(Collection<ProcessErrorStateInfo> errList) { 192 if (errList == null) return null; 193 194 StringBuilder builder = new StringBuilder(); 195 196 Iterator<ProcessErrorStateInfo> iter = errList.iterator(); 197 while (iter.hasNext()) { 198 ProcessErrorStateInfo entry = iter.next(); 199 200 String condition; 201 switch (entry.condition) { 202 case ActivityManager.ProcessErrorStateInfo.CRASHED: 203 condition = "CRASHED"; 204 break; 205 case ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING: 206 condition = "ANR"; 207 break; 208 default: 209 condition = "<unknown>"; 210 break; 211 } 212 213 builder.append("Process error ").append(condition).append(" "); 214 builder.append(" ").append(entry.shortMsg); 215 builder.append(" detected in ").append(entry.processName).append(" ").append(entry.tag); 216 builder.append("\n"); 217 } 218 return builder.toString(); 219 } 220 221 /** 222 * A {@link ProcessErrorStateInfo} wrapper class that hashes how we want (so that equivalent 223 * crashes are considered equal). 224 */ 225 static class ProcessError { 226 public final ProcessErrorStateInfo info; 227 228 public ProcessError(ProcessErrorStateInfo newInfo) { 229 info = newInfo; 230 } 231 232 public static Collection<ProcessError> fromCollection(Collection<ProcessErrorStateInfo> in) 233 { 234 List<ProcessError> out = new ArrayList<ProcessError>(in.size()); 235 for (ProcessErrorStateInfo info : in) { 236 out.add(new ProcessError(info)); 237 } 238 return out; 239 } 240 241 private boolean strEquals(String a, String b) { 242 if ((a == null) && (b == null)) { 243 return true; 244 } else if ((a == null) || (b == null)) { 245 return false; 246 } else { 247 return a.equals(b); 248 } 249 } 250 251 @Override 252 public boolean equals(Object other) { 253 if (other == null) return false; 254 if (!(other instanceof ProcessError)) return false; 255 ProcessError peOther = (ProcessError) other; 256 257 return (info.condition == peOther.info.condition) 258 && strEquals(info.longMsg, peOther.info.longMsg) 259 && (info.pid == peOther.info.pid) 260 && strEquals(info.processName, peOther.info.processName) 261 && strEquals(info.shortMsg, peOther.info.shortMsg) 262 && strEquals(info.stackTrace, peOther.info.stackTrace) 263 && strEquals(info.tag, peOther.info.tag) 264 && (info.uid == peOther.info.uid); 265 } 266 267 private int hash(Object obj) { 268 if (obj == null) { 269 return 13; 270 } else { 271 return obj.hashCode(); 272 } 273 } 274 275 @Override 276 public int hashCode() { 277 int code = 17; 278 code += info.condition; 279 code *= hash(info.longMsg); 280 code += info.pid; 281 code *= hash(info.processName); 282 code *= hash(info.shortMsg); 283 code *= hash(info.stackTrace); 284 code *= hash(info.tag); 285 code += info.uid; 286 return code; 287 } 288 } 289} 290