diff --git a/build/automation.py.in b/build/automation.py.in index 15da17a198d3..84858041e60a 100644 --- a/build/automation.py.in +++ b/build/automation.py.in @@ -51,7 +51,10 @@ import sys import threading import tempfile - +_DEFAULT_WEB_SERVER = "127.0.0.1" +_DEFAULT_HTTP_PORT = 8888 +_DEFAULT_SSL_PORT = 4443 + #expand _DIST_BIN = __XPC_BIN_PATH__ #expand _IS_WIN32 = len("__WIN32__") != 0 #expand _IS_MAC = __IS_MAC__ != 0 @@ -130,6 +133,10 @@ class Automation(object): # timeout, in seconds DEFAULT_TIMEOUT = 60.0 + DEFAULT_WEB_SERVER = _DEFAULT_WEB_SERVER + DEFAULT_HTTP_PORT = _DEFAULT_HTTP_PORT + DEFAULT_SSL_PORT = _DEFAULT_SSL_PORT + log = logging.getLogger() def __init__(self): @@ -141,6 +148,13 @@ class Automation(object): self.log.setLevel(logging.INFO) self.log.addHandler(handler) + self.setServerInfo() + + def setServerInfo(self, webServer = _DEFAULT_WEB_SERVER, httpPort = _DEFAULT_HTTP_PORT, sslPort = _DEFAULT_SSL_PORT): + self.webServer = webServer + self.httpPort = httpPort + self.sslPort = sslPort + @property def __all__(self): return [ @@ -303,7 +317,7 @@ user_pref("capability.principal.codebase.p%(i)d.granted", user_pref("capability.principal.codebase.p%(i)d.id", "%(origin)s"); user_pref("capability.principal.codebase.p%(i)d.subjectName", ""); """ % { "i": i, - "origin": (l.scheme + "://" + l.host + ":" + l.port) } + "origin": (l.scheme + "://" + l.host + ":" + str(l.port)) } prefs.append(part) # We need to proxy every server but the primary one. @@ -335,11 +349,14 @@ function FindProxyForURL(url, host) if (origins.indexOf(origin) < 0) return 'DIRECT'; if (isHttp) - return 'PROXY 127.0.0.1:8888'; + return 'PROXY %(remote)s:%(httpport)s'; if (isHttps) - return 'PROXY 127.0.0.1:4443'; + return 'PROXY %(remote)s:%(sslport)s'; return 'DIRECT'; -}""" % { "origins": origins } +}""" % { "origins": origins, + "remote":self.webServer, + "httpport":self.httpPort, + "sslport":self.sslPort } pacURL = "".join(pacURL.splitlines()) part = """ @@ -385,8 +402,8 @@ user_pref("camino.use_system_proxy_settings", false); // Camino-only, harmless t sslTunnelConfig.write("httpproxy:1\n") sslTunnelConfig.write("certdbdir:%s\n" % certPath) - sslTunnelConfig.write("forward:127.0.0.1:8888\n") - sslTunnelConfig.write("listen:*:4443:pgo server certificate\n") + sslTunnelConfig.write("forward:127.0.0.1:%s\n" % self.httpPort) + sslTunnelConfig.write("listen:*:%s:pgo server certificate\n" % self.sslPort) # Configure automatic certificate and bind custom certificates, client authentication locations = self.readLocations() @@ -399,14 +416,14 @@ user_pref("camino.use_system_proxy_settings", false); // Camino-only, harmless t match = customCertRE.match(option) if match: customcert = match.group("nickname"); - sslTunnelConfig.write("listen:%s:%s:4443:%s\n" % - (loc.host, loc.port, customcert)) + sslTunnelConfig.write("listen:%s:%s:%s:%s\n" % + (loc.host, loc.port, self.sslPort, customcert)) match = clientAuthRE.match(option) if match: clientauth = match.group("clientauth"); - sslTunnelConfig.write("clientauth:%s:%s:4443:%s\n" % - (loc.host, loc.port, clientauth)) + sslTunnelConfig.write("clientauth:%s:%s:%s:%s\n" % + (loc.host, loc.port, self.sslPort, clientauth)) sslTunnelConfig.close() diff --git a/testing/mochitest/runtests.py.in b/testing/mochitest/runtests.py.in index e8f08a67cfd4..66f6048a72bc 100644 --- a/testing/mochitest/runtests.py.in +++ b/testing/mochitest/runtests.py.in @@ -199,6 +199,12 @@ class MochitestOptions(optparse.OptionParser): help = "copy specified files/dirs to testing profile") defaults["extraProfileFiles"] = [] + self.add_option("--profile-path", action = "store", + type = "string", dest = "profilePath", + help = "Directory where the profile will be stored." + "This directory will be deleted after the tests are finished") + defaults["profilePath"] = tempfile.mkdtemp() + # -h, --help are automatically handled by OptionParser self.set_defaults(**defaults) @@ -212,6 +218,49 @@ See for details on the logg self.set_usage(usage) + def verifyOptions(self, options, mochitest): + """ verify correct options and cleanup paths """ + + if options.totalChunks is not None and options.thisChunk is None: + self.error("thisChunk must be specified when totalChunks is specified") + + if options.totalChunks: + if not 1 <= options.thisChunk <= options.totalChunks: + self.error("thisChunk must be between 1 and totalChunks") + + if options.xrePath is None: + # default xrePath to the app path if not provided + # but only if an app path was explicitly provided + if options.app != self.defaults['app']: + options.xrePath = os.path.dirname(options.app) + else: + # otherwise default to dist/bin + options.xrePath = self._automation.DIST_BIN + + # allow relative paths + options.xrePath = mochitest.getFullPath(options.xrePath) + + options.profilePath = mochitest.getFullPath(options.profilePath) + + 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?""" + print msg % {"app": options.app} + return None + + options.utilityPath = mochitest.getFullPath(options.utilityPath) + options.certPath = mochitest.getFullPath(options.certPath) + if options.symbolsPath: + options.symbolsPath = mochitest.getFullPath(options.symbolsPath) + + options.webServer = self._automation.DEFAULT_WEB_SERVER + options.httpPort = self._automation.DEFAULT_HTTP_PORT + options.sslPort = self._automation.DEFAULT_SSL_PORT + return options + + ####################### # HTTP SERVER SUPPORT # ####################### @@ -219,14 +268,15 @@ See for details on the logg class MochitestServer: "Web server used to serve Mochitests, for closer fidelity to the real web." - def __init__(self, automation, options, profileDir, shutdownURL): + def __init__(self, automation, options): self._automation = automation self._closeWhenDone = options.closeWhenDone self._utilityPath = options.utilityPath self._xrePath = options.xrePath - self._profileDir = profileDir - self.shutdownURL = shutdownURL - self.webServer = "127.0.0.1" + self._profileDir = options.profilePath + self.webServer = options.webServer + self.httpPort = options.httpPort + self.shutdownURL = "http://%(server)s:%(port)s/server/shutdown" % { "server" : self.webServer, "port" : self.httpPort } def start(self): "Run the Mochitest server, returning the process ID of the server." @@ -239,7 +289,8 @@ class MochitestServer: args = ["-g", self._xrePath, "-v", "170", "-f", "./" + "httpd.js", - '-e', 'const _SERVER_ADDR="' + self.webServer + '";', + "-e", "const _PROFILE_PATH = '%(profile)s';const _SERVER_PORT = '%(port)s'; const _SERVER_ADDR ='%(server)s';" % + {"profile" : self._profileDir.replace('\\', '\\\\'), "port" : self.httpPort, "server" : self.webServer }, "-f", "./" + "server.js"] xpcshell = os.path.join(self._utilityPath, @@ -278,7 +329,6 @@ class MochitestServer: class Mochitest(object): # Path to the test script on the server - TEST_SERVER_HOST = "mochi.test:8888" TEST_PATH = "/tests/" CHROME_PATH = "/redirect.html"; A11Y_PATH = "/redirect-a11y.html" @@ -301,17 +351,13 @@ class Mochitest(object): 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." + " Get an absolute path relative to self.oldcwd." return os.path.normpath(os.path.join(self.oldcwd, os.path.expanduser(path))) def buildTestPath(self, options): - """ build the url path to the specific test harness and test file or directory """ - testHost = "http://" + self.TEST_SERVER_HOST + """ Build the url path to the specific test harness and test file or directory """ + testHost = "http://mochi.test:8888" testURL = testHost + self.TEST_PATH + options.testPath if options.chrome: testURL = testHost + self.CHROME_PATH @@ -326,9 +372,11 @@ class Mochitest(object): return testURL def startWebServer(self, options): - """ create the webserver and start it up """ - shutdownURL = "http://" + self.TEST_SERVER_HOST + "/server/shutdown" - self.server = MochitestServer(self.automation, options, self.PROFILE_DIRECTORY, shutdownURL) + if options.webServer != '127.0.0.1': + return + + """ Create the webserver and start it up. """ + self.server = MochitestServer(self.automation, options) self.server.start() # If we're lucky, the server has fully started by now, and all paths are @@ -337,10 +385,13 @@ class Mochitest(object): # we'll try to kill the server and exit with an error. self.server.ensureReady(self.SERVER_STARTUP_TIMEOUT) - def stopWebServer(self): + def stopWebServer(self, options): """ 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. """ + if options.webServer != '127.0.0.1': + return + self.server.stop() def getLogFilePath(self, logFile): @@ -351,7 +402,7 @@ class Mochitest(object): def buildProfile(self, options): """ create the profile and add optional chrome bits and files if requested """ - self.automation.initializeProfile(self.PROFILE_DIRECTORY, options.extraPrefs) + self.automation.initializeProfile(options.profilePath, options.extraPrefs) manifest = self.addChromeToProfile(options) self.copyExtraFilesToProfile(options) return manifest @@ -371,7 +422,7 @@ class Mochitest(object): return None browserEnv[v[:ix]] = v[ix + 1:] - browserEnv["XPCOM_MEM_BLOAT_LOG"] = self.LEAK_REPORT_FILE + browserEnv["XPCOM_MEM_BLOAT_LOG"] = self.leak_report_file if options.fatalAssertions: browserEnv["XPCOM_DEBUG_BREAK"] = "stack-and-abort" @@ -385,7 +436,7 @@ class Mochitest(object): 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"], + options.profilePath, ["-silent"], utilityPath = options.utilityPath, xrePath = options.xrePath, symbolsPath=options.symbolsPath) @@ -407,7 +458,7 @@ class Mochitest(object): # allow relative paths for logFile if options.logFile: - options.logFile = self.getFullPath(options.logFile) + options.logFile = self.getLogFilePath(options.logFile) if options.browserChrome: self.makeTestConfig(options) else: @@ -430,16 +481,18 @@ class Mochitest(object): if options.shuffle: self.urlOpts.append("shuffle=1") - def cleanup(self, manifest): + def cleanup(self, manifest, options): """ remove temporary files and profile """ os.remove(manifest) - shutil.rmtree(self.PROFILE_DIRECTORY) + shutil.rmtree(options.profilePath) def runTests(self, options): """ Prepare, configure, run tests and cleanup """ debuggerInfo = getDebuggerInfo(self.oldcwd, options.debugger, options.debuggerArgs, options.debuggerInteractive); + self.leak_report_file = os.path.join(options.profilePath, "runtests_leaks.log") + browserEnv = self.buildBrowserEnv(options) if (browserEnv == None): return 1 @@ -457,8 +510,8 @@ class Mochitest(object): # 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) + if os.path.exists(self.leak_report_file): + os.remove(self.leak_report_file) # then again to actually run mochitest if options.timeout: @@ -469,7 +522,7 @@ class Mochitest(object): 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, + options.profilePath, options.browserArgs, runSSLTunnel = self.runSSLTunnel, utilityPath = options.utilityPath, xrePath = options.xrePath, @@ -478,11 +531,11 @@ class Mochitest(object): symbolsPath=options.symbolsPath, timeout = timeout) - self.stopWebServer() - processLeakLog(self.LEAK_REPORT_FILE, options.leakThreshold) + self.stopWebServer(options) + processLeakLog(self.leak_report_file, options.leakThreshold) self.automation.log.info("\nINFO | runtests.py | Running tests: end.") - self.cleanup(manifest) + self.cleanup(manifest, options) return status def makeTestConfig(self, options): @@ -505,7 +558,7 @@ class Mochitest(object): "logPath": logFile, "testPath": testPath} - config = open(os.path.join(self.PROFILE_DIRECTORY, "testConfig.js"), "w") + config = open(os.path.join(options.profilePath, "testConfig.js"), "w") config.write(content) config.close() @@ -513,7 +566,7 @@ class Mochitest(object): def addChromeToProfile(self, options): "Adds MochiKit chrome tests to the profile." - chromedir = os.path.join(self.PROFILE_DIRECTORY, "chrome") + chromedir = os.path.join(options.profilePath, "chrome") os.mkdir(chromedir) chrome = """ @@ -528,7 +581,7 @@ toolbar#nav-bar { """ # write userChrome.css - chromeFile = open(os.path.join(self.PROFILE_DIRECTORY, "userChrome.css"), "a") + chromeFile = open(os.path.join(options.profilePath, "userChrome.css"), "a") chromeFile.write(chrome) chromeFile.close() @@ -561,7 +614,7 @@ overlay chrome://browser/content/browser.xul chrome://mochikit/content/browser-t "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)) + dest = os.path.join(options.profilePath, os.path.basename(abspath)) if os.path.isdir(abspath): shutil.copytree(abspath, dest) else: @@ -573,38 +626,11 @@ def main(): parser = MochitestOptions(automation, mochitest.SCRIPT_DIRECTORY) options, args = parser.parse_args() - if options.totalChunks is not None and options.thisChunk is None: - parser.error("thisChunk must be specified when totalChunks is specified") - - if options.totalChunks: - if not 1 <= options.thisChunk <= options.totalChunks: - parser.error("thisChunk must be between 1 and totalChunks") - - if options.xrePath is None: - # default xrePath to the app path if not provided - # but only if an app path was explicitly provided - if options.app != parser.defaults['app']: - options.xrePath = os.path.dirname(options.app) - else: - # otherwise default to dist/bin - options.xrePath = automation.DIST_BIN - - # allow relative paths - options.xrePath = mochitest.getFullPath(options.xrePath) - - 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?""" - print msg % {"app": options.app} + options = parser.verifyOptions(options, mochitest) + if (options == None): sys.exit(1) - options.utilityPath = mochitest.getFullPath(options.utilityPath) - options.certPath = mochitest.getFullPath(options.certPath) - if options.symbolsPath: - options.symbolsPath = mochitest.getFullPath(options.symbolsPath) - + automation.setServerInfo(options.webServer, options.httpPort, options.sslPort) sys.exit(mochitest.runTests(options)) if __name__ == "__main__": diff --git a/testing/mochitest/server.js b/testing/mochitest/server.js index 46fc9873e191..fdc5e9d7e671 100644 --- a/testing/mochitest/server.js +++ b/testing/mochitest/server.js @@ -49,9 +49,6 @@ let (ios = Cc["@mozilla.org/network/io-service;1"] ios.offline = false; } -const SERVER_PORT = 8888; -var gServerAddress = "127.0.0.1"; - var server; // for use in the shutdown handler, if necessary // @@ -169,9 +166,18 @@ function runServer() if (!invalid) gServerAddress = _SERVER_ADDR; else - dumpn("WARNING: invalid server address ('" + _SERVER_ADDR + "'), using localhost"); + throw "invalid _SERVER_ADDR, please specify a valid IP Address"; } } + } else { + throw "please defined _SERVER_ADDR (as an ip address) before running server.js"; + } + + if (typeof(_SERVER_PORT) != "undefined") { + if (parseInt(_SERVER_PORT) > 0 && parseInt(_SERVER_PORT) < 32000) + SERVER_PORT = _SERVER_PORT; + } else { + throw "please define _SERVER_PORT (as a port number) before running server.js"; } server._start(SERVER_PORT, gServerAddress); @@ -181,8 +187,13 @@ function runServer() .createInstance(Ci.nsIFileOutputStream); var serverAlive = Cc["@mozilla.org/file/local;1"] .createInstance(Ci.nsILocalFile); - serverAlive.initWithFile(serverBasePath); - serverAlive.append("mochitesttestingprofile"); + + if (typeof(_PROFILE_PATH) == "undefined") { + serverAlive.initWithFile(serverBasePath); + serverAlive.append("mochitesttestingprofile"); + } else { + serverAlive.initWithPath(_PROFILE_PATH); + } // If we're running outside of the test harness, there might // not be a test profile directory present