change summary to average/stddev rather than average/worst

- stddev will tell the fluctuation in performance which can be better
  indicator than worst which can be caused by unknown background task
- also add get_csv_report.py which generates csv report under a given dir.

Change-Id: I70333d968beaa159fa3cde15147ec59dd469efe3
diff --git a/suite/pts/deviceTests/dram/src/com/android/pts/dram/BandwidthTest.java b/suite/pts/deviceTests/dram/src/com/android/pts/dram/BandwidthTest.java
index 9c23f22..28ae4a9 100644
--- a/suite/pts/deviceTests/dram/src/com/android/pts/dram/BandwidthTest.java
+++ b/suite/pts/deviceTests/dram/src/com/android/pts/dram/BandwidthTest.java
@@ -52,8 +52,9 @@
         Log.i(TAG, " x " + size.x + " y " + size.y);
         double pixels = size.x * size.y;
         // now this represents how many times the whole screen can be copied in a sec.
-        double screensPerSecMin = stat.mMin / pixels * 1024.0 * 1024.0 / 4.0;
         double screensPerSecAverage = stat.mAverage / pixels * 1024.0 * 1024.0 / 4.0;
-        getReportLog().printSummary("screen copies per sec", screensPerSecMin, screensPerSecAverage);
+        double screensPerSecStddev = stat.mStddev / pixels * 1024.0 * 1024.0 / 4.0;
+        getReportLog().printSummary("screen copies per sec", screensPerSecAverage,
+                screensPerSecStddev);
     }
 }
diff --git a/suite/pts/deviceTests/filesystemperf/src/com/android/pts/filesystemperf/FileUtil.java b/suite/pts/deviceTests/filesystemperf/src/com/android/pts/filesystemperf/FileUtil.java
index 57f868a..2cb3854 100644
--- a/suite/pts/deviceTests/filesystemperf/src/com/android/pts/filesystemperf/FileUtil.java
+++ b/suite/pts/deviceTests/filesystemperf/src/com/android/pts/filesystemperf/FileUtil.java
@@ -276,7 +276,6 @@
         final int runsInOneGo = 16;
         final int readsInOneMeasure = totalReadCount / runsInOneGo;
 
-
         final RandomAccessFile randomFile = new RandomAccessFile(file, "rw");
         double[] rdAmount = new double[runsInOneGo];
         double[] wrAmount = new double[runsInOneGo];
@@ -300,7 +299,7 @@
         report.printArray("Rd amount", rdAmount, true);
         Stat.StatResult stat = Stat.getStat(mbps);
 
