Bug 530475 - convert all python test harnesses to classes for easier reuse, patch=jmaher, r=ted

This commit is contained in:
Joel Maher 2010-01-06 12:01:18 -06:00
parent b20bff31e2
commit 3d0c15f608
7 changed files with 1095 additions and 1059 deletions

File diff suppressed because it is too large Load Diff

View File

@ -46,18 +46,19 @@ import os
import sys
import logging
from getopt import getopt
import automation
from automation import Automation
PORT = 8888
SCRIPT_DIR = os.path.abspath(os.path.realpath(os.path.dirname(sys.argv[0])))
PROFILE_DIRECTORY = os.path.abspath(os.path.join(SCRIPT_DIR, "./leakprofile"))
DIST_BIN = os.path.join(SCRIPT_DIR, automation.DIST_BIN)
os.chdir(SCRIPT_DIR)
class EasyServer(SocketServer.TCPServer):
allow_reuse_address = True
if __name__ == '__main__':
automation = Automation()
DIST_BIN = os.path.join(SCRIPT_DIR, automation.DIST_BIN)
opts, extraArgs = getopt(sys.argv[1:], 'l:')
if len(opts) > 0:
try:

View File

@ -36,7 +36,7 @@
#
# ***** END LICENSE BLOCK *****
import automation
from automation import Automation
import os
import re
import shutil
@ -47,6 +47,8 @@ import sys
#expand PROFILE_DIR = __PROFILE_DIR__
#expand CERTS_SRC_DIR = __CERTS_SRC_DIR__
automation = Automation()
dbFiles = [
re.compile("^cert[0-9]+\.db$"),
re.compile("^key[0-9]+\.db$"),

View File

@ -46,7 +46,7 @@ import os
import sys
import shutil
from datetime import datetime
import automation
from automation import Automation
PORT = 8888
SCRIPT_DIR = os.path.abspath(os.path.realpath(os.path.dirname(sys.argv[0])))
@ -57,6 +57,7 @@ class EasyServer(SocketServer.TCPServer):
allow_reuse_address = True
if __name__ == '__main__':
automation = Automation()
httpd = EasyServer(("", PORT), SimpleHTTPServer.SimpleHTTPRequestHandler)
t = threading.Thread(target=httpd.serve_forever)
t.setDaemon(True) # don't hang on exit

View File

@ -44,54 +44,128 @@ Runs the reftest test harness.
import sys, shutil, os, os.path
SCRIPT_DIRECTORY = os.path.abspath(os.path.realpath(os.path.dirname(sys.argv[0])))
sys.path.append(SCRIPT_DIRECTORY)
import automation
from automation import Automation
from automationutils import *
from optparse import OptionParser
from tempfile import mkdtemp
oldcwd = os.getcwd()
os.chdir(SCRIPT_DIRECTORY)
class RefTest(object):
def getFullPath(path):
"Get an absolute path relative to oldcwd."
return os.path.normpath(os.path.join(oldcwd, os.path.expanduser(path)))
oldcwd = os.getcwd()
def createReftestProfile(options, profileDir):
"Sets up a profile for reftest."
def __init__(self, automation):
self.automation = automation
os.chdir(SCRIPT_DIRECTORY)
# Set preferences.
prefsFile = open(os.path.join(profileDir, "user.js"), "w")
prefsFile.write("""user_pref("browser.dom.window.dump.enabled", true);
""")
prefsFile.write('user_pref("reftest.timeout", %d);\n' % (options.timeout * 1000))
prefsFile.write('user_pref("ui.caretBlinkTime", -1);\n')
def getFullPath(self, path):
"Get an absolute path relative to self.oldcwd."
return os.path.normpath(os.path.join(self.oldcwd, os.path.expanduser(path)))
for v in options.extraPrefs:
thispref = v.split("=")
if len(thispref) < 2:
print "Error: syntax error in --setpref=" + v
sys.exit(1)
part = 'user_pref("%s", %s);\n' % (thispref[0], thispref[1])
prefsFile.write(part)
# no slow script dialogs
prefsFile.write('user_pref("dom.max_script_run_time", 0);')
prefsFile.write('user_pref("dom.max_chrome_script_run_time", 0);')
prefsFile.close()
def createReftestProfile(self, options, profileDir):
"Sets up a profile for reftest."
# Set preferences.
prefsFile = open(os.path.join(profileDir, "user.js"), "w")
prefsFile.write("""user_pref("browser.dom.window.dump.enabled", true);
""")
prefsFile.write('user_pref("reftest.timeout", %d);\n' % (options.timeout * 1000))
prefsFile.write('user_pref("ui.caretBlinkTime", -1);\n')
for v in options.extraPrefs:
thispref = v.split("=")
if len(thispref) < 2:
print "Error: syntax error in --setpref=" + v
sys.exit(1)
part = 'user_pref("%s", %s);\n' % (thispref[0], thispref[1])
prefsFile.write(part)
# no slow script dialogs
prefsFile.write('user_pref("dom.max_script_run_time", 0);')
prefsFile.write('user_pref("dom.max_chrome_script_run_time", 0);')
prefsFile.close()
# install the reftest extension bits into the profile
profileExtensionsPath = os.path.join(profileDir, "extensions")
os.mkdir(profileExtensionsPath)
reftestExtensionPath = os.path.join(SCRIPT_DIRECTORY, "reftest")
extFile = open(os.path.join(profileExtensionsPath, "reftest@mozilla.org"), "w")
extFile.write(reftestExtensionPath)
extFile.close()
def runTests(self, manifest, options):
debuggerInfo = getDebuggerInfo(self.oldcwd, options.debugger, options.debuggerArgs,
options.debuggerInteractive);
profileDir = None
try:
profileDir = mkdtemp()
self.createReftestProfile(options, profileDir)
self.copyExtraFilesToProfile(options, profileDir)
# browser environment
browserEnv = self.automation.environment(xrePath = options.xrePath)
browserEnv["XPCOM_DEBUG_BREAK"] = "stack"
# Enable leaks detection to its own log file.
leakLogFile = os.path.join(profileDir, "runreftest_leaks.log")
browserEnv["XPCOM_MEM_BLOAT_LOG"] = leakLogFile
# run once with -silent to let the extension manager do its thing
# and then exit the app
self.automation.log.info("REFTEST INFO | runreftest.py | Performing extension manager registration: start.\n")
# Don't care about this |status|: |runApp()| reporting it should be enough.
status = self.automation.runApp(None, browserEnv, options.app, profileDir,
["-silent"],
utilityPath = options.utilityPath,
xrePath=options.xrePath,
symbolsPath=options.symbolsPath)
# We don't care to call |processLeakLog()| for this step.
self.automation.log.info("\nREFTEST INFO | runreftest.py | Performing extension manager registration: end.")
# Remove the leak detection file so it can't "leak" to the tests run.
# The file is not there if leak logging was not enabled in the application build.
if os.path.exists(leakLogFile):
os.remove(leakLogFile)
# then again to actually run reftest
self.automation.log.info("REFTEST INFO | runreftest.py | Running tests: start.\n")
reftestlist = self.getFullPath(manifest)
status = self.automation.runApp(None, browserEnv, options.app, profileDir,
["-reftest", reftestlist],
utilityPath = options.utilityPath,
xrePath=options.xrePath,
debuggerInfo=debuggerInfo,
symbolsPath=options.symbolsPath,
# give the JS harness 30 seconds to deal
# with its own timeouts
timeout=options.timeout + 30.0)
processLeakLog(leakLogFile, options.leakThreshold)
self.automation.log.info("\nREFTEST INFO | runreftest.py | Running tests: end.")
finally:
if profileDir:
shutil.rmtree(profileDir)
return status
def copyExtraFilesToProfile(self, options, profileDir):
"Copy extra files or dirs specified on the command line to the testing profile."
for f in options.extraProfileFiles:
abspath = self.getFullPath(f)
dest = os.path.join(profileDir, os.path.basename(abspath))
if os.path.isdir(abspath):
shutil.copytree(abspath, dest)
else:
shutil.copy(abspath, dest)
# install the reftest extension bits into the profile
profileExtensionsPath = os.path.join(profileDir, "extensions")
os.mkdir(profileExtensionsPath)
reftestExtensionPath = os.path.join(SCRIPT_DIRECTORY, "reftest")
extFile = open(os.path.join(profileExtensionsPath, "reftest@mozilla.org"), "w")
extFile.write(reftestExtensionPath)
extFile.close()
def main():
automation = Automation()
parser = OptionParser()
reftest = RefTest(automation)
# we want to pass down everything from automation.__all__
addCommonOptions(parser, defaults=dict(zip(automation.__all__, [getattr(automation, x) for x in automation.__all__])))
automation.addExtraCommonOptions(parser)
addCommonOptions(parser,
defaults=dict(zip(automation.__all__,
[getattr(automation, x) for x in automation.__all__])))
automation.addCommonOptions(parser)
parser.add_option("--appname",
action = "store", type = "string", dest = "app",
default = os.path.join(SCRIPT_DIRECTORY, automation.DEFAULT_APP),
@ -118,90 +192,28 @@ def main():
"programs (xpcshell, ssltunnel, certutil)")
options, args = parser.parse_args()
if len(args) != 1:
print >>sys.stderr, "No reftest.list specified."
sys.exit(1)
options.app = getFullPath(options.app)
options.app = reftest.getFullPath(options.app)
if not os.path.exists(options.app):
print """Error: Path %(app)s doesn't exist.
Are you executing $objdir/_tests/reftest/runreftest.py?""" \
% {"app": options.app}
% {"app": options.app}
sys.exit(1)
if options.xrePath is None:
options.xrePath = os.path.dirname(options.app)
else:
# allow relative paths
options.xrePath = getFullPath(options.xrePath)
options.xrePath = reftest.getFullPath(options.xrePath)
if options.symbolsPath:
options.symbolsPath = getFullPath(options.symbolsPath)
options.utilityPath = getFullPath(options.utilityPath)
debuggerInfo = getDebuggerInfo(oldcwd, options.debugger, options.debuggerArgs,
options.debuggerInteractive);
profileDir = None
try:
profileDir = mkdtemp()
createReftestProfile(options, profileDir)
copyExtraFilesToProfile(options, profileDir)
# browser environment
browserEnv = automation.environment(xrePath = options.xrePath)
browserEnv["XPCOM_DEBUG_BREAK"] = "stack"
# Enable leaks detection to its own log file.
leakLogFile = os.path.join(profileDir, "runreftest_leaks.log")
browserEnv["XPCOM_MEM_BLOAT_LOG"] = leakLogFile
# run once with -silent to let the extension manager do its thing
# and then exit the app
automation.log.info("REFTEST INFO | runreftest.py | Performing extension manager registration: start.\n")
# Don't care about this |status|: |runApp()| reporting it should be enough.
status = automation.runApp(None, browserEnv, options.app, profileDir,
["-silent"],
utilityPath = options.utilityPath,
xrePath=options.xrePath,
symbolsPath=options.symbolsPath)
# We don't care to call |processLeakLog()| for this step.
automation.log.info("\nREFTEST INFO | runreftest.py | Performing extension manager registration: end.")
# Remove the leak detection file so it can't "leak" to the tests run.
# The file is not there if leak logging was not enabled in the application build.
if os.path.exists(leakLogFile):
os.remove(leakLogFile)
# then again to actually run reftest
automation.log.info("REFTEST INFO | runreftest.py | Running tests: start.\n")
reftestlist = getFullPath(args[0])
status = automation.runApp(None, browserEnv, options.app, profileDir,
["-reftest", reftestlist],
utilityPath = options.utilityPath,
xrePath=options.xrePath,
debuggerInfo=debuggerInfo,
symbolsPath=options.symbolsPath,
# give the JS harness 30 seconds to deal
# with its own timeouts
timeout=options.timeout + 30.0)
processLeakLog(leakLogFile, options.leakThreshold)
automation.log.info("\nREFTEST INFO | runreftest.py | Running tests: end.")
finally:
if profileDir:
shutil.rmtree(profileDir)
sys.exit(status)
def copyExtraFilesToProfile(options, profileDir):
"Copy extra files or dirs specified on the command line to the testing profile."
for f in options.extraProfileFiles:
abspath = getFullPath(f)
dest = os.path.join(profileDir, os.path.basename(abspath))
if os.path.isdir(abspath):
shutil.copytree(abspath, dest)
else:
shutil.copy(abspath, dest)
options.symbolsPath = reftest.getFullPath(options.symbolsPath)
options.utilityPath = reftest.getFullPath(options.utilityPath)
sys.exit(reftest.runTests(args[0], options))
if __name__ == "__main__":
main()

View File

@ -52,40 +52,9 @@ import shutil
from urllib import quote_plus as encodeURIComponent
import urllib2
import commands
import automation
from automation import Automation
from automationutils import *
# Path to the test script on the server
TEST_SERVER_HOST = "localhost:8888"
TEST_PATH = "/tests/"
CHROME_PATH = "/redirect.html";
A11Y_PATH = "/redirect-a11y.html"
TESTS_URL = "http://" + TEST_SERVER_HOST + TEST_PATH
CHROMETESTS_URL = "http://" + TEST_SERVER_HOST + CHROME_PATH
A11YTESTS_URL = "http://" + TEST_SERVER_HOST + A11Y_PATH
SERVER_SHUTDOWN_URL = "http://" + TEST_SERVER_HOST + "/server/shutdown"
# main browser chrome URL, same as browser.chromeURL pref
#ifdef MOZ_SUITE
BROWSER_CHROME_URL = "chrome://navigator/content/navigator.xul"
#else
BROWSER_CHROME_URL = "chrome://browser/content/browser.xul"
#endif
# Max time in seconds to wait for server startup before tests will fail -- if
# this seems big, it's mostly for debug machines where cold startup
# (particularly after a build) takes forever.
if automation.IS_DEBUG_BUILD:
SERVER_STARTUP_TIMEOUT = 180
else:
SERVER_STARTUP_TIMEOUT = 90
oldcwd = os.getcwd()
SCRIPT_DIRECTORY = os.path.abspath(os.path.realpath(os.path.dirname(sys.argv[0])))
os.chdir(SCRIPT_DIRECTORY)
PROFILE_DIRECTORY = os.path.abspath("./mochitesttestingprofile")
LEAK_REPORT_FILE = os.path.join(PROFILE_DIRECTORY, "runtests_leaks.log")
#######################
# COMMANDLINE OPTIONS #
@ -93,13 +62,15 @@ LEAK_REPORT_FILE = os.path.join(PROFILE_DIRECTORY, "runtests_leaks.log")
class MochitestOptions(optparse.OptionParser):
"""Parses Mochitest commandline options."""
def __init__(self, **kwargs):
def __init__(self, automation, scriptdir, **kwargs):
self._automation = automation
optparse.OptionParser.__init__(self, **kwargs)
defaults = {}
# we want to pass down everything from automation.__all__
addCommonOptions(self, defaults=dict(zip(automation.__all__, [getattr(automation, x) for x in automation.__all__])))
automation.addExtraCommonOptions(self)
# we want to pass down everything from self._automation.__all__
addCommonOptions(self, defaults=dict(zip(self._automation.__all__,
[getattr(self._automation, x) for x in self._automation.__all__])))
self._automation.addCommonOptions(self)
self.add_option("--close-when-done",
action = "store_true", dest = "closeWhenDone",
@ -109,17 +80,17 @@ class MochitestOptions(optparse.OptionParser):
self.add_option("--appname",
action = "store", type = "string", dest = "app",
help = "absolute path to application, overriding default")
defaults["app"] = os.path.join(SCRIPT_DIRECTORY, automation.DEFAULT_APP)
defaults["app"] = os.path.join(scriptdir, self._automation.DEFAULT_APP)
self.add_option("--utility-path",
action = "store", type = "string", dest = "utilityPath",
help = "absolute path to directory containing utility programs (xpcshell, ssltunnel, certutil)")
defaults["utilityPath"] = automation.DIST_BIN
defaults["utilityPath"] = self._automation.DIST_BIN
self.add_option("--certificate-path",
action = "store", type = "string", dest = "certPath",
help = "absolute path to directory containing certificate store to use testing profile")
defaults["certPath"] = automation.CERTS_SRC_DIR
defaults["certPath"] = self._automation.CERTS_SRC_DIR
self.add_option("--log-file",
action = "store", type = "string",
@ -249,17 +220,19 @@ See <http://mochikit.com/doc/html/MochiKit/Logging.html> for details on the logg
class MochitestServer:
"Web server used to serve Mochitests, for closer fidelity to the real web."
def __init__(self, options):
def __init__(self, automation, options, profileDir):
self._automation = automation
self._closeWhenDone = options.closeWhenDone
self._utilityPath = options.utilityPath
self._xrePath = options.xrePath
self._profileDir = profileDir
def start(self):
"Run the Mochitest server, returning the process ID of the server."
env = automation.environment(xrePath = self._xrePath)
env = self._automation.environment(xrePath = self._xrePath)
env["XPCOM_DEBUG_BREAK"] = "warn"
if automation.IS_WIN32:
if self._automation.IS_WIN32:
env["PATH"] = env["PATH"] + ";" + self._xrePath
args = ["-g", self._xrePath,
@ -268,19 +241,18 @@ class MochitestServer:
"-f", "./" + "server.js"]
xpcshell = os.path.join(self._utilityPath,
"xpcshell" + automation.BIN_SUFFIX)
self._process = automation.Process([xpcshell] + args, env = env)
"xpcshell" + self._automation.BIN_SUFFIX)
self._process = self._automation.Process([xpcshell] + args, env = env)
pid = self._process.pid
if pid < 0:
print "Error starting server."
sys.exit(2)
automation.log.info("INFO | runtests.py | Server pid: %d", pid)
self._automation.log.info("INFO | runtests.py | Server pid: %d", pid)
def ensureReady(self, timeout):
assert timeout >= 0
aliveFile = os.path.join(PROFILE_DIRECTORY, "server_alive.txt")
aliveFile = os.path.join(self._profileDir, "server_alive.txt")
i = 0
while i < timeout:
if os.path.exists(aliveFile):
@ -301,16 +273,261 @@ class MochitestServer:
except:
self._process.kill()
def getFullPath(path):
"Get an absolute path relative to oldcwd."
return os.path.normpath(os.path.join(oldcwd, os.path.expanduser(path)))
#################
# MAIN FUNCTION #
#################
class Mochitest(object):
# Path to the test script on the server
TEST_SERVER_HOST = "localhost:8888"
TEST_PATH = "/tests/"
CHROME_PATH = "/redirect.html";
A11Y_PATH = "/redirect-a11y.html"
TESTS_URL = "http://" + TEST_SERVER_HOST + TEST_PATH
CHROMETESTS_URL = "http://" + TEST_SERVER_HOST + CHROME_PATH
A11YTESTS_URL = "http://" + TEST_SERVER_HOST + A11Y_PATH
SERVER_SHUTDOWN_URL = "http://" + TEST_SERVER_HOST + "/server/shutdown"
oldcwd = os.getcwd()
def __init__(self, automation):
self.automation = automation
# Max time in seconds to wait for server startup before tests will fail -- if
# this seems big, it's mostly for debug machines where cold startup
# (particularly after a build) takes forever.
if self.automation.IS_DEBUG_BUILD:
self.SERVER_STARTUP_TIMEOUT = 180
else:
self.SERVER_STARTUP_TIMEOUT = 90
self.SCRIPT_DIRECTORY = os.path.abspath(os.path.realpath(os.path.dirname(__file__)))
os.chdir(self.SCRIPT_DIRECTORY)
self.PROFILE_DIRECTORY = os.path.abspath("./mochitesttestingprofile")
self.LEAK_REPORT_FILE = os.path.join(self.PROFILE_DIRECTORY, "runtests_leaks.log")
def getFullPath(self, path):
"Get an absolute path relative to self.oldcwd."
return os.path.normpath(os.path.join(self.oldcwd, os.path.expanduser(path)))
def runTests(self, options):
debuggerInfo = getDebuggerInfo(self.oldcwd, options.debugger, options.debuggerArgs,
options.debuggerInteractive);
# browser environment
browserEnv = self.automation.environment(xrePath = options.xrePath)
# These variables are necessary for correct application startup; change
# via the commandline at your own risk.
browserEnv["XPCOM_DEBUG_BREAK"] = "stack"
for v in options.environment:
ix = v.find("=")
if ix <= 0:
print "Error: syntax error in --setenv=" + v
return 1
browserEnv[v[:ix]] = v[ix + 1:]
self.automation.initializeProfile(self.PROFILE_DIRECTORY, options.extraPrefs)
manifest = self.addChromeToProfile(options)
self.copyExtraFilesToProfile(options)
server = MochitestServer(self.automation, options, self.PROFILE_DIRECTORY)
server.start()
# If we're lucky, the server has fully started by now, and all paths are
# ready, etc. However, xpcshell cold start times suck, at least for debug
# builds. We'll try to connect to the server for awhile, and if we fail,
# we'll try to kill the server and exit with an error.
server.ensureReady(self.SERVER_STARTUP_TIMEOUT)
# URL parameters to test URL:
#
# autorun -- kick off tests automatically
# closeWhenDone -- runs quit.js after tests
# logFile -- logs test run to an absolute path
# totalChunks -- how many chunks to split tests into
# thisChunk -- which chunk to run
# timeout -- per-test timeout in seconds
#
# consoleLevel, fileLevel: set the logging level of the console and
# file logs, if activated.
# <http://mochikit.com/doc/html/MochiKit/Logging.html>
testURL = self.TESTS_URL + options.testPath
urlOpts = []
if options.chrome:
testURL = self.CHROMETESTS_URL
if options.testPath:
urlOpts.append("testPath=" + encodeURIComponent(options.testPath))
elif options.a11y:
testURL = self.A11YTESTS_URL
if options.testPath:
urlOpts.append("testPath=" + encodeURIComponent(options.testPath))
elif options.browserChrome:
testURL = "about:blank"
# allow relative paths for logFile
if options.logFile:
options.logFile = self.getFullPath(options.logFile)
if options.browserChrome:
self.makeTestConfig(options)
else:
if options.autorun:
urlOpts.append("autorun=1")
if options.timeout:
urlOpts.append("timeout=%d" % options.timeout)
if options.closeWhenDone:
urlOpts.append("closeWhenDone=1")
if options.logFile:
urlOpts.append("logFile=" + encodeURIComponent(options.logFile))
urlOpts.append("fileLevel=" + encodeURIComponent(options.fileLevel))
if options.consoleLevel:
urlOpts.append("consoleLevel=" + encodeURIComponent(options.consoleLevel))
if options.totalChunks:
urlOpts.append("totalChunks=%d" % options.totalChunks)
urlOpts.append("thisChunk=%d" % options.thisChunk)
if options.chunkByDir:
urlOpts.append("chunkByDir=%d" % options.chunkByDir)
if options.shuffle:
urlOpts.append("shuffle=1")
if len(urlOpts) > 0:
testURL += "?" + "&".join(urlOpts)
browserEnv["XPCOM_MEM_BLOAT_LOG"] = self.LEAK_REPORT_FILE
if options.fatalAssertions:
browserEnv["XPCOM_DEBUG_BREAK"] = "stack-and-abort"
# run once with -silent to let the extension manager do its thing
# and then exit the app
self.automation.log.info("INFO | runtests.py | Performing extension manager registration: start.\n")
# Don't care about this |status|: |runApp()| reporting it should be enough.
status = self.automation.runApp(None, browserEnv, options.app,
self.PROFILE_DIRECTORY, ["-silent"],
utilityPath = options.utilityPath,
xrePath = options.xrePath,
symbolsPath=options.symbolsPath)
# We don't care to call |processLeakLog()| for this step.
self.automation.log.info("\nINFO | runtests.py | Performing extension manager registration: end.")
# Remove the leak detection file so it can't "leak" to the tests run.
# The file is not there if leak logging was not enabled in the application build.
if os.path.exists(self.LEAK_REPORT_FILE):
os.remove(self.LEAK_REPORT_FILE)
# then again to actually run mochitest
if options.timeout:
timeout = options.timeout + 30
elif options.autorun:
timeout = None
else:
timeout = 330.0 # default JS harness timeout is 300 seconds
self.automation.log.info("INFO | runtests.py | Running tests: start.\n")
status = self.automation.runApp(testURL, browserEnv, options.app,
self.PROFILE_DIRECTORY, options.browserArgs,
runSSLTunnel = True,
utilityPath = options.utilityPath,
xrePath = options.xrePath,
certPath=options.certPath,
debuggerInfo=debuggerInfo,
symbolsPath=options.symbolsPath,
timeout = timeout)
# Server's no longer needed, and perhaps more importantly, anything it might
# spew to console shouldn't disrupt the leak information table we print next.
server.stop()
processLeakLog(self.LEAK_REPORT_FILE, options.leakThreshold)
self.automation.log.info("\nINFO | runtests.py | Running tests: end.")
# delete the profile and manifest
os.remove(manifest)
# hanging due to non-halting threads is no fun; assume we hit the errors we
# were going to hit already and exit.
return status
def makeTestConfig(self, options):
"Creates a test configuration file for customizing test execution."
def boolString(b):
if b:
return "true"
return "false"
logFile = options.logFile.replace("\\", "\\\\")
testPath = options.testPath.replace("\\", "\\\\")
content = """\
({
autoRun: %(autorun)s,
closeWhenDone: %(closeWhenDone)s,
logPath: "%(logPath)s",
testPath: "%(testPath)s"
})""" % {"autorun": boolString(options.autorun),
"closeWhenDone": boolString(options.closeWhenDone),
"logPath": logFile,
"testPath": testPath}
config = open(os.path.join(self.PROFILE_DIRECTORY, "testConfig.js"), "w")
config.write(content)
config.close()
def addChromeToProfile(self, options):
"Adds MochiKit chrome tests to the profile."
chromedir = os.path.join(self.PROFILE_DIRECTORY, "chrome")
os.mkdir(chromedir)
chrome = """
@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"); /* set default namespace to XUL */
toolbar,
toolbarpalette {
background-color: rgb(235, 235, 235) !important;
}
toolbar#nav-bar {
background-image: none !important;
}
"""
# write userChrome.css
chromeFile = open(os.path.join(self.PROFILE_DIRECTORY, "userChrome.css"), "a")
chromeFile.write(chrome)
chromeFile.close()
# register our chrome dir
chrometestDir = os.path.abspath(".") + "/"
if self.automation.IS_WIN32:
chrometestDir = "file:///" + chrometestDir.replace("\\", "/")
(path, leaf) = os.path.split(options.app)
manifest = os.path.join(path, "chrome", "mochikit.manifest")
manifestFile = open(manifest, "w")
manifestFile.write("content mochikit " + chrometestDir + " contentaccessible=yes\n")
if options.browserChrome:
manifestFile.write("""overlay chrome://navigator/content/navigator.xul chrome://mochikit/content/browser-test-overlay.xul
overlay chrome://browser/content/browser.xul chrome://mochikit/content/browser-test-overlay.xul
""")
manifestFile.close()
return manifest
def copyExtraFilesToProfile(self, options):
"Copy extra files or dirs specified on the command line to the testing profile."
for f in options.extraProfileFiles:
abspath = self.getFullPath(f)
dest = os.path.join(self.PROFILE_DIRECTORY, os.path.basename(abspath))
if os.path.isdir(abspath):
shutil.copytree(abspath, dest)
else:
shutil.copy(abspath, dest)
def main():
parser = MochitestOptions()
automation = Automation()
mochitest = Mochitest(automation)
parser = MochitestOptions(automation, mochitest.SCRIPT_DIRECTORY)
options, args = parser.parse_args()
if options.totalChunks is not None and options.thisChunk is None:
@ -318,7 +535,7 @@ def main():
if options.totalChunks:
if not 1 <= options.thisChunk <= options.totalChunks:
parser.error("thisChunk must be between 1 and totalChunks")
parser.error("thisChunk must be between 1 and totalChunks")
if options.xrePath is None:
# default xrePath to the app path if not provided
@ -330,248 +547,22 @@ def main():
options.xrePath = automation.DIST_BIN
# allow relative paths
options.xrePath = getFullPath(options.xrePath)
options.xrePath = mochitest.getFullPath(options.xrePath)
options.app = getFullPath(options.app)
options.app = mochitest.getFullPath(options.app)
if not os.path.exists(options.app):
msg = """\
Error: Path %(app)s doesn't exist.
Are you executing $objdir/_tests/testing/mochitest/runtests.py?"""
Error: Path %(app)s doesn't exist.
Are you executing $objdir/_tests/testing/mochitest/runtests.py?"""
print msg % {"app": options.app}
sys.exit(1)
options.utilityPath = getFullPath(options.utilityPath)
options.certPath = getFullPath(options.certPath)
options.utilityPath = mochitest.getFullPath(options.utilityPath)
options.certPath = mochitest.getFullPath(options.certPath)
if options.symbolsPath:
options.symbolsPath = getFullPath(options.symbolsPath)
options.symbolsPath = mochitest.getFullPath(options.symbolsPath)
debuggerInfo = getDebuggerInfo(oldcwd, options.debugger, options.debuggerArgs,
options.debuggerInteractive);
# browser environment
browserEnv = automation.environment(xrePath = options.xrePath)
# These variables are necessary for correct application startup; change
# via the commandline at your own risk.
browserEnv["XPCOM_DEBUG_BREAK"] = "stack"
for v in options.environment:
ix = v.find("=")
if ix <= 0:
print "Error: syntax error in --setenv=" + v
sys.exit(1)
browserEnv[v[:ix]] = v[ix + 1:]
automation.initializeProfile(PROFILE_DIRECTORY, options.extraPrefs)
manifest = addChromeToProfile(options)
copyExtraFilesToProfile(options)
server = MochitestServer(options)
server.start()
# If we're lucky, the server has fully started by now, and all paths are
# ready, etc. However, xpcshell cold start times suck, at least for debug
# builds. We'll try to connect to the server for awhile, and if we fail,
# we'll try to kill the server and exit with an error.
server.ensureReady(SERVER_STARTUP_TIMEOUT)
# URL parameters to test URL:
#
# autorun -- kick off tests automatically
# closeWhenDone -- runs quit.js after tests
# logFile -- logs test run to an absolute path
# totalChunks -- how many chunks to split tests into
# thisChunk -- which chunk to run
# timeout -- per-test timeout in seconds
#
# consoleLevel, fileLevel: set the logging level of the console and
# file logs, if activated.
# <http://mochikit.com/doc/html/MochiKit/Logging.html>
testURL = TESTS_URL + options.testPath
urlOpts = []
if options.chrome:
testURL = CHROMETESTS_URL
if options.testPath:
urlOpts.append("testPath=" + encodeURIComponent(options.testPath))
elif options.a11y:
testURL = A11YTESTS_URL
if options.testPath:
urlOpts.append("testPath=" + encodeURIComponent(options.testPath))
elif options.browserChrome:
testURL = "about:blank"
# allow relative paths for logFile
if options.logFile:
options.logFile = getFullPath(options.logFile)
if options.browserChrome:
makeTestConfig(options)
else:
if options.autorun:
urlOpts.append("autorun=1")
if options.timeout:
urlOpts.append("timeout=%d" % options.timeout)
if options.closeWhenDone:
urlOpts.append("closeWhenDone=1")
if options.logFile:
urlOpts.append("logFile=" + encodeURIComponent(options.logFile))
urlOpts.append("fileLevel=" + encodeURIComponent(options.fileLevel))
if options.consoleLevel:
urlOpts.append("consoleLevel=" + encodeURIComponent(options.consoleLevel))
if options.totalChunks:
urlOpts.append("totalChunks=%d" % options.totalChunks)
urlOpts.append("thisChunk=%d" % options.thisChunk)
if options.chunkByDir:
urlOpts.append("chunkByDir=%d" % options.chunkByDir)
if options.shuffle:
urlOpts.append("shuffle=1")
if len(urlOpts) > 0:
testURL += "?" + "&".join(urlOpts)
browserEnv["XPCOM_MEM_BLOAT_LOG"] = LEAK_REPORT_FILE
if options.fatalAssertions:
browserEnv["XPCOM_DEBUG_BREAK"] = "stack-and-abort"
# run once with -silent to let the extension manager do its thing
# and then exit the app
automation.log.info("INFO | runtests.py | Performing extension manager registration: start.\n")
# Don't care about this |status|: |runApp()| reporting it should be enough.
status = automation.runApp(None, browserEnv, options.app,
PROFILE_DIRECTORY, ["-silent"],
utilityPath = options.utilityPath,
xrePath = options.xrePath,
symbolsPath=options.symbolsPath)
# We don't care to call |processLeakLog()| for this step.
automation.log.info("\nINFO | runtests.py | Performing extension manager registration: end.")
# Remove the leak detection file so it can't "leak" to the tests run.
# The file is not there if leak logging was not enabled in the application build.
if os.path.exists(LEAK_REPORT_FILE):
os.remove(LEAK_REPORT_FILE)
# then again to actually run mochitest
if options.timeout:
timeout = options.timeout + 30
elif options.autorun:
timeout = None
else:
timeout = 330.0 # default JS harness timeout is 300 seconds
automation.log.info("INFO | runtests.py | Running tests: start.\n")
status = automation.runApp(testURL, browserEnv, options.app,
PROFILE_DIRECTORY, options.browserArgs,
runSSLTunnel = True,
utilityPath = options.utilityPath,
xrePath = options.xrePath,
certPath=options.certPath,
debuggerInfo=debuggerInfo,
symbolsPath=options.symbolsPath,
timeout = timeout)
# Server's no longer needed, and perhaps more importantly, anything it might
# spew to console shouldn't disrupt the leak information table we print next.
server.stop()
processLeakLog(LEAK_REPORT_FILE, options.leakThreshold)
automation.log.info("\nINFO | runtests.py | Running tests: end.")
# delete the profile and manifest
os.remove(manifest)
# hanging due to non-halting threads is no fun; assume we hit the errors we
# were going to hit already and exit.
sys.exit(status)
#######################
# CONFIGURATION SETUP #
#######################
def makeTestConfig(options):
"Creates a test configuration file for customizing test execution."
def boolString(b):
if b:
return "true"
return "false"
logFile = options.logFile.replace("\\", "\\\\")
testPath = options.testPath.replace("\\", "\\\\")
content = """\
({
autoRun: %(autorun)s,
closeWhenDone: %(closeWhenDone)s,
logPath: "%(logPath)s",
testPath: "%(testPath)s"
})""" % {"autorun": boolString(options.autorun),
"closeWhenDone": boolString(options.closeWhenDone),
"logPath": logFile,
"testPath": testPath}
config = open(os.path.join(PROFILE_DIRECTORY, "testConfig.js"), "w")
config.write(content)
config.close()
def addChromeToProfile(options):
"Adds MochiKit chrome tests to the profile."
chromedir = os.path.join(PROFILE_DIRECTORY, "chrome")
os.mkdir(chromedir)
chrome = []
part = """
@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"); /* set default namespace to XUL */
toolbar,
toolbarpalette {
background-color: rgb(235, 235, 235) !important;
}
toolbar#nav-bar {
background-image: none !important;
}
"""
chrome.append(part)
# write userChrome.css
chromeFile = open(os.path.join(PROFILE_DIRECTORY, "userChrome.css"), "a")
chromeFile.write("".join(chrome))
chromeFile.close()
# register our chrome dir
chrometestDir = os.path.abspath(".") + "/"
if automation.IS_WIN32:
chrometestDir = "file:///" + chrometestDir.replace("\\", "/")
(path, leaf) = os.path.split(options.app)
manifest = os.path.join(path, "chrome", "mochikit.manifest")
manifestFile = open(manifest, "w")
manifestFile.write("content mochikit " + chrometestDir + " contentaccessible=yes\n")
if options.browserChrome:
overlayLine = "overlay " + BROWSER_CHROME_URL + " " \
"chrome://mochikit/content/browser-test-overlay.xul\n"
manifestFile.write(overlayLine)
manifestFile.close()
return manifest
def copyExtraFilesToProfile(options):
"Copy extra files or dirs specified on the command line to the testing profile."
for f in options.extraProfileFiles:
abspath = getFullPath(f)
dest = os.path.join(PROFILE_DIRECTORY, os.path.basename(abspath))
if os.path.isdir(abspath):
shutil.copytree(abspath, dest)
else:
shutil.copy(abspath, dest)
#########
# DO IT #
#########
sys.exit(mochitest.runTests(options))
if __name__ == "__main__":
main()

View File

@ -22,6 +22,7 @@
# Contributor(s):
# Serge Gautherie <sgautherie.bz@free.fr>
# Ted Mielczarek <ted.mielczarek@gmail.com>
# Joel Maher <joel.maher@gmail.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
@ -45,243 +46,246 @@ from tempfile import mkdtemp
from automationutils import *
# Init logging
log = logging.getLogger()
handler = logging.StreamHandler(sys.stdout)
log.setLevel(logging.INFO)
log.addHandler(handler)
class XPCShellTests(object):
oldcwd = os.getcwd()
log = logging.getLogger()
oldcwd = os.getcwd()
def readManifest(manifest):
"""Given a manifest file containing a list of test directories,
return a list of absolute paths to the directories contained within."""
manifestdir = os.path.dirname(manifest)
testdirs = []
try:
f = open(manifest, "r")
for line in f:
dir = line.rstrip()
path = os.path.join(manifestdir, dir)
if os.path.isdir(path):
testdirs.append(path)
f.close()
except:
pass # just eat exceptions
return testdirs
def __init__(self):
# Init logging
handler = logging.StreamHandler(sys.stdout)
self.log.setLevel(logging.INFO)
self.log.addHandler(handler)
def runTests(xpcshell, xrePath=None, symbolsPath=None,
manifest=None, testdirs=[], testPath=None,
interactive=False, logfiles=True,
debuggerInfo=None):
"""Run xpcshell tests.
def readManifest(self, manifest):
"""Given a manifest file containing a list of test directories,
return a list of absolute paths to the directories contained within."""
manifestdir = os.path.dirname(manifest)
testdirs = []
try:
f = open(manifest, "r")
for line in f:
dir = line.rstrip()
path = os.path.join(manifestdir, dir)
if os.path.isdir(path):
testdirs.append(path)
f.close()
except:
pass # just eat exceptions
return testdirs
|xpcshell|, is the xpcshell executable to use to run the tests.
|xrePath|, if provided, is the path to the XRE to use.
|symbolsPath|, if provided is the path to a directory containing
breakpad symbols for processing crashes in tests.
|manifest|, if provided, is a file containing a list of
test directories to run.
|testdirs|, if provided, is a list of absolute paths of test directories.
No-manifest only option.
|testPath|, if provided, indicates a single path and/or test to run.
|interactive|, if set to True, indicates to provide an xpcshell prompt
instead of automatically executing the test.
|logfiles|, if set to False, indicates not to save output to log files.
Non-interactive only option.
|debuggerInfo|, if set, specifies the debugger and debugger arguments
that will be used to launch xpcshell.
"""
def runTests(self, xpcshell, xrePath=None, symbolsPath=None,
manifest=None, testdirs=[], testPath=None,
interactive=False, logfiles=True,
debuggerInfo=None):
"""Run xpcshell tests.
if not testdirs and not manifest:
# nothing to test!
print >>sys.stderr, "Error: No test dirs or test manifest specified!"
return False
|xpcshell|, is the xpcshell executable to use to run the tests.
|xrePath|, if provided, is the path to the XRE to use.
|symbolsPath|, if provided is the path to a directory containing
breakpad symbols for processing crashes in tests.
|manifest|, if provided, is a file containing a list of
test directories to run.
|testdirs|, if provided, is a list of absolute paths of test directories.
No-manifest only option.
|testPath|, if provided, indicates a single path and/or test to run.
|interactive|, if set to True, indicates to provide an xpcshell prompt
instead of automatically executing the test.
|logfiles|, if set to False, indicates not to save output to log files.
Non-interactive only option.
|debuggerInfo|, if set, specifies the debugger and debugger arguments
that will be used to launch xpcshell.
"""
passCount = 0
failCount = 0
if not testdirs and not manifest:
# nothing to test!
print >>sys.stderr, "Error: No test dirs or test manifest specified!"
return False
testharnessdir = os.path.dirname(os.path.abspath(__file__))
xpcshell = os.path.abspath(xpcshell)
# we assume that httpd.js lives in components/ relative to xpcshell
httpdJSPath = os.path.join(os.path.dirname(xpcshell), "components", "httpd.js").replace("\\", "/");
passCount = 0
failCount = 0
env = dict(os.environ)
# Make assertions fatal
env["XPCOM_DEBUG_BREAK"] = "stack-and-abort"
# Don't launch the crash reporter client
env["MOZ_CRASHREPORTER_NO_REPORT"] = "1"
testharnessdir = os.path.dirname(os.path.abspath(__file__))
xpcshell = os.path.abspath(xpcshell)
# we assume that httpd.js lives in components/ relative to xpcshell
httpdJSPath = os.path.join(os.path.dirname(xpcshell), "components", "httpd.js").replace("\\", "/");
if xrePath is None:
xrePath = os.path.dirname(xpcshell)
else:
xrePath = os.path.abspath(xrePath)
if sys.platform == 'win32':
env["PATH"] = env["PATH"] + ";" + xrePath
elif sys.platform in ('os2emx', 'os2knix'):
os.environ["BEGINLIBPATH"] = xrePath + ";" + env["BEGINLIBPATH"]
os.environ["LIBPATHSTRICT"] = "T"
elif sys.platform == 'osx':
env["DYLD_LIBRARY_PATH"] = xrePath
else: # unix or linux?
env["LD_LIBRARY_PATH"] = xrePath
env = dict(os.environ)
# Make assertions fatal
env["XPCOM_DEBUG_BREAK"] = "stack-and-abort"
# Don't launch the crash reporter client
env["MOZ_CRASHREPORTER_NO_REPORT"] = "1"
# xpcsRunArgs: <head.js> function to call to run the test.
# pStdout, pStderr: Parameter values for later |Popen()| call.
if interactive:
xpcsRunArgs = [
if xrePath is None:
xrePath = os.path.dirname(xpcshell)
else:
xrePath = os.path.abspath(xrePath)
if sys.platform == 'win32':
env["PATH"] = env["PATH"] + ";" + xrePath
elif sys.platform in ('os2emx', 'os2knix'):
os.environ["BEGINLIBPATH"] = xrePath + ";" + env["BEGINLIBPATH"]
os.environ["LIBPATHSTRICT"] = "T"
elif sys.platform == 'osx':
env["DYLD_LIBRARY_PATH"] = xrePath
else: # unix or linux?
env["LD_LIBRARY_PATH"] = xrePath
# xpcsRunArgs: <head.js> function to call to run the test.
# pStdout, pStderr: Parameter values for later |Popen()| call.
if interactive:
xpcsRunArgs = [
'-e', 'print("To start the test, type |_execute_test();|.");',
'-i']
pStdout = None
pStderr = None
else:
xpcsRunArgs = ['-e', '_execute_test();']
if (debuggerInfo and debuggerInfo["interactive"]):
pStdout = None
pStderr = None
else:
if sys.platform == 'os2emx':
xpcsRunArgs = ['-e', '_execute_test();']
if (debuggerInfo and debuggerInfo["interactive"]):
pStdout = None
pStderr = None
else:
pStdout = PIPE
pStderr = STDOUT
if sys.platform == 'os2emx':
pStdout = None
else:
pStdout = PIPE
pStderr = STDOUT
# <head.js> has to be loaded by xpchell: it can't load itself.
xpcsCmd = [xpcshell, '-g', xrePath, '-j', '-s'] + \
['-e', 'const _HTTPD_JS_PATH = "%s";' % httpdJSPath,
'-f', os.path.join(testharnessdir, 'head.js')]
# <head.js> has to be loaded by xpchell: it can't load itself.
xpcsCmd = [xpcshell, '-g', xrePath, '-j', '-s'] + \
['-e', 'const _HTTPD_JS_PATH = "%s";' % httpdJSPath,
'-f', os.path.join(testharnessdir, 'head.js')]
if debuggerInfo:
xpcsCmd = [debuggerInfo["path"]] + debuggerInfo["args"] + xpcsCmd
if debuggerInfo:
xpcsCmd = [debuggerInfo["path"]] + debuggerInfo["args"] + xpcsCmd
# |testPath| will be the optional path only, or |None|.
# |singleFile| will be the optional test only, or |None|.
singleFile = None
if testPath:
if testPath.endswith('.js'):
# Split into path and file.
if testPath.find('/') == -1:
# Test only.
singleFile = testPath
testPath = None
# |testPath| will be the optional path only, or |None|.
# |singleFile| will be the optional test only, or |None|.
singleFile = None
if testPath:
if testPath.endswith('.js'):
# Split into path and file.
if testPath.find('/') == -1:
# Test only.
singleFile = testPath
testPath = None
else:
# Both path and test.
# Reuse |testPath| temporarily.
testPath = testPath.rsplit('/', 1)
singleFile = testPath[1]
testPath = testPath[0]
else:
# Both path and test.
# Reuse |testPath| temporarily.
testPath = testPath.rsplit('/', 1)
singleFile = testPath[1]
testPath = testPath[0]
else:
# Path only.
# Simply remove optional ending separator.
testPath = testPath.rstrip("/")
# Path only.
# Simply remove optional ending separator.
testPath = testPath.rstrip("/")
# Override testdirs.
if manifest is not None:
testdirs = readManifest(os.path.abspath(manifest))
# Override testdirs.
if manifest is not None:
testdirs = self.readManifest(os.path.abspath(manifest))
# Process each test directory individually.
for testdir in testdirs:
if testPath and not testdir.endswith(testPath):
continue
testdir = os.path.abspath(testdir)
# get the list of head and tail files from the directory
testHeadFiles = []
for f in sorted(glob(os.path.join(testdir, "head_*.js"))):
if os.path.isfile(f):
testHeadFiles += [f]
testTailFiles = []
# Tails are executed in the reverse order, to "match" heads order,
# as in "h1-h2-h3 then t3-t2-t1".
for f in reversed(sorted(glob(os.path.join(testdir, "tail_*.js")))):
if os.path.isfile(f):
testTailFiles += [f]
# if a single test file was specified, we only want to execute that test
testfiles = sorted(glob(os.path.join(testdir, "test_*.js")))
if singleFile:
if singleFile in [os.path.basename(x) for x in testfiles]:
testfiles = [os.path.join(testdir, singleFile)]
else: # not in this dir? skip it
# Process each test directory individually.
for testdir in testdirs:
if testPath and not testdir.endswith(testPath):
continue
cmdH = ", ".join(['"' + f.replace('\\', '/') + '"'
for f in testHeadFiles])
cmdT = ", ".join(['"' + f.replace('\\', '/') + '"'
for f in testTailFiles])
cmdH = xpcsCmd + \
['-e', 'const _HEAD_FILES = [%s];' % cmdH] + \
['-e', 'const _TAIL_FILES = [%s];' % cmdT]
testdir = os.path.abspath(testdir)
# Now execute each test individually.
for test in testfiles:
# The test file will have to be loaded after the head files.
cmdT = ['-e', 'const _TEST_FILE = ["%s"];' %
os.path.join(testdir, test).replace('\\', '/')]
# get the list of head and tail files from the directory
testHeadFiles = []
for f in sorted(glob(os.path.join(testdir, "head_*.js"))):
if os.path.isfile(f):
testHeadFiles += [f]
testTailFiles = []
# Tails are executed in the reverse order, to "match" heads order,
# as in "h1-h2-h3 then t3-t2-t1".
for f in reversed(sorted(glob(os.path.join(testdir, "tail_*.js")))):
if os.path.isfile(f):
testTailFiles += [f]
# create a temp dir that the JS harness can stick a profile in
profileDir = None
try:
profileDir = mkdtemp()
env["XPCSHELL_TEST_PROFILE_DIR"] = profileDir
# if a single test file was specified, we only want to execute that test
testfiles = sorted(glob(os.path.join(testdir, "test_*.js")))
if singleFile:
if singleFile in [os.path.basename(x) for x in testfiles]:
testfiles = [os.path.join(testdir, singleFile)]
else: # not in this dir? skip it
continue
# Enable leaks (only) detection to its own log file.
leakLogFile = os.path.join(profileDir, "runxpcshelltests_leaks.log")
env["XPCOM_MEM_LEAK_LOG"] = leakLogFile
cmdH = ", ".join(['"' + f.replace('\\', '/') + '"'
for f in testHeadFiles])
cmdT = ", ".join(['"' + f.replace('\\', '/') + '"'
for f in testTailFiles])
cmdH = xpcsCmd + \
['-e', 'const _HEAD_FILES = [%s];' % cmdH] + \
['-e', 'const _TAIL_FILES = [%s];' % cmdT]
proc = Popen(cmdH + cmdT + xpcsRunArgs,
stdout=pStdout, stderr=pStderr, env=env, cwd=testdir)
# Now execute each test individually.
for test in testfiles:
# The test file will have to be loaded after the head files.
cmdT = ['-e', 'const _TEST_FILE = ["%s"];' %
os.path.join(testdir, test).replace('\\', '/')]
# allow user to kill hung subprocess with SIGINT w/o killing this script
# - don't move this line above Popen, or child will inherit the SIG_IGN
signal.signal(signal.SIGINT, signal.SIG_IGN)
# |stderr == None| as |pStderr| was either |None| or redirected to |stdout|.
stdout, stderr = proc.communicate()
signal.signal(signal.SIGINT, signal.SIG_DFL)
# create a temp dir that the JS harness can stick a profile in
profileDir = None
try:
profileDir = mkdtemp()
env["XPCSHELL_TEST_PROFILE_DIR"] = profileDir
if interactive:
# Not sure what else to do here...
return True
# Enable leaks (only) detection to its own log file.
leakLogFile = os.path.join(profileDir, "runxpcshelltests_leaks.log")
env["XPCOM_MEM_LEAK_LOG"] = leakLogFile
if proc.returncode != 0 or (stdout and re.search("^TEST-UNEXPECTED-FAIL", stdout, re.MULTILINE)):
print """TEST-UNEXPECTED-FAIL | %s | test failed (with xpcshell return code: %d), see following log:
proc = Popen(cmdH + cmdT + xpcsRunArgs,
stdout=pStdout, stderr=pStderr, env=env, cwd=testdir)
# allow user to kill hung subprocess with SIGINT w/o killing this script
# - don't move this line above Popen, or child will inherit the SIG_IGN
signal.signal(signal.SIGINT, signal.SIG_IGN)
# |stderr == None| as |pStderr| was either |None| or redirected to |stdout|.
stdout, stderr = proc.communicate()
signal.signal(signal.SIGINT, signal.SIG_DFL)
if interactive:
# Not sure what else to do here...
return True
if proc.returncode != 0 or (stdout and re.search("^TEST-UNEXPECTED-FAIL", stdout, re.MULTILINE)):
print """TEST-UNEXPECTED-FAIL | %s | test failed (with xpcshell return code: %d), see following log:
>>>>>>>
%s
<<<<<<<""" % (test, proc.returncode, stdout)
checkForCrashes(testdir, symbolsPath, testName=test)
failCount += 1
else:
print "TEST-PASS | %s | test passed" % test
passCount += 1
checkForCrashes(testdir, symbolsPath, testName=test)
failCount += 1
else:
print "TEST-PASS | %s | test passed" % test
passCount += 1
dumpLeakLog(leakLogFile, True)
dumpLeakLog(leakLogFile, True)
if logfiles and stdout:
try:
f = open(test + ".log", "w")
f.write(stdout)
if logfiles and stdout:
try:
f = open(test + ".log", "w")
f.write(stdout)
if os.path.exists(leakLogFile):
leaks = open(leakLogFile, "r")
f.write(leaks.read())
leaks.close()
finally:
if f:
f.close()
finally:
if profileDir:
shutil.rmtree(profileDir)
if os.path.exists(leakLogFile):
leaks = open(leakLogFile, "r")
f.write(leaks.read())
leaks.close()
finally:
if f:
f.close()
finally:
if profileDir:
shutil.rmtree(profileDir)
if passCount == 0 and failCount == 0:
print "TEST-UNEXPECTED-FAIL | runxpcshelltests.py | No tests run. Did you pass an invalid --test-path?"
failCount = 1
if passCount == 0 and failCount == 0:
print "TEST-UNEXPECTED-FAIL | runxpcshelltests.py | No tests run. Did you pass an invalid --test-path?"
failCount = 1
print """INFO | Result summary:
INFO | Passed: %d
INFO | Failed: %d""" % (passCount, failCount)
print """INFO | Result summary:
INFO | Passed: %d
INFO | Failed: %d""" % (passCount, failCount)
return failCount == 0
return failCount == 0
def main():
"""Process command line arguments and call runTests() to do the real work."""
@ -307,27 +311,29 @@ def main():
if len(args) < 2 and options.manifest is None or \
(len(args) < 1 and options.manifest is not None):
print >>sys.stderr, """Usage: %s <path to xpcshell> <test dirs>
or: %s --manifest=test.manifest <path to xpcshell>""" % (sys.argv[0],
print >>sys.stderr, """Usage: %s <path to xpcshell> <test dirs>
or: %s --manifest=test.manifest <path to xpcshell>""" % (sys.argv[0],
sys.argv[0])
sys.exit(1)
sys.exit(1)
debuggerInfo = getDebuggerInfo(oldcwd, options.debugger, options.debuggerArgs,
xpcsh = XPCShellTests()
debuggerInfo = getDebuggerInfo(xpcsh.oldcwd, options.debugger, options.debuggerArgs,
options.debuggerInteractive);
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 runTests(args[0],
xrePath=options.xrePath,
symbolsPath=options.symbolsPath,
manifest=options.manifest,
testdirs=args[1:],
testPath=options.testPath,
interactive=options.interactive,
logfiles=options.logfiles,
debuggerInfo=debuggerInfo):
if not xpcsh.runTests(args[0],
xrePath=options.xrePath,
symbolsPath=options.symbolsPath,
manifest=options.manifest,
testdirs=args[1:],
testPath=options.testPath,
interactive=options.interactive,
logfiles=options.logfiles,
debuggerInfo=debuggerInfo):
sys.exit(1)
if __name__ == '__main__':