diff options
author | rdivacky <rdivacky@FreeBSD.org> | 2010-03-06 09:22:29 +0000 |
---|---|---|
committer | rdivacky <rdivacky@FreeBSD.org> | 2010-03-06 09:22:29 +0000 |
commit | 9bef28eb9e224d641ce31a423e215ccf82bf1d43 (patch) | |
tree | 542734eaa7870f95912cbaebccb87dbec0c20b4f /utils/lit/TestRunner.py | |
parent | 8230c40430a1325b5cc5bc0221931487b4bd573c (diff) | |
download | FreeBSD-src-9bef28eb9e224d641ce31a423e215ccf82bf1d43.zip FreeBSD-src-9bef28eb9e224d641ce31a423e215ccf82bf1d43.tar.gz |
Update LLVM to r97873.
Diffstat (limited to 'utils/lit/TestRunner.py')
-rw-r--r-- | utils/lit/TestRunner.py | 517 |
1 files changed, 0 insertions, 517 deletions
diff --git a/utils/lit/TestRunner.py b/utils/lit/TestRunner.py deleted file mode 100644 index 20fbc6c..0000000 --- a/utils/lit/TestRunner.py +++ /dev/null @@ -1,517 +0,0 @@ -import os, signal, subprocess, sys -import StringIO - -import ShUtil -import Test -import Util - -import platform -import tempfile - -class InternalShellError(Exception): - def __init__(self, command, message): - self.command = command - self.message = message - -# Don't use close_fds on Windows. -kUseCloseFDs = platform.system() != 'Windows' - -# Use temporary files to replace /dev/null on Windows. -kAvoidDevNull = platform.system() == 'Windows' - -def executeCommand(command, cwd=None, env=None): - p = subprocess.Popen(command, cwd=cwd, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - env=env) - out,err = p.communicate() - exitCode = p.wait() - - # Detect Ctrl-C in subprocess. - if exitCode == -signal.SIGINT: - raise KeyboardInterrupt - - return out, err, exitCode - -def executeShCmd(cmd, cfg, cwd, results): - if isinstance(cmd, ShUtil.Seq): - if cmd.op == ';': - res = executeShCmd(cmd.lhs, cfg, cwd, results) - return executeShCmd(cmd.rhs, cfg, cwd, results) - - if cmd.op == '&': - raise NotImplementedError,"unsupported test command: '&'" - - if cmd.op == '||': - res = executeShCmd(cmd.lhs, cfg, cwd, results) - 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 - stderrTempFiles = [] - # To avoid deadlock, we use a single stderr stream for piped - # output. This is null until we have seen some output using - # stderr. - for i,j in enumerate(cmd.commands): - # Apply the redirections, we use (N,) as a sentinal to indicate stdin, - # stdout, stderr for N equal to 0, 1, or 2 respectively. Redirects to or - # from a file are represented with a list [file, mode, file-object] - # where file-object is initially None. - redirects = [(0,), (1,), (2,)] - for r in j.redirects: - if r[0] == ('>',2): - redirects[2] = [r[1], 'w', None] - elif r[0] == ('>>',2): - redirects[2] = [r[1], 'a', None] - elif r[0] == ('>&',2) and r[1] in '012': - redirects[2] = redirects[int(r[1])] - elif r[0] == ('>&',) or r[0] == ('&>',): - redirects[1] = redirects[2] = [r[1], 'w', None] - elif r[0] == ('>',): - redirects[1] = [r[1], 'w', None] - elif r[0] == ('>>',): - redirects[1] = [r[1], 'a', None] - elif r[0] == ('<',): - redirects[0] = [r[1], 'r', None] - else: - raise NotImplementedError,"Unsupported redirect: %r" % (r,) - - # Map from the final redirections to something subprocess can handle. - final_redirects = [] - for index,r in enumerate(redirects): - if r == (0,): - result = input - elif r == (1,): - if index == 0: - raise NotImplementedError,"Unsupported redirect for stdin" - elif index == 1: - result = subprocess.PIPE - else: - result = subprocess.STDOUT - elif r == (2,): - if index != 2: - raise NotImplementedError,"Unsupported redirect on stdout" - result = subprocess.PIPE - else: - if r[2] is None: - if kAvoidDevNull and r[0] == '/dev/null': - r[2] = tempfile.TemporaryFile(mode=r[1]) - else: - r[2] = open(r[0], r[1]) - # Workaround a Win32 and/or subprocess bug when appending. - if r[1] == 'a': - r[2].seek(0, 2) - result = r[2] - final_redirects.append(result) - - stdin, stdout, stderr = final_redirects - - # If stderr wants to come from stdout, but stdout isn't a pipe, then put - # stderr on a pipe and treat it as stdout. - if (stderr == subprocess.STDOUT and stdout != subprocess.PIPE): - stderr = subprocess.PIPE - stderrIsStdout = True - else: - stderrIsStdout = False - - # Don't allow stderr on a PIPE except for the last - # process, this could deadlock. - # - # FIXME: This is slow, but so is deadlock. - if stderr == subprocess.PIPE and j != cmd.commands[-1]: - stderr = tempfile.TemporaryFile(mode='w+b') - stderrTempFiles.append((i, stderr)) - - # Resolve the executable path ourselves. - args = list(j.args) - args[0] = Util.which(args[0], cfg.environment['PATH']) - if not args[0]: - raise InternalShellError(j, '%r: command not found' % j.args[0]) - - procs.append(subprocess.Popen(args, cwd=cwd, - stdin = stdin, - stdout = stdout, - stderr = stderr, - env = cfg.environment, - close_fds = kUseCloseFDs)) - - # Immediately close stdin for any process taking stdin from us. - if stdin == subprocess.PIPE: - procs[-1].stdin.close() - procs[-1].stdin = None - - # Update the current stdin source. - if stdout == subprocess.PIPE: - input = procs[-1].stdout - elif stderrIsStdout: - input = procs[-1].stderr - else: - input = subprocess.PIPE - - # FIXME: There is probably still deadlock potential here. Yawn. - 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) - - # Read stderr out of the temp files. - for i,f in stderrTempFiles: - f.seek(0, 0) - procData[i] = (procData[i][0], f.read()) - - exitCode = None - for i,(out,err) in enumerate(procData): - res = procs[i].wait() - # Detect Ctrl-C in subprocess. - if res == -signal.SIGINT: - raise KeyboardInterrupt - - results.append((cmd.commands[i], out, err, res)) - if cmd.pipe_err: - # Python treats the exit code as a signed char. - if res < 0: - exitCode = min(exitCode, res) - else: - exitCode = max(exitCode, res) - else: - exitCode = res - - if cmd.negate: - exitCode = not exitCode - - return exitCode - -def executeScriptInternal(test, litConfig, tmpBase, commands, cwd): - ln = ' &&\n'.join(commands) - try: - cmd = ShUtil.ShParser(ln, litConfig.isWindows).parse() - except: - return (Test.FAIL, "shell parser error on: %r" % ln) - - results = [] - try: - exitCode = executeShCmd(cmd, test.config, cwd, results) - except InternalShellError,e: - out = '' - err = e.message - exitCode = 255 - - 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 executeTclScriptInternal(test, litConfig, tmpBase, commands, cwd): - import TclUtil - cmds = [] - for ln in commands: - # Given the unfortunate way LLVM's test are written, the line gets - # backslash substitution done twice. - ln = TclUtil.TclLexer(ln).lex_unquoted(process_all = True) - - try: - tokens = list(TclUtil.TclLexer(ln).lex()) - except: - return (Test.FAIL, "Tcl lexer error on: %r" % ln) - - # Validate there are no control tokens. - for t in tokens: - if not isinstance(t, str): - return (Test.FAIL, - "Invalid test line: %r containing %r" % (ln, t)) - - try: - cmds.append(TclUtil.TclExecCommand(tokens).parse_pipeline()) - except: - return (Test.FAIL, "Tcl 'exec' parse error on: %r" % ln) - - cmd = cmds[0] - for c in cmds[1:]: - cmd = ShUtil.Seq(cmd, '&&', c) - - # FIXME: This is lame, we shouldn't need bash. See PR5240. - bashPath = litConfig.getBashPath() - if litConfig.useTclAsSh and bashPath: - script = tmpBase + '.script' - - # Write script file - f = open(script,'w') - print >>f, 'set -o pipefail' - cmd.toShell(f, pipefail = True) - f.close() - - if 0: - print >>sys.stdout, cmd - print >>sys.stdout, open(script).read() - print >>sys.stdout - return '', '', 0 - - command = [litConfig.getBashPath(), script] - out,err,exitCode = executeCommand(command, cwd=cwd, - env=test.config.environment) - - # Tcl commands fail on standard error output. - if err: - exitCode = 1 - out = 'Command has output on stderr!\n\n' + out - - return out,err,exitCode - else: - results = [] - try: - exitCode = executeShCmd(cmd, test.config, cwd, results) - except InternalShellError,e: - results.append((e.command, '', e.message + '\n', 255)) - exitCode = 255 - - out = err = '' - - # Tcl commands fail on standard error output. - if [True for _,_,err,res in results if err]: - exitCode = 1 - out += 'Command has output on stderr!\n\n' - - 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(test, litConfig, tmpBase, commands, cwd): - script = tmpBase + '.script' - if litConfig.isWindows: - script += '.bat' - - # Write script file - f = open(script,'w') - if litConfig.isWindows: - f.write('\nif %ERRORLEVEL% NEQ 0 EXIT\n'.join(commands)) - else: - f.write(' &&\n'.join(commands)) - f.write('\n') - f.close() - - if litConfig.isWindows: - command = ['cmd','/c', script] - else: - command = ['/bin/sh', script] - if litConfig.useValgrind: - # FIXME: Running valgrind on sh is overkill. We probably could just - # run on clang with no real loss. - valgrindArgs = ['valgrind', '-q', - '--tool=memcheck', '--trace-children=yes', - '--error-exitcode=123'] - valgrindArgs.extend(litConfig.valgrindArgs) - - command = valgrindArgs + command - - return executeCommand(command, cwd=cwd, env=test.config.environment) - -def isExpectedFail(xfails, xtargets, target_triple): - # Check if any xfail matches this target. - for item in xfails: - if item == '*' or item in target_triple: - break - else: - return False - - # If so, see if it is expected to pass on this target. - # - # FIXME: Rename XTARGET to something that makes sense, like XPASS. - for item in xtargets: - if item == '*' or item in target_triple: - return False - - return True - -def parseIntegratedTestScript(test): - """parseIntegratedTestScript - Scan an LLVM/Clang style integrated test - script and extract the lines to 'RUN' as well as 'XFAIL' and 'XTARGET' - information. The RUN lines also will have variable substitution performed. - """ - - # Get the temporary location, this is always relative to the test suite - # root, not test source root. - # - # FIXME: This should not be here? - sourcepath = test.getSourcePath() - execpath = test.getExecPath() - execdir,execbase = os.path.split(execpath) - tmpBase = os.path.join(execdir, 'Output', execbase) - if test.index is not None: - tmpBase += '_%d' % test.index - - # We use #_MARKER_# to hide %% while we do the other substitutions. - substitutions = [('%%', '#_MARKER_#')] - substitutions.extend(test.config.substitutions) - substitutions.extend([('%s', sourcepath), - ('%S', os.path.dirname(sourcepath)), - ('%p', os.path.dirname(sourcepath)), - ('%t', tmpBase + '.tmp'), - # FIXME: Remove this once we kill DejaGNU. - ('%abs_tmp', tmpBase + '.tmp'), - ('#_MARKER_#', '%')]) - - # Collect the test lines from the script. - script = [] - xfails = [] - xtargets = [] - for ln in open(sourcepath): - if 'RUN:' in ln: - # Isolate the command to run. - index = ln.index('RUN:') - ln = ln[index+4:] - - # Trim trailing whitespace. - ln = ln.rstrip() - - # Collapse lines with trailing '\\'. - if script and script[-1][-1] == '\\': - script[-1] = script[-1][:-1] + ln - else: - script.append(ln) - elif 'XFAIL:' in ln: - items = ln[ln.index('XFAIL:') + 6:].split(',') - xfails.extend([s.strip() for s in items]) - elif 'XTARGET:' in ln: - items = ln[ln.index('XTARGET:') + 8:].split(',') - xtargets.extend([s.strip() for s in items]) - elif 'END.' in ln: - # Check for END. lines. - if ln[ln.index('END.'):].strip() == 'END.': - break - - # Apply substitutions to the script. - def processLine(ln): - # Apply substitutions - for a,b in substitutions: - ln = ln.replace(a,b) - - # Strip the trailing newline and any extra whitespace. - return ln.strip() - script = map(processLine, script) - - # Verify the script contains a run line. - if not script: - return (Test.UNRESOLVED, "Test has no run line!") - - if script[-1][-1] == '\\': - return (Test.UNRESOLVED, "Test has unterminated run lines (with '\\')") - - isXFail = isExpectedFail(xfails, xtargets, test.suite.config.target_triple) - return script,isXFail,tmpBase,execdir - -def formatTestOutput(status, out, err, exitCode, script): - output = StringIO.StringIO() - print >>output, "Script:" - print >>output, "--" - print >>output, '\n'.join(script) - print >>output, "--" - print >>output, "Exit Code: %r" % exitCode - print >>output, "Command Output (stdout):" - print >>output, "--" - output.write(out) - print >>output, "--" - print >>output, "Command Output (stderr):" - print >>output, "--" - output.write(err) - print >>output, "--" - return (status, output.getvalue()) - -def executeTclTest(test, litConfig): - if test.config.unsupported: - return (Test.UNSUPPORTED, 'Test is unsupported') - - res = parseIntegratedTestScript(test) - if len(res) == 2: - return res - - script, isXFail, tmpBase, execdir = res - - if litConfig.noExecute: - return (Test.PASS, '') - - # Create the output directory if it does not already exist. - Util.mkdir_p(os.path.dirname(tmpBase)) - - res = executeTclScriptInternal(test, litConfig, tmpBase, script, execdir) - if len(res) == 2: - return res - - out,err,exitCode = res - if isXFail: - ok = exitCode != 0 - status = (Test.XPASS, Test.XFAIL)[ok] - else: - ok = exitCode == 0 - status = (Test.FAIL, Test.PASS)[ok] - - if ok: - return (status,'') - - return formatTestOutput(status, out, err, exitCode, script) - -def executeShTest(test, litConfig, useExternalSh): - if test.config.unsupported: - return (Test.UNSUPPORTED, 'Test is unsupported') - - res = parseIntegratedTestScript(test) - if len(res) == 2: - return res - - script, isXFail, tmpBase, execdir = res - - if litConfig.noExecute: - return (Test.PASS, '') - - # Create the output directory if it does not already exist. - Util.mkdir_p(os.path.dirname(tmpBase)) - - if useExternalSh: - res = executeScript(test, litConfig, tmpBase, script, execdir) - else: - res = executeScriptInternal(test, litConfig, tmpBase, script, execdir) - if len(res) == 2: - return res - - out,err,exitCode = res - if isXFail: - ok = exitCode != 0 - status = (Test.XPASS, Test.XFAIL)[ok] - else: - ok = exitCode == 0 - status = (Test.FAIL, Test.PASS)[ok] - - if ok: - return (status,'') - - return formatTestOutput(status, out, err, exitCode, script) |