summaryrefslogtreecommitdiffstats
path: root/testing/mozbase/mozdevice/mozdevice/dmcli.py
diff options
context:
space:
mode:
Diffstat (limited to 'testing/mozbase/mozdevice/mozdevice/dmcli.py')
-rw-r--r--testing/mozbase/mozdevice/mozdevice/dmcli.py382
1 files changed, 382 insertions, 0 deletions
diff --git a/testing/mozbase/mozdevice/mozdevice/dmcli.py b/testing/mozbase/mozdevice/mozdevice/dmcli.py
new file mode 100644
index 000000000..7ba65e842
--- /dev/null
+++ b/testing/mozbase/mozdevice/mozdevice/dmcli.py
@@ -0,0 +1,382 @@
+# 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 logging
+import os
+import posixpath
+import StringIO
+import sys
+import mozdevice
+import mozlog
+import argparse
+
+
+class DMCli(object):
+
+ def __init__(self):
+ self.commands = {'deviceroot': {'function': self.deviceroot,
+ 'help': 'get device root directory for storing temporary '
+ 'files'},
+ 'install': {'function': self.install,
+ 'args': [{'name': 'file'}],
+ 'help': 'push this package file to the device'
+ ' and install it'},
+ 'uninstall': {'function': self.uninstall,
+ 'args': [{'name': 'packagename'}],
+ 'help': 'uninstall the named app from the device'},
+ 'killapp': {'function': self.kill,
+ 'args': [{'name': 'process_name', 'nargs': '*'}],
+ 'help': 'kills any processes with name(s) on device'},
+ 'launchapp': {'function': self.launchapp,
+ 'args': [{'name': 'appname'},
+ {'name': 'activity_name'},
+ {'name': '--intent',
+ 'action': 'store',
+ 'default': 'android.intent.action.VIEW'},
+ {'name': '--url',
+ 'action': 'store'},
+ {'name': '--no-fail-if-running',
+ 'action': 'store_true',
+ 'help': 'Don\'t fail if application is'
+ ' already running'}
+ ],
+ 'help': 'launches application on device'},
+ 'listapps': {'function': self.listapps,
+ 'help': 'list applications on device'},
+ 'push': {'function': self.push,
+ 'args': [{'name': 'local_file'},
+ {'name': 'remote_file'}
+ ],
+ 'help': 'copy file/dir to device'},
+ 'pull': {'function': self.pull,
+ 'args': [{'name': 'local_file'},
+ {'name': 'remote_file', 'nargs': '?'}],
+ 'help': 'copy file/dir from device'},
+ 'shell': {'function': self.shell,
+ 'args': [{'name': 'command', 'nargs': argparse.REMAINDER},
+ {'name': '--root', 'action': 'store_true',
+ 'help': 'Run command as root'}],
+ 'help': 'run shell command on device'},
+ 'info': {'function': self.getinfo,
+ 'args': [{'name': 'directive', 'nargs': '?'}],
+ 'help': 'get information on specified '
+ 'aspect of the device (if no argument '
+ 'given, print all available information)'
+ },
+ 'ps': {'function': self.processlist,
+ 'help': 'get information on running processes on device'
+ },
+ 'logcat': {'function': self.logcat,
+ 'help': 'get logcat from device'
+ },
+ 'ls': {'function': self.listfiles,
+ 'args': [{'name': 'remote_dir'}],
+ 'help': 'list files on device'
+ },
+ 'rm': {'function': self.removefile,
+ 'args': [{'name': 'remote_file'}],
+ 'help': 'remove file from device'
+ },
+ 'isdir': {'function': self.isdir,
+ 'args': [{'name': 'remote_dir'}],
+ 'help': 'print if remote file is a directory'
+ },
+ 'mkdir': {'function': self.mkdir,
+ 'args': [{'name': 'remote_dir'}],
+ 'help': 'makes a directory on device'
+ },
+ 'rmdir': {'function': self.rmdir,
+ 'args': [{'name': 'remote_dir'}],
+ 'help': 'recursively remove directory from device'
+ },
+ 'screencap': {'function': self.screencap,
+ 'args': [{'name': 'png_file'}],
+ 'help': 'capture screenshot of device in action'
+ },
+ 'sutver': {'function': self.sutver,
+ 'help': 'SUTAgent\'s product name and version (SUT only)'
+ },
+ 'clearlogcat': {'function': self.clearlogcat,
+ 'help': 'clear the logcat'
+ },
+ 'reboot': {'function': self.reboot,
+ 'help': 'reboot the device',
+ 'args': [{'name': '--wait',
+ 'action': 'store_true',
+ 'help': 'Wait for device to come back up'
+ ' before exiting'}]
+
+ },
+ 'isfile': {'function': self.isfile,
+ 'args': [{'name': 'remote_file'}],
+ 'help': 'check whether a file exists on the device'
+ },
+ 'launchfennec': {'function': self.launchfennec,
+ 'args': [{'name': 'appname'},
+ {'name': '--intent', 'action': 'store',
+ 'default': 'android.intent.action.VIEW'},
+ {'name': '--url', 'action': 'store'},
+ {'name': '--extra-args', 'action': 'store'},
+ {'name': '--mozenv', 'action': 'store',
+ 'help': 'Gecko environment variables to set'
+ ' in "KEY1=VAL1 KEY2=VAL2" format'},
+ {'name': '--no-fail-if-running',
+ 'action': 'store_true',
+ 'help': 'Don\'t fail if application is '
+ 'already running'}
+ ],
+ 'help': 'launch fennec'
+ },
+ 'getip': {'function': self.getip,
+ 'args': [{'name': 'interface', 'nargs': '*'}],
+ 'help': 'get the ip address of the device'
+ }
+ }
+
+ self.parser = argparse.ArgumentParser()
+ self.add_options(self.parser)
+ self.add_commands(self.parser)
+ mozlog.commandline.add_logging_group(self.parser)
+
+ def run(self, args=sys.argv[1:]):
+ args = self.parser.parse_args()
+
+ mozlog.commandline.setup_logging(
+ 'mozdevice', args, {'mach': sys.stdout})
+
+ if args.dmtype == "sut" and not args.host and not args.hwid:
+ self.parser.error("Must specify device ip in TEST_DEVICE or "
+ "with --host option with SUT")
+
+ self.dm = self.getDevice(dmtype=args.dmtype, hwid=args.hwid,
+ host=args.host, port=args.port,
+ verbose=args.verbose)
+
+ ret = args.func(args)
+ if ret is None:
+ ret = 0
+
+ sys.exit(ret)
+
+ def add_options(self, parser):
+ parser.add_argument("-v", "--verbose", action="store_true",
+ help="Verbose output from DeviceManager",
+ default=bool(os.environ.get('VERBOSE')))
+ parser.add_argument("--host", action="store",
+ help="Device hostname (only if using TCP/IP, "
+ "defaults to TEST_DEVICE environment "
+ "variable if present)",
+ default=os.environ.get('TEST_DEVICE'))
+ parser.add_argument("-p", "--port", action="store",
+ type=int,
+ help="Custom device port (if using SUTAgent or "
+ "adb-over-tcp)", default=None)
+ parser.add_argument("-m", "--dmtype", action="store",
+ help="DeviceManager type (adb or sut, defaults "
+ "to DM_TRANS environment variable, if "
+ "present, or adb)",
+ default=os.environ.get('DM_TRANS', 'adb'))
+ parser.add_argument("-d", "--hwid", action="store",
+ help="HWID", default=None)
+ parser.add_argument("--package-name", action="store",
+ help="Packagename (if using DeviceManagerADB)",
+ default=None)
+
+ def add_commands(self, parser):
+ subparsers = parser.add_subparsers(title="Commands", metavar="<command>")
+ for (commandname, commandprops) in sorted(self.commands.iteritems()):
+ subparser = subparsers.add_parser(commandname, help=commandprops['help'])
+ if commandprops.get('args'):
+ for arg in commandprops['args']:
+ # this is more elegant but doesn't work in python 2.6
+ # (which we still use on tbpl @ mozilla where we install
+ # this package)
+ # kwargs = { k: v for k,v in arg.items() if k is not 'name' }
+ kwargs = {}
+ for (k, v) in arg.items():
+ if k is not 'name':
+ kwargs[k] = v
+ subparser.add_argument(arg['name'], **kwargs)
+ subparser.set_defaults(func=commandprops['function'])
+
+ def getDevice(self, dmtype="adb", hwid=None, host=None, port=None,
+ packagename=None, verbose=False):
+ '''
+ Returns a device with the specified parameters
+ '''
+ logLevel = logging.ERROR
+ if verbose:
+ logLevel = logging.DEBUG
+
+ if hwid:
+ return mozdevice.DroidConnectByHWID(hwid, logLevel=logLevel)
+
+ if dmtype == "adb":
+ if host and not port:
+ port = 5555
+ return mozdevice.DroidADB(packageName=packagename,
+ host=host, port=port,
+ logLevel=logLevel)
+ elif dmtype == "sut":
+ if not host:
+ self.parser.error("Must specify host with SUT!")
+ if not port:
+ port = 20701
+ return mozdevice.DroidSUT(host=host, port=port,
+ logLevel=logLevel)
+ else:
+ self.parser.error("Unknown device manager type: %s" % type)
+
+ def deviceroot(self, args):
+ print self.dm.deviceRoot
+
+ def push(self, args):
+ (src, dest) = (args.local_file, args.remote_file)
+ if os.path.isdir(src):
+ self.dm.pushDir(src, dest)
+ else:
+ dest_is_dir = dest[-1] == '/' or self.dm.dirExists(dest)
+ dest = posixpath.normpath(dest)
+ if dest_is_dir:
+ dest = posixpath.join(dest, os.path.basename(src))
+ self.dm.pushFile(src, dest)
+
+ def pull(self, args):
+ (src, dest) = (args.local_file, args.remote_file)
+ if not self.dm.fileExists(src):
+ print 'No such file or directory'
+ return
+ if not dest:
+ dest = posixpath.basename(src)
+ if self.dm.dirExists(src):
+ self.dm.getDirectory(src, dest)
+ else:
+ self.dm.getFile(src, dest)
+
+ def install(self, args):
+ basename = os.path.basename(args.file)
+ app_path_on_device = posixpath.join(self.dm.deviceRoot,
+ basename)
+ self.dm.pushFile(args.file, app_path_on_device)
+ self.dm.installApp(app_path_on_device)
+
+ def uninstall(self, args):
+ self.dm.uninstallApp(args.packagename)
+
+ def launchapp(self, args):
+ self.dm.launchApplication(args.appname, args.activity_name,
+ args.intent, url=args.url,
+ failIfRunning=(not args.no_fail_if_running))
+
+ def listapps(self, args):
+ for app in self.dm.getInstalledApps():
+ print app
+
+ def stopapp(self, args):
+ self.dm.stopApplication(args.appname)
+
+ def kill(self, args):
+ for name in args.process_name:
+ self.dm.killProcess(name)
+
+ def shell(self, args):
+ buf = StringIO.StringIO()
+ self.dm.shell(args.command, buf, root=args.root)
+ print str(buf.getvalue()[0:-1]).rstrip()
+
+ def getinfo(self, args):
+ info = self.dm.getInfo(directive=args.directive)
+ for (infokey, infoitem) in sorted(info.iteritems()):
+ if infokey == "process":
+ pass # skip process list: get that through ps
+ elif args.directive is None:
+ print "%s: %s" % (infokey.upper(), infoitem)
+ else:
+ print infoitem
+
+ def logcat(self, args):
+ print ''.join(self.dm.getLogcat())
+
+ def clearlogcat(self, args):
+ self.dm.recordLogcat()
+
+ def reboot(self, args):
+ self.dm.reboot(wait=args.wait)
+
+ def processlist(self, args):
+ pslist = self.dm.getProcessList()
+ for ps in pslist:
+ print " ".join(str(i) for i in ps)
+
+ def listfiles(self, args):
+ filelist = self.dm.listFiles(args.remote_dir)
+ for file in filelist:
+ print file
+
+ def removefile(self, args):
+ self.dm.removeFile(args.remote_file)
+
+ def isdir(self, args):
+ if self.dm.dirExists(args.remote_dir):
+ print "TRUE"
+ return
+
+ print "FALSE"
+ return errno.ENOTDIR
+
+ def mkdir(self, args):
+ self.dm.mkDir(args.remote_dir)
+
+ def rmdir(self, args):
+ self.dm.removeDir(args.remote_dir)
+
+ def screencap(self, args):
+ self.dm.saveScreenshot(args.png_file)
+
+ def sutver(self, args):
+ if args.dmtype == 'sut':
+ print '%s Version %s' % (self.dm.agentProductName,
+ self.dm.agentVersion)
+ else:
+ print 'Must use SUT transport to get SUT version.'
+
+ def isfile(self, args):
+ if self.dm.fileExists(args.remote_file):
+ print "TRUE"
+ return
+ print "FALSE"
+ return errno.ENOENT
+
+ def launchfennec(self, args):
+ mozEnv = None
+ if args.mozenv:
+ mozEnv = {}
+ keyvals = args.mozenv.split()
+ for keyval in keyvals:
+ (key, _, val) = keyval.partition("=")
+ mozEnv[key] = val
+ self.dm.launchFennec(args.appname, intent=args.intent,
+ mozEnv=mozEnv,
+ extraArgs=args.extra_args, url=args.url,
+ failIfRunning=(not args.no_fail_if_running))
+
+ def getip(self, args):
+ if args.interface:
+ print(self.dm.getIP(args.interface))
+ else:
+ print(self.dm.getIP())
+
+
+def cli(args=sys.argv[1:]):
+ # process the command line
+ cli = DMCli()
+ cli.run(args)
+
+if __name__ == '__main__':
+ cli()