http://scons.tigris.org/issues/show_bug.cgi?id=2345
[scons.git] / runtest.py
index 731f36f27b32564a1909ada69801b69a53d8b544..11f87e1af2ad89779a80d20a282de814b6246040 100644 (file)
 # you can find the appropriate code in the 0.04 version of this script,
 # rather than reinventing that wheel.)
 #
+from __future__ import generators  ### KEEP FOR COMPATIBILITY FIXERS
 
 import getopt
 import glob
 import os
-import os.path
 import re
 import stat
-import string
 import sys
 import time
 
 if not hasattr(os, 'WEXITSTATUS'):
     os.WEXITSTATUS = lambda x: x
 
+try:
+    sorted
+except NameError:
+    # Pre-2.4 Python has no sorted() function.
+    #
+    # The pre-2.4 Python list.sort() method does not support
+    # list.sort(key=) nor list.sort(reverse=) keyword arguments, so
+    # we must implement the functionality of those keyword arguments
+    # by hand instead of passing them to list.sort().
+    def sorted(iterable, cmp=None, key=None, reverse=0):
+        if key is not None:
+            result = [(key(x), x) for x in iterable]
+        else:
+            result = iterable[:]
+        if cmp is None:
+            # Pre-2.3 Python does not support list.sort(None).
+            result.sort()
+        else:
+            result.sort(cmp)
+        if key is not None:
+            result = [t1 for t0,t1 in result]
+        if reverse:
+            result.reverse()
+        return result
+
 cwd = os.getcwd()
 
 all = 0
@@ -216,9 +240,9 @@ for o, a in opts:
     elif o in ['-q', '--quiet']:
         printcommand = 0
     elif o in ['--sp']:
-        sp = string.split(a, os.pathsep)
+        sp = a.split(os.pathsep)
     elif o in ['--spe']:
-        spe = string.split(a, os.pathsep)
+        spe = a.split(os.pathsep)
     elif o in ['-t', '--time']:
         print_times = 1
     elif o in ['--verbose']:
@@ -245,8 +269,8 @@ runtest.py:  No tests were specified.
 if sys.platform in ('win32', 'cygwin'):
 
     def whereis(file):
-        pathext = [''] + string.split(os.environ['PATHEXT'], os.pathsep)
-        for dir in string.split(os.environ['PATH'], os.pathsep):
+        pathext = [''] + os.environ['PATHEXT'].split(os.pathsep)
+        for dir in os.environ['PATH'].split(os.pathsep):
             f = os.path.join(dir, file)
             for ext in pathext:
                 fext = f + ext
@@ -257,7 +281,7 @@ if sys.platform in ('win32', 'cygwin'):
 else:
 
     def whereis(file):
-        for dir in string.split(os.environ['PATH'], os.pathsep):
+        for dir in os.environ['PATH'].split(os.pathsep):
             f = os.path.join(dir, file)
             if os.path.isfile(f):
                 try:
@@ -295,10 +319,10 @@ if format == '--aegis' and aegis:
     if change:
         if sp is None:
             paths = os.popen("aesub '$sp' 2>/dev/null", "r").read()[:-1]
-            sp = string.split(paths, os.pathsep)
+            sp = paths.split(os.pathsep)
         if spe is None:
             spe = os.popen("aesub '$spe' 2>/dev/null", "r").read()[:-1]
-            spe = string.split(spe, os.pathsep)
+            spe = spe.split(os.pathsep)
     else:
         aegis = None
 
@@ -316,7 +340,7 @@ _ws = re.compile('\s')
 def escape(s):
     if _ws.search(s):
         s = '"' + s + '"'
-    s = string.replace(s, '\\', '\\\\')
+    s = s.replace('\\', '\\\\')
     return s
 
 # Set up lowest-common-denominator spawning of a process on both Windows
@@ -334,7 +358,7 @@ except AttributeError:
 else:
     def spawn_it(command_args):
         command = command_args[0]
-        command_args = map(escape, command_args)
+        command_args = list(map(escape, command_args))
         return os.spawnv(os.P_WAIT, command, command_args)
 
 class Base:
@@ -373,6 +397,8 @@ except ImportError:
                 self.status = childerr.close()
                 if not self.status:
                     self.status = 0
+                else:
+                    self.status = self.status >> 8
     else:
         class PopenExecutor(Base):
             def execute(self):
@@ -381,13 +407,16 @@ except ImportError:
                 self.stdout = p.fromchild.read()
                 self.stderr = p.childerr.read()
                 self.status = p.wait()
+                self.status = self.status >> 8
 else:
     class PopenExecutor(Base):
         def execute(self):
