1 2 3 4 5 6 7 8 9 10 11 12 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 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
|
#!/usr/bin/env python
from __future__ import print_function
import os
import sys
import subprocess
import re
import difflib
import scriptCommon
from scriptCommon import catchPath
rootPath = os.path.join(catchPath, 'projects/SelfTest/Baselines')
filelocParser = re.compile(r'''
.*/
(.+\.[ch]pp) # filename
(?::|\() # : is starting separator between filename and line number on Linux, ( on Windows
([0-9]*) # line number
\)? # Windows also has an ending separator, )
''', re.VERBOSE)
lineNumberParser = re.compile(r' line="[0-9]*"')
hexParser = re.compile(r'\b(0[xX][0-9a-fA-F]+)\b')
durationsParser = re.compile(r' time="[0-9]*\.[0-9]*"')
timestampsParser = re.compile(r' timestamp="\d{4}-\d{2}-\d{2}T\d{2}\:\d{2}\:\d{2}Z"')
versionParser = re.compile(r'Catch v[0-9]+\.[0-9]+\.[0-9]+(-develop\.[0-9]+)?')
nullParser = re.compile(r'\b(__null|nullptr)\b')
exeNameParser = re.compile(r'''
\b
(CatchSelfTest|SelfTest) # Expected executable name
(?:.exe)? # Executable name contains .exe on Windows.
\b
''', re.VERBOSE)
# This is a hack until something more reasonable is figured out
specialCaseParser = re.compile(r'file\((\d+)\)')
# errno macro expands into various names depending on platform, so we need to fix them up as well
errnoParser = re.compile(r'''
\(\*__errno_location\ \(\)\)
|
\(\*__error\(\)\)
''', re.VERBOSE)
if len(sys.argv) == 2:
cmdPath = sys.argv[1]
else:
cmdPath = os.path.join(catchPath, scriptCommon.getBuildExecutable())
overallResult = 0
def diffFiles(fileA, fileB):
with open(fileA, 'r') as file:
aLines = [line.rstrip() for line in file.readlines()]
with open(fileB, 'r') as file:
bLines = [line.rstrip() for line in file.readlines()]
shortenedFilenameA = fileA.rsplit(os.sep, 1)[-1]
shortenedFilenameB = fileB.rsplit(os.sep, 1)[-1]
diff = difflib.unified_diff(aLines, bLines, fromfile=shortenedFilenameA, tofile=shortenedFilenameB, n=0)
return [line for line in diff if line[0] in ('+', '-')]
def filterLine(line):
if catchPath in line:
# make paths relative to Catch root
line = line.replace(catchPath + os.sep, '')
# go from \ in windows paths to /
line = line.replace('\\', '/')
# strip source line numbers
m = filelocParser.match(line)
if m:
# note that this also strips directories, leaving only the filename
filename, lnum = m.groups()
lnum = ":<line number>" if lnum else ""
line = filename + lnum + line[m.end():]
else:
line = lineNumberParser.sub(" ", line)
# strip Catch version number
line = versionParser.sub("<version>", line)
# replace *null* with 0
line = nullParser.sub("0", line)
# strip executable name
line = exeNameParser.sub("<exe-name>", line)
# strip hexadecimal numbers (presumably pointer values)
line = hexParser.sub("0x<hex digits>", line)
# strip durations and timestamps
line = durationsParser.sub(' time="{duration}"', line)
line = timestampsParser.sub(' timestamp="{iso8601-timestamp}"', line)
line = specialCaseParser.sub('file:\g<1>', line)
line = errnoParser.sub('errno', line)
return line
def approve(baseName, args):
global overallResult
args[0:0] = [cmdPath]
if not os.path.exists(cmdPath):
raise Exception("Executable doesn't exist at " + cmdPath)
baselinesPath = os.path.join(rootPath, '{0}.approved.txt'.format(baseName))
rawResultsPath = os.path.join(rootPath, '_{0}.tmp'.format(baseName))
filteredResultsPath = os.path.join(rootPath, '{0}.unapproved.txt'.format(baseName))
f = open(rawResultsPath, 'w')
subprocess.call(args, stdout=f, stderr=f)
f.close()
rawFile = open(rawResultsPath, 'r')
filteredFile = open(filteredResultsPath, 'w')
for line in rawFile:
filteredFile.write(filterLine(line).rstrip() + "\n")
filteredFile.close()
rawFile.close()
os.remove(rawResultsPath)
print()
print(baseName + ":")
if os.path.exists(baselinesPath):
diffResult = diffFiles(baselinesPath, filteredResultsPath)
if diffResult:
print('\n'.join(diffResult))
print(" \n****************************\n \033[91mResults differed")
if len(diffResult) > overallResult:
overallResult = len(diffResult)
else:
os.remove(filteredResultsPath)
print(" \033[92mResults matched")
print("\033[0m")
else:
print(" first approval")
if overallResult == 0:
overallResult = 1
print("Running approvals against executable:")
print(" " + cmdPath)
# Standard console reporter
approve("console.std", ["~[c++11]~[!nonportable]", "--order", "lex"])
# console reporter, include passes, warn about No Assertions
approve("console.sw", ["~[c++11]~[!nonportable]", "-s", "-w", "NoAssertions", "--order", "lex"])
# console reporter, include passes, warn about No Assertions, limit failures to first 4
approve("console.swa4", ["~[c++11]~[!nonportable]", "-s", "-w", "NoAssertions", "-x", "4", "--order", "lex"])
# junit reporter, include passes, warn about No Assertions
approve("junit.sw", ["~[c++11]~[!nonportable]", "-s", "-w", "NoAssertions", "-r", "junit", "--order", "lex"])
# xml reporter, include passes, warn about No Assertions
approve("xml.sw", ["~[c++11]~[!nonportable]", "-s", "-w", "NoAssertions", "-r", "xml", "--order", "lex"])
if overallResult != 0:
print("If these differences are expected, run approve.py to approve new baselines.")
exit(overallResult)
|