CtsVerifier Test List Infrastructure

Bug 2732160

Add a button to the main activity that launches a list activity
showing the list of manual tests that can be run. The test list
activity queries for activities that support intents with the
category "android.cts.intent.category.MANUAL_TEST" category.

Add some simple tests using ActivityMonitor to make sure that
the whole thing somewhat works. Its not entirely necessary,
but it was useful to learn about ActivityMonitor and be able
to play around with the AndroidManifest.xml to see what breaks.

Launch emulator and use the following command to build, install,
and run (out path omitted for brevity):

mmm cts/apps &&
adb install -r out/.../CtsVerifier.apk &&
adb install -r out/.../CtsVerifierTests.apk &&
runtest -b --path cts/apps/CtsVerifier

Change-Id: I915dfb64f58f6e080d14c45d07a037f7eb81dd9d
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 54611c3..ebd0114 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -21,13 +21,23 @@
       android:versionName="1.0">
 
     <application android:label="@string/app_name">
-        <activity android:name=".CtsVerifierActivity"
-                  android:label="@string/app_name">
+
+        <activity android:name=".CtsVerifierActivity" android:label="@string/app_name">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+
+        <activity android:name=".TestListActivity" android:label="@string/test_list_title" />
+
+        <activity android:name=".suid.SuidBinariesActivity" android:label="@string/suid_binaries">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+        </activity>
+
     </application>
 
 </manifest> 
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/main.xml b/apps/CtsVerifier/res/layout/main.xml
index c1fa365..52da8b8 100644
--- a/apps/CtsVerifier/res/layout/main.xml
+++ b/apps/CtsVerifier/res/layout/main.xml
@@ -21,7 +21,14 @@
     <TextView  
         android:id="@+id/welcome"
         android:layout_width="fill_parent" 
-        android:layout_height="wrap_content" 
+        android:layout_height="wrap_content"
         android:text="@string/welcome_text"
         />
+    <Button
+        android:id="@+id/continue_button"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:onClick="continueButtonClickHandler"
+        android:text="@string/continue_button_text"
+        />
 </LinearLayout>
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 5137dd1..9c1399d 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -16,4 +16,7 @@
 <resources>
     <string name="app_name">CTS Verifier</string>
     <string name="welcome_text">Welcome to the CTS Verifier!</string>
+    <string name="continue_button_text">Continue</string>
+    <string name="test_list_title">Manual Test List</string>
+    <string name="suid_binaries">SUID Binaries</string>
 </resources>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/CtsVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/CtsVerifierActivity.java
index f1b06d1..9c0566e 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/CtsVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/CtsVerifierActivity.java
@@ -17,8 +17,11 @@
 package com.android.cts.verifier;
 
 import android.app.Activity;
+import android.content.Intent;
 import android.os.Bundle;
+import android.view.View;
 
+/** {@link Activity} that displays an introduction to the verifier. */
 public class CtsVerifierActivity extends Activity {
 
     /** Called when the activity is first created. */
@@ -27,4 +30,8 @@
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
     }
