Skip to content

Commit e4104b8

Browse files
Omari StephensAndroid (Google) Code Review
authored andcommitted
Merge "Add a method to the smoke test that launches each app"
2 parents 5f9d991 + 9f5a511 commit e4104b8

File tree

7 files changed

+343
-14
lines changed

7 files changed

+343
-14
lines changed

tests/SmokeTest/tests/src/com/android/smoketest/ProcessErrorsTest.java

Lines changed: 164 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,66 +19,147 @@
1919
import com.android.internal.os.RuntimeInit;
2020

2121
import android.app.ActivityManager;
22+
import android.app.ActivityManager.ProcessErrorStateInfo;
2223
import android.content.Context;
24+
import android.content.ComponentName;
25+
import android.content.Intent;
26+
import android.content.pm.PackageManager;
27+
import android.content.pm.ResolveInfo;
2328
import android.test.AndroidTestCase;
2429
import android.util.Log;
2530

31+
import java.util.ArrayList;
32+
import java.util.Collection;
33+
import java.util.HashSet;
2634
import java.util.Iterator;
2735
import java.util.List;
36+
import java.util.Set;
2837

2938
/**
3039
* This smoke test is designed to quickly sniff for any error conditions
3140
* encountered after initial startup.
3241
*/
3342
public class ProcessErrorsTest extends AndroidTestCase {
3443

35-
private final String TAG = "ProcessErrorsTest";
44+
private static final String TAG = "ProcessErrorsTest";
3645

3746
protected ActivityManager mActivityManager;
47+
protected PackageManager mPackageManager;
3848

3949
@Override
4050
public void setUp() throws Exception {
4151
super.setUp();
42-
mActivityManager = (ActivityManager)
52+
mActivityManager = (ActivityManager)
4353
getContext().getSystemService(Context.ACTIVITY_SERVICE);
54+
mPackageManager = getContext().getPackageManager();
4455
}
4556

4657
public void testSetUpConditions() throws Exception {
4758
assertNotNull(mActivityManager);
59+
assertNotNull(mPackageManager);
4860
}
4961

5062
public void testNoProcessErrors() throws Exception {
51-
List<ActivityManager.ProcessErrorStateInfo> errList;
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;
5274
errList = mActivityManager.getProcessesInErrorState();
5375

5476
// note: this contains information about each process that is currently in an error
5577
// condition. if the list is empty (null) then "we're good".
5678

5779
// if the list is non-empty, then it's useful to report the contents of the list
58-
// we'll put a copy in the log, and we'll report it back to the framework via the assert.
5980
final String reportMsg = reportListContents(errList);
60-
if (reportMsg != null) {
61-
Log.w(TAG, reportMsg);
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)));
62138
}
63-
64-
// report a non-empty list back to the test framework
65-
assertNull(reportMsg, errList);
66139
}
67-
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+
68149
/**
69150
* This helper function will dump the actual error reports.
70151
*
71152
* @param errList The error report containing one or more error records.
72153
* @return Returns a string containing all of the errors.
73154
*/
74-
private String reportListContents(List<ActivityManager.ProcessErrorStateInfo> errList) {
155+
private String reportListContents(Collection<ProcessErrorStateInfo> errList) {
75156
if (errList == null) return null;
76157

77158
StringBuilder builder = new StringBuilder();
78159

79-
Iterator<ActivityManager.ProcessErrorStateInfo> iter = errList.iterator();
160+
Iterator<ProcessErrorStateInfo> iter = errList.iterator();
80161
while (iter.hasNext()) {
81-
ActivityManager.ProcessErrorStateInfo entry = iter.next();
162+
ProcessErrorStateInfo entry = iter.next();
82163

83164
String condition;
84165
switch (entry.condition) {
@@ -96,8 +177,77 @@ private String reportListContents(List<ActivityManager.ProcessErrorStateInfo> er
96177
builder.append("Process error ").append(condition).append(" ");
97178
builder.append(" ").append(entry.shortMsg);
98179
builder.append(" detected in ").append(entry.processName).append(" ").append(entry.tag);
180+
builder.append("\n");
99181
}
100182
return builder.toString();
101183
}
102-
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+
}
103253
}

tests/SmokeTestApps/Android.mk

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
LOCAL_PATH:= $(call my-dir)
2+
include $(CLEAR_VARS)
3+
4+
LOCAL_MODULE_TAGS := tests
5+
6+
LOCAL_SRC_FILES := $(call all-java-files-under, src)
7+
8+
LOCAL_PACKAGE_NAME := SmokeTestTriggerApps
9+
10+
include $(BUILD_PACKAGE)
11+
12+
include $(call all-makefiles-under,$(LOCAL_PATH))
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!-- Copyright (C) 2012 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+
17+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
18+
package="com.android.smoketest.triggers">
19+
20+
<application android:label="something">
21+
<activity android:name=".CrashyApp"
22+
android:label="Test Crashy App">
23+
<intent-filter>
24+
<action android:name="android.intent.action.MAIN" />
25+
<category android:name="android.intent.category.LAUNCHER" />
26+
</intent-filter>
27+
</activity>
28+
29+
<activity android:name=".CrashyApp2"
30+
android:label="Test Crashy App2">
31+
<intent-filter>
32+
<action android:name="android.intent.action.MAIN" />
33+
<category android:name="android.intent.category.LAUNCHER" />
34+
</intent-filter>
35+
</activity>
36+
37+
<activity android:name=".UnresponsiveApp"
38+
android:label="Test Unresponsive App">
39+
<intent-filter>
40+
<action android:name="android.intent.action.MAIN" />
41+
<category android:name="android.intent.category.LAUNCHER" />
42+
</intent-filter>
43+
</activity>
44+
</application>
45+
</manifest>

tests/SmokeTestApps/README

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
The apps in this folder are intentionally bad-behaving apps that are intended
2+
to trigger the smoke tests to fail. They are otherwise not useful.
3+
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright (C) 2012 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+
17+
package com.android.smoketest.triggers;
18+
19+
import android.app.Activity;
20+
import android.os.Bundle;
21+
import android.widget.TextView;
22+
23+
public class CrashyApp extends Activity {
24+
/** Called when the activity is first created. */
25+
@Override
26+
public void onCreate(Bundle savedInstanceState) {
27+
super.onCreate(savedInstanceState);
28+
TextView tv = new TextView(this);
29+
tv.setText("Hello, Crashy Android");
30+
setContentView(tv);
31+
}
32+
33+
@Override
34+
public void onResume() {
35+
((String) null).length();
36+
}
37+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright (C) 2012 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+
17+
package com.android.smoketest.triggers;
18+
19+
import android.app.Activity;
20+
import android.os.Bundle;
21+
import android.widget.TextView;
22+
23+
public class CrashyApp2 extends Activity {
24+
/** Called when the activity is first created. */
25+
@Override
26+
public void onCreate(Bundle savedInstanceState) {
27+
super.onCreate(savedInstanceState);
28+
TextView tv = new TextView(this);
29+
tv.setText("Hello, Other Crashy Android");
30+
setContentView(tv);
31+
}
32+
33+
34+
@Override
35+
public void onResume() {
36+
throw new RuntimeException("Two drums and a cymbal fall off a cliff...");
37+
}
38+
}

0 commit comments

Comments
 (0)