Bug 858622 - Make jit-tests runnable on mobile;r=terrence
☠☠ backed out by c21468186d3d ☠ ☠
authorDan Minor <dminor@mozilla.com>
Mon, 19 Aug 2013 15:21:34 -0400
changeset 143150 57ee0ba35de63fda5b9e026308583e46f2ca32e0
parent 143149 8dc4c45cfda39545317ad0d00952598cea568f23
child 143151 7187d131a09394dda1c01593fcad07535c898292
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersterrence
bugs858622
milestone26.0a1
Bug 858622 - Make jit-tests runnable on mobile;r=terrence
js/src/jit-test/jit_test.py
js/src/tests/lib/jittests.py
--- a/js/src/jit-test/jit_test.py
+++ b/js/src/jit-test/jit_test.py
@@ -1,14 +1,14 @@
 #!/usr/bin/env python
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-import os, shlex, subprocess, sys, traceback
+import os, posixpath, shlex, subprocess, sys, traceback
 
 def add_libdir_to_path():
     from os.path import dirname, exists, join, realpath
     js_src_dir = dirname(dirname(realpath(sys.argv[0])))
     assert exists(join(js_src_dir,'jsapi.h'))
     sys.path.append(join(js_src_dir, 'lib'))
     sys.path.append(join(js_src_dir, 'tests', 'lib'))
 
@@ -69,16 +69,33 @@ def main(argv):
     op.add_option('--write-failure-output', dest='write_failure_output', action='store_true',
                   help='With --write-failures=FILE, additionally write the output of failed tests to [FILE]')
     op.add_option('--ion', dest='ion', action='store_true',
                   help='Run tests once with --ion-eager and once with --no-jm (ignores --jitflags)')
     op.add_option('--tbpl', dest='tbpl', action='store_true',
                   help='Run tests with all IonMonkey option combinations (ignores --jitflags)')
     op.add_option('-j', '--worker-count', dest='max_jobs', type=int, default=max_jobs_default,
                   help='Number of tests to run in parallel (default %default)')
+    op.add_option('--remote', action='store_true',
+                  help='Run tests on a remote device')
+    op.add_option('--deviceIP', action='store',
+                  type='string', dest='device_ip',
+                  help='IP address of remote device to test')
+    op.add_option('--devicePort', action='store',
+                  type=int, dest='device_port', default=20701,
+                  help='port of remote device to test')
+    op.add_option('--deviceTransport', action='store',
+                  type='string', dest='device_transport', default='sut',
+                  help='The transport to use to communicate with device: [adb|sut]; default=sut')
+    op.add_option('--remoteTestRoot', dest='remote_test_root', action='store',
+                  type='string', default='/data/local/tests',
+                  help='The remote directory to use as test root (eg. /data/local/tests)')
+    op.add_option('--localLib', dest='local_lib', action='store',
+                  type='string',
+                  help='The location of libraries to push -- preferably stripped')
 
     options, args = op.parse_args(argv)
     if len(args) < 1:
         op.error('missing JS_SHELL argument')
     # We need to make sure we are using backslashes on Windows.
     test_args = args[1:]
 
     if jittests.stdio_might_be_broken():
@@ -166,32 +183,38 @@ def main(argv):
         jitflags_list = jittests.parse_jitflags(options)
         for test in test_list:
             for jitflags in jitflags_list:
                 new_test = test.copy()
                 new_test.jitflags.extend(jitflags)
                 job_list.append(new_test)
 
     prefix = [os.path.abspath(args[0])] + shlex.split(options.shell_args)
-    prefix += ['-f', os.path.join(jittests.LIB_DIR, 'prolog.js')]
+    prolog = os.path.join(jittests.LIB_DIR, 'prolog.js')
+    if options.remote:
+        prolog = posixpath.join(options.remote_test_root, 'jit-tests/lib/prolog.js')
+
+    prefix += ['-f', prolog]
     if options.debug:
         if len(job_list) > 1:
             print 'Multiple tests match command line arguments, debugger can only run one'
             for tc in job_list:
                 print '    %s' % tc.path
             sys.exit(1)
 
         tc = job_list[0]
         cmd = ['gdb', '--args'] + tc.command(prefix)
         subprocess.call(cmd)
         sys.exit()
 
     try:
         ok = None
