From a844a7ae48863be939f7311764787a4eea67cdef Mon Sep 17 00:00:00 2001 From: Kevin Cox Date: Sun, 23 Mar 2014 10:20:32 -0400 Subject: [PATCH] Bug 958897 - ssltunnel lives if mochitest killed. r=jmaher [PATCH] Made Mochitest.runApp() clean up after itself in case of exception --- testing/mochitest/runtests.py | 245 +++++++++++++++++----------------- 1 file changed, 124 insertions(+), 121 deletions(-) diff --git a/testing/mochitest/runtests.py b/testing/mochitest/runtests.py index bfa2133bab0b..6497cce04fe8 100644 --- a/testing/mochitest/runtests.py +++ b/testing/mochitest/runtests.py @@ -907,149 +907,152 @@ class Mochitest(MochitestUtilsMixin): # copy env so we don't munge the caller's environment env = env.copy() - # set process log environment variable - tmpfd, processLog = tempfile.mkstemp(suffix='pidlog') - os.close(tmpfd) - env["MOZ_PROCESS_LOG"] = processLog + # make sure we clean up after ourselves. + try: + # set process log environment variable + tmpfd, processLog = tempfile.mkstemp(suffix='pidlog') + os.close(tmpfd) + env["MOZ_PROCESS_LOG"] = processLog - if self.runSSLTunnel: + 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 + # 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], - env=environment(xrePath=xrePath)) - ssltunnelProcess.run() - log.info("INFO | runtests.py | SSL tunnel pid: %d", ssltunnelProcess.pid) - else: - ssltunnelProcess = None + # 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], + 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. - timeout = None - signal.signal(signal.SIGINT, lambda sigid, frame: None) + if interactive: + # If an interactive debugger is attached, + # don't use timeouts, and don't capture ctrl-c. + timeout = None + signal.signal(signal.SIGINT, lambda sigid, frame: None) - # build command line - cmd = os.path.abspath(app) - args = list(extraArgs) - # TODO: mozrunner should use -foreground at least for mac - # https://bugzilla.mozilla.org/show_bug.cgi?id=916512 - args.append('-foreground') - if testUrl: - if debuggerInfo and debuggerInfo['requiresEscapedArgs']: - testUrl = testUrl.replace("&", "\\&") - args.append(testUrl) + # build command line + cmd = os.path.abspath(app) + args = list(extraArgs) + # TODO: mozrunner should use -foreground at least for mac + # https://bugzilla.mozilla.org/show_bug.cgi?id=916512 + args.append('-foreground') + if testUrl: + if debuggerInfo and debuggerInfo['requiresEscapedArgs']: + testUrl = testUrl.replace("&", "\\&") + args.append(testUrl) - if mozinfo.info["debug"] and not webapprtChrome: - shutdownLeaks = ShutdownLeaks(log.info) - else: - shutdownLeaks = None + if mozinfo.info["debug"] and not webapprtChrome: + shutdownLeaks = ShutdownLeaks(log.info) + else: + shutdownLeaks = None - # create an instance to process the output - outputHandler = self.OutputHandler(harness=self, - utilityPath=utilityPath, - symbolsPath=symbolsPath, - dump_screen_on_timeout=not debuggerInfo, - hide_subtests=hide_subtests, - shutdownLeaks=shutdownLeaks, - ) + # create an instance to process the output + outputHandler = self.OutputHandler(harness=self, + utilityPath=utilityPath, + symbolsPath=symbolsPath, + dump_screen_on_timeout=not debuggerInfo, + hide_subtests=hide_subtests, + shutdownLeaks=shutdownLeaks, + ) - def timeoutHandler(): - outputHandler.log_output_buffer() - browserProcessId = outputHandler.browserProcessId - self.handleTimeout(timeout, proc, utilityPath, debuggerInfo, browserProcessId) - kp_kwargs = {'kill_on_timeout': False, - 'onTimeout': [timeoutHandler]} - kp_kwargs['processOutputLine'] = [outputHandler] + def timeoutHandler(): + outputHandler.log_output_buffer() + browserProcessId = outputHandler.browserProcessId + self.handleTimeout(timeout, proc, utilityPath, debuggerInfo, browserProcessId) + kp_kwargs = {'kill_on_timeout': False, + 'onTimeout': [timeoutHandler]} + kp_kwargs['processOutputLine'] = [outputHandler] - # create mozrunner instance and start the system under test process - self.lastTestSeen = self.test_name - startTime = datetime.now() + # create mozrunner instance and start the system under test process + self.lastTestSeen = self.test_name + startTime = datetime.now() - # b2g desktop requires FirefoxRunner even though appname is b2g - if mozinfo.info.get('appname') == 'b2g' and mozinfo.info.get('toolkit') != 'gonk': - runner_cls = mozrunner.FirefoxRunner - else: - runner_cls = mozrunner.runners.get(mozinfo.info.get('appname', 'firefox'), - mozrunner.Runner) - runner = runner_cls(profile=self.profile, - binary=cmd, - cmdargs=args, - env=env, - process_class=mozprocess.ProcessHandlerMixin, - kp_kwargs=kp_kwargs, - ) + # b2g desktop requires FirefoxRunner even though appname is b2g + if mozinfo.info.get('appname') == 'b2g' and mozinfo.info.get('toolkit') != 'gonk': + runner_cls = mozrunner.FirefoxRunner + else: + runner_cls = mozrunner.runners.get(mozinfo.info.get('appname', 'firefox'), + mozrunner.Runner) + runner = runner_cls(profile=self.profile, + binary=cmd, + cmdargs=args, + env=env, + process_class=mozprocess.ProcessHandlerMixin, + kp_kwargs=kp_kwargs, + ) - # XXX work around bug 898379 until mozrunner is updated for m-c; see - # https://bugzilla.mozilla.org/show_bug.cgi?id=746243#c49 - runner.kp_kwargs = kp_kwargs + # XXX work around bug 898379 until mozrunner is updated for m-c; see + # https://bugzilla.mozilla.org/show_bug.cgi?id=746243#c49 + runner.kp_kwargs = kp_kwargs - # start the runner - runner.start(debug_args=debug_args, - interactive=interactive, - outputTimeout=timeout) - proc = runner.process_handler - log.info("INFO | runtests.py | Application pid: %d", proc.pid) + # start the runner + runner.start(debug_args=debug_args, + interactive=interactive, + outputTimeout=timeout) + proc = runner.process_handler + log.info("INFO | runtests.py | Application pid: %d", proc.pid) - if onLaunch is not None: - # Allow callers to specify an onLaunch callback to be fired after the - # app is launched. - # We call onLaunch for b2g desktop mochitests so that we can - # run a Marionette script after gecko has completed startup. - onLaunch() + if onLaunch is not None: + # Allow callers to specify an onLaunch callback to be fired after the + # app is launched. + # We call onLaunch for b2g desktop mochitests so that we can + # run a Marionette script after gecko has completed startup. + onLaunch() - # wait until app is finished - # XXX copy functionality from - # https://github.com/mozilla/mozbase/blob/master/mozrunner/mozrunner/runner.py#L61 - # until bug 913970 is fixed regarding mozrunner `wait` not returning status - # see https://bugzilla.mozilla.org/show_bug.cgi?id=913970 - status = proc.wait() - runner.process_handler = None + # wait until app is finished + # XXX copy functionality from + # https://github.com/mozilla/mozbase/blob/master/mozrunner/mozrunner/runner.py#L61 + # until bug 913970 is fixed regarding mozrunner `wait` not returning status + # see https://bugzilla.mozilla.org/show_bug.cgi?id=913970 + status = proc.wait() + runner.process_handler = None - if timeout is None: - didTimeout = False - else: - didTimeout = proc.didTimeout + if timeout is None: + didTimeout = False + else: + didTimeout = proc.didTimeout - # finalize output handler - outputHandler.finish(didTimeout) + # finalize output handler + outputHandler.finish(didTimeout) - # record post-test information - if status: - log.info("TEST-UNEXPECTED-FAIL | %s | application terminated with exit code %s", self.lastTestSeen, status) - else: - self.lastTestSeen = 'Main app process exited normally' + # record post-test information + if status: + log.info("TEST-UNEXPECTED-FAIL | %s | application terminated with exit code %s", self.lastTestSeen, status) + else: + self.lastTestSeen = 'Main app process exited normally' - log.info("INFO | runtests.py | Application ran for: %s", str(datetime.now() - startTime)) + log.info("INFO | runtests.py | Application ran for: %s", str(datetime.now() - startTime)) - # Do a final check for zombie child processes. - zombieProcesses = self.checkForZombies(processLog, utilityPath, debuggerInfo) + # Do a final check for zombie child processes. + zombieProcesses = self.checkForZombies(processLog, utilityPath, debuggerInfo) - # check for crashes - minidump_path = os.path.join(self.profile.profile, "minidumps") - crashed = mozcrash.check_for_crashes(minidump_path, - symbolsPath, - test_name=self.lastTestSeen) + # check for crashes + minidump_path = os.path.join(self.profile.profile, "minidumps") + crashed = mozcrash.check_for_crashes(minidump_path, + symbolsPath, + test_name=self.lastTestSeen) - if crashed or zombieProcesses: - status = 1 + if crashed or zombieProcesses: + status = 1 - # cleanup - if os.path.exists(processLog): - os.remove(processLog) - if ssltunnelProcess: - ssltunnelProcess.kill() + finally: + # cleanup + if os.path.exists(processLog): + os.remove(processLog) + if ssltunnelProcess: + ssltunnelProcess.kill() return status