Implementing Jank Test for UI Scrolling.

The UI scrolling tests share a lot with the OpenGL tests so common code
has been pulled up into a new super class. The ScrollingActivity has
also changed to allow the number of elements to be given through the
intent, this should not affect the existing ScrollTest as the default
value is the same as it was before.

Change-Id: I1ca58f535ac81bc0e9032934758dc5a49dbac0fb
diff --git a/suite/pts/PtsBenchmarkingList.mk b/suite/pts/PtsBenchmarkingList.mk
index 4a59c4f..7945f6d 100644
--- a/suite/pts/PtsBenchmarkingList.mk
+++ b/suite/pts/PtsBenchmarkingList.mk
@@ -30,12 +30,13 @@
     PtsDeviceTaskswitchingAppA \
     PtsDeviceTaskswitchingAppB \
     PtsDeviceTaskswitchingControl \
+    PtsDeviceUi \
     PtsDeviceOpenGl
 
 PTS_HOST_CASES := \
     PtsHostBootup \
     PtsHostUi \
-    PtsHostJankOpenGl
+    PtsHostJank
 
 pts_device_lib_tests := \
-    PtsDeviceJankOpenGl
\ No newline at end of file
+    PtsDeviceJank
\ No newline at end of file
diff --git a/suite/pts/deviceTests/ui/src/com/android/pts/ui/ScrollingActivity.java b/suite/pts/deviceTests/ui/src/com/android/pts/ui/ScrollingActivity.java
index fbec65d..c69b4c5 100644
--- a/suite/pts/deviceTests/ui/src/com/android/pts/ui/ScrollingActivity.java
+++ b/suite/pts/deviceTests/ui/src/com/android/pts/ui/ScrollingActivity.java
@@ -31,16 +31,20 @@
  */
 public class ScrollingActivity extends ListActivity implements OnScrollListener {
     static final String TAG = "ScrollingActivity";
-    private static final int NUMBER_ELEMENTS = 10000;
+    private static final String NUM_ELEMENTS_EXTRA = "num_elements";
+    private static final int NUM_ELEMENTS_DEFAULT = 10000;
     private static final int SCROLL_TIME_IN_MS = 1;
     private static final int WAIT_TIMEOUT_IN_SECS = 5 * 60;
-    private String[] mItems = new String[NUMBER_ELEMENTS];
+    private String[] mItems;
     private CountDownLatch mLatchStop = null;
     private int mTargetLoc;
+    private int mNumElements;
 
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
-        for (int i = 0; i < NUMBER_ELEMENTS; i++) {
+        mNumElements = getIntent().getIntExtra(NUM_ELEMENTS_EXTRA, NUM_ELEMENTS_DEFAULT);
+        mItems = new String[mNumElements];
+        for (int i = 0; i < mNumElements; i++) {
             mItems[i] = Integer.toString(i);
         }
         setListAdapter(new ArrayAdapter<String>(this,
@@ -53,7 +57,7 @@
         return doScroll(0);
     }
     public boolean scrollToBottom() {
-        return doScroll(NUMBER_ELEMENTS - 1);
+        return doScroll(mNumElements - 1);
     }
 
     private boolean doScroll(final int loc) {
diff --git a/suite/pts/hostTests/jank/Android.mk b/suite/pts/hostTests/jank/Android.mk
index 1d5346c..c1fd2d6 100644
--- a/suite/pts/hostTests/jank/Android.mk
+++ b/suite/pts/hostTests/jank/Android.mk
@@ -20,20 +20,20 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_MODULE := PtsHostJankOpenGl
+LOCAL_MODULE := PtsHostJank
 
 LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed-prebuilt ddmlib-prebuilt ptscommonutilhost
 
-LOCAL_CTS_TEST_PACKAGE := com.android.pts.jank.opengl
+LOCAL_CTS_TEST_PACKAGE := com.android.pts.jank
 
-LOCAL_DEVICE_JAR_ := PtsDeviceJankOpenGl
+LOCAL_DEVICE_JAR_ := PtsDeviceJank
 cts_library_jar_ := $(CTS_TESTCASES_OUT)/$(LOCAL_DEVICE_JAR_).jar
 
 $(cts_library_jar_): $(call intermediates-dir-for,JAVA_LIBRARIES,$(LOCAL_DEVICE_JAR_))/javalib.jar | $(ACP)
 	$(hide) mkdir -p $(CTS_TESTCASES_OUT)
 	$(hide) $(ACP) -fp $(call intermediates-dir-for,JAVA_LIBRARIES,$(LOCAL_DEVICE_JAR_))/javalib.jar $@
 
-$(CTS_TESTCASES_OUT)/PtsHostJankOpenGl.xml: $(cts_library_jar_)
+$(CTS_TESTCASES_OUT)/PtsHostJank.xml: $(cts_library_jar_)
 
 include $(BUILD_CTS_HOST_JAVA_LIBRARY)
 
diff --git a/suite/pts/hostTests/jank/app/Android.mk b/suite/pts/hostTests/jank/app/Android.mk
index 81f5f73..62b950d7 100644
--- a/suite/pts/hostTests/jank/app/Android.mk
+++ b/suite/pts/hostTests/jank/app/Android.mk
@@ -18,7 +18,7 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE := PtsDeviceJankOpenGl
+LOCAL_MODULE := PtsDeviceJank
 LOCAL_DEX_PREOPT := false
 
 LOCAL_JAVA_LIBRARIES := uiautomator.core
diff --git a/suite/pts/hostTests/jank/app/src/com/android/pts/jank/PtsJankTestBase.java b/suite/pts/hostTests/jank/app/src/com/android/pts/jank/PtsJankTestBase.java
new file mode 100644
index 0000000..cbb6969
--- /dev/null
+++ b/suite/pts/hostTests/jank/app/src/com/android/pts/jank/PtsJankTestBase.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2013 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.pts.jank;
+
+import android.os.Bundle;
+import android.util.Log;
+
+import com.android.uiautomator.platform.JankTestBase;
+import com.android.uiautomator.platform.SurfaceFlingerHelper;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Scanner;
+
+public class PtsJankTestBase extends JankTestBase {
+    private final static String TAG = PtsJankTestBase.class.getName();
+    protected final static String START_CMD = "am start -W -a android.intent.action.MAIN -n %s";
+    protected final static String STOP_CMD = "am force-stop %s";
+    protected final static String INTENT_STRING_EXTRA = " --es %s %s";
+    protected final static String INTENT_BOOLEAN_EXTRA = " --ez %s %b";
+    protected final static String INTENT_INTEGER_EXTRA = " --ei %s %d";
+    protected static long SLEEP_TIME = 2000; // 2 seconds
+    protected static int NUM_ITERATIONS = 5;
+    protected static int TRACE_TIME = 5;
+
+    @Override
+    protected String getPropertyString(Bundle params, String key)
+            throws FileNotFoundException, IOException {
+        if (key.equals("iteration")) {
+            return NUM_ITERATIONS + "";
+        }
+        if (key.equals("tracetime")) {
+            return TRACE_TIME + "";
+        }
+        return super.getPropertyString(params, key);
+    }
+
+    protected void runShellCommand(String command) throws Exception {
+        Process p = null;
+        Scanner out = null;
+        Scanner err = null;
+        try {
+            p = Runtime.getRuntime().exec(command);
+
+            StringBuilder outStr = new StringBuilder();
+            StringBuilder errStr = new StringBuilder();
+            out = new Scanner(p.getInputStream());
+            err = new Scanner(p.getErrorStream());
+            boolean read = true;
+            while (read) {
+                if (out.hasNextLine()) {
+                    outStr.append(out.nextLine());
+                    outStr.append("\n");
+                } else if (err.hasNextLine()) {
+                    errStr.append(err.nextLine());
+                    errStr.append("\n");
+                } else {
+                    read = false;
+                }
+            }
+            Log.i(TAG, command);
+            if (outStr.length() > 0) {
+                Log.i(TAG, outStr.toString());
+            }
+            if (errStr.length() > 0) {
+                Log.e(TAG, errStr.toString());
+            }
+        } finally {
+            if (p != null) {
+                int status = p.waitFor();
+                if (status != 0) {
+                    throw new RuntimeException(
+                            String.format("Run shell command: %s, status: %s", command, status));
+                }
+                p.destroy();
+                p = null;
+            }
+            if (out != null) {
+                out.close();
+            }
+            if (err != null) {
+                err.close();
+            }
+        }
+    }
+}
diff --git a/suite/pts/hostTests/jank/app/src/com/android/pts/jank/opengl/PtsDeviceJankOpenGl.java b/suite/pts/hostTests/jank/app/src/com/android/pts/jank/opengl/PtsDeviceJankOpenGl.java
index 259649c..0a5fea7 100644
--- a/suite/pts/hostTests/jank/app/src/com/android/pts/jank/opengl/PtsDeviceJankOpenGl.java
+++ b/suite/pts/hostTests/jank/app/src/com/android/pts/jank/opengl/PtsDeviceJankOpenGl.java
@@ -14,43 +14,17 @@
 
 package com.android.pts.jank.opengl;
 
-import android.os.Bundle;
 import android.util.Log;
 
-import com.android.uiautomator.platform.JankTestBase;
+import com.android.pts.jank.PtsJankTestBase;
 import com.android.uiautomator.platform.SurfaceFlingerHelper;
 
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.Scanner;
-import java.util.concurrent.Semaphore;
-
-public class PtsDeviceJankOpenGl extends JankTestBase {
-    private final static String TAG = "JankTest";
+public class PtsDeviceJankOpenGl extends PtsJankTestBase {
+    private final static String TAG = PtsDeviceJankOpenGl.class.getName();
     private final static String PACKAGE = "com.android.pts.opengl";
     private final static String COMPONENT =
             PACKAGE + "/" + PACKAGE + ".primitive.GLPrimitiveActivity";
-    private final static String START_CMD = "am start -W -a android.intent.action.MAIN -n %s";
-    private final static String STOP_CMD = "am force-stop %s";
-    private final static String INTENT_STRING_EXTRA = " --es %s %s";
-    private final static String INTENT_BOOLEAN_EXTRA = " --ez %s %b";
-    private final static String INTENT_INTEGER_EXTRA = " --ei %s %d";
     private static String APP_WINDOW_NAME = "SurfaceView";
-    private static long SLEEP_TIME = 2000; // 2 seconds
-    private static int NUM_ITERATIONS = 5;
-    private static int TRACE_TIME = 5;
-
-    @Override
-    protected String getPropertyString(Bundle params, String key)
-            throws FileNotFoundException, IOException {
-        if (key.equals("iteration")) {
-            return NUM_ITERATIONS + "";
-        }
-        if (key.equals("tracetime")) {
-            return TRACE_TIME + "";
-        }
-        return super.getPropertyString(params, key);
-    }
 
     /**
      * Runs the full OpenGL ES 2.0 pipeline test.
@@ -105,6 +79,9 @@
             // Start activity
             runShellCommand(startCommand);
 
+            // Wait for the activity to start
+            sleep(SLEEP_TIME / 2);
+
             // Start systrace
             // TODO(jgennis): Systrace has been commented out because of read-tgid permission error
             // startTrace(mTestCaseName, i);
@@ -132,54 +109,4 @@
         // Stop any remaining instances
         runShellCommand(stopCommand);
     }
-
-    private void runShellCommand(String command) throws Exception {
-        Process p = null;
-        Scanner out = null;
-        Scanner err = null;
-        try {
-            p = Runtime.getRuntime().exec(command);
-
-            StringBuilder outStr = new StringBuilder();
-            StringBuilder errStr = new StringBuilder();
-            out = new Scanner(p.getInputStream());
-            err = new Scanner(p.getErrorStream());
-            boolean read = true;
-            while (read) {
-                if (out.hasNextLine()) {
-                    outStr.append(out.nextLine());
-                    outStr.append("\n");
-                } else if (err.hasNextLine()) {
-                    errStr.append(err.nextLine());
-                    errStr.append("\n");
-                } else {
-                    read = false;
-                }
-            }
-            Log.i(TAG, command);
-            if (outStr.length() > 0) {
-                Log.i(TAG, outStr.toString());
-            }
-            if (errStr.length() > 0) {
-                Log.e(TAG, errStr.toString());
-            }
-        } finally {
-            if (p != null) {
-                int status = p.waitFor();
-                if (status != 0) {
-                    throw new RuntimeException(
-                            String.format("Run shell command: %s, status: %s", command, status));
-                }
-                p.destroy();
-                p = null;
-            }
-            if (out != null) {
-                out.close();
-            }
-            if (err != null) {
-                err.close();
-            }
-        }
-    }
-
 }
diff --git a/suite/pts/hostTests/jank/app/src/com/android/pts/jank/ui/PtsDeviceJankUi.java b/suite/pts/hostTests/jank/app/src/com/android/pts/jank/ui/PtsDeviceJankUi.java
new file mode 100644
index 0000000..ef3d738f
--- /dev/null
+++ b/suite/pts/hostTests/jank/app/src/com/android/pts/jank/ui/PtsDeviceJankUi.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2013 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.pts.jank.ui;
+
+import android.util.Log;
+import android.widget.ListView;
+
+import com.android.pts.jank.PtsJankTestBase;
+import com.android.uiautomator.core.UiScrollable;
+import com.android.uiautomator.core.UiSelector;
+import com.android.uiautomator.platform.SurfaceFlingerHelper;
+
+public class PtsDeviceJankUi extends PtsJankTestBase {
+    private final static String TAG = PtsDeviceJankUi.class.getName();
+    private final static String PACKAGE = "com.android.pts.ui";
+    private final static String COMPONENT =
+            PACKAGE + "/" + PACKAGE + ".ScrollingActivity";
+    private final static int NUM_ELEMENTS = 1000;
+    private static String APP_WINDOW_NAME = COMPONENT;
+
+    // TODO(stuartscott): expand deviceTests/ui app to have a more complex UI, such as fragments.
+
+    /**
+     * Runs the ScrollingActivity and measures jank during a scroll.
+     */
+    public void testScrolling() throws Exception {
+        // Start activity command
+        final StringBuilder sb = new StringBuilder();
+        sb.append(String.format(START_CMD, COMPONENT));
+        sb.append(String.format(INTENT_INTEGER_EXTRA, "num_elements", NUM_ELEMENTS));
+        final String startCommand = sb.toString();
+        final String stopCommand = String.format(STOP_CMD, PACKAGE);
+
+        Log.i(TAG, "Start command: " + startCommand);
+        Log.i(TAG, "Stop command: " + stopCommand);
+
+        setIteration(NUM_ITERATIONS);
+        for (int i = 0; i < NUM_ITERATIONS; i++) {
+            // Stop any existing instances
+            runShellCommand(stopCommand);
+            // Start activity
+            runShellCommand(startCommand);
+
+            // Wait for the activity to start
+            sleep(SLEEP_TIME / 2);
+
+            UiScrollable list = new UiScrollable(
+                    new UiSelector().className(ListView.class.getName()));
+
+            // Start systrace
+            // TODO(jgennis): Systrace has been commented out because of read-tgid permission error
+            // startTrace(mTestCaseName, i);
+
+            // Clear SurfaceFlinger buffer
+            Log.i(TAG, "Clearing SurfaceFlinger buffer");
+            SurfaceFlingerHelper.clearBuffer(APP_WINDOW_NAME);
+
+            list.flingToEnd(2);
+
+            // Dump SurfaceFlinger buffer
+            Log.i(TAG, "Dumping SurfaceFlinger buffer");
+            boolean result = SurfaceFlingerHelper.dumpFrameLatency(APP_WINDOW_NAME, true);
+            assertTrue("SurfaceFlingerHelper could not get timestamps", result);
+
+            // Stop systrace
+            // endTrace();
+
+            // Record results
+            recordResults(mTestCaseName, i);
+        }
+        // Save aggregated results
+        saveResults(mTestCaseName);
+        // Stop any remaining instances
+        runShellCommand(stopCommand);
+    }
+}
diff --git a/suite/pts/hostTests/jank/src/com/android/pts/jank/PtsHostJankTest.java b/suite/pts/hostTests/jank/src/com/android/pts/jank/PtsHostJankTest.java
index 1264786..9c2b481 100644
--- a/suite/pts/hostTests/jank/src/com/android/pts/jank/PtsHostJankTest.java
+++ b/suite/pts/hostTests/jank/src/com/android/pts/jank/PtsHostJankTest.java
@@ -14,7 +14,6 @@
 package com.android.pts.jank;
 
 import com.android.cts.tradefed.build.CtsBuildHelper;
-import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
 import com.android.ddmlib.IShellOutputReceiver;
 import com.android.ddmlib.Log;
 import com.android.ddmlib.Log.LogLevel;
@@ -24,17 +23,10 @@
 import com.android.pts.util.ResultUnit;
 import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.result.CollectingTestListener;
-import com.android.tradefed.result.TestRunResult;
 import com.android.tradefed.testtype.DeviceTestCase;
 import com.android.tradefed.testtype.IBuildReceiver;
 
-import java.io.BufferedReader;
 import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.InputStreamReader;
-import java.io.IOException;
-import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Scanner;
 
diff --git a/suite/pts/hostTests/jank/src/com/android/pts/jank/opengl/PtsHostJankOpenGl.java b/suite/pts/hostTests/jank/src/com/android/pts/jank/opengl/PtsHostJankOpenGl.java
index aea0b11..8d9c3e72 100644
--- a/suite/pts/hostTests/jank/src/com/android/pts/jank/opengl/PtsHostJankOpenGl.java
+++ b/suite/pts/hostTests/jank/src/com/android/pts/jank/opengl/PtsHostJankOpenGl.java
@@ -13,20 +13,18 @@
  */
 package com.android.pts.jank.opengl;
 
-import com.android.ddmlib.Log;
-import com.android.ddmlib.Log.LogLevel;
 import com.android.pts.jank.PtsHostJankTest;
 
 import java.io.File;
 
 public class PtsHostJankOpenGl extends PtsHostJankTest {
 
-    private static final String APK_PACKAGE = "com.android.pts.opengl";
+    private static final String APK_PACKAGE = "com.android.pts";
     private static final String APK = "PtsDeviceOpenGl.apk";
     private static final String PACKAGE = "com.android.pts.jank.opengl";
     private static final String HOST_CLASS = PtsHostJankOpenGl.class.getName();
     private static final String DEVICE_CLASS = PACKAGE + ".PtsDeviceJankOpenGl";
-    private static final String JAR_NAME = "PtsDeviceJankOpenGl.jar";
+    private static final String JAR_NAME = "PtsDeviceJank.jar";
 
     public PtsHostJankOpenGl() {
         super(JAR_NAME, DEVICE_CLASS, HOST_CLASS);
diff --git a/suite/pts/hostTests/jank/src/com/android/pts/jank/ui/PtsHostJankUi.java b/suite/pts/hostTests/jank/src/com/android/pts/jank/ui/PtsHostJankUi.java
new file mode 100644
index 0000000..245a892
--- /dev/null
+++ b/suite/pts/hostTests/jank/src/com/android/pts/jank/ui/PtsHostJankUi.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2013 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.pts.jank.ui;
+
+import com.android.pts.jank.PtsHostJankTest;
+
+import java.io.File;
+
+public class PtsHostJankUi extends PtsHostJankTest {
+
+    private static final String APK_PACKAGE = "com.android.pts";
+    private static final String APK = "PtsDeviceUi.apk";
+    private static final String PACKAGE = "com.android.pts.jank.ui";
+    private static final String HOST_CLASS = PtsHostJankUi.class.getName();
+    private static final String DEVICE_CLASS = PACKAGE + ".PtsDeviceJankUi";
+    private static final String JAR_NAME = "PtsDeviceJank.jar";
+
+    public PtsHostJankUi() {
+        super(JAR_NAME, DEVICE_CLASS, HOST_CLASS);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        // Install the app.
+        mDevice.uninstallPackage(APK_PACKAGE);
+        File app = mBuild.getTestApp(APK);
+        mDevice.installPackage(app, false);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        // Uninstall the app.
+        mDevice.uninstallPackage(APK_PACKAGE);
+        super.tearDown();
+    }
+
+    public void testScrolling() throws Exception {
+        runUiAutomatorTest("testScrolling");
+    }
+}