Bug 669549 - Some DeviceManagerADB functions do not work; r=jmaher, a=test-only
authorGeoff Brown <gbrown@mozilla.com>
Sun, 17 Jul 2011 12:04:40 -0400
changeset 73706 1627d7efe03538f5cfc848f2cd62fad614d649af
parent 73705 de00c2c0f208723a6d8ada6c3997159e93b974a9
child 73707 ac3b2bd0a5818ce179506e3f510cf668e754dbe5
push id235
push userbzbarsky@mozilla.com
push dateTue, 27 Sep 2011 17:13:04 +0000
treeherdermozilla-beta@2d1e082d176a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjmaher, test-only
bugs669549
milestone8.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 669549 - Some DeviceManagerADB functions do not work; r=jmaher, a=test-only
build/mobile/devicemanagerADB.py
--- a/build/mobile/devicemanagerADB.py
+++ b/build/mobile/devicemanagerADB.py
@@ -1,87 +1,134 @@
 import subprocess
 from devicemanager import DeviceManager, DMError
 import re
+import os
 
 class DeviceManagerADB(DeviceManager):
 
-  def __init__(self, host = None, port = 20701, retrylimit = 5):
+  def __init__(self, host = None, port = 20701, retrylimit = 5, packageName = "org.mozilla.fennec_unofficial"):
     self.host = host
     self.port = port
     self.retrylimit = retrylimit
     self.retries = 0
     self._sock = None
-    self.getDeviceRoot()
+    self.Init(packageName)
+
+  def Init(self, packageName):
+    # Initialization code that may fail: Catch exceptions here to allow
+    # successful initialization even if, for example, adb is not installed.
+    try:
+      root = self.getDeviceRoot()
+      self.verifyPackage(packageName)
+      self.tmpDir = root + "/tmp"
+      if (not self.dirExists(self.tmpDir)):
+        self.mkDir(self.tmpDir)
+    except:
+      self.packageName = None
+      self.tmpDir = None
     try:
       # a test to see if we have root privs
       self.checkCmd(["shell", "ls", "/sbin"])
     except:
       try:
         self.checkCmd(["root"])
       except:
         print "restarting as root failed"
 
   # external function
   # returns:
   #  success: True
   #  failure: False
   def pushFile(self, localname, destname):
     try:
-      self.checkCmd(["push", localname, destname])
+      if (os.name == "nt"):
+        destname = destname.replace('\\', '/')
+      if (self.packageName):
+        remoteTmpFile = self.tmpDir + "/" + os.path.basename(localname)
+        self.checkCmd(["push", os.path.realpath(localname), remoteTmpFile])
+        self.checkCmdAs(["shell", "cp", remoteTmpFile, destname])
+        self.checkCmd(["shell", "rm", remoteTmpFile])
+      else:
+        self.checkCmd(["push", os.path.realpath(localname), destname])
+      if (self.isDir(destname)):
+        destname = destname + "/" + os.path.basename(localname)
       self.chmodDir(destname)
       return True
     except:
       return False
 
   # external function
   # returns:
   #  success: directory name
   #  failure: None
   def mkDir(self, name):
     try:
-      self.checkCmd(["shell", "mkdir", name])
+      self.checkCmdAs(["shell", "mkdir", name])
       self.chmodDir(name)
       return name
     except:
       return None
 
   # make directory structure on the device
   # external function
   # returns:
   #  success: directory structure that we created
   #  failure: None
   def mkDirs(self, filename):
-    self.checkCmd(["shell", "mkdir", "-p ", name])
-    return filename
+    parts = filename.split('/')
+    name = ""
+    for part in parts:
+      if (part == parts[-1]): break
+      if (part != ""):
+        name += '/' + part
+        if (not self.dirExists(name)):
+          if (self.mkDir(name) == None):
+            print "failed making directory: " + str(name)
+            return None
+    return name
 
   # push localDir from host to remoteDir on the device
   # external function
   # returns:
   #  success: remoteDir
   #  failure: None
   def pushDir(self, localDir, remoteDir):
+    # 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 push file-by-file to get around this limitation
     try:
