diff --git a/build/mobile/b2gautomation.py b/build/mobile/b2gautomation.py index 26b94dcb42f0..b1dbf219bd49 100644 --- a/build/mobile/b2gautomation.py +++ b/build/mobile/b2gautomation.py @@ -18,8 +18,8 @@ from devicemanager import DeviceManager, NetworkTools from mozprocess import ProcessHandlerMixin -class LogcatProc(ProcessHandlerMixin): - """Process handler for logcat which puts all output in a Queue. +class StdOutProc(ProcessHandlerMixin): + """Process handler for b2g which puts all output in a Queue. """ def __init__(self, cmd, queue, **kwargs): @@ -76,6 +76,20 @@ class B2GRemoteAutomation(Automation): env['MOZ_HIDE_RESULTS_TABLE'] = '1' return env + def waitForNet(self): + active = False + time_out = 0 + while not active and time_out < 40: + data = self._devicemanager.runCmd(['shell', '/system/bin/netcfg']).stdout.readlines() + data.pop(0) + for line in data: + if (re.search(r'UP\s+(?:[0-9]{1,3}\.){3}[0-9]{1,3}', line)): + active = True + break + time_out += 1 + time.sleep(1) + return active + def checkForCrashes(self, directory, symbolsPath): # XXX: This will have to be updated after crash reporting on b2g # is in place. @@ -160,7 +174,11 @@ class B2GRemoteAutomation(Automation): serial, status = self.getDeviceStatus() # reboot! - self._devicemanager.checkCmd(['reboot']) + self._devicemanager.runCmd(['shell', '/system/bin/reboot']) + + # The above command can return while adb still thinks the device is + # connected, so wait a little bit for it to disconnect from adb. + time.sleep(10) # wait for device to come back to previous status print 'waiting for device to come back online after reboot' @@ -183,21 +201,26 @@ class B2GRemoteAutomation(Automation): # to pass env variables into the B2G process, but this doesn't # seem to matter. - instance = self.B2GInstance(self._devicemanager) - # reboot device so it starts up with the mochitest profile # XXX: We could potentially use 'stop b2g' + 'start b2g' to achieve # a similar effect; will see which is more stable while attempting # to bring up the continuous integration. - if self._is_emulator: - self.restartB2G() - else: + if not self._is_emulator: self.rebootDevice() + time.sleep(5) + #wait for wlan to come up + if not self.waitForNet(): + raise Exception("network did not come up, please configure the network" + + " prior to running before running the automation framework") - # Infrequently, gecko comes up before networking does, so wait a little - # bit to give the network time to become available. - # XXX: need a more robust mechanism for this - time.sleep(40) + # stop b2g + self._devicemanager.runCmd(['shell', 'stop', 'b2g']) + time.sleep(5) + + # relaunch b2g inside b2g instance + instance = self.B2GInstance(self._devicemanager) + + time.sleep(5) # Set up port forwarding again for Marionette, since any that # existed previously got wiped out by the reboot. @@ -206,6 +229,8 @@ class B2GRemoteAutomation(Automation): 'tcp:%s' % self.marionette.port, 'tcp:%s' % self.marionette.port]) + time.sleep(5) + # start a marionette session session = self.marionette.start_session() if 'b2g' not in session: @@ -228,26 +253,29 @@ class B2GRemoteAutomation(Automation): def __init__(self, dm): self.dm = dm - self.logcat_proc = None + self.stdout_proc = None self.queue = Queue.Queue() - # Launch logcat in a separate thread, and dump all output lines + # Launch b2g in a separate thread, and dump all output lines # into a queue. The lines in this queue are # retrieved and returned by accessing the stdout property of # this class. cmd = [self.dm.adbPath] if self.dm.deviceSerial: cmd.extend(['-s', self.dm.deviceSerial]) - cmd.append('logcat') - proc = threading.Thread(target=self._save_logcat_proc, args=(cmd, self.queue)) + cmd.append('shell') + cmd.append('/system/bin/b2g.sh') + proc = threading.Thread(target=self._save_stdout_proc, args=(cmd, self.queue)) proc.daemon = True proc.start() - def _save_logcat_proc(self, cmd, queue): - self.logcat_proc = LogcatProc(cmd, queue) - self.logcat_proc.run() - self.logcat_proc.waitForFinish() - self.logcat_proc = None + def _save_stdout_proc(self, cmd, queue): + self.stdout_proc = StdOutProc(cmd, queue) + self.stdout_proc.run() + if hasattr(self.stdout_proc, 'processOutput'): + self.stdout_proc.processOutput() + self.stdout_proc.waitForFinish(timeout=10) + self.stdout_proc = None @property def pid(self): @@ -257,7 +285,7 @@ class B2GRemoteAutomation(Automation): @property def stdout(self): # Return any lines in the queue used by the - # logcat process handler. + # b2g process handler. lines = [] while True: try: diff --git a/testing/mochitest/runtestsb2g.py b/testing/mochitest/runtestsb2g.py index 111dd126548f..9e5c7b76b39a 100644 --- a/testing/mochitest/runtestsb2g.py +++ b/testing/mochitest/runtestsb2g.py @@ -194,32 +194,42 @@ class B2GMochitest(Mochitest): self.remoteProfile = options.remoteTestRoot + '/profile' self._automation.setRemoteProfile(self.remoteProfile) self.remoteLog = options.remoteLogFile + self.localLog = None self.userJS = '/data/local/user.js' self.remoteMozillaPath = '/data/b2g/mozilla' self.remoteProfilesIniPath = os.path.join(self.remoteMozillaPath, 'profiles.ini') self.originalProfilesIni = None - def cleanup(self, manifest, options): - # Restore the original profiles.ini. - if self.originalProfilesIni: - try: - if not options.emulator: - self.restoreProfilesIni() - os.remove(self.originalProfilesIni) - except: - pass + def copyRemoteFile(self, src, dest): + if self._dm.useDDCopy: + self._dm.checkCmdAs(['shell', 'dd', 'if=%s' % src,'of=%s' % dest]) + else: + self._dm.checkCmdAs(['shell', 'cp', src, dest]) - if not options.emulator: + def origUserJSExists(self): + return self._dm.fileExists('/data/local/user.js.orig') + + def cleanup(self, manifest, options): + if self.localLog: self._dm.getFile(self.remoteLog, self.localLog) self._dm.removeFile(self.remoteLog) - self._dm.removeDir(self.remoteProfile) - # Restore the original user.js. - self._dm.checkCmdAs(['shell', 'rm', '-f', self.userJS]) - if self._dm.useDDCopy: - self._dm.checkCmdAs(['shell', 'dd', 'if=%s.orig' % self.userJS, 'of=%s' % self.userJS]) - else: - self._dm.checkCmdAs(['shell', 'cp', '%s.orig' % self.userJS, self.userJS]) + if not options.emulator: + # Remove the test profile + self._dm.checkCmdAs(['shell', 'rm', '-r', self.remoteProfile]) + + if self.origUserJSExists(): + # Restore the original user.js + self._dm.removeFile(self.userJS) + self.copyRemoteFile('%s.orig' % self.userJS, self.userJS) + self._dm.removeFile("%s.orig" % self.userJS) + + if self._dm.fileExists('%s.orig' % self.remoteProfilesIniPath): + # Restore the original profiles.ini + self._dm.removeFile(self.remoteProfilesIniPath) + self.copyRemoteFile('%s.orig' % self.remoteProfilesIniPath, + self.remoteProfilesIniPath) + self._dm.removeFile("%s.orig" % self.remoteProfilesIniPath) # We've restored the original profile, so reboot the device so that # it gets picked up. @@ -313,14 +323,6 @@ class B2GMochitest(Mochitest): options.profilePath = self.remoteProfile return manifest - def restoreProfilesIni(self): - # restore profiles.ini on the device to its previous state - if not self.originalProfilesIni or not os.access(self.originalProfilesIni, os.F_OK): - raise DMError('Unable to install original profiles.ini; file not found: %s', - self.originalProfilesIni) - - self._dm.pushFile(self.originalProfilesIni, self.remoteProfilesIniPath) - def updateProfilesIni(self, profilePath): # update profiles.ini on the device to point to the test profile self.originalProfilesIni = tempfile.mktemp() @@ -338,8 +340,11 @@ class B2GMochitest(Mochitest): config.write(configfile) self._dm.pushFile(newProfilesIni, self.remoteProfilesIniPath) + self._dm.pushFile(self.originalProfilesIni, '%s.orig' % self.remoteProfilesIniPath) + try: os.remove(newProfilesIni) + os.remove(self.originalProfilesIni) except: pass @@ -369,21 +374,16 @@ user_pref("network.dns.localDomains","app://system.gaiamobile.org");\n f.close() # Copy the profile to the device. - self._dm.removeDir(self.remoteProfile) + self._dm.checkCmdAs(['shell', 'rm', '-r', self.remoteProfile]) if self._dm.pushDir(options.profilePath, self.remoteProfile) == None: raise devicemanager.FileError("Unable to copy profile to device.") # In B2G, user.js is always read from /data/local, not the profile # directory. Backup the original user.js first so we can restore it. - self._dm.checkCmdAs(['shell', 'rm', '-f', '%s.orig' % self.userJS]) - if self._dm.useDDCopy: - self._dm.checkCmdAs(['shell', 'dd', 'if=%s' % self.userJS, 'of=%s.orig' % self.userJS]) - else: - self._dm.checkCmdAs(['shell', 'cp', self.userJS, '%s.orig' % self.userJS]) + if not self._dm.fileExists('%s.orig' % self.userJS): + self.copyRemoteFile(self.userJS, '%s.orig' % self.userJS) self._dm.pushFile(os.path.join(options.profilePath, "user.js"), self.userJS) - self.updateProfilesIni(self.remoteProfile) - options.profilePath = self.remoteProfile options.logFile = self.localLog return retVal @@ -419,7 +419,6 @@ def main(): kwargs.update({'host': options.deviceIP, 'port': options.devicePort}) dm = devicemanagerADB.DeviceManagerADB(**kwargs) - auto.setDeviceManager(dm) options = parser.verifyRemoteOptions(options, auto) if (options == None): @@ -435,12 +434,12 @@ def main(): sys.exit(1) logParent = os.path.dirname(options.remoteLogFile) - dm.mkDir(logParent); + dm.mkDir(logParent) auto.setRemoteLog(options.remoteLogFile) auto.setServerInfo(options.webServer, options.httpPort, options.sslPort) - retVal = 1 try: + mochitest.cleanup(None, options) retVal = mochitest.runTests(options) except: print "TEST-UNEXPECTED-FAIL | %s | Exception caught while running tests." % sys.exc_info()[1]