blob: 6b79314a4385b8a91da04b9d62de965effc42786 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080019import android.app.Activity;
20import android.app.Instrumentation;
21import android.content.Intent;
22import android.os.Bundle;
23import android.util.Log;
24import android.view.KeyEvent;
25
26import java.lang.reflect.Field;
Gilles Debunne192ab902010-02-24 15:50:40 -080027import java.lang.reflect.InvocationTargetException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import java.lang.reflect.Method;
29import java.lang.reflect.Modifier;
Gilles Debunne192ab902010-02-24 15:50:40 -080030
31import junit.framework.TestCase;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032
33/**
34 * A test case that has access to {@link Instrumentation}.
Stephan Linznerb51617f2016-01-27 18:09:50 -080035 *
36 * @deprecated Use
37 * <a href="{@docRoot}reference/android/support/test/InstrumentationRegistry.html">
38 * InstrumentationRegistry</a> instead. New tests should be written using the
39 * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040 */
Stephan Linznerb51617f2016-01-27 18:09:50 -080041@Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042public class InstrumentationTestCase extends TestCase {
43
44 private Instrumentation mInstrumentation;
45
46 /**
47 * Injects instrumentation into this test case. This method is
48 * called by the test runner during test setup.
Stephan Linznerb51617f2016-01-27 18:09:50 -080049 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050 * @param instrumentation the instrumentation to use with this instance
51 */
Jack Wang7aba54b2009-08-20 19:20:54 -070052 public void injectInstrumentation(Instrumentation instrumentation) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053 mInstrumentation = instrumentation;
54 }
55
56 /**
Jack Wang7aba54b2009-08-20 19:20:54 -070057 * Injects instrumentation into this test case. This method is
58 * called by the test runner during test setup.
59 *
60 * @param instrumentation the instrumentation to use with this instance
61 *
62 * @deprecated Incorrect spelling,
Jack Wang3fc03e62010-10-19 15:13:07 -070063 * use {@link #injectInstrumentation(android.app.Instrumentation)} instead.
Jack Wang7aba54b2009-08-20 19:20:54 -070064 */
65 @Deprecated
66 public void injectInsrumentation(Instrumentation instrumentation) {
67 injectInstrumentation(instrumentation);
68 }
69
70 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071 * Inheritors can access the instrumentation using this.
72 * @return instrumentation
73 */
74 public Instrumentation getInstrumentation() {
75 return mInstrumentation;
76 }
77
78 /**
79 * Utility method for launching an activity.
80 *
81 * <p>The {@link Intent} used to launch the Activity is:
82 * action = {@link Intent#ACTION_MAIN}
83 * extras = null, unless a custom bundle is provided here
84 * All other fields are null or empty.
Andy Stadlerdf2a4632009-04-21 11:51:43 -070085 *
86 * <p><b>NOTE:</b> The parameter <i>pkg</i> must refer to the package identifier of the
87 * package hosting the activity to be launched, which is specified in the AndroidManifest.xml
88 * file. This is not necessarily the same as the java package name.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080089 *
90 * @param pkg The package hosting the activity to be launched.
91 * @param activityCls The activity class to launch.
92 * @param extras Optional extra stuff to pass to the activity.
93 * @return The activity, or null if non launched.
94 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080095 public final <T extends Activity> T launchActivity(
96 String pkg,
97 Class<T> activityCls,
98 Bundle extras) {
99 Intent intent = new Intent(Intent.ACTION_MAIN);
100 if (extras != null) {
101 intent.putExtras(extras);
102 }
103 return launchActivityWithIntent(pkg, activityCls, intent);
104 }
105
106 /**
107 * Utility method for launching an activity with a specific Intent.
Andy Stadlerdf2a4632009-04-21 11:51:43 -0700108 *
109 * <p><b>NOTE:</b> The parameter <i>pkg</i> must refer to the package identifier of the
110 * package hosting the activity to be launched, which is specified in the AndroidManifest.xml
111 * file. This is not necessarily the same as the java package name.
112 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800113 * @param pkg The package hosting the activity to be launched.
114 * @param activityCls The activity class to launch.
115 * @param intent The intent to launch with
116 * @return The activity, or null if non launched.
117 */
118 @SuppressWarnings("unchecked")
119 public final <T extends Activity> T launchActivityWithIntent(
120 String pkg,
121 Class<T> activityCls,
122 Intent intent) {
123 intent.setClassName(pkg, activityCls.getName());
124 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
125 T activity = (T) getInstrumentation().startActivitySync(intent);
126 getInstrumentation().waitForIdleSync();
127 return activity;
128 }
129
130 /**
131 * Helper for running portions of a test on the UI thread.
132 *
133 * Note, in most cases it is simpler to annotate the test method with
134 * {@link android.test.UiThreadTest}, which will run the entire test method on the UI thread.
135 * Use this method if you need to switch in and out of the UI thread to perform your test.
136 *
137 * @param r runnable containing test code in the {@link Runnable#run()} method
138 */
139 public void runTestOnUiThread(final Runnable r) throws Throwable {
140 final Throwable[] exceptions = new Throwable[1];
141 getInstrumentation().runOnMainSync(new Runnable() {
142 public void run() {
143 try {
144 r.run();
145 } catch (Throwable throwable) {
146 exceptions[0] = throwable;
147 }
148 }
149 });
150 if (exceptions[0] != null) {
151 throw exceptions[0];
152 }
153 }
154
155 /**
156 * Runs the current unit test. If the unit test is annotated with
157 * {@link android.test.UiThreadTest}, the test is run on the UI thread.
158 */
159 @Override
160 protected void runTest() throws Throwable {
161 String fName = getName();
162 assertNotNull(fName);
163 Method method = null;
164 try {
165 // use getMethod to get all public inherited
166 // methods. getDeclaredMethods returns all
167 // methods of this class but excludes the
168 // inherited ones.
169 method = getClass().getMethod(fName, (Class[]) null);
170 } catch (NoSuchMethodException e) {
171 fail("Method \""+fName+"\" not found");
172 }
173
174 if (!Modifier.isPublic(method.getModifiers())) {
175 fail("Method \""+fName+"\" should be public");
176 }
177
178 int runCount = 1;
Jack Wang3fc03e62010-10-19 15:13:07 -0700179 boolean isRepetitive = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800180 if (method.isAnnotationPresent(FlakyTest.class)) {
181 runCount = method.getAnnotation(FlakyTest.class).tolerance();
Jack Wang3fc03e62010-10-19 15:13:07 -0700182 } else if (method.isAnnotationPresent(RepetitiveTest.class)) {
183 runCount = method.getAnnotation(RepetitiveTest.class).numIterations();
184 isRepetitive = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800185 }
186
187 if (method.isAnnotationPresent(UiThreadTest.class)) {
188 final int tolerance = runCount;
Jack Wang3fc03e62010-10-19 15:13:07 -0700189 final boolean repetitive = isRepetitive;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800190 final Method testMethod = method;
191 final Throwable[] exceptions = new Throwable[1];
192 getInstrumentation().runOnMainSync(new Runnable() {
193 public void run() {
194 try {
Jack Wang3fc03e62010-10-19 15:13:07 -0700195 runMethod(testMethod, tolerance, repetitive);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800196 } catch (Throwable throwable) {
197 exceptions[0] = throwable;
198 }
199 }
200 });
201 if (exceptions[0] != null) {
202 throw exceptions[0];
203 }
204 } else {
Jack Wang3fc03e62010-10-19 15:13:07 -0700205 runMethod(method, runCount, isRepetitive);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800206 }
207 }
208
Jack Wang3fc03e62010-10-19 15:13:07 -0700209 // For backwards-compatibility after adding isRepetitive
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800210 private void runMethod(Method runMethod, int tolerance) throws Throwable {
Jack Wang3fc03e62010-10-19 15:13:07 -0700211 runMethod(runMethod, tolerance, false);
212 }
213
214 private void runMethod(Method runMethod, int tolerance, boolean isRepetitive) throws Throwable {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800215 Throwable exception = null;
216
217 int runCount = 0;
218 do {
219 try {
220 runMethod.invoke(this, (Object[]) null);
221 exception = null;
222 } catch (InvocationTargetException e) {
223 e.fillInStackTrace();
224 exception = e.getTargetException();
225 } catch (IllegalAccessException e) {
226 e.fillInStackTrace();
227 exception = e;
228 } finally {
229 runCount++;
Jack Wang3fc03e62010-10-19 15:13:07 -0700230 // Report current iteration number, if test is repetitive
231 if (isRepetitive) {
232 Bundle iterations = new Bundle();
233 iterations.putInt("currentiterations", runCount);
234 getInstrumentation().sendStatus(2, iterations);
235 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800236 }
Jack Wang3fc03e62010-10-19 15:13:07 -0700237 } while ((runCount < tolerance) && (isRepetitive || exception != null));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800238
239 if (exception != null) {
240 throw exception;
241 }
242 }
243
244 /**
245 * Sends a series of key events through instrumentation and waits for idle. The sequence
246 * of keys is a string containing the key names as specified in KeyEvent, without the
247 * KEYCODE_ prefix. For instance: sendKeys("DPAD_LEFT A B C DPAD_CENTER"). Each key can
248 * be repeated by using the N* prefix. For instance, to send two KEYCODE_DPAD_LEFT, use
249 * the following: sendKeys("2*DPAD_LEFT").
250 *
251 * @param keysSequence The sequence of keys.
252 */
253 public void sendKeys(String keysSequence) {
254 final String[] keys = keysSequence.split(" ");
255 final int count = keys.length;
256
257 final Instrumentation instrumentation = getInstrumentation();
258
259 for (int i = 0; i < count; i++) {
260 String key = keys[i];
261 int repeater = key.indexOf('*');
262
263 int keyCount;
264 try {
265 keyCount = repeater == -1 ? 1 : Integer.parseInt(key.substring(0, repeater));
266 } catch (NumberFormatException e) {
267 Log.w("ActivityTestCase", "Invalid repeat count: " + key);
268 continue;
269 }
270
271 if (repeater != -1) {
272 key = key.substring(repeater + 1);
273 }
274
275 for (int j = 0; j < keyCount; j++) {
276 try {
277 final Field keyCodeField = KeyEvent.class.getField("KEYCODE_" + key);
278 final int keyCode = keyCodeField.getInt(null);
Dianne Hackbornd7ed9172009-06-18 18:36:37 -0700279 try {
280 instrumentation.sendKeyDownUpSync(keyCode);
281 } catch (SecurityException e) {
282 // Ignore security exceptions that are now thrown
283 // when trying to send to another app, to retain
284 // compatibility with existing tests.
285 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800286 } catch (NoSuchFieldException e) {
287 Log.w("ActivityTestCase", "Unknown keycode: KEYCODE_" + key);
288 break;
289 } catch (IllegalAccessException e) {
290 Log.w("ActivityTestCase", "Unknown keycode: KEYCODE_" + key);
291 break;
292 }
293 }
294 }
295
296 instrumentation.waitForIdleSync();
297 }
298
299 /**
300 * Sends a series of key events through instrumentation and waits for idle. For instance:
301 * sendKeys(KEYCODE_DPAD_LEFT, KEYCODE_DPAD_CENTER).
302 *
303 * @param keys The series of key codes to send through instrumentation.
304 */
305 public void sendKeys(int... keys) {
306 final int count = keys.length;
307 final Instrumentation instrumentation = getInstrumentation();
308
309 for (int i = 0; i < count; i++) {
Dianne Hackbornd7ed9172009-06-18 18:36:37 -0700310 try {
311 instrumentation.sendKeyDownUpSync(keys[i]);
312 } catch (SecurityException e) {
313 // Ignore security exceptions that are now thrown
314 // when trying to send to another app, to retain
315 // compatibility with existing tests.
316 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800317 }
318
319 instrumentation.waitForIdleSync();
320 }
321
322 /**
323 * Sends a series of key events through instrumentation and waits for idle. Each key code
324 * must be preceded by the number of times the key code must be sent. For instance:
325 * sendRepeatedKeys(1, KEYCODE_DPAD_CENTER, 2, KEYCODE_DPAD_LEFT).
326 *
327 * @param keys The series of key repeats and codes to send through instrumentation.
328 */
329 public void sendRepeatedKeys(int... keys) {
330 final int count = keys.length;
331 if ((count & 0x1) == 0x1) {
332 throw new IllegalArgumentException("The size of the keys array must "
333 + "be a multiple of 2");
334 }
335
336 final Instrumentation instrumentation = getInstrumentation();
337
338 for (int i = 0; i < count; i += 2) {
339 final int keyCount = keys[i];
340 final int keyCode = keys[i + 1];
341 for (int j = 0; j < keyCount; j++) {
Dianne Hackbornd7ed9172009-06-18 18:36:37 -0700342 try {
343 instrumentation.sendKeyDownUpSync(keyCode);
344 } catch (SecurityException e) {
345 // Ignore security exceptions that are now thrown
346 // when trying to send to another app, to retain
347 // compatibility with existing tests.
348 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800349 }
350 }
351
352 instrumentation.waitForIdleSync();
353 }
354
355 /**
356 * Make sure all resources are cleaned up and garbage collected before moving on to the next
357 * test. Subclasses that override this method should make sure they call super.tearDown()
358 * at the end of the overriding method.
359 *
360 * @throws Exception
361 */
Gilles Debunne192ab902010-02-24 15:50:40 -0800362 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800363 protected void tearDown() throws Exception {
364 Runtime.getRuntime().gc();
365 Runtime.getRuntime().runFinalization();
366 Runtime.getRuntime().gc();
367 super.tearDown();
368 }
Jack Wang3fc03e62010-10-19 15:13:07 -0700369}