logparser.py 5.94 KB
Newer Older
Daniel Smith's avatar
Daniel Smith committed
1 2 3
import os
import argparse
import re
4 5 6
import io
import tempfile
import requests
Daniel Smith's avatar
Daniel Smith committed
7 8 9

failedTests = {}  # Typing.dict()
totalCount = 0
10
totalTestCaseCount = 0
Daniel Smith's avatar
Daniel Smith committed
11 12 13

def findFailedTests(logfile: str):
    global totalCount
14 15
    global totalTestCaseCount
    pattern = re.compile(r'[0-9]{1,}(?<!0) failed') # Look for the testresults line that includes the number of failed tests, where not 0.
Daniel Smith's avatar
Daniel Smith committed
16

17
    with open(logfile) as log:  #Open the log file
Daniel Smith's avatar
Daniel Smith committed
18
        logtext = log.read()
19 20 21 22 23 24
        if pattern.search(logtext): # Find match(es) with the regex pattern above.
            
            resultsFilename = ""  # Typing.str()
            if args.suppressFile:
                resultsFilename = os.devnull
            else:
25 26 27 28 29
                if args.logDir:
                    resultsFilename = "{0}{1}{2}_failures.txt".format(os.path.join(args.logDir, "parser_results"), os.sep,
                                                                      logfile[logfile.rfind(os.sep) + 1: logfile.rfind('.txt')])
                else:  # Assume URL was passed and we only have one file, and --outputdir was specified.
                    resultsFilename = "{0}{1}parser_results.txt".format(os.path.join(args.outDir, "parser_results"), os.sep)
30 31 32
            
            with open(resultsFilename, mode='w+') as outfile:
                for match in pattern.finditer(logtext):  # Iterate through the matches for failed tests and strip out the individual cases.
Daniel Smith's avatar
Daniel Smith committed
33
                    tstnameindex = 0
34
                    tstnameindex = logtext.rfind("********* Start testing of ", 0, match.span()[0]) + 27  # Search for the test case.
Daniel Smith's avatar
Daniel Smith committed
35
                    tstname = logtext[tstnameindex:
36 37 38 39
                                      logtext.find("*********", tstnameindex)-1]
                    if not args.suppressConsole:
                        print("{0}: {1}".format(tstname,match.group(0)))  # Print the test name and count failed.
                    outfile.writelines("{0}: {1}\r\n".format(tstname,match.group(0)))  # Write it to file.
Daniel Smith's avatar
Daniel Smith committed
40
                    
41
                    if int(re.match(r'[0-9]{0,}', match.group(0)).group(0)) > 9 and not args.printAll:  #
Daniel Smith's avatar
Daniel Smith committed
42
                        outfile.writelines("Too many fail cases. Not printing the failures here. See the original log for failure details.")
43 44
                        if not args.suppressConsole:
                            print("Too many fail cases. Not printing the failures here. See the original log for failure details.")
Daniel Smith's avatar
Daniel Smith committed
45 46
                        break

47
                    logsnip = logtext[tstnameindex : logtext.find("********* Finished testing of ", tstnameindex)]  # Save a snip of the log with the failed test(s)
Daniel Smith's avatar
Daniel Smith committed
48

49 50 51
                    for failcasematch in re.finditer('FAIL!  : ', logsnip):  # Find the actual fail case
                        totalTestCaseCount += 1
                        # Look for the end of the case. We know coin always print the file location. Grab up the the newline when we find that.
Daniel Smith's avatar
Daniel Smith committed
52
                        failcasestring = logsnip[failcasematch.span()[0] : logsnip.find('\n', logsnip.find("Loc: ", failcasematch.span()[0]))]
53 54
                        if not args.suppressConsole:
                            print("{0}\r\n".format(failcasestring))
Daniel Smith's avatar
Daniel Smith committed
55 56
                        outfile.writelines("{0}\r\n".format(failcasestring))
                    totalCount += 1
57 58

    if not args.suppressConsole: print("\r\n")  # just a spacer fore readability when printing results to screen.
Daniel Smith's avatar
Daniel Smith committed
59

60 61 62 63 64 65 66 67 68 69

def retrieveLogFromURL(url: str):
    r = ""
    try:
        r = requests.get(url)
    except Exception as e:
        print(e)
    return r.content


Daniel Smith's avatar
Daniel Smith committed
70 71 72
if __name__ == "__main__":

    parser = argparse.ArgumentParser()
73 74 75
    parser.add_argument('--logdir',dest='logDir', type=str, help='The full path to a directory of logs')
    parser.add_argument('--logurl', dest='logURL', type=str, help='The URL to a logfile in txt or gzip format. Suitable for use directy with COIN.')
    parser.add_argument('--outputdir', dest='outDir', type=str, help='Specify the output location for result files. Defaults to $\{logdir\}/parser_results')
76 77 78
    parser.add_argument('--printall', action='store_true', dest="printAll", help="Set --printall to force printing details of tests with more than 9 fail cases. ")
    parser.add_argument('--suppressconsole', action='store_true', dest="suppressConsole", help="Set --supressconsole to prevent writing to screen.")
    parser.add_argument('--suppressfile', action='store_true', dest="suppressFile", help="Set --supressfile to prevent writing results to file.")
Daniel Smith's avatar
Daniel Smith committed
79 80 81 82

    args = parser.parse_args()
    logfiles = os.listdir(args.logDir)

83 84 85 86 87 88 89 90 91 92
    if not args.logDir and not (args.logURL and (False if args.logURL and not args.suppressFile else True)):
        print("\r\nWARNING: You must supply either --logdir or --logurl as an argument.\r\n--outputdir must be\
specified when using --logurl unless file output is supressed with --suppressfile.")
        exit(0)

    print("\r\n")  # Just to help with formatting the output on screen.

    if args.logDir:
        if not os.path.exists(os.path.join(args.logDir, "parser_results")) and not args.suppressFile:
            os.makedirs(os.path.join(args.logDir, "parser_results"))
93 94


95 96 97 98
        for logfile in logfiles:
            print("Processing {0}".format(logfile))
            if not os.path.isdir(os.path.join(args.logDir, logfile)):
                findFailedTests(os.path.join(args.logDir, logfile))
Daniel Smith's avatar
Daniel Smith committed
99

100 101 102 103 104 105 106 107 108 109
    else:  # must be args.logURL supplied.
        log = tempfile.NamedTemporaryFile(mode='wb', suffix=".txt", delete=False)
        filebytes=retrieveLogFromURL(args.logURL)
        log.writelines(io.BytesIO(filebytes))
        log.close()
        findFailedTests(log.name)
        try:
            os.remove(log.name)
        except Exception as e:
            os.unlink(log.name)  # try unlinking as a backup?
Daniel Smith's avatar
Daniel Smith committed
110

111
    print("Done parsing.\r\nTotal failed test applications: {0}\r\nTotal failed test cases: {1}".format(totalCount, totalTestCaseCount))