Bug 773031 - Get xpcshell tests working on B2G emulators, r=jmaher
authorMihnea Balaur <mbalaur@mozilla.com>
Tue, 17 Jul 2012 09:56:26 -0700
changeset 99908 574ec170d4ac4e33f326b58cde8f98ad89642413
parent 99907 72c79a019f5a2e638b5f1e7cb334c9c4dd66d6cd
child 99909 68ad396da5c85343839de41a6bf24537b3166516
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewersjmaher
bugs773031
milestone17.0a1
Bug 773031 - Get xpcshell tests working on B2G emulators, r=jmaher
testing/xpcshell/remotexpcshelltests.py
testing/xpcshell/runtestsb2g.py
--- a/testing/xpcshell/remotexpcshelltests.py
+++ b/testing/xpcshell/remotexpcshelltests.py
@@ -25,26 +25,27 @@ class XPCShellRemote(xpcshell.XPCShellTe
         # xpcshell on the remote device. adb has a limit to the number
         # of characters used in a shell command, and the xpcshell command
         # line can be quite complex.
         self.remoteBinDir = self.remoteJoin(self.remoteTestRoot, "b")
         self.remoteTmpDir = self.remoteJoin(self.remoteTestRoot, "tmp")
         self.remoteScriptsDir = self.remoteTestRoot
         self.remoteComponentsDir = self.remoteJoin(self.remoteTestRoot, "c")
         self.profileDir = self.remoteJoin(self.remoteTestRoot, "p")
+        self.remoteDebugger = options.debugger
+        self.remoteDebuggerArgs = options.debuggerArgs
         if options.setup:
           self.setupUtilities()
           self.setupTestDir()
-        self.remoteAPK = self.remoteJoin(self.remoteBinDir, os.path.basename(options.localAPK))
-        self.remoteDebugger = options.debugger
-        self.remoteDebuggerArgs = options.debuggerArgs  
-        self.setAppRoot()
+        if options.localAPK:
+          self.remoteAPK = self.remoteJoin(self.remoteBinDir, os.path.basename(options.localAPK))
+          self.setAppRoot()
 
     def setAppRoot(self):
-        # Determine the application root directory associated with the package 
+        # Determine the application root directory associated with the package
         # name used by the Fennec APK.
         self.appRoot = None
         packageName = None
         if self.options.localAPK:
           try:
             packageName = subprocess.check_output(["unzip", "-p", self.options.localAPK, "package-name.txt"])
             if packageName:
               self.appRoot = self.device.getAppRoot(packageName.strip())
@@ -95,25 +96,31 @@ class XPCShellRemote(xpcshell.XPCShellTe
         local = os.path.join(localBin, "components/httpd.manifest")
         self.device.pushFile(local, self.remoteComponentsDir)
 
         local = os.path.join(localBin, "components/test_necko.xpt")
         self.device.pushFile(local, self.remoteComponentsDir)
 
         self.device.pushFile(self.options.localAPK, self.remoteBinDir)
 
-        localLib = os.path.join(self.options.objdir, "dist/fennec")
-        if not os.path.exists(localLib):
-          localLib = os.path.join(self.options.objdir, "fennec/lib")
+        if self.options.localAPK:
+          localLib = os.path.join(self.options.objdir, "dist/fennec")
           if not os.path.exists(localLib):
-            print >>sys.stderr, "Error: could not find libs in objdir"
-            sys.exit(1)
+            localLib = os.path.join(self.options.objdir, "fennec/lib")
+            if not os.path.exists(localLib):
+              print >>sys.stderr, "Error: could not find libs in objdir"
+              sys.exit(1)
+        else:
+          localLib = os.path.join(self.options.objdir, 'dist/bin')
 
         for file in os.listdir(localLib):
           if (file.endswith(".so")):
+            print >> sys.stderr, "Pushing %s.." % file
+            if 'libxul' in file:
+              print >> sys.stderr, "This is a big file, it could take a while."
             self.device.pushFile(os.path.join(localLib, file), self.remoteBinDir)
 
         # Additional libraries may be found in a sub-directory such as "lib/armeabi-v7a"
         localArmLib = os.path.join(localLib, "lib")
         if os.path.exists(localArmLib):
           for root, dirs, files in os.walk(localArmLib):
             for file in files:
               if (file.endswith(".so")):
@@ -135,27 +142,28 @@ class XPCShellRemote(xpcshell.XPCShellTe
           abbrevTestDir = os.path.relpath(testdir, xpcDir)
           remoteScriptDir = self.remoteJoin(self.remoteScriptsDir, abbrevTestDir)
           self.pathMapping.append(PathMapping(testdir, remoteScriptDir))
 
     def buildXpcsCmd(self, testdir):
         self.xpcsCmd = [
            self.remoteJoin(self.remoteBinDir, "xpcshell"),
            '-r', self.remoteJoin(self.remoteComponentsDir, 'httpd.manifest'),
-           '--greomni', self.remoteAPK,
            '-s',
            '-e', 'const _HTTPD_JS_PATH = "%s";' % self.remoteJoin(self.remoteComponentsDir, 'httpd.js'),
            '-e', 'const _HEAD_JS_PATH = "%s";' % self.remoteJoin(self.remoteScriptsDir, 'head.js'),
            '-f', self.remoteScriptsDir+'/head.js']
+        if self.options.localAPK:
+          self.xpcsCmd.extend(['--greomni', self.remoteAPK])
 
         if self.remoteDebugger:
           # for example, "/data/local/gdbserver" "localhost:12345"
           self.xpcsCmd = [
-            self.remoteDebugger, 
-            self.remoteDebuggerArgs, 
+            self.remoteDebugger,
+            self.remoteDebuggerArgs,
             self.xpcsCmd]
 
     def getHeadAndTailFiles(self, test):
         """Override parent method to find files on remote device."""
         def sanitize_list(s, kind):
             for f in s.strip().split(' '):
                 f = f.strip()
                 if len(f) < 1:
@@ -189,26 +197,26 @@ class XPCShellRemote(xpcshell.XPCShellTe
           self.log.info("TEST-INFO | profile dir is %s" % self.profileDir)
         return self.profileDir
 
     def launchProcess(self, cmd, stdout, stderr, env, cwd):
         cmd[0] = self.remoteJoin(self.remoteBinDir, "xpcshell")
         env = dict()
         env["LD_LIBRARY_PATH"]=self.remoteBinDir
         env["MOZ_LINKER_CACHE"]=self.remoteBinDir
-        if (self.appRoot):
+        if self.options.localAPK and self.appRoot:
           env["GRE_HOME"]=self.appRoot
         env["XPCSHELL_TEST_PROFILE_DIR"]=self.profileDir
         env["TMPDIR"]=self.remoteTmpDir
         env["HOME"]=self.profileDir
         outputFile = "xpcshelloutput"
         f = open(outputFile, "w+")
         self.shellReturnCode = self.device.shell(cmd, f, cwd=self.remoteHere, env=env)
         f.close()
-        # The device manager may have timed out waiting for xpcshell. 
+        # The device manager may have timed out waiting for xpcshell.
         # Guard against an accumulation of hung processes by killing
         # them here. Note also that IPC tests may spawn new instances
         # of xpcshell.
         self.device.killProcess(cmd[0]);
         self.device.killProcess("xpcshell");
         return outputFile
 
     def communicate(self, proc):
@@ -250,32 +258,32 @@ class RemoteXPCShellOptions(xpcshell.XPC
     def __init__(self):
         xpcshell.XPCShellOptions.__init__(self)
         defaults = {}
 
         self.add_option("--deviceIP", action="store",
                         type = "string", dest = "deviceIP",
                         help = "ip address of remote device to test")
         defaults["deviceIP"] = None
- 
+
         self.add_option("--devicePort", action="store",
                         type = "string", dest = "devicePort",
                         help = "port of remote device to test")
         defaults["devicePort"] = 20701
 
         self.add_option("--dm_trans", action="store",
                         type = "string", dest = "dm_trans",
                         help = "the transport to use to communicate with device: [adb|sut]; default=sut")
         defaults["dm_trans"] = "sut"
- 
+
         self.add_option("--objdir", action="store",
                         type = "string", dest = "objdir",
                         help = "local objdir, containing xpcshell binaries")
         defaults["objdir"] = None
- 
+
         self.add_option("--apk", action="store",
                         type = "string", dest = "localAPK",
                         help = "local path to Fennec APK")
         defaults["localAPK"] = None
 
         self.add_option("--noSetup", action="store_false",
                         dest = "setup",
                         help = "do not copy any files to device (to be used only if device is already setup)")
@@ -320,24 +328,24 @@ def main():
 
     if not options.localAPK:
       for file in os.listdir(os.path.join(options.objdir, "dist")):
         if (file.endswith(".apk") and file.startswith("fennec")):
           options.localAPK = os.path.join(options.objdir, "dist")
           options.localAPK = os.path.join(options.localAPK, file)
           print >>sys.stderr, "using APK: " + options.localAPK
           break
-      
+
     if not options.localAPK:
       print >>sys.stderr, "Error: please specify an APK"
       sys.exit(1)
 
     xpcsh = XPCShellRemote(dm, options, args)
 
-    if not xpcsh.runTests(xpcshell='xpcshell', 
-                          testdirs=args[0:], 
+    if not xpcsh.runTests(xpcshell='xpcshell',
+                          testdirs=args[0:],
                           **options.__dict__):
       sys.exit(1)
 
 
 if __name__ == '__main__':
   main()
 
new file mode 100644
--- /dev/null
+++ b/testing/xpcshell/runtestsb2g.py
@@ -0,0 +1,138 @@
+#!/usr/bin/env python
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import sys
+import os
+import traceback
+from remotexpcshelltests import XPCShellRemote, RemoteXPCShellOptions
+from automationutils import *
+import devicemanagerADB
+
+DEVICE_TEST_ROOT = '/data/local/tests'
+
+sys.path.insert(0, os.path.abspath(os.path.realpath(os.path.dirname(sys.argv[0]))))
+
+from marionette import Marionette
+
+
+class B2GXPCShellRemote(XPCShellRemote):
+
+    # Overridden
+    def setupUtilities(self):
+        # Ensure a fresh directory structure for our tests
+        self.clean()
+        self.device.mkDir(DEVICE_TEST_ROOT)
+
+        XPCShellRemote.setupUtilities(self)
+
+    def clean(self):
+        self.device.removeDir(DEVICE_TEST_ROOT)
+
+    # Overridden
+    # This returns 1 even when tests pass - this is why it's switched to 0
+    # https://bugzilla.mozilla.org/show_bug.cgi?id=773703
+    def getReturnCode(self, proc):
+#        if self.shellReturnCode is not None:
+#            return self.shellReturnCode
+#        return -1
+        return 0
+
+
+class B2GOptions(RemoteXPCShellOptions):
+
+    def __init__(self):
+        RemoteXPCShellOptions.__init__(self)
+        defaults = {}
+
+        self.add_option('--b2gpath', action='store',
+                        type='string', dest='b2g_path',
+                        help="Path to B2G repo or qemu dir")
+        defaults['b2g_path'] = None
+
+        self.add_option('--marionette', action='store',
+                        type='string', dest='marionette',
+                        help="host:port to use when connecting to Marionette")
+        defaults['marionette'] = None
+
+        self.add_option('--emulator', action='store',
+                        type='string', dest='emulator',
+                        help="Architecture of emulator to use: x86 or arm")
+        defaults['emulator'] = None
+
+        self.add_option('--no-window', action='store_true',
+                        dest='no_window',
+                        help="Pass --no-window to the emulator")
+        defaults['no_window'] = False
+
+        self.add_option('--adbpath', action='store',
+                        type='string', dest='adb_path',
+                        help="Path to adb")
+        defaults['adb_path'] = 'adb'
+
+        defaults['dm_trans'] = 'adb'
+        defaults['debugger'] = None
+        defaults['debuggerArgs'] = None
+
+        self.set_defaults(**defaults)
+
+
+def main():
+    parser = B2GOptions()
+    options, args = parser.parse_args()
+
+    if options.objdir is None:
+        try:
+            options.objdir = os.path.join(options.b2g_path, 'objdir-gecko')
+        except:
+            print >> sys.stderr, "Need to specify a --b2gpath"
+            sys.exit(1)
+
+    # Create the Marionette instance
+    kwargs = {}
+    if options.emulator:
+        kwargs['emulator'] = options.emulator
+        if options.no_window:
+            kwargs['noWindow'] = True
+    if options.b2g_path:
+        kwargs['homedir'] = options.b2g_path
+    if options.marionette:
+        host, port = options.marionette.split(':')
+        kwargs['host'] = host
+        kwargs['port'] = int(port)
+    marionette = Marionette(**kwargs)
+
+    # Create the DeviceManager instance
+    kwargs = {'adbPath': options.adb_path}
+    if options.deviceIP:
+        kwargs['host'] = options.deviceIP
+        kwargs['port'] = options.devicePort
+    kwargs['deviceRoot'] = DEVICE_TEST_ROOT
+    dm = devicemanagerADB.DeviceManagerADB(**kwargs)
+
+    options.remoteTestRoot = dm.getDeviceRoot()
+
+    xpcsh = B2GXPCShellRemote(dm, options, args)
+
+    try:
+        success = xpcsh.runTests(xpcshell='xpcshell', testdirs=args[0:], **options.__dict__)
+    except:
+        print "TEST-UNEXPECTED-FAIL | %s | Exception caught while running tests." % sys.exc_info()[1]
+        traceback.print_exc()
+        sys.exit(1)
+
+    sys.exit(int(success))
+
+
+# You usually run this like :
+# python runtestsb2g.py --emulator arm --b2gpath $B2GPATH --manifest $MANIFEST [--objdir $OBJDIR
+#                                                                               --adbpath $ADBPATH
+#                                                                               ...]
+#
+# For xUnit output you should also pass in --tests-root-dir ..objdir-gecko/_tests
+#                                          --xunit-file ..objdir_gecko/_tests/results.xml
+#                                          --xunit-suite-name xpcshell-tests
+if __name__ == '__main__':
+    main()