blob: 03c2923900f6ec30f96419cd439553b74763b336 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080019import android.app.ActivityManager;
Omari Stephens9f5a5112012-01-18 22:36:42 -080020import android.app.ActivityManager.ProcessErrorStateInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080021import android.content.Context;
Omari Stephens9f5a5112012-01-18 22:36:42 -080022import android.content.ComponentName;
23import android.content.Intent;
24import android.content.pm.PackageManager;
25import android.content.pm.ResolveInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026import android.test.AndroidTestCase;
27import android.util.Log;
28
Omari Stephens9f5a5112012-01-18 22:36:42 -080029import java.util.ArrayList;
30import java.util.Collection;
Omari Stephensa6a35362012-04-25 21:07:57 -070031import java.util.Collections;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032import java.util.Iterator;
Omari Stephensa6a35362012-04-25 21:07:57 -070033import java.util.LinkedHashSet;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import java.util.List;
Omari Stephens9f5a5112012-01-18 22:36:42 -080035import java.util.Set;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036
37/**
Omari Stephensa6a35362012-04-25 21:07:57 -070038 * This smoke test is designed to check for crashes and ANRs in an attempt to quickly determine if
39 * all minimal functionality in the build is working properly.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040 */
41public class ProcessErrorsTest extends AndroidTestCase {
Omari Stephensa6a35362012-04-25 21:07:57 -070042
Omari Stephens9f5a5112012-01-18 22:36:42 -080043 private static final String TAG = "ProcessErrorsTest";
Omari Stephens5ae3dfe2012-03-08 18:27:33 -080044
45 private final Intent mHomeIntent;
46
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047 protected ActivityManager mActivityManager;
Omari Stephens9f5a5112012-01-18 22:36:42 -080048 protected PackageManager mPackageManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049
Omari Stephensa6a35362012-04-25 21:07:57 -070050 /**
51 * Used to buffer asynchronously-caused crashes and ANRs so that we can have a big fail-party
52 * in the catch-all testCase.
53 */
54 private static final Collection<ProcessError> mAsyncErrors =
55 Collections.synchronizedSet(new LinkedHashSet<ProcessError>());
56
Omari Stephens5ae3dfe2012-03-08 18:27:33 -080057 public ProcessErrorsTest() {
58 mHomeIntent = new Intent(Intent.ACTION_MAIN);
59 mHomeIntent.addCategory(Intent.CATEGORY_HOME);
60 mHomeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
61 }
62
Omari Stephensa6a35362012-04-25 21:07:57 -070063 /**
64 * {@inheritDoc}
65 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080066 @Override
67 public void setUp() throws Exception {
68 super.setUp();
Omari Stephensa6a35362012-04-25 21:07:57 -070069 // First, make sure we have a Context
70 assertNotNull("getContext() returned null!", getContext());
71
Omari Stephens9f5a5112012-01-18 22:36:42 -080072 mActivityManager = (ActivityManager)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073 getContext().getSystemService(Context.ACTIVITY_SERVICE);
Omari Stephens9f5a5112012-01-18 22:36:42 -080074 mPackageManager = getContext().getPackageManager();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075 }
76
77 public void testSetUpConditions() throws Exception {
78 assertNotNull(mActivityManager);
Omari Stephens9f5a5112012-01-18 22:36:42 -080079 assertNotNull(mPackageManager);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080080 }
81
Omari Stephens5ae3dfe2012-03-08 18:27:33 -080082 public void testNoProcessErrorsAfterBoot() throws Exception {
Omari Stephens9f5a5112012-01-18 22:36:42 -080083 final String reportMsg = checkForProcessErrors();
84 if (reportMsg != null) {
85 Log.w(TAG, reportMsg);
86 }
87
88 // report a non-empty list back to the test framework
89 assertNull(reportMsg, reportMsg);
90 }
91
Omari Stephensa6a35362012-04-25 21:07:57 -070092 /**
93 * A test that runs all Launcher-launchable activities and verifies that no ANRs or crashes
94 * happened while doing so.
95 */
96 public void testRunAllActivities() throws Exception {
97 final Set<ProcessError> errSet = new LinkedHashSet<ProcessError>();
Omari Stephens5ae3dfe2012-03-08 18:27:33 -080098
Omari Stephensa6a35362012-04-25 21:07:57 -070099 for (ResolveInfo app : getLauncherActivities(mPackageManager)) {
100 final Collection<ProcessError> errProcs = runOneActivity(app);
101 if (errProcs != null) {
102 errSet.addAll(errProcs);
103 }
104 }
Omari Stephens5ae3dfe2012-03-08 18:27:33 -0800105
Omari Stephensa6a35362012-04-25 21:07:57 -0700106 if (!errSet.isEmpty()) {
107 fail(String.format("Got %d errors:\n%s", errSet.size(),
108 reportWrappedListContents(errSet)));
109 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110 }
Omari Stephens9f5a5112012-01-18 22:36:42 -0800111
112 /**
Omari Stephensa6a35362012-04-25 21:07:57 -0700113 * This test checks for asynchronously-caused errors (crashes or ANRs) and fails in case any
114 * were found. This prevents us from needing to fail unrelated testcases when, for instance
115 * a background thread causes a crash or ANR.
116 * <p />
117 * Because this behavior depends on the contents of static member {@link mAsyncErrors}, we clear
118 * that state here as a side-effect so that if two successive runs happen in the same process,
119 * the asynchronous errors in the second test run won't include errors produced during the first
120 * run.
Omari Stephens5ae3dfe2012-03-08 18:27:33 -0800121 */
Omari Stephensa6a35362012-04-25 21:07:57 -0700122 public void testZZReportAsyncErrors() throws Exception {
123 try {
124 if (!mAsyncErrors.isEmpty()) {
125 fail(String.format("Got %d asynchronous errors:\n%s", mAsyncErrors.size(),
126 reportWrappedListContents(mAsyncErrors)));
127 }
128 } finally {
129 // Reset state just in case we should get another set of runs in the same process
130 mAsyncErrors.clear();
131 }
Omari Stephens5ae3dfe2012-03-08 18:27:33 -0800132 }
133
Omari Stephens5ae3dfe2012-03-08 18:27:33 -0800134
135 /**
136 * A method to run the specified Activity and return a {@link Collection} of the Activities that
137 * were in an error state, as listed by {@link ActivityManager.getProcessesInErrorState()}.
138 * <p />
139 * The method will launch the app, wait for 7 seconds, check for apps in the error state, send
140 * the Home intent, wait for 2 seconds, and then return.
141 */
Omari Stephensad8037e2012-03-13 21:52:13 -0700142 public Collection<ProcessError> runOneActivity(ResolveInfo app) {
Omari Stephens5ae3dfe2012-03-08 18:27:33 -0800143 final long appLaunchWait = 7000;
144 final long homeLaunchWait = 2000;
145
146 Log.i(TAG, String.format("Running activity %s/%s", app.activityInfo.packageName,
147 app.activityInfo.name));
148
Omari Stephensad8037e2012-03-13 21:52:13 -0700149 // We check for any Crash or ANR dialogs that are already up, and we ignore them. This is
150 // so that we don't report crashes that were caused by prior apps (which those particular
Omari Stephensa6a35362012-04-25 21:07:57 -0700151 // tests should have caught and reported already).
Omari Stephensad8037e2012-03-13 21:52:13 -0700152 final Collection<ProcessError> preErrProcs =
153 ProcessError.fromCollection(mActivityManager.getProcessesInErrorState());
154
Omari Stephens5ae3dfe2012-03-08 18:27:33 -0800155 // launch app, and wait 7 seconds for it to start/settle
156 final Intent intent = intentForActivity(app);
157 getContext().startActivity(intent);
158 try {
159 Thread.sleep(appLaunchWait);
160 } catch (InterruptedException e) {
161 // ignore
162 }
163
Omari Stephens5ae3dfe2012-03-08 18:27:33 -0800164 // Send the "home" intent and wait 2 seconds for us to get there
165 getContext().startActivity(mHomeIntent);
166 try {
167 Thread.sleep(homeLaunchWait);
168 } catch (InterruptedException e) {
169 // ignore
170 }
171
Omari Stephensbf291212012-03-13 23:17:45 -0700172 // See if there are any errors. We wait until down here to give ANRs as much time as
173 // possible to occur.
174 final Collection<ProcessError> errProcs =
175 ProcessError.fromCollection(mActivityManager.getProcessesInErrorState());
Omari Stephensa6a35362012-04-25 21:07:57 -0700176
177 // Distinguish the asynchronous crashes/ANRs from the synchronous ones by checking the
178 // crash package name against the package name for {@code app}
179 if (errProcs != null) {
180 Iterator<ProcessError> errIter = errProcs.iterator();
181 while (errIter.hasNext()) {
182 ProcessError err = errIter.next();
183 if (!packageMatches(app, err)) {
184 // async! Drop into mAsyncErrors and don't report now
185 mAsyncErrors.add(err);
186 errIter.remove();
187 }
188 }
189 }
190 // Take the difference between the remaining current error processes and the ones that were
191 // present when we started. The result is guaranteed to be:
192 // 1) Errors that are pertinent to this app's package
193 // 2) Errors that are pertinent to this particular app invocation
Omari Stephensbf291212012-03-13 23:17:45 -0700194 if (errProcs != null && preErrProcs != null) {
195 errProcs.removeAll(preErrProcs);
196 }
197
Omari Stephens5ae3dfe2012-03-08 18:27:33 -0800198 return errProcs;
199 }
200
Omari Stephensa6a35362012-04-25 21:07:57 -0700201 private String checkForProcessErrors() throws Exception {
202 List<ProcessErrorStateInfo> errList;
203 errList = mActivityManager.getProcessesInErrorState();
Omari Stephens9f5a5112012-01-18 22:36:42 -0800204
Omari Stephensa6a35362012-04-25 21:07:57 -0700205 // note: this contains information about each process that is currently in an error
206 // condition. if the list is empty (null) then "we're good".
Omari Stephens9f5a5112012-01-18 22:36:42 -0800207
Omari Stephensa6a35362012-04-25 21:07:57 -0700208 // if the list is non-empty, then it's useful to report the contents of the list
209 final String reportMsg = reportListContents(errList);
210 return reportMsg;
Omari Stephens9f5a5112012-01-18 22:36:42 -0800211 }
212
Omari Stephensa6a35362012-04-25 21:07:57 -0700213 /**
214 * A helper function that checks whether the specified error could have been caused by the
215 * specified app.
216 *
217 * @param app The app to check against
218 * @param err The error that we're considering
219 */
220 private static boolean packageMatches(ResolveInfo app, ProcessError err) {
221 final String appPkg = app.activityInfo.packageName;
222 final String errPkg = err.info.processName;
223 Log.d(TAG, String.format("packageMatches(%s, %s)", appPkg, errPkg));
224 return appPkg.equals(errPkg);
225 }
226
227 /**
228 * A helper function to query the provided {@link PackageManager} for a list of Activities that
229 * can be launched from Launcher.
230 */
231 static List<ResolveInfo> getLauncherActivities(PackageManager pm) {
232 final Intent launchable = new Intent(Intent.ACTION_MAIN);
233 launchable.addCategory(Intent.CATEGORY_LAUNCHER);
234 final List<ResolveInfo> activities = pm.queryIntentActivities(launchable, 0);
235 return activities;
236 }
237
238 /**
239 * A helper function to create an {@link Intent} to run, given a {@link ResolveInfo} specifying
240 * an activity to be launched.
241 */
242 static Intent intentForActivity(ResolveInfo app) {
243 final ComponentName component = new ComponentName(app.activityInfo.packageName,
244 app.activityInfo.name);
245 final Intent intent = new Intent(Intent.ACTION_MAIN);
246 intent.setComponent(component);
247 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
248 intent.addFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
249 return intent;
250 }
251
252 /**
253 * Report error reports for {@link ProcessErrorStateInfo} instances that are wrapped inside of
254 * {@link ProcessError} instances. Just unwraps and calls
255 * {@see reportListContents(Collection<ProcessErrorStateInfo>)}.
256 */
257 static String reportWrappedListContents(Collection<ProcessError> errList) {
Omari Stephens9f5a5112012-01-18 22:36:42 -0800258 List<ProcessErrorStateInfo> newList = new ArrayList<ProcessErrorStateInfo>(errList.size());
259 for (ProcessError err : errList) {
260 newList.add(err.info);
261 }
262 return reportListContents(newList);
263 }
264
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800265 /**
266 * This helper function will dump the actual error reports.
267 *
268 * @param errList The error report containing one or more error records.
269 * @return Returns a string containing all of the errors.
270 */
Omari Stephensa6a35362012-04-25 21:07:57 -0700271 private static String reportListContents(Collection<ProcessErrorStateInfo> errList) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800272 if (errList == null) return null;
273
274 StringBuilder builder = new StringBuilder();
275
Omari Stephens9f5a5112012-01-18 22:36:42 -0800276 Iterator<ProcessErrorStateInfo> iter = errList.iterator();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800277 while (iter.hasNext()) {
Omari Stephens9f5a5112012-01-18 22:36:42 -0800278 ProcessErrorStateInfo entry = iter.next();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800279
280 String condition;
281 switch (entry.condition) {
282 case ActivityManager.ProcessErrorStateInfo.CRASHED:
Omari Stephensbf291212012-03-13 23:17:45 -0700283 condition = "a CRASH";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800284 break;
285 case ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING:
Omari Stephensbf291212012-03-13 23:17:45 -0700286 condition = "an ANR";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800287 break;
288 default:
Omari Stephensbf291212012-03-13 23:17:45 -0700289 condition = "an unknown error";
Dan Egnorb7f03672009-12-09 16:22:32 -0800290 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800291 }
292
Omari Stephensbf291212012-03-13 23:17:45 -0700293 builder.append(String.format("Process %s encountered %s (%s)", entry.processName,
294 condition, entry.shortMsg));
295 if (entry.condition == ActivityManager.ProcessErrorStateInfo.CRASHED) {
296 builder.append(String.format(" with stack trace:\n%s\n", entry.stackTrace));
297 }
Omari Stephens9f5a5112012-01-18 22:36:42 -0800298 builder.append("\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800299 }
300 return builder.toString();
301 }
Omari Stephens9f5a5112012-01-18 22:36:42 -0800302
303 /**
304 * A {@link ProcessErrorStateInfo} wrapper class that hashes how we want (so that equivalent
305 * crashes are considered equal).
306 */
Omari Stephens5ae3dfe2012-03-08 18:27:33 -0800307 static class ProcessError {
Omari Stephens9f5a5112012-01-18 22:36:42 -0800308 public final ProcessErrorStateInfo info;
309
310 public ProcessError(ProcessErrorStateInfo newInfo) {
311 info = newInfo;
312 }
313
314 public static Collection<ProcessError> fromCollection(Collection<ProcessErrorStateInfo> in)
315 {
Omari Stephensad8037e2012-03-13 21:52:13 -0700316 if (in == null) {
317 return null;
318 }
319
Omari Stephens9f5a5112012-01-18 22:36:42 -0800320 List<ProcessError> out = new ArrayList<ProcessError>(in.size());
321 for (ProcessErrorStateInfo info : in) {
322 out.add(new ProcessError(info));
323 }
324 return out;
325 }
326
327 private boolean strEquals(String a, String b) {
328 if ((a == null) && (b == null)) {
329 return true;
330 } else if ((a == null) || (b == null)) {
331 return false;
332 } else {
333 return a.equals(b);
334 }
335 }
336
337 @Override
338 public boolean equals(Object other) {
339 if (other == null) return false;
340 if (!(other instanceof ProcessError)) return false;
341 ProcessError peOther = (ProcessError) other;
342
343 return (info.condition == peOther.info.condition)
344 && strEquals(info.longMsg, peOther.info.longMsg)
345 && (info.pid == peOther.info.pid)
346 && strEquals(info.processName, peOther.info.processName)
347 && strEquals(info.shortMsg, peOther.info.shortMsg)
348 && strEquals(info.stackTrace, peOther.info.stackTrace)
349 && strEquals(info.tag, peOther.info.tag)
350 && (info.uid == peOther.info.uid);
351 }
352
353 private int hash(Object obj) {
354 if (obj == null) {
355 return 13;
356 } else {
357 return obj.hashCode();
358 }
359 }
360
361 @Override
362 public int hashCode() {
363 int code = 17;
364 code += info.condition;
365 code *= hash(info.longMsg);
366 code += info.pid;
367 code *= hash(info.processName);
368 code *= hash(info.shortMsg);
369 code *= hash(info.stackTrace);
370 code *= hash(info.tag);
371 code += info.uid;
372 return code;
373 }
374 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800375}