Wed, 31 Dec 2014 06:55:50 +0100
Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2
1 # This Source Code Form is subject to the terms of the Mozilla Public
2 # License, v. 2.0. If a copy of the MPL was not distributed with this
3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 import sys
6 import os
7 import time
8 import tempfile
9 import re
10 import traceback
11 import shutil
12 import math
13 import base64
15 sys.path.insert(0, os.path.abspath(os.path.realpath(os.path.dirname(__file__))))
17 from automation import Automation
18 from remoteautomation import RemoteAutomation, fennecLogcatFilters
19 from runtests import Mochitest
20 from runtests import MochitestServer
21 from mochitest_options import MochitestOptions
23 import devicemanager
24 import droid
25 import manifestparser
26 import mozinfo
27 import mozlog
28 import moznetwork
30 SCRIPT_DIR = os.path.abspath(os.path.realpath(os.path.dirname(__file__)))
31 log = mozlog.getLogger('Mochi-Remote')
33 class RemoteOptions(MochitestOptions):
35 def __init__(self, automation, **kwargs):
36 defaults = {}
37 self._automation = automation or Automation()
38 MochitestOptions.__init__(self)
40 self.add_option("--remote-app-path", action="store",
41 type = "string", dest = "remoteAppPath",
42 help = "Path to remote executable relative to device root using only forward slashes. Either this or app must be specified but not both")
43 defaults["remoteAppPath"] = None
45 self.add_option("--deviceIP", action="store",
46 type = "string", dest = "deviceIP",
47 help = "ip address of remote device to test")
48 defaults["deviceIP"] = None
50 self.add_option("--dm_trans", action="store",
51 type = "string", dest = "dm_trans",
52 help = "the transport to use to communicate with device: [adb|sut]; default=sut")
53 defaults["dm_trans"] = "sut"
55 self.add_option("--devicePort", action="store",
56 type = "string", dest = "devicePort",
57 help = "port of remote device to test")
58 defaults["devicePort"] = 20701
60 self.add_option("--remote-product-name", action="store",
61 type = "string", dest = "remoteProductName",
62 help = "The executable's name of remote product to test - either fennec or firefox, defaults to fennec")
63 defaults["remoteProductName"] = "fennec"
65 self.add_option("--remote-logfile", action="store",
66 type = "string", dest = "remoteLogFile",
67 help = "Name of log file on the device relative to the device root. PLEASE ONLY USE A FILENAME.")
68 defaults["remoteLogFile"] = None
70 self.add_option("--remote-webserver", action = "store",
71 type = "string", dest = "remoteWebServer",
72 help = "ip address where the remote web server is hosted at")
73 defaults["remoteWebServer"] = None
75 self.add_option("--http-port", action = "store",
76 type = "string", dest = "httpPort",
77 help = "http port of the remote web server")
78 defaults["httpPort"] = automation.DEFAULT_HTTP_PORT
80 self.add_option("--ssl-port", action = "store",
81 type = "string", dest = "sslPort",
82 help = "ssl port of the remote web server")
83 defaults["sslPort"] = automation.DEFAULT_SSL_PORT
85 self.add_option("--robocop-ini", action = "store",
86 type = "string", dest = "robocopIni",
87 help = "name of the .ini file containing the list of tests to run")
88 defaults["robocopIni"] = ""
90 self.add_option("--robocop", action = "store",
91 type = "string", dest = "robocop",
92 help = "name of the .ini file containing the list of tests to run. [DEPRECATED- please use --robocop-ini")
93 defaults["robocop"] = ""
95 self.add_option("--robocop-apk", action = "store",
96 type = "string", dest = "robocopApk",
97 help = "name of the Robocop APK to use for ADB test running")
98 defaults["robocopApk"] = ""
100 self.add_option("--robocop-path", action = "store",
101 type = "string", dest = "robocopPath",
102 help = "Path to the folder where robocop.apk is located at. Primarily used for ADB test running. [DEPRECATED- please use --robocop-apk]")
103 defaults["robocopPath"] = ""
105 self.add_option("--robocop-ids", action = "store",
106 type = "string", dest = "robocopIds",
107 help = "name of the file containing the view ID map (fennec_ids.txt)")
108 defaults["robocopIds"] = ""
110 self.add_option("--remoteTestRoot", action = "store",
111 type = "string", dest = "remoteTestRoot",
112 help = "remote directory to use as test root (eg. /mnt/sdcard/tests or /data/local/tests)")
113 defaults["remoteTestRoot"] = None
115 defaults["logFile"] = "mochitest.log"
116 defaults["autorun"] = True
117 defaults["closeWhenDone"] = True
118 defaults["testPath"] = ""
119 defaults["app"] = None
120 defaults["utilityPath"] = None
122 self.set_defaults(**defaults)
124 def verifyRemoteOptions(self, options, automation):
125 if not options.remoteTestRoot:
126 options.remoteTestRoot = automation._devicemanager.getDeviceRoot()
128 if options.remoteWebServer == None:
129 if os.name != "nt":
130 options.remoteWebServer = moznetwork.get_ip()
131 else:
132 log.error("you must specify a --remote-webserver=<ip address>")
133 return None
135 options.webServer = options.remoteWebServer
137 if (options.deviceIP == None):
138 log.error("you must provide a device IP")
139 return None
141 if (options.remoteLogFile == None):
142 options.remoteLogFile = options.remoteTestRoot + '/logs/mochitest.log'
144 if (options.remoteLogFile.count('/') < 1):
145 options.remoteLogFile = options.remoteTestRoot + '/' + options.remoteLogFile
147 # remoteAppPath or app must be specified to find the product to launch
148 if (options.remoteAppPath and options.app):
149 log.error("You cannot specify both the remoteAppPath and the app setting")
150 return None
151 elif (options.remoteAppPath):
152 options.app = options.remoteTestRoot + "/" + options.remoteAppPath
153 elif (options.app == None):
154 # Neither remoteAppPath nor app are set -- error
155 log.error("You must specify either appPath or app")
156 return None
158 # Only reset the xrePath if it wasn't provided
159 if (options.xrePath == None):
160 options.xrePath = options.utilityPath
162 if (options.pidFile != ""):
163 f = open(options.pidFile, 'w')
164 f.write("%s" % os.getpid())
165 f.close()
167 # Robocop specific deprecated options.
168 if options.robocop:
169 if options.robocopIni:
170 log.error("can not use deprecated --robocop and replacement --robocop-ini together")
171 return None
172 options.robocopIni = options.robocop
173 del options.robocop
175 if options.robocopPath:
176 if options.robocopApk:
177 log.error("can not use deprecated --robocop-path and replacement --robocop-apk together")
178 return None
179 options.robocopApk = os.path.join(options.robocopPath, 'robocop.apk')
180 del options.robocopPath
182 # Robocop specific options
183 if options.robocopIni != "":
184 if not os.path.exists(options.robocopIni):
185 log.error("Unable to find specified robocop .ini manifest '%s'", options.robocopIni)
186 return None
187 options.robocopIni = os.path.abspath(options.robocopIni)
189 if options.robocopApk != "":
190 if not os.path.exists(options.robocopApk):
191 log.error("Unable to find robocop APK '%s'", options.robocopApk)
192 return None
193 options.robocopApk = os.path.abspath(options.robocopApk)
195 if options.robocopIds != "":
196 if not os.path.exists(options.robocopIds):
197 log.error("Unable to find specified robocop IDs file '%s'", options.robocopIds)
198 return None
199 options.robocopIds = os.path.abspath(options.robocopIds)
201 # allow us to keep original application around for cleanup while running robocop via 'am'
202 options.remoteappname = options.app
203 return options
205 def verifyOptions(self, options, mochitest):
206 # since we are reusing verifyOptions, it will exit if App is not found
207 temp = options.app
208 options.app = __file__
209 tempPort = options.httpPort
210 tempSSL = options.sslPort
211 tempIP = options.webServer
212 # We are going to override this option later anyway, just pretend
213 # like it's not set for verification purposes.
214 options.dumpOutputDirectory = None
215 options = MochitestOptions.verifyOptions(self, options, mochitest)
216 options.webServer = tempIP
217 options.app = temp
218 options.sslPort = tempSSL
219 options.httpPort = tempPort
221 return options
223 class MochiRemote(Mochitest):
225 _automation = None
226 _dm = None
227 localProfile = None
228 logLines = []
230 def __init__(self, automation, devmgr, options):
231 self._automation = automation
232 Mochitest.__init__(self)
233 self._dm = devmgr
234 self.environment = self._automation.environment
235 self.remoteProfile = options.remoteTestRoot + "/profile"
236 self._automation.setRemoteProfile(self.remoteProfile)
237 self.remoteLog = options.remoteLogFile
238 self.localLog = options.logFile
239 self._automation.deleteANRs()
240 self.certdbNew = True
242 def cleanup(self, manifest, options):
243 if self._dm.fileExists(self.remoteLog):
244 self._dm.getFile(self.remoteLog, self.localLog)
245 self._dm.removeFile(self.remoteLog)
246 else:
247 log.warn("Unable to retrieve log file (%s) from remote device",
248 self.remoteLog)
249 self._dm.removeDir(self.remoteProfile)
250 Mochitest.cleanup(self, manifest, options)
252 def findPath(self, paths, filename = None):
253 for path in paths:
254 p = path
255 if filename:
256 p = os.path.join(p, filename)
257 if os.path.exists(self.getFullPath(p)):
258 return path
259 return None
261 def makeLocalAutomation(self):
262 localAutomation = Automation()
263 localAutomation.IS_WIN32 = False
264 localAutomation.IS_LINUX = False
265 localAutomation.IS_MAC = False
266 localAutomation.UNIXISH = False
267 hostos = sys.platform
268 if (hostos == 'mac' or hostos == 'darwin'):
269 localAutomation.IS_MAC = True
270 elif (hostos == 'linux' or hostos == 'linux2'):
271 localAutomation.IS_LINUX = True
272 localAutomation.UNIXISH = True
273 elif (hostos == 'win32' or hostos == 'win64'):
274 localAutomation.BIN_SUFFIX = ".exe"
275 localAutomation.IS_WIN32 = True
276 return localAutomation
278 # This seems kludgy, but this class uses paths from the remote host in the
279 # options, except when calling up to the base class, which doesn't
280 # understand the distinction. This switches out the remote values for local
281 # ones that the base class understands. This is necessary for the web
282 # server, SSL tunnel and profile building functions.
283 def switchToLocalPaths(self, options):
284 """ Set local paths in the options, return a function that will restore remote values """
285 remoteXrePath = options.xrePath
286 remoteProfilePath = options.profilePath
287 remoteUtilityPath = options.utilityPath
289 localAutomation = self.makeLocalAutomation()
290 paths = [
291 options.xrePath,
292 localAutomation.DIST_BIN,
293 self._automation._product,
294 os.path.join('..', self._automation._product)
295 ]
296 options.xrePath = self.findPath(paths)
297 if options.xrePath == None:
298 log.error("unable to find xulrunner path for %s, please specify with --xre-path", os.name)
299 sys.exit(1)
301 xpcshell = "xpcshell"
302 if (os.name == "nt"):
303 xpcshell += ".exe"
305 if options.utilityPath:
306 paths = [options.utilityPath, options.xrePath]
307 else:
308 paths = [options.xrePath]
309 options.utilityPath = self.findPath(paths, xpcshell)
311 if options.utilityPath == None:
312 log.error("unable to find utility path for %s, please specify with --utility-path", os.name)
313 sys.exit(1)
315 xpcshell_path = os.path.join(options.utilityPath, xpcshell)
316 if localAutomation.elf_arm(xpcshell_path):
317 log.error('xpcshell at %s is an ARM binary; please use '
318 'the --utility-path argument to specify the path '
319 'to a desktop version.' % xpcshell_path)
320 sys.exit(1)
322 if self.localProfile:
323 options.profilePath = self.localProfile
324 else:
325 options.profilePath = tempfile.mkdtemp()
327 def fixup():
328 options.xrePath = remoteXrePath
329 options.utilityPath = remoteUtilityPath
330 options.profilePath = remoteProfilePath
332 return fixup
334 def startServers(self, options, debuggerInfo):
335 """ Create the servers on the host and start them up """
336 restoreRemotePaths = self.switchToLocalPaths(options)
337 Mochitest.startServers(self, options, debuggerInfo)
338 restoreRemotePaths()
340 def buildProfile(self, options):
341 restoreRemotePaths = self.switchToLocalPaths(options)
342 manifest = Mochitest.buildProfile(self, options)
343 self.localProfile = options.profilePath
344 self._dm.removeDir(self.remoteProfile)
346 # we do not need this for robotium based tests, lets save a LOT of time
347 if options.robocopIni:
348 shutil.rmtree(os.path.join(options.profilePath, 'webapps'))
349 shutil.rmtree(os.path.join(options.profilePath, 'extensions', 'staged', 'mochikit@mozilla.org'))
350 shutil.rmtree(os.path.join(options.profilePath, 'extensions', 'staged', 'worker-test@mozilla.org'))
351 shutil.rmtree(os.path.join(options.profilePath, 'extensions', 'staged', 'workerbootstrap-test@mozilla.org'))
352 os.remove(os.path.join(options.profilePath, 'userChrome.css'))
354 try:
355 self._dm.pushDir(options.profilePath, self.remoteProfile)
356 except devicemanager.DMError:
357 log.error("Automation Error: Unable to copy profile to device.")
358 raise
360 restoreRemotePaths()
361 options.profilePath = self.remoteProfile
362 return manifest
364 def buildURLOptions(self, options, env):
365 self.localLog = options.logFile
366 options.logFile = self.remoteLog
367 options.profilePath = self.localProfile
368 env["MOZ_HIDE_RESULTS_TABLE"] = "1"
369 retVal = Mochitest.buildURLOptions(self, options, env)
371 if not options.robocopIni:
372 #we really need testConfig.js (for browser chrome)
373 try:
374 self._dm.pushDir(options.profilePath, self.remoteProfile)
375 except devicemanager.DMError:
376 log.error("Automation Error: Unable to copy profile to device.")
377 raise
379 options.profilePath = self.remoteProfile
380 options.logFile = self.localLog
381 return retVal
383 def buildTestPath(self, options):
384 if options.robocopIni != "":
385 # Skip over manifest building if we just want to run
386 # robocop tests.
387 return self.buildTestURL(options)
388 else:
389 return super(MochiRemote, self).buildTestPath(options)
391 def installChromeFile(self, filename, options):
392 parts = options.app.split('/')
393 if (parts[0] == options.app):
394 return "NO_CHROME_ON_DROID"
395 path = '/'.join(parts[:-1])
396 manifest = path + "/chrome/" + os.path.basename(filename)
397 try:
398 self._dm.pushFile(filename, manifest)
399 except devicemanager.DMError:
400 log.error("Automation Error: Unable to install Chrome files on device.")
401 raise
403 return manifest
405 def getLogFilePath(self, logFile):
406 return logFile
408 # In the future we could use LogParser: http://hg.mozilla.org/automation/logparser/
409 def addLogData(self):
410 with open(self.localLog) as currentLog:
411 data = currentLog.readlines()
413 restart = re.compile('0 INFO SimpleTest START.*')
414 reend = re.compile('([0-9]+) INFO TEST-START . Shutdown.*')
415 refail = re.compile('([0-9]+) INFO TEST-UNEXPECTED-FAIL.*')
416 start_found = False
417 end_found = False
418 fail_found = False
419 for line in data:
420 if reend.match(line):
421 end_found = True
422 start_found = False
423 break
425 if start_found and not end_found:
426 # Append the line without the number to increment
427 self.logLines.append(' '.join(line.split(' ')[1:]))
429 if restart.match(line):
430 start_found = True
431 if refail.match(line):
432 fail_found = True
433 result = 0
434 if fail_found:
435 result = 1
436 if not end_found:
437 log.error("Automation Error: Missing end of test marker (process crashed?)")
438 result = 1
439 return result
441 def printLog(self):
442 passed = 0
443 failed = 0
444 todo = 0
445 incr = 1
446 logFile = []
447 logFile.append("0 INFO SimpleTest START")
448 for line in self.logLines:
449 if line.startswith("INFO TEST-PASS"):
450 passed += 1
451 elif line.startswith("INFO TEST-UNEXPECTED"):
452 failed += 1
453 elif line.startswith("INFO TEST-KNOWN"):
454 todo += 1
455 incr += 1
457 logFile.append("%s INFO TEST-START | Shutdown" % incr)
458 incr += 1
459 logFile.append("%s INFO Passed: %s" % (incr, passed))
460 incr += 1
461 logFile.append("%s INFO Failed: %s" % (incr, failed))
462 incr += 1
463 logFile.append("%s INFO Todo: %s" % (incr, todo))
464 incr += 1
465 logFile.append("%s INFO SimpleTest FINISHED" % incr)
467 # TODO: Consider not printing to stdout because we might be duplicating output
468 print '\n'.join(logFile)
469 with open(self.localLog, 'w') as localLog:
470 localLog.write('\n'.join(logFile))
472 if failed > 0:
473 return 1
474 return 0
476 def printScreenshots(self, screenShotDir):
477 # TODO: This can be re-written after completion of bug 749421
478 if not self._dm.dirExists(screenShotDir):
479 log.info("SCREENSHOT: No ScreenShots directory available: " + screenShotDir)
480 return
482 printed = 0
483 for name in self._dm.listFiles(screenShotDir):
484 fullName = screenShotDir + "/" + name
485 log.info("SCREENSHOT: FOUND: [%s]", fullName)
486 try:
487 image = self._dm.pullFile(fullName)
488 encoded = base64.b64encode(image)
489 log.info("SCREENSHOT: data:image/jpg;base64,%s", encoded)
490 printed += 1
491 except:
492 log.info("SCREENSHOT: Could not be parsed")
493 pass
495 log.info("SCREENSHOT: TOTAL PRINTED: [%s]", printed)
497 def printDeviceInfo(self, printLogcat=False):
498 try:
499 if printLogcat:
500 logcat = self._dm.getLogcat(filterOutRegexps=fennecLogcatFilters)
501 log.info('\n'+(''.join(logcat)))
502 log.info("Device info: %s", self._dm.getInfo())
503 log.info("Test root: %s", self._dm.getDeviceRoot())
504 except devicemanager.DMError:
505 log.warn("Error getting device information")
507 def buildRobotiumConfig(self, options, browserEnv):
508 deviceRoot = self._dm.getDeviceRoot()
509 fHandle = tempfile.NamedTemporaryFile(suffix='.config',
510 prefix='robotium-',
511 dir=os.getcwd(),
512 delete=False)
513 fHandle.write("profile=%s\n" % (self.remoteProfile))
514 fHandle.write("logfile=%s\n" % (options.remoteLogFile))
515 fHandle.write("host=http://mochi.test:8888/tests\n")
516 fHandle.write("rawhost=http://%s:%s/tests\n" % (options.remoteWebServer, options.httpPort))
518 if browserEnv:
519 envstr = ""
520 delim = ""
521 for key, value in browserEnv.items():
522 try:
523 value.index(',')
524 log.error("buildRobotiumConfig: browserEnv - Found a ',' in our value, unable to process value. key=%s,value=%s", key, value)
525 log.error("browserEnv=%s", browserEnv)
526 except ValueError, e:
527 envstr += "%s%s=%s" % (delim, key, value)
528 delim = ","
530 fHandle.write("envvars=%s\n" % envstr)
531 fHandle.close()
533 self._dm.removeFile(os.path.join(deviceRoot, "robotium.config"))
534 self._dm.pushFile(fHandle.name, os.path.join(deviceRoot, "robotium.config"))
535 os.unlink(fHandle.name)
537 def buildBrowserEnv(self, options, debugger=False):
538 browserEnv = Mochitest.buildBrowserEnv(self, options, debugger=debugger)
539 self.buildRobotiumConfig(options, browserEnv)
540 return browserEnv
542 def runApp(self, *args, **kwargs):
543 """front-end automation.py's `runApp` functionality until FennecRunner is written"""
545 # automation.py/remoteautomation `runApp` takes the profile path,
546 # whereas runtest.py's `runApp` takes a mozprofile object.
547 if 'profileDir' not in kwargs and 'profile' in kwargs:
548 kwargs['profileDir'] = kwargs.pop('profile').profile
550 # We're handling ssltunnel, so we should lie to automation.py to avoid
551 # it trying to set up ssltunnel as well
552 kwargs['runSSLTunnel'] = False
554 return self._automation.runApp(*args, **kwargs)
556 def main():
557 auto = RemoteAutomation(None, "fennec")
558 parser = RemoteOptions(auto)
559 options, args = parser.parse_args()
561 if (options.dm_trans == "adb"):
562 if (options.deviceIP):
563 dm = droid.DroidADB(options.deviceIP, options.devicePort, deviceRoot=options.remoteTestRoot)
564 else:
565 dm = droid.DroidADB(deviceRoot=options.remoteTestRoot)
566 else:
567 dm = droid.DroidSUT(options.deviceIP, options.devicePort, deviceRoot=options.remoteTestRoot)
568 auto.setDeviceManager(dm)
569 options = parser.verifyRemoteOptions(options, auto)
570 if (options == None):
571 log.error("Invalid options specified, use --help for a list of valid options")
572 sys.exit(1)
574 productPieces = options.remoteProductName.split('.')
575 if (productPieces != None):
576 auto.setProduct(productPieces[0])
577 else:
578 auto.setProduct(options.remoteProductName)
579 auto.setAppName(options.remoteappname)
581 mochitest = MochiRemote(auto, dm, options)
583 options = parser.verifyOptions(options, mochitest)
584 if (options == None):
585 sys.exit(1)
587 logParent = os.path.dirname(options.remoteLogFile)
588 dm.mkDir(logParent);
589 auto.setRemoteLog(options.remoteLogFile)
590 auto.setServerInfo(options.webServer, options.httpPort, options.sslPort)
592 mochitest.printDeviceInfo()
594 # Add Android version (SDK level) to mozinfo so that manifest entries
595 # can be conditional on android_version.
596 androidVersion = dm.shellCheckOutput(['getprop', 'ro.build.version.sdk'])
597 log.info("Android sdk version '%s'; will use this to filter manifests" % str(androidVersion))
598 mozinfo.info['android_version'] = androidVersion
600 deviceRoot = dm.getDeviceRoot()
601 if options.dmdPath:
602 dmdLibrary = "libdmd.so"
603 dmdPathOnDevice = os.path.join(deviceRoot, dmdLibrary)
604 dm.removeFile(dmdPathOnDevice)
605 dm.pushFile(os.path.join(options.dmdPath, dmdLibrary), dmdPathOnDevice)
606 options.dmdPath = deviceRoot
608 options.dumpOutputDirectory = deviceRoot
610 procName = options.app.split('/')[-1]
611 dm.killProcess(procName)
613 if options.robocopIni != "":
614 # sut may wait up to 300 s for a robocop am process before returning
615 dm.default_timeout = 320
616 mp = manifestparser.TestManifest(strict=False)
617 # TODO: pull this in dynamically
618 mp.read(options.robocopIni)
619 robocop_tests = mp.active_tests(exists=False, **mozinfo.info)
620 tests = []
621 my_tests = tests
622 for test in robocop_tests:
623 tests.append(test['name'])
625 if options.totalChunks:
626 tests_per_chunk = math.ceil(len(tests) / (options.totalChunks * 1.0))
627 start = int(round((options.thisChunk-1) * tests_per_chunk))
628 end = int(round(options.thisChunk * tests_per_chunk))
629 if end > len(tests):
630 end = len(tests)
631 my_tests = tests[start:end]
632 log.info("Running tests %d-%d/%d", start+1, end, len(tests))
634 dm.removeFile(os.path.join(deviceRoot, "fennec_ids.txt"))
635 fennec_ids = os.path.abspath(os.path.join(SCRIPT_DIR, "fennec_ids.txt"))
636 if not os.path.exists(fennec_ids) and options.robocopIds:
637 fennec_ids = options.robocopIds
638 dm.pushFile(fennec_ids, os.path.join(deviceRoot, "fennec_ids.txt"))
639 options.extraPrefs.append('browser.search.suggest.enabled=true')
640 options.extraPrefs.append('browser.search.suggest.prompted=true')
641 options.extraPrefs.append('layout.css.devPixelsPerPx=1.0')
642 options.extraPrefs.append('browser.chrome.dynamictoolbar=false')
643 options.extraPrefs.append('browser.snippets.enabled=false')
645 if (options.dm_trans == 'adb' and options.robocopApk):
646 dm._checkCmd(["install", "-r", options.robocopApk])
648 retVal = None
649 for test in robocop_tests:
650 if options.testPath and options.testPath != test['name']:
651 continue
653 if not test['name'] in my_tests:
654 continue
656 if 'disabled' in test:
657 log.info('TEST-INFO | skipping %s | %s' % (test['name'], test['disabled']))
658 continue
660 # When running in a loop, we need to create a fresh profile for each cycle
661 if mochitest.localProfile:
662 options.profilePath = mochitest.localProfile
663 os.system("rm -Rf %s" % options.profilePath)
664 options.profilePath = tempfile.mkdtemp()
665 mochitest.localProfile = options.profilePath
667 options.app = "am"
668 options.browserArgs = ["instrument", "-w", "-e", "deviceroot", deviceRoot, "-e", "class"]
669 options.browserArgs.append("org.mozilla.gecko.tests.%s" % test['name'])
670 options.browserArgs.append("org.mozilla.roboexample.test/org.mozilla.gecko.FennecInstrumentationTestRunner")
672 # If the test is for checking the import from bookmarks then make sure there is data to import
673 if test['name'] == "testImportFromAndroid":
675 # Get the OS so we can run the insert in the apropriate database and following the correct table schema
676 osInfo = dm.getInfo("os")
677 devOS = " ".join(osInfo['os'])
679 if ("pandaboard" in devOS):
680 delete = ['execsu', 'sqlite3', "/data/data/com.android.browser/databases/browser2.db \'delete from bookmarks where _id > 14;\'"]
681 else:
682 delete = ['execsu', 'sqlite3', "/data/data/com.android.browser/databases/browser.db \'delete from bookmarks where _id > 14;\'"]
683 if (options.dm_trans == "sut"):
684 dm._runCmds([{"cmd": " ".join(delete)}])
686 # Insert the bookmarks
687 log.info("Insert bookmarks in the default android browser database")
688 for i in range(20):
689 if ("pandaboard" in devOS):
690 cmd = ['execsu', 'sqlite3', "/data/data/com.android.browser/databases/browser2.db 'insert or replace into bookmarks(_id,title,url,folder,parent,position) values (" + str(30 + i) + ",\"Bookmark"+ str(i) + "\",\"http://www.bookmark" + str(i) + ".com\",0,1," + str(100 + i) + ");'"]
691 else:
692 cmd = ['execsu', 'sqlite3', "/data/data/com.android.browser/databases/browser.db 'insert into bookmarks(title,url,bookmark) values (\"Bookmark"+ str(i) + "\",\"http://www.bookmark" + str(i) + ".com\",1);'"]
693 if (options.dm_trans == "sut"):
694 dm._runCmds([{"cmd": " ".join(cmd)}])
695 try:
696 screenShotDir = "/mnt/sdcard/Robotium-Screenshots"
697 dm.removeDir(screenShotDir)
698 dm.recordLogcat()
699 result = mochitest.runTests(options)
700 if result != 0:
701 log.error("runTests() exited with code %s", result)
702 log_result = mochitest.addLogData()
703 if result != 0 or log_result != 0:
704 mochitest.printDeviceInfo(printLogcat=True)
705 mochitest.printScreenshots(screenShotDir)
706 # Ensure earlier failures aren't overwritten by success on this run
707 if retVal is None or retVal == 0:
708 retVal = result
709 except:
710 log.error("Automation Error: Exception caught while running tests")
711 traceback.print_exc()
712 mochitest.stopServers()
713 try:
714 mochitest.cleanup(None, options)
715 except devicemanager.DMError:
716 # device error cleaning up... oh well!
717 pass
718 retVal = 1
719 break
720 finally:
721 # Clean-up added bookmarks
722 if test['name'] == "testImportFromAndroid":
723 if ("pandaboard" in devOS):
724 cmd_del = ['execsu', 'sqlite3', "/data/data/com.android.browser/databases/browser2.db \'delete from bookmarks where _id > 14;\'"]
725 else:
726 cmd_del = ['execsu', 'sqlite3', "/data/data/com.android.browser/databases/browser.db \'delete from bookmarks where _id > 14;\'"]
727 if (options.dm_trans == "sut"):
728 dm._runCmds([{"cmd": " ".join(cmd_del)}])
729 if retVal is None:
730 log.warn("No tests run. Did you pass an invalid TEST_PATH?")
731 retVal = 1
732 else:
733 # if we didn't have some kind of error running the tests, make
734 # sure the tests actually passed
735 print "INFO | runtests.py | Test summary: start."
736 overallResult = mochitest.printLog()
737 print "INFO | runtests.py | Test summary: end."
738 if retVal == 0:
739 retVal = overallResult
740 else:
741 try:
742 dm.recordLogcat()
743 retVal = mochitest.runTests(options)
744 except:
745 log.error("Automation Error: Exception caught while running tests")
746 traceback.print_exc()
747 mochitest.stopServers()
748 try:
749 mochitest.cleanup(None, options)
750 except devicemanager.DMError:
751 # device error cleaning up... oh well!
752 pass
753 retVal = 1
755 mochitest.printDeviceInfo(printLogcat=True)
757 sys.exit(retVal)
759 if __name__ == "__main__":
760 main()