-        report.printSummary("MB/s", stat.mMin, stat.mAverage);
+        report.printSummary("MB/s", stat.mAverage, stat.mStddev);
     }
 
     /**
@@ -327,7 +326,6 @@
         final int runsInOneGo = 16;
         final int writesInOneMeasure = totalWriteCount / runsInOneGo; // 32MB at a time
 
-
         final RandomAccessFile randomFile = new RandomAccessFile(file, "rw");
         double[] rdAmount = new double[runsInOneGo];
         double[] wrAmount = new double[runsInOneGo];
@@ -351,7 +349,7 @@
         report.printArray("Wr amount", wrAmount, true);
         Stat.StatResult stat = Stat.getStat(mbps);
 
-        report.printSummary("MB/s", stat.mMin, stat.mAverage);
+        report.printSummary("MB/s", stat.mAverage, stat.mStddev);
     }
 
     /**
@@ -369,12 +367,11 @@
         File file = FileUtil.createNewFilledFile(context,
                 dirName, fileSize);
         final byte[] data = FileUtil.generateRandomData(bufferSize);
-        double[] worsts = new double[numberRepetition];
-        double[] averages = new double[numberRepetition];
+        int numberRepeatInOneRun = (int)(fileSize / bufferSize);
+        double[] mbpsAll = new double[numberRepetition * numberRepeatInOneRun];
         for (int i = 0; i < numberRepetition; i++) {
             final FileOutputStream out = new FileOutputStream(file);
-            int numberRepeat = (int)(fileSize / bufferSize);
-            double[] times = MeasureTime.measure(numberRepeat, new MeasureRun() {
+            double[] times = MeasureTime.measure(numberRepeatInOneRun, new MeasureRun() {
 
                 @Override
                 public void run(int i) throws IOException {
@@ -387,12 +384,9 @@
                     times);
             report.printArray(i + "-th round MB/s",
                     mbps, true);
-            Stat.StatResult stat = Stat.getStat(mbps);
-            worsts[i] = stat.mMin;
-            averages[i] = stat.mAverage;
+            ReportLog.copyArray(mbps, mbpsAll, i * numberRepeatInOneRun);
         }
-        Stat.StatResult statWorsts = Stat.getStat(worsts);
-        Stat.StatResult statAverages = Stat.getStat(averages);
-        report.printSummary("MB/s", statWorsts.mMin, statAverages.mAverage);
+        Stat.StatResult stat = Stat.getStat(mbpsAll);
+        report.printSummary("MB/s", stat.mAverage, stat.mStddev);
     }
 }
diff --git a/suite/pts/deviceTests/filesystemperf/src/com/android/pts/filesystemperf/SequentialRWTest.java b/suite/pts/deviceTests/filesystemperf/src/com/android/pts/filesystemperf/SequentialRWTest.java
index eb66da2..252c59e 100644
--- a/suite/pts/deviceTests/filesystemperf/src/com/android/pts/filesystemperf/SequentialRWTest.java
+++ b/suite/pts/deviceTests/filesystemperf/src/com/android/pts/filesystemperf/SequentialRWTest.java
@@ -65,7 +65,7 @@
                 mbps, true);
         getReportLog().printArray("Wr amount", wrAmount, true);
         Stat.StatResult stat = Stat.getStat(mbps);
-        getReportLog().printSummary("MB/s", stat.mMin, stat.mAverage);
+        getReportLog().printSummary("MB/s", stat.mAverage, stat.mStddev);
     }
 
     @TimeoutReq(minutes = 60)
@@ -106,6 +106,6 @@
         getReportLog().printArray("read MB/s",
                 mbps, true);
         Stat.StatResult stat = Stat.getStat(mbps);
-        getReportLog().printSummary("MB/s", stat.mMin, stat.mAverage);
+        getReportLog().printSummary("MB/s", stat.mAverage, stat.mStddev);
     }
 }
diff --git a/suite/pts/deviceTests/ui/src/com/android/pts/ui/ScrollingTest.java b/suite/pts/deviceTests/ui/src/com/android/pts/ui/ScrollingTest.java
index 7491f8a..8e4bbed 100644
--- a/suite/pts/deviceTests/ui/src/com/android/pts/ui/ScrollingTest.java
+++ b/suite/pts/deviceTests/ui/src/com/android/pts/ui/ScrollingTest.java
@@ -67,6 +67,6 @@
         });
         getReportLog().printArray("ms", results, false);
         Stat.StatResult stat = Stat.getStat(results);
-        getReportLog().printSummary("Time ms", stat.mMax, stat.mAverage);
+        getReportLog().printSummary("Time ms", stat.mAverage, stat.mStddev);
     }
 }
diff --git a/suite/pts/hostTests/bootup/src/com/android/pts/bootup/BootupTimeTest.java b/suite/pts/hostTests/bootup/src/com/android/pts/bootup/BootupTimeTest.java
index 97d9dc8..709588b 100644
--- a/suite/pts/hostTests/bootup/src/com/android/pts/bootup/BootupTimeTest.java
+++ b/suite/pts/hostTests/bootup/src/com/android/pts/bootup/BootupTimeTest.java
@@ -63,7 +63,7 @@
         });
         mReport.printArray("time in ms", result, false);
         StatResult stat = Stat.getStat(result);
-        mReport.printSummary("time in ms", stat.mMax, stat.mAverage);
+        mReport.printSummary("time in ms", stat.mAverage, stat.mStddev);
     }
 
     private void rebootDevice() throws DeviceNotAvailableException {
diff --git a/suite/pts/lib/commonutil/src/com/android/pts/util/ReportLog.java b/suite/pts/lib/commonutil/src/com/android/pts/util/ReportLog.java
index 1fe3c59..0d69c2f 100644
--- a/suite/pts/lib/commonutil/src/com/android/pts/util/ReportLog.java
+++ b/suite/pts/lib/commonutil/src/com/android/pts/util/ReportLog.java
@@ -72,9 +72,9 @@
         printLog(builder.toString());
     }
 
-    public void printSummary(String header, double worst, double average) {
-        mSummary = header + LOG_ELEM_SEPARATOR + "worst " + worst + LOG_ELEM_SEPARATOR +
-                "average " + average;
+    public void printSummary(String header, double average, double stddev) {
+        mSummary = header + LOG_ELEM_SEPARATOR + "average " + average + LOG_ELEM_SEPARATOR +
+                "stddev " + stddev;
     }
 
     public void throwReportToHost() throws PtsException {
@@ -132,6 +132,19 @@
     }
 
     /**
+     * copy array from src to dst with given offset in dst.
+     * dst should be big enough to hold src
+     * @param src
+     * @param dst
+     * @param dstOffset
+     */
+    public static void copyArray(double[] src, double[] dst, int dstOffset) {
+        for (int i = 0; i < src.length; i++) {
+            dst[dstOffset + i] = src[i];
+        }
+    }
+
+    /**
      * get classname.methodname from call stack of the current thread
      *
      * @return
diff --git a/suite/pts/utils/get_csv_report.py b/suite/pts/utils/get_csv_report.py
new file mode 100755
index 0000000..fe3dd74
--- /dev/null
+++ b/suite/pts/utils/get_csv_report.py
@@ -0,0 +1,194 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2012 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.
+#
+import csv
+import os
+import re
+import subprocess
+import sys
+from xml.dom import Node
+from xml.dom import minidom
+
+
+def getChildrenWithTag(parent, tagName):
+    children = []
+    for child in  parent.childNodes:
+        if (child.nodeType == Node.ELEMENT_NODE) and (child.tagName == tagName):
+            #print "parent " + parent.getAttribute("name") + " " + tagName +\
+            #    " " + child.getAttribute("name")
+            children.append(child)
+    return children
+
+class TestCase(object):
+    def __init__(self, name, average, stddev, passFail):
+        self.name = name
+        self.average = average
+        self.stddev = stddev
+        self.passFail = passFail
+
+    def getName(self):
+        return self.name
+
+    def getStddev(self):
+        return self.stddev
+
+    def getAverage(self):
+        return self.average
+
+    def getPassFail(self):
+        return self.passFail
+
+def parseSuite(suite, parentName):
+    if parentName != "":
+        parentName += '.'
+    cases = {}
+    childSuites = getChildrenWithTag(suite, "TestSuite")
+    for child in childSuites:
+        cases.update(parseSuite(child, parentName + child.getAttribute("name")))
+    childTestCases = getChildrenWithTag(suite, "TestCase")
+    for child in childTestCases:
+        className = parentName + child.getAttribute("name")
+        for test in getChildrenWithTag(child, "Test"):
+            methodName = test.getAttribute("name")
+            # do not include this
+            if methodName == "testAndroidTestCaseSetupProperly":
+                continue
+            caseName = className + "#" + methodName
+            passFail = test.getAttribute("result")
+            average = ""
+            stddev = ""
+            failedScene = getChildrenWithTag(test, "FailedScene")
+            if len(failedScene) > 0:
+                message = failedScene[0].getAttribute("message")
+                #print message
+                messages = message.split('|')
+                if len(messages) > 2:
+                    average = messages[1].split()[1]
+                    stddev = messages[2].split()[1]
+            testCase = TestCase(caseName, average, stddev, passFail)
+            cases[caseName] = testCase
+    return cases
+
+
+class Result(object):
+    def __init__(self, reportXml):
+        self.results = {}
+        self.infoKeys = []
+        self.infoValues = []
+        doc = minidom.parse(reportXml)
+        testResult = doc.getElementsByTagName("TestResult")[0]
+        buildInfo = testResult.getElementsByTagName("BuildInfo")[0]
+        buildId = buildInfo.getAttribute("buildID")
+        deviceId = buildInfo.getAttribute("deviceID")
+        deviceName = buildInfo.getAttribute("build_device")
+        boardName = buildInfo.getAttribute("build_board")
+        partitions = buildInfo.getAttribute("partitions")
+        m = re.search(r'.*;/data\s+(\w+)\s+(\w+)\s+(\w+)\s+(\w+);', partitions)
+        dataPartitionSize = m.group(1)
+        self.addKV("device", deviceName)
+        self.addKV("board", boardName)
+        self.addKV("serial", deviceId)
+        self.addKV("build", buildId)
+        self.addKV("data size", dataPartitionSize)
+        packages = getChildrenWithTag(testResult, "TestPackage")
+        for package in packages:
+            casesFromChild = parseSuite(package, "")
+            self.results.update(casesFromChild)
+        #print self.results.keys()
+
+    def addKV(self, key, value):
+        self.infoKeys.append(key)
+        self.infoValues.append(value)
+
+    def getResults(self):
+        return self.results
+
+    def getKeys(self):
+        return self.infoKeys
+
+    def getValues(self):
+        return self.infoValues
+
+def executeWithResult(command):
+    p = subprocess.Popen(command.split(), stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+    out, err = p.communicate()
+    return out
+
+def main(argv):
+    if len(argv) < 3:
+        print "get_csv_report.py pts_report_dir output_file"
+        sys.exit(1)
+    reportPath = os.path.abspath(argv[1])
+    outputCsv = os.path.abspath(argv[2])
+
+    deviceResults = []
+    xmls = executeWithResult("find " + reportPath + " -name testResult.xml -print")
+    print "xml files found :"
+    print xmls
+    for xml in xmls.splitlines():
+        result = Result(xml)
+        deviceResults.append(result)
+    reportInfo = []
+    keys = deviceResults[0].getKeys()
+    noDevices = len(deviceResults)
+    for i in xrange(len(keys)):
+        reportInfo.append([])
+        reportInfo[i].append(keys[i])
+        # for worst/average
+        reportInfo[i].append("")
+        for j in xrange(noDevices):
+            reportInfo[i].append(deviceResults[j].getValues()[i])
+    #print reportInfo
+
+    tests = []
+    for deviceResult in deviceResults:
+        for key in deviceResult.getResults().keys():
+            if not key in tests:
+                tests.append(key)
+    tests.sort()
+    #print tests
+
+    reportTests = []
+    for i in xrange(len(tests)):
+        reportTests.append([])
+        reportTests.append([])
+        reportTests[2 * i].append(tests[i])
+        reportTests[2 * i + 1].append(tests[i])
+        reportTests[2 * i].append("average")
+        reportTests[2 * i + 1].append("stddev")
+        for j in xrange(noDevices):
+            if deviceResults[j].getResults().has_key(tests[i]):
+                result = deviceResults[j].getResults()[tests[i]]
+                if result.getPassFail() == "pass":
+                    reportTests[2 * i].append(result.getAverage())
+                    reportTests[2 * i + 1].append(result.getStddev())
+                else:
+                    reportTests[2 * i].append("fail")
+                    reportTests[2 * i + 1].append("fail")
+            else:
+                reportTests[2 * i].append("")
+                reportTests[2 * i + 1].append("")
+
+    #print reportTests
+
+    with open(outputCsv, 'wb') as f:
+        writer = csv.writer(f)
+        writer.writerows(reportInfo)
+        writer.writerows(reportTests)
+
+
+if __name__ == '__main__':
+    main(sys.argv)