|  | #!/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: | 
|  | """ | 
|  | 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: | 
|  | 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 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 f.startswith('gyptest') and f.endswith('.py') ]) | 
|  | 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'] += os.pathsep + extra_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: | 
|  | 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 = { | 
|  | 'freebsd7': ['make'], | 
|  | 'freebsd8': ['make'], | 
|  | 'cygwin':   ['msvs'], | 
|  | 'win32':    ['msvs', 'ninja'], | 
|  | 'linux2':   ['make', 'ninja'], | 
|  | 'linux3':   ['make', 'ninja'], | 
|  | 'darwin':   ['make', 'ninja', 'xcode'], | 
|  | }[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()) |