+
+    public void continueButtonClickHandler(View target) {
+        startActivity(new Intent(this, TestListActivity.class));
+    }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/TestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/TestListActivity.java
new file mode 100644
index 0000000..7bd3488
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/TestListActivity.java
@@ -0,0 +1,120 @@
+/*
+ * 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.
+ */
+
+package com.android.cts.verifier;
+
+import android.app.ListActivity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import android.widget.SimpleAdapter;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/** {@link ListActivity} that displays a  list of manual tests. */
+public class TestListActivity extends ListActivity {
+
+    /** Activities implementing {@link Intent#ACTION_MAIN} and this will appear in the list. */
+    static final String CATEGORY_MANUAL_TEST = "android.cts.intent.category.MANUAL_TEST";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setListAdapter(new TestListAdapter(this));
+    }
+
+
+    /** Launch the activity when its {@link ListView} item is clicked. */
+    @Override
+    protected void onListItemClick(ListView listView, View view, int position, long id) {
+        super.onListItemClick(listView, view, position, id);
+        ListAdapter adapter = getListAdapter();
+        Intent intent = getIntent(position);
+        startActivity(intent);
+    }
+
+    @SuppressWarnings("unchecked")
+    private Intent getIntent(int position) {
+        ListAdapter adapter = getListAdapter();
+        Map<String, ?> data = (Map<String, ?>) adapter.getItem(position);
+        return (Intent) data.get(TestListAdapter.INTENT);
+    }
+
+    /**
+     * Each {@link ListView} item will have a map associated it with containing the title to
+     * display and the intent used to launch it.
+     */
+    static class TestListAdapter extends SimpleAdapter {
+
+        static final String TITLE = "title";
+
+        static final String INTENT = "intent";
+
+        TestListAdapter(Context context) {
+            super(context, getData(context), android.R.layout.simple_list_item_1,
+                    new String[] {TITLE}, new int[] {android.R.id.text1});
+        }
+
+        static List<Map<String, ?>> getData(Context context) {
+            List<Map<String, ?>> data = new ArrayList<Map<String,?>>();
+
+            Intent mainIntent = new Intent(Intent.ACTION_MAIN);
+            mainIntent.addCategory(CATEGORY_MANUAL_TEST);
+
+            PackageManager packageManager = context.getPackageManager();
+            List<ResolveInfo> list = packageManager.queryIntentActivities(mainIntent, 0);
+            for (int i = 0; i < list.size(); i++) {
+                ResolveInfo info = list.get(i);
+                String title = getTitle(context, info.activityInfo);
+                Intent intent = getActivityIntent(info.activityInfo);
+                addItem(data, title, intent);
+            }
+
+            return data;
+        }
+
+        static String getTitle(Context context, ActivityInfo activityInfo) {
+            if (activityInfo.labelRes != 0) {
+                return context.getString(activityInfo.labelRes);
+            } else {
+                return activityInfo.name;
+            }
+        }
+
+        static Intent getActivityIntent(ActivityInfo activityInfo) {
+            Intent intent = new Intent();
+            intent.setClassName(activityInfo.packageName, activityInfo.name);
+            return intent;
+        }
+
+        @SuppressWarnings("unchecked")
+        static void addItem(List<Map<String, ?>> data, String title, Intent intent) {
+            HashMap item = new HashMap(2);
+            item.put(TITLE, title);
+            item.put(INTENT, intent);
+            data.add(item);
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/suid/SuidBinariesActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/suid/SuidBinariesActivity.java
new file mode 100644
index 0000000..9f973bc
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/suid/SuidBinariesActivity.java
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+package com.android.cts.verifier.suid;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class SuidBinariesActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+    }
+}
diff --git a/apps/CtsVerifier/tests/src/com/android/cts/verifier/CtsVerifierActivityTest.java b/apps/CtsVerifier/tests/src/com/android/cts/verifier/CtsVerifierActivityTest.java
index 8e45f7a..c3e01b2 100644
--- a/apps/CtsVerifier/tests/src/com/android/cts/verifier/CtsVerifierActivityTest.java
+++ b/apps/CtsVerifier/tests/src/com/android/cts/verifier/CtsVerifierActivityTest.java
@@ -17,15 +17,23 @@
 package com.android.cts.verifier;
 
 import android.app.Activity;
+import android.app.Instrumentation;
+import android.app.Instrumentation.ActivityMonitor;
 import android.test.ActivityInstrumentationTestCase2;
+import android.widget.Button;
 import android.widget.TextView;
 