-            p = subprocess.Popen(self.command_str, shell=True)
-            p.stdin.close()
+            p = subprocess.Popen(self.command_str,
+                                 stdout=subprocess.PIPE,
+                                 stderr=subprocess.PIPE,
+                                 shell=True)
             self.stdout = p.stdout.read()
-            self.stdout = p.stderr.read()
+            self.stderr = p.stderr.read()
             self.status = p.wait()
 
 class Aegis(SystemExecutor):
@@ -442,7 +471,7 @@ if package:
         'deb'        : os.path.join('python2.1', 'site-packages')
     }
 
-    if not dir.has_key(package):
+    if package not in dir:
         sys.stderr.write("Unknown package '%s'\n" % package)
         sys.exit(2)
 
@@ -485,7 +514,7 @@ else:
     #                sd = d
     #                scons = f
     #    spe = map(lambda x: os.path.join(x, 'src', 'engine'), spe)
-    #    ld = string.join(spe, os.pathsep)
+    #    ld = os.pathsep.join(spe)
 
     if not baseline or baseline == '.':
         base = cwd
@@ -564,9 +593,9 @@ for dir in sp:
         q = os.path.join(dir, 'QMTest')
     pythonpaths.append(q)
 
-os.environ['SCONS_SOURCE_PATH_EXECUTABLE'] = string.join(spe, os.pathsep)
+os.environ['SCONS_SOURCE_PATH_EXECUTABLE'] = os.pathsep.join(spe)
 
-os.environ['PYTHONPATH'] = string.join(pythonpaths, os.pathsep)
+os.environ['PYTHONPATH'] = os.pathsep.join(pythonpaths)
 
 if old_pythonpath:
     os.environ['PYTHONPATH'] = os.environ['PYTHONPATH'] + \
@@ -575,6 +604,25 @@ if old_pythonpath:
 
 tests = []
 
+def find_Tests_py(tdict, dirname, names):
+    for n in [n for n in names if n[-8:] == "Tests.py"]:
+        tdict[os.path.join(dirname, n)] = 1
+
+def find_py(tdict, dirname, names):
+    tests = [n for n in names if n[-3:] == ".py"]
+    try:
+        excludes = open(os.path.join(dirname,".exclude_tests")).readlines()
+    except (OSError, IOError):
+        pass
+    else:
+        for exclude in excludes:
+            exclude = exclude.split('#' , 1)[0]
+            exclude = exclude.strip()
+            if not exclude: continue
+            tests = [n for n in tests if n != exclude]
+    for n in tests:
+        tdict[os.path.join(dirname, n)] = 1
+
 if args:
     if spe:
         for a in args:
@@ -589,11 +637,20 @@ if args:
                         break
     else:
         for a in args:
-            tests.extend(glob.glob(a))
+            for path in glob.glob(a):
+                if os.path.isdir(path):
+                    tdict = {}
+                    if path[:3] == 'src':
+                        os.path.walk(path, find_Tests_py, tdict)
+                    elif path[:4] == 'test':
+                        os.path.walk(path, find_py, tdict)
+                    tests.extend(sorted(tdict.keys()))
+                else:
+                    tests.append(path)
 elif testlistfile:
     tests = open(testlistfile, 'r').readlines()
-    tests = filter(lambda x: x[0] != '#', tests)
-    tests = map(lambda x: x[:-1], tests)
+    tests = [x for x in tests if x[0] != '#']
+    tests = [x[:-1] for x in tests]
 elif all and not qmtest:
     # Find all of the SCons functional tests in the local directory
     # tree.  This is anything under the 'src' subdirectory that ends
@@ -607,45 +664,24 @@ elif all and not qmtest:
     # by the Aegis packaging build to make sure that we're building
     # things correctly.)
     tdict = {}
-
-    def find_Tests_py(tdict, dirname, names):
-        for n in filter(lambda n: n[-8:] == "Tests.py", names):
-            tdict[os.path.join(dirname, n)] = 1
     os.path.walk('src', find_Tests_py, tdict)
-
-    def find_py(tdict, dirname, names):
-        tests = filter(lambda n: n[-3:] == ".py", names)
-        try:
-            excludes = open(os.path.join(dirname,".exclude_tests")).readlines()
-        except (OSError, IOError):
-            pass
-        else:
-            for exclude in excludes:
-                exclude = string.split(exclude, '#' , 1)[0]
-                exclude = string.strip(exclude)
-                if not exclude: continue
-                tests = filter(lambda n, ex = exclude: n != ex, tests)
-        for n in tests:
-            tdict[os.path.join(dirname, n)] = 1
     os.path.walk('test', find_py, tdict)
-
     if format == '--aegis' and aegis:
         cmd = "aegis -list -unf pf 2>/dev/null"
         for line in os.popen(cmd, "r").readlines():
