blob: 946299b599e6d291cbf7ee6f6ba55b8f15354472 [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);
Brett Chabotc0104142014-01-02 14:41:21 -0800157 if (intent == null) {
158 Log.i(TAG, String.format("Activity %s/%s is disabled, skipping",
159 app.activityInfo.packageName, app.activityInfo.name));
160 return Collections.EMPTY_LIST;
161 }
Omari Stephens5ae3dfe2012-03-08 18:27:33 -0800162 getContext().startActivity(intent);
163 try {
164 Thread.sleep(appLaunchWait);
165 } catch (InterruptedException e) {
166 // ignore
167 }
168
Omari Stephens5ae3dfe2012-03-08 18:27:33 -0800169 // Send the "home" intent and wait 2 seconds for us to get there
170 getContext().startActivity(mHomeIntent);
171 try {
172 Thread.sleep(homeLaunchWait);
173 } catch (InterruptedException e) {
174 // ignore
175 }
176
Omari Stephensbf291212012-03-13 23:17:45 -0700177 // See if there are any errors. We wait until down here to give ANRs as much time as
178 // possible to occur.
179 final Collection<ProcessError> errProcs =
180 ProcessError.fromCollection(mActivityManager.getProcessesInErrorState());
Omari Stephensa6a35362012-04-25 21:07:57 -0700181
182 // Distinguish the asynchronous crashes/ANRs from the synchronous ones by checking the
183 // crash package name against the package name for {@code app}
184 if (errProcs != null) {
185 Iterator<ProcessError> errIter = errProcs.iterator();
186 while (errIter.hasNext()) {
187 ProcessError err = errIter.next();
188 if (!packageMatches(app, err)) {
189 // async! Drop into mAsyncErrors and don't report now
190 mAsyncErrors.add(err);
191 errIter.remove();
192 }
193 }
194 }
195 // Take the difference between the remaining current error processes and the ones that were
196 // present when we started. The result is guaranteed to be:
197 // 1) Errors that are pertinent to this app's package
198 // 2) Errors that are pertinent to this particular app invocation
Omari Stephensbf291212012-03-13 23:17:45 -0700199 if (errProcs != null && preErrProcs != null) {
200 errProcs.removeAll(preErrProcs);
201 }
202
Omari Stephens5ae3dfe2012-03-08 18:27:33 -0800203 return errProcs;
204 }
205
Omari Stephensa6a35362012-04-25 21:07:57 -0700206 private String checkForProcessErrors() throws Exception {
207 List<ProcessErrorStateInfo> errList;
208 errList = mActivityManager.getProcessesInErrorState();
Omari Stephens9f5a5112012-01-18 22:36:42 -0800209
Omari Stephensa6a35362012-04-25 21:07:57 -0700210 // note: this contains information about each process that is currently in an error
211 // condition. if the list is empty (null) then "we're good".
Omari Stephens9f5a5112012-01-18 22:36:42 -0800212
Omari Stephensa6a35362012-04-25 21:07:57 -0700213 // if the list is non-empty, then it's useful to report the contents of the list
214 final String reportMsg = reportListContents(errList);
215 return reportMsg;
Omari Stephens9f5a5112012-01-18 22:36:42 -0800216 }
217
Omari Stephensa6a35362012-04-25 21:07:57 -0700218 /**
219 * A helper function that checks whether the specified error could have been caused by the
220 * specified app.
221 *
222 * @param app The app to check against
223 * @param err The error that we're considering
224 */
225 private static boolean packageMatches(ResolveInfo app, ProcessError err) {
226 final String appPkg = app.activityInfo.packageName;
227 final String errPkg = err.info.processName;
228 Log.d(TAG, String.format("packageMatches(%s, %s)", appPkg, errPkg));
229 return appPkg.equals(errPkg);
230 }
231
232 /**
233 * A helper function to query the provided {@link PackageManager} for a list of Activities that
234 * can be launched from Launcher.
235 */
236 static List<ResolveInfo> getLauncherActivities(PackageManager pm) {
237 final Intent launchable = new Intent(Intent.ACTION_MAIN);
238 launchable.addCategory(Intent.CATEGORY_LAUNCHER);
239 final List<ResolveInfo> activities = pm.queryIntentActivities(launchable, 0);
240 return activities;
241 }
242
243 /**
244 * A helper function to create an {@link Intent} to run, given a {@link ResolveInfo} specifying
245 * an activity to be launched.
Brett Chabotc0104142014-01-02 14:41:21 -0800246 *
247 * @return the {@link Intent} or <code>null</code> if given app is disabled
Omari Stephensa6a35362012-04-25 21:07:57 -0700248 */
Brett Chabotc0104142014-01-02 14:41:21 -0800249 Intent intentForActivity(ResolveInfo app) {
Omari Stephensa6a35362012-04-25 21:07:57 -0700250 final ComponentName component = new ComponentName(app.activityInfo.packageName,
251 app.activityInfo.name);
Brett Chabotc0104142014-01-02 14:41:21 -0800252 if (getContext().getPackageManager().getComponentEnabledSetting(component) ==
253 PackageManager.COMPONENT_ENABLED_STATE_DISABLED) {
254 return null;
255 }
Omari Stephensa6a35362012-04-25 21:07:57 -0700256 final Intent intent = new Intent(Intent.ACTION_MAIN);
257 intent.setComponent(component);
258 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
259 intent.addFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
260 return intent;
261 }
262
263 /**
264 * Report error reports for {@link ProcessErrorStateInfo} instances that are wrapped inside of
265 * {@link ProcessError} instances. Just unwraps and calls
266 * {@see reportListContents(Collection<ProcessErrorStateInfo>)}.
267 */
268 static String reportWrappedListContents(Collection<ProcessError> errList) {
Omari Stephens9f5a5112012-01-18 22:36:42 -0800269 List<ProcessErrorStateInfo> newList = new ArrayList<ProcessErrorStateInfo>(errList.size());
270 for (ProcessError err : errList) {
271 newList.add(err.info);
272 }
273 return reportListContents(newList);
274 }
275
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800276 /**
277 * This helper function will dump the actual error reports.
278 *
279 * @param errList The error report containing one or more error records.
280 * @return Returns a string containing all of the errors.
281 */
Omari Stephensa6a35362012-04-25 21:07:57 -0700282 private static String reportListContents(Collection<ProcessErrorStateInfo> errList) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800283 if (errList == null) return null;
284
285 StringBuilder builder = new StringBuilder();
286
Omari Stephens9f5a5112012-01-18 22:36:42 -0800287 Iterator<ProcessErrorStateInfo> iter = errList.iterator();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800288 while (iter.hasNext()) {
Omari Stephens9f5a5112012-01-18 22:36:42 -0800289 ProcessErrorStateInfo entry = iter.next();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800290
291 String condition;
292 switch (entry.condition) {
293 case ActivityManager.ProcessErrorStateInfo.CRASHED:
Omari Stephensbf291212012-03-13 23:17:45 -0700294 condition = "a CRASH";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800295 break;
296 case ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING:
Omari Stephensbf291212012-03-13 23:17:45 -0700297 condition = "an ANR";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800298 break;
299 default:
Omari Stephensbf291212012-03-13 23:17:45 -0700300 condition = "an unknown error";
Dan Egnorb7f03672009-12-09 16:22:32 -0800301 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800302 }
303
Omari Stephensbf291212012-03-13 23:17:45 -0700304 builder.append(String.format("Process %s encountered %s (%s)", entry.processName,
305 condition, entry.shortMsg));
306 if (entry.condition == ActivityManager.ProcessErrorStateInfo.CRASHED) {
307 builder.append(String.format(" with stack trace:\n%s\n", entry.stackTrace));
308 }
Omari Stephens9f5a5112012-01-18 22:36:42 -0800309 builder.append("\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800310 }
311 return builder.toString();
312 }
Omari Stephens9f5a5112012-01-18 22:36:42 -0800313
314 /**
315 * A {@link ProcessErrorStateInfo} wrapper class that hashes how we want (so that equivalent
316 * crashes are considered equal).
317 */
Omari Stephens5ae3dfe2012-03-08 18:27:33 -0800318 static class ProcessError {
Omari Stephens9f5a5112012-01-18 22:36:42 -0800319 public final ProcessErrorStateInfo info;
320
321 public ProcessError(ProcessErrorStateInfo newInfo) {
322 info = newInfo;
323 }
324
325 public static Collection<ProcessError> fromCollection(Collection<ProcessErrorStateInfo> in)
326 {
Omari Stephensad8037e2012-03-13 21:52:13 -0700327 if (in == null) {
328 return null;
329 }
330
Omari Stephens9f5a5112012-01-18 22:36:42 -0800331 List<ProcessError> out = new ArrayList<ProcessError>(in.size());
332 for (ProcessErrorStateInfo info : in) {
333 out.add(new ProcessError(info));
334 }
335 return out;
336 }
337
338 private boolean strEquals(String a, String b) {
339 if ((a == null) && (b == null)) {
340 return true;
341 } else if ((a == null) || (b == null)) {
342 return false;
343 } else {
344 return a.equals(b);
345 }
346 }
347
348 @Override
349 public boolean equals(Object other) {
350 if (other == null) return false;
351 if (!(other instanceof ProcessError)) return false;
352 ProcessError peOther = (ProcessError) other;
353
354 return (info.condition == peOther.info.condition)
355 && strEquals(info.longMsg, peOther.info.longMsg)
356 && (info.pid == peOther.info.pid)
357 && strEquals(info.processName, peOther.info.processName)
358 && strEquals(info.shortMsg, peOther.info.shortMsg)
359 && strEquals(info.stackTrace, peOther.info.stackTrace)
360 && strEquals(info.tag, peOther.info.tag)
361 && (info.uid == peOther.info.uid);
362 }
363
364 private int hash(Object obj) {
365 if (obj == null) {
366 return 13;
367 } else {
368 return obj.hashCode();
369 }
370 }
371
372 @Override
373 public int hashCode() {
374 int code = 17;
375 code += info.condition;
376 code *= hash(info.longMsg);
377 code += info.pid;
378 code *= hash(info.processName);
379 code *= hash(info.shortMsg);
380 code *= hash(info.stackTrace);
381 code *= hash(info.tag);
382 code += info.uid;
383 return code;
384 }
385 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800386}