Bug 818080 - Bump mozdevice version to 0.16;r=ahal
authorWilliam Lachance <wlachance@mozilla.com>
Wed, 05 Dec 2012 12:17:38 -0500
changeset 115104 5488aa7e3fc3855cb481addac7e8f9532e277d72
parent 115103 51cbdd0f1ba456f204acb55eee400bb0a008e108
child 115105 61b7d462bc9e49130dd16f442f8d31930cc68dc8
push id23973
push useremorley@mozilla.com
push dateThu, 06 Dec 2012 10:04:18 +0000
treeherdermozilla-central@ddda5400c826 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersahal
bugs818080
milestone20.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 818080 - Bump mozdevice version to 0.16;r=ahal
testing/mozbase/mozdevice/mozdevice/devicemanager.py
testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py
testing/mozbase/mozdevice/mozdevice/dmcli.py
testing/mozbase/mozdevice/setup.py
--- a/testing/mozbase/mozdevice/mozdevice/devicemanager.py
+++ b/testing/mozbase/mozdevice/mozdevice/devicemanager.py
@@ -57,17 +57,17 @@ class DeviceManager:
         timeout - specified in seconds, defaults to 'default_timeout'
         root - Specifies whether command requires root privileges
         """
         buf = StringIO.StringIO()
         retval = self.shell(cmd, buf, env=env, cwd=cwd, timeout=timeout, root=root)
         output = str(buf.getvalue()[0:-1]).rstrip()
         buf.close()
         if retval != 0:
-            raise DMError("Non-zero return code for command: %s (output: '%s', retval: '%i')" % (cmd, output, retval))
+            raise DMError("Non-zero return code for command: %s (output: '%s', retval: '%s')" % (cmd, output, retval))
         return output
 
     @abstractmethod
     def pushFile(self, localname, destname):
         """
         Copies localname from the host to destname on the device
         """
 
@@ -77,24 +77,26 @@ class DeviceManager:
         Creates a single directory on the device file system
         """
 
     def mkDirs(self, filename):
         """
         Make directory structure on the device
         WARNING: does not create last part of the path
         """
-        parts = filename.split('/')
-        name = ""
-        for part in parts:
-            if (part == parts[-1]):
-                break
-            if (part != ""):
-                name += '/' + part
-                self.mkDir(name) # mkDir will check previous existence
+        dirParts = filename.rsplit('/', 1)
+        if not self.dirExists(dirParts[0]):
+            parts = filename.split('/')
+            name = ""
+            for part in parts:
+                if part == parts[-1]:
+                    break
+                if part != "":
+                    name += '/' + part
+                    self.mkDir(name) # mkDir will check previous existence
 
     @abstractmethod
     def pushDir(self, localDir, remoteDir):
         """
         Push localDir from host to remoteDir on the device
         """
 
     @abstractmethod
--- a/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py
+++ b/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py
@@ -118,33 +118,39 @@ class DeviceManagerADB(DeviceManager):
             envstr = '; '.join(map(lambda x: 'export %s=%s' % (x[0], x[1]), env.iteritems()))
             cmdline = envstr + "; " + cmdline
 
         # all output should be in stdout
         args=[self._adbPath]
         if self._deviceSerial:
             args.extend(['-s', self._deviceSerial])
         args.extend(["shell", cmdline])
-        proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+
+        procOut = tempfile.SpooledTemporaryFile()
+        procErr = tempfile.SpooledTemporaryFile()
+        proc = subprocess.Popen(args, stdout=procOut, stderr=procErr)
 
         if not timeout:
             # We are asserting that all commands will complete in this time unless otherwise specified
             timeout = self.default_timeout
 
         timeout = int(timeout)
         start_time = time.time()
         ret_code = proc.poll()
         while ((time.time() - start_time) <= timeout) and ret_code == None:
             time.sleep(self._pollingInterval)
             ret_code = proc.poll()
         if ret_code == None:
             proc.kill()
             raise DMError("Timeout exceeded for shell call")
-        (stdout, stderr) = proc.communicate()
-        outputfile.write(stdout.rstrip('\n'))
+
+        procOut.seek(0)
+        outputfile.write(procOut.read().rstrip('\n'))
+        procOut.close()
+        procErr.close()
 
         lastline = _pop_last_line(outputfile)
         if lastline:
             m = re.search('([0-9]+)', lastline)
             if m:
                 return_code = m.group(1)
                 outputfile.seek(-2, 2)
                 outputfile.truncate() # truncate off the return code
--- a/testing/mozbase/mozdevice/mozdevice/dmcli.py
+++ b/testing/mozbase/mozdevice/mozdevice/dmcli.py
@@ -1,27 +1,30 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this file,
 # You can obtain one at http://mozilla.org/MPL/2.0/.
 
 """
 Command-line client to control a device
 """
 
+import errno
 import os
 import posixpath
 import StringIO
 import sys
 import textwrap
 import mozdevice
 from optparse import OptionParser
 
 class DMCli(object):
 
