Bug 573478 - Allow remote reftests to start their own httpd.js webserver r=jmaher
authorClint Talbert <ctalbert@mozilla.com>
Thu, 24 Jun 2010 02:32:01 -0700
changeset 46152 68d98f30eda0168041cc8e82dab380e3f47cc748
parent 46151 abddae3485f93dbc0c5ea3f58c74fc6a8a0a0f34
child 46153 96ad49c97c4f7579d0bfb41a3fa3f4826268d9c1
push id14042
push userctalbert@mozilla.com
push dateThu, 24 Jun 2010 09:25:53 +0000
treeherdermozilla-central@68d98f30eda0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjmaher
bugs573478
milestone1.9.3a6pre
first release with
nightly linux32
68d98f30eda0 / 3.7a6pre / 20100624030247 / files
nightly linux64
68d98f30eda0 / 3.7a6pre / 20100624031214 / files
nightly mac
68d98f30eda0 / 3.7a6pre / 20100624031042 / files
nightly win32
68d98f30eda0 / 3.7a6pre / 20100624051723 / files
nightly win64
68d98f30eda0 / 3.7a6pre / 20100624035238 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 573478 - Allow remote reftests to start their own httpd.js webserver r=jmaher
layout/tools/reftest/remotereftest.py
--- a/layout/tools/reftest/remotereftest.py
+++ b/layout/tools/reftest/remotereftest.py
@@ -33,20 +33,20 @@
 # 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 *****
 
 import sys
 import os
 import time
+import tempfile
 
+# We need to know our current directory so that we can serve our test files from it.
 SCRIPT_DIRECTORY = os.path.abspath(os.path.realpath(os.path.dirname(sys.argv[0])))
-sys.path.append(SCRIPT_DIRECTORY)
-#os.chdir(SCRIPT_DIRECTORY)         
 
 from runreftest import RefTest
 from runreftest import ReftestOptions
 from automation import Automation
 from devicemanager import DeviceManager
 from remoteautomation import RemoteAutomation
 
 class RemoteOptions(ReftestOptions):
@@ -125,25 +125,142 @@ class RemoteOptions(ReftestOptions):
             print "ERROR: You must specify the path to the controller xre directory"
             return None
 
         # TODO: Copied from main, but I think these are no longer used in a post xulrunner world
         #options.xrePath = options.remoteTestRoot + self._automation._product + '/xulrunner'
         #options.utilityPath = options.testRoot + self._automation._product + '/bin'
         return options
 