-        if options.max_jobs > 1 and jittests.HAVE_MULTIPROCESSING:
+        if options.remote:
+            ok = jittests.run_tests_remote(job_list, prefix, options)
+        elif options.max_jobs > 1 and jittests.HAVE_MULTIPROCESSING:
             ok = jittests.run_tests_parallel(job_list, prefix, options)
         else:
             ok = jittests.run_tests(job_list, prefix, options)
         if not ok:
             sys.exit(2)
     except OSError:
         if not os.path.exists(prefix[0]):
             print >> sys.stderr, "JS shell argument: file does not exist: '%s'" % prefix[0]
--- a/js/src/tests/lib/jittests.py
+++ b/js/src/tests/lib/jittests.py
@@ -2,21 +2,22 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 
 # jit_test.py -- Python harness for JavaScript trace tests.
 
 from __future__ import print_function
-import os, sys, tempfile, traceback, time
+import os, posixpath, sys, tempfile, traceback, time
 import subprocess
 from subprocess import Popen, PIPE
 from threading import Thread
 import signal
+import StringIO
 
 try:
     from multiprocessing import Process, Manager, cpu_count
     HAVE_MULTIPROCESSING = True
 except ImportError:
     HAVE_MULTIPROCESSING = False
 
 from progressbar import ProgressBar, NullProgressBar
@@ -147,25 +148,29 @@ class Test:
                     else:
                         print('warning: unrecognized |jit-test| attribute %s' % part)
 
         if options.valgrind_all:
             test.valgrind = True
 
         return test
 
-    def command(self, prefix):
-        scriptdir_var = os.path.dirname(self.path);
+    def command(self, prefix, libdir, remote_prefix=None):
+        path = self.path
+        if remote_prefix:
+            path = self.path.replace(TEST_DIR, remote_prefix)
+
+        scriptdir_var = os.path.dirname(path);
         if not scriptdir_var.endswith('/'):
             scriptdir_var += '/'
-        expr = ("const platform=%r; const libdir=%r; const scriptdir=%r"
-                % (sys.platform, LIB_DIR, scriptdir_var))
+        expr = ('const platform="%s"; const libdir="%s"; const scriptdir="%s"'
+                % (sys.platform, libdir, scriptdir_var))
         # We may have specified '-a' or '-d' twice: once via --jitflags, once
         # via the "|jit-test|" line.  Remove dups because they are toggles.
-        cmd = prefix + list(set(self.jitflags)) + ['-e', expr, '-f', self.path]
+        cmd = prefix + list(set(self.jitflags)) + ['-e', expr, '-f', path]
         if self.valgrind:
             cmd = self.VALGRIND_CMD + cmd
         return cmd
 
 def find_tests(substring=None):
     ans = []
     for dirpath, dirnames, filenames in os.walk(TEST_DIR):
         dirnames.sort()
@@ -263,32 +268,52 @@ def run_cmd(cmdline, env, timeout):
 def run_cmd_avoid_stdio(cmdline, env, timeout):
     stdoutPath, stderrPath = tmppath('jsstdout'), tmppath('jsstderr')
     env['JS_STDOUT'] = stdoutPath
     env['JS_STDERR'] = stderrPath
     _, __, code = run_timeout_cmd(cmdline, { 'env': env }, timeout)
     return read_and_unlink(stdoutPath), read_and_unlink(stderrPath), code
 
 def run_test(test, prefix, options):
-    cmd = test.command(prefix)
+    cmd = test.command(prefix, LIB_DIR)
     if options.show_cmd:
         print(subprocess.list2cmdline(cmd))
 
     if options.avoid_stdio:
         run = run_cmd_avoid_stdio
     else:
         run = run_cmd
 
     env = os.environ.copy()
     if test.tz_pacific:
         env['TZ'] = 'PST8PDT'
 
     out, err, code, timed_out = run(cmd, env, options.timeout)
     return TestOutput(test, cmd, out, err, code, None, timed_out)
 
