Bug 932362 - Create mach target for running b2g xpcshell tests, r=jgriffin
authorAndrew Halberstadt <ahalberstadt@mozilla.com>
Tue, 11 Mar 2014 10:08:25 -0400
changeset 173036 5b490c2f1fb4e5eb98755d35ad9509ce6bb03584
parent 173035 1f24f6d7a426092f3fc10cdbe3820339dc555121
child 173037 cbf30c2f215b665a6e0389ff55fd46205684d347
push id26386
push userryanvm@gmail.com
push dateTue, 11 Mar 2014 19:42:31 +0000
treeherdermozilla-central@23005b395ae8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjgriffin
bugs932362
milestone30.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 932362 - Create mach target for running b2g xpcshell tests, r=jgriffin
testing/xpcshell/mach_commands.py
testing/xpcshell/runtestsb2g.py
--- a/testing/xpcshell/mach_commands.py
+++ b/testing/xpcshell/mach_commands.py
@@ -6,31 +6,41 @@
 
 from __future__ import unicode_literals, print_function
 
 import mozpack.path
 import logging
 import os
 import shutil
 import sys
+import urllib2
 
 from StringIO import StringIO
 
 from mozbuild.base import (
     MachCommandBase,
     MozbuildObject,
     MachCommandConditions as conditions,
 )
 
 from mach.decorators import (
     CommandArgument,
     CommandProvider,
     Command,
 )
 
+ADB_NOT_FOUND = '''
+The %s command requires the adb binary to be on your path.
+
+If you have a B2G build, this can be found in
+'%s/out/host/<platform>/bin'.
+'''.lstrip()
+
+BUSYBOX_URL = 'http://www.busybox.net/downloads/binaries/latest/busybox-armv7l'
+
 
 if sys.version_info[0] < 3:
     unicode_type = unicode
 else:
     unicode_type = str
 
 # Simple filter to omit the message emitted as a test file begins.
 class TestStartFilter(logging.Filter):
@@ -269,19 +279,109 @@ class AndroidXPCShellRunner(MozbuildObje
                       mobileArgs=xpcshell.mobileArgs,
                       **options.__dict__)
 
         self.log_manager.terminal_handler.removeFilter(xpcshell_filter)
         self.log_manager.disable_unstructured()
 
         return int(not result)
 
+class B2GXPCShellRunner(MozbuildObject):
+    def __init__(self, *args, **kwargs):
+        MozbuildObject.__init__(self, *args, **kwargs)
+
+        # TODO Bug 794506 remove once mach integrates with virtualenv.
+        build_path = os.path.join(self.topobjdir, 'build')
+        if build_path not in sys.path:
+            sys.path.append(build_path)
+
+        build_path = os.path.join(self.topsrcdir, 'build')
+        if build_path not in sys.path:
+            sys.path.append(build_path)
+
+        self.tests_dir = os.path.join(self.topobjdir, '_tests')
+        self.xpcshell_dir = os.path.join(self.tests_dir, 'xpcshell')
+        self.bin_dir = os.path.join(self.distdir, 'bin')
+
+    def _download_busybox(self, b2g_home):
+        system_bin = os.path.join(b2g_home, 'out', 'target', 'product', 'generic', 'system', 'bin')
+        busybox_path = os.path.join(system_bin, 'busybox')
+
+        if os.path.isfile(busybox_path):
+            return busybox_path
+
+        if not os.path.isdir(system_bin):
+            os.makedirs(system_bin)
+
+        try:
+            data = urllib2.urlopen(BUSYBOX_URL)
+        except urllib2.URLError:
+            print('There was a problem downloading busybox. Proceeding without it,' \
+                  'initial setup will be slow.')
+            return
+
+        with open(busybox_path, 'wb') as f:
+            f.write(data.read())
+        return busybox_path
+
+    def run_test(self, test_file, b2g_home=None, busybox=None,
+                 # ignore parameters from other platforms' options
+                 **kwargs):
+        try:
+            import which
+            which.which('adb')
+        except which.WhichError:
+            # TODO Find adb automatically if it isn't on the path
+            print(ADB_NOT_FOUND % ('mochitest-remote', b2g_home))
+            sys.exit(1)
+
+        test_path = None
+        if test_file:
+            test_path = self._wrap_path_argument(test_file).relpath()
+
+        import runtestsb2g
+        parser = runtestsb2g.B2GOptions()
+        options, args = parser.parse_args([])
+
+        options.b2g_path = b2g_home
+        options.busybox = busybox or os.environ.get('BUSYBOX')
+        options.emulator = 'arm'
+        options.localLib = self.bin_dir
+        options.localBin = self.bin_dir
+        options.logcat_dir = self.xpcshell_dir
+        options.manifest = os.path.join(self.xpcshell_dir, 'xpcshell_b2g.ini')
+        options.mozInfo = os.path.join(self.topobjdir, 'mozinfo.json')
+        options.objdir = self.topobjdir
+        options.symbolsPath = os.path.join(self.distdir, 'crashreporter-symbols'),
+        options.testingModulesDir = os.path.join(self.tests_dir, 'modules')
+        options.testsRootDir = self.xpcshell_dir
+        options.testPath = test_path
+        options.use_device_libs = True
+
+        if not options.busybox:
+            options.busybox = self._download_busybox(b2g_home)
+
+        return runtestsb2g.run_remote_xpcshell(parser, options, args)
+
+def is_platform_supported(cls):
+    """Must have a Firefox, Android or B2G build."""
+    return conditions.is_android(cls) or \
+           conditions.is_b2g(cls) or \
+           conditions.is_firefox(cls)
+
 @CommandProvider
 class MachCommands(MachCommandBase):
