зеркало из https://github.com/mozilla/gecko-dev.git
Bug 907770 - Part 1: Making ssltunnel run for android and b2g mochitests. r=jmaher
This commit is contained in:
Родитель
d1212d39f6
Коммит
2739930979
|
@ -409,6 +409,13 @@ class MochitestOptions(optparse.OptionParser):
|
|||
"dest": "quiet",
|
||||
"help": "Do not print test log lines unless a failure occurs."
|
||||
}],
|
||||
[["--pidfile"],
|
||||
{ "action": "store",
|
||||
"type": "string",
|
||||
"dest": "pidFile",
|
||||
"help": "name of the pidfile to generate",
|
||||
"default": "",
|
||||
}],
|
||||
]
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
|
@ -669,13 +676,6 @@ class B2GOptions(MochitestOptions):
|
|||
"help": "ip address where the remote web server is hosted at",
|
||||
"default": None,
|
||||
}],
|
||||
[["--pidfile"],
|
||||
{ "action": "store",
|
||||
"type": "string",
|
||||
"dest": "pidFile",
|
||||
"help": "name of the pidfile to generate",
|
||||
"default": "",
|
||||
}],
|
||||
[["--gecko-path"],
|
||||
{ "action": "store",
|
||||
"type": "string",
|
||||
|
|
|
@ -262,6 +262,10 @@ class MochitestUtilsMixin(object):
|
|||
|
||||
def __init__(self):
|
||||
self.update_mozinfo()
|
||||
self.server = None
|
||||
self.wsserver = None
|
||||
self.sslTunnel = None
|
||||
self._locations = None
|
||||
|
||||
def update_mozinfo(self):
|
||||
"""walk up directories to find mozinfo.json update the info"""
|
||||
|
@ -287,6 +291,14 @@ class MochitestUtilsMixin(object):
|
|||
"""
|
||||
return self.getFullPath(logFile)
|
||||
|
||||
@property
|
||||
def locations(self):
|
||||
if self._locations is not None:
|
||||
return self._locations
|
||||
locations_file = os.path.join(SCRIPT_DIR, 'server-locations.txt')
|
||||
self._locations = ServerLocations(locations_file)
|
||||
return self._locations
|
||||
|
||||
def buildURLOptions(self, options, env):
|
||||
""" Add test control options from the command line to the url
|
||||
|
||||
|
@ -507,43 +519,71 @@ class MochitestUtilsMixin(object):
|
|||
|
||||
def startWebSocketServer(self, options, debuggerInfo):
|
||||
""" Launch the websocket server """
|
||||
if options.webServer != '127.0.0.1':
|
||||
return
|
||||
|
||||
self.wsserver = WebSocketServer(options, SCRIPT_DIR, debuggerInfo)
|
||||
self.wsserver.start()
|
||||
|
||||
def stopWebSocketServer(self, options):
|
||||
if options.webServer != '127.0.0.1':
|
||||
return
|
||||
|
||||
log.info('Stopping web socket server')
|
||||
self.wsserver.stop()
|
||||
|
||||
def startWebServer(self, options):
|
||||
"""Create the webserver and start it up"""
|
||||
|
||||
if options.webServer != '127.0.0.1':
|
||||
return
|
||||
|
||||
self.server = MochitestServer(options)
|
||||
self.server.start()
|
||||
|
||||
if options.pidFile != "":
|
||||
with open(options.pidFile + ".xpcshell.pid", 'w') as f:
|
||||
f.write("%s" % self.server._process.pid)
|
||||
|
||||
def startServers(self, options, debuggerInfo):
|
||||
# start servers and set ports
|
||||
# TODO: pass these values, don't set on `self`
|
||||
self.webServer = options.webServer
|
||||
self.httpPort = options.httpPort
|
||||
self.sslPort = options.sslPort
|
||||
self.webSocketPort = options.webSocketPort
|
||||
|
||||
# httpd-path is specified by standard makefile targets and may be specified
|
||||
# on the command line to select a particular version of httpd.js. If not
|
||||
# specified, try to select the one from hostutils.zip, as required in bug 882932.
|
||||
if not options.httpdPath:
|
||||
options.httpdPath = os.path.join(options.utilityPath, "components")
|
||||
|
||||
self.startWebServer(options)
|
||||
self.startWebSocketServer(options, debuggerInfo)
|
||||
|
||||
# start SSL pipe
|
||||
self.sslTunnel = SSLTunnel(options)
|
||||
self.sslTunnel.buildConfig(self.locations)
|
||||
self.sslTunnel.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.
|
||||
self.server.ensureReady(self.SERVER_STARTUP_TIMEOUT)
|
||||
if self.server is not None:
|
||||
self.server.ensureReady(self.SERVER_STARTUP_TIMEOUT)
|
||||
|
||||
def stopWebServer(self, options):
|
||||
""" Server's no longer needed, and perhaps more importantly, anything it might
|
||||
spew to console shouldn't disrupt the leak information table we print next.
|
||||
"""
|
||||
if options.webServer != '127.0.0.1':
|
||||
return
|
||||
def stopServers(self):
|
||||
"""Servers are no longer needed, and perhaps more importantly, anything they
|
||||
might spew to console might confuse things."""
|
||||
if self.server is not None:
|
||||
try:
|
||||
log.info('Stopping web server')
|
||||
self.server.stop()
|
||||
except Exception:
|
||||
log.exception('Exception when stopping web server')
|
||||
|
||||
log.info('Stopping web server')
|
||||
self.server.stop()
|
||||
if self.wsserver is not None:
|
||||
try:
|
||||
log.info('Stopping web socket server')
|
||||
self.wsserver.stop()
|
||||
except Exception:
|
||||
log.exception('Exception when stopping web socket server');
|
||||
|
||||
if self.sslTunnel is not None:
|
||||
try:
|
||||
log.info('Stopping ssltunnel')
|
||||
self.sslTunnel.stop()
|
||||
except Exception:
|
||||
log.exception('Exception stopping ssltunnel');
|
||||
|
||||
def copyExtraFilesToProfile(self, options):
|
||||
"Copy extra files or dirs specified on the command line to the testing profile."
|
||||
|
@ -642,9 +682,81 @@ overlay chrome://webapprt/content/webapp.xul chrome://mochikit/content/browser-t
|
|||
extensions.append(os.path.join(SCRIPT_DIR, self.jarDir))
|
||||
return extensions
|
||||
|
||||
class SSLTunnel:
|
||||
def __init__(self, options):
|
||||
self.process = None
|
||||
self.utilityPath = options.utilityPath
|
||||
self.xrePath = options.xrePath
|
||||
self.certPath = options.certPath
|
||||
self.sslPort = options.sslPort
|
||||
self.httpPort = options.httpPort
|
||||
self.webServer = options.webServer
|
||||
self.webSocketPort = options.webSocketPort
|
||||
|
||||
self.customCertRE = re.compile("^cert=(?P<nickname>[0-9a-zA-Z_ ]+)")
|
||||
self.clientAuthRE = re.compile("^clientauth=(?P<clientauth>[a-z]+)")
|
||||
self.redirRE = re.compile("^redir=(?P<redirhost>[0-9a-zA-Z_ .]+)")
|
||||
|
||||
def writeLocation(self, config, loc):
|
||||
for option in loc.options:
|
||||
match = self.customCertRE.match(option)
|
||||
if match:
|
||||
customcert = match.group("nickname");
|
||||
config.write("listen:%s:%s:%s:%s\n" %
|
||||
(loc.host, loc.port, self.sslPort, customcert))
|
||||
|
||||
match = self.clientAuthRE.match(option)
|
||||
if match:
|
||||
clientauth = match.group("clientauth");
|
||||
config.write("clientauth:%s:%s:%s:%s\n" %
|
||||
(loc.host, loc.port, self.sslPort, clientauth))
|
||||
|
||||
match = self.redirRE.match(option)
|
||||
if match:
|
||||
redirhost = match.group("redirhost")
|
||||
config.write("redirhost:%s:%s:%s:%s\n" %
|
||||
(loc.host, loc.port, self.sslPort, redirhost))
|
||||
|
||||
def buildConfig(self, locations):
|
||||
"""Create the ssltunnel configuration file"""
|
||||
configFd, self.configFile = tempfile.mkstemp(prefix="ssltunnel", suffix=".cfg")
|
||||
with os.fdopen(configFd, "w") as config:
|
||||
config.write("httpproxy:1\n")
|
||||
config.write("certdbdir:%s\n" % self.certPath)
|
||||
config.write("forward:127.0.0.1:%s\n" % self.httpPort)
|
||||
config.write("websocketserver:%s:%s\n" % (self.webServer, self.webSocketPort))
|
||||
config.write("listen:*:%s:pgo server certificate\n" % self.sslPort)
|
||||
|
||||
for loc in locations:
|
||||
if loc.scheme == "https" and "nocert" not in loc.options:
|
||||
self.writeLocation(config, loc)
|
||||
|
||||
def start(self):
|
||||
""" Starts the SSL Tunnel """
|
||||
|
||||
# start ssltunnel to provide https:// URLs capability
|
||||
bin_suffix = mozinfo.info.get('bin_suffix', '')
|
||||
ssltunnel = os.path.join(self.utilityPath, "ssltunnel" + bin_suffix)
|
||||
if not os.path.exists(ssltunnel):
|
||||
log.error("INFO | runtests.py | expected to find ssltunnel at %s", ssltunnel)
|
||||
exit(1)
|
||||
|
||||
env = environment(xrePath=self.xrePath)
|
||||
self.process = mozprocess.ProcessHandler([ssltunnel, self.configFile],
|
||||
env=env)
|
||||
self.process.run()
|
||||
log.info("INFO | runtests.py | SSL tunnel pid: %d", self.process.pid)
|
||||
|
||||
def stop(self):
|
||||
""" Stops the SSL Tunnel and cleans up """
|
||||
if self.process is not None:
|
||||
self.process.kill()
|
||||
if os.path.exists(self.configFile):
|
||||
os.remove(self.configFile)
|
||||
|
||||
class Mochitest(MochitestUtilsMixin):
|
||||
runSSLTunnel = True
|
||||
certdbNew = False
|
||||
sslTunnel = None
|
||||
vmwareHelper = None
|
||||
DEFAULT_TIMEOUT = 60.0
|
||||
|
||||
|
@ -666,8 +778,6 @@ class Mochitest(MochitestUtilsMixin):
|
|||
# metro browser sub process id
|
||||
self.browserProcessId = None
|
||||
|
||||
# cached server locations
|
||||
self._locations = {}
|
||||
|
||||
self.haveDumpedScreen = False
|
||||
|
||||
|
@ -680,6 +790,50 @@ class Mochitest(MochitestUtilsMixin):
|
|||
print str(e)
|
||||
sys.exit(1)
|
||||
|
||||
def fillCertificateDB(self, options):
|
||||
# TODO: move -> mozprofile:
|
||||
# https://bugzilla.mozilla.org/show_bug.cgi?id=746243#c35
|
||||
|
||||
pwfilePath = os.path.join(options.profilePath, ".crtdbpw")
|
||||
with open(pwfilePath, "w") as pwfile:
|
||||
pwfile.write("\n")
|
||||
|
||||
# Pre-create the certification database for the profile
|
||||
env = self.environment(xrePath=options.xrePath)
|
||||
bin_suffix = mozinfo.info.get('bin_suffix', '')
|
||||
certutil = os.path.join(options.utilityPath, "certutil" + bin_suffix)
|
||||
pk12util = os.path.join(options.utilityPath, "pk12util" + bin_suffix)
|
||||
|
||||
if self.certdbNew:
|
||||
# android and b2g use the new DB formats exclusively
|
||||
certdbPath = "sql:" + options.profilePath
|
||||
else:
|
||||
# desktop seems to use the old
|
||||
certdbPath = options.profilePath
|
||||
|
||||
status = call([certutil, "-N", "-d", certdbPath, "-f", pwfilePath], env=env)
|
||||
if status:
|
||||
return status
|
||||
|
||||
# Walk the cert directory and add custom CAs and client certs
|
||||
files = os.listdir(options.certPath)
|
||||
for item in files:
|
||||
root, ext = os.path.splitext(item)
|
||||
if ext == ".ca":
|
||||
trustBits = "CT,,"
|
||||
if root.endswith("-object"):
|
||||
trustBits = "CT,,CT"
|
||||
call([certutil, "-A", "-i", os.path.join(options.certPath, item),
|
||||
"-d", certdbPath, "-f", pwfilePath, "-n", root, "-t", trustBits],
|
||||
env=env)
|
||||
elif ext == ".client":
|
||||
call([pk12util, "-i", os.path.join(options.certPath, item),
|
||||
"-w", pwfilePath, "-d", certdbPath],
|
||||
env=env)
|
||||
|
||||
os.unlink(pwfilePath)
|
||||
return 0
|
||||
|
||||
def buildProfile(self, options):
|
||||
""" create the profile and add optional chrome bits and files if requested """
|
||||
if options.browserChrome and options.timeout:
|
||||
|
@ -698,10 +852,6 @@ class Mochitest(MochitestUtilsMixin):
|
|||
else:
|
||||
apps = None
|
||||
|
||||
# locations
|
||||
locations_file = os.path.join(SCRIPT_DIR, 'server-locations.txt')
|
||||
locations = ServerLocations(locations_file)
|
||||
|
||||
# preferences
|
||||
prefsPath = os.path.join(SCRIPT_DIR, 'profile_data', 'prefs_general.js')
|
||||
prefs = dict(Preferences.read_prefs(prefsPath))
|
||||
|
@ -727,10 +877,11 @@ class Mochitest(MochitestUtilsMixin):
|
|||
'ws': options.sslPort
|
||||
}
|
||||
|
||||
|
||||
# create a profile
|
||||
self.profile = Profile(profile=options.profilePath,
|
||||
addons=extensions,
|
||||
locations=locations,
|
||||
locations=self.locations,
|
||||
preferences=prefs,
|
||||
apps=apps,
|
||||
proxy=proxy
|
||||
|
@ -741,6 +892,14 @@ class Mochitest(MochitestUtilsMixin):
|
|||
|
||||
manifest = self.addChromeToProfile(options)
|
||||
self.copyExtraFilesToProfile(options)
|
||||
|
||||
# create certificate database for the profile
|
||||
# TODO: this should really be upstreamed somewhere, maybe mozprofile
|
||||
certificateStatus = self.fillCertificateDB(options)
|
||||
if certificateStatus:
|
||||
log.info("TEST-UNEXPECTED-FAIL | runtests.py | Certificate integration failed")
|
||||
return None
|
||||
|
||||
return manifest
|
||||
|
||||
def buildBrowserEnv(self, options, debugger=False):
|
||||
|
@ -782,6 +941,13 @@ class Mochitest(MochitestUtilsMixin):
|
|||
""" remove temporary files and profile """
|
||||
os.remove(manifest)
|
||||
del self.profile
|
||||
if options.pidFile != "":
|
||||
try:
|
||||
os.remove(options.pidFile)
|
||||
if os.path.exists(options.pidFile + ".xpcshell.pid"):
|
||||
os.remove(options.pidFile + ".xpcshell.pid")
|
||||
except:
|
||||
log.warn("cleaning up pidfile '%s' was unsuccessful from the test harness", options.pidFile)
|
||||
|
||||
def dumpScreen(self, utilityPath):
|
||||
if self.haveDumpedScreen:
|
||||
|
@ -868,15 +1034,17 @@ class Mochitest(MochitestUtilsMixin):
|
|||
|
||||
def stopVMwareRecording(self):
|
||||
""" stops recording inside VMware VM using the recording helper dll """
|
||||
assert mozinfo.isWin
|
||||
if self.vmwareHelper is not None:
|
||||
log.info("runtests.py | Stopping VMware recording.")
|
||||
try:
|
||||
try:
|
||||
assert mozinfo.isWin
|
||||
if self.vmwareHelper is not None:
|
||||
log.info("runtests.py | Stopping VMware recording.")
|
||||
self.vmwareHelper.StopRecording()
|
||||
except Exception, e:
|
||||
log.warning("runtests.py | Failed to stop "
|
||||
"VMware recording: (%s)" % str(e))
|
||||
self.vmwareHelper = None
|
||||
except Exception, e:
|
||||
log.warning("runtests.py | Failed to stop "
|
||||
"VMware recording: (%s)" % str(e))
|
||||
log.exception('Error stopping VMWare recording')
|
||||
|
||||
self.vmwareHelper = None
|
||||
|
||||
def runApp(self,
|
||||
testUrl,
|
||||
|
@ -885,8 +1053,6 @@ class Mochitest(MochitestUtilsMixin):
|
|||
profile,
|
||||
extraArgs,
|
||||
utilityPath,
|
||||
xrePath,
|
||||
certPath,
|
||||
debuggerInfo=None,
|
||||
symbolsPath=None,
|
||||
timeout=-1,
|
||||
|
@ -906,16 +1072,6 @@ class Mochitest(MochitestUtilsMixin):
|
|||
interactive = debuggerInfo['interactive']
|
||||
debug_args = [debuggerInfo['path']] + debuggerInfo['args']
|
||||
|
||||
# ensure existence of required paths
|
||||
required_paths = ('utilityPath', 'xrePath', 'certPath')
|
||||
missing = [(path, locals()[path])
|
||||
for path in required_paths
|
||||
if not os.path.exists(locals()[path])]
|
||||
if missing:
|
||||
log.error("runtests.py | runApp called with missing paths: %s" % (
|
||||
', '.join([("%s->%s" % (key, value)) for key, value in missing])))
|
||||
return 1
|
||||
|
||||
# fix default timeout
|
||||
if timeout == -1:
|
||||
timeout = self.DEFAULT_TIMEOUT
|
||||
|
@ -934,28 +1090,6 @@ class Mochitest(MochitestUtilsMixin):
|
|||
os.close(tmpfd)
|
||||
env["MOZ_PROCESS_LOG"] = processLog
|
||||
|
||||
if self.runSSLTunnel:
|
||||
|
||||
# create certificate database for the profile
|
||||
# TODO: this should really be upstreamed somewhere, maybe mozprofile
|
||||
certificateStatus = self.fillCertificateDB(self.profile.profile,
|
||||
certPath,
|
||||
utilityPath,
|
||||
xrePath)
|
||||
if certificateStatus:
|
||||
log.info("TEST-UNEXPECTED-FAIL | runtests.py | Certificate integration failed")
|
||||
return certificateStatus
|
||||
|
||||
# start ssltunnel to provide https:// URLs capability
|
||||
ssltunnel = os.path.join(utilityPath, "ssltunnel" + bin_suffix)
|
||||
ssltunnel_cfg = os.path.join(self.profile.profile, "ssltunnel.cfg")
|
||||
ssltunnelProcess = mozprocess.ProcessHandler([ssltunnel, ssltunnel_cfg], cwd=SCRIPT_DIR,
|
||||
env=environment(xrePath=xrePath))
|
||||
ssltunnelProcess.run()
|
||||
log.info("INFO | runtests.py | SSL tunnel pid: %d", ssltunnelProcess.pid)
|
||||
else:
|
||||
ssltunnelProcess = None
|
||||
|
||||
if interactive:
|
||||
# If an interactive debugger is attached,
|
||||
# don't use timeouts, and don't capture ctrl-c.
|
||||
|
@ -1074,8 +1208,6 @@ class Mochitest(MochitestUtilsMixin):
|
|||
# cleanup
|
||||
if os.path.exists(processLog):
|
||||
os.remove(processLog)
|
||||
if ssltunnelProcess:
|
||||
ssltunnelProcess.kill()
|
||||
|
||||
return status
|
||||
|
||||
|
@ -1106,104 +1238,82 @@ class Mochitest(MochitestUtilsMixin):
|
|||
if manifest is None:
|
||||
return 1
|
||||
|
||||
# start servers and set ports
|
||||
# TODO: pass these values, don't set on `self`
|
||||
self.webServer = options.webServer
|
||||
self.httpPort = options.httpPort
|
||||
self.sslPort = options.sslPort
|
||||
self.webSocketPort = options.webSocketPort
|
||||
|
||||
try:
|
||||
self.startWebServer(options)
|
||||
self.startWebSocketServer(options, debuggerInfo)
|
||||
self.startServers(options, debuggerInfo)
|
||||
|
||||
testURL = self.buildTestPath(options)
|
||||
self.buildURLOptions(options, browserEnv)
|
||||
if self.urlOpts:
|
||||
testURL += "?" + "&".join(self.urlOpts)
|
||||
testURL = self.buildTestPath(options)
|
||||
self.buildURLOptions(options, browserEnv)
|
||||
if self.urlOpts:
|
||||
testURL += "?" + "&".join(self.urlOpts)
|
||||
|
||||
if options.webapprtContent:
|
||||
options.browserArgs.extend(('-test-mode', testURL))
|
||||
testURL = None
|
||||
if options.webapprtContent:
|
||||
options.browserArgs.extend(('-test-mode', testURL))
|
||||
testURL = None
|
||||
|
||||
if options.immersiveMode:
|
||||
options.browserArgs.extend(('-firefoxpath', options.app))
|
||||
options.app = self.immersiveHelperPath
|
||||
if options.immersiveMode:
|
||||
options.browserArgs.extend(('-firefoxpath', options.app))
|
||||
options.app = self.immersiveHelperPath
|
||||
|
||||
if options.jsdebugger:
|
||||
options.browserArgs.extend(['-jsdebugger'])
|
||||
if options.jsdebugger:
|
||||
options.browserArgs.extend(['-jsdebugger'])
|
||||
|
||||
# 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)
|
||||
# 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.debugger or not options.autorun:
|
||||
timeout = None
|
||||
else:
|
||||
timeout = 330.0 # default JS harness timeout is 300 seconds
|
||||
# then again to actually run mochitest
|
||||
if options.timeout:
|
||||
timeout = options.timeout + 30
|
||||
elif options.debugger or not options.autorun:
|
||||
timeout = None
|
||||
else:
|
||||
timeout = 330.0 # default JS harness timeout is 300 seconds
|
||||
|
||||
if options.vmwareRecording:
|
||||
self.startVMwareRecording(options);
|
||||
if options.vmwareRecording:
|
||||
self.startVMwareRecording(options);
|
||||
|
||||
log.info("runtests.py | Running tests: start.\n")
|
||||
try:
|
||||
status = self.runApp(testURL,
|
||||
browserEnv,
|
||||
options.app,
|
||||
profile=self.profile,
|
||||
extraArgs=options.browserArgs,
|
||||
utilityPath=options.utilityPath,
|
||||
xrePath=options.xrePath,
|
||||
certPath=options.certPath,
|
||||
debuggerInfo=debuggerInfo,
|
||||
symbolsPath=options.symbolsPath,
|
||||
timeout=timeout,
|
||||
onLaunch=onLaunch,
|
||||
webapprtChrome=options.webapprtChrome,
|
||||
hide_subtests=options.hide_subtests,
|
||||
screenshotOnFail=options.screenshotOnFail
|
||||
)
|
||||
except KeyboardInterrupt:
|
||||
log.info("runtests.py | Received keyboard interrupt.\n");
|
||||
status = -1
|
||||
except:
|
||||
traceback.print_exc()
|
||||
log.error("Automation Error: Received unexpected exception while running application\n")
|
||||
status = 1
|
||||
log.info("runtests.py | Running tests: start.\n")
|
||||
try:
|
||||
status = self.runApp(testURL,
|
||||
browserEnv,
|
||||
options.app,
|
||||
profile=self.profile,
|
||||
extraArgs=options.browserArgs,
|
||||
utilityPath=options.utilityPath,
|
||||
debuggerInfo=debuggerInfo,
|
||||
symbolsPath=options.symbolsPath,
|
||||
timeout=timeout,
|
||||
onLaunch=onLaunch,
|
||||
webapprtChrome=options.webapprtChrome,
|
||||
hide_subtests=options.hide_subtests,
|
||||
screenshotOnFail=options.screenshotOnFail
|
||||
)
|
||||
except KeyboardInterrupt:
|
||||
log.info("runtests.py | Received keyboard interrupt.\n");
|
||||
status = -1
|
||||
except:
|
||||
traceback.print_exc()
|
||||
log.error("Automation Error: Received unexpected exception while running application\n")
|
||||
status = 1
|
||||
|
||||
finally:
|
||||
if options.vmwareRecording:
|
||||
try:
|
||||
self.stopVMwareRecording();
|
||||
except Exception:
|
||||
log.exception('Error stopping VMWare recording')
|
||||
if options.vmwareRecording:
|
||||
self.stopVMwareRecording();
|
||||
self.stopServers()
|
||||
|
||||
try:
|
||||
self.stopWebServer(options)
|
||||
except Exception:
|
||||
log.exception('Exception when stopping web server')
|
||||
processLeakLog(self.leak_report_file, options.leakThreshold)
|
||||
|
||||
try:
|
||||
self.stopWebSocketServer(options)
|
||||
except Exception:
|
||||
log.exception('Exception when stopping websocket server')
|
||||
if self.nsprLogs:
|
||||
with zipfile.ZipFile("%s/nsprlog.zip" % browserEnv["MOZ_UPLOAD_DIR"], "w", zipfile.ZIP_DEFLATED) as logzip:
|
||||
for logfile in glob.glob("%s/nspr*.log*" % tempfile.gettempdir()):
|
||||
logzip.write(logfile)
|
||||
os.remove(logfile)
|
||||
|
||||
processLeakLog(self.leak_report_file, options.leakThreshold)
|
||||
log.info("runtests.py | Running tests: end.")
|
||||
|
||||
if self.nsprLogs:
|
||||
with zipfile.ZipFile("%s/nsprlog.zip" % browserEnv["MOZ_UPLOAD_DIR"], "w", zipfile.ZIP_DEFLATED) as logzip:
|
||||
for logfile in glob.glob("%s/nspr*.log*" % tempfile.gettempdir()):
|
||||
logzip.write(logfile)
|
||||
os.remove(logfile)
|
||||
|
||||
log.info("runtests.py | Running tests: end.")
|
||||
|
||||
if manifest is not None:
|
||||
self.cleanup(manifest, options)
|
||||
if manifest is not None:
|
||||
self.cleanup(manifest, options)
|
||||
|
||||
return status
|
||||
|
||||
|
@ -1428,93 +1538,6 @@ class Mochitest(MochitestUtilsMixin):
|
|||
for path in self.getExtensionsToInstall(options):
|
||||
self.installExtensionFromPath(options, path)
|
||||
|
||||
def readLocations(self, locations_file):
|
||||
"""
|
||||
Reads the locations at which the Mochitest HTTP server is available from
|
||||
`locations_file`.
|
||||
"""
|
||||
path = os.path.realpath(locations_file)
|
||||
return self._locations.setdefault(path, ServerLocations(path))
|
||||
|
||||
def fillCertificateDB(self, profileDir, certPath, utilityPath, xrePath):
|
||||
# TODO: move -> mozprofile:
|
||||
# https://bugzilla.mozilla.org/show_bug.cgi?id=746243#c35
|
||||
|
||||
pwfilePath = os.path.join(profileDir, ".crtdbpw")
|
||||
with open(pwfilePath, "w") as pwfile:
|
||||
pwfile.write("\n")
|
||||
|
||||
# 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:%s\n" % self.httpPort)
|
||||
sslTunnelConfig.write("websocketserver:%s:%s\n" % (self.webServer, self.webSocketPort))
|
||||
sslTunnelConfig.write("listen:*:%s:pgo server certificate\n" % self.sslPort)
|
||||
|
||||
# Configure automatic certificate and bind custom certificates, client authentication
|
||||
locations = self.readLocations(os.path.join(SCRIPT_DIR, 'server-locations.txt'))
|
||||
|
||||
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]+)")
|
||||
redirRE = re.compile("^redir=(?P<redirhost>[0-9a-zA-Z_ .]+)")
|
||||
for option in loc.options:
|
||||
match = customCertRE.match(option)
|
||||
if match:
|
||||
customcert = match.group("nickname");
|
||||
sslTunnelConfig.write("listen:%s:%s:%s:%s\n" %
|
||||
(loc.host, loc.port, self.sslPort, customcert))
|
||||
|
||||
match = clientAuthRE.match(option)
|
||||
if match:
|
||||
clientauth = match.group("clientauth");
|
||||
sslTunnelConfig.write("clientauth:%s:%s:%s:%s\n" %
|
||||
(loc.host, loc.port, self.sslPort, clientauth))
|
||||
|
||||
match = redirRE.match(option)
|
||||
if match:
|
||||
redirhost = match.group("redirhost")
|
||||
sslTunnelConfig.write("redirhost:%s:%s:%s:%s\n" %
|
||||
(loc.host, loc.port, self.sslPort, redirhost))
|
||||
|
||||
sslTunnelConfig.close()
|
||||
|
||||
# Pre-create the certification database for the profile
|
||||
env = self.environment(xrePath=xrePath)
|
||||
bin_suffix = mozinfo.info.get('bin_suffix', '')
|
||||
certutil = os.path.join(utilityPath, "certutil" + bin_suffix)
|
||||
pk12util = os.path.join(utilityPath, "pk12util" + bin_suffix)
|
||||
|
||||
status = call([certutil, "-N", "-d", profileDir, "-f", pwfilePath], env=env)
|
||||
printstatus(status, "certutil")
|
||||
if status:
|
||||
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"
|
||||
status = call([certutil, "-A", "-i", os.path.join(certPath, item),
|
||||
"-d", profileDir, "-f", pwfilePath, "-n", root, "-t", trustBits],
|
||||
env=env)
|
||||
printstatus(status, "certutil")
|
||||
elif ext == ".client":
|
||||
status = call([pk12util, "-i", os.path.join(certPath, item), "-w",
|
||||
pwfilePath, "-d", profileDir],
|
||||
env=env)
|
||||
printstatus(status, "pk2util")
|
||||
|
||||
os.unlink(pwfilePath)
|
||||
return 0
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ class B2GMochitest(MochitestUtilsMixin):
|
|||
super(B2GMochitest, self).__init__()
|
||||
self.marionette = marionette
|
||||
self.out_of_process = out_of_process
|
||||
self.locations = locations
|
||||
self.locations_file = locations
|
||||
self.preferences = []
|
||||
self.webapps = None
|
||||
self.test_script = os.path.join(here, 'b2g_start_script.js')
|
||||
|
@ -92,7 +92,7 @@ class B2GMochitest(MochitestUtilsMixin):
|
|||
kwargs = {
|
||||
'addons': self.getExtensionsToInstall(options),
|
||||
'apps': self.webapps,
|
||||
'locations': self.locations,
|
||||
'locations': self.locations_file,
|
||||
'preferences': prefs,
|
||||
'proxy': {"remote": options.webServer}
|
||||
}
|
||||
|
@ -114,8 +114,7 @@ class B2GMochitest(MochitestUtilsMixin):
|
|||
self.leak_report_file = os.path.join(options.profilePath, "runtests_leaks.log")
|
||||
manifest = self.build_profile(options)
|
||||
|
||||
self.startWebServer(options)
|
||||
self.startWebSocketServer(options, None)
|
||||
self.startServers(options, None)
|
||||
self.buildURLOptions(options, {'MOZ_HIDE_RESULTS_TABLE': '1'})
|
||||
self.test_script_args.append(not options.emulator)
|
||||
self.test_script_args.append(options.wifi)
|
||||
|
@ -155,8 +154,7 @@ class B2GMochitest(MochitestUtilsMixin):
|
|||
self.runner.check_for_crashes()
|
||||
status = 1
|
||||
|
||||
self.stopWebServer(options)
|
||||
self.stopWebSocketServer(options)
|
||||
self.stopServers()
|
||||
|
||||
log.info("runtestsb2g.py | Running tests: end.")
|
||||
|
||||
|
@ -199,26 +197,20 @@ class B2GDeviceMochitest(B2GMochitest):
|
|||
self.runner.cleanup()
|
||||
self.runner = None
|
||||
|
||||
def startWebServer(self, options):
|
||||
""" Create the webserver on the host and start it up """
|
||||
d = vars(options).copy()
|
||||
d['xrePath'] = self.local_binary_dir
|
||||
d['utilityPath'] = self.local_binary_dir
|
||||
d['profilePath'] = tempfile.mkdtemp()
|
||||
if d.get('httpdPath') is None:
|
||||
d['httpdPath'] = os.path.abspath(os.path.join(self.local_binary_dir, 'components'))
|
||||
self.server = MochitestServer(d)
|
||||
self.server.start()
|
||||
def startServers(self, options, debuggerInfo):
|
||||
""" Create the servers on the host and start them up """
|
||||
savedXre = options.xrePath
|
||||
savedUtility = options.utilityPath
|
||||
savedProfie = options.profilePath
|
||||
options.xrePath = self.local_binary_dir
|
||||
options.utilityPath = self.local_binary_dir
|
||||
options.profilePath = tempfile.mkdtemp()
|
||||
|
||||
if (options.pidFile != ""):
|
||||
f = open(options.pidFile + ".xpcshell.pid", 'w')
|
||||
f.write("%s" % self.server._process.pid)
|
||||
f.close()
|
||||
self.server.ensureReady(90)
|
||||
MochitestUtilsMixin.startServers(self, options, debuggerInfo)
|
||||
|
||||
def stopWebServer(self, options):
|
||||
if hasattr(self, 'server'):
|
||||
self.server.stop()
|
||||
options.xrePath = savedXre
|
||||
options.utilityPath = savedUtility
|
||||
options.profilePath = savedProfie
|
||||
|
||||
def buildURLOptions(self, options, env):
|
||||
self.local_log = options.logFile
|
||||
|
@ -238,6 +230,7 @@ class B2GDesktopMochitest(B2GMochitest, Mochitest):
|
|||
def __init__(self, marionette, profile_data_dir):
|
||||
B2GMochitest.__init__(self, marionette, out_of_process=False, profile_data_dir=profile_data_dir)
|
||||
Mochitest.__init__(self)
|
||||
self.certdbNew = True
|
||||
|
||||
def runMarionetteScript(self, marionette, test_script, test_script_args):
|
||||
assert(marionette.wait_for_port())
|
||||
|
@ -340,8 +333,7 @@ def run_remote_mochitests(parser, options):
|
|||
except:
|
||||
print "Automation Error: Exception caught while running tests"
|
||||
traceback.print_exc()
|
||||
mochitest.stopWebServer(options)
|
||||
mochitest.stopWebSocketServer(options)
|
||||
mochitest.stopServers()
|
||||
try:
|
||||
mochitest.cleanup(None, options)
|
||||
except:
|
||||
|
|
|
@ -82,11 +82,6 @@ class RemoteOptions(MochitestOptions):
|
|||
help = "ssl port of the remote web server")
|
||||
defaults["sslPort"] = automation.DEFAULT_SSL_PORT
|
||||
|
||||
self.add_option("--pidfile", action = "store",
|
||||
type = "string", dest = "pidFile",
|
||||
help = "name of the pidfile to generate")
|
||||
defaults["pidFile"] = ""
|
||||
|
||||
self.add_option("--robocop-ini", action = "store",
|
||||
type = "string", dest = "robocopIni",
|
||||
help = "name of the .ini file containing the list of tests to run")
|
||||
|
@ -223,7 +218,7 @@ class RemoteOptions(MochitestOptions):
|
|||
options.sslPort = tempSSL
|
||||
options.httpPort = tempPort
|
||||
|
||||
return options
|
||||
return options
|
||||
|
||||
class MochiRemote(Mochitest):
|
||||
|
||||
|
@ -236,13 +231,13 @@ class MochiRemote(Mochitest):
|
|||
self._automation = automation
|
||||
Mochitest.__init__(self)
|
||||
self._dm = devmgr
|
||||
self.runSSLTunnel = False
|
||||
self.environment = self._automation.environment
|
||||
self.remoteProfile = options.remoteTestRoot + "/profile"
|
||||
self._automation.setRemoteProfile(self.remoteProfile)
|
||||
self.remoteLog = options.remoteLogFile
|
||||
self.localLog = options.logFile
|
||||
self._automation.deleteANRs()
|
||||
self.certdbNew = True
|
||||
|
||||
def cleanup(self, manifest, options):
|
||||
if self._dm.fileExists(self.remoteLog):
|
||||
|
@ -252,13 +247,7 @@ class MochiRemote(Mochitest):
|
|||
log.warn("Unable to retrieve log file (%s) from remote device",
|
||||
self.remoteLog)
|
||||
self._dm.removeDir(self.remoteProfile)
|
||||
|
||||
if (options.pidFile != ""):
|
||||
try:
|
||||
os.remove(options.pidFile)
|
||||
os.remove(options.pidFile + ".xpcshell.pid")
|
||||
except:
|
||||
log.warn("cleaning up pidfile '%s' was unsuccessful from the test harness", options.pidFile)
|
||||
Mochitest.cleanup(self, manifest, options)
|
||||
|
||||
def findPath(self, paths, filename = None):
|
||||
for path in paths:
|
||||
|
@ -269,11 +258,7 @@ class MochiRemote(Mochitest):
|
|||
return path
|
||||
return None
|
||||
|
||||
def startWebServer(self, options):
|
||||
""" Create the webserver on the host and start it up """
|
||||
remoteXrePath = options.xrePath
|
||||
remoteProfilePath = options.profilePath
|
||||
remoteUtilityPath = options.utilityPath
|
||||
def makeLocalAutomation(self):
|
||||
localAutomation = Automation()
|
||||
localAutomation.IS_WIN32 = False
|
||||
localAutomation.IS_LINUX = False
|
||||
|
@ -281,15 +266,33 @@ class MochiRemote(Mochitest):
|
|||
localAutomation.UNIXISH = False
|
||||
hostos = sys.platform
|
||||
if (hostos == 'mac' or hostos == 'darwin'):
|
||||
localAutomation.IS_MAC = True
|
||||
localAutomation.IS_MAC = True
|
||||
elif (hostos == 'linux' or hostos == 'linux2'):
|
||||
localAutomation.IS_LINUX = True
|
||||
localAutomation.UNIXISH = True
|
||||
localAutomation.IS_LINUX = True
|
||||
localAutomation.UNIXISH = True
|
||||
elif (hostos == 'win32' or hostos == 'win64'):
|
||||
localAutomation.BIN_SUFFIX = ".exe"
|
||||
localAutomation.IS_WIN32 = True
|
||||
localAutomation.BIN_SUFFIX = ".exe"
|
||||
localAutomation.IS_WIN32 = True
|
||||
return localAutomation
|
||||
|
||||
paths = [options.xrePath, localAutomation.DIST_BIN, self._automation._product, os.path.join('..', self._automation._product)]
|
||||
# This seems kludgy, but this class uses paths from the remote host in the
|
||||
# options, except when calling up to the base class, which doesn't
|
||||
# understand the distinction. This switches out the remote values for local
|
||||
# ones that the base class understands. This is necessary for the web
|
||||
# server, SSL tunnel and profile building functions.
|
||||
def switchToLocalPaths(self, options):
|
||||
""" Set local paths in the options, return a function that will restore remote values """
|
||||
remoteXrePath = options.xrePath
|
||||
remoteProfilePath = options.profilePath
|
||||
remoteUtilityPath = options.utilityPath
|
||||
|
||||
localAutomation = self.makeLocalAutomation()
|
||||
paths = [
|
||||
options.xrePath,
|
||||
localAutomation.DIST_BIN,
|
||||
self._automation._product,
|
||||
os.path.join('..', self._automation._product)
|
||||
]
|
||||
options.xrePath = self.findPath(paths)
|
||||
if options.xrePath == None:
|
||||
log.error("unable to find xulrunner path for %s, please specify with --xre-path", os.name)
|
||||
|
@ -298,20 +301,16 @@ class MochiRemote(Mochitest):
|
|||
xpcshell = "xpcshell"
|
||||
if (os.name == "nt"):
|
||||
xpcshell += ".exe"
|
||||
|
||||
|
||||
if options.utilityPath:
|
||||
paths = [options.utilityPath, options.xrePath]
|
||||
else:
|
||||
paths = [options.xrePath]
|
||||
options.utilityPath = self.findPath(paths, xpcshell)
|
||||
|
||||
if options.utilityPath == None:
|
||||
log.error("unable to find utility path for %s, please specify with --utility-path", os.name)
|
||||
sys.exit(1)
|
||||
# httpd-path is specified by standard makefile targets and may be specified
|
||||
# on the command line to select a particular version of httpd.js. If not
|
||||
# specified, try to select the one from hostutils.zip, as required in bug 882932.
|
||||
if not options.httpdPath:
|
||||
options.httpdPath = os.path.join(options.utilityPath, "components")
|
||||
|
||||
xpcshell_path = os.path.join(options.utilityPath, xpcshell)
|
||||
if localAutomation.elf_arm(xpcshell_path):
|
||||
|
@ -320,27 +319,26 @@ class MochiRemote(Mochitest):
|
|||
'to a desktop version.' % xpcshell_path)
|
||||
sys.exit(1)
|
||||
|
||||
options.profilePath = tempfile.mkdtemp()
|
||||
self.server = MochitestServer(options)
|
||||
self.server.start()
|
||||
|
||||
if (options.pidFile != ""):
|
||||
f = open(options.pidFile + ".xpcshell.pid", 'w')
|
||||
f.write("%s" % self.server._process.pid)
|
||||
f.close()
|
||||
self.server.ensureReady(self.SERVER_STARTUP_TIMEOUT)
|
||||
|
||||
options.xrePath = remoteXrePath
|
||||
options.utilityPath = remoteUtilityPath
|
||||
options.profilePath = remoteProfilePath
|
||||
|
||||
def stopWebServer(self, options):
|
||||
if hasattr(self, 'server'):
|
||||
self.server.stop()
|
||||
|
||||
def buildProfile(self, options):
|
||||
if self.localProfile:
|
||||
options.profilePath = self.localProfile
|
||||
else:
|
||||
options.profilePath = tempfile.mkdtemp()
|
||||
|
||||
def fixup():
|
||||
options.xrePath = remoteXrePath
|
||||
options.utilityPath = remoteUtilityPath
|
||||
options.profilePath = remoteProfilePath
|
||||
|
||||
return fixup
|
||||
|
||||
def startServers(self, options, debuggerInfo):
|
||||
""" Create the servers on the host and start them up """
|
||||
restoreRemotePaths = self.switchToLocalPaths(options)
|
||||
Mochitest.startServers(self, options, debuggerInfo)
|
||||
restoreRemotePaths()
|
||||
|
||||
def buildProfile(self, options):
|
||||
restoreRemotePaths = self.switchToLocalPaths(options)
|
||||
manifest = Mochitest.buildProfile(self, options)
|
||||
self.localProfile = options.profilePath
|
||||
self._dm.removeDir(self.remoteProfile)
|
||||
|
@ -359,9 +357,10 @@ class MochiRemote(Mochitest):
|
|||
log.error("Automation Error: Unable to copy profile to device.")
|
||||
raise
|
||||
|
||||
restoreRemotePaths()
|
||||
options.profilePath = self.remoteProfile
|
||||
return manifest
|
||||
|
||||
|
||||
def buildURLOptions(self, options, env):
|
||||
self.localLog = options.logFile
|
||||
options.logFile = self.remoteLog
|
||||
|
@ -403,7 +402,7 @@ class MochiRemote(Mochitest):
|
|||
|
||||
return manifest
|
||||
|
||||
def getLogFilePath(self, logFile):
|
||||
def getLogFilePath(self, logFile):
|
||||
return logFile
|
||||
|
||||
# In the future we could use LogParser: http://hg.mozilla.org/automation/logparser/
|
||||
|
@ -444,7 +443,7 @@ class MochiRemote(Mochitest):
|
|||
failed = 0
|
||||
todo = 0
|
||||
incr = 1
|
||||
logFile = []
|
||||
logFile = []
|
||||
logFile.append("0 INFO SimpleTest START")
|
||||
for line in self.logLines:
|
||||
if line.startswith("INFO TEST-PASS"):
|
||||
|
@ -548,6 +547,10 @@ class MochiRemote(Mochitest):
|
|||
if 'profileDir' not in kwargs and 'profile' in kwargs:
|
||||
kwargs['profileDir'] = kwargs.pop('profile').profile
|
||||
|
||||
# We're handling ssltunnel, so we should lie to automation.py to avoid
|
||||
# it trying to set up ssltunnel as well
|
||||
kwargs['runSSLTunnel'] = False
|
||||
|
||||
return self._automation.runApp(*args, **kwargs)
|
||||
|
||||
def main():
|
||||
|
@ -580,7 +583,7 @@ def main():
|
|||
options = parser.verifyOptions(options, mochitest)
|
||||
if (options == None):
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
logParent = os.path.dirname(options.remoteLogFile)
|
||||
dm.mkDir(logParent);
|
||||
auto.setRemoteLog(options.remoteLogFile)
|
||||
|
@ -668,7 +671,7 @@ def main():
|
|||
|
||||
# If the test is for checking the import from bookmarks then make sure there is data to import
|
||||
if test['name'] == "testImportFromAndroid":
|
||||
|
||||
|
||||
# Get the OS so we can run the insert in the apropriate database and following the correct table schema
|
||||
osInfo = dm.getInfo("os")
|
||||
devOS = " ".join(osInfo['os'])
|
||||
|
@ -706,8 +709,7 @@ def main():
|
|||
except:
|
||||
log.error("Automation Error: Exception caught while running tests")
|
||||
traceback.print_exc()
|
||||
mochitest.stopWebServer(options)
|
||||
mochitest.stopWebSocketServer(options)
|
||||
mochitest.stopServers()
|
||||
try:
|
||||
mochitest.cleanup(None, options)
|
||||
except devicemanager.DMError:
|
||||
|
@ -742,8 +744,7 @@ def main():
|
|||
except:
|
||||
log.error("Automation Error: Exception caught while running tests")
|
||||
traceback.print_exc()
|
||||
mochitest.stopWebServer(options)
|
||||
mochitest.stopWebSocketServer(options)
|
||||
mochitest.stopServers()
|
||||
try:
|
||||
mochitest.cleanup(None, options)
|
||||
except devicemanager.DMError:
|
||||
|
|
Загрузка…
Ссылка в новой задаче