-      self.checkCmd(["push", localDir, remoteDir])
-      self.chmodDir(remoteDir)
+      for root, dirs, files in os.walk(localDir):
+        relRoot = os.path.relpath(root, localDir)
+        for file in files:
+          localFile = os.path.join(root, file)
+          remoteFile = remoteDir + "/"
+          if (relRoot!="."):
+            remoteFile = remoteFile + relRoot + "/"
+          remoteFile = remoteFile + file
+          self.pushFile(localFile, remoteFile)
+        for dir in dirs:
+          targetDir = remoteDir + "/"
+          if (relRoot!="."):
+            targetDir = targetDir + relRoot + "/"
+          targetDir = targetDir + dir
+          if (not self.dirExists(targetDir)):
+            self.mkDir(targetDir)
+      self.checkCmdAs(["shell", "chmod", "777", remoteDir])
       return True
     except:
       print "pushing " + localDir + " to " + remoteDir + " failed"
       return False
 
   # external function
   # returns:
   #  success: True
   #  failure: False
   def dirExists(self, dirname):
-    try:
-      self.checkCmd(["shell", "ls", dirname])
-      return True
-    except:
-      return False
+    return self.isDir(dirname)
 
   # Because we always have / style paths we make this a lot easier with some
   # assumptions
   # external function
   # returns:
   #  success: True
   #  failure: False
   def fileExists(self, filepath):
@@ -107,38 +154,38 @@ class DeviceManagerADB(DeviceManager):
   def removeDir(self, remoteDir):
       out = ""
       if (self.isDir(remoteDir)):
           files = self.listFiles(remoteDir.strip())
           for f in files:
               if (self.isDir(remoteDir.strip() + "/" + f.strip())):
                   out += self.removeDir(remoteDir.strip() + "/" + f.strip())
               else:
-                  out += self.removeFile(remoteDir.strip())
-          out += self.removeSingleDir(remoteDir)
+                  out += self.removeFile(remoteDir.strip() + "/" + f.strip())
+          out += self.removeSingleDir(remoteDir.strip())
       else:
           out += self.removeFile(remoteDir.strip())
       return out
 
   def isDir(self, remotePath):
-      p = self.runCmd(["shell", "ls", remotePath])
+      p = self.runCmd(["shell", "ls", "-a", remotePath])
       data = p.stdout.readlines()
       if (len(data) == 0):
           return True
       if (len(data) == 1):
-          if (data[0] == remotePath):
+          if (data[0].rstrip() == remotePath):
               return False
           if (data[0].find("No such file or directory") != -1):
               return False
           if (data[0].find("Not a directory") != -1):
               return False
       return True
 
   def listFiles(self, rootdir):
-      p = self.runCmd(["shell", "ls", rootdir])
+      p = self.runCmd(["shell", "ls", "-a", rootdir])
       data = p.stdout.readlines()
       if (len(data) == 1):
           if (data[0] == rootdir):
               return []
           if (data[0].find("No such file or directory") != -1):
               return []
           if (data[0].find("Not a directory") != -1):
               return []
@@ -160,25 +207,29 @@ class DeviceManagerADB(DeviceManager):
       proc =  p.stdout.readline()
     return ret
 
   # external function
   # returns:
   #  success: pid
   #  failure: None
   def fireProcess(self, appname, failIfRunning=False):
-    return self.runCmd(["shell", appname]).pid
+    #strip out env vars
+    parts = appname.split('"');
+    if (len(parts) > 2):
+      parts = parts[2:]
+    return self.launchProcess(parts, failIfRunning)
 
   # external function
   # returns:
   #  success: output filename
   #  failure: None
   def launchProcess(self, cmd, outputFile = "process.txt", cwd = '', env = '', failIfRunning=False):
     acmd = ["shell", "am","start"]
-    cmd = ' '.join(cmd)
+    cmd = ' '.join(cmd).strip()
     i = cmd.find(" ")
     acmd.append("-n")
     acmd.append(cmd[0:i] + "/.App")
     acmd.append("--es")
     acmd.append("args")
     acmd.append(cmd[i:])
     print acmd
     self.checkCmd(acmd)
@@ -294,19 +345,28 @@ class DeviceManagerADB(DeviceManager):
   #       /reftest
   #       /mochitest
   #
   # external function
   # returns:
   #  success: path for device root
   #  failure: None
   def getDeviceRoot(self):
