1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/testing/xpcshell/remotexpcshelltests.py Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,610 @@ 1.4 +#!/usr/bin/env python 1.5 +# 1.6 +# This Source Code Form is subject to the terms of the Mozilla Public 1.7 +# License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 +# file, You can obtain one at http://mozilla.org/MPL/2.0/. 1.9 + 1.10 +import posixpath 1.11 +import sys, os 1.12 +import subprocess 1.13 +import runxpcshelltests as xpcshell 1.14 +import tempfile 1.15 +from automationutils import replaceBackSlashes 1.16 +from mozdevice import devicemanagerADB, devicemanagerSUT, devicemanager 1.17 +from zipfile import ZipFile 1.18 +import shutil 1.19 +import mozfile 1.20 +import mozinfo 1.21 + 1.22 +here = os.path.dirname(os.path.abspath(__file__)) 1.23 + 1.24 +def remoteJoin(path1, path2): 1.25 + return posixpath.join(path1, path2) 1.26 + 1.27 +class RemoteXPCShellTestThread(xpcshell.XPCShellTestThread): 1.28 + def __init__(self, *args, **kwargs): 1.29 + xpcshell.XPCShellTestThread.__init__(self, *args, **kwargs) 1.30 + 1.31 + # embed the mobile params from the harness into the TestThread 1.32 + mobileArgs = kwargs.get('mobileArgs') 1.33 + for key in mobileArgs: 1.34 + setattr(self, key, mobileArgs[key]) 1.35 + 1.36 + def buildCmdTestFile(self, name): 1.37 + remoteDir = self.remoteForLocal(os.path.dirname(name)) 1.38 + if remoteDir == self.remoteHere: 1.39 + remoteName = os.path.basename(name) 1.40 + else: 1.41 + remoteName = remoteJoin(remoteDir, os.path.basename(name)) 1.42 + return ['-e', 'const _TEST_FILE = ["%s"];' % 1.43 + replaceBackSlashes(remoteName)] 1.44 + 1.45 + def remoteForLocal(self, local): 1.46 + for mapping in self.pathMapping: 1.47 + if (os.path.abspath(mapping.local) == os.path.abspath(local)): 1.48 + return mapping.remote 1.49 + return local 1.50 + 1.51 + 1.52 + def setupTempDir(self): 1.53 + # make sure the temp dir exists 1.54 + if not self.device.dirExists(self.remoteTmpDir): 1.55 + self.device.mkDir(self.remoteTmpDir) 1.56 + # env var is set in buildEnvironment 1.57 + return self.remoteTmpDir 1.58 + 1.59 + def setupPluginsDir(self): 1.60 + if not os.path.isdir(self.pluginsPath): 1.61 + return None 1.62 + 1.63 + # making sure tmp dir is set up 1.64 + self.setupTempDir() 1.65 + 1.66 + pluginsDir = remoteJoin(self.remoteTmpDir, "plugins") 1.67 + self.device.pushDir(self.pluginsPath, pluginsDir) 1.68 + if self.interactive: 1.69 + self.log.info("TEST-INFO | plugins dir is %s" % pluginsDir) 1.70 + return pluginsDir 1.71 + 1.72 + def setupProfileDir(self): 1.73 + self.device.removeDir(self.profileDir) 1.74 + self.device.mkDir(self.profileDir) 1.75 + if self.interactive or self.singleFile: 1.76 + self.log.info("TEST-INFO | profile dir is %s" % self.profileDir) 1.77 + return self.profileDir 1.78 + 1.79 + def logCommand(self, name, completeCmd, testdir): 1.80 + self.log.info("TEST-INFO | %s | full command: %r" % (name, completeCmd)) 1.81 + self.log.info("TEST-INFO | %s | current directory: %r" % (name, self.remoteHere)) 1.82 + self.log.info("TEST-INFO | %s | environment: %s" % (name, self.env)) 1.83 + 1.84 + def getHeadAndTailFiles(self, test): 1.85 + """Override parent method to find files on remote device.""" 1.86 + def sanitize_list(s, kind): 1.87 + for f in s.strip().split(' '): 1.88 + f = f.strip() 1.89 + if len(f) < 1: 1.90 + continue 1.91 + 1.92 + path = remoteJoin(self.remoteHere, f) 1.93 + if not self.device.fileExists(path): 1.94 + raise Exception('%s file does not exist: %s' % ( kind, 1.95 + path)) 1.96 + 1.97 + yield path 1.98 + 1.99 + self.remoteHere = self.remoteForLocal(test['here']) 1.100 + 1.101 + return (list(sanitize_list(test['head'], 'head')), 1.102 + list(sanitize_list(test['tail'], 'tail'))) 1.103 + 1.104 + def buildXpcsCmd(self, testdir): 1.105 + # change base class' paths to remote paths and use base class to build command 1.106 + self.xpcshell = remoteJoin(self.remoteBinDir, "xpcw") 1.107 + self.headJSPath = remoteJoin(self.remoteScriptsDir, 'head.js') 1.108 + self.httpdJSPath = remoteJoin(self.remoteComponentsDir, 'httpd.js') 1.109 + self.httpdManifest = remoteJoin(self.remoteComponentsDir, 'httpd.manifest') 1.110 + self.testingModulesDir = self.remoteModulesDir 1.111 + self.testharnessdir = self.remoteScriptsDir 1.112 + xpcshell.XPCShellTestThread.buildXpcsCmd(self, testdir) 1.113 + # remove "-g <dir> -a <dir>" and add "--greomni <apk>" 1.114 + del(self.xpcsCmd[1:5]) 1.115 + if self.options.localAPK: 1.116 + self.xpcsCmd.insert(3, '--greomni') 1.117 + self.xpcsCmd.insert(4, self.remoteAPK) 1.118 + 1.119 + if self.remoteDebugger: 1.120 + # for example, "/data/local/gdbserver" "localhost:12345" 1.121 + self.xpcsCmd = [ 1.122 + self.remoteDebugger, 1.123 + self.remoteDebuggerArgs, 1.124 + self.xpcsCmd] 1.125 + 1.126 + def testTimeout(self, test_file, proc): 1.127 + self.timedout = True 1.128 + if not self.retry: 1.129 + self.log.error("TEST-UNEXPECTED-FAIL | %s | Test timed out" % test_file) 1.130 + self.kill(proc) 1.131 + 1.132 + def launchProcess(self, cmd, stdout, stderr, env, cwd): 1.133 + self.timedout = False 1.134 + cmd.insert(1, self.remoteHere) 1.135 + outputFile = "xpcshelloutput" 1.136 + with open(outputFile, 'w+') as f: 1.137 + try: 1.138 + self.shellReturnCode = self.device.shell(cmd, f) 1.139 + except devicemanager.DMError as e: 1.140 + if self.timedout: 1.141 + # If the test timed out, there is a good chance the SUTagent also 1.142 + # timed out and failed to return a return code, generating a 1.143 + # DMError. Ignore the DMError to simplify the error report. 1.144 + self.shellReturnCode = None 1.145 + pass 1.146 + else: 1.147 + raise e 1.148 + # The device manager may have timed out waiting for xpcshell. 1.149 + # Guard against an accumulation of hung processes by killing 1.150 + # them here. Note also that IPC tests may spawn new instances 1.151 + # of xpcshell. 1.152 + self.device.killProcess(cmd[0]) 1.153 + self.device.killProcess("xpcshell") 1.154 + return outputFile 1.155 + 1.156 + def checkForCrashes(self, 1.157 + dump_directory, 1.158 + symbols_path, 1.159 + test_name=None): 1.160 + if not self.device.dirExists(self.remoteMinidumpDir): 1.161 + # The minidumps directory is automatically created when Fennec 1.162 + # (first) starts, so its lack of presence is a hint that 1.163 + # something went wrong. 1.164 + print "Automation Error: No crash directory (%s) found on remote device" % self.remoteMinidumpDir 1.165 + # Whilst no crash was found, the run should still display as a failure 1.166 + return True 1.167 + with mozfile.TemporaryDirectory() as dumpDir: 1.168 + self.device.getDirectory(self.remoteMinidumpDir, dumpDir) 1.169 + crashed = xpcshell.XPCShellTestThread.checkForCrashes(self, dumpDir, symbols_path, test_name) 1.170 + self.device.removeDir(self.remoteMinidumpDir) 1.171 + self.device.mkDir(self.remoteMinidumpDir) 1.172 + return crashed 1.173 + 1.174 + def communicate(self, proc): 1.175 + f = open(proc, "r") 1.176 + contents = f.read() 1.177 + f.close() 1.178 + os.remove(proc) 1.179 + return contents, "" 1.180 + 1.181 + def poll(self, proc): 1.182 + if self.device.processExist("xpcshell") is None: 1.183 + return self.getReturnCode(proc) 1.184 + # Process is still running 1.185 + return None 1.186 + 1.187 + def kill(self, proc): 1.188 + return self.device.killProcess("xpcshell", True) 1.189 + 1.190 + def getReturnCode(self, proc): 1.191 + if self.shellReturnCode is not None: 1.192 + return self.shellReturnCode 1.193 + else: 1.194 + return -1 1.195 + 1.196 + def removeDir(self, dirname): 1.197 + self.device.removeDir(dirname) 1.198 + 1.199 + #TODO: consider creating a separate log dir. We don't have the test file structure, 1.200 + # so we use filename.log. Would rather see ./logs/filename.log 1.201 + def createLogFile(self, test, stdout): 1.202 + try: 1.203 + f = None 1.204 + filename = test.replace('\\', '/').split('/')[-1] + ".log" 1.205 + f = open(filename, "w") 1.206 + f.write(stdout) 1.207 + 1.208 + finally: 1.209 + if f is not None: 1.210 + f.close() 1.211 + 1.212 + 1.213 +# A specialization of XPCShellTests that runs tests on an Android device 1.214 +# via devicemanager. 1.215 +class XPCShellRemote(xpcshell.XPCShellTests, object): 1.216 + 1.217 + def __init__(self, devmgr, options, args, log=None): 1.218 + xpcshell.XPCShellTests.__init__(self, log) 1.219 + 1.220 + # Add Android version (SDK level) to mozinfo so that manifest entries 1.221 + # can be conditional on android_version. 1.222 + androidVersion = devmgr.shellCheckOutput(['getprop', 'ro.build.version.sdk']) 1.223 + mozinfo.info['android_version'] = androidVersion 1.224 + 1.225 + self.localLib = options.localLib 1.226 + self.localBin = options.localBin 1.227 + self.options = options 1.228 + self.device = devmgr 1.229 + self.pathMapping = [] 1.230 + self.remoteTestRoot = self.device.getTestRoot("xpcshell") 1.231 + # remoteBinDir contains xpcshell and its wrapper script, both of which must 1.232 + # be executable. Since +x permissions cannot usually be set on /mnt/sdcard, 1.233 + # and the test root may be on /mnt/sdcard, remoteBinDir is set to be on 1.234 + # /data/local, always. 1.235 + self.remoteBinDir = "/data/local/xpcb" 1.236 + # Terse directory names are used here ("c" for the components directory) 1.237 + # to minimize the length of the command line used to execute 1.238 + # xpcshell on the remote device. adb has a limit to the number 1.239 + # of characters used in a shell command, and the xpcshell command 1.240 + # line can be quite complex. 1.241 + self.remoteTmpDir = remoteJoin(self.remoteTestRoot, "tmp") 1.242 + self.remoteScriptsDir = self.remoteTestRoot 1.243 + self.remoteComponentsDir = remoteJoin(self.remoteTestRoot, "c") 1.244 + self.remoteModulesDir = remoteJoin(self.remoteTestRoot, "m") 1.245 + self.remoteMinidumpDir = remoteJoin(self.remoteTestRoot, "minidumps") 1.246 + self.profileDir = remoteJoin(self.remoteTestRoot, "p") 1.247 + self.remoteDebugger = options.debugger 1.248 + self.remoteDebuggerArgs = options.debuggerArgs 1.249 + self.testingModulesDir = options.testingModulesDir 1.250 + 1.251 + self.env = {} 1.252 + 1.253 + if self.options.objdir: 1.254 + self.xpcDir = os.path.join(self.options.objdir, "_tests/xpcshell") 1.255 + elif os.path.isdir(os.path.join(here, 'tests')): 1.256 + self.xpcDir = os.path.join(here, 'tests') 1.257 + else: 1.258 + print >> sys.stderr, "Couldn't find local xpcshell test directory" 1.259 + sys.exit(1) 1.260 + 1.261 + if options.localAPK: 1.262 + self.localAPKContents = ZipFile(options.localAPK) 1.263 + if options.setup: 1.264 + self.setupUtilities() 1.265 + self.setupModules() 1.266 + self.setupTestDir() 1.267 + self.setupMinidumpDir() 1.268 + self.remoteAPK = None 1.269 + if options.localAPK: 1.270 + self.remoteAPK = remoteJoin(self.remoteBinDir, os.path.basename(options.localAPK)) 1.271 + self.setAppRoot() 1.272 + 1.273 + # data that needs to be passed to the RemoteXPCShellTestThread 1.274 + self.mobileArgs = { 1.275 + 'device': self.device, 1.276 + 'remoteBinDir': self.remoteBinDir, 1.277 + 'remoteScriptsDir': self.remoteScriptsDir, 1.278 + 'remoteComponentsDir': self.remoteComponentsDir, 1.279 + 'remoteModulesDir': self.remoteModulesDir, 1.280 + 'options': self.options, 1.281 + 'remoteDebugger': self.remoteDebugger, 1.282 + 'pathMapping': self.pathMapping, 1.283 + 'profileDir': self.profileDir, 1.284 + 'remoteTmpDir': self.remoteTmpDir, 1.285 + 'remoteMinidumpDir': self.remoteMinidumpDir, 1.286 + } 1.287 + if self.remoteAPK: 1.288 + self.mobileArgs['remoteAPK'] = self.remoteAPK 1.289 + 1.290 + def setLD_LIBRARY_PATH(self): 1.291 + self.env["LD_LIBRARY_PATH"] = self.remoteBinDir 1.292 + 1.293 + def pushWrapper(self): 1.294 + # Rather than executing xpcshell directly, this wrapper script is 1.295 + # used. By setting environment variables and the cwd in the script, 1.296 + # the length of the per-test command line is shortened. This is 1.297 + # often important when using ADB, as there is a limit to the length 1.298 + # of the ADB command line. 1.299 + localWrapper = tempfile.mktemp() 1.300 + f = open(localWrapper, "w") 1.301 + f.write("#!/system/bin/sh\n") 1.302 + for envkey, envval in self.env.iteritems(): 1.303 + f.write("export %s=%s\n" % (envkey, envval)) 1.304 + f.write("cd $1\n") 1.305 + f.write("echo xpcw: cd $1\n") 1.306 + f.write("shift\n") 1.307 + f.write("echo xpcw: xpcshell \"$@\"\n") 1.308 + f.write("%s/xpcshell \"$@\"\n" % self.remoteBinDir) 1.309 + f.close() 1.310 + remoteWrapper = remoteJoin(self.remoteBinDir, "xpcw") 1.311 + self.device.pushFile(localWrapper, remoteWrapper) 1.312 + os.remove(localWrapper) 1.313 + self.device.chmodDir(self.remoteBinDir) 1.314 + 1.315 + def buildEnvironment(self): 1.316 + self.buildCoreEnvironment() 1.317 + self.setLD_LIBRARY_PATH() 1.318 + self.env["MOZ_LINKER_CACHE"] = self.remoteBinDir 1.319 + if self.options.localAPK and self.appRoot: 1.320 + self.env["GRE_HOME"] = self.appRoot 1.321 + self.env["XPCSHELL_TEST_PROFILE_DIR"] = self.profileDir 1.322 + self.env["TMPDIR"] = self.remoteTmpDir 1.323 + self.env["HOME"] = self.profileDir 1.324 + self.env["XPCSHELL_TEST_TEMP_DIR"] = self.remoteTmpDir 1.325 + self.env["XPCSHELL_MINIDUMP_DIR"] = self.remoteMinidumpDir 1.326 + if self.options.setup: 1.327 + self.pushWrapper() 1.328 + 1.329 + def setAppRoot(self): 1.330 + # Determine the application root directory associated with the package 1.331 + # name used by the Fennec APK. 1.332 + self.appRoot = None 1.333 + packageName = None 1.334 + if self.options.localAPK: 1.335 + try: 1.336 + packageName = self.localAPKContents.read("package-name.txt") 1.337 + if packageName: 1.338 + self.appRoot = self.device.getAppRoot(packageName.strip()) 1.339 + except Exception as detail: 1.340 + print "unable to determine app root: " + str(detail) 1.341 + pass 1.342 + return None 1.343 + 1.344 + def setupUtilities(self): 1.345 + if (not self.device.dirExists(self.remoteBinDir)): 1.346 + # device.mkDir may fail here where shellCheckOutput may succeed -- see bug 817235 1.347 + try: 1.348 + self.device.shellCheckOutput(["mkdir", self.remoteBinDir]); 1.349 + except devicemanager.DMError: 1.350 + # Might get a permission error; try again as root, if available 1.351 + self.device.shellCheckOutput(["mkdir", self.remoteBinDir], root=True); 1.352 + self.device.shellCheckOutput(["chmod", "777", self.remoteBinDir], root=True); 1.353 + 1.354 + remotePrefDir = remoteJoin(self.remoteBinDir, "defaults/pref") 1.355 + if (self.device.dirExists(self.remoteTmpDir)): 1.356 + self.device.removeDir(self.remoteTmpDir) 1.357 + self.device.mkDir(self.remoteTmpDir) 1.358 + if (not self.device.dirExists(remotePrefDir)): 1.359 + self.device.mkDirs(remoteJoin(remotePrefDir, "extra")) 1.360 + if (not self.device.dirExists(self.remoteScriptsDir)): 1.361 + self.device.mkDir(self.remoteScriptsDir) 1.362 + if (not self.device.dirExists(self.remoteComponentsDir)): 1.363 + self.device.mkDir(self.remoteComponentsDir) 1.364 + 1.365 + local = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'head.js') 1.366 + remoteFile = remoteJoin(self.remoteScriptsDir, "head.js") 1.367 + self.device.pushFile(local, remoteFile) 1.368 + 1.369 + local = os.path.join(self.localBin, "xpcshell") 1.370 + remoteFile = remoteJoin(self.remoteBinDir, "xpcshell") 1.371 + self.device.pushFile(local, remoteFile) 1.372 + 1.373 + local = os.path.join(self.localBin, "components/httpd.js") 1.374 + remoteFile = remoteJoin(self.remoteComponentsDir, "httpd.js") 1.375 + self.device.pushFile(local, remoteFile) 1.376 + 1.377 + local = os.path.join(self.localBin, "components/httpd.manifest") 1.378 + remoteFile = remoteJoin(self.remoteComponentsDir, "httpd.manifest") 1.379 + self.device.pushFile(local, remoteFile) 1.380 + 1.381 + local = os.path.join(self.localBin, "components/test_necko.xpt") 1.382 + remoteFile = remoteJoin(self.remoteComponentsDir, "test_necko.xpt") 1.383 + self.device.pushFile(local, remoteFile) 1.384 + 1.385 + if self.options.localAPK: 1.386 + remoteFile = remoteJoin(self.remoteBinDir, os.path.basename(self.options.localAPK)) 1.387 + self.device.pushFile(self.options.localAPK, remoteFile) 1.388 + 1.389 + self.pushLibs() 1.390 + 1.391 + def pushLibs(self): 1.392 + pushed_libs_count = 0 1.393 + if self.options.localAPK: 1.394 + try: 1.395 + dir = tempfile.mkdtemp() 1.396 + szip = os.path.join(self.localBin, '..', 'host', 'bin', 'szip') 1.397 + if not os.path.exists(szip): 1.398 + # Tinderbox builds must run szip from the test package 1.399 + szip = os.path.join(self.localBin, 'host', 'szip') 1.400 + if not os.path.exists(szip): 1.401 + # If the test package doesn't contain szip, it means files 1.402 + # are not szipped in the test package. 1.403 + szip = None 1.404 + for info in self.localAPKContents.infolist(): 1.405 + if info.filename.endswith(".so"): 1.406 + print >> sys.stderr, "Pushing %s.." % info.filename 1.407 + remoteFile = remoteJoin(self.remoteBinDir, os.path.basename(info.filename)) 1.408 + self.localAPKContents.extract(info, dir) 1.409 + file = os.path.join(dir, info.filename) 1.410 + if szip: 1.411 + out = subprocess.check_output([szip, '-d', file], stderr=subprocess.STDOUT) 1.412 + self.device.pushFile(os.path.join(dir, info.filename), remoteFile) 1.413 + pushed_libs_count += 1 1.414 + finally: 1.415 + shutil.rmtree(dir) 1.416 + return pushed_libs_count 1.417 + 1.418 + for file in os.listdir(self.localLib): 1.419 + if (file.endswith(".so")): 1.420 + print >> sys.stderr, "Pushing %s.." % file 1.421 + if 'libxul' in file: 1.422 + print >> sys.stderr, "This is a big file, it could take a while." 1.423 + remoteFile = remoteJoin(self.remoteBinDir, file) 1.424 + self.device.pushFile(os.path.join(self.localLib, file), remoteFile) 1.425 + pushed_libs_count += 1 1.426 + 1.427 + # Additional libraries may be found in a sub-directory such as "lib/armeabi-v7a" 1.428 + localArmLib = os.path.join(self.localLib, "lib") 1.429 + if os.path.exists(localArmLib): 1.430 + for root, dirs, files in os.walk(localArmLib): 1.431 + for file in files: 1.432 + if (file.endswith(".so")): 1.433 + print >> sys.stderr, "Pushing %s.." % file 1.434 + remoteFile = remoteJoin(self.remoteBinDir, file) 1.435 + self.device.pushFile(os.path.join(root, file), remoteFile) 1.436 + pushed_libs_count += 1 1.437 + 1.438 + return pushed_libs_count 1.439 + 1.440 + def setupModules(self): 1.441 + if self.testingModulesDir: 1.442 + self.device.pushDir(self.testingModulesDir, self.remoteModulesDir) 1.443 + 1.444 + def setupTestDir(self): 1.445 + print 'pushing %s' % self.xpcDir 1.446 + try: 1.447 + self.device.pushDir(self.xpcDir, self.remoteScriptsDir, retryLimit=10) 1.448 + except TypeError: 1.449 + # Foopies have an older mozdevice ver without retryLimit 1.450 + self.device.pushDir(self.xpcDir, self.remoteScriptsDir) 1.451 + 1.452 + def setupMinidumpDir(self): 1.453 + if self.device.dirExists(self.remoteMinidumpDir): 1.454 + self.device.removeDir(self.remoteMinidumpDir) 1.455 + self.device.mkDir(self.remoteMinidumpDir) 1.456 + 1.457 + def buildTestList(self): 1.458 + xpcshell.XPCShellTests.buildTestList(self) 1.459 + uniqueTestPaths = set([]) 1.460 + for test in self.alltests: 1.461 + uniqueTestPaths.add(test['here']) 1.462 + for testdir in uniqueTestPaths: 1.463 + abbrevTestDir = os.path.relpath(testdir, self.xpcDir) 1.464 + remoteScriptDir = remoteJoin(self.remoteScriptsDir, abbrevTestDir) 1.465 + self.pathMapping.append(PathMapping(testdir, remoteScriptDir)) 1.466 + 1.467 +class RemoteXPCShellOptions(xpcshell.XPCShellOptions): 1.468 + 1.469 + def __init__(self): 1.470 + xpcshell.XPCShellOptions.__init__(self) 1.471 + defaults = {} 1.472 + 1.473 + self.add_option("--deviceIP", action="store", 1.474 + type = "string", dest = "deviceIP", 1.475 + help = "ip address of remote device to test") 1.476 + defaults["deviceIP"] = None 1.477 + 1.478 + self.add_option("--devicePort", action="store", 1.479 + type = "string", dest = "devicePort", 1.480 + help = "port of remote device to test") 1.481 + defaults["devicePort"] = 20701 1.482 + 1.483 + self.add_option("--dm_trans", action="store", 1.484 + type = "string", dest = "dm_trans", 1.485 + help = "the transport to use to communicate with device: [adb|sut]; default=sut") 1.486 + defaults["dm_trans"] = "sut" 1.487 + 1.488 + self.add_option("--objdir", action="store", 1.489 + type = "string", dest = "objdir", 1.490 + help = "local objdir, containing xpcshell binaries") 1.491 + defaults["objdir"] = None 1.492 + 1.493 + self.add_option("--apk", action="store", 1.494 + type = "string", dest = "localAPK", 1.495 + help = "local path to Fennec APK") 1.496 + defaults["localAPK"] = None 1.497 + 1.498 + self.add_option("--noSetup", action="store_false", 1.499 + dest = "setup", 1.500 + help = "do not copy any files to device (to be used only if device is already setup)") 1.501 + defaults["setup"] = True 1.502 + 1.503 + self.add_option("--local-lib-dir", action="store", 1.504 + type = "string", dest = "localLib", 1.505 + help = "local path to library directory") 1.506 + defaults["localLib"] = None 1.507 + 1.508 + self.add_option("--local-bin-dir", action="store", 1.509 + type = "string", dest = "localBin", 1.510 + help = "local path to bin directory") 1.511 + defaults["localBin"] = None 1.512 + 1.513 + self.add_option("--remoteTestRoot", action = "store", 1.514 + type = "string", dest = "remoteTestRoot", 1.515 + help = "remote directory to use as test root (eg. /mnt/sdcard/tests or /data/local/tests)") 1.516 + defaults["remoteTestRoot"] = None 1.517 + 1.518 + self.set_defaults(**defaults) 1.519 + 1.520 + def verifyRemoteOptions(self, options): 1.521 + if options.localLib is None: 1.522 + if options.localAPK and options.objdir: 1.523 + for path in ['dist/fennec', 'fennec/lib']: 1.524 + options.localLib = os.path.join(options.objdir, path) 1.525 + if os.path.isdir(options.localLib): 1.526 + break 1.527 + else: 1.528 + self.error("Couldn't find local library dir, specify --local-lib-dir") 1.529 + elif options.objdir: 1.530 + options.localLib = os.path.join(options.objdir, 'dist/bin') 1.531 + elif os.path.isfile(os.path.join(here, '..', 'bin', 'xpcshell')): 1.532 + # assume tests are being run from a tests.zip 1.533 + options.localLib = os.path.abspath(os.path.join(here, '..', 'bin')) 1.534 + else: 1.535 + self.error("Couldn't find local library dir, specify --local-lib-dir") 1.536 + 1.537 + if options.localBin is None: 1.538 + if options.objdir: 1.539 + for path in ['dist/bin', 'bin']: 1.540 + options.localBin = os.path.join(options.objdir, path) 1.541 + if os.path.isdir(options.localBin): 1.542 + break 1.543 + else: 1.544 + self.error("Couldn't find local binary dir, specify --local-bin-dir") 1.545 + elif os.path.isfile(os.path.join(here, '..', 'bin', 'xpcshell')): 1.546 + # assume tests are being run from a tests.zip 1.547 + options.localBin = os.path.abspath(os.path.join(here, '..', 'bin')) 1.548 + else: 1.549 + self.error("Couldn't find local binary dir, specify --local-bin-dir") 1.550 + return options 1.551 + 1.552 +class PathMapping: 1.553 + 1.554 + def __init__(self, localDir, remoteDir): 1.555 + self.local = localDir 1.556 + self.remote = remoteDir 1.557 + 1.558 +def main(): 1.559 + 1.560 + if sys.version_info < (2,7): 1.561 + print >>sys.stderr, "Error: You must use python version 2.7 or newer but less than 3.0" 1.562 + sys.exit(1) 1.563 + 1.564 + parser = RemoteXPCShellOptions() 1.565 + options, args = parser.parse_args() 1.566 + if not options.localAPK: 1.567 + for file in os.listdir(os.path.join(options.objdir, "dist")): 1.568 + if (file.endswith(".apk") and file.startswith("fennec")): 1.569 + options.localAPK = os.path.join(options.objdir, "dist") 1.570 + options.localAPK = os.path.join(options.localAPK, file) 1.571 + print >>sys.stderr, "using APK: " + options.localAPK 1.572 + break 1.573 + else: 1.574 + print >>sys.stderr, "Error: please specify an APK" 1.575 + sys.exit(1) 1.576 + 1.577 + options = parser.verifyRemoteOptions(options) 1.578 + 1.579 + if len(args) < 1 and options.manifest is None: 1.580 + print >>sys.stderr, """Usage: %s <test dirs> 1.581 + or: %s --manifest=test.manifest """ % (sys.argv[0], sys.argv[0]) 1.582 + sys.exit(1) 1.583 + 1.584 + if (options.dm_trans == "adb"): 1.585 + if (options.deviceIP): 1.586 + dm = devicemanagerADB.DeviceManagerADB(options.deviceIP, options.devicePort, packageName=None, deviceRoot=options.remoteTestRoot) 1.587 + else: 1.588 + dm = devicemanagerADB.DeviceManagerADB(packageName=None, deviceRoot=options.remoteTestRoot) 1.589 + else: 1.590 + dm = devicemanagerSUT.DeviceManagerSUT(options.deviceIP, options.devicePort, deviceRoot=options.remoteTestRoot) 1.591 + if (options.deviceIP == None): 1.592 + print "Error: you must provide a device IP to connect to via the --device option" 1.593 + sys.exit(1) 1.594 + 1.595 + if options.interactive and not options.testPath: 1.596 + print >>sys.stderr, "Error: You must specify a test filename in interactive mode!" 1.597 + sys.exit(1) 1.598 + 1.599 + xpcsh = XPCShellRemote(dm, options, args) 1.600 + 1.601 + # we don't run concurrent tests on mobile 1.602 + options.sequential = True 1.603 + 1.604 + if not xpcsh.runTests(xpcshell='xpcshell', 1.605 + testClass=RemoteXPCShellTestThread, 1.606 + testdirs=args[0:], 1.607 + mobileArgs=xpcsh.mobileArgs, 1.608 + **options.__dict__): 1.609 + sys.exit(1) 1.610 + 1.611 + 1.612 +if __name__ == '__main__': 1.613 + main()