ProcessErrorsTest.java revision 9f5a511a08c56906edac40230ac97d670d7ad0c6
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 com.android.internal.os.RuntimeInit; 20 21import android.app.ActivityManager; 22import android.app.ActivityManager.ProcessErrorStateInfo; 23import android.content.Context; 24import android.content.ComponentName; 25import android.content.Intent; 26import android.content.pm.PackageManager; 27import android.content.pm.ResolveInfo; 28import android.test.AndroidTestCase; 29import android.util.Log; 30 31import java.util.ArrayList; 32import java.util.Collection; 33import java.util.HashSet; 34import java.util.Iterator; 35import java.util.List; 36import java.util.Set; 37 38/** 39 * This smoke test is designed to quickly sniff for any error conditions 40 * encountered after initial startup. 41 */ 42public class ProcessErrorsTest extends AndroidTestCase { 43 44 private static final String TAG = "ProcessErrorsTest"; 45 46 protected ActivityManager mActivityManager; 47 protected PackageManager mPackageManager; 48 49 @Override 50 public void setUp() throws Exception { 51 super.setUp(); 52 mActivityManager = (ActivityManager) 53 getContext().getSystemService(Context.ACTIVITY_SERVICE); 54 mPackageManager = getContext().getPackageManager(); 55 } 56 57 public void testSetUpConditions() throws Exception { 58 assertNotNull(mActivityManager); 59 assertNotNull(mPackageManager); 60 } 61 62 public void testNoProcessErrors() throws Exception { 63 final String reportMsg = checkForProcessErrors(); 64 if (reportMsg != null) { 65 Log.w(TAG, reportMsg); 66 } 67 68 // report a non-empty list back to the test framework 69 assertNull(reportMsg, reportMsg); 70 } 71 72 private String checkForProcessErrors() throws Exception { 73 List<ProcessErrorStateInfo> errList; 74 errList = mActivityManager.getProcessesInErrorState(); 75 76 // note: this contains information about each process that is currently in an error 77 // condition. if the list is empty (null) then "we're good". 78 79 // if the list is non-empty, then it's useful to report the contents of the list 80 final String reportMsg = reportListContents(errList); 81 return reportMsg; 82 } 83 84 /** 85 * A test that runs all Launcher-launchable activities and verifies that no ANRs or crashes 86 * happened while doing so. 87 * <p /> 88 * FIXME: Doesn't detect multiple crashing apps properly, since the crash dialog for the 89 * FIXME: first app doesn't go away. 90 */ 91 public void testRunAllActivities() throws Exception { 92 final Intent home = new Intent(Intent.ACTION_MAIN); 93 home.addCategory(Intent.CATEGORY_HOME); 94 home.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 95 96 final Intent launchable = new Intent(Intent.ACTION_MAIN); 97 launchable.addCategory(Intent.CATEGORY_LAUNCHER); 98 final List<ResolveInfo> activities = mPackageManager.queryIntentActivities(launchable, 0); 99 final Set<ProcessError> errSet = new HashSet<ProcessError>(); 100 101 for (ResolveInfo info : activities) { 102 Log.i(TAG, String.format("Got %s/%s", info.activityInfo.packageName, 103 info.activityInfo.name)); 104 105 // build an Intent to launch the app 106 final ComponentName component = new ComponentName(info.activityInfo.packageName, 107 info.activityInfo.name); 108 final Intent intent = new Intent(Intent.ACTION_MAIN); 109 intent.setComponent(component); 110 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 111 112 // launch app, and wait 7 seconds for it to start/settle 113 getContext().startActivity(intent); 114 try { 115 Thread.sleep(7000); 116 } catch (InterruptedException e) { 117 // ignore 118 } 119 120 // See if there are any errors 121 Collection<ProcessErrorStateInfo> procs = mActivityManager.getProcessesInErrorState(); 122 if (procs != null) { 123 errSet.addAll(ProcessError.fromCollection(procs)); 124 } 125 126 // Send the "home" intent and wait 2 seconds for us to get there 127 getContext().startActivity(home); 128 try { 129 Thread.sleep(2000); 130 } catch (InterruptedException e) { 131 // ignore 132 } 133 } 134 135 if (!errSet.isEmpty()) { 136 fail(String.format("Got %d errors: %s", errSet.size(), 137 reportWrappedListContents(errSet))); 138 } 139 } 140 141 private String reportWrappedListContents(Collection<ProcessError> errList) { 142 List<ProcessErrorStateInfo> newList = new ArrayList<ProcessErrorStateInfo>(errList.size()); 143 for (ProcessError err : errList) { 144 newList.add(err.info); 145 } 146 return reportListContents(newList); 147 } 148 149 /** 150 * This helper function will dump the actual error reports. 151 * 152 * @param errList The error report containing one or more error records. 153 * @return Returns a string containing all of the errors. 154 */ 155 private String reportListContents(Collection<ProcessErrorStateInfo> errList) { 156 if (errList == null) return null; 157 158 StringBuilder builder = new StringBuilder(); 159 160 Iterator<ProcessErrorStateInfo> iter = errList.iterator(); 161 while (iter.hasNext()) { 162 ProcessErrorStateInfo entry = iter.next(); 163 164 String condition; 165 switch (entry.condition) { 166 case ActivityManager.ProcessErrorStateInfo.CRASHED: 167 condition = "CRASHED"; 168 break; 169 case ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING: 170 condition = "ANR"; 171 break; 172 default: 173 condition = "<unknown>"; 174 break; 175 } 176 177 builder.append("Process error ").append(condition).append(" "); 178 builder.append(" ").append(entry.shortMsg); 179 builder.append(" detected in ").append(entry.processName).append(" ").append(entry.tag); 180 builder.append("\n"); 181 } 182 return builder.toString(); 183 } 184 185 /** 186 * A {@link ProcessErrorStateInfo} wrapper class that hashes how we want (so that equivalent 187 * crashes are considered equal). 188 */ 189 private static class ProcessError { 190 public final ProcessErrorStateInfo info; 191 192 public ProcessError(ProcessErrorStateInfo newInfo) { 193 info = newInfo; 194 } 195 196 public static Collection<ProcessError> fromCollection(Collection<ProcessErrorStateInfo> in) 197 { 198 List<ProcessError> out = new ArrayList<ProcessError>(in.size()); 199 for (ProcessErrorStateInfo info : in) { 200 out.add(new ProcessError(info)); 201 } 202 return out; 203 } 204 205 private boolean strEquals(String a, String b) { 206 if ((a == null) && (b == null)) { 207 return true; 208 } else if ((a == null) || (b == null)) { 209 return false; 210 } else { 211 return a.equals(b); 212 } 213 } 214 215 @Override 216 public boolean equals(Object other) { 217 if (other == null) return false; 218 if (!(other instanceof ProcessError)) return false; 219 ProcessError peOther = (ProcessError) other; 220 221 return (info.condition == peOther.info.condition) 222 && strEquals(info.longMsg, peOther.info.longMsg) 223 && (info.pid == peOther.info.pid) 224 && strEquals(info.processName, peOther.info.processName) 225 && strEquals(info.shortMsg, peOther.info.shortMsg) 226 && strEquals(info.stackTrace, peOther.info.stackTrace) 227 && strEquals(info.tag, peOther.info.tag) 228 && (info.uid == peOther.info.uid); 229 } 230 231 private int hash(Object obj) { 232 if (obj == null) { 233 return 13; 234 } else { 235 return obj.hashCode(); 236 } 237 } 238 239 @Override 240 public int hashCode() { 241 int code = 17; 242 code += info.condition; 243 code *= hash(info.longMsg); 244 code += info.pid; 245 code *= hash(info.processName); 246 code *= hash(info.shortMsg); 247 code *= hash(info.stackTrace); 248 code *= hash(info.tag); 249 code += info.uid; 250 return code; 251 } 252 } 253} 254