Bug 1301431 - Change reftest harness to accept Namespace object instead of kwargs, r=armenzg
authorAndrew Halberstadt <ahalberstadt@mozilla.com>
Tue, 13 Sep 2016 09:26:09 -0400
changeset 314028 1ee290adf8da8c3671d7f8d6b49c779b843ae790
parent 314027 efcd99879deeac3a528579aa908574ed5bb7bac1
child 314029 17c4dbd978c72dd978ddba3ef0d78cf8bfd7ba6a
push id30704
push userkwierso@gmail.com
push dateThu, 15 Sep 2016 23:10:53 +0000
treeherdermozilla-central@9b42d4b1f448 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersarmenzg
bugs1301431
milestone51.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 1301431 - Change reftest harness to accept Namespace object instead of kwargs, r=armenzg Reftests hackily create a second argument parser and set defaults on it to get around the fact that the mach command sends the harness a dict rather than a Namespace object. This is bad because: 1. It's much less hacky to just create a Namespace object directly (rather than making a second parser) 2. Most other mach commands actually *do* return a Namespace object, reftest is the odd one out here. So this patch makes the reftest mach commands convert to the Namespace object, which allows us to get rid of the hacks in the reftest harness. This also does some light refactoring of the entry points to the reftest harness so make it more consistent with mochitest and xpcshell. MozReview-Commit-ID: 5HMfm64wmK2
layout/tools/reftest/mach_commands.py
layout/tools/reftest/mach_test_package_commands.py
layout/tools/reftest/remotereftest.py
layout/tools/reftest/runreftest.py
layout/tools/reftest/runreftestb2g.py
layout/tools/reftest/runreftestmulet.py
--- a/layout/tools/reftest/mach_commands.py
+++ b/layout/tools/reftest/mach_commands.py
@@ -4,16 +4,17 @@
 
 from __future__ import absolute_import, unicode_literals
 
 import os
 import re
 import sys
 import warnings
 import which
+from argparse import Namespace
 
 from mozbuild.base import (
     MachCommandBase,
     MachCommandConditions as conditions,
     MozbuildObject,
 )
 
 from mach.decorators import (
@@ -56,16 +57,18 @@ The profile should be generated in a dir
 
 MARIONETTE_DISABLED = '''
 The reftest command requires a marionette enabled build on Mulet.
 
 Add 'ENABLE_MARIONETTE=1' to your mozconfig file and re-build the application.
 Your currently active mozconfig is %s.
 '''.lstrip()
 
+parser = None
+
 
 class ReftestRunner(MozbuildObject):
     """Easily run reftests.
 
     This currently contains just the basics for running reftests. We may want
     to hook up result parsing, etc.
     """
     def __init__(self, *args, **kwargs):
@@ -77,26 +80,26 @@ class ReftestRunner(MozbuildObject):
             sys.path.append(build_path)
 
         self.tests_dir = os.path.join(self.topobjdir, '_tests')
         self.reftest_dir = os.path.join(self.tests_dir, 'reftest')
 
     def _make_shell_string(self, s):
         return "'%s'" % re.sub("'", r"'\''", s)
 
-    def _setup_objdir(self, **kwargs):
+    def _setup_objdir(self, args):
         # reftest imports will happen from the objdir
         sys.path.insert(0, self.reftest_dir)
 
-        if not kwargs["tests"]:
+        if not args.tests:
             test_subdir = {
                 "reftest": os.path.join('layout', 'reftests'),
                 "crashtest": os.path.join('layout', 'crashtest'),
-            }[kwargs['suite']]
-            kwargs["tests"] = [test_subdir]
+            }[args.suite]
+            args.tests = [test_subdir]
 
         tests = os.path.join(self.reftest_dir, 'tests')
         if not os.path.isdir(tests):
             os.symlink(self.topsrcdir, tests)
 
     def run_b2g_test(self, b2g_home=None, xre_path=None, **kwargs):
         """Runs a b2g reftest.
 
@@ -105,180 +108,185 @@ class ReftestRunner(MozbuildObject):
 
         tests is a list of paths. It can be a relative path from the
         top source directory, an absolute filename, or a directory containing
         test files.
 
         suite is the type of reftest to run. It can be one of ('reftest',
         'crashtest').
         """