-            a = string.split(line)
-            if a[0] == "test" and not tdict.has_key(a[-1]):
+            a = line.split()
+            if a[0] == "test" and a[-1] not in tdict:
                 tdict[a[-1]] = Test(a[-1], spe)
         cmd = "aegis -list -unf cf 2>/dev/null"
         for line in os.popen(cmd, "r").readlines():
-            a = string.split(line)
+            a = line.split()
             if a[0] == "test":
                 if a[1] == "remove":
                     del tdict[a[-1]]
-                elif not tdict.has_key(a[-1]):
+                elif a[-1] not in tdict:
                     tdict[a[-1]] = Test(a[-1], spe)
 
-    tests = tdict.keys()
-    tests.sort()
+    tests = sorted(tdict.keys())
 
 if qmtest:
     if baseline:
@@ -686,11 +722,11 @@ if qmtest:
         qmtest_args.append(rs)
 
     if format == '--aegis':
-        tests = map(lambda x: string.replace(x, cwd+os.sep, ''), tests)
+        tests = [x.replace(cwd+os.sep, '') for x in tests]
     else:
         os.environ['SCONS'] = os.path.join(cwd, 'src', 'script', 'scons.py')
 
-    cmd = string.join(qmtest_args + tests, ' ')
+    cmd = ' '.join(qmtest_args + tests)
     if printcommand:
         sys.stdout.write(cmd + '\n')
         sys.stdout.flush()
@@ -704,7 +740,7 @@ if qmtest:
 #except OSError:
 #    pass
 
-tests = map(Test, tests)
+tests = list(map(Test, tests))
 
 class Unbuffered:
     def __init__(self, file):
@@ -716,6 +752,7 @@ class Unbuffered:
         return getattr(self.file, attr)
 
 sys.stdout = Unbuffered(sys.stdout)
+sys.stderr = Unbuffered(sys.stderr)
 
 if list_only:
     for t in tests:
@@ -745,11 +782,12 @@ else:
 
 total_start_time = time_func()
 for t in tests:
-    t.command_args = [python, '-tt']
+    command_args = ['-tt']
     if debug:
-        t.command_args.append(debug)
-    t.command_args.append(t.path)
-    t.command_str = string.join(map(escape, t.command_args), " ")
+        command_args.append(debug)
+    command_args.append(t.path)
+    t.command_args = [python] + command_args
+    t.command_str = " ".join([escape(python)] + command_args)
     if printcommand:
         sys.stdout.write(t.command_str + "\n")
     test_start_time = time_func()
@@ -761,9 +799,9 @@ if len(tests) > 0:
     tests[0].total_time = time_func() - total_start_time
     print_time_func("Total execution time for all tests: %.1f seconds\n", tests[0].total_time)
 
-passed = filter(lambda t: t.status == 0, tests)
-fail = filter(lambda t: t.status == 1, tests)
-no_result = filter(lambda t: t.status == 2, tests)
+passed = [t for t in tests if t.status == 0]
+fail = [t for t in tests if t.status == 1]
+no_result = [t for t in tests if t.status == 2]
 
 if len(tests) != 1 and execute_tests:
     if passed and print_passed_summary:
@@ -771,22 +809,22 @@ if len(tests) != 1 and execute_tests:
             sys.stdout.write("\nPassed the following test:\n")
         else:
             sys.stdout.write("\nPassed the following %d tests:\n" % len(passed))
-        paths = map(lambda x: x.path, passed)
-        sys.stdout.write("\t" + string.join(paths, "\n\t") + "\n")
+        paths = [x.path for x in passed]
+        sys.stdout.write("\t" + "\n\t".join(paths) + "\n")
     if fail:
         if len(fail) == 1:
             sys.stdout.write("\nFailed the following test:\n")
         else:
             sys.stdout.write("\nFailed the following %d tests:\n" % len(fail))
-        paths = map(lambda x: x.path, fail)
-        sys.stdout.write("\t" + string.join(paths, "\n\t") + "\n")
+        paths = [x.path for x in fail]
+        sys.stdout.write("\t" + "\n\t".join(paths) + "\n")
     if no_result:
         if len(no_result) == 1:
             sys.stdout.write("\nNO RESULT from the following test:\n")
         else:
             sys.stdout.write("\nNO RESULT from the following %d tests:\n" % len(no_result))
-        paths = map(lambda x: x.path, no_result)
-        sys.stdout.write("\t" + string.join(paths, "\n\t") + "\n")
+        paths = [x.path for x in no_result]
+        sys.stdout.write("\t" + "\n\t".join(paths) + "\n")
 
 if outputfile:
     if outputfile == '-':