Browse Source

Re-instate removals in 1440714-17

Ian Neal 3 months ago
parent
commit
7641139019

+ 2 - 3
mozilla-release/patches/1428718-1-72a1.patch

@@ -2,7 +2,7 @@
 # User Edwin Takahashi <egao@mozilla.com>
 # Date 1574710997 0
 # Node ID 8a4fb397fa5cb696042eb34c75ca7cc25d675990
-# Parent  ee6fa734036905ab752a97115a5583efae63f3dc
+# Parent  87e32c17d3208e7815b22ffa2ddc0764b290ef26
 Bug 1428718 - remove moztest/moztest/output directory r=gbrown,mozbase
 
 Changes:
@@ -99,7 +99,7 @@ diff --git a/testing/mozbase/moztest/moztest/output/base.py b/testing/mozbase/mo
 deleted file mode 100644
 --- a/testing/mozbase/moztest/moztest/output/base.py
 +++ /dev/null
-@@ -1,53 +0,0 @@
+@@ -1,52 +0,0 @@
 -# 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/.
@@ -113,7 +113,6 @@ deleted file mode 100644
 -    from abc import abstractmethod
 -except ImportError:
 -    # abc is python 2.6+
--    # from https://github.com/mozilla/mozbase/blob/master/mozdevice/mozdevice/devicemanager.py
 -    def abstractmethod(method):
 -        line = method.func_code.co_firstlineno
 -        filename = method.func_code.co_filename

+ 2435 - 1
mozilla-release/patches/1440714-17-61a1.patch

@@ -2,9 +2,2443 @@
 # User Geoff Brown <gbrown@mozilla.com>
 # Date 1524760988 21600
 # Node ID ad8b8a0eb0d2e98f3dbe51979d16d3200d95fc6a
-# Parent  7bc9cdc13e72d98c1700debfea3e37aee4ab2354
+# Parent  fe3937113be1a54c091bf6db9046db6380c12fd7
 Bug 1440714 - Remove DeviceManagerADB and Droid classes from mozdevice; r=bc
 
