Esempio n. 1
0
    def test_serial(self):
        serial = "abcdef1234567890"
        adb = Adb(serial)
        self.assertEqual(adb.default_serial, serial)

        adb.devices = MagicMock()
        adb.devices.return_value = [serial, "123456"]
        self.assertEqual(adb.device_serial(), serial)
Esempio n. 2
0
    def test_serial(self):
        serial = "abcdef1234567890"
        adb = Adb(serial)
        self.assertEqual(adb.default_serial, serial)

        adb.devices = MagicMock()
        adb.devices.return_value = [serial, "123456"]
        self.assertEqual(adb.device_serial(), serial)
Esempio n. 3
0
 def test_devices(self):
     adb = Adb()
     adb.raw_cmd = MagicMock()
     adb.raw_cmd.return_value.communicate.return_value = (
         b"List of devices attached \r\n014E05DE0F02000E\tdevice\r\n489328DKFL7DF\tdevice",
         b"")
     self.assertEqual(adb.devices(), {
         "014E05DE0F02000E": "device",
         "489328DKFL7DF": "device"
     })
     adb.raw_cmd.assert_called_once_with("devices")
     adb.raw_cmd.return_value.communicate.return_value = (
         b"List of devices attached \n\r014E05DE0F02000E\tdevice\n\r489328DKFL7DF\tdevice",
         b"")
     self.assertEqual(adb.devices(), {
         "014E05DE0F02000E": "device",
         "489328DKFL7DF": "device"
     })
     adb.raw_cmd.return_value.communicate.return_value = (
         b"List of devices attached \r014E05DE0F02000E\tdevice\r489328DKFL7DF\tdevice",
         b"")
     self.assertEqual(adb.devices(), {
         "014E05DE0F02000E": "device",
         "489328DKFL7DF": "device"
     })
     adb.raw_cmd.return_value.communicate.return_value = (
         b"List of devices attached \n014E05DE0F02000E\tdevice\n489328DKFL7DF\tdevice",
         b"")
     self.assertEqual(adb.devices(), {
         "014E05DE0F02000E": "device",
         "489328DKFL7DF": "device"
     })
     adb.raw_cmd.return_value.communicate.return_value = (b"not match", "")
     with self.assertRaises(EnvironmentError):
         adb.devices()
Esempio n. 4
0
 def __init__(self,
              serial=None,
              local_port=None,
              device_port=None,
              adb_server_host=None,
              adb_server_port=None):
     self.uiautomator_process = None
     self.adb = Adb(serial=serial,
                    adb_server_host=adb_server_host,
                    adb_server_port=adb_server_port)
     self.device_port = int(device_port) if device_port else DEVICE_PORT
     self.__local_port = local_port
Esempio n. 5
0
    def test_adb_cmd(self):
        adb = Adb()
        adb.device_serial = MagicMock()
        adb.device_serial.return_value = "ANDROID_SERIAL"
        adb.raw_cmd = MagicMock()
        args = ["a", "b", "c"]
        adb.cmd(*args)
        adb.raw_cmd.assert_called_once_with("-s", "%s" % adb.device_serial(), *args)

        adb.device_serial.return_value = "ANDROID SERIAL"
        adb.raw_cmd = MagicMock()
        args = ["a", "b", "c"]
        adb.cmd(*args)
        adb.raw_cmd.assert_called_once_with("-s", "'%s'" % adb.device_serial(), *args)
Esempio n. 6
0
 def __logdump(self, dirname):
     """Take various logs at certain point in time."""
     """打log"""
     curtime = datetime.now().strftime('%Y-%m-%d-%H-%M-%S')
     dirpath = self.__logdump_tcdir(dirname)
     logfpath = os.path.join(dirpath, curtime)
     if not os.path.exists(logfpath):
         os.makedirs(logfpath)
     ssfile = '%s_%s.png' % (self.devname, curtime)
     sspath = os.path.join(logfpath, ssfile)
     self.dev.GetDevice.screenshot(sspath)
     dpfile = '%s_%s.xml' % (self.devname, curtime)
     dppath = os.path.join(logfpath, dpfile)
     self.dev.GetDevice.dump(filename=dppath, compressed=False, pretty=True)
     log_cmds = [
         ('shell bugreport', 'bugreport.txt'),
         ('shell dmesg', 'kernel.txt'),
         ('shell ps', 'ps.txt'),
         ('shell dumpsys activity service android.phone.TelephonyDebugService', 'ateldumpsys.txt')
     ]
     for i, (cmd, filename) in enumerate(log_cmds):
         response = Adb(self.dev.GetDeviceSerial).cmd(*cmd.split()).communicate()[0]
         with open(os.path.join(logfpath, filename), 'w') as fd:
             fd.write(response)
     for logfile_path in self.__get_logfile_paths(opt=dirname):
         shutil.copy2(logfile_path, logfpath)
     self.log.info('[LogDump] %s', logfpath)
Esempio n. 7
0
 def test_adb_from_find(self):
     with patch.dict('os.environ', {}, clear=True):
         with patch("distutils.spawn.find_executable") as find_executable:
             find_executable.return_value = "/usr/bin/adb"
             with patch("os.path.realpath") as realpath:
                 realpath.return_value = "/home/user/android/platform-tools/adb"
                 self.assertEqual(realpath.return_value, Adb().adb())
                 find_executable.assert_called_once_with(
                     "adb")  # find_exectable should be called once
                 realpath.assert_called_once_with(
                     find_executable.return_value)
                 realpath.return_value = find_executable.return_value
                 self.assertEqual(find_executable.return_value, Adb().adb())
             find_executable.return_value = None
             call_count = find_executable.call_count
             with self.assertRaises(EnvironmentError):
                 Adb().adb()
             self.assertEqual(call_count + 1, find_executable.call_count)
