From 6ecdfebf48d88fb54755254f750a844bb42d7db0 Mon Sep 17 00:00:00 2001 From: William Lachance Date: Wed, 8 Feb 2012 11:07:19 -0800 Subject: [PATCH] Bug 722429 - Should not mirror mozbase/mozdevice into m-c r=ctalbert --- testing/mozbase/Makefile.in | 1 - testing/mozbase/mozdevice/README.md | 5 - .../mozbase/mozdevice/mozdevice/__init__.py | 39 - .../mozdevice/mozdevice/devicemanager.py | 538 ------- .../mozdevice/mozdevice/devicemanagerADB.py | 702 ---------- .../mozdevice/mozdevice/devicemanagerSUT.py | 1239 ----------------- testing/mozbase/mozdevice/setup.py | 67 - 7 files changed, 2591 deletions(-) delete mode 100644 testing/mozbase/mozdevice/README.md delete mode 100644 testing/mozbase/mozdevice/mozdevice/__init__.py delete mode 100644 testing/mozbase/mozdevice/mozdevice/devicemanager.py delete mode 100644 testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py delete mode 100644 testing/mozbase/mozdevice/mozdevice/devicemanagerSUT.py delete mode 100644 testing/mozbase/mozdevice/setup.py diff --git a/testing/mozbase/Makefile.in b/testing/mozbase/Makefile.in index 1b96b7165047..a638772fd572 100644 --- a/testing/mozbase/Makefile.in +++ b/testing/mozbase/Makefile.in @@ -51,7 +51,6 @@ include $(topsrcdir)/config/rules.mk # Packages later in the list can depend only on packages earlier in the list. MOZBASE_PACKAGES = \ manifestdestiny \ - mozdevice \ mozhttpd \ mozinfo \ mozinstall \ diff --git a/testing/mozbase/mozdevice/README.md b/testing/mozbase/mozdevice/README.md deleted file mode 100644 index 80083f2a7908..000000000000 --- a/testing/mozbase/mozdevice/README.md +++ /dev/null @@ -1,5 +0,0 @@ -[mozdevice](https://github.com/mozilla/mozbase/tree/master/mozdevice) provides -an interface to interact with a remote device such as an Android phone connected -to a workstation. Currently there are two implementations of the interface: one -uses a TCP-based protocol to communicate with a server running on the device, -another uses Android's adb utility. diff --git a/testing/mozbase/mozdevice/mozdevice/__init__.py b/testing/mozbase/mozdevice/mozdevice/__init__.py deleted file mode 100644 index 91b4444e1a1f..000000000000 --- a/testing/mozbase/mozdevice/mozdevice/__init__.py +++ /dev/null @@ -1,39 +0,0 @@ -# ***** 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 Mozbase. -# -# The Initial Developer of the Original Code is -# The Mozilla Foundation. -# Portions created by the Initial Developer are Copyright (C) 2011 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Will Lachance -# -# 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 ***** - -from devicemanagerADB import DeviceManagerADB -from devicemanagerSUT import DeviceManagerSUT diff --git a/testing/mozbase/mozdevice/mozdevice/devicemanager.py b/testing/mozbase/mozdevice/mozdevice/devicemanager.py deleted file mode 100644 index ad8750119422..000000000000 --- a/testing/mozbase/mozdevice/mozdevice/devicemanager.py +++ /dev/null @@ -1,538 +0,0 @@ -# ***** 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 Test Automation Framework. -# -# The Initial Developer of the Original Code is Joel Maher. -# -# Portions created by the Initial Developer are Copyright (C) 2009 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Joel Maher (Original Developer) -# Clint Talbert -# Mark Cote -# -# 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 hashlib -import socket -import os -import re - -class FileError(Exception): - " Signifies an error which occurs while doing a file operation." - - def __init__(self, msg = ''): - self.msg = msg - - def __str__(self): - return self.msg - -class DMError(Exception): - "generic devicemanager exception." - - def __init__(self, msg= ''): - self.msg = msg - - def __str__(self): - return self.msg - - -class DeviceManager: - # external function - # returns: - # success: True - # failure: False - def pushFile(self, localname, destname): - assert 0 == 1 - return False - - # external function - # returns: - # success: directory name - # failure: None - def mkDir(self, name): - assert 0 == 1 - return None - - # make directory structure on the device - # external function - # returns: - # success: directory structure that we created - # failure: None - def mkDirs(self, filename): - assert 0 == 1 - return None - - # push localDir from host to remoteDir on the device - # external function - # returns: - # success: remoteDir - # failure: None - def pushDir(self, localDir, remoteDir): - assert 0 == 1 - return None - - # external function - # returns: - # success: True - # failure: False - def dirExists(self, dirname): - assert 0 == 1 - return False - - # 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): - assert 0 == 1 - return False - - # list files on the device, requires cd to directory first - # external function - # returns: - # success: array of filenames, ['file1', 'file2', ...] - # failure: [] - def listFiles(self, rootdir): - assert 0 == 1 - return [] - - # external function - # returns: - # success: output of telnet, i.e. "removing file: /mnt/sdcard/tests/test.txt" - # failure: None - def removeFile(self, filename): - assert 0 == 1 - return False - - # does a recursive delete of directory on the device: rm -Rf remoteDir - # external function - # returns: - # success: output of telnet, i.e. "removing file: /mnt/sdcard/tests/test.txt" - # failure: None - def removeDir(self, remoteDir): - assert 0 == 1 - return None - - # external function - # returns: - # success: array of process tuples - # failure: [] - def getProcessList(self): - assert 0 == 1 - return [] - - # external function - # returns: - # success: pid - # failure: None - def fireProcess(self, appname, failIfRunning=False): - assert 0 == 1 - return None - - # external function - # returns: - # success: output filename - # failure: None - def launchProcess(self, cmd, outputFile = "process.txt", cwd = '', env = '', failIfRunning=False): - assert 0 == 1 - return None - - # loops until 'process' has exited or 'timeout' seconds is reached - # loop sleeps for 'interval' seconds between iterations - # external function - # returns: - # success: [file contents, None] - # failure: [None, None] - def communicate(self, process, timeout = 600, interval = 5): - timed_out = True - if (timeout > 0): - total_time = 0 - while total_time < timeout: - time.sleep(interval) - if self.processExist(process) == None: - timed_out = False - break - total_time += interval - - if (timed_out == True): - return [None, None] - - return [self.getFile(process, "temp.txt"), None] - - # iterates process list and returns pid if exists, otherwise None - # external function - # returns: - # success: pid - # failure: None - def processExist(self, appname): - pid = None - - #filter out extra spaces - parts = filter(lambda x: x != '', appname.split(' ')) - appname = ' '.join(parts) - - #filter out the quoted env string if it exists - #ex: '"name=value;name2=value2;etc=..." process args' -> 'process args' - parts = appname.split('"') - if (len(parts) > 2): - appname = ' '.join(parts[2:]).strip() - - pieces = appname.split(' ') - parts = pieces[0].split('/') - app = parts[-1] - procre = re.compile('.*' + app + '.*') - - procList = self.getProcessList() - if (procList == []): - return None - - for proc in procList: - if (procre.match(proc[1])): - pid = proc[0] - break - return pid - - # external function - # returns: - # success: output from testagent - # failure: None - def killProcess(self, appname): - assert 0 == 1 - return None - - # external function - # returns: - # success: filecontents - # failure: None - def catFile(self, remoteFile): - assert 0 == 1 - return None - - # external function - # returns: - # success: output of pullfile, string - # failure: None - def pullFile(self, remoteFile): - assert 0 == 1 - return None - - # copy file from device (remoteFile) to host (localFile) - # external function - # returns: - # success: output of pullfile, string - # failure: None - def getFile(self, remoteFile, localFile = ''): - assert 0 == 1 - return None - - # copy directory structure from device (remoteDir) to host (localDir) - # external function - # checkDir exists so that we don't create local directories if the - # remote directory doesn't exist but also so that we don't call isDir - # twice when recursing. - # returns: - # success: list of files, string - # failure: None - def getDirectory(self, remoteDir, localDir, checkDir=True): - assert 0 == 1 - return None - - # external function - # returns: - # success: True - # failure: False - # Throws a FileError exception when null (invalid dir/filename) - def isDir(self, remotePath): - assert 0 == 1 - return False - - # true/false check if the two files have the same md5 sum - # external function - # returns: - # success: True - # failure: False - def validateFile(self, remoteFile, localFile): - assert 0 == 1 - return False - - # return the md5 sum of a remote file - # internal function - # returns: - # success: MD5 hash for given filename - # failure: None - def getRemoteHash(self, filename): - assert 0 == 1 - return None - - # return the md5 sum of a file on the host - # internal function - # returns: - # success: MD5 hash for given filename - # failure: None - def getLocalHash(self, filename): - file = open(filename, 'rb') - if (file == None): - return None - - try: - mdsum = hashlib.md5() - except: - return None - - while 1: - data = file.read(1024) - if not data: - break - mdsum.update(data) - - file.close() - hexval = mdsum.hexdigest() - if (self.debug >= 3): print "local hash returned: '" + hexval + "'" - return hexval - # Gets the device root for the testing area on the device - # For all devices we will use / type slashes and depend on the device-agent - # to sort those out. The agent will return us the device location where we - # should store things, we will then create our /tests structure relative to - # that returned path. - # Structure on the device is as follows: - # /tests - # /| --> approot - # /profile - # /xpcshell - # /reftest - # /mochitest - # - # external function - # returns: - # success: path for device root - # failure: None - def getDeviceRoot(self): - assert 0 == 1 - return None - - # 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 - def getAppRoot(self): - 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.firefox')): - return 'org.mozilla.firefox' - elif (self.dirExists('/data/data/org.mozilla.fennec_aurora')): - return 'org.mozilla.fennec_aurora' - elif (self.dirExists('/data/data/org.mozilla.firefox_beta')): - return '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: - # success: path for test root - # failure: None - def getTestRoot(self, type): - devroot = self.getDeviceRoot() - if (devroot == None): - return None - - if (re.search('xpcshell', type, re.I)): - self.testRoot = devroot + '/xpcshell' - elif (re.search('?(i)reftest', type)): - self.testRoot = devroot + '/reftest' - elif (re.search('?(i)mochitest', type)): - self.testRoot = devroot + '/mochitest' - return self.testRoot - - # Sends a specific process ID a signal code and action. - # For Example: SIGINT and SIGDFL to process x - def signal(self, processID, signalType, signalAction): - # currently not implemented in device agent - todo - pass - - # Get a return code from process ending -- needs support on device-agent - def getReturnCode(self, processID): - # TODO: make this real - return 0 - - # external function - # returns: - # success: output of unzip command - # failure: None - def unpackFile(self, filename): - return None - - # external function - # returns: - # success: status from test agent - # failure: None - def reboot(self, ipAddr=None, port=30000): - assert 0 == 1 - return None - - # validate localDir from host to remoteDir on the device - # external function - # returns: - # success: True - # failure: False - def validateDir(self, localDir, remoteDir): - if (self.debug >= 2): print "validating directory: " + localDir + " to " + remoteDir - for root, dirs, files in os.walk(localDir): - parts = root.split(localDir) - for file in files: - remoteRoot = remoteDir + '/' + parts[1] - remoteRoot = remoteRoot.replace('/', '/') - if (parts[1] == ""): remoteRoot = remoteDir - remoteName = remoteRoot + '/' + file - if (self.validateFile(remoteName, os.path.join(root, file)) <> True): - return False - return True - - # Returns information about the device: - # Directive indicates the information you want to get, your choices are: - # os - name of the os - # id - unique id of the device - # uptime - uptime of the device - # systime - system time of the device - # screen - screen resolution - # memory - memory stats - # process - list of running processes (same as ps) - # disk - total, free, available bytes on disk - # power - power status (charge, battery temp) - # all - all of them - or call it with no parameters to get all the information - # returns: - # success: dict of info strings by directive name - # failure: {} - def getInfo(self, directive=None): - assert 0 == 1 - return {} - - # external function - # returns: - # success: output from agent for inst command - # failure: None - def installApp(self, appBundlePath, destPath=None): - assert 0 == 1 - return None - - # external function - # returns: - # success: True - # failure: None - def uninstallAppAndReboot(self, appName, installPath=None): - assert 0 == 1 - return None - - # external function - # returns: - # success: text status from command or callback server - # failure: None - def updateApp(self, appBundlePath, processName=None, destPath=None, ipAddr=None, port=30000): - assert 0 == 1 - return None - - # external function - # returns: - # success: time in ms - # failure: None - def getCurrentTime(self): - assert 0 == 1 - return None - - -class NetworkTools: - def __init__(self): - pass - - # Utilities to get the local ip address - def getInterfaceIp(self, ifname): - if os.name != "nt": - import fcntl - import struct - s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - return socket.inet_ntoa(fcntl.ioctl( - s.fileno(), - 0x8915, # SIOCGIFADDR - struct.pack('256s', ifname[:15]) - )[20:24]) - else: - return None - - def getLanIp(self): - ip = socket.gethostbyname(socket.gethostname()) - if ip.startswith("127.") and os.name != "nt": - interfaces = ["eth0","eth1","eth2","wlan0","wlan1","wifi0","ath0","ath1","ppp0"] - for ifname in interfaces: - try: - ip = self.getInterfaceIp(ifname) - break; - except IOError: - pass - return ip - - # Gets an open port starting with the seed by incrementing by 1 each time - def findOpenPort(self, ip, seed): - try: - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - connected = False - if isinstance(seed, basestring): - seed = int(seed) - maxportnum = seed + 5000 # We will try at most 5000 ports to find an open one - while not connected: - try: - s.bind((ip, seed)) - connected = True - s.close() - break - except: - if seed > maxportnum: - print "Could not find open port after checking 5000 ports" - raise - seed += 1 - except: - print "Socket error trying to find open port" - - return seed - diff --git a/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py b/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py deleted file mode 100644 index 08aabb52ca2c..000000000000 --- a/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py +++ /dev/null @@ -1,702 +0,0 @@ -import subprocess -from devicemanager import DeviceManager, DMError -import re -import os -import sys -import tempfile - -class DeviceManagerADB(DeviceManager): - - def __init__(self, host = None, port = 20701, retrylimit = 5, packageName = None): - self.host = host - self.port = port - self.retrylimit = retrylimit - self.retries = 0 - self._sock = None - self.useRunAs = False - self.haveRoot = False - self.useZip = False - self.packageName = None - self.tempDir = None - if packageName == None: - if os.getenv('USER'): - packageName = 'org.mozilla.fennec_' + os.getenv('USER') - else: - packageName = 'org.mozilla.fennec_' - 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: - self.verifyADB() - self.verifyRunAs(packageName) - except: - self.useRunAs = False - self.packageName = None - try: - self.verifyZip() - except: - self.useZip = False - - def verifyRoot(): - # a test to see if we have root privs - files = self.listFiles("/data/data") - if (len(files) == 1): - if (files[0].find("Permission denied") != -1): - print "NOT running as root" - raise Exception("not running as root") - self.haveRoot = True - - try: - verifyRoot() - except: - try: - self.checkCmd(["root"]) - # The root command does not fail even if ADB cannot get - # root rights (e.g. due to production builds), so we have - # to check again ourselves that we have root now. - verifyRoot() - except: - if (self.useRunAs): - print "restarting as root failed, but run-as available" - else: - print "restarting as root failed" - - # external function - # returns: - # success: True - # failure: False - def pushFile(self, localname, destname): - try: - if (os.name == "nt"): - destname = destname.replace('\\', '/') - if (self.useRunAs): - remoteTmpFile = self.getTempDir() + "/" + os.path.basename(localname) - self.checkCmd(["push", os.path.realpath(localname), remoteTmpFile]) - if self.useDDCopy: - self.checkCmdAs(["shell", "dd", "if=" + remoteTmpFile, "of=" + destname]) - else: - 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.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): - 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 either zip/unzip or push file-by-file to get around this - # limitation - try: - if (self.useZip): - localZip = tempfile.mktemp()+".zip" - remoteZip = remoteDir + "/adbdmtmp.zip" - subprocess.check_output(["zip", "-r", localZip, '.'], cwd=localDir) - self.pushFile(localZip, remoteZip) - os.remove(localZip) - self.checkCmdAs(["shell", "unzip", "-o", remoteZip, "-d", remoteDir]) - self.checkCmdAs(["shell", "rm", remoteZip]) - else: - if (not self.dirExists(remoteDir)): - self.mkDirs(remoteDir+"/x") - for root, dirs, files in os.walk(localDir, followlinks='true'): - 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 remoteDir - except: - print "pushing " + localDir + " to " + remoteDir + " failed" - return None - - # external function - # returns: - # success: True - # failure: False - def dirExists(self, dirname): - 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): - p = self.runCmd(["shell", "ls", "-a", filepath]) - data = p.stdout.readlines() - if (len(data) == 1): - if (data[0].rstrip() == filepath): - return True - return False - - def removeFile(self, filename): - return self.runCmd(["shell", "rm", filename]).stdout.read() - - # does a recursive delete of directory on the device: rm -Rf remoteDir - # external function - # returns: - # success: output of telnet, i.e. "removing file: /mnt/sdcard/tests/test.txt" - # failure: None - def removeSingleDir(self, remoteDir): - return self.runCmd(["shell", "rmdir", remoteDir]).stdout.read() - - # does a recursive delete of directory on the device: rm -Rf remoteDir - # external function - # returns: - # success: output of telnet, i.e. "removing file: /mnt/sdcard/tests/test.txt" - # failure: None - 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() + "/" + f.strip()) - out += self.removeSingleDir(remoteDir.strip()) - else: - out += self.removeFile(remoteDir.strip()) - return out - - def isDir(self, 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].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", "-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 [] - return data - - # external function - # returns: - # success: array of process tuples - # failure: [] - def getProcessList(self): - p = self.runCmd(["shell", "ps"]) - # first line is the headers - p.stdout.readline() - proc = p.stdout.readline() - ret = [] - while (proc): - els = proc.split() - ret.append(list([els[1], els[len(els) - 1], els[0]])) - proc = p.stdout.readline() - return ret - - # external function - # returns: - # success: pid - # failure: None - def fireProcess(self, appname, failIfRunning=False): - #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).strip() - i = cmd.find(" ") - # SUT identifies the URL by looking for :\\ -- another strategy to consider - re_url = re.compile('^[http|file|chrome|about].*') - last = cmd.rfind(" ") - uri = "" - args = "" - if re_url.match(cmd[last:].strip()): - args = cmd[i:last].strip() - uri = cmd[last:].strip() - else: - args = cmd[i:].strip() - acmd.append("-n") - acmd.append(cmd[0:i] + "/.App") - acmd.append("--es") - if args != "": - acmd.append("args") - acmd.append(args) - if uri != "": - acmd.append("-d") - acmd.append(''.join(['\'',uri, '\''])); - print acmd - self.checkCmd(acmd) - return outputFile; - - # external function - # returns: - # success: output from testagent - # failure: None - def killProcess(self, appname): - procs = self.getProcessList() - for (pid, name, user) in procs: - if name == appname: - p = self.runCmdAs(["shell", "kill", pid]) - return p.stdout.read() - return None - - # external function - # returns: - # success: filecontents - # failure: None - def catFile(self, remoteFile): - #p = self.runCmd(["shell", "cat", remoteFile]) - #return p.stdout.read() - return self.getFile(remoteFile) - - # external function - # returns: - # success: output of pullfile, string - # failure: None - def pullFile(self, remoteFile): - #return self.catFile(remoteFile) - return self.getFile(remoteFile) - - # copy file from device (remoteFile) to host (localFile) - # external function - # returns: - # success: output of pullfile, string - # failure: None - def getFile(self, remoteFile, localFile = 'tmpfile_dm_adb'): - # TODO: add debug flags and allow for printing stdout - # self.runCmd(["pull", remoteFile, localFile]) - try: - - # First attempt to pull file regularly - outerr = self.runCmd(["pull", remoteFile, localFile]).communicate() - - # Now check stderr for errors - errl = outerr[1].splitlines() - if (len(errl) == 1): - if (((errl[0].find("Permission denied") != -1) - or (errl[0].find("does not exist") != -1)) - 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. - remoteTmpFile = self.getTempDir() + "/" + os.path.basename(remoteFile) - self.checkCmdAs(["shell", "dd", "if=" + remoteFile, "of=" + remoteTmpFile]) - self.checkCmdAs(["shell", "chmod", "777", remoteTmpFile]) - self.runCmd(["pull", remoteTmpFile, localFile]).stdout.read() - # Clean up temporary file - self.checkCmdAs(["shell", "rm", remoteTmpFile]) - - f = open(localFile) - ret = f.read() - f.close() - return ret; - except: - return None - - # copy directory structure from device (remoteDir) to host (localDir) - # external function - # checkDir exists so that we don't create local directories if the - # remote directory doesn't exist but also so that we don't call isDir - # twice when recursing. - # returns: - # success: list of files, string - # failure: None - def getDirectory(self, remoteDir, localDir, checkDir=True): - ret = [] - p = self.runCmd(["pull", remoteDir, localDir]) - p.stderr.readline() - line = p.stderr.readline() - while (line): - els = line.split() - f = els[len(els) - 1] - i = f.find(localDir) - if (i != -1): - if (localDir[len(localDir) - 1] != '/'): - i = i + 1 - f = f[i + len(localDir):] - i = f.find("/") - if (i > 0): - f = f[0:i] - ret.append(f) - line = p.stderr.readline() - #the last line is a summary - if (len(ret) > 0): - ret.pop() - return ret - - - - # true/false check if the two files have the same md5 sum - # external function - # returns: - # success: True - # failure: False - def validateFile(self, remoteFile, localFile): - return self.getRemoteHash(remoteFile) == self.getLocalHash(localFile) - - # return the md5 sum of a remote file - # internal function - # returns: - # success: MD5 hash for given filename - # failure: None - def getRemoteHash(self, filename): - data = p = self.runCmd(["shell", "ls", "-l", filename]).stdout.read() - return data.split()[3] - - def getLocalHash(self, filename): - data = p = subprocess.Popen(["ls", "-l", filename], stdout=subprocess.PIPE).stdout.read() - return data.split()[4] - - # Gets the device root for the testing area on the device - # For all devices we will use / type slashes and depend on the device-agent - # to sort those out. The agent will return us the device location where we - # should store things, we will then create our /tests structure relative to - # that returned path. - # Structure on the device is as follows: - # /tests - # /| --> approot - # /profile - # /xpcshell - # /reftest - # /mochitest - # - # external function - # returns: - # success: path for device root - # failure: None - def getDeviceRoot(self): - # /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 - - # Gets the temporary directory we are using on this device - # base on our device root, ensuring also that it exists. - # - # internal function - # returns: - # success: path for temporary directory - # failure: None - def getTempDir(self): - # Cache result to speed up operations depending - # on the temporary directory. - if self.tempDir == None: - self.tempDir = self.getDeviceRoot() + "/tmp" - if (not self.dirExists(self.tempDir)): - return self.mkDir(self.tempDir) - - return self.tempDir - - # 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 - def getAppRoot(self): - 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.packageName and self.dirExists('/data/data/' + self.packageName)): - return '/data/data/' + self.packageName - - # Failure (either not installed or not a recognized platform) - print "devicemanagerADB: getAppRoot failed" - return None - - # Gets the directory location on the device for a specific test type - # Type is one of: xpcshell|reftest|mochitest - # external function - # returns: - # success: path for test root - # failure: None - def getTestRoot(self, type): - devroot = self.getDeviceRoot() - if (devroot == None): - return None - - if (re.search('xpcshell', type, re.I)): - self.testRoot = devroot + '/xpcshell' - elif (re.search('?(i)reftest', type)): - self.testRoot = devroot + '/reftest' - elif (re.search('?(i)mochitest', type)): - self.testRoot = devroot + '/mochitest' - return self.testRoot - - - # external function - # returns: - # success: status from test agent - # failure: None - def reboot(self, wait = False): - ret = self.runCmd(["reboot"]).stdout.read() - if (not wait): - return "Success" - countdown = 40 - while (countdown > 0): - countdown - try: - self.checkCmd(["wait-for-device", "shell", "ls", "/sbin"]) - return ret - except: - try: - self.checkCmd(["root"]) - except: - time.sleep(1) - print "couldn't get root" - return "Success" - - # external function - # returns: - # success: text status from command or callback server - # failure: None - def updateApp(self, appBundlePath, processName=None, destPath=None, ipAddr=None, port=30000): - return self.runCmd(["install", "-r", appBundlePath]).stdout.read() - - # external function - # returns: - # success: time in ms - # failure: None - def getCurrentTime(self): - timestr = self.runCmd(["shell", "date", "+%s"]).stdout.read().strip() - if (not timestr or not timestr.isdigit()): - return None - return str(int(timestr)*1000) - - # Returns information about the device: - # Directive indicates the information you want to get, your choices are: - # os - name of the os - # id - unique id of the device - # uptime - uptime of the device - # systime - system time of the device - # screen - screen resolution - # memory - memory stats - # process - list of running processes (same as ps) - # disk - total, free, available bytes on disk - # power - power status (charge, battery temp) - # all - all of them - or call it with no parameters to get all the information - # returns: - # success: dict of info strings by directive name - # failure: {} - def getInfo(self, directive="all"): - ret = {} - if (directive == "id" or directive == "all"): - ret["id"] = self.runCmd(["get-serialno"]).stdout.read() - if (directive == "os" or directive == "all"): - ret["os"] = self.runCmd(["shell", "getprop", "ro.build.display.id"]).stdout.read() - if (directive == "uptime" or directive == "all"): - utime = self.runCmd(["shell", "uptime"]).stdout.read() - if (not utime): - raise DMError("error getting uptime") - utime = utime[9:] - hours = utime[0:utime.find(":")] - utime = utime[utime[1:].find(":") + 2:] - minutes = utime[0:utime.find(":")] - utime = utime[utime[1:].find(":") + 2:] - seconds = utime[0:utime.find(",")] - ret["uptime"] = ["0 days " + hours + " hours " + minutes + " minutes " + seconds + " seconds"] - if (directive == "process" or directive == "all"): - ret["process"] = self.runCmd(["shell", "ps"]).stdout.read() - if (directive == "systime" or directive == "all"): - ret["systime"] = self.runCmd(["shell", "date"]).stdout.read() - print ret - return ret - - def runCmd(self, args): - # If we are not root but have run-as, and we're trying to execute - # a shell command then using run-as is the best we can do - if (not self.haveRoot and self.useRunAs and args[0] == "shell" and args[1] != "run-as"): - args.insert(1, "run-as") - args.insert(2, self.packageName) - args.insert(0, "adb") - return subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - - def runCmdAs(self, args): - if self.useRunAs: - args.insert(1, "run-as") - args.insert(2, self.packageName) - return self.runCmd(args) - - def checkCmd(self, args): - # If we are not root but have run-as, and we're trying to execute - # a shell command then using run-as is the best we can do - if (not self.haveRoot and self.useRunAs and args[0] == "shell" and args[1] != "run-as"): - args.insert(1, "run-as") - args.insert(2, self.packageName) - args.insert(0, "adb") - return subprocess.check_call(args) - - def checkCmdAs(self, args): - if (self.useRunAs): - args.insert(1, "run-as") - args.insert(2, self.packageName) - return self.checkCmd(args) - - def chmodDir(self, remoteDir): - 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.checkCmdAs(["shell", "chmod", "777", remoteDir.strip()]) - print "chmod " + remoteDir.strip() - self.checkCmdAs(["shell", "chmod", "777", remoteDir]) - print "chmod " + remoteDir - else: - self.checkCmdAs(["shell", "chmod", "777", remoteDir.strip()]) - print "chmod " + remoteDir.strip() - - def verifyADB(self): - # Check to see if adb itself can be executed. - try: - self.runCmd(["version"]) - except: - print "unable to execute ADB: ensure Android SDK is installed and adb is in your $PATH" - - def isCpAvailable(self): - # Some Android systems may not have a cp command installed, - # or it may not be executable by the user. - data = self.runCmd(["shell", "cp"]).stdout.read() - if (re.search('Usage', data)): - return True - else: - 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 - return True - print "unable to execute 'cp' on device; consider installing busybox from Android Market" - return False - - def verifyRunAs(self, packageName): - # If a valid package name is available, and certain other - # conditions are met, devicemanagerADB can execute file operations - # via the "run-as" command, so that pushed files and directories - # are created by the uid associated with the package, more closely - # 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 - devroot = self.getDeviceRoot() - if (packageName and self.isCpAvailable() and devroot): - tmpDir = self.getTempDir() - - self.checkCmd(["shell", "run-as", packageName, "mkdir", devroot + "/sanity"]) - self.checkCmd(["push", os.path.abspath(sys.argv[0]), tmpDir + "/tmpfile"]) - if self.useDDCopy: - self.checkCmd(["shell", "run-as", packageName, "dd", "if=" + tmpDir + "/tmpfile", "of=" + devroot + "/sanity/tmpfile"]) - else: - self.checkCmd(["shell", "run-as", packageName, "cp", tmpDir + "/tmpfile", devroot + "/sanity"]) - if (self.fileExists(devroot + "/sanity/tmpfile")): - print "will execute commands via run-as " + packageName - self.packageName = packageName - self.useRunAs = True - self.checkCmd(["shell", "rm", devroot + "/tmp/tmpfile"]) - self.checkCmd(["shell", "run-as", packageName, "rm", "-r", devroot + "/sanity"]) - - def isUnzipAvailable(self): - data = self.runCmd(["shell", "unzip"]).stdout.read() - if (re.search('Usage', data)): - return True - else: - return False - - def isLocalZipAvailable(self): - try: - subprocess.check_call(["zip", "-?"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) - except: - return False - return True - - def verifyZip(self): - # 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 - if (self.isUnzipAvailable() and self.isLocalZipAvailable()): - print "will use zip to push directories" - self.useZip = True diff --git a/testing/mozbase/mozdevice/mozdevice/devicemanagerSUT.py b/testing/mozbase/mozdevice/mozdevice/devicemanagerSUT.py deleted file mode 100644 index 3b1888128472..000000000000 --- a/testing/mozbase/mozdevice/mozdevice/devicemanagerSUT.py +++ /dev/null @@ -1,1239 +0,0 @@ -# ***** 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 Test Automation Framework. -# -# The Initial Developer of the Original Code is Joel Maher. -# -# Portions created by the Initial Developer are Copyright (C) 2009 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Joel Maher (Original Developer) -# Clint Talbert -# Mark Cote -# -# 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 socket -import SocketServer -import time, datetime -import os -import re -import hashlib -import subprocess -from threading import Thread -import traceback -import sys -from devicemanager import DeviceManager, DMError, FileError, NetworkTools - -class DeviceManagerSUT(DeviceManager): - host = '' - port = 0 - debug = 2 - retries = 0 - tempRoot = os.getcwd() - base_prompt = '$>' - base_prompt_re = '\$\>' - prompt_sep = '\x00' - prompt_regex = '.*(' + base_prompt_re + prompt_sep + ')' - agentErrorRE = re.compile('^##AGENT-WARNING##.*') - - # TODO: member variable to indicate error conditions. - # This should be set to a standard error from the errno module. - # So, for example, when an error occurs because of a missing file/directory, - # before returning, the function would do something like 'self.error = errno.ENOENT'. - # The error would be set where appropriate--so sendCMD() could set socket errors, - # pushFile() and other file-related commands could set filesystem errors, etc. - - def __init__(self, host, port = 20701, retrylimit = 5): - self.host = host - self.port = port - self.retrylimit = retrylimit - self.retries = 0 - self._sock = None - self.getDeviceRoot() - - def cmdNeedsResponse(self, cmd): - """ Not all commands need a response from the agent: - * if the cmd matches the pushRE then it is the first half of push - and therefore we want to wait until the second half before looking - for a response - * rebt obviously doesn't get a response - * uninstall performs a reboot to ensure starting in a clean state and - so also doesn't look for a response - """ - noResponseCmds = [re.compile('^push .*$'), - re.compile('^rebt'), - re.compile('^uninst .*$'), - re.compile('^pull .*$')] - - for c in noResponseCmds: - if (c.match(cmd)): - return False - - # If the command is not in our list, then it gets a response - return True - - def shouldCmdCloseSocket(self, cmd): - """ Some commands need to close the socket after they are sent: - * push - * rebt - * uninst - * quit - """ - - socketClosingCmds = [re.compile('^push .*$'), - re.compile('^quit.*'), - re.compile('^rebt.*'), - re.compile('^uninst .*$')] - - for c in socketClosingCmds: - if (c.match(cmd)): - return True - - return False - - # convenience function to enable checks for agent errors - def verifySendCMD(self, cmdline, newline = True): - return self.sendCMD(cmdline, newline, False) - - - # - # create a wrapper for sendCMD that loops up to self.retrylimit iterations. - # this allows us to move the retry logic outside of the _doCMD() to make it - # easier for debugging in the future. - # note that since cmdline is a list of commands, they will all be retried if - # one fails. this is necessary in particular for pushFile(), where we don't want - # to accidentally send extra data if a failure occurs during data transmission. - # - def sendCMD(self, cmdline, newline = True, ignoreAgentErrors = True): - done = False - while (not done): - retVal = self._doCMD(cmdline, newline) - if (retVal is None): - self.retries += 1 - else: - self.retries = 0 - if ignoreAgentErrors == False: - if (self.agentErrorRE.match(retVal)): - raise DMError("error on the agent executing '%s'" % cmdline) - return retVal - - if (self.retries >= self.retrylimit): - done = True - - raise DMError("unable to connect to %s after %s attempts" % (self.host, self.retrylimit)) - - def _doCMD(self, cmdline, newline = True): - promptre = re.compile(self.prompt_regex + '$') - data = "" - shouldCloseSocket = False - recvGuard = 1000 - - if (self._sock == None): - try: - if (self.debug >= 1): - print "reconnecting socket" - self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - except: - self._sock = None - if (self.debug >= 2): - print "unable to create socket" - return None - - try: - self._sock.connect((self.host, int(self.port))) - self._sock.recv(1024) - except: - self._sock.close() - self._sock = None - if (self.debug >= 2): - print "unable to connect socket" - return None - - for cmd in cmdline: - if newline: cmd += '\r\n' - - try: - numbytes = self._sock.send(cmd) - if (numbytes != len(cmd)): - print "ERROR: our cmd was " + str(len(cmd)) + " bytes and we only sent " + str(numbytes) - return None - if (self.debug >= 4): print "send cmd: " + str(cmd) - except: - self._sock.close() - self._sock = None - return None - - # Check if the command should close the socket - shouldCloseSocket = self.shouldCmdCloseSocket(cmd) - - # Handle responses from commands - if (self.cmdNeedsResponse(cmd)): - found = False - loopguard = 0 - - while (found == False and (loopguard < recvGuard)): - temp = '' - if (self.debug >= 4): print "recv'ing..." - - # Get our response - try: - temp = self._sock.recv(1024) - if (self.debug >= 4): print "response: " + str(temp) - except: - self._sock.close() - self._sock = None - return None - - # If something goes wrong in the agent it will send back a string that - # starts with '##AGENT-ERROR##' - if (self.agentErrorRE.match(temp)): - data = temp - break - - lines = temp.split('\n') - - for line in lines: - if (promptre.match(line)): - found = True - data += temp - - # If we violently lose the connection to the device, this loop tends to spin, - # this guard prevents that - if (temp == ''): - loopguard += 1 - - if (shouldCloseSocket == True): - try: - self._sock.close() - self._sock = None - except: - self._sock = None - return None - - return data - - # internal function - # take a data blob and strip instances of the prompt '$>\x00' - def stripPrompt(self, data): - promptre = re.compile(self.prompt_regex + '.*') - retVal = [] - lines = data.split('\n') - for line in lines: - try: - while (promptre.match(line)): - pieces = line.split(self.prompt_sep) - index = pieces.index('$>') - pieces.pop(index) - line = self.prompt_sep.join(pieces) - except(ValueError): - pass - retVal.append(line) - - return '\n'.join(retVal) - - - # external function - # returns: - # success: True - # failure: False - def pushFile(self, localname, destname): - if (os.name == "nt"): - destname = destname.replace('\\', '/') - - if (self.debug >= 3): print "in push file with: " + localname + ", and: " + destname - if (self.dirExists(destname)): - if (not destname.endswith('/')): - destname = destname + '/' - destname = destname + os.path.basename(localname) - if (self.validateFile(destname, localname) == True): - if (self.debug >= 3): print "files are validated" - return True - - if self.mkDirs(destname) == None: - print "unable to make dirs: " + destname - return False - - if (self.debug >= 3): print "sending: push " + destname - - filesize = os.path.getsize(localname) - f = open(localname, 'rb') - data = f.read() - f.close() - - try: - retVal = self.verifySendCMD(['push ' + destname + ' ' + str(filesize) + '\r\n', data], newline = False) - except(DMError): - retVal = False - - if (self.debug >= 3): print "push returned: " + str(retVal) - - validated = False - if (retVal): - retline = self.stripPrompt(retVal).strip() - if (retline == None): - # Then we failed to get back a hash from agent, try manual validation - validated = self.validateFile(destname, localname) - else: - # Then we obtained a hash from push - localHash = self.getLocalHash(localname) - if (str(localHash) == str(retline)): - validated = True - else: - # We got nothing back from sendCMD, try manual validation - validated = self.validateFile(destname, localname) - - if (validated): - if (self.debug >= 3): print "Push File Validated!" - return True - else: - if (self.debug >= 2): print "Push File Failed to Validate!" - return False - - # external function - # returns: - # success: directory name - # failure: None - def mkDir(self, name): - if (self.dirExists(name)): - return name - else: - try: - retVal = self.verifySendCMD(['mkdr ' + name]) - except(DMError): - retVal = None - return retVal - - # make directory structure on the device - # external function - # returns: - # success: directory structure that we created - # failure: None - def mkDirs(self, filename): - parts = filename.split('/') - name = "" - for part in parts: - if (part == parts[-1]): break - if (part != ""): - name += '/' + part - 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): - if (self.debug >= 2): print "pushing directory: %s to %s" % (localDir, remoteDir) - for root, dirs, files in os.walk(localDir): - parts = root.split(localDir) - for file in files: - remoteRoot = remoteDir + '/' + parts[1] - if (remoteRoot.endswith('/')): - remoteName = remoteRoot + file - else: - remoteName = remoteRoot + '/' + file - if (parts[1] == ""): remoteRoot = remoteDir - if (self.pushFile(os.path.join(root, file), remoteName) == False): - # retry once - self.removeFile(remoteName) - if (self.pushFile(os.path.join(root, file), remoteName) == False): - return None - return remoteDir - - # external function - # returns: - # success: True - # failure: False - def dirExists(self, dirname): - match = ".*" + dirname + "$" - dirre = re.compile(match) - try: - data = self.verifySendCMD(['cd ' + dirname, 'cwd']) - except(DMError): - return False - - retVal = self.stripPrompt(data) - data = retVal.split('\n') - found = False - for d in data: - if (dirre.match(d)): - found = True - - return found - - # 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): - s = filepath.split('/') - containingpath = '/'.join(s[:-1]) - listfiles = self.listFiles(containingpath) - for f in listfiles: - if (f == s[-1]): - return True - return False - - # list files on the device, requires cd to directory first - # external function - # returns: - # success: array of filenames, ['file1', 'file2', ...] - # failure: [] - def listFiles(self, rootdir): - rootdir = rootdir.rstrip('/') - if (self.dirExists(rootdir) == False): - return [] - try: - data = self.verifySendCMD(['cd ' + rootdir, 'ls']) - except(DMError): - return [] - - retVal = self.stripPrompt(data) - files = filter(lambda x: x, retVal.split('\n')) - if len(files) == 1 and files[0] == '': - # special case on the agent: empty directories return just the string "" - return [] - return files - - # external function - # returns: - # success: output of telnet, i.e. "removing file: /mnt/sdcard/tests/test.txt" - # failure: None - def removeFile(self, filename): - if (self.debug>= 2): print "removing file: " + filename - try: - retVal = self.verifySendCMD(['rm ' + filename]) - except(DMError): - return None - - return retVal - - # does a recursive delete of directory on the device: rm -Rf remoteDir - # external function - # returns: - # success: output of telnet, i.e. "removing file: /mnt/sdcard/tests/test.txt" - # failure: None - def removeDir(self, remoteDir): - try: - retVal = self.verifySendCMD(['rmdr ' + remoteDir]) - except(DMError): - return None - - return retVal - - # external function - # returns: - # success: array of process tuples - # failure: [] - def getProcessList(self): - try: - data = self.verifySendCMD(['ps']) - except DMError: - return [] - - retVal = self.stripPrompt(data) - lines = retVal.split('\n') - files = [] - for line in lines: - if (line.strip() != ''): - pidproc = line.strip().split() - if (len(pidproc) == 2): - files += [[pidproc[0], pidproc[1]]] - elif (len(pidproc) == 3): - #android returns - files += [[pidproc[1], pidproc[2], pidproc[0]]] - return files - - # external function - # returns: - # success: pid - # failure: None - def fireProcess(self, appname, failIfRunning=False): - if (not appname): - if (self.debug >= 1): print "WARNING: fireProcess called with no command to run" - return None - - if (self.debug >= 2): print "FIRE PROC: '" + appname + "'" - - if (self.processExist(appname) != None): - print "WARNING: process %s appears to be running already\n" % appname - if (failIfRunning): - return None - - try: - data = self.verifySendCMD(['exec ' + appname]) - except(DMError): - return None - - # wait up to 30 seconds for process to start up - timeslept = 0 - while (timeslept <= 30): - process = self.processExist(appname) - if (process is not None): - break - time.sleep(3) - timeslept += 3 - - if (self.debug >= 4): print "got pid: %s for process: %s" % (process, appname) - return process - - # external function - # returns: - # success: output filename - # failure: None - def launchProcess(self, cmd, outputFile = "process.txt", cwd = '', env = '', failIfRunning=False): - if not cmd: - if (self.debug >= 1): print "WARNING: launchProcess called without command to run" - return None - - cmdline = subprocess.list2cmdline(cmd) - if (outputFile == "process.txt" or outputFile == None): - outputFile = self.getDeviceRoot(); - if outputFile is None: - return None - outputFile += "/process.txt" - cmdline += " > " + outputFile - - # Prepend our env to the command - cmdline = '%s %s' % (self.formatEnvString(env), cmdline) - - if self.fireProcess(cmdline, failIfRunning) is None: - return None - return outputFile - - # iterates process list and returns pid if exists, otherwise None - # external function - # returns: - # success: pid - # failure: None - def processExist(self, appname): - pid = None - - #filter out extra spaces - parts = filter(lambda x: x != '', appname.split(' ')) - appname = ' '.join(parts) - - #filter out the quoted env string if it exists - #ex: '"name=value;name2=value2;etc=..." process args' -> 'process args' - parts = appname.split('"') - if (len(parts) > 2): - appname = ' '.join(parts[2:]).strip() - - pieces = appname.split(' ') - parts = pieces[0].split('/') - app = parts[-1] - procre = re.compile('.*' + app + '.*') - - procList = self.getProcessList() - if (procList == []): - return None - - for proc in procList: - if (procre.match(proc[1])): - pid = proc[0] - break - return pid - - # external function - # returns: - # success: output from testagent - # failure: None - def killProcess(self, appname): - try: - data = self.verifySendCMD(['kill ' + appname]) - except(DMError): - return None - - return data - - # external function - # returns: - # success: tmpdir, string - # failure: None - def getTempDir(self): - try: - data = self.verifySendCMD(['tmpd']) - except(DMError): - return None - - return self.stripPrompt(data).strip('\n') - - # external function - # returns: - # success: filecontents - # failure: None - def catFile(self, remoteFile): - try: - data = self.verifySendCMD(['cat ' + remoteFile]) - except(DMError): - return None - - return self.stripPrompt(data) - - # external function - # returns: - # success: output of pullfile, string - # failure: None - def pullFile(self, remoteFile): - """Returns contents of remoteFile using the "pull" command. - The "pull" command is different from other commands in that DeviceManager - has to read a certain number of bytes instead of just reading to the - next prompt. This is more robust than the "cat" command, which will be - confused if the prompt string exists within the file being catted. - However it means we can't use the response-handling logic in sendCMD(). - """ - - def err(error_msg): - err_str = 'error returned from pull: %s' % error_msg - print err_str - self._sock = None - raise FileError(err_str) - - # FIXME: We could possibly move these socket-reading functions up to - # the class level if we wanted to refactor sendCMD(). For now they are - # only used to pull files. - - def uread(to_recv, error_msg): - """ unbuffered read """ - try: - data = self._sock.recv(to_recv) - if not data: - err(error_msg) - return None - return data - except: - err(error_msg) - return None - - def read_until_char(c, buffer, error_msg): - """ read until 'c' is found; buffer rest """ - while not '\n' in buffer: - data = uread(1024, error_msg) - if data == None: - err(error_msg) - return ('', '', '') - buffer += data - return buffer.partition(c) - - def read_exact(total_to_recv, buffer, error_msg): - """ read exact number of 'total_to_recv' bytes """ - while len(buffer) < total_to_recv: - to_recv = min(total_to_recv - len(buffer), 1024) - data = uread(to_recv, error_msg) - if data == None: - return None - buffer += data - return buffer - - prompt = self.base_prompt + self.prompt_sep - buffer = '' - - # expected return value: - # ,\n - # or, if error, - # ,-1\n - try: - data = self.verifySendCMD(['pull ' + remoteFile]) - except(DMError): - return None - - # read metadata; buffer the rest - metadata, sep, buffer = read_until_char('\n', buffer, 'could not find metadata') - if not metadata: - return None - if self.debug >= 3: - print 'metadata: %s' % metadata - - filename, sep, filesizestr = metadata.partition(',') - if sep == '': - err('could not find file size in returned metadata') - return None - try: - filesize = int(filesizestr) - except ValueError: - err('invalid file size in returned metadata') - return None - - if filesize == -1: - # read error message - error_str, sep, buffer = read_until_char('\n', buffer, 'could not find error message') - if not error_str: - return None - # prompt should follow - read_exact(len(prompt), buffer, 'could not find prompt') - print "DeviceManager: error pulling file '%s': %s" % (remoteFile, error_str) - return None - - # read file data - total_to_recv = filesize + len(prompt) - buffer = read_exact(total_to_recv, buffer, 'could not get all file data') - if buffer == None: - return None - if buffer[-len(prompt):] != prompt: - err('no prompt found after file data--DeviceManager may be out of sync with agent') - return buffer - return buffer[:-len(prompt)] - - # copy file from device (remoteFile) to host (localFile) - # external function - # returns: - # success: output of pullfile, string - # failure: None - def getFile(self, remoteFile, localFile = ''): - if localFile == '': - localFile = os.path.join(self.tempRoot, "temp.txt") - - try: - retVal = self.pullFile(remoteFile) - except: - return None - - if (retVal is None): - return None - - fhandle = open(localFile, 'wb') - fhandle.write(retVal) - fhandle.close() - if not self.validateFile(remoteFile, localFile): - print 'failed to validate file when downloading %s!' % remoteFile - return None - return retVal - - # copy directory structure from device (remoteDir) to host (localDir) - # external function - # checkDir exists so that we don't create local directories if the - # remote directory doesn't exist but also so that we don't call isDir - # twice when recursing. - # returns: - # success: list of files, string - # failure: None - def getDirectory(self, remoteDir, localDir, checkDir=True): - if (self.debug >= 2): print "getting files in '" + remoteDir + "'" - if checkDir: - try: - is_dir = self.isDir(remoteDir) - except FileError: - return None - if not is_dir: - return None - - filelist = self.listFiles(remoteDir) - if (self.debug >= 3): print filelist - if not os.path.exists(localDir): - os.makedirs(localDir) - - for f in filelist: - if f == '.' or f == '..': - continue - remotePath = remoteDir + '/' + f - localPath = os.path.join(localDir, f) - try: - is_dir = self.isDir(remotePath) - except FileError: - print 'isdir failed on file "%s"; continuing anyway...' % remotePath - continue - if is_dir: - if (self.getDirectory(remotePath, localPath, False) == None): - print 'failed to get directory "%s"' % remotePath - return None - else: - # It's sometimes acceptable to have getFile() return None, such as - # when the agent encounters broken symlinks. - # FIXME: This should be improved so we know when a file transfer really - # failed. - if self.getFile(remotePath, localPath) == None: - print 'failed to get file "%s"; continuing anyway...' % remotePath - return filelist - - # external function - # returns: - # success: True - # failure: False - # Throws a FileError exception when null (invalid dir/filename) - def isDir(self, remotePath): - try: - data = self.verifySendCMD(['isdir ' + remotePath]) - except(DMError): - # normally there should be no error here; a nonexistent file/directory will - # return the string ": No such file or directory". - # However, I've seen AGENT-WARNING returned before. - return False - retVal = self.stripPrompt(data).strip() - if not retVal: - raise FileError('isdir returned null') - return retVal == 'TRUE' - - # true/false check if the two files have the same md5 sum - # external function - # returns: - # success: True - # failure: False - def validateFile(self, remoteFile, localFile): - remoteHash = self.getRemoteHash(remoteFile) - localHash = self.getLocalHash(localFile) - - if (remoteHash == None): - return False - - if (remoteHash == localHash): - return True - - return False - - # return the md5 sum of a remote file - # internal function - # returns: - # success: MD5 hash for given filename - # failure: None - def getRemoteHash(self, filename): - try: - data = self.verifySendCMD(['hash ' + filename]) - except(DMError): - return None - - retVal = self.stripPrompt(data) - if (retVal != None): - retVal = retVal.strip('\n') - if (self.debug >= 3): print "remote hash returned: '" + retVal + "'" - return retVal - - # Gets the device root for the testing area on the device - # For all devices we will use / type slashes and depend on the device-agent - # to sort those out. The agent will return us the device location where we - # should store things, we will then create our /tests structure relative to - # that returned path. - # Structure on the device is as follows: - # /tests - # /| --> approot - # /profile - # /xpcshell - # /reftest - # /mochitest - # - # external function - # returns: - # success: path for device root - # failure: None - def getDeviceRoot(self): - try: - data = self.verifySendCMD(['testroot']) - except: - return None - - deviceRoot = self.stripPrompt(data).strip('\n') + '/tests' - - if (not self.dirExists(deviceRoot)): - if (self.mkDir(deviceRoot) == None): - return None - - return deviceRoot - - # external function - # returns: - # success: output of unzip command - # failure: None - def unpackFile(self, filename): - devroot = self.getDeviceRoot() - if (devroot == None): - return None - - dir = '' - parts = filename.split('/') - if (len(parts) > 1): - if self.fileExists(filename): - dir = '/'.join(parts[:-1]) - elif self.fileExists('/' + filename): - dir = '/' + filename - elif self.fileExists(devroot + '/' + filename): - dir = devroot + '/' + filename - else: - return None - - try: - data = self.verifySendCMD(['cd ' + dir, 'unzp ' + filename]) - except(DMError): - return None - - return data - - # external function - # returns: - # success: status from test agent - # failure: None - def reboot(self, ipAddr=None, port=30000): - cmd = 'rebt' - - if (self.debug > 3): print "INFO: sending rebt command" - callbacksvrstatus = None - - if (ipAddr is not None): - #create update.info file: - try: - destname = '/data/data/com.mozilla.SUTAgentAndroid/files/update.info' - data = "%s,%s\rrebooting\r" % (ipAddr, port) - self.verifySendCMD(['push ' + destname + ' ' + str(len(data)) + '\r\n', data], newline = False) - except(DMError): - return None - - ip, port = self.getCallbackIpAndPort(ipAddr, port) - cmd += " %s %s" % (ip, port) - # Set up our callback server - callbacksvr = callbackServer(ip, port, self.debug) - - try: - status = self.verifySendCMD([cmd]) - except(DMError): - return None - - if (ipAddr is not None): - status = callbacksvr.disconnect() - - if (self.debug > 3): print "INFO: rebt- got status back: " + str(status) - return status - - # Returns information about the device: - # Directive indicates the information you want to get, your choices are: - # os - name of the os - # id - unique id of the device - # uptime - uptime of the device - # systime - system time of the device - # screen - screen resolution - # memory - memory stats - # process - list of running processes (same as ps) - # disk - total, free, available bytes on disk - # power - power status (charge, battery temp) - # all - all of them - or call it with no parameters to get all the information - # returns: - # success: dict of info strings by directive name - # failure: {} - def getInfo(self, directive=None): - data = None - result = {} - collapseSpaces = re.compile(' +') - - directives = ['os', 'id','uptime','systime','screen','memory','process', - 'disk','power'] - if (directive in directives): - directives = [directive] - - for d in directives: - data = self.verifySendCMD(['info ' + d]) - if (data is None): - continue - data = self.stripPrompt(data) - data = collapseSpaces.sub(' ', data) - result[d] = data.split('\n') - - # Get rid of any 0 length members of the arrays - for k, v in result.iteritems(): - result[k] = filter(lambda x: x != '', result[k]) - - # Format the process output - if 'process' in result: - proclist = [] - for l in result['process']: - if l: - proclist.append(l.split('\t')) - result['process'] = proclist - - if (self.debug >= 3): print "results: " + str(result) - return result - - """ - Installs the application onto the device - Application bundle - path to the application bundle on the device - Destination - destination directory of where application should be - installed to (optional) - Returns None for success, or output if known failure - """ - # external function - # returns: - # success: output from agent for inst command - # failure: None - def installApp(self, appBundlePath, destPath=None): - cmd = 'inst ' + appBundlePath - if destPath: - cmd += ' ' + destPath - try: - data = self.verifySendCMD([cmd]) - except(DMError): - return None - - f = re.compile('Failure') - for line in data.split(): - if (f.match(line)): - return data - return None - - """ - Uninstalls the named application from device and causes a reboot. - Takes an optional argument of installation path - the path to where the application - was installed. - Returns True, but it doesn't mean anything other than the command was sent, - the reboot happens and we don't know if this succeeds or not. - """ - # external function - # returns: - # success: True - # failure: None - def uninstallAppAndReboot(self, appName, installPath=None): - cmd = 'uninst ' + appName - if installPath: - cmd += ' ' + installPath - try: - data = self.verifySendCMD([cmd]) - except(DMError): - return None - - if (self.debug > 3): print "uninstallAppAndReboot: " + str(data) - return True - - """ - Updates the application on the device. - Application bundle - path to the application bundle on the device - Process name of application - used to end the process if the applicaiton is - currently running - Destination - Destination directory to where the application should be - installed (optional) - ipAddr - IP address to await a callback ping to let us know that the device has updated - properly - defaults to current IP. - port - port to await a callback ping to let us know that the device has updated properly - defaults to 30000, and counts up from there if it finds a conflict - Returns True if succeeds, False if not - """ - # external function - # returns: - # success: text status from command or callback server - # failure: None - def updateApp(self, appBundlePath, processName=None, destPath=None, ipAddr=None, port=30000): - status = None - cmd = 'updt ' - if (processName == None): - # Then we pass '' for processName - cmd += "'' " + appBundlePath - else: - cmd += processName + ' ' + appBundlePath - - if (destPath): - cmd += " " + destPath - - if (ipAddr is not None): - ip, port = self.getCallbackIpAndPort(ipAddr, port) - cmd += " %s %s" % (ip, port) - # Set up our callback server - callbacksvr = callbackServer(ip, port, self.debug) - - if (self.debug >= 3): print "INFO: updateApp using command: " + str(cmd) - - try: - status = self.verifySendCMD([cmd]) - except(DMError): - return None - - if ipAddr is not None: - status = callbacksvr.disconnect() - - if (self.debug >= 3): print "INFO: updateApp: got status back: " + str(status) - - return status - - """ - return the current time on the device - """ - # external function - # returns: - # success: time in ms - # failure: None - def getCurrentTime(self): - try: - data = self.verifySendCMD(['clok']) - except(DMError): - return None - - return self.stripPrompt(data).strip('\n') - - """ - Connect the ipaddress and port for a callback ping. Defaults to current IP address - And ports starting at 30000. - NOTE: the detection for current IP address only works on Linux! - """ - # external function - # returns: - # success: output of unzip command - # failure: None - def unpackFile(self, filename): - devroot = self.getDeviceRoot() - if (devroot == None): - return None - - dir = '' - parts = filename.split('/') - if (len(parts) > 1): - if self.fileExists(filename): - dir = '/'.join(parts[:-1]) - elif self.fileExists('/' + filename): - dir = '/' + filename - elif self.fileExists(devroot + '/' + filename): - dir = devroot + '/' + filename - else: - return None - - try: - data = self.verifySendCMD(['cd ' + dir, 'unzp ' + filename]) - except(DMError): - return None - - return data - - def getCallbackIpAndPort(self, aIp, aPort): - ip = aIp - nettools = NetworkTools() - if (ip == None): - ip = nettools.getLanIp() - if (aPort != None): - port = nettools.findOpenPort(ip, aPort) - else: - port = nettools.findOpenPort(ip, 30000) - return ip, port - - """ - Returns a properly formatted env string for the agent. - Input - env, which is either None, '', or a dict - Output - a quoted string of the form: '"envvar1=val1,envvar2=val2..."' - If env is None or '' return '' (empty quoted string) - """ - def formatEnvString(self, env): - if (env == None or env == ''): - return '' - - retVal = '"%s"' % ','.join(map(lambda x: '%s=%s' % (x[0], x[1]), env.iteritems())) - if (retVal == '""'): - return '' - - return retVal - - """ - adjust the screen resolution on the device, REBOOT REQUIRED - NOTE: this only works on a tegra ATM - success: True - failure: False - - supported resolutions: 640x480, 800x600, 1024x768, 1152x864, 1200x1024, 1440x900, 1680x1050, 1920x1080 - """ - def adjustResolution(self, width=1680, height=1050, type='hdmi'): - if self.getInfo('os')['os'][0].split()[0] != 'harmony-eng': - if (self.debug >= 2): print "WARNING: unable to adjust screen resolution on non Tegra device" - return False - - results = self.getInfo('screen') - parts = results['screen'][0].split(':') - if (self.debug >= 3): print "INFO: we have a current resolution of %s, %s" % (parts[1].split()[0], parts[2].split()[0]) - - #verify screen type is valid, and set it to the proper value (https://bugzilla.mozilla.org/show_bug.cgi?id=632895#c4) - screentype = -1 - if (type == 'hdmi'): - screentype = 5 - elif (type == 'vga' or type == 'crt'): - screentype = 3 - else: - return False - - #verify we have numbers - if not (isinstance(width, int) and isinstance(height, int)): - return False - - if (width < 100 or width > 9999): - return False - - if (height < 100 or height > 9999): - return False - - if (self.debug >= 3): print "INFO: adjusting screen resolution to %s, %s and rebooting" % (width, height) - try: - self.verifySendCMD(["exec setprop persist.tegra.dpy%s.mode.width %s" % (screentype, width)]) - self.verifySendCMD(["exec setprop persist.tegra.dpy%s.mode.height %s" % (screentype, height)]) - except(DMError): - return False - - return True - -gCallbackData = '' - -class myServer(SocketServer.TCPServer): - allow_reuse_address = True - -class callbackServer(): - def __init__(self, ip, port, debuglevel): - global gCallbackData - if (debuglevel >= 1): print "DEBUG: gCallbackData is: %s on port: %s" % (gCallbackData, port) - gCallbackData = '' - self.ip = ip - self.port = port - self.connected = False - self.debug = debuglevel - if (self.debug >= 3): print "Creating server with " + str(ip) + ":" + str(port) - self.server = myServer((ip, port), self.myhandler) - self.server_thread = Thread(target=self.server.serve_forever) - self.server_thread.setDaemon(True) - self.server_thread.start() - - def disconnect(self, step = 60, timeout = 600): - t = 0 - if (self.debug >= 3): print "Calling disconnect on callback server" - while t < timeout: - if (gCallbackData): - # Got the data back - if (self.debug >= 3): print "Got data back from agent: " + str(gCallbackData) - break - else: - if (self.debug >= 0): print '.', - time.sleep(step) - t += step - - try: - if (self.debug >= 3): print "Shutting down server now" - self.server.shutdown() - except: - if (self.debug >= 1): print "Unable to shutdown callback server - check for a connection on port: " + str(self.port) - - #sleep 1 additional step to ensure not only we are online, but all our services are online - time.sleep(step) - return gCallbackData - - class myhandler(SocketServer.BaseRequestHandler): - def handle(self): - global gCallbackData - gCallbackData = self.request.recv(1024) - #print "Callback Handler got data: " + str(gCallbackData) - self.request.send("OK") - diff --git a/testing/mozbase/mozdevice/setup.py b/testing/mozbase/mozdevice/setup.py deleted file mode 100644 index 91b368bc4557..000000000000 --- a/testing/mozbase/mozdevice/setup.py +++ /dev/null @@ -1,67 +0,0 @@ -# ***** 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 mozdevice. -# -# The Initial Developer of the Original Code is -# The Mozilla Foundation. -# Portions created by the Initial Developer are Copyright (C) 2011 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Will Lachance -# -# 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 os -from setuptools import setup, find_packages - -version = '0.1' - -# take description from README -here = os.path.dirname(os.path.abspath(__file__)) -try: - description = file(os.path.join(here, 'README.md')).read() -except (OSError, IOError): - description = '' - -setup(name='mozdevice', - version=version, - description="Mozilla-authored device management", - long_description=description, - classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers - keywords='', - author='Mozilla Automation and Testing Team', - author_email='tools@lists.mozilla.com', - url='http://github.com/mozilla/mozbase', - license='MPL', - packages=find_packages(exclude=['ez_setup', 'examples', 'tests']), - include_package_data=True, - zip_safe=False, - install_requires=[], - entry_points=""" - # -*- Entry points: -*- - """, - )