|
1 #!/usr/bin/env python |
|
2 # This Source Code Form is subject to the terms of the Mozilla Public |
|
3 # License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 # file, You can obtain one at http://mozilla.org/MPL/2.0/. |
|
5 |
|
6 import math, os, posixpath, shlex, shutil, subprocess, sys, traceback |
|
7 |
|
8 def add_libdir_to_path(): |
|
9 from os.path import dirname, exists, join, realpath |
|
10 js_src_dir = dirname(dirname(realpath(sys.argv[0]))) |
|
11 assert exists(join(js_src_dir,'jsapi.h')) |
|
12 sys.path.insert(0, join(js_src_dir, 'lib')) |
|
13 sys.path.insert(0, join(js_src_dir, 'tests', 'lib')) |
|
14 |
|
15 add_libdir_to_path() |
|
16 |
|
17 import jittests |
|
18 from tests import TBPL_FLAGS |
|
19 |
|
20 def main(argv): |
|
21 |
|
22 # If no multiprocessing is available, fallback to serial test execution |
|
23 max_jobs_default = 1 |
|
24 if jittests.HAVE_MULTIPROCESSING: |
|
25 try: |
|
26 max_jobs_default = jittests.cpu_count() |
|
27 except NotImplementedError: |
|
28 pass |
|
29 |
|
30 # The [TESTS] optional arguments are paths of test files relative |
|
31 # to the jit-test/tests directory. |
|
32 |
|
33 from optparse import OptionParser |
|
34 op = OptionParser(usage='%prog [options] JS_SHELL [TESTS]') |
|
35 op.add_option('-s', '--show-cmd', dest='show_cmd', action='store_true', |
|
36 help='show js shell command run') |
|
37 op.add_option('-f', '--show-failed-cmd', dest='show_failed', |
|
38 action='store_true', help='show command lines of failed tests') |
|
39 op.add_option('-o', '--show-output', dest='show_output', action='store_true', |
|
40 help='show output from js shell') |
|
41 op.add_option('-x', '--exclude', dest='exclude', action='append', |
|
42 help='exclude given test dir or path') |
|
43 op.add_option('--no-slow', dest='run_slow', action='store_false', |
|
44 help='do not run tests marked as slow') |
|
45 op.add_option('-t', '--timeout', dest='timeout', type=float, default=150.0, |
|
46 help='set test timeout in seconds') |
|
47 op.add_option('--no-progress', dest='hide_progress', action='store_true', |
|
48 help='hide progress bar') |
|
49 op.add_option('--tinderbox', dest='tinderbox', action='store_true', |
|
50 help='Tinderbox-parseable output format') |
|
51 op.add_option('--args', dest='shell_args', default='', |
|
52 help='extra args to pass to the JS shell') |
|
53 op.add_option('-w', '--write-failures', dest='write_failures', metavar='FILE', |
|
54 help='Write a list of failed tests to [FILE]') |
|
55 op.add_option('-r', '--read-tests', dest='read_tests', metavar='FILE', |
|
56 help='Run test files listed in [FILE]') |
|
57 op.add_option('-R', '--retest', dest='retest', metavar='FILE', |
|
58 help='Retest using test list file [FILE]') |
|
59 op.add_option('-g', '--debug', dest='debug', action='store_true', |
|
60 help='Run test in gdb') |
|
61 op.add_option('--valgrind', dest='valgrind', action='store_true', |
|
62 help='Enable the |valgrind| flag, if valgrind is in $PATH.') |
|
63 op.add_option('--valgrind-all', dest='valgrind_all', action='store_true', |
|
64 help='Run all tests with valgrind, if valgrind is in $PATH.') |
|
65 op.add_option('--jitflags', dest='jitflags', default='', |
|
66 help='Example: --jitflags=m,mn to run each test with "-m" and "-m -n" [default="%default"]. ' + |
|
67 'Long flags, such as "--ion-eager", should be set using --args.') |
|
68 op.add_option('--avoid-stdio', dest='avoid_stdio', action='store_true', |
|
69 help='Use js-shell file indirection instead of piping stdio.') |
|
70 op.add_option('--write-failure-output', dest='write_failure_output', action='store_true', |
|
71 help='With --write-failures=FILE, additionally write the output of failed tests to [FILE]') |
|
72 op.add_option('--ion', dest='ion', action='store_true', |
|
73 help='Run tests once with --ion-eager and once with --baseline-eager (ignores --jitflags)') |
|
74 op.add_option('--tbpl', dest='tbpl', action='store_true', |
|
75 help='Run tests with all IonMonkey option combinations (ignores --jitflags)') |
|
76 op.add_option('-j', '--worker-count', dest='max_jobs', type=int, default=max_jobs_default, |
|
77 help='Number of tests to run in parallel (default %default)') |
|
78 op.add_option('--remote', action='store_true', |
|
79 help='Run tests on a remote device') |
|
80 op.add_option('--deviceIP', action='store', |
|
81 type='string', dest='device_ip', |
|
82 help='IP address of remote device to test') |
|
83 op.add_option('--devicePort', action='store', |
|
84 type=int, dest='device_port', default=20701, |
|
85 help='port of remote device to test') |
|
86 op.add_option('--deviceSerial', action='store', |
|
87 type='string', dest='device_serial', default=None, |
|
88 help='ADB device serial number of remote device to test') |
|
89 op.add_option('--deviceTransport', action='store', |
|
90 type='string', dest='device_transport', default='sut', |
|
91 help='The transport to use to communicate with device: [adb|sut]; default=sut') |
|
92 op.add_option('--remoteTestRoot', dest='remote_test_root', action='store', |
|
93 type='string', default='/data/local/tests', |
|
94 help='The remote directory to use as test root (eg. /data/local/tests)') |
|
95 op.add_option('--localLib', dest='local_lib', action='store', |
|
96 type='string', |
|
97 help='The location of libraries to push -- preferably stripped') |
|
98 op.add_option('--repeat', type=int, default=1, |
|
99 help='Repeat tests the given number of times.') |
|
100 op.add_option('--this-chunk', type=int, default=1, |
|
101 help='The test chunk to run.') |
|
102 op.add_option('--total-chunks', type=int, default=1, |
|
103 help='The total number of test chunks.') |
|
104 |
|
105 options, args = op.parse_args(argv) |
|
106 if len(args) < 1: |
|
107 op.error('missing JS_SHELL argument') |
|
108 # We need to make sure we are using backslashes on Windows. |
|
109 test_args = args[1:] |
|
110 |
|
111 if jittests.stdio_might_be_broken(): |
|
112 # Prefer erring on the side of caution and not using stdio if |
|
113 # it might be broken on this platform. The file-redirect |
|
114 # fallback should work on any platform, so at worst by |
|
115 # guessing wrong we might have slowed down the tests a bit. |
|
116 # |
|
117 # XXX technically we could check for broken stdio, but it |
|
118 # really seems like overkill. |
|
119 options.avoid_stdio = True |
|
120 |
|
121 if options.retest: |
|
122 options.read_tests = options.retest |
|
123 options.write_failures = options.retest |
|
124 |
|
125 test_list = [] |
|
126 read_all = True |
|
127 |
|
128 if test_args: |
|
129 read_all = False |
|
130 for arg in test_args: |
|
131 test_list += jittests.find_tests(arg) |
|
132 |
|
133 if options.read_tests: |
|
134 read_all = False |
|
135 try: |
|
136 f = open(options.read_tests) |
|
137 for line in f: |
|
138 test_list.append(os.path.join(jittests.TEST_DIR, line.strip('\n'))) |
|
139 f.close() |
|
140 except IOError: |
|
141 if options.retest: |
|
142 read_all = True |
|
143 else: |
|
144 sys.stderr.write("Exception thrown trying to read test file '%s'\n"% |
|
145 options.read_tests) |
|
146 traceback.print_exc() |
|
147 sys.stderr.write('---\n') |
|
148 |
|
149 if read_all: |
|
150 test_list = jittests.find_tests() |
|
151 |
|
152 if options.exclude: |
|
153 exclude_list = [] |
|
154 for exclude in options.exclude: |
|
155 exclude_list += jittests.find_tests(exclude) |
|
156 test_list = [ test for test in test_list if test not in set(exclude_list) ] |
|
157 |
|
158 if not test_list: |
|
159 print >> sys.stderr, "No tests found matching command line arguments." |
|
160 sys.exit(0) |
|
161 |
|
162 test_list = [jittests.Test.from_file(_, options) for _ in test_list] |
|
163 |
|
164 if not options.run_slow: |
|
165 test_list = [ _ for _ in test_list if not _.slow ] |
|
166 |
|
167 # If chunking is enabled, determine which tests are part of this chunk. |
|
168 # This code was adapted from testing/mochitest/runtestsremote.py. |
|
169 if options.total_chunks > 1: |
|
170 total_tests = len(test_list) |
|
171 tests_per_chunk = math.ceil(total_tests / float(options.total_chunks)) |
|
172 start = int(round((options.this_chunk - 1) * tests_per_chunk)) |
|
173 end = int(round(options.this_chunk * tests_per_chunk)) |
|
174 test_list = test_list[start:end] |
|
175 |
|
176 # The full test list is ready. Now create copies for each JIT configuration. |
|
177 job_list = [] |
|
178 if options.tbpl: |
|
179 # Running all bits would take forever. Instead, we test a few interesting combinations. |
|
180 for test in test_list: |
|
181 for variant in TBPL_FLAGS: |
|
182 new_test = test.copy() |
|
183 new_test.jitflags.extend(variant) |
|
184 job_list.append(new_test) |
|
185 elif options.ion: |
|
186 flags = [['--baseline-eager'], ['--ion-eager', '--ion-parallel-compile=off']] |
|
187 for test in test_list: |
|
188 for variant in flags: |
|
189 new_test = test.copy() |
|
190 new_test.jitflags.extend(variant) |
|
191 job_list.append(new_test) |
|
192 else: |
|
193 jitflags_list = jittests.parse_jitflags(options) |
|
194 for test in test_list: |
|
195 for jitflags in jitflags_list: |
|
196 new_test = test.copy() |
|
197 new_test.jitflags.extend(jitflags) |
|
198 job_list.append(new_test) |
|
199 |
|
200 prefix = [os.path.abspath(args[0])] + shlex.split(options.shell_args) |
|
201 prolog = os.path.join(jittests.LIB_DIR, 'prolog.js') |
|
202 if options.remote: |
|
203 prolog = posixpath.join(options.remote_test_root, 'jit-tests', 'jit-tests', 'lib', 'prolog.js') |
|
204 |
|
205 prefix += ['-f', prolog] |
|
206 |
|
207 # Avoid racing on the cache by having the js shell create a new cache |
|
208 # subdir for each process. The js shell takes care of deleting these |
|
209 # subdirs when the process exits. |
|
210 if options.max_jobs > 1 and jittests.HAVE_MULTIPROCESSING: |
|
211 prefix += ['--js-cache-per-process'] |
|
212 |
|
213 # Clean up any remnants from previous crashes etc |
|
214 shutil.rmtree(jittests.JS_CACHE_DIR, ignore_errors=True) |
|
215 os.mkdir(jittests.JS_CACHE_DIR) |
|
216 |
|
217 if options.debug: |
|
218 if len(job_list) > 1: |
|
219 print 'Multiple tests match command line arguments, debugger can only run one' |
|
220 for tc in job_list: |
|
221 print ' %s' % tc.path |
|
222 sys.exit(1) |
|
223 |
|
224 tc = job_list[0] |
|
225 cmd = ['gdb', '--args'] + tc.command(prefix, jittests.LIB_DIR) |
|
226 subprocess.call(cmd) |
|
227 sys.exit() |
|
228 |
|
229 try: |
|
230 ok = None |
|
231 if options.remote: |
|
232 ok = jittests.run_tests_remote(job_list, prefix, options) |
|
233 elif options.max_jobs > 1 and jittests.HAVE_MULTIPROCESSING: |
|
234 ok = jittests.run_tests_parallel(job_list, prefix, options) |
|
235 else: |
|
236 ok = jittests.run_tests(job_list, prefix, options) |
|
237 if not ok: |
|
238 sys.exit(2) |
|
239 except OSError: |
|
240 if not os.path.exists(prefix[0]): |
|
241 print >> sys.stderr, "JS shell argument: file does not exist: '%s'" % prefix[0] |
|
242 sys.exit(1) |
|
243 else: |
|
244 raise |
|
245 |
|
246 if __name__ == '__main__': |
|
247 main(sys.argv[1:]) |