Skip to content

Commit

Permalink
change logic of start uiautomator, upgrade apk version
Browse files Browse the repository at this point in the history
  • Loading branch information
codeskyblue committed Dec 25, 2019
1 parent 26de342 commit b1aa1df
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 30 deletions.
13 changes: 13 additions & 0 deletions examples/test_simple_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# coding: utf-8
#

import uiautomator2 as u2


def test_simple():
d = u2.connect()
print(d.info)


if __name__ == "__main__":
test_simple()
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ whichcraft
logzero~=1.5
progress~=1.3
retry~=0.9
adbutils>=0.6.4,<1.0
adbutils>=0.7.0,<1.0
Deprecated~=1.2.6
Pillow # not support in QPython
lxml>=4.3 # not support in QPython
10 changes: 10 additions & 0 deletions tests/runtest.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/bin/bash
#

set -e

if [[ $# -eq 0 ]]
then
python3 -m adbutils -i https://github.com/appium/java-client/raw/master/src/test/java/io/appium/java_client/ApiDemos-debug.apk
fi
py.test -v "$@"
79 changes: 62 additions & 17 deletions uiautomator2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,12 @@ def alive(self):
except EnvironmentError:
return False

def _kill_process_by_name(self, name):
for p in self._iter_process():
if p.name == name and p.user == "shell":
logger.debug("kill uiautomator")
self.shell(["kill", "-9", str(p.pid)])

def service(self, name):
""" Manage service start or stop
Expand All @@ -648,10 +654,20 @@ def raise_for_status(self, res):
res.raise_for_status()

def start(self):
"""
Manually run with the following command:
adb shell am instrument -w -r -e debug false -e class com.github.uiautomator.stub.Stub \
com.github.uiautomator.test/android.support.test.runner.AndroidJUnitRunner
"""
# kill uiautomator
res = u2obj._reqsess.post(self.service_url)
self.raise_for_status(res)

def stop(self):
"""
1. stop command which launched with uiautomator 1.0
Eg: adb shell uiautomator runtest androidUiAutomator.jar
"""
res = u2obj._reqsess.delete(self.service_url)
self.raise_for_status(res)

Expand Down Expand Up @@ -694,6 +710,7 @@ def reset_uiautomator(self):
with self.__uiautomator_lock:
if self.alive:
return

logger.debug("force reset uiautomator")
success = self._force_reset_uiautomator_v2() # uiautomator 2.0
if not success:
Expand Down Expand Up @@ -721,42 +738,47 @@ def _start_uiautomator_app(self):
package_name = "com.github.uiautomator"
if self.settings['uiautomator_runtest_app_background']:
self.shell(f"pm grant {package_name} android.permission.READ_PHONE_STATE")
self.app_start(package_name, launch_timeout=10)
self.app_start(package_name, ".ToastActivity")
else:
self.shell(f'am startservice -n {package_name}/.Service')

def _force_reset_uiautomator_v2(self):
brand = self.shell("getprop ro.product.brand").output.strip()
logger.debug("Product-brand: %s", brand)
logger.debug("Device: %s, %s", brand, self.serial)
package_name = "com.github.uiautomator"

first_killed = False
logger.debug("app-start com.github.uiautomator")
self._start_uiautomator_app()
self.uiautomator.start()

def is_infront():
return self.app_current()['package'] == package_name
# logger.debug("app-start com.github.uiautomator")

self.shell(["am", "force-stop", package_name])
logger.debug("stop app: %s", package_name)
# self._start_uiautomator_app()
# self.uiautomator.start()
self.uiautomator.stop()

# stop command which launched with uiautomator 1.0
# eg: adb shell uiautomator runtest androidUiAutomator.jar

logger.debug("kill process(ps): uiautomator")
self._kill_process_by_name("uiautomator")
self.uiautomator.start()

time.sleep(1)
# wait until uiautomator2 service is working
time.sleep(.5)
deadline = time.time() + 20.0
while time.time() < deadline:
logger.debug("uiautomator is starting ...")
logger.debug("uiautomator-v2 is starting ...")
if not self.uiautomator.running():
break

# apk might killed when call uiautomator runtest, so here launch again
if not first_killed and not is_infront():
logger.debug("app-start com.github.uiautomator again")
self._start_uiautomator_app()
if not first_killed and package_name not in self.app_list_running():
first_killed = True
# self._start_uiautomator_app()

if self.alive:
if is_infront():
self.shell(['input', 'keyevent', 'BACK'])
return True
time.sleep(1)
time.sleep(1.0)

self.uiautomator.stop()
return False
Expand Down Expand Up @@ -1093,8 +1115,31 @@ def app_list_running(self) -> list:
"""
output, _ = self.shell(['pm', 'list', 'packages'])
packages = re.findall(r'package:([^\s]+)', output)
process_names = re.findall(r'([^\s]+)$', self.shell('ps').output, re.M)
process_names = re.findall(r'([^\s]+)$', self.shell('ps; ps -A').output, re.M)
return list(set(packages).intersection(process_names))

def _iter_process(self):
"""
List processes by cmd:ps
Returns:
list of Process(pid, name)
"""
headers, pids = [], {}
Header = None
Process = namedtuple("Process", ["user", "pid", "name"])
for line in self.shell("ps; ps -A").output.splitlines():
# USER PID ..... NAME
fields = line.strip().split()
if fields[0] == "USER":
continue
if not fields[1].isdigit():
continue
user, pid, name = fields[0], int(fields[1]), fields[-1]
if pid in pids:
continue
pids[pid] = True
yield Process(user, pid, name)

def app_stop(self, pkg_name):
""" Stop one application: am force-stop"""
Expand Down
8 changes: 8 additions & 0 deletions uiautomator2/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,14 @@ def __init__(self, d: "uiautomator2.Device"):

def send_click(self, x, y):
return self._d.click(x, y)

def getpixel(self, x, y):
"""
Returns:
(r, g, b)
"""
screenshot = self.screenshot()
return screenshot.convert("RGB").getpixel((x, y))

def match(self, imdata: Union[np.ndarray, str]):
"""
Expand Down
25 changes: 16 additions & 9 deletions uiautomator2/init.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# coding: utf-8
#

import datetime
import hashlib
import logging
import os
Expand All @@ -12,7 +13,9 @@
import requests
from logzero import logger, setup_logger
from retry import retry
from uiautomator2.version import __apk_version__, __atx_agent_version__, __jar_version__, __version__

from uiautomator2.version import (__apk_version__, __atx_agent_version__,
__jar_version__, __version__)

appdir = os.path.join(os.path.expanduser("~"), '.uiautomator2')

Expand Down Expand Up @@ -201,14 +204,21 @@ def is_apk_outdated(self):
"""
apk_debug = self._device.package_info("com.github.uiautomator")
apk_debug_test = self._device.package_info("com.github.uiautomator.test")
self.logger.debug("apk-debug package-info: %s", apk_debug)
self.logger.debug("apk-debug-test package-info: %s", apk_debug_test)
if not apk_debug or not apk_debug_test:
return True
if apk_debug['version_name'] != __apk_version__:
self.logger.info("package com.github.uiautomator version %s, latest %s", apk_debug['version_name'], __apk_version__)
return True
self.logger.debug("signature: %s", apk_debug['signature'])

if apk_debug['signature'] != apk_debug_test['signature']:
self.logger.info("package com.github.uiautomator does not have a signature matching the target com.github.uiautomator")
return True
# On vivo-Y67 signature might not same, but signature matched.
# So here need to check first_install_time again
max_delta = datetime.timedelta(minutes=3)
if abs(apk_debug['first_install_time'] - apk_debug_test['first_install_time']) > max_delta:
self.logger.debug("package com.github.uiautomator does not have a signature matching the target com.github.uiautomator")
return True
return False

def is_atx_agent_outdated(self):
Expand Down Expand Up @@ -245,11 +255,8 @@ def check_install(self):

if self.is_atx_agent_outdated():
return False

packages = d.list_packages()
if 'com.github.uiautomator' not in packages:
return False
if 'com.github.uiautomator.test' not in packages:

if self.is_apk_outdated():
return False

return True
Expand Down
3 changes: 1 addition & 2 deletions uiautomator2/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -596,8 +596,7 @@ def set_clipboard(self, text, label=None):
def __getattr__(self, key):
if key in [
"wait_timeout", "window_size", "shell", "xpath", "widget",
"watcher", "settings",
"app_current", "app_start", "app_stop"
"watcher", "settings", "app_current", "app_start", "app_stop"
]:
return getattr(self.server, key)
raise AttributeError(f"Session object has no attribute '{key}'")
Expand Down
4 changes: 3 additions & 1 deletion uiautomator2/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@

# See ChangeLog for details

__apk_version__ = '2.0.3'
__apk_version__ = '2.0.5'
# 2.0.5 add ToastActivity to show toast or just launch and quit
# 2.0.4 fix floatingWindow crash on Sumsung Android 9
# 2.0.3 use android.app.Service instead of android.app.intentService to simpfy logic
# 2.0.2 fix error: AndroidQ Service must be explicit
# 2.0.1 fix AndroidQ support
Expand Down

0 comments on commit b1aa1df

Please sign in to comment.