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