forked from OSchip/llvm-project
lit: Add internal script execution.
- Off by default, you can test it with the --no-sh argument. - For me it works for all but 3 tests, but there a number of FIXMEs and QOI issues: o Redirection isn't completely accurate -- in practice it can't portably be, but I would like to error out if someone writes something which isn't going to work. This is the source of the 3 test failures. o Some pipe configurations have the potential to deadlock. o It is significantly slower when multithreaded. I believe this is due to locking happening under the hood, there is probably some kind of solution but I haven't investigated yet. o Log output is ugly. llvm-svn: 77784
This commit is contained in:
parent
82fa6f5e9d
commit
590c358681
|
@ -249,6 +249,9 @@ def main():
|
|||
group.add_option("", "--path", dest="path",
|
||||
help="Additional paths to add to testing environment",
|
||||
action="append", type=str, default=[])
|
||||
group.add_option("", "--no-sh", dest="useExternalShell",
|
||||
help="Run tests using an external shell",
|
||||
action="store_false", default=True)
|
||||
group.add_option("", "--vg", dest="useValgrind",
|
||||
help="Run tests under valgrind",
|
||||
action="store_true", default=False)
|
||||
|
@ -314,6 +317,7 @@ def main():
|
|||
cfg.clang = opts.clang
|
||||
cfg.clangcc = opts.clangcc
|
||||
cfg.useValgrind = opts.useValgrind
|
||||
cfg.useExternalShell = opts.useExternalShell
|
||||
|
||||
# FIXME: It could be worth loading these in parallel with testing.
|
||||
allTests = list(getTests(cfg, args))
|
||||
|
|
|
@ -5,6 +5,7 @@ import signal
|
|||
import subprocess
|
||||
import sys
|
||||
|
||||
import ShUtil
|
||||
import Util
|
||||
|
||||
kSystemName = platform.system()
|
||||
|
@ -21,6 +22,117 @@ class TestStatus:
|
|||
def getName(code):
|
||||
return TestStatus.kNames[code]
|
||||
|
||||
def executeShCmd(cmd, cfg, cwd, results):
|
||||
if isinstance(cmd, ShUtil.Seq):
|
||||
if cmd.op == ';':
|
||||
res = executeShCmd(cmd.lhs, cfg, cwd, results)
|
||||
if res is None:
|
||||
return res
|
||||
|
||||
return executeShCmd(cmd.rhs, cfg, cwd, results)
|
||||
|
||||
if cmd.op == '&':
|
||||
Util.warning("unsupported test command: '&'")
|
||||
return None
|
||||
|
||||
if cmd.op == '||':
|
||||
res = executeShCmd(cmd.lhs, cfg, cwd, results)
|
||||
if res is None:
|
||||
return res
|
||||
|
||||
if res != 0:
|
||||
res = executeShCmd(cmd.rhs, cfg, cwd, results)
|
||||
return res
|
||||
if cmd.op == '&&':
|
||||
res = executeShCmd(cmd.lhs, cfg, cwd, results)
|
||||
if res is None:
|
||||
return res
|
||||
|
||||
if res == 0:
|
||||
res = executeShCmd(cmd.rhs, cfg, cwd, results)
|
||||
return res
|
||||
|
||||
raise ValueError,'Unknown shell command: %r' % cmd.op
|
||||
|
||||
assert isinstance(cmd, ShUtil.Pipeline)
|
||||
procs = []
|
||||
input = subprocess.PIPE
|
||||
for j in cmd.commands:
|
||||
# FIXME: This is broken, it doesn't account for the accumulative nature
|
||||
# of redirects.
|
||||
stdin = input
|
||||
stdout = stderr = subprocess.PIPE
|
||||
for r in j.redirects:
|
||||
if r[0] == ('>',2):
|
||||
stderr = open(r[1], 'w')
|
||||
elif r[0] == ('>&',2) and r[1] == '1':
|
||||
stderr = subprocess.STDOUT
|
||||
elif r[0] == ('>',):
|
||||
stdout = open(r[1], 'w')
|
||||
elif r[0] == ('<',):
|
||||
stdin = open(r[1], 'r')
|
||||
else:
|
||||
return None
|
||||
|
||||
procs.append(subprocess.Popen(j.args, cwd=cwd,
|
||||
stdin = stdin,
|
||||
stdout = stdout,
|
||||
stderr = stderr,
|
||||
env = cfg.environment))
|
||||
|
||||
# Immediately close stdin for any process taking stdin from us.
|
||||
if stdin == subprocess.PIPE:
|
||||
procs[-1].stdin.close()
|
||||
procs[-1].stdin = None
|
||||
|
||||
if stdout == subprocess.PIPE:
|
||||
input = procs[-1].stdout
|
||||
else:
|
||||
input = subprocess.PIPE
|
||||
|
||||
# FIXME: There is a potential for deadlock here, when we have a pipe and
|
||||
# some process other than the last one ends up blocked on stderr.
|
||||
procData = [None] * len(procs)
|
||||
procData[-1] = procs[-1].communicate()
|
||||
for i in range(len(procs) - 1):
|
||||
if procs[i].stdout is not None:
|
||||
out = procs[i].stdout.read()
|
||||
else:
|
||||
out = ''
|
||||
if procs[i].stderr is not None:
|
||||
err = procs[i].stderr.read()
|
||||
else:
|
||||
err = ''
|
||||
procData[i] = (out,err)
|
||||
|
||||
# FIXME: Fix tests to work with pipefail, and make exitCode max across
|
||||
# procs.
|
||||
for i,(out,err) in enumerate(procData):
|
||||
exitCode = res = procs[i].wait()
|
||||
results.append((cmd.commands[i], out, err, res))
|
||||
|
||||
if cmd.negate:
|
||||
exitCode = not exitCode
|
||||
|
||||
return exitCode
|
||||
|
||||
def executeScriptInternal(cfg, commands, cwd):
|
||||
cmd = ShUtil.ShParser(' &&\n'.join(commands)).parse()
|
||||
|
||||
results = []
|
||||
exitCode = executeShCmd(cmd, cfg, cwd, results)
|
||||
if exitCode is None:
|
||||
return None
|
||||
|
||||
out = err = ''
|
||||
for i,(cmd, cmd_out,cmd_err,res) in enumerate(results):
|
||||
out += 'Command %d: %s\n' % (i, ' '.join('"%s"' % s for s in cmd.args))
|
||||
out += 'Command %d Result: %r\n' % (i, res)
|
||||
out += 'Command %d Output:\n%s\n\n' % (i, cmd_out)
|
||||
out += 'Command %d Stderr:\n%s\n\n' % (i, cmd_err)
|
||||
|
||||
return out, err, exitCode
|
||||
|
||||
def executeScript(cfg, script, commands, cwd):
|
||||
# Write script file
|
||||
f = open(script,'w')
|
||||
|
@ -50,10 +162,6 @@ def executeScript(cfg, script, commands, cwd):
|
|||
out,err = p.communicate()
|
||||
exitCode = p.wait()
|
||||
|
||||
# Detect Ctrl-C in subprocess.
|
||||
if exitCode == -signal.SIGINT:
|
||||
raise KeyboardInterrupt
|
||||
|
||||
return out, err, exitCode
|
||||
|
||||
import StringIO
|
||||
|
@ -118,8 +226,26 @@ def runOneTest(cfg, testPath, tmpBase):
|
|||
# Strip off '&&'
|
||||
scriptLines[i] = ln[:-2]
|
||||
|
||||
out, err, exitCode = executeScript(cfg, script, scriptLines,
|
||||
os.path.dirname(testPath))
|
||||
if not cfg.useExternalShell:
|
||||
res = executeScriptInternal(cfg, scriptLines, os.path.dirname(testPath))
|
||||
|
||||
if res is not None:
|
||||
out, err, exitCode = res
|
||||
elif True:
|
||||
return (TestStatus.Fail,
|
||||
"Unable to execute internally:\n%s\n"
|
||||
% '\n'.join(scriptLines))
|
||||
else:
|
||||
out, err, exitCode = executeScript(cfg, script, scriptLines,
|
||||
os.path.dirname(testPath))
|
||||
else:
|
||||
out, err, exitCode = executeScript(cfg, script, scriptLines,
|
||||
os.path.dirname(testPath))
|
||||
|
||||
# Detect Ctrl-C in subprocess.
|
||||
if exitCode == -signal.SIGINT:
|
||||
raise KeyboardInterrupt
|
||||
|
||||
if xfailLines:
|
||||
ok = exitCode != 0
|
||||
status = (TestStatus.XPass, TestStatus.XFail)[ok]
|
||||
|
|
|
@ -19,6 +19,7 @@ class TestingConfig:
|
|||
# Variables set internally.
|
||||
self.root = None
|
||||
self.useValgrind = None
|
||||
self.useExternalShell = None
|
||||
|
||||
# FIXME: These need to move into a substitutions mechanism.
|
||||
self.clang = None
|
||||
|
|
Loading…
Reference in New Issue