OSDN Git Service

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