+class ReftestServer:
+    """ Web server used to serve Reftests, for closer fidelity to the real web.
+        It is virtually identical to the server used in mochitest and will only
+        be used for running reftests remotely.
+        Bug xxx has been filed to refactor this wrapper around httpd.js into
+        it's own class and use it in both remote and non-remote testing. """
+
+    def __init__(self, automation, options):
+        self._automation = automation
+        self._utilityPath = options.utilityPath
+        self._xrePath = options.xrePath
+        self._profileDir = options.serverProfilePath
+        self.webServer = options.remoteWebServer
+        self.httpPort = options.httpPort
+        self.shutdownURL = "http://%(server)s:%(port)s/server/shutdown" % { "server" : self.webServer, "port" : self.httpPort }
+
+    def start(self):
+        "Run the Refest server, returning the process ID of the server."
+          
+        env = self._automation.environment(xrePath = self._xrePath)
+        env["XPCOM_DEBUG_BREAK"] = "warn"
+        if self._automation.IS_WIN32:
+            env["PATH"] = env["PATH"] + ";" + self._xrePath
+
+        args = ["-g", self._xrePath,
+                "-v", "170",
+                "-f", "./" + "httpd.js",
+                "-e", "const _PROFILE_PATH = '%(profile)s';const _SERVER_PORT = '%(port)s'; const _SERVER_ADDR ='%(server)s';" % 
+                       {"profile" : self._profileDir.replace('\\', '\\\\'), "port" : self.httpPort, "server" : self.webServer },
+                "-f", "./" + "server.js"]
+
+        xpcshell = os.path.join(self._utilityPath,
+                                "xpcshell" + self._automation.BIN_SUFFIX)
+        self._process = self._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 | remotereftests.py | Server pid: %d", pid)
+
+    def ensureReady(self, timeout):
+        assert timeout >= 0
+
+        aliveFile = os.path.join(self._profileDir, "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."
+            self.stop()
+            sys.exit(1)
+
+    def stop(self):
+        try:
+            c = urllib2.urlopen(self.shutdownURL)
+            c.read()
+            c.close()
+
+            rtncode = self._process.poll()
+            if (rtncode == None):
+                self._process.terminate()
+        except:
+            self._process.kill()
+
 class RemoteReftest(RefTest):
     remoteApp = ''
 
     def __init__(self, automation, devicemanager, options, scriptDir):
         RefTest.__init__(self, automation)
         self._devicemanager = devicemanager
         self.scriptDir = scriptDir
         self.remoteApp = options.app
+        self.remoteProfile = options.remoteProfile
         self.remoteTestRoot = options.remoteTestRoot
+        if self.automation.IS_DEBUG_BUILD:
+            self.SERVER_STARTUP_TIMEOUT = 180
+        else:
+            self.SERVER_STARTUP_TIMEOUT = 90
+
+    def findPath(self, paths, filename = None):
+        for path in paths:
+            p = path
+            if filename:
+                p = os.path.join(p, filename)
+            if os.path.exists(self.getFullPath(p)):
+                return path
+        return None
+
+    def startWebServer(self, options):
+        """ Create the webserver on the host and start it up """
+        remoteXrePath = options.xrePath
+        remoteUtilityPath = options.utilityPath
+        localAutomation = Automation()
+
+        paths = [options.xrePath, localAutomation.DIST_BIN, self.automation._product, os.path.join('..', self.automation._product)]
+        options.xrePath = self.findPath(paths)
+        if options.xrePath == None:
+            print "ERROR: unable to find xulrunner path for %s, please specify with --xre-path" % (os.name)
+            sys.exit(1)
+        paths.append("bin")
+        paths.append(os.path.join("..", "bin"))
+
+        xpcshell = "xpcshell"
+        if (os.name == "nt"):
+            xpcshell += ".exe"
+      
+        if (options.utilityPath):
+            paths.insert(0, options.utilityPath)
+        options.utilityPath = self.findPath(paths, xpcshell)
+        if options.utilityPath == None:
+            print "ERROR: unable to find utility path for %s, please specify with --utility-path" % (os.name)
+            sys.exit(1)
+
+        options.serverProfilePath = tempfile.mkdtemp()
+        self.server = ReftestServer(localAutomation, options)
+        self.server.start()
+
+        self.server.ensureReady(self.SERVER_STARTUP_TIMEOUT)
+        options.xrePath = remoteXrePath
+        options.utilityPath = remoteUtilityPath
+         
+    def stopWebServer(self, options):
+        self.server.stop()
 
     def createReftestProfile(self, options, profileDir):
         RefTest.createReftestProfile(self, options, profileDir)
 
         if (self._devicemanager.pushDir(profileDir, options.remoteProfile) == None):
             raise devicemanager.FileError("Failed to copy profiledir to device")
 
     def copyExtraFilesToProfile(self, options, profileDir):
@@ -164,17 +281,17 @@ class RemoteReftest(RefTest):
                                    maxTime = 20)
         # We don't care to call |processLeakLog()| for this step.
         self.automation.log.info("\nREFTEST INFO | runreftest.py | Performing extension manager registration: end.")
 
     def getManifestPath(self, path):
         return path
 
     def cleanup(self, profileDir):
-        self._devicemanager.removeDir(self.remoteProfileDir)
+        self._devicemanager.removeDir(self.remoteProfile)
         self._devicemanager.removeDir(self.remoteTestRoot)
         RefTest.cleanup(self, profileDir)
 
 def main():
     dm = DeviceManager(None, None)
     automation = RemoteAutomation(dm)
     parser = RemoteOptions(automation)
     options, args = parser.parse_args()
@@ -197,15 +314,18 @@ def main():
 
     automation.setAppName(options.app)
     automation.setRemoteProfile(options.remoteProfile)
     reftest = RemoteReftest(automation, dm, options, SCRIPT_DIRECTORY)
 
     if (options.remoteWebServer == "127.0.0.1"):
         print "Error: remoteWebServer must be non localhost"
         sys.exit(1)
-
+    
+    # Start the webserver
+    reftest.startWebServer(options)
 #an example manifest name to use on the cli
 #    manifest = "http://" + options.remoteWebServer + "/reftests/layout/reftests/reftest-sanity/reftest.list"
     reftest.runTests(args[0], options)
+    reftest.stopWebServer(options)
 
 if __name__ == "__main__":
     main()