-    if (not self.dirExists("/data/local/tests")):
-      self.mkDir("/data/local/tests")
-    return "/data/local/tests"
+    # /mnt/sdcard/tests is preferred to /data/local/tests, but this can be
+    # over-ridden by creating /data/local/tests
+    testRoot = "/data/local/tests"
+    if (self.dirExists(testRoot)):
+      return testRoot
+    root = "/mnt/sdcard"
+    if (not self.dirExists(root)):
+      root = "/data/local"
+    testRoot = root + "/tests"
+    if (not self.dirExists(testRoot)):
+      self.mkDir(testRoot)
+    return testRoot
 
   # Either we will have /tests/fennec or /tests/firefox but we will never have
   # both.  Return the one that exists
   # TODO: ensure we can support org.mozilla.firefox
   # external function
   # returns:
   #  success: path for app root
   #  failure: None
@@ -314,20 +374,26 @@ class DeviceManagerADB(DeviceManager):
     devroot = self.getDeviceRoot()
     if (devroot == None):
       return None
 
     if (self.dirExists(devroot + '/fennec')):
       return devroot + '/fennec'
     elif (self.dirExists(devroot + '/firefox')):
       return devroot + '/firefox'
-    elif (self.dirExsts('/data/data/org.mozilla.fennec')):
-      return 'org.mozilla.fennec'
+    elif (self.dirExists('/data/data/org.mozilla.fennec')):
+      return '/data/data/org.mozilla.fennec'
     elif (self.dirExists('/data/data/org.mozilla.firefox')):
-      return 'org.mozilla.firefox'
+      return '/data/data/org.mozilla.firefox'
+    elif (self.dirExists('/data/data/org.mozilla.fennec_unofficial')):
+      return '/data/data/org.mozilla.fennec_unofficial'
+    elif (self.dirExists('/data/data/org.mozilla.fennec_aurora')):
+      return '/data/data/org.mozilla.fennec_aurora'
+    elif (self.dirExists('/data/data/org.mozilla.firefox_beta')):
+      return '/data/data/org.mozilla.firefox_beta'
 
     # Failure (either not installed or not a recognized platform)
     return None
 
   # Gets the directory location on the device for a specific test type
   # Type is one of: xpcshell|reftest|mochitest
   # external function
   # returns:
@@ -418,23 +484,39 @@ class DeviceManagerADB(DeviceManager):
   def runCmd(self, args):
     args.insert(0, "adb")
     return subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
 
   def checkCmd(self, args):
     args.insert(0, "adb")
     return subprocess.check_call(args)
 
+  def checkCmdAs(self, args):
+    if (self.packageName):
+      args.insert(1, "run-as")
+      args.insert(2, self.packageName)
+    return self.checkCmd(args)
+
   def chmodDir(self, remoteDir):
-    print "called chmodDir"
     if (self.isDir(remoteDir)):
       files = self.listFiles(remoteDir.strip())
       for f in files:
         if (self.isDir(remoteDir.strip() + "/" + f.strip())):
           self.chmodDir(remoteDir.strip() + "/" + f.strip())
         else:
-          self.checkCmd(["shell", "chmod", "777", remoteDir.strip()])
+          self.checkCmdAs(["shell", "chmod", "777", remoteDir.strip()])
           print "chmod " + remoteDir.strip()
-      self.checkCmd(["shell", "chmod", "777", remoteDir])
+      self.checkCmdAs(["shell", "chmod", "777", remoteDir])
       print "chmod " + remoteDir
     else:
-      self.checkCmd(["shell", "chmod", "777", remoteDir.strip()])
-      print "chmod " + remoteDir
+      self.checkCmdAs(["shell", "chmod", "777", remoteDir.strip()])
+      print "chmod " + remoteDir.strip()
+
+  def verifyPackage(self, packageName):
+    # If a valid package name is specified, it will be used for certain
+    # file operations, so that pushed files and directories are created
+    # by the uid associated with the package.
+    self.packageName = None
+    if (packageName):
+      data = self.runCmd(["shell", "run-as", packageName, "pwd"]).stdout.read()
+      if (not re.search('is unknown', data)):
+        self.packageName = packageName
+        print "package set: " + self.packageName