Bug 464191 - ssltunnel process persists after Mochitest run. r=jwalden
☠☠ backed out by 49bcf923de32 ☠ ☠
authorbjarne@runitsoft.com
Mon, 12 Jan 2009 11:23:28 -0800
changeset 23771 a2018012b3eebb1a7ed8a027bcedda8c272af0df
parent 23770 62ff51a9286cece65a82c87f5c3039e05f74f037
child 23772 49bcf923de322ded0e2a7665c770baf7e098fff7
push id4718
push userjwalden@mit.edu
push dateFri, 16 Jan 2009 00:37:47 +0000
treeherdermozilla-central@a2018012b3ee [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwalden
bugs464191
milestone1.9.2a1pre
Bug 464191 - ssltunnel process persists after Mochitest run. r=jwalden
build/pgo/automation.py.in
build/pgo/genpgocert.py.in
testing/mochitest/runtests.py.in
--- a/build/pgo/automation.py.in
+++ b/build/pgo/automation.py.in
@@ -36,20 +36,21 @@
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ***** END LICENSE BLOCK *****
 
 import codecs
 from datetime import datetime
 import itertools
 import logging
-import shutil
 import os
 import re
+import shutil
 import signal
+import subprocess
 import sys
 import threading
 
 """
 Runs the browser from a script, and provides useful utilities
 for setting up the browser environment.
 """
 
@@ -98,110 +99,28 @@ handler = logging.StreamHandler(sys.stdo
 log.setLevel(logging.INFO)
 log.addHandler(handler)
 
 
 #################
 # SUBPROCESSING #
 #################
 
-class Process:
+class Process(subprocess.Popen):
   """
-  Represents a subprocess of this process.  We don't just directly use the
-  subprocess module here because we want compatibility with Python 2.3 on
-  non-Windows platforms.  :-(
+  Represents our view of a subprocess.
+  It adds a kill() method which allows it to be stopped explicitly.
   """
 
-  def __init__(self, command, args, env, inputdata = None):
-    """
-    Creates a process representing the execution of the given command, which
-    must be an absolute path, with the given arguments in the given environment.
-    The process is then started.
-    """
-    command = os.path.abspath(command)
+  def kill(self):
     if IS_WIN32:
-      import tempfile
-      import subprocess
-      
-      if inputdata:
-        inputfile = tempfile.TemporaryFile()
-        inputfile.write(inputdata)
-        inputfile.seek(0)
-      else:
-        inputfile = None
-        
-      cmd = [command]
-      cmd.extend(args)
-      p = subprocess.Popen(cmd, env = env,
-                           stdout = subprocess.PIPE,
-                           stderr = subprocess.STDOUT,
-                           stdin = inputfile)
-      self._out = p.stdout
+      pid = "%i" % self.pid
+      subprocess.Popen(["taskkill", "/F", "/PID", pid]).wait()
     else:
-      import popen2
-      cmd = []
-      if env:
-        for (k, v) in env.iteritems():
-          cmd.append(k + "='" + v + "' ")
-          
-      cmd.append("'" + command + "'")
-      cmd.extend(map(lambda x: "'" + x + "'", args))
-      cmd = " ".join(cmd)
-      p = popen2.Popen4(cmd)
-      self._out = p.fromchild
-
-      if inputdata:
-        p.tochild.write(inputdata)
-        p.tochild.close()
-
-    self._process = p
-    self.pid = p.pid
-    
-    self._thread = threading.Thread(target = lambda: self._run())
-    self._thread.start()
-
-  def _run(self):
-    "Continues execution of this process on a separate thread."
-    p = self._process
-    out = self._out
-
-    if IS_WIN32:
-      running = lambda: p.poll() is None
-    else:
-      running = lambda: p.poll() == -1
-
-    # read in lines until the process finishes, then read in any last remaining
-    # buffered lines
-    while running():
-      line = out.readline().rstrip()
-      if len(line) > 0:
-        log.info(line)
-    for line in out:
-      line = line.rstrip()
-      if len(line) > 0:
-        log.info(line)
-    self._status = p.poll()
-
-  def wait(self):
-    "Waits for this process to finish, then returns the process's status."
-    self._thread.join()
-    return self._status
-
-  def kill(self):
-    "Kills this process."
-    try:
-      if not IS_WIN32:
-        os.kill(self._process.pid, signal.SIGKILL)
-      else:
-        import subprocess
-        pid = "%i" % self.pid
-        process = subprocess.Popen(["taskkill", "/F", "/PID", pid])
-        process.wait()
-    except:
-      pass
+      os.kill(self.pid, signal.SIGTERM)
 
 
 #################
 # PROFILE SETUP #
 #################
 
 class SyntaxError(Exception):
   "Signifies a syntax error on a particular line in server-locations.txt."
@@ -427,31 +346,36 @@ def fillCertificateDB(profileDir):
               (loc.host, loc.port, clientauth))
 
   sslTunnelConfig.close()
 
   # Pre-create the certification database for the profile
   certutil = DIST_BIN + "/certutil" + BIN_SUFFIX
   pk12util = DIST_BIN + "/pk12util" + BIN_SUFFIX
 
-  status = Process(certutil, ["-N", "-d", profileDir, "-f", pwfilePath], environment()).wait()
+  status = Process([certutil, "-N", "-d", profileDir, "-f", pwfilePath], env = environment()).wait()
   if status != 0:
     return status
 
   # Walk the cert directory and add custom CAs and client certs
   files = os.listdir(CERTS_DIR)
   for item in files:
     root, ext = os.path.splitext(item)
     if ext == ".ca":
-      Process(certutil, ["-A", "-i", os.path.join(CERTS_DIR, item),
-        "-d", profileDir, "-f", pwfilePath, "-n", root, "-t", "CT,,"],
-        environment()).wait()
+      args = ["-A", "-i", os.path.join(CERTS_DIR, item),
+              "-d", profileDir,
+              "-f", pwfilePath,
+              "-n", root,
+              "-t", "CT,,"]
+      Process([certutil] + args, env = environment()).wait()
     if ext == ".client":
-      Process(pk12util, ["-i", os.path.join(CERTS_DIR, item), "-w", pwfilePath,
-        "-d", profileDir], environment()).wait()
+      args = ["-i", os.path.join(CERTS_DIR, item),
+              "-w", pwfilePath,
+              "-d", profileDir]
+      Process([pk12util] + args, env = environment()).wait()
 
   os.unlink(pwfilePath)
   return 0
 
 def environment(env = None):
   if env == None:
     env = dict(os.environ)
 
@@ -473,17 +397,18 @@ def runApp(testURL, env, app, profileDir
     # create certificate database for the profile
     certificateStatus = fillCertificateDB(profileDir)
     if certificateStatus != 0:
       log.info("ERROR FAIL Certificate integration")
       return certificateStatus
   
     # start ssltunnel to provide https:// URLs capability
     ssltunnel = DIST_BIN + "/ssltunnel" + BIN_SUFFIX
-    ssltunnelProcess = Process(ssltunnel, [os.path.join(CERTS_DIR, "ssltunnel.cfg")], environment())
+    args = [os.path.join(CERTS_DIR, "ssltunnel.cfg")]
+    ssltunnelProcess = Process([ssltunnel] + args, env = environment())
     log.info("SSL tunnel pid: %d", ssltunnelProcess.pid)
   
   "Run the app, returning the time at which it was started."
   # mark the start
   start = datetime.now()
 
   # now run with the profile we created
   cmd = app
@@ -501,17 +426,17 @@ def runApp(testURL, env, app, profileDir
     profileDirectory = profileDir + "/"
 
   args.extend(("-no-remote", "-profile", profileDirectory))
   if IS_CAMINO:
     args.extend(("-url", testURL))
   else:
     args.append((testURL))
   args.extend(extraArgs)
-  proc = Process(cmd, args, env = environment(env))
+  proc = Process([cmd] + args, env = environment(env))
   log.info("Application pid: %d", proc.pid)
   status = proc.wait()
   if status != 0:
     log.info("ERROR FAIL Exited with code %d during test run", status)
 
   if (IS_TEST_BUILD):
     ssltunnelProcess.kill()
   
--- a/build/pgo/genpgocert.py.in
+++ b/build/pgo/genpgocert.py.in
@@ -72,18 +72,21 @@ def installDbFiles(path, dest):
   for root, dirs, files in os.walk(path):
     for name in files:
       for dbFile in dbFiles:
         if dbFile.match(name):
           shutil.copy(os.path.join(root, name), os.path.join(dest, name))
 
 
 def runUtil(util, args, inputdata = None):
-  proc = automation.Process(util, args, automation.environment(), inputdata)
-  return proc.wait()
+  if inputdata:
+    proc = automation.Process([util] + args, env = automation.environment(), stdin = automation.PIPE)
+    proc.communicate(inputdata)
+    return proc.returncode
+  return automation.Process([util] + args, env = automation.environment()).wait()
 
 
 def createRandomFile(randomFile):
   import random
   file = open(randomFile, "wb");
   for count in xrange(0, 2048):
     file.write(chr(random.randint(0, 255)))
   file.close()
--- a/testing/mochitest/runtests.py.in
+++ b/testing/mochitest/runtests.py.in
@@ -217,17 +217,17 @@ class MochitestServer:
       env["MOZILLA_FIVE_HOME"] = automation.DIST_BIN
       env["XPCOM_DEBUG_BREAK"] = "warn"
 
     args = ["-v", "170",
             "-f", "./" + "httpd.js",
             "-f", "./" + "server.js"]
 
     xpcshell = automation.DIST_BIN + "/" + "xpcshell";
-    self._process = automation.Process(xpcshell, args, env = env)
+    self._process = automation.Process([xpcshell] + args, env = env)
     pid = self._process.pid
     if pid < 0:
       print "Error starting server."
       sys.exit(2)
     log.info("Server pid: %d", pid)
     
 
   def ensureReady(self, timeout):