2828
2929import java .util .ArrayList ;
3030import java .util .Collection ;
31- import java .util .HashSet ;
31+ import java .util .Collections ;
3232import java .util .Iterator ;
33+ import java .util .LinkedHashSet ;
3334import java .util .List ;
3435import java .util .Set ;
3536
3637/**
37- * This smoke test is designed to quickly sniff for any error conditions
38- * encountered after initial startup .
38+ * 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 .
3940 */
4041public class ProcessErrorsTest extends AndroidTestCase {
41-
42+
4243 private static final String TAG = "ProcessErrorsTest" ;
4344
4445 private final Intent mHomeIntent ;
4546
4647 protected ActivityManager mActivityManager ;
4748 protected PackageManager mPackageManager ;
4849
50+ /**
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+
4957 public ProcessErrorsTest () {
5058 mHomeIntent = new Intent (Intent .ACTION_MAIN );
5159 mHomeIntent .addCategory (Intent .CATEGORY_HOME );
5260 mHomeIntent .addFlags (Intent .FLAG_ACTIVITY_NEW_TASK );
5361 }
5462
63+ /**
64+ * {@inheritDoc}
65+ */
5566 @ Override
5667 public void setUp () throws Exception {
5768 super .setUp ();
69+ // First, make sure we have a Context
70+ assertNotNull ("getContext() returned null!" , getContext ());
71+
5872 mActivityManager = (ActivityManager )
5973 getContext ().getSystemService (Context .ACTIVITY_SERVICE );
6074 mPackageManager = getContext ().getPackageManager ();
@@ -75,43 +89,48 @@ public void testNoProcessErrorsAfterBoot() throws Exception {
7589 assertNull (reportMsg , reportMsg );
7690 }
7791
78- private String checkForProcessErrors () throws Exception {
79- List <ProcessErrorStateInfo > errList ;
80- errList = mActivityManager .getProcessesInErrorState ();
92+ /**
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 >();
8198
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".
99+ for (ResolveInfo app : getLauncherActivities (mPackageManager )) {
100+ final Collection <ProcessError > errProcs = runOneActivity (app );
101+ if (errProcs != null ) {
102+ errSet .addAll (errProcs );
103+ }
104+ }
84105
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 ;
106+ if (!errSet .isEmpty ()) {
107+ fail (String .format ("Got %d errors:\n %s" , errSet .size (),
108+ reportWrappedListContents (errSet )));
109+ }
88110 }
89111
90112 /**
91- * A helper function to query the provided {@link PackageManager} for a list of Activities that
92- * can be launched from Launcher.
113+ * 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.
93121 */
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 ;
122+ 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+ }
99132 }
100133
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- }
115134
116135 /**
117136 * A method to run the specified Activity and return a {@link Collection} of the Activities that
@@ -129,8 +148,7 @@ public Collection<ProcessError> runOneActivity(ResolveInfo app) {
129148
130149 // We check for any Crash or ANR dialogs that are already up, and we ignore them. This is
131150 // 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.
151+ // tests should have caught and reported already).
134152 final Collection <ProcessError > preErrProcs =
135153 ProcessError .fromCollection (mActivityManager .getProcessesInErrorState ());
136154
@@ -155,36 +173,88 @@ public Collection<ProcessError> runOneActivity(ResolveInfo app) {
155173 // possible to occur.
156174 final Collection <ProcessError > errProcs =
157175 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
176+
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
160194 if (errProcs != null && preErrProcs != null ) {
161195 errProcs .removeAll (preErrProcs );
162196 }
163197
164198 return errProcs ;
165199 }
166200
201+ private String checkForProcessErrors () throws Exception {
202+ List <ProcessErrorStateInfo > errList ;
203+ errList = mActivityManager .getProcessesInErrorState ();
204+
205+ // 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".
207+
208+ // 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 ;
211+ }
212+
167213 /**
168- * A test that runs all Launcher-launchable activities and verifies that no ANRs or crashes
169- * happened while doing so.
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
170219 */
171- public void testRunAllActivities () throws Exception {
172- final Set <ProcessError > errSet = new HashSet <ProcessError >();
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+ }
173226
174- for (ResolveInfo app : getLauncherActivities (mPackageManager )) {
175- final Collection <ProcessError > errProcs = runOneActivity (app );
176- if (errProcs != null ) {
177- errSet .addAll (errProcs );
178- }
179- }
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+ }
180237
181- if (!errSet .isEmpty ()) {
182- fail (String .format ("Got %d errors:\n %s" , errSet .size (),
183- reportWrappedListContents (errSet )));
184- }
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 ;
185250 }
186251
187- String reportWrappedListContents (Collection <ProcessError > errList ) {
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 ) {
188258 List <ProcessErrorStateInfo > newList = new ArrayList <ProcessErrorStateInfo >(errList .size ());
189259 for (ProcessError err : errList ) {
190260 newList .add (err .info );
@@ -198,7 +268,7 @@ String reportWrappedListContents(Collection<ProcessError> errList) {
198268 * @param errList The error report containing one or more error records.
199269 * @return Returns a string containing all of the errors.
200270 */
201- private String reportListContents (Collection <ProcessErrorStateInfo > errList ) {
271+ private static String reportListContents (Collection <ProcessErrorStateInfo > errList ) {
202272 if (errList == null ) return null ;
203273
204274 StringBuilder builder = new StringBuilder ();
0 commit comments