--- a/build/automation.py.in
+++ b/build/automation.py.in
@@ -46,35 +46,107 @@ import re
import select
import shutil
import signal
import subprocess
import sys
import threading
import tempfile
+"""
+Runs the browser from a script, and provides useful utilities
+for setting up the browser environment.
+"""
-#expand _DIST_BIN = __XPC_BIN_PATH__
-#expand _IS_WIN32 = len("__WIN32__") != 0
-#expand _IS_MAC = __IS_MAC__ != 0
-#expand _IS_LINUX = __IS_LINUX__ != 0
+SCRIPT_DIR = os.path.abspath(os.path.realpath(os.path.dirname(sys.argv[0])))
+sys.path.insert(0, SCRIPT_DIR);
+from automationutils import checkForCrashes
+
+__all__ = [
+ "UNIXISH",
+ "IS_WIN32",
+ "IS_MAC",
+ "log",
+ "runApp",
+ "Process",
+ "addExtraCommonOptions",
+ "initializeProfile",
+ "DIST_BIN",
+ "DEFAULT_APP",
+ "CERTS_SRC_DIR",
+ "environment",
+ "IS_TEST_BUILD",
+ "IS_DEBUG_BUILD",
+ "DEFAULT_TIMEOUT",
+ ]
+
+# timeout, in seconds
+DEFAULT_TIMEOUT = 60.0
+
+# These are generated in mozilla/build/Makefile.in
+#expand DIST_BIN = __XPC_BIN_PATH__
+#expand IS_WIN32 = len("__WIN32__") != 0
+#expand IS_MAC = __IS_MAC__ != 0
+#expand IS_LINUX = __IS_LINUX__ != 0
#ifdef IS_CYGWIN
-#expand _IS_CYGWIN = __IS_CYGWIN__ == 1
+#expand IS_CYGWIN = __IS_CYGWIN__ == 1
#else
-_IS_CYGWIN = False
+IS_CYGWIN = False
#endif
-#expand _IS_CAMINO = __IS_CAMINO__ != 0
-#expand _BIN_SUFFIX = __BIN_SUFFIX__
-#expand _PERL = __PERL__
+#expand IS_CAMINO = __IS_CAMINO__ != 0
+#expand BIN_SUFFIX = __BIN_SUFFIX__
+#expand PERL = __PERL__
+
+UNIXISH = not IS_WIN32 and not IS_MAC
+
+#expand DEFAULT_APP = "./" + __BROWSER_PATH__
+#expand CERTS_SRC_DIR = __CERTS_SRC_DIR__
+#expand IS_TEST_BUILD = __IS_TEST_BUILD__
+#expand IS_DEBUG_BUILD = __IS_DEBUG_BUILD__
+#expand CRASHREPORTER = __CRASHREPORTER__ == 1
+
+###########
+# LOGGING #
+###########
+
+# We use the logging system here primarily because it'll handle multiple
+# threads, which is needed to process the output of the server and application
+# processes simultaneously.
+log = logging.getLogger()
+handler = logging.StreamHandler(sys.stdout)
+log.setLevel(logging.INFO)
+log.addHandler(handler)
+
-#expand _DEFAULT_APP = "./" + __BROWSER_PATH__
-#expand _CERTS_SRC_DIR = __CERTS_SRC_DIR__
-#expand _IS_TEST_BUILD = __IS_TEST_BUILD__
-#expand _IS_DEBUG_BUILD = __IS_DEBUG_BUILD__
-#expand _CRASHREPORTER = __CRASHREPORTER__ == 1
+#################
+# SUBPROCESSING #
+#################
+
+class Process(subprocess.Popen):
+ """
+ Represents our view of a subprocess.
+ It adds a kill() method which allows it to be stopped explicitly.
+ """
+
+ def kill(self):
+ if IS_WIN32:
+ import platform
+ pid = "%i" % self.pid
+ if platform.release() == "2000":
+ # Windows 2000 needs 'kill.exe' from the 'Windows 2000 Resource Kit tools'. (See bug 475455.)
+ try:
+ subprocess.Popen(["kill", "-f", pid]).wait()
+ except:
+ log.info("TEST-UNEXPECTED-FAIL | automation.py | Missing 'kill' utility to kill process with pid=%s. Kill it manually!", pid)
+ else:
+ # Windows XP and later.
+ subprocess.Popen(["taskkill", "/F", "/PID", pid]).wait()
+ else:
+ os.kill(self.pid, signal.SIGKILL)
+
#################
# PROFILE SETUP #
#################
class SyntaxError(Exception):
"Signifies a syntax error on a particular line in server-locations.txt."
@@ -95,166 +167,83 @@ class Location:
"Represents a location line in server-locations.txt."
def __init__(self, scheme, host, port, options):
self.scheme = scheme
self.host = host
self.port = port
self.options = options
-class Automation(object):
+
+def readLocations(locationsPath = "server-locations.txt"):
"""
- Runs the browser from a script, and provides useful utilities
- for setting up the browser environment.
+ Reads the locations at which the Mochitest HTTP server is available from
+ server-locations.txt.
"""
- DIST_BIN = _DIST_BIN
- IS_WIN32 = _IS_WIN32
- IS_MAC = _IS_MAC
- IS_LINUX = _IS_LINUX
- IS_CYGWIN = _IS_CYGWIN
- IS_CAMINO = _IS_CAMINO
- BIN_SUFFIX = _BIN_SUFFIX
- PERL = _PERL
-
- UNIXISH = not IS_WIN32 and not IS_MAC
-
- DEFAULT_APP = _DEFAULT_APP
- CERTS_SRC_DIR = _CERTS_SRC_DIR
- IS_TEST_BUILD = _IS_TEST_BUILD
- IS_DEBUG_BUILD = _IS_DEBUG_BUILD
- CRASHREPORTER = _CRASHREPORTER
-
- SCRIPT_DIR = os.path.abspath(os.path.realpath(os.path.dirname(sys.argv[0])))
- sys.path.insert(0, SCRIPT_DIR)
- automationutils = __import__('automationutils')
-
- # timeout, in seconds
- DEFAULT_TIMEOUT = 60.0
-
- log = logging.getLogger()
-
- def __init__(self):
-
- # We use the logging system here primarily because it'll handle multiple
- # threads, which is needed to process the output of the server and application
- # processes simultaneously.
- handler = logging.StreamHandler(sys.stdout)
- self.log.setLevel(logging.INFO)
- self.log.addHandler(handler)
+ locationFile = codecs.open(locationsPath, "r", "UTF-8")
- @property
- def __all__(self):
- return [
- "UNIXISH",
- "IS_WIN32",
- "IS_MAC",
- "log",
- "runApp",
- "Process",
- "addCommonOptions",
- "initializeProfile",
- "DIST_BIN",
- "DEFAULT_APP",
- "CERTS_SRC_DIR",
- "environment",
- "IS_TEST_BUILD",
- "IS_DEBUG_BUILD",
- "DEFAULT_TIMEOUT",
- ]
-
- class Process(subprocess.Popen):
- """
- Represents our view of a subprocess.
- It adds a kill() method which allows it to be stopped explicitly.
- """
-
- def kill(self):
- if Automation().IS_WIN32:
- import platform
- pid = "%i" % self.pid
- if platform.release() == "2000":
- # Windows 2000 needs 'kill.exe' from the
- #'Windows 2000 Resource Kit tools'. (See bug 475455.)
- try:
- subprocess.Popen(["kill", "-f", pid]).wait()
- except:
- self.log.info("TEST-UNEXPECTED-FAIL | automation.py | Missing 'kill' utility to kill process with pid=%s. Kill it manually!", pid)
- else:
- # Windows XP and later.
- subprocess.Popen(["taskkill", "/F", "/PID", pid]).wait()
- else:
- os.kill(self.pid, signal.SIGKILL)
-
- def readLocations(self, locationsPath = "server-locations.txt"):
- """
- Reads the locations at which the Mochitest HTTP server is available from
- server-locations.txt.
- """
-
- locationFile = codecs.open(locationsPath, "r", "UTF-8")
-
- # Perhaps more detail than necessary, but it's the easiest way to make sure
- # we get exactly the format we want. See server-locations.txt for the exact
- # format guaranteed here.
- lineRe = re.compile(r"^(?P<scheme>[a-z][-a-z0-9+.]*)"
+ # Perhaps more detail than necessary, but it's the easiest way to make sure
+ # we get exactly the format we want. See server-locations.txt for the exact
+ # format guaranteed here.
+ lineRe = re.compile(r"^(?P<scheme>[a-z][-a-z0-9+.]*)"
r"://"
r"(?P<host>"
r"\d+\.\d+\.\d+\.\d+"
r"|"
r"(?:[a-z0-9](?:[-a-z0-9]*[a-z0-9])?\.)*"
r"[a-z](?:[-a-z0-9]*[a-z0-9])?"
r")"
r":"
r"(?P<port>\d+)"
r"(?:"
r"\s+"
r"(?P<options>\S+(?:,\S+)*)"
r")?$")
- locations = []
- lineno = 0
- seenPrimary = False
- for line in locationFile:
- lineno += 1
- if line.startswith("#") or line == "\n":
- continue
+ locations = []
+ lineno = 0
+ seenPrimary = False
+ for line in locationFile:
+ lineno += 1
+ if line.startswith("#") or line == "\n":
+ continue
- match = lineRe.match(line)
- if not match:
- raise SyntaxError(lineno)
+ match = lineRe.match(line)
+ if not match:
+ raise SyntaxError(lineno)
- options = match.group("options")
- if options:
- options = options.split(",")
- if "primary" in options:
- if seenPrimary:
- raise SyntaxError(lineno, "multiple primary locations")
- seenPrimary = True
- else:
- options = []
+ options = match.group("options")
+ if options:
+ options = options.split(",")
+ if "primary" in options:
+ if seenPrimary:
+ raise SyntaxError(lineno, "multiple primary locations")
+ seenPrimary = True
+ else:
+ options = []
- locations.append(Location(match.group("scheme"), match.group("host"),
- match.group("port"), options))
+ locations.append(Location(match.group("scheme"), match.group("host"),
+ match.group("port"), options))
- if not seenPrimary:
- raise SyntaxError(lineno + 1, "missing primary location")
+ if not seenPrimary:
+ raise SyntaxError(lineno + 1, "missing primary location")
- return locations
+ return locations
- def initializeProfile(self, profileDir, extraPrefs = []):
- "Sets up the standard testing profile."
+def initializeProfile(profileDir, extraPrefs = []):
+ "Sets up the standard testing profile."
- # Start with a clean slate.
- shutil.rmtree(profileDir, True)
- os.mkdir(profileDir)
+ # Start with a clean slate.
+ shutil.rmtree(profileDir, True)
+ os.mkdir(profileDir)
- prefs = []
+ prefs = []
- part = """\
+ part = """\
user_pref("browser.dom.window.dump.enabled", true);
user_pref("dom.allow_scripts_to_close_windows", true);
user_pref("dom.disable_open_during_load", false);
user_pref("dom.max_script_run_time", 0); // no slow script dialogs
user_pref("dom.max_chrome_script_run_time", 0);
user_pref("dom.popup_maximum", -1);
user_pref("signed.applets.codebase_principal_support", true);
user_pref("security.warn_submit_insecure", false);
@@ -283,40 +272,40 @@ user_pref("camino.warn_when_closing", fa
user_pref("urlclassifier.updateinterval", 172800);
// Point the url-classifier to the local testing server for fast failures
user_pref("browser.safebrowsing.provider.0.gethashURL", "http://localhost:8888/safebrowsing-dummy/gethash");
user_pref("browser.safebrowsing.provider.0.keyURL", "http://localhost:8888/safebrowsing-dummy/newkey");
user_pref("browser.safebrowsing.provider.0.lookupURL", "http://localhost:8888/safebrowsing-dummy/lookup");
user_pref("browser.safebrowsing.provider.0.updateURL", "http://localhost:8888/safebrowsing-dummy/update");
"""
- prefs.append(part)
+ prefs.append(part)
- locations = self.readLocations()
+ locations = readLocations()
- # Grant God-power to all the privileged servers on which tests run.
- privileged = filter(lambda loc: "privileged" in loc.options, locations)
- for (i, l) in itertools.izip(itertools.count(1), privileged):
- part = """
+ # Grant God-power to all the privileged servers on which tests run.
+ privileged = filter(lambda loc: "privileged" in loc.options, locations)
+ for (i, l) in itertools.izip(itertools.count(1), privileged):
+ part = """
user_pref("capability.principal.codebase.p%(i)d.granted",
"UniversalXPConnect UniversalBrowserRead UniversalBrowserWrite \
UniversalPreferencesRead UniversalPreferencesWrite \
UniversalFileRead");
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) }
- prefs.append(part)
+ prefs.append(part)
- # We need to proxy every server but the primary one.
- origins = ["'%s://%s:%s'" % (l.scheme, l.host, l.port)
- for l in filter(lambda l: "primary" not in l.options, locations)]
- origins = ", ".join(origins)
+ # We need to proxy every server but the primary one.
+ origins = ["'%s://%s:%s'" % (l.scheme, l.host, l.port)
+ for l in filter(lambda l: "primary" not in l.options, locations)]
+ origins = ", ".join(origins)
- pacURL = """data:text/plain,
+ pacURL = """data:text/plain,
function FindProxyForURL(url, host)
{
var origins = [%(origins)s];
var regex = new RegExp('^([a-z][-a-z0-9+.]*)' +
'://' +
'(?:[^/@]*@)?' +
'(.*?)' +
'(?::(\\\\\\\\d+))?/');
@@ -335,401 +324,387 @@ function FindProxyForURL(url, host)
if (origins.indexOf(origin) < 0)
return 'DIRECT';
if (isHttp)
return 'PROXY 127.0.0.1:8888';
if (isHttps)
return 'PROXY 127.0.0.1:4443';
return 'DIRECT';
}""" % { "origins": origins }
- pacURL = "".join(pacURL.splitlines())
+ pacURL = "".join(pacURL.splitlines())
- part = """
+ part = """
user_pref("network.proxy.type", 2);
user_pref("network.proxy.autoconfig_url", "%(pacURL)s");
user_pref("camino.use_system_proxy_settings", false); // Camino-only, harmless to others
""" % {"pacURL": pacURL}
+ prefs.append(part)
+
+ for v in 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])
prefs.append(part)
- for v in 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])
- prefs.append(part)
+ # write the preferences
+ prefsFile = open(profileDir + "/" + "user.js", "a")
+ prefsFile.write("".join(prefs))
+ prefsFile.close()
- # write the preferences
- prefsFile = open(profileDir + "/" + "user.js", "a")
- prefsFile.write("".join(prefs))
- prefsFile.close()
-
- def addCommonOptions(self, parser):
- "Adds command-line options which are common to mochitest and reftest."
+def addExtraCommonOptions(parser):
+ "Adds command-line options which are common to mochitest and reftest."
- parser.add_option("--setpref",
- action = "append", type = "string",
- default = [],
- dest = "extraPrefs", metavar = "PREF=VALUE",
- help = "defines an extra user preference")
+ parser.add_option("--setpref",
+ action = "append", type = "string",
+ default = [],
+ dest = "extraPrefs", metavar = "PREF=VALUE",
+ help = "defines an extra user preference")
- def fillCertificateDB(self, profileDir, certPath, utilityPath, xrePath):
- pwfilePath = os.path.join(profileDir, ".crtdbpw")
+def fillCertificateDB(profileDir, certPath, utilityPath, xrePath):
+ pwfilePath = os.path.join(profileDir, ".crtdbpw")
- pwfile = open(pwfilePath, "w")
- pwfile.write("\n")
- pwfile.close()
+ pwfile = open(pwfilePath, "w")
+ pwfile.write("\n")
+ pwfile.close()
- # Create head of the ssltunnel configuration file
- sslTunnelConfigPath = os.path.join(profileDir, "ssltunnel.cfg")
- sslTunnelConfig = open(sslTunnelConfigPath, "w")
+ # Create head of the ssltunnel configuration file
+ sslTunnelConfigPath = os.path.join(profileDir, "ssltunnel.cfg")
+ sslTunnelConfig = open(sslTunnelConfigPath, "w")
- 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("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")
- # Configure automatic certificate and bind custom certificates, client authentication
- locations = self.readLocations()
- locations.pop(0)
- for loc in locations:
- if loc.scheme == "https" and "nocert" not in loc.options:
- customCertRE = re.compile("^cert=(?P<nickname>[0-9a-zA-Z_ ]+)")
- clientAuthRE = re.compile("^clientauth=(?P<clientauth>[a-z]+)")
- for option in loc.options:
- match = customCertRE.match(option)
- if match:
- customcert = match.group("nickname");
- sslTunnelConfig.write("listen:%s:%s:4443:%s\n" %
- (loc.host, loc.port, customcert))
+ # Configure automatic certificate and bind custom certificates, client authentication
+ locations = readLocations()
+ locations.pop(0)
+ for loc in locations:
+ if loc.scheme == "https" and "nocert" not in loc.options:
+ customCertRE = re.compile("^cert=(?P<nickname>[0-9a-zA-Z_ ]+)")
+ clientAuthRE = re.compile("^clientauth=(?P<clientauth>[a-z]+)")
+ for option in loc.options:
+ match = customCertRE.match(option)
+ if match:
+ customcert = match.group("nickname");
+ sslTunnelConfig.write("listen:%s:%s:4443:%s\n" %
+ (loc.host, loc.port, customcert))
- match = clientAuthRE.match(option)
- if match:
- clientauth = match.group("clientauth");
- sslTunnelConfig.write("clientauth:%s:%s:4443:%s\n" %
- (loc.host, loc.port, clientauth))
+ match = clientAuthRE.match(option)
+ if match:
+ clientauth = match.group("clientauth");
+ sslTunnelConfig.write("clientauth:%s:%s:4443:%s\n" %
+ (loc.host, loc.port, clientauth))
- sslTunnelConfig.close()
+ sslTunnelConfig.close()
- # Pre-create the certification database for the profile
- env = self.environment(xrePath = xrePath)
- certutil = os.path.join(utilityPath, "certutil" + self.BIN_SUFFIX)
- pk12util = os.path.join(utilityPath, "pk12util" + self.BIN_SUFFIX)
+ # Pre-create the certification database for the profile
+ env = environment(xrePath = xrePath)
+ certutil = os.path.join(utilityPath, "certutil" + BIN_SUFFIX)
+ pk12util = os.path.join(utilityPath, "pk12util" + BIN_SUFFIX)
- status = self.Process([certutil, "-N", "-d", profileDir, "-f", pwfilePath], env = env).wait()
- if status != 0:
- return status
+ status = Process([certutil, "-N", "-d", profileDir, "-f", pwfilePath], env = env).wait()
+ if status != 0:
+ return status
- # Walk the cert directory and add custom CAs and client certs
- files = os.listdir(certPath)
- for item in files:
- root, ext = os.path.splitext(item)
- if ext == ".ca":
- trustBits = "CT,,"
- if root.endswith("-object"):
- trustBits = "CT,,CT"
- self.Process([certutil, "-A", "-i", os.path.join(certPath, item),
- "-d", profileDir, "-f", pwfilePath, "-n", root, "-t", trustBits],
- env = env).wait()
- if ext == ".client":
- self.Process([pk12util, "-i", os.path.join(certPath, item), "-w",
- pwfilePath, "-d", profileDir],
- env = env).wait()
-
- os.unlink(pwfilePath)
- return 0
+ # Walk the cert directory and add custom CAs and client certs
+ files = os.listdir(certPath)
+ for item in files:
+ root, ext = os.path.splitext(item)
+ if ext == ".ca":
+ trustBits = "CT,,"
+ if root.endswith("-object"):
+ trustBits = "CT,,CT"
+ Process([certutil, "-A", "-i", os.path.join(certPath, item),
+ "-d", profileDir, "-f", pwfilePath, "-n", root, "-t", trustBits],
+ env = env).wait()
+ if ext == ".client":
+ Process([pk12util, "-i", os.path.join(certPath, item), "-w",
+ pwfilePath, "-d", profileDir],
+ env = env).wait()
- def environment(self, env = None, xrePath = None, crashreporter = True):
- if xrePath == None:
- xrePath = self.DIST_BIN
- if env == None:
- env = dict(os.environ)
+ os.unlink(pwfilePath)
+ return 0
+
+def environment(env = None, xrePath = DIST_BIN, crashreporter = True):
+ if env == None:
+ env = dict(os.environ)
- ldLibraryPath = os.path.abspath(os.path.join(self.SCRIPT_DIR, xrePath))
- if self.UNIXISH or self.IS_MAC:
- envVar = "LD_LIBRARY_PATH"
- if self.IS_MAC:
- envVar = "DYLD_LIBRARY_PATH"
- else: # unixish
- env['MOZILLA_FIVE_HOME'] = xrePath
- if envVar in env:
- ldLibraryPath = ldLibraryPath + ":" + env[envVar]
- env[envVar] = ldLibraryPath
- elif self.IS_WIN32:
- env["PATH"] = env["PATH"] + ";" + ldLibraryPath
+ ldLibraryPath = os.path.abspath(os.path.join(SCRIPT_DIR, xrePath))
+ if UNIXISH or IS_MAC:
+ envVar = "LD_LIBRARY_PATH"
+ if IS_MAC:
+ envVar = "DYLD_LIBRARY_PATH"
+ else: # unixish
+ env['MOZILLA_FIVE_HOME'] = xrePath
+ if envVar in env:
+ ldLibraryPath = ldLibraryPath + ":" + env[envVar]
+ env[envVar] = ldLibraryPath
+ elif IS_WIN32:
+ env["PATH"] = env["PATH"] + ";" + ldLibraryPath
- if crashreporter:
- env['MOZ_CRASHREPORTER_NO_REPORT'] = '1'
- env['MOZ_CRASHREPORTER'] = '1'
- else:
- env['MOZ_CRASHREPORTER_DISABLE'] = '1'
+ if crashreporter:
+ env['MOZ_CRASHREPORTER_NO_REPORT'] = '1'
+ env['MOZ_CRASHREPORTER'] = '1'
+ else:
+ env['MOZ_CRASHREPORTER_DISABLE'] = '1'
- env['GNOME_DISABLE_CRASH_DIALOG'] = '1'
- env['XRE_NO_WINDOWS_CRASH_DIALOG'] = '1'
- return env
+ env['GNOME_DISABLE_CRASH_DIALOG'] = "1"
+ env['XRE_NO_WINDOWS_CRASH_DIALOG'] = '1'
+ return env
- if IS_WIN32:
- ctypes = __import__('ctypes')
- wintypes = __import__('ctypes.wintypes')
- time = __import__('time')
- msvcrt = __import__('msvcrt')
- PeekNamedPipe = ctypes.windll.kernel32.PeekNamedPipe
- GetLastError = ctypes.windll.kernel32.GetLastError
+if IS_WIN32:
+ import ctypes, ctypes.wintypes, time, msvcrt
+ PeekNamedPipe = ctypes.windll.kernel32.PeekNamedPipe
+ GetLastError = ctypes.windll.kernel32.GetLastError
- def readWithTimeout(self, f, timeout):
- """Try to read a line of output from the file object |f|.
- |f| must be a pipe, like the |stdout| member of a subprocess.Popen
- object created with stdout=PIPE. If no output
- is received within |timeout| seconds, return a blank line.
- Returns a tuple (line, did_timeout), where |did_timeout| is True
- if the read timed out, and False otherwise."""
- if timeout is None:
- # shortcut to allow callers to pass in "None" for no timeout.
+ def readWithTimeout(f, timeout):
+ """Try to read a line of output from the file object |f|.
+ |f| must be a pipe, like the |stdout| member of a subprocess.Popen
+ object created with stdout=PIPE. If no output
+ is received within |timeout| seconds, return a blank line.
+ Returns a tuple (line, did_timeout), where |did_timeout| is True
+ if the read timed out, and False otherwise."""
+ if timeout is None:
+ # shortcut to allow callers to pass in "None" for no timeout.
+ return (f.readline(), False)
+ x = msvcrt.get_osfhandle(f.fileno())
+ l = ctypes.c_long()
+ done = time.time() + timeout
+ while time.time() < done:
+ if PeekNamedPipe(x, None, 0, None, ctypes.byref(l), None) == 0:
+ err = GetLastError()
+ if err == 38 or err == 109: # ERROR_HANDLE_EOF || ERROR_BROKEN_PIPE
+ return ('', False)
+ else:
+ log.error("readWithTimeout got error: %d", err)
+ if l > 0:
+ # we're assuming that the output is line-buffered,
+ # which is not unreasonable
return (f.readline(), False)
- x = self.msvcrt.get_osfhandle(f.fileno())
- l = self.ctypes.c_long()
- done = self.time.time() + timeout
- while self.time.time() < done:
- if self.PeekNamedPipe(x, None, 0, None, self.ctypes.byref(l), None) == 0:
- err = self.GetLastError()
- if err == 38 or err == 109: # ERROR_HANDLE_EOF || ERROR_BROKEN_PIPE
- return ('', False)
- else:
- log.error("readWithTimeout got error: %d", err)
- if l > 0:
- # we're assuming that the output is line-buffered,
- # which is not unreasonable
- return (f.readline(), False)
- self.time.sleep(0.01)
- return ('', True)
+ time.sleep(0.01)
+ return ('', True)
- def isPidAlive(self, pid):
- STILL_ACTIVE = 259
- PROCESS_QUERY_LIMITED_INFORMATION = 0x1000
- pHandle = self.ctypes.windll.kernel32.OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, 0, pid)
- if not pHandle:
- return False
- pExitCode = self.wintypes.DWORD()
- self.ctypes.windll.kernel32.GetExitCodeProcess(pHandle, self.ctypes.byref(pExitCode))
- self.ctypes.windll.kernel32.CloseHandle(pHandle)
- if (pExitCode.value == STILL_ACTIVE):
- return True
- else:
- return False
+ def isPidAlive(pid):
+ STILL_ACTIVE = 259
+ PROCESS_QUERY_LIMITED_INFORMATION = 0x1000
+ pHandle = ctypes.windll.kernel32.OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, 0, pid)
+ if not pHandle:
+ return False
+ pExitCode = ctypes.wintypes.DWORD()
+ ctypes.windll.kernel32.GetExitCodeProcess(pHandle, ctypes.byref(pExitCode))
+ ctypes.windll.kernel32.CloseHandle(pHandle)
+ if (pExitCode.value == STILL_ACTIVE):
+ return True
+ else:
+ return False
- def killPid(self, pid):
- PROCESS_TERMINATE = 0x0001
- pHandle = self.ctypes.windll.kernel32.OpenProcess(PROCESS_TERMINATE, 0, pid)
- if not pHandle:
- return
- success = self.ctypes.windll.kernel32.TerminateProcess(pHandle, 1)
- self.ctypes.windll.kernel32.CloseHandle(pHandle)
+ def killPid(pid):
+ PROCESS_TERMINATE = 0x0001
+ pHandle = ctypes.windll.kernel32.OpenProcess(PROCESS_TERMINATE, 0, pid)
+ if not pHandle:
+ return
+ success = ctypes.windll.kernel32.TerminateProcess(pHandle, 1)
+ ctypes.windll.kernel32.CloseHandle(pHandle)
- else:
- errno = __import__('errno')
-
- def readWithTimeout(self, f, timeout):
- """Try to read a line of output from the file object |f|. If no output
- is received within |timeout| seconds, return a blank line.
- Returns a tuple (line, did_timeout), where |did_timeout| is True
- if the read timed out, and False otherwise."""
- (r, w, e) = select.select([f], [], [], timeout)
- if len(r) == 0:
- return ('', True)
- return (f.readline(), False)
+else:
+ import errno
- def isPidAlive(self, pid):
- try:
- # kill(pid, 0) checks for a valid PID without actually sending a signal
- # The method throws OSError if the PID is invalid, which we catch below.
- os.kill(pid, 0)
+ def readWithTimeout(f, timeout):
+ """Try to read a line of output from the file object |f|. If no output
+ is received within |timeout| seconds, return a blank line.
+ Returns a tuple (line, did_timeout), where |did_timeout| is True
+ if the read timed out, and False otherwise."""
+ (r, w, e) = select.select([f], [], [], timeout)
+ if len(r) == 0:
+ return ('', True)
+ return (f.readline(), False)
- # Wait on it to see if it's a zombie. This can throw OSError.ECHILD if
- # the process terminates before we get to this point.
- wpid, wstatus = os.waitpid(pid, os.WNOHANG)
- if wpid == 0:
- return True
-
- return False
- except OSError, err:
- # Catch the errors we might expect from os.kill/os.waitpid,
- # and re-raise any others
- if err.errno == self.errno.ESRCH or err.errno == self.errno.ECHILD:
- return False
- raise
+ def isPidAlive(pid):
+ try:
+ # kill(pid, 0) checks for a valid PID without actually sending a signal
+ # The method throws OSError if the PID is invalid, which we catch below.
+ os.kill(pid, 0)
- def killPid(self, pid):
- os.kill(pid, signal.SIGKILL)
+ # Wait on it to see if it's a zombie. This can throw OSError.ECHILD if
+ # the process terminates before we get to this point.
+ wpid, wstatus = os.waitpid(pid, os.WNOHANG)
+ if wpid == 0:
+ return True
- def triggerBreakpad(self, proc, utilityPath):
- """Attempt to kill this process in a way that triggers Breakpad crash
- reporting, if we know how for this platform. Otherwise just .kill() it."""
- if self.CRASHREPORTER:
- if self.UNIXISH:
- # SEGV will get picked up by Breakpad's signal handler
- os.kill(proc.pid, signal.SIGSEGV)
- return
- elif self.IS_WIN32:
- # We should have a "crashinject" program in our utility path
- crashinject = os.path.normpath(os.path.join(utilityPath, "crashinject.exe"))
- if os.path.exists(crashinject) and subprocess.Popen([crashinject, str(proc.pid)]).wait() == 0:
- return
- #TODO: kill the process such that it triggers Breakpad on OS X (bug 525296)
- self.log.info("Can't trigger Breakpad, just killing process")
- proc.kill()
+ return False
+ except OSError, err:
+ # Catch the errors we might expect from os.kill/os.waitpid,
+ # and re-raise any others
+ if err.errno == errno.ESRCH or err.errno == errno.ECHILD:
+ return False
+ raise
+
+ def killPid(pid):
+ os.kill(pid, signal.SIGKILL)
- def runApp(self, testURL, env, app, profileDir, extraArgs,
- runSSLTunnel = False, utilityPath = None,
- xrePath = None, certPath = None,
- debuggerInfo = None, symbolsPath = None,
- timeout = -1, maxTime = None):
- """
- Run the app, log the duration it took to execute, return the status code.
- Kills the app if it runs for longer than |maxTime| seconds, or outputs nothing for |timeout| seconds.
- """
+def triggerBreakpad(proc, utilityPath):
+ """Attempt to kill this process in a way that triggers Breakpad crash
+ reporting, if we know how for this platform. Otherwise just .kill() it."""
+ if CRASHREPORTER:
+ if UNIXISH:
+ # SEGV will get picked up by Breakpad's signal handler
+ os.kill(proc.pid, signal.SIGSEGV)
+ return
+ elif IS_WIN32:
+ # We should have a "crashinject" program in our utility path
+ crashinject = os.path.normpath(os.path.join(utilityPath, "crashinject.exe"))
+ if os.path.exists(crashinject) and subprocess.Popen([crashinject, str(proc.pid)]).wait() == 0:
+ return
+ #TODO: kill the process such that it triggers Breakpad on OS X (bug 525296)
+ log.info("Can't trigger Breakpad, just killing process")
+ proc.kill()
- if utilityPath == None:
- utilityPath = self.DIST_BIN
- if xrePath == None:
- xrePath = self.DIST_BIN
- if certPath == None:
- certPath = self.CERTS_SRC_DIR
- if timeout == -1:
- timeout = self.DEFAULT_TIMEOUT
+###############
+# RUN THE APP #
+###############
- # copy env so we don't munge the caller's environment
- env = dict(env);
- env["NO_EM_RESTART"] = "1"
- tmpfd, processLog = tempfile.mkstemp(suffix='pidlog')
- os.close(tmpfd)
- env["MOZ_PROCESS_LOG"] = processLog
+def runApp(testURL, env, app, profileDir, extraArgs,
+ runSSLTunnel = False, utilityPath = DIST_BIN,
+ xrePath = DIST_BIN, certPath = CERTS_SRC_DIR,
+ debuggerInfo = None, symbolsPath = None,
+ timeout = DEFAULT_TIMEOUT, maxTime = None):
+ """
+ Run the app, log the duration it took to execute, return the status code.
+ Kills the app if it runs for longer than |maxTime| seconds, or outputs nothing for |timeout| seconds.
+ """
- if self.IS_TEST_BUILD and runSSLTunnel:
- # create certificate database for the profile
- certificateStatus = self.fillCertificateDB(profileDir, certPath, utilityPath, xrePath)
- if certificateStatus != 0:
- self.log.info("TEST-UNEXPECTED FAIL | automation.py | Certificate integration failed")
- return certificateStatus
+ # copy env so we don't munge the caller's environment
+ env = dict(env);
+ env["NO_EM_RESTART"] = "1"
+ tmpfd, processLog = tempfile.mkstemp(suffix='pidlog')
+ os.close(tmpfd)
+ env["MOZ_PROCESS_LOG"] = processLog
- # start ssltunnel to provide https:// URLs capability
- ssltunnel = os.path.join(utilityPath, "ssltunnel" + self.BIN_SUFFIX)
- ssltunnelProcess = self.Process([ssltunnel,
- os.path.join(profileDir, "ssltunnel.cfg")],
- env = self.environment(xrePath = xrePath))
- self.log.info("INFO | automation.py | SSL tunnel pid: %d", ssltunnelProcess.pid)
+ if IS_TEST_BUILD and runSSLTunnel:
+ # create certificate database for the profile
+ certificateStatus = fillCertificateDB(profileDir, certPath, utilityPath, xrePath)
+ if certificateStatus != 0:
+ log.info("TEST-UNEXPECTED FAIL | automation.py | Certificate integration failed")
+ return certificateStatus
- # now run with the profile we created
- cmd = app
- if self.IS_MAC and not self.IS_CAMINO and not cmd.endswith("-bin"):
- cmd += "-bin"
- cmd = os.path.abspath(cmd)
+ # start ssltunnel to provide https:// URLs capability
+ ssltunnel = os.path.join(utilityPath, "ssltunnel" + BIN_SUFFIX)
+ ssltunnelProcess = Process([ssltunnel, os.path.join(profileDir, "ssltunnel.cfg")], env = environment(xrePath = xrePath))
+ log.info("INFO | automation.py | SSL tunnel pid: %d", ssltunnelProcess.pid)
- args = []
+ # now run with the profile we created
+ cmd = app
+ if IS_MAC and not IS_CAMINO and not cmd.endswith("-bin"):
+ cmd += "-bin"
+ cmd = os.path.abspath(cmd)
+
+ args = []
- if debuggerInfo:
- args.extend(debuggerInfo["args"])
- args.append(cmd)
- cmd = os.path.abspath(debuggerInfo["path"])
+ if debuggerInfo:
+ args.extend(debuggerInfo["args"])
+ args.append(cmd)
+ cmd = os.path.abspath(debuggerInfo["path"])
+
+ if IS_MAC:
+ args.append("-foreground")
- if self.IS_MAC:
- args.append("-foreground")
+ if IS_CYGWIN:
+ profileDirectory = commands.getoutput("cygpath -w \"" + profileDir + "/\"")
+ else:
+ profileDirectory = profileDir + "/"
- if self.IS_CYGWIN:
- profileDirectory = commands.getoutput("cygpath -w \"" + profileDir + "/\"")
+ args.extend(("-no-remote", "-profile", profileDirectory))
+ if testURL is not None:
+ if IS_CAMINO:
+ args.extend(("-url", testURL))
else:
- profileDirectory = profileDir + "/"
+ args.append((testURL))
+ args.extend(extraArgs)
- args.extend(("-no-remote", "-profile", profileDirectory))
- if testURL is not None:
- if self.IS_CAMINO:
- args.extend(("-url", testURL))
- else:
- args.append((testURL))
- args.extend(extraArgs)
+ startTime = datetime.now()
- startTime = datetime.now()
+ # Don't redirect stdout and stderr if an interactive debugger is attached
+ if debuggerInfo and debuggerInfo["interactive"]:
+ outputPipe = None
+ else:
+ outputPipe = subprocess.PIPE
- # Don't redirect stdout and stderr if an interactive debugger is attached
- if debuggerInfo and debuggerInfo["interactive"]:
- outputPipe = None
- else:
- outputPipe = subprocess.PIPE
-
- proc = self.Process([cmd] + args,
- env = self.environment(env, xrePath = xrePath,
+ proc = Process([cmd] + args,
+ env = environment(env, xrePath = xrePath,
crashreporter = not debuggerInfo),
stdout = outputPipe,
stderr = subprocess.STDOUT)
- self.log.info("INFO | automation.py | Application pid: %d", proc.pid)
+ log.info("INFO | automation.py | Application pid: %d", proc.pid)
- stackFixerProcess = None
- didTimeout = False
- if outputPipe is None:
- self.log.info("TEST-INFO: Not logging stdout or stderr due to debugger connection")
- else:
- logsource = proc.stdout
- if self.IS_DEBUG_BUILD:
- stackFixerCommand = None
- if self.IS_MAC:
- stackFixerCommand = "fix-macosx-stack.pl"
- elif self.IS_LINUX:
- stackFixerCommand = "fix-linux-stack.pl"
- if stackFixerCommand is not None:
- stackFixerProcess = self.Process([self.PERL, os.path.join(utilityPath, stackFixerCommand)],
- stdin=logsource,
- stdout=subprocess.PIPE)
- logsource = stackFixerProcess.stdout
+ stackFixerProcess = None
+ didTimeout = False
+ if outputPipe is None:
+ log.info("TEST-INFO: Not logging stdout or stderr due to debugger connection")
+ else:
+ logsource = proc.stdout
+ if IS_DEBUG_BUILD:
+ stackFixerCommand = None
+ if IS_MAC:
+ stackFixerCommand = "fix-macosx-stack.pl"
+ elif IS_LINUX:
+ stackFixerCommand = "fix-linux-stack.pl"
+ if stackFixerCommand is not None:
+ stackFixerProcess = Process([PERL, os.path.join(utilityPath, stackFixerCommand)], stdin=logsource, stdout=subprocess.PIPE)
+ logsource = stackFixerProcess.stdout
- (line, didTimeout) = self.readWithTimeout(logsource, timeout)
- hitMaxTime = False
- while line != "" and not didTimeout:
- self.log.info(line.rstrip())
- (line, didTimeout) = self.readWithTimeout(logsource, timeout)
- if not hitMaxTime and maxTime and datetime.now() - startTime > timedelta(seconds = maxTime):
- # Kill the application, but continue reading from stack fixer so as not to deadlock on stackFixerProcess.wait().
- hitMaxTime = True
- self.log.info("TEST-UNEXPECTED-FAIL | automation.py | application ran for longer than allowed maximum time of %d seconds", int(maxTime))
- self.triggerBreakpad(proc, utilityPath)
- if didTimeout:
- self.log.info("TEST-UNEXPECTED-FAIL | automation.py | application timed out after %d seconds with no output", int(timeout))
- self.triggerBreakpad(proc, utilityPath)
+ (line, didTimeout) = readWithTimeout(logsource, timeout)
+ hitMaxTime = False
+ while line != "" and not didTimeout:
+ log.info(line.rstrip())
+ (line, didTimeout) = readWithTimeout(logsource, timeout)
+ if not hitMaxTime and maxTime and datetime.now() - startTime > timedelta(seconds = maxTime):
+ # Kill the application, but continue reading from stack fixer so as not to deadlock on stackFixerProcess.wait().
+ hitMaxTime = True
+ log.info("TEST-UNEXPECTED-FAIL | automation.py | application ran for longer than allowed maximum time of %d seconds", int(maxTime))
+ triggerBreakpad(proc, utilityPath)
+ if didTimeout:
+ log.info("TEST-UNEXPECTED-FAIL | automation.py | application timed out after %d seconds with no output", int(timeout))
+ triggerBreakpad(proc, utilityPath)
- status = proc.wait()
- if status != 0 and not didTimeout and not hitMaxTime:
- self.log.info("TEST-UNEXPECTED-FAIL | automation.py | Exited with code %d during test run", status)
- if stackFixerProcess is not None:
- fixerStatus = stackFixerProcess.wait()
- if fixerStatus != 0 and not didTimeout and not hitMaxTime:
- self.log.info("TEST-UNEXPECTED-FAIL | automation.py | Stack fixer process exited with code %d during test run", fixerStatus)
- self.log.info("INFO | automation.py | Application ran for: %s", str(datetime.now() - startTime))
+ status = proc.wait()
+ if status != 0 and not didTimeout and not hitMaxTime:
+ log.info("TEST-UNEXPECTED-FAIL | automation.py | Exited with code %d during test run", status)
+ if stackFixerProcess is not None:
+ fixerStatus = stackFixerProcess.wait()
+ if fixerStatus != 0 and not didTimeout and not hitMaxTime:
+ log.info("TEST-UNEXPECTED-FAIL | automation.py | Stack fixer process exited with code %d during test run", fixerStatus)
+ log.info("INFO | automation.py | Application ran for: %s", str(datetime.now() - startTime))
- # Do a final check for zombie child processes.
- if not os.path.exists(processLog):
- self.log.info('INFO | automation.py | PID log not found: %s', processLog)
- else:
- self.log.info('INFO | automation.py | Reading PID log: %s', processLog)
- processList = []
- pidRE = re.compile(r'launched child process (\d+)$')
- processLogFD = open(processLog)
- for line in processLogFD:
- self.log.info(line.rstrip())
- m = pidRE.search(line)
- if m:
- processList.append(int(m.group(1)))
- processLogFD.close()
+ # Do a final check for zombie child processes.
+ if not os.path.exists(processLog):
+ log.info('INFO | automation.py | PID log not found: %s', processLog)
+ else:
+ log.info('INFO | automation.py | Reading PID log: %s', processLog)
+ processList = []
+ pidRE = re.compile(r'launched child process (\d+)$')
+ processLogFD = open(processLog)
+ for line in processLogFD:
+ log.info(line.rstrip())
+ m = pidRE.search(line)
+ if m:
+ processList.append(int(m.group(1)))
+ processLogFD.close()
- for processPID in processList:
- self.log.info("INFO | automation.py | Checking for orphan process with PID: %d", processPID)
- if self.isPidAlive(processPID):
- self.log.info("TEST-UNEXPECTED-FAIL | automation.py | child process %d still alive after shutdown", processPID)
- self.killPid(processPID)
+ for processPID in processList:
+ log.info("INFO | automation.py | Checking for orphan process with PID: %d", processPID)
+ if isPidAlive(processPID):
+ log.info("TEST-UNEXPECTED-FAIL | automation.py | child process %d still alive after shutdown", processPID)
+ killPid(processPID)
- if self.automationutils.checkForCrashes(os.path.join(profileDir, "minidumps"), symbolsPath):
- status = -1
+ if checkForCrashes(os.path.join(profileDir, "minidumps"), symbolsPath):
+ status = -1
- if os.path.exists(processLog):
- os.unlink(processLog)
+ if os.path.exists(processLog):
+ os.unlink(processLog)
- if self.IS_TEST_BUILD and runSSLTunnel:
- ssltunnelProcess.kill()
+ if IS_TEST_BUILD and runSSLTunnel:
+ ssltunnelProcess.kill()
- return status
+ return status
--- a/build/leaktest.py.in
+++ b/build/leaktest.py.in
@@ -41,29 +41,28 @@
import SimpleHTTPServer
import SocketServer
import threading
import os
import sys
import logging
from getopt import getopt
-from automation import 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:
automation.log.addHandler(logging.FileHandler(opts[0][1], "w"))
except:
automation.log.info("Unable to open logfile " + opts[0][1] + \
"ONLY logging to stdout.")
--- a/build/pgo/genpgocert.py.in
+++ b/build/pgo/genpgocert.py.in
@@ -31,29 +31,27 @@
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
-from automation import Automation
+import automation
import os
import re
import shutil
import sys
#expand DIST_BIN = __XPC_BIN_PATH__
#expand BIN_SUFFIX = __BIN_SUFFIX__
#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$"),
re.compile("^secmod\.db$")
]
def unlinkDbFiles(path):
for root, dirs, files in os.walk(path):
--- a/build/pgo/profileserver.py.in
+++ b/build/pgo/profileserver.py.in
@@ -41,28 +41,27 @@
import SimpleHTTPServer
import SocketServer
import socket
import threading
import os
import sys
import shutil
from datetime import datetime
-from automation import 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, "./pgoprofile"))
os.chdir(SCRIPT_DIR)
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
t.start()
automation.initializeProfile(PROFILE_DIRECTORY)
browserEnv = automation.environment()
browserEnv["XPCOM_DEBUG_BREAK"] = "warn"
--- a/layout/tools/reftest/runreftest.py
+++ b/layout/tools/reftest/runreftest.py
@@ -39,138 +39,64 @@
"""
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)
-from automation import Automation
+import automation
from automationutils import *
from optparse import OptionParser
from tempfile import mkdtemp
-class RefTest(object):
-
- oldcwd = os.getcwd()
-
- def __init__(self, automation):
- self.automation = automation
- os.chdir(SCRIPT_DIRECTORY)
+oldcwd = os.getcwd()
+os.chdir(SCRIPT_DIRECTORY)
- 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 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')
+def getFullPath(path):
+ "Get an absolute path relative to oldcwd."
+ return os.path.normpath(os.path.join(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(options, profileDir):
+ "Sets up a profile for reftest."
- # 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)
+ # 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')
- # 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)
+ 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()
- # 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.addCommonOptions(parser)
+ addCommonOptions(parser, defaults=dict(zip(automation.__all__, [getattr(automation, x) for x in automation.__all__])))
+ automation.addExtraCommonOptions(parser)
parser.add_option("--appname",
action = "store", type = "string", dest = "app",
default = os.path.join(SCRIPT_DIRECTORY, automation.DEFAULT_APP),
help = "absolute path to application, overriding default")
parser.add_option("--extra-profile-file",
action = "append", dest = "extraProfileFiles",
default = [],
help = "copy specified files/dirs to testing profile")
@@ -187,33 +113,95 @@ def main():
"than the given number")
parser.add_option("--utility-path",
action = "store", type = "string", dest = "utilityPath",
default = automation.DIST_BIN,
help = "absolute path to directory containing utility "
"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 = reftest.getFullPath(options.app)
+ options.app = 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 = reftest.getFullPath(options.xrePath)
+ options.xrePath = getFullPath(options.xrePath)
if options.symbolsPath:
- options.symbolsPath = reftest.getFullPath(options.symbolsPath)
- options.utilityPath = reftest.getFullPath(options.utilityPath)
+ 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.")
- sys.exit(reftest.runTests(args[0], options))
-
+ # 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)
+
if __name__ == "__main__":
main()
--- a/testing/mochitest/runtests.py.in
+++ b/testing/mochitest/runtests.py.in
@@ -47,55 +47,84 @@ import optparse
import os
import os.path
import sys
import time
import shutil
from urllib import quote_plus as encodeURIComponent
import urllib2
import commands
-from automation import 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 #
#######################
class MochitestOptions(optparse.OptionParser):
"""Parses Mochitest commandline options."""
- def __init__(self, automation, scriptdir, **kwargs):
- self._automation = automation
+ def __init__(self, **kwargs):
optparse.OptionParser.__init__(self, **kwargs)
defaults = {}
- # 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)
+ # 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)
self.add_option("--close-when-done",
action = "store_true", dest = "closeWhenDone",
help = "close the application when tests are done running")
defaults["closeWhenDone"] = False
self.add_option("--appname",
action = "store", type = "string", dest = "app",
help = "absolute path to application, overriding default")
- defaults["app"] = os.path.join(scriptdir, self._automation.DEFAULT_APP)
+ defaults["app"] = os.path.join(SCRIPT_DIRECTORY, 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"] = self._automation.DIST_BIN
+ defaults["utilityPath"] = 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"] = self._automation.CERTS_SRC_DIR
+ defaults["certPath"] = automation.CERTS_SRC_DIR
self.add_option("--log-file",
action = "store", type = "string",
dest = "logFile", metavar = "FILE",
help = "file to which logging occurs")
defaults["logFile"] = ""
self.add_option("--autorun",
@@ -215,49 +244,48 @@ See <http://mochikit.com/doc/html/MochiK
#######################
# HTTP SERVER SUPPORT #
#######################
class MochitestServer:
"Web server used to serve Mochitests, for closer fidelity to the real web."
- def __init__(self, automation, options, profileDir):
- self._automation = automation
+ def __init__(self, options):
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 = self._automation.environment(xrePath = self._xrePath)
+ env = automation.environment(xrePath = self._xrePath)
env["XPCOM_DEBUG_BREAK"] = "warn"
- if self._automation.IS_WIN32:
+ if automation.IS_WIN32:
env["PATH"] = env["PATH"] + ";" + self._xrePath
args = ["-g", self._xrePath,
"-v", "170",
"-f", "./" + "httpd.js",
"-f", "./" + "server.js"]
xpcshell = os.path.join(self._utilityPath,
- "xpcshell" + self._automation.BIN_SUFFIX)
- self._process = self._automation.Process([xpcshell] + args, env = env)
+ "xpcshell" + automation.BIN_SUFFIX)
+ self._process = automation.Process([xpcshell] + args, env = env)
pid = self._process.pid
if pid < 0:
print "Error starting server."
sys.exit(2)
- self._automation.log.info("INFO | runtests.py | Server pid: %d", pid)
+ automation.log.info("INFO | runtests.py | Server pid: %d", pid)
+
def ensureReady(self, timeout):
assert timeout >= 0
- aliveFile = os.path.join(self._profileDir, "server_alive.txt")
+ aliveFile = os.path.join(PROFILE_DIRECTORY, "server_alive.txt")
i = 0
while i < timeout:
if os.path.exists(aliveFile):
break
time.sleep(1)
i += 1
else:
print "Timed out while waiting for server startup."
@@ -268,301 +296,282 @@ class MochitestServer:
try:
c = urllib2.urlopen(SERVER_SHUTDOWN_URL)
c.read()
c.close()
self._process.wait()
except:
self._process.kill()
-
-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"
+def getFullPath(path):
+ "Get an absolute path relative to oldcwd."
+ return os.path.normpath(os.path.join(oldcwd, os.path.expanduser(path)))
- # 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)
+#################
+# MAIN FUNCTION #
+#################
def main():
- automation = Automation()
- mochitest = Mochitest(automation)
- parser = MochitestOptions(automation, mochitest.SCRIPT_DIRECTORY)
+ parser = MochitestOptions()
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")
+ 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.xrePath = getFullPath(options.xrePath)
- options.app = mochitest.getFullPath(options.app)
+ options.app = 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 = mochitest.getFullPath(options.utilityPath)
- options.certPath = mochitest.getFullPath(options.certPath)
+ options.utilityPath = getFullPath(options.utilityPath)
+ options.certPath = getFullPath(options.certPath)
if options.symbolsPath:
- options.symbolsPath = mochitest.getFullPath(options.symbolsPath)
+ options.symbolsPath = 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)
- sys.exit(mochitest.runTests(options))
+ # 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 #
+#########
if __name__ == "__main__":
main()
--- a/testing/xpcshell/runxpcshelltests.py
+++ b/testing/xpcshell/runxpcshelltests.py
@@ -17,17 +17,16 @@
#
# The Initial Developer of the Original Code is The Mozilla Foundation
# Portions created by the Initial Developer are Copyright (C) 2009
# the Initial Developer. All Rights Reserved.
#
# 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
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
@@ -41,256 +40,253 @@
import re, sys, os, os.path, logging, shutil, signal
from glob import glob
from optparse import OptionParser
from subprocess import Popen, PIPE, STDOUT
from tempfile import mkdtemp
from automationutils import *
-class XPCShellTests(object):
-
- log = logging.getLogger()
- oldcwd = os.getcwd()
+# Init logging
+log = logging.getLogger()
+handler = logging.StreamHandler(sys.stdout)
+log.setLevel(logging.INFO)
+log.addHandler(handler)
- def __init__(self):
- # Init logging
- handler = logging.StreamHandler(sys.stdout)
- self.log.setLevel(logging.INFO)
- self.log.addHandler(handler)
+oldcwd = os.getcwd()
- 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
+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 runTests(self, xpcshell, xrePath=None, symbolsPath=None,
- manifest=None, testdirs=[], testPath=None,
- interactive=False, logfiles=True,
- debuggerInfo=None):
- """Run xpcshell tests.
+def runTests(xpcshell, xrePath=None, symbolsPath=None,
+ manifest=None, testdirs=[], testPath=None,
+ interactive=False, logfiles=True,
+ debuggerInfo=None):
+ """Run xpcshell tests.
- |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.
- """
+ |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.
+ """
- if not testdirs and not manifest:
- # nothing to test!
- print >>sys.stderr, "Error: No test dirs or test manifest specified!"
- return False
+ if not testdirs and not manifest:
+ # nothing to test!
+ print >>sys.stderr, "Error: No test dirs or test manifest specified!"
+ return False
- passCount = 0
- failCount = 0
+ passCount = 0
+ failCount = 0
- 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("\\", "/");
+ 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("\\", "/");
- 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"
+ 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"
- 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
+ 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 = [
+ # 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:
- xpcsRunArgs = ['-e', '_execute_test();']
- if (debuggerInfo and debuggerInfo["interactive"]):
+ if sys.platform == 'os2emx':
pStdout = None
- pStderr = None
else:
- if sys.platform == 'os2emx':
- pStdout = None
- else:
- pStdout = PIPE
- pStderr = STDOUT
+ 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
- else:
- # Both path and test.
- # Reuse |testPath| temporarily.
- testPath = testPath.rsplit('/', 1)
- singleFile = testPath[1]
- testPath = testPath[0]
+ # |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:
- # Path only.
- # Simply remove optional ending separator.
- testPath = testPath.rstrip("/")
+ # 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("/")
+
+ # Override testdirs.
+ if manifest is not None:
+ testdirs = readManifest(os.path.abspath(manifest))
+
+ # Process each test directory individually.
+ for testdir in testdirs:
+ if testPath and not testdir.endswith(testPath):
+ continue
- # Override testdirs.
- if manifest is not None:
- testdirs = self.readManifest(os.path.abspath(manifest))
+ testdir = os.path.abspath(testdir)
- # Process each test directory individually.
- for testdir in testdirs:
- if testPath and not testdir.endswith(testPath):
+ # 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
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]
+ 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]
- # 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
+ # 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('\\', '/')]
- 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]
+ # create a temp dir that the JS harness can stick a profile in
+ profileDir = None
+ try:
+ profileDir = mkdtemp()
+ env["XPCSHELL_TEST_PROFILE_DIR"] = profileDir
- # 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('\\', '/')]
+ # Enable leaks (only) detection to its own log file.
+ leakLogFile = os.path.join(profileDir, "runxpcshelltests_leaks.log")
+ env["XPCOM_MEM_LEAK_LOG"] = leakLogFile
- # create a temp dir that the JS harness can stick a profile in
- profileDir = None
- try:
- profileDir = mkdtemp()
- env["XPCSHELL_TEST_PROFILE_DIR"] = profileDir
-
- # Enable leaks (only) detection to its own log file.
- leakLogFile = os.path.join(profileDir, "runxpcshelltests_leaks.log")
- env["XPCOM_MEM_LEAK_LOG"] = leakLogFile
+ proc = Popen(cmdH + cmdT + xpcsRunArgs,
+ stdout=pStdout, stderr=pStderr, env=env, cwd=testdir)
- 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)
- # 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 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:
+ 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:
+ 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."""
parser = OptionParser()
addCommonOptions(parser)
parser.add_option("--interactive",
action="store_true", dest="interactive", default=False,
@@ -306,35 +302,33 @@ def main():
help="don't create log files")
parser.add_option("--test-path",
type="string", dest="testPath", default=None,
help="single path and/or test filename to test")
options, args = parser.parse_args()
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)
- xpcsh = XPCShellTests()
- debuggerInfo = getDebuggerInfo(xpcsh.oldcwd, options.debugger, options.debuggerArgs,
+ debuggerInfo = getDebuggerInfo(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 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):
+ 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):
sys.exit(1)
if __name__ == '__main__':
main()