#!/usr/bin/env python
#
+# __COPYRIGHT__
+#
# runtest.py - wrapper script for running SCons tests
#
# This script mainly exists to set PYTHONPATH to the right list of
# directories to test the SCons modules.
#
# By default, it directly uses the modules in the local tree:
-# ./src/ (source files we ship) and ./etc/ (other modules we don't)
+# ./src/ (source files we ship) and ./QMTest/ (other modules we don't).
+#
+# HOWEVER, now that SCons has Repository support, we don't have
+# Aegis copy all of the files into the local tree. So if you're
+# using Aegis and want to run tests by hand using this script, you
+# must "aecp ." the entire source tree into your local directory
+# structure. When you're done with your change, you can then
+# "aecpu -unch ." to un-copy any files that you haven't changed.
#
-# When any -p option is specified, it assumes it's in a directory
-# in which a build has been performed, and sets PYTHONPATH so that it
-# *only* references the modules that have unpacked from the specified
-# built package, to test whether the packages are good.
+# When any -p option is specified, this script assumes it's in a
+# directory in which a build has been performed, and sets PYTHONPATH
+# so that it *only* references the modules that have unpacked from
+# the specified built package, to test whether the packages are good.
#
# Options:
#
-# -a Run all tests; does a virtual 'find' for
-# all SCons tests under the current directory.
+# -a Run all tests; does a virtual 'find' for
+# all SCons tests under the current directory.
+#
+# --aegis Print test results to an output file (specified
+# by the -o option) in the format expected by
+# aetest(5). This is intended for use in the
+# batch_test_command field in the Aegis project
+# config file.
+#
+# -d Debug. Runs the script under the Python
+# debugger (pdb.py) so you don't have to
+# muck with PYTHONPATH yourself.
#
-# -d Debug. Runs the script under the Python
-# debugger (pdb.py) so you don't have to
-# muck with PYTHONPATH yourself.
+# -f file Only execute the tests listed in the specified
+# file.
#
# -h Print the help and exit.
#
-# -o file Print test results to the specified file
-# in the format expected by aetest(5). This
-# is intended for use in the batch_test_command
-# field in the Aegis project config file.
+# -l List available tests and exit.
+#
+# -n No execute, just print command lines.
+#
+# -o file Print test results to the specified file.
+# The --aegis and --xml options specify the
+# output format.
+#
+# -P Python Use the specified Python interpreter.
#
-# -p package Test against the specified package.
+# -p package Test against the specified package.
#
-# -q Quiet. By default, runtest.py prints the
-# command line it will execute before
-# executing it. This suppresses that print.
+# --passed In the final summary, also report which tests
+# passed. The default is to only report tests
+# which failed or returned NO RESULT.
#
-# -X The scons "script" is an executable; don't
-# feed it to Python.
+# -q Quiet. By default, runtest.py prints the
+# command line it will execute before
+# executing it. This suppresses that print.
+#
+# --sp The Aegis search path.
+#
+# --spe The Aegis executable search path.
+#
+# -t Print the execution time of each test.
+#
+# -X The scons "script" is an executable; don't
+# feed it to Python.
#
# -x scons The scons script to use for tests.
#
+# --xml Print test results to an output file (specified
+# by the -o option) in an SCons-specific XML format.
+# This is (will be) used for reporting results back
+# to a central SCons test monitoring infrastructure.
+#
# (Note: There used to be a -v option that specified the SCons
# version to be tested, when we were installing in a version-specific
# library directory. If we ever resurrect that as the default, then
# 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 string
+import stat
import sys
+import time
+
+if not hasattr(os, 'WEXITSTATUS'):
+ os.WEXITSTATUS = lambda x: x
+
+cwd = os.getcwd()
all = 0
+baseline = 0
+builddir = os.path.join(cwd, 'build')
debug = ''
-tests = []
-printcmd = 1
+execute_tests = 1
+format = None
+list_only = None
+printcommand = 1
package = None
+print_passed_summary = None
scons = None
scons_exec = None
-output = None
-
-cwd = os.getcwd()
-
-if sys.platform == 'win32':
- lib_dir = os.path.join(sys.exec_prefix, "lib")
-else:
- # The hard-coded "python" here is the directory name,
- # not an executable, so it's all right.
- lib_dir = os.path.join(sys.exec_prefix, "lib", "python" + sys.version[0:3])
+outputfile = None
+testlistfile = None
+version = ''
+print_times = None
+python = None
+sp = None
+spe = None
helpstr = """\
Usage: runtest.py [OPTIONS] [TEST ...]
Options:
-a, --all Run all tests.
+ --aegis Print results in Aegis format.
+ -b BASE, --baseline BASE Run test scripts against baseline BASE.
+ --builddir DIR Directory in which packages were built.
-d, --debug Run test scripts under the Python debugger.
+ -f FILE, --file FILE Run tests in specified FILE.
-h, --help Print this message and exit.
- -o FILE, --output FILE Print test results to FILE (Aegis format).
+ -l, --list List available tests and exit.
+ -n, --no-exec No execute, just print command lines.
+ --noqmtest Execute tests directly, not using QMTest.
+ -o FILE, --output FILE Print test results to FILE.
+ -P Python Use the specified Python interpreter.
-p PACKAGE, --package PACKAGE
Test against the specified PACKAGE:
- deb Debian
- rpm Red Hat
- src-tar-gz .tar.gz source package
- src-zip .zip source package
- tar-gz .tar.gz distribution
- zip .zip distribution
+ deb Debian
+ local-tar-gz .tar.gz standalone package
+ local-zip .zip standalone package
+ rpm Red Hat
+ src-tar-gz .tar.gz source package
+ src-zip .zip source package
+ tar-gz .tar.gz distribution
+ zip .zip distribution
+ --passed Summarize which tests passed.
+ --qmtest Run using the QMTest harness.
-q, --quiet Don't print the test being executed.
+ --sp PATH The Aegis search path.
+ --spe PATH The Aegis executable search path.
+ -t, --time Print test execution time.
+ -v version Specify the SCons version.
+ --verbose=LEVEL Set verbose level: 1 = print executed commands,
+ 2 = print commands and non-zero output,
+ 3 = print commands and all output.
-X Test script is executable, don't feed to Python.
-x SCRIPT, --exec SCRIPT Test SCRIPT.
+ --xml Print results in SCons XML format.
"""
-opts, args = getopt.getopt(sys.argv[1:], "adho:p:qXx:",
- ['all', 'debug', 'help', 'output=',
- 'package=', 'quiet', 'exec='])
+opts, args = getopt.getopt(sys.argv[1:], "ab:df:hlno:P:p:qv:Xx:t",
+ ['all', 'aegis', 'baseline=', 'builddir=',
+ 'debug', 'file=', 'help',
+ 'list', 'no-exec', 'noqmtest', 'output=',
+ 'package=', 'passed', 'python=', 'qmtest',
+ 'quiet', 'sp=', 'spe=', 'time',
+ 'version=', 'exec=',
+ 'verbose=', 'xml'])
for o, a in opts:
- if o == '-a' or o == '--all':
+ if o in ['-a', '--all']:
all = 1
- elif o == '-d' or o == '--debug':
- debug = os.path.join(lib_dir, "pdb.py")
- elif o == '-h' or o == '--help':
+ elif o in ['-b', '--baseline']:
+ baseline = a
+ elif o in ['--builddir']:
+ builddir = a
+ if not os.path.isabs(builddir):
+ builddir = os.path.normpath(os.path.join(cwd, builddir))
+ elif o in ['-d', '--debug']:
+ for dir in sys.path:
+ pdb = os.path.join(dir, 'pdb.py')
+ if os.path.exists(pdb):
+ debug = pdb
+ break
+ elif o in ['-f', '--file']:
+ if not os.path.isabs(a):
+ a = os.path.join(cwd, a)
+ testlistfile = a
+ elif o in ['-h', '--help']:
print helpstr
sys.exit(0)
- elif o == '-o' or o == '--output':
- if not os.path.isabs(a):
+ elif o in ['-l', '--list']:
+ list_only = 1
+ elif o in ['-n', '--no-exec']:
+ execute_tests = None
+ elif o in ['--noqmtest']:
+ qmtest = None
+ elif o in ['-o', '--output']:
+ if a != '-' and not os.path.isabs(a):
a = os.path.join(cwd, a)
- output = a
- elif o == '-p' or o == '--package':
+ outputfile = a
+ elif o in ['-p', '--package']:
package = a
- elif o == '-q' or o == '--quiet':
- printcmd = 0
- elif o == '-X':
+ elif o in ['--passed']:
+ print_passed_summary = 1
+ elif o in ['-P', '--python']:
+ python = a
+ elif o in ['--qmtest']:
+ if sys.platform == 'win32':
+ # typically in c:/PythonXX/Scripts
+ qmtest = 'qmtest.py'
+ else:
+ qmtest = 'qmtest'
+ elif o in ['-q', '--quiet']:
+ printcommand = 0
+ elif o in ['--sp']:
+ sp = a.split(os.pathsep)
+ elif o in ['--spe']:
+ spe = a.split(os.pathsep)
+ elif o in ['-t', '--time']:
+ print_times = 1
+ elif o in ['--verbose']:
+ os.environ['TESTCMD_VERBOSE'] = a
+ elif o in ['-v', '--version']:
+ version = a
+ elif o in ['-X']:
scons_exec = 1
- elif o == '-x' or o == '--exec':
+ elif o in ['-x', '--exec']:
scons = a
+ elif o in ['--aegis', '--xml']:
+ format = o
+
+if not args and not all and not testlistfile:
+ sys.stderr.write("""\
+runtest.py: No tests were specified.
+ List one or more tests on the command line, use the
+ -f option to specify a file containing a list of tests,
+ or use the -a option to find and run all tests.
+
+""")
+ sys.exit(1)
+
+if sys.platform in ('win32', 'cygwin'):
-class Test:
- def __init__(self, path):
+ def whereis(file):
+ 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
+ if os.path.isfile(fext):
+ return fext
+ return None
+
+else:
+
+ def whereis(file):
+ for dir in os.environ['PATH'].split(os.pathsep):
+ f = os.path.join(dir, file)
+ if os.path.isfile(f):
+ try:
+ st = os.stat(f)
+ except OSError:
+ continue
+ if stat.S_IMODE(st[stat.ST_MODE]) & 0111:
+ return f
+ return None
+
+# See if --qmtest or --noqmtest specified
+try:
+ qmtest
+except NameError:
+ qmtest = None
+ # Old code for using QMTest by default if it's installed.
+ # We now default to not using QMTest unless explicitly asked for.
+ #for q in ['qmtest', 'qmtest.py']:
+ # path = whereis(q)
+ # if path:
+ # # The name was found on $PATH; just execute the found name so
+ # # we don't have to worry about paths containing white space.
+ # qmtest = q
+ # break
+ #if not qmtest:
+ # msg = ('Warning: found neither qmtest nor qmtest.py on $PATH;\n' +
+ # '\tassuming --noqmtest option.\n')
+ # sys.stderr.write(msg)
+ # sys.stderr.flush()
+
+aegis = whereis('aegis')
+
+if format == '--aegis' and aegis:
+ change = os.popen("aesub '$c' 2>/dev/null", "r").read()
+ if change:
+ if sp is None:
+ paths = os.popen("aesub '$sp' 2>/dev/null", "r").read()[:-1]
+ sp = paths.split(os.pathsep)
+ if spe is None:
+ spe = os.popen("aesub '$spe' 2>/dev/null", "r").read()[:-1]
+ spe = spe.split(os.pathsep)
+ else:
+ aegis = None
+
+if sp is None:
+ sp = []
+if spe is None:
+ spe = []
+
+sp.append(builddir)
+sp.append(cwd)
+
+#
+_ws = re.compile('\s')
+
+def escape(s):
+ if _ws.search(s):
+ s = '"' + s + '"'
+ s = s.replace('\\', '\\\\')
+ return s
+
+# Set up lowest-common-denominator spawning of a process on both Windows
+# and non-Windows systems that works all the way back to Python 1.5.2.
+try:
+ os.spawnv
+except AttributeError:
+ def spawn_it(command_args):
+ pid = os.fork()
+ if pid == 0:
+ os.execv(command_args[0], command_args)
+ else:
+ pid, status = os.waitpid(pid, 0)
+ return status >> 8
+else:
+ def spawn_it(command_args):
+ command = command_args[0]
+ command_args = list(map(escape, command_args))
+ return os.spawnv(os.P_WAIT, command, command_args)
+
+class Base:
+ def __init__(self, path, spe=None):
self.path = path
self.abspath = os.path.abspath(path)
+ if spe:
+ for dir in spe:
+ f = os.path.join(dir, path)
+ if os.path.isfile(f):
+ self.abspath = f
+ break
self.status = None
-if args:
- for a in args:
- for g in glob.glob(a):
- tests.append(Test(g))
-elif all:
- def find_Test_py(arg, dirname, names, t=tests):
- for n in filter(lambda n: n[-8:] == "Tests.py", names):
- t.append(Test(os.path.join(dirname, n)))
- os.path.walk('src', find_Test_py, 0)
-
- def find_py(arg, dirname, names, t=tests):
- for n in filter(lambda n: n[-3:] == ".py", names):
- t.append(Test(os.path.join(dirname, n)))
- os.path.walk('test', find_py, 0)
-
- tests.sort(lambda a, b: cmp(a.path, b.path))
+class SystemExecutor(Base):
+ def execute(self):
+ s = spawn_it(self.command_args)
+ self.status = s
+ if s < 0 or s > 2:
+ sys.stdout.write("Unexpected exit status %d\n" % s)
+
+try:
+ import subprocess
+except ImportError:
+ import popen2
+ try:
+ popen2.Popen3
+ except AttributeError:
+ class PopenExecutor(Base):
+ def execute(self):
+ (tochild, fromchild, childerr) = os.popen3(self.command_str)
+ tochild.close()
+ self.stderr = childerr.read()
+ self.stdout = fromchild.read()
+ fromchild.close()
+ self.status = childerr.close()
+ if not self.status:
+ self.status = 0
+ else:
+ self.status = self.status >> 8
+ else:
+ class PopenExecutor(Base):
+ def execute(self):
+ p = popen2.Popen3(self.command_str, 1)
+ p.tochild.close()
+ 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,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ shell=True)
+ self.stdout = p.stdout.read()
+ self.stderr = p.stderr.read()
+ self.status = p.wait()
+
+class Aegis(SystemExecutor):
+ def header(self, f):
+ f.write('test_result = [\n')
+ def write(self, f):
+ f.write(' { file_name = "%s";\n' % self.path)
+ f.write(' exit_status = %d; },\n' % self.status)
+ def footer(self, f):
+ f.write('];\n')
+
+class XML(PopenExecutor):
+ def header(self, f):
+ f.write(' <results>\n')
+ def write(self, f):
+ f.write(' <test>\n')
+ f.write(' <file_name>%s</file_name>\n' % self.path)
+ f.write(' <command_line>%s</command_line>\n' % self.command_str)
+ f.write(' <exit_status>%s</exit_status>\n' % self.status)
+ f.write(' <stdout>%s</stdout>\n' % self.stdout)
+ f.write(' <stderr>%s</stderr>\n' % self.stderr)
+ f.write(' <time>%.1f</time>\n' % self.test_time)
+ f.write(' </test>\n')
+ def footer(self, f):
+ f.write(' <time>%.1f</time>\n' % self.total_time)
+ f.write(' </results>\n')
+
+format_class = {
+ None : SystemExecutor,
+ '--aegis' : Aegis,
+ '--xml' : XML,
+}
+
+Test = format_class[format]
if package:
dir = {
- 'deb' : 'usr',
- 'rpm' : 'usr',
- 'src-tar-gz' : '',
- 'src-zip' : '',
- 'tar-gz' : '',
- 'zip' : '',
+ 'deb' : 'usr',
+ 'local-tar-gz' : None,
+ 'local-zip' : None,
+ 'rpm' : 'usr',
+ 'src-tar-gz' : '',
+ 'src-zip' : '',
+ 'tar-gz' : '',
+ 'zip' : '',
}
# The hard-coded "python2.1" here is the library directory
'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)
- test_dir = os.path.join(cwd, 'build', 'test-%s' % package)
+ test_dir = os.path.join(builddir, 'test-%s' % package)
- if sys.platform == 'win32':
- scons_dir = os.path.join(test_dir, dir[package], 'Scripts')
- lib_dir = os.path.join(test_dir, dir[package])
+ if dir[package] is None:
+ scons_script_dir = test_dir
+ globs = glob.glob(os.path.join(test_dir, 'scons-local-*'))
+ if not globs:
+ sys.stderr.write("No `scons-local-*' dir in `%s'\n" % test_dir)
+ sys.exit(2)
+ scons_lib_dir = None
+ pythonpath_dir = globs[len(globs)-1]
+ elif sys.platform == 'win32':
+ scons_script_dir = os.path.join(test_dir, dir[package], 'Scripts')
+ scons_lib_dir = os.path.join(test_dir, dir[package])
+ pythonpath_dir = scons_lib_dir
else:
- scons_dir = os.path.join(test_dir, dir[package], 'bin')
+ scons_script_dir = os.path.join(test_dir, dir[package], 'bin')
l = lib.get(package, 'scons')
- lib_dir = os.path.join(test_dir, dir[package], 'lib', l)
+ scons_lib_dir = os.path.join(test_dir, dir[package], 'lib', l)
+ pythonpath_dir = scons_lib_dir
+
+ scons_runtest_dir = builddir
else:
+ sd = None
+ ld = None
+
+ # XXX: Logic like the following will be necessary once
+ # we fix runtest.py to run tests within an Aegis change
+ # without symlinks back to the baseline(s).
+ #
+ #if spe:
+ # if not scons:
+ # for dir in spe:
+ # d = os.path.join(dir, 'src', 'script')
+ # f = os.path.join(d, 'scons.py')
+ # if os.path.isfile(f):
+ # sd = d
+ # scons = f
+ # spe = map(lambda x: os.path.join(x, 'src', 'engine'), spe)
+ # ld = os.pathsep.join(spe)
+
+ if not baseline or baseline == '.':
+ base = cwd
+ elif baseline == '-':
+ # Tentative code for fetching information directly from the
+ # QMTest context file.
+ #
+ #import qm.common
+ #import qm.test.context
+ #qm.rc.Load("test")
+ #context = qm.test.context.Context()
+ #context.Read('context')
- scons_dir = os.path.join(cwd, 'src', 'script')
+ url = None
+ svn_info = os.popen("svn info 2>&1", "r").read()
+ match = re.search('URL: (.*)', svn_info)
+ if match:
+ url = match.group(1)
+ if not url:
+ sys.stderr.write('runtest.py: could not find a URL:\n')
+ sys.stderr.write(svn_info)
+ sys.exit(1)
+ import tempfile
+ base = tempfile.mkdtemp(prefix='runtest-tmp-')
- lib_dir = os.path.join(cwd, 'src', 'engine')
+ command = 'cd %s && svn co -q %s' % (base, url)
+
+ base = os.path.join(base, os.path.split(url)[1])
+ if printcommand:
+ print command
+ if execute_tests:
+ os.system(command)
+ else:
+ base = baseline
+
+ scons_runtest_dir = base
+
+ scons_script_dir = sd or os.path.join(base, 'src', 'script')
+
+ scons_lib_dir = ld or os.path.join(base, 'src', 'engine')
+
+ pythonpath_dir = scons_lib_dir
if scons:
# Let the version of SCons that the -x option pointed to find
# its own modules.
os.environ['SCONS'] = scons
-else:
+elif scons_lib_dir:
# Because SCons is really aggressive about finding its modules,
# it sometimes finds SCons modules elsewhere on the system.
# This forces SCons to use the modules that are being tested.
- os.environ['SCONS_LIB_DIR'] = lib_dir
+ os.environ['SCONS_LIB_DIR'] = scons_lib_dir
if scons_exec:
os.environ['SCONS_EXEC'] = '1'
-os.environ['PYTHONPATH'] = lib_dir + \
- os.pathsep + \
- os.path.join(cwd, 'build', 'etc') + \
- os.pathsep + \
- os.path.join(cwd, 'etc')
+os.environ['SCONS_RUNTEST_DIR'] = scons_runtest_dir
+os.environ['SCONS_SCRIPT_DIR'] = scons_script_dir
+os.environ['SCONS_CWD'] = cwd
+
+os.environ['SCONS_VERSION'] = version
+
+old_pythonpath = os.environ.get('PYTHONPATH')
+
+# FIXME: the following is necessary to pull in half of the testing
+# harness from $srcdir/etc. Those modules should be transfered
+# to QMTest/ once we completely cut over to using that as
+# the harness, in which case this manipulation of PYTHONPATH
+# should be able to go away.
+pythonpaths = [ pythonpath_dir ]
+
+for dir in sp:
+ if format == '--aegis':
+ q = os.path.join(dir, 'build', 'QMTest')
+ else:
+ q = os.path.join(dir, 'QMTest')
+ pythonpaths.append(q)
+
+os.environ['SCONS_SOURCE_PATH_EXECUTABLE'] = os.pathsep.join(spe)
+
+os.environ['PYTHONPATH'] = os.pathsep.join(pythonpaths)
+
+if old_pythonpath:
+ os.environ['PYTHONPATH'] = os.environ['PYTHONPATH'] + \
+ os.pathsep + \
+ 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:
+ if os.path.isabs(a):
+ tests.extend(glob.glob(a))
+ else:
+ for dir in spe:
+ x = os.path.join(dir, a)
+ globs = glob.glob(x)
+ if globs:
+ tests.extend(globs)
+ break
+ else:
+ for a in args:
+ 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)
+ t = tdict.keys()
+ t.sort()
+ tests.extend(t)
+ else:
+ tests.append(path)
+elif testlistfile:
+ tests = open(testlistfile, 'r').readlines()
+ 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
+ # with 'Tests.py', or any Python script (*.py) under the 'test'
+ # subdirectory.
+ #
+ # Note that there are some tests under 'src' that *begin* with
+ # 'test_', but they're packaging and installation tests, not
+ # functional tests, so we don't execute them by default. (They can
+ # still be executed by hand, though, and are routinely executed
+ # by the Aegis packaging build to make sure that we're building
+ # things correctly.)
+ tdict = {}
+ os.path.walk('src', find_Tests_py, tdict)
+ 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 = 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 = line.split()
+ if a[0] == "test":
+ if a[1] == "remove":
+ del tdict[a[-1]]
+ elif a[-1] not in tdict:
+ tdict[a[-1]] = Test(a[-1], spe)
+
+ tests = tdict.keys()
+ tests.sort()
+
+if qmtest:
+ if baseline:
+ aegis_result_stream = 'scons_tdb.AegisBaselineStream'
+ qmr_file = 'baseline.qmr'
+ else:
+ aegis_result_stream = 'scons_tdb.AegisChangeStream'
+ qmr_file = 'results.qmr'
+
+ if print_times:
+ aegis_result_stream = aegis_result_stream + "(print_time='1')"
-os.chdir(scons_dir)
+ qmtest_args = [ qmtest, ]
+
+ if format == '--aegis':
+ dir = builddir
+ if not os.path.isdir(dir):
+ dir = cwd
+ qmtest_args.extend(['-D', dir])
+
+ qmtest_args.extend([
+ 'run',
+ '--output %s' % qmr_file,
+ '--format none',
+ '--result-stream="%s"' % aegis_result_stream,
+ ])
+
+ if python:
+ qmtest_args.append('--context python="%s"' % python)
+
+ if outputfile:
+ if format == '--xml':
+ rsclass = 'scons_tdb.SConsXMLResultStream'
+ else:
+ rsclass = 'scons_tdb.AegisBatchStream'
+ qof = "r'" + outputfile + "'"
+ rs = '--result-stream="%s(filename=%s)"' % (rsclass, qof)
+ qmtest_args.append(rs)
+
+ if format == '--aegis':
+ tests = [x.replace(cwd+os.sep, '') for x in tests]
+ else:
+ os.environ['SCONS'] = os.path.join(cwd, 'src', 'script', 'scons.py')
+
+ cmd = ' '.join(qmtest_args + tests)
+ if printcommand:
+ sys.stdout.write(cmd + '\n')
+ sys.stdout.flush()
+ status = 0
+ if execute_tests:
+ status = os.WEXITSTATUS(os.system(cmd))
+ sys.exit(status)
+
+#try:
+# os.chdir(scons_script_dir)
+#except OSError:
+# pass
+
+tests = list(map(Test, tests))
class Unbuffered:
def __init__(self, file):
return getattr(self.file, attr)
sys.stdout = Unbuffered(sys.stdout)
+sys.stderr = Unbuffered(sys.stderr)
+if list_only:
+ for t in tests:
+ sys.stdout.write(t.path + "\n")
+ sys.exit(0)
+
+#
+if not python:
+ if os.name == 'java':
+ python = os.path.join(sys.prefix, 'jython')
+ else:
+ python = sys.executable
+
+# time.clock() is the suggested interface for doing benchmarking timings,
+# but time.time() does a better job on Linux systems, so let that be
+# the non-Windows default.
+
+if sys.platform == 'win32':
+ time_func = time.clock
+else:
+ time_func = time.time
+
+if print_times:
+ print_time_func = lambda fmt, time: sys.stdout.write(fmt % time)
+else:
+ print_time_func = lambda fmt, time: None
+
+total_start_time = time_func()
for t in tests:
- cmd = string.join([sys.executable, debug, t.abspath], " ")
- if printcmd:
- sys.stdout.write(cmd + "\n")
- s = os.system(cmd)
- if s >= 256:
- s = s / 256
- t.status = s
- if s < 0 or s > 2:
- sys.stdout.write("Unexpected exit status %d\n" % s)
-
-fail = filter(lambda t: t.status == 1, tests)
-no_result = filter(lambda t: t.status == 2, tests)
-
-if len(tests) != 1:
+ command_args = ['-tt']
+ if debug:
+ 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()
+ if execute_tests:
+ t.execute()
+ t.test_time = time_func() - test_start_time
+ print_time_func("Test execution time: %.1f seconds\n", t.test_time)
+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 = [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:
+ if len(passed) == 1:
+ sys.stdout.write("\nPassed the following test:\n")
+ else:
+ sys.stdout.write("\nPassed the following %d tests:\n" % len(passed))
+ 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))
- sys.stdout.write("\t" + string.join(fail, "\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))
- sys.stdout.write("\t" + string.join(no_result, "\n\t") + "\n")
+ paths = [x.path for x in no_result]
+ sys.stdout.write("\t" + "\n\t".join(paths) + "\n")
-if output:
- f = open(output, 'w')
- f.write("test_result = [\n")
+if outputfile:
+ if outputfile == '-':
+ f = sys.stdout
+ else:
+ f = open(outputfile, 'w')
+ tests[0].header(f)
+ #f.write("test_result = [\n")
for t in tests:
- f.write(' { file_name = "%s";\n' % t.path)
- f.write(' exit_status = %d; },\n' % t.status)
- f.write("];\n")
- f.close()
+ t.write(f)
+ tests[0].footer(f)
+ #f.write("];\n")
+ if outputfile != '-':
+ f.close()
+
+if format == '--aegis':
+ sys.exit(0)
+elif len(fail):
+ sys.exit(1)
+elif len(no_result):
+ sys.exit(2)
+else:
+ sys.exit(0)
-sys.exit(len(fail) + len(no_result))
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4: