Bug 958897 - ssltunnel lives if mochitest killed. r=jmaher
authorKevin Cox <kevincox@kevincox.ca>
Sun, 23 Mar 2014 10:20:32 -0400
changeset 175260 e2bc23496d67
parent 175259 0c0cfce48311
child 175261 1f924b14ed31
push id26485
push userkwierso@gmail.com
push dateWed, 26 Mar 2014 02:55:13 +0000
treeherdermozilla-central@196bf8197122 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjmaher
bugs958897
milestone31.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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
--- a/testing/mochitest/runtests.py
+++ b/testing/mochitest/runtests.py
@@ -902,159 +902,162 @@ class Mochitest(MochitestUtilsMixin):
 
     # build parameters
     is_test_build = mozinfo.info.get('tests_enabled', True)
     bin_suffix = mozinfo.info.get('bin_suffix', '')
 
     # 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
 
   def runTests(self, options, onLaunch=None):
     """ Prepare, configure, run tests and cleanup """
 
     # get debugger info, a dict of:
     # {'path': path to the debugger (string),