PageRenderTime 29ms CodeModel.GetById 1ms RepoModel.GetById 1ms app.codeStats 0ms

/hphp/test/tools/import_spec_test.py

https://gitlab.com/iranjith4/hhvm
Python | 312 lines | 267 code | 22 blank | 23 comment | 10 complexity | 180268018ccc8b6559003d53f84b00db MD5 | raw file
  1. #!/usr/bin/env python
  2. # @lint-avoid-pyflakes3
  3. # @lint-avoid-pyflakes2
  4. # @lint-avoid-python-3-compatibility-imports
  5. #
  6. # Copies all the phplang-spec tests to a temporary directory, runs them in
  7. # interp mode, then copies the good ones to test/spec/good and the bad ones to
  8. # test/spec/bad.
  9. #
  10. import argparse
  11. import glob
  12. import json
  13. import os
  14. import re
  15. import shutil
  16. import subprocess
  17. import sys
  18. import tarfile
  19. import urllib2
  20. import zipfile
  21. # Don't even pull these into the repo.
  22. # We want running the bad tests to still complete.
  23. no_import = (
  24. '.gitignore',
  25. 'README.md',
  26. 'make_phpt',
  27. )
  28. # For marking tests as always failing. Used to keep flaky tests in flaky/.
  29. flaky_tests = ()
  30. # Tests that work but not in repo mode
  31. norepo_tests = (
  32. # TODO: See if any of these should work in repo mode
  33. '/classes/setting_state.php',
  34. '/expressions/error_control_operator/error_control.php',
  35. '/expressions/general/order_of_evaluation.php',
  36. '/expressions/primary_expressions/intrinsics_eval.php',
  37. '/tests/expressions/primary_expressions/intrinsics_unset.php',
  38. '/expressions/source_file_inclusion/include.php',
  39. '/expressions/source_file_inclusion/require.php',
  40. '/functions/basics.php',
  41. '/functions/byrefs.php',
  42. )
  43. # Random other files that zend wants
  44. other_files = (
  45. '/classes/Aircraft.inc',
  46. '/classes/MathLibrary.inc',
  47. '/classes/MyCollection.inc',
  48. '/classes/PassengerJet.inc',
  49. '/classes/Point.inc',
  50. '/classes/Point2.inc',
  51. '/classes/Vehicle.inc',
  52. '/constants/includefile.inc',
  53. '/exception_handling/MyRangeException.inc',
  54. '/expressions/primary_expressions/Point.inc',
  55. '/expressions/primary_expressions/Point2.inc',
  56. '/expressions/source_file_inclusion/Circle.inc',
  57. '/expressions/source_file_inclusion/Point.inc',
  58. '/expressions/source_file_inclusion/Positions.inc',
  59. '/expressions/yield_operator/Testfile.txt',
  60. '/functions/TestInc.inc',
  61. '/interfaces/MyCollection.inc',
  62. '/interfaces/MyList.inc',
  63. '/namespaces/Circle.inc',
  64. '/namespaces/Point.inc',
  65. '/serialization/Point.inc',
  66. '/config.ini',
  67. )
  68. parser = argparse.ArgumentParser()
  69. parser.add_argument(
  70. "-o",
  71. "--only",
  72. type=str,
  73. action='append',
  74. help="only import tests whose path contains this substring."
  75. )
  76. parser.add_argument(
  77. "--dirty",
  78. action='store_true',
  79. help="leave around test/spec/all directory."
  80. )
  81. parser.add_argument(
  82. "-v",
  83. "--verbose",
  84. action='store_true',
  85. help="print out extra stuff."
  86. )
  87. args = parser.parse_args()
  88. def mkdir_p(path):
  89. try:
  90. os.makedirs(path)
  91. except OSError as exc: # Python >2.5
  92. pass
  93. def walk(filename, dest_subdir):
  94. dest_filename = os.path.basename(filename)
  95. mkdir_p(dest_subdir)
  96. full_dest_filename = os.path.join(dest_subdir, dest_filename)
  97. # Exactly mirror spec's directories
  98. # We'll only move things we want into 'good'
  99. shutil.copyfile(filename, full_dest_filename)
  100. print("Importing %s" % full_dest_filename)
  101. def should_import(filename):
  102. for bad in no_import:
  103. if bad in filename:
  104. return False
  105. if 'phpt' in os.path.dirname(os.path.realpath(filename)):
  106. return False
  107. if 'tests' in os.path.dirname(os.path.realpath(filename)):
  108. return True
  109. return False
  110. def index_containing_substring(the_list, substring):
  111. if not the_list:
  112. return -1
  113. for i, s in enumerate(the_list):
  114. if substring in s:
  115. return i
  116. return -1
  117. script_dir = os.path.dirname(__file__)
  118. spec_dir = os.path.normpath(os.path.join(script_dir, '../spec'))
  119. all_dir = os.path.join(spec_dir, 'all')
  120. spec_release_name = "php-langspec-master"
  121. spec_release_filename = spec_release_name + ".zip"
  122. spec_release_archive = os.path.join(spec_dir, spec_release_filename)
  123. spec_release_path = os.path.join(spec_dir, spec_release_name)
  124. # Any tests that fail will be added to the global skipif via a makework PHP
  125. # program by this script. It gets emptied out every time the
  126. # script is run
  127. skip_code_file = spec_dir + '/config.skipif'
  128. skip_code_file_bak = spec_dir + '/config.skipif.bak'
  129. # make a backup in case something goes wrong and we pass dirty
  130. shutil.copyfile(skip_code_file, skip_code_file_bak)
  131. prev_skip_content = open(skip_code_file,'r').read().splitlines()
  132. # empty the skipif file so we can run all the tests again without
  133. # skipping them
  134. open(skip_code_file, 'w').write('')
  135. if not os.path.exists(spec_dir):
  136. os.makedirs(spec_dir)
  137. if not os.path.isfile(spec_release_archive):
  138. print('Downloading ' + spec_release_name + '...')
  139. spec_release_url = 'https://github.com/JoelMarcey/'
  140. spec_release_url = spec_release_url + 'php-langspec/archive/master.zip'
  141. spec_release_request = urllib2.urlopen(spec_release_url)
  142. output = open(spec_release_archive, "w")
  143. output.write(spec_release_request.read())
  144. output.close()
  145. if not os.path.isdir(spec_release_path):
  146. print('Extracting ' + spec_release_name + '...')
  147. with zipfile.ZipFile(spec_release_archive, 'r') as zf:
  148. zf.extractall(spec_dir)
  149. for root, dirs, files in os.walk(spec_release_path):
  150. for filename in files:
  151. full_file = os.path.join(root, filename)
  152. def matches(patterns):
  153. if not patterns:
  154. return True
  155. for other_file in other_files:
  156. if not other_file.endswith('.phpt') and other_file in full_file:
  157. return True
  158. for pattern in patterns:
  159. if pattern in full_file:
  160. return True
  161. return False
  162. if (
  163. matches(args.only) and
  164. should_import(full_file)
  165. ):
  166. walk(
  167. full_file,
  168. os.path.join(all_dir, os.path.relpath(root, spec_release_path))
  169. )
  170. if not os.path.isdir(all_dir):
  171. if args.only:
  172. print("No test/spec/all. " +
  173. "Your --only arg didn't match any test that should be imported.")
  174. else:
  175. print("No test/spec/all. Maybe no tests were imported?")
  176. sys.exit(0)
  177. else:
  178. print("Running all tests from spec/all")
  179. stdout = subprocess.Popen(
  180. [
  181. os.path.join(script_dir, '../run'),
  182. '--fbmake',
  183. '-m',
  184. 'interp',
  185. all_dir,
  186. ],
  187. stdout=subprocess.PIPE,
  188. stderr=subprocess.STDOUT
  189. ).communicate()[0]
  190. # segfaults also print on stderr
  191. stdout = re.sub('\nsh: line 1:.*', '', stdout)
  192. # just the last line
  193. last_line = stdout.strip().split("\n")[-1]
  194. results = json.loads(last_line)['results']
  195. if args.verbose:
  196. print(results)
  197. else:
  198. print("Test run done, moving files")
  199. skip_code = '<?php' + os.linesep + os.linesep + '$need_fix = array('
  200. skip_code = skip_code + os.linesep
  201. for test in results:
  202. filename = test['name']
  203. dest_file = filename.replace('all', '.', 1)
  204. mkdir_p(os.path.dirname(dest_file))
  205. good = (test['status'] == 'passed')
  206. flaky_test = False
  207. for test in flaky_tests:
  208. if test in filename:
  209. good = False
  210. flaky_test = True
  211. needs_norepo = False
  212. for test in norepo_tests:
  213. if test in filename:
  214. needs_norepo = True
  215. if not good:
  216. idx = index_containing_substring(prev_skip_content, dest_file)
  217. if idx != -1:
  218. skip_code = skip_code + prev_skip_content[idx]
  219. else:
  220. skip_code = skip_code + ' "' + dest_file
  221. skip_code = skip_code + '", // ADD WHY COMMENT HERE'
  222. skip_code = skip_code + os.linesep
  223. exps = glob.glob(filename + '.expect*')
  224. if not exps:
  225. # this file is probably generated while running tests :(
  226. continue
  227. source_file_exp = exps[0]
  228. _, dest_ext = os.path.splitext(source_file_exp)
  229. shutil.copyfile(filename, dest_file)
  230. open(dest_file + dest_ext, 'w').write(
  231. open(source_file_exp).read().replace('/all', '/.')
  232. )
  233. if needs_norepo:
  234. open(dest_file + '.norepo', 'w')
  235. if os.path.exists(filename + '.skipif'):
  236. shutil.copyfile(filename + '.skipif', dest_file + '.skipif')
  237. if os.path.exists(filename + '.ini'):
  238. shutil.copyfile(filename + '.ini', dest_file + '.ini')
  239. skip_code = skip_code + ');' + os.linesep
  240. skip_code = skip_code + '$test = "./" '
  241. skip_code = skip_code + '. substr($argv[1], strrpos($argv[1], "tests/"));'
  242. skip_code = skip_code + os.linesep + 'if (in_array($test, $need_fix)) {'
  243. skip_code = skip_code + os.linesep + ' echo "skip";' + os.linesep + '}'
  244. skip_code = skip_code + os.linesep
  245. open(skip_code_file, 'w').write(skip_code)
  246. # extra random files needed for tests...
  247. for root, dirs, files in os.walk(all_dir):
  248. for filename in files:
  249. filename = os.path.join(root, filename)
  250. for name in other_files:
  251. if name in filename:
  252. # root config.ini file goes in directory level above
  253. # the tests themselves
  254. if 'config.ini' in filename:
  255. dest = spec_dir + '/config.ini'
  256. data = open(filename).read()
  257. open(dest, 'w').write(data)
  258. else:
  259. dest = filename.replace('all', '.', 1)
  260. dir = os.path.dirname(dest)
  261. mkdir_p(dir)
  262. data = open(filename).read()
  263. open(dest, 'w').write(data)
  264. if not args.dirty:
  265. shutil.rmtree(all_dir)
  266. shutil.rmtree(spec_release_path)
  267. os.remove(skip_code_file_bak)