blob: 6bd19a62231ba141ac8a4b6b6fee7b241b69fd0e [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 android.test;
18
19import android.app.Activity;
20import android.app.Application;
21import android.content.ComponentName;
22import android.content.Context;
23import android.content.Intent;
24import android.content.pm.ActivityInfo;
25import android.os.Bundle;
26import android.os.IBinder;
27import android.test.mock.MockApplication;
28import android.view.Window;
29
30/**
31 * This class provides isolated testing of a single activity. The activity under test will
32 * be created with minimal connection to the system infrastructure, and you can inject mocked or
33 * wrappered versions of many of Activity's dependencies. Most of the work is handled
34 * automatically here by {@link #setUp} and {@link #tearDown}.
35 *
36 * <p>If you prefer a functional test, see {@link android.test.ActivityInstrumentationTestCase}.
37 *
38 * <p>It must be noted that, as a true unit test, your Activity will not be running in the
39 * normal system and will not participate in the normal interactions with other Activities.
40 * The following methods should not be called in this configuration - most of them will throw
41 * exceptions:
42 * <ul>
43 * <li>{@link android.app.Activity#createPendingResult(int, Intent, int)}</li>
44 * <li>{@link android.app.Activity#startActivityIfNeeded(Intent, int)}</li>
45 * <li>{@link android.app.Activity#startActivityFromChild(Activity, Intent, int)}</li>
46 * <li>{@link android.app.Activity#startNextMatchingActivity(Intent)}</li>
47 * <li>{@link android.app.Activity#getCallingActivity()}</li>
48 * <li>{@link android.app.Activity#getCallingPackage()}</li>
49 * <li>{@link android.app.Activity#createPendingResult(int, Intent, int)}</li>
50 * <li>{@link android.app.Activity#getTaskId()}</li>
51 * <li>{@link android.app.Activity#isTaskRoot()}</li>
52 * <li>{@link android.app.Activity#moveTaskToBack(boolean)}</li>
53 * <li>{@link android.app.Activity#setPersistent(boolean)}</li>
54 * </ul>
55 *
56 * <p>The following methods may be called but will not do anything. For test purposes, you can use
57 * the methods {@link #getStartedActivityIntent()} and {@link #getStartedActivityRequest()} to
58 * inspect the parameters that they were called with.
59 * <ul>
60 * <li>{@link android.app.Activity#startActivity(Intent)}</li>
61 * <li>{@link android.app.Activity#startActivityForResult(Intent, int)}</li>
62 * </ul>
63 *
64 * <p>The following methods may be called but will not do anything. For test purposes, you can use
65 * the methods {@link #isFinishCalled()} and {@link #getFinishedActivityRequest()} to inspect the
66 * parameters that they were called with.
67 * <ul>
68 * <li>{@link android.app.Activity#finish()}</li>
69 * <li>{@link android.app.Activity#finishFromChild(Activity child)}</li>
70 * <li>{@link android.app.Activity#finishActivity(int requestCode)}</li>
71 * </ul>
72 *
73 */
74public abstract class ActivityUnitTestCase<T extends Activity>
75 extends ActivityTestCase {
76
77 private Class<T> mActivityClass;
78
79 private Context mActivityContext;
80 private Application mApplication;
81 private MockParent mMockParent;
82
83 private boolean mAttached = false;
84 private boolean mCreated = false;
85
86 public ActivityUnitTestCase(Class<T> activityClass) {
87 mActivityClass = activityClass;
88 }
89
90 @Override
91 public T getActivity() {
92 return (T) super.getActivity();
93 }
94
95 @Override
96 protected void setUp() throws Exception {
97 super.setUp();
98
99 // default value for target context, as a default
100 mActivityContext = getInstrumentation().getTargetContext();
101 }
102
103 /**
104 * Start the activity under test, in the same way as if it was started by
105 * {@link android.content.Context#startActivity Context.startActivity()}, providing the
106 * arguments it supplied. When you use this method to start the activity, it will automatically
107 * be stopped by {@link #tearDown}.
108 *
109 * <p>This method will call onCreate(), but if you wish to further exercise Activity life
110 * cycle methods, you must call them yourself from your test case.
111 *
112 * <p><i>Do not call from your setUp() method. You must call this method from each of your
113 * test methods.</i>
114 *
115 * @param intent The Intent as if supplied to {@link android.content.Context#startActivity}.
116 * @param savedInstanceState The instance state, if you are simulating this part of the life
117 * cycle. Typically null.
118 * @param lastNonConfigurationInstance This Object will be available to the
119 * Activity if it calls {@link android.app.Activity#getLastNonConfigurationInstance()}.
120 * Typically null.
121 * @return Returns the Activity that was created
122 */
123 protected T startActivity(Intent intent, Bundle savedInstanceState,
124 Object lastNonConfigurationInstance) {
125 assertFalse("Activity already created", mCreated);
126
127 if (!mAttached) {
128 assertNotNull(mActivityClass);
129 setActivity(null);
130 T newActivity = null;
131 try {
132 IBinder token = null;
133 if (mApplication == null) {
134 setApplication(new MockApplication());
135 }
136 ComponentName cn = new ComponentName(mActivityClass.getPackage().getName(),
137 mActivityClass.getName());
138 intent.setComponent(cn);
Brett Chabotff51fe22009-04-02 09:56:38 -0700139 ActivityInfo info = new ActivityInfo();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800140 CharSequence title = mActivityClass.getName();
141 mMockParent = new MockParent();
142 String id = null;
143
144 newActivity = (T) getInstrumentation().newActivity(mActivityClass, mActivityContext,
145 token, mApplication, intent, info, title, mMockParent, id,
146 lastNonConfigurationInstance);
147 } catch (Exception e) {
148 assertNotNull(newActivity);
149 }
150
151 assertNotNull(newActivity);
152 setActivity(newActivity);
153
154 mAttached = true;
155 }
156
157 T result = getActivity();
158 if (result != null) {
159 getInstrumentation().callActivityOnCreate(getActivity(), savedInstanceState);
160 mCreated = true;
161 }
162 return result;
163 }
164
165 @Override
166 protected void tearDown() throws Exception {
167
168 setActivity(null);
169
170 // Scrub out members - protects against memory leaks in the case where someone
171 // creates a non-static inner class (thus referencing the test case) and gives it to
172 // someone else to hold onto
173 scrubClass(ActivityInstrumentationTestCase.class);
174
175 super.tearDown();
176 }
177
178 /**
179 * Set the application for use during the test. You must call this function before calling
180 * {@link #startActivity}. If your test does not call this method,
181 * @param application The Application object that will be injected into the Activity under test.
182 */
183 public void setApplication(Application application) {
184 mApplication = application;
185 }
186
187 /**
188 * If you wish to inject a Mock, Isolated, or otherwise altered context, you can do so
189 * here. You must call this function before calling {@link #startActivity}. If you wish to
190 * obtain a real Context, as a building block, use getInstrumentation().getTargetContext().
191 */
192 public void setActivityContext(Context activityContext) {
193 mActivityContext = activityContext;
194 }
195
196 /**
197 * This method will return the value if your Activity under test calls
198 * {@link android.app.Activity#setRequestedOrientation}.
199 */
200 public int getRequestedOrientation() {
201 if (mMockParent != null) {
202 return mMockParent.mRequestedOrientation;
203 }
204 return 0;
205 }
206
207 /**
208 * This method will return the launch intent if your Activity under test calls
209 * {@link android.app.Activity#startActivity(Intent)} or
210 * {@link android.app.Activity#startActivityForResult(Intent, int)}.
211 * @return The Intent provided in the start call, or null if no start call was made.
212 */
213 public Intent getStartedActivityIntent() {
214 if (mMockParent != null) {
215 return mMockParent.mStartedActivityIntent;
216 }
217 return null;
218 }
219
220 /**
221 * This method will return the launch request code if your Activity under test calls
222 * {@link android.app.Activity#startActivityForResult(Intent, int)}.
223 * @return The request code provided in the start call, or -1 if no start call was made.
224 */
225 public int getStartedActivityRequest() {
226 if (mMockParent != null) {
227 return mMockParent.mStartedActivityRequest;
228 }
229 return 0;
230 }
231
232 /**
233 * This method will notify you if the Activity under test called
234 * {@link android.app.Activity#finish()},
235 * {@link android.app.Activity#finishFromChild(Activity)}, or
236 * {@link android.app.Activity#finishActivity(int)}.
237 * @return Returns true if one of the listed finish methods was called.
238 */
239 public boolean isFinishCalled() {
240 if (mMockParent != null) {
241 return mMockParent.mFinished;
242 }
243 return false;
244 }
245
246 /**
247 * This method will return the request code if the Activity under test called
248 * {@link android.app.Activity#finishActivity(int)}.
249 * @return The request code provided in the start call, or -1 if no finish call was made.
250 */
251 public int getFinishedActivityRequest() {
252 if (mMockParent != null) {
253 return mMockParent.mFinishedActivityRequest;
254 }
255 return 0;
256 }
257
258 /**
259 * This mock Activity represents the "parent" activity. By injecting this, we allow the user
260 * to call a few more Activity methods, including:
261 * <ul>
262 * <li>{@link android.app.Activity#getRequestedOrientation()}</li>
263 * <li>{@link android.app.Activity#setRequestedOrientation(int)}</li>
264 * <li>{@link android.app.Activity#finish()}</li>
265 * <li>{@link android.app.Activity#finishActivity(int requestCode)}</li>
266 * <li>{@link android.app.Activity#finishFromChild(Activity child)}</li>
267 * </ul>
268 *
269 * TODO: Make this overrideable, and the unit test can look for calls to other methods
270 */
271 private static class MockParent extends Activity {
272
273 public int mRequestedOrientation = 0;
274 public Intent mStartedActivityIntent = null;
275 public int mStartedActivityRequest = -1;
276 public boolean mFinished = false;
277 public int mFinishedActivityRequest = -1;
278
279 /**
280 * Implementing in the parent allows the user to call this function on the tested activity.
281 */
282 @Override
283 public void setRequestedOrientation(int requestedOrientation) {
284 mRequestedOrientation = requestedOrientation;
285 }
286
287 /**
288 * Implementing in the parent allows the user to call this function on the tested activity.
289 */
290 @Override
291 public int getRequestedOrientation() {
292 return mRequestedOrientation;
293 }
294
295 /**
296 * By returning null here, we inhibit the creation of any "container" for the window.
297 */
298 @Override
299 public Window getWindow() {
300 return null;
301 }
302
303 /**
304 * By defining this in the parent, we allow the tested activity to call
305 * <ul>
306 * <li>{@link android.app.Activity#startActivity(Intent)}</li>
307 * <li>{@link android.app.Activity#startActivityForResult(Intent, int)}</li>
308 * </ul>
309 */
310 @Override
311 public void startActivityFromChild(Activity child, Intent intent, int requestCode) {
312 mStartedActivityIntent = intent;
313 mStartedActivityRequest = requestCode;
314 }
315
316 /**
317 * By defining this in the parent, we allow the tested activity to call
318 * <ul>
319 * <li>{@link android.app.Activity#finish()}</li>
320 * <li>{@link android.app.Activity#finishFromChild(Activity child)}</li>
321 * </ul>
322 */
323 @Override
324 public void finishFromChild(Activity child) {
325 mFinished = true;
326 }
327
328 /**
329 * By defining this in the parent, we allow the tested activity to call
330 * <ul>
331 * <li>{@link android.app.Activity#finishActivity(int requestCode)}</li>
332 * </ul>
333 */
334 @Override
335 public void finishActivityFromChild(Activity child, int requestCode) {
336 mFinished = true;
337 mFinishedActivityRequest = requestCode;
338 }
339 }
340}