runtest.py 5.99 KB
Newer Older
Sven Brauch's avatar
Sven Brauch committed
1
#!/usr/bin/env python3
2
# -*- coding: utf-8 -*-
3

4 5 6
# Copyright 2014 by Sven Brauch
# License: GPL v2+

7 8 9
import sys
import subprocess

10
import io
11

12
from PyQt4.QtCore import QProcess, QByteArray
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43

import re

def colorize(s, colnum):
    return "\033[9" + str(colnum) + "m" + str(s) + "\033[0m"

def green(s):
    return colorize(s, 2)

def red(s):
    return colorize(s, 1)

def yellow(s):
    return colorize(s, 3)

def blue(s):
    return colorize(s, 4)

def pink(s):
    return colorize(s, 5)

def cyan(s):
    return colorize(s, 6)

def white(s):
    return colorize(s, 7)

def indent(s, level = 1):
    return '\n'.join(["    "*level + line for line in s.splitlines()])
        
class FailedTest():
44
    def __init__(self, name, reason):
45 46 47
        self.filename = '<none>'
        self.lineno = '<none>'
        self.name = name
48
        self.reason = reason
49
        self.failExpected = False
50 51 52 53 54 55 56 57 58 59 60 61
        
    def __str__(self):
        return "%s:%s %s" % (self.filename, self.lineno, self.name)

class TestRunner():
    def __init__(self, testfile):
        self.testfile = testfile
        self.data = ""
        self.passed_tests = []
        self.failed_tests = []
    
    def writeStdout(self):
62
        data = self.process.readAllStandardOutput().data().decode("utf-8").replace(r'\n', '\n')
63 64
        if "debug" in sys.argv:
            sys.stdout.write(data)
65 66 67 68 69
        else:
            for line in data.split('\n'):
                if line[:4] == "PASS" or line[:5] == "FAIL!" or line[:5] == "XFAIL":
                    sys.stdout.write(".")
                    sys.stdout.flush()
70 71 72
        self.data += data
    
    def writeStderr(self):
73
        data = self.process.readAllStandardError().data().decode("utf-8").replace(r'\n', '\n')
74 75 76 77 78 79 80
        if "debug" in sys.argv:
            sys.stdout.write(data)
        self.data += data
    
    def doTestrun(self):
        data = self.fetchOutputForJob()
        last_failed = False
81
        for line in data.split('\n'):
82 83 84 85
            success = re.match(r"PASS\s*:\s*(.*)", line)
            if success:
                function = success.groups()[0]
                self.passed_tests.append(function)
86
                last_failed = False
87 88 89
            
            if last_failed:
                getLocation = re.match(r"\s*Loc:\s*\[(.*)\((\d*)\)]", line)
90 91 92 93 94 95
                if getLocation:
                    filename = ".../" + '/'.join(getLocation.groups()[0].split('/')[-2:])
                    lineno = getLocation.groups()[1]
                    self.failed_tests[-1].filename = filename
                    self.failed_tests[-1].lineno = lineno
                    last_failed = False
96
            
97
            fail = re.match(r"FAIL!\s*:\s*(.*)\((.*)\)\s+(.*)", line)
98 99
            if fail:
                function = fail.groups()[0]
100 101 102 103
                args = fail.groups()[1]
                function = function + "(" + args + ")"
                reason = fail.groups()[2]
                self.failed_tests.append(FailedTest(function, reason))
104
                last_failed = True
105 106 107 108 109 110 111 112 113
            xfail = re.match(r"XFAIL\s*:\s*(.*)\((.*)\)\s+(.*)", line)
            if xfail:
                function = xfail.groups()[0]
                args = xfail.groups()[1]
                function = function + "(" + args + ")"
                reason = xfail.groups()[2]
                self.failed_tests.append(FailedTest(function, reason))
                self.failed_tests[-1].failExpected = True
                last_failed = True
114
            
115
            fatal_fail = re.match(r"(QFATAL|ASSERT)\s*", line)
116
            if fatal_fail:
117
                print(self.data)
Yuri Chornoivan's avatar
Yuri Chornoivan committed
118
                print(red("Fatal error occurred, aborting"))
119
                return
120 121
        
        passed, failed = len(self.passed_tests), len(self.failed_tests)
122 123 124
        try:
            percent = round((float(passed) / (failed+passed)) * 100)
        except ZeroDivisionError:
125
            percent = 0
126 127 128
        percent = green(percent) if percent == 100 else yellow(percent) if percent > 80 else red(percent)
        total = white(passed+failed)
        passed, failed = green(passed), red(failed)
129 130 131
        print("\n Done. Summary: %s tests reported total, %s passed, %s failed (%s%% passed)." % (total, passed, failed, percent))
        print(" Detailed information:\n")
        print(white("  ==="), green("Passed tests:"), white("==="))
132 133
        namespaceFunctionArgs = r"(.*)::(.*)\((.*)\)"
        
134
        if len(self.passed_tests):
135 136 137
            for test in self.passed_tests:
                test = re.match(namespaceFunctionArgs, test)
                test = test.groups()
138
                test = "[%s] " % test[0] + green(test[1]) + "(" + white(test[2]) + ")"
139
                print(indent(green("✔ ") + test))
140 141
        
        if len(self.failed_tests):
142
            print("\n" + white("  ==="), red("Failed tests:"), white("==="))
143 144 145 146
            for test in self.failed_tests:
                namespace, function, args = re.match(namespaceFunctionArgs, test.name).groups()
                filename = test.filename.split('/')[-1]
                path = '/'.join(test.filename.split('/')[:-1]) + "/"
147 148
                print(indent((yellow("✘ ") if test.failExpected else red("✘ ")) + white(filename) + ":" + blue(test.lineno) +
                      " "*(5-len(str(lineno))) + red(function) + "(" + yellow(args) + ")"))
149
                if 'noreason' not in sys.argv:
150 151
                    print(indent("Reason of failure:" + blue(" ❮") + white(test.reason) + blue("❯ ") 
                                 + ( "(Failure expected)" if test.failExpected else "" ), 2))
152
                #print "[in %s]" % namespace
153 154 155 156 157
    
    def fetchOutputForJob(self):
        self.process = QProcess()
        self.process.readyReadStandardOutput.connect(self.writeStdout)
        self.process.readyReadStandardError.connect(self.writeStderr)
158
        print(" Please wait, running tests", end=' ')
159
        sys.stdout.flush()
160
        self.process.start(self.testfile, ["-maxwarnings", "0"])
161
        self.process.waitForFinished(-1)
162 163 164 165
        return str(self.data)
    
if __name__ == '__main__':
    runner = TestRunner(sys.argv[1])
166
    runner.doTestrun()