mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 759885 - Add support for running mochitest-plain on B2G emulators, r=jmaher, DONTBUILD because NPOTB
This commit is contained in:
parent
97a0230af4
commit
36e0617209
@ -3,7 +3,9 @@
|
||||
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
import automationutils
|
||||
import threading
|
||||
import os
|
||||
import Queue
|
||||
import re
|
||||
import socket
|
||||
import shutil
|
||||
@ -13,6 +15,21 @@ import time
|
||||
|
||||
from automation import Automation
|
||||
from devicemanager import DeviceManager, NetworkTools
|
||||
from mozprocess import ProcessHandlerMixin
|
||||
|
||||
|
||||
class LogcatProc(ProcessHandlerMixin):
|
||||
"""Process handler for logcat which puts all output in a Queue.
|
||||
"""
|
||||
|
||||
def __init__(self, cmd, queue, **kwargs):
|
||||
self.queue = queue
|
||||
kwargs.setdefault('processOutputLine', []).append(self.handle_output)
|
||||
ProcessHandlerMixin.__init__(self, cmd, **kwargs)
|
||||
|
||||
def handle_output(self, line):
|
||||
self.queue.put_nowait(line)
|
||||
|
||||
|
||||
class B2GRemoteAutomation(Automation):
|
||||
_devicemanager = None
|
||||
@ -24,11 +41,15 @@ class B2GRemoteAutomation(Automation):
|
||||
self._remoteProfile = None
|
||||
self._remoteLog = remoteLog
|
||||
self.marionette = marionette
|
||||
self._is_emulator = False
|
||||
|
||||
# Default our product to b2g
|
||||
self._product = "b2g"
|
||||
Automation.__init__(self)
|
||||
|
||||
def setEmulator(self, is_emulator):
|
||||
self._is_emulator = is_emulator
|
||||
|
||||
def setDeviceManager(self, deviceManager):
|
||||
self._devicemanager = deviceManager
|
||||
|
||||
@ -51,6 +72,8 @@ class B2GRemoteAutomation(Automation):
|
||||
if env is None:
|
||||
env = {}
|
||||
|
||||
# We always hide the results table in B2G; it's much slower if we don't.
|
||||
env['MOZ_HIDE_RESULTS_TABLE'] = '1'
|
||||
return env
|
||||
|
||||
def checkForCrashes(self, directory, symbolsPath):
|
||||
@ -119,6 +142,14 @@ class B2GRemoteAutomation(Automation):
|
||||
|
||||
return (serial, status)
|
||||
|
||||
def restartB2G(self):
|
||||
self._devicemanager.checkCmd(['shell', 'stop', 'b2g'])
|
||||
# Wait for a bit to make sure B2G has completely shut down.
|
||||
time.sleep(5)
|
||||
self._devicemanager.checkCmd(['shell', 'start', 'b2g'])
|
||||
if self._is_emulator:
|
||||
self.marionette.emulator.wait_for_port()
|
||||
|
||||
def rebootDevice(self):
|
||||
# find device's current status and serial number
|
||||
serial, status = self.getDeviceStatus()
|
||||
@ -153,7 +184,10 @@ class B2GRemoteAutomation(Automation):
|
||||
# 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.
|
||||
self.rebootDevice()
|
||||
if self._is_emulator:
|
||||
self.restartB2G()
|
||||
else:
|
||||
self.rebootDevice()
|
||||
|
||||
# Infrequently, gecko comes up before networking does, so wait a little
|
||||
# bit to give the network time to become available.
|
||||
@ -162,9 +196,10 @@ class B2GRemoteAutomation(Automation):
|
||||
|
||||
# Set up port forwarding again for Marionette, since any that
|
||||
# existed previously got wiped out by the reboot.
|
||||
self._devicemanager.checkCmd(['forward',
|
||||
'tcp:%s' % self.marionette.port,
|
||||
'tcp:%s' % self.marionette.port])
|
||||
if not self._is_emulator:
|
||||
self._devicemanager.checkCmd(['forward',
|
||||
'tcp:%s' % self.marionette.port,
|
||||
'tcp:%s' % self.marionette.port])
|
||||
|
||||
# start a marionette session
|
||||
session = self.marionette.start_session()
|
||||
@ -185,7 +220,26 @@ class B2GRemoteAutomation(Automation):
|
||||
|
||||
def __init__(self, dm):
|
||||
self.dm = dm
|
||||
self.lastloglines = []
|
||||
self.logcat_proc = None
|
||||
self.queue = Queue.Queue()
|
||||
|
||||
# Launch logcat 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))
|
||||
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
|
||||
|
||||
@property
|
||||
def pid(self):
|
||||
@ -194,33 +248,15 @@ class B2GRemoteAutomation(Automation):
|
||||
|
||||
@property
|
||||
def stdout(self):
|
||||
# Return any part of logcat output that wasn't returned
|
||||
# previously. This is done by fetching about the last 50
|
||||
# lines of the log (logcat -t 50 generally fetches 50-58 lines),
|
||||
# and comparing against the previous set to find new lines.
|
||||
t = self.dm.runCmd(['logcat', '-t', '50']).stdout.read()
|
||||
if t == None: return ''
|
||||
|
||||
t = t.strip('\n').strip()
|
||||
loglines = t.split('\n')
|
||||
line_index = 0
|
||||
|
||||
# If there are more than 50 lines, we skip the first 20; this
|
||||
# is because the number of lines returned
|
||||
# by logcat -t 50 varies (usually between 50 and 58).
|
||||
log_index = 20 if len(loglines) > 50 else 0
|
||||
|
||||
for index, line in enumerate(loglines[log_index:]):
|
||||
line_index = index + log_index + 1
|
||||
# Return any lines in the queue used by the
|
||||
# logcat process handler.
|
||||
lines = []
|
||||
while True:
|
||||
try:
|
||||
self.lastloglines.index(line)
|
||||
except ValueError:
|
||||
lines.append(self.queue.get_nowait())
|
||||
except Queue.Empty:
|
||||
break
|
||||
|
||||
newoutput = '\n'.join(loglines[line_index:])
|
||||
self.lastloglines = loglines
|
||||
|
||||
return newoutput
|
||||
return '\n'.join(lines)
|
||||
|
||||
def wait(self, timeout = None):
|
||||
# this should never happen
|
||||
|
@ -40,10 +40,10 @@ class B2GOptions(MochitestOptions):
|
||||
help = "host:port to use when connecting to Marionette")
|
||||
defaults["marionette"] = None
|
||||
|
||||
self.add_option("--emulator", action="store_true",
|
||||
dest = "emulator",
|
||||
help = "True if using a b2g emulator")
|
||||
defaults["emulator"] = False
|
||||
self.add_option("--emulator", action="store",
|
||||
type="string", dest = "emulator",
|
||||
help = "Architecture of emulator to use: x86 or arm")
|
||||
defaults["emulator"] = None
|
||||
|
||||
self.add_option("--adbpath", action="store",
|
||||
type = "string", dest = "adbPath",
|
||||
@ -186,21 +186,38 @@ class B2GMochitest(Mochitest):
|
||||
self.remoteProfile = options.remoteTestRoot + '/profile'
|
||||
self._automation.setRemoteProfile(self.remoteProfile)
|
||||
self.remoteLog = options.remoteLogFile
|
||||
self.remoteProfilesIniPath = '/data/b2g/mozilla/profiles.ini'
|
||||
self.userJS = '/data/local/user.js'
|
||||
self.testDir = '/data/local/tests'
|
||||
self.remoteMozillaPath = '/data/b2g/mozilla'
|
||||
self.remoteProfilesIniPath = os.path.join(self.remoteMozillaPath, 'profiles.ini')
|
||||
self.originalProfilesIni = None
|
||||
|
||||
def cleanup(self, manifest, options):
|
||||
self._dm.getFile(self.remoteLog, self.localLog)
|
||||
self._dm.removeFile(self.remoteLog)
|
||||
self._dm.removeDir(self.remoteProfile)
|
||||
|
||||
# Restore the original profiles.ini.
|
||||
if self.originalProfilesIni:
|
||||
try:
|
||||
self.restoreProfilesIni()
|
||||
if not options.emulator:
|
||||
self.restoreProfilesIni()
|
||||
os.remove(self.originalProfilesIni)
|
||||
except:
|
||||
pass
|
||||
|
||||
if not options.emulator:
|
||||
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])
|
||||
|
||||
# We've restored the original profile, so reboot the device so that
|
||||
# it gets picked up.
|
||||
self._automation.rebootDevice()
|
||||
|
||||
if options.pidFile != "":
|
||||
try:
|
||||
os.remove(options.pidFile)
|
||||
@ -208,10 +225,6 @@ class B2GMochitest(Mochitest):
|
||||
except:
|
||||
print "Warning: cleaning up pidfile '%s' was unsuccessful from the test harness" % options.pidFile
|
||||
|
||||
# We've restored the original profile, so reboot the device so that
|
||||
# it gets picked up.
|
||||
self._automation.rebootDevice()
|
||||
|
||||
def findPath(self, paths, filename = None):
|
||||
for path in paths:
|
||||
p = path
|
||||
@ -283,7 +296,6 @@ class B2GMochitest(Mochitest):
|
||||
def buildProfile(self, options):
|
||||
if self.localProfile:
|
||||
options.profilePath = self.localProfile
|
||||
print 'buildProfile', repr(options)
|
||||
manifest = Mochitest.buildProfile(self, options)
|
||||
self.localProfile = options.profilePath
|
||||
|
||||
@ -318,6 +330,10 @@ class B2GMochitest(Mochitest):
|
||||
config.write(configfile)
|
||||
|
||||
self._dm.pushFile(newProfilesIni, self.remoteProfilesIniPath)
|
||||
try:
|
||||
os.remove(newProfilesIni)
|
||||
except:
|
||||
pass
|
||||
|
||||
def buildURLOptions(self, options, env):
|
||||
self.localLog = options.logFile
|
||||
@ -334,13 +350,23 @@ class B2GMochitest(Mochitest):
|
||||
# Set the B2G homepage as a static local page, since wi-fi generally
|
||||
# isn't available as soon as the device boots.
|
||||
f = open(os.path.join(options.profilePath, "user.js"), "a")
|
||||
f.write('user_pref("browser.homescreenURL", "data:text/html,mochitest-plain should start soon");\n')
|
||||
f.write('user_pref("browser.homescreenURL", "data:text/html,<h1>mochitest-plain should start soon</h1>");\n')
|
||||
f.close()
|
||||
|
||||
# Copy the profile to the device.
|
||||
self._dm.removeDir(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])
|
||||
self._dm.pushFile(os.path.join(options.profilePath, "user.js"), self.userJS)
|
||||
|
||||
self.updateProfilesIni(self.remoteProfile)
|
||||
|
||||
options.profilePath = self.remoteProfile
|
||||
@ -355,7 +381,10 @@ def main():
|
||||
options, args = parser.parse_args()
|
||||
|
||||
# create our Marionette instance
|
||||
kwargs = {'emulator': options.emulator}
|
||||
kwargs = {}
|
||||
if options.emulator:
|
||||
kwargs['emulator'] = options.emulator
|
||||
auto.setEmulator(True)
|
||||
if options.b2gPath:
|
||||
kwargs['homedir'] = options.b2gPath
|
||||
if options.marionette:
|
||||
@ -383,6 +412,10 @@ def main():
|
||||
|
||||
mochitest = B2GMochitest(auto, dm, options)
|
||||
|
||||
# Create /data/local/tests, to force its use by DeviceManagerADB;
|
||||
# B2G won't run correctly with the profile installed to /mnt/sdcard.
|
||||
dm.mkDirs(mochitest.testDir)
|
||||
|
||||
options = parser.verifyOptions(options, mochitest)
|
||||
if (options == None):
|
||||
sys.exit(1)
|
||||
|
Loading…
Reference in New Issue
Block a user