text: #!/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.
  
  """gyptest.py -- test runner for GYP tests."""
  
  from __future__ import print_function
  
  import argparse
  import math
  import os
  import platform
  import subprocess
  import sys
  import time
  
  
  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):
      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
  
    parser = argparse.ArgumentParser()
    parser.add_argument("-a", "--all", action="store_true",
        help="run all tests")
    parser.add_argument("-C", "--chdir", action="store",
        help="change to directory")
    parser.add_argument("-f", "--format", action="store", default='',
        help="run tests with the specified formats")
    parser.add_argument("-G", '--gyp_option', action="append", default=[],
        help="Add -G options to the gyp command line")
    parser.add_argument("-l", "--list", action="store_true",
        help="list available tests and exit")
    parser.add_argument("-n", "--no-exec", action="store_true",
        help="no execute, just print the command line")
    parser.add_argument("--path", action="append", default=[],
        help="additional $PATH directory")
    parser.add_argument("-q", "--quiet", action="store_true",
        help="quiet, don't print anything unless there are failures")
    parser.add_argument("-v", "--verbose", action="store_true",
        help="print configuration info and test results.")
    parser.add_argument('tests', nargs='*')
    args = parser.parse_args(argv[1:])
  
    if args.chdir:
      os.chdir(args.chdir)
  
    if args.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.tests:
      if not args.all:
        sys.stderr.write('Specify -a to get all tests.\n')
        return 1
      args.tests = ['test']
  
    tests = []
    for arg in args.tests:
      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(arg, 'is not a valid gyp test name.', file=sys.stderr)
          sys.exit(1)
        tests.append(arg)
  
    if args.list:
      for test in tests:
        print(test)
      sys.exit(0)
  
    os.environ['PYTHONPATH'] = os.path.abspath('test/lib')
  
    if args.verbose:
      print_configuration_info()
  
    if args.gyp_option and not args.quiet:
      print('Extra Gyp options: %s\n' % args.gyp_option)
  
    if args.format:
      format_list = args.format.split(',')
    else:
      format_list = {
        'aix5':     ['make'],
        'freebsd7': ['make'],
        'freebsd8': ['make'],
        'openbsd5': ['make'],
        'cygwin':   ['msvs'],
        'win32':    ['msvs', 'ninja'],
        'linux':    ['make', 'ninja'],
        'linux2':   ['make', 'ninja'],
        'linux3':   ['make', 'ninja'],
  
        # TODO: Re-enable xcode-ninja.
        # https://bugs.chromium.org/p/gyp/issues/detail?id=530
        # 'darwin':   ['make', 'ninja', 'xcode', 'xcode-ninja'],
        'darwin':   ['make', 'ninja', 'xcode'],
      }[sys.platform]
  
    gyp_options = []
    for option in args.gyp_option:
      gyp_options += ['-G', option]
  
    runner = Runner(format_list, tests, gyp_options, args.verbose)
    runner.run()
  
    if not args.quiet:
      runner.print_results()
  
    if runner.failures:
      return 1
    else:
      return 0
  
  
  def print_configuration_info():
    print('Test configuration:')
    if sys.platform == 'darwin':
      sys.path.append(os.path.abspath('test/lib'))
      import TestMac
      print('  Mac %s %s' % (platform.mac_ver()[0], platform.mac_ver()[2]))
      print('  Xcode %s' % TestMac.Xcode.Version())
    elif sys.platform == 'win32':
      sys.path.append(os.path.abspath('pylib'))
      import gyp.MSVSVersion
      print('  Win %s %s\n' % platform.win32_ver()[0:2])
      print('  MSVS %s' %
            gyp.MSVSVersion.SelectVisualStudioVersion().Description())
    elif sys.platform in ('linux', 'linux2'):
      print('  Linux %s' % ' '.join(platform.linux_distribution()))
    print('  Python %s' % platform.python_version())
    print('  PYTHONPATH=%s' % os.environ['PYTHONPATH'])
    print()
  
  
  class Runner(object):
    def __init__(self, formats, tests, gyp_options, verbose):
      self.formats = formats
      self.tests = tests
      self.verbose = verbose
      self.gyp_options = gyp_options
      self.failures = []
      self.num_tests = len(formats) * len(tests)
      num_digits = len(str(self.num_tests))
      self.fmt_str = '[%%%dd/%%%dd] (%%s) %%s' % (num_digits, num_digits)
      self.isatty = sys.stdout.isatty() and not self.verbose
      self.env = os.environ.copy()
      self.hpos = 0
  
    def run(self):
      run_start = time.time()
  
      i = 1
      for fmt in self.formats:
        for test in self.tests:
          self.run_test(test, fmt, i)
          i += 1
  
      if self.isatty:
        self.erase_current_line()
  
      self.took = time.time() - run_start
  
    def run_test(self, test, fmt, i):
      if self.isatty:
        self.erase_current_line()
  
      msg = self.fmt_str % (i, self.num_tests, fmt, test)
      self.print_(msg)
  
      start = time.time()
      cmd = [sys.executable, test] + self.gyp_options
      self.env['TESTGYP_FORMAT'] = fmt
      proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
                              stderr=subprocess.STDOUT, env=self.env)
      proc.wait()
      took = time.time() - start
  
      stdout = proc.stdout.read().decode('utf8')
      if proc.returncode == 2:
        res = 'skipped'
      elif proc.returncode:
        res = 'failed'
        self.failures.append('(%s) %s' % (test, fmt))
      else:
        res = 'passed'
      res_msg = ' %s %.3fs' % (res, took)
      self.print_(res_msg)
  
      if (stdout and
          not stdout.endswith('PASSED\n') and
          not (stdout.endswith('NO RESULT\n'))):
        print()
        for l in stdout.splitlines():
          print('    %s' % l)
      elif not self.isatty:
        print()
  
    def print_(self, msg):
      print(msg, end='')
      index = msg.rfind('\n')
      if index == -1:
        self.hpos += len(msg)
      else:
        self.hpos = len(msg) - index
      sys.stdout.flush()
  
    def erase_current_line(self):
      print('\b' * self.hpos + ' ' * self.hpos + '\b' * self.hpos, end='')
      sys.stdout.flush()
      self.hpos = 0
  
    def print_results(self):
      num_failures = len(self.failures)
      if num_failures:
        print()
        if num_failures == 1:
          print("Failed the following test:")
        else:
          print("Failed the following %d tests:" % num_failures)
        print("\t" + "\n\t".join(sorted(self.failures)))
        print()
      print('Ran %d tests in %.3fs, %d failed.' % (self.num_tests, self.took,
                                                   num_failures))
      print()
  
  
  if __name__ == "__main__":
    sys.exit(main())
  