зеркало из https://github.com/microsoft/clang-1.git
300 строки
8.8 KiB
Python
Executable File
300 строки
8.8 KiB
Python
Executable File
#!/usr/bin/env python
|
|
#
|
|
# TestRunner.py - This script is used to run arbitrary unit tests. Unit
|
|
# tests must contain the command used to run them in the input file, starting
|
|
# immediately after a "RUN:" string.
|
|
#
|
|
# This runner recognizes and replaces the following strings in the command:
|
|
#
|
|
# %s - Replaced with the input name of the program, or the program to
|
|
# execute, as appropriate.
|
|
# %S - Replaced with the directory where the input resides.
|
|
# %llvmgcc - llvm-gcc command
|
|
# %llvmgxx - llvm-g++ command
|
|
# %prcontext - prcontext.tcl script
|
|
# %t - temporary file name (derived from testcase name)
|
|
#
|
|
|
|
import errno
|
|
import os
|
|
import re
|
|
import signal
|
|
import subprocess
|
|
import sys
|
|
|
|
# Increase determinism for things that use the terminal width.
|
|
#
|
|
# FIXME: Find a better place for this hack.
|
|
os.environ['COLUMNS'] = '0'
|
|
|
|
class TestStatus:
|
|
Pass = 0
|
|
XFail = 1
|
|
Fail = 2
|
|
XPass = 3
|
|
NoRunLine = 4
|
|
Invalid = 5
|
|
|
|
kNames = ['Pass','XFail','Fail','XPass','NoRunLine','Invalid']
|
|
@staticmethod
|
|
def getName(code):
|
|
return TestStatus.kNames[code]
|
|
|
|
def mkdir_p(path):
|
|
if not path:
|
|
pass
|
|
elif os.path.exists(path):
|
|
pass
|
|
else:
|
|
parent = os.path.dirname(path)
|
|
if parent != path:
|
|
mkdir_p(parent)
|
|
try:
|
|
os.mkdir(path)
|
|
except OSError,e:
|
|
if e.errno != errno.EEXIST:
|
|
raise
|
|
|
|
def remove(path):
|
|
try:
|
|
os.remove(path)
|
|
except OSError:
|
|
pass
|
|
|
|
def cat(path, output):
|
|
f = open(path)
|
|
output.writelines(f)
|
|
f.close()
|
|
|
|
def runOneTest(FILENAME, SUBST, OUTPUT, TESTNAME, CLANG, CLANGCC,
|
|
useValgrind=False,
|
|
useDGCompat=False,
|
|
useScript=None,
|
|
output=sys.stdout):
|
|
OUTPUT = os.path.abspath(OUTPUT)
|
|
if useValgrind:
|
|
VG_OUTPUT = '%s.vg'%(OUTPUT,)
|
|
os.system('rm -f %s.*'%(VG_OUTPUT))
|
|
VALGRIND = 'valgrind -q --tool=memcheck --leak-check=full --trace-children=yes --log-file=%s.%%p'%(VG_OUTPUT)
|
|
CLANG = '%s %s'%(VALGRIND, CLANG)
|
|
CLANGCC = '%s %s'%(VALGRIND, CLANGCC)
|
|
|
|
# Create the output directory if it does not already exist.
|
|
mkdir_p(os.path.dirname(OUTPUT))
|
|
|
|
# FIXME
|
|
#ulimit -t 40
|
|
|
|
# FIXME: Load script once
|
|
# FIXME: Support "short" script syntax
|
|
|
|
if useScript:
|
|
scriptFile = useScript
|
|
else:
|
|
# See if we have a per-dir test script.
|
|
dirScriptFile = os.path.join(os.path.dirname(FILENAME), 'test.script')
|
|
if os.path.exists(dirScriptFile):
|
|
scriptFile = dirScriptFile
|
|
else:
|
|
scriptFile = FILENAME
|
|
|
|
# Verify the script contains a run line.
|
|
for ln in open(scriptFile):
|
|
if 'RUN:' in ln:
|
|
break
|
|
else:
|
|
print >>output, "******************** TEST '%s' HAS NO RUN LINE! ********************"%(TESTNAME,)
|
|
output.flush()
|
|
return TestStatus.NoRunLine
|
|
|
|
FILENAME = os.path.abspath(FILENAME)
|
|
SCRIPT = OUTPUT + '.script'
|
|
TEMPOUTPUT = OUTPUT + '.tmp'
|
|
|
|
substitutions = [('%s',SUBST),
|
|
('%S',os.path.dirname(SUBST)),
|
|
('%llvmgcc','llvm-gcc -emit-llvm -w'),
|
|
('%llvmgxx','llvm-g++ -emit-llvm -w'),
|
|
('%prcontext','prcontext.tcl'),
|
|
('%t',TEMPOUTPUT),
|
|
(' clang ', ' ' + CLANG + ' '),
|
|
(' clang-cc ', ' ' + CLANGCC + ' ')]
|
|
scriptLines = []
|
|
xfailLines = []
|
|
for ln in open(scriptFile):
|
|
if 'RUN:' in ln:
|
|
# Isolate run parameters
|
|
index = ln.index('RUN:')
|
|
ln = ln[index+4:]
|
|
|
|
# Apply substitutions
|
|
for a,b in substitutions:
|
|
ln = ln.replace(a,b)
|
|
|
|
if useDGCompat:
|
|
ln = re.sub(r'\{(.*)\}', r'"\1"', ln)
|
|
scriptLines.append(ln)
|
|
elif 'XFAIL' in ln:
|
|
xfailLines.append(ln)
|
|
|
|
if xfailLines:
|
|
print >>output, "XFAILED '%s':"%(TESTNAME,)
|
|
output.writelines(xfailLines)
|
|
|
|
# Write script file
|
|
f = open(SCRIPT,'w')
|
|
f.write(''.join(scriptLines))
|
|
f.close()
|
|
|
|
outputFile = open(OUTPUT,'w')
|
|
p = None
|
|
try:
|
|
p = subprocess.Popen(["/bin/sh",SCRIPT],
|
|
cwd=os.path.dirname(FILENAME),
|
|
stdin=subprocess.PIPE,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE)
|
|
out,err = p.communicate()
|
|
outputFile.write(out)
|
|
outputFile.write(err)
|
|
SCRIPT_STATUS = p.wait()
|
|
|
|
# Detect Ctrl-C in subprocess.
|
|
if SCRIPT_STATUS == -signal.SIGINT:
|
|
raise KeyboardInterrupt
|
|
except KeyboardInterrupt:
|
|
raise
|
|
outputFile.close()
|
|
|
|
if xfailLines:
|
|
SCRIPT_STATUS = not SCRIPT_STATUS
|
|
|
|
if useValgrind:
|
|
VG_OUTPUT = capture(['/bin/sh','-c','cat %s.*'%(VG_OUTPUT)])
|
|
VG_STATUS = len(VG_OUTPUT)
|
|
else:
|
|
VG_STATUS = 0
|
|
|
|
if SCRIPT_STATUS or VG_STATUS:
|
|
print >>output, "******************** TEST '%s' FAILED! ********************"%(TESTNAME,)
|
|
print >>output, "Command: "
|
|
output.writelines(scriptLines)
|
|
if not SCRIPT_STATUS:
|
|
print >>output, "Output:"
|
|
else:
|
|
print >>output, "Incorrect Output:"
|
|
cat(OUTPUT, output)
|
|
if VG_STATUS:
|
|
print >>output, "Valgrind Output:"
|
|
print >>output, VG_OUTPUT
|
|
print >>output, "******************** TEST '%s' FAILED! ********************"%(TESTNAME,)
|
|
output.flush()
|
|
if xfailLines:
|
|
return TestStatus.XPass
|
|
else:
|
|
return TestStatus.Fail
|
|
|
|
if xfailLines:
|
|
return TestStatus.XFail
|
|
else:
|
|
return TestStatus.Pass
|
|
|
|
def capture(args):
|
|
p = subprocess.Popen(args, stdout=subprocess.PIPE)
|
|
out,_ = p.communicate()
|
|
return out
|
|
|
|
def which(command):
|
|
# Would be nice if Python had a lib function for this.
|
|
res = capture(['which',command])
|
|
res = res.strip()
|
|
if res and os.path.exists(res):
|
|
return res
|
|
return None
|
|
|
|
def inferClang():
|
|
# Determine which clang to use.
|
|
clang = os.getenv('CLANG')
|
|
|
|
# If the user set clang in the environment, definitely use that and don't
|
|
# try to validate.
|
|
if clang:
|
|
return clang
|
|
|
|
# Otherwise look in the path.
|
|
clang = which('clang')
|
|
|
|
if not clang:
|
|
print >>sys.stderr, "error: couldn't find 'clang' program, try setting CLANG in your environment"
|
|
sys.exit(1)
|
|
|
|
return clang
|
|
|
|
def inferClangCC(clang):
|
|
clangcc = os.getenv('CLANGCC')
|
|
|
|
# If the user set clang in the environment, definitely use that and don't
|
|
# try to validate.
|
|
if clangcc:
|
|
return clangcc
|
|
|
|
# Otherwise try adding -cc since we expect to be looking in a build
|
|
# directory.
|
|
clangcc = which(clang + '-cc')
|
|
if not clangcc:
|
|
# Otherwise ask clang.
|
|
res = capture([clang, '-print-prog-name=clang-cc'])
|
|
res = res.strip()
|
|
if res and os.path.exists(res):
|
|
clangcc = res
|
|
|
|
if not clangcc:
|
|
print >>sys.stderr, "error: couldn't find 'clang-cc' program, try setting CLANGCC in your environment"
|
|
sys.exit(1)
|
|
|
|
return clangcc
|
|
|
|
def main():
|
|
global options
|
|
from optparse import OptionParser
|
|
parser = OptionParser("usage: %prog [options] {tests}")
|
|
parser.add_option("", "--clang", dest="clang",
|
|
help="Program to use as \"clang\"",
|
|
action="store", default=None)
|
|
parser.add_option("", "--clang-cc", dest="clangcc",
|
|
help="Program to use as \"clang-cc\"",
|
|
action="store", default=None)
|
|
parser.add_option("", "--vg", dest="useValgrind",
|
|
help="Run tests under valgrind",
|
|
action="store_true", default=False)
|
|
parser.add_option("", "--dg", dest="useDGCompat",
|
|
help="Use llvm dejagnu compatibility mode",
|
|
action="store_true", default=False)
|
|
(opts, args) = parser.parse_args()
|
|
|
|
if not args:
|
|
parser.error('No tests specified')
|
|
|
|
if opts.clang is None:
|
|
opts.clang = inferClang()
|
|
if opts.clangcc is None:
|
|
opts.clangcc = inferClangCC(opts.clang)
|
|
|
|
for path in args:
|
|
command = path
|
|
# Use hand concatentation here because we want to override
|
|
# absolute paths.
|
|
output = 'Output/' + path + '.out'
|
|
testname = path
|
|
|
|
res = runOneTest(path, command, output, testname,
|
|
opts.clang, opts.clangcc,
|
|
useValgrind=opts.useValgrind,
|
|
useDGCompat=opts.useDGCompat,
|
|
useScript=os.getenv("TEST_SCRIPT"))
|
|
|
|
sys.exit(res == TestStatus.Fail or res == TestStatus.XPass)
|
|
|
|
if __name__=='__main__':
|
|
main()
|