-        if kwargs["suite"] not in ('reftest', 'crashtest'):
+        args = Namespace(**kwargs)
+        if args.suite not in ('reftest', 'crashtest'):
             raise Exception('None or unrecognized reftest suite type.')
 
-        self._setup_objdir(**kwargs)
+        self._setup_objdir(args)
         import runreftestb2g
 
-        for i, path in enumerate(kwargs["tests"]):
+        for i, path in enumerate(args.tests):
             # Non-absolute paths are relative to the packaged directory, which
             # has an extra tests/ at the start
             if os.path.exists(os.path.abspath(path)):
                 path = os.path.relpath(path, os.path.join(self.topsrcdir))
-            kwargs["tests"][i] = os.path.join('tests', path)
+            args.tests[i] = os.path.join('tests', path)
 
         try:
             which.which('adb')
         except which.WhichError:
             # TODO Find adb automatically if it isn't on the path
-            raise Exception(ADB_NOT_FOUND % ('%s-remote' % kwargs["suite"], b2g_home))
+            raise Exception(ADB_NOT_FOUND % ('%s-remote' % args.suite, b2g_home))
 
-        kwargs["b2gPath"] = b2g_home
-        kwargs["logdir"] = self.reftest_dir
-        kwargs["httpdPath"] = os.path.join(self.topsrcdir, 'netwerk', 'test', 'httpserver')
-        kwargs["xrePath"] = xre_path
-        kwargs["ignoreWindowSize"] = True
+        args.b2gPath = b2g_home
+        args.logdir = self.reftest_dir
+        args.httpdPath = os.path.join(self.topsrcdir, 'netwerk', 'test', 'httpserver')
+        args.xrePath = xre_path
+        args.ignoreWindowSize = True
 
         # Don't enable oop for crashtest until they run oop in automation
-        if kwargs["suite"] == 'reftest':
-            kwargs["oop"] = True
+        if args.suite == 'reftest':
+            args.oop = True
 
-        return runreftestb2g.run(**kwargs)
+        return runreftestb2g.run_test_harness(parser, args)
 
     def run_mulet_test(self, **kwargs):
         """Runs a mulet reftest."""
-        self._setup_objdir(**kwargs)
+        args = Namespace(**kwargs)
+        self._setup_objdir(args)
+
         import runreftestmulet
 
         if self.substs.get('ENABLE_MARIONETTE') != '1':
             print(MARIONETTE_DISABLED % self.mozconfig['path'])
             return 1
 
-        if not kwargs["profile"]:
+        if not args.profile:
             gaia_profile = os.environ.get('GAIA_PROFILE')
             if not gaia_profile:
                 print(GAIA_PROFILE_NOT_FOUND)
                 return 1
-            kwargs["profile"] = gaia_profile
+            args.profile = gaia_profile
 
