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()