+    def __init__(self, context):
+        MachCommandBase.__init__(self, context)
+
+        for attr in ('b2g_home', 'device_name'):
+            setattr(self, attr, getattr(context, attr, None))
+
     @Command('xpcshell-test', category='testing',
+        conditions=[is_platform_supported],
         description='Run XPCOM Shell tests.')
     @CommandArgument('test_file', default='all', nargs='?', metavar='TEST',
         help='Test to run. Can be specified as a single JS file, a directory, '
              'or omitted. If omitted, the entire test suite is executed.')
     @CommandArgument("--debugger", default=None, metavar='DEBUGGER',
                      help = "Run xpcshell under the given debugger.")
     @CommandArgument("--debugger-args", default=None, metavar='ARGS', type=str,
                      dest = "debuggerArgs",
@@ -308,29 +408,34 @@ class MachCommands(MachCommandBase):
     @CommandArgument('--port', type=int, default=20701,
         help='(Android) Port of device')
     @CommandArgument('--remote_test_root', type=str, default=None,
         help='(Android) Remote test root such as /mnt/sdcard or /data/local')
     @CommandArgument('--no-setup', action='store_true',
         help='(Android) Do not copy files to device')
     @CommandArgument('--local-apk', type=str, default=None,
         help='(Android) Use specified Fennec APK')
+    @CommandArgument('--busybox', type=str, default=None,
+        help='(B2G) Path to busybox binary (speeds up installation of tests).')
     def run_xpcshell_test(self, **params):
         from mozbuild.controller.building import BuildDriver
 
         # We should probably have a utility function to ensure the tree is
         # ready to run tests. Until then, we just create the state dir (in
         # case the tree wasn't built with mach).
         self._ensure_state_subdir_exists('.')
 
         driver = self._spawn(BuildDriver)
         driver.install_tests(remove=False)
 
         if conditions.is_android(self):
             xpcshell = self._spawn(AndroidXPCShellRunner)
+        elif conditions.is_b2g(self):
+            xpcshell = self._spawn(B2GXPCShellRunner)
+            params['b2g_home'] = self.b2g_home
         else:
             xpcshell = self._spawn(XPCShellRunner)
         xpcshell.cwd = self._mach_context.cwd
 
         try:
             return xpcshell.run_test(**params)
         except InvalidTestPathError as e:
             print(e.message)
--- a/testing/xpcshell/runtestsb2g.py
+++ b/testing/xpcshell/runtestsb2g.py
@@ -148,19 +148,17 @@ class B2GOptions(RemoteXPCShellOptions):
 
         if options.geckoPath and not options.emulator:
             self.error("You must specify --emulator if you specify --gecko-path")
 
         if options.logcat_dir and not options.emulator:
             self.error("You must specify --emulator if you specify --logcat-dir")
         return RemoteXPCShellOptions.verifyRemoteOptions(self, options)
 
-def main():
-    parser = B2GOptions()
-    options, args = parser.parse_args()
+def run_remote_xpcshell(parser, options, args):
     options = parser.verifyRemoteOptions(options)
 
     # Create the Marionette instance
     kwargs = {}
     if options.emulator:
         kwargs['emulator'] = options.emulator
         if options.no_window:
             kwargs['noWindow'] = True
@@ -207,16 +205,21 @@ def main():
                                  mobileArgs=xpcsh.mobileArgs,
                                  **options.__dict__):
             sys.exit(1)
     except:
         print "Automation Error: Exception caught while running tests"
         traceback.print_exc()
         sys.exit(1)
 
+def main():
+    parser = B2GOptions()
+    options, args = parser.parse_args()
+
+    run_remote_xpcshell(parser, options, args)
 
 # You usually run this like :
 # python runtestsb2g.py --emulator arm --b2gpath $B2GPATH --manifest $MANIFEST [--xre-path $MOZ_HOST_BIN
 #                                                                               --adbpath $ADB_PATH
 #                                                                               ...]
 #
 # For xUnit output you should also pass in --tests-root-dir ..objdir-gecko/_tests
 #                                          --xunit-file ..objdir_gecko/_tests/results.xml