|
1 # Library for JSTest tests. |
|
2 # |
|
3 # This contains classes that represent an individual test, including |
|
4 # metadata, and know how to run the tests and determine failures. |
|
5 |
|
6 import datetime, os, sys, time |
|
7 from subprocess import Popen, PIPE |
|
8 from threading import Thread |
|
9 |
|
10 from results import TestOutput |
|
11 |
|
12 # When run on tbpl, we run each test multiple times with the following arguments. |
|
13 TBPL_FLAGS = [ |
|
14 [], # no flags, normal baseline and ion |
|
15 ['--ion-eager', '--ion-parallel-compile=off'], # implies --baseline-eager |
|
16 ['--ion-eager', '--ion-parallel-compile=off', '--ion-check-range-analysis', '--no-sse3'], |
|
17 ['--baseline-eager'], |
|
18 ['--baseline-eager', '--no-fpu'], |
|
19 ['--no-baseline', '--no-ion'], |
|
20 ] |
|
21 |
|
22 def do_run_cmd(cmd): |
|
23 l = [ None, None ] |
|
24 th_run_cmd(cmd, l) |
|
25 return l[1] |
|
26 |
|
27 def set_limits(): |
|
28 # resource module not supported on all platforms |
|
29 try: |
|
30 import resource |
|
31 GB = 2**30 |
|
32 resource.setrlimit(resource.RLIMIT_AS, (2*GB, 2*GB)) |
|
33 except: |
|
34 return |
|
35 |
|
36 def th_run_cmd(cmd, l): |
|
37 t0 = datetime.datetime.now() |
|
38 |
|
39 # close_fds and preexec_fn are not supported on Windows and will |
|
40 # cause a ValueError. |
|
41 options = {} |
|
42 if sys.platform != 'win32': |
|
43 options["close_fds"] = True |
|
44 options["preexec_fn"] = set_limits |
|
45 p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE, **options) |
|
46 |
|
47 l[0] = p |
|
48 out, err = p.communicate() |
|
49 t1 = datetime.datetime.now() |
|
50 dd = t1-t0 |
|
51 dt = dd.seconds + 1e-6 * dd.microseconds |
|
52 l[1] = (out, err, p.returncode, dt) |
|
53 |
|
54 def run_cmd(cmd, timeout=60.0): |
|
55 if timeout is None: |
|
56 return do_run_cmd(cmd) |
|
57 |
|
58 l = [ None, None ] |
|
59 timed_out = False |
|
60 th = Thread(target=th_run_cmd, args=(cmd, l)) |
|
61 th.start() |
|
62 th.join(timeout) |
|
63 while th.isAlive(): |
|
64 if l[0] is not None: |
|
65 try: |
|
66 # In Python 3, we could just do l[0].kill(). |
|
67 import signal |
|
68 if sys.platform != 'win32': |
|
69 os.kill(l[0].pid, signal.SIGKILL) |
|
70 time.sleep(.1) |
|
71 timed_out = True |
|
72 except OSError: |
|
73 # Expecting a "No such process" error |
|
74 pass |
|
75 th.join() |
|
76 return l[1] + (timed_out,) |
|
77 |
|
78 class Test(object): |
|
79 """A runnable test.""" |
|
80 def __init__(self, path): |
|
81 self.path = path # str: path of JS file relative to tests root dir |
|
82 |
|
83 @staticmethod |
|
84 def prefix_command(path): |
|
85 """Return the '-f shell.js' options needed to run a test with the given path.""" |
|
86 if path == '': |
|
87 return [ '-f', 'shell.js' ] |
|
88 head, base = os.path.split(path) |
|
89 return Test.prefix_command(head) + [ '-f', os.path.join(path, 'shell.js') ] |
|
90 |
|
91 def get_command(self, js_cmd_prefix): |
|
92 dirname, filename = os.path.split(self.path) |
|
93 cmd = js_cmd_prefix + self.options + Test.prefix_command(dirname) + [ '-f', self.path ] |
|
94 return cmd |
|
95 |
|
96 def run(self, js_cmd_prefix, timeout=30.0): |
|
97 cmd = self.get_command(js_cmd_prefix) |
|
98 out, err, rc, dt, timed_out = run_cmd(cmd, timeout) |
|
99 return TestOutput(self, cmd, out, err, rc, dt, timed_out) |
|
100 |
|
101 class TestCase(Test): |
|
102 """A test case consisting of a test and an expected result.""" |
|
103 js_cmd_prefix = None |
|
104 |
|
105 def __init__(self, path): |
|
106 Test.__init__(self, path) |
|
107 self.enable = True # bool: True => run test, False => don't run |
|
108 self.expect = True # bool: expected result, True => pass |
|
109 self.random = False # bool: True => ignore output as 'random' |
|
110 self.slow = False # bool: True => test may run slowly |
|
111 self.options = [] # [str]: Extra options to pass to the shell |
|
112 |
|
113 # The terms parsed to produce the above properties. |
|
114 self.terms = None |
|
115 |
|
116 # The tag between |...| in the test header. |
|
117 self.tag = None |
|
118 |
|
119 # Anything occuring after -- in the test header. |
|
120 self.comment = None |
|
121 |
|
122 def __str__(self): |
|
123 ans = self.path |
|
124 if not self.enable: |
|
125 ans += ', skip' |
|
126 if not self.expect: |
|
127 ans += ', fails' |
|
128 if self.random: |
|
129 ans += ', random' |
|
130 if self.slow: |
|
131 ans += ', slow' |
|
132 if '-d' in self.options: |
|
133 ans += ', debugMode' |
|
134 return ans |
|
135 |
|
136 @classmethod |
|
137 def set_js_cmd_prefix(self, js_path, js_args, debugger_prefix): |
|
138 parts = [] |
|
139 if debugger_prefix: |
|
140 parts += debugger_prefix |
|
141 parts.append(js_path) |
|
142 if js_args: |
|
143 parts += js_args |
|
144 self.js_cmd_prefix = parts |
|
145 |
|
146 def __cmp__(self, other): |
|
147 if self.path == other.path: |
|
148 return 0 |
|
149 elif self.path < other.path: |
|
150 return -1 |
|
151 return 1 |
|
152 |
|
153 def __hash__(self): |
|
154 return self.path.__hash__() |