diff options
Diffstat (limited to 'layout/tools/reftest/reftestcommandline.py')
-rw-r--r-- | layout/tools/reftest/reftestcommandline.py | 749 |
1 files changed, 749 insertions, 0 deletions
diff --git a/layout/tools/reftest/reftestcommandline.py b/layout/tools/reftest/reftestcommandline.py new file mode 100644 index 000000000..da76fbd9a --- /dev/null +++ b/layout/tools/reftest/reftestcommandline.py @@ -0,0 +1,749 @@ +import argparse +import os +import sys +from collections import OrderedDict +from urlparse import urlparse + +import mozlog + +here = os.path.abspath(os.path.dirname(__file__)) + + +class ReftestArgumentsParser(argparse.ArgumentParser): + def __init__(self, **kwargs): + super(ReftestArgumentsParser, self).__init__(**kwargs) + + # Try to import a MozbuildObject. Success indicates that we are + # running from a source tree. This allows some defaults to be set + # from the source tree. + try: + from mozbuild.base import MozbuildObject + self.build_obj = MozbuildObject.from_environment(cwd=here) + except ImportError: + self.build_obj = None + + self.add_argument("--xre-path", + action="store", + type=str, + dest="xrePath", + # individual scripts will set a sane default + default=None, + help="absolute path to directory containing XRE (probably xulrunner)") + + self.add_argument("--symbols-path", + action="store", + type=str, + dest="symbolsPath", + default=None, + help="absolute path to directory containing breakpad symbols, or the URL of a zip file containing symbols") + + self.add_argument("--debugger", + action="store", + dest="debugger", + help="use the given debugger to launch the application") + + self.add_argument("--debugger-args", + action="store", + dest="debuggerArgs", + help="pass the given args to the debugger _before_ " + "the application on the command line") + + self.add_argument("--debugger-interactive", + action="store_true", + dest="debuggerInteractive", + help="prevents the test harness from redirecting " + "stdout and stderr for interactive debuggers") + + self.add_argument("--appname", + action="store", + type=str, + dest="app", + default=None, + help="absolute path to application, overriding default") + + self.add_argument("--extra-profile-file", + action="append", + dest="extraProfileFiles", + default=[], + help="copy specified files/dirs to testing profile") + + self.add_argument("--timeout", + action="store", + dest="timeout", + type=int, + default=5 * 60, # 5 minutes per bug 479518 + help="reftest will timeout in specified number of seconds. [default %(default)s].") + + self.add_argument("--leak-threshold", + action="store", + type=int, + dest="defaultLeakThreshold", + default=0, + help="fail if the number of bytes leaked in default " + "processes through refcounted objects (or bytes " + "in classes with MOZ_COUNT_CTOR and MOZ_COUNT_DTOR) " + "is greater than the given number") + + self.add_argument("--utility-path", + action="store", + type=str, + dest="utilityPath", + default=self.build_obj.bindir if self.build_obj else None, + help="absolute path to directory containing utility " + "programs (xpcshell, ssltunnel, certutil)") + + self.add_argument("--total-chunks", + type=int, + dest="totalChunks", + help="how many chunks to split the tests up into") + + self.add_argument("--this-chunk", + type=int, + dest="thisChunk", + help="which chunk to run between 1 and --total-chunks") + + self.add_argument("--log-file", + action="store", + type=str, + dest="logFile", + default=None, + help="file to log output to in addition to stdout") + + self.add_argument("--skip-slow-tests", + dest="skipSlowTests", + action="store_true", + default=False, + help="skip tests marked as slow when running") + + self.add_argument("--ignore-window-size", + dest="ignoreWindowSize", + action="store_true", + default=False, + help="ignore the window size, which may cause spurious failures and passes") + + self.add_argument("--install-extension", + action="append", + dest="extensionsToInstall", + default=[], + help="install the specified extension in the testing profile. " + "The extension file's name should be <id>.xpi where <id> is " + "the extension's id as indicated in its install.rdf. " + "An optional path can be specified too.") + + self.add_argument("--marionette", + default=None, + help="host:port to use when connecting to Marionette") + + self.add_argument("--marionette-port-timeout", + default=None, + help=argparse.SUPPRESS) + + self.add_argument("--marionette-socket-timeout", + default=None, + help=argparse.SUPPRESS) + + self.add_argument("--marionette-startup-timeout", + default=None, + help=argparse.SUPPRESS) + + self.add_argument("--setenv", + action="append", + type=str, + default=[], + dest="environment", + metavar="NAME=VALUE", + help="sets the given variable in the application's " + "environment") + + self.add_argument("--filter", + action="store", + type=str, + dest="filter", + help="specifies a regular expression (as could be passed to the JS " + "RegExp constructor) to test against URLs in the reftest manifest; " + "only test items that have a matching test URL will be run.") + + self.add_argument("--shuffle", + action="store_true", + default=False, + dest="shuffle", + help="run reftests in random order") + + self.add_argument("--run-until-failure", + action="store_true", + default=False, + dest="runUntilFailure", + help="stop running on the first failure. Useful for RR recordings.") + + self.add_argument("--repeat", + action="store", + type=int, + default=0, + dest="repeat", + help="number of times the select test(s) will be executed. Useful for " + "finding intermittent failures.") + + self.add_argument("--focus-filter-mode", + action="store", + type=str, + dest="focusFilterMode", + default="all", + help="filters tests to run by whether they require focus. " + "Valid values are `all', `needs-focus', or `non-needs-focus'. " + "Defaults to `all'.") + + self.add_argument("--disable-e10s", + action="store_false", + default=True, + dest="e10s", + help="disables content processes") + + self.add_argument("--setpref", + action="append", + type=str, + default=[], + dest="extraPrefs", + metavar="PREF=VALUE", + help="defines an extra user preference") + + self.add_argument("--reftest-extension-path", + action="store", + dest="reftestExtensionPath", + help="Path to the reftest extension") + + self.add_argument("--special-powers-extension-path", + action="store", + dest="specialPowersExtensionPath", + help="Path to the special powers extension") + + self.add_argument("--suite", + choices=["reftest", "crashtest", "jstestbrowser"], + default=None, + help=argparse.SUPPRESS) + + self.add_argument("--cleanup-crashes", + action = "store_true", + dest = "cleanupCrashes", + default = False, + help = "Delete pending crash reports before running tests.") + + self.add_argument("tests", + metavar="TEST_PATH", + nargs="*", + help="Path to test file, manifest file, or directory containing tests") + + mozlog.commandline.add_logging_group(self) + + def get_ip(self): + import moznetwork + if os.name != "nt": + return moznetwork.get_ip() + else: + self.error( + "ERROR: you must specify a --remote-webserver=<ip address>\n") + + def set_default_suite(self, options): + manifests = OrderedDict([("reftest.list", "reftest"), + ("crashtests.list", "crashtest"), + ("jstests.list", "jstestbrowser")]) + + for test_path in options.tests: + file_name = os.path.basename(test_path) + if file_name in manifests: + options.suite = manifests[file_name] + return + + for test_path in options.tests: + for manifest_file, suite in manifests.iteritems(): + if os.path.exists(os.path.join(test_path, manifest_file)): + options.suite = suite + return + + self.error("Failed to determine test suite; supply --suite to set this explicitly") + + def validate(self, options, reftest): + if not options.tests: + # Can't just set this in the argument parser because mach will set a default + self.error("Must supply at least one path to a manifest file, test directory, or test file to run.") + + if options.suite is None: + self.set_default_suite(options) + + if options.totalChunks is not None and options.thisChunk is None: + self.error( + "thisChunk must be specified when totalChunks is specified") + + if options.totalChunks: + if not 1 <= options.thisChunk <= options.totalChunks: + self.error("thisChunk must be between 1 and totalChunks") + + if options.logFile: + options.logFile = reftest.getFullPath(options.logFile) + + if options.xrePath is not None: + if not os.access(options.xrePath, os.F_OK): + self.error("--xre-path '%s' not found" % options.xrePath) + if not os.path.isdir(options.xrePath): + self.error("--xre-path '%s' is not a directory" % + options.xrePath) + options.xrePath = reftest.getFullPath(options.xrePath) + + if options.reftestExtensionPath is None: + if self.build_obj is not None: + reftestExtensionPath = os.path.join(self.build_obj.topobjdir, "_tests", + "reftest", "reftest") + else: + reftestExtensionPath = os.path.join(here, "reftest") + options.reftestExtensionPath = os.path.normpath(reftestExtensionPath) + + if (options.specialPowersExtensionPath is None and + options.suite in ["crashtest", "jstestbrowser"]): + if self.build_obj is not None: + specialPowersExtensionPath = os.path.join(self.build_obj.topobjdir, "_tests", + "reftest", "specialpowers") + else: + specialPowersExtensionPath = os.path.join(here, "specialpowers") + options.specialPowersExtensionPath = os.path.normpath(specialPowersExtensionPath) + + options.leakThresholds = { + "default": options.defaultLeakThreshold, + "tab": 5000, # See dependencies of bug 1051230. + } + + +class DesktopArgumentsParser(ReftestArgumentsParser): + def __init__(self, **kwargs): + super(DesktopArgumentsParser, self).__init__(**kwargs) + + self.add_argument("--run-tests-in-parallel", + action="store_true", + default=False, + dest="runTestsInParallel", + help="run tests in parallel if possible") + + def _prefs_gpu(self): + if mozinfo.os != "win": + return ["layers.acceleration.force-enabled=true"] + return [] + + def validate(self, options, reftest): + super(DesktopArgumentsParser, self).validate(options, reftest) + + if options.runTestsInParallel: + if options.logFile is not None: + self.error("cannot specify logfile with parallel tests") + if options.totalChunks is not None or options.thisChunk is not None: + self.error( + "cannot specify thisChunk or totalChunks with parallel tests") + if options.focusFilterMode != "all": + self.error("cannot specify focusFilterMode with parallel tests") + if options.debugger is not None: + self.error("cannot specify a debugger with parallel tests") + + if options.debugger: + # valgrind and some debuggers may cause Gecko to start slowly. Make sure + # marionette waits long enough to connect. + options.marionette_port_timeout = 900 + options.marionette_socket_timeout = 540 + + if not options.tests: + self.error("No test files specified.") + + if options.app is None: + bin_dir = (self.build_obj.get_binary_path() if + self.build_obj and self.build_obj.substs[ + 'MOZ_BUILD_APP'] != 'mobile/android' + else None) + + if bin_dir: + options.app = bin_dir + + if options.symbolsPath and len(urlparse(options.symbolsPath).scheme) < 2: + options.symbolsPath = reftest.getFullPath(options.symbolsPath) + + options.utilityPath = reftest.getFullPath(options.utilityPath) + + +class B2GArgumentParser(ReftestArgumentsParser): + def __init__(self, **kwargs): + super(B2GArgumentParser, self).__init__(**kwargs) + + self.add_argument("--browser-arg", + action="store", + type=str, + dest="browser_arg", + help="Optional command-line arg to pass to the browser") + + self.add_argument("--b2gpath", + action="store", + type=str, + dest="b2gPath", + help="path to B2G repo or qemu dir") + + self.add_argument("--emulator", + action="store", + type=str, + dest="emulator", + help="Architecture of emulator to use: x86 or arm") + + self.add_argument("--emulator-res", + action="store", + type=str, + dest="emulator_res", + help="Emulator resolution of the format '<width>x<height>'") + + self.add_argument("--no-window", + action="store_true", + dest="noWindow", + default=False, + help="Pass --no-window to the emulator") + + self.add_argument("--adbpath", + action="store", + type=str, + dest="adb_path", + default="adb", + help="path to adb") + + self.add_argument("--deviceIP", + action="store", + type=str, + dest="deviceIP", + help="ip address of remote device to test") + + self.add_argument("--devicePort", + action="store", + type=str, + dest="devicePort", + default="20701", + help="port of remote device to test") + + self.add_argument("--remote-logfile", + action="store", + type=str, + dest="remoteLogFile", + help="Name of log file on the device relative to the device root. PLEASE ONLY USE A FILENAME.") + + self.add_argument("--remote-webserver", + action="store", + type=str, + dest="remoteWebServer", + help="ip address where the remote web server is hosted at") + + self.add_argument("--http-port", + action="store", + type=str, + dest="httpPort", + help="ip address where the remote web server is hosted at") + + self.add_argument("--ssl-port", + action="store", + type=str, + dest="sslPort", + help="ip address where the remote web server is hosted at") + + self.add_argument("--pidfile", + action="store", + type=str, + dest="pidFile", + default="", + help="name of the pidfile to generate") + + self.add_argument("--gecko-path", + action="store", + type=str, + dest="geckoPath", + help="the path to a gecko distribution that should " + "be installed on the emulator prior to test") + + self.add_argument("--logdir", + action="store", + type=str, + dest="logdir", + help="directory to store log files") + + self.add_argument('--busybox', + action='store', + type=str, + dest='busybox', + help="Path to busybox binary to install on device") + + self.add_argument("--httpd-path", + action="store", + type=str, + dest="httpdPath", + help="path to the httpd.js file") + + self.add_argument("--profile", + action="store", + type=str, + dest="profile", + help="for mulet testing, the path to the " + "gaia profile to use") + + self.add_argument("--mulet", + action="store_true", + dest="mulet", + default=False, + help="Run the tests on a B2G desktop build") + + self.set_defaults(remoteTestRoot=None, + logFile="reftest.log", + autorun=True, + closeWhenDone=True, + testPath="") + + def validate_remote(self, options, automation): + if not options.app: + options.app = automation.DEFAULT_APP + + if not options.remoteTestRoot: + options.remoteTestRoot = automation._devicemanager.deviceRoot + \ + "/reftest" + + options.remoteProfile = options.remoteTestRoot + "/profile" + + productRoot = options.remoteTestRoot + "/" + automation._product + if options.utilityPath is None: + options.utilityPath = productRoot + "/bin" + + if not options.httpPort: + options.httpPort = automation.DEFAULT_HTTP_PORT + + if not options.sslPort: + options.sslPort = automation.DEFAULT_SSL_PORT + + if options.remoteWebServer is None: + options.remoteWebServer = self.get_ip() + + options.webServer = options.remoteWebServer + + if options.geckoPath and not options.emulator: + self.error( + "You must specify --emulator if you specify --gecko-path") + + if options.logdir and not options.emulator: + self.error("You must specify --emulator if you specify --logdir") + + if options.remoteLogFile is None: + options.remoteLogFile = "reftest.log" + + options.localLogName = options.remoteLogFile + options.remoteLogFile = options.remoteTestRoot + \ + '/' + options.remoteLogFile + + # Ensure that the options.logfile (which the base class uses) is set to + # the remote setting when running remote. Also, if the user set the + # log file name there, use that instead of reusing the remotelogfile as + # above. + if (options.logFile): + # If the user specified a local logfile name use that + options.localLogName = options.logFile + options.logFile = options.remoteLogFile + + # Only reset the xrePath if it wasn't provided + if options.xrePath is None: + options.xrePath = options.utilityPath + options.xrePath = os.path.abspath(options.xrePath) + + if options.pidFile != "": + f = open(options.pidFile, 'w') + f.write("%s" % os.getpid()) + f.close() + + # httpd-path is specified by standard makefile targets and may be specified + # on the command line to select a particular version of httpd.js. If not + # specified, try to select the one from from the xre bundle, as + # required in bug 882932. + if not options.httpdPath: + options.httpdPath = os.path.join(options.xrePath, "components") + + return options + + +class RemoteArgumentsParser(ReftestArgumentsParser): + def __init__(self, **kwargs): + super(RemoteArgumentsParser, self).__init__() + + # app, xrePath and utilityPath variables are set in main function + self.set_defaults(logFile="reftest.log", + app="", + xrePath="", + utilityPath="", + localLogName=None) + + self.add_argument("--remote-app-path", + action="store", + type=str, + dest="remoteAppPath", + help="Path to remote executable relative to device root using only forward slashes. Either this or app must be specified, but not both.") + + self.add_argument("--adbpath", + action="store", + type=str, + dest="adb_path", + default="adb", + help="path to adb") + + self.add_argument("--deviceIP", + action="store", + type=str, + dest="deviceIP", + help="ip address of remote device to test") + + self.add_argument("--deviceSerial", + action="store", + type=str, + dest="deviceSerial", + help="adb serial number of remote device to test") + + self.add_argument("--devicePort", + action="store", + type=str, + default="20701", + dest="devicePort", + help="port of remote device to test") + + self.add_argument("--remote-product-name", + action="store", + type=str, + dest="remoteProductName", + default="fennec", + help="Name of product to test - either fennec or firefox, defaults to fennec") + + self.add_argument("--remote-webserver", + action="store", + type=str, + dest="remoteWebServer", + help="IP Address of the webserver hosting the reftest content") + + self.add_argument("--http-port", + action="store", + type=str, + dest="httpPort", + help="port of the web server for http traffic") + + self.add_argument("--ssl-port", + action="store", + type=str, + dest="sslPort", + help="Port for https traffic to the web server") + + self.add_argument("--remote-logfile", + action="store", + type=str, + dest="remoteLogFile", + default="reftest.log", + help="Name of log file on the device relative to device root. PLEASE USE ONLY A FILENAME.") + + self.add_argument("--pidfile", + action="store", + type=str, + dest="pidFile", + default="", + help="name of the pidfile to generate") + + self.add_argument("--dm_trans", + action="store", + type=str, + dest="dm_trans", + default="sut", + help="the transport to use to communicate with device: [adb|sut]; default=sut") + + self.add_argument("--remoteTestRoot", + action="store", + type=str, + dest="remoteTestRoot", + help="remote directory to use as test root (eg. /mnt/sdcard/tests or /data/local/tests)") + + self.add_argument("--httpd-path", + action="store", + type=str, + dest="httpdPath", + help="path to the httpd.js file") + + self.add_argument("--no-device-info", + action="store_false", + dest="printDeviceInfo", + default=True, + help="do not display verbose diagnostics about the remote device") + + def validate_remote(self, options, automation): + # Ensure our defaults are set properly for everything we can infer + if not options.remoteTestRoot: + options.remoteTestRoot = automation._devicemanager.deviceRoot + \ + '/reftest' + options.remoteProfile = options.remoteTestRoot + "/profile" + + if options.remoteWebServer is None: + options.remoteWebServer = self.get_ip() + + # Verify that our remotewebserver is set properly + if options.remoteWebServer == '127.0.0.1': + self.error("ERROR: Either you specified the loopback for the remote webserver or ", + "your local IP cannot be detected. Please provide the local ip in --remote-webserver") + + if not options.httpPort: + options.httpPort = automation.DEFAULT_HTTP_PORT + + if not options.sslPort: + options.sslPort = automation.DEFAULT_SSL_PORT + + # One of remoteAppPath (relative path to application) or the app (executable) must be + # set, but not both. If both are set, we destroy the user's selection for app + # so instead of silently destroying a user specificied setting, we + # error. + if options.remoteAppPath and options.app: + self.error( + "ERROR: You cannot specify both the remoteAppPath and the app") + elif options.remoteAppPath: + options.app = options.remoteTestRoot + "/" + options.remoteAppPath + elif options.app is None: + # Neither remoteAppPath nor app are set -- error + self.error("ERROR: You must specify either appPath or app") + + if options.xrePath is None: + self.error( + "ERROR: You must specify the path to the controller xre directory") + else: + # Ensure xrepath is a full path + options.xrePath = os.path.abspath(options.xrePath) + + options.localLogName = options.remoteLogFile + options.remoteLogFile = options.remoteTestRoot + \ + '/' + options.remoteLogFile + + # Ensure that the options.logfile (which the base class uses) is set to + # the remote setting when running remote. Also, if the user set the + # log file name there, use that instead of reusing the remotelogfile as + # above. + if options.logFile: + # If the user specified a local logfile name use that + options.localLogName = options.logFile + + options.logFile = options.remoteLogFile + + if options.pidFile != "": + with open(options.pidFile, 'w') as f: + f.write(str(os.getpid())) + + # httpd-path is specified by standard makefile targets and may be specified + # on the command line to select a particular version of httpd.js. If not + # specified, try to select the one from hostutils.zip, as required in + # bug 882932. + if not options.httpdPath: + options.httpdPath = os.path.join(options.utilityPath, "components") + + if not options.ignoreWindowSize: + parts = automation._devicemanager.getInfo( + 'screen')['screen'][0].split() + width = int(parts[0].split(':')[1]) + height = int(parts[1].split(':')[1]) + if (width < 1366 or height < 1050): + self.error("ERROR: Invalid screen resolution %sx%s, please adjust to 1366x1050 or higher" % ( + width, height)) + + # Disable e10s by default on Android because we don't run Android + # e10s jobs anywhere yet. + options.e10s = False + return options |