+import java.util.concurrent.TimeUnit;
+
 public class CtsVerifierActivityTest
         extends ActivityInstrumentationTestCase2<CtsVerifierActivity> {
 
     private Activity mActivity;
+    private Instrumentation mInstrumentation;
     private TextView mWelcomeTextView;
+    private Button mContinueButton;
     private String mWelcomeText;
+    private String mContinueText;
 
     public CtsVerifierActivityTest() {
         super(CtsVerifierActivity.class);
@@ -35,16 +43,39 @@
     protected void setUp() throws Exception {
         super.setUp();
         mActivity = getActivity();
+        mInstrumentation = getInstrumentation();
         mWelcomeTextView = (TextView) mActivity.findViewById(R.id.welcome);
         mWelcomeText = mActivity.getString(R.string.welcome_text);
+        mContinueButton = (Button) mActivity.findViewById(R.id.continue_button);
+        mContinueText = mActivity.getString(R.string.continue_button_text);
     }
 
     public void testPreconditions() {
         assertNotNull(mWelcomeTextView);
         assertNotNull(mWelcomeText);
+        assertNotNull(mContinueButton);
     }
 
     public void testWelcome() {
         assertEquals(mWelcomeText, mWelcomeTextView.getText().toString());
+        assertEquals(mContinueText, mContinueButton.getText().toString());
+    }
+
+    /** Check that the continue button leads to the test list successfully. */
+    public void testContinueButton() throws Throwable {
+        ActivityMonitor monitor =
+                new ActivityMonitor(TestListActivity.class.getName(), null, false);
+        mInstrumentation.addMonitor(monitor);
+
+        runTestOnUiThread(new Runnable() {
+            public void run() {
+               assertTrue(mContinueButton.performClick());
+            }
+        });
+
+        Activity activity = mInstrumentation.waitForMonitorWithTimeout(monitor,
+                TimeUnit.SECONDS.toMillis(10));
+        assertNotNull(activity);
+        activity.finish();
     }
 }
diff --git a/apps/CtsVerifier/tests/src/com/android/cts/verifier/TestListActivityTest.java b/apps/CtsVerifier/tests/src/com/android/cts/verifier/TestListActivityTest.java
new file mode 100644
index 0000000..5175f33
--- /dev/null
+++ b/apps/CtsVerifier/tests/src/com/android/cts/verifier/TestListActivityTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+package com.android.cts.verifier;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.app.Instrumentation.ActivityMonitor;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.test.ActivityInstrumentationTestCase2;
+import android.view.KeyEvent;
+
+import java.util.concurrent.TimeUnit;
+
+public class TestListActivityTest
+        extends ActivityInstrumentationTestCase2<TestListActivity> {
+
+    private TestListActivity mActivity;
+    private Instrumentation mInstrumentation;
+
+    public TestListActivityTest() {
+        super(TestListActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mActivity = getActivity();
+        mInstrumentation = getInstrumentation();
+    }
+
+    /** Check that querying the for manual tests somewhat works. */
+    public void testListAdapter() {
+        assertNotNull(mActivity.getListAdapter());
+        assertTrue(mActivity.getListAdapter().getCount() > 0);
+    }
+
+    /** Test that clicking on an item launches a test. */
+    public void testListItem() throws Throwable {
+        IntentFilter filter = new IntentFilter(Intent.ACTION_MAIN);
+        filter.addCategory(TestListActivity.CATEGORY_MANUAL_TEST);
+
+        ActivityMonitor monitor = new ActivityMonitor(filter, null, false);
+        mInstrumentation.addMonitor(monitor);
+
+        sendKeys(KeyEvent.KEYCODE_ENTER);
+
+        Activity activity = mInstrumentation.waitForMonitorWithTimeout(monitor,
+                TimeUnit.SECONDS.toMillis(10));
+        assertNotNull(activity);
+        activity.finish();
+    }
+}
diff --git a/development/ide/eclipse/.classpath b/development/ide/eclipse/.classpath
index f20ab22..e9fb45d 100644
--- a/development/ide/eclipse/.classpath
+++ b/development/ide/eclipse/.classpath
@@ -58,6 +58,8 @@
     <classpathentry kind="src" path="cts/tools/test-progress-new/src"/>
     <classpathentry kind="src" path="cts/tools/utils"/>
     <classpathentry kind="src" path="cts/tools/vm-tests/src"/>
-    <classpathentry kind="src" path="out/target/common/obj/APPS/CtsTestStubs_intermediates/src/src"/>
     <classpathentry kind="src" path="out/target/common/obj/APPS/CtsAccessibilityServiceTestCases_intermediates/src/src"/>
+    <classpathentry kind="src" path="out/target/common/obj/APPS/CtsTestStubs_intermediates/src/src"/>
+    <classpathentry kind="src" path="out/target/common/obj/APPS/CtsVerifier_intermediates/src"/>
+    <classpathentry kind="src" path="out/target/common/obj/APPS/CtsVerifierTests_intermediates/src"/>
 </classpath>