Bug 811388 - Update mozdevice to 0.15, update consumers;r=ahal

This commit is contained in:
William Lachance 2012-11-14 09:49:04 -08:00
parent 807a0d0fb3
commit 5a3130a3ae
8 changed files with 143 additions and 121 deletions

View File

@ -294,9 +294,9 @@ class B2GRemoteAutomation(Automation):
# 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 = [self.dm._adbPath]
if self.dm._deviceSerial:
cmd.extend(['-s', self.dm._deviceSerial])
cmd.append('shell')
cmd.append('/system/bin/b2g.sh')
proc = threading.Thread(target=self._save_stdout_proc, args=(cmd, self.queue))

View File

@ -253,7 +253,7 @@ class B2GReftest(RefTest):
# Restore the original user.js.
self._devicemanager._checkCmdAs(['shell', 'rm', '-f', self.userJS])
if self._devicemanager.useDDCopy:
if self._devicemanager._useDDCopy:
self._devicemanager._checkCmdAs(['shell', 'dd', 'if=%s.orig' % self.userJS, 'of=%s' % self.userJS])
else:
self._devicemanager._checkCmdAs(['shell', 'cp', '%s.orig' % self.userJS, self.userJS])
@ -435,7 +435,7 @@ user_pref("capability.principal.codebase.p2.id", "http://%s:%s");
# 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._devicemanager._checkCmdAs(['shell', 'rm', '-f', '%s.orig' % self.userJS])
if self._devicemanager.useDDCopy:
if self._devicemanager._useDDCopy:
self._devicemanager._checkCmdAs(['shell', 'dd', 'if=%s' % self.userJS, 'of=%s.orig' % self.userJS])
else:
self._devicemanager._checkCmdAs(['shell', 'cp', self.userJS, '%s.orig' % self.userJS])

View File

@ -212,7 +212,7 @@ class B2GMochitest(Mochitest):
self.originalProfilesIni = None
def copyRemoteFile(self, src, dest):
if self._dm.useDDCopy:
if self._dm._useDDCopy:
self._dm._checkCmdAs(['shell', 'dd', 'if=%s' % src,'of=%s' % dest])
else:
self._dm._checkCmdAs(['shell', 'cp', src, dest])

View File