+def run_test_remote(test, device, prefix, options):
+    cmd = test.command(prefix, posixpath.join(options.remote_test_root, 'lib/'), posixpath.join(options.remote_test_root, 'tests'))
+    if options.show_cmd:
+        print(subprocess.list2cmdline(cmd))
+
+    env = {}
+    if test.tz_pacific:
+        env['TZ'] = 'PST8PDT'
+
+    env['LD_LIBRARY_PATH'] = options.remote_test_root
+
+    buf = StringIO.StringIO()
+    returncode = device.shell(cmd, buf, env=env, cwd=options.remote_test_root,
+                              timeout=int(options.timeout))
+
+    out = buf.getvalue()
+    # We can't distinguish between stdout and stderr so we pass
+    # the same buffer to both.
+    return TestOutput(test, cmd, out, out, returncode, None, False)
+
 def check_output(out, err, rc, test):
     if test.expect_error:
         # The shell exits with code 3 on uncaught exceptions.
         return test.expect_error in err and rc == 3
 
     for line in out.split('\n'):
         if line.startswith('Trace stats check failed'):
             return False
@@ -536,16 +561,68 @@ def get_serial_results(tests, prefix, op
     for test in tests:
         yield run_test(test, prefix, options)
 
 def run_tests(tests, prefix, options):
     gen = get_serial_results(tests, prefix, options)
     ok = process_test_results(gen, len(tests), options)
     return ok
 
+def get_remote_results(tests, device, prefix, options):
+    for test in tests:
+        yield run_test_remote(test, device, prefix, options)
+
+def push_libs(options, device):
+    # This saves considerable time in pushing unnecessary libraries
+    # to the device but needs to be updated if the dependencies change.
+    required_libs = ['libnss3.so', 'libmozglue.so']
+
+    for file in os.listdir(options.local_lib):
+        if file in required_libs:
+            remote_file = posixpath.join(options.remote_test_root, file)
+            device.pushFile(os.path.join(options.local_lib, file), remote_file)
+
+def push_progs(options, device, progs):
+    for local_file in progs:
+        remote_file = posixpath.join(options.remote_test_root, os.path.basename(local_file))
+        device.pushFile(local_file, remote_file)
+
+def run_tests_remote(tests, prefix, options):
+    # Setup device with everything needed to run our tests.
+    from mozdevice import devicemanager, devicemanagerADB, devicemanagerSUT
+
+    if options.device_transport == 'adb':
+        if options.device_ip:
+            dm = devicemanagerADB.DeviceManagerADB(options.device_ip, options.devicePort, packageName=None, deviceRoot=options.remoteTestRoot)
+        else:
+            dm = devicemanagerADB.DeviceManagerADB(packageName=None, deviceRoot=options.remote_test_root)
+    else:
+        dm = devicemanagerSUT.DeviceManagerSUT(options.device_ip, options.device_port, deviceRoot=options.remote_test_root)
+        if options.device_ip == None:
+            print('Error: you must provide a device IP to connect to via the --device option')
+            sys.exit(1)
+
+    # Update the test root to point to our test directory.
+    options.remote_test_root = posixpath.join(options.remote_test_root, 'jit-tests')
+
+    # Push js shell and libraries.
+    if dm.dirExists(options.remote_test_root):
+        dm.removeDir(options.remote_test_root)
+    dm.mkDir(options.remote_test_root)
+    push_libs(options, dm)
+    push_progs(options, dm, [prefix[0]])
+    dm.chmodDir(options.remote_test_root)
+    dm.pushDir(os.path.dirname(TEST_DIR), options.remote_test_root)
+    prefix[0] = os.path.join(options.remote_test_root, 'js')
+
+    # Run all tests.
+    gen = get_remote_results(tests, dm, prefix, options)
+    ok = process_test_results(gen, len(tests), options)
+    return ok
+
 def parse_jitflags(options):
     jitflags = [ [ '-' + flag for flag in flags ]
                  for flags in options.jitflags.split(',') ]
     for flags in jitflags:
         for flag in flags:
             if flag not in ('-m', '-a', '-p', '-d', '-n'):
                 print('Invalid jit flag: "%s"' % flag)
                 sys.exit(1)