1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/tests/lib/tests.py Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,154 @@ 1.4 +# Library for JSTest tests. 1.5 +# 1.6 +# This contains classes that represent an individual test, including 1.7 +# metadata, and know how to run the tests and determine failures. 1.8 + 1.9 +import datetime, os, sys, time 1.10 +from subprocess import Popen, PIPE 1.11 +from threading import Thread 1.12 + 1.13 +from results import TestOutput 1.14 + 1.15 +# When run on tbpl, we run each test multiple times with the following arguments. 1.16 +TBPL_FLAGS = [ 1.17 + [], # no flags, normal baseline and ion 1.18 + ['--ion-eager', '--ion-parallel-compile=off'], # implies --baseline-eager 1.19 + ['--ion-eager', '--ion-parallel-compile=off', '--ion-check-range-analysis', '--no-sse3'], 1.20 + ['--baseline-eager'], 1.21 + ['--baseline-eager', '--no-fpu'], 1.22 + ['--no-baseline', '--no-ion'], 1.23 +] 1.24 + 1.25 +def do_run_cmd(cmd): 1.26 + l = [ None, None ] 1.27 + th_run_cmd(cmd, l) 1.28 + return l[1] 1.29 + 1.30 +def set_limits(): 1.31 + # resource module not supported on all platforms 1.32 + try: 1.33 + import resource 1.34 + GB = 2**30 1.35 + resource.setrlimit(resource.RLIMIT_AS, (2*GB, 2*GB)) 1.36 + except: 1.37 + return 1.38 + 1.39 +def th_run_cmd(cmd, l): 1.40 + t0 = datetime.datetime.now() 1.41 + 1.42 + # close_fds and preexec_fn are not supported on Windows and will 1.43 + # cause a ValueError. 1.44 + options = {} 1.45 + if sys.platform != 'win32': 1.46 + options["close_fds"] = True 1.47 + options["preexec_fn"] = set_limits 1.48 + p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE, **options) 1.49 + 1.50 + l[0] = p 1.51 + out, err = p.communicate() 1.52 + t1 = datetime.datetime.now() 1.53 + dd = t1-t0 1.54 + dt = dd.seconds + 1e-6 * dd.microseconds 1.55 + l[1] = (out, err, p.returncode, dt) 1.56 + 1.57 +def run_cmd(cmd, timeout=60.0): 1.58 + if timeout is None: 1.59 + return do_run_cmd(cmd) 1.60 + 1.61 + l = [ None, None ] 1.62 + timed_out = False 1.63 + th = Thread(target=th_run_cmd, args=(cmd, l)) 1.64 + th.start() 1.65 + th.join(timeout) 1.66 + while th.isAlive(): 1.67 + if l[0] is not None: 1.68 + try: 1.69 + # In Python 3, we could just do l[0].kill(). 1.70 + import signal 1.71 + if sys.platform != 'win32': 1.72 + os.kill(l[0].pid, signal.SIGKILL) 1.73 + time.sleep(.1) 1.74 + timed_out = True 1.75 + except OSError: 1.76 + # Expecting a "No such process" error 1.77 + pass 1.78 + th.join() 1.79 + return l[1] + (timed_out,) 1.80 + 1.81 +class Test(object): 1.82 + """A runnable test.""" 1.83 + def __init__(self, path): 1.84 + self.path = path # str: path of JS file relative to tests root dir 1.85 + 1.86 + @staticmethod 1.87 + def prefix_command(path): 1.88 + """Return the '-f shell.js' options needed to run a test with the given path.""" 1.89 + if path == '': 1.90 + return [ '-f', 'shell.js' ] 1.91 + head, base = os.path.split(path) 1.92 + return Test.prefix_command(head) + [ '-f', os.path.join(path, 'shell.js') ] 1.93 + 1.94 + def get_command(self, js_cmd_prefix): 1.95 + dirname, filename = os.path.split(self.path) 1.96 + cmd = js_cmd_prefix + self.options + Test.prefix_command(dirname) + [ '-f', self.path ] 1.97 + return cmd 1.98 + 1.99 + def run(self, js_cmd_prefix, timeout=30.0): 1.100 + cmd = self.get_command(js_cmd_prefix) 1.101 + out, err, rc, dt, timed_out = run_cmd(cmd, timeout) 1.102 + return TestOutput(self, cmd, out, err, rc, dt, timed_out) 1.103 + 1.104 +class TestCase(Test): 1.105 + """A test case consisting of a test and an expected result.""" 1.106 + js_cmd_prefix = None 1.107 + 1.108 + def __init__(self, path): 1.109 + Test.__init__(self, path) 1.110 + self.enable = True # bool: True => run test, False => don't run 1.111 + self.expect = True # bool: expected result, True => pass 1.112 + self.random = False # bool: True => ignore output as 'random' 1.113 + self.slow = False # bool: True => test may run slowly 1.114 + self.options = [] # [str]: Extra options to pass to the shell 1.115 + 1.116 + # The terms parsed to produce the above properties. 1.117 + self.terms = None 1.118 + 1.119 + # The tag between |...| in the test header. 1.120 + self.tag = None 1.121 + 1.122 + # Anything occuring after -- in the test header. 1.123 + self.comment = None 1.124 + 1.125 + def __str__(self): 1.126 + ans = self.path 1.127 + if not self.enable: 1.128 + ans += ', skip' 1.129 + if not self.expect: 1.130 + ans += ', fails' 1.131 + if self.random: 1.132 + ans += ', random' 1.133 + if self.slow: 1.134 + ans += ', slow' 1.135 + if '-d' in self.options: 1.136 + ans += ', debugMode' 1.137 + return ans 1.138 + 1.139 + @classmethod 1.140 + def set_js_cmd_prefix(self, js_path, js_args, debugger_prefix): 1.141 + parts = [] 1.142 + if debugger_prefix: 1.143 + parts += debugger_prefix 1.144 + parts.append(js_path) 1.145 + if js_args: 1.146 + parts += js_args 1.147 + self.js_cmd_prefix = parts 1.148 + 1.149 + def __cmp__(self, other): 1.150 + if self.path == other.path: 1.151 + return 0 1.152 + elif self.path < other.path: 1.153 + return -1 1.154 + return 1 1.155 + 1.156 + def __hash__(self): 1.157 + return self.path.__hash__()