Adding render stats APIs to UiAutomation (CTS).

bug:12927198

Change-Id: Ic9b3aeed6d75e8ceaeda84ec962cd3caa0ed7bbe
diff --git a/CtsTestCaseList.mk b/CtsTestCaseList.mk
index 0daf064..52456e7 100644
--- a/CtsTestCaseList.mk
+++ b/CtsTestCaseList.mk
@@ -115,6 +115,7 @@
     CtsTextTestCases \
     CtsTextureViewTestCases \
     CtsThemeTestCases \
+    CtsUiAutomationTestCases \
     CtsUtilTestCases \
     CtsViewTestCases \
     CtsWebkitTestCases \
diff --git a/tests/tests/uiautomation/Android.mk b/tests/tests/uiautomation/Android.mk
new file mode 100644
index 0000000..bb0fc19
--- /dev/null
+++ b/tests/tests/uiautomation/Android.mk
@@ -0,0 +1,30 @@
+# Copyright (C) 2014 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)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner ub-uiautomator
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsUiAutomationTestCases
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
+
diff --git a/tests/tests/uiautomation/AndroidManifest.xml b/tests/tests/uiautomation/AndroidManifest.xml
new file mode 100644
index 0000000..06b31c8
--- /dev/null
+++ b/tests/tests/uiautomation/AndroidManifest.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ * Copyright (C) 2014 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="android.app.cts.uiautomation">
+
+  <application android:theme="@android:style/Theme.Holo.NoActionBar" >
+
+      <uses-library android:name="android.test.runner"/>
+
+      <activity
+          android:name="android.app.uiautomation.cts.UiAutomationTestFirstActivity"
+          android:exported="true">
+      </activity>
+
+      <activity
+          android:name="android.app.uiautomation.cts.UiAutomationTestSecondActivity"
+          android:exported="true">
+      </activity>
+
+  </application>
+
+  <instrumentation android:name="android.support.test.uiautomator.UiAutomatorInstrumentationTestRunner"
+                   android:targetPackage="android.app.cts.uiautomation">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
+
+</manifest>
diff --git a/tests/tests/uiautomation/res/layout/ui_automation_test.xml b/tests/tests/uiautomation/res/layout/ui_automation_test.xml
new file mode 100644
index 0000000..fb9621d
--- /dev/null
+++ b/tests/tests/uiautomation/res/layout/ui_automation_test.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2014, 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.
+*/
+-->
+<ListView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/list_view"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent">
+</ListView>
diff --git a/tests/tests/uiautomation/res/values/strings.xml b/tests/tests/uiautomation/res/values/strings.xml
new file mode 100644
index 0000000..7e4e4e4
--- /dev/null
+++ b/tests/tests/uiautomation/res/values/strings.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright (C) 2014 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.
+-->
+
+<resources>
+
+    <string name="uiautomation_test_activity">Cheeses</string>
+
+    <string-array name="some_cheeses">
+        <item>Abbaye de Belloc</item>
+        <item>Abbaye de Belval</item>
+        <item>Abbaye de Citeaux</item>
+        <item>Abbaye du Mont des Cats</item>
+        <item>Abbot’s Gold</item>
+        <item>Acapella</item>
+        <item>Acorn</item>
+        <item>Adelost</item>
+        <item>Affidelice au Chablis</item>
+        <item>Afuega\'l Pitu</item>
+        <item>Aged Gouda</item>
+        <item>Airag</item>
+        <item>Airedale</item>
+        <item>Aisy Cendre</item>
+        <item>Allgauer Emmentaler</item>
+        <item>Babybel</item>
+        <item>Baby Swiss</item>
+        <item>Baguette Laonnaise</item>
+        <item>Bakers</item>
+        <item>Balaton</item>
+        <item>Bandal</item>
+        <item>Banon</item>
+        <item>Barry\'s Bay Cheddar</item>
+        <item>Basing</item>
+        <item>Basket Cheese</item>
+        <item>Bath Cheese</item>
+        <item>Bavarian Bergkase</item>
+        <item>Baylough</item>
+        <item>Beauvoorde</item>
+        <item>Beemster 2% Milk</item>
+    </string-array>
+
+</resources>
diff --git a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java
new file mode 100644
index 0000000..6d80819
--- /dev/null
+++ b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2014 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.app.uiautomation.cts;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.app.Activity;
+import android.app.UiAutomation;
+import android.content.Intent;
+import android.view.FrameStats;
+import android.view.WindowAnimationFrameStats;
+import android.view.WindowContentFrameStats;
+import android.view.accessibility.AccessibilityWindowInfo;
+import android.support.test.uiautomator.UiScrollable;
+import android.support.test.uiautomator.UiSelector;
+import android.support.test.uiautomator.UiAutomatorTestCase;
+
+import java.util.List;
+
+/**
+ * Tests for the UiAutomation APIs.
+ */
+public class UiAutomationTest extends UiAutomatorTestCase {
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        AccessibilityServiceInfo info = getInstrumentation().getUiAutomation().getServiceInfo();
+        info.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
+        getInstrumentation().getUiAutomation().setServiceInfo(info);
+    }
+
+    public void testWindowContentFrameStats() throws Exception {
+        Activity activity = null;
+        try {
+            UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+
+            // Start an activity.
+            Intent intent = new Intent(getInstrumentation().getContext(),
+                    UiAutomationTestFirstActivity.class);
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            activity = getInstrumentation().startActivitySync(intent);
+
+            // Wait for things to settle.
+            getUiDevice().waitForIdle();
+
+            // Find the application window.
+            final int windowId = findAppWindowId(uiAutomation.getWindows());
+            assertTrue(windowId >= 0);
+
+            // Clear stats to be with a clean slate.
+            assertTrue(uiAutomation.clearWindowContentFrameStats(windowId));
+
+            // Find the list to scroll around.
+            UiScrollable listView = new UiScrollable(new UiSelector().resourceId(
+                    "android.app.cts.uiautomation:id/list_view"));
+
+            // Scoll a bit.
+            listView.scrollToEnd(Integer.MAX_VALUE);
+            listView.scrollToBeginning(Integer.MAX_VALUE);
+
+            // Get the frame stats.
+            WindowContentFrameStats stats = uiAutomation.getWindowContentFrameStats(windowId);
+
+            // Check the frame stats...
+
+            // We should have somethong.
+            assertNotNull(stats);
+
+            // The refresh presiod is always positive.
+            assertTrue(stats.getRefreshPeriodNano() > 0);
+
+            // There is some frame data.
+            final int frameCount = stats.getFrameCount();
+            assertTrue(frameCount > 0);
+
+            // The frames are ordered in ascending order.
+            assertWindowContentTimestampsInAscendingOrder(stats);
+
+            // The start and end times are based on first and last frame.
+            assertEquals(stats.getStartTimeNano(), stats.getFramePresentedTimeNano(0));
+            assertEquals(stats.getEndTimeNano(), stats.getFramePresentedTimeNano(frameCount - 1));
+        } finally {
+            // Clean up.
+            if (activity != null) {
+                activity.finish();
+            }
+        }
+    }
+
+    public void testWindowContentFrameStatsNoAnimation() throws Exception {
+        Activity activity = null;
+        try {
+            UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+
+            // Start an activity.
+            Intent intent = new Intent(getInstrumentation().getContext(),
+                    UiAutomationTestFirstActivity.class);
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            activity = getInstrumentation().startActivitySync(intent);
+
+            // Wait for things to settle.
+            getUiDevice().waitForIdle();
+
+            // Find the application window.
+            final int windowId = findAppWindowId(uiAutomation.getWindows());
+            assertTrue(windowId >= 0);
+
+            // Clear stats to be with a clean slate.
+            assertTrue(uiAutomation.clearWindowContentFrameStats(windowId));
+
+            // Get the frame stats.
+            WindowContentFrameStats stats = uiAutomation.getWindowContentFrameStats(windowId);
+
+            // Check the frame stats...
+
+            // We should have somethong.
+            assertNotNull(stats);
+
+            // The refresh presiod is always positive.
+            assertTrue(stats.getRefreshPeriodNano() > 0);
+
+            // There is no data.
+            assertTrue(stats.getFrameCount() == 0);
+
+            // The start and end times are undefibed as we have no data.
+            assertEquals(stats.getStartTimeNano(), FrameStats.UNDEFINED_TIME_NANO);
+            assertEquals(stats.getEndTimeNano(), FrameStats.UNDEFINED_TIME_NANO);
+        } finally {
+            // Clean up.
+            if (activity != null) {
+                activity.finish();
+            }
+        }
+    }
+
+    public void testWindowAnimationFrameStats() throws Exception {
+        Activity firstActivity = null;
+        Activity secondActivity = null;
+        try {
+            UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+
+            // Start the frist activity.
+            Intent firstIntent = new Intent(getInstrumentation().getContext(),
+                    UiAutomationTestFirstActivity.class);
+            firstIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            firstActivity = getInstrumentation().startActivitySync(firstIntent);
+
+            // Wait for things to settle.
+            getUiDevice().waitForIdle();
+
+            // Clear the window animation stats to be with a clean slate.
+            uiAutomation.clearWindowAnimationFrameStats();
+
+            // Start the second activity
+            Intent secondIntent = new Intent(getInstrumentation().getContext(),
+                    UiAutomationTestSecondActivity.class);
+            secondIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            secondActivity = getInstrumentation().startActivitySync(secondIntent);
+
+            // Wait for things to settle.
+            getUiDevice().waitForIdle();
+
+            // Get the frame stats.
+            WindowAnimationFrameStats stats = uiAutomation.getWindowAnimationFrameStats();
+
+            // Check the frame stats...
+
+            // We should have somethong.
+            assertNotNull(stats);
+
+            // The refresh presiod is always positive.
+            assertTrue(stats.getRefreshPeriodNano() > 0);
+
+            // There is some frame data.
+            final int frameCount = stats.getFrameCount();
+            assertTrue(frameCount > 0);
+
+            // The frames are ordered in ascending order.
+            assertWindowAnimationTimestampsInAscendingOrder(stats);
+
+            // The start and end times are based on first and last frame.
+            assertEquals(stats.getStartTimeNano(), stats.getFramePresentedTimeNano(0));
+            assertEquals(stats.getEndTimeNano(), stats.getFramePresentedTimeNano(frameCount - 1));
+        } finally {
+            // Clean up.
+            if (firstActivity != null) {
+                firstActivity.finish();
+            }
+            if (secondActivity != null) {
+                secondActivity.finish();
+            }
+        }
+    }
+
+    public void testWindowAnimationFrameStatsNoAnimation() throws Exception {
+        UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+
+        // Wait for things to settle.
+        getUiDevice().waitForIdle();
+
+        // Clear the window animation stats to be with a clean slate.
+        uiAutomation.clearWindowAnimationFrameStats();
+
+        // Get the frame stats.
+        WindowAnimationFrameStats stats = uiAutomation.getWindowAnimationFrameStats();
+
+        // Check the frame stats...
+
+        // We should have somethong.
+        assertNotNull(stats);
+
+        // The refresh presiod is always positive.
+        assertTrue(stats.getRefreshPeriodNano() > 0);
+
+        // There is no data.
+        assertTrue(stats.getFrameCount() == 0);
+
+        // The start and end times are undefibed as we have no data.
+        assertEquals(stats.getStartTimeNano(), FrameStats.UNDEFINED_TIME_NANO);
+        assertEquals(stats.getEndTimeNano(), FrameStats.UNDEFINED_TIME_NANO);
+    }
+
+    private void assertWindowContentTimestampsInAscendingOrder(WindowContentFrameStats stats) {
+        long lastExpectedTimeNano = 0;
+        long lastPresentedTimeNano = 0;
+        long lastPreparedTimeNano = 0;
+
+        final int frameCount = stats.getFrameCount();
+        for (int i = 0; i < frameCount; i++) {
+            final long expectedTimeNano = stats.getFramePostedTimeNano(i);
+            assertTrue(expectedTimeNano > lastExpectedTimeNano);
+            lastExpectedTimeNano = expectedTimeNano;
+
+            final long presentedTimeNano = stats.getFramePresentedTimeNano(i);
+            if (lastPresentedTimeNano == FrameStats.UNDEFINED_TIME_NANO) {
+                assertTrue(presentedTimeNano == FrameStats.UNDEFINED_TIME_NANO);
+            } else if (presentedTimeNano != FrameStats.UNDEFINED_TIME_NANO) {
+                assertTrue(presentedTimeNano > lastPresentedTimeNano);
+            }
+            lastPresentedTimeNano = presentedTimeNano;
+
+            final long preparedTimeNano = stats.getFrameReadyTimeNano(i);
+            if (lastPreparedTimeNano == FrameStats.UNDEFINED_TIME_NANO) {
+                assertTrue(preparedTimeNano == FrameStats.UNDEFINED_TIME_NANO);
+            } else if (preparedTimeNano != FrameStats.UNDEFINED_TIME_NANO) {
+                assertTrue(preparedTimeNano > lastPreparedTimeNano);
+            }
+            lastPreparedTimeNano = preparedTimeNano;
+        }
+    }
+
+    private void assertWindowAnimationTimestampsInAscendingOrder(WindowAnimationFrameStats stats) {
+        long lastPresentedTimeNano = 0;
+
+        final int frameCount = stats.getFrameCount();
+        for (int i = 0; i < frameCount; i++) {
+            final long presentedTimeNano = stats.getFramePresentedTimeNano(i);
+            if (lastPresentedTimeNano == FrameStats.UNDEFINED_TIME_NANO) {
+                assertTrue(presentedTimeNano == FrameStats.UNDEFINED_TIME_NANO);
+            } else if (presentedTimeNano != FrameStats.UNDEFINED_TIME_NANO) {
+                assertTrue(presentedTimeNano > lastPresentedTimeNano);
+            }
+            lastPresentedTimeNano = presentedTimeNano;
+        }
+    }
+
+    private int findAppWindowId(List<AccessibilityWindowInfo> windows) {
+        final int windowCount = windows.size();
+        for (int i = 0; i < windowCount; i++) {
+            AccessibilityWindowInfo window = windows.get(i);
+            if (window.getType() == AccessibilityWindowInfo.TYPE_APPLICATION) {
+                return window.getId();
+            }
+        }
+        return -1;
+    }
+}
diff --git a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTestFirstActivity.java b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTestFirstActivity.java
new file mode 100644
index 0000000..49791ab
--- /dev/null
+++ b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTestFirstActivity.java
@@ -0,0 +1,42 @@
+/*
+* Copyright (C) 2014 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.app.uiautomation.cts;
+
+import android.app.cts.uiautomation.R;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+/**
+* Activity for testing the UiAutomatoin APIs.
+*/
+public class UiAutomationTestFirstActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.ui_automation_test);
+
+        String[] cheeses = getResources().getStringArray(R.array.some_cheeses);
+        ArrayAdapter<String> cheeseAdapter = new ArrayAdapter<String>(this,
+                android.R.layout.simple_list_item_1, cheeses);
+
+        ListView listView = (ListView) findViewById(R.id.list_view);
+        listView.setAdapter(cheeseAdapter);
+    }
+}
diff --git a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTestSecondActivity.java b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTestSecondActivity.java
new file mode 100644
index 0000000..7def379
--- /dev/null
+++ b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTestSecondActivity.java
@@ -0,0 +1,41 @@
+/*
+* Copyright (C) 2014 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.app.uiautomation.cts;
+
+import android.app.Activity;
+import android.app.cts.uiautomation.R;
+import android.os.Bundle;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+/**
+* Activity for testing the UiAutomatoin APIs.
+*/
+public class UiAutomationTestSecondActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.ui_automation_test);
+
+        String[] cheeses = getResources().getStringArray(R.array.some_cheeses);
+        ArrayAdapter<String> cheeseAdapter = new ArrayAdapter<String>(this,
+                android.R.layout.simple_list_item_1, cheeses);
+
+        ListView listView = (ListView) findViewById(R.id.list_view);
+        listView.setAdapter(cheeseAdapter);
+    }
+}