Retry test-runner tests move.

This time change the frameworks makefile so it only includes test-runner/src
in the public API.
diff --git a/test-runner/tests/Android.mk b/test-runner/tests/Android.mk
new file mode 100644
index 0000000..d1efe7b
--- /dev/null
+++ b/test-runner/tests/Android.mk
@@ -0,0 +1,29 @@
+# Copyright 2010, 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.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# We only want this apk build for tests.
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+# Include all test java files.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := FrameworkTestRunnerTests
+
+include $(BUILD_PACKAGE)
+
diff --git a/test-runner/tests/AndroidManifest.xml b/test-runner/tests/AndroidManifest.xml
new file mode 100644
index 0000000..4f32392
--- /dev/null
+++ b/test-runner/tests/AndroidManifest.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.frameworks.testrunner.tests">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+        <activity android:name="android.test.StubTestBrowserActivity"
+            android:label="Stubbed Test Browser">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.FOR_TESTS_ONLY"/>
+            </intent-filter>
+        </activity>
+
+        <activity android:name="android.test.TestBrowserTests"
+            android:label="Test Browser Tests">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.UNIT_TEST"/>
+            </intent-filter>
+        </activity>`
+    </application>
+
+    <instrumentation
+        android:name="android.test.InstrumentationTestRunner"
+        android:targetPackage="com.android.frameworks.testrunner.tests"
+        android:label="Framework testrunner tests" />
+</manifest>
diff --git a/test-runner/tests/src/android/test/AndroidTestRunnerTest.java b/test-runner/tests/src/android/test/AndroidTestRunnerTest.java
new file mode 100644
index 0000000..0574704
--- /dev/null
+++ b/test-runner/tests/src/android/test/AndroidTestRunnerTest.java
@@ -0,0 +1,294 @@
+/*
+ * 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 android.test.mock.MockContext;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.google.android.collect.Lists;
+
+import junit.framework.TestCase;
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import junit.framework.TestListener;
+
+import java.util.List;
+import java.util.Arrays;
+
+/**
+ * Unit tests for {@link AndroidTestRunner}
+ */
+@SmallTest
+public class AndroidTestRunnerTest extends TestCase {
+    private AndroidTestRunner mAndroidTestRunner;
+    private StubContext mStubContext;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mStubContext = new StubContext(getClass().getClassLoader());
+
+        mAndroidTestRunner = new AndroidTestRunner();
+        mAndroidTestRunner.setContext(mStubContext);
+    }
+
+    public void testLoadNoTestCases() throws Exception {
+        mAndroidTestRunner.setTestClassName(TestSuite.class.getName(), null);
+
+        List<TestCase> testCases = mAndroidTestRunner.getTestCases();
+        assertNotNull(testCases);
+        assertEquals(1, testCases.size());
+        assertEquals("warning", testCases.get(0).getName());
+        assertEquals(TestSuite.class.getSimpleName(), mAndroidTestRunner.getTestClassName());
+    }
+
+    public void testSetTestSuiteWithOneTestCase() throws Exception {
+        mAndroidTestRunner.setTestClassName(OneTestTestCase.class.getName(), null);
+
+        List<TestCase> testCases = mAndroidTestRunner.getTestCases();
+        assertNotNull(testCases);
+        assertEquals(1, testCases.size());
+        assertEquals("testOne", testCases.get(0).getName());
+        assertEquals(OneTestTestCase.class.getSimpleName(), mAndroidTestRunner.getTestClassName());
+    }
+
+    public void testRunTest() throws Exception {
+        mAndroidTestRunner.setTestClassName(OneTestTestCase.class.getName(), null);
+
+        TestListenerStub testListenerStub = new TestListenerStub();
+        mAndroidTestRunner.addTestListener(testListenerStub);
+
+        mAndroidTestRunner.runTest();
+
+        assertTrue(testListenerStub.saw("testOne"));
+    }
+
+    public void testRunTestWithAndroidTestCase() throws Exception {
+        mAndroidTestRunner.setTestClassName(
+                OneAndroidTestTestCase.class.getName(), "testOneAndroid");
+
+        TestListenerStub testListenerStub = new TestListenerStub();
+        mAndroidTestRunner.addTestListener(testListenerStub);
+
+        assertNull(((AndroidTestCase) mAndroidTestRunner.getTestCases().get(0)).getContext());
+
+        mAndroidTestRunner.runTest();
+
+        assertTrue(testListenerStub.saw("testOneAndroid"));
+        assertSame(mStubContext,
+                ((AndroidTestCase) mAndroidTestRunner.getTestCases().get(0)).getContext());
+    }
+
+    public void testRunTestWithAndroidTestCaseInSuite() throws Exception {
+        mAndroidTestRunner.setTestClassName(OneAndroidTestTestCase.class.getName(), null);
+
+        TestListenerStub testListenerStub = new TestListenerStub();
+        mAndroidTestRunner.addTestListener(testListenerStub);
+
+        mAndroidTestRunner.runTest();
+
+        assertTrue(testListenerStub.saw("testOneAndroid"));
+
+        List<TestCase> testCases = mAndroidTestRunner.getTestCases();
+        for (TestCase testCase : testCases) {
+            assertSame(mStubContext, ((AndroidTestCase) testCase).getContext());
+        }
+    }
+
+    public void testRunTestWithAndroidTestCaseInNestedSuite() throws Exception {
+        mAndroidTestRunner.setTestClassName(AndroidTestCaseTestSuite.class.getName(), null);
+
+        TestListenerStub testListenerStub = new TestListenerStub();
+        mAndroidTestRunner.addTestListener(testListenerStub);
+
+        mAndroidTestRunner.runTest();
+
+        assertTrue(testListenerStub.saw("testOneAndroid"));
+
+        List<TestCase> testCases = mAndroidTestRunner.getTestCases();
+        for (TestCase testCase : testCases) {
+            assertSame(mStubContext, ((AndroidTestCase) testCase).getContext());
+        }
+    }
+
+    public void testRunTestWithNullListener() throws Exception {
+        mAndroidTestRunner.setTestClassName(OneTestTestCase.class.getName(), null);
+
+        mAndroidTestRunner.addTestListener(null);
+        try {
+            mAndroidTestRunner.runTest();
+        } catch (NullPointerException e) {
+            fail("Should not add a null TestListener");
+        }
+    }
+
+    public void testSetTestClassWithTestSuiteProvider() throws Exception {
+        mAndroidTestRunner.setTestClassName(SampleTestSuiteProvider.class.getName(), null);
+        List<TestCase> testCases = mAndroidTestRunner.getTestCases();
+        List<String> testNames = Lists.newArrayList();
+        for (TestCase testCase : testCases) {
+            testNames.add(testCase.getName());
+        }
+
+        // Use the test suite provided by the interface method rather than the static suite method.
+        assertEquals(Arrays.asList("testOne"), testNames);
+    }
+
+    public void testSetTestClassWithTestSuite() throws Exception {
+        mAndroidTestRunner.setTestClassName(SampleTestSuite.class.getName(), null);
+        List<TestCase> testCases = mAndroidTestRunner.getTestCases();
+        List<String> testNames = Lists.newArrayList();
+        for (TestCase testCase : testCases) {
+            testNames.add(testCase.getName());
+        }
+        assertEquals(Arrays.asList("testOne", "testOne", "testTwo"), testNames);
+    }
+
+    public void testRunSingleTestMethod() throws Exception {
+        String testMethodName = "testTwo";
+        mAndroidTestRunner.setTestClassName(TwoTestTestCase.class.getName(), testMethodName);
+        List<TestCase> testCases = mAndroidTestRunner.getTestCases();
+        List<String> testNames = Lists.newArrayList();
+        for (TestCase testCase : testCases) {
+            testNames.add(testCase.getName());
+        }
+        assertEquals(Arrays.asList(testMethodName), testNames);
+    }
+
+    public void testSetTestClassInvalidClass() throws Exception {
+        try {
+            mAndroidTestRunner.setTestClassName("class.that.does.not.exist", null);
+            fail("expected exception not thrown");
+        } catch (RuntimeException e) {
+            // expected
+        }
+    }
+    
+    public void testRunSkipExecution() throws Exception {
+        String testMethodName = "testFail";
+        mAndroidTestRunner.setTestClassName(
+                OnePassOneErrorOneFailTestCase.class.getName(), testMethodName);
+        
+        TestListenerStub testListenerStub = new TestListenerStub();
+        mAndroidTestRunner.addTestListener(testListenerStub);
+        
+        // running the failing test should pass - ie as if its not run
+        mAndroidTestRunner.runTest();
+        
+        assertTrue(testListenerStub.saw("testFail"));
+    }
+
+    public static class SampleTestSuiteProvider implements TestSuiteProvider {
+
+        public TestSuite getTestSuite() {
+            TestSuite testSuite = new TestSuite();
+            testSuite.addTestSuite(OneTestTestCase.class);
+            return testSuite;
+        }
+
+        public static Test suite() {
+            return SampleTestSuite.suite();
+        }
+    }
+
+    public static class SampleTestSuite {
+        public static TestSuite suite() {
+            TestSuite testSuite = new TestSuite();
+            testSuite.addTestSuite(OneTestTestCase.class);
+            testSuite.addTestSuite(TwoTestTestCase.class);
+            return testSuite;
+        }
+    }
+
+    public static class AndroidTestCaseTestSuite {
+        public static TestSuite suite() {
+            TestSuite testSuite = new TestSuite();
+            testSuite.addTestSuite(OneAndroidTestTestCase.class);
+            return testSuite;
+        }
+    }
+
+    public static class OneAndroidTestTestCase extends AndroidTestCase {
+        public void testOneAndroid() throws Exception {
+        }
+    }
+
+    public static class OneTestTestCase extends TestCase {
+        public void testOne() throws Exception {
+        }
+    }
+
+    public static class TwoTestTestCase extends TestCase {
+        public void testOne() throws Exception {
+        }
+
+        public void testTwo() throws Exception {
+        }
+    }
+
+    public static class OnePassOneErrorOneFailTestCase extends TestCase {
+        public void testPass() throws Exception {
+        }
+
+        public void testError() throws Exception {
+            throw new Exception();
+        }
+
+        public void testFail() throws Exception {
+            fail();
+        }
+    }
+
+    private static class TestListenerStub implements TestListener {
+        List<String> testNames = Lists.newArrayList();
+
+        public void addError(Test test, Throwable t) {
+        }
+
+        public void addFailure(Test test, AssertionFailedError t) {
+        }
+
+        public void endTest(Test test) {
+        }
+
+        public void startTest(Test test) {
+            if (test instanceof TestCase) {
+                testNames.add(((TestCase) test).getName());
+            } else if (test instanceof TestSuite) {
+                testNames.add(((TestSuite) test).getName());
+            }
+        }
+
+        public boolean saw(String testName) {
+            return testNames.contains(testName);
+        }
+    }
+
+    private static class StubContext extends MockContext {
+        private ClassLoader mClassLoader;
+
+        public StubContext(ClassLoader classLoader) {
+            this.mClassLoader = classLoader;
+        }
+
+        @Override
+        public ClassLoader getClassLoader() {
+            return mClassLoader;
+        }
+    }
+}
diff --git a/test-runner/tests/src/android/test/InstrumentationTestRunnerTest.java b/test-runner/tests/src/android/test/InstrumentationTestRunnerTest.java
new file mode 100644
index 0000000..d9afd54
--- /dev/null
+++ b/test-runner/tests/src/android/test/InstrumentationTestRunnerTest.java
@@ -0,0 +1,272 @@
+/*
+ * 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 android.content.Context;
+import android.os.Bundle;
+import android.test.mock.MockContext;
+import android.test.suitebuilder.ListTestCaseNames;
+import android.test.suitebuilder.ListTestCaseNames.TestDescriptor;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import java.util.List;
+
+/**
+ * Tests for {@link InstrumentationTestRunner}
+ */
+@SmallTest
+public class InstrumentationTestRunnerTest extends TestCase {
+    private StubInstrumentationTestRunner mInstrumentationTestRunner;
+    private StubAndroidTestRunner mStubAndroidTestRunner;
+    private String mTargetContextPackageName;
+
+    protected void setUp() throws Exception {
+        super.setUp();
+        mStubAndroidTestRunner = new StubAndroidTestRunner();
+        mTargetContextPackageName = "android.test.suitebuilder.examples";
+        mInstrumentationTestRunner = new StubInstrumentationTestRunner(
+                new StubContext("com.google.foo.tests"),
+                new StubContext(mTargetContextPackageName), mStubAndroidTestRunner);
+    }
+
+    public void testOverrideTestToRunWithClassArgument() throws Exception {
+        String expectedTestClassName = PlaceHolderTest.class.getName();
+        mInstrumentationTestRunner.onCreate(createBundle(
+                InstrumentationTestRunner.ARGUMENT_TEST_CLASS, expectedTestClassName));
+
+        assertTestRunnerCalledWithExpectedParameters(expectedTestClassName, "testPlaceHolder");
+    }
+
+    public void testOverrideTestToRunWithClassAndMethodArgument() throws Exception {
+        String expectedTestClassName = PlaceHolderTest.class.getName();
+        String expectedTestMethodName = "testPlaceHolder";
+        String classAndMethod = expectedTestClassName + "#" + expectedTestMethodName;
+        mInstrumentationTestRunner.onCreate(createBundle(
+                InstrumentationTestRunner.ARGUMENT_TEST_CLASS, classAndMethod));
+
+        assertTestRunnerCalledWithExpectedParameters(expectedTestClassName,
+                expectedTestMethodName);
+    }
+
+    public void testUseSelfAsTestSuiteProviderWhenNoMetaDataOrClassArgument() throws Exception {
+        TestSuite testSuite = new TestSuite();
+        testSuite.addTestSuite(PlaceHolderTest.class);
+        mInstrumentationTestRunner.setAllTestsSuite(testSuite);
+        mInstrumentationTestRunner.onCreate(null);
+        assertTestRunnerCalledWithExpectedParameters(
+                PlaceHolderTest.class.getName(), "testPlaceHolder");
+    }
+    
+    public void testMultipleTestClass() throws Exception {
+        String classArg = PlaceHolderTest.class.getName() + "," + 
+            PlaceHolderTest2.class.getName();
+        mInstrumentationTestRunner.onCreate(createBundle(
+                InstrumentationTestRunner.ARGUMENT_TEST_CLASS, classArg));
+        
+        Test test = mStubAndroidTestRunner.getTest();
+
+        assertContentsInOrder(ListTestCaseNames.getTestNames((TestSuite) test),
+            new TestDescriptor(PlaceHolderTest.class.getName(), "testPlaceHolder"), 
+            new TestDescriptor(PlaceHolderTest2.class.getName(), "testPlaceHolder2"));
+        
+    }
+
+    public void testDelayParameter() throws Exception {
+        int delayMsec = 1000;
+        Bundle args = new Bundle();
+        args.putInt(InstrumentationTestRunner.ARGUMENT_DELAY_MSEC, delayMsec);
+        args.putString(InstrumentationTestRunner.ARGUMENT_TEST_CLASS,
+                PlaceHolderTest.class.getName() + "," +
+                PlaceHolderTest2.class.getName());
+        mInstrumentationTestRunner.onCreate(args);
+        Thread t = new Thread() { public void run() { mInstrumentationTestRunner.onStart(); } };
+
+        // Should delay three times: before, between, and after the two tests.
+        long beforeTest = System.currentTimeMillis();
+        t.start();
+        t.join();
+        assertTrue(System.currentTimeMillis() > beforeTest + delayMsec * 3);
+        assertTrue(mInstrumentationTestRunner.isStarted());
+        assertTrue(mInstrumentationTestRunner.isFinished());
+        assertTrue(mStubAndroidTestRunner.isRun());
+    }
+
+    private void assertContentsInOrder(List<TestDescriptor> actual, TestDescriptor... source) {
+        TestDescriptor[] clonedSource = source.clone();
+        assertEquals("Unexpected number of items.", clonedSource.length, actual.size());
+        for (int i = 0; i < actual.size(); i++) {
+            TestDescriptor actualItem = actual.get(i);
+            TestDescriptor sourceItem = clonedSource[i];
+            assertEquals("Unexpected item. Index: " + i, sourceItem, actualItem);
+        }
+    }
+
+    private void assertTestRunnerCalledWithExpectedParameters(
+            String expectedTestClassName, String expectedTestMethodName) {
+        Test test = mStubAndroidTestRunner.getTest();
+        assertContentsInOrder(ListTestCaseNames.getTestNames((TestSuite) test),
+                new TestDescriptor(expectedTestClassName, expectedTestMethodName));  
+        assertTrue(mInstrumentationTestRunner.isStarted());
+        assertFalse(mInstrumentationTestRunner.isFinished());
+    }
+
+    private Bundle createBundle(String key, String value) {
+        Bundle bundle = new Bundle();
+        bundle.putString(key, value);
+        return bundle;
+    }
+
+    private static class StubInstrumentationTestRunner extends InstrumentationTestRunner {
+        private Context mContext;
+        private Context mTargetContext;
+        private boolean mStarted;
+        private boolean mFinished;
+        private AndroidTestRunner mAndroidTestRunner;
+        private TestSuite mTestSuite;
+        private TestSuite mDefaultTestSuite;
+        private String mPackageNameForDefaultTests;
+
+        public StubInstrumentationTestRunner(Context context, Context targetContext,
+                AndroidTestRunner androidTestRunner) {
+            this.mContext = context;
+            this.mTargetContext = targetContext;
+            this.mAndroidTestRunner = androidTestRunner;
+        }
+
+        public Context getContext() {
+            return mContext;
+        }
+
+        public TestSuite getAllTests() {
+            return mTestSuite;
+        }
+
+        public Context getTargetContext() {
+            return mTargetContext;
+        }
+
+        protected AndroidTestRunner getAndroidTestRunner() {
+            return mAndroidTestRunner;
+        }
+
+        public void start() {
+            mStarted = true;
+        }
+
+        public void finish(int resultCode, Bundle results) {
+            mFinished = true;
+        }
+
+        public boolean isStarted() {
+            return mStarted;
+        }
+
+        public boolean isFinished() {
+            return mFinished;
+        }
+
+        public void setAllTestsSuite(TestSuite testSuite) {
+            mTestSuite = testSuite;
+        }
+        
+        public void setDefaultTestsSuite(TestSuite testSuite) {
+            mDefaultTestSuite = testSuite;
+        }
+
+        public String getPackageNameForDefaultTests() {
+            return mPackageNameForDefaultTests;
+        }
+    }
+
+    private static class StubContext extends MockContext {
+        private String mPackageName;
+
+        public StubContext(String packageName) {
+            this.mPackageName = packageName;
+        }
+
+        @Override
+        public String getPackageCodePath() {
+            return mPackageName;
+        }
+
+        @Override
+        public String getPackageName() {
+            return mPackageName;
+        }
+
+        @Override
+        public ClassLoader getClassLoader() {
+            return getClass().getClassLoader();
+        }
+    }
+
+    private static class StubAndroidTestRunner extends AndroidTestRunner {
+        private Test mTest;
+        private boolean mRun;
+
+        public boolean isRun() {
+            return mRun;
+        }
+
+        public void setTest(Test test) {
+            super.setTest(test);
+            mTest = test;
+        }
+
+        public Test getTest() {
+            return mTest;
+        }
+
+        public void runTest() {
+            super.runTest();
+            mRun = true;
+        }
+    }
+
+    /**
+     * Empty test used for validation
+     */
+    public static class PlaceHolderTest extends TestCase {
+
+        public PlaceHolderTest() {
+            super("testPlaceHolder");
+        }
+
+        public void testPlaceHolder() throws Exception {
+
+        }
+    }
+    
+    /**
+     * Empty test used for validation
+     */
+    public static class PlaceHolderTest2 extends TestCase {
+
+        public PlaceHolderTest2() {
+            super("testPlaceHolder2");
+        }
+
+        public void testPlaceHolder2() throws Exception {
+
+        }
+    }
+}
diff --git a/test-runner/tests/src/android/test/StubTestBrowserActivity.java b/test-runner/tests/src/android/test/StubTestBrowserActivity.java
new file mode 100644
index 0000000..97ed3ce
--- /dev/null
+++ b/test-runner/tests/src/android/test/StubTestBrowserActivity.java
@@ -0,0 +1,33 @@
+/*
+ * 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.TestSuite;
+
+public class StubTestBrowserActivity extends TestBrowserActivity {
+
+    private static TestSuite mTestSuite;
+
+    static void setTopTestSuite(TestSuite testSuite) {
+        mTestSuite = testSuite;
+    }
+
+    @Override
+    public TestSuite getTopTestSuite() {
+        return mTestSuite;
+    }
+}
diff --git a/test-runner/tests/src/android/test/TestBrowserActivityTest.java b/test-runner/tests/src/android/test/TestBrowserActivityTest.java
new file mode 100644
index 0000000..355409e
--- /dev/null
+++ b/test-runner/tests/src/android/test/TestBrowserActivityTest.java
@@ -0,0 +1,275 @@
+/*
+ * 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.Activity;
+import android.app.Instrumentation;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.view.IWindowManager;
+import android.widget.ListView;
+
+import com.google.android.collect.Lists;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import java.util.List;
+
+public class TestBrowserActivityTest extends InstrumentationTestCase {
+
+    private TestBrowserActivity mTestBrowserActivity;
+    private StubTestBrowserController mTestBrowserController;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        StubTestBrowserActivity.setTopTestSuite(null);
+        mTestBrowserController = new StubTestBrowserController();
+        ServiceLocator.setTestBrowserController(mTestBrowserController);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        if (mTestBrowserActivity != null) {
+            mTestBrowserActivity.finish();
+        }
+        mTestBrowserActivity = null;
+        super.tearDown();
+    }
+
+    public void testEmptyListContent() throws Exception {
+        StubTestBrowserActivity.setTopTestSuite(new TestSuite());
+
+        mTestBrowserActivity = createActivity();
+
+        ListView listView = getListView();
+        // There is always an item on the list for running all tests.
+        assertEquals("Unexpected number of items on list view.", 1, listView.getCount());
+
+        assertEquals("Stubbed Test Browser", mTestBrowserActivity.getTitle().toString());
+    }
+
+    public void testOneListContent() throws Exception {
+        List<String> testCaseNames = Lists.newArrayList("AllTests");
+        StubTestBrowserActivity.setTopTestSuite(createTestSuite(testCaseNames));
+
+        mTestBrowserActivity = createActivity();
+
+        ListView listView = getListView();
+        assertListViewContents(testCaseNames, listView);
+    }
+
+    public void testListWithTestCases() throws Exception {
+        List<String> testCaseNames = Lists.newArrayList("AllTests", "Apples", "Bananas", "Oranges");
+        StubTestBrowserActivity.setTopTestSuite(createTestSuite(testCaseNames));
+
+        mTestBrowserActivity = createActivity();
+
+        ListView listView = getListView();
+        assertListViewContents(testCaseNames, listView);
+    }
+
+    public void testListWithTestSuite() throws Exception {
+        List<String> testCaseNames = Lists.newArrayList(OneTestTestCase.class.getSimpleName());
+        StubTestBrowserActivity.setTopTestSuite(new OneTestInTestSuite());
+
+        mTestBrowserActivity = createActivity();
+
+        ListView listView = getListView();
+        assertListViewContents(testCaseNames, listView);
+    }
+
+    public void testSelectATestCase() throws Exception {
+        List<String> testCaseNames = Lists.newArrayList("AllTests");
+        TestSuite testSuite = createTestSuite(testCaseNames);
+        StubTestBrowserActivity.setTopTestSuite(testSuite);
+
+        mTestBrowserController.setTestCase(OneTestTestCase.class);
+        mTestBrowserActivity = createActivity();
+
+        Instrumentation.ActivityMonitor activityMonitor = getInstrumentation().addMonitor(
+                TestBrowserControllerImpl.TEST_RUNNER_ACTIVITY_CLASS_NAME, null, false);
+        try {
+            assertEquals(0, activityMonitor.getHits());
+
+            ListView listView = getListView();
+            int invokedTestCaseIndex = 0;
+            listView.performItemClick(listView, invokedTestCaseIndex, 0);
+
+            Activity activity = activityMonitor.waitForActivityWithTimeout(2000);
+            assertNotNull(activity);
+            try {
+                assertEquals(1, activityMonitor.getHits());
+                assertEquals(invokedTestCaseIndex, mTestBrowserController.getLastPosition());
+            } finally {
+                activity.finish();
+            }
+        } finally {
+            getInstrumentation().removeMonitor(activityMonitor);
+        }
+    }
+
+    public void testCreateFromIntentWithOneTest() throws Exception {
+        List<String> testCaseNames = Lists.newArrayList("testOne");
+
+        mTestBrowserActivity = launchTestBrowserActivity(new TestSuite(OneTestTestCase.class));
+
+        ListView listView = getListView();
+        assertListViewContents(testCaseNames, listView);
+    }
+
+    public void testUpdateListOnStart() throws Exception {
+        StubTestBrowserActivity.setTopTestSuite(new TestSuite());
+
+        mTestBrowserActivity = createActivity();
+
+        ListView listView = getListView();
+        assertEquals("Unexpected number of items on list view.", 1, listView.getCount());
+
+        List<String> testCaseNames = Lists.newArrayList("AllTests");
+        StubTestBrowserActivity.setTopTestSuite(createTestSuite(testCaseNames));
+
+        getInstrumentation().runOnMainSync(new Runnable() {
+            public void run() {
+                ((StubTestBrowserActivity) mTestBrowserActivity).onStart();
+            }
+        });
+
+        listView = getListView();
+        assertListViewContents(testCaseNames, listView);
+    }
+
+    public void testTitleHasTestSuiteName() throws Exception {
+        final String testSuiteName = "com.android.TestSuite";
+        StubTestBrowserActivity.setTopTestSuite(new TestSuite(testSuiteName));
+
+        mTestBrowserActivity = createActivity();
+
+        assertEquals("TestSuite", mTestBrowserActivity.getTitle().toString());
+    }
+    
+    private TestSuite createTestSuite(List<String> testCaseNames) {
+        return createTestSuite(testCaseNames.toArray(new String[testCaseNames.size()]));
+    }
+
+    private TestSuite createTestSuite(String... testCaseNames) {
+        TestSuite testSuite = new TestSuite();
+        for (String testCaseName : testCaseNames) {
+            testSuite.addTest(new FakeTestCase(testCaseName));
+        }
+
+        return testSuite;
+    }
+
+    public static class FakeTestCase extends TestCase {
+        public FakeTestCase(String name) {
+            super(name);
+        }
+    }
+
+    public static class OneTestTestCase extends TestCase {
+        public void testOne() throws Exception {
+        }
+    }
+
+    public static class OneTestInTestSuite extends TestSuite {
+        public static Test suite() {
+            TestSuite suite = new TestSuite(OneTestInTestSuite.class.getName());
+            suite.addTestSuite(OneTestTestCase.class);
+            return suite;
+        }
+    }
+
+    private void assertListViewContents(List<String> expectedTestCaseNames, ListView listView) {
+        assertEquals("Run All", listView.getItemAtPosition(0).toString());
+        assertEquals("Unexpected number of items on list view.",
+                expectedTestCaseNames.size() + 1, listView.getCount());
+        for (int i = 0; i < expectedTestCaseNames.size(); i++) {
+            String expectedTestCaseName = expectedTestCaseNames.get(i);
+            String actualTestCaseName = listView.getItemAtPosition(i + 1).toString();
+            assertEquals("Unexpected test case name. Index: " + i,
+                    expectedTestCaseName, actualTestCaseName);
+        }
+    }
+
+    private ListView getListView() {
+        return mTestBrowserActivity.getListView();
+    }
+
+    private TestBrowserActivity createActivity() throws RemoteException {
+        return launchActivity(getAndroidPackageName(), StubTestBrowserActivity.class, null);
+    }
+
+    private Intent createIntent(TestSuite testSuite) {
+        Intent intent = new Intent(Intent.ACTION_RUN);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        String className = StubTestBrowserActivity.class.getName();
+        String packageName = getAndroidPackageName();
+        intent.setClassName(packageName, className);
+        intent.setData(Uri.parse(testSuite.getName()));
+        return intent;
+    }
+
+    private String getAndroidPackageName() {
+        String packageName = getInstrumentation().getTargetContext().getPackageName();
+        return packageName;
+    }
+
+    private TestBrowserActivity launchTestBrowserActivity(TestSuite testSuite)
+            throws RemoteException {
+        getInstrumentation().setInTouchMode(false);
+
+        TestBrowserActivity activity =
+                (TestBrowserActivity) getInstrumentation().startActivitySync(
+                        createIntent(testSuite));
+        getInstrumentation().waitForIdleSync();
+        return activity;
+    }
+
+    private static class StubTestBrowserController extends TestBrowserControllerImpl {
+        private int mPosition;
+        private Class<? extends TestCase> mTestCaseClass;
+
+        public Intent getIntentForTestAt(int position) {
+            mPosition = position;
+
+            Intent intent = new Intent();
+            intent.setAction(Intent.ACTION_RUN);
+
+            String className = TestBrowserControllerImpl.TEST_RUNNER_ACTIVITY_CLASS_NAME;
+            String testName = mTestCaseClass.getClass().getName();
+
+            String packageName = className.substring(0, className.lastIndexOf("."));
+            intent.setClassName(packageName, className);
+            intent.setData(Uri.parse(testName));
+
+            return intent;
+        }
+
+        public void setTestCase(Class<? extends TestCase> testCaseClass) {
+            mTestCaseClass = testCaseClass;
+        }
+
+        public int getLastPosition() {
+            return mPosition;
+        }
+    }
+}
diff --git a/test-runner/tests/src/android/test/TestBrowserControllerImplTest.java b/test-runner/tests/src/android/test/TestBrowserControllerImplTest.java
new file mode 100644
index 0000000..1315606
--- /dev/null
+++ b/test-runner/tests/src/android/test/TestBrowserControllerImplTest.java
@@ -0,0 +1,134 @@
+/*
+ * 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.content.Intent;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class TestBrowserControllerImplTest extends TestCase {
+    private TestBrowserControllerImpl mTestBrowserController;
+    private TestBrowserViewStub mTestBrowserView;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mTestBrowserController = new TestBrowserControllerImpl();
+        mTestBrowserView = new TestBrowserViewStub();
+        mTestBrowserController.registerView(mTestBrowserView);
+    }
+
+    public void testSetTestSuite() throws Exception {
+        TestSuite testSuite = new TestSuite();
+        testSuite.addTestSuite(DummyTestCase.class);
+
+        mTestBrowserController.setTestSuite(testSuite);
+
+        verifyTestNames(Arrays.asList("Run All", DummyTestCase.class.getSimpleName()),
+                mTestBrowserView.getTestNames());
+    }
+
+    private static void verifyTestNames(List<String> expectedTestNames,
+            List<String> actualTestNames) {
+        assertEquals(expectedTestNames.size(), actualTestNames.size());
+
+        // We use endsWith instead of equals because the return value of
+        // class.getSimpleName(), when called on an inner class, varies
+        // from one vm to another.
+        // This allows the test to pass in multiple environments.
+        for (int i = 0; i < expectedTestNames.size(); i++) {
+            assertTrue(actualTestNames.get(i).endsWith(expectedTestNames.get(i)));
+        }
+    }
+
+    public void testGetIntentForTestSuite() throws Exception {
+        TestSuite testSuite = new TestSuite();
+        testSuite.addTestSuite(DummyTestCase.class);
+
+        String targetBrowserActvityClassName = "com.android.bogus.DummyActivity";
+        String expectedTargetPackageName = "com.android.bogus";
+        mTestBrowserController.setTargetBrowserActivityClassName(targetBrowserActvityClassName);
+        mTestBrowserController.setTestSuite(testSuite);
+        mTestBrowserController.setTargetPackageName(expectedTargetPackageName);
+        Intent intent = mTestBrowserController.getIntentForTestAt(1);
+        verifyIntent(intent, DummyTestCase.class, expectedTargetPackageName);
+        assertEquals(targetBrowserActvityClassName, intent.getComponent().getClassName());
+    }
+
+    public void testGetIntentForTestCase() throws Exception {
+        TestSuite testSuite = new TestSuite();
+        testSuite.addTest(new DummyTestCase());
+
+        mTestBrowserController.setTestSuite(testSuite);
+        Intent intent = mTestBrowserController.getIntentForTestAt(1);
+        verifyIntent(intent, DummyTestCase.class, "com.android.testharness");
+        assertEquals(TestBrowserControllerImpl.TEST_RUNNER_ACTIVITY_CLASS_NAME,
+                intent.getComponent().getClassName());
+        assertEquals("testDummyTest",
+                intent.getStringExtra(TestBrowserController.BUNDLE_EXTRA_TEST_METHOD_NAME));
+    }
+
+    public void testGetIntentForRunAll() throws Exception {
+        TestSuite testSuite = new DummyTestSuite();
+        testSuite.addTestSuite(DummyTestCase.class);
+
+        mTestBrowserController.setTestSuite(testSuite);
+        Intent intent = mTestBrowserController.getIntentForTestAt(0);
+        verifyIntent(intent, DummyTestSuite.class, "com.android.testharness");
+    }
+
+    private static void verifyIntent(Intent intent, Class testClass, String expectedPackageName) {
+        assertEquals(Intent.ACTION_RUN, intent.getAction());
+        assertEquals(Intent.FLAG_ACTIVITY_NEW_TASK,
+                intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK);
+        assertEquals(Intent.FLAG_ACTIVITY_MULTIPLE_TASK,
+                intent.getFlags() & Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+        assertEquals(testClass.getName(), intent.getData().toString());
+        assertEquals(expectedPackageName, intent.getComponent().getPackageName());
+    }
+
+    private static class DummyTestSuite extends TestSuite {
+        private DummyTestSuite() {
+            super(DummyTestSuite.class.getName());
+        }
+    }
+
+    private static class DummyTestCase extends TestCase {
+        private DummyTestCase() {
+            super("testDummyTest");
+        }
+
+        public void testDummyTest() throws Exception {
+        }
+    }
+
+    private class TestBrowserViewStub implements TestBrowserView {
+        private List<String> mTestNames;
+
+        public void setTestNames(List<String> testNames) {
+            mTestNames = testNames;
+        }
+
+        public List<String> getTestNames() {
+            return mTestNames;
+        }
+    }
+}
diff --git a/test-runner/tests/src/android/test/TestBrowserTests.java b/test-runner/tests/src/android/test/TestBrowserTests.java
new file mode 100644
index 0000000..535e2f8
--- /dev/null
+++ b/test-runner/tests/src/android/test/TestBrowserTests.java
@@ -0,0 +1,22 @@
+// Copyright 2007 The Android Open Source Project
+
+
+package android.test;
+
+import junit.framework.TestSuite;
+
+public class TestBrowserTests extends TestBrowserActivity {
+
+    @Override
+    public TestSuite getTopTestSuite() {
+        return suite();
+    }
+
+    public static TestSuite suite() {
+        TestSuite testSuite = new TestSuite(TestBrowserTests.class.getName());
+        testSuite.addTestSuite(TestBrowserControllerImplTest.class);
+        testSuite.addTestSuite(TestCaseUtilTest.class);
+
+        return testSuite;
+    }
+}
diff --git a/test-runner/tests/src/android/test/TestCaseUtilTest.java b/test-runner/tests/src/android/test/TestCaseUtilTest.java
new file mode 100644
index 0000000..bc6fa92
--- /dev/null
+++ b/test-runner/tests/src/android/test/TestCaseUtilTest.java
@@ -0,0 +1,100 @@
+/*
+ * 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.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import java.util.List;
+
+public class TestCaseUtilTest extends TestCase {
+
+    public void testGetTestCaseNamesForTestSuiteWithSuiteMethod() throws Exception {
+        TestSuite testSuite = new TwoTestsInTestSuite();
+
+        List<String> testCaseNames = TestCaseUtil.getTestCaseNames(testSuite, false);
+
+        assertEquals(2, testCaseNames.size());
+        assertTrue(testCaseNames.get(0).endsWith("OneTestTestCase"));
+        assertTrue(testCaseNames.get(1).endsWith("OneTestTestSuite"));
+    }
+    
+    public void testGetTestCaseNamesForTestCaseWithSuiteMethod() throws Exception {
+        TestCase testCase = new OneTestTestCaseWithSuite();
+
+        List<String> testCaseNames = TestCaseUtil.getTestCaseNames(testCase, false);
+
+        assertEquals(1, testCaseNames.size());
+        assertTrue(testCaseNames.get(0).endsWith("testOne"));
+    }
+
+    public void testCreateTestForTestCase() throws Exception {
+        Test test = TestCaseUtil.createTestSuite(OneTestTestCase.class);
+        assertEquals(1, test.countTestCases());
+    }
+    
+    public void testCreateTestForTestSuiteWithSuiteMethod() throws Exception {
+        Test test = TestCaseUtil.createTestSuite(TwoTestsInTestSuite.class);
+        assertEquals(2, test.countTestCases());
+    }
+
+    public void testCreateTestForTestCaseWithSuiteMethod() throws Exception {
+        Test test = TestCaseUtil.createTestSuite(OneTestTestCaseWithSuite.class);
+        assertEquals(1, test.countTestCases());
+    }
+    
+    public void testReturnEmptyStringForTestSuiteWithNoName() throws Exception {
+        assertEquals("", TestCaseUtil.getTestName(new TestSuite()));
+    }
+
+    public static class OneTestTestCase extends TestCase {
+        public void testOne() throws Exception {
+        }
+    }
+
+    public static class OneTestTestCaseWithSuite extends TestCase {
+        public static Test suite()  {
+            TestCase testCase = new OneTestTestCase();
+            testCase.setName("testOne");
+            return testCase;
+        }
+
+        public void testOne() throws Exception {
+        }
+
+        public void testTwo() throws Exception {
+        }
+    }
+
+    public static class OneTestTestSuite {
+        public static Test suite() {
+            TestSuite suite = new TestSuite(OneTestTestSuite.class.getName());
+            suite.addTestSuite(OneTestTestCase.class);
+            return suite;
+        }
+    }
+
+    public static class TwoTestsInTestSuite extends TestSuite {
+        public static Test suite() {
+            TestSuite suite = new TestSuite(TwoTestsInTestSuite.class.getName());
+            suite.addTestSuite(OneTestTestCase.class);
+            suite.addTest(OneTestTestSuite.suite());
+            return suite;
+        }
+    }
+}
diff --git a/test-runner/tests/src/android/test/suitebuilder/AssignableFromTest.java b/test-runner/tests/src/android/test/suitebuilder/AssignableFromTest.java
new file mode 100644
index 0000000..0f73e89
--- /dev/null
+++ b/test-runner/tests/src/android/test/suitebuilder/AssignableFromTest.java
@@ -0,0 +1,85 @@
+/*
+ * 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;
+
+import junit.framework.TestCase;
+
+import java.lang.reflect.Method;
+
+public class AssignableFromTest extends TestCase {
+    private AssignableFrom assignableFrom;
+
+
+    protected void setUp() throws Exception {
+        super.setUp();
+        assignableFrom = new AssignableFrom(Animal.class);
+    }
+
+    public void testSelfIsAssignable() throws Exception {
+        assertTrue(assignableFrom.apply(testMethodFor(Animal.class)));
+    }
+
+    public void testSubclassesAreAssignable() throws Exception {
+        assertTrue(assignableFrom.apply(testMethodFor(Mammal.class)));
+        assertTrue(assignableFrom.apply(testMethodFor(Human.class)));
+    }
+
+    public void testNotAssignable() throws Exception {
+        assertFalse(assignableFrom.apply(testMethodFor(Pencil.class)));
+    }
+
+    public void testImplementorsAreAssignable() throws Exception {
+        assignableFrom = new AssignableFrom(WritingInstrument.class);
+
+        assertTrue(assignableFrom.apply(testMethodFor(Pencil.class)));
+        assertTrue(assignableFrom.apply(testMethodFor(Pen.class)));
+    }
+
+    private TestMethod testMethodFor(Class<? extends TestCase> aClass)
+            throws NoSuchMethodException {
+        Method method = aClass.getMethod("testX");
+        return new TestMethod(method, aClass);
+    }
+
+    private class Animal extends TestCase {
+        public void testX() {
+        }
+    }
+
+    private class Mammal extends Animal {
+        public void testX() {
+        }
+    }
+
+    private class Human extends Mammal {
+        public void testX() {
+        }
+    }
+
+    private interface WritingInstrument {
+    }
+
+    private class Pencil extends TestCase implements WritingInstrument {
+        public void testX() {
+        }
+    }
+
+    private class Pen extends TestCase implements WritingInstrument {
+        public void testX() {
+        }
+    }
+}
diff --git a/test-runner/tests/src/android/test/suitebuilder/InstrumentationTestSuiteBuilderTest.java b/test-runner/tests/src/android/test/suitebuilder/InstrumentationTestSuiteBuilderTest.java
new file mode 100644
index 0000000..1872803
--- /dev/null
+++ b/test-runner/tests/src/android/test/suitebuilder/InstrumentationTestSuiteBuilderTest.java
@@ -0,0 +1,116 @@
+/*
+ * 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;
+
+import static android.test.suitebuilder.ListTestCaseNames.getTestCaseNames;
+import android.test.suitebuilder.examples.OuterTest;
+import android.test.suitebuilder.examples.instrumentation.InstrumentationTest;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestListener;
+import junit.framework.TestResult;
+import junit.framework.TestSuite;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class InstrumentationTestSuiteBuilderTest extends TestCase {
+
+    private InstrumentationTestSuiteBuilder instrumentationTestSuiteBuilder;
+
+    protected void setUp() throws Exception {
+        super.setUp();
+        instrumentationTestSuiteBuilder = new InstrumentationTestSuiteBuilder(getClass());
+    }
+
+    public void testShouldIncludeIntrumentationTests() throws Exception {
+        instrumentationTestSuiteBuilder.includePackages(packageFor(InstrumentationTest.class));
+
+        SuiteExecutionRecorder recorder = runSuite(instrumentationTestSuiteBuilder);
+
+        assertEquals(1, recorder.testsSeen.size());
+        assertTrue(recorder.saw("InstrumentationTest.testInstrumentation"));
+    }
+
+    public void testShouldOnlyIncludeIntrumentationTests() throws Exception {
+        TestSuite testSuite = new OuterTest()
+                .buildTestsUnderHereWith(instrumentationTestSuiteBuilder);
+        List<String> testCaseNames = getTestCaseNames(testSuite);
+        assertEquals(1, testCaseNames.size());
+        assertEquals("testInstrumentation", testCaseNames.get(0));
+    }
+
+    private static String packageFor(Class clazz) {
+        String className = clazz.getName();
+        return className.substring(0, className.lastIndexOf('.'));
+    }
+
+    private SuiteExecutionRecorder runSuite(TestSuiteBuilder builder) {
+        TestSuite suite = builder.build();
+        SuiteExecutionRecorder recorder = new SuiteExecutionRecorder();
+        TestResult result = new TestResult();
+        result.addListener(recorder);
+        suite.run(result);
+        return recorder;
+    }
+
+    private class SuiteExecutionRecorder implements TestListener {
+
+        private Set<String> failures = new HashSet<String>();
+        private Set<String> errors = new HashSet<String>();
+        private Set<String> testsSeen = new HashSet<String>();
+
+        public void addError(Test test, Throwable t) {
+            errors.add(testName(test));
+        }
+
+        public void addFailure(Test test, AssertionFailedError t) {
+            failures.add(testName(test));
+        }
+
+        public void endTest(Test test) {
+        }
+
+        public void startTest(Test test) {
+            testsSeen.add(testName(test));
+        }
+
+        public boolean saw(String testName) {
+            return testsSeen.contains(testName);
+        }
+
+        public boolean failed(String testName) {
+            return failures.contains(testName);
+        }
+
+        public boolean errored(String testName) {
+            return errors.contains(testName);
+        }
+
+        public boolean passed(String testName) {
+            return saw(testName) && !failed(testName) && !errored(testName);
+        }
+
+        private String testName(Test test) {
+            TestCase testCase = (TestCase) test;
+            return testCase.getClass().getSimpleName() + "." + testCase.getName();
+        }
+    }
+}
diff --git a/test-runner/tests/src/android/test/suitebuilder/ListTestCaseNames.java b/test-runner/tests/src/android/test/suitebuilder/ListTestCaseNames.java
new file mode 100644
index 0000000..37ec328
--- /dev/null
+++ b/test-runner/tests/src/android/test/suitebuilder/ListTestCaseNames.java
@@ -0,0 +1,102 @@
+/*
+ * 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;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class ListTestCaseNames {
+    public static List<String> getTestCaseNames(TestSuite suite) {
+        // TODO: deprecate this method and move all callers to use getTestNames
+        List<Test> tests = Collections.<Test>list(suite.tests());
+        ArrayList<String> testCaseNames = new ArrayList<String>();
+        for (Test test : tests) {
+            if (test instanceof TestCase) {
+                testCaseNames.add(((TestCase) test).getName());
+            } else if (test instanceof TestSuite) {
+                testCaseNames.addAll(getTestCaseNames((TestSuite) test));
+            }
+        }
+        return testCaseNames;
+    }
+    
+    /** 
+     * Returns a list of test class and method names for each TestCase in suite.  
+     */
+    public static List<TestDescriptor> getTestNames(TestSuite suite) {
+        List<Test> tests = Collections.<Test>list(suite.tests());
+        ArrayList<TestDescriptor> testNames = new ArrayList<TestDescriptor>();
+        for (Test test : tests) {
+            if (test instanceof TestCase) {
+                String className = test.getClass().getName();
+                String testName = ((TestCase) test).getName();
+                testNames.add(new TestDescriptor(className, testName));
+            } else if (test instanceof TestSuite) {
+                testNames.addAll(getTestNames((TestSuite) test));
+            }
+        }
+        return testNames;
+    }
+    
+    /**
+     * Data holder for test case info
+     */
+    public static class TestDescriptor {
+       private String mClassName;
+       private String mTestName;
+      
+       public TestDescriptor(String className, String testName) {
+           mClassName = className;
+           mTestName = testName;
+       }
+       
+       public String getClassName() {
+           return mClassName;
+       }
+       
+       public String getTestName() {
+           return mTestName;
+       }
+       
+       /**
+        * Override parent to do string-based class and test name comparison
+        */
+       @Override
+       public boolean equals(Object otherObj) {
+           if (otherObj instanceof TestDescriptor) {
+               TestDescriptor otherDesc = (TestDescriptor)otherObj;
+               return otherDesc.getClassName().equals(this.getClassName()) && 
+                      otherDesc.getTestName().equals(this.getTestName());
+               
+           }
+           return false;
+       }
+       
+       /**
+        * Override parent to return a more user-friendly display string
+        */
+       @Override
+       public String toString() {
+           return getClassName() + "#" + getTestName();
+       }
+    }
+}
diff --git a/test-runner/tests/src/android/test/suitebuilder/SmokeTestSuiteBuilderTest.java b/test-runner/tests/src/android/test/suitebuilder/SmokeTestSuiteBuilderTest.java
new file mode 100644
index 0000000..f817297
--- /dev/null
+++ b/test-runner/tests/src/android/test/suitebuilder/SmokeTestSuiteBuilderTest.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;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import java.util.List;
+
+public class SmokeTestSuiteBuilderTest extends TestCase {
+
+    public void testShouldOnlyIncludeSmokeTests() throws Exception {
+        TestSuite testSuite = new SmokeTestSuiteBuilder(getClass())
+                .includeAllPackagesUnderHere().build();
+
+        List<String> testCaseNames = ListTestCaseNames.getTestCaseNames(testSuite);
+        assertEquals("Unexpected number of smoke tests.", 1, testCaseNames.size());
+        assertEquals("Unexpected test name", "testSmoke", testCaseNames.get(0));
+    }
+}
diff --git a/test-runner/tests/src/android/test/suitebuilder/TestSuiteBuilderTest.java b/test-runner/tests/src/android/test/suitebuilder/TestSuiteBuilderTest.java
new file mode 100644
index 0000000..293c813
--- /dev/null
+++ b/test-runner/tests/src/android/test/suitebuilder/TestSuiteBuilderTest.java
@@ -0,0 +1,218 @@
+/*
+ * 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;
+
+import com.android.internal.util.Predicate;
+import static android.test.suitebuilder.ListTestCaseNames.getTestCaseNames;
+import android.test.suitebuilder.examples.OuterTest;
+import android.test.suitebuilder.examples.suppress.SuppressedTest;
+import android.test.suitebuilder.examples.error.ErrorTest;
+import android.test.suitebuilder.examples.error.FailingTest;
+import android.test.suitebuilder.examples.nested.Level1Test;
+import android.test.suitebuilder.examples.nested.nested.Level2Test;
+import android.test.suitebuilder.examples.simple.SimpleTest;
+import android.test.suitebuilder.examples.subclass.SubclassTest;
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestListener;
+import junit.framework.TestResult;
+import junit.framework.TestSuite;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+
+public class TestSuiteBuilderTest extends TestCase {
+
+    private TestSuiteBuilder testSuiteBuilder;
+
+    protected void setUp() throws Exception {
+        super.setUp();
+        testSuiteBuilder = new TestSuiteBuilder(getClass());
+    }
+
+    public void testShouldRunSimpleTests() throws Exception {
+        testSuiteBuilder.includePackages(packageFor(SimpleTest.class));
+
+        SuiteExecutionRecorder recorder = runSuite(testSuiteBuilder);
+
+        assertTrue(recorder.passed("SimpleTest.testSimpleOne"));
+        assertTrue(recorder.passed("SimpleTest.testSimpleTwo"));
+        assertTrue(recorder.passed("AnotherSimpleTest.testAnotherOne"));
+    }
+
+    public void testShouldOnlyIncludeTestsThatSatisfyAllPredicates() throws Exception {
+        testSuiteBuilder.includePackages(packageFor(SimpleTest.class))
+                .addRequirements(testsWhoseNameContains("test"))
+                .addRequirements(testsWhoseNameContains("Simple"))
+                .addRequirements(testsWhoseNameContains("Two"));
+
+        SuiteExecutionRecorder recorder = runSuite(testSuiteBuilder);
+
+        assertTrue(recorder.passed("SimpleTest.testSimpleTwo"));
+    }
+
+    public void testShouldAddFailingTestsToSuite() throws Exception {
+        testSuiteBuilder.includePackages(packageFor(FailingTest.class));
+
+        SuiteExecutionRecorder recorder = runSuite(testSuiteBuilder);
+
+        assertTrue(recorder.failed("FailingTest.testFailOne"));
+        assertTrue(recorder.failed("FailingTest.testFailTwo"));
+    }
+
+    public void testShouldAddTestsWithErrorsToSuite() throws Exception {
+        testSuiteBuilder.includePackages(packageFor(ErrorTest.class));
+
+        SuiteExecutionRecorder recorder = runSuite(testSuiteBuilder);
+
+        assertTrue(recorder.errored("ErrorTest.testErrorOne"));
+        assertTrue(recorder.errored("ErrorTest.testErrorTwo"));
+    }
+
+    public void testShouldRunTestsInheritedFromSuperclass() throws Exception {
+        testSuiteBuilder.includePackages(packageFor(SubclassTest.class));
+
+        SuiteExecutionRecorder recorder = runSuite(testSuiteBuilder);
+
+        assertEquals(2, getTestCaseNames(testSuiteBuilder.build()).size());
+
+        assertTrue(recorder.passed("SubclassTest.testSubclass"));
+        assertTrue(recorder.passed("SubclassTest.testSuperclass"));
+        assertFalse(recorder.saw("SuperclassTest.testSuperclass"));
+    }
+
+    public void testShouldIncludeTestsInSubPackagesRecursively() throws Exception {
+        testSuiteBuilder.includePackages(packageFor(Level1Test.class));
+
+        SuiteExecutionRecorder recorder = runSuite(testSuiteBuilder);
+
+        assertTrue(recorder.passed("Level1Test.testLevel1"));
+        assertTrue(recorder.passed("Level2Test.testLevel2"));
+    }
+
+    public void testExcludePackage() throws Exception {
+        testSuiteBuilder.includePackages(packageFor(SimpleTest.class),
+                packageFor(Level1Test.class)).excludePackages(packageFor(Level2Test.class));
+
+        TestSuite testSuite = testSuiteBuilder.build();
+        assertContentsInOrder(getTestCaseNames(testSuite),
+                "testLevel1", "testAnotherOne", "testSimpleOne", "testSimpleTwo");
+    }
+
+    public void testShouldExcludeSuppressedTests() throws Exception {
+        testSuiteBuilder.includePackages(packageFor(SuppressedTest.class));
+        testSuiteBuilder.build();
+
+        SuiteExecutionRecorder recorder = runSuite(testSuiteBuilder);
+
+        assertEquals(1, recorder.testsSeen.size());
+        assertTrue(recorder.passed("PartiallySuppressedTest.testUnSuppressedMethod"));
+    }
+
+    /**
+     * This test calls {@link OuterTest#buildTestsUnderHereRecursively()} to control
+     * the packages under test. The call to {@link TestSuiteBuilder#includeAllPackagesUnderHere()}
+     * is made from there so that only return the example tests.
+     */
+    public void testIncludeAllPackagesUnderHere() throws Exception {
+
+        TestSuite testSuite = new OuterTest().buildTestsUnderHereRecursively();
+        assertContentsInOrder(getTestCaseNames(testSuite),
+                "testOuter", "testErrorOne", "testErrorTwo", "testFailOne", "testFailTwo",
+                "testInstrumentation", "testLevel1", "testLevel2", "testAnotherOne",
+                "testSimpleOne", "testSimpleTwo", "testNonSmoke", "testSmoke", "testSubclass",
+                "testSuperclass", "testUnSuppressedMethod");
+    }
+
+    private void assertContentsInOrder(List<String> actual, String... source) {
+        String[] clonedSource = source.clone();
+        assertEquals("Unexpected number of items.", clonedSource.length, actual.size());
+        for (int i = 0; i < actual.size(); i++) {
+            String actualItem = actual.get(i);
+            String sourceItem = clonedSource[i];
+            assertEquals("Unexpected item. Index: " + i, sourceItem, actualItem);
+        }
+    }
+
+    private static String packageFor(Class clazz) {
+        String className = clazz.getName();
+        return className.substring(0, className.lastIndexOf('.'));
+    }
+
+    private Predicate<TestMethod> testsWhoseNameContains(final String string) {
+        return new Predicate<TestMethod>() {
+            public boolean apply(TestMethod testMethod) {
+                return testMethod.getName().contains(string);
+            }
+        };
+    }
+
+    private SuiteExecutionRecorder runSuite(TestSuiteBuilder builder) {
+        TestSuite suite = builder.build();
+        SuiteExecutionRecorder recorder = new SuiteExecutionRecorder();
+        TestResult result = new TestResult();
+        result.addListener(recorder);
+        suite.run(result);
+        return recorder;
+    }
+
+    private class SuiteExecutionRecorder implements TestListener {
+
+        private Set<String> failures = new HashSet<String>();
+        private Set<String> errors = new HashSet<String>();
+        private Set<String> testsSeen = new HashSet<String>();
+
+        public void addError(Test test, Throwable t) {
+            errors.add(testName(test));
+        }
+
+        public void addFailure(Test test, AssertionFailedError t) {
+            failures.add(testName(test));
+        }
+
+        public void endTest(Test test) {
+        }
+
+        public void startTest(Test test) {
+            testsSeen.add(testName(test));
+        }
+
+        public boolean saw(String testName) {
+            return testsSeen.contains(testName);
+        }
+
+        public boolean failed(String testName) {
+            return failures.contains(testName);
+        }
+
+        public boolean errored(String testName) {
+            return errors.contains(testName);
+        }
+
+        public boolean passed(String testName) {
+            return saw(testName) && !failed(testName) && !errored(testName);
+        }
+
+        private String testName(Test test) {
+            TestCase testCase = (TestCase) test;
+            return testCase.getClass().getSimpleName() + "." + testCase.getName();
+        }
+    }
+}
diff --git a/test-runner/tests/src/android/test/suitebuilder/UnitTestSuiteBuilderTest.java b/test-runner/tests/src/android/test/suitebuilder/UnitTestSuiteBuilderTest.java
new file mode 100644
index 0000000..469938e
--- /dev/null
+++ b/test-runner/tests/src/android/test/suitebuilder/UnitTestSuiteBuilderTest.java
@@ -0,0 +1,109 @@
+/*
+ * 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;
+
+import android.test.suitebuilder.examples.instrumentation.InstrumentationTest;
+
+import junit.framework.Assert;
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestListener;
+import junit.framework.TestResult;
+import junit.framework.TestSuite;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class UnitTestSuiteBuilderTest extends TestCase {
+
+    private UnitTestSuiteBuilder unitTestSuiteBuilder;
+
+    protected void setUp() throws Exception {
+        super.setUp();
+        unitTestSuiteBuilder = new UnitTestSuiteBuilder(getClass());
+    }
+
+    public void testShouldExcludeIntrumentationTests() throws Exception {
+        unitTestSuiteBuilder.includePackages(packageFor(InstrumentationTest.class));
+
+        TestSuite testSuite = unitTestSuiteBuilder.build();
+        Assert.assertEquals(0, ListTestCaseNames.getTestCaseNames(testSuite).size());
+
+        SuiteExecutionRecorder recorder = runSuite(unitTestSuiteBuilder);
+
+        assertFalse(recorder.saw("InstrumentationTest.testInstrumentation"));
+        assertTrue(recorder.testsSeen.isEmpty());
+    }
+
+    private static String packageFor(Class clazz) {
+        String className = clazz.getName();
+        return className.substring(0, className.lastIndexOf('.'));
+    }
+
+    private SuiteExecutionRecorder runSuite(TestSuiteBuilder builder) {
+        TestSuite suite = builder.build();
+        SuiteExecutionRecorder recorder = new SuiteExecutionRecorder();
+        TestResult result = new TestResult();
+        result.addListener(recorder);
+        suite.run(result);
+        return recorder;
+    }
+
+    private class SuiteExecutionRecorder implements TestListener {
+
+        private Set<String> failures = new HashSet<String>();
+        private Set<String> errors = new HashSet<String>();
+        private Set<String> testsSeen = new HashSet<String>();
+
+        public void addError(Test test, Throwable t) {
+            errors.add(testName(test));
+        }
+
+        public void addFailure(Test test, AssertionFailedError t) {
+            failures.add(testName(test));
+        }
+
+        public void endTest(Test test) {
+        }
+
+        public void startTest(Test test) {
+            testsSeen.add(testName(test));
+        }
+
+        public boolean saw(String testName) {
+            return testsSeen.contains(testName);
+        }
+
+        public boolean failed(String testName) {
+            return failures.contains(testName);
+        }
+
+        public boolean errored(String testName) {
+            return errors.contains(testName);
+        }
+
+        public boolean passed(String testName) {
+            return saw(testName) && !failed(testName) && !errored(testName);
+        }
+
+        private String testName(Test test) {
+            TestCase testCase = (TestCase) test;
+            return testCase.getClass().getSimpleName() + "." + testCase.getName();
+        }
+    }
+}
diff --git a/test-runner/tests/src/android/test/suitebuilder/annotation/HasAnnotationTest.java b/test-runner/tests/src/android/test/suitebuilder/annotation/HasAnnotationTest.java
new file mode 100644
index 0000000..edf067d
--- /dev/null
+++ b/test-runner/tests/src/android/test/suitebuilder/annotation/HasAnnotationTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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 android.test.suitebuilder.TestMethod;
+import junit.framework.TestCase;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.Method;
+
+public class HasAnnotationTest extends TestCase {
+
+    public void testThatMethodWithAnnotationIsReportedAsBeingAnnotated() throws Exception {
+        assertTrue(hasExampleAnnotation(ClassWithAnnotation.class, "testWithAnnotation"));
+        assertTrue(hasExampleAnnotation(ClassWithoutAnnotation.class, "testWithAnnotation"));
+    }
+
+    public void testThatMethodWithOutAnnotationIsNotReportedAsBeingAnnotated() throws Exception {
+        assertFalse(hasExampleAnnotation(ClassWithoutAnnotation.class, "testWithoutAnnotation"));
+    }
+
+    public void testThatClassAnnotatioCausesAllMethodsToBeReportedAsBeingAnnotated()
+            throws Exception {
+        assertTrue(hasExampleAnnotation(ClassWithAnnotation.class, "testWithoutAnnotation"));
+    }
+
+    private boolean hasExampleAnnotation(Class<? extends TestCase> aClass, String methodName)
+            throws NoSuchMethodException {
+        Method method = aClass.getMethod(methodName);
+        TestMethod testMethod = new TestMethod(method, aClass);
+        return new HasAnnotation(Example.class).apply(testMethod);
+    }
+
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target({ElementType.TYPE, ElementType.METHOD})
+    public @interface Example {
+    }
+
+    @Example
+    static class ClassWithAnnotation extends TestCase {
+
+        @Example
+        public void testWithAnnotation() {
+        }
+
+        public void testWithoutAnnotation() {
+        }
+    }
+
+    static class ClassWithoutAnnotation extends TestCase {
+
+        @Example
+        public void testWithAnnotation() {
+        }
+
+        public void testWithoutAnnotation() {
+        }
+    }
+}
diff --git a/test-runner/tests/src/android/test/suitebuilder/annotation/HasClassAnnotationTest.java b/test-runner/tests/src/android/test/suitebuilder/annotation/HasClassAnnotationTest.java
new file mode 100644
index 0000000..051ea54
--- /dev/null
+++ b/test-runner/tests/src/android/test/suitebuilder/annotation/HasClassAnnotationTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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 android.test.suitebuilder.TestMethod;
+import junit.framework.TestCase;
+
+import java.lang.reflect.Method;
+
+public class HasClassAnnotationTest extends TestCase {
+
+    public void testShouldTellIfParentClassHasSpecifiedClassification()
+            throws NoSuchMethodException {
+        assertTrue(classHasAnnotation(SmokeTestExample.class, Smoke.class));
+    }
+
+    public void testShouldTellIfParentClassDoesNotHaveSpecifiedClassification()
+            throws NoSuchMethodException {
+        assertFalse(classHasAnnotation(NonSmokeTestExample.class, Smoke.class));
+    }
+
+    private boolean classHasAnnotation(
+            Class<? extends TestCase> aClass,
+            Class<Smoke> expectedClassification) throws NoSuchMethodException {
+        Method method = aClass.getMethod("testSomeTest");
+
+        TestMethod testMethod = new TestMethod(method, aClass);
+        return new HasClassAnnotation(expectedClassification).apply(testMethod);
+    }
+
+    @Smoke
+    static class SmokeTestExample extends TestCase {
+
+        public void testSomeTest() {
+        }
+    }
+
+    static class NonSmokeTestExample extends TestCase {
+
+        public void testSomeTest() {
+        }
+    }
+}
diff --git a/test-runner/tests/src/android/test/suitebuilder/annotation/HasMethodAnnotationTest.java b/test-runner/tests/src/android/test/suitebuilder/annotation/HasMethodAnnotationTest.java
new file mode 100644
index 0000000..c864e28
--- /dev/null
+++ b/test-runner/tests/src/android/test/suitebuilder/annotation/HasMethodAnnotationTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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 android.test.suitebuilder.TestMethod;
+import junit.framework.TestCase;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+
+
+public class HasMethodAnnotationTest extends TestCase {
+
+    public void testMethodWithSpecifiedAttribute() throws Exception {
+        assertTrue(methodHasAnnotation(AnnotatedMethodExample.class,
+                "testThatIsAnnotated", Smoke.class));
+    }
+
+    public void testMethodWithoutSpecifiedAttribute() throws Exception {
+        assertFalse(methodHasAnnotation(AnnotatedMethodExample.class,
+                "testThatIsNotAnnotated", Smoke.class));
+    }
+
+    private boolean methodHasAnnotation(Class<? extends TestCase> aClass,
+            String methodName,
+            Class<? extends Annotation> expectedClassification
+    ) throws NoSuchMethodException {
+        Method method = aClass.getMethod(methodName);
+        TestMethod testMethod = new TestMethod(method, aClass);
+        return new HasMethodAnnotation(expectedClassification).apply(testMethod);
+    }
+
+    static class AnnotatedMethodExample extends TestCase {
+
+        @Smoke
+        public void testThatIsAnnotated() {
+        }
+
+        public void testThatIsNotAnnotated() {
+        }
+    }
+}
diff --git a/test-runner/tests/src/android/test/suitebuilder/examples/OuterTest.java b/test-runner/tests/src/android/test/suitebuilder/examples/OuterTest.java
new file mode 100644
index 0000000..4659bf9
--- /dev/null
+++ b/test-runner/tests/src/android/test/suitebuilder/examples/OuterTest.java
@@ -0,0 +1,37 @@
+/*
+ * 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.examples;
+
+import android.test.suitebuilder.TestSuiteBuilder;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+public class OuterTest extends TestCase {
+
+    public void testOuter() {
+        assertTrue(true);
+    }
+
+    public TestSuite buildTestsUnderHereRecursively() {
+        return buildTestsUnderHereWith(new TestSuiteBuilder(getClass()));
+    }
+
+    public TestSuite buildTestsUnderHereWith(TestSuiteBuilder testSuiteBuilder) {
+        return testSuiteBuilder.includeAllPackagesUnderHere().build();
+    }
+}
diff --git a/test-runner/tests/src/android/test/suitebuilder/examples/error/ErrorTest.java b/test-runner/tests/src/android/test/suitebuilder/examples/error/ErrorTest.java
new file mode 100644
index 0000000..f1f6113
--- /dev/null
+++ b/test-runner/tests/src/android/test/suitebuilder/examples/error/ErrorTest.java
@@ -0,0 +1,30 @@
+/*
+ * 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.examples.error;
+
+import junit.framework.TestCase;
+
+public class ErrorTest extends TestCase {
+
+    public void testErrorOne() throws Exception {
+        throw new RuntimeException("Expected");
+    }
+
+    public void testErrorTwo() throws Exception {
+        throw new RuntimeException("Expected");
+    }
+}
diff --git a/test-runner/tests/src/android/test/suitebuilder/examples/error/FailingTest.java b/test-runner/tests/src/android/test/suitebuilder/examples/error/FailingTest.java
new file mode 100644
index 0000000..428fd23
--- /dev/null
+++ b/test-runner/tests/src/android/test/suitebuilder/examples/error/FailingTest.java
@@ -0,0 +1,30 @@
+/*
+ * 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.examples.error;
+
+import junit.framework.TestCase;
+
+public class FailingTest extends TestCase {
+
+    public void testFailOne() throws Exception {
+        fail("Expected");
+    }
+
+    public void testFailTwo() throws Exception {
+        fail("Expected");
+    }
+}
diff --git a/test-runner/tests/src/android/test/suitebuilder/examples/instrumentation/InstrumentationTest.java b/test-runner/tests/src/android/test/suitebuilder/examples/instrumentation/InstrumentationTest.java
new file mode 100644
index 0000000..5158a90
--- /dev/null
+++ b/test-runner/tests/src/android/test/suitebuilder/examples/instrumentation/InstrumentationTest.java
@@ -0,0 +1,26 @@
+/*
+ * 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.examples.instrumentation;
+
+import android.test.InstrumentationTestCase;
+
+public class InstrumentationTest extends InstrumentationTestCase {
+
+    public void testInstrumentation() throws Exception {
+        assertTrue(true);
+    }
+}
diff --git a/test-runner/tests/src/android/test/suitebuilder/examples/nested/Level1Test.java b/test-runner/tests/src/android/test/suitebuilder/examples/nested/Level1Test.java
new file mode 100644
index 0000000..17d39d6
--- /dev/null
+++ b/test-runner/tests/src/android/test/suitebuilder/examples/nested/Level1Test.java
@@ -0,0 +1,26 @@
+/*
+ * 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.examples.nested;
+
+import junit.framework.TestCase;
+
+public class Level1Test extends TestCase {
+
+    public void testLevel1() throws Exception {
+        assertTrue(true);
+    }
+}
diff --git a/test-runner/tests/src/android/test/suitebuilder/examples/nested/nested/Level2Test.java b/test-runner/tests/src/android/test/suitebuilder/examples/nested/nested/Level2Test.java
new file mode 100644
index 0000000..6f0daca
--- /dev/null
+++ b/test-runner/tests/src/android/test/suitebuilder/examples/nested/nested/Level2Test.java
@@ -0,0 +1,26 @@
+/*
+ * 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.examples.nested.nested;
+
+import junit.framework.TestCase;
+
+public class Level2Test extends TestCase {
+
+    public void testLevel2() throws Exception {
+        assertTrue(true);
+    }
+}
diff --git a/test-runner/tests/src/android/test/suitebuilder/examples/simple/AnotherSimpleTest.java b/test-runner/tests/src/android/test/suitebuilder/examples/simple/AnotherSimpleTest.java
new file mode 100644
index 0000000..0dfeda8
--- /dev/null
+++ b/test-runner/tests/src/android/test/suitebuilder/examples/simple/AnotherSimpleTest.java
@@ -0,0 +1,26 @@
+/*
+ * 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.examples.simple;
+
+import junit.framework.TestCase;
+
+public class AnotherSimpleTest extends TestCase {
+
+    public void testAnotherOne() throws Exception {
+        assertTrue(true);
+    }
+}
diff --git a/test-runner/tests/src/android/test/suitebuilder/examples/simple/SimpleTest.java b/test-runner/tests/src/android/test/suitebuilder/examples/simple/SimpleTest.java
new file mode 100644
index 0000000..4dcac44
--- /dev/null
+++ b/test-runner/tests/src/android/test/suitebuilder/examples/simple/SimpleTest.java
@@ -0,0 +1,30 @@
+/*
+ * 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.examples.simple;
+
+import junit.framework.TestCase;
+
+public class SimpleTest extends TestCase {
+
+    public void testSimpleOne() throws Exception {
+        assertTrue(true);
+    }
+
+    public void testSimpleTwo() throws Exception {
+        assertTrue(true);
+    }
+}
diff --git a/test-runner/tests/src/android/test/suitebuilder/examples/smoke/NonSmokeTest.java b/test-runner/tests/src/android/test/suitebuilder/examples/smoke/NonSmokeTest.java
new file mode 100644
index 0000000..1512ba7
--- /dev/null
+++ b/test-runner/tests/src/android/test/suitebuilder/examples/smoke/NonSmokeTest.java
@@ -0,0 +1,26 @@
+/*
+ * 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.examples.smoke;
+
+import junit.framework.TestCase;
+
+public class NonSmokeTest extends TestCase {
+
+    public void testNonSmoke() throws Exception {
+        assertTrue(true);
+    }
+}
diff --git a/test-runner/tests/src/android/test/suitebuilder/examples/smoke/SmokeTest.java b/test-runner/tests/src/android/test/suitebuilder/examples/smoke/SmokeTest.java
new file mode 100644
index 0000000..c3515df
--- /dev/null
+++ b/test-runner/tests/src/android/test/suitebuilder/examples/smoke/SmokeTest.java
@@ -0,0 +1,28 @@
+/*
+ * 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.examples.smoke;
+
+import android.test.suitebuilder.annotation.Smoke;
+import junit.framework.TestCase;
+
+@Smoke
+public class SmokeTest extends TestCase {
+
+    public void testSmoke() throws Exception {
+        assertTrue(true);
+    }
+}
diff --git a/test-runner/tests/src/android/test/suitebuilder/examples/subclass/SubclassTest.java b/test-runner/tests/src/android/test/suitebuilder/examples/subclass/SubclassTest.java
new file mode 100644
index 0000000..0ab8c72
--- /dev/null
+++ b/test-runner/tests/src/android/test/suitebuilder/examples/subclass/SubclassTest.java
@@ -0,0 +1,24 @@
+/*
+ * 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.examples.subclass;
+
+public class SubclassTest extends SuperclassTest {
+
+    public void testSubclass() throws Exception {
+        assertTrue(true);
+    }
+}
diff --git a/test-runner/tests/src/android/test/suitebuilder/examples/subclass/SuperclassTest.java b/test-runner/tests/src/android/test/suitebuilder/examples/subclass/SuperclassTest.java
new file mode 100644
index 0000000..05513c5
--- /dev/null
+++ b/test-runner/tests/src/android/test/suitebuilder/examples/subclass/SuperclassTest.java
@@ -0,0 +1,26 @@
+/*
+ * 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.examples.subclass;
+
+import junit.framework.TestCase;
+
+public abstract class SuperclassTest extends TestCase {
+
+    public void testSuperclass() throws Exception {
+        assertTrue(true);
+    }
+}
diff --git a/test-runner/tests/src/android/test/suitebuilder/examples/suppress/PartiallySuppressedTest.java b/test-runner/tests/src/android/test/suitebuilder/examples/suppress/PartiallySuppressedTest.java
new file mode 100644
index 0000000..3ca0f70
--- /dev/null
+++ b/test-runner/tests/src/android/test/suitebuilder/examples/suppress/PartiallySuppressedTest.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.examples.suppress;
+
+import android.test.suitebuilder.annotation.Suppress;
+
+import junit.framework.TestCase;
+
+public class PartiallySuppressedTest extends TestCase {
+
+    @Suppress
+    public void testSuppressedMethod() throws Exception {
+        assertTrue(true);
+    }
+
+    public void testUnSuppressedMethod() throws Exception {
+        assertTrue(true);
+    }
+}
diff --git a/test-runner/tests/src/android/test/suitebuilder/examples/suppress/SuppressedTest.java b/test-runner/tests/src/android/test/suitebuilder/examples/suppress/SuppressedTest.java
new file mode 100644
index 0000000..c4e0e07
--- /dev/null
+++ b/test-runner/tests/src/android/test/suitebuilder/examples/suppress/SuppressedTest.java
@@ -0,0 +1,29 @@
+/*
+ * 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.examples.suppress;
+
+import android.test.suitebuilder.annotation.Suppress;
+
+import junit.framework.TestCase;
+
+@Suppress
+public class SuppressedTest extends TestCase {
+
+    public void testSuppressedClass() throws Exception {
+        assertTrue(true);
+    }
+}