Esempio n. 8
0
 def skip_test_adb_raw_cmd(self):
     import subprocess
     adb = Adb()
     adb.adb = MagicMock()
     adb.adb.return_value = "adb"
     args = ["a", "b", "c"]
     with patch("subprocess.Popen") as Popen:
         os.name = "posix"
         adb.raw_cmd(*args)
         Popen.assert_called_once_with(["%s %s" % (adb.adb(), " ".join(args))], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
     with patch("subprocess.Popen") as Popen:
         os.name = "nt"
         adb.raw_cmd(*args)
         Popen.assert_called_once_with([adb.adb()] + list(args), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Esempio n. 9
0
 def test_devices(self):
     adb = Adb()
     adb.raw_cmd = MagicMock()
     adb.raw_cmd.return_value.communicate.return_value = (b"List of devices attached \r\n014E05DE0F02000E\tdevice\r\n489328DKFL7DF\tdevice", b"")
     self.assertEqual(adb.devices(), {"014E05DE0F02000E": "device", "489328DKFL7DF": "device"})
     adb.raw_cmd.assert_called_once_with("devices")
     adb.raw_cmd.return_value.communicate.return_value = (b"List of devices attached \n\r014E05DE0F02000E\tdevice\n\r489328DKFL7DF\tdevice", b"")
     self.assertEqual(adb.devices(), {"014E05DE0F02000E": "device", "489328DKFL7DF": "device"})
     adb.raw_cmd.return_value.communicate.return_value = (b"List of devices attached \r014E05DE0F02000E\tdevice\r489328DKFL7DF\tdevice", b"")
     self.assertEqual(adb.devices(), {"014E05DE0F02000E": "device", "489328DKFL7DF": "device"})
     adb.raw_cmd.return_value.communicate.return_value = (b"List of devices attached \n014E05DE0F02000E\tdevice\n489328DKFL7DF\tdevice", b"")
     self.assertEqual(adb.devices(), {"014E05DE0F02000E": "device", "489328DKFL7DF": "device"})
     adb.raw_cmd.return_value.communicate.return_value = (b"not match", "")
     with self.assertRaises(EnvironmentError):
         adb.devices()
Esempio n. 10
0
    def test_adb_from_env(self):
        home_dir = '/android/home'
        with patch.dict('os.environ', {'ANDROID_HOME': home_dir}):
            with patch('os.path.exists') as exists:
                exists.return_value = True

                os.name = "posix"  # linux
                adb_obj = Adb()
                adb_path = os.path.join(home_dir, "platform-tools", "adb")
                self.assertEqual(adb_obj.adb(), adb_path)
                exists.assert_called_once_with(adb_path)
                self.assertEqual(adb_obj.adb(), adb_path)
                # the second call will return the __adb_cmd directly
                exists.assert_called_once_with(adb_path)

                os.name = "nt"  # linux
                adb_obj = Adb()
                adb_path = os.path.join(home_dir, "platform-tools", "adb.exe")
                self.assertEqual(adb_obj.adb(), adb_path)

                exists.return_value = False
                with self.assertRaises(EnvironmentError):
                    Adb().adb()
Esempio n. 11
0
 def __init__(self,
              serial=None,
              local_port=None,
              device_port=None,
              adb_server_host=None,
              adb_server_port=None):
     self.uiautomator_process = None
     self.adb = Adb(serial=serial,
                    adb_server_host=adb_server_host,
                    adb_server_port=adb_server_port)
     self.device_port = int(device_port) if device_port else DEVICE_PORT
     if local_port:
         self.local_port = local_port
     else:
         try:  # first we will try to use the local port already adb forwarded
             for s, lp, rp in self.adb.forward_list():
                 if s == self.adb.device_serial(
                 ) and rp == 'tcp:%d' % self.device_port:
                     self.local_port = int(lp[4:])
                     break
             else:
                 self.local_port = next_local_port(adb_server_host)
         except:
             self.local_port = next_local_port(adb_server_host)
Esempio n. 12
0
 def skip_test_adb_raw_cmd(self):
     import subprocess
     adb = Adb()
     adb.adb = MagicMock()
     adb.adb.return_value = "adb"
     args = ["a", "b", "c"]
     with patch("subprocess.Popen") as Popen:
         os.name = "posix"
         adb.raw_cmd(*args)
         Popen.assert_called_once_with(
             ["%s %s" % (adb.adb(), " ".join(args))],
             shell=True,
             stdout=subprocess.PIPE,
             stderr=subprocess.PIPE)
     with patch("subprocess.Popen") as Popen:
         os.name = "nt"
         adb.raw_cmd(*args)
         Popen.assert_called_once_with([adb.adb()] + list(args),
                                       shell=True,
                                       stdout=subprocess.PIPE,
                                       stderr=subprocess.PIPE)
Esempio n. 13
0
    def test_forward_list(self):
        adb = Adb()
        adb.version = MagicMock()
        adb.version.return_value = ['1.0.31', '1', '0', '31']
        adb.raw_cmd = MagicMock()
        adb.raw_cmd.return_value.communicate.return_value = (b"014E05DE0F02000E    tcp:9008    tcp:9008\r\n489328DKFL7DF    tcp:9008    tcp:9008", b"")
        self.assertEqual(adb.forward_list(), [['014E05DE0F02000E', 'tcp:9008', 'tcp:9008'], ['489328DKFL7DF', 'tcp:9008', 'tcp:9008']])

        adb.version.return_value = ['1.0.29', '1', '0', '29']
        with self.assertRaises(EnvironmentError):
            adb.forward_list()
Esempio n. 14
0
    def test_forward_list(self):
        adb = Adb()
        adb.version = MagicMock()
        adb.version.return_value = ['1.0.31', '1', '0', '31']
        adb.raw_cmd = MagicMock()
        adb.raw_cmd.return_value.communicate.return_value = (
            b"014E05DE0F02000E    tcp:9008    tcp:9008\r\n489328DKFL7DF    tcp:9008    tcp:9008",
            b"")
        self.assertEqual(adb.forward_list(),
                         [['014E05DE0F02000E', 'tcp:9008', 'tcp:9008'],
                          ['489328DKFL7DF', 'tcp:9008', 'tcp:9008']])

        adb.version.return_value = ['1.0.29', '1', '0', '29']
        with self.assertRaises(EnvironmentError):
            adb.forward_list()
Esempio n. 15
0
    def test_adb_from_env(self):
        home_dir = '/android/home'
        with patch.dict('os.environ', {'ANDROID_HOME': home_dir}):
            with patch('os.path.exists') as exists:
                exists.return_value = True

                os.name = "posix"  # linux
                adb_obj = Adb()
                adb_path = os.path.join(home_dir, "platform-tools", "adb")
                self.assertEqual(adb_obj.adb(), adb_path)
                exists.assert_called_once_with(adb_path)
                self.assertEqual(adb_obj.adb(), adb_path)
                # the second call will return the __adb_cmd directly
                exists.assert_called_once_with(adb_path)

                os.name = "nt"  # linux
                adb_obj = Adb()
                adb_path = os.path.join(home_dir, "platform-tools", "adb.exe")
                self.assertEqual(adb_obj.adb(), adb_path)

                exists.return_value = False
                with self.assertRaises(EnvironmentError):
                    Adb().adb()
Esempio n. 16
0
class AutomatorServer(object):
    """start and quit rpc server on device.
    """
    __jar_files = {
        "bundle.jar": "libs/bundle.jar",
        "uiautomator-stub.jar": "libs/uiautomator-stub.jar"
    }

    __apk_files = ["libs/app-uiautomator.apk", "libs/app-uiautomator-test.apk"]
    # Used for check if installed
    __apk_vercode = 1
    __apk_pkgname = 'com.github.uiautomator'
    __apk_pkgname_test = 'com.github.uiautomator.test'

    __sdk = 0

    handlers = NotFoundHandler()  # handler UI Not Found exception

    def __init__(self,
                 serial=None,
                 local_port=None,
                 device_port=None,
                 adb_server_host=None,
                 adb_server_port=None):
        self.uiautomator_process = None
        self.adb = Adb(serial=serial,
                       adb_server_host=adb_server_host,
                       adb_server_port=adb_server_port)
        self.device_port = int(device_port) if device_port else DEVICE_PORT
        if local_port:
            self.local_port = local_port
        else:
            try:  # first we will try to use the local port already adb forwarded
                for s, lp, rp in self.adb.forward_list():
                    if s == self.adb.device_serial(
                    ) and rp == 'tcp:%d' % self.device_port:
                        self.local_port = int(lp[4:])
                        break
                else:
                    self.local_port = next_local_port(adb_server_host)
            except:
                self.local_port = next_local_port(adb_server_host)

    def push(self):
        base_dir = os.path.dirname(__file__)
        for jar, url in self.__jar_files.items():
            filename = os.path.join(base_dir, url)
            self.adb.cmd("push", filename, "/data/local/tmp/").wait()
        return list(self.__jar_files.keys())

    def need_install(self):
        pkginfo = self.adb.package_info(self.__apk_pkgname)
        if pkginfo is None:
            return True
        if pkginfo['version_code'] != self.__apk_vercode:
            return True
        if self.adb.package_info(self.__apk_pkgname_test) is None:
            return True
        return False

    def install(self):
        base_dir = os.path.dirname(__file__)
        if self.need_install():
            for apk in self.__apk_files:
                self.adb.cmd("install", "-rt", os.path.join(base_dir,
                                                            apk)).wait()

    @property
    def jsonrpc(self):
        return self.jsonrpc_wrap(
            timeout=int(os.environ.get("jsonrpc_timeout", 90)))

    def jsonrpc_wrap(self, timeout):
        server = self
        error_code_base = -32000

        def _JsonRPCMethod(url, method, timeout, restart=True):
            _method_obj = JsonRPCMethod(url, method, timeout)
            _URLError = requests.exceptions.ConnectionError

            def wrapper(*args, **kwargs):
                try:
                    return _method_obj(*args, **kwargs)
                except (_URLError, socket.error, HTTPException) as e:
                    if restart:
                        server.stop()
                        server.start(timeout=30)
                        return _JsonRPCMethod(url, method, timeout,
                                              False)(*args, **kwargs)
                    else:
                        raise
                except JsonRPCError as e:
                    if e.code >= error_code_base - 1:
                        server.stop()
                        server.start()
                        return _method_obj(*args, **kwargs)
                    elif e.code == error_code_base - 2 and self.handlers[
                            'on']:  # Not Found
                        try:
                            self.handlers['on'] = False
                            # any handler returns True will break the left handlers
                            any(
                                handler(self.handlers.get('device', None))
                                for handler in self.handlers['handlers'])
                        finally:
                            self.handlers['on'] = True
                        return _method_obj(*args, **kwargs)
                    raise

            return wrapper

        return JsonRPCClient(self.rpc_uri,
                             timeout=timeout,
                             method_class=_JsonRPCMethod)

    def __jsonrpc(self):
        return JsonRPCClient(self.rpc_uri,
                             timeout=int(os.environ.get("JSONRPC_TIMEOUT",
                                                        90)))

    def sdk_version(self):
        '''sdk version of connected device.'''
        if self.__sdk == 0:
            try:
                self.__sdk = int(
                    self.adb.cmd("shell", "getprop",
                                 "ro.build.version.sdk").communicate()
                    [0].decode("utf-8").strip())
            except:
                pass
        return self.__sdk

    def start(self, timeout=5):
        # always use uiautomator runtest
        # This is because use am instrument can not found a way to stop it
        # this will stuck when Automator not started error occurs
        if True or self.sdk_version() < 18:  # FIXME(ssx): hot fix here
            files = self.push()
            cmd = list(
                itertools.chain(["shell", "uiautomator", "runtest"], files,
                                ["-c", "com.github.uiautomatorstub.Stub"]))
        else:
            self.install()
            cmd = [
                "shell", "am", "instrument", "-w",
                "com.github.uiautomator.test/android.support.test.runner.AndroidJUnitRunner"
            ]

        self.uiautomator_process = self.adb.cmd(*cmd)
        self.adb.forward(self.local_port, self.device_port)

        while not self.alive and timeout > 0:
            time.sleep(0.1)
            timeout -= 0.1
        if not self.alive:
            raise IOError("RPC server not started!")

    def ping(self):
        try:
            return self.__jsonrpc().ping()
        except:
            return None

    @property
    def alive(self):
        '''Check if the rpc server is alive.'''
        return self.ping() == "pong"

    def stop(self):
        '''Stop the rpc server.'''
        if self.uiautomator_process and self.uiautomator_process.poll(
        ) is None:
            res = None
            try:
                res = urllib2.urlopen(self.stop_uri)
                self.uiautomator_process.wait()
            except:
                self.uiautomator_process.kill()
            finally:
                if res is not None:
                    res.close()
                self.uiautomator_process = None
        try:
            out = self.adb.cmd("shell", "ps", "-C",
                               "uiautomator").communicate()[0].decode(
                                   "utf-8").strip().splitlines()
            if out:
                index = out[0].split().index("PID")
                for line in out[1:]:
                    if len(line.split()) > index:
                        self.adb.cmd("shell", "kill", "-9",
                                     line.split()[index]).wait()
        except:
            pass

    @property
    def stop_uri(self):
        return "http://%s:%d/stop" % (self.adb.adb_server_host,
                                      self.local_port)

    @property
    def rpc_uri(self):
        return "http://%s:%d/jsonrpc/0" % (self.adb.adb_server_host,
                                           self.local_port)

    @property
    def screenshot_uri(self):
        return "http://%s:%d/screenshot/0" % (self.adb.adb_server_host,
                                              self.local_port)

    def screenshot(self, filename=None, scale=1.0, quality=100):
        if self.sdk_version() >= 18:
            try:
                req = urllib2.Request("%s?scale=%f&quality=%f" %
                                      (self.screenshot_uri, scale, quality))
                result = urllib2.urlopen(req, timeout=30)
                if filename:
                    with open(filename, 'wb') as f:
                        f.write(result.read())
                        return filename
                else:
                    return result.read()
            except:
                pass
        return None
Esempio n. 17
0
class AutomatorServer(object):
    """start and quit rpc server on device.
    """
    __jar_files = {
        "bundle.jar": "libs/bundle.jar",
        "uiautomator-stub.jar": "libs/uiautomator-stub.jar"
    }

    __apk_files = ["libs/app-uiautomator.apk", "libs/app-uiautomator-test.apk"]
    # Used for check if installed
    __apk_vercode = 2
    __apk_pkgname = 'com.github.uiautomator'
    __apk_pkgname_test = 'com.github.uiautomator.test'

    __sdk = 0
    __httpsession = requests.Session()  # use a standalone session

    handlers = NotFoundHandler()  # handler UI Not Found exception

    def __init__(self,
                 serial=None,
                 local_port=None,
                 device_port=None,
                 adb_server_host=None,
                 adb_server_port=None):
        self.uiautomator_process = None
        self.adb = Adb(serial=serial,
                       adb_server_host=adb_server_host,
                       adb_server_port=adb_server_port)
        self.device_port = int(device_port) if device_port else DEVICE_PORT
        self.__local_port = local_port

    def get_forwarded_port(self):
        for s, lp, rp in self.adb.forward_list():
            if s == self.adb.device_serial(
            ) and rp == 'tcp:%d' % self.device_port:
                return int(lp[4:])
        return None

    @property
    def local_port(self):
        if self.__local_port:
            return self.__local_port
        for i in range(10):  # Max retry 10 times
            forwarded_port = self.get_forwarded_port()
            if forwarded_port:
                self.__local_port = forwarded_port
                return self.__local_port

            port = next_local_port(self.adb.adb_server_host)
            self.adb.forward(port, self.device_port, rebind=False)
        raise RuntimeError("Error run: adb forward tcp:<any> tcp:%d" %
                           self.device_port)

    def push(self):
        base_dir = os.path.dirname(__file__)
        for jar, url in self.__jar_files.items():
            filename = os.path.join(base_dir, url)
            self.adb.run_cmd("push", filename, "/data/local/tmp/")
        return list(self.__jar_files.keys())

    def need_install(self):
        pkginfo = self.adb.package_info(self.__apk_pkgname)
        if pkginfo is None:
            return True
        if pkginfo['version_code'] != self.__apk_vercode:
            return True
        if self.adb.package_info(self.__apk_pkgname_test) is None:
            return True
        return False

    def install(self):
        base_dir = os.path.dirname(__file__)
        if self.need_install():
            debug_print("install apks", self.__apk_files)
            for apk in self.__apk_files:
                self.adb.cmd("install", "-r", os.path.join(base_dir,
                                                           apk)).wait()
        else:
            debug_print("already installed, skip")

    @property
    def jsonrpc(self):
        return self.jsonrpc_wrap(
            timeout=int(os.environ.get("jsonrpc_timeout", 90)))

    def jsonrpc_wrap(self, timeout):
        server = self
        error_code_base = -32000

        def _JsonRPCMethod(url, method, timeout, restart=True):
            _method_obj = JsonRPCMethod(url, method, timeout)
            _URLError = requests.exceptions.ConnectionError

            def wrapper(*args, **kwargs):
                try:
                    return _method_obj(*args, **kwargs)
                except (_URLError, socket.error) as e:
                    if restart:
                        debug_print('restart')
                        server.stop()
                        server.start(timeout=30)
                        return _JsonRPCMethod(url, method, timeout,
                                              False)(*args, **kwargs)
                    else:
                        raise
                except JsonRPCError as e:
                    debug_print('rpc error', e.code, e.message)
                    raise
                    # if e.code >= error_code_base - 1:
                    #     server.stop()
                    #     server.start(timeout=10)
                    #     return _method_obj(*args, **kwargs)
                    # elif e.code == error_code_base - 2 and self.handlers['on']:  # Not Found
                    #     try:
                    #         self.handlers['on'] = False
                    #         # any handler returns True will break the left handlers
                    #         any(handler(self.handlers.get('device', None)) for handler in self.handlers['handlers'])
                    #     finally:
                    #         self.handlers['on'] = True
                    #     return _method_obj(*args, **kwargs)
                    # raise

            return wrapper

        return JsonRPCClient(self.rpc_uri,
                             timeout=timeout,
                             method_class=_JsonRPCMethod)

    def __jsonrpc(self):
        return JsonRPCClient(self.rpc_uri,
                             timeout=int(os.environ.get("JSONRPC_TIMEOUT",
                                                        90)))

    def sdk_version(self):
        '''sdk version of connected device.'''
        if self.__sdk == 0:
            try:
                self.__sdk = int(
                    self.adb.cmd("shell", "getprop",
                                 "ro.build.version.sdk").communicate()
                    [0].decode("utf-8").strip())
            except:
                pass
        return self.__sdk

    def ro_product(self):
        return self.adb.shell("getprop", "ro.build.product").strip()

    def ro_manufacturer(self):
        return self.adb.shell("getprop",
                              "ro.product.manufacturer").strip().lower()

    def start(self, timeout=5):
        # 对应关系列表
        # http://www.cnblogs.com/lipeineng/archive/2017/01/06/6257859.html
        # Android 4.3 (sdk=18)
        # Android 5.0 (sdk=21)
        debug_print('sdk version(instrument>=18)', self.sdk_version())
        debug_print('product', self.ro_product())
        debug_print('manufacturer', self.ro_manufacturer())
        if self.sdk_version() >= 100:
            self.install()
            cmd = [
                "shell", "am", "instrument", "-w", "-e", "debug", "false",
                "-e", "class", "com.github.uiautomator.stub.Stub",
                "com.github.uiautomator.test/android.support.test.runner.AndroidJUnitRunner"
            ]
        else:
            files = self.push()
            cmd = list(
                itertools.chain(["shell", "uiautomator", "runtest"], files,
                                ["-c", "com.github.uiautomatorstub.Stub"]))

        debug_print('$ ' + subprocess.list2cmdline(list(cmd)))
        self.uiautomator_process = self.adb.cmd(*cmd)
        self.adb.forward(self.local_port, self.device_port)

        while not self.alive and timeout > 0:
            time.sleep(0.2)
            timeout -= 0.2
            debug_print('poll', self.uiautomator_process.poll())
            if self.uiautomator_process.poll() is not None:
                stdout = self.uiautomator_process.stdout.read()
                raise IOError("uiautomator start failed: " + stdout)
        if not self.alive:
            raise IOError("RPC server not started!")

    def ping(self):
        try:
            return self.__jsonrpc().ping()
        except:
            return None

    def info(self):
        try:
            return self.__jsonrpc().deviceInfo()
        except:
            return False

    @property
    def alive(self):
        '''Check if the rpc server is alive.'''
        return self.ping() == "pong" and self.info()
        # return self.ping() == "pong"

    def stop(self):
        '''Stop the rpc server.'''
        if self.uiautomator_process and self.uiautomator_process.poll(
        ) is None:
            res = None
            try:
                res = requests.get(self.stop_uri)
                self.uiautomator_process.wait()
            except:
                self.uiautomator_process.kill()
            finally:
                if res is not None:
                    res.close()
                self.uiautomator_process = None
        try:
            out = self.adb.cmd("shell", "ps", "-C",
                               "uiautomator").communicate()[0].decode(
                                   "utf-8").strip().splitlines()
            if out:
                index = out[0].split().index("PID")
                for line in out[1:]:
                    if len(line.split()) > index:
                        self.adb.cmd("shell", "kill", "-9",
                                     line.split()[index]).wait()
        except:
            pass

    @property
    def stop_uri(self):
        return "http://%s:%d/stop" % (self.adb.adb_server_host,
                                      self.local_port)

    @property
    def rpc_uri(self):
        return "http://%s:%d/jsonrpc/0" % (self.adb.adb_server_host,
                                           self.local_port)

    @property
    def screenshot_uri(self):
        return "http://%s:%d/screenshot/0" % (self.adb.adb_server_host,
                                              self.local_port)

    def screenshot(self, filename=None, scale=1.0, quality=100):
        # since sdk version is always great 18, so no check here
        # also can not use requests.Session, this will break /jsonrpc/0 requests
        try:
            r = self.__httpsession.get(self.screenshot_uri,
                                       params=dict(scale=scale,
                                                   quality=quality),
                                       timeout=30)
            if filename:
                with open(filename, 'wb') as f:
                    f.write(r.content)
                    return filename
            else:
                return r.content
        except:
            pass
Esempio n. 18
0
 def __init__(self, serial=None, local_port=None, device_port=None, adb_server_host=None, adb_server_port=None):
     self.uiautomator_process = None
     self.adb = Adb(serial=serial, adb_server_host=adb_server_host, adb_server_port=adb_server_port)
     self.device_port = int(device_port) if device_port else DEVICE_PORT
     self.__local_port = local_port
Esempio n. 19
0
class AutomatorServer(object):
    """start and quit rpc server on device.
    """
    __jar_files = {
        "bundle.jar": "libs/bundle.jar",
        "uiautomator-stub.jar": "libs/uiautomator-stub.jar"
    }

    __apk_files = ["libs/app-uiautomator.apk", "libs/app-uiautomator-test.apk"]
    # Used for check if installed
    # 2: fix bug
    # 3: screenrecord support
    __apk_vercode = 3
    __apk_pkgname = 'com.github.uiautomator'
    __apk_pkgname_test = 'com.github.uiautomator.test'

    __sdk = 0
    __httpsession = requests.Session() # use a standalone session

    handlers = NotFoundHandler()  # handler UI Not Found exception

    def __init__(self, serial=None, local_port=None, device_port=None, adb_server_host=None, adb_server_port=None):
        self.uiautomator_process = None
        self.adb = Adb(serial=serial, adb_server_host=adb_server_host, adb_server_port=adb_server_port)
        self.device_port = int(device_port) if device_port else DEVICE_PORT
        self.__local_port = local_port

    def get_forwarded_port(self):
        for s, lp, rp in self.adb.forward_list():
            if s == self.adb.device_serial() and rp == 'tcp:%d' % self.device_port:
                return int(lp[4:])
        return None

    @property
    def local_port(self):
        if self.__local_port:
            return self.__local_port
        for i in range(10): # Max retry 10 times
            forwarded_port = self.get_forwarded_port()
            if forwarded_port:
                self.__local_port = forwarded_port
                return self.__local_port

            port = next_local_port(self.adb.adb_server_host)
            self.adb.forward(port, self.device_port, rebind=False)
        raise RuntimeError("Error run: adb forward tcp:<any> tcp:%d" % self.device_port)

    def push(self):
        base_dir = os.path.dirname(__file__)
        for jar, url in self.__jar_files.items():
            filename = os.path.join(base_dir, url)
            self.adb.run_cmd("push", filename, "/data/local/tmp/")
        return list(self.__jar_files.keys())

    def need_install(self):
        pkginfo = self.adb.package_info(self.__apk_pkgname)
        if pkginfo is None:
            return True
        if pkginfo['version_code'] != self.__apk_vercode:
            return True
        if self.adb.package_info(self.__apk_pkgname_test) is None:
            return True
        return False
        
    def install(self):
        base_dir = os.path.dirname(__file__)
        if self.need_install():
            debug_print("install apks", self.__apk_files)
            for apk in self.__apk_files:
                self.adb.cmd("install", "-r", os.path.join(base_dir, apk)).wait()
        else:
            debug_print("already installed, skip")
    
    def uninstall(self):
        self.adb.cmd("uninstall", self.__apk_pkgname)
        self.adb.cmd("uninstall", self.__apk_pkgname_test)

    @property
    def jsonrpc(self):
        return self.jsonrpc_wrap(timeout=int(os.environ.get("jsonrpc_timeout", 90)))

    def jsonrpc_wrap(self, timeout):
        server = self
        error_code_base = -32000

        def _JsonRPCMethod(url, method, timeout, restart=True):
            _method_obj = JsonRPCMethod(url, method, timeout)
            _URLError = requests.exceptions.ConnectionError

            def wrapper(*args, **kwargs):
                try:
                    return _method_obj(*args, **kwargs)
                except (_URLError, socket.error) as e:
                    if restart:
                        debug_print('restart')
                        server.stop()
                        server.start(timeout=30)
                        return _JsonRPCMethod(url, method, timeout, False)(*args, **kwargs)
                    else:
                        raise
                except JsonRPCError as e:
                    debug_print('rpc error', e.code, e.message)
                    if 'UiAutomation not connected' in e.message:
                        self.uninstall()
                        return _JsonRPCMethod(url, method, timeout, False)(*args, **kwargs)
                    else:
                    # if e.message
                        raise
                    # if e.code >= error_code_base - 1:
                    #     server.stop()
                    #     server.start(timeout=10)
                    #     return _method_obj(*args, **kwargs)
                    # elif e.code == error_code_base - 2 and self.handlers['on']:  # Not Found
                    #     try:
                    #         self.handlers['on'] = False
                    #         # any handler returns True will break the left handlers
                    #         any(handler(self.handlers.get('device', None)) for handler in self.handlers['handlers'])
                    #     finally:
                    #         self.handlers['on'] = True
                    #     return _method_obj(*args, **kwargs)
                    # raise
            return wrapper

        return JsonRPCClient(self.rpc_uri,
                             timeout=timeout,
                             method_class=_JsonRPCMethod)

    def __jsonrpc(self):
        return JsonRPCClient(self.rpc_uri, timeout=int(os.environ.get("JSONRPC_TIMEOUT", 90)))

    def sdk_version(self):
        '''sdk version of connected device.'''
        if self.__sdk == 0:
            try:
                self.__sdk = int(self.adb.cmd("shell", "getprop", "ro.build.version.sdk").communicate()[0].decode("utf-8").strip())
            except:
                pass
        return self.__sdk

    def ro_product(self):
        return self.adb.shell("getprop", "ro.build.product").strip()

    def ro_manufacturer(self):
        return self.adb.shell("getprop", "ro.product.manufacturer").strip().lower()

    def start(self, timeout=5):
        # 对应关系列表
        # http://www.cnblogs.com/lipeineng/archive/2017/01/06/6257859.html
        # Android 4.3 (sdk=18)
        # Android 5.0 (sdk=21)
        debug_print('sdk version(instrument>=18)', self.sdk_version())
        debug_print('product', self.ro_product())
        debug_print('manufacturer', self.ro_manufacturer())
        if self.sdk_version() >= 18:
            self.install()
            cmd = ["shell", "am", "instrument", "-w", "-r",
                    "-e", "debug", "false", 
                    "-e", "class", "com.github.uiautomator.stub.Stub",
                    "com.github.uiautomator.test/android.support.test.runner.AndroidJUnitRunner"]
        else:
            files = self.push()
            cmd = list(itertools.chain(
                ["shell", "uiautomator", "runtest"],
                files,
                ["-c", "com.github.uiautomatorstub.Stub"]
            ))

        debug_print('$ ' + subprocess.list2cmdline(list(cmd)))
        self.uiautomator_process = self.adb.cmd(*cmd)
        self.adb.forward(self.local_port, self.device_port)

        while not self.alive and timeout > 0:
            time.sleep(0.2)
            timeout -= 0.2
            debug_print('poll', self.uiautomator_process.poll())
            if self.uiautomator_process.poll() is not None:
                stdout = self.uiautomator_process.stdout.read()
                raise IOError("uiautomator start failed: " + str(stdout))
        if not self.alive:
            raise IOError("RPC server not started!")

    def ping(self):
        try:
            return self.__jsonrpc().ping()
        except:
            return None

    def info(self):
        try:
            return self.__jsonrpc().deviceInfo()
        except:
            return False

    @property
    def alive(self):
        '''Check if the rpc server is alive.'''
        return self.ping() == "pong" and self.info()
        # return self.ping() == "pong"

    def stop(self):
        '''Stop the rpc server.'''
        if self.uiautomator_process and self.uiautomator_process.poll() is None:
            res = None
            try:
                res = requests.get(self.stop_uri)
                self.uiautomator_process.wait()
            except:
                self.uiautomator_process.kill()
            finally:
                if res is not None:
                    res.close()
                self.uiautomator_process = None
        try:
            out = self.adb.cmd("shell", "ps", "-C", "uiautomator").communicate()[0].decode("utf-8").strip().splitlines()
            if out:
                index = out[0].split().index("PID")
                for line in out[1:]:
                    if len(line.split()) > index:
                        self.adb.cmd("shell", "kill", "-9", line.split()[index]).wait()
        except:
            pass

    @property
    def stop_uri(self):
        return "http://%s:%d/stop" % (self.adb.adb_server_host, self.local_port)

    @property
    def rpc_uri(self):
        return "http://%s:%d/jsonrpc/0" % (self.adb.adb_server_host, self.local_port)

    @property
    def screenshot_uri(self):
        return "http://%s:%d/screenshot/0" % (self.adb.adb_server_host, self.local_port)

    def screenshot(self, filename=None, scale=1.0, quality=100):
        # since sdk version is always great 18, so no check here
        # also can not use requests.Session, this will break /jsonrpc/0 requests
        try:
            r = self.__httpsession.get(self.screenshot_uri, params=dict(scale=scale, quality=quality), timeout=30)
            if filename:
                with open(filename, 'wb') as f:
                    f.write(r.content)
                    return filename
            else:
                return r.content
        except:
            pass
Esempio n. 20
0
    def skip_test_adb_cmd_server_host(self):
        adb = Adb(adb_server_host="localhost", adb_server_port=5037)
        adb.adb = MagicMock()
        adb.adb.return_value = "adb"
        adb.device_serial = MagicMock()
        adb.device_serial.return_value = "ANDROID_SERIAL"
        args = ["a", "b", "c"]
        with patch("subprocess.Popen") as Popen:
            os.name = "nt"
            adb.raw_cmd(*args)
            Popen.assert_called_once_with(
                [adb.adb()] + args,
                shell=True,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE
            )

        adb = Adb(adb_server_host="test.com", adb_server_port=1000)
        adb.adb = MagicMock()
        adb.adb.return_value = "adb"
        adb.device_serial = MagicMock()
        adb.device_serial.return_value = "ANDROID_SERIAL"
        args = ["a", "b", "c"]
        with patch("subprocess.Popen") as Popen:
            os.name = "posix"
            adb.raw_cmd(*args)
            Popen.assert_called_once_with(
                [" ".join([adb.adb()] + ["-H", "test.com", "-P", "1000"] + args)],
                shell=True,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE
            )
Esempio n. 21
0
    def test_device_serial(self):
        with patch.dict('os.environ', {'ANDROID_SERIAL': "ABCDEF123456"}):
            adb = Adb()
            adb.devices = MagicMock()
            adb.devices.return_value = {"ABCDEF123456": "device"}
            self.assertEqual(adb.device_serial(), "ABCDEF123456")
        with patch.dict('os.environ', {'ANDROID_SERIAL': "ABCDEF123456"}):
            adb = Adb()
            adb.devices = MagicMock()
            adb.devices.return_value = {"ABCDEF123456": "device", "123456ABCDEF": "device"}
            self.assertEqual(adb.device_serial(), "ABCDEF123456")
        with patch.dict('os.environ', {'ANDROID_SERIAL': "HIJKLMN098765"}):
            adb = Adb()
            adb.devices = MagicMock()
            adb.devices.return_value = {"ABCDEF123456": "device", "123456ABCDEF": "device"}
            self.assertEqual(adb.device_serial(), "HIJKLMN098765")
        with patch.dict('os.environ', {}, clear=True):
            adb = Adb()
            adb.devices = MagicMock()
            adb.devices.return_value = {"ABCDEF123456": "device", "123456ABCDEF": "device"}
            with self.assertRaises(EnvironmentError):
                adb.device_serial()
        with patch.dict('os.environ', {}, clear=True):
            adb = Adb()
            adb.devices = MagicMock()
            adb.devices.return_value = {"ABCDEF123456": "device"}
            self.assertEqual(adb.device_serial(), "ABCDEF123456")

        with self.assertRaises(EnvironmentError):
            adb = Adb()
            adb.devices = MagicMock()
            adb.devices.return_value = {}
            adb.device_serial()
Esempio n. 22
0
    def test_device_serial(self):
        with patch.dict('os.environ', {'ANDROID_SERIAL': "ABCDEF123456"}):
            adb = Adb()
            adb.devices = MagicMock()
            adb.devices.return_value = {"ABCDEF123456": "device"}
            self.assertEqual(adb.device_serial(), "ABCDEF123456")
        with patch.dict('os.environ', {'ANDROID_SERIAL': "ABCDEF123456"}):
            adb = Adb()
            adb.devices = MagicMock()
            adb.devices.return_value = {
                "ABCDEF123456": "device",
                "123456ABCDEF": "device"
            }
            self.assertEqual(adb.device_serial(), "ABCDEF123456")
        with patch.dict('os.environ', {'ANDROID_SERIAL': "HIJKLMN098765"}):
            adb = Adb()
            adb.devices = MagicMock()
            adb.devices.return_value = {
                "ABCDEF123456": "device",
                "123456ABCDEF": "device"
            }
            self.assertEqual(adb.device_serial(), "HIJKLMN098765")
        with patch.dict('os.environ', {}, clear=True):
            adb = Adb()
            adb.devices = MagicMock()
            adb.devices.return_value = {
                "ABCDEF123456": "device",
                "123456ABCDEF": "device"
            }
            with self.assertRaises(EnvironmentError):
                adb.device_serial()
        with patch.dict('os.environ', {}, clear=True):
            adb = Adb()
            adb.devices = MagicMock()
            adb.devices.return_value = {"ABCDEF123456": "device"}
            self.assertEqual(adb.device_serial(), "ABCDEF123456")

        with self.assertRaises(EnvironmentError):
            adb = Adb()
            adb.devices = MagicMock()
            adb.devices.return_value = {}
            adb.device_serial()
Esempio n. 23
0
    def skip_test_adb_cmd_server_host(self):
        adb = Adb(adb_server_host="localhost", adb_server_port=5037)
        adb.adb = MagicMock()
        adb.adb.return_value = "adb"
        adb.device_serial = MagicMock()
        adb.device_serial.return_value = "ANDROID_SERIAL"
        args = ["a", "b", "c"]
        with patch("subprocess.Popen") as Popen:
            os.name = "nt"
            adb.raw_cmd(*args)
            Popen.assert_called_once_with([adb.adb()] + args,
                                          shell=True,
                                          stdout=subprocess.PIPE,
                                          stderr=subprocess.PIPE)

        adb = Adb(adb_server_host="test.com", adb_server_port=1000)
        adb.adb = MagicMock()
        adb.adb.return_value = "adb"
        adb.device_serial = MagicMock()
        adb.device_serial.return_value = "ANDROID_SERIAL"
        args = ["a", "b", "c"]
        with patch("subprocess.Popen") as Popen:
            os.name = "posix"
            adb.raw_cmd(*args)
            Popen.assert_called_once_with([
                " ".join([adb.adb()] + ["-H", "test.com", "-P", "1000"] + args)
            ],
                                          shell=True,
                                          stdout=subprocess.PIPE,
                                          stderr=subprocess.PIPE)
Esempio n. 24
0
    def test_adb_cmd(self):
        adb = Adb()
        adb.device_serial = MagicMock()
        adb.device_serial.return_value = "ANDROID_SERIAL"
        adb.raw_cmd = MagicMock()
        args = ["a", "b", "c"]
        adb.cmd(*args)
        adb.raw_cmd.assert_called_once_with("-s", "%s" % adb.device_serial(),
                                            *args)

        adb.device_serial.return_value = "ANDROID SERIAL"
        adb.raw_cmd = MagicMock()
        args = ["a", "b", "c"]
        adb.cmd(*args)
        adb.raw_cmd.assert_called_once_with("-s", "'%s'" % adb.device_serial(),
                                            *args)
Esempio n. 25
0
 def test_forward(self):
     adb = Adb()
     adb.cmd = MagicMock()
     adb.forward(90, 91)
     adb.cmd.assert_called_once_with("forward", "tcp:90", "tcp:91")
     adb.cmd.return_value.wait.assert_called_once_with()
Esempio n. 26
0
 def test_forward(self):
     adb = Adb()
     adb.cmd = MagicMock()
     adb.forward(90, 91)
     adb.cmd.assert_called_once_with("forward", "tcp:90", "tcp:91")
     adb.cmd.return_value.wait.assert_called_once_with()