mirror of https://github.com/opencv/opencv.git
Open Source Computer Vision Library
https://opencv.org/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
235 lines
7.3 KiB
235 lines
7.3 KiB
#!/usr/bin/env python |
|
""" Parse XML test log file. |
|
|
|
This module serves as utility for other scripts. |
|
""" |
|
import collections |
|
import re |
|
import os.path |
|
import sys |
|
from xml.dom.minidom import parse |
|
|
|
def cmp(a, b): return (a>b)-(a<b) |
|
|
|
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") |
|
|
|
custom_status = xmlnode.getAttribute("custom_status") |
|
failures = xmlnode.getElementsByTagName("failure") |
|
|
|
if len(custom_status) > 0: |
|
self.status = custom_status |
|
elif len(failures) > 0: |
|
self.status = "failed" |
|
else: |
|
self.status = xmlnode.getAttribute("status") |
|
|
|
if self.name.startswith("DISABLED_"): |
|
if self.status == 'notrun': |
|
self.status = "disabled" |
|
self.fixture = self.fixture.replace("DISABLED_", "") |
|
self.name = self.name.replace("DISABLED_", "") |
|
self.properties = { |
|
prop.getAttribute("name") : prop.getAttribute("value") |
|
for prop in xmlnode.getElementsByTagName("property") |
|
if prop.hasAttribute("name") and prop.hasAttribute("value") |
|
} |
|
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"); |
|
self.parseFloatMetric(xmlnode, "time"); |
|
self.parseLongMetric(xmlnode, "total_memory_usage"); |
|
|
|
def parseLongMetric(self, xmlnode, name, default = 0): |
|
if name in self.properties: |
|
self.metrix[name] = int(self.properties[name]) |
|
elif xmlnode.hasAttribute(name): |
|
self.metrix[name] = int(xmlnode.getAttribute(name)) |
|
else: |
|
self.metrix[name] = default |
|
|
|
def parseIntMetric(self, xmlnode, name, default = 0): |
|
if name in self.properties: |
|
self.metrix[name] = int(self.properties[name]) |
|
elif xmlnode.hasAttribute(name): |
|
self.metrix[name] = int(xmlnode.getAttribute(name)) |
|
else: |
|
self.metrix[name] = default |
|
|
|
def parseFloatMetric(self, xmlnode, name, default = 0): |
|
if name in self.properties: |
|
self.metrix[name] = float(self.properties[name]) |
|
elif xmlnode.hasAttribute(name): |
|
self.metrix[name] = float(xmlnode.getAttribute(name)) |
|
else: |
|
self.metrix[name] = default |
|
|
|
def parseStringMetric(self, xmlnode, name, default = None): |
|
if name in self.properties: |
|
self.metrix[name] = self.properties[name].strip() |
|
elif xmlnode.hasAttribute(name): |
|
self.metrix[name] = xmlnode.getAttribute(name).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 |
|
if name == "time": |
|
return self.metrix.get("time") |
|
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 == "us" or units == "mks": # mks is typo error for microsecond (<= OpenCV 3.4) |
|
scale = 1000000.0 |
|
if units == "ns": |
|
scale = 1000000000.0 |
|
if units == "ticks": |
|
frequency = int(1) |
|
scale = int(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)) |
|
|
|
|
|
def getName(self): |
|
pos = self.name.find("/") |
|
if pos > 0: |
|
return self.name[:pos] |
|
return self.name |
|
|
|
|
|
def getFixture(self): |
|
if self.fixture.endswith(self.getName()): |
|
fixture = self.fixture[:-len(self.getName())] |
|
else: |
|
fixture = self.fixture |
|
if fixture.endswith("_"): |
|
fixture = fixture[:-1] |
|
return fixture |
|
|
|
|
|
def param(self): |
|
return '::'.join(filter(None, [self.type_param, self.value_param])) |
|
|
|
def shortName(self): |
|
name = self.getName() |
|
fixture = self.getFixture() |
|
return '::'.join(filter(None, [name, fixture])) |
|
|
|
|
|
def __str__(self): |
|
name = self.getName() |
|
fixture = self.getFixture() |
|
return '::'.join(filter(None, [name, fixture, self.type_param, self.value_param])) |
|
|
|
|
|
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 |
|
|
|
def __lt__(self, other): |
|
return self.__cmp__(other) == -1 |
|
|
|
# This is a Sequence for compatibility with old scripts, |
|
# which treat parseLogFile's return value as a list. |
|
class TestRunInfo(object): |
|
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] |
|
|
|
def parseLogFile(filename): |
|
log = parse(filename) |
|
|
|
properties = { |
|
attr_name[3:]: attr_value |
|
for (attr_name, attr_value) in log.documentElement.attributes.items() |
|
if attr_name.startswith('cv_') |
|
} |
|
|
|
tests = list(map(TestInfo, log.getElementsByTagName("testcase"))) |
|
|
|
return TestRunInfo(properties, tests) |
|
|
|
|
|
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:]: |
|
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): |
|
t.dump() |
|
|
|
print()
|
|
|