1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/tests/lib/jittests.py Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,725 @@ 1.4 +#!/usr/bin/env python 1.5 +# This Source Code Form is subject to the terms of the Mozilla Public 1.6 +# License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 +# file, You can obtain one at http://mozilla.org/MPL/2.0/. 1.8 + 1.9 + 1.10 +# jit_test.py -- Python harness for JavaScript trace tests. 1.11 + 1.12 +from __future__ import print_function 1.13 +import os, posixpath, sys, tempfile, traceback, time 1.14 +import subprocess 1.15 +from subprocess import Popen, PIPE 1.16 +from threading import Thread 1.17 +import signal 1.18 +import StringIO 1.19 + 1.20 +try: 1.21 + from multiprocessing import Process, Manager, cpu_count 1.22 + HAVE_MULTIPROCESSING = True 1.23 +except ImportError: 1.24 + HAVE_MULTIPROCESSING = False 1.25 + 1.26 +from progressbar import ProgressBar, NullProgressBar 1.27 +from results import TestOutput 1.28 + 1.29 +TESTS_LIB_DIR = os.path.dirname(os.path.abspath(__file__)) 1.30 +JS_DIR = os.path.dirname(os.path.dirname(TESTS_LIB_DIR)) 1.31 +TOP_SRC_DIR = os.path.dirname(os.path.dirname(JS_DIR)) 1.32 +TEST_DIR = os.path.join(JS_DIR, 'jit-test', 'tests') 1.33 +LIB_DIR = os.path.join(JS_DIR, 'jit-test', 'lib') + os.path.sep 1.34 +JS_CACHE_DIR = os.path.join(JS_DIR, 'jit-test', '.js-cache') 1.35 +JS_TESTS_DIR = posixpath.join(JS_DIR, 'tests') 1.36 + 1.37 +# Backported from Python 3.1 posixpath.py 1.38 +def _relpath(path, start=None): 1.39 + """Return a relative version of a path""" 1.40 + 1.41 + if not path: 1.42 + raise ValueError("no path specified") 1.43 + 1.44 + if start is None: 1.45 + start = os.curdir 1.46 + 1.47 + start_list = os.path.abspath(start).split(os.sep) 1.48 + path_list = os.path.abspath(path).split(os.sep) 1.49 + 1.50 + # Work out how much of the filepath is shared by start and path. 1.51 + i = len(os.path.commonprefix([start_list, path_list])) 1.52 + 1.53 + rel_list = [os.pardir] * (len(start_list)-i) + path_list[i:] 1.54 + if not rel_list: 1.55 + return os.curdir 1.56 + return os.path.join(*rel_list) 1.57 + 1.58 +os.path.relpath = _relpath 1.59 + 1.60 +class Test: 1.61 + 1.62 + VALGRIND_CMD = [] 1.63 + paths = (d for d in os.environ['PATH'].split(os.pathsep)) 1.64 + valgrinds = (os.path.join(d, 'valgrind') for d in paths) 1.65 + if any(os.path.exists(p) for p in valgrinds): 1.66 + VALGRIND_CMD = [ 1.67 + 'valgrind', '-q', '--smc-check=all-non-file', 1.68 + '--error-exitcode=1', '--gen-suppressions=all', 1.69 + '--show-possibly-lost=no', '--leak-check=full', 1.70 + ] 1.71 + if os.uname()[0] == 'Darwin': 1.72 + VALGRIND_CMD.append('--dsymutil=yes') 1.73 + 1.74 + del paths 1.75 + del valgrinds 1.76 + 1.77 + def __init__(self, path): 1.78 + # Absolute path of the test file. 1.79 + self.path = path 1.80 + 1.81 + # Path relative to the top mozilla/ directory. 1.82 + self.relpath_top = os.path.relpath(path, TOP_SRC_DIR) 1.83 + 1.84 + # Path relative to mozilla/js/src/jit-test/tests/. 1.85 + self.relpath_tests = os.path.relpath(path, TEST_DIR) 1.86 + 1.87 + self.jitflags = [] # jit flags to enable 1.88 + self.slow = False # True means the test is slow-running 1.89 + self.allow_oom = False # True means that OOM is not considered a failure 1.90 + self.valgrind = False # True means run under valgrind 1.91 + self.tz_pacific = False # True means force Pacific time for the test 1.92 + self.expect_error = '' # Errors to expect and consider passing 1.93 + self.expect_status = 0 # Exit status to expect from shell 1.94 + 1.95 + def copy(self): 1.96 + t = Test(self.path) 1.97 + t.jitflags = self.jitflags[:] 1.98 + t.slow = self.slow 1.99 + t.allow_oom = self.allow_oom 1.100 + t.valgrind = self.valgrind 1.101 + t.tz_pacific = self.tz_pacific 1.102 + t.expect_error = self.expect_error 1.103 + t.expect_status = self.expect_status 1.104 + return t 1.105 + 1.106 + COOKIE = '|jit-test|' 1.107 + CacheDir = JS_CACHE_DIR 1.108 + 1.109 + @classmethod 1.110 + def from_file(cls, path, options): 1.111 + test = cls(path) 1.112 + 1.113 + line = open(path).readline() 1.114 + i = line.find(cls.COOKIE) 1.115 + if i != -1: 1.116 + meta = line[i + len(cls.COOKIE):].strip('\n') 1.117 + parts = meta.split(';') 1.118 + for part in parts: 1.119 + part = part.strip() 1.120 + if not part: 1.121 + continue 1.122 + name, _, value = part.partition(':') 1.123 + if value: 1.124 + value = value.strip() 1.125 + if name == 'error': 1.126 + test.expect_error = value 1.127 + elif name == 'exitstatus': 1.128 + try: 1.129 + test.expect_status = int(value, 0); 1.130 + except ValueError: 1.131 + print("warning: couldn't parse exit status %s" % value) 1.132 + elif name == 'thread-count': 1.133 + try: 1.134 + test.jitflags.append('--thread-count=' + int(value, 0)); 1.135 + except ValueError: 1.136 + print("warning: couldn't parse thread-count %s" % value) 1.137 + else: 1.138 + print('warning: unrecognized |jit-test| attribute %s' % part) 1.139 + else: 1.140 + if name == 'slow': 1.141 + test.slow = True 1.142 + elif name == 'allow-oom': 1.143 + test.allow_oom = True 1.144 + elif name == 'valgrind': 1.145 + test.valgrind = options.valgrind 1.146 + elif name == 'tz-pacific': 1.147 + test.tz_pacific = True 1.148 + elif name == 'debug': 1.149 + test.jitflags.append('--debugjit') 1.150 + elif name == 'ion-eager': 1.151 + test.jitflags.append('--ion-eager') 1.152 + elif name == 'no-ion': 1.153 + test.jitflags.append('--no-ion') 1.154 + elif name == 'dump-bytecode': 1.155 + test.jitflags.append('--dump-bytecode') 1.156 + else: 1.157 + print('warning: unrecognized |jit-test| attribute %s' % part) 1.158 + 1.159 + if options.valgrind_all: 1.160 + test.valgrind = True 1.161 + 1.162 + return test 1.163 + 1.164 + def command(self, prefix, libdir, remote_prefix=None): 1.165 + path = self.path 1.166 + if remote_prefix: 1.167 + path = self.path.replace(TEST_DIR, remote_prefix) 1.168 + 1.169 + scriptdir_var = os.path.dirname(path); 1.170 + if not scriptdir_var.endswith('/'): 1.171 + scriptdir_var += '/' 1.172 + 1.173 + # Platforms where subprocess immediately invokes exec do not care 1.174 + # whether we use double or single quotes. On windows and when using 1.175 + # a remote device, however, we have to be careful to use the quote 1.176 + # style that is the opposite of what the exec wrapper uses. 1.177 + # This uses %r to get single quotes on windows and special cases 1.178 + # the remote device. 1.179 + fmt = 'const platform=%r; const libdir=%r; const scriptdir=%r' 1.180 + if remote_prefix: 1.181 + fmt = 'const platform="%s"; const libdir="%s"; const scriptdir="%s"' 1.182 + expr = fmt % (sys.platform, libdir, scriptdir_var) 1.183 + 1.184 + # We may have specified '-a' or '-d' twice: once via --jitflags, once 1.185 + # via the "|jit-test|" line. Remove dups because they are toggles. 1.186 + cmd = prefix + ['--js-cache', Test.CacheDir] 1.187 + cmd += list(set(self.jitflags)) + ['-e', expr, '-f', path] 1.188 + if self.valgrind: 1.189 + cmd = self.VALGRIND_CMD + cmd 1.190 + return cmd 1.191 + 1.192 +def find_tests(substring=None): 1.193 + ans = [] 1.194 + for dirpath, dirnames, filenames in os.walk(TEST_DIR): 1.195 + dirnames.sort() 1.196 + filenames.sort() 1.197 + if dirpath == '.': 1.198 + continue 1.199 + for filename in filenames: 1.200 + if not filename.endswith('.js'): 1.201 + continue 1.202 + if filename in ('shell.js', 'browser.js', 'jsref.js'): 1.203 + continue 1.204 + test = os.path.join(dirpath, filename) 1.205 + if substring is None or substring in os.path.relpath(test, TEST_DIR): 1.206 + ans.append(test) 1.207 + return ans 1.208 + 1.209 +def tmppath(token): 1.210 + fd, path = tempfile.mkstemp(prefix=token) 1.211 + os.close(fd) 1.212 + return path 1.213 + 1.214 +def read_and_unlink(path): 1.215 + f = open(path) 1.216 + d = f.read() 1.217 + f.close() 1.218 + os.unlink(path) 1.219 + return d 1.220 + 1.221 +def th_run_cmd(cmdline, options, l): 1.222 + # close_fds is not supported on Windows and will cause a ValueError. 1.223 + if sys.platform != 'win32': 1.224 + options["close_fds"] = True 1.225 + p = Popen(cmdline, stdin=PIPE, stdout=PIPE, stderr=PIPE, **options) 1.226 + 1.227 + l[0] = p 1.228 + out, err = p.communicate() 1.229 + l[1] = (out, err, p.returncode) 1.230 + 1.231 +def run_timeout_cmd(cmdline, options, timeout=60.0): 1.232 + l = [ None, None ] 1.233 + timed_out = False 1.234 + th = Thread(target=th_run_cmd, args=(cmdline, options, l)) 1.235 + 1.236 + # If our SIGINT handler is set to SIG_IGN (ignore) 1.237 + # then we are running as a child process for parallel 1.238 + # execution and we must ensure to kill our child 1.239 + # when we are signaled to exit. 1.240 + sigint_handler = signal.getsignal(signal.SIGINT) 1.241 + sigterm_handler = signal.getsignal(signal.SIGTERM) 1.242 + if (sigint_handler == signal.SIG_IGN): 1.243 + def handleChildSignal(sig, frame): 1.244 + try: 1.245 + if sys.platform != 'win32': 1.246 + os.kill(l[0].pid, signal.SIGKILL) 1.247 + else: 1.248 + import ctypes 1.249 + ctypes.windll.kernel32.TerminateProcess(int(l[0]._handle), -1) 1.250 + except OSError: 1.251 + pass 1.252 + if (sig == signal.SIGTERM): 1.253 + sys.exit(0) 1.254 + signal.signal(signal.SIGINT, handleChildSignal) 1.255 + signal.signal(signal.SIGTERM, handleChildSignal) 1.256 + 1.257 + th.start() 1.258 + th.join(timeout) 1.259 + while th.isAlive(): 1.260 + if l[0] is not None: 1.261 + try: 1.262 + # In Python 3, we could just do l[0].kill(). 1.263 + if sys.platform != 'win32': 1.264 + os.kill(l[0].pid, signal.SIGKILL) 1.265 + else: 1.266 + import ctypes 1.267 + ctypes.windll.kernel32.TerminateProcess(int(l[0]._handle), -1) 1.268 + time.sleep(.1) 1.269 + timed_out = True 1.270 + except OSError: 1.271 + # Expecting a "No such process" error 1.272 + pass 1.273 + th.join() 1.274 + 1.275 + # Restore old signal handlers 1.276 + if (sigint_handler == signal.SIG_IGN): 1.277 + signal.signal(signal.SIGINT, signal.SIG_IGN) 1.278 + signal.signal(signal.SIGTERM, sigterm_handler) 1.279 + 1.280 + (out, err, code) = l[1] 1.281 + 1.282 + return (out, err, code, timed_out) 1.283 + 1.284 +def run_cmd(cmdline, env, timeout): 1.285 + return run_timeout_cmd(cmdline, { 'env': env }, timeout) 1.286 + 1.287 +def run_cmd_avoid_stdio(cmdline, env, timeout): 1.288 + stdoutPath, stderrPath = tmppath('jsstdout'), tmppath('jsstderr') 1.289 + env['JS_STDOUT'] = stdoutPath 1.290 + env['JS_STDERR'] = stderrPath 1.291 + _, __, code = run_timeout_cmd(cmdline, { 'env': env }, timeout) 1.292 + return read_and_unlink(stdoutPath), read_and_unlink(stderrPath), code 1.293 + 1.294 +def run_test(test, prefix, options): 1.295 + cmd = test.command(prefix, LIB_DIR) 1.296 + if options.show_cmd: 1.297 + print(subprocess.list2cmdline(cmd)) 1.298 + 1.299 + if options.avoid_stdio: 1.300 + run = run_cmd_avoid_stdio 1.301 + else: 1.302 + run = run_cmd 1.303 + 1.304 + env = os.environ.copy() 1.305 + if test.tz_pacific: 1.306 + env['TZ'] = 'PST8PDT' 1.307 + 1.308 + # Ensure interpreter directory is in shared library path. 1.309 + pathvar = '' 1.310 + if sys.platform.startswith('linux'): 1.311 + pathvar = 'LD_LIBRARY_PATH' 1.312 + elif sys.platform.startswith('darwin'): 1.313 + pathvar = 'DYLD_LIBRARY_PATH' 1.314 + elif sys.platform.startswith('win'): 1.315 + pathvar = 'PATH' 1.316 + if pathvar: 1.317 + bin_dir = os.path.dirname(cmd[0]) 1.318 + if pathvar in env: 1.319 + env[pathvar] = '%s%s%s' % (bin_dir, os.pathsep, env[pathvar]) 1.320 + else: 1.321 + env[pathvar] = bin_dir 1.322 + 1.323 + out, err, code, timed_out = run(cmd, env, options.timeout) 1.324 + return TestOutput(test, cmd, out, err, code, None, timed_out) 1.325 + 1.326 +def run_test_remote(test, device, prefix, options): 1.327 + cmd = test.command(prefix, posixpath.join(options.remote_test_root, 'lib/'), posixpath.join(options.remote_test_root, 'tests')) 1.328 + if options.show_cmd: 1.329 + print(subprocess.list2cmdline(cmd)) 1.330 + 1.331 + env = {} 1.332 + if test.tz_pacific: 1.333 + env['TZ'] = 'PST8PDT' 1.334 + 1.335 + env['LD_LIBRARY_PATH'] = options.remote_test_root 1.336 + 1.337 + buf = StringIO.StringIO() 1.338 + returncode = device.shell(cmd, buf, env=env, cwd=options.remote_test_root, 1.339 + timeout=int(options.timeout)) 1.340 + 1.341 + out = buf.getvalue() 1.342 + # We can't distinguish between stdout and stderr so we pass 1.343 + # the same buffer to both. 1.344 + return TestOutput(test, cmd, out, out, returncode, None, False) 1.345 + 1.346 +def check_output(out, err, rc, timed_out, test): 1.347 + if timed_out: 1.348 + # The shell sometimes hangs on shutdown on Windows 7 and Windows 1.349 + # Server 2008. See bug 970063 comment 7 for a description of the 1.350 + # problem. Until bug 956899 is fixed, ignore timeouts on these 1.351 + # platforms (versions 6.0 and 6.1). 1.352 + if sys.platform == 'win32': 1.353 + ver = sys.getwindowsversion() 1.354 + if ver.major == 6 and ver.minor <= 1: 1.355 + return True 1.356 + return False 1.357 + 1.358 + if test.expect_error: 1.359 + # The shell exits with code 3 on uncaught exceptions. 1.360 + # Sometimes 0 is returned on Windows for unknown reasons. 1.361 + # See bug 899697. 1.362 + if sys.platform in ['win32', 'cygwin']: 1.363 + if rc != 3 and rc != 0: 1.364 + return False 1.365 + else: 1.366 + if rc != 3: 1.367 + return False 1.368 + 1.369 + return test.expect_error in err 1.370 + 1.371 + for line in out.split('\n'): 1.372 + if line.startswith('Trace stats check failed'): 1.373 + return False 1.374 + 1.375 + for line in err.split('\n'): 1.376 + if 'Assertion failed:' in line: 1.377 + return False 1.378 + 1.379 + if rc != test.expect_status: 1.380 + # Tests which expect a timeout check for exit code 6. 1.381 + # Sometimes 0 is returned on Windows for unknown reasons. 1.382 + # See bug 899697. 1.383 + if sys.platform in ['win32', 'cygwin'] and rc == 0: 1.384 + return True 1.385 + 1.386 + # Allow a non-zero exit code if we want to allow OOM, but only if we 1.387 + # actually got OOM. 1.388 + return test.allow_oom and 'out of memory' in err and 'Assertion failure' not in err 1.389 + 1.390 + return True 1.391 + 1.392 +def print_tinderbox(ok, res): 1.393 + # Output test failures in a TBPL parsable format, eg: 1.394 + # TEST-RESULT | filename.js | Failure description (code N, args "--foobar") 1.395 + # 1.396 + # Example: 1.397 + # TEST-PASS | foo/bar/baz.js | (code 0, args "--ion-eager") 1.398 + # TEST-UNEXPECTED-FAIL | foo/bar/baz.js | TypeError: or something (code -9, args "--no-ion") 1.399 + # INFO exit-status : 3 1.400 + # INFO timed-out : False 1.401 + # INFO stdout > foo 1.402 + # INFO stdout > bar 1.403 + # INFO stdout > baz 1.404 + # INFO stderr 2> TypeError: or something 1.405 + # TEST-UNEXPECTED-FAIL | jit_test.py: Test execution interrupted by user 1.406 + result = "TEST-PASS" if ok else "TEST-UNEXPECTED-FAIL" 1.407 + message = "Success" if ok else res.describe_failure() 1.408 + jitflags = " ".join(res.test.jitflags) 1.409 + print("{} | {} | {} (code {}, args \"{}\")".format( 1.410 + result, res.test.relpath_top, message, res.rc, jitflags)) 1.411 + 1.412 + # For failed tests, print as much information as we have, to aid debugging. 1.413 + if ok: 1.414 + return 1.415 + print("INFO exit-status : {}".format(res.rc)) 1.416 + print("INFO timed-out : {}".format(res.timed_out)) 1.417 + for line in res.out.splitlines(): 1.418 + print("INFO stdout > " + line.strip()) 1.419 + for line in res.err.splitlines(): 1.420 + print("INFO stderr 2> " + line.strip()) 1.421 + 1.422 +def wrap_parallel_run_test(test, prefix, resultQueue, options): 1.423 + # Ignore SIGINT in the child 1.424 + signal.signal(signal.SIGINT, signal.SIG_IGN) 1.425 + result = run_test(test, prefix, options) 1.426 + resultQueue.put(result) 1.427 + return result 1.428 + 1.429 +def run_tests_parallel(tests, prefix, options): 1.430 + # This queue will contain the results of the various tests run. 1.431 + # We could make this queue a global variable instead of using 1.432 + # a manager to share, but this will not work on Windows. 1.433 + queue_manager = Manager() 1.434 + async_test_result_queue = queue_manager.Queue() 1.435 + 1.436 + # This queue will be used by the result process to indicate 1.437 + # that it has received a result and we can start a new process 1.438 + # on our end. The advantage is that we don't have to sleep and 1.439 + # check for worker completion ourselves regularly. 1.440 + notify_queue = queue_manager.Queue() 1.441 + 1.442 + # This queue will contain the return value of the function 1.443 + # processing the test results. 1.444 + total_tests = len(tests) * options.repeat 1.445 + result_process_return_queue = queue_manager.Queue() 1.446 + result_process = Process(target=process_test_results_parallel, 1.447 + args=(async_test_result_queue, result_process_return_queue, 1.448 + notify_queue, total_tests, options)) 1.449 + result_process.start() 1.450 + 1.451 + # Ensure that a SIGTERM is handled the same way as SIGINT 1.452 + # to terminate all child processes. 1.453 + sigint_handler = signal.getsignal(signal.SIGINT) 1.454 + signal.signal(signal.SIGTERM, sigint_handler) 1.455 + 1.456 + worker_processes = [] 1.457 + 1.458 + def remove_completed_workers(workers): 1.459 + new_workers = [] 1.460 + for worker in workers: 1.461 + if worker.is_alive(): 1.462 + new_workers.append(worker) 1.463 + else: 1.464 + worker.join() 1.465 + return new_workers 1.466 + 1.467 + try: 1.468 + testcnt = 0 1.469 + # Initially start as many jobs as allowed to run parallel 1.470 + for i in range(min(options.max_jobs,total_tests)): 1.471 + notify_queue.put(True) 1.472 + 1.473 + # For every item in the notify queue, start one new worker. 1.474 + # Every completed worker adds a new item to this queue. 1.475 + while notify_queue.get(): 1.476 + if (testcnt < total_tests): 1.477 + # Start one new worker 1.478 + test = tests[testcnt % len(tests)] 1.479 + worker_process = Process(target=wrap_parallel_run_test, args=(test, prefix, async_test_result_queue, options)) 1.480 + worker_processes.append(worker_process) 1.481 + worker_process.start() 1.482 + testcnt += 1 1.483 + 1.484 + # Collect completed workers 1.485 + worker_processes = remove_completed_workers(worker_processes) 1.486 + else: 1.487 + break 1.488 + 1.489 + # Wait for all processes to terminate 1.490 + while len(worker_processes) > 0: 1.491 + worker_processes = remove_completed_workers(worker_processes) 1.492 + 1.493 + # Signal completion to result processor, then wait for it to complete on its own 1.494 + async_test_result_queue.put(None) 1.495 + result_process.join() 1.496 + 1.497 + # Return what the result process has returned to us 1.498 + return result_process_return_queue.get() 1.499 + except (Exception, KeyboardInterrupt) as e: 1.500 + # Print the exception if it's not an interrupt, 1.501 + # might point to a bug or other faulty condition 1.502 + if not isinstance(e,KeyboardInterrupt): 1.503 + traceback.print_exc() 1.504 + 1.505 + for worker in worker_processes: 1.506 + try: 1.507 + worker.terminate() 1.508 + except: 1.509 + pass 1.510 + 1.511 + result_process.terminate() 1.512 + 1.513 + return False 1.514 + 1.515 +def get_parallel_results(async_test_result_queue, notify_queue): 1.516 + while True: 1.517 + async_test_result = async_test_result_queue.get() 1.518 + 1.519 + # Check if we are supposed to terminate 1.520 + if (async_test_result == None): 1.521 + return 1.522 + 1.523 + # Notify parent that we got a result 1.524 + notify_queue.put(True) 1.525 + 1.526 + yield async_test_result 1.527 + 1.528 +def process_test_results_parallel(async_test_result_queue, return_queue, notify_queue, num_tests, options): 1.529 + gen = get_parallel_results(async_test_result_queue, notify_queue) 1.530 + ok = process_test_results(gen, num_tests, options) 1.531 + return_queue.put(ok) 1.532 + 1.533 +def print_test_summary(num_tests, failures, complete, doing, options): 1.534 + if failures: 1.535 + if options.write_failures: 1.536 + try: 1.537 + out = open(options.write_failures, 'w') 1.538 + # Don't write duplicate entries when we are doing multiple failures per job. 1.539 + written = set() 1.540 + for res in failures: 1.541 + if res.test.path not in written: 1.542 + out.write(os.path.relpath(res.test.path, TEST_DIR) + '\n') 1.543 + if options.write_failure_output: 1.544 + out.write(res.out) 1.545 + out.write(res.err) 1.546 + out.write('Exit code: ' + str(res.rc) + "\n") 1.547 + written.add(res.test.path) 1.548 + out.close() 1.549 + except IOError: 1.550 + sys.stderr.write("Exception thrown trying to write failure file '%s'\n"% 1.551 + options.write_failures) 1.552 + traceback.print_exc() 1.553 + sys.stderr.write('---\n') 1.554 + 1.555 + def show_test(res): 1.556 + if options.show_failed: 1.557 + print(' ' + subprocess.list2cmdline(res.cmd)) 1.558 + else: 1.559 + print(' ' + ' '.join(res.test.jitflags + [res.test.path])) 1.560 + 1.561 + print('FAILURES:') 1.562 + for res in failures: 1.563 + if not res.timed_out: 1.564 + show_test(res) 1.565 + 1.566 + print('TIMEOUTS:') 1.567 + for res in failures: 1.568 + if res.timed_out: 1.569 + show_test(res) 1.570 + else: 1.571 + print('PASSED ALL' + ('' if complete else ' (partial run -- interrupted by user %s)' % doing)) 1.572 + 1.573 + if options.tinderbox: 1.574 + num_failures = len(failures) if failures else 0 1.575 + print('Result summary:') 1.576 + print('Passed: %d' % (num_tests - num_failures)) 1.577 + print('Failed: %d' % num_failures) 1.578 + 1.579 + return not failures 1.580 + 1.581 +def process_test_results(results, num_tests, options): 1.582 + pb = NullProgressBar() 1.583 + if not options.hide_progress and not options.show_cmd and ProgressBar.conservative_isatty(): 1.584 + fmt = [ 1.585 + {'value': 'PASS', 'color': 'green'}, 1.586 + {'value': 'FAIL', 'color': 'red'}, 1.587 + {'value': 'TIMEOUT', 'color': 'blue'}, 1.588 + {'value': 'SKIP', 'color': 'brightgray'}, 1.589 + ] 1.590 + pb = ProgressBar(num_tests, fmt) 1.591 + 1.592 + failures = [] 1.593 + timeouts = 0 1.594 + complete = False 1.595 + doing = 'before starting' 1.596 + try: 1.597 + for i, res in enumerate(results): 1.598 + if options.show_output: 1.599 + sys.stdout.write(res.out) 1.600 + sys.stdout.write(res.err) 1.601 + sys.stdout.write('Exit code: %s\n' % res.rc) 1.602 + if res.test.valgrind: 1.603 + sys.stdout.write(res.err) 1.604 + 1.605 + ok = check_output(res.out, res.err, res.rc, res.timed_out, res.test) 1.606 + doing = 'after %s' % res.test.relpath_tests 1.607 + if not ok: 1.608 + failures.append(res) 1.609 + if res.timed_out: 1.610 + pb.message("TIMEOUT - %s" % res.test.relpath_tests) 1.611 + timeouts += 1 1.612 + else: 1.613 + pb.message("FAIL - %s" % res.test.relpath_tests) 1.614 + 1.615 + if options.tinderbox: 1.616 + print_tinderbox(ok, res) 1.617 + 1.618 + n = i + 1 1.619 + pb.update(n, { 1.620 + 'PASS': n - len(failures), 1.621 + 'FAIL': len(failures), 1.622 + 'TIMEOUT': timeouts, 1.623 + 'SKIP': 0} 1.624 + ) 1.625 + complete = True 1.626 + except KeyboardInterrupt: 1.627 + print("TEST-UNEXPECTED-FAIL | jit_test.py" + 1.628 + " : Test execution interrupted by user") 1.629 + 1.630 + pb.finish(True) 1.631 + return print_test_summary(num_tests, failures, complete, doing, options) 1.632 + 1.633 +def get_serial_results(tests, prefix, options): 1.634 + for i in xrange(0, options.repeat): 1.635 + for test in tests: 1.636 + yield run_test(test, prefix, options) 1.637 + 1.638 +def run_tests(tests, prefix, options): 1.639 + gen = get_serial_results(tests, prefix, options) 1.640 + ok = process_test_results(gen, len(tests) * options.repeat, options) 1.641 + return ok 1.642 + 1.643 +def get_remote_results(tests, device, prefix, options): 1.644 + for i in xrange(0, options.repeat): 1.645 + for test in tests: 1.646 + yield run_test_remote(test, device, prefix, options) 1.647 + 1.648 +def push_libs(options, device): 1.649 + # This saves considerable time in pushing unnecessary libraries 1.650 + # to the device but needs to be updated if the dependencies change. 1.651 + required_libs = ['libnss3.so', 'libmozglue.so'] 1.652 + 1.653 + for file in os.listdir(options.local_lib): 1.654 + if file in required_libs: 1.655 + remote_file = posixpath.join(options.remote_test_root, file) 1.656 + device.pushFile(os.path.join(options.local_lib, file), remote_file) 1.657 + 1.658 +def push_progs(options, device, progs): 1.659 + for local_file in progs: 1.660 + remote_file = posixpath.join(options.remote_test_root, os.path.basename(local_file)) 1.661 + device.pushFile(local_file, remote_file) 1.662 + 1.663 +def run_tests_remote(tests, prefix, options): 1.664 + # Setup device with everything needed to run our tests. 1.665 + from mozdevice import devicemanager, devicemanagerADB, devicemanagerSUT 1.666 + 1.667 + if options.device_transport == 'adb': 1.668 + if options.device_ip: 1.669 + dm = devicemanagerADB.DeviceManagerADB(options.device_ip, options.device_port, deviceSerial=options.device_serial, packageName=None, deviceRoot=options.remote_test_root) 1.670 + else: 1.671 + dm = devicemanagerADB.DeviceManagerADB(deviceSerial=options.device_serial, packageName=None, deviceRoot=options.remote_test_root) 1.672 + else: 1.673 + dm = devicemanagerSUT.DeviceManagerSUT(options.device_ip, options.device_port, deviceRoot=options.remote_test_root) 1.674 + if options.device_ip == None: 1.675 + print('Error: you must provide a device IP to connect to via the --device option') 1.676 + sys.exit(1) 1.677 + 1.678 + # Update the test root to point to our test directory. 1.679 + jit_tests_dir = posixpath.join(options.remote_test_root, 'jit-tests') 1.680 + options.remote_test_root = posixpath.join(jit_tests_dir, 'jit-tests') 1.681 + 1.682 + # Push js shell and libraries. 1.683 + if dm.dirExists(jit_tests_dir): 1.684 + dm.removeDir(jit_tests_dir) 1.685 + dm.mkDirs(options.remote_test_root) 1.686 + push_libs(options, dm) 1.687 + push_progs(options, dm, [prefix[0]]) 1.688 + dm.chmodDir(options.remote_test_root) 1.689 + 1.690 + Test.CacheDir = posixpath.join(options.remote_test_root, '.js-cache') 1.691 + dm.mkDir(Test.CacheDir) 1.692 + 1.693 + dm.pushDir(JS_TESTS_DIR, posixpath.join(jit_tests_dir, 'tests'), timeout=600) 1.694 + 1.695 + dm.pushDir(os.path.dirname(TEST_DIR), options.remote_test_root, timeout=600) 1.696 + prefix[0] = os.path.join(options.remote_test_root, 'js') 1.697 + 1.698 + # Run all tests. 1.699 + gen = get_remote_results(tests, dm, prefix, options) 1.700 + ok = process_test_results(gen, len(tests) * options.repeat, options) 1.701 + return ok 1.702 + 1.703 +def parse_jitflags(options): 1.704 + jitflags = [ [ '-' + flag for flag in flags ] 1.705 + for flags in options.jitflags.split(',') ] 1.706 + for flags in jitflags: 1.707 + for flag in flags: 1.708 + if flag not in ('-m', '-a', '-p', '-d', '-n'): 1.709 + print('Invalid jit flag: "%s"' % flag) 1.710 + sys.exit(1) 1.711 + return jitflags 1.712 + 1.713 +def platform_might_be_android(): 1.714 + try: 1.715 + # The python package for SL4A provides an |android| module. 1.716 + # If that module is present, we're likely in SL4A-python on 1.717 + # device. False positives and negatives are possible, 1.718 + # however. 1.719 + import android 1.720 + return True 1.721 + except ImportError: 1.722 + return False 1.723 + 1.724 +def stdio_might_be_broken(): 1.725 + return platform_might_be_android() 1.726 + 1.727 +if __name__ == '__main__': 1.728 + print('Use ../jit-test/jit_test.py to run these tests.')