| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235 | #!/usr/bin/env python3from __future__ import print_functionimport ioimport osimport sysimport subprocessimport reimport difflibimport scriptCommonfrom scriptCommon import catchPathif os.name == 'nt':    # Enable console colours on windows    os.system('')rootPath = os.path.join(catchPath, 'tests/SelfTest/Baselines')langFilenameParser = re.compile(r'(.+\.[ch]pp)')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')# Note: junit must serialize time with 3 (or or less) decimal places#       before generalizing this parser, make sure that this is checked#       in other places too.junitDurationsParser = re.compile(r' time="[0-9]+\.[0-9]{3}"')durationParser = re.compile(r''' duration=['"][0-9]+['"]''')timestampsParser = re.compile(r'\d{4}-\d{2}-\d{2}T\d{2}\:\d{2}\:\d{2}Z')versionParser = re.compile(r'[0-9]+\.[0-9]+\.[0-9]+(-\w*\.[0-9]+)?')nullParser = re.compile(r'\b(__null|nullptr)\b')exeNameParser = re.compile(r'''    \b    SelfTest                  # Expected executable name    (?:.exe)?                 # Executable name contains .exe on Windows.    \b''', re.VERBOSE)# This is a hack until something more reasonable is figured outspecialCaseParser = re.compile(r'file\((\d+)\)')sinceEpochParser = re.compile(r'\d+ .+ since epoch')# The weird OR is there to always have at least empty string for group 1tapTestNumParser = re.compile(r'^((?:not ok)|(?:ok)|(?:warning)|(?:info)) (\d+) -')if len(sys.argv) == 2:    cmdPath = sys.argv[1]else:    cmdPath = os.path.join(catchPath, scriptCommon.getBuildExecutable())overallResult = 0def diffFiles(fileA, fileB):    with io.open(fileA, 'r', encoding='utf-8', errors='surrogateescape') as file:        aLines = [line.rstrip() for line in file.readlines()]    with io.open(fileB, 'r', encoding='utf-8', errors='surrogateescape') 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 normalizeFilepath(line):    # Sometimes the path separators used by compiler and Python can differ,    # so we try to match the path with both forward and backward path    # separators, to make the paths relative to Catch2 repo root.    forwardSlashPath = catchPath.replace('\\', '/')    if forwardSlashPath in line:        line = line.replace(forwardSlashPath + '/', '')    backwardSlashPath = catchPath.replace('/', '\\')    if backwardSlashPath in line:        line = line.replace(backwardSlashPath + '\\', '')    m = langFilenameParser.match(line)    if m:        filepath = m.group(0)        # go from \ in windows paths to /        filepath = filepath.replace('\\', '/')        # remove start of relative path        filepath = filepath.replace('../', '')        line = line[:m.start()] + filepath + line[m.end():]    return linedef filterLine(line, isCompact):    line = normalizeFilepath(line)    # 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)    if isCompact:        line = line.replace(': FAILED', ': failed')        line = line.replace(': PASSED', ': passed')    # strip out the test order number in TAP to avoid massive diffs for every change    line = tapTestNumParser.sub("\g<1> {test-number} -", line)    # strip Catch2 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 = junitDurationsParser.sub(' time="{duration}"', line)    line = durationParser.sub(' duration="{duration}"', line)    line = timestampsParser.sub('{iso8601-timestamp}', line)    line = specialCaseParser.sub('file:\g<1>', line)    line = sinceEpochParser.sub('{since-epoch-report}', line)    return linedef get_rawResultsPath(baseName):    return os.path.join(rootPath, '_{0}.tmp'.format(baseName))def get_baselinesPath(baseName):    return os.path.join(rootPath, '{0}.approved.txt'.format(baseName))def get_filteredResultsPath(baseName):    return os.path.join(rootPath, '{0}.unapproved.txt'.format(baseName))def run_test(baseName, args):    args[0:0] = [cmdPath]    if not os.path.exists(cmdPath):        raise Exception("Executable doesn't exist at " + cmdPath)    print(args)    rawResultsPath = get_rawResultsPath(baseName)    f = open(rawResultsPath, 'w')    subprocess.call(args, stdout=f, stderr=f)    f.close()def check_outputs(baseName):    global overallResult    rawResultsPath = get_rawResultsPath(baseName)    baselinesPath = get_baselinesPath(baseName)    filteredResultsPath = get_filteredResultsPath(baseName)    rawFile = io.open(rawResultsPath, 'r', encoding='utf-8', errors='surrogateescape')    filteredFile = io.open(filteredResultsPath, 'w', encoding='utf-8', errors='surrogateescape')    for line in rawFile:        filteredFile.write(filterLine(line, 'compact' in baseName).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 = 1def approve(baseName, args):    run_test(baseName, args)    check_outputs(baseName)print("Running approvals against executable:")print("  " + cmdPath)base_args = ["--order", "lex", "--rng-seed", "1", "--colour-mode", "none"]## special cases first:# Standard console reporterapprove("console.std", ["~[!nonportable]~[!benchmark]~[approvals] *"] + base_args)# console reporter, include passes, warn about No Assertions, limit failures to first 4approve("console.swa4", ["~[!nonportable]~[!benchmark]~[approvals] *", "-s", "-w", "NoAssertions", "-x", "4"] + base_args)## Common reporter checks: include passes, warn about No Assertionsreporters = ('console', 'junit', 'xml', 'compact', 'sonarqube', 'tap', 'teamcity', 'automake')for reporter in reporters:    filename = '{}.sw'.format(reporter)    common_args = ["~[!nonportable]~[!benchmark]~[approvals] *", "-s", "-w", "NoAssertions"] + base_args    reporter_args = ['-r', reporter]    approve(filename, common_args + reporter_args)## All reporters at the same timecommon_args = ["~[!nonportable]~[!benchmark]~[approvals] *", "-s", "-w", "NoAssertions"] + base_argsfilenames = ['{}.sw.multi'.format(reporter) for reporter in reporters]reporter_args = []for reporter, filename in zip(reporters, filenames):    reporter_args += ['-r', '{}::out={}'.format(reporter, get_rawResultsPath(filename))]run_test("default.sw.multi", common_args + reporter_args)check_outputs("default.sw.multi")for reporter, filename in zip(reporters, filenames):    check_outputs(filename)if overallResult != 0:    print("If these differences are expected, run approve.py to approve new baselines.")exit(overallResult)
 |