+diff --git a/testing/mozbase/mozdevice/adb_tests/test_device_running_adb_as_root.py b/testing/mozbase/mozdevice/adb_tests/test_device_running_adb_as_root.py
+deleted file mode 100644
+--- a/testing/mozbase/mozdevice/adb_tests/test_device_running_adb_as_root.py
++++ /dev/null
+@@ -1,52 +0,0 @@
+-"""
+- This test is to test devices that adbd does not get started as root.
+- Specifically devices that have ro.secure == 1 and ro.debuggable == 1
+-
+- Running this test case requires various reboots which makes it a
+- very slow test case to run.
+-"""
+-
+-from __future__ import absolute_import, print_function
+-
+-import unittest
+-import sys
+-
+-from mozdevice import DeviceManagerADB
+-
+-
+-class TestFileOperations(unittest.TestCase):
+-
+-    def setUp(self):
+-        dm = DeviceManagerADB()
+-        dm.reboot(wait=True)
+-
+-    def test_run_adb_as_root_parameter(self):
+-        dm = DeviceManagerADB()
+-        self.assertTrue(dm.processInfo("adbd")[2] != "root")
+-        dm = DeviceManagerADB(runAdbAsRoot=True)
+-        self.assertTrue(dm.processInfo("adbd")[2] == "root")
+-
+-    def test_after_reboot_adb_runs_as_root(self):
+-        dm = DeviceManagerADB(runAdbAsRoot=True)
+-        self.assertTrue(dm.processInfo("adbd")[2] == "root")
+-        dm.reboot(wait=True)
+-        self.assertTrue(dm.processInfo("adbd")[2] == "root")
+-
+-    def tearDown(self):
+-        dm = DeviceManagerADB()
+-        dm.reboot()
+-
+-
+-if __name__ == "__main__":
+-    dm = DeviceManagerADB()
+-    if not dm.devices():
+-        print("There are no connected adb devices")
+-        sys.exit(1)
+-    else:
+-        if not (int(dm._runCmd(["shell", "getprop", "ro.secure"]).output[0]) and
+-                int(dm._runCmd(["shell", "getprop", "ro.debuggable"]).output[0])):
+-            print("This test case is meant for devices with devices that start "
+-                  "adbd as non-root and allows for adbd to be restarted as root.")
+-            sys.exit(1)
+-
+-    unittest.main()
+diff --git a/testing/mozbase/mozdevice/adb_tests/test_devicemanagerADB.py b/testing/mozbase/mozdevice/adb_tests/test_devicemanagerADB.py
+deleted file mode 100644
+--- a/testing/mozbase/mozdevice/adb_tests/test_devicemanagerADB.py
++++ /dev/null
+@@ -1,220 +0,0 @@
+-"""
+- Info:
+-   This tests DeviceManagerADB with a real device
+-
+- Requirements:
+-   - You must have a device connected
+-      - It should be listed under 'adb devices'
+-
+- Notes:
+-   - Not all functions have been covered.
+-     In particular, functions from the parent class
+-   - No testing of properties is done
+-   - The test case are very simple and it could be
+-     done with deeper inspection of the return values
+-
+- Author(s):
+-   - Armen Zambrano <armenzg@mozilla.com>
+-
+- Functions that are not being tested:
+- - launchProcess - DEPRECATED
+- - getIP
+- - recordLogcat
+- - saveScreenshot
+- - validateDir
+- - mkDirs
+- - getDeviceRoot
+- - shellCheckOutput
+- - processExist
+-
+- I assume these functions are only useful for Android
+- - getAppRoot()
+- - updateApp()
+- - uninstallApp()
+- - uninstallAppAndReboot()
+-"""
+-from __future__ import absolute_import, print_function
+-
+-import os
+-import re
+-import socket
+-import sys
+-import tempfile
+-import unittest
+-from StringIO import StringIO
+-
+-from mozdevice import DeviceManagerADB, DMError
+-
+-
+-def find_mount_permissions(dm, mount_path):
+-    for mount_point in dm._runCmd(["shell", "mount"]).output:
+-        if mount_point.find(mount_path) > 0:
+-            return re.search('(ro|rw)(?=,)', mount_point).group(0)
+-
+-
+-class DeviceManagerADBTestCase(unittest.TestCase):
+-    tempLocalDir = "tempDir"
+-    tempLocalFile = os.path.join(tempLocalDir, "tempfile.txt")
+-    tempRemoteDir = None
+-    tempRemoteFile = None
+-    tempRemoteSystemFile = None
+-
+-    def setUp(self):
+-        self.assertTrue(find_mount_permissions(self.dm, "/system"), "ro")
+-
+-        self.assertTrue(os.path.exists(self.tempLocalDir))
+-        self.assertTrue(os.path.exists(self.tempLocalFile))
+-
+-        if self.dm.fileExists(self.tempRemoteFile):
+-            self.dm.removeFile(self.tempRemoteFile)
+-        self.assertFalse(self.dm.fileExists(self.tempRemoteFile))
+-
+-        if self.dm.fileExists(self.tempRemoteSystemFile):
+-            self.dm.removeFile(self.tempRemoteSystemFile)
+-
+-        self.assertTrue(self.dm.dirExists(self.tempRemoteDir))
+-
+-    @classmethod
+-    def setUpClass(self):
+-        self.dm = DeviceManagerADB()
+-        if not os.path.exists(self.tempLocalDir):
+-            os.mkdir(self.tempLocalDir)
+-        if not os.path.exists(self.tempLocalFile):
+-            # Create empty file
+-            open(self.tempLocalFile, 'w').close()
+-        self.tempRemoteDir = self.dm.getTempDir()
+-        self.tempRemoteFile = os.path.join(self.tempRemoteDir,
+-                                           os.path.basename(self.tempLocalFile))
+-        self.tempRemoteSystemFile = \
+-            os.path.join("/system", os.path.basename(self.tempLocalFile))
+-
+-    @classmethod
+-    def tearDownClass(self):
+-        os.remove(self.tempLocalFile)
+-        os.rmdir(self.tempLocalDir)
+-        if self.dm.dirExists(self.tempRemoteDir):
+-            # self.tempRemoteFile will get deleted with it
+-            self.dm.removeDir(self.tempRemoteDir)
+-        if self.dm.fileExists(self.tempRemoteSystemFile):
+-            self.dm.removeFile(self.tempRemoteSystemFile)
+-
+-
+-class TestFileOperations(DeviceManagerADBTestCase):
+-
+-    def test_make_and_remove_directory(self):
+-        dir1 = os.path.join(self.tempRemoteDir, "dir1")
+-        self.assertFalse(self.dm.dirExists(dir1))
+-        self.dm.mkDir(dir1)
+-        self.assertTrue(self.dm.dirExists(dir1))
+-        self.dm.removeDir(dir1)
+-        self.assertFalse(self.dm.dirExists(dir1))
+-
+-    def test_push_and_remove_file(self):
+-        self.dm.pushFile(self.tempLocalFile, self.tempRemoteFile)
+-        self.assertTrue(self.dm.fileExists(self.tempRemoteFile))
+-        self.dm.removeFile(self.tempRemoteFile)
+-        self.assertFalse(self.dm.fileExists(self.tempRemoteFile))
+-
+-    def test_push_and_pull_file(self):
+-        self.dm.pushFile(self.tempLocalFile, self.tempRemoteFile)
+-        self.assertTrue(self.dm.fileExists(self.tempRemoteFile))
+-        self.assertFalse(os.path.exists("pulled.txt"))
+-        self.dm.getFile(self.tempRemoteFile, "pulled.txt")
+-        self.assertTrue(os.path.exists("pulled.txt"))
+-        os.remove("pulled.txt")
+-
+-    def test_push_and_pull_directory_and_list_files(self):
+-        self.dm.removeDir(self.tempRemoteDir)
+-        self.assertFalse(self.dm.dirExists(self.tempRemoteDir))
+-        self.dm.pushDir(self.tempLocalDir, self.tempRemoteDir)
+-        self.assertTrue(self.dm.dirExists(self.tempRemoteDir))
+-        response = self.dm.listFiles(self.tempRemoteDir)
+-        # The local dir that was pushed contains the tempLocalFile
+-        self.assertIn(os.path.basename(self.tempLocalFile), response)
+-        # Create a temp dir to pull to
+-        temp_dir = tempfile.mkdtemp()
+-        self.assertTrue(os.path.exists(temp_dir))
+-        self.dm.getDirectory(self.tempRemoteDir, temp_dir)
+-        self.assertTrue(os.path.exists(self.tempLocalFile))
+-
+-    def test_move_and_remove_directories(self):
+-        dir1 = os.path.join(self.tempRemoteDir, "dir1")
+-        dir2 = os.path.join(self.tempRemoteDir, "dir2")
+-
+-        self.assertFalse(self.dm.dirExists(dir1))
+-        self.dm.mkDir(dir1)
+-        self.assertTrue(self.dm.dirExists(dir1))
+-
+-        self.assertFalse(self.dm.dirExists(dir2))
+-        self.dm.moveTree(dir1, dir2)
+-        self.assertTrue(self.dm.dirExists(dir2))
+-
+-        self.dm.removeDir(dir1)
+-        self.dm.removeDir(dir2)
+-        self.assertFalse(self.dm.dirExists(dir1))
+-        self.assertFalse(self.dm.dirExists(dir2))
+-
+-    def test_push_and_remove_system_file(self):
+-        out = StringIO()
+-        self.assertTrue(find_mount_permissions(self.dm, "/system") == "ro")
+-        self.assertFalse(self.dm.fileExists(self.tempRemoteSystemFile))
+-        self.assertRaises(DMError, self.dm.pushFile, self.tempLocalFile, self.tempRemoteSystemFile)
+-        self.dm.shell(['mount', '-w', '-o', 'remount', '/system'], out)
+-        self.assertTrue(find_mount_permissions(self.dm, "/system") == "rw")
+-        self.assertFalse(self.dm.fileExists(self.tempRemoteSystemFile))
+-        self.dm.pushFile(self.tempLocalFile, self.tempRemoteSystemFile)
+-        self.assertTrue(self.dm.fileExists(self.tempRemoteSystemFile))
+-        self.dm.removeFile(self.tempRemoteSystemFile)
+-        self.assertFalse(self.dm.fileExists(self.tempRemoteSystemFile))
+-        self.dm.shell(['mount', '-r', '-o', 'remount', '/system'], out)
+-        out.close()
+-        self.assertTrue(find_mount_permissions(self.dm, "/system") == "ro")
+-
+-
+-class TestOther(DeviceManagerADBTestCase):
+-
+-    def test_get_list_of_processes(self):
+-        self.assertEquals(type(self.dm.getProcessList()), list)
+-
+-    def test_get_current_time(self):
+-        self.assertEquals(type(self.dm.getCurrentTime()), int)
+-
+-    def test_get_info(self):
+-        self.assertEquals(type(self.dm.getInfo()), dict)
+-
+-    def test_list_devices(self):
+-        self.assertEquals(len(list(self.dm.devices())), 1)
+-
+-    def test_shell(self):
+-        out = StringIO()
+-        self.dm.shell(["echo", "$COMPANY", ";", "pwd"], out,
+-                      env={"COMPANY": "Mozilla"}, cwd="/", timeout=4, root=True)
+-        output = str(out.getvalue()).rstrip().splitlines()
+-        out.close()
+-        self.assertEquals(output, ['Mozilla', '/'])
+-
+-    def test_port_forwarding(self):
+-        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+-        s.bind(("", 0))
+-        port = s.getsockname()[1]
+-        s.close()
+-        # If successful then no exception is raised
+-        self.dm.forward("tcp:%s" % port, "tcp:2828")
+-
+-    def test_port_forwarding_error(self):
+-        self.assertRaises(DMError, self.dm.forward, "", "")
+-
+-
+-if __name__ == '__main__':
+-    dm = DeviceManagerADB()
+-    if not dm.devices():
+-        print("There are no connected adb devices")
+-        sys.exit(1)
+-
+-    if find_mount_permissions(dm, "/system") == "rw":
+-        print("We've found out that /system is mounted as 'rw'. This is because the command "
+-              "'adb remount' has been run before running this test case. Please reboot the "
+-              "device and try again.")
+-        sys.exit(1)
+-
+-    unittest.main()
+diff --git a/testing/mozbase/mozdevice/mozdevice/__init__.py b/testing/mozbase/mozdevice/mozdevice/__init__.py
+--- a/testing/mozbase/mozdevice/mozdevice/__init__.py
++++ b/testing/mozbase/mozdevice/mozdevice/__init__.py
+@@ -3,15 +3,11 @@
+ # You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+ from __future__ import absolute_import
+ 
+ from .adb import ADBError, ADBRootError, ADBTimeoutError
+ from .adb import ADBProcess, ADBCommand, ADBHost, ADBDevice
+ from .adb_android import ADBAndroid
+ from .adb_b2g import ADBB2G
+-from .devicemanager import DeviceManager, DMError
+-from .devicemanagerADB import DeviceManagerADB
+-from .droid import DroidADB
+ 
+-__all__ = ['ADBError', 'ADBRootError', 'ADBTimeoutError', 'ADBProcess', 'ADBCommand', 'ADBHost',
+-           'ADBDevice', 'ADBAndroid', 'ADBB2G', 'DeviceManager', 'DMError',
+-           'DeviceManagerADB', 'DroidADB']
++__all__ = ['ADBError', 'ADBRootError', 'ADBTimeoutError',
++           'ADBProcess', 'ADBCommand', 'ADBHost', 'ADBDevice', 'ADBAndroid', 'ADBB2G']
+diff --git a/testing/mozbase/mozdevice/mozdevice/devicemanager.py b/testing/mozbase/mozdevice/mozdevice/devicemanager.py
+deleted file mode 100644
+--- a/testing/mozbase/mozdevice/mozdevice/devicemanager.py
++++ /dev/null
+@@ -1,638 +0,0 @@
+-# 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/.
+-
+-from __future__ import absolute_import
+-
+-import hashlib
+-import mozlog
+-import logging
+-import os
+-import posixpath
+-import re
+-import struct
+-import StringIO
+-import zlib
+-
+-from functools import wraps
+-
+-
+-class DMError(Exception):
+-    "generic devicemanager exception."
+-
+-    def __init__(self, msg='', fatal=False):
+-        self.msg = msg
+-        self.fatal = fatal
+-
+-    def __str__(self):
+-        return self.msg
+-
+-
+-def abstractmethod(method):
+-    line = method.func_code.co_firstlineno
+-    filename = method.func_code.co_filename
+-
+-    @wraps(method)
+-    def not_implemented(*args, **kwargs):
+-        raise NotImplementedError('Abstract method %s at File "%s", line %s '
+-                                  'should be implemented by a concrete class' %
+-                                  (repr(method), filename, line))
+-    return not_implemented
+-
+-
+-class DeviceManager(object):
+-    """
+-    Represents a connection to a device. Once an implementation of this class
+-    is successfully instantiated, you may do things like list/copy files to
+-    the device, launch processes on the device, and install or remove
+-    applications from the device.
+-
+-    Never instantiate this class directly! Instead, instantiate an
+-    implementation of it like DeviceManagerADB. New projects should strongly
+-    consider using adb.py as an alternative.
+-    """
+-
+-    _logcatNeedsRoot = True
+-    default_timeout = 300
+-    short_timeout = 30
+-
+-    def __init__(self, logLevel=None, deviceRoot=None):
+-        try:
+-            self._logger = mozlog.get_default_logger(component="mozdevice")
+-            if not self._logger:  # no global structured logger, fall back to reg logging
+-                self._logger = mozlog.unstructured.getLogger("mozdevice")
+-                if logLevel is not None:
+-                    self._logger.setLevel(logLevel)
+-        except AttributeError:
+-            # Structured logging doesn't work on Python 2.6
+-            self._logger = None
+-        self._logLevel = logLevel
+-        self._remoteIsWin = None
+-        self._isDeviceRootSetup = False
+-        self._deviceRoot = deviceRoot
+-
+-    def _log(self, data):
+-        """
+-        This helper function is called by ProcessHandler to log
+-        the output produced by processes
+-        """
+-        self._logger.debug(data)
+-
+-    @property
+-    def remoteIsWin(self):
+-        if self._remoteIsWin is None:
+-            self._remoteIsWin = self.getInfo("os")["os"][0] == "windows"
+-        return self._remoteIsWin
+-
+-    @property
+-    def logLevel(self):
+-        return self._logLevel
+-
+-    @logLevel.setter
+-    def logLevel_setter(self, newLogLevel):
+-        self._logLevel = newLogLevel
+-        self._logger.setLevel(self._logLevel)
+-
+-    @property
+-    def debug(self):
+-        self._logger.warning("dm.debug is deprecated. Use logLevel.")
+-        levels = {logging.DEBUG: 5, logging.INFO: 3, logging.WARNING: 2,
+-                  logging.ERROR: 1, logging.CRITICAL: 0}
+-        return levels[self.logLevel]
+-
+-    @debug.setter
+-    def debug_setter(self, newDebug):
+-        self._logger.warning("dm.debug is deprecated. Use logLevel.")
+-        newDebug = 5 if newDebug > 5 else newDebug  # truncate >=5 to 5
+-        levels = {5: logging.DEBUG, 3: logging.INFO, 2: logging.WARNING,
+-                  1: logging.ERROR, 0: logging.CRITICAL}
+-        self.logLevel = levels[newDebug]
+-
+-    @abstractmethod
+-    def getInfo(self, directive=None):
+-        """
+-        Returns a dictionary of information strings about the device.
+-
+-        :param directive: information you want to get. Options are:
+-
+-          - `os` - name of the os
+-          - `id` - unique id of the device
+-          - `uptime` - uptime of the device
+-          - `uptimemillis` - uptime of the device in milliseconds
+-            (NOT supported on all implementations)
+-          - `systime` - system time of the device
+-          - `screen` - screen resolution
+-          - `memory` - memory stats
+-          - `memtotal` - total memory available on the device, for example 927208 kB
+-          - `process` - list of running processes (same as ps)
+-          - `disk` - total, free, available bytes on disk
+-          - `power` - power status (charge, battery temp)
+-          - `temperature` - device temperature
+-
+-         If `directive` is `None`, will return all available information
+-        """
+-
+-    @abstractmethod
+-    def getCurrentTime(self):
+-        """
+-        Returns device time in milliseconds since the epoch.
+-        """
+-
+-    def getIP(self, interfaces=['eth0', 'wlan0']):
+-        """
+-        Returns the IP of the device, or None if no connection exists.
+-        """
+-        for interface in interfaces:
+-            match = re.match(r"%s: ip (\S+)" % interface,
+-                             self.shellCheckOutput(['ifconfig', interface],
+-                                                   timeout=self.short_timeout))
+-            if match:
+-                return match.group(1)
+-
+-    def recordLogcat(self):
+-        """
+-        Clears the logcat file making it easier to view specific events.
+-        """
+-        # TODO: spawn this off in a separate thread/process so we can collect all
+-        # the logcat information
+-
+-        # Right now this is just clearing the logcat so we can only see what
+-        # happens after this call.
+-        self.shellCheckOutput(['/system/bin/logcat', '-c'], root=self._logcatNeedsRoot,
+-                              timeout=self.short_timeout)
+-
+-    def getLogcat(self, filterSpecs=["dalvikvm:I", "ConnectivityService:S",
+-                                     "WifiMonitor:S", "WifiStateTracker:S",
+-                                     "wpa_supplicant:S", "NetworkStateTracker:S"],
+-                  format="time",
+-                  filterOutRegexps=[]):
+-        """
+-        Returns the contents of the logcat file as a list of
+-        '\n' terminated strings
+-        """
+-        cmdline = ["/system/bin/logcat", "-v", format, "-d"] + filterSpecs
+-        output = self.shellCheckOutput(cmdline,
+-                                       root=self._logcatNeedsRoot,
+-                                       timeout=self.short_timeout)
+-        lines = output.replace('\r\n', '\n').splitlines(True)
+-
+-        for regex in filterOutRegexps:
+-            lines = [line for line in lines if not re.search(regex, line)]
+-
+-        return lines
+-
+-    def saveScreenshot(self, filename):
+-        """
+-        Takes a screenshot of what's being display on the device. Uses
+-        "screencap" on newer (Android 3.0+) devices (and some older ones with
+-        the functionality backported). This function also works on B2G.
+-
+-        Throws an exception on failure. This will always fail on devices
+-        without the screencap utility.
+-        """
+-        screencap = '/system/bin/screencap'
+-        if not self.fileExists(screencap):
+-            raise DMError("Unable to capture screenshot on device: no screencap utility")
+-
+-        with open(filename, 'w') as pngfile:
+-            # newer versions of screencap can write directly to a png, but some
+-            # older versions can't
+-            tempScreenshotFile = self.deviceRoot + "/ss-dm.tmp"
+-            self.shellCheckOutput(["sh", "-c", "%s > %s" %
+-                                   (screencap, tempScreenshotFile)],
+-                                  root=True)
+-            buf = self.pullFile(tempScreenshotFile)
+-            width = int(struct.unpack("I", buf[0:4])[0])
+-            height = int(struct.unpack("I", buf[4:8])[0])
+-            with open(filename, 'w') as pngfile:
+-                pngfile.write(self._writePNG(buf[12:], width, height))
+-            self.removeFile(tempScreenshotFile)
+-
+-    @abstractmethod
+-    def pushFile(self, localFilename, remoteFilename, retryLimit=1, createDir=True):
+-        """
+-        Copies localname from the host to destname on the device.
+-        """
+-
+-    @abstractmethod
+-    def pushDir(self, localDirname, remoteDirname, retryLimit=1, timeout=None):
+-        """
+-        Push local directory from host to remote directory on the device,
+-        """
+-
+-    @abstractmethod
+-    def pullFile(self, remoteFilename, offset=None, length=None):
+-        """
+-        Returns contents of remoteFile using the "pull" command.
+-
+-        :param remoteFilename: Path to file to pull from remote device.
+-        :param offset: Offset in bytes from which to begin reading (optional)
+-        :param length: Number of bytes to read (optional)
+-        """
+-
+-    @abstractmethod
+-    def getFile(self, remoteFilename, localFilename):
+-        """
+-        Copy file from remote device to local file on host.
+-        """
+-
+-    @abstractmethod
+-    def getDirectory(self, remoteDirname, localDirname, checkDir=True):
+-        """
+-        Copy directory structure from device (remoteDirname) to host (localDirname).
+-        """
+-
+-    @abstractmethod
+-    def validateFile(self, remoteFilename, localFilename):
+-        """
+-        Returns True if a file on the remote device has the same md5 hash as a local one.
+-        """
+-
+-    def validateDir(self, localDirname, remoteDirname):
+-        """
+-        Returns True if remoteDirname on device is same as localDirname on host.
+-        """
+-
+-        self._logger.info("validating directory: %s to %s" % (localDirname, remoteDirname))
+-        for root, dirs, files in os.walk(localDirname):
+-            parts = root.split(localDirname)
+-            for f in files:
+-                remoteRoot = remoteDirname + '/' + parts[1]
+-                remoteRoot = remoteRoot.replace('/', '/')
+-                if (parts[1] == ""):
+-                    remoteRoot = remoteDirname
+-                remoteName = remoteRoot + '/' + f
+-                if (self.validateFile(remoteName, os.path.join(root, f)) is not True):
+-                    return False
+-        return True
+-
+-    @abstractmethod
+-    def mkDir(self, remoteDirname):
+-        """
+-        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. For example, if asked to
+-        create `/mnt/sdcard/foo/bar/baz`, it will only create `/mnt/sdcard/foo/bar`
+-        """
+-        filename = posixpath.normpath(filename)
+-        containing = posixpath.dirname(filename)
+-        if not self.dirExists(containing):
+-            parts = filename.split('/')
+-            name = "/" if not self.remoteIsWin else parts.pop(0)
+-            for part in parts[:-1]:
+-                if part != "":
+-                    name = posixpath.join(name, part)
+-                    self.mkDir(name)  # mkDir will check previous existence
+-
+-    @abstractmethod
+-    def dirExists(self, dirpath):
+-        """
+-        Returns whether dirpath exists and is a directory on the device file system.
+-        """
+-
+-    @abstractmethod
+-    def fileExists(self, filepath):
+-        """
+-        Return whether filepath exists on the device file system,
+-        regardless of file type.
+-        """
+-
+-    @abstractmethod
+-    def listFiles(self, rootdir):
+-        """
+-        Lists files on the device rootdir.
+-
+-        Returns array of filenames, ['file1', 'file2', ...]
+-        """
+-
+-    @abstractmethod
+-    def removeFile(self, filename):
+-        """
+-        Removes filename from the device.
+-        """
+-
+-    @abstractmethod
+-    def removeDir(self, remoteDirname):
+-        """
+-        Does a recursive delete of directory on the device: rm -Rf remoteDirname.
+-        """
+-
+-    @abstractmethod
+-    def moveTree(self, source, destination):
+-        """
+-        Does a move of the file or directory on the device.
+-
+-       :param source: Path to the original file or directory
+-       :param destination: Path to the destination file or directory
+-        """
+-
+-    @abstractmethod
+-    def copyTree(self, source, destination):
+-        """
+-        Does a copy of the file or directory on the device.
+-
+-       :param source: Path to the original file or directory
+-       :param destination: Path to the destination file or directory
+-        """
+-
+-    @abstractmethod
+-    def chmodDir(self, remoteDirname, mask="777"):
+-        """
+-        Recursively changes file permissions in a directory.
+-        """
+-
+-    @property
+-    def deviceRoot(self):
+-        """
+-        The device root on the device filesystem for putting temporary
+-        testing files.
+-        """
+-        # derive deviceroot value if not set
+-        if not self._deviceRoot or not self._isDeviceRootSetup:
+-            self._deviceRoot = self._setupDeviceRoot(self._deviceRoot)
+-            self._isDeviceRootSetup = True
+-
+-        return self._deviceRoot
+-
+-    @abstractmethod
+-    def _setupDeviceRoot(self):
+-        """
+-        Sets up and returns a device root location that can be written to by tests.
+-        """
+-
+-    def getDeviceRoot(self):
+-        """
+-        Get the device root on the device filesystem for putting temporary
+-        testing files.
+-
+-        .. deprecated:: 0.38
+-          Use the :py:attr:`deviceRoot` property instead.
+-        """
+-        return self.deviceRoot
+-
+-    @abstractmethod
+-    def getTempDir(self):
+-        """
+-        Returns a temporary directory we can use on this device, ensuring
+-        also that it exists.
+-        """
+-
+-    @abstractmethod
+-    def shell(self, cmd, outputfile, env=None, cwd=None, timeout=None, root=False):
+-        """
+-        Executes shell command on device and returns exit code.
+-
+-        :param cmd: Commandline list to execute
+-        :param outputfile: File to store output
+-        :param env: Environment to pass to exec command
+-        :param cwd: Directory to execute command from
+-        :param timeout: specified in seconds, defaults to 'default_timeout'
+-        :param root: Specifies whether command requires root privileges
+-        """
+-
+-    def shellCheckOutput(self, cmd, env=None, cwd=None, timeout=None, root=False):
+-        """
+-        Executes shell command on device and returns output as a string. Raises if
+-        the return code is non-zero.
+-
+-        :param cmd: Commandline list to execute
+-        :param env: Environment to pass to exec command
+-        :param cwd: Directory to execute command from
+-        :param timeout: specified in seconds, defaults to 'default_timeout'
+-        :param root: Specifies whether command requires root privileges
+-        :raises: DMError
+-        """
+-        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: '%s')" % (cmd, output, retval))
+-        return output
+-
+-    @abstractmethod
+-    def getProcessList(self):
+-        """
+-        Returns array of tuples representing running processes on the device.
+-
+-        Format of tuples is (processId, processName, userId)
+-        """
+-
+-    def processInfo(self, processName):
+-        """
+-        Returns information on the process with processName.
+-        Information on process is in tuple format: (pid, process path, user)
+-        If a process with the specified name does not exist this function will return None.
+-        """
+-        if not isinstance(processName, basestring):
+-            raise TypeError("Process name %s is not a string" % processName)
+-
+-        processInfo = None
+-
+-        # filter out extra spaces
+-        parts = filter(lambda x: x != '', processName.split(' '))
+-        processName = ' '.join(parts)
+-
+-        # filter out the quoted env string if it exists
+-        # ex: '"name=value;name2=value2;etc=..." process args' -> 'process args'
+-        parts = processName.split('"')
+-        if (len(parts) > 2):
+-            processName = ' '.join(parts[2:]).strip()
+-
+-        pieces = processName.split(' ')
+-        parts = pieces[0].split('/')
+-        app = parts[-1]
+-
+-        procList = self.getProcessList()
+-        if (procList == []):
+-            return None
+-
+-        for proc in procList:
+-            procName = proc[1].split('/')[-1]
+-            if (procName == app):
+-                processInfo = proc
+-                break
+-        return processInfo
+-
+-    def processExist(self, processName):
+-        """
+-        Returns True if process with name processName is running on device.
+-        """
+-        processInfo = self.processInfo(processName)
+-        if processInfo:
+-            return processInfo[0]
+-
+-    @abstractmethod
+-    def killProcess(self, processName, sig=None):
+-        """
+-        Kills the process named processName. If sig is not None, process is
+-        killed with the specified signal.
+-
+-        :param processName: path or name of the process to kill
+-        :param sig: signal to pass into the kill command (optional)
+-        """
+-
+-    @abstractmethod
+-    def reboot(self, wait=False, ipAddr=None):
+-        """
+-        Reboots the device.
+-
+-        :param wait: block on device to come back up before returning
+-        :param ipAddr: deprecated; do not use
+-        """
+-
+-    @abstractmethod
+-    def installApp(self, appBundlePath, destPath=None):
+-        """
+-        Installs an application onto the device.
+-
+-        :param appBundlePath: path to the application bundle on the device
+-        :param destPath: destination directory of where application should be
+-                         installed to (optional)
+-        """
+-
+-    @abstractmethod
+-    def uninstallApp(self, appName, installPath=None):
+-        """
+-        Uninstalls the named application from device and DOES NOT cause a reboot.
+-
+-        :param appName: the name of the application (e.g org.mozilla.fennec)
+-        :param installPath: the path to where the application was installed (optional)
+-        """
+-
+-    @abstractmethod
+-    def uninstallAppAndReboot(self, appName, installPath=None):
+-        """
+-        Uninstalls the named application from device and causes a reboot.
+-
+-        :param appName: the name of the application (e.g org.mozilla.fennec)
+-        :param installPath: the path to where the application was installed (optional)
+-        """
+-
+-    @abstractmethod
+-    def updateApp(self, appBundlePath, processName=None, destPath=None,
+-                  wait=False, ipAddr=None):
+-        """
+-        Updates the application on the device and reboots.
+-
+-        :param appBundlePath: path to the application bundle on the device
+-        :param processName: used to end the process if the applicaiton is
+-                            currently running (optional)
+-        :param destPath: Destination directory to where the application should
+-                         be installed (optional)
+-        :param wait: block on device to come back up before returning
+-        :param ipAddr: deprecated; do not use
+-        """
+-
+-    @staticmethod
+-    def _writePNG(buf, width, height):
+-        """
+-        Method for writing a PNG from a buffer, used by getScreenshot on older devices,
+-        """
+-        # Based on: http://code.activestate.com/recipes/577443-write-a-png-image-in-native-python/
+-        width_byte_4 = width * 4
+-        raw_data = b"".join(b'\x00' + buf[span:span + width_byte_4]
+-                            for span in range(0, (height - 1) * width * 4, width_byte_4))
+-
+-        def png_pack(png_tag, data):
+-            chunk_head = png_tag + data
+-            return struct.pack("!I", len(data)) \
+-                + chunk_head \
+-                + struct.pack("!I", 0xFFFFFFFF & zlib.crc32(chunk_head))
+-        return b"".join([
+-            b'\x89PNG\r\n\x1a\n',
+-            png_pack(b'IHDR', struct.pack("!2I5B", width, height, 8, 6, 0, 0, 0)),
+-            png_pack(b'IDAT', zlib.compress(raw_data, 9)),
+-            png_pack(b'IEND', b'')])
+-
+-    @abstractmethod
+-    def _getRemoteHash(self, filename):
+-        """
+-        Return the md5 sum of a file on the device.
+-        """
+-
+-    @staticmethod
+-    def _getLocalHash(filename):
+-        """
+-        Return the MD5 sum of a file on the host.
+-        """
+-        f = open(filename, 'rb')
+-        if f is None:
+-            return None
+-
+-        try:
+-            mdsum = hashlib.md5()
+-        except Exception:
+-            return None
+-
+-        while 1:
+-            data = f.read(1024)
+-            if not data:
+-                break
+-            mdsum.update(data)
+-
+-        f.close()
+-        hexval = mdsum.hexdigest()
+-        return hexval
+-
+-    @staticmethod
+-    def _escapedCommandLine(cmd):
+-        """
+-        Utility function to return escaped and quoted version of command line.
+-        """
+-        quotedCmd = []
+-
+-        for arg in cmd:
+-            arg.replace('&', '\&')
+-
+-            needsQuoting = False
+-            for char in [' ', '(', ')', '"', '&']:
+-                if arg.find(char) >= 0:
+-                    needsQuoting = True
+-                    break
+-            if needsQuoting:
+-                arg = '\'%s\'' % arg
+-
+-            quotedCmd.append(arg)
+-
+-        return " ".join(quotedCmd)
+-
+-
+-def _pop_last_line(file_obj):
+-    """
+-    Utility function to get the last line from a file. Function also removes
+-    it from the file. Intended to strip off the return code from a shell
+-    command.
+-    """
+-    bytes_from_end = 1
+-    file_obj.seek(0, 2)
+-    length = file_obj.tell() + 1
+-    while bytes_from_end < length:
+-        file_obj.seek((-1) * bytes_from_end, 2)
+-        data = file_obj.read()
+-
+-        if bytes_from_end == length - 1 and len(data) == 0:  # no data, return None
+-            return None
+-
+-        if data[0] == '\n' or bytes_from_end == length - 1:
+-            # found the last line, which should have the return value
+-            if data[0] == '\n':
+-                data = data[1:]
+-
+-            # truncate off the return code line
+-            file_obj.truncate(length - bytes_from_end)
+-            file_obj.seek(0, 2)
+-            file_obj.write('\0')
+-
+-            return data
+-
+-        bytes_from_end += 1
+-
+-    return None
+diff --git a/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py b/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py
+deleted file mode 100644
+--- a/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py
++++ /dev/null
+@@ -1,886 +0,0 @@
+-# 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/.
+-
+-from __future__ import absolute_import
+-
+-import logging
+-import re
+-import os
+-import tempfile
+-import time
+-import traceback
+-
+-from distutils import dir_util
+-
+-from .devicemanager import DeviceManager, DMError
+-from mozprocess import ProcessHandler
+-import mozfile
+-from . import version_codes
+-
+-
+-class DeviceManagerADB(DeviceManager):
+-    """
+-    Implementation of DeviceManager interface that uses the Android "adb"
+-    utility to communicate with the device. Normally used to communicate
+-    with a device that is directly connected with the host machine over a USB
+-    port.
+-    """
+-
+-    _haveRootShell = None
+-    _haveSu = None
+-    _suModifier = None
+-    _lsModifier = None
+-    _useZip = False
+-    _logcatNeedsRoot = False
+-    _pollingInterval = 0.01
+-    _packageName = None
+-    _tempDir = None
+-    _adb_version = None
+-    _sdk_version = None
+-    connected = False
+-
+-    def __init__(self, host=None, port=5555, retryLimit=5, packageName='fennec',
+-                 adbPath=None, deviceSerial=None, deviceRoot=None,
+-                 logLevel=logging.ERROR, autoconnect=True, runAdbAsRoot=False,
+-                 serverHost=None, serverPort=None, **kwargs):
+-        DeviceManager.__init__(self, logLevel=logLevel,
+-                               deviceRoot=deviceRoot)
+-        self.host = host
+-        self.port = port
+-        self.retryLimit = retryLimit
+-
+-        self._serverHost = serverHost
+-        self._serverPort = serverPort
+-
+-        # the path to adb, or 'adb' to assume that it's on the PATH
+-        self._adbPath = adbPath or 'adb'
+-
+-        # The serial number of the device to use with adb, used in cases
+-        # where multiple devices are being managed by the same adb instance.
+-        self._deviceSerial = deviceSerial
+-
+-        # Some devices do no start adb as root, if allowed you can use
+-        # this to reboot adbd on the device as root automatically
+-        self._runAdbAsRoot = runAdbAsRoot
+-
+-        if packageName == 'fennec':
+-            if os.getenv('USER'):
+-                self._packageName = 'org.mozilla.fennec_' + os.getenv('USER')
+-            else:
+-                self._packageName = 'org.mozilla.fennec_'
+-        elif packageName:
+-            self._packageName = packageName
+-
+-        # verify that we can run the adb command. can't continue otherwise
+-        self._verifyADB()
+-
+-        if autoconnect:
+-            self.connect()
+-
+-    def connect(self):
+-        if not self.connected:
+-            # try to connect to the device over tcp/ip if we have a hostname
+-            if self.host:
+-                self._connectRemoteADB()
+-
+-            # verify that we can connect to the device. can't continue
+-            self._verifyDevice()
+-
+-            # Note SDK version
+-            try:
+-                proc = self._runCmd(["shell", "getprop", "ro.build.version.sdk"],
+-                                    timeout=self.short_timeout)
+-                self._sdk_version = int(proc.output[0])
+-            except (OSError, ValueError):
+-                self._sdk_version = 0
+-            self._logger.info("Detected Android sdk %d" % self._sdk_version)
+-
+-            # Some commands require root to work properly, even with ADB (e.g.
+-            # grabbing APKs out of /data). For these cases, we check whether
+-            # we're running as root. If that isn't true, check for the
+-            # existence of an su binary
+-            self._checkForRoot()
+-
+-            # can we use zip to speed up some file operations? (currently not
+-            # required)
+-            try:
+-                self._verifyZip()
+-            except DMError:
+-                pass
+-
+-    def __del__(self):
+-        if self.host:
+-            self._disconnectRemoteADB()
+-
+-    def shell(self, cmd, outputfile, env=None, cwd=None, timeout=None, root=False):
+-        # FIXME: this function buffers all output of the command into memory,
+-        # always. :(
+-
+-        # If requested to run as root, check that we can actually do that
+-        if root:
+-            if self._haveRootShell is None and self._haveSu is None:
+-                self._checkForRoot()
+-            if not self._haveRootShell and not self._haveSu:
+-                raise DMError(
+-                    "Shell command '%s' requested to run as root but root "
+-                    "is not available on this device. Root your device or "
+-                    "refactor the test/harness to not require root." %
+-                    self._escapedCommandLine(cmd))
+-
+-        # Getting the return code is more complex than you'd think because adb
+-        # doesn't actually return the return code from a process, so we have to
+-        # capture the output to get it
+-        if root and self._haveSu:
+-            cmdline = "su %s \"%s\"" % (self._suModifier,
+-                                        self._escapedCommandLine(cmd))
+-        else:
+-            cmdline = self._escapedCommandLine(cmd)
+-        cmdline += "; echo $?"
+-
+-        # prepend cwd and env to command if necessary
+-        if cwd:
+-            cmdline = "cd %s; %s" % (cwd, cmdline)
+-        if env:
+-            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._serverHost is not None:
+-            args.extend(['-H', self._serverHost])
+-        if self._serverPort is not None:
+-            args.extend(['-P', str(self._serverPort)])
+-        if self._deviceSerial:
+-            args.extend(['-s', self._deviceSerial])
+-        args.extend(["shell", cmdline])
+-
+-        def _timeout():
+-            self._logger.error("Timeout exceeded for shell call '%s'" % ' '.join(args))
+-
+-        self._logger.debug("shell - command: %s" % ' '.join(args))
+-        proc = ProcessHandler(args, processOutputLine=self._log, onTimeout=_timeout)
+-
+-        if not timeout:
+-            # We are asserting that all commands will complete in this time unless
+-            # otherwise specified
+-            timeout = self.default_timeout
+-
+-        timeout = int(timeout)
+-        proc.run(timeout)
+-        proc.wait()
+-        output = proc.output
+-
+-        if output:
+-            lastline = output[-1]
+-            if lastline:
+-                m = re.search('([0-9]+)', lastline)
+-                if m:
+-                    return_code = m.group(1)
+-                    for line in output:
+-                        outputfile.write(line + '\n')
+-                    outputfile.seek(-2, 2)
+-                    outputfile.truncate()  # truncate off the return code
+-                    return int(return_code)
+-
+-        return None
+-
+-    def forward(self, local, remote):
+-        """
+-        Forward socket connections.
+-
+-        Forward specs are one of:
+-          tcp:<port>
+-          localabstract:<unix domain socket name>
+-          localreserved:<unix domain socket name>
+-          localfilesystem:<unix domain socket name>
+-          dev:<character device name>
+-          jdwp:<process pid> (remote only)
+-        """
+-        if not self._checkCmd(['forward', local, remote], timeout=self.short_timeout) == 0:
+-            raise DMError("Failed to forward socket connection.")
+-
+-    def remove_forward(self, local=None):
+-        """
+-        Turn off forwarding of socket connection.
+-        """
+-        cmd = ['forward']
+-        if local is None:
+-            cmd.extend(['--remove-all'])
+-        else:
+-            cmd.extend(['--remove', local])
+-        if not self._checkCmd(cmd, timeout=self.short_timeout) == 0:
+-            raise DMError("Failed to remove connection forwarding.")
+-
+-    def remount(self):
+-        "Remounts the /system partition on the device read-write."
+-        return self._checkCmd(['remount'], timeout=self.short_timeout)
+-
+-    def devices(self):
+-        "Return a list of connected devices as (serial, status) tuples."
+-        proc = self._runCmd(['devices'])
+-        proc.output.pop(0)  # ignore first line of output
+-        devices = []
+-        for line in proc.output:
+-            result = re.match('(.*?)\t(.*)', line)
+-            if result:
+-                devices.append((result.group(1), result.group(2)))
+-        return devices
+-
+-    def _connectRemoteADB(self):
+-        self._checkCmd(["connect", self.host + ":" + str(self.port)])
+-
+-    def _disconnectRemoteADB(self):
+-        self._checkCmd(["disconnect", self.host + ":" + str(self.port)])
+-
+-    def pushFile(self, localname, destname, retryLimit=None, createDir=True):
+-        # you might expect us to put the file *in* the directory in this case,
+-        # but that would be inconsistent with historical behavior.
+-        retryLimit = retryLimit or self.retryLimit
+-        if self.dirExists(destname):
+-            raise DMError("Attempted to push a file (%s) to a directory (%s)!" %
+-                          (localname, destname))
+-        if not os.access(localname, os.F_OK):
+-            raise DMError("File not found: %s" % localname)
+-
+-        proc = self._runCmd(["push", os.path.realpath(localname), destname],
+-                            retryLimit=retryLimit)
+-        if proc.returncode != 0:
+-            raise DMError("Error pushing file %s -> %s; output: %s" %
+-                          (localname, destname, proc.output))
+-
+-    def mkDir(self, name):
+-        result = self._runCmd(["shell", "mkdir", name], timeout=self.short_timeout).output
+-        if len(result) and 'read-only file system' in result[0].lower():
+-            raise DMError("Error creating directory: read only file system")
+-
+-    def pushDir(self, localDir, remoteDir, retryLimit=None, timeout=None):
+-        # 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 re-copy the directory into a temporary
+-        # one to get around this limitation
+-        retryLimit = retryLimit or self.retryLimit
+-        if self._useZip:
+-            self.removeDir(remoteDir)
+-            self.mkDirs(remoteDir + "/x")
+-            try:
+-                localZip = tempfile.mktemp() + ".zip"
+-                remoteZip = remoteDir + "/adbdmtmp.zip"
+-                proc = ProcessHandler(["zip", "-r", localZip, '.'], cwd=localDir,
+-                                      processOutputLine=self._log)
+-                proc.run()
+-                proc.wait()
+-                self.pushFile(localZip, remoteZip, retryLimit=retryLimit, createDir=False)
+-                mozfile.remove(localZip)
+-                data = self._runCmd(["shell", "unzip", "-o", remoteZip,
+-                                     "-d", remoteDir]).output[0]
+-                self._checkCmd(["shell", "rm", remoteZip],
+-                               retryLimit=retryLimit, timeout=self.short_timeout)
+-                if re.search("unzip: exiting", data) or re.search("Operation not permitted", data):
+-                    raise Exception("unzip failed, or permissions error")
+-            except Exception:
+-                self._logger.warning(traceback.format_exc())
+-                self._logger.warning("zip/unzip failure: falling back to normal push")
+-                self._useZip = False
+-                self.pushDir(localDir, remoteDir, retryLimit=retryLimit, timeout=timeout)
+-        else:
+-            localDir = os.path.normpath(localDir)
+-            remoteDir = os.path.normpath(remoteDir)
+-            tempParent = tempfile.mkdtemp()
+-            remoteName = os.path.basename(remoteDir)
+-            newLocal = os.path.join(tempParent, remoteName)
+-            dir_util.copy_tree(localDir, newLocal)
+-            # See do_sync_push in
+-            # https://android.googlesource.com/platform/system/core/+/master/adb/file_sync_client.cpp
+-            # Work around change in behavior in adb 1.0.36 where if
+-            # the remote destination directory exists, adb push will
+-            # copy the source directory *into* the destination
+-            # directory otherwise it will copy the source directory
+-            # *onto* the destination directory.
+-            if self._adb_version >= '1.0.36':
+-                remoteDir = '/'.join(remoteDir.rstrip('/').split('/')[:-1])
+-            try:
+-                if self._checkCmd(["push", newLocal, remoteDir],
+-                                  retryLimit=retryLimit, timeout=timeout):
+-                    raise DMError("failed to push %s (copy of %s) to %s" %
+-                                  (newLocal, localDir, remoteDir))
+-            except BaseException:
+-                raise
+-            finally:
+-                mozfile.remove(tempParent)
+-
+-    def dirExists(self, remotePath):
+-        self._detectLsModifier()
+-        data = self._runCmd(["shell", "ls", self._lsModifier, remotePath + '/'],
+-                            timeout=self.short_timeout).output
+-
+-        if len(data) == 1:
+-            res = data[0]
+-            if "Not a directory" in res or "No such file or directory" in res:
+-                return False
+-        return True
+-
+-    def fileExists(self, filepath):
+-        self._detectLsModifier()
+-        data = self._runCmd(["shell", "ls", self._lsModifier, filepath],
+-                            timeout=self.short_timeout).output
+-        if len(data) == 1:
+-            foundpath = data[0].decode('utf-8').rstrip()
+-            if foundpath == filepath:
+-                return True
+-        return False
+-
+-    def removeFile(self, filename):
+-        if self.fileExists(filename):
+-            self._checkCmd(["shell", "rm", filename], timeout=self.short_timeout)
+-
+-    def removeDir(self, remoteDir):
+-        if self.dirExists(remoteDir):
+-            self._checkCmd(["shell", "rm", "-r", remoteDir], timeout=self.short_timeout)
+-        else:
+-            self.removeFile(remoteDir.strip())
+-
+-    def moveTree(self, source, destination):
+-        self._checkCmd(["shell", "mv", source, destination], timeout=self.short_timeout)
+-
+-    def copyTree(self, source, destination):
+-        self._checkCmd(["shell", "dd", "if=%s" % source, "of=%s" % destination])
+-
+-    def listFiles(self, rootdir):
+-        self._detectLsModifier()
+-        data = self._runCmd(["shell", "ls", self._lsModifier, rootdir],
+-                            timeout=self.short_timeout).output
+-        data[:] = [item.rstrip('\r\n') for item in data]
+-        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 []
+-            if (data[0].find("Permission denied") != -1):
+-                return []
+-            if (data[0].find("opendir failed") != -1):
+-                return []
+-            if (data[0].find("Device or resource busy") != -1):
+-                return []
+-        return data
+-
+-    def getProcessList(self):
+-        ret = []
+-        p = self._runCmd(["shell", "ps"], timeout=self.short_timeout)
+-        if not p or not p.output or len(p.output) < 1:
+-            return ret
+-        # first line is the headers
+-        p.output.pop(0)
+-        for proc in p.output:
+-            els = proc.split()
+-            # We need to figure out if this is "user pid name" or
+-            # "pid user vsz stat command"
+-            if els[1].isdigit():
+-                ret.append(list([int(els[1]), els[len(els) - 1], els[0]]))
+-            else:
+-                ret.append(list([int(els[0]), els[len(els) - 1], els[1]]))
+-        return ret
+-
+-    def fireProcess(self, appname, failIfRunning=False):
+-        """
+-        Starts a process
+-
+-        returns: pid
+-
+-        DEPRECATED: Use shell() or launchApplication() for new code
+-        """
+-        # strip out env vars
+-        parts = appname.split('"')
+-        if (len(parts) > 2):
+-            parts = parts[2:]
+-        return self.launchProcess(parts, failIfRunning)
+-
+-    def launchProcess(self, cmd, outputFile="process.txt", cwd='', env='', failIfRunning=False):
+-        """
+-        Launches a process, redirecting output to standard out
+-
+-        WARNING: Does not work how you expect on Android! The application's
+-        own output will be flushed elsewhere.
+-
+-        DEPRECATED: Use shell() or launchApplication() for new code
+-        """
+-        if cmd[0] == "am":
+-            self._checkCmd(["shell"] + cmd)
+-            return outputFile
+-
+-        acmd = ["-W"]
+-        cmd = ' '.join(cmd).strip()
+-        i = cmd.find(" ")
+-        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] + "/org.mozilla.gecko.BrowserApp")
+-        if args != "":
+-            acmd.append("--es")
+-            acmd.append("args")
+-            acmd.append(args)
+-        if env != '' and env is not None:
+-            envCnt = 0
+-            # env is expected to be a dict of environment variables
+-            for envkey, envval in env.iteritems():
+-                acmd.append("--es")
+-                acmd.append("env" + str(envCnt))
+-                acmd.append(envkey + "=" + envval)
+-                envCnt += 1
+-        if uri != "":
+-            acmd.append("-d")
+-            acmd.append(uri)
+-
+-        acmd = ["shell", ' '.join(map(lambda x: '"' + x + '"', ["am", "start"] + acmd))]
+-        self._logger.info(acmd)
+-        self._checkCmd(acmd)
+-        return outputFile
+-
+-    def killProcess(self, appname, sig=None):
+-        try:
+-            self.shellCheckOutput(["am", "force-stop", appname], timeout=self.short_timeout)
+-        except Exception:
+-            # no problem - will kill it instead
+-            self._logger.info("killProcess failed force-stop of %s" % appname)
+-
+-        shell_args = ["shell"]
+-        if self._sdk_version >= version_codes.N:
+-            # Bug 1334613 - force use of root
+-            if self._haveRootShell is None and self._haveSu is None:
+-                self._checkForRoot()
+-            if not self._haveRootShell and not self._haveSu:
+-                raise DMError(
+-                    "killProcess '%s' requested to run as root but root "
+-                    "is not available on this device. Root your device or "
+-                    "refactor the test/harness to not require root." %
+-                    appname)
+-            if not self._haveRootShell:
+-                shell_args.extend(["su", self._suModifier])
+-
+-        procs = self.getProcessList()
+-        for (pid, name, user) in procs:
+-            if name == appname:
+-                args = list(shell_args)
+-                args.append("kill")
+-                if sig:
+-                    args.append("-%d" % sig)
+-                args.append(str(pid))
+-                p = self._runCmd(args, timeout=self.short_timeout)
+-                if p.returncode != 0 and len(p.output) > 0 and \
+-                   'No such process' not in p.output[0]:
+-                    raise DMError("Error killing process "
+-                                  "'%s': %s" % (appname, p.output))
+-
+-    def _runPull(self, remoteFile, localFile):
+-        """
+-        Pulls remoteFile from device to host
+-        """
+-        try:
+-            self._runCmd(["pull",  remoteFile, localFile])
+-        except (OSError, ValueError):
+-            raise DMError("Error pulling remote file '%s' to '%s'" % (remoteFile, localFile))
+-
+-    def pullFile(self, remoteFile, offset=None, length=None):
+-        # TODO: add debug flags and allow for printing stdout
+-        with mozfile.NamedTemporaryFile() as tf:
+-            self._runPull(remoteFile, tf.name)
+-            # we need to reopen the file to get the written contents
+-            with open(tf.name) as tf2:
+-                # ADB pull does not support offset and length, but we can
+-                # instead read only the requested portion of the local file
+-                if offset is not None and length is not None:
+-                    tf2.seek(offset)
+-                    return tf2.read(length)
+-                elif offset is not None:
+-                    tf2.seek(offset)
+-                    return tf2.read()
+-                else:
+-                    return tf2.read()
+-
+-    def getFile(self, remoteFile, localFile):
+-        self._runPull(remoteFile, localFile)
+-
+-    def getDirectory(self, remoteDir, localDir, checkDir=True):
+-        localDir = os.path.normpath(localDir)
+-        remoteDir = os.path.normpath(remoteDir)
+-        copyRequired = False
+-        originalLocal = localDir
+-        if self._adb_version >= '1.0.36' and \
+-           os.path.isdir(localDir) and self.dirExists(remoteDir):
+-            # See do_sync_pull in
+-            # https://android.googlesource.com/platform/system/core/+/master/adb/file_sync_client.cpp
+-            # Work around change in behavior in adb 1.0.36 where if
+-            # the local destination directory exists, adb pull will
+-            # copy the source directory *into* the destination
+-            # directory otherwise it will copy the source directory
+-            # *onto* the destination directory.
+-            #
+-            # If the destination directory does exist, pull to its
+-            # parent directory. If the source and destination leaf
+-            # directory names are different, pull the source directory
+-            # into a temporary directory and then copy the temporary
+-            # directory onto the destination.
+-            localName = os.path.basename(localDir)
+-            remoteName = os.path.basename(remoteDir)
+-            if localName != remoteName:
+-                copyRequired = True
+-                tempParent = tempfile.mkdtemp()
+-                localDir = os.path.join(tempParent, remoteName)
+-            else:
+-                localDir = '/'.join(localDir.rstrip('/').split('/')[:-1])
+-        self._runCmd(["pull", remoteDir, localDir]).wait()
+-        if copyRequired:
+-            dir_util.copy_tree(localDir, originalLocal)
+-            mozfile.remove(tempParent)
+-
+-    def validateFile(self, remoteFile, localFile):
+-        md5Remote = self._getRemoteHash(remoteFile)
+-        md5Local = self._getLocalHash(localFile)
+-        if md5Remote is None or md5Local is None:
+-            return None
+-        return md5Remote == md5Local
+-
+-    def _getRemoteHash(self, remoteFile):
+-        """
+-        Return the md5 sum of a file on the device
+-        """
+-        with tempfile.NamedTemporaryFile() as f:
+-            self._runPull(remoteFile, f.name)
+-
+-            return self._getLocalHash(f.name)
+-
+-    def _setupDeviceRoot(self, deviceRoot):
+-        # user-specified device root, create it and return it
+-        if deviceRoot:
+-            self.mkDir(deviceRoot)
+-            return deviceRoot
+-
+-        # we must determine the device root ourselves
+-        paths = [('/storage/sdcard0', 'tests'),
+-                 ('/storage/sdcard1', 'tests'),
+-                 ('/storage/sdcard', 'tests'),
+-                 ('/mnt/sdcard', 'tests'),
+-                 ('/sdcard', 'tests'),
+-                 ('/data/local', 'tests')]
+-        for (basePath, subPath) in paths:
+-            if self.dirExists(basePath):
+-                root = os.path.join(basePath, subPath)
+-                try:
+-                    self.mkDir(root)
+-                    return root
+-                except Exception:
+-                    pass
+-
+-        raise DMError("Unable to set up device root using paths: [%s]"
+-                      % ", ".join(["'%s'" % os.path.join(b, s) for b, s in paths]))
+-
+-    def getTempDir(self):
+-        # Cache result to speed up operations depending
+-        # on the temporary directory.
+-        if not self._tempDir:
+-            self._tempDir = "%s/tmp" % self.deviceRoot
+-            self.mkDir(self._tempDir)
+-
+-        return self._tempDir
+-
+-    def reboot(self, wait=False, **kwargs):
+-        self._checkCmd(["reboot"])
+-        if wait:
+-            self._checkCmd(["wait-for-device"])
+-            if self._runAdbAsRoot:
+-                self._adb_root()
+-            self._checkCmd(["shell", "ls", "/sbin"], timeout=self.short_timeout)
+-
+-    def updateApp(self, appBundlePath, **kwargs):
+-        return self._runCmd(["install", "-r", appBundlePath]).output
+-
+-    def getCurrentTime(self):
+-        timestr = str(self._runCmd(["shell", "date", "+%s"], timeout=self.short_timeout).output[0])
+-        if (not timestr or not timestr.isdigit()):
+-            raise DMError("Unable to get current time using date (got: '%s')" % timestr)
+-        return int(timestr) * 1000
+-
+-    def getInfo(self, directive=None):
+-        directive = directive or "all"
+-        ret = {}
+-        if directive == "id" or directive == "all":
+-            ret["id"] = self._runCmd(["get-serialno"], timeout=self.short_timeout).output[0]
+-        if directive == "os" or directive == "all":
+-            ret["os"] = self.shellCheckOutput(
+-                ["getprop", "ro.build.display.id"], timeout=self.short_timeout)
+-        if directive == "uptime" or directive == "all":
+-            uptime = self.shellCheckOutput(["uptime"], timeout=self.short_timeout)
+-            if not uptime:
+-                raise DMError("error getting uptime")
+-            m = re.match("up time: ((\d+) days, )*(\d{2}):(\d{2}):(\d{2})", uptime)
+-            if m:
+-                uptime = "%d days %d hours %d minutes %d seconds" % tuple(
+-                    [int(g or 0) for g in m.groups()[1:]])
+-            ret["uptime"] = uptime
+-        if directive == "process" or directive == "all":
+-            data = self.shellCheckOutput(["ps"], timeout=self.short_timeout)
+-            ret["process"] = data.split('\n')
+-        if directive == "systime" or directive == "all":
+-            ret["systime"] = self.shellCheckOutput(["date"], timeout=self.short_timeout)
+-        if directive == "memtotal" or directive == "all":
+-            meminfo = {}
+-            for line in self.pullFile("/proc/meminfo").splitlines():
+-                key, value = line.split(":")
+-                meminfo[key] = value.strip()
+-            ret["memtotal"] = meminfo["MemTotal"]
+-        if directive == "disk" or directive == "all":
+-            data = self.shellCheckOutput(
+-                ["df", "/data", "/system", "/sdcard"], timeout=self.short_timeout)
+-            ret["disk"] = data.split('\n')
+-        self._logger.debug("getInfo: %s" % ret)
+-        return ret
+-
+-    def uninstallApp(self, appName, installPath=None):
+-        status = self._runCmd(["uninstall", appName]).output[0].strip()
+-        if status != 'Success':
+-            raise DMError("uninstall failed for %s. Got: %s" % (appName, status))
+-
+-    def uninstallAppAndReboot(self, appName, installPath=None):
+-        self.uninstallApp(appName)
+-        self.reboot()
+-
+-    def _runCmd(self, args, timeout=None, retryLimit=None):
+-        """
+-        Runs a command using adb
+-        If timeout is specified, the process is killed after <timeout> seconds.
+-
+-        returns: instance of ProcessHandler
+-        """
+-        retryLimit = retryLimit or self.retryLimit
+-        finalArgs = [self._adbPath]
+-        if self._serverHost is not None:
+-            finalArgs.extend(['-H', self._serverHost])
+-        if self._serverPort is not None:
+-            finalArgs.extend(['-P', str(self._serverPort)])
+-        if self._deviceSerial:
+-            finalArgs.extend(['-s', self._deviceSerial])
+-        finalArgs.extend(args)
+-        self._logger.debug("_runCmd - command: %s" % ' '.join(finalArgs))
+-        if not timeout:
+-            timeout = self.default_timeout
+-
+-        def _timeout():
+-            self._logger.error("Timeout exceeded for _runCmd call '%s'" % ' '.join(finalArgs))
+-
+-        retries = 0
+-        while retries < retryLimit:
+-            proc = ProcessHandler(finalArgs, storeOutput=True,
+-                                  processOutputLine=self._log, onTimeout=_timeout)
+-            proc.run(timeout=timeout)
+-            proc.returncode = proc.wait()
+-            if proc.returncode is None:
+-                proc.kill()
+-                retries += 1
+-            else:
+-                return proc
+-
+-    # timeout is specified in seconds, and if no timeout is given,
+-    # we will run until we hit the default_timeout specified in the __init__
+-    def _checkCmd(self, args, timeout=None, retryLimit=None):
+-        """
+-        Runs a command using adb and waits for the command to finish.
+-        If timeout is specified, the process is killed after <timeout> seconds.
+-
+-        returns: returncode from process
+-        """
+-        retryLimit = retryLimit or self.retryLimit
+-        finalArgs = [self._adbPath]
+-        if self._serverHost is not None:
+-            finalArgs.extend(['-H', self._serverHost])
+-        if self._serverPort is not None:
+-            finalArgs.extend(['-P', str(self._serverPort)])
+-        if self._deviceSerial:
+-            finalArgs.extend(['-s', self._deviceSerial])
+-        finalArgs.extend(args)
+-        self._logger.debug("_checkCmd - command: %s" % ' '.join(finalArgs))
+-        if not timeout:
+-            # We are asserting that all commands will complete in this
+-            # time unless otherwise specified
+-            timeout = self.default_timeout
+-
+-        def _timeout():
+-            self._logger.error("Timeout exceeded for _checkCmd call '%s'" % ' '.join(finalArgs))
+-
+-        timeout = int(timeout)
+-        retries = 0
+-        while retries < retryLimit:
+-            proc = ProcessHandler(finalArgs, processOutputLine=self._log, onTimeout=_timeout)
+-            proc.run(timeout=timeout)
+-            ret_code = proc.wait()
+-            if ret_code is None:
+-                proc.kill()
+-                retries += 1
+-            else:
+-                return ret_code
+-
+-        raise DMError("Timeout exceeded for _checkCmd call after %d retries." % retries)
+-
+-    def chmodDir(self, remoteDir, mask="777"):
+-        if (self.dirExists(remoteDir)):
+-            if '/sdcard' in remoteDir:
+-                self._logger.debug("chmod %s -- skipped (/sdcard)" % remoteDir)
+-            else:
+-                files = self.listFiles(remoteDir.strip())
+-                for f in files:
+-                    remoteEntry = remoteDir.strip() + "/" + f.strip()
+-                    if (self.dirExists(remoteEntry)):
+-                        self.chmodDir(remoteEntry)
+-                    else:
+-                        self._checkCmd(["shell", "chmod", mask, remoteEntry],
+-                                       timeout=self.short_timeout)
+-                        self._logger.info("chmod %s" % remoteEntry)
+-                self._checkCmd(["shell", "chmod", mask, remoteDir], timeout=self.short_timeout)
+-                self._logger.debug("chmod %s" % remoteDir)
+-        else:
+-            self._checkCmd(["shell", "chmod", mask, remoteDir.strip()], timeout=self.short_timeout)
+-            self._logger.debug("chmod %s" % remoteDir.strip())
+-
+-    def _verifyADB(self):
+-        """
+-        Check to see if adb itself can be executed.
+-        """
+-        if self._adbPath != 'adb':
+-            if not os.access(self._adbPath, os.X_OK):
+-                raise DMError("invalid adb path, or adb not executable: %s" % self._adbPath)
+-
+-        try:
+-            re_version = re.compile(r'Android Debug Bridge version (.*)')
+-            proc = self._runCmd(["version"], timeout=self.short_timeout)
+-            self._adb_version = re_version.match(proc.output[0]).group(1)
+-            self._logger.info("Detected adb %s" % self._adb_version)
+-        except os.error as err:
+-            raise DMError(
+-                "unable to execute ADB (%s): ensure Android SDK is installed "
+-                "and adb is in your $PATH" % err)
+-
+-    def _verifyDevice(self):
+-        # If there is a device serial number, see if adb is connected to it
+-        if self._deviceSerial:
+-            deviceStatus = None
+-            for line in self._runCmd(["devices"]).output:
+-                m = re.match('(.+)?\s+(.+)$', line)
+-                if m:
+-                    if self._deviceSerial == m.group(1):
+-                        deviceStatus = m.group(2)
+-            if deviceStatus is None:
+-                raise DMError("device not found: %s" % self._deviceSerial)
+-            elif deviceStatus != "device":
+-                raise DMError("bad status for device %s: %s" % (self._deviceSerial, deviceStatus))
+-
+-        # Check to see if we can connect to device and run a simple command
+-        if not self._checkCmd(["shell", "echo"], timeout=self.short_timeout) == 0:
+-            raise DMError("unable to connect to device")
+-
+-    def _checkForRoot(self):
+-        self._haveRootShell = False
+-        self._haveSu = False
+-        # If requested to attempt to run adbd as root, do so before
+-        # checking whether adbs is running as root.
+-        if self._runAdbAsRoot:
+-            self._adb_root()
+-
+-        # Check whether we _are_ root by default (some development boards work
+-        # this way, this is also the result of some relatively rare rooting
+-        # techniques)
+-        proc = self._runCmd(["shell", "id"], timeout=self.short_timeout)
+-        if proc.output and 'uid=0(root)' in proc.output[0]:
+-            self._haveRootShell = True
+-            # if this returns true, we don't care about su
+-            return
+-
+-        # if root shell is not available, check if 'su' can be used to gain
+-        # root
+-        def su_id(su_modifier, timeout):
+-            proc = self._runCmd(["shell", "su", su_modifier, "id"],
+-                                timeout=timeout)
+-
+-            # wait for response for maximum of 15 seconds, in case su
+-            # prompts for a password or triggers the Android SuperUser
+-            # prompt
+-            start_time = time.time()
+-            retcode = None
+-            while (time.time() - start_time) <= 15 and retcode is None:
+-                retcode = proc.poll()
+-            if retcode is None:  # still not terminated, kill
+-                proc.kill()
+-
+-            if proc.output and 'uid=0(root)' in proc.output[0]:
+-                return True
+-            return False
+-
+-        if su_id('0', self.short_timeout):
+-            self._haveSu = True
+-            self._suModifier = '0'
+-        elif su_id('-c', self.short_timeout):
+-            self._haveSu = True
+-            self._suModifier = '-c'
+-
+-    def _isUnzipAvailable(self):
+-        data = self._runCmd(["shell", "unzip"]).output
+-        for line in data:
+-            if (re.search('Usage', line)):
+-                return True
+-        return False
+-
+-    def _isLocalZipAvailable(self):
+-        def _noOutput(line):
+-            # suppress output from zip ProcessHandler
+-            pass
+-        try:
+-            proc = ProcessHandler(["zip", "-?"], storeOutput=False, processOutputLine=_noOutput)
+-            proc.run()
+-            proc.wait()
+-        except Exception:
+-            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()):
+-            self._logger.info("will use zip to push directories")
+-            self._useZip = True
+-        else:
+-            raise DMError("zip not available")
+-
+-    def _adb_root(self):
+-        """ Some devices require us to reboot adbd as root.
+-            This function takes care of it.
+-        """
+-        if self.processInfo("adbd")[2] != "root":
+-            self._checkCmd(["root"])
+-            self._checkCmd(["wait-for-device"])
+-            if self.processInfo("adbd")[2] != "root":
+-                raise DMError("We tried rebooting adbd as root, however, it failed.")
+-
+-    def _detectLsModifier(self):
+-        if self._lsModifier is None:
+-            # Check if busybox -1A is required in order to get one
+-            # file per line.
+-            output = self._runCmd(["shell", "ls", "-1A", "/"],
+-                                  timeout=self.short_timeout).output
+-            output = ' '.join(output)
+-            if 'error: device not found' in output:
+-                raise DMError(output)
+-            if "Unknown option '-1'. Aborting." in output:
+-                self._lsModifier = "-a"
+-            elif "No such file or directory" in output:
+-                self._lsModifier = "-a"
+-            else:
+-                self._lsModifier = "-1A"
+diff --git a/testing/mozbase/mozdevice/mozdevice/dmcli.py b/testing/mozbase/mozdevice/mozdevice/dmcli.py
+deleted file mode 100644
+--- a/testing/mozbase/mozdevice/mozdevice/dmcli.py
++++ /dev/null
+@@ -1,352 +0,0 @@
+-# 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
+-"""
+-from __future__ import absolute_import, print_function
+-
+-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'
+-                                       },
+-                         '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})
+-
+-        self.dm = self.getDevice(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 "
+-                            "adb-over-tcp)", default=None)
+-        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, 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 host and not port:
+-            port = 5555
+-        return mozdevice.DroidADB(packageName=packagename,
+-                                  host=host, port=port,
+-                                  logLevel=logLevel)
+-
+-    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 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()
+diff --git a/testing/mozbase/mozdevice/mozdevice/droid.py b/testing/mozbase/mozdevice/mozdevice/droid.py
+deleted file mode 100644
+--- a/testing/mozbase/mozdevice/mozdevice/droid.py
++++ /dev/null
+@@ -1,198 +0,0 @@
+-# 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/.
+-
+-from __future__ import absolute_import
+-
+-import StringIO
+-import re
+-import time
+-
+-from mozdevice import version_codes
+-
+-from .devicemanagerADB import DeviceManagerADB
+-from .devicemanager import DMError
+-
+-
+-class DroidMixin(object):
+-    """Mixin to extend DeviceManager with Android-specific functionality"""
+-
+-    _stopApplicationNeedsRoot = True
+-
+-    def _getExtraAmStartArgs(self):
+-        return []
+-
+-    def launchApplication(self, appName, activityName, intent, url=None,
+-                          extras=None, wait=True, failIfRunning=True):
+-        """
+-        Launches an Android application
+-
+-        :param appName: Name of application (e.g. `com.android.chrome`)
+-        :param activityName: Name of activity to launch (e.g. `.Main`)
+-        :param intent: Intent to launch application with
+-        :param url: URL to open
+-        :param extras: Dictionary of extra arguments to launch application with
+-        :param wait: If True, wait for application to start before returning
+-        :param failIfRunning: Raise an exception if instance of application is already running
+-        """
+-
+-        # If failIfRunning is True, we throw an exception here. Only one
+-        # instance of an application can be running at once on Android,
+-        # starting a new instance may not be what we want depending on what
+-        # we want to do
+-        if failIfRunning and self.processExist(appName):
+-            raise DMError("Only one instance of an application may be running "
+-                          "at once")
+-
+-        acmd = ["am", "start"] + self._getExtraAmStartArgs() + \
+-            ["-W" if wait else '', "-n", "%s/%s" % (appName, activityName)]
+-
+-        if intent:
+-            acmd.extend(["-a", intent])
+-
+-        if extras:
+-            for (key, val) in extras.iteritems():
+-                if type(val) is int:
+-                    extraTypeParam = "--ei"
+-                elif type(val) is bool:
+-                    extraTypeParam = "--ez"
+-                else:
+-                    extraTypeParam = "--es"
+-                acmd.extend([extraTypeParam, str(key), str(val)])
+-
+-        if url:
+-            acmd.extend(["-d", url])
+-
+-        # shell output not that interesting and debugging logs should already
+-        # show what's going on here... so just create an empty memory buffer
+-        # and ignore (except on error)
+-        shellOutput = StringIO.StringIO()
+-        if self.shell(acmd, shellOutput) == 0:
+-            return
+-
+-        shellOutput.seek(0)
+-        raise DMError("Unable to launch application (shell output: '%s')" % shellOutput.read())
+-
+-    def launchFennec(self, appName, intent="android.intent.action.VIEW",
+-                     mozEnv=None, extraArgs=None, url=None, wait=True,
+-                     failIfRunning=True):
+-        """
+-        Convenience method to launch Fennec on Android with various debugging
+-        arguments
+-
+-        :param appName: Name of fennec application (e.g. `org.mozilla.fennec`)
+-        :param intent: Intent to launch application with
+-        :param mozEnv: Mozilla specific environment to pass into application
+-        :param extraArgs: Extra arguments to be parsed by fennec
+-        :param url: URL to open
+-        :param wait: If True, wait for application to start before returning
+-        :param failIfRunning: Raise an exception if instance of application is already running
+-        """
+-        extras = {}
+-
+-        if mozEnv:
+-            # mozEnv is expected to be a dictionary of environment variables: Fennec
+-            # itself will set them when launched
+-            for (envCnt, (envkey, envval)) in enumerate(mozEnv.iteritems()):
+-                extras["env" + str(envCnt)] = envkey + "=" + envval
+-
+-        # Additional command line arguments that fennec will read and use (e.g.
+-        # with a custom profile)
+-        if extraArgs:
+-            extras['args'] = " ".join(extraArgs)
+-
+-        self.launchApplication(appName, "org.mozilla.gecko.BrowserApp", intent, url=url,
+-                               extras=extras,
+-                               wait=wait, failIfRunning=failIfRunning)
+-
+-    def getInstalledApps(self):
+-        """
+-        Lists applications installed on this Android device
+-
+-        Returns a list of application names in the form [ 'org.mozilla.fennec', ... ]
+-        """
+-        output = self.shellCheckOutput(["pm", "list", "packages", "-f"])
+-        apps = []
+-        for line in output.splitlines():
+-            # lines are of form: package:/system/app/qik-tmo.apk=com.qiktmobile.android
+-            apps.append(line.split('=')[1])
+-
+-        return apps
+-
+-    def stopApplication(self, appName):
+-        """
+-        Stops the specified application
+-
+-        For Android 3.0+, we use the "am force-stop" to do this, which is
+-        reliable and does not require root. For earlier versions of Android,
+-        we simply try to manually kill the processes started by the app
+-        repeatedly until none is around any more. This is less reliable and
+-        does require root.
+-
+-        :param appName: Name of application (e.g. `com.android.chrome`)
+-        """
+-        version = self.shellCheckOutput(["getprop", "ro.build.version.sdk"])
+-        if int(version) >= version_codes.HONEYCOMB:
+-            self.shellCheckOutput(["am", "force-stop", appName],
+-                                  root=self._stopApplicationNeedsRoot)
+-        else:
+-            num_tries = 0
+-            max_tries = 5
+-            while self.processExist(appName):
+-                if num_tries > max_tries:
+-                    raise DMError("Couldn't successfully kill %s after %s "
+-                                  "tries" % (appName, max_tries))
+-                self.killProcess(appName)
+-                num_tries += 1
+-
+-                # sleep for a short duration to make sure there are no
+-                # additional processes in the process of being launched
+-                # (this is not 100% guaranteed to work since it is inherently
+-                # racey, but it's the best we can do)
+-                time.sleep(1)
+-
+-
+-class DroidADB(DeviceManagerADB, DroidMixin):
+-
+-    _stopApplicationNeedsRoot = False
+-
+-    def getTopActivity(self):
+-        package = None
+-        data = None
+-        try:
+-            # Increased timeout to 60 seconds after intermittent timeouts at 30.
+-            data = self.shellCheckOutput(
+-                ["dumpsys", "window", "windows"], timeout=60)
+-        except Exception:
+-            # dumpsys seems to intermittently fail (seen on 4.3 emulator), producing
+-            # no output.
+-            return ""
+-        # "dumpsys window windows" produces many lines of input. The top/foreground
+-        # activity is indicated by something like:
+-        #   mFocusedApp=AppWindowToken{483e6db0 token=HistoryRecord{484dcad8 com.mozilla.something/.something}} # noqa
+-        # or, on other devices:
+-        #   FocusedApplication: name='AppWindowToken{41a65340 token=ActivityRecord{418fbd68 org.mozilla.fennec_mozdev/org.mozilla.gecko.BrowserApp}}', dispatchingTimeout=5000.000ms # noqa
+-        # Extract this line, ending in the forward slash:
+-        m = re.search('mFocusedApp(.+)/', data)
+-        if not m:
+-            m = re.search('FocusedApplication(.+)/', data)
+-        if m:
+-            line = m.group(0)
+-            # Extract package name: string of non-whitespace ending in forward slash
+-            m = re.search('(\S+)/$', line)
+-            if m:
+-                package = m.group(1)
+-        if not package:
+-            # On some Android 4.4 devices, when the home screen is displayed,
+-            # dumpsys reports "mFocusedApp=null". Guard against this case and
+-            # others where the focused app can not be determined by returning
+-            # an empty string.
+-            package = ""
+-        return package
+-
+-    def getAppRoot(self, packageName):
+-        """
+-        Returns the root directory for the specified android application
+-        """
+-        # relying on convention
+-        return '/data/data/%s' % packageName
+diff --git a/testing/mozbase/mozdevice/setup.py b/testing/mozbase/mozdevice/setup.py
+--- a/testing/mozbase/mozdevice/setup.py
++++ b/testing/mozbase/mozdevice/setup.py
+@@ -29,12 +29,10 @@ setup(name=PACKAGE_NAME,
+       url='https://wiki.mozilla.org/Auto-tools/Projects/Mozbase',
+       license='MPL',
+       packages=['mozdevice'],
+       include_package_data=True,
+       zip_safe=False,
+       install_requires=deps,
+       entry_points="""
+       # -*- Entry points: -*-
+-      [console_scripts]
+-      dm = mozdevice.dmcli:cli
+       """,
+       )
+diff --git a/testing/mozbase/moztest/moztest/output/base.py b/testing/mozbase/moztest/moztest/output/base.py
+--- a/testing/mozbase/moztest/moztest/output/base.py
++++ b/testing/mozbase/moztest/moztest/output/base.py
+@@ -6,17 +6,16 @@ from __future__ import absolute_import, 
+ 
+ from contextlib import closing
+ from StringIO import StringIO
+ 
+ try:
+     from abc import abstractmethod
+ except ImportError:
+     # abc is python 2.6+
+-    # from https://github.com/mozilla/mozbase/blob/master/mozdevice/mozdevice/devicemanager.py
+     def abstractmethod(method):
+         line = method.func_code.co_firstlineno
+         filename = method.func_code.co_filename
+ 
+         def not_implemented(*args, **kwargs):
+             raise NotImplementedError('Abstract method %s at File "%s", '
+                                       'line %s  should be implemented by a concrete class' %
+                                       (repr(method), filename, line))
 diff --git a/testing/xpcshell/runxpcshelltests.py b/testing/xpcshell/runxpcshelltests.py
 --- a/testing/xpcshell/runxpcshelltests.py
 +++ b/testing/xpcshell/runxpcshelltests.py

File diff suppressed because it is too large
+ 4 - 2384
mozilla-release/patches/TOP-1906540-mozdevice-removal-mozilla-25320.patch


Some files were not shown because too many files changed in this diff