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.

test_gyp.py 7.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. #!/usr/bin/env python3
  2. # Copyright (c) 2012 Google Inc. All rights reserved.
  3. # Use of this source code is governed by a BSD-style license that can be
  4. # found in the LICENSE file.
  5. """gyptest.py -- test runner for GYP tests."""
  6. import argparse
  7. import os
  8. import platform
  9. import subprocess
  10. import sys
  11. import time
  12. def is_test_name(f):
  13. return f.startswith("gyptest") and f.endswith(".py")
  14. def find_all_gyptest_files(directory):
  15. result = []
  16. for root, dirs, files in os.walk(directory):
  17. result.extend([os.path.join(root, f) for f in files if is_test_name(f)])
  18. result.sort()
  19. return result
  20. def main(argv=None):
  21. if argv is None:
  22. argv = sys.argv
  23. parser = argparse.ArgumentParser()
  24. parser.add_argument("-a", "--all", action="store_true", help="run all tests")
  25. parser.add_argument("-C", "--chdir", action="store", help="change to directory")
  26. parser.add_argument(
  27. "-f",
  28. "--format",
  29. action="store",
  30. default="",
  31. help="run tests with the specified formats",
  32. )
  33. parser.add_argument(
  34. "-G",
  35. "--gyp_option",
  36. action="append",
  37. default=[],
  38. help="Add -G options to the gyp command line",
  39. )
  40. parser.add_argument(
  41. "-l", "--list", action="store_true", help="list available tests and exit"
  42. )
  43. parser.add_argument(
  44. "-n",
  45. "--no-exec",
  46. action="store_true",
  47. help="no execute, just print the command line",
  48. )
  49. parser.add_argument(
  50. "--path", action="append", default=[], help="additional $PATH directory"
  51. )
  52. parser.add_argument(
  53. "-q",
  54. "--quiet",
  55. action="store_true",
  56. help="quiet, don't print anything unless there are failures",
  57. )
  58. parser.add_argument(
  59. "-v",
  60. "--verbose",
  61. action="store_true",
  62. help="print configuration info and test results.",
  63. )
  64. parser.add_argument("tests", nargs="*")
  65. args = parser.parse_args(argv[1:])
  66. if args.chdir:
  67. os.chdir(args.chdir)
  68. if args.path:
  69. extra_path = [os.path.abspath(p) for p in args.path]
  70. extra_path = os.pathsep.join(extra_path)
  71. os.environ["PATH"] = extra_path + os.pathsep + os.environ["PATH"]
  72. if not args.tests:
  73. if not args.all:
  74. sys.stderr.write("Specify -a to get all tests.\n")
  75. return 1
  76. args.tests = ["test"]
  77. tests = []
  78. for arg in args.tests:
  79. if os.path.isdir(arg):
  80. tests.extend(find_all_gyptest_files(os.path.normpath(arg)))
  81. else:
  82. if not is_test_name(os.path.basename(arg)):
  83. print(arg, "is not a valid gyp test name.", file=sys.stderr)
  84. sys.exit(1)
  85. tests.append(arg)
  86. if args.list:
  87. for test in tests:
  88. print(test)
  89. sys.exit(0)
  90. os.environ["PYTHONPATH"] = os.path.abspath("test/lib")
  91. if args.verbose:
  92. print_configuration_info()
  93. if args.gyp_option and not args.quiet:
  94. print("Extra Gyp options: %s\n" % args.gyp_option)
  95. if args.format:
  96. format_list = args.format.split(",")
  97. else:
  98. format_list = {
  99. "aix5": ["make"],
  100. "os400": ["make"],
  101. "freebsd7": ["make"],
  102. "freebsd8": ["make"],
  103. "openbsd5": ["make"],
  104. "cygwin": ["msvs"],
  105. "win32": ["msvs", "ninja"],
  106. "linux": ["make", "ninja"],
  107. "linux2": ["make", "ninja"],
  108. "linux3": ["make", "ninja"],
  109. # TODO: Re-enable xcode-ninja.
  110. # https://bugs.chromium.org/p/gyp/issues/detail?id=530
  111. # 'darwin': ['make', 'ninja', 'xcode', 'xcode-ninja'],
  112. "darwin": ["make", "ninja", "xcode"],
  113. }[sys.platform]
  114. gyp_options = []
  115. for option in args.gyp_option:
  116. gyp_options += ["-G", option]
  117. runner = Runner(format_list, tests, gyp_options, args.verbose)
  118. runner.run()
  119. if not args.quiet:
  120. runner.print_results()
  121. return 1 if runner.failures else 0
  122. def print_configuration_info():
  123. print("Test configuration:")
  124. if sys.platform == "darwin":
  125. sys.path.append(os.path.abspath("test/lib"))
  126. import TestMac
  127. print(f" Mac {platform.mac_ver()[0]} {platform.mac_ver()[2]}")
  128. print(f" Xcode {TestMac.Xcode.Version()}")
  129. elif sys.platform == "win32":
  130. sys.path.append(os.path.abspath("pylib"))
  131. import gyp.MSVSVersion
  132. print(" Win %s %s\n" % platform.win32_ver()[0:2])
  133. print(" MSVS %s" % gyp.MSVSVersion.SelectVisualStudioVersion().Description())
  134. elif sys.platform in ("linux", "linux2"):
  135. print(" Linux %s" % " ".join(platform.linux_distribution()))
  136. print(f" Python {platform.python_version()}")
  137. print(f" PYTHONPATH={os.environ['PYTHONPATH']}")
  138. print()
  139. class Runner:
  140. def __init__(self, formats, tests, gyp_options, verbose):
  141. self.formats = formats
  142. self.tests = tests
  143. self.verbose = verbose
  144. self.gyp_options = gyp_options
  145. self.failures = []
  146. self.num_tests = len(formats) * len(tests)
  147. num_digits = len(str(self.num_tests))
  148. self.fmt_str = "[%%%dd/%%%dd] (%%s) %%s" % (num_digits, num_digits)
  149. self.isatty = sys.stdout.isatty() and not self.verbose
  150. self.env = os.environ.copy()
  151. self.hpos = 0
  152. def run(self):
  153. run_start = time.time()
  154. i = 1
  155. for fmt in self.formats:
  156. for test in self.tests:
  157. self.run_test(test, fmt, i)
  158. i += 1
  159. if self.isatty:
  160. self.erase_current_line()
  161. self.took = time.time() - run_start
  162. def run_test(self, test, fmt, i):
  163. if self.isatty:
  164. self.erase_current_line()
  165. msg = self.fmt_str % (i, self.num_tests, fmt, test)
  166. self.print_(msg)
  167. start = time.time()
  168. cmd = [sys.executable, test] + self.gyp_options
  169. self.env["TESTGYP_FORMAT"] = fmt
  170. proc = subprocess.Popen(
  171. cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=self.env
  172. )
  173. proc.wait()
  174. took = time.time() - start
  175. stdout = proc.stdout.read().decode("utf8")
  176. if proc.returncode == 2:
  177. res = "skipped"
  178. elif proc.returncode:
  179. res = "failed"
  180. self.failures.append(f"({test}) {fmt}")
  181. else:
  182. res = "passed"
  183. res_msg = f" {res} {took:.3f}s"
  184. self.print_(res_msg)
  185. if stdout and not stdout.endswith(("PASSED\n", "NO RESULT\n")):
  186. print()
  187. print("\n".join(f" {line}" for line in stdout.splitlines()))
  188. elif not self.isatty:
  189. print()
  190. def print_(self, msg):
  191. print(msg, end="")
  192. index = msg.rfind("\n")
  193. if index == -1:
  194. self.hpos += len(msg)
  195. else:
  196. self.hpos = len(msg) - index
  197. sys.stdout.flush()
  198. def erase_current_line(self):
  199. print("\b" * self.hpos + " " * self.hpos + "\b" * self.hpos, end="")
  200. sys.stdout.flush()
  201. self.hpos = 0
  202. def print_results(self):
  203. num_failures = len(self.failures)
  204. if num_failures:
  205. print()
  206. if num_failures == 1:
  207. print("Failed the following test:")
  208. else:
  209. print("Failed the following %d tests:" % num_failures)
  210. print("\t" + "\n\t".join(sorted(self.failures)))
  211. print()
  212. print(
  213. "Ran %d tests in %.3fs, %d failed."
  214. % (self.num_tests, self.took, num_failures)
  215. )
  216. print()
  217. if __name__ == "__main__":
  218. sys.exit(main())