testlog_parser.py 6.47 KB
Newer Older
1
#!/usr/bin/env python
2

3 4 5 6
import collections
import re
import os.path
import sys
7 8 9 10 11 12 13 14 15
from xml.dom.minidom import parse

class TestInfo(object):

    def __init__(self, xmlnode):
        self.fixture = xmlnode.getAttribute("classname")
        self.name = xmlnode.getAttribute("name")
        self.value_param = xmlnode.getAttribute("value_param")
        self.type_param = xmlnode.getAttribute("type_param")
16

17
        custom_status = xmlnode.getAttribute("custom_status")
18
        failures = xmlnode.getElementsByTagName("failure")
19 20 21 22 23

        if len(custom_status) > 0:
            self.status = custom_status
        elif len(failures) > 0:
            self.status = "failed"
24 25
        else:
            self.status = xmlnode.getAttribute("status")
26

27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
        if self.name.startswith("DISABLED_"):
            self.status = "disabled"
            self.fixture = self.fixture.replace("DISABLED_", "")
            self.name = self.name.replace("DISABLED_", "")
        self.metrix = {}
        self.parseLongMetric(xmlnode, "bytesIn");
        self.parseLongMetric(xmlnode, "bytesOut");
        self.parseIntMetric(xmlnode, "samples");
        self.parseIntMetric(xmlnode, "outliers");
        self.parseFloatMetric(xmlnode, "frequency", 1);
        self.parseLongMetric(xmlnode, "min");
        self.parseLongMetric(xmlnode, "median");
        self.parseLongMetric(xmlnode, "gmean");
        self.parseLongMetric(xmlnode, "mean");
        self.parseLongMetric(xmlnode, "stddev");
        self.parseFloatMetric(xmlnode, "gstddev");
43
        self.parseFloatMetric(xmlnode, "time");
44

45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
    def parseLongMetric(self, xmlnode, name, default = 0):
        if xmlnode.hasAttribute(name):
            tmp = xmlnode.getAttribute(name)
            val = long(tmp)
            self.metrix[name] = val
        else:
            self.metrix[name] = default

    def parseIntMetric(self, xmlnode, name, default = 0):
        if xmlnode.hasAttribute(name):
            tmp = xmlnode.getAttribute(name)
            val = int(tmp)
            self.metrix[name] = val
        else:
            self.metrix[name] = default

    def parseFloatMetric(self, xmlnode, name, default = 0):
        if xmlnode.hasAttribute(name):
            tmp = xmlnode.getAttribute(name)
            val = float(tmp)
            self.metrix[name] = val
        else:
            self.metrix[name] = default

    def parseStringMetric(self, xmlnode, name, default = None):
        if xmlnode.hasAttribute(name):
            tmp = xmlnode.getAttribute(name)
            self.metrix[name] = tmp.strip()
        else:
            self.metrix[name] = default

    def get(self, name, units="ms"):
        if name == "classname":
            return self.fixture
        if name == "name":
            return self.name
        if name == "fullname":
            return self.__str__()
        if name == "value_param":
            return self.value_param
        if name == "type_param":
            return self.type_param
        if name == "status":
            return self.status
        val = self.metrix.get(name, None)
        if not val:
            return val
92 93
        if name == "time":
            return self.metrix.get("time")
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
        if name in ["gmean", "min", "mean", "median", "stddev"]:
            scale = 1.0
            frequency = self.metrix.get("frequency", 1.0) or 1.0
            if units == "ms":
                scale = 1000.0
            if units == "mks":
                scale = 1000000.0
            if units == "ns":
                scale = 1000000000.0
            if units == "ticks":
                frequency = long(1)
                scale = long(1)
            return val * scale / frequency
        return val


    def dump(self, units="ms"):
        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 116
        pos = self.name.find("/")
        if pos > 0:
117 118 119 120 121 122 123
            return self.name[:pos]
        return self.name


    def getFixture(self):
        if self.fixture.endswith(self.getName()):
            fixture = self.fixture[:-len(self.getName())]
124 125 126 127
        else:
            fixture = self.fixture
        if fixture.endswith("_"):
            fixture = fixture[:-1]
128 129 130 131 132 133 134 135 136
        return fixture


    def param(self):
        return '::'.join(filter(None, [self.type_param, self.value_param]))

    def shortName(self):
        name = self.getName()
        fixture = self.getFixture()
137 138
        return '::'.join(filter(None, [name, fixture]))

139

140
    def __str__(self):
141 142
        name = self.getName()
        fixture = self.getFixture()
143 144
        return '::'.join(filter(None, [name, fixture, self.type_param, self.value_param]))

145

146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
    def __cmp__(self, other):
        r = cmp(self.fixture, other.fixture);
        if r != 0:
            return r
        if self.type_param:
            if other.type_param:
                r = cmp(self.type_param, other.type_param);
                if r != 0:
                     return r
            else:
                return -1
        else:
            if other.type_param:
                return 1
        if self.value_param:
            if other.value_param:
                r = cmp(self.value_param, other.value_param);
                if r != 0:
                     return r
            else:
                return -1
        else:
            if other.value_param:
                return 1
        return 0

172 173 174 175 176 177 178 179 180 181 182 183 184
# This is a Sequence for compatibility with old scripts,
# which treat parseLogFile's return value as a list.
class TestRunInfo(collections.Sequence):
    def __init__(self, properties, tests):
        self.properties = properties
        self.tests = tests

    def __len__(self):
        return len(self.tests)

    def __getitem__(self, key):
        return self.tests[key]

185 186
def parseLogFile(filename):
    log = parse(filename)
187 188 189 190 191 192 193 194 195 196

    properties = {
        attr_name[3:]: attr_value
        for (attr_name, attr_value) in log.documentElement.attributes.items()
        if attr_name.startswith('cv_')
    }

    tests = map(TestInfo, log.getElementsByTagName("testcase"))

    return TestRunInfo(properties, tests)
197 198 199 200 201 202 203 204


if __name__ == "__main__":
    if len(sys.argv) < 2:
        print "Usage:\n", os.path.basename(sys.argv[0]), "<log_name>.xml"
        exit(0)

    for arg in sys.argv[1:]:
205 206 207 208 209 210 211 212 213 214 215 216
        print "Processing {}...".format(arg)

        run = parseLogFile(arg)

        print "Properties:"

        for (prop_name, prop_value) in run.properties.items():
          print "\t{} = {}".format(prop_name, prop_value)

        print "Tests:"

        for t in sorted(run.tests):
217
            t.dump()
218

219
        print