You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
274 lines
7.8 KiB
274 lines
7.8 KiB
#!/usr/bin/env python |
|
|
|
# Copyright (c) 2012 Google Inc. All rights reserved. |
|
# Use of this source code is governed by a BSD-style license that can be |
|
# found in the LICENSE file. |
|
|
|
__doc__ = """ |
|
gyptest.py -- test runner for GYP tests. |
|
""" |
|
|
|
import os |
|
import optparse |
|
import subprocess |
|
import sys |
|
|
|
class CommandRunner(object): |
|
""" |
|
Executor class for commands, including "commands" implemented by |
|
Python functions. |
|
""" |
|
verbose = True |
|
active = True |
|
|
|
def __init__(self, dictionary={}): |
|
self.subst_dictionary(dictionary) |
|
|
|
def subst_dictionary(self, dictionary): |
|
self._subst_dictionary = dictionary |
|
|
|
def subst(self, string, dictionary=None): |
|
""" |
|
Substitutes (via the format operator) the values in the specified |
|
dictionary into the specified command. |
|
|
|
The command can be an (action, string) tuple. In all cases, we |
|
perform substitution on strings and don't worry if something isn't |
|
a string. (It's probably a Python function to be executed.) |
|
""" |
|
if dictionary is None: |
|
dictionary = self._subst_dictionary |
|
if dictionary: |
|
try: |
|
string = string % dictionary |
|
except TypeError: |
|
pass |
|
return string |
|
|
|
def display(self, command, stdout=None, stderr=None): |
|
if not self.verbose: |
|
return |
|
if type(command) == type(()): |
|
func = command[0] |
|
args = command[1:] |
|
s = '%s(%s)' % (func.__name__, ', '.join(map(repr, args))) |
|
if type(command) == type([]): |
|
# TODO: quote arguments containing spaces |
|
# TODO: handle meta characters? |
|
s = ' '.join(command) |
|
else: |
|
s = self.subst(command) |
|
if not s.endswith('\n'): |
|
s += '\n' |
|
sys.stdout.write(s) |
|
sys.stdout.flush() |
|
|
|
def execute(self, command, stdout=None, stderr=None): |
|
""" |
|
Executes a single command. |
|
""" |
|
if not self.active: |
|
return 0 |
|
if type(command) == type(''): |
|
command = self.subst(command) |
|
cmdargs = shlex.split(command) |
|
if cmdargs[0] == 'cd': |
|
command = (os.chdir,) + tuple(cmdargs[1:]) |
|
if type(command) == type(()): |
|
func = command[0] |
|
args = command[1:] |
|
return func(*args) |
|
else: |
|
if stdout is sys.stdout: |
|
# Same as passing sys.stdout, except python2.4 doesn't fail on it. |
|
subout = None |
|
else: |
|
# Open pipe for anything else so Popen works on python2.4. |
|
subout = subprocess.PIPE |
|
if stderr is sys.stderr: |
|
# Same as passing sys.stderr, except python2.4 doesn't fail on it. |
|
suberr = None |
|
elif stderr is None: |
|
# Merge with stdout if stderr isn't specified. |
|
suberr = subprocess.STDOUT |
|
else: |
|
# Open pipe for anything else so Popen works on python2.4. |
|
suberr = subprocess.PIPE |
|
p = subprocess.Popen(command, |
|
shell=(sys.platform == 'win32'), |
|
stdout=subout, |
|
stderr=suberr) |
|
p.wait() |
|
if stdout is None: |
|
self.stdout = p.stdout.read() |
|
elif stdout is not sys.stdout: |
|
stdout.write(p.stdout.read()) |
|
if stderr not in (None, sys.stderr): |
|
stderr.write(p.stderr.read()) |
|
return p.returncode |
|
|
|
def run(self, command, display=None, stdout=None, stderr=None): |
|
""" |
|
Runs a single command, displaying it first. |
|
""" |
|
if display is None: |
|
display = command |
|
self.display(display) |
|
return self.execute(command, stdout, stderr) |
|
|
|
|
|
class Unbuffered(object): |
|
def __init__(self, fp): |
|
self.fp = fp |
|
def write(self, arg): |
|
self.fp.write(arg) |
|
self.fp.flush() |
|
def __getattr__(self, attr): |
|
return getattr(self.fp, attr) |
|
|
|
sys.stdout = Unbuffered(sys.stdout) |
|
sys.stderr = Unbuffered(sys.stderr) |
|
|
|
|
|
def is_test_name(f): |
|
return f.startswith('gyptest') and f.endswith('.py') |
|
|
|
|
|
def find_all_gyptest_files(directory): |
|
result = [] |
|
for root, dirs, files in os.walk(directory): |
|
if '.svn' in dirs: |
|
dirs.remove('.svn') |
|
result.extend([ os.path.join(root, f) for f in files if is_test_name(f) ]) |
|
result.sort() |
|
return result |
|
|
|
|
|
def main(argv=None): |
|
if argv is None: |
|
argv = sys.argv |
|
|
|
usage = "gyptest.py [-ahlnq] [-f formats] [test ...]" |
|
parser = optparse.OptionParser(usage=usage) |
|
parser.add_option("-a", "--all", action="store_true", |
|
help="run all tests") |
|
parser.add_option("-C", "--chdir", action="store", default=None, |
|
help="chdir to the specified directory") |
|
parser.add_option("-f", "--format", action="store", default='', |
|
help="run tests with the specified formats") |
|
parser.add_option("-G", '--gyp_option', action="append", default=[], |
|
help="Add -G options to the gyp command line") |
|
parser.add_option("-l", "--list", action="store_true", |
|
help="list available tests and exit") |
|
parser.add_option("-n", "--no-exec", action="store_true", |
|
help="no execute, just print the command line") |
|
parser.add_option("--passed", action="store_true", |
|
help="report passed tests") |
|
parser.add_option("--path", action="append", default=[], |
|
help="additional $PATH directory") |
|
parser.add_option("-q", "--quiet", action="store_true", |
|
help="quiet, don't print test command lines") |
|
opts, args = parser.parse_args(argv[1:]) |
|
|
|
if opts.chdir: |
|
os.chdir(opts.chdir) |
|
|
|
if opts.path: |
|
extra_path = [os.path.abspath(p) for p in opts.path] |
|
extra_path = os.pathsep.join(extra_path) |
|
os.environ['PATH'] = extra_path + os.pathsep + os.environ['PATH'] |
|
|
|
if not args: |
|
if not opts.all: |
|
sys.stderr.write('Specify -a to get all tests.\n') |
|
return 1 |
|
args = ['test'] |
|
|
|
tests = [] |
|
for arg in args: |
|
if os.path.isdir(arg): |
|
tests.extend(find_all_gyptest_files(os.path.normpath(arg))) |
|
else: |
|
if not is_test_name(os.path.basename(arg)): |
|
print >>sys.stderr, arg, 'is not a valid gyp test name.' |
|
sys.exit(1) |
|
tests.append(arg) |
|
|
|
if opts.list: |
|
for test in tests: |
|
print test |
|
sys.exit(0) |
|
|
|
CommandRunner.verbose = not opts.quiet |
|
CommandRunner.active = not opts.no_exec |
|
cr = CommandRunner() |
|
|
|
os.environ['PYTHONPATH'] = os.path.abspath('test/lib') |
|
if not opts.quiet: |
|
sys.stdout.write('PYTHONPATH=%s\n' % os.environ['PYTHONPATH']) |
|
|
|
passed = [] |
|
failed = [] |
|
no_result = [] |
|
|
|
if opts.format: |
|
format_list = opts.format.split(',') |
|
else: |
|
# TODO: not duplicate this mapping from pylib/gyp/__init__.py |
|
format_list = { |
|
'aix5': ['make'], |
|
'freebsd7': ['make'], |
|
'freebsd8': ['make'], |
|
'openbsd5': ['make'], |
|
'cygwin': ['msvs'], |
|
'win32': ['msvs', 'ninja'], |
|
'linux2': ['make', 'ninja'], |
|
'linux3': ['make', 'ninja'], |
|
'darwin': ['make', 'ninja', 'xcode', 'xcode-ninja'], |
|
}[sys.platform] |
|
|
|
for format in format_list: |
|
os.environ['TESTGYP_FORMAT'] = format |
|
if not opts.quiet: |
|
sys.stdout.write('TESTGYP_FORMAT=%s\n' % format) |
|
|
|
gyp_options = [] |
|
for option in opts.gyp_option: |
|
gyp_options += ['-G', option] |
|
if gyp_options and not opts.quiet: |
|
sys.stdout.write('Extra Gyp options: %s\n' % gyp_options) |
|
|
|
for test in tests: |
|
status = cr.run([sys.executable, test] + gyp_options, |
|
stdout=sys.stdout, |
|
stderr=sys.stderr) |
|
if status == 2: |
|
no_result.append(test) |
|
elif status: |
|
failed.append(test) |
|
else: |
|
passed.append(test) |
|
|
|
if not opts.quiet: |
|
def report(description, tests): |
|
if tests: |
|
if len(tests) == 1: |
|
sys.stdout.write("\n%s the following test:\n" % description) |
|
else: |
|
fmt = "\n%s the following %d tests:\n" |
|
sys.stdout.write(fmt % (description, len(tests))) |
|
sys.stdout.write("\t" + "\n\t".join(tests) + "\n") |
|
|
|
if opts.passed: |
|
report("Passed", passed) |
|
report("Failed", failed) |
|
report("No result from", no_result) |
|
|
|
if failed: |
|
return 1 |
|
else: |
|
return 0 |
|
|
|
|
|
if __name__ == "__main__": |
|
sys.exit(main())
|
|
|