testing/xpcshell/remotexpcshelltests.py

Wed, 31 Dec 2014 06:55:46 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:46 +0100
changeset 1
ca08bd8f51b2
permissions
-rw-r--r--

Added tag TORBROWSER_REPLICA for changeset 6474c204b198

michael@0 1 #!/usr/bin/env python
michael@0 2 #
michael@0 3 # This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 # License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
michael@0 6
michael@0 7 import posixpath
michael@0 8 import sys, os
michael@0 9 import subprocess
michael@0 10 import runxpcshelltests as xpcshell
michael@0 11 import tempfile
michael@0 12 from automationutils import replaceBackSlashes
michael@0 13 from mozdevice import devicemanagerADB, devicemanagerSUT, devicemanager
michael@0 14 from zipfile import ZipFile
michael@0 15 import shutil
michael@0 16 import mozfile
michael@0 17 import mozinfo
michael@0 18
michael@0 19 here = os.path.dirname(os.path.abspath(__file__))
michael@0 20
michael@0 21 def remoteJoin(path1, path2):
michael@0 22 return posixpath.join(path1, path2)
michael@0 23
michael@0 24 class RemoteXPCShellTestThread(xpcshell.XPCShellTestThread):
michael@0 25 def __init__(self, *args, **kwargs):
michael@0 26 xpcshell.XPCShellTestThread.__init__(self, *args, **kwargs)
michael@0 27
michael@0 28 # embed the mobile params from the harness into the TestThread
michael@0 29 mobileArgs = kwargs.get('mobileArgs')
michael@0 30 for key in mobileArgs:
michael@0 31 setattr(self, key, mobileArgs[key])
michael@0 32
michael@0 33 def buildCmdTestFile(self, name):
michael@0 34 remoteDir = self.remoteForLocal(os.path.dirname(name))
michael@0 35 if remoteDir == self.remoteHere:
michael@0 36 remoteName = os.path.basename(name)
michael@0 37 else:
michael@0 38 remoteName = remoteJoin(remoteDir, os.path.basename(name))
michael@0 39 return ['-e', 'const _TEST_FILE = ["%s"];' %
michael@0 40 replaceBackSlashes(remoteName)]
michael@0 41
michael@0 42 def remoteForLocal(self, local):
michael@0 43 for mapping in self.pathMapping:
michael@0 44 if (os.path.abspath(mapping.local) == os.path.abspath(local)):
michael@0 45 return mapping.remote
michael@0 46 return local
michael@0 47
michael@0 48
michael@0 49 def setupTempDir(self):
michael@0 50 # make sure the temp dir exists
michael@0 51 if not self.device.dirExists(self.remoteTmpDir):
michael@0 52 self.device.mkDir(self.remoteTmpDir)
michael@0 53 # env var is set in buildEnvironment
michael@0 54 return self.remoteTmpDir
michael@0 55
michael@0 56 def setupPluginsDir(self):
michael@0 57 if not os.path.isdir(self.pluginsPath):
michael@0 58 return None
michael@0 59
michael@0 60 # making sure tmp dir is set up
michael@0 61 self.setupTempDir()
michael@0 62
michael@0 63 pluginsDir = remoteJoin(self.remoteTmpDir, "plugins")
michael@0 64 self.device.pushDir(self.pluginsPath, pluginsDir)
michael@0 65 if self.interactive:
michael@0 66 self.log.info("TEST-INFO | plugins dir is %s" % pluginsDir)
michael@0 67 return pluginsDir
michael@0 68
michael@0 69 def setupProfileDir(self):
michael@0 70 self.device.removeDir(self.profileDir)
michael@0 71 self.device.mkDir(self.profileDir)
michael@0 72 if self.interactive or self.singleFile:
michael@0 73 self.log.info("TEST-INFO | profile dir is %s" % self.profileDir)
michael@0 74 return self.profileDir
michael@0 75
michael@0 76 def logCommand(self, name, completeCmd, testdir):
michael@0 77 self.log.info("TEST-INFO | %s | full command: %r" % (name, completeCmd))
michael@0 78 self.log.info("TEST-INFO | %s | current directory: %r" % (name, self.remoteHere))
michael@0 79 self.log.info("TEST-INFO | %s | environment: %s" % (name, self.env))
michael@0 80
michael@0 81 def getHeadAndTailFiles(self, test):
michael@0 82 """Override parent method to find files on remote device."""
michael@0 83 def sanitize_list(s, kind):
michael@0 84 for f in s.strip().split(' '):
michael@0 85 f = f.strip()
michael@0 86 if len(f) < 1:
michael@0 87 continue
michael@0 88
michael@0 89 path = remoteJoin(self.remoteHere, f)
michael@0 90 if not self.device.fileExists(path):
michael@0 91 raise Exception('%s file does not exist: %s' % ( kind,
michael@0 92 path))
michael@0 93
michael@0 94 yield path
michael@0 95
michael@0 96 self.remoteHere = self.remoteForLocal(test['here'])
michael@0 97
michael@0 98 return (list(sanitize_list(test['head'], 'head')),
michael@0 99 list(sanitize_list(test['tail'], 'tail')))
michael@0 100
michael@0 101 def buildXpcsCmd(self, testdir):
michael@0 102 # change base class' paths to remote paths and use base class to build command
michael@0 103 self.xpcshell = remoteJoin(self.remoteBinDir, "xpcw")
michael@0 104 self.headJSPath = remoteJoin(self.remoteScriptsDir, 'head.js')
michael@0 105 self.httpdJSPath = remoteJoin(self.remoteComponentsDir, 'httpd.js')
michael@0 106 self.httpdManifest = remoteJoin(self.remoteComponentsDir, 'httpd.manifest')
michael@0 107 self.testingModulesDir = self.remoteModulesDir
michael@0 108 self.testharnessdir = self.remoteScriptsDir
michael@0 109 xpcshell.XPCShellTestThread.buildXpcsCmd(self, testdir)
michael@0 110 # remove "-g <dir> -a <dir>" and add "--greomni <apk>"
michael@0 111 del(self.xpcsCmd[1:5])
michael@0 112 if self.options.localAPK:
michael@0 113 self.xpcsCmd.insert(3, '--greomni')
michael@0 114 self.xpcsCmd.insert(4, self.remoteAPK)
michael@0 115
michael@0 116 if self.remoteDebugger:
michael@0 117 # for example, "/data/local/gdbserver" "localhost:12345"
michael@0 118 self.xpcsCmd = [
michael@0 119 self.remoteDebugger,
michael@0 120 self.remoteDebuggerArgs,
michael@0 121 self.xpcsCmd]
michael@0 122
michael@0 123 def testTimeout(self, test_file, proc):
michael@0 124 self.timedout = True
michael@0 125 if not self.retry:
michael@0 126 self.log.error("TEST-UNEXPECTED-FAIL | %s | Test timed out" % test_file)
michael@0 127 self.kill(proc)
michael@0 128
michael@0 129 def launchProcess(self, cmd, stdout, stderr, env, cwd):
michael@0 130 self.timedout = False
michael@0 131 cmd.insert(1, self.remoteHere)
michael@0 132 outputFile = "xpcshelloutput"
michael@0 133 with open(outputFile, 'w+') as f:
michael@0 134 try:
michael@0 135 self.shellReturnCode = self.device.shell(cmd, f)
michael@0 136 except devicemanager.DMError as e:
michael@0 137 if self.timedout:
michael@0 138 # If the test timed out, there is a good chance the SUTagent also
michael@0 139 # timed out and failed to return a return code, generating a
michael@0 140 # DMError. Ignore the DMError to simplify the error report.
michael@0 141 self.shellReturnCode = None
michael@0 142 pass
michael@0 143 else:
michael@0 144 raise e
michael@0 145 # The device manager may have timed out waiting for xpcshell.
michael@0 146 # Guard against an accumulation of hung processes by killing
michael@0 147 # them here. Note also that IPC tests may spawn new instances
michael@0 148 # of xpcshell.
michael@0 149 self.device.killProcess(cmd[0])
michael@0 150 self.device.killProcess("xpcshell")
michael@0 151 return outputFile
michael@0 152
michael@0 153 def checkForCrashes(self,
michael@0 154 dump_directory,
michael@0 155 symbols_path,
michael@0 156 test_name=None):
michael@0 157 if not self.device.dirExists(self.remoteMinidumpDir):
michael@0 158 # The minidumps directory is automatically created when Fennec
michael@0 159 # (first) starts, so its lack of presence is a hint that
michael@0 160 # something went wrong.
michael@0 161 print "Automation Error: No crash directory (%s) found on remote device" % self.remoteMinidumpDir
michael@0 162 # Whilst no crash was found, the run should still display as a failure
michael@0 163 return True
michael@0 164 with mozfile.TemporaryDirectory() as dumpDir:
michael@0 165 self.device.getDirectory(self.remoteMinidumpDir, dumpDir)
michael@0 166 crashed = xpcshell.XPCShellTestThread.checkForCrashes(self, dumpDir, symbols_path, test_name)
michael@0 167 self.device.removeDir(self.remoteMinidumpDir)
michael@0 168 self.device.mkDir(self.remoteMinidumpDir)
michael@0 169 return crashed
michael@0 170
michael@0 171 def communicate(self, proc):
michael@0 172 f = open(proc, "r")
michael@0 173 contents = f.read()
michael@0 174 f.close()
michael@0 175 os.remove(proc)
michael@0 176 return contents, ""
michael@0 177
michael@0 178 def poll(self, proc):
michael@0 179 if self.device.processExist("xpcshell") is None:
michael@0 180 return self.getReturnCode(proc)
michael@0 181 # Process is still running
michael@0 182 return None
michael@0 183
michael@0 184 def kill(self, proc):
michael@0 185 return self.device.killProcess("xpcshell", True)
michael@0 186
michael@0 187 def getReturnCode(self, proc):
michael@0 188 if self.shellReturnCode is not None:
michael@0 189 return self.shellReturnCode
michael@0 190 else:
michael@0 191 return -1
michael@0 192
michael@0 193 def removeDir(self, dirname):
michael@0 194 self.device.removeDir(dirname)
michael@0 195
michael@0 196 #TODO: consider creating a separate log dir. We don't have the test file structure,
michael@0 197 # so we use filename.log. Would rather see ./logs/filename.log
michael@0 198 def createLogFile(self, test, stdout):
michael@0 199 try:
michael@0 200 f = None
michael@0 201 filename = test.replace('\\', '/').split('/')[-1] + ".log"
michael@0 202 f = open(filename, "w")
michael@0 203 f.write(stdout)
michael@0 204
michael@0 205 finally:
michael@0 206 if f is not None:
michael@0 207 f.close()
michael@0 208
michael@0 209
michael@0 210 # A specialization of XPCShellTests that runs tests on an Android device
michael@0 211 # via devicemanager.
michael@0 212 class XPCShellRemote(xpcshell.XPCShellTests, object):
michael@0 213
michael@0 214 def __init__(self, devmgr, options, args, log=None):
michael@0 215 xpcshell.XPCShellTests.__init__(self, log)
michael@0 216
michael@0 217 # Add Android version (SDK level) to mozinfo so that manifest entries
michael@0 218 # can be conditional on android_version.
michael@0 219 androidVersion = devmgr.shellCheckOutput(['getprop', 'ro.build.version.sdk'])
michael@0 220 mozinfo.info['android_version'] = androidVersion
michael@0 221
michael@0 222 self.localLib = options.localLib
michael@0 223 self.localBin = options.localBin
michael@0 224 self.options = options
michael@0 225 self.device = devmgr
michael@0 226 self.pathMapping = []
michael@0 227 self.remoteTestRoot = self.device.getTestRoot("xpcshell")
michael@0 228 # remoteBinDir contains xpcshell and its wrapper script, both of which must
michael@0 229 # be executable. Since +x permissions cannot usually be set on /mnt/sdcard,
michael@0 230 # and the test root may be on /mnt/sdcard, remoteBinDir is set to be on
michael@0 231 # /data/local, always.
michael@0 232 self.remoteBinDir = "/data/local/xpcb"
michael@0 233 # Terse directory names are used here ("c" for the components directory)
michael@0 234 # to minimize the length of the command line used to execute
michael@0 235 # xpcshell on the remote device. adb has a limit to the number
michael@0 236 # of characters used in a shell command, and the xpcshell command
michael@0 237 # line can be quite complex.
michael@0 238 self.remoteTmpDir = remoteJoin(self.remoteTestRoot, "tmp")
michael@0 239 self.remoteScriptsDir = self.remoteTestRoot
michael@0 240 self.remoteComponentsDir = remoteJoin(self.remoteTestRoot, "c")
michael@0 241 self.remoteModulesDir = remoteJoin(self.remoteTestRoot, "m")
michael@0 242 self.remoteMinidumpDir = remoteJoin(self.remoteTestRoot, "minidumps")
michael@0 243 self.profileDir = remoteJoin(self.remoteTestRoot, "p")
michael@0 244 self.remoteDebugger = options.debugger
michael@0 245 self.remoteDebuggerArgs = options.debuggerArgs
michael@0 246 self.testingModulesDir = options.testingModulesDir
michael@0 247
michael@0 248 self.env = {}
michael@0 249
michael@0 250 if self.options.objdir:
michael@0 251 self.xpcDir = os.path.join(self.options.objdir, "_tests/xpcshell")
michael@0 252 elif os.path.isdir(os.path.join(here, 'tests')):
michael@0 253 self.xpcDir = os.path.join(here, 'tests')
michael@0 254 else:
michael@0 255 print >> sys.stderr, "Couldn't find local xpcshell test directory"
michael@0 256 sys.exit(1)
michael@0 257
michael@0 258 if options.localAPK:
michael@0 259 self.localAPKContents = ZipFile(options.localAPK)
michael@0 260 if options.setup:
michael@0 261 self.setupUtilities()
michael@0 262 self.setupModules()
michael@0 263 self.setupTestDir()
michael@0 264 self.setupMinidumpDir()
michael@0 265 self.remoteAPK = None
michael@0 266 if options.localAPK:
michael@0 267 self.remoteAPK = remoteJoin(self.remoteBinDir, os.path.basename(options.localAPK))
michael@0 268 self.setAppRoot()
michael@0 269
michael@0 270 # data that needs to be passed to the RemoteXPCShellTestThread
michael@0 271 self.mobileArgs = {
michael@0 272 'device': self.device,
michael@0 273 'remoteBinDir': self.remoteBinDir,
michael@0 274 'remoteScriptsDir': self.remoteScriptsDir,
michael@0 275 'remoteComponentsDir': self.remoteComponentsDir,
michael@0 276 'remoteModulesDir': self.remoteModulesDir,
michael@0 277 'options': self.options,
michael@0 278 'remoteDebugger': self.remoteDebugger,
michael@0 279 'pathMapping': self.pathMapping,
michael@0 280 'profileDir': self.profileDir,
michael@0 281 'remoteTmpDir': self.remoteTmpDir,
michael@0 282 'remoteMinidumpDir': self.remoteMinidumpDir,
michael@0 283 }
michael@0 284 if self.remoteAPK:
michael@0 285 self.mobileArgs['remoteAPK'] = self.remoteAPK
michael@0 286
michael@0 287 def setLD_LIBRARY_PATH(self):
michael@0 288 self.env["LD_LIBRARY_PATH"] = self.remoteBinDir
michael@0 289
michael@0 290 def pushWrapper(self):
michael@0 291 # Rather than executing xpcshell directly, this wrapper script is
michael@0 292 # used. By setting environment variables and the cwd in the script,
michael@0 293 # the length of the per-test command line is shortened. This is
michael@0 294 # often important when using ADB, as there is a limit to the length
michael@0 295 # of the ADB command line.
michael@0 296 localWrapper = tempfile.mktemp()
michael@0 297 f = open(localWrapper, "w")
michael@0 298 f.write("#!/system/bin/sh\n")
michael@0 299 for envkey, envval in self.env.iteritems():
michael@0 300 f.write("export %s=%s\n" % (envkey, envval))
michael@0 301 f.write("cd $1\n")
michael@0 302 f.write("echo xpcw: cd $1\n")
michael@0 303 f.write("shift\n")
michael@0 304 f.write("echo xpcw: xpcshell \"$@\"\n")
michael@0 305 f.write("%s/xpcshell \"$@\"\n" % self.remoteBinDir)
michael@0 306 f.close()
michael@0 307 remoteWrapper = remoteJoin(self.remoteBinDir, "xpcw")
michael@0 308 self.device.pushFile(localWrapper, remoteWrapper)
michael@0 309 os.remove(localWrapper)
michael@0 310 self.device.chmodDir(self.remoteBinDir)
michael@0 311
michael@0 312 def buildEnvironment(self):
michael@0 313 self.buildCoreEnvironment()
michael@0 314 self.setLD_LIBRARY_PATH()
michael@0 315 self.env["MOZ_LINKER_CACHE"] = self.remoteBinDir
michael@0 316 if self.options.localAPK and self.appRoot:
michael@0 317 self.env["GRE_HOME"] = self.appRoot
michael@0 318 self.env["XPCSHELL_TEST_PROFILE_DIR"] = self.profileDir
michael@0 319 self.env["TMPDIR"] = self.remoteTmpDir
michael@0 320 self.env["HOME"] = self.profileDir
michael@0 321 self.env["XPCSHELL_TEST_TEMP_DIR"] = self.remoteTmpDir
michael@0 322 self.env["XPCSHELL_MINIDUMP_DIR"] = self.remoteMinidumpDir
michael@0 323 if self.options.setup:
michael@0 324 self.pushWrapper()
michael@0 325
michael@0 326 def setAppRoot(self):
michael@0 327 # Determine the application root directory associated with the package
michael@0 328 # name used by the Fennec APK.
michael@0 329 self.appRoot = None
michael@0 330 packageName = None
michael@0 331 if self.options.localAPK:
michael@0 332 try:
michael@0 333 packageName = self.localAPKContents.read("package-name.txt")
michael@0 334 if packageName:
michael@0 335 self.appRoot = self.device.getAppRoot(packageName.strip())
michael@0 336 except Exception as detail:
michael@0 337 print "unable to determine app root: " + str(detail)
michael@0 338 pass
michael@0 339 return None
michael@0 340
michael@0 341 def setupUtilities(self):
michael@0 342 if (not self.device.dirExists(self.remoteBinDir)):
michael@0 343 # device.mkDir may fail here where shellCheckOutput may succeed -- see bug 817235
michael@0 344 try:
michael@0 345 self.device.shellCheckOutput(["mkdir", self.remoteBinDir]);
michael@0 346 except devicemanager.DMError:
michael@0 347 # Might get a permission error; try again as root, if available
michael@0 348 self.device.shellCheckOutput(["mkdir", self.remoteBinDir], root=True);
michael@0 349 self.device.shellCheckOutput(["chmod", "777", self.remoteBinDir], root=True);
michael@0 350
michael@0 351 remotePrefDir = remoteJoin(self.remoteBinDir, "defaults/pref")
michael@0 352 if (self.device.dirExists(self.remoteTmpDir)):
michael@0 353 self.device.removeDir(self.remoteTmpDir)
michael@0 354 self.device.mkDir(self.remoteTmpDir)
michael@0 355 if (not self.device.dirExists(remotePrefDir)):
michael@0 356 self.device.mkDirs(remoteJoin(remotePrefDir, "extra"))
michael@0 357 if (not self.device.dirExists(self.remoteScriptsDir)):
michael@0 358 self.device.mkDir(self.remoteScriptsDir)
michael@0 359 if (not self.device.dirExists(self.remoteComponentsDir)):
michael@0 360 self.device.mkDir(self.remoteComponentsDir)
michael@0 361
michael@0 362 local = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'head.js')
michael@0 363 remoteFile = remoteJoin(self.remoteScriptsDir, "head.js")
michael@0 364 self.device.pushFile(local, remoteFile)
michael@0 365
michael@0 366 local = os.path.join(self.localBin, "xpcshell")
michael@0 367 remoteFile = remoteJoin(self.remoteBinDir, "xpcshell")
michael@0 368 self.device.pushFile(local, remoteFile)
michael@0 369
michael@0 370 local = os.path.join(self.localBin, "components/httpd.js")
michael@0 371 remoteFile = remoteJoin(self.remoteComponentsDir, "httpd.js")
michael@0 372 self.device.pushFile(local, remoteFile)
michael@0 373
michael@0 374 local = os.path.join(self.localBin, "components/httpd.manifest")
michael@0 375 remoteFile = remoteJoin(self.remoteComponentsDir, "httpd.manifest")
michael@0 376 self.device.pushFile(local, remoteFile)
michael@0 377
michael@0 378 local = os.path.join(self.localBin, "components/test_necko.xpt")
michael@0 379 remoteFile = remoteJoin(self.remoteComponentsDir, "test_necko.xpt")
michael@0 380 self.device.pushFile(local, remoteFile)
michael@0 381
michael@0 382 if self.options.localAPK:
michael@0 383 remoteFile = remoteJoin(self.remoteBinDir, os.path.basename(self.options.localAPK))
michael@0 384 self.device.pushFile(self.options.localAPK, remoteFile)
michael@0 385
michael@0 386 self.pushLibs()
michael@0 387
michael@0 388 def pushLibs(self):
michael@0 389 pushed_libs_count = 0
michael@0 390 if self.options.localAPK:
michael@0 391 try:
michael@0 392 dir = tempfile.mkdtemp()
michael@0 393 szip = os.path.join(self.localBin, '..', 'host', 'bin', 'szip')
michael@0 394 if not os.path.exists(szip):
michael@0 395 # Tinderbox builds must run szip from the test package
michael@0 396 szip = os.path.join(self.localBin, 'host', 'szip')
michael@0 397 if not os.path.exists(szip):
michael@0 398 # If the test package doesn't contain szip, it means files
michael@0 399 # are not szipped in the test package.
michael@0 400 szip = None
michael@0 401 for info in self.localAPKContents.infolist():
michael@0 402 if info.filename.endswith(".so"):
michael@0 403 print >> sys.stderr, "Pushing %s.." % info.filename
michael@0 404 remoteFile = remoteJoin(self.remoteBinDir, os.path.basename(info.filename))
michael@0 405 self.localAPKContents.extract(info, dir)
michael@0 406 file = os.path.join(dir, info.filename)
michael@0 407 if szip:
michael@0 408 out = subprocess.check_output([szip, '-d', file], stderr=subprocess.STDOUT)
michael@0 409 self.device.pushFile(os.path.join(dir, info.filename), remoteFile)
michael@0 410 pushed_libs_count += 1
michael@0 411 finally:
michael@0 412 shutil.rmtree(dir)
michael@0 413 return pushed_libs_count
michael@0 414
michael@0 415 for file in os.listdir(self.localLib):
michael@0 416 if (file.endswith(".so")):
michael@0 417 print >> sys.stderr, "Pushing %s.." % file
michael@0 418 if 'libxul' in file:
michael@0 419 print >> sys.stderr, "This is a big file, it could take a while."
michael@0 420 remoteFile = remoteJoin(self.remoteBinDir, file)
michael@0 421 self.device.pushFile(os.path.join(self.localLib, file), remoteFile)
michael@0 422 pushed_libs_count += 1
michael@0 423
michael@0 424 # Additional libraries may be found in a sub-directory such as "lib/armeabi-v7a"
michael@0 425 localArmLib = os.path.join(self.localLib, "lib")
michael@0 426 if os.path.exists(localArmLib):
michael@0 427 for root, dirs, files in os.walk(localArmLib):
michael@0 428 for file in files:
michael@0 429 if (file.endswith(".so")):
michael@0 430 print >> sys.stderr, "Pushing %s.." % file
michael@0 431 remoteFile = remoteJoin(self.remoteBinDir, file)
michael@0 432 self.device.pushFile(os.path.join(root, file), remoteFile)
michael@0 433 pushed_libs_count += 1
michael@0 434
michael@0 435 return pushed_libs_count
michael@0 436
michael@0 437 def setupModules(self):
michael@0 438 if self.testingModulesDir:
michael@0 439 self.device.pushDir(self.testingModulesDir, self.remoteModulesDir)
michael@0 440
michael@0 441 def setupTestDir(self):
michael@0 442 print 'pushing %s' % self.xpcDir
michael@0 443 try:
michael@0 444 self.device.pushDir(self.xpcDir, self.remoteScriptsDir, retryLimit=10)
michael@0 445 except TypeError:
michael@0 446 # Foopies have an older mozdevice ver without retryLimit
michael@0 447 self.device.pushDir(self.xpcDir, self.remoteScriptsDir)
michael@0 448
michael@0 449 def setupMinidumpDir(self):
michael@0 450 if self.device.dirExists(self.remoteMinidumpDir):
michael@0 451 self.device.removeDir(self.remoteMinidumpDir)
michael@0 452 self.device.mkDir(self.remoteMinidumpDir)
michael@0 453
michael@0 454 def buildTestList(self):
michael@0 455 xpcshell.XPCShellTests.buildTestList(self)
michael@0 456 uniqueTestPaths = set([])
michael@0 457 for test in self.alltests:
michael@0 458 uniqueTestPaths.add(test['here'])
michael@0 459 for testdir in uniqueTestPaths:
michael@0 460 abbrevTestDir = os.path.relpath(testdir, self.xpcDir)
michael@0 461 remoteScriptDir = remoteJoin(self.remoteScriptsDir, abbrevTestDir)
michael@0 462 self.pathMapping.append(PathMapping(testdir, remoteScriptDir))
michael@0 463
michael@0 464 class RemoteXPCShellOptions(xpcshell.XPCShellOptions):
michael@0 465
michael@0 466 def __init__(self):
michael@0 467 xpcshell.XPCShellOptions.__init__(self)
michael@0 468 defaults = {}
michael@0 469
michael@0 470 self.add_option("--deviceIP", action="store",
michael@0 471 type = "string", dest = "deviceIP",
michael@0 472 help = "ip address of remote device to test")
michael@0 473 defaults["deviceIP"] = None
michael@0 474
michael@0 475 self.add_option("--devicePort", action="store",
michael@0 476 type = "string", dest = "devicePort",
michael@0 477 help = "port of remote device to test")
michael@0 478 defaults["devicePort"] = 20701
michael@0 479
michael@0 480 self.add_option("--dm_trans", action="store",
michael@0 481 type = "string", dest = "dm_trans",
michael@0 482 help = "the transport to use to communicate with device: [adb|sut]; default=sut")
michael@0 483 defaults["dm_trans"] = "sut"
michael@0 484
michael@0 485 self.add_option("--objdir", action="store",
michael@0 486 type = "string", dest = "objdir",
michael@0 487 help = "local objdir, containing xpcshell binaries")
michael@0 488 defaults["objdir"] = None
michael@0 489
michael@0 490 self.add_option("--apk", action="store",
michael@0 491 type = "string", dest = "localAPK",
michael@0 492 help = "local path to Fennec APK")
michael@0 493 defaults["localAPK"] = None
michael@0 494
michael@0 495 self.add_option("--noSetup", action="store_false",
michael@0 496 dest = "setup",
michael@0 497 help = "do not copy any files to device (to be used only if device is already setup)")
michael@0 498 defaults["setup"] = True
michael@0 499
michael@0 500 self.add_option("--local-lib-dir", action="store",
michael@0 501 type = "string", dest = "localLib",
michael@0 502 help = "local path to library directory")
michael@0 503 defaults["localLib"] = None
michael@0 504
michael@0 505 self.add_option("--local-bin-dir", action="store",
michael@0 506 type = "string", dest = "localBin",
michael@0 507 help = "local path to bin directory")
michael@0 508 defaults["localBin"] = None
michael@0 509
michael@0 510 self.add_option("--remoteTestRoot", action = "store",
michael@0 511 type = "string", dest = "remoteTestRoot",
michael@0 512 help = "remote directory to use as test root (eg. /mnt/sdcard/tests or /data/local/tests)")
michael@0 513 defaults["remoteTestRoot"] = None
michael@0 514
michael@0 515 self.set_defaults(**defaults)
michael@0 516
michael@0 517 def verifyRemoteOptions(self, options):
michael@0 518 if options.localLib is None:
michael@0 519 if options.localAPK and options.objdir:
michael@0 520 for path in ['dist/fennec', 'fennec/lib']:
michael@0 521 options.localLib = os.path.join(options.objdir, path)
michael@0 522 if os.path.isdir(options.localLib):
michael@0 523 break
michael@0 524 else:
michael@0 525 self.error("Couldn't find local library dir, specify --local-lib-dir")
michael@0 526 elif options.objdir:
michael@0 527 options.localLib = os.path.join(options.objdir, 'dist/bin')
michael@0 528 elif os.path.isfile(os.path.join(here, '..', 'bin', 'xpcshell')):
michael@0 529 # assume tests are being run from a tests.zip
michael@0 530 options.localLib = os.path.abspath(os.path.join(here, '..', 'bin'))
michael@0 531 else:
michael@0 532 self.error("Couldn't find local library dir, specify --local-lib-dir")
michael@0 533
michael@0 534 if options.localBin is None:
michael@0 535 if options.objdir:
michael@0 536 for path in ['dist/bin', 'bin']:
michael@0 537 options.localBin = os.path.join(options.objdir, path)
michael@0 538 if os.path.isdir(options.localBin):
michael@0 539 break
michael@0 540 else:
michael@0 541 self.error("Couldn't find local binary dir, specify --local-bin-dir")
michael@0 542 elif os.path.isfile(os.path.join(here, '..', 'bin', 'xpcshell')):
michael@0 543 # assume tests are being run from a tests.zip
michael@0 544 options.localBin = os.path.abspath(os.path.join(here, '..', 'bin'))
michael@0 545 else:
michael@0 546 self.error("Couldn't find local binary dir, specify --local-bin-dir")
michael@0 547 return options
michael@0 548
michael@0 549 class PathMapping:
michael@0 550
michael@0 551 def __init__(self, localDir, remoteDir):
michael@0 552 self.local = localDir
michael@0 553 self.remote = remoteDir
michael@0 554
michael@0 555 def main():
michael@0 556
michael@0 557 if sys.version_info < (2,7):
michael@0 558 print >>sys.stderr, "Error: You must use python version 2.7 or newer but less than 3.0"
michael@0 559 sys.exit(1)
michael@0 560
michael@0 561 parser = RemoteXPCShellOptions()
michael@0 562 options, args = parser.parse_args()
michael@0 563 if not options.localAPK:
michael@0 564 for file in os.listdir(os.path.join(options.objdir, "dist")):
michael@0 565 if (file.endswith(".apk") and file.startswith("fennec")):
michael@0 566 options.localAPK = os.path.join(options.objdir, "dist")
michael@0 567 options.localAPK = os.path.join(options.localAPK, file)
michael@0 568 print >>sys.stderr, "using APK: " + options.localAPK
michael@0 569 break
michael@0 570 else:
michael@0 571 print >>sys.stderr, "Error: please specify an APK"
michael@0 572 sys.exit(1)
michael@0 573
michael@0 574 options = parser.verifyRemoteOptions(options)
michael@0 575
michael@0 576 if len(args) < 1 and options.manifest is None:
michael@0 577 print >>sys.stderr, """Usage: %s <test dirs>
michael@0 578 or: %s --manifest=test.manifest """ % (sys.argv[0], sys.argv[0])
michael@0 579 sys.exit(1)
michael@0 580
michael@0 581 if (options.dm_trans == "adb"):
michael@0 582 if (options.deviceIP):
michael@0 583 dm = devicemanagerADB.DeviceManagerADB(options.deviceIP, options.devicePort, packageName=None, deviceRoot=options.remoteTestRoot)
michael@0 584 else:
michael@0 585 dm = devicemanagerADB.DeviceManagerADB(packageName=None, deviceRoot=options.remoteTestRoot)
michael@0 586 else:
michael@0 587 dm = devicemanagerSUT.DeviceManagerSUT(options.deviceIP, options.devicePort, deviceRoot=options.remoteTestRoot)
michael@0 588 if (options.deviceIP == None):
michael@0 589 print "Error: you must provide a device IP to connect to via the --device option"
michael@0 590 sys.exit(1)
michael@0 591
michael@0 592 if options.interactive and not options.testPath:
michael@0 593 print >>sys.stderr, "Error: You must specify a test filename in interactive mode!"
michael@0 594 sys.exit(1)
michael@0 595
michael@0 596 xpcsh = XPCShellRemote(dm, options, args)
michael@0 597
michael@0 598 # we don't run concurrent tests on mobile
michael@0 599 options.sequential = True
michael@0 600
michael@0 601 if not xpcsh.runTests(xpcshell='xpcshell',
michael@0 602 testClass=RemoteXPCShellTestThread,
michael@0 603 testdirs=args[0:],
michael@0 604 mobileArgs=xpcsh.mobileArgs,
michael@0 605 **options.__dict__):
michael@0 606 sys.exit(1)
michael@0 607
michael@0 608
michael@0 609 if __name__ == '__main__':
michael@0 610 main()

mercurial