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.content.Intent;
21 
22 import java.lang.reflect.Method;
23 
24 /**
25  * This class provides functional testing of a single activity.  The activity under test will
26  * be created using the system infrastructure (by calling InstrumentationTestCase.launchActivity())
27  * and you will then be able to manipulate your Activity directly.
28  *
29  * <p>Other options supported by this test case include:
30  * <ul>
31  * <li>You can run any test method on the UI thread (see {@link android.test.UiThreadTest}).</li>
32  * <li>You can inject custom Intents into your Activity (see
33  * {@link #setActivityIntent(Intent)}).</li>
34  * </ul>
35  *
36  * <p>This class replaces {@link android.test.ActivityInstrumentationTestCase}, which is deprecated.
37  * New tests should be written using this base class.
38  *
39  * <p>If you prefer an isolated unit test, see {@link android.test.ActivityUnitTestCase}.
40  *
41  * <div class="special reference">
42  * <h3>Developer Guides</h3>
43  * <p>For more information about application testing, read the
44  * <a href="{@docRoot}guide/topics/testing/index.html">Testing</a> developer guide.</p>
45  * </div>
46  */
47 public abstract class ActivityInstrumentationTestCase2<T extends Activity>
48         extends ActivityTestCase {
49     Class<T> mActivityClass;
50     boolean mInitialTouchMode = false;
51     Intent mActivityIntent = null;
52 
53     /**
54      * Creates an {@link ActivityInstrumentationTestCase2}.
55      *
56      * @param pkg ignored - no longer in use.
57      * @param activityClass The activity to test. This must be a class in the instrumentation
58      * targetPackage specified in the AndroidManifest.xml
59      *
60      * @deprecated use {@link #ActivityInstrumentationTestCase2(Class)} instead
61      */
62     @Deprecated
ActivityInstrumentationTestCase2(String pkg, Class<T> activityClass)63     public ActivityInstrumentationTestCase2(String pkg, Class<T> activityClass) {
64         this(activityClass);
65     }
66 
67     /**
68      * Creates an {@link ActivityInstrumentationTestCase2}.
69      *
70      * @param activityClass The activity to test. This must be a class in the instrumentation
71      * targetPackage specified in the AndroidManifest.xml
72      */
ActivityInstrumentationTestCase2(Class<T> activityClass)73     public ActivityInstrumentationTestCase2(Class<T> activityClass) {
74         mActivityClass = activityClass;
75     }
76 
77     /**
78      * Get the Activity under test, starting it if necessary.
79      *
80      * For each test method invocation, the Activity will not actually be created until the first
81      * time this method is called.
82      *
83      * <p>If you wish to provide custom setup values to your Activity, you may call
84      * {@link #setActivityIntent(Intent)} and/or {@link #setActivityInitialTouchMode(boolean)}
85      * before your first call to getActivity().  Calling them after your Activity has
86      * started will have no effect.
87      *
88      * <p><b>NOTE:</b> Activities under test may not be started from within the UI thread.
89      * If your test method is annotated with {@link android.test.UiThreadTest}, then your Activity
90      * will be started automatically just before your test method is run.  You still call this
91      * method in order to get the Activity under test.
92      *
93      * @return the Activity under test
94      */
95     @Override
getActivity()96     public T getActivity() {
97         Activity a = super.getActivity();
98         if (a == null) {
99             // set initial touch mode
100             getInstrumentation().setInTouchMode(mInitialTouchMode);
101             final String targetPackage = getInstrumentation().getTargetContext().getPackageName();
102             // inject custom intent, if provided
103             if (mActivityIntent == null) {
104                 a = launchActivity(targetPackage, mActivityClass, null);
105             } else {
106                 a = launchActivityWithIntent(targetPackage, mActivityClass, mActivityIntent);
107             }
108             setActivity(a);
109         }
110         return (T) a;
111     }
112 
113     /**
114      * Call this method before the first call to {@link #getActivity} to inject a customized Intent
115      * into the Activity under test.
116      *
117      * <p>If you do not call this, the default intent will be provided.  If you call this after
118      * your Activity has been started, it will have no effect.
119      *
120      * <p><b>NOTE:</b> Activities under test may not be started from within the UI thread.
121      * If your test method is annotated with {@link android.test.UiThreadTest}, then you must call
122      * {@link #setActivityIntent(Intent)} from {@link #setUp()}.
123      *
124      * <p>The default Intent (if this method is not called) is:
125      *  action = {@link Intent#ACTION_MAIN}
126      *  flags = {@link Intent#FLAG_ACTIVITY_NEW_TASK}
127      * All other fields are null or empty.
128      *
129      * @param i The Intent to start the Activity with, or null to reset to the default Intent.
130      */
setActivityIntent(Intent i)131     public void setActivityIntent(Intent i) {
132         mActivityIntent = i;
133     }
134 
135     /**
136      * Call this method before the first call to {@link #getActivity} to set the initial touch
137      * mode for the Activity under test.
138      *
139      * <p>If you do not call this, the touch mode will be false.  If you call this after
140      * your Activity has been started, it will have no effect.
141      *
142      * <p><b>NOTE:</b> Activities under test may not be started from within the UI thread.
143      * If your test method is annotated with {@link android.test.UiThreadTest}, then you must call
144      * {@link #setActivityInitialTouchMode(boolean)} from {@link #setUp()}.
145      *
146      * @param initialTouchMode true if the Activity should be placed into "touch mode" when started
147      */
setActivityInitialTouchMode(boolean initialTouchMode)148     public void setActivityInitialTouchMode(boolean initialTouchMode) {
149         mInitialTouchMode = initialTouchMode;
150     }
151 
152     @Override
setUp()153     protected void setUp() throws Exception {
154         super.setUp();
155 
156         mInitialTouchMode = false;
157         mActivityIntent = null;
158     }
159 
160     @Override
tearDown()161     protected void tearDown() throws Exception {
162         // Finish the Activity off (unless was never launched anyway)
163         Activity a = super.getActivity();
164         if (a != null) {
165             a.finish();
166             setActivity(null);
167         }
168 
169         // Scrub out members - protects against memory leaks in the case where someone
170         // creates a non-static inner class (thus referencing the test case) and gives it to
171         // someone else to hold onto
172         scrubClass(ActivityInstrumentationTestCase2.class);
173 
174         super.tearDown();
175     }
176 
177     /**
178      * Runs the current unit test. If the unit test is annotated with
179      * {@link android.test.UiThreadTest}, force the Activity to be created before switching to
180      * the UI thread.
181      */
182     @Override
runTest()183     protected void runTest() throws Throwable {
184         try {
185             Method method = getClass().getMethod(getName(), (Class[]) null);
186             if (method.isAnnotationPresent(UiThreadTest.class)) {
187                 getActivity();
188             }
189         } catch (Exception e) {
190             // eat the exception here; super.runTest() will catch it again and handle it properly
191         }
192         super.runTest();
193     }
194 
195 }
196