diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
commit | 54b6cfa9a9e5b861a9930af873580d6dc20f773c (patch) | |
tree | 35051494d2af230dce54d6b31c6af8fc24091316 /core/java/android/test | |
download | frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.zip frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.tar.gz frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.tar.bz2 |
Initial Contribution
Diffstat (limited to 'core/java/android/test')
-rw-r--r-- | core/java/android/test/AndroidTestCase.java | 86 | ||||
-rw-r--r-- | core/java/android/test/FlakyTest.java | 41 | ||||
-rw-r--r-- | core/java/android/test/InstrumentationTestCase.java | 260 | ||||
-rw-r--r-- | core/java/android/test/InstrumentationTestSuite.java | 74 | ||||
-rw-r--r-- | core/java/android/test/PerformanceTestCase.java | 65 | ||||
-rw-r--r-- | core/java/android/test/UiThreadTest.java | 33 | ||||
-rw-r--r-- | core/java/android/test/package.html | 5 | ||||
-rw-r--r-- | core/java/android/test/suitebuilder/annotation/Smoke.java | 34 | ||||
-rw-r--r-- | core/java/android/test/suitebuilder/annotation/Suppress.java | 33 |
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 { +} |