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