-    def __init__(self, args=sys.argv[1:]):
+    def __init__(self):
+        # a value of None for 'max_args' means there is no limit to the number
+        # of arguments.  'min_args' should always have an integer value >= 0.
         self.commands = { 'install': { 'function': self.install,
                                        'min_args': 1,
                                        'max_args': 1,
                                        'help_args': '<file>',
                                        'help': 'push this package file to the device and install it' },
                           'killapp': { 'function': self.killapp,
                                        'min_args': 1,
                                        'max_args': 1,
@@ -43,41 +46,59 @@ class DMCli(object):
                                     'help_args': '<local> [remote]',
                                     'help': 'copy file/dir from device' },
                           'shell': { 'function': self.shell,
                                      'min_args': 1,
                                      'max_args': None,
                                      'help_args': '<command>',
                                      'help': 'run shell command on device' },
                           'info': { 'function': self.getinfo,
-                                    'min_args': None,
+                                    'min_args': 0,
                                     'max_args': 1,
                                     'help_args': '[os|id|uptime|systime|screen|memory|processes]',
                                     'help': 'get information on a specified '
                                     'aspect of the device (if no argument '
                                     'given, print all available information)'
                                     },
                           'ps': { 'function': self.processlist,
-                                    'min_args': None,
+                                    'min_args': 0,
                                     'max_args': 0,
                                     'help_args': '',
                                     'help': 'get information on running processes on device'
                                 },
+                          'logcat' : { 'function': self.logcat,
+                                       'min_args': 0,
+                                       'max_args': 0,
+                                       'help_args': '',
+                                       'help': 'get logcat from device'
+                                },
                           'ls': { 'function': self.listfiles,
                                   'min_args': 1,
                                   'max_args': 1,
                                   'help_args': '<remote>',
                                   'help': 'list files on device'
                                 },
                           'rm': { 'function': lambda f: self.dm.removeFile(f),
                                     'min_args': 1,
                                     'max_args': 1,
                                     'help_args': '<remote>',
                                     'help': 'remove file from device'
                                 },
+                          'isdir': { 'function': self.isdir,
+                                     'min_args': 1,
+                                     'max_args': 1,
+                                     'help_args': '<remote>',
+                                     'help': 'print if remote file is a directory'
+                                },
+                          'mkdir': { 'function': lambda d: self.dm.mkDir(d),
+                                     'min_args': 1,
+                                     'max_args': 1,
+                                     'help_args': '<remote>',
+                                     'help': 'makes a directory on device'
+                                },
                           'rmdir': { 'function': lambda d: self.dm.removeDir(d),
                                     'min_args': 1,
                                     'max_args': 1,
                                     'help_args': '<remote>',
                                     'help': 'recursively remove directory from device'
                                 },
                           'screencap': { 'function': lambda f: self.dm.saveScreenshot(f),
                                           'min_args': 1,
@@ -95,41 +116,48 @@ class DMCli(object):
                                           initial_indent="  ",
                                           subsequent_indent="      ")
                             for (cmdname, cmd) in 
                             sorted(self.commands.iteritems())])
 
         self.parser = OptionParser(usage)
         self.add_options(self.parser)
 
+
+    def run(self, args=sys.argv[1:]):
         (self.options, self.args) = self.parser.parse_args(args)
 
         if len(self.args) < 1:
             self.parser.error("must specify command")
 
         if self.options.dmtype == "sut" and not self.options.host and \
                 not self.options.hwid:
             self.parser.error("Must specify device ip in TEST_DEVICE or "
                               "with --host option with SUT")
 
         (command_name, command_args) = (self.args[0], self.args[1:])
         if command_name not in self.commands:
             self.parser.error("Invalid command. Valid commands: %s" %
                               " ".join(self.commands.keys()))
 
         command = self.commands[command_name]
-        if command['min_args'] and len(command_args) < command['min_args'] or \
-                command['max_args'] and len(command_args) > command['max_args']:
+        if (len(command_args) < command['min_args'] or
+            (command['max_args'] is not None and len(command_args) > 
+             command['max_args'])):
             self.parser.error("Wrong number of arguments")
 
         self.dm = self.getDevice(dmtype=self.options.dmtype,
                                  hwid=self.options.hwid,
                                  host=self.options.host,
                                  port=self.options.port)
-        command['function'](*command_args)
+        ret = command['function'](*command_args)
+        if ret is None:
+            ret = 0
+
+        sys.exit(ret)
 
     def add_options(self, parser):
         parser.add_option("-v", "--verbose", action="store_true",
                           dest="verbose",
                           help="Verbose output from DeviceManager",
                           default=False)
         parser.add_option("--host", action="store",
                           type="string", dest="host",
@@ -227,24 +255,36 @@ class DMCli(object):
             elif not directive and not infoitem:
                 print "%s:" % infokey.upper()
             elif not directive:
                 for line in infoitem:
                     print "%s: %s" % (infokey.upper(), line)
             else:
                 print "%s" % "\n".join(infoitem)
 
+    def logcat(self):
+        print ''.join(self.dm.getLogcat())
+
     def processlist(self):
         pslist = self.dm.getProcessList()
         for ps in pslist:
             print " ".join(str(i) for i in ps)
 
     def listfiles(self, dir):
         filelist = self.dm.listFiles(dir)
         for file in filelist:
             print file
 
+    def isdir(self, file):
+        if self.dm.dirExists(file):
+            print "TRUE"
+            return 0
+
+        print "FALSE"
+        return errno.ENOTDIR
+
 def cli(args=sys.argv[1:]):
     # process the command line
-    cli = DMCli(args)
+    cli = DMCli()
+    cli.run(args)
 
 if __name__ == '__main__':
     cli()
--- a/testing/mozbase/mozdevice/setup.py
+++ b/testing/mozbase/mozdevice/setup.py
@@ -1,16 +1,16 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this file,
 # You can obtain one at http://mozilla.org/MPL/2.0/.
 
 import os
 from setuptools import setup
 
-PACKAGE_VERSION = '0.15'
+PACKAGE_VERSION = '0.16'
 
 # 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 = ''