]> granicus.if.org Git - clang/commitdiff
lit: Add internal script execution.
authorDaniel Dunbar <daniel@zuster.org>
Sat, 1 Aug 2009 10:18:01 +0000 (10:18 +0000)
committerDaniel Dunbar <daniel@zuster.org>
Sat, 1 Aug 2009 10:18:01 +0000 (10:18 +0000)
 - 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.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@77784 91177308-0d34-0410-b5e6-96231b3b80d8

utils/test/MultiTestRunner.py
utils/test/TestRunner.py
utils/test/TestingConfig.py

index 00cdfd49368c28ceb1cbca5b716a4e03cc41c4b4..df67b0ef17e8711a46e3560cf729608b8f52c0ac 100755 (executable)
@@ -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))
index 23038b3e2166cd7f8105fa65285058d630c8fafd..e605ef60138de3b537e73e97afdf62b56dfa2dad 100755 (executable)
@@ -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]
index 998642b47b7ca6003da52403053182962f8835b3..a43bd76560083355c5c4aca7dfa4fa645119b733 100644 (file)
@@ -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