1#!/usr/bin/env python
2
3import collections
4import re
5import os.path
6import sys
7from xml.dom.minidom import parse
8
9class TestInfo(object):
10
11    def __init__(self, xmlnode):
12        self.fixture = xmlnode.getAttribute("classname")
13        self.name = xmlnode.getAttribute("name")
14        self.value_param = xmlnode.getAttribute("value_param")
15        self.type_param = xmlnode.getAttribute("type_param")
16
17        custom_status = xmlnode.getAttribute("custom_status")
18        failures = xmlnode.getElementsByTagName("failure")
19
20        if len(custom_status) > 0:
21            self.status = custom_status
22        elif len(failures) > 0:
23            self.status = "failed"
24        else:
25            self.status = xmlnode.getAttribute("status")
26
27        if self.name.startswith("DISABLED_"):
28            self.status = "disabled"
29            self.fixture = self.fixture.replace("DISABLED_", "")
30            self.name = self.name.replace("DISABLED_", "")
31        self.metrix = {}
32        self.parseLongMetric(xmlnode, "bytesIn");
33        self.parseLongMetric(xmlnode, "bytesOut");
34        self.parseIntMetric(xmlnode, "samples");
35        self.parseIntMetric(xmlnode, "outliers");
36        self.parseFloatMetric(xmlnode, "frequency", 1);
37        self.parseLongMetric(xmlnode, "min");
38        self.parseLongMetric(xmlnode, "median");
39        self.parseLongMetric(xmlnode, "gmean");
40        self.parseLongMetric(xmlnode, "mean");
41        self.parseLongMetric(xmlnode, "stddev");
42        self.parseFloatMetric(xmlnode, "gstddev");
43        self.parseFloatMetric(xmlnode, "time");
44
45    def parseLongMetric(self, xmlnode, name, default = 0):
46        if xmlnode.hasAttribute(name):
47            tmp = xmlnode.getAttribute(name)
48            val = long(tmp)
49            self.metrix[name] = val
50        else:
51            self.metrix[name] = default
52
53    def parseIntMetric(self, xmlnode, name, default = 0):
54        if xmlnode.hasAttribute(name):
55            tmp = xmlnode.getAttribute(name)
56            val = int(tmp)
57            self.metrix[name] = val
58        else:
59            self.metrix[name] = default
60
61    def parseFloatMetric(self, xmlnode, name, default = 0):
62        if xmlnode.hasAttribute(name):
63            tmp = xmlnode.getAttribute(name)
64            val = float(tmp)
65            self.metrix[name] = val
66        else:
67            self.metrix[name] = default
68
69    def parseStringMetric(self, xmlnode, name, default = None):
70        if xmlnode.hasAttribute(name):
71            tmp = xmlnode.getAttribute(name)
72            self.metrix[name] = tmp.strip()
73        else:
74            self.metrix[name] = default
75
76    def get(self, name, units="ms"):
77        if name == "classname":
78            return self.fixture
79        if name == "name":
80            return self.name
81        if name == "fullname":
82            return self.__str__()
83        if name == "value_param":
84            return self.value_param
85        if name == "type_param":
86            return self.type_param
87        if name == "status":
88            return self.status
89        val = self.metrix.get(name, None)
90        if not val:
91            return val
92        if name == "time":
93            return self.metrix.get("time")
94        if name in ["gmean", "min", "mean", "median", "stddev"]:
95            scale = 1.0
96            frequency = self.metrix.get("frequency", 1.0) or 1.0
97            if units == "ms":
98                scale = 1000.0
99            if units == "mks":
100                scale = 1000000.0
101            if units == "ns":
102                scale = 1000000000.0
103            if units == "ticks":
104                frequency = long(1)
105                scale = long(1)
106            return val * scale / frequency
107        return val
108
109
110    def dump(self, units="ms"):
111        print "%s ->\t\033[1;31m%s\033[0m = \t%.2f%s" % (str(self), self.status, self.get("gmean", units), units)
112
113
114    def getName(self):
115        pos = self.name.find("/")
116        if pos > 0:
117            return self.name[:pos]
118        return self.name
119
120
121    def getFixture(self):
122        if self.fixture.endswith(self.getName()):
123            fixture = self.fixture[:-len(self.getName())]
124        else:
125            fixture = self.fixture
126        if fixture.endswith("_"):
127            fixture = fixture[:-1]
128        return fixture
129
130
131    def param(self):
132        return '::'.join(filter(None, [self.type_param, self.value_param]))
133
134    def shortName(self):
135        name = self.getName()
136        fixture = self.getFixture()
137        return '::'.join(filter(None, [name, fixture]))
138
139
140    def __str__(self):
141        name = self.getName()
142        fixture = self.getFixture()
143        return '::'.join(filter(None, [name, fixture, self.type_param, self.value_param]))
144
145
146    def __cmp__(self, other):
147        r = cmp(self.fixture, other.fixture);
148        if r != 0:
149            return r
150        if self.type_param:
151            if other.type_param:
152                r = cmp(self.type_param, other.type_param);
153                if r != 0:
154                     return r
155            else:
156                return -1
157        else:
158            if other.type_param:
159                return 1
160        if self.value_param:
161            if other.value_param:
162                r = cmp(self.value_param, other.value_param);
163                if r != 0:
164                     return r
165            else:
166                return -1
167        else:
168            if other.value_param:
169                return 1
170        return 0
171
172# This is a Sequence for compatibility with old scripts,
173# which treat parseLogFile's return value as a list.
174class TestRunInfo(collections.Sequence):
175    def __init__(self, properties, tests):
176        self.properties = properties
177        self.tests = tests
178
179    def __len__(self):
180        return len(self.tests)
181
182    def __getitem__(self, key):
183        return self.tests[key]
184
185def parseLogFile(filename):
186    log = parse(filename)
187
188    properties = {
189        attr_name[3:]: attr_value
190        for (attr_name, attr_value) in log.documentElement.attributes.items()
191        if attr_name.startswith('cv_')
192    }
193
194    tests = map(TestInfo, log.getElementsByTagName("testcase"))
195
196    return TestRunInfo(properties, tests)
197
198
199if __name__ == "__main__":
200    if len(sys.argv) < 2:
201        print "Usage:\n", os.path.basename(sys.argv[0]), "<log_name>.xml"
202        exit(0)
203
204    for arg in sys.argv[1:]:
205        print "Processing {}...".format(arg)
206
207        run = parseLogFile(arg)
208
209        print "Properties:"
210
211        for (prop_name, prop_value) in run.properties.items():
212          print "\t{} = {}".format(prop_name, prop_value)
213
214        print "Tests:"
215
216        for t in sorted(run.tests):
217            t.dump()
218
219        print
220