зеркало из https://github.com/mozilla/pjs.git
Bug 573263 - Refactor remote reftest to work on android, create shared remoteautomation class r=jmaher
This commit is contained in:
Родитель
31e4b12204
Коммит
c1ea68a998
|
@ -50,6 +50,7 @@ import subprocess
|
|||
import sys
|
||||
import threading
|
||||
import tempfile
|
||||
import zipfile
|
||||
|
||||
SCRIPT_DIR = os.path.abspath(os.path.realpath(os.path.dirname(sys.argv[0])))
|
||||
sys.path.insert(0, SCRIPT_DIR)
|
||||
|
@ -825,3 +826,48 @@ user_pref("camino.use_system_proxy_settings", false); // Camino-only, harmless t
|
|||
ssltunnelProcess.kill()
|
||||
|
||||
return status
|
||||
|
||||
"""
|
||||
Copies an "installed" extension into the extensions directory of the given profile
|
||||
extensionSource - the source location of the extension files. This can be either
|
||||
a directory or a path to an xpi file.
|
||||
profileDir - the profile directory we are copying into. We will create the
|
||||
"extensions" directory there if it doesn't exist
|
||||
extensionID - the id of the extension to be used as the containing directory for the
|
||||
extension, i.e.
|
||||
this is the name of the folder in the <profileDir>/extensions/<extensionID>
|
||||
"""
|
||||
def installExtension(self, extensionSource, profileDir, extensionID):
|
||||
if (not os.path.exists(extensionSource)):
|
||||
self.log.info("INFO | automation.py | Cannot install extension no source at: %s", extensionSource)
|
||||
|
||||
if (not os.path.exists(profileDir)):
|
||||
self.log.info("INFO | automation.py | Cannot install extension invalid profileDir at: %s", profileDir)
|
||||
|
||||
# See if we have an XPI or a directory
|
||||
if (os.path.isfile(extensionSource)):
|
||||
tmpd = tempfile.mkdtemp()
|
||||
extrootdir = self.extractZip(extensionSource, tmpd)
|
||||
else:
|
||||
extrootdir = extensionSource
|
||||
extnsdir = os.path.join(profileDir, "extensions")
|
||||
extnshome = os.path.join(extnsdir, extensionID)
|
||||
|
||||
# Now we copy the extension source into the extnshome
|
||||
shutil.copytree(extrootdir, extnshome)
|
||||
|
||||
def extractZip(self, filename, dest):
|
||||
z = zipfile.ZipFile(filename, 'r')
|
||||
for n in z.namelist():
|
||||
fullpath = os.path.join(dest, n)
|
||||
parentdir = os.path.dirname(fullpath)
|
||||
if not os.path.isdir(parentdir):
|
||||
os.makedirs(parentdir)
|
||||
if (not n.endswith(os.sep)):
|
||||
data = z.read(n)
|
||||
f = open(fullpath, 'w')
|
||||
f.write(data)
|
||||
f.close()
|
||||
z.close()
|
||||
return dest
|
||||
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is mozilla.org code.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Joel Maher.
|
||||
#
|
||||
# Portions created by the Initial Developer are Copyright (C) 2010
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Joel Maher <joel.maher@gmail.com> (Original Developer)
|
||||
# Clint Talbert <cmtalbert@gmail.com>
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
import time
|
||||
import sys
|
||||
import os
|
||||
|
||||
from automation import Automation
|
||||
from devicemanager import DeviceManager
|
||||
|
||||
class RemoteAutomation(Automation):
|
||||
_devicemanager = None
|
||||
|
||||
def __init__(self, deviceManager, appName = ''):
|
||||
self._devicemanager = deviceManager
|
||||
self._appName = appName
|
||||
self._remoteProfile = None
|
||||
# Default our product to fennec
|
||||
self._product = "fennec"
|
||||
Automation.__init__(self)
|
||||
|
||||
def setDeviceManager(self, deviceManager):
|
||||
self._devicemanager = deviceManager
|
||||
|
||||
def setAppName(self, appName):
|
||||
self._appName = appName
|
||||
|
||||
def setRemoteProfile(self, remoteProfile):
|
||||
self._remoteProfile = remoteProfile
|
||||
|
||||
def setProduct(self, product):
|
||||
self._product = product
|
||||
|
||||
def waitForFinish(self, proc, utilityPath, timeout, maxTime, startTime, debuggerInfo):
|
||||
# maxTime is used to override the default timeout, we should honor that
|
||||
status = proc.wait(timeout = maxTime)
|
||||
|
||||
print proc.stdout
|
||||
|
||||
if (status == 1 and self._devicemanager.processExist(proc.procName)):
|
||||
# Then we timed out, make sure Fennec is dead
|
||||
proc.kill()
|
||||
|
||||
return status
|
||||
|
||||
def buildCommandLine(self, app, debuggerInfo, profileDir, testURL, extraArgs):
|
||||
# If remote profile is specified, use that instead
|
||||
if (self._remoteProfile):
|
||||
profileDir = self._remoteProfile
|
||||
|
||||
cmd, args = Automation.buildCommandLine(self, app, debuggerInfo, profileDir, testURL, extraArgs)
|
||||
# Remove -foreground if it exists, if it doesn't this just returns
|
||||
try:
|
||||
args.remove('-foreground')
|
||||
except:
|
||||
pass
|
||||
#TODO: figure out which platform require NO_EM_RESTART
|
||||
# return app, ['--environ:NO_EM_RESTART=1'] + args
|
||||
return app, args
|
||||
|
||||
def Process(self, cmd, stdout = None, stderr = None, env = None, cwd = '.'):
|
||||
return self.RProcess(self._devicemanager, cmd, stdout, stderr, env, cwd)
|
||||
|
||||
# be careful here as this inner class doesn't have access to outer class members
|
||||
class RProcess(object):
|
||||
# device manager process
|
||||
dm = None
|
||||
def __init__(self, dm, cmd, stdout = None, stderr = None, env = None, cwd = '.'):
|
||||
self.dm = dm
|
||||
print "going to launch process: " + str(self.dm.host)
|
||||
self.proc = dm.launchProcess(cmd)
|
||||
exepath = cmd[0]
|
||||
name = exepath.split('/')[-1]
|
||||
self.procName = name
|
||||
|
||||
# Setting timeout at 1 hour since on a remote device this takes much longer
|
||||
self.timeout = 3600
|
||||
time.sleep(15)
|
||||
|
||||
@property
|
||||
def pid(self):
|
||||
hexpid = self.dm.processExist(self.procName)
|
||||
if (hexpid == '' or hexpid == None):
|
||||
hexpid = "0x0"
|
||||
return int(hexpid, 0)
|
||||
|
||||
@property
|
||||
def stdout(self):
|
||||
return self.dm.getFile(self.proc)
|
||||
|
||||
def wait(self, timeout = None):
|
||||
timer = 0
|
||||
interval = 5
|
||||
|
||||
if timeout == None:
|
||||
timeout = self.timeout
|
||||
|
||||
while (self.dm.processExist(self.procName)):
|
||||
time.sleep(interval)
|
||||
timer += interval
|
||||
if (timer > timeout):
|
||||
break
|
||||
|
||||
if (timer >= timeout):
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def kill(self):
|
||||
self.dm.killProcess(self.procName)
|
|
@ -79,6 +79,7 @@ _HARNESS_FILES = \
|
|||
$(topsrcdir)/build/mobile/devicemanager.py \
|
||||
$(topsrcdir)/build/automationutils.py \
|
||||
$(topsrcdir)/build/poster.zip \
|
||||
$(topsrcdir)/build/mobile/remoteautomation.py \
|
||||
$(NULL)
|
||||
|
||||
$(_DEST_DIR):
|
||||
|
|
|
@ -47,83 +47,7 @@ from runreftest import RefTest
|
|||
from runreftest import ReftestOptions
|
||||
from automation import Automation
|
||||
from devicemanager import DeviceManager
|
||||
|
||||
class RemoteAutomation(Automation):
|
||||
_devicemanager = None
|
||||
|
||||
def __init__(self, deviceManager, product = ''):
|
||||
self._devicemanager = deviceManager
|
||||
self._product = product
|
||||
Automation.__init__(self)
|
||||
|
||||
def setDeviceManager(self, deviceManager):
|
||||
self._devicemanager = deviceManager
|
||||
|
||||
def setProduct(self, productName):
|
||||
self._product = productName
|
||||
|
||||
def setRemoteApp(self, remoteAppName):
|
||||
self._remoteAppName = remoteAppName
|
||||
|
||||
def setTestRoot(self, testRoot):
|
||||
self._testRoot = testRoot
|
||||
|
||||
def waitForFinish(self, proc, utilityPath, timeout, maxTime, startTime):
|
||||
status = proc.wait()
|
||||
print proc.stdout
|
||||
# todo: consider pulling log file from remote
|
||||
return status
|
||||
|
||||
def buildCommandLine(self, app, debuggerInfo, profileDir, testURL, extraArgs):
|
||||
remoteProfileDir = self._testRoot + 'profile'
|
||||
cmd, args = Automation.buildCommandLine(self, app, debuggerInfo, remoteProfileDir, testURL, extraArgs)
|
||||
return app, ['--environ:NO_EM_RESTART=1'] + args
|
||||
|
||||
def Process(self, cmd, stdout = None, stderr = None, env = None, cwd = '.'):
|
||||
return self.RProcess(self._devicemanager, self._remoteAppName, cmd, stdout, stderr, env, cwd)
|
||||
|
||||
class RProcess(object):
|
||||
#device manager process
|
||||
dm = None
|
||||
def __init__(self, dm, appName, cmd, stdout = None, stderr = None, env = None, cwd = '.'):
|
||||
self.dm = dm
|
||||
print "going to launch process: " + str(self.dm.host)
|
||||
self.proc = dm.launchProcess(cmd)
|
||||
self.procName = appName
|
||||
|
||||
# Setting this at 1 hour since remote testing is much slower
|
||||
self.timeout = 3600
|
||||
time.sleep(5)
|
||||
|
||||
@property
|
||||
def pid(self):
|
||||
hexpid = self.dm.processExist(self.procName)
|
||||
if (hexpid == '' or hexpid == None):
|
||||
hexpid = 0
|
||||
return int(hexpid, 0)
|
||||
|
||||
@property
|
||||
def stdout(self):
|
||||
return self.dm.getFile(self.proc)
|
||||
|
||||
def wait(self, timeout = None):
|
||||
timer = 0
|
||||
if timeout == None:
|
||||
timeout = self.timeout
|
||||
|
||||
while (self.dm.process.isAlive()):
|
||||
time.sleep(1)
|
||||
timer += 1
|
||||
if (timer > timeout):
|
||||
break
|
||||
|
||||
if (timer >= timeout):
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def kill(self):
|
||||
self.dm.killProcess(self.procName)
|
||||
|
||||
from remoteautomation import RemoteAutomation
|
||||
|
||||
class RemoteOptions(ReftestOptions):
|
||||
def __init__(self, automation):
|
||||
|
@ -132,38 +56,80 @@ class RemoteOptions(ReftestOptions):
|
|||
defaults = {}
|
||||
defaults["logFile"] = "reftest.log"
|
||||
# app, xrePath and utilityPath variables are set in main function
|
||||
defaults["testRoot"] = "/tests/"
|
||||
defaults["remoteTestRoot"] = None
|
||||
defaults["app"] = ""
|
||||
defaults["xrePath"] = ""
|
||||
defaults["utilityPath"] = ""
|
||||
|
||||
self.add_option("--device", action="store",
|
||||
type = "string", dest = "device",
|
||||
self.add_option("--remote-app-path", action="store",
|
||||
type = "string", dest = "remoteAppPath",
|
||||
help = "Path to remote executable relative to device root using only forward slashes. Either this or app must be specified, but not both.")
|
||||
defaults["remoteAppPath"] = None
|
||||
|
||||
self.add_option("--deviceIP", action="store",
|
||||
type = "string", dest = "deviceIP",
|
||||
help = "ip address of remote device to test")
|
||||
defaults["device"] = None
|
||||
defaults["deviceIP"] = None
|
||||
|
||||
self.add_option("--devicePort", action="store",
|
||||
type = "string", dest = "devicePort",
|
||||
help = "port of remote device to test")
|
||||
defaults["devicePort"] = 27020
|
||||
defaults["devicePort"] = 20701
|
||||
|
||||
self.add_option("--remoteProductName", action="store",
|
||||
self.add_option("--remote-product-name", action="store",
|
||||
type = "string", dest = "remoteProductName",
|
||||
help = "Name of remote product to test - either fennec or firefox, defaults to fennec")
|
||||
help = "Name of product to test - either fennec or firefox, defaults to fennec")
|
||||
defaults["remoteProductName"] = "fennec"
|
||||
|
||||
self.add_option("--remoteAppName", action="store",
|
||||
type = "string", dest = "remoteAppName",
|
||||
help = "Executable name for remote device, OS dependent, defaults to fennec.exe")
|
||||
defaults["remoteAppName"] = "fennec.exe"
|
||||
|
||||
self.add_option("--remote-webserver", action="store",
|
||||
type = "string", dest = "remoteWebServer",
|
||||
help = "IP Address of the webserver hosting the reftest content")
|
||||
defaults["remoteWebServer"] = "127.0.0.1"
|
||||
defaults["remoteWebServer"] = None
|
||||
|
||||
self.add_option("--http-port", action = "store",
|
||||
type = "string", dest = "httpPort",
|
||||
help = "port of the web server for http traffic")
|
||||
defaults["httpPort"] = automation.DEFAULT_HTTP_PORT
|
||||
|
||||
self.add_option("--ssl-port", action = "store",
|
||||
type = "string", dest = "sslPort",
|
||||
help = "Port for https traffic to the web server")
|
||||
defaults["sslPort"] = automation.DEFAULT_SSL_PORT
|
||||
|
||||
self.add_option("--remote-logfile", action="store",
|
||||
type = "string", dest = "remoteLogFile",
|
||||
help = "Name of log file on the device relative to device root. PLEASE USE ONLY A FILENAME.")
|
||||
defaults["remoteLogFile"] = "reftest.log"
|
||||
|
||||
self.set_defaults(**defaults)
|
||||
|
||||
def verifyRemoteOptions(self, options):
|
||||
# Ensure our defaults are set properly for everything we can infer
|
||||
options.remoteTestRoot = self._automation._devicemanager.getDeviceRoot() + '/reftest'
|
||||
options.remoteProfile = options.remoteTestRoot + "/profile"
|
||||
|
||||
# One of remoteAppPath (relative path to application) or the app (executable) must be
|
||||
# set, but not both. If both are set, we destroy the user's selection for app
|
||||
# so instead of silently destroying a user specificied setting, we error.
|
||||
if (options.remoteAppPath and options.app):
|
||||
print "ERROR: You cannot specify both the remoteAppPath and the app"
|
||||
return None
|
||||
elif (options.remoteAppPath):
|
||||
options.app = options.remoteTestRoot + "/" + options.remoteAppPath
|
||||
elif (options.app == None):
|
||||
# Neither remoteAppPath nor app are set -- error
|
||||
print "ERROR: You must specify either appPath or app"
|
||||
return None
|
||||
|
||||
if (options.xrePath == None):
|
||||
print "ERROR: You must specify the path to the controller xre directory"
|
||||
return None
|
||||
|
||||
# TODO: Copied from main, but I think these are no longer used in a post xulrunner world
|
||||
#options.xrePath = options.remoteTestRoot + self._automation._product + '/xulrunner'
|
||||
#options.utilityPath = options.testRoot + self._automation._product + '/bin'
|
||||
return options
|
||||
|
||||
class RemoteReftest(RefTest):
|
||||
remoteApp = ''
|
||||
|
||||
|
@ -172,39 +138,32 @@ class RemoteReftest(RefTest):
|
|||
self._devicemanager = devicemanager
|
||||
self.scriptDir = scriptDir
|
||||
self.remoteApp = options.app
|
||||
self.remoteTestRoot = options.testRoot
|
||||
self.remoteProfileDir = options.testRoot + 'profile'
|
||||
self.remoteTestRoot = options.remoteTestRoot
|
||||
|
||||
def createReftestProfile(self, options, profileDir):
|
||||
RefTest.createReftestProfile(self, options, profileDir)
|
||||
|
||||
self.remoteTestRoot += "reftest/"
|
||||
|
||||
# install the reftest extension bits into the remoteProfile
|
||||
profileExtensionsPath = os.path.join(profileDir, "extensions")
|
||||
reftestExtensionPath = self.remoteTestRoot.replace('/', '\\')
|
||||
extFile = open(os.path.join(profileExtensionsPath, "reftest@mozilla.org"), "w")
|
||||
extFile.write(reftestExtensionPath)
|
||||
extFile.close()
|
||||
|
||||
if (self._devicemanager.pushDir(profileDir, self.remoteProfileDir) == None):
|
||||
raise devicemanager.FileError("Failed to copy profiledir to device")
|
||||
|
||||
if (self._devicemanager.pushDir(self.scriptDir + '/reftest', self.remoteTestRoot) == None):
|
||||
raise devicemanager.FileError("Failed to copy extension dir to device")
|
||||
|
||||
if (self._devicemanager.pushDir(profileDir, options.remoteProfile) == None):
|
||||
raise devicemanager.FileError("Failed to copy profiledir to device")
|
||||
|
||||
def copyExtraFilesToProfile(self, options, profileDir):
|
||||
RefTest.copyExtraFilesToProfile(self, options, profileDir)
|
||||
if (self._devicemanager.pushDir(profileDir, self.remoteProfileDir) == None):
|
||||
raise devicemanager.FileError("Failed to copy extra files in profile dir to device")
|
||||
if (self._devicemanager.pushDir(profileDir, options.remoteProfile) == None):
|
||||
raise devicemanager.FileError("Failed to copy extra files to device")
|
||||
|
||||
def registerExtension(self, browserEnv, options, profileDir):
|
||||
"""
|
||||
It appears that we do not need to do the extension registration on winmo.
|
||||
This is something we should look into for winmo as the -silent option isn't working
|
||||
"""
|
||||
pass
|
||||
def registerExtension(self, browserEnv, options, profileDir, extraArgs = ['-silent'] ):
|
||||
self.automation.log.info("REFTEST INFO | runreftest.py | Performing extension manager registration: start.\n")
|
||||
# Because our startProcess code doesn't return until fennec starts we just give it
|
||||
# a maxTime of 20 secs before timing it out and ensuring it is dead.
|
||||
# Besides registering the extension, this works around fennec bug 570027
|
||||
status = self.automation.runApp(None, browserEnv, options.app, profileDir,
|
||||
extraArgs,
|
||||
utilityPath = options.utilityPath,
|
||||
xrePath=options.xrePath,
|
||||
symbolsPath=options.symbolsPath,
|
||||
maxTime = 20)
|
||||
# We don't care to call |processLeakLog()| for this step.
|
||||
self.automation.log.info("\nREFTEST INFO | runreftest.py | Performing extension manager registration: end.")
|
||||
|
||||
def getManifestPath(self, path):
|
||||
return path
|
||||
|
@ -220,22 +179,24 @@ def main():
|
|||
parser = RemoteOptions(automation)
|
||||
options, args = parser.parse_args()
|
||||
|
||||
if (options.device == None):
|
||||
if (options.deviceIP == None):
|
||||
print "Error: you must provide a device IP to connect to via the --device option"
|
||||
sys.exit(1)
|
||||
|
||||
if options.remoteAppName.rfind('/') < 0:
|
||||
options.app = options.testRoot + options.remoteProductName + '/'
|
||||
options.app += str(options.remoteAppName)
|
||||
|
||||
options.xrePath = options.testRoot + options.remoteProductName + '/xulrunner'
|
||||
options.utilityPath = options.testRoot + options.remoteProductName + '/bin'
|
||||
|
||||
dm = DeviceManager(options.device, options.devicePort)
|
||||
dm = DeviceManager(options.deviceIP, options.devicePort)
|
||||
automation.setDeviceManager(dm)
|
||||
automation.setProduct(options.remoteProductName)
|
||||
automation.setRemoteApp(options.remoteAppName)
|
||||
automation.setTestRoot(options.testRoot)
|
||||
|
||||
if (options.remoteProductName != None):
|
||||
automation.setProduct(options.remoteProductName)
|
||||
|
||||
# Set up the defaults and ensure options are set
|
||||
options = parser.verifyRemoteOptions(options)
|
||||
if (options == None):
|
||||
print "ERROR: Invalid options specified, use --help for a list of valid options"
|
||||
sys.exit(1)
|
||||
|
||||
automation.setAppName(options.app)
|
||||
automation.setRemoteProfile(options.remoteProfile)
|
||||
reftest = RemoteReftest(automation, dm, options, SCRIPT_DIRECTORY)
|
||||
|
||||
if (options.remoteWebServer == "127.0.0.1"):
|
||||
|
|
|
@ -93,12 +93,10 @@ class RefTest(object):
|
|||
prefsFile.close()
|
||||
|
||||
# install the reftest extension bits into the profile
|
||||
profileExtensionsPath = os.path.join(profileDir, "extensions")
|
||||
os.mkdir(profileExtensionsPath)
|
||||
reftestExtensionPath = os.path.join(SCRIPT_DIRECTORY, "reftest")
|
||||
extFile = open(os.path.join(profileExtensionsPath, "reftest@mozilla.org"), "w")
|
||||
extFile.write(reftestExtensionPath)
|
||||
extFile.close()
|
||||
self.automation.installExtension(os.path.join(SCRIPT_DIRECTORY, "reftest"),
|
||||
profileDir,
|
||||
"reftest@mozilla.org")
|
||||
|
||||
|
||||
def registerExtension(self, browserEnv, options, profileDir, extraArgs = ['-silent']):
|
||||
# run once with -silent to let the extension manager do its thing
|
||||
|
@ -149,7 +147,6 @@ class RefTest(object):
|
|||
# then again to actually run reftest
|
||||
self.automation.log.info("REFTEST INFO | runreftest.py | Running tests: start.\n")
|
||||
reftestlist = self.getManifestPath(manifest)
|
||||
|
||||
status = self.automation.runApp(None, browserEnv, options.app, profileDir,
|
||||
["-reftest", reftestlist],
|
||||
utilityPath = options.utilityPath,
|
||||
|
@ -179,14 +176,15 @@ class RefTest(object):
|
|||
class ReftestOptions(OptionParser):
|
||||
|
||||
def __init__(self, automation):
|
||||
self._automation = automation
|
||||
OptionParser.__init__(self)
|
||||
defaults = {}
|
||||
|
||||
# we want to pass down everything from automation.__all__
|
||||
addCommonOptions(self,
|
||||
defaults=dict(zip(automation.__all__,
|
||||
[getattr(automation, x) for x in automation.__all__])))
|
||||
automation.addCommonOptions(self)
|
||||
defaults=dict(zip(self._automation.__all__,
|
||||
[getattr(self._automation, x) for x in self._automation.__all__])))
|
||||
self._automation.addCommonOptions(self)
|
||||
self.add_option("--appname",
|
||||
action = "store", type = "string", dest = "app",
|
||||
default = os.path.join(SCRIPT_DIRECTORY, automation.DEFAULT_APP),
|
||||
|
@ -208,10 +206,10 @@ class ReftestOptions(OptionParser):
|
|||
"than the given number")
|
||||
self.add_option("--utility-path",
|
||||
action = "store", type = "string", dest = "utilityPath",
|
||||
default = automation.DIST_BIN,
|
||||
default = self._automation.DIST_BIN,
|
||||
help = "absolute path to directory containing utility "
|
||||
"programs (xpcshell, ssltunnel, certutil)")
|
||||
defaults["utilityPath"] = automation.DIST_BIN
|
||||
defaults["utilityPath"] = self._automation.DIST_BIN
|
||||
|
||||
self.add_option("--total-chunks",
|
||||
type = "int", dest = "totalChunks",
|
||||
|
|
|
@ -66,6 +66,7 @@ _SERV_FILES = \
|
|||
$(topsrcdir)/build/mobile/devicemanager.py \
|
||||
$(topsrcdir)/build/automationutils.py \
|
||||
$(topsrcdir)/build/poster.zip \
|
||||
$(topsrcdir)/build/mobile/remoteautomation.py \
|
||||
gen_template.pl \
|
||||
server.js \
|
||||
harness-a11y.xul \
|
||||
|
|
|
@ -341,7 +341,10 @@ class MochitestServer:
|
|||
c = urllib2.urlopen(self.shutdownURL)
|
||||
c.read()
|
||||
c.close()
|
||||
self._process.wait()
|
||||
|
||||
rtncode = self._process.poll()
|
||||
if (rtncode == None):
|
||||
self._process.terminate()
|
||||
except:
|
||||
self._process.kill()
|
||||
|
||||
|
|
|
@ -44,100 +44,24 @@ import tempfile
|
|||
sys.path.insert(0, os.path.abspath(os.path.realpath(os.path.dirname(sys.argv[0]))))
|
||||
|
||||
from automation import Automation
|
||||
from remoteautomation import RemoteAutomation
|
||||
from runtests import Mochitest
|
||||
from runtests import MochitestOptions
|
||||
from runtests import MochitestServer
|
||||
|
||||
import devicemanager
|
||||
|
||||
class RemoteAutomation(Automation):
|
||||
_devicemanager = None
|
||||
|
||||
def __init__(self, deviceManager, product):
|
||||
self._devicemanager = deviceManager
|
||||
self._product = product
|
||||
Automation.__init__(self)
|
||||
|
||||
def setDeviceManager(self, deviceManager):
|
||||
self._devicemanager = deviceManager
|
||||
|
||||
def setProduct(self, productName):
|
||||
self._product = productName
|
||||
|
||||
def waitForFinish(self, proc, utilityPath, timeout, maxTime, startTime, debuggerInfo):
|
||||
status = proc.wait()
|
||||
print proc.stdout
|
||||
# todo: consider pulling log file from remote
|
||||
return status
|
||||
|
||||
def buildCommandLine(self, app, debuggerInfo, profileDir, testURL, extraArgs):
|
||||
cmd, args = Automation.buildCommandLine(self, app, debuggerInfo, profileDir, testURL, extraArgs)
|
||||
# Remove -foreground if it exists, if it doesn't this just returns
|
||||
try:
|
||||
args.remove('-foreground')
|
||||
except:
|
||||
pass
|
||||
#TODO: figure out which platform require NO_EM_RESTART
|
||||
# return app, ['--environ:NO_EM_RESTART=1'] + args
|
||||
return app, args
|
||||
|
||||
def Process(self, cmd, stdout = None, stderr = None, env = None, cwd = '.'):
|
||||
return self.RProcess(self._devicemanager, self._product, cmd, stdout, stderr, env, cwd)
|
||||
|
||||
# be careful here as this inner class doesn't have access to outer class members
|
||||
class RProcess(object):
|
||||
# device manager process
|
||||
dm = None
|
||||
def __init__(self, dm, product, cmd, stdout = None, stderr = None, env = None, cwd = '.'):
|
||||
self.dm = dm
|
||||
print "going to launch process: " + str(self.dm.host)
|
||||
self.proc = dm.launchProcess(cmd)
|
||||
exepath = cmd[0]
|
||||
name = exepath.split('/')[-1]
|
||||
self.procName = name
|
||||
|
||||
# Setting timeout at 1 hour since on a remote device this takes much longer
|
||||
self.timeout = 3600
|
||||
time.sleep(15)
|
||||
|
||||
@property
|
||||
def pid(self):
|
||||
hexpid = self.dm.processExist(self.procName)
|
||||
if (hexpid == '' or hexpid == None):
|
||||
hexpid = "0x0"
|
||||
return int(hexpid, 0)
|
||||
|
||||
@property
|
||||
def stdout(self):
|
||||
return self.dm.getFile(self.proc)
|
||||
|
||||
def wait(self, timeout = None):
|
||||
timer = 0
|
||||
interval = 5
|
||||
|
||||
if timeout == None:
|
||||
timeout = self.timeout
|
||||
|
||||
while (self.dm.processExist(self.procName)):
|
||||
time.sleep(interval)
|
||||
timer += interval
|
||||
if (timer > timeout):
|
||||
break
|
||||
|
||||
if (timer >= timeout):
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def kill(self):
|
||||
self.dm.killProcess(self.procName)
|
||||
|
||||
|
||||
class RemoteOptions(MochitestOptions):
|
||||
|
||||
def __init__(self, automation, scriptdir, **kwargs):
|
||||
defaults = {}
|
||||
MochitestOptions.__init__(self, automation, scriptdir)
|
||||
|
||||
self.add_option("--remote-app-path", action="store",
|
||||
type = "string", dest = "remoteAppPath",
|
||||
help = "Path to remote executable relative to device root using only forward slashes. Either this or app must be specified but not both")
|
||||
defaults["remoteAppPath"] = None
|
||||
|
||||
self.add_option("--deviceIP", action="store",
|
||||
type = "string", dest = "deviceIP",
|
||||
help = "ip address of remote device to test")
|
||||
|
@ -148,7 +72,7 @@ class RemoteOptions(MochitestOptions):
|
|||
help = "port of remote device to test")
|
||||
defaults["devicePort"] = 20701
|
||||
|
||||
self.add_option("--remoteProductName", action="store",
|
||||
self.add_option("--remote-product-name", action="store",
|
||||
type = "string", dest = "remoteProductName",
|
||||
help = "The executable's name of remote product to test - either fennec or firefox, defaults to fennec")
|
||||
defaults["remoteProductName"] = "fennec"
|
||||
|
@ -185,41 +109,46 @@ class RemoteOptions(MochitestOptions):
|
|||
def verifyRemoteOptions(self, options, automation):
|
||||
options.remoteTestRoot = automation._devicemanager.getDeviceRoot()
|
||||
|
||||
options.utilityPath = options.remoteTestRoot + "/bin"
|
||||
options.certPath = options.remoteTestRoot + "/certs"
|
||||
|
||||
if options.remoteWebServer == None:
|
||||
if os.name != "nt":
|
||||
|
||||
if options.remoteWebServer == None and os.name != "nt":
|
||||
options.remoteWebServer = get_lan_ip()
|
||||
else:
|
||||
elif os.name == "nt":
|
||||
print "ERROR: you must specify a remoteWebServer ip address\n"
|
||||
return None
|
||||
|
||||
options.webServer = options.remoteWebServer
|
||||
|
||||
if (options.deviceIP == None):
|
||||
print "ERROR: you must provide a device IP"
|
||||
return None
|
||||
print "ERROR: you must provide a device IP"
|
||||
return None
|
||||
|
||||
if (options.remoteLogFile == None):
|
||||
options.remoteLogFile = automation._devicemanager.getDeviceRoot() + '/test.log'
|
||||
options.remoteLogFile = automation._devicemanager.getDeviceRoot() + '/test.log'
|
||||
|
||||
# Set up our options that we depend on based on the above
|
||||
productRoot = options.remoteTestRoot + "/" + automation._product
|
||||
options.utilityPath = productRoot + "/bin"
|
||||
|
||||
# Set this only if the user hasn't set it
|
||||
if (options.utilityPath == None):
|
||||
options.utilityPath = productRoot + "/bin"
|
||||
|
||||
# If provided, use cli value, otherwise reset as remoteTestRoot
|
||||
if (options.app == None):
|
||||
options.app = productRoot + "/" + options.remoteProductName
|
||||
# remoteAppPath or app must be specified to find the product to launch
|
||||
if (options.remoteAppPath and options.app):
|
||||
print "ERROR: You cannot specify both the remoteAppPath and the app setting"
|
||||
return None
|
||||
elif (options.remoteAppPath):
|
||||
options.app = options.remoteTestRoot + "/" + options.remoteAppPath
|
||||
elif (options.app == None):
|
||||
# Neither remoteAppPath nor app are set -- error
|
||||
print "ERROR: You must specify either appPath or app"
|
||||
return None
|
||||
|
||||
# Only reset the xrePath if it wasn't provided
|
||||
if (options.xrePath == None):
|
||||
if (automation._product == "fennec"):
|
||||
options.xrePath = productRoot + "/xulrunner"
|
||||
else:
|
||||
options.xrePath = options.utilityPath
|
||||
if (automation._product == "fennec"):
|
||||
options.xrePath = productRoot + "/xulrunner"
|
||||
else:
|
||||
options.xrePath = options.utilityPath
|
||||
|
||||
return options
|
||||
|
||||
|
@ -283,7 +212,7 @@ class MochiRemote(Mochitest):
|
|||
xpcshell = "xpcshell"
|
||||
if (os.name == "nt"):
|
||||
xpcshell += ".exe"
|
||||
|
||||
|
||||
if (options.utilityPath):
|
||||
paths.insert(0, options.utilityPath)
|
||||
options.utilityPath = self.findPath(paths, xpcshell)
|
||||
|
@ -303,9 +232,6 @@ class MochiRemote(Mochitest):
|
|||
def stopWebServer(self, options):
|
||||
self.server.stop()
|
||||
|
||||
def runExtensionRegistration(self, options, browserEnv):
|
||||
pass
|
||||
|
||||
def buildProfile(self, options):
|
||||
manifest = Mochitest.buildProfile(self, options)
|
||||
self.localProfile = options.profilePath
|
||||
|
@ -314,7 +240,24 @@ class MochiRemote(Mochitest):
|
|||
|
||||
options.profilePath = self.remoteProfile
|
||||
return manifest
|
||||
|
||||
|
||||
def runExtensionRegistration(self, options, browserEnv):
|
||||
""" run once with -silent to let the extension manager do its thing
|
||||
and then exit the app
|
||||
We do this on every run because we need to work around bug 570027
|
||||
"""
|
||||
self._automation.log.info("INFO | runtestsremote.py | Performing extension manager registration: start.\n")
|
||||
# Don't care about this |status|: |runApp()| reporting it should be enough.
|
||||
# Because process() doesn't return until fennec starts, we just give it a fudge
|
||||
# factor of 20s before timing it out and killing it.
|
||||
status = self._automation.runApp(None, browserEnv, options.app,
|
||||
options.profilePath, ["-silent"],
|
||||
utilityPath = options.utilityPath,
|
||||
xrePath = options.xrePath,
|
||||
symbolsPath=options.symbolsPath,
|
||||
maxTime = 20)
|
||||
# We don't care to call |processLeakLog()| for this step.
|
||||
self._automation.log.info("\nINFO | runtestsremote.py | Performing extension manager registration: end.")
|
||||
def buildURLOptions(self, options):
|
||||
self.localLog = options.logFile
|
||||
options.logFile = self.remoteLog
|
||||
|
|
Загрузка…
Ссылка в новой задаче