[cfe-commits] r77784 - in /cfe/trunk/utils/test: MultiTestRunner.py TestRunner.py TestingConfig.py

Daniel Dunbar daniel at zuster.org
Sat Aug 1 03:18:05 PDT 2009


Author: ddunbar
Date: Sat Aug  1 05:18:01 2009
New Revision: 77784

URL: http://llvm.org/viewvc/llvm-project?rev=77784&view=rev
Log:
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.

Modified:
    cfe/trunk/utils/test/MultiTestRunner.py
    cfe/trunk/utils/test/TestRunner.py
    cfe/trunk/utils/test/TestingConfig.py

Modified: cfe/trunk/utils/test/MultiTestRunner.py
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/utils/test/MultiTestRunner.py?rev=77784&r1=77783&r2=77784&view=diff

==============================================================================
--- cfe/trunk/utils/test/MultiTestRunner.py (original)
+++ cfe/trunk/utils/test/MultiTestRunner.py Sat Aug  1 05:18:01 2009
@@ -249,6 +249,9 @@
     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 @@
     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))

Modified: cfe/trunk/utils/test/TestRunner.py
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/utils/test/TestRunner.py?rev=77784&r1=77783&r2=77784&view=diff

==============================================================================
--- cfe/trunk/utils/test/TestRunner.py (original)
+++ cfe/trunk/utils/test/TestRunner.py Sat Aug  1 05:18:01 2009
@@ -5,6 +5,7 @@
 import subprocess
 import sys
 
+import ShUtil
 import Util
 
 kSystemName = platform.system()
@@ -21,6 +22,117 @@
     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 @@
     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 @@
         # 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]

Modified: cfe/trunk/utils/test/TestingConfig.py
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/utils/test/TestingConfig.py?rev=77784&r1=77783&r2=77784&view=diff

==============================================================================
--- cfe/trunk/utils/test/TestingConfig.py (original)
+++ cfe/trunk/utils/test/TestingConfig.py Sat Aug  1 05:18:01 2009
@@ -19,6 +19,7 @@
         # Variables set internally.
         self.root = None
         self.useValgrind = None
+        self.useExternalShell = None
 
         # FIXME: These need to move into a substitutions mechanism.
         self.clang = None





More information about the cfe-commits mailing list