summaryrefslogtreecommitdiffstats
path: root/core/java/android/test
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android/test')
-rw-r--r--core/java/android/test/AndroidTestCase.java86
-rw-r--r--core/java/android/test/FlakyTest.java41
-rw-r--r--core/java/android/test/InstrumentationTestCase.java260
-rw-r--r--core/java/android/test/InstrumentationTestSuite.java74
-rw-r--r--core/java/android/test/PerformanceTestCase.java65
-rw-r--r--core/java/android/test/UiThreadTest.java33
-rw-r--r--core/java/android/test/package.html5
-rw-r--r--core/java/android/test/suitebuilder/annotation/Smoke.java34
-rw-r--r--core/java/android/test/suitebuilder/annotation/Suppress.java33
9 files changed, 631 insertions, 0 deletions
diff --git a/core/java/android/test/AndroidTestCase.java b/core/java/android/test/AndroidTestCase.java
new file mode 100644
index 0000000..9bafa32
--- /dev/null
+++ b/core/java/android/test/AndroidTestCase.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.test;
+
+import android.content.Context;
+
+import java.lang.reflect.Field;
+
+import junit.framework.TestCase;
+
+/**
+ * Extend this if you need to access Resources or other things that depend on Activity Context.
+ */
+public class AndroidTestCase extends TestCase {
+
+ protected Context mContext;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ public void testAndroidTestCaseSetupProperly() {
+ assertNotNull("Context is null. setContext should be called before tests are run",
+ mContext);
+ }
+
+ public void setContext(Context context) {
+ mContext = context;
+ }
+
+ public Context getContext() {
+ return mContext;
+ }
+
+ /**
+ * This function is called by various TestCase implementations, at tearDown() time, in order
+ * to scrub out any class variables. This protects against memory leaks in the case where a
+ * test case creates a non-static inner class (thus referencing the test case) and gives it to
+ * someone else to hold onto.
+ *
+ * @param testCaseClass The class of the derived TestCase implementation.
+ *
+ * @throws IllegalAccessException
+ */
+ protected void scrubClass(final Class<?> testCaseClass)
+ throws IllegalAccessException {
+ final Field[] fields = getClass().getDeclaredFields();
+ for (Field field : fields) {
+ final Class<?> fieldClass = field.getDeclaringClass();
+ if (testCaseClass.isAssignableFrom(fieldClass) && !field.getType().isPrimitive()) {
+ try {
+ field.setAccessible(true);
+ field.set(this, null);
+ } catch (Exception e) {
+ android.util.Log.d("TestCase", "Error: Could not nullify field!");
+ }
+
+ if (field.get(this) != null) {
+ android.util.Log.d("TestCase", "Error: Could not nullify field!");
+ }
+ }
+ }
+ }
+
+
+}
diff --git a/core/java/android/test/FlakyTest.java b/core/java/android/test/FlakyTest.java
new file mode 100644
index 0000000..919767f
--- /dev/null
+++ b/core/java/android/test/FlakyTest.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.test;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.annotation.ElementType;
+
+/**
+ * This annotation can be used on an {@link android.test.InstrumentationTestCase}'s
+ * test methods. When the annotation is present, the test method is re-executed if
+ * the test fails. The total number of executions is specified by the tolerance and
+ * defaults to 1.
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface FlakyTest {
+ /**
+ * Indicates how many times a test can run and fail before being reported
+ * as a failed test. If the tolerance factor is less than 1, the test runs
+ * only once.
+ *
+ * @return The total number of allowed run, the default is 1.
+ */
+ int tolerance() default 1;
+}
diff --git a/core/java/android/test/InstrumentationTestCase.java b/core/java/android/test/InstrumentationTestCase.java
new file mode 100644
index 0000000..08a8ad1
--- /dev/null
+++ b/core/java/android/test/InstrumentationTestCase.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.test;
+
+import junit.framework.TestCase;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.KeyEvent;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * A test case that has access to {@link Instrumentation}. See
+ * <code>InstrumentationTestRunner</code>.
+ */
+public class InstrumentationTestCase extends TestCase {
+
+ private Instrumentation mInstrumentation;
+
+ /**
+ * Injects instrumentation into this test case. This method is
+ * called by the test runner during test setup.
+ *
+ * @param instrumentation the instrumentation to use with this instance
+ */
+ public void injectInsrumentation(Instrumentation instrumentation) {
+ mInstrumentation = instrumentation;
+ }
+
+ /**
+ * Inheritors can access the instrumentation using this.
+ * @return instrumentation
+ */
+ public Instrumentation getInstrumentation() {
+ return mInstrumentation;
+ }
+
+ /**
+ * Utility method for launching an activity.
+ * @param pkg The package hosting the activity to be launched.
+ * @param activityCls The activity class to launch.
+ * @param extras Optional extra stuff to pass to the activity.
+ * @return The activity, or null if non launched.
+ */
+ @SuppressWarnings("unchecked")
+ public final <T extends Activity> T launchActivity(
+ String pkg,
+ Class<T> activityCls,
+ Bundle extras) {
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.setClassName(pkg, activityCls.getName());
+ if (extras != null) {
+ intent.putExtras(extras);
+ }
+ T activity = (T) getInstrumentation().startActivitySync(intent);
+ getInstrumentation().waitForIdleSync();
+ return activity;
+ }
+
+ /**
+ * Runs the current unit test. If the unit test is annotated with
+ * {@link android.test.UiThreadTest}, the test is run on the UI thread.
+ */
+ protected void runTest() throws Throwable {
+ String fName = getName();
+ assertNotNull(fName);
+ Method method = null;
+ try {
+ // use getMethod to get all public inherited
+ // methods. getDeclaredMethods returns all
+ // methods of this class but excludes the
+ // inherited ones.
+ method = getClass().getMethod(fName, (Class[]) null);
+ } catch (NoSuchMethodException e) {
+ fail("Method \""+fName+"\" not found");
+ }
+
+ if (!Modifier.isPublic(method.getModifiers())) {
+ fail("Method \""+fName+"\" should be public");
+ }
+
+ int runCount = 1;
+ if (method.isAnnotationPresent(FlakyTest.class)) {
+ runCount = method.getAnnotation(FlakyTest.class).tolerance();
+ }
+
+ if (method.isAnnotationPresent(UiThreadTest.class)) {
+ final int tolerance = runCount;
+ final Method testMethod = method;
+ final Throwable[] exceptions = new Throwable[1];
+ getInstrumentation().runOnMainSync(new Runnable() {
+ public void run() {
+ try {
+ runMethod(testMethod, tolerance);
+ } catch (Throwable throwable) {
+ exceptions[0] = throwable;
+ }
+ }
+ });
+ if (exceptions[0] != null) {
+ throw exceptions[0];
+ }
+ } else {
+ runMethod(method, runCount);
+ }
+ }
+
+ private void runMethod(Method runMethod, int tolerance) throws Throwable {
+ Throwable exception = null;
+
+ int runCount = 0;
+ do {
+ try {
+ runMethod.invoke(this, (Object[]) null);
+ exception = null;
+ } catch (InvocationTargetException e) {
+ e.fillInStackTrace();
+ exception = e.getTargetException();
+ } catch (IllegalAccessException e) {
+ e.fillInStackTrace();
+ exception = e;
+ } finally {
+ runCount++;
+ }
+ } while ((runCount < tolerance) && (exception != null));
+
+ if (exception != null) {
+ throw exception;
+ }
+ }
+
+ /**
+ * Sends a series of key events through instrumentation and waits for idle. The sequence
+ * of keys is a string containing the key names as specified in KeyEvent, without the
+ * KEYCODE_ prefix. For instance: sendKeys("DPAD_LEFT A B C DPAD_CENTER"). Each key can
+ * be repeated by using the N* prefix. For instance, to send two KEYCODE_DPAD_LEFT, use
+ * the following: sendKeys("2*DPAD_LEFT").
+ *
+ * @param keysSequence The sequence of keys.
+ */
+ public void sendKeys(String keysSequence) {
+ final String[] keys = keysSequence.split(" ");
+ final int count = keys.length;
+
+ final Instrumentation instrumentation = getInstrumentation();
+
+ for (int i = 0; i < count; i++) {
+ String key = keys[i];
+ int repeater = key.indexOf('*');
+
+ int keyCount;
+ try {
+ keyCount = repeater == -1 ? 1 : Integer.parseInt(key.substring(0, repeater));
+ } catch (NumberFormatException e) {
+ Log.w("ActivityTestCase", "Invalid repeat count: " + key);
+ continue;
+ }
+
+ if (repeater != -1) {
+ key = key.substring(repeater + 1);
+ }
+
+ for (int j = 0; j < keyCount; j++) {
+ try {
+ final Field keyCodeField = KeyEvent.class.getField("KEYCODE_" + key);
+ final int keyCode = keyCodeField.getInt(null);
+ instrumentation.sendKeyDownUpSync(keyCode);
+ } catch (NoSuchFieldException e) {
+ Log.w("ActivityTestCase", "Unknown keycode: KEYCODE_" + key);
+ break;
+ } catch (IllegalAccessException e) {
+ Log.w("ActivityTestCase", "Unknown keycode: KEYCODE_" + key);
+ break;
+ }
+ }
+ }
+
+ instrumentation.waitForIdleSync();
+ }
+
+ /**
+ * Sends a series of key events through instrumentation and waits for idle. For instance:
+ * sendKeys(KEYCODE_DPAD_LEFT, KEYCODE_DPAD_CENTER).
+ *
+ * @param keys The series of key codes to send through instrumentation.
+ */
+ public void sendKeys(int... keys) {
+ final int count = keys.length;
+ final Instrumentation instrumentation = getInstrumentation();
+
+ for (int i = 0; i < count; i++) {
+ instrumentation.sendKeyDownUpSync(keys[i]);
+ }
+
+ instrumentation.waitForIdleSync();
+ }
+
+ /**
+ * Sends a series of key events through instrumentation and waits for idle. Each key code
+ * must be preceded by the number of times the key code must be sent. For instance:
+ * sendRepeatedKeys(1, KEYCODE_DPAD_CENTER, 2, KEYCODE_DPAD_LEFT).
+ *
+ * @param keys The series of key repeats and codes to send through instrumentation.
+ */
+ public void sendRepeatedKeys(int... keys) {
+ final int count = keys.length;
+ if ((count & 0x1) == 0x1) {
+ throw new IllegalArgumentException("The size of the keys array must "
+ + "be a multiple of 2");
+ }
+
+ final Instrumentation instrumentation = getInstrumentation();
+
+ for (int i = 0; i < count; i += 2) {
+ final int keyCount = keys[i];
+ final int keyCode = keys[i + 1];
+ for (int j = 0; j < keyCount; j++) {
+ instrumentation.sendKeyDownUpSync(keyCode);
+ }
+ }
+
+ instrumentation.waitForIdleSync();
+ }
+
+ /**
+ * Make sure all resources are cleaned up and garbage collected before moving on to the next
+ * test. Subclasses that override this method should make sure they call super.tearDown()
+ * at the end of the overriding method.
+ *
+ * @throws Exception
+ */
+ protected void tearDown() throws Exception {
+ Runtime.getRuntime().gc();
+ Runtime.getRuntime().runFinalization();
+ Runtime.getRuntime().gc();
+ super.tearDown();
+ }
+}
diff --git a/core/java/android/test/InstrumentationTestSuite.java b/core/java/android/test/InstrumentationTestSuite.java
new file mode 100644
index 0000000..2ab949e
--- /dev/null
+++ b/core/java/android/test/InstrumentationTestSuite.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.test;
+
+import android.app.Instrumentation;
+
+import junit.framework.TestSuite;
+import junit.framework.Test;
+import junit.framework.TestResult;
+
+/**
+ * A {@link junit.framework.TestSuite} that injects {@link android.app.Instrumentation} into
+ * {@link InstrumentationTestCase} before running them.
+ */
+public class InstrumentationTestSuite extends TestSuite {
+
+ private final Instrumentation mInstrumentation;
+
+ /**
+ * @param instr The instrumentation that will be injected into each
+ * test before running it.
+ */
+ public InstrumentationTestSuite(Instrumentation instr) {
+ mInstrumentation = instr;
+ }
+
+
+ public InstrumentationTestSuite(String name, Instrumentation instr) {
+ super(name);
+ mInstrumentation = instr;
+ }
+
+ /**
+ * @param theClass Inspected for methods starting with 'test'
+ * @param instr The instrumentation to inject into each test before
+ * running.
+ */
+ public InstrumentationTestSuite(final Class theClass, Instrumentation instr) {
+ super(theClass);
+ mInstrumentation = instr;
+ }
+
+
+ @Override
+ public void addTestSuite(Class testClass) {
+ addTest(new InstrumentationTestSuite(testClass, mInstrumentation));
+ }
+
+
+ @Override
+ public void runTest(Test test, TestResult result) {
+
+ if (test instanceof InstrumentationTestCase) {
+ ((InstrumentationTestCase) test).injectInsrumentation(mInstrumentation);
+ }
+
+ // run the test as usual
+ super.runTest(test, result);
+ }
+}
diff --git a/core/java/android/test/PerformanceTestCase.java b/core/java/android/test/PerformanceTestCase.java
new file mode 100644
index 0000000..679ad40
--- /dev/null
+++ b/core/java/android/test/PerformanceTestCase.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.test;
+
+/**
+ * More complex interface performance for test cases.
+ *
+ * If you want your test to be used as a performance test, you must
+ * implement this interface.
+ */
+public interface PerformanceTestCase
+{
+ /**
+ * Callbacks for {@link PerformanceTestCase}.
+ */
+ public interface Intermediates
+ {
+ void setInternalIterations(int count);
+ void startTiming(boolean realTime);
+ void addIntermediate(String name);
+ void addIntermediate(String name, long timeInNS);
+ void finishTiming(boolean realTime);
+ }
+
+ /**
+ * Set up to begin performance tests. The 'intermediates' is a
+ * communication channel to send back intermediate performance numbers --
+ * if you use it, you will probably want to ensure your test is only
+ * executed once by returning 1. Otherwise, return 0 to allow the test
+ * harness to decide the number of iterations.
+ *
+ * <p>If you return a non-zero iteration count, you should call
+ * {@link Intermediates#startTiming intermediates.startTiming} and
+ * {@link Intermediates#finishTiming intermediates.endTiming} to report the
+ * duration of the test whose performance should actually be measured.
+ *
+ * @param intermediates Callback for sending intermediate results.
+ *
+ * @return int Maximum number of iterations to run, or 0 to let the caller
+ * decide.
+ */
+ int startPerformance(Intermediates intermediates);
+
+ /**
+ * This method is used to determine what modes this test case can run in.
+ *
+ * @return true if this test case can only be run in performance mode.
+ */
+ boolean isPerformanceOnly();
+}
+
diff --git a/core/java/android/test/UiThreadTest.java b/core/java/android/test/UiThreadTest.java
new file mode 100644
index 0000000..cd92231
--- /dev/null
+++ b/core/java/android/test/UiThreadTest.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.test;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.annotation.ElementType;
+
+/**
+ * This annotation can be used on an {@link InstrumentationTestCase}'s test methods.
+ * When the annotation is present, the test method is executed on the application's
+ * main thread (or UI thread.) Note that instrumentation methods may not be used
+ * when this annotation is present.
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface UiThreadTest {
+}
diff --git a/core/java/android/test/package.html b/core/java/android/test/package.html
new file mode 100644
index 0000000..1972bed
--- /dev/null
+++ b/core/java/android/test/package.html
@@ -0,0 +1,5 @@
+<HTML>
+<BODY>
+A framework for writing Android test cases and suites.
+</BODY>
+</HTML> \ No newline at end of file
diff --git a/core/java/android/test/suitebuilder/annotation/Smoke.java b/core/java/android/test/suitebuilder/annotation/Smoke.java
new file mode 100644
index 0000000..237e033
--- /dev/null
+++ b/core/java/android/test/suitebuilder/annotation/Smoke.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.test.suitebuilder.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marks a test that should run as part of the smoke tests.
+ * The <code>android.test.suitebuilder.SmokeTestSuiteBuilder</code>
+ * will run all tests with this annotation.
+ *
+ * @see android.test.suitebuilder.SmokeTestSuiteBuilder
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD, ElementType.TYPE})
+public @interface Smoke {
+}
diff --git a/core/java/android/test/suitebuilder/annotation/Suppress.java b/core/java/android/test/suitebuilder/annotation/Suppress.java
new file mode 100644
index 0000000..f16c8fa
--- /dev/null
+++ b/core/java/android/test/suitebuilder/annotation/Suppress.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.test.suitebuilder.annotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.annotation.ElementType;
+
+/**
+ * Use this annotation on test classes or test methods that should not be included in a test
+ * suite. If the annotation appears on the class then no tests in that class will be included. If
+ * the annotation appears only on a test method then only that method will be excluded.
+ *
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD, ElementType.TYPE})
+public @interface Suppress {
+}