1#!/usr/bin/env python
2#
3# Copyright (C) 2013 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the 'License');
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#    http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an 'AS IS' BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17import os
18import re
19import subprocess
20import sys
21from xml.dom import Node
22from xml.dom import minidom
23
24def getChildrenWithTag(parent, tagName):
25  children = []
26  for child in  parent.childNodes:
27    if (child.nodeType == Node.ELEMENT_NODE) and (child.tagName == tagName):
28      #print "parent " + parent.getAttribute("name") + " " + tagName +\
29      #  " " + child.getAttribute("name")
30      children.append(child)
31  return children
32
33def getText(tag):
34  return str(tag.firstChild.nodeValue)
35
36class TestCase(object):
37  def __init__(self, name, summary, details, result):
38    self.name = name
39    self.summary = summary
40    self.details = details
41    self.result = result
42
43  def getName(self):
44    return self.name
45
46  def getSummary(self):
47    return self.summary
48
49  def getDetails(self):
50    return self.details
51
52  def getResult(self):
53    return self.result
54
55def parseSuite(suite, parentName):
56  if parentName != "":
57    parentName += '.'
58  cases = {}
59  childSuites = getChildrenWithTag(suite, "TestSuite")
60  for child in childSuites:
61    cases.update(parseSuite(child, parentName + child.getAttribute("name")))
62  childTestCases = getChildrenWithTag(suite, "TestCase")
63  for child in childTestCases:
64    className = parentName + child.getAttribute("name")
65    for test in getChildrenWithTag(child, "Test"):
66      methodName = test.getAttribute("name")
67      # do not include this
68      if methodName == "testAndroidTestCaseSetupProperly":
69        continue
70      caseName = str(className + "#" + methodName)
71      result = str(test.getAttribute("result"))
72      summary = {}
73      details = {}
74      if result == "pass":
75        sts = getChildrenWithTag(test, "Summary")
76        dts = getChildrenWithTag(test, "Details")
77        if len(sts) == len(dts) == 1:
78          summary[sts[0].getAttribute("message")] = getText(sts[0])
79          for d in getChildrenWithTag(dts[0], "ValueArray"):
80            values = []
81            for c in getChildrenWithTag(d, "Value"):
82              values.append(getText(c))
83            details[d.getAttribute("message")] = values
84        else:
85          result = "no results"
86      testCase = TestCase(caseName, summary, details, result)
87      cases[caseName] = testCase
88  return cases
89
90
91class Result(object):
92  def __init__(self, reportXml):
93    self.results = {}
94    self.infoKeys = []
95    self.infoValues = []
96    doc = minidom.parse(reportXml)
97    testResult = doc.getElementsByTagName("TestResult")[0]
98    buildInfos = testResult.getElementsByTagName("BuildInfo")
99    if buildInfos != None and len(buildInfos) > 0:
100      buildInfo = buildInfos[0]
101      buildId = buildInfo.getAttribute("buildID")
102      deviceId = buildInfo.getAttribute("deviceID")
103      deviceName = buildInfo.getAttribute("build_device")
104      boardName = buildInfo.getAttribute("build_board")
105      partitions = buildInfo.getAttribute("partitions")
106      m = re.search(r'.*;/data\s+([\w\.]+)\s+([\w\.]+)\s+([\w\.]+)\s+([\w\.]+);', partitions)
107      dataPartitionSize = m.group(1)
108      self.addKV("device", deviceName)
109      self.addKV("board", boardName)
110      self.addKV("serial", deviceId)
111      self.addKV("build", buildId)
112      self.addKV("data size", dataPartitionSize)
113    packages = getChildrenWithTag(testResult, "TestPackage")
114    for package in packages:
115      casesFromChild = parseSuite(package, "")
116      self.results.update(casesFromChild)
117    #print self.results.keys()
118
119  def addKV(self, key, value):
120    self.infoKeys.append(key)
121    self.infoValues.append(value)
122
123  def getResults(self):
124    return self.results
125
126  def getKeys(self):
127    return self.infoKeys
128
129  def getValues(self):
130    return self.infoValues
131
132  def getDeviceName(self):
133    return self.getInfoV("device")
134
135  def getInfoV(self, key):
136    if key in self.infoKeys:
137      return self.infoValues[self.infoKeys.index(key)]
138    else:
139      return "unknown"
140
141def executeWithResult(command):
142  p = subprocess.Popen(command.split(), stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
143  out, err = p.communicate()
144  return out
145
146def parseReports(path):
147  deviceResults = []
148  xmls = executeWithResult("find " + path + " -name testResult.xml -print")
149  print "xml files found :"
150  print xmls
151  for xml in xmls.splitlines():
152    result = Result(xml)
153    deviceResults.append(result)
154  reportInfo = {}
155  keys = ["device", "board", "serial", "build", "data size"]
156  numDevices = len(deviceResults)
157  for i in xrange(len(keys)):
158    values = []
159    for j in xrange(numDevices):
160      values.append(str(deviceResults[j].getInfoV(keys[i])))
161    reportInfo[keys[i]] = values
162  #print reportInfo
163
164  tests = []
165  for deviceResult in deviceResults:
166    for key in deviceResult.getResults().keys():
167      if not key in tests:
168        tests.append(key)
169  #print tests
170
171  reportTests = {}
172  for i in xrange(len(tests)):
173    test = tests[i]
174    reportTests[test] = []
175    for j in xrange(numDevices):
176      values = {}
177      if deviceResults[j].getResults().has_key(test):
178        result = deviceResults[j].getResults()[test]
179        values["result"] = result.getResult()
180        values["summary"] = result.getSummary()
181        values["details"] = result.getDetails()
182        values["device"] = deviceResults[j].getDeviceName()
183      # even if report does not have test, put empty dict
184      # otherwise, there is no way to distinguish results from the same device
185      reportTests[test].append(values)
186  #print reportTests
187  return (reportInfo, reportTests)
188
189def main(argv):
190  if len(argv) < 3:
191    print "get_csv_report.py cts_report_dir output_file"
192    sys.exit(1)
193  reportPath = os.path.abspath(argv[1])
194  outputCsv = os.path.abspath(argv[2])
195
196  (reportInfo, reportTests) = parseReports(reportPath)
197
198  with open(outputCsv, 'w') as f:
199    for key in reportInfo:
200      f.write(key)
201      for value in reportInfo[key]:
202        f.write(',')
203        f.write(value)
204      f.write('\n')
205    sortedTest = sorted(reportTests)
206    for test in sortedTest:
207      f.write(test)
208      for report in reportTests[test]:
209        f.write(',')
210        if 'summary' in report:
211          summaryValues = report['summary'].values()
212          if len(summaryValues) > 0:
213            f.write(summaryValues[0])
214        # else: no data printed but just empty cell
215      # close a test with line
216      f.write('\n')
217
218if __name__ == '__main__':
219  main(sys.argv)
220