--- a/testing/xpcshell/mach_commands.py
+++ b/testing/xpcshell/mach_commands.py
@@ -1,17 +1,16 @@
# 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/.
# Integrates the xpcshell test runner with mach.
from __future__ import absolute_import, unicode_literals, print_function
-import argparse
import errno
import os
import sys
from mozlog import structured
from mozbuild.base import (
MachCommandBase,
--- a/testing/xpcshell/remotexpcshelltests.py
+++ b/testing/xpcshell/remotexpcshelltests.py
@@ -3,36 +3,39 @@
# 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 absolute_import, print_function
import logging
import posixpath
-import sys, os
+import os
+import sys
import subprocess
import runxpcshelltests as xpcshell
import tempfile
import time
from argparse import Namespace
from zipfile import ZipFile
from mozlog import commandline
import shutil
import mozdevice
import mozfile
import mozinfo
from xpcshellcommandline import parser_remote
here = os.path.dirname(os.path.abspath(__file__))
+
def remoteJoin(path1, path2):
return posixpath.join(path1, path2)
+
class RemoteXPCShellTestThread(xpcshell.XPCShellTestThread):
def __init__(self, *args, **kwargs):
xpcshell.XPCShellTestThread.__init__(self, *args, **kwargs)
self.shellReturnCode = None
# embed the mobile params from the harness into the TestThread
mobileArgs = kwargs.get('mobileArgs')
for key in mobileArgs:
@@ -40,17 +43,17 @@ class RemoteXPCShellTestThread(xpcshell.
def buildCmdTestFile(self, name):
remoteDir = self.remoteForLocal(os.path.dirname(name))
if remoteDir == self.remoteHere:
remoteName = os.path.basename(name)
else:
remoteName = remoteJoin(remoteDir, os.path.basename(name))
return ['-e', 'const _TEST_FILE = ["%s"];' %
- remoteName.replace('\\', '/')]
+ remoteName.replace('\\', '/')]
def remoteForLocal(self, local):
for mapping in self.pathMapping:
if (os.path.abspath(mapping.local) == os.path.abspath(local)):
return mapping.remote
return local
def setupTempDir(self):
@@ -165,22 +168,24 @@ class RemoteXPCShellTestThread(xpcshell.
def checkForCrashes(self,
dump_directory,
symbols_path,
test_name=None):
if not self.device.dirExists(self.remoteMinidumpDir):
# The minidumps directory is automatically created when Fennec
# (first) starts, so its lack of presence is a hint that
# something went wrong.
- print("Automation Error: No crash directory (%s) found on remote device" % self.remoteMinidumpDir)
+ print("Automation Error: No crash directory (%s) found on remote device" %
+ self.remoteMinidumpDir)
# Whilst no crash was found, the run should still display as a failure
return True
with mozfile.TemporaryDirectory() as dumpDir:
self.device.getDirectory(self.remoteMinidumpDir, dumpDir)
- crashed = xpcshell.XPCShellTestThread.checkForCrashes(self, dumpDir, symbols_path, test_name)
+ crashed = xpcshell.XPCShellTestThread.checkForCrashes(
+ self, dumpDir, symbols_path, test_name)
self.clearRemoteDir(self.remoteMinidumpDir)
return crashed
def communicate(self, proc):
f = open(proc, "r")
contents = f.read()
f.close()
os.remove(proc)
@@ -212,18 +217,18 @@ class RemoteXPCShellTestThread(xpcshell.
self.log.info("unable to delete %s: '%s'" % (remoteDir, str(out)))
self.log.info("retrying after 10 seconds...")
time.sleep(10)
try:
out = self.device.shellCheckOutput([self.remoteClearDirScript, remoteDir])
except mozdevice.DMError:
self.log.error("failed to delete %s: '%s'" % (remoteDir, str(out)))
- #TODO: consider creating a separate log dir. We don't have the test file structure,
- # so we use filename.log. Would rather see ./logs/filename.log
+ # TODO: consider creating a separate log dir. We don't have the test file structure,
+ # so we use filename.log. Would rather see ./logs/filename.log
def createLogFile(self, test, stdout):
try:
f = None
filename = test.replace('\\', '/').split('/')[-1] + ".log"
f = open(filename, "w")
f.write(stdout)
finally:
@@ -380,21 +385,21 @@ class XPCShellRemote(xpcshell.XPCShellTe
print("unable to determine app root: " + str(detail))
pass
return None
def setupUtilities(self):
if (not self.device.dirExists(self.remoteBinDir)):
# device.mkDir may fail here where shellCheckOutput may succeed -- see bug 817235
try:
- self.device.shellCheckOutput(["mkdir", self.remoteBinDir]);
+ self.device.shellCheckOutput(["mkdir", self.remoteBinDir])
except mozdevice.DMError:
# Might get a permission error; try again as root, if available
- self.device.shellCheckOutput(["mkdir", self.remoteBinDir], root=True);
- self.device.shellCheckOutput(["chmod", "777", self.remoteBinDir], root=True);
+ self.device.shellCheckOutput(["mkdir", self.remoteBinDir], root=True)
+ self.device.shellCheckOutput(["chmod", "777", self.remoteBinDir], root=True)
remotePrefDir = remoteJoin(self.remoteBinDir, "defaults/pref")
if (self.device.dirExists(self.remoteTmpDir)):
self.device.removeDir(self.remoteTmpDir)
self.device.mkDir(self.remoteTmpDir)
if (not self.device.dirExists(remotePrefDir)):
self.device.mkDirs(remoteJoin(remotePrefDir, "extra"))
if (not self.device.dirExists(self.remoteScriptsDir)):
@@ -418,17 +423,18 @@ class XPCShellRemote(xpcshell.XPCShellTe
"GenerateOCSPResponse"]
for fname in binaries:
local = os.path.join(self.localBin, fname)
if os.path.isfile(local):
print("Pushing %s.." % fname, file=sys.stderr)
remoteFile = remoteJoin(self.remoteBinDir, fname)
self.device.pushFile(local, remoteFile)
else:
- print("*** Expected binary %s not found in %s!" % (fname, self.localBin), file=sys.stderr)
+ print("*** Expected binary %s not found in %s!" %
+ (fname, self.localBin), file=sys.stderr)
local = os.path.join(self.localBin, "components/httpd.js")
remoteFile = remoteJoin(self.remoteComponentsDir, "httpd.js")
self.device.pushFile(local, remoteFile)
local = os.path.join(self.localBin, "components/httpd.manifest")
remoteFile = remoteJoin(self.remoteComponentsDir, "httpd.manifest")
self.device.pushFile(local, remoteFile)
@@ -507,25 +513,27 @@ class XPCShellRemote(xpcshell.XPCShellTe
self.device.pushDir(self.xpcDir, self.remoteScriptsDir)
def setupMinidumpDir(self):
if self.device.dirExists(self.remoteMinidumpDir):
self.device.removeDir(self.remoteMinidumpDir)
self.device.mkDir(self.remoteMinidumpDir)
def buildTestList(self, test_tags=None, test_paths=None, verify=False):
- xpcshell.XPCShellTests.buildTestList(self, test_tags=test_tags, test_paths=test_paths, verify=verify)
+ xpcshell.XPCShellTests.buildTestList(
+ self, test_tags=test_tags, test_paths=test_paths, verify=verify)
uniqueTestPaths = set([])
for test in self.alltests:
uniqueTestPaths.add(test['here'])
for testdir in uniqueTestPaths:
abbrevTestDir = os.path.relpath(testdir, self.xpcDir)
remoteScriptDir = remoteJoin(self.remoteScriptsDir, abbrevTestDir)
self.pathMapping.append(PathMapping(testdir, remoteScriptDir))
+
def verifyRemoteOptions(parser, options):
if isinstance(options, Namespace):
options = vars(options)
if options['localLib'] is None:
if options['localAPK'] and options['objdir']:
for path in ['dist/fennec', 'fennec/lib']:
options['localLib'] = os.path.join(options['objdir'], path)
@@ -551,24 +559,26 @@ def verifyRemoteOptions(parser, options)
parser.error("Couldn't find local binary dir, specify --local-bin-dir")
elif os.path.isfile(os.path.join(here, '..', 'bin', 'xpcshell')):
# assume tests are being run from a tests.zip
options['localBin'] = os.path.abspath(os.path.join(here, '..', 'bin'))
else:
parser.error("Couldn't find local binary dir, specify --local-bin-dir")
return options
+
class PathMapping:
def __init__(self, localDir, remoteDir):
self.local = localDir
self.remote = remoteDir
+
def main():
- if sys.version_info < (2,7):
+ if sys.version_info < (2, 7):
print("Error: You must use python version 2.7 or newer but less than 3.0", file=sys.stderr)
sys.exit(1)
parser = parser_remote()
options = parser.parse_args()
if not options.localAPK:
for file in os.listdir(os.path.join(options.objdir, "dist")):
if (file.endswith(".apk") and file.startswith("fennec")):
--- a/testing/xpcshell/runxpcshelltests.py
+++ b/testing/xpcshell/runxpcshelltests.py
@@ -2,32 +2,29 @@
#
# 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 absolute_import, print_function
import copy
-import importlib
import json
-import math
import mozdebug
-import mozinfo
import os
import random
import re
import shutil
import signal
import sys
import tempfile
import time
import traceback
-from argparse import ArgumentParser, Namespace
+from argparse import Namespace
from collections import defaultdict, deque, namedtuple
from datetime import datetime, timedelta
from distutils import dir_util
from functools import partial
from multiprocessing import cpu_count
from subprocess import Popen, PIPE, STDOUT
from tempfile import mkdtemp, gettempdir
from threading import (
@@ -84,41 +81,49 @@ from mozrunner.utils import get_stack_fi
# --------------------------------------------------------------
# TODO: perhaps this should be in a more generally shared location?
# This regex matches all of the C0 and C1 control characters
# (U+0000 through U+001F; U+007F; U+0080 through U+009F),
# except TAB (U+0009), CR (U+000D), LF (U+000A) and backslash (U+005C).
# A raw string is deliberately not used.
_cleanup_encoding_re = re.compile(u'[\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\x9f\\\\]')
+
+
def _cleanup_encoding_repl(m):
c = m.group(0)
return '\\\\' if c == '\\' else '\\x{0:02X}'.format(ord(c))
+
+
def cleanup_encoding(s):
"""S is either a byte or unicode string. Either way it may
contain control characters, unpaired surrogates, reserved code
points, etc. If it is a byte string, it is assumed to be
UTF-8, but it may not be *correct* UTF-8. Return a
sanitized unicode object."""
if not isinstance(s, basestring):
return unicode(s)
if not isinstance(s, unicode):
s = s.decode('utf-8', 'replace')
# Replace all C0 and C1 control characters with \xNN escapes.
return _cleanup_encoding_re.sub(_cleanup_encoding_repl, s)
+
""" Control-C handling """
gotSIGINT = False
+
+
def markGotSIGINT(signum, stackFrame):
global gotSIGINT
gotSIGINT = True
+
class XPCShellTestThread(Thread):
def __init__(self, test_object, retry=True, verbose=False, usingTSan=False,
- **kwargs):
+ **kwargs):
Thread.__init__(self)
self.daemon = True
self.test_object = test_object
self.retry = retry
self.verbose = verbose
self.usingTSan = usingTSan
@@ -164,17 +169,17 @@ class XPCShellTestThread(Thread):
self.saw_proc_start = False
self.saw_proc_end = False
self.complete_command = None
self.harness_timeout = kwargs.get('harness_timeout')
self.timedout = False
# event from main thread to signal work done
self.event = kwargs.get('event')
- self.done = False # explicitly set flag so we don't rely on thread.isAlive
+ self.done = False # explicitly set flag so we don't rely on thread.isAlive
def run(self):
try:
self.run_test()
except Exception as e:
self.exception = e
self.traceback = traceback.format_exc()
else:
@@ -249,17 +254,17 @@ class XPCShellTestThread(Thread):
"""
# timeout is needed by remote xpcshell to extend the
# devicemanager.shell() timeout. It is not used in this function.
if HAVE_PSUTIL:
popen_func = psutil.Popen
else:
popen_func = Popen
proc = popen_func(cmd, stdout=stdout, stderr=stderr,
- env=env, cwd=cwd)
+ env=env, cwd=cwd)
return proc
def checkForCrashes(self,
dump_directory,
symbols_path,
test_name=None):
"""
Simple wrapper to check for crashes.
@@ -326,17 +331,17 @@ class XPCShellTestThread(Thread):
self.clean_temp_dirs(self.test_object['path'])
def buildCmdTestFile(self, name):
"""
Build the command line arguments for the test file.
On a remote system, this may be overloaded to use a remote path structure.
"""
return ['-e', 'const _TEST_FILE = ["%s"];' %
- name.replace('\\', '/')]
+ name.replace('\\', '/')]
def setupTempDir(self):
tempDir = mkdtemp(prefix='xpc-other-', dir=self._rootTempDir)
self.env["XPCSHELL_TEST_TEMP_DIR"] = tempDir
if self.interactive:
self.log.info("temp dir is %s" % tempDir)
return tempDir
@@ -382,28 +387,29 @@ class XPCShellTestThread(Thread):
mozinfo.output_to_file(mozInfoJSPath)
return mozInfoJSPath
def buildCmdHead(self, headfiles, xpcscmd):
"""
Build the command line arguments for the head files,
along with the address of the webserver which some tests require.
- On a remote system, this is overloaded to resolve quoting issues over a secondary command line.
+ On a remote system, this is overloaded to resolve quoting issues over a
+ secondary command line.
"""
cmdH = ", ".join(['"' + f.replace('\\', '/') + '"'
- for f in headfiles])
+ for f in headfiles])
dbgport = 0 if self.jsDebuggerInfo is None else self.jsDebuggerInfo.port
- return xpcscmd + \
- ['-e', 'const _SERVER_ADDR = "localhost"',
- '-e', 'const _HEAD_FILES = [%s];' % cmdH,
- '-e', 'const _JSDEBUGGER_PORT = %d;' % dbgport,
- ]
+ return xpcscmd + [
+ '-e', 'const _SERVER_ADDR = "localhost"',
+ '-e', 'const _HEAD_FILES = [%s];' % cmdH,
+ '-e', 'const _JSDEBUGGER_PORT = %d;' % dbgport,
+ ]
def getHeadFiles(self, test):
"""Obtain lists of head- files. Returns a list of head files.
"""
def sanitize_list(s, kind):
for f in s.strip().split(' '):
f = f.strip()
if len(f) < 1:
@@ -418,18 +424,19 @@ class XPCShellTestThread(Thread):
yield path
headlist = test.get('head', '')
return list(sanitize_list(headlist, 'head'))
def buildXpcsCmd(self):
"""
- Load the root head.js file as the first file in our test path, before other head, and test files.
- On a remote system, we overload this to add additional command line arguments, so this gets overloaded.
+ Load the root head.js file as the first file in our test path, before other head,
+ and test files. On a remote system, we overload this to add additional command
+ line arguments, so this gets overloaded.
"""
# - NOTE: if you rename/add any of the constants set here, update
# do_load_child_test_harness() in head.js
if not self.appPath:
self.appPath = self.xrePath
self.xpcsCmd = [
self.xpcshell,
@@ -464,18 +471,19 @@ class XPCShellTestThread(Thread):
self.pluginsDir = self.setupPluginsDir()
if self.pluginsDir:
self.xpcsCmd.extend(['-p', self.pluginsDir])
def cleanupDir(self, directory, name):
if not os.path.exists(directory):
return
- TRY_LIMIT = 25 # up to TRY_LIMIT attempts (one every second), because
- # the Windows filesystem is slow to react to the changes
+ # up to TRY_LIMIT attempts (one every second), because
+ # the Windows filesystem is slow to react to the changes
+ TRY_LIMIT = 25
try_count = 0
while try_count < TRY_LIMIT:
try:
self.removeDir(directory)
except OSError:
self.log.info("Failed to remove directory: %s. Waiting." % directory)
# We suspect the filesystem may still be making changes. Wait a
# little bit and try again.
@@ -521,17 +529,17 @@ class XPCShellTestThread(Thread):
line = self.fix_text_output(line).rstrip('\r\n')
self.log.process_output(self.proc_ident,
line,
command=self.complete_command)
else:
if 'message' in line:
line['message'] = self.fix_text_output(line['message'])
if 'xpcshell_process' in line:
- line['thread'] = ' '.join([current_thread().name, line['xpcshell_process']])
+ line['thread'] = ' '.join([current_thread().name, line['xpcshell_process']])
else:
line['thread'] = current_thread().name
self.log.log_raw(line)
def log_full_output(self):
"""Logs any buffered output from the test process, and clears the buffer."""
if not self.output_lines:
return
@@ -574,17 +582,17 @@ class XPCShellTestThread(Thread):
self.has_failure_output = (self.has_failure_output or
'expected' in line_object or
action == 'log' and line_object['level'] == 'ERROR')
self.report_message(line_object)
if action == 'log' and line_object['message'] == 'CHILD-TEST-STARTED':
- self.saw_proc_start = True
+ self.saw_proc_start = True
elif action == 'log' and line_object['message'] == 'CHILD-TEST-COMPLETED':
self.saw_proc_end = True
def run_test(self):
"""Run an individual xpcshell test."""
global gotSIGINT
name = self.test_object['id']
@@ -663,17 +671,17 @@ class XPCShellTestThread(Thread):
self.env['DMD_PRELOAD_VAR'] = preloadEnvVar
self.env['DMD_PRELOAD_VALUE'] = libdmd
if self.test_object.get('subprocess') == 'true':
self.env['PYTHON'] = sys.executable
if self.test_object.get('headless', False):
self.env["MOZ_HEADLESS"] = '1'
- self.env["DISPLAY"] = '77' # Set a fake display.
+ self.env["DISPLAY"] = '77' # Set a fake display.
testTimeoutInterval = self.harness_timeout
# Allow a test to request a multiple of the timeout if it is expected to take long
if 'requesttimeoutfactor' in self.test_object:
testTimeoutInterval *= int(self.test_object['requesttimeoutfactor'])
testTimer = None
if not self.interactive and not self.debuggerInfo and not self.jsDebuggerInfo:
@@ -684,17 +692,19 @@ class XPCShellTestThread(Thread):
process_output = None
try:
self.log.test_start(name)
if self.verbose:
self.logCommand(name, self.complete_command, test_dir)
proc = self.launchProcess(self.complete_command,
- stdout=self.pStdout, stderr=self.pStderr, env=self.env, cwd=test_dir, timeout=testTimeoutInterval)
+ stdout=self.pStdout, stderr=self.pStderr,
+ env=self.env, cwd=test_dir,
+ timeout=testTimeoutInterval)
if hasattr(proc, "pid"):
self.proc_ident = proc.pid
else:
# On mobile, "proc" is just a file.
self.proc_ident = name
if self.interactive:
@@ -801,16 +811,17 @@ class XPCShellTestThread(Thread):
if self.keep_going:
gotSIGINT = False
else:
self.keep_going = False
return
self.keep_going = True
+
class XPCShellTests(object):
def __init__(self, log=None):
""" Initializes node status and logger. """
self.log = log
self.harness_timeout = HARNESS_TIMEOUT
self.nodeProc = {}
@@ -831,17 +842,18 @@ class XPCShellTests(object):
else:
print >> sys.stderr, ("Failed to find manifest at %s; use --manifest "
"to set path explicitly." % (ini_path,))
sys.exit(1)
def normalizeTest(self, root, test_object):
path = test_object.get('file_relpath', test_object['relpath'])
if 'dupe-manifest' in test_object and 'ancestor-manifest' in test_object:
- test_object['id'] = '%s:%s' % (os.path.basename(test_object['ancestor-manifest']), path)
+ test_object['id'] = '%s:%s' % (os.path.basename
+ (test_object['ancestor-manifest']), path)
else:
test_object['id'] = path
if root:
test_object['manifest'] = os.path.relpath(test_object['manifest'], root)
if os.sep != '/':
for key in ('id', 'manifest'):
@@ -898,30 +910,31 @@ class XPCShellTests(object):
with open(self.dump_tests, 'w') as dumpFile:
dumpFile.write(json.dumps({'active_tests': self.alltests}))
self.log.info("Dumping active_tests to %s file." % self.dump_tests)
sys.exit()
def setAbsPath(self):
"""
- Set the absolute path for xpcshell, httpdjspath and xrepath.
- These 3 variables depend on input from the command line and we need to allow for absolute paths.
+ Set the absolute path for xpcshell, httpdjspath and xrepath. These 3 variables
+ depend on input from the command line and we need to allow for absolute paths.
This function is overloaded for a remote solution as os.path* won't work remotely.
"""
self.testharnessdir = os.path.dirname(os.path.abspath(__file__))
self.headJSPath = self.testharnessdir.replace("\\", "/") + "/head.js"
self.xpcshell = os.path.abspath(self.xpcshell)
if self.xrePath is None:
self.xrePath = os.path.dirname(self.xpcshell)
if mozinfo.isMac:
# Check if we're run from an OSX app bundle and override
# self.xrePath if we are.
- appBundlePath = os.path.join(os.path.dirname(os.path.dirname(self.xpcshell)), 'Resources')
+ appBundlePath = os.path.join(os.path.dirname(os.path.dirname(self.xpcshell)),
+ 'Resources')
if os.path.exists(os.path.join(appBundlePath, 'application.ini')):
self.xrePath = appBundlePath
else:
self.xrePath = os.path.abspath(self.xrePath)
# httpd.js belongs in xrePath/components, which is Contents/Resources on mac
self.httpdJSPath = os.path.join(self.xrePath, 'components', 'httpd.js')
self.httpdJSPath = self.httpdJSPath.replace('\\', '/')
@@ -929,17 +942,18 @@ class XPCShellTests(object):
self.httpdManifest = os.path.join(self.xrePath, 'components', 'httpd.manifest')
self.httpdManifest = self.httpdManifest.replace('\\', '/')
if self.mozInfo is None:
self.mozInfo = os.path.join(self.testharnessdir, "mozinfo.json")
def buildCoreEnvironment(self):
"""
- Add environment variables likely to be used across all platforms, including remote systems.
+ Add environment variables likely to be used across all platforms, including
+ remote systems.
"""
# Make assertions fatal
self.env["XPCOM_DEBUG_BREAK"] = "stack-and-abort"
# Crash reporting interferes with debugging
if not self.debuggerInfo:
self.env["MOZ_CRASHREPORTER"] = "1"
# Don't launch the crash reporter client
self.env["MOZ_CRASHREPORTER_NO_REPORT"] = "1"
@@ -955,50 +969,53 @@ class XPCShellTests(object):
# Disable the content process sandbox for the xpcshell tests. They
# currently attempt to do things like bind() sockets, which is not
# compatible with the sandbox.
self.env["MOZ_DISABLE_CONTENT_SANDBOX"] = "1"
def buildEnvironment(self):
"""
- Create and returns a dictionary of self.env to include all the appropriate env variables and values.
- On a remote system, we overload this to set different values and are missing things like os.environ and PATH.
+ Create and returns a dictionary of self.env to include all the appropriate env
+ variables and values. On a remote system, we overload this to set different
+ values and are missing things like os.environ and PATH.
"""
self.env = dict(os.environ)
self.buildCoreEnvironment()
if sys.platform == 'win32':
self.env["PATH"] = self.env["PATH"] + ";" + self.xrePath
elif sys.platform in ('os2emx', 'os2knix'):
os.environ["BEGINLIBPATH"] = self.xrePath + ";" + self.env["BEGINLIBPATH"]
os.environ["LIBPATHSTRICT"] = "T"
elif sys.platform == 'osx' or sys.platform == "darwin":
self.env["DYLD_LIBRARY_PATH"] = os.path.join(os.path.dirname(self.xrePath), 'MacOS')
- else: # unix or linux?
- if not "LD_LIBRARY_PATH" in self.env or self.env["LD_LIBRARY_PATH"] is None:
+ else: # unix or linux?
+ if "LD_LIBRARY_PATH" not in self.env or self.env["LD_LIBRARY_PATH"] is None:
self.env["LD_LIBRARY_PATH"] = self.xrePath
else:
self.env["LD_LIBRARY_PATH"] = ":".join([self.xrePath, self.env["LD_LIBRARY_PATH"]])
usingASan = "asan" in self.mozInfo and self.mozInfo["asan"]
usingTSan = "tsan" in self.mozInfo and self.mozInfo["tsan"]
if usingASan or usingTSan:
# symbolizer support
llvmsym = os.path.join(
self.xrePath,
"llvm-symbolizer" + self.mozInfo["bin_suffix"].encode('ascii'))
if os.path.isfile(llvmsym):
if usingASan:
self.env["ASAN_SYMBOLIZER_PATH"] = llvmsym
else:
oldTSanOptions = self.env.get("TSAN_OPTIONS", "")
- self.env["TSAN_OPTIONS"] = "external_symbolizer_path={} {}".format(llvmsym, oldTSanOptions)
+ self.env["TSAN_OPTIONS"] = ("external_symbolizer_path={} {}".format(llvmsym,
+ oldTSanOptions))
self.log.info("runxpcshelltests.py | using symbolizer at %s" % llvmsym)
else:
- self.log.error("TEST-UNEXPECTED-FAIL | runxpcshelltests.py | Failed to find symbolizer at %s" % llvmsym)
+ self.log.error("TEST-UNEXPECTED-FAIL | runxpcshelltests.py | "
+ "Failed to find symbolizer at %s" % llvmsym)
return self.env
def getPipes(self):
"""
Determine the value of the stdout and stderr for the test.
Return value is a list (pStdout, pStderr).
"""
@@ -1026,24 +1043,26 @@ class XPCShellTests(object):
def trySetupNode(self):
"""
Run node for HTTP/2 tests, if available, and updates mozinfo as appropriate.
"""
if os.getenv('MOZ_ASSUME_NODE_RUNNING', None):
self.log.info('Assuming required node servers are already running')
if not os.getenv('MOZHTTP2_PORT', None):
- self.log.warning('MOZHTTP2_PORT environment variable not set. Tests requiring http/2 will fail.')
+ self.log.warning('MOZHTTP2_PORT environment variable not set. '
+ 'Tests requiring http/2 will fail.')
return
# We try to find the node executable in the path given to us by the user in
# the MOZ_NODE_PATH environment variable
nodeBin = os.getenv('MOZ_NODE_PATH', None)
if not nodeBin:
- self.log.warning('MOZ_NODE_PATH environment variable not set. Tests requiring http/2 will fail.')
+ self.log.warning('MOZ_NODE_PATH environment variable not set. '
+ 'Tests requiring http/2 will fail.')
return
if not os.path.exists(nodeBin) or not os.path.isfile(nodeBin):
error = 'node not found at MOZ_NODE_PATH %s' % (nodeBin)
self.log.error(error)
raise IOError(error)
self.log.info('Found node at %s' % (nodeBin,))
@@ -1055,26 +1074,26 @@ class XPCShellTests(object):
raise IOError(error)
# OK, we found our server, let's try to get it running
self.log.info('Found %s at %s' % (name, serverJs))
try:
# We pipe stdin to node because the server will exit when its
# stdin reaches EOF
process = Popen([nodeBin, serverJs], stdin=PIPE, stdout=PIPE,
- stderr=PIPE, env=self.env, cwd=os.getcwd())
+ stderr=PIPE, env=self.env, cwd=os.getcwd())
self.nodeProc[name] = process
# Check to make sure the server starts properly by waiting for it to
# tell us it's started
msg = process.stdout.readline()
if 'server listening' in msg:
- searchObj = re.search( r'HTTP2 server listening on port (.*)', msg, 0)
+ searchObj = re.search(r'HTTP2 server listening on port (.*)', msg, 0)
if searchObj:
- self.env["MOZHTTP2_PORT"] = searchObj.group(1)
+ self.env["MOZHTTP2_PORT"] = searchObj.group(1)
except OSError as e:
# This occurs if the subprocess couldn't be started
self.log.error('Could not run %s server: %s' % (name, str(e)))
raise
myDir = os.path.split(os.path.abspath(__file__))[0]
startServer('moz-http2', os.path.join(myDir, 'moz-http2', 'moz-http2.js'))
@@ -1083,48 +1102,51 @@ class XPCShellTests(object):
Shut down our node process, if it exists
"""
for name, proc in self.nodeProc.iteritems():
self.log.info('Node %s server shutting down ...' % name)
if proc.poll() is not None:
self.log.info('Node server %s already dead %s' % (name, proc.poll()))
else:
proc.terminate()
+
def dumpOutput(fd, label):
firstTime = True
for msg in fd:
if firstTime:
- firstTime = False;
+ firstTime = False
self.log.info('Process %s' % label)
self.log.info(msg)
dumpOutput(proc.stdout, "stdout")
dumpOutput(proc.stderr, "stderr")
def buildXpcsRunArgs(self):
"""
Add arguments to run the test or make it interactive.
"""
if self.interactive:
self.xpcsRunArgs = [
- '-e', 'print("To start the test, type |_execute_test();|.");',
- '-i']
+ '-e', 'print("To start the test, type |_execute_test();|.");',
+ '-i'
+ ]
else:
self.xpcsRunArgs = ['-e', '_execute_test(); quit(0);']
def addTestResults(self, test):
self.passCount += test.passCount
self.failCount += test.failCount
self.todoCount += test.todoCount
def updateMozinfo(self):
# Handle filenames in mozInfo
if not isinstance(self.mozInfo, dict):
mozInfoFile = self.mozInfo
if not os.path.isfile(mozInfoFile):
- self.log.error("Error: couldn't find mozinfo.json at '%s'. Perhaps you need to use --build-info-json?" % mozInfoFile)
+ self.log.error("Error: couldn't find mozinfo.json at '%s'. Perhaps you "
+ "need to use --build-info-json?" % mozInfoFile)
return False
self.mozInfo = json.load(open(mozInfoFile))
# mozinfo.info is used as kwargs. Some builds are done with
# an older Python that can't handle Unicode keys in kwargs.
# All of the keys in question should be ASCII.
fixedInfo = {}
for k, v in self.mozInfo.items():
@@ -1162,20 +1184,20 @@ class XPCShellTests(object):
if not options.get('testingModulesDir'):
possible = os.path.join(here, os.path.pardir, 'modules')
if os.path.isdir(possible):
testingModulesDir = possible
if options.get('rerun_failures'):
if os.path.exists(options.get('failure_manifest')):
- rerun_manifest = os.path.join(os.path.dirname(options['failure_manifest']), "rerun.ini")
+ rerun_manifest = os.path.join(os.path.dirname
+ (options['failure_manifest']), "rerun.ini")
shutil.copyfile(options['failure_manifest'], rerun_manifest)
os.remove(options['failure_manifest'])
- manifest = rerun_manifest
else:
print >> sys.stderr, "No failures were found to re-run."
sys.exit(1)
if options.get('testingModulesDir'):
# The resource loader expects native paths. Depending on how we were
# invoked, a UNIX style path may sneak in on Windows. We try to
# normalize that.
@@ -1186,17 +1208,18 @@ class XPCShellTests(object):
if not testingModulesDir.endswith(os.path.sep):
testingModulesDir += os.path.sep
self.debuggerInfo = None
if options.get('debugger'):
self.debuggerInfo = mozdebug.get_debugger_info(options.get('debugger'),
- options.get('debuggerArgs'), options.get('debuggerInteractive'))
+ options.get('debuggerArgs'),
+ options.get('debuggerInteractive'))
self.jsDebuggerInfo = None
if options.get('jsDebugger'):
# A namedtuple let's us keep .port instead of ['port']
JSDebuggerInfo = namedtuple('JSDebuggerInfo', ['port'])
self.jsDebuggerInfo = JSDebuggerInfo(port=options['jsDebuggerPort'])
self.xpcshell = options.get('xpcshell')
@@ -1232,17 +1255,18 @@ class XPCShellTests(object):
self.event = Event()
if not self.updateMozinfo():
return False
self.stack_fixer_function = None
if self.utility_path and os.path.exists(self.utility_path):
- self.stack_fixer_function = get_stack_fixer_function(self.utility_path, self.symbolsPath)
+ self.stack_fixer_function = get_stack_fixer_function(self.utility_path,
+ self.symbolsPath)
# buildEnvironment() needs mozInfo, so we call it after mozInfo is initialized.
self.buildEnvironment()
# The appDirKey is a optional entry in either the default or individual test
# sections that defines a relative application directory for test runs. If
# defined we pass 'grePath/$appDirKey' for the -a parameter of the xpcshell
# test harness.
@@ -1251,17 +1275,18 @@ class XPCShellTests(object):
appDirKey = self.mozInfo["appname"] + "-appdir"
# We have to do this before we run tests that depend on having the node
# http/2 server.
self.trySetupNode()
pStdout, pStderr = self.getPipes()
- self.buildTestList(options.get('test_tags'), options.get('testPaths'), options.get('verify'))
+ self.buildTestList(options.get('test_tags'), options.get('testPaths'),
+ options.get('verify'))
if self.singleFile:
self.sequential = True
if options.get('shuffle'):
random.shuffle(self.alltests)
self.cleanup_dir_list = []
@@ -1275,17 +1300,17 @@ class XPCShellTests(object):
'pluginsPath': self.pluginsPath,
'httpdManifest': self.httpdManifest,
'httpdJSPath': self.httpdJSPath,
'headJSPath': self.headJSPath,
'tempDir': self.tempDir,
'testharnessdir': self.testharnessdir,
'profileName': self.profileName,
'singleFile': self.singleFile,
- 'env': self.env, # making a copy of this in the testthreads
+ 'env': self.env, # making a copy of this in the testthreads
'symbolsPath': self.symbolsPath,
'logfiles': self.logfiles,
'xpcshell': self.xpcshell,
'xpcsRunArgs': self.xpcsRunArgs,
'failureManifest': self.failure_manifest,
'jscovdir': self.jscovdir,
'harness_timeout': self.harness_timeout,
'stack_fixer_function': self.stack_fixer_function,
@@ -1310,17 +1335,18 @@ class XPCShellTests(object):
# If we have an interactive debugger, disable SIGINT entirely.
if self.debuggerInfo.interactive:
signal.signal(signal.SIGINT, lambda signum, frame: None)
if "lldb" in self.debuggerInfo.path:
# Ask people to start debugging using 'process launch', see bug 952211.
self.log.info("It appears that you're using LLDB to debug this test. " +
- "Please use the 'process launch' command instead of the 'run' command to start xpcshell.")
+ "Please use the 'process launch' command instead of "
+ "the 'run' command to start xpcshell.")
if self.jsDebuggerInfo:
# The js debugger magic needs more work to do the right thing
# if debugging multiple files.
if len(self.alltests) != 1:
self.log.error("Error: --jsdebugger can only be used with a single test!")
return False
@@ -1341,85 +1367,86 @@ class XPCShellTests(object):
path = test_object['path']
if self.singleFile and not path.endswith(self.singleFile):
continue
self.testCount += 1
- test = testClass(test_object,
- verbose=self.verbose or test_object.get("verbose") == "true",
- usingTSan=usingTSan,
- mobileArgs=mobileArgs, **kwargs)
+ test = testClass(
+ test_object,
+ verbose=self.verbose or test_object.get("verbose") == "true",
+ usingTSan=usingTSan, mobileArgs=mobileArgs, **kwargs)
if 'run-sequentially' in test_object or self.sequential:
sequential_tests.append(test)
else:
tests_queue.append(test)
status = self.runTestList(tests_queue, sequential_tests, testClass,
- mobileArgs, **kwargs)
+ mobileArgs, **kwargs)
else:
#
# Test verification: Run each test many times, in various configurations,
# in hopes of finding intermittent failures.
#
def step1():
# Run tests sequentially. Parallel mode would also work, except that
# the logging system gets confused when 2 or more tests with the same
# name run at the same time.
sequential_tests = []
for i in xrange(VERIFY_REPEAT):
self.testCount += 1
test = testClass(test_object, retry=False,
- mobileArgs=mobileArgs, **kwargs)
+ mobileArgs=mobileArgs, **kwargs)
sequential_tests.append(test)
status = self.runTestList(tests_queue, sequential_tests,
- testClass, mobileArgs, **kwargs)
+ testClass, mobileArgs, **kwargs)
return status
steps = [
("1. Run each test %d times, sequentially." % VERIFY_REPEAT,
step1),
]
startTime = datetime.now()
maxTime = timedelta(seconds=options['verifyMaxTime'])
for test_object in self.alltests:
stepResults = {}
for (descr, step) in steps:
stepResults[descr] = "not run / incomplete"
finalResult = "PASSED"
for (descr, step) in steps:
if (datetime.now() - startTime) > maxTime:
self.log.info("::: Test verification is taking too long: Giving up!")
- self.log.info("::: So far, all checks passed, but not all checks were run.")
+ self.log.info("::: So far, all checks passed, but not "
+ "all checks were run.")
break
self.log.info(':::')
self.log.info('::: Running test verification step "%s"...' % descr)
self.log.info(':::')
status = step()
- if status != True:
+ if status is not True:
stepResults[descr] = "FAIL"
finalResult = "FAILED!"
break
stepResults[descr] = "Pass"
self.log.info(':::')
self.log.info('::: Test verification summary for: %s' % test_object['path'])
self.log.info(':::')
for descr in sorted(stepResults.keys()):
self.log.info('::: %s : %s' % (descr, stepResults[descr]))
self.log.info(':::')
self.log.info('::: Test verification %s' % finalResult)
self.log.info(':::')
return status
def runTestList(self, tests_queue, sequential_tests, testClass,
- mobileArgs, **kwargs):
+ mobileArgs, **kwargs):
if self.sequential:
self.log.info("Running tests sequentially.")
else:
self.log.info("Using at most %d threads." % self.threadCount)
# keep a set of threadCount running tests and start running the
# tests in the queue at most threadCount at a time
@@ -1453,17 +1480,17 @@ class XPCShellTests(object):
self.event.wait(1)
self.event.clear()
# find what tests are done (might be more than 1)
done_tests = set()
for test in running_tests:
if test.done:
done_tests.add(test)
- test.join(1) # join with timeout so we don't hang on blocked threads
+ test.join(1) # join with timeout so we don't hang on blocked threads
# if the test had trouble, we will try running it again
# at the end of the run
if test.retry or test.is_alive():
# if the join call timed out, test.is_alive => True
self.try_again_list.append(test.test_object)
continue
# did the test encounter any exception?
if test.exception:
@@ -1477,18 +1504,19 @@ class XPCShellTests(object):
# make room for new tests to run
running_tests.difference_update(done_tests)
if keep_going:
# run the other tests sequentially
for test in sequential_tests:
if not keep_going:
- self.log.error("TEST-UNEXPECTED-FAIL | Received SIGINT (control-C), so stopped run. " \
- "(Use --keep-going to keep running tests after killing one with SIGINT)")
+ self.log.error("TEST-UNEXPECTED-FAIL | Received SIGINT (control-C), so "
+ "stopped run. (Use --keep-going to keep running tests "
+ "after killing one with SIGINT)")
break
# we don't want to retry these tests
test.retry = False
test.start()
test.join()
self.addTestResults(test)
# did the test encounter any exception?
if test.exception:
@@ -1497,20 +1525,20 @@ class XPCShellTests(object):
break
keep_going = test.keep_going
# retry tests that failed when run in parallel
if self.try_again_list:
self.log.info("Retrying tests that failed when run in parallel.")
for test_object in self.try_again_list:
test = testClass(test_object,
- retry=False,
- verbose=self.verbose,
- mobileArgs=mobileArgs,
- **kwargs)
+ retry=False,
+ verbose=self.verbose,
+ mobileArgs=mobileArgs,
+ **kwargs)
test.start()
test.join()
self.addTestResults(test)
# did the test encounter any exception?
if test.exception:
exceptions.append(test.exception)
tracebacks.append(test.traceback)
break
@@ -1541,19 +1569,20 @@ class XPCShellTests(object):
self.failCount = 1
self.log.info("INFO | Result summary:")
self.log.info("INFO | Passed: %d" % self.passCount)
self.log.info("INFO | Failed: %d" % self.failCount)
self.log.info("INFO | Todo: %d" % self.todoCount)
self.log.info("INFO | Retried: %d" % len(self.try_again_list))
- if gotSIGINT and not keepGoing:
- self.log.error("TEST-UNEXPECTED-FAIL | Received SIGINT (control-C), so stopped run. " \
- "(Use --keep-going to keep running tests after killing one with SIGINT)")
+ if gotSIGINT and not keep_going:
+ self.log.error("TEST-UNEXPECTED-FAIL | Received SIGINT (control-C), so stopped run. "
+ "(Use --keep-going to keep running tests after "
+ "killing one with SIGINT)")
return False
self.log.suite_end()
return self.failCount == 0
def main():
parser = parser_desktop()
@@ -1568,10 +1597,11 @@ def main():
if options.interactive and not options.testPath:
print >>sys.stderr, "Error: You must specify a test filename in interactive mode!"
sys.exit(1)
if not xpcsh.runTests(options):
sys.exit(1)
+
if __name__ == '__main__':
main()
--- a/testing/xpcshell/selftest.py
+++ b/testing/xpcshell/selftest.py
@@ -25,21 +25,22 @@ build_obj = MozbuildObject.from_environm
from runxpcshelltests import XPCShellTests
mozinfo.find_and_update_from_json()
objdir = build_obj.topobjdir.encode("utf-8")
if mozinfo.isMac:
- xpcshellBin = os.path.join(objdir, "dist", substs['MOZ_MACBUNDLE_NAME'], "Contents", "MacOS", "xpcshell")
+ xpcshellBin = os.path.join(objdir, "dist", substs['MOZ_MACBUNDLE_NAME'],
+ "Contents", "MacOS", "xpcshell")
else:
- xpcshellBin = os.path.join(objdir, "dist", "bin", "xpcshell")
- if sys.platform == "win32":
- xpcshellBin += ".exe"
+ xpcshellBin = os.path.join(objdir, "dist", "bin", "xpcshell")
+ if sys.platform == "win32":
+ xpcshellBin += ".exe"
TEST_PASS_STRING = "TEST-PASS"
TEST_FAIL_STRING = "TEST-UNEXPECTED-FAIL"
SIMPLE_PASSING_TEST = "function run_test() { do_check_true(true); }"
SIMPLE_FAILING_TEST = "function run_test() { do_check_true(false); }"
SIMPLE_UNCAUGHT_REJECTION_TEST = '''
@@ -443,33 +444,35 @@ function run_test() {
CHILD_MOZINFO = '''
function run_test () { run_next_test(); }
add_test(function test_child_mozinfo () {
run_test_in_child("test_mozinfo.js");
run_next_test();
});
'''
+
+
class XPCShellTestsTests(unittest.TestCase):
"""
Yes, these are unit tests for a unit test harness.
"""
def setUp(self):
self.log = StringIO()
self.tempdir = tempfile.mkdtemp()
self.utility_path = os.path.join(objdir, 'dist', 'bin')
logger = structured.commandline.setup_logging("selftest%s" % id(self),
{},
{"tbpl": self.log})
self.x = XPCShellTests(logger)
self.x.harness_timeout = 15
self.symbols_path = None
candidate_path = os.path.join(build_obj.distdir, 'crashreporter-symbols')
if (os.path.isdir(candidate_path)):
- self.symbols_path = candidate_path
+ self.symbols_path = candidate_path
def tearDown(self):
shutil.rmtree(self.tempdir)
def writeFile(self, name, contents):
"""
Write |contents| to a file named |name| in the temp directory,
and return the full path to the file.
@@ -591,19 +594,21 @@ tail =
self.writeManifest(["test_assert.js"])
self.assertTestResult(False)
self.assertInLog("###!!! ASSERTION")
log_lines = self.log.getvalue().splitlines()
line_pat = "#\d\d:"
unknown_pat = "#\d\d\: \?\?\?\[.* \+0x[a-f0-9]+\]"
self.assertFalse(any(re.search(unknown_pat, line) for line in log_lines),
- "An stack frame without symbols was found in\n%s" % pprint.pformat(log_lines))
+ "An stack frame without symbols was found in\n%s" %
+ pprint.pformat(log_lines))
self.assertTrue(any(re.search(line_pat, line) for line in log_lines),
- "No line resembling a stack frame was found in\n%s" % pprint.pformat(log_lines))
+ "No line resembling a stack frame was found in\n%s" %
+ pprint.pformat(log_lines))
def testChildPass(self):
"""
Check that a simple test running in a child process passes.
"""
self.writeFile("test_pass.js", SIMPLE_PASSING_TEST)
self.writeFile("test_child_pass.js", CHILD_TEST_PASSING)
self.writeManifest(["test_child_pass.js"])
@@ -889,18 +894,18 @@ add_test({
Ensure a simple test with an uncaught rejection is reported.
"""
self.writeFile("test_simple_uncaught_rejection.js", SIMPLE_UNCAUGHT_REJECTION_TEST)
self.writeManifest(["test_simple_uncaught_rejection.js"])
self.assertTestResult(False)
self.assertInLog(TEST_FAIL_STRING)
if not substs.get('RELEASE_OR_BETA'):
- # async stacks are currently not enabled in release builds.
- self.assertInLog("test_simple_uncaught_rejection.js:3:3")
+ # async stacks are currently not enabled in release builds.
+ self.assertInLog("test_simple_uncaught_rejection.js:3:3")
self.assertInLog("Test rejection.")
self.assertEquals(1, self.x.testCount)
self.assertEquals(0, self.x.passCount)
self.assertEquals(1, self.x.failCount)
def testUncaughtRejectionJSM(self):
"""
Ensure a simple test with an uncaught rejection from Promise.jsm is reported.
@@ -989,70 +994,70 @@ add_test({
self.assertEquals(1, self.x.passCount)
self.assertEquals(0, self.x.failCount)
def testAddTaskTestMultiple(self):
"""
Ensure multiple calls to add_test_task() work as expected.
"""
self.writeFile("test_add_task_multiple.js",
- ADD_TASK_MULTIPLE)
+ ADD_TASK_MULTIPLE)
self.writeManifest(["test_add_task_multiple.js"])
self.assertTestResult(True)
self.assertEquals(1, self.x.testCount)
self.assertEquals(1, self.x.passCount)
self.assertEquals(0, self.x.failCount)
def testAddTaskTestRejected(self):
"""
Ensure rejected task reports as failure.
"""
self.writeFile("test_add_task_rejected.js",
- ADD_TASK_REJECTED)
+ ADD_TASK_REJECTED)
self.writeManifest(["test_add_task_rejected.js"])
self.assertTestResult(False)
self.assertEquals(1, self.x.testCount)
self.assertEquals(0, self.x.passCount)
self.assertEquals(1, self.x.failCount)
def testAddTaskTestFailureInside(self):
"""
Ensure tests inside task are reported as failures.
"""
self.writeFile("test_add_task_failure_inside.js",
- ADD_TASK_FAILURE_INSIDE)
+ ADD_TASK_FAILURE_INSIDE)
self.writeManifest(["test_add_task_failure_inside.js"])
self.assertTestResult(False)
self.assertEquals(1, self.x.testCount)
self.assertEquals(0, self.x.passCount)
self.assertEquals(1, self.x.failCount)
def testAddTaskRunNextTest(self):
"""
Calling run_next_test() from inside add_task() results in failure.
"""
self.writeFile("test_add_task_run_next_test.js",
- ADD_TASK_RUN_NEXT_TEST)
+ ADD_TASK_RUN_NEXT_TEST)
self.writeManifest(["test_add_task_run_next_test.js"])
self.assertTestResult(False)
self.assertEquals(1, self.x.testCount)
self.assertEquals(0, self.x.passCount)
self.assertEquals(1, self.x.failCount)
def testAddTaskStackTrace(self):
"""
Ensuring that calling Assert.ok(false) from inside add_task()
results in a human-readable stack trace.
"""
self.writeFile("test_add_task_stack_trace.js",
- ADD_TASK_STACK_TRACE)
+ ADD_TASK_STACK_TRACE)
self.writeManifest(["test_add_task_stack_trace.js"])
self.assertTestResult(False)
self.assertInLog("this_test_will_fail")
self.assertInLog("run_next_test")
self.assertInLog("run_test")
self.assertNotInLog("Task.jsm")
@@ -1362,10 +1367,11 @@ add_test({
self.assertTestResult(True)
self.assertEquals(1, self.x.testCount)
self.assertEquals(1, self.x.passCount)
self.assertEquals(0, self.x.failCount)
self.assertEquals(0, self.x.todoCount)
self.assertInLog(TEST_PASS_STRING)
self.assertNotInLog(TEST_FAIL_STRING)
+
if __name__ == "__main__":
mozunit.main()
--- a/testing/xpcshell/xpcshellcommandline.py
+++ b/testing/xpcshell/xpcshellcommandline.py
@@ -35,43 +35,48 @@ def add_common_arguments(parser):
parser.add_argument("--temp-dir",
dest="tempDir", default=None,
help="Directory to use for temporary files")
parser.add_argument("--testing-modules-dir",
dest="testingModulesDir", default=None,
help="Directory where testing modules are located.")
parser.add_argument("--test-plugin-path",
type=str, dest="pluginsPath", default=None,
- help="Path to the location of a plugins directory containing the test plugin or plugins required for tests. "
- "By default xpcshell's dir svc provider returns gre/plugins. Use test-plugin-path to add a directory "
+ help="Path to the location of a plugins directory containing the "
+ "test plugin or plugins required for tests. "
+ "By default xpcshell's dir svc provider returns gre/plugins. "
+ "Use test-plugin-path to add a directory "
"to return for NS_APP_PLUGINS_DIR_LIST when queried.")
parser.add_argument("--total-chunks",
type=int, dest="totalChunks", default=1,
help="how many chunks to split the tests up into")
parser.add_argument("--this-chunk",
type=int, dest="thisChunk", default=1,
help="which chunk to run between 1 and --total-chunks")
parser.add_argument("--profile-name",
type=str, dest="profileName", default=None,
help="name of application profile being tested")
parser.add_argument("--build-info-json",
type=str, dest="mozInfo", default=None,
- help="path to a mozinfo.json including information about the build configuration. defaults to looking for mozinfo.json next to the script.")
+ help="path to a mozinfo.json including information about the build "
+ "configuration. defaults to looking for mozinfo.json next to "
+ "the script.")
parser.add_argument("--shuffle",
action="store_true", dest="shuffle", default=False,
help="Execute tests in random order")
parser.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)")
parser.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")
+ help="absolute path to directory containing breakpad symbols, "
+ "or the URL of a zip file containing symbols")
parser.add_argument("--jscov-dir-prefix",
action="store", type=str, dest="jscovdir",
default=argparse.SUPPRESS,
help="Directory to store per-test javascript line coverage data as json.")
parser.add_argument("--debugger",
action="store", dest="debugger",
help="use the given debugger to launch the application")
parser.add_argument("--debugger-args",
@@ -111,55 +116,56 @@ def add_common_arguments(parser):
action="store_true",
help="Rerun failures from the previous run, if any")
parser.add_argument("--failure-manifest",
action="store",
help="Path to a manifest file from which to rerun failures "
"(with --rerun-failure) or in which to record failed tests")
parser.add_argument("--threads",
type=int, dest="threadCount", default=0,
- help="override the number of jobs (threads) when running tests in parallel, "
- "the default is CPU x 1.5 when running via mach and CPU x 4 when running "
- "in automation")
+ help="override the number of jobs (threads) when running tests "
+ "in parallel, the default is CPU x 1.5 when running via mach "
+ "and CPU x 4 when running in automation")
parser.add_argument("testPaths", nargs="*", default=None,
help="Paths of tests to run.")
parser.add_argument("--verify",
action="store_true", default=False,
help="Verify test(s) by running multiple times.")
parser.add_argument("--verify-max-time",
dest="verifyMaxTime",
type=int, default=3600,
help="Maximum time, in seconds, to run in --verify mode.")
+
def add_remote_arguments(parser):
parser.add_argument("--deviceIP", action="store", type=str, dest="deviceIP",
help="ip address of remote device to test")
parser.add_argument("--devicePort", action="store", type=str, dest="devicePort",
default=20701, help="port of remote device to test")
parser.add_argument("--objdir", action="store", type=str, dest="objdir",
help="local objdir, containing xpcshell binaries")
-
parser.add_argument("--apk", action="store", type=str, dest="localAPK",
help="local path to Fennec APK")
-
parser.add_argument("--noSetup", action="store_false", dest="setup", default=True,
- help="do not copy any files to device (to be used only if device is already setup)")
+ help="do not copy any files to device (to be used only if "
+ "device is already setup)")
parser.add_argument("--local-lib-dir", action="store", type=str, dest="localLib",
help="local path to library directory")
parser.add_argument("--local-bin-dir", action="store", type=str, dest="localBin",
help="local path to bin directory")
parser.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)")
+ help="remote directory to use as test root "
+ "(eg. /mnt/sdcard/tests or /data/local/tests)")
def parser_desktop():
parser = argparse.ArgumentParser()
add_common_arguments(parser)
commandline.add_logging_group(parser)
return parser
--- a/tools/lint/flake8.yml
+++ b/tools/lint/flake8.yml
@@ -11,16 +11,17 @@ flake8:
- testing/firefox-ui
- testing/mach_commands.py
- testing/marionette/client
- testing/marionette/harness
- testing/marionette/puppeteer
- testing/mozbase
- testing/mochitest
- testing/talos/
+ - testing/xpcshell
- tools/git
- tools/lint
- tools/mercurial
- tools/tryselect
- toolkit/components/telemetry
# Excludes should be added to topsrcdir/.flake8 due to a bug in flake8 where
# specifying --exclude causes custom configuration files to be ignored.
exclude: []