File indexing completed on 2024-04-21 15:25:29
0001 #!/usr/bin/env python3 0002 # -*- coding: utf-8 -*- 0003 0004 # SPDX-FileCopyrightText: 2014 Sven Brauch 0005 # SPDX-License-Identifier: GPL-2.0-or-later 0006 0007 import sys 0008 import subprocess 0009 0010 import io 0011 0012 from PyQt4.QtCore import QProcess, QByteArray 0013 0014 import re 0015 0016 def colorize(s, colnum): 0017 return "\033[9" + str(colnum) + "m" + str(s) + "\033[0m" 0018 0019 def green(s): 0020 return colorize(s, 2) 0021 0022 def red(s): 0023 return colorize(s, 1) 0024 0025 def yellow(s): 0026 return colorize(s, 3) 0027 0028 def blue(s): 0029 return colorize(s, 4) 0030 0031 def pink(s): 0032 return colorize(s, 5) 0033 0034 def cyan(s): 0035 return colorize(s, 6) 0036 0037 def white(s): 0038 return colorize(s, 7) 0039 0040 def indent(s, level = 1): 0041 return '\n'.join([" "*level + line for line in s.splitlines()]) 0042 0043 class FailedTest(): 0044 def __init__(self, name, reason): 0045 self.filename = '<none>' 0046 self.lineno = '<none>' 0047 self.name = name 0048 self.reason = reason 0049 self.failExpected = False 0050 0051 def __str__(self): 0052 return "%s:%s %s" % (self.filename, self.lineno, self.name) 0053 0054 class TestRunner(): 0055 def __init__(self, testfile): 0056 self.testfile = testfile 0057 self.data = "" 0058 self.passed_tests = [] 0059 self.failed_tests = [] 0060 0061 def writeStdout(self): 0062 data = self.process.readAllStandardOutput().data().decode("utf-8").replace(r'\n', '\n') 0063 if "debug" in sys.argv: 0064 sys.stdout.write(data) 0065 else: 0066 for line in data.split('\n'): 0067 if line[:4] == "PASS" or line[:5] == "FAIL!" or line[:5] == "XFAIL": 0068 sys.stdout.write(".") 0069 sys.stdout.flush() 0070 self.data += data 0071 0072 def writeStderr(self): 0073 data = self.process.readAllStandardError().data().decode("utf-8").replace(r'\n', '\n') 0074 if "debug" in sys.argv: 0075 sys.stdout.write(data) 0076 self.data += data 0077 0078 def doTestrun(self): 0079 data = self.fetchOutputForJob() 0080 last_failed = False 0081 for line in data.split('\n'): 0082 success = re.match(r"PASS\s*:\s*(.*)", line) 0083 if success: 0084 function = success.groups()[0] 0085 self.passed_tests.append(function) 0086 last_failed = False 0087 0088 if last_failed: 0089 getLocation = re.match(r"\s*Loc:\s*\[(.*)\((\d*)\)]", line) 0090 if getLocation: 0091 filename = ".../" + '/'.join(getLocation.groups()[0].split('/')[-2:]) 0092 lineno = getLocation.groups()[1] 0093 self.failed_tests[-1].filename = filename 0094 self.failed_tests[-1].lineno = lineno 0095 last_failed = False 0096 0097 fail = re.match(r"FAIL!\s*:\s*(.*)\((.*)\)\s+(.*)", line) 0098 if fail: 0099 function = fail.groups()[0] 0100 args = fail.groups()[1] 0101 function = function + "(" + args + ")" 0102 reason = fail.groups()[2] 0103 self.failed_tests.append(FailedTest(function, reason)) 0104 last_failed = True 0105 xfail = re.match(r"XFAIL\s*:\s*(.*)\((.*)\)\s+(.*)", line) 0106 if xfail: 0107 function = xfail.groups()[0] 0108 args = xfail.groups()[1] 0109 function = function + "(" + args + ")" 0110 reason = xfail.groups()[2] 0111 self.failed_tests.append(FailedTest(function, reason)) 0112 self.failed_tests[-1].failExpected = True 0113 last_failed = True 0114 0115 fatal_fail = re.match(r"(QFATAL|ASSERT)\s*", line) 0116 if fatal_fail: 0117 print(self.data) 0118 print(red("Fatal error occurred, aborting")) 0119 return 0120 0121 passed, failed = len(self.passed_tests), len(self.failed_tests) 0122 try: 0123 percent = round((float(passed) / (failed+passed)) * 100) 0124 except ZeroDivisionError: 0125 percent = 0 0126 percent = green(percent) if percent == 100 else yellow(percent) if percent > 80 else red(percent) 0127 total = white(passed+failed) 0128 passed, failed = green(passed), red(failed) 0129 print("\n Done. Summary: %s tests reported total, %s passed, %s failed (%s%% passed)." % (total, passed, failed, percent)) 0130 print(" Detailed information:\n") 0131 print(white(" ==="), green("Passed tests:"), white("===")) 0132 namespaceFunctionArgs = r"(.*)::(.*)\((.*)\)" 0133 0134 if len(self.passed_tests): 0135 for test in self.passed_tests: 0136 test = re.match(namespaceFunctionArgs, test) 0137 test = test.groups() 0138 test = "[%s] " % test[0] + green(test[1]) + "(" + white(test[2]) + ")" 0139 print(indent(green("✔ ") + test)) 0140 0141 if len(self.failed_tests): 0142 print("\n" + white(" ==="), red("Failed tests:"), white("===")) 0143 for test in self.failed_tests: 0144 namespace, function, args = re.match(namespaceFunctionArgs, test.name).groups() 0145 filename = test.filename.split('/')[-1] 0146 path = '/'.join(test.filename.split('/')[:-1]) + "/" 0147 print(indent((yellow("✘ ") if test.failExpected else red("✘ ")) + white(filename) + ":" + blue(test.lineno) + 0148 " "*(5-len(str(lineno))) + red(function) + "(" + yellow(args) + ")")) 0149 if 'noreason' not in sys.argv: 0150 print(indent("Reason of failure:" + blue(" ❮") + white(test.reason) + blue("❯ ") 0151 + ( "(Failure expected)" if test.failExpected else "" ), 2)) 0152 #print "[in %s]" % namespace 0153 0154 def fetchOutputForJob(self): 0155 self.process = QProcess() 0156 self.process.readyReadStandardOutput.connect(self.writeStdout) 0157 self.process.readyReadStandardError.connect(self.writeStderr) 0158 print(" Please wait, running tests", end=' ') 0159 sys.stdout.flush() 0160 self.process.start(self.testfile, ["-maxwarnings", "0"]) 0161 self.process.waitForFinished(-1) 0162 return str(self.data) 0163 0164 if __name__ == '__main__': 0165 runner = TestRunner(sys.argv[1]) 0166 runner.doTestrun()