Add Flatland benchmark runner

Change-Id: Ib5baed34db4d11e0e2deae2406bb26567e6ea340
diff --git a/prod-tests/src/com/android/graphics/tests/FlatlandTest.java b/prod-tests/src/com/android/graphics/tests/FlatlandTest.java
new file mode 100644
index 0000000..1d47487
--- /dev/null
+++ b/prod-tests/src/com/android/graphics/tests/FlatlandTest.java
@@ -0,0 +1,165 @@
+/*
+ * 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 com.android.graphics.tests;
+
+import com.android.tradefed.config.Option;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.result.ITestInvocationListener;
+import com.android.tradefed.testtype.IDeviceTest;
+import com.android.tradefed.testtype.IRemoteTest;
+import com.android.tradefed.util.RunUtil;
+
+import junit.framework.Assert;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Test Runner for graphics Flatland Benchmark test.
+ * <p>
+ * Flatland test is a benchmark for measuring GPU performance in various 2D UI rendering and
+ * window composition scenarios.
+ * <p>
+ * Since it is measuring the hardware performance, the test should be executed in
+ * as consistent and static an environment as possible.
+ * <ul>
+ * <li>The display should be turned off and background service should be stopped before
+ * running the benchmark. Running 'adb shell stop' is probably sufficient for this,
+ * but if there are device specific background services that consume
+ * much CPU cycles, memory bandwidth, or might otherwise interfere with GPU rendering,
+ * those should be stopped as well
+ * <li>All relevant hardware clocks should be locked at particular frequency when running the test.
+ * </ul>
+ * <p>
+ * If running the benchmark with clocks locked causes thermal throttling, set option "--sleep-time"
+ * to 10 to 50 (ms) to insert sleep between each benchmark sample run.
+ * <p>
+ * Output interpretation:
+ * For each test case, the expected time in milliseconds that a single frame of the scenario
+ * takes to complete will be printed out. Four types of values could displayed:
+ * <ul>
+ * <li>fast - frames of the scenarios are completed too fast to be reliably benchmarked. This
+ * corresponds to frame time less than 3 ms. The scenario was skipped. "0" will be posted into
+ * the dashboard.
+ * <li>slow - frame time is too long, normally orver 50 ms. The scenario was skipped. "1000" will
+ * be posted into the dashboard.
+ * <li>varies - frame time was not stable. rerun the test to get a stable results. If that results
+ * show repeatedly, something is wrong with the environment, signal to file a bug.
+ * <li>decimal number - frame time for the scenarios are measured.
+ * </ul>
+ */
+public class FlatlandTest implements IDeviceTest, IRemoteTest {
+    private static final String COMMAND = "flatland";
+    private static final String FIRST_LINE = "cmdline:";
+    private static final String TITLE = "Scenario";
+    private static final long START_TIMER = 2 * 60 * 1000; // 2 minutes
+    private static final String RESULT_FAST = "fast";
+    private static final String RESULT_SLOW = "slow";
+    private static final String RESULT_VARIES = "varies";
+
+    private ITestDevice mTestDevice = null;
+    // HashMap to store results for
+    public Map<String, String> mResultMap = new HashMap<String, String>();
+
+    @Option(name = "ru-key", description = "Reporting unit key to use when posting results")
+    private String mRuKey = "flatland";
+
+    @Option(name = "run-path",
+            description = "path for the binary")
+    private String mRunPath = "/data/local/tmp/";
+
+    @Option(name = "sleep-time",
+            description = "sleep for N ms between samples, set to 10 - 50 ms if the locked CPU"
+                    + " frequency causes thermal throttle.")
+    private int mSleepTime = 50;
+
+    @Option(name = "schema-map",
+            description = "map a test case name to a schema key")
+    private Map<String, String> mSchemaMap = new HashMap<String, String>();
+
+    @Override
+    public void setDevice(ITestDevice testDevice) {
+        mTestDevice = testDevice;
+    }
+
+    @Override
+    public ITestDevice getDevice() {
+        return mTestDevice;
+    }
+
+    @Override
+    public void run(ITestInvocationListener standardListener) throws DeviceNotAvailableException {
+        Assert.assertNotNull(mRunPath);
+        RunUtil.getDefault().sleep(START_TIMER);
+
+        // execute test
+        StringBuilder cmd = new StringBuilder();
+        cmd.append(mRunPath);
+        cmd.append(COMMAND);
+        if (mSleepTime > 0) {
+            cmd.append(" -s ");
+            cmd.append(mSleepTime);
+        }
+        standardListener.testRunStarted(mRuKey, 1);
+        long start = System.currentTimeMillis();
+        String result = mTestDevice.executeShellCommand(cmd.toString());
+        if (result == null) {
+            CLog.v("no test results returned. Test failed?");
+            return;
+        }
+        // parse results and report metrics
+        parseResult(result);
+        standardListener.testRunEnded((System.currentTimeMillis() - start), mResultMap);
+    }
+
+    /**
+     * Parse results returned from running the benchmark
+     */
+    public void parseResult(String result) {
+        String[] lines = result.split(System.getProperty("line.separator"));
+        if (lines.length <= 0) {
+            return;
+        }
+        for (int i = 0; i < lines.length; i++) {
+            if (!lines[i].contains(FIRST_LINE) && !(lines[i].contains(TITLE))) {
+                // skip the first two lines
+                String[] items = lines[i].trim().split("\\|");
+                if (items.length == 3) {
+                    String schemaKey = String.format("%s %s", items[0].trim(), items[1].trim());
+                    if (mSchemaMap.get(schemaKey) != null) {
+                        // get the mapped schema key if there is any
+                        schemaKey = mSchemaMap.get(schemaKey);
+                    }
+                    String renderTime = items[2].trim();
+                    if (renderTime != null) {
+                        if (renderTime.equals(RESULT_FAST)) {
+                            mResultMap.put(schemaKey, "0");
+                        } else if (renderTime.equals(RESULT_SLOW)) {
+                            mResultMap.put(schemaKey, "1000");
+                        } else if (renderTime.equals(RESULT_VARIES)){
+                            mResultMap.put(schemaKey, "-1");
+                        } else {
+                            mResultMap.put(schemaKey, renderTime);
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/prod-tests/src/com/android/graphics/tests/FlatlandTestFuncTest.java b/prod-tests/src/com/android/graphics/tests/FlatlandTestFuncTest.java
new file mode 100644
index 0000000..da837a2
--- /dev/null
+++ b/prod-tests/src/com/android/graphics/tests/FlatlandTestFuncTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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 com.android.graphics.tests;
+
+import com.android.tradefed.log.LogUtil.CLog;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import java.util.Map;
+
+public class FlatlandTestFuncTest extends TestCase {
+
+    private final String output = " cmdline: /data/local/tmp/flatland\n" +
+            "               Scenario               | Resolution  | Time (ms)\n" +
+            " 16:10 Single Static Window           | 1280 x  800 |   fast\n" +
+            " 16:10 Single Static Window           | 1920 x 1200 |  3.136\n" +
+            " 16:10 Single Static Window           | 2560 x 1600 |  5.524\n" +
+            " 16:10 Single Static Window           | 3840 x 2400 | 11.841\n" +
+            " 4:3 Single Static Window             | 2048 x 1536 |  4.292\n" +
+            " 16:10 App -> Home Transition         | 1280 x  800 |  varies\n" +
+            " 16:10 App -> Home Transition         | 1920 x 1200 |  5.724\n" +
+            " 16:10 App -> Home Transition         | 2560 x 1600 | 10.033\n" +
+            " 16:10 App -> Home Transition         | 3840 x 2400 | 22.034\n" +
+            " 4:3 App -> Home Transition           | 2048 x 1536 |  8.003\n" +
+            " 16:10 SurfaceView -> Home Transition | 1280 x  800 |   slow\n" +
+            " 16:10 SurfaceView -> Home Transition | 1920 x 1200 |  7.023\n" +
+            " 16:10 SurfaceView -> Home Transition | 2560 x 1600 | 12.337\n" +
+            " 16:10 SurfaceView -> Home Transition | 3840 x 2400 | 27.283\n" +
+            " 4:3 SurfaceView -> Home Transition   | 2048 x 1536 |  9.918\n";
+
+    public void testPraseResults() throws Exception {
+        FlatlandTest ft = new FlatlandTest();
+        ft.parseResult(output);
+        // "0" represents a "fast" result
+        String t = ft.mResultMap.get("16:10 Single Static Window 1280 x  800");
+        Assert.assertTrue(t.equals("0"));
+
+        t = ft.mResultMap.get("16:10 Single Static Window 1920 x 1200");
+        Assert.assertTrue(t.equals("3.136"));
+
+        t = ft.mResultMap.get("16:10 App -> Home Transition 1280 x  800");
+        Assert.assertTrue(t.equals("-1"));
+
+        t = ft.mResultMap.get("16:10 SurfaceView -> Home Transition 1280 x  800");
+        Assert.assertTrue(t.equals("1000"));
+
+        for(Map.Entry<String, String> entry: ft.mResultMap.entrySet()) {
+            CLog.e("key:%s, value:%s", entry.getKey(), entry.getValue());
+        }
+    }
+}