runtests.py 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769
  1. #!/usr/bin/env python
  2. #-------------------------------------------------------------------------------------------------------
  3. # Copyright (C) Microsoft. All rights reserved.
  4. # Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  5. #-------------------------------------------------------------------------------------------------------
  6. from __future__ import print_function
  7. from datetime import datetime
  8. from multiprocessing import Pool, Manager, cpu_count
  9. from threading import Timer
  10. import sys
  11. import os
  12. import glob
  13. import subprocess as SP
  14. import traceback
  15. import argparse
  16. import xml.etree.ElementTree as ET
  17. import re
  18. import time
  19. # handle command line args
  20. parser = argparse.ArgumentParser(
  21. description='ChakraCore *nix Test Script',
  22. formatter_class=argparse.RawDescriptionHelpFormatter,
  23. epilog='''\
  24. Samples:
  25. test all folders:
  26. runtests.py
  27. test only Array:
  28. runtests.py Array
  29. test a single file:
  30. runtests.py Basics/hello.js
  31. ''')
  32. DEFAULT_TIMEOUT = 60
  33. SLOW_TIMEOUT = 180
  34. parser.add_argument('folders', metavar='folder', nargs='*',
  35. help='folder subset to run tests')
  36. parser.add_argument('-b', '--binary', metavar='bin',
  37. help='ch full path')
  38. parser.add_argument('-v', '--verbose', action='store_true',
  39. help='increase verbosity of output')
  40. parser.add_argument('--sanitize', metavar='sanitizers',
  41. help='ignore tests known to be broken with these sanitizers')
  42. parser.add_argument('-d', '--debug', action='store_true',
  43. help='use debug build');
  44. parser.add_argument('-t', '--test', '--test-build', action='store_true',
  45. help='use test build')
  46. parser.add_argument('--static', action='store_true',
  47. help='mark that we are testing a static build')
  48. parser.add_argument('--variants', metavar='variant', nargs='+',
  49. help='run specified test variants')
  50. parser.add_argument('--include-slow', action='store_true',
  51. help='include slow tests (timeout ' + str(SLOW_TIMEOUT) + ' seconds)')
  52. parser.add_argument('--only-slow', action='store_true',
  53. help='run only slow tests')
  54. parser.add_argument('--nightly', action='store_true',
  55. help='run as nightly tests')
  56. parser.add_argument('--tag', nargs='*',
  57. help='select tests with given tags')
  58. parser.add_argument('--not-tag', action='append',
  59. help='exclude tests with given tags')
  60. parser.add_argument('--flags', default='',
  61. help='global test flags to ch')
  62. parser.add_argument('--timeout', type=int, default=DEFAULT_TIMEOUT,
  63. help='test timeout (default ' + str(DEFAULT_TIMEOUT) + ' seconds)')
  64. parser.add_argument('--swb', action='store_true',
  65. help='use binary from VcBuild.SWB to run the test')
  66. parser.add_argument('--lldb', default=None,
  67. help='run test suit with lldb batch mode to get call stack for crashing processes (ignores baseline matching)', action='store_true')
  68. parser.add_argument('--x86', action='store_true',
  69. help='use x86 build')
  70. parser.add_argument('--x64', action='store_true',
  71. help='use x64 build')
  72. parser.add_argument('--arm', action='store_true',
  73. help='use arm build')
  74. parser.add_argument('--arm64', action='store_true',
  75. help='use arm64 build')
  76. parser.add_argument('-j', '--processcount', metavar='processcount', type=int,
  77. help='number of parallel threads to use')
  78. parser.add_argument('--warn-on-timeout', action='store_true',
  79. help='warn when a test times out instead of labelling it as an error immediately')
  80. parser.add_argument('--override-test-root', type=str,
  81. help='change the base directory for the tests (where rlexedirs will be sought)')
  82. parser.add_argument('--extra-flags', type=str,
  83. help='add extra flags to all executed tests')
  84. parser.add_argument('--orc','--only-return-code', action='store_true',
  85. help='only consider test return 0/non-0 for pass-fail (no baseline checks)')
  86. parser.add_argument('--show-passes', action='store_true',
  87. help='display passed tests, when false only failures and the result summary are shown')
  88. args = parser.parse_args()
  89. test_root = os.path.dirname(os.path.realpath(__file__))
  90. repo_root = os.path.dirname(test_root)
  91. # new test root
  92. if args.override_test_root:
  93. test_root = os.path.realpath(args.override_test_root)
  94. # arch: x86, x64, arm, arm64
  95. arch = None
  96. if args.x86:
  97. arch = 'x86'
  98. elif args.x64:
  99. arch = 'x64'
  100. elif args.arm:
  101. arch = 'arm'
  102. elif args.arm64:
  103. arch = 'arm64'
  104. if arch == None:
  105. arch = os.environ.get('_BuildArch', 'x86')
  106. if sys.platform != 'win32':
  107. arch = 'x64' # xplat: hard code arch == x64
  108. arch_alias = 'amd64' if arch == 'x64' else None
  109. # flavor: debug, test, release
  110. flavor = 'Debug' if args.debug else ('Test' if args.test else None)
  111. if flavor == None:
  112. print("ERROR: Test build target wasn't defined.")
  113. print("Try '-t' (test build) or '-d' (debug build).")
  114. sys.exit(1)
  115. # handling for extra flags
  116. extra_flags = ['-WERExceptionSupport']
  117. if args.extra_flags:
  118. extra_flags = args.extra_flags.split()
  119. # test variants
  120. if not args.variants:
  121. args.variants = ['interpreted', 'dynapogo']
  122. # append exe to the binary name on windows
  123. binary_name = "ch"
  124. if sys.platform == 'win32':
  125. binary_name = "ch.exe"
  126. # binary: full ch path
  127. binary = args.binary
  128. if binary == None:
  129. if sys.platform == 'win32':
  130. build = "VcBuild.SWB" if args.swb else "VcBuild"
  131. binary = os.path.join(repo_root, 'Build', build, 'bin', '{}_{}'.format(arch, flavor), binary_name)
  132. else:
  133. binary = os.path.join(repo_root, 'out', flavor, binary_name)
  134. if not os.path.isfile(binary):
  135. print('{} not found. Did you run ./build.sh already?'.format(binary))
  136. sys.exit(1)
  137. # global tags/not_tags
  138. tags = set(args.tag or [])
  139. not_tags = set(args.not_tag or []).union(['fail', 'exclude_' + arch, 'exclude_' + flavor])
  140. if arch_alias:
  141. not_tags.add('exclude_' + arch_alias)
  142. if args.only_slow:
  143. tags.add('Slow')
  144. elif not args.include_slow:
  145. not_tags.add('Slow')
  146. elif args.include_slow and args.timeout == DEFAULT_TIMEOUT:
  147. args.timeout = SLOW_TIMEOUT
  148. not_tags.add('exclude_nightly' if args.nightly else 'nightly')
  149. # verbosity
  150. verbose = False
  151. show_passes = False
  152. if args.verbose:
  153. verbose = True
  154. show_passes = True
  155. print("Emitting verbose output...")
  156. elif args.show_passes:
  157. show_passes = True
  158. # xplat: temp hard coded to exclude unsupported tests
  159. if sys.platform != 'win32':
  160. not_tags.add('exclude_xplat')
  161. not_tags.add('require_winglob')
  162. not_tags.add('require_simd')
  163. else:
  164. not_tags.add('exclude_windows')
  165. # exclude tests that depend on features not supported on a platform
  166. if arch == 'arm' or arch == 'arm64':
  167. not_tags.add('require_asmjs')
  168. # exclude tests known to fail under certain sanitizers
  169. if args.sanitize != None:
  170. not_tags.add('exclude_sanitize_'+args.sanitize)
  171. if args.static != None:
  172. not_tags.add('exclude_static')
  173. if sys.platform == 'darwin':
  174. not_tags.add('exclude_mac')
  175. if 'require_icu' in not_tags or 'exclude_noicu' in not_tags:
  176. not_tags.add('Intl')
  177. not_compile_flags = None
  178. # use -j flag to specify number of parallel processes
  179. processcount = cpu_count()
  180. if args.processcount != None:
  181. processcount = int(args.processcount)
  182. # handle warn on timeout
  183. warn_on_timeout = False
  184. if args.warn_on_timeout == True:
  185. warn_on_timeout = True
  186. # handle limiting test result analysis to return codes
  187. return_code_only = False
  188. if args.orc == True:
  189. return_code_only = True
  190. # use tags/not_tags/not_compile_flags as case-insensitive
  191. def lower_set(s):
  192. return set([x.lower() for x in s] if s else [])
  193. tags = lower_set(tags)
  194. not_tags = lower_set(not_tags)
  195. not_compile_flags = lower_set(not_compile_flags)
  196. # split tags text into tags set
  197. _empty_set = set()
  198. def split_tags(text):
  199. return set(x.strip() for x in text.lower().split(',')) if text \
  200. else _empty_set
  201. # remove carriage returns at end of line to avoid platform difference
  202. def normalize_new_line(text):
  203. return re.sub(b'[\r]+\n', b'\n', text)
  204. # A test simply contains a collection of test attributes.
  205. # Misc attributes added by test run:
  206. # id unique counter to identify a test
  207. # filename full path of test file
  208. # elapsed_time elapsed time when running the test
  209. #
  210. class Test(dict):
  211. __setattr__ = dict.__setitem__
  212. __delattr__ = dict.__delitem__
  213. # support dot syntax for normal attribute access
  214. def __getattr__(self, key):
  215. return super(Test, self).__getattr__(key) if key.startswith('__') \
  216. else self.get(key)
  217. # mark start of this test run, to compute elapsed_time
  218. def start(self):
  219. self.start_time = datetime.now()
  220. # mark end of this test run, compute elapsed_time
  221. def done(self):
  222. if not self.elapsed_time:
  223. self.elapsed_time = (datetime.now() - self.start_time)\
  224. .total_seconds()
  225. # records pass_count/fail_count
  226. class PassFailCount(object):
  227. def __init__(self):
  228. self.pass_count = 0
  229. self.fail_count = 0
  230. def __str__(self):
  231. return 'passed {}, failed {}'.format(self.pass_count, self.fail_count)
  232. def total_count(self):
  233. return self.pass_count + self.fail_count
  234. # records total and individual folder's pass_count/fail_count
  235. class TestResult(PassFailCount):
  236. def __init__(self):
  237. super(self.__class__, self).__init__()
  238. self.folders = {}
  239. def _get_folder_result(self, folder):
  240. r = self.folders.get(folder)
  241. if not r:
  242. r = PassFailCount()
  243. self.folders[folder] = r
  244. return r
  245. def log(self, filename, fail=False):
  246. folder = os.path.basename(os.path.dirname(filename))
  247. r = self._get_folder_result(folder)
  248. if fail:
  249. r.fail_count += 1
  250. self.fail_count += 1
  251. else:
  252. r.pass_count += 1
  253. self.pass_count += 1
  254. # test variants:
  255. # interpreted: -maxInterpretCount:1 -maxSimpleJitRunCount:1 -bgjit-
  256. # dynapogo: -forceNative -off:simpleJit -bgJitDelay:0
  257. class TestVariant(object):
  258. def __init__(self, name, compile_flags=[], variant_not_tags=[]):
  259. self.name = name
  260. self.compile_flags = \
  261. ['-ExtendedErrorStackForTestHost',
  262. '-BaselineMode'] + compile_flags
  263. self._compile_flags_has_expansion = self._has_expansion(compile_flags)
  264. self.tags = tags.copy()
  265. self.not_tags = not_tags.union(variant_not_tags).union(
  266. ['{}_{}'.format(x, name) for x in ('fails','exclude')])
  267. self.msg_queue = Manager().Queue() # messages from multi processes
  268. self.test_result = TestResult()
  269. self.test_count = 0
  270. self._print_lines = [] # _print lines buffer
  271. self._last_len = 0
  272. if verbose:
  273. print("Added variant {0}:".format(name))
  274. print("Flags: " + ", ".join(self.compile_flags))
  275. print("Tags: " + ", ".join(self.tags))
  276. print("NotTags: " + ", ".join(self.not_tags))
  277. @staticmethod
  278. def _has_expansion(flags):
  279. return any(re.match('.*\${.*}', f) for f in flags)
  280. @staticmethod
  281. def _expand(flag, test):
  282. return re.sub('\${id}', str(test.id), flag)
  283. def _expand_compile_flags(self, test):
  284. if self._compile_flags_has_expansion:
  285. return [self._expand(flag, test) for flag in self.compile_flags]
  286. return self.compile_flags
  287. # check if this test variant should run a given test
  288. def _should_test(self, test):
  289. tags = split_tags(test.get('tags'))
  290. if not tags.isdisjoint(self.not_tags):
  291. return False
  292. if self.tags and not self.tags.issubset(tags):
  293. return False
  294. if not_compile_flags: # exclude unsupported compile-flags if any
  295. flags = test.get('compile-flags')
  296. if flags and \
  297. not not_compile_flags.isdisjoint(flags.lower().split()):
  298. return False
  299. return True
  300. # print output from multi-process run, to be sent with result message
  301. def _print(self, line):
  302. self._print_lines.append(line)
  303. # queue a test result from multi-process runs
  304. def _log_result(self, test, fail):
  305. if fail or show_passes:
  306. output = u'\n'.join(self._print_lines).encode('utf-8') # collect buffered _print output
  307. else:
  308. output = ''
  309. self._print_lines = []
  310. self.msg_queue.put((test.filename, fail, test.elapsed_time, output))
  311. # (on main process) process one queued message
  312. def _process_msg(self, msg):
  313. filename, fail, elapsed_time, output = msg
  314. self.test_result.log(filename, fail=fail)
  315. if fail or show_passes:
  316. line = '[{}/{} {:4.2f}] {} -> {}'.format(
  317. self.test_result.total_count(),
  318. self.test_count,
  319. elapsed_time,
  320. 'Failed' if fail else 'Passed',
  321. self._short_name(filename))
  322. padding = self._last_len - len(line)
  323. print(line + ' ' * padding, end='\n' if fail else '\r')
  324. self._last_len = len(line) if not fail else 0
  325. if len(output) > 0:
  326. print(output)
  327. # get a shorter test file path for display only
  328. def _short_name(self, filename):
  329. folder = os.path.basename(os.path.dirname(filename))
  330. return os.path.join(folder, os.path.basename(filename))
  331. # (on main process) wait and process one queued message
  332. def _process_one_msg(self):
  333. self._process_msg(self.msg_queue.get())
  334. # log a failed test with details
  335. def _show_failed(self, test, flags, exit_code, output,
  336. expected_output=None, timedout=False):
  337. if timedout:
  338. if warn_on_timeout:
  339. self._print('WARNING: Test timed out!')
  340. else:
  341. self._print('ERROR: Test timed out!')
  342. self._print('{} {} {}'.format(binary, ' '.join(flags), test.filename))
  343. if not return_code_only:
  344. if expected_output == None or timedout:
  345. self._print("\nOutput:")
  346. self._print("----------------------------")
  347. self._print(output.decode('utf-8'))
  348. self._print("----------------------------")
  349. else:
  350. lst_output = output.split(b'\n')
  351. lst_expected = expected_output.split(b'\n')
  352. ln = min(len(lst_output), len(lst_expected))
  353. for i in range(0, ln):
  354. if lst_output[i] != lst_expected[i]:
  355. self._print("Output: (at line " + str(i+1) + ")")
  356. self._print("----------------------------")
  357. self._print(lst_output[i])
  358. self._print("----------------------------")
  359. self._print("Expected Output:")
  360. self._print("----------------------------")
  361. self._print(lst_expected[i])
  362. self._print("----------------------------")
  363. break
  364. self._print("exit code: {}".format(exit_code))
  365. if warn_on_timeout and timedout:
  366. self._log_result(test, fail=False)
  367. else:
  368. self._log_result(test, fail=True)
  369. # temp: try find real file name on hard drive if case mismatch
  370. def _check_file(self, folder, filename):
  371. path = os.path.join(folder, filename)
  372. if os.path.isfile(path):
  373. return path # file exists on disk
  374. filename_lower = filename.lower()
  375. files = os.listdir(folder)
  376. for i in range(len(files)):
  377. if files[i].lower() == filename_lower:
  378. self._print('\nWARNING: {} should be {}\n'.format(
  379. path, files[i]))
  380. return os.path.join(folder, files[i])
  381. # can't find the file, just return the path and let it error out
  382. return path
  383. # run one test under this variant
  384. def test_one(self, test):
  385. try:
  386. test.start()
  387. self._run_one_test(test)
  388. except Exception:
  389. test.done()
  390. self._print(traceback.format_exc())
  391. self._log_result(test, fail=True)
  392. # internally perform one test run
  393. def _run_one_test(self, test):
  394. folder = test.folder
  395. js_file = test.filename = self._check_file(folder, test.files)
  396. js_output = b''
  397. working_path = os.path.dirname(js_file)
  398. flags = test.get('compile-flags') or ''
  399. flags = self._expand_compile_flags(test) + \
  400. args.flags.split() + \
  401. flags.split()
  402. if test.get('custom-config-file') != None:
  403. flags = ['-CustomConfigFile:' + test.get('custom-config-file')]
  404. if args.lldb == None:
  405. cmd = [binary] + flags + [os.path.basename(js_file)]
  406. else:
  407. lldb_file = open(working_path + '/' + os.path.basename(js_file) + '.lldb.cmd', 'w')
  408. lldb_command = ['run'] + flags + [os.path.basename(js_file)]
  409. lldb_file.write(' '.join([str(s) for s in lldb_command]));
  410. lldb_file.close()
  411. cmd = ['lldb'] + [binary] + ['-s'] + [os.path.basename(js_file) + '.lldb.cmd'] + ['-o bt'] + ['-b']
  412. test.start()
  413. proc = SP.Popen(cmd, stdout=SP.PIPE, stderr=SP.STDOUT, cwd=working_path)
  414. timeout_data = [proc, False]
  415. def timeout_func(timeout_data):
  416. timeout_data[0].kill()
  417. timeout_data[1] = True
  418. timeout = test.get('timeout', args.timeout) # test override or default
  419. timer = Timer(timeout, timeout_func, [timeout_data])
  420. skip_baseline_match = False
  421. try:
  422. timer.start()
  423. js_output = normalize_new_line(proc.communicate()[0])
  424. exit_code = proc.wait()
  425. # if -lldb was set; check if test was crashed before corrupting the output
  426. search_for = " exited with status = 0 (0x00000000)"
  427. if args.lldb != None and exit_code == 0 and js_output.index(search_for) > 0:
  428. js_output = js_output[0:js_output.index(search_for)]
  429. exit_pos = js_output.rfind('\nProcess ')
  430. if exit_pos > len(js_output) - 20: # if [Process ????? <seach for>]
  431. if 'baseline' not in test:
  432. js_output = "pass"
  433. else:
  434. skip_baseline_match = True
  435. finally:
  436. timer.cancel()
  437. test.done()
  438. # shared _show_failed args
  439. fail_args = { 'test': test, 'flags': flags,
  440. 'exit_code': exit_code, 'output': js_output }
  441. # check timed out
  442. if (timeout_data[1]):
  443. return self._show_failed(timedout=True, **fail_args)
  444. # check ch failed
  445. if exit_code != 0:
  446. return self._show_failed(**fail_args)
  447. if not return_code_only:
  448. # check output
  449. if 'baseline' not in test:
  450. # output lines must be 'pass' or 'passed' or empty with at least 1 not empty
  451. passes = 0
  452. for line in js_output.split(b'\n'):
  453. if line !=b'':
  454. low = line.lower()
  455. if low == b'pass' or low == b'passed':
  456. passes = 1
  457. else:
  458. return self._show_failed(**fail_args)
  459. if passes == 0:
  460. return self._show_failed(**fail_args)
  461. else:
  462. baseline = test.get('baseline')
  463. if not skip_baseline_match and baseline:
  464. # perform baseline comparison
  465. baseline = self._check_file(folder, baseline)
  466. with open(baseline, 'rb') as bs_file:
  467. baseline_output = bs_file.read()
  468. # Clean up carriage return
  469. # todo: remove carriage return at the end of the line
  470. # or better fix ch to output same on all platforms
  471. expected_output = normalize_new_line(baseline_output)
  472. if expected_output != js_output:
  473. return self._show_failed(
  474. expected_output=expected_output, **fail_args)
  475. # passed
  476. if verbose:
  477. self._print('{} {} {}'.format(binary, ' '.join(flags), test.filename))
  478. self._log_result(test, fail=False)
  479. # run tests under this variant, using given multiprocessing Pool
  480. def _run(self, tests, pool):
  481. # filter tests to run
  482. tests = [x for x in tests if self._should_test(x)]
  483. self.test_count += len(tests)
  484. # run tests in parallel
  485. pool.map_async(run_one, [(self,test) for test in tests])
  486. while self.test_result.total_count() != self.test_count:
  487. self._process_one_msg()
  488. # print test result summary
  489. def print_summary(self, time):
  490. print('\n############ Results for {} tests ###########'\
  491. .format(self.name))
  492. for folder, result in sorted(self.test_result.folders.items()):
  493. print('{}: {}'.format(folder, result))
  494. print("----------------------------")
  495. print('Total: {}'.format(self.test_result))
  496. print('Time taken for {} tests: {} seconds\n'.format(self.name, round(time.total_seconds(),2)))
  497. sys.stdout.flush()
  498. # run all tests from testLoader
  499. def run(self, testLoader, pool, sequential_pool):
  500. print('\n############# Starting {} tests #############'\
  501. .format(self.name))
  502. if self.tags:
  503. print(' tags: {}'.format(self.tags))
  504. for x in self.not_tags:
  505. print(' exclude: {}'.format(x))
  506. sys.stdout.flush()
  507. start_time = datetime.now()
  508. tests, sequential_tests = [], []
  509. for folder in testLoader.folders():
  510. if folder.tags.isdisjoint(self.not_tags):
  511. dest = tests if not folder.is_sequential else sequential_tests
  512. dest += folder.tests
  513. if tests:
  514. self._run(tests, pool)
  515. if sequential_tests:
  516. self._run(sequential_tests, sequential_pool)
  517. self.print_summary(datetime.now() - start_time)
  518. # global run one test function for multiprocessing, used by TestVariant
  519. def run_one(data):
  520. try:
  521. variant, test = data
  522. variant.test_one(test)
  523. except Exception:
  524. print('ERROR: Unhandled exception!!!')
  525. traceback.print_exc()
  526. # A test folder contains a list of tests and maybe some tags.
  527. class TestFolder(object):
  528. def __init__(self, tests, tags=_empty_set):
  529. self.tests = tests
  530. self.tags = tags
  531. self.is_sequential = 'sequential' in tags
  532. # TestLoader loads all tests
  533. class TestLoader(object):
  534. def __init__(self, paths):
  535. self._folder_tags = self._load_folder_tags()
  536. self._test_id = 0
  537. self._folders = []
  538. for path in paths:
  539. if os.path.isfile(path):
  540. folder, file = os.path.dirname(path), os.path.basename(path)
  541. else:
  542. folder, file = path, None
  543. ftags = self._get_folder_tags(folder)
  544. if ftags != None: # Only honor entries listed in rlexedirs.xml
  545. tests = self._load_tests(folder, file)
  546. self._folders.append(TestFolder(tests, ftags))
  547. def folders(self):
  548. return self._folders
  549. # load folder/tags info from test_root/rlexedirs.xml
  550. @staticmethod
  551. def _load_folder_tags():
  552. xmlpath = os.path.join(test_root, 'rlexedirs.xml')
  553. try:
  554. xml = ET.parse(xmlpath).getroot()
  555. except IOError:
  556. print('ERROR: failed to read {}'.format(xmlpath))
  557. exit(-1)
  558. folder_tags = {}
  559. for x in xml:
  560. d = x.find('default')
  561. key = d.find('files').text.lower() # avoid case mismatch
  562. tags = d.find('tags')
  563. folder_tags[key] = \
  564. split_tags(tags.text) if tags != None else _empty_set
  565. return folder_tags
  566. # get folder tags if any
  567. def _get_folder_tags(self, folder):
  568. key = os.path.basename(os.path.normpath(folder)).lower()
  569. return self._folder_tags.get(key)
  570. def _next_test_id(self):
  571. self._test_id += 1
  572. return self._test_id
  573. # load all tests in folder using rlexe.xml file
  574. def _load_tests(self, folder, file):
  575. try:
  576. xmlpath = os.path.join(folder, 'rlexe.xml')
  577. xml = ET.parse(xmlpath).getroot()
  578. except IOError:
  579. return []
  580. def test_override(condition, check_tag, check_value, test):
  581. target = condition.find(check_tag)
  582. if target != None and target.text == check_value:
  583. for override in condition.find('override'):
  584. test[override.tag] = override.text
  585. def load_test(testXml):
  586. test = Test(folder=folder)
  587. for c in testXml.find('default'):
  588. if c.tag == 'timeout': # timeout seconds
  589. test[c.tag] = int(c.text)
  590. elif c.tag == 'tags' and c.tag in test: # merge multiple <tags>
  591. test[c.tag] = test[c.tag] + ',' + c.text
  592. else:
  593. test[c.tag] = c.text
  594. condition = testXml.find('condition')
  595. if condition != None:
  596. test_override(condition, 'target', arch_alias, test)
  597. return test
  598. tests = [load_test(x) for x in xml]
  599. if file != None:
  600. tests = [x for x in tests if x.files == file]
  601. if len(tests) == 0 and self.is_jsfile(file):
  602. tests = [Test(folder=folder, files=file, baseline='')]
  603. for test in tests: # assign unique test.id
  604. test.id = self._next_test_id()
  605. return tests
  606. @staticmethod
  607. def is_jsfile(path):
  608. return os.path.splitext(path)[1] == '.js'
  609. def main():
  610. # Set the right timezone, the tests need Pacific Standard Time
  611. # TODO: Windows. time.tzset only supports Unix
  612. if hasattr(time, 'tzset'):
  613. os.environ['TZ'] = 'US/Pacific'
  614. time.tzset()
  615. # By default run all tests
  616. if len(args.folders) == 0:
  617. files = (os.path.join(test_root, x) for x in os.listdir(test_root))
  618. args.folders = [f for f in sorted(files) if not os.path.isfile(f)]
  619. # load all tests
  620. testLoader = TestLoader(args.folders)
  621. # test variants
  622. variants = [x for x in [
  623. TestVariant('interpreted', extra_flags + [
  624. '-maxInterpretCount:1', '-maxSimpleJitRunCount:1', '-bgjit-',
  625. '-dynamicprofilecache:profile.dpl.${id}'
  626. ], [
  627. 'require_disable_jit'
  628. ]),
  629. TestVariant('dynapogo', extra_flags + [
  630. '-forceNative', '-off:simpleJit', '-bgJitDelay:0',
  631. '-dynamicprofileinput:profile.dpl.${id}'
  632. ], [
  633. 'require_disable_jit'
  634. ]),
  635. TestVariant('disable_jit', extra_flags + [
  636. '-nonative'
  637. ], [
  638. 'exclude_interpreted', 'fails_interpreted', 'require_backend'
  639. ])
  640. ] if x.name in args.variants]
  641. # rm profile.dpl.*
  642. for f in glob.glob(test_root + '/*/profile.dpl.*'):
  643. os.remove(f)
  644. print('############# ChakraCore Test Suite #############')
  645. print('Testing {} build'.format('Test' if flavor is 'Test' else 'Debug'))
  646. print('Using {} threads'.format(processcount))
  647. # run each variant
  648. pool, sequential_pool = Pool(processcount), Pool(1)
  649. start_time = datetime.now()
  650. for variant in variants:
  651. variant.run(testLoader, pool, sequential_pool)
  652. elapsed_time = datetime.now() - start_time
  653. failed = any(variant.test_result.fail_count > 0 for variant in variants)
  654. print('[{} seconds] {}'.format(
  655. round(elapsed_time.total_seconds(),2), 'Success!' if not failed else 'Failed!'))
  656. return 1 if failed else 0
  657. if __name__ == '__main__':
  658. sys.exit(main())