23
25
packageName = 'org.mozilla.fennec_'
24
26
self.Init(packageName)
30
self.disconnectRemoteADB()
26
32
def Init(self, packageName):
27
33
# Initialization code that may fail: Catch exceptions here to allow
28
34
# successful initialization even if, for example, adb is not installed.
38
self.connectRemoteADB()
31
39
self.verifyRunAs(packageName)
33
41
self.useRunAs = False
34
self.packageName = None
42
self.packageName = packageName
38
46
self.useZip = False
40
49
# a test to see if we have root privs
41
50
files = self.listFiles("/data/data")
42
51
if (len(files) == 1):
43
52
if (files[0].find("Permission denied") != -1):
44
53
print "NOT running as root"
45
54
raise Exception("not running as root")
48
61
self.checkCmd(["root"])
62
# The root command does not fail even if ADB cannot get
63
# root rights (e.g. due to production builds), so we have
64
# to check again ourselves that we have root now.
50
print "restarting as root failed"
68
print "restarting as root failed, but run-as available"
70
print "restarting as root failed"
72
# external function: executes shell command on device
74
# success: <return code>
76
def shell(self, cmd, outputfile, env=None, cwd=None):
77
# need to quote special characters here
78
for (index, arg) in enumerate(cmd):
79
if arg.find(" ") or arg.find("(") or arg.find(")") or arg.find("\""):
80
cmd[index] = '\'%s\'' % arg
82
# This is more complex than you'd think because adb doesn't actually
83
# return the return code from a process, so we have to capture the output
85
# FIXME: this function buffers all output of the command into memory,
87
cmdline = " ".join(cmd) + "; echo $?"
89
# prepend cwd and env to command if necessary
91
cmdline = "cd %s; %s" % (cwd, cmdline)
93
envstr = '; '.join(map(lambda x: 'export %s=%s' % (x[0], x[1]), env.iteritems()))
94
cmdline = envstr + "; " + cmdline
96
# all output should be in stdout
97
proc = subprocess.Popen(["adb", "shell", cmdline],
98
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
99
(stdout, stderr) = proc.communicate()
100
outputfile.write(stdout.rstrip('\n'))
102
lastline = _pop_last_line(outputfile)
104
m = re.search('([0-9]+)', lastline)
106
return_code = m.group(1)
107
outputfile.seek(-2, 2)
108
outputfile.truncate() # truncate off the return code
113
def connectRemoteADB(self):
114
self.checkCmd(["connect", self.host + ":" + str(self.port)])
116
def disconnectRemoteADB(self):
117
self.checkCmd(["disconnect", self.host + ":" + str(self.port)])
52
119
# external function
58
125
if (os.name == "nt"):
59
126
destname = destname.replace('\\', '/')
60
127
if (self.useRunAs):
61
remoteTmpFile = self.tmpDir + "/" + os.path.basename(localname)
128
remoteTmpFile = self.getTempDir() + "/" + os.path.basename(localname)
62
129
self.checkCmd(["push", os.path.realpath(localname), remoteTmpFile])
63
self.checkCmdAs(["shell", "cp", remoteTmpFile, destname])
131
self.checkCmdAs(["shell", "dd", "if=" + remoteTmpFile, "of=" + destname])
133
self.checkCmdAs(["shell", "cp", remoteTmpFile, destname])
64
134
self.checkCmd(["shell", "rm", remoteTmpFile])
66
136
self.checkCmd(["push", os.path.realpath(localname), destname])
112
182
# files; we either zip/unzip or push file-by-file to get around this
185
if (not self.dirExists(remoteDir)):
186
self.mkDirs(remoteDir+"/x")
115
187
if (self.useZip):
116
localZip = tempfile.mktemp()+".zip"
117
remoteZip = remoteDir + "/adbdmtmp.zip"
118
subprocess.check_output(["zip", "-r", localZip, '.'], cwd=localDir)
119
self.pushFile(localZip, remoteZip)
121
self.checkCmdAs(["shell", "unzip", "-o", remoteZip, "-d", remoteDir])
122
self.checkCmdAs(["shell", "rm", remoteZip])
189
localZip = tempfile.mktemp()+".zip"
190
remoteZip = remoteDir + "/adbdmtmp.zip"
191
subprocess.check_output(["zip", "-r", localZip, '.'], cwd=localDir)
192
self.pushFile(localZip, remoteZip)
194
data = self.runCmdAs(["shell", "unzip", "-o", remoteZip, "-d", remoteDir]).stdout.read()
195
self.checkCmdAs(["shell", "rm", remoteZip])
196
if (re.search("unzip: exiting", data) or re.search("Operation not permitted", data)):
197
raise Exception("unzip failed, or permissions error")
199
print "zip/unzip failure: falling back to normal push"
201
self.pushDir(localDir, remoteDir)
124
if (not self.dirExists(remoteDir)):
125
self.mkDirs(remoteDir+"/x")
126
203
for root, dirs, files in os.walk(localDir, followlinks='true'):
127
204
relRoot = os.path.relpath(root, localDir)
128
205
for file in files:
250
329
return self.launchProcess(parts, failIfRunning)
252
331
# external function
332
# DEPRECATED: Use shell() or launchApplication() for new code
254
334
# success: output filename
256
336
def launchProcess(self, cmd, outputFile = "process.txt", cwd = '', env = '', failIfRunning=False):
257
acmd = ["shell", "am","start"]
338
self.checkCmd(["shell"] + cmd)
341
acmd = ["shell", "am", "start", "-W"]
258
342
cmd = ' '.join(cmd).strip()
259
343
i = cmd.find(" ")
260
344
# SUT identifies the URL by looking for :\\ -- another strategy to consider
269
353
args = cmd[i:].strip()
270
354
acmd.append("-n")
271
355
acmd.append(cmd[0:i] + "/.App")
274
358
acmd.append("args")
275
359
acmd.append(args)
360
if env != '' and env != None:
362
# env is expected to be a dict of environment variables
363
for envkey, envval in env.iteritems():
365
acmd.append("env" + str(envCnt))
366
acmd.append(envkey + "=" + envval);
277
369
acmd.append("-d")
278
370
acmd.append(''.join(['\'',uri, '\'']));
280
372
self.checkCmd(acmd)
283
375
# external function
318
411
# TODO: add debug flags and allow for printing stdout
319
412
# self.runCmd(["pull", remoteFile, localFile])
321
self.runCmd(["pull", remoteFile, localFile]).stdout.read()
415
# First attempt to pull file regularly
416
outerr = self.runCmd(["pull", remoteFile, localFile]).communicate()
418
# Now check stderr for errors
419
errl = outerr[1].splitlines()
421
if (((errl[0].find("Permission denied") != -1)
422
or (errl[0].find("does not exist") != -1))
424
# If we lack permissions to read but have run-as, then we should try
425
# to copy the file to a world-readable location first before attempting
427
remoteTmpFile = self.getTempDir() + "/" + os.path.basename(remoteFile)
428
self.checkCmdAs(["shell", "dd", "if=" + remoteFile, "of=" + remoteTmpFile])
429
self.checkCmdAs(["shell", "chmod", "777", remoteTmpFile])
430
self.runCmd(["pull", remoteTmpFile, localFile]).stdout.read()
431
# Clean up temporary file
432
self.checkCmdAs(["shell", "rm", remoteTmpFile])
322
434
f = open(localFile)
411
523
self.mkDir(testRoot)
526
# Gets the temporary directory we are using on this device
527
# base on our device root, ensuring also that it exists.
531
# success: path for temporary directory
533
def getTempDir(self):
534
# Cache result to speed up operations depending
535
# on the temporary directory.
536
if self.tempDir == None:
537
self.tempDir = self.getDeviceRoot() + "/tmp"
538
if (not self.dirExists(self.tempDir)):
539
return self.mkDir(self.tempDir)
414
543
# Either we will have /tests/fennec or /tests/firefox but we will never have
415
544
# both. Return the one that exists
416
545
# TODO: ensure we can support org.mozilla.firefox
419
548
# success: path for app root
421
def getAppRoot(self):
550
def getAppRoot(self, packageName):
422
551
devroot = self.getDeviceRoot()
423
552
if (devroot == None):
426
if (self.dirExists(devroot + '/fennec')):
427
return devroot + '/fennec'
428
elif (self.dirExists(devroot + '/firefox')):
429
return devroot + '/firefox'
555
if (packageName and self.dirExists('/data/data/' + packageName)):
556
self.packageName = packageName
557
return '/data/data/' + packageName
430
558
elif (self.packageName and self.dirExists('/data/data/' + self.packageName)):
431
559
return '/data/data/' + self.packageName
535
663
def runCmd(self, args):
664
# If we are not root but have run-as, and we're trying to execute
665
# a shell command then using run-as is the best we can do
666
if (not self.haveRoot and self.useRunAs and args[0] == "shell" and args[1] != "run-as"):
667
args.insert(1, "run-as")
668
args.insert(2, self.packageName)
536
669
args.insert(0, "adb")
537
return subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
670
return subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
539
672
def runCmdAs(self, args):
540
673
if self.useRunAs:
595
738
self.useRunAs = False
596
739
devroot = self.getDeviceRoot()
597
740
if (packageName and self.isCpAvailable() and devroot):
598
self.tmpDir = devroot + "/tmp"
599
if (not self.dirExists(self.tmpDir)):
600
self.mkDir(self.tmpDir)
601
self.checkCmd(["shell", "run-as", packageName, "mkdir", devroot + "/sanity"])
602
self.checkCmd(["push", os.path.abspath(sys.argv[0]), self.tmpDir + "/tmpfile"])
603
self.checkCmd(["shell", "run-as", packageName, "cp", self.tmpDir + "/tmpfile", devroot + "/sanity"])
741
tmpDir = self.getTempDir()
743
# The problem here is that run-as doesn't cause a non-zero exit code
744
# when failing because of a non-existent or non-debuggable package :(
745
runAsOut = self.runCmd(["shell", "run-as", packageName, "mkdir", devroot + "/sanity"]).communicate()[0]
746
if runAsOut.startswith("run-as:") and ("not debuggable" in runAsOut[0] or
747
"is unknown" in runAsOut[0]):
748
raise DMError("run-as failed sanity check")
750
self.checkCmd(["push", os.path.abspath(sys.argv[0]), tmpDir + "/tmpfile"])
752
self.checkCmd(["shell", "run-as", packageName, "dd", "if=" + tmpDir + "/tmpfile", "of=" + devroot + "/sanity/tmpfile"])
754
self.checkCmd(["shell", "run-as", packageName, "cp", tmpDir + "/tmpfile", devroot + "/sanity"])
604
755
if (self.fileExists(devroot + "/sanity/tmpfile")):
605
756
print "will execute commands via run-as " + packageName
606
757
self.packageName = packageName
609
760
self.checkCmd(["shell", "run-as", packageName, "rm", "-r", devroot + "/sanity"])
611
762
def isUnzipAvailable(self):
612
data = self.runCmd(["shell", "unzip"]).stdout.read()
763
data = self.runCmdAs(["shell", "unzip"]).stdout.read()
613
764
if (re.search('Usage', data)):