-        if os.path.isfile(os.path.join(kwargs["profile"], 'extensions',
+        if os.path.isfile(os.path.join(args.profile, 'extensions',
                                        'httpd@gaiamobile.org')):
-            print(GAIA_PROFILE_IS_DEBUG % kwargs["profile"])
+            print(GAIA_PROFILE_IS_DEBUG % args.profile)
             return 1
 
-        kwargs['app'] = self.get_binary_path()
-        kwargs['mulet'] = True
-        kwargs['oop'] = True
+        args.app = self.get_binary_path()
+        args.mulet = True
+        args.oop = True
 
-        if kwargs['oop']:
-            kwargs['browser_arg'] = '-oop'
-        if not kwargs['app'].endswith('-bin'):
-            kwargs['app'] = '%s-bin' % kwargs['app']
-        if not os.path.isfile(kwargs['app']):
-            kwargs['app'] = kwargs['app'][:-len('-bin')]
+        if args.oop:
+            args.browser_arg = '-oop'
+        if not args.app.endswith('-bin'):
+            args.app = '%s-bin' % args.app
+        if not os.path.isfile(args.app):
+            args.app = args.app[:-len('-bin')]
 
-        return runreftestmulet.run(**kwargs)
+        return runreftestmulet.run_test_harness(parser, args)
 
     def run_desktop_test(self, **kwargs):
         """Runs a reftest, in desktop Firefox."""
         import runreftest
 
-        if kwargs["suite"] not in ('reftest', 'crashtest', 'jstestbrowser'):
+        args = Namespace(**kwargs)
+        if args.suite not in ('reftest', 'crashtest', 'jstestbrowser'):
             raise Exception('None or unrecognized reftest suite type.')
 
         default_manifest = {
             "reftest": (self.topsrcdir, "layout", "reftests", "reftest.list"),
             "crashtest": (self.topsrcdir, "testing", "crashtest", "crashtests.list"),
             "jstestbrowser": (self.topobjdir, "dist", "test-stage", "jsreftest", "tests",
                               "jstests.list")
         }
 
-        kwargs["extraProfileFiles"].append(os.path.join(self.topobjdir, "dist", "plugins"))
-        kwargs["symbolsPath"] = os.path.join(self.topobjdir, "crashreporter-symbols")
+        args.extraProfileFiles.append(os.path.join(self.topobjdir, "dist", "plugins"))
+        args.symbolsPath = os.path.join(self.topobjdir, "crashreporter-symbols")
 
-        if not kwargs["tests"]:
-            kwargs["tests"] = [os.path.join(*default_manifest[kwargs["suite"]])]
+        if not args.tests:
+            args.tests = [os.path.join(*default_manifest[args.suite])]
 
-        if kwargs["suite"] == "jstestbrowser":
-            kwargs["extraProfileFiles"].append(os.path.join(self.topobjdir, "dist",
+        if args.suite == "jstestbrowser":
+            args.extraProfileFiles.append(os.path.join(self.topobjdir, "dist",
                                                             "test-stage", "jsreftest",
                                                             "tests", "user.js"))
 
         self.log_manager.enable_unstructured()
         try:
-            rv = runreftest.run(**kwargs)
+            rv = runreftest.run_test_harness(parser, args)
         finally:
             self.log_manager.disable_unstructured()
 
         return rv
 
     def run_android_test(self, **kwargs):
         """Runs a reftest, in Firefox for Android."""
 
-        if kwargs["suite"] not in ('reftest', 'crashtest', 'jstestbrowser'):
+        args = Namespace(**kwargs)
+        if args.suite not in ('reftest', 'crashtest', 'jstestbrowser'):
             raise Exception('None or unrecognized reftest suite type.')
-        if "ipc" in kwargs.keys():
+        if hasattr(args, 'ipc'):
             raise Exception('IPC tests not supported on Android.')
 
-        self._setup_objdir(**kwargs)
+        self._setup_objdir(args)
         import remotereftest
 
         default_manifest = {
             "reftest": (self.topsrcdir, "layout", "reftests", "reftest.list"),
             "crashtest": (self.topsrcdir, "testing", "crashtest", "crashtests.list"),
             "jstestbrowser": ("jsreftest", "tests", "jstests.list")
         }
 
-        if not kwargs["tests"]:
-            kwargs["tests"] = [os.path.join(*default_manifest[kwargs["suite"]])]
+        if not args.tests:
+            args.tests = [os.path.join(*default_manifest[args.suite])]
 
-        kwargs["extraProfileFiles"].append(
+        args.extraProfileFiles.append(
             os.path.join(self.topsrcdir, "mobile", "android", "fonts"))
 
         hyphenation_path = os.path.join(self.topsrcdir, "intl", "locales")
 
         for (dirpath, dirnames, filenames) in os.walk(hyphenation_path):
             for filename in filenames:
                 if filename.endswith('.dic'):
-                    kwargs["extraProfileFiles"].append(os.path.join(dirpath, filename))
+                    args.extraProfileFiles.append(os.path.join(dirpath, filename))
 
-        if not kwargs["httpdPath"]:
-            kwargs["httpdPath"] = os.path.join(self.tests_dir, "modules")
-        if not kwargs["symbolsPath"]:
-            kwargs["symbolsPath"] = os.path.join(self.topobjdir, "crashreporter-symbols")
-        if not kwargs["xrePath"]:
-            kwargs["xrePath"] = os.environ.get("MOZ_HOST_BIN")
-        if not kwargs["app"]:
-            kwargs["app"] = self.substs["ANDROID_PACKAGE_NAME"]
-        if not kwargs["utilityPath"]:
-            kwargs["utilityPath"] = kwargs["xrePath"]
-        kwargs["dm_trans"] = "adb"
-        kwargs["ignoreWindowSize"] = True
-        kwargs["printDeviceInfo"] = False
+        if not args.httpdPath:
+            args.httpdPath = os.path.join(self.tests_dir, "modules")
+        if not args.symbolsPath:
+            args.symbolsPath = os.path.join(self.topobjdir, "crashreporter-symbols")
+        if not args.xrePath:
+            args.xrePath = os.environ.get("MOZ_HOST_BIN")
+        if not args.app:
+            args.app = self.substs["ANDROID_PACKAGE_NAME"]
+        if not args.utilityPath:
+            args.utilityPath = args.xrePath
+        args.dm_trans = "adb"
+        args.ignoreWindowSize = True
+        args.printDeviceInfo = False
 
         from mozrunner.devices.android_device import grant_runtime_permissions
         grant_runtime_permissions(self)
 
         # A symlink and some path manipulations are required so that test
         # manifests can be found both locally and remotely (via a url)
         # using the same relative path.
-        if kwargs["suite"] == "jstestbrowser":
+        if args.suite == "jstestbrowser":
             staged_js_dir = os.path.join(self.topobjdir, "dist", "test-stage", "jsreftest")
             tests = os.path.join(self.reftest_dir, 'jsreftest')
             if not os.path.isdir(tests):
                 os.symlink(staged_js_dir, tests)
-            kwargs["extraProfileFiles"].append(os.path.join(staged_js_dir, "tests", "user.js"))
+            args.extraProfileFiles.append(os.path.join(staged_js_dir, "tests", "user.js"))
         else:
             tests = os.path.join(self.reftest_dir, "tests")
             if not os.path.isdir(tests):
                 os.symlink(self.topsrcdir, tests)
-            for i, path in enumerate(kwargs["tests"]):
+            for i, path in enumerate(args.tests):
                 # Non-absolute paths are relative to the packaged directory, which
                 # has an extra tests/ at the start
                 if os.path.exists(os.path.abspath(path)):
                     path = os.path.relpath(path, os.path.join(self.topsrcdir))
-                kwargs["tests"][i] = os.path.join('tests', path)
+                args.tests[i] = os.path.join('tests', path)
 
         self.log_manager.enable_unstructured()
         try:
             rv = remotereftest.run(**kwargs)
         finally:
             self.log_manager.disable_unstructured()
 
         return rv
@@ -292,24 +300,26 @@ def process_test_objects(kwargs):
     if "test_objects" in kwargs:
         if kwargs["tests"] is None:
             kwargs["tests"] = []
         kwargs["tests"].extend(item["path"] for item in kwargs["test_objects"])
         del kwargs["test_objects"]
 
 
 def get_parser():
+    global parser
     here = os.path.abspath(os.path.dirname(__file__))
     build_obj = MozbuildObject.from_environment(cwd=here)
     if conditions.is_android(build_obj):
-        return reftestcommandline.RemoteArgumentsParser()
+        parser = reftestcommandline.RemoteArgumentsParser()
     elif conditions.is_mulet(build_obj):
-        return reftestcommandline.B2GArgumentParser()
+        parser = reftestcommandline.B2GArgumentParser()
     else:
-        return reftestcommandline.DesktopArgumentsParser()
+        parser = reftestcommandline.DesktopArgumentsParser()
+    return parser
 
 
 @CommandProvider
 class MachCommands(MachCommandBase):
     @Command('reftest',
              category='testing',
              description='Run reftests (layout and graphics correctness).',
              parser=get_parser)
@@ -379,25 +389,25 @@ class B2GCommands(MachCommandBase):
         MachCommandBase.__init__(self, context)
 
         for attr in ('b2g_home', 'xre_path', 'device_name'):
             setattr(self, attr, getattr(context, attr, None))
 
     @Command('reftest-remote', category='testing',
              description='Run a remote reftest (b2g layout and graphics correctness, remote device).',
              conditions=[conditions.is_b2g, is_emulator],
-             parser=reftestcommandline.B2GArgumentParser)
+             parser=get_parser)
     def run_reftest_remote(self, **kwargs):
         kwargs["suite"] = "reftest"
         return self._run_reftest(**kwargs)
 
     @Command('crashtest-remote', category='testing',
              description='Run a remote crashtest (Check if b2g crashes on a page, remote device).',
              conditions=[conditions.is_b2g, is_emulator],
-             parser=reftestcommandline.B2GArgumentParser)
+             parser=get_parser)
     def run_crashtest_remote(self, test_file, **kwargs):
         kwargs["suite"] = "crashtest"
         return self._run_reftest(**kwargs)
 
     def _run_reftest(self, **kwargs):
         process_test_objects(kwargs)
         if self.device_name:
             if self.device_name.startswith('emulator'):
--- a/layout/tools/reftest/mach_test_package_commands.py
+++ b/layout/tools/reftest/mach_test_package_commands.py
@@ -1,44 +1,55 @@
 # 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/.
 
 from __future__ import unicode_literals
 
 import os
+from argparse import Namespace
 from functools import partial
 
 from mach.decorators import (
     CommandProvider,
     Command,
 )
 
 
 def run_reftest(context, **kwargs):
-    kwargs['app'] = kwargs['app'] or context.firefox_bin
-    kwargs['e10s'] = context.mozharness_config.get('e10s', kwargs['e10s'])
-    kwargs['certPath'] = context.certs_dir
-    kwargs['utilityPath'] = context.bin_dir
-    kwargs['extraProfileFiles'].append(os.path.join(context.bin_dir, 'plugins'))
+    import mozinfo
 
-    if not kwargs['tests']:
-        kwargs['tests'] = [os.path.join('layout', 'reftests', 'reftest.list')]
+    args = Namespace(**kwargs)
+    args.e10s = context.mozharness_config.get('e10s', args.e10s)
+
+    if not args.tests:
+        args.tests = [os.path.join('layout', 'reftests', 'reftest.list')]
 
     test_root = os.path.join(context.package_root, 'reftest', 'tests')
     normalize = partial(context.normalize_test_path, test_root)
-    kwargs['tests'] = map(normalize, kwargs['tests'])
+    args.tests = map(normalize, args.tests)
+
+    return run_reftest_desktop(context, args)
+
 
-    from runreftest import run as run_test_harness
-    return run_test_harness(**kwargs)
+def run_reftest_desktop(context, args):
+    from runreftest import run_test_harness
+
+    args.app = args.app or context.firefox_bin
+    args.extraProfileFiles.append(os.path.join(context.bin_dir, 'plugins'))
+    args.utilityPath = context.bin_dir
+
+    return run_test_harness(parser, args)
 
 
 def setup_argument_parser():
-    from reftestcommandline import DesktopArgumentsParser
-    return DesktopArgumentsParser()
+    import reftestcommandline
+    global parser
+    parser = reftestcommandline.DesktopArgumentsParser()
+    return parser
 
 
 @CommandProvider
 class ReftestCommands(object):
 
     def __init__(self, context):
         self.context = context
 
--- a/layout/tools/reftest/remotereftest.py
+++ b/layout/tools/reftest/remotereftest.py
@@ -315,17 +315,18 @@ class RemoteReftest(RefTest):
         RefTest.cleanup(self, profileDir)
         if (self.pidFile != ""):
             try:
                 os.remove(self.pidFile)
                 os.remove(self.pidFile + ".xpcshell.pid")
             except:
                 print "Warning: cleaning up pidfile '%s' was unsuccessful from the test harness" % self.pidFile
 
-def runTests(options, parser):
+
+def run_test_harness(parser, options):
     if (options.dm_trans == 'sut' and options.deviceIP == None):
         print "Error: If --dm_trans = sut, you must provide a device IP to connect to via the --deviceIP option"
         return 1
 
     try:
         if (options.dm_trans == "adb"):
             if (options.deviceIP):
                 dm = mozdevice.DroidADB(options.deviceIP, options.devicePort, deviceRoot=options.remoteTestRoot)
@@ -393,25 +394,13 @@ def runTests(options, parser):
 
     reftest.stopWebServer(options)
 
     if options.printDeviceInfo:
         reftest.printDeviceInfo(printLogcat=True)
 
     return retVal
 
-def run(**kwargs):
-    # Mach gives us kwargs; this is a way to turn them back into an
-    # options object
-    parser = reftestcommandline.RemoteArgumentsParser()
-    parser.set_defaults(**kwargs)
-    options = parser.parse_args(kwargs["tests"])
-    retVal = runTests(options, parser)
-    return retVal
 
-def main():
+if __name__ == "__main__":
     parser = reftestcommandline.RemoteArgumentsParser()
     options = parser.parse_args()
-    retVal = runTests(options, parser)
-    return retVal
-
-if __name__ == "__main__":
-    sys.exit(main())
+    sys.exit(run_test_harness(parser, options))
--- a/layout/tools/reftest/runreftest.py
+++ b/layout/tools/reftest/runreftest.py
@@ -708,28 +708,17 @@ class RefTest(object):
                 dest = os.path.join(profileDir, os.path.basename(abspath))
                 shutil.copytree(abspath, dest)
             else:
                 self.log.warning(
                     "runreftest.py | Failed to copy %s to profile" % abspath)
                 continue
 
 
-def run(**kwargs):
-    parser = reftestcommandline.DesktopArgumentsParser()
-
-    # Mach gives us kwargs; this is a way to turn them back into an
-    # options object
-    parser.set_defaults(**kwargs)
-
-    if 'tests' in kwargs:
-        options = parser.parse_args(kwargs["tests"])
-    else:
-        options = parser.parse_args()
-
+def run_test_harness(parser, options):
     reftest = RefTest()
     parser.validate(options, reftest)
 
     # We have to validate options.app here for the case when the mach
     # command is able to find it after argument parsing. This can happen
     # when running from a tests.zip.
     if not options.app:
         parser.error("could not find the application path, --appname must be specified")
@@ -741,9 +730,11 @@ def run(**kwargs):
 
     if options.xrePath is None:
         options.xrePath = os.path.dirname(options.app)
 
     return reftest.runTests(options.tests, options)
 
 
 if __name__ == "__main__":
-    sys.exit(run())
+    parser = reftestcommandline.DesktopArgumentsParser()
+    options = parser.parse_args()
+    sys.exit(run_test_harness(parser, options))
--- a/layout/tools/reftest/runreftestb2g.py
+++ b/layout/tools/reftest/runreftestb2g.py
@@ -320,17 +320,20 @@ class B2GRemoteReftest(RefTest):
                                         xrePath=options.xrePath,
                                         debuggerInfo=debuggerInfo,
                                         symbolsPath=symbolsPath,
                                         timeout=timeout,
                                         outputHandler=outputHandler)
         return status
 
 
-def run_remote_reftests(parser, options):
+def run_test_harness(parser, options):
+    if options.mulet:
+        return run_mulet_reftests(parser, options)
+
     auto = B2GRemoteAutomation(None, "fennec")
 
     # create our Marionette instance
     kwargs = {}
     if options.emulator:
         kwargs['emulator'] = options.emulator
         auto.setEmulator(True)
         if options.noWindow:
@@ -415,28 +418,12 @@ def run_remote_reftests(parser, options)
         except:
             pass
         return 1
 
     reftest.stopWebServer(options)
     return retVal
 
 
-def run(**kwargs):
-    # Mach gives us kwargs; this is a way to turn them back into an
-    # options object
+if __name__ == "__main__":
     parser = reftestcommandline.B2GArgumentParser()
-    parser.set_defaults(**kwargs)
-    options = parser.parse_args(kwargs["tests"])
-    return run_remote_reftests(parser, options)
-
-
-def main(args=sys.argv[1:]):
-    parser = reftestcommandline.B2GArgumentParser()
-    options = parser.parse_args(args)
-
-    if options.mulet:
-        return run_mulet_reftests(parser, options)
-    return run_remote_reftests(parser, options)
-
-
-if __name__ == "__main__":
-    sys.exit(main())
+    options = parser.parse_args()
+    sys.exit(run_test_harness(parser, options))
--- a/layout/tools/reftest/runreftestmulet.py
+++ b/layout/tools/reftest/runreftestmulet.py
@@ -196,18 +196,9 @@ def run_test_harness(parser, options):
             options.app = "%s-bin" % options.app
 
     if options.xrePath is None:
         options.xrePath = os.path.dirname(options.app)
 
     if options.mulet and not options.profile:
         raise Exception("must specify --profile when specifying --mulet")
 
-    sys.exit(reftest.run_tests(options.tests, options))
-
-
-def run(**kwargs):
-    # Mach gives us kwargs; this is a way to turn them back into an
-    # options object
-    parser = reftestcommandline.B2GArgumentParser()
-    parser.set_defaults(**kwargs)
-    options = parser.parse_args(kwargs['tests'])
-    return run_test_harness(parser, options)
+    return reftest.run_tests(options.tests, options)