@ -33,7 +33,7 @@ def abstractmethod(method):
class DeviceManager:
logcatNeedsRoot = True
_logcatNeedsRoot = True
@abstractmethod
def shell(self, cmd, outputfile, env=None, cwd=None, timeout=None, root=False):
@ -362,13 +362,15 @@ class DeviceManager:
return 0
def getIP(self, conn_type='eth0'):
def getIP(self, interfaces=['eth0', 'wlan0']):
"""
Gets the IP of the device, or None if no connection exists.
"""
match = re.match(r"%s: ip (\S+)" % conn_type, self.shellCheckOutput(['ifconfig', conn_type]))
if match:
return match.group(1)
for interface in interfaces:
match = re.match(r"%s: ip (\S+)" % interface,
self.shellCheckOutput(['ifconfig', interface]))
if match:
return match.group(1)
@abstractmethod
def unpackFile(self, file_path, dest_dir=None):
@ -489,7 +491,7 @@ class DeviceManager:
#TODO: spawn this off in a separate thread/process so we can collect all the logcat information
# Right now this is just clearing the logcat so we can only see what happens after this call.
self.shellCheckOutput(['/system/bin/logcat', '-c'], root=self.logcatNeedsRoot)
self.shellCheckOutput(['/system/bin/logcat', '-c'], root=self._logcatNeedsRoot)
def getLogcat(self, filterSpecs=["dalvikvm:S", "ConnectivityService:S",
"WifiMonitor:S", "WifiStateTracker:S",
@ -501,7 +503,7 @@ class DeviceManager:
"""
cmdline = ["/system/bin/logcat", "-v", format, "-d"] + filterSpecs
lines = self.shellCheckOutput(cmdline,
root=self.logcatNeedsRoot).split('\r')
root=self._logcatNeedsRoot).split('\r')
for regex in filterOutRegexps:
lines = [line for line in lines if not re.search(regex, line)]

View File

@ -6,44 +6,44 @@ import subprocess
from devicemanager import DeviceManager, DMError, _pop_last_line
import re
import os
import shutil
import tempfile
import time
class DeviceManagerADB(DeviceManager):
_haveRootShell = False
_haveSu = False
_useRunAs = False
_useDDCopy = False
_useZip = False
_logcatNeedsRoot = False
_pollingInterval = 0.01
_packageName = None
_tempDir = None
default_timeout = 300
def __init__(self, host=None, port=20701, retrylimit=5, packageName='fennec',
adbPath='adb', deviceSerial=None, deviceRoot=None, **kwargs):
self.host = host
self.port = port
self.retrylimit = retrylimit
self.retries = 0
self._sock = None
self.haveRootShell = False
self.haveSu = False
self.useRunAs = False
self.useDDCopy = False
self.useZip = False
self.logcatNeedsRoot = False
self.packageName = None
self.tempDir = None
self.deviceRoot = deviceRoot
self.default_timeout = 300
self.pollingInterval = 0.01
# the path to adb, or 'adb' to assume that it's on the PATH
self.adbPath = adbPath
self._adbPath = adbPath
# The serial number of the device to use with adb, used in cases
# where multiple devices are being managed by the same adb instance.
self.deviceSerial = deviceSerial
self._deviceSerial = deviceSerial
if packageName == 'fennec':
if os.getenv('USER'):
self.packageName = 'org.mozilla.fennec_' + os.getenv('USER')
self._packageName = 'org.mozilla.fennec_' + os.getenv('USER')
else:
self.packageName = 'org.mozilla.fennec_'
self._packageName = 'org.mozilla.fennec_'
elif packageName:
self.packageName = packageName
self._packageName = packageName
# verify that we can run the adb command. can't continue otherwise
self._verifyADB()
@ -96,7 +96,7 @@ class DeviceManagerADB(DeviceManager):
# always. :(
# If requested to run as root, check that we can actually do that
if root and not self.haveRootShell and not self.haveSu:
if root and not self._haveRootShell and not self._haveSu:
raise DMError("Shell command '%s' requested to run as root but root "
"is not available on this device. Root your device or "
"refactor the test/harness to not require root." %
@ -105,7 +105,7 @@ class DeviceManagerADB(DeviceManager):
# Getting the return code is more complex than you'd think because adb
# doesn't actually return the return code from a process, so we have to
# capture the output to get it
if root and not self.haveRootShell:
if root and not self._haveRootShell:
cmdline = "su -c \"%s\"" % self._escapedCommandLine(cmd)
else:
cmdline = self._escapedCommandLine(cmd)
@ -119,9 +119,9 @@ class DeviceManagerADB(DeviceManager):
cmdline = envstr + "; " + cmdline
# all output should be in stdout
args=[self.adbPath]
if self.deviceSerial:
args.extend(['-s', self.deviceSerial])
args=[self._adbPath]
if self._deviceSerial:
args.extend(['-s', self._deviceSerial])
args.extend(["shell", cmdline])
proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
@ -133,7 +133,7 @@ class DeviceManagerADB(DeviceManager):
start_time = time.time()
ret_code = proc.poll()
while ((time.time() - start_time) <= timeout) and ret_code == None:
time.sleep(self.pollingInterval)
time.sleep(self._pollingInterval)
ret_code = proc.poll()
if ret_code == None:
proc.kill()
@ -170,10 +170,10 @@ class DeviceManagerADB(DeviceManager):
raise DMError("Attempted to push a file (%s) to a directory (%s)!" %
(localname, destname))
if self.useRunAs:
if self._useRunAs:
remoteTmpFile = self.getTempDir() + "/" + os.path.basename(localname)
self._checkCmd(["push", os.path.realpath(localname), remoteTmpFile])
if self.useDDCopy:
if self._useDDCopy:
self.shellCheckOutput(["dd", "if=" + remoteTmpFile, "of=" + destname])
else:
self.shellCheckOutput(["cp", remoteTmpFile, destname])
@ -195,11 +195,11 @@ class DeviceManagerADB(DeviceManager):
"""
# adb "push" accepts a directory as an argument, but if the directory
# contains symbolic links, the links are pushed, rather than the linked
# files; we either zip/unzip or push file-by-file to get around this
# limitation
# files; we either zip/unzip or re-copy the directory into a temporary
# one to get around this limitation
if not self.dirExists(remoteDir):
self.mkDirs(remoteDir+"/x")
if self.useZip:
if self._useZip:
try:
localZip = tempfile.mktemp() + ".zip"
remoteZip = remoteDir + "/adbdmtmp.zip"
@ -212,24 +212,15 @@ class DeviceManagerADB(DeviceManager):
raise Exception("unzip failed, or permissions error")
except:
print "zip/unzip failure: falling back to normal push"
self.useZip = False
self._useZip = False
self.pushDir(localDir, remoteDir)
else:
for root, dirs, files in os.walk(localDir, followlinks=True):
relRoot = os.path.relpath(root, localDir)
for f in files:
localFile = os.path.join(root, f)
remoteFile = remoteDir + "/"
if relRoot != ".":
remoteFile = remoteFile + relRoot + "/"
remoteFile = remoteFile + f
self.pushFile(localFile, remoteFile)
for d in dirs:
targetDir = remoteDir + "/"
if relRoot != ".":
targetDir = targetDir + relRoot + "/"
targetDir = targetDir + d
self.mkDir(targetDir)
tmpDir = tempfile.mkdtemp()
# copytree's target dir must not already exist, so create a subdir
tmpDirTarget = os.path.join(tmpDir, "tmp")
shutil.copytree(localDir, tmpDirTarget)
self._checkCmd(["push", tmpDirTarget, remoteDir])
shutil.rmtree(tmpDir)
def dirExists(self, remotePath):
"""
@ -418,7 +409,7 @@ class DeviceManagerADB(DeviceManager):
if (len(errl) == 1):
if (((errl[0].find("Permission denied") != -1)
or (errl[0].find("does not exist") != -1))
and self.useRunAs):
and self._useRunAs):
# If we lack permissions to read but have run-as, then we should try
# to copy the file to a world-readable location first before attempting
# to pull it again.
@ -544,11 +535,11 @@ class DeviceManagerADB(DeviceManager):
"""
# Cache result to speed up operations depending
# on the temporary directory.
if not self.tempDir:
self.tempDir = self.getDeviceRoot() + "/tmp"
self.mkDir(self.tempDir)
if not self._tempDir:
self._tempDir = self.getDeviceRoot() + "/tmp"
self.mkDir(self._tempDir)
return self.tempDir
return self._tempDir
def getAppRoot(self, packageName):
"""
@ -561,10 +552,10 @@ class DeviceManagerADB(DeviceManager):
return None
if (packageName and self.dirExists('/data/data/' + packageName)):
self.packageName = packageName
self._packageName = packageName
return '/data/data/' + packageName
elif (self.packageName and self.dirExists('/data/data/' + self.packageName)):
return '/data/data/' + self.packageName
elif (self._packageName and self.dirExists('/data/data/' + self._packageName)):
return '/data/data/' + self._packageName
# Failure (either not installed or not a recognized platform)
raise DMError("Failed to get application root for: %s" % packageName)
@ -674,27 +665,28 @@ class DeviceManagerADB(DeviceManager):
returns: returncode from subprocess.Popen
"""
finalArgs = [self.adbPath]
if self.deviceSerial:
finalArgs.extend(['-s', self.deviceSerial])
finalArgs = [self._adbPath]
if self._deviceSerial:
finalArgs.extend(['-s', self._deviceSerial])
# use run-as to execute commands as the package we're testing if
# possible
if not self.haveRootShell and self.useRunAs and args[0] == "shell" and args[1] != "run-as":
if not self._haveRootShell and self._useRunAs and \
args[0] == "shell" and args[1] not in [ "run-as", "am" ]:
args.insert(1, "run-as")
args.insert(2, self.packageName)
args.insert(2, self._packageName)
finalArgs.extend(args)
return subprocess.Popen(finalArgs, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
def _runCmdAs(self, args):
"""
Runs a command using adb
If self.useRunAs is True, the command is run-as user specified in self.packageName
If self._useRunAs is True, the command is run-as user specified in self._packageName
returns: returncode from subprocess.Popen
"""
if self.useRunAs:
if self._useRunAs:
args.insert(1, "run-as")
args.insert(2, self.packageName)
args.insert(2, self._packageName)
return self._runCmd(args)
# timeout is specified in seconds, and if no timeout is given,
@ -708,12 +700,13 @@ class DeviceManagerADB(DeviceManager):
"""
# use run-as to execute commands as the package we're testing if
# possible
finalArgs = [self.adbPath]
if self.deviceSerial:
finalArgs.extend(['-s', self.deviceSerial])
if not self.haveRootShell and self.useRunAs and args[0] == "shell" and args[1] != "run-as":
finalArgs = [self._adbPath]
if self._deviceSerial:
finalArgs.extend(['-s', self._deviceSerial])
if not self._haveRootShell and self._useRunAs and \
args[0] == "shell" and args[1] not in [ "run-as", "am" ]:
args.insert(1, "run-as")
args.insert(2, self.packageName)
args.insert(2, self._packageName)
finalArgs.extend(args)
if not timeout:
# We are asserting that all commands will complete in this time unless otherwise specified
@ -724,7 +717,7 @@ class DeviceManagerADB(DeviceManager):
start_time = time.time()
ret_code = proc.poll()
while ((time.time() - start_time) <= timeout) and ret_code == None:
time.sleep(self.pollingInterval)
time.sleep(self._pollingInterval)
ret_code = proc.poll()
if ret_code == None:
proc.kill()
@ -734,14 +727,14 @@ class DeviceManagerADB(DeviceManager):
def _checkCmdAs(self, args, timeout=None):
"""
Runs a command using adb and waits for command to finish
If self.useRunAs is True, the command is run-as user specified in self.packageName
If self._useRunAs is True, the command is run-as user specified in self._packageName
If timeout is specified, the process is killed after <timeout> seconds
returns: returncode from subprocess.Popen
"""
if (self.useRunAs):
if (self._useRunAs):
args.insert(1, "run-as")
args.insert(2, self.packageName)
args.insert(2, self._packageName)
return self._checkCmd(args, timeout)
def chmodDir(self, remoteDir, mask="777"):
@ -767,9 +760,9 @@ class DeviceManagerADB(DeviceManager):
"""
Check to see if adb itself can be executed.
"""
if self.adbPath != 'adb':
if not os.access(self.adbPath, os.X_OK):
raise DMError("invalid adb path, or adb not executable: %s", self.adbPath)
if self._adbPath != 'adb':
if not os.access(self._adbPath, os.X_OK):
raise DMError("invalid adb path, or adb not executable: %s", self._adbPath)
try:
self._checkCmd(["version"])
@ -780,20 +773,20 @@ class DeviceManagerADB(DeviceManager):
def _verifyDevice(self):
# If there is a device serial number, see if adb is connected to it
if self.deviceSerial:
if self._deviceSerial:
deviceStatus = None
proc = subprocess.Popen([self.adbPath, "devices"],
proc = subprocess.Popen([self._adbPath, "devices"],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
for line in proc.stdout:
m = re.match('(.+)?\s+(.+)$', line)
if m:
if self.deviceSerial == m.group(1):
if self._deviceSerial == m.group(1):
deviceStatus = m.group(2)
if deviceStatus == None:
raise DMError("device not found: %s" % self.deviceSerial)
raise DMError("device not found: %s" % self._deviceSerial)
elif deviceStatus != "device":
raise DMError("bad status for device %s: %s" % (self.deviceSerial, deviceStatus))
raise DMError("bad status for device %s: %s" % (self._deviceSerial, deviceStatus))
# Check to see if we can connect to device and run a simple command
try:
@ -814,7 +807,7 @@ class DeviceManagerADB(DeviceManager):
data = self._runCmd(["shell", "dd", "-"]).stdout.read()
if (re.search('unknown operand', data)):
print "'cp' not found, but 'dd' was found as a replacement"
self.useDDCopy = True
self._useDDCopy = True
return True
print "unable to execute 'cp' on device; consider installing busybox from Android Market"
return False
@ -827,28 +820,28 @@ class DeviceManagerADB(DeviceManager):
# echoing conditions encountered by Fennec at run time.
# Check to see if run-as can be used here, by verifying a
# file copy via run-as.
self.useRunAs = False
self._useRunAs = False
devroot = self.getDeviceRoot()
if (self.packageName and self._isCpAvailable() and devroot):
if (self._packageName and self._isCpAvailable() and devroot):
tmpDir = self.getTempDir()
# The problem here is that run-as doesn't cause a non-zero exit code
# when failing because of a non-existent or non-debuggable package :(
runAsOut = self._runCmd(["shell", "run-as", self.packageName, "mkdir", devroot + "/sanity"]).communicate()[0]
runAsOut = self._runCmd(["shell", "run-as", self._packageName, "mkdir", devroot + "/sanity"]).communicate()[0]
if runAsOut.startswith("run-as:") and ("not debuggable" in runAsOut or "is unknown" in runAsOut):
raise DMError("run-as failed sanity check")
tmpfile = tempfile.NamedTemporaryFile()
self._checkCmd(["push", tmpfile.name, tmpDir + "/tmpfile"])
if self.useDDCopy:
self._checkCmd(["shell", "run-as", self.packageName, "dd", "if=" + tmpDir + "/tmpfile", "of=" + devroot + "/sanity/tmpfile"])
if self._useDDCopy:
self._checkCmd(["shell", "run-as", self._packageName, "dd", "if=" + tmpDir + "/tmpfile", "of=" + devroot + "/sanity/tmpfile"])
else:
self._checkCmd(["shell", "run-as", self.packageName, "cp", tmpDir + "/tmpfile", devroot + "/sanity"])
self._checkCmd(["shell", "run-as", self._packageName, "cp", tmpDir + "/tmpfile", devroot + "/sanity"])
if (self.fileExists(devroot + "/sanity/tmpfile")):
print "will execute commands via run-as " + self.packageName
self.useRunAs = True
print "will execute commands via run-as " + self._packageName
self._useRunAs = True
self._checkCmd(["shell", "rm", devroot + "/tmp/tmpfile"])
self._checkCmd(["shell", "run-as", self.packageName, "rm", "-r", devroot + "/sanity"])
self._checkCmd(["shell", "run-as", self._packageName, "rm", "-r", devroot + "/sanity"])
def _checkForRoot(self):
# Check whether we _are_ root by default (some development boards work
@ -857,7 +850,7 @@ class DeviceManagerADB(DeviceManager):
proc = self._runCmd(["shell", "id"])
data = proc.stdout.read()
if data.find('uid=0(root)') >= 0:
self.haveRootShell = True
self._haveRootShell = True
# if this returns true, we don't care about su
return
@ -876,7 +869,7 @@ class DeviceManagerADB(DeviceManager):
data = proc.stdout.read()
if data.find('uid=0(root)') >= 0:
self.haveSu = True
self._haveSu = True
def _isUnzipAvailable(self):
data = self._runCmdAs(["shell", "unzip"]).stdout.read()
@ -896,9 +889,9 @@ class DeviceManagerADB(DeviceManager):
# If "zip" can be run locally, and "unzip" can be run remotely, then pushDir
# can use these to push just one file per directory -- a significant
# optimization for large directories.
self.useZip = False
self._useZip = False
if (self._isUnzipAvailable() and self._isLocalZipAvailable()):
print "will use zip to push directories"
self.useZip = True
self._useZip = True
else:
raise DMError("zip not available")

View File

@ -18,11 +18,11 @@ from distutils.version import StrictVersion
class DeviceManagerSUT(DeviceManager):
debug = 2
base_prompt = '$>'
base_prompt_re = '\$\>'
prompt_sep = '\x00'
prompt_regex = '.*(' + base_prompt_re + prompt_sep + ')'
agentErrorRE = re.compile('^##AGENT-WARNING##\ ?(.*)')
_base_prompt = '$>'
_base_prompt_re = '\$\>'
_prompt_sep = '\x00'
_prompt_regex = '.*(' + _base_prompt_re + _prompt_sep + ')'
_agentErrorRE = re.compile('^##AGENT-WARNING##\ ?(.*)')
default_timeout = 300
def __init__(self, host, port = 20701, retrylimit = 5, deviceRoot = None, **kwargs):
@ -61,7 +61,7 @@ class DeviceManagerSUT(DeviceManager):
"""
take a data blob and strip instances of the prompt '$>\x00'
"""
promptre = re.compile(self.prompt_regex + '.*')
promptre = re.compile(self._prompt_regex + '.*')
retVal = []
lines = data.split('\n')
for line in lines:
@ -69,10 +69,10 @@ class DeviceManagerSUT(DeviceManager):
try:
while (promptre.match(line)):
foundPrompt = True
pieces = line.split(self.prompt_sep)
pieces = line.split(self._prompt_sep)
index = pieces.index('$>')
pieces.pop(index)
line = self.prompt_sep.join(pieces)
line = self._prompt_sep.join(pieces)
except(ValueError):
pass
@ -141,7 +141,7 @@ class DeviceManagerSUT(DeviceManager):
return outputfile.read()
def _doCmds(self, cmdlist, outputfile, timeout):
promptre = re.compile(self.prompt_regex + '$')
promptre = re.compile(self._prompt_regex + '$')
shouldCloseSocket = False
if not timeout:
@ -242,7 +242,7 @@ class DeviceManagerSUT(DeviceManager):
# If something goes wrong in the agent it will send back a string that
# starts with '##AGENT-WARNING##'
if not commandFailed:
errorMatch = self.agentErrorRE.match(data)
errorMatch = self._agentErrorRE.match(data)
if errorMatch:
# We still need to consume the prompt, so raise an error after
# draining the rest of the buffer.
@ -450,11 +450,20 @@ class DeviceManagerSUT(DeviceManager):
for line in data.splitlines():
if line:
pidproc = line.strip().split()
if (len(pidproc) == 2):
processTuples += [[pidproc[0], pidproc[1]]]
elif (len(pidproc) == 3):
#android returns <userID> <procID> <procName>
processTuples += [[int(pidproc[1]), pidproc[2], int(pidproc[0])]]
try:
if (len(pidproc) == 2):
processTuples += [[pidproc[0], pidproc[1]]]
elif (len(pidproc) == 3):
# android returns <userID> <procID> <procName>
processTuples += [[int(pidproc[1]), pidproc[2], int(pidproc[0])]]
else:
# unexpected format
raise ValueError
except ValueError:
print "ERROR: Unable to parse process list (bug 805969)"
print "Line: %s\nFull output of process list:\n%s" % (line, data)
raise DMError("Invalid process line: %s" % line)
return processTuples
def fireProcess(self, appname, failIfRunning=False):
@ -596,7 +605,7 @@ class DeviceManagerSUT(DeviceManager):
buf += data
return buf
prompt = self.base_prompt + self.prompt_sep
prompt = self._base_prompt + self._prompt_sep
buf = ''
# expected return value:

View File

@ -5,7 +5,7 @@
import os
from setuptools import setup
PACKAGE_VERSION = '0.14'
PACKAGE_VERSION = '0.15'
# take description from README
here = os.path.dirname(os.path.abspath(__file__))

View File

@ -8,6 +8,10 @@ class PsTest(unittest.TestCase):
"10029 549 com.android.launcher\n"
"10066 1198 com.twitter.android")]
bad_pscommands = [('ps',
"abcdef 549 com.android.launcher\n"
"10066 1198 com.twitter.android")]
def test_processList(self):
a = MockAgent(self,
commands=self.pscommands)
@ -19,6 +23,20 @@ class PsTest(unittest.TestCase):
a.wait()
def test_badProcessList(self):
a = MockAgent(self,
commands=self.bad_pscommands)
d = mozdevice.DroidSUT("127.0.0.1", port=a.port)
exceptionTriggered = False
try:
d.getProcessList()
except mozdevice.DMError:
exceptionTriggered = True
self.assertTrue(exceptionTriggered)
a.wait()
def test_processExist(self):
for i in [('com.android.launcher', 549),
('com.fennec.android', None)]: