Bug 1288827 - Allow running mochitest from test-package without specifying --appname, r?armenzg
Because it is now possible for options.app to get set after 'parse_args' time, we need to make sure
the argument validation happens later. To accomplish this we pass in the parser instance to
'run_test_harness' and do parser.validate there. This unfortunately requires some minor uses of
global to accomplish easily due to how mach handles parsers.
MozReview-Commit-ID: s3Js1aZlSE
--- a/testing/mochitest/mach_commands.py
+++ b/testing/mochitest/mach_commands.py
@@ -126,16 +126,18 @@ ALL_FLAVORS = {
}
},
}
SUPPORTED_APPS = ['firefox', 'b2g', 'android', 'mulet']
SUPPORTED_FLAVORS = list(chain.from_iterable([f['aliases'] for f in ALL_FLAVORS.values()]))
CANONICAL_FLAVORS = sorted([f['aliases'][0] for f in ALL_FLAVORS.values()])
+parser = None
+
class MochitestRunner(MozbuildObject):
"""Easily run mochitests.
This currently contains just the basics for running mochitests. We may want
to hook up result parsing, etc.
"""
@@ -192,17 +194,17 @@ class MochitestRunner(MozbuildObject):
options = Namespace(**kwargs)
from manifestparser import TestManifest
if tests:
manifest = TestManifest()
manifest.tests.extend(tests)
options.manifestFile = manifest
- return mochitest.run_test_harness(options)
+ return mochitest.run_test_harness(parser, options)
def run_desktop_test(self, context, tests=None, suite=None, **kwargs):
"""Runs a mochitest.
suite is the type of mochitest to run. It can be one of ('plain',
'chrome', 'browser', 'a11y', 'jetpack-package', 'jetpack-addon').
"""
# runtests.py is ambiguous, so we load the file/module manually.
@@ -237,17 +239,17 @@ class MochitestRunner(MozbuildObject):
# refresh the page to pick up modifications. Therefore leave the browser
# open if only running a single mochitest-plain test. This behaviour can
# be overridden by passing in --keep-open=false.
if len(tests) == 1 and options.keep_open is None and suite == 'plain':
options.keep_open = True
# We need this to enable colorization of output.
self.log_manager.enable_unstructured()
- result = mochitest.run_test_harness(options)
+ result = mochitest.run_test_harness(parser, options)
self.log_manager.disable_unstructured()
return result
def run_android_test(self, context, tests, suite=None, **kwargs):
host_ret = verify_host_bin()
if host_ret != 0:
return host_ret
@@ -261,17 +263,17 @@ class MochitestRunner(MozbuildObject):
options = Namespace(**kwargs)
from manifestparser import TestManifest
if tests:
manifest = TestManifest()
manifest.tests.extend(tests)
options.manifestFile = manifest
- return runtestsremote.run_test_harness(options)
+ return runtestsremote.run_test_harness(parser, options)
def run_robocop_test(self, context, tests, suite=None, **kwargs):
host_ret = verify_host_bin()
if host_ret != 0:
return host_ret
import imp
path = os.path.join(self.mochitest_dir, 'runrobocop.py')
@@ -283,17 +285,17 @@ class MochitestRunner(MozbuildObject):
options = Namespace(**kwargs)
from manifestparser import TestManifest
if tests:
manifest = TestManifest()
manifest.tests.extend(tests)
options.manifestFile = manifest
- return runrobocop.run_test_harness(options)
+ return runrobocop.run_test_harness(parser, options)
# parser
def setup_argument_parser():
build_obj = MozbuildObject.from_environment(cwd=here)
build_path = os.path.join(build_obj.topobjdir, 'build')
@@ -316,17 +318,19 @@ def setup_argument_parser():
if conditions.is_android(build_obj):
# On Android, check for a connected device (and offer to start an
# emulator if appropriate) before running tests. This check must
# be done in this admittedly awkward place because
# MochitestArgumentParser initialization fails if no device is found.
from mozrunner.devices.android_device import verify_android_device
verify_android_device(build_obj, install=True, xre=True)
- return MochitestArgumentParser()
+ global parser
+ parser = MochitestArgumentParser()
+ return parser
# condition filters
def is_buildapp_in(*apps):
def is_buildapp_supported(cls):
for a in apps:
c = getattr(conditions, 'is_{}'.format(a), None)
--- a/testing/mochitest/mach_test_package_commands.py
+++ b/testing/mochitest/mach_test_package_commands.py
@@ -1,36 +1,49 @@
# 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
-import os
+from functools import partial
from mach.decorators import (
CommandProvider,
Command,
)
+parser = None
+
def run_mochitest(context, **kwargs):
args = Namespace(**kwargs)
args.certPath = context.certs_dir
args.utilityPath = context.bin_dir
args.extraProfileFiles.append(os.path.join(context.bin_dir, 'plugins'))
+ if not args.app:
+ args.app = context.find_firefox()
+
+ if args.test_paths:
+ test_root = os.path.join(context.package_root, 'mochitest', 'tests')
+ normalize = partial(context.normalize_test_path, test_root)
+ args.test_paths = map(normalize, args.test_paths)
+
from runtests import run_test_harness
- return run_test_harness(args)
+ return run_test_harness(parser, args)
def setup_argument_parser():
from mochitest_options import MochitestArgumentParser
- return MochitestArgumentParser(app='generic')
+ global parser
+ parser = MochitestArgumentParser(app='generic')
+ return parser
@CommandProvider
class MochitestCommands(object):
def __init__(self, context):
self.context = context
--- a/testing/mochitest/mochitest_options.py
+++ b/testing/mochitest/mochitest_options.py
@@ -1246,15 +1246,8 @@ class MochitestArgumentParser(ArgumentPa
containers = container_map[self.app]
self._containers = [c() for c in containers]
return self._containers
def validate(self, args):
for container in self.containers:
args = container.validate(self, args, self.context)
return args
-
- def parse_args(self, *args, **kwargs):
- return self.validate(ArgumentParser.parse_args(self, *args, **kwargs))
-
- def parse_known_args(self, *args, **kwargs):
- args, remainder = ArgumentParser.parse_known_args(self, *args, **kwargs)
- return (self.validate(args), remainder)
--- a/testing/mochitest/runrobocop.py
+++ b/testing/mochitest/runrobocop.py
@@ -527,17 +527,19 @@ class RobocopTestRunner(MochitestDesktop
print "INFO | runtests.py | Test summary: start."
logResult = self.logTestSummary()
print "INFO | runtests.py | Test summary: end."
if worstTestResult == 0:
worstTestResult = logResult
return worstTestResult
-def run_test_harness(options):
+def run_test_harness(parser, options):
+ parser.validate(options)
+
if options is None:
raise ValueError(
"Invalid options specified, use --help for a list of valid options")
message_logger = MessageLogger(logger=None)
process_args = {'messageLogger': message_logger}
auto = RemoteAutomation(None, "fennec", processArgs=process_args)
auto.setDeviceManager(options.dm)
runResult = -1
@@ -572,12 +574,12 @@ def run_test_harness(options):
pass
message_logger.finish()
return runResult
def main(args=sys.argv[1:]):
parser = MochitestArgumentParser(app='android')
options = parser.parse_args(args)
- return run_test_harness(options)
+ return run_test_harness(parser, options)
if __name__ == "__main__":
sys.exit(main())
--- a/testing/mochitest/runtests.py
+++ b/testing/mochitest/runtests.py
@@ -46,20 +46,21 @@ from manifestparser.filters import (
pathprefix,
subsuite,
tags,
)
try:
from marionette import Marionette
from marionette_driver.addons import Addons
-
-except ImportError:
- # Marionette not needed nor supported on android
- Marionette = None
+except ImportError, e:
+ # Defer ImportError until attempt to use Marionette
+ def reraise(*args, **kwargs):
+ raise(e)
+ Marionette = reraise
from leaks import ShutdownLeaks, LSANLeaks
from mochitest_options import (
MochitestArgumentParser, build_obj, get_default_valgrind_suppression_files
)
from mozprofile import Profile, Preferences
from mozprofile.permissions import ServerLocations
from urllib import quote_plus as encodeURIComponent
@@ -2660,17 +2661,19 @@ class MochitestDesktop(MochitestBase):
rootdir = '/'.join(test['path'].split('/')[:-1])
if rootdir not in dirlist:
dirlist.append(rootdir)
return dirlist
-def run_test_harness(options):
+def run_test_harness(parser, options):
+ parser.validate(options)
+
logger_options = {
key: value for key, value in vars(options).iteritems()
if key.startswith('log') or key == 'valgrind'}
runner = MochitestDesktop(logger_options)
options.runByDir = False
@@ -2712,12 +2715,12 @@ def run_test_harness(options):
def cli(args=sys.argv[1:]):
# parse command line options
parser = MochitestArgumentParser(app='generic')
options = parser.parse_args(args)
if options is None:
# parsing error
sys.exit(1)
- return run_test_harness(options)
+ return run_test_harness(parser, options)
if __name__ == "__main__":
sys.exit(cli())
--- a/testing/mochitest/runtestsb2g.py
+++ b/testing/mochitest/runtestsb2g.py
@@ -355,17 +355,19 @@ class MochitestB2G(MochitestBase):
if len(self.urlOpts) > 0:
test_url += "?" + "&".join(self.urlOpts)
self.start_script_args.append(test_url)
options.profilePath = self.app_ctx.remote_profile
options.logFile = self.local_log
-def run_test_harness(options):
+def run_test_harness(parser, options):
+ parser.validate(options)
+
# create our Marionette instance
marionette_args = {
'adb_path': options.adbPath,
'emulator': options.emulator,
'no_window': options.noWindow,
'logdir': options.logdir,
'busybox': options.busybox,
'symbols_path': options.symbolsPath,
@@ -408,12 +410,12 @@ def run_test_harness(options):
mochitest.message_logger.finish()
return retVal
def main():
parser = MochitestArgumentParser(app='b2g')
options = parser.parse_args()
- return run_test_harness(options)
+ return run_test_harness(parser, options)
if __name__ == "__main__":
sys.exit(main())
--- a/testing/mochitest/runtestsremote.py
+++ b/testing/mochitest/runtestsremote.py
@@ -288,17 +288,19 @@ class MochiRemote(MochitestDesktop):
# remove args not supported by automation.py
kwargs.pop('marionette_args', None)
kwargs.pop('quiet', None)
return self._automation.runApp(*args, **kwargs)
-def run_test_harness(options):
+def run_test_harness(parser, options):
+ parser.validate(options)
+
message_logger = MessageLogger(logger=None)
process_args = {'messageLogger': message_logger}
auto = RemoteAutomation(None, "fennec", processArgs=process_args)
if options is None:
raise ValueError("Invalid options specified, use --help for a list of valid options")
options.runByDir = False
@@ -380,13 +382,13 @@ def run_test_harness(options):
return retVal
def main(args=sys.argv[1:]):
parser = MochitestArgumentParser(app='android')
options = parser.parse_args(args)
- return run_test_harness(options)
+ return run_test_harness(parser, options)
if __name__ == "__main__":
sys.exit(main())