Skip to content

Commit

Permalink
use u2.jar instead of test apk (#1004)
Browse files Browse the repository at this point in the history
* use u2.jar instead of test apk
* update toast api, use d.last_toast to get toast, d.clear_toast() to reset it
* add more unittests
* fix multi thread when use connect
  • Loading branch information
codeskyblue authored Jul 16, 2024
1 parent 7ba4852 commit c25a3e5
Show file tree
Hide file tree
Showing 36 changed files with 757 additions and 278 deletions.
8 changes: 6 additions & 2 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
branch = True

omit =
"tests/*"
"docs/*"
/tests/**
/docs/*
/*_tests/**

[report]
; Regexes for lines to exclude from consideration
Expand All @@ -23,4 +24,7 @@ exclude_also =
; Don't complain about abstract methods, they aren't run:
@(abc\.)?abstractmethod

except adbutils.AdbError
@deprecated

ignore_errors = True
5 changes: 5 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ format:
test:
poetry run pytest -v mobile_tests/

covtest:
poetry run coverage run -m pytest -v demo_tests tests
poetry run coverage html --include 'uiautomator2/**'


cov:
poetry run pytest -v tests/ \
--cov-config=.coveragerc \
Expand Down
48 changes: 47 additions & 1 deletion QUICK_REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,23 @@ import uiautomator2 as u2
d = u2.connect("--serial-here--") # 只有一个设备也可以省略参数
d = u2.connect() # 一个设备时, read env-var ANDROID_SERIAL

# 信息获取
print(d.info)
print(d.device_info)
width, height = d.window_size()
print(d.wlan_ip)
print(d.serial)

## 截图
d.screenshot() # Pillow.Image.Image格式
d.screenshot().save("current_screen.jpg")

# 获取hierarchy
d.dump_hierarchy() # str

# 设置查找元素等待时间,单位秒
d.implicitly_wait(10)

d.app_current() # 获取前台应用 packageName, activity
d.app_start("io.appium.android.apis") # 启动应用
d.app_start("io.appium.android.apis", stop=True) # 启动应用前停止应用
Expand All @@ -18,29 +35,58 @@ app.click(10, 20) # 坐标点击

# 无session状态下操作
d.click(10, 20) # 坐标点击
d.long_click(10, 10)
d.double_click(10, 20)

d.swipe(10, 20, 80, 90) # 从(10, 20)滑动到(80, 90)
d.swipe_ext("right") # 整个屏幕右滑动
d.swipe_ext("right", scale=0.9) # 屏幕右滑,滑动距离为屏幕宽度的90%
d.drag(10, 10, 80, 80)

d.press("back") # 模拟点击返回键
d.press("home") # 模拟Home键
d.long_press("volume_up")

d.send_keys("hello world") # 模拟输入,需要光标已经在输入框中才可以
d.clear_text() # 清空输入框

d.screen_on() # wakeUp
d.screen_off() # sleep screen

print(d.orientation) # left|right|natural|upsidedown
d.orientation = 'natural'
d.freeze_rotation(True)

print(d.last_toast) # 获取显示的toast文本
d.clear_toast() # 重置一下

d.open_notification()
d.open_quick_settings()

d.open_url("https://www.baidu.com")
d.keyevent("HOME") # same as: input keyevent HOME

# 执行shell命令
output, exit_code = d.shell("ps -A", timeout=60) # 执行shell命令,获取输出和exitCode
output = d.shell("pwd").output # 这样也可以
exit_code = d.shell("pwd").exit_code # 这样也可以

# Selector操作
sel = d(text="Gmail")
sel.wait()
sel.click()

```

```python
# XPath操作
# 元素操作
d.xpath("立即开户").wait() # 等待元素,最长等10s(默认)
d.xpath("立即开户").wait(timeout=10) # 修改默认等待时间

# 常用配置
d.settings['wait_timeout'] = 20 # 控件查找默认等待时间(默认20s)

# xpath操作
d.xpath("立即开户").click() # 包含查找等待+点击操作,匹配text或者description等于立即开户的按钮
d.xpath("//*[@text='私人FM']/../android.widget.ImageView").click()

Expand Down
25 changes: 4 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1292,30 +1292,13 @@ print(d.current_ime()) # 获取当前输入法ID
> 更多参考: [IME_ACTION_CODE](https://developer.android.com/reference/android/view/inputmethod/EditorInfo)
### Toast (2.2版本之后有添加回来)
Show Toast (好像有点bug)
### Toast
```python
d.toast.show("Hello world")
d.toast.show("Hello world", 1.0) # show for 1.0s, default 1.0s
print(d.last_toast) # get last toast, if not toast return None
d.clear_toast()
```
Get Toast
```python
# [Args]
# 5.0: max wait timeout. Default 10.0
# 10.0: cache time. return cache toast if already toast already show up in recent 10 seconds. Default 10.0 (Maybe change in the furture)
# "default message": return if no toast finally get. Default None
d.toast.get_message(5.0, 10.0, "default message")
# common usage
assert "Short message" in d.toast.get_message(5.0, default="")
# clear cached toast
d.toast.reset()
# Now d.toast.get_message(0) is None
```
> Fixed in version 3.2.0
### XPath
Java uiautoamtor中默认是不支持xpath的,所以这里属于扩展的一个功能。速度不是这么的快。
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
19 changes: 19 additions & 0 deletions demo_tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# coding: utf-8
# author: codeskyblue

import pytest
import uiautomator2 as u2


@pytest.fixture(scope="function")
def d():
_d = u2.connect_usb()
_d.press("home")
yield _d


@pytest.fixture(scope="function")
def app(d: u2.Device):
d.app_start("com.example.u2testdemo", stop=True)
d(text="Addition").wait()
yield d
57 changes: 57 additions & 0 deletions demo_tests/test_app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# coding: utf-8
# author: codeskyblue

import pytest
import uiautomator2 as u2


PACKAGE = "com.example.u2testdemo"


def test_wait_activity(d: u2.Device):
# assert app.wait_activity('.MainActivity', timeout=10)

d.app_start(PACKAGE, activity=".AdditionActivity", wait=True)
assert d.wait_activity('.AdditionActivity', timeout=3)
assert not d.wait_activity('.NotExistActivity', timeout=1)


def test_app_wait(app: u2.Device):
assert app.app_wait(PACKAGE, front=True)


def test_app_start_stop(d: u2.Device):
assert PACKAGE in d.app_list()
d.app_stop(PACKAGE)
assert PACKAGE not in d.app_list_running()
d.app_start(PACKAGE, wait=True)
assert PACKAGE in d.app_list_running()


def test_app_clear(d: u2.Device):
d.app_clear(PACKAGE)
# d.app_stop_all()


def test_app_info(d: u2.Device):
d.app_info(PACKAGE)
with pytest.raises(u2.AppNotFoundError):
d.app_info("not.exist.package")


def test_auto_grant_permissions(d: u2.Device):
d.app_auto_grant_permissions(PACKAGE)


def test_session(d: u2.Device):
app = d.session(PACKAGE)
assert app.running() is True
assert app.pid > 0
old_pid = app.pid

app.restart()
assert old_pid != app.pid

with app:
app(text="Addition").info

41 changes: 41 additions & 0 deletions demo_tests/test_core.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# coding: utf-8
# author: codeskyblue

from typing import Optional
import uiautomator2 as u2


def get_app_process_pid(d: u2.Device) -> Optional[int]:
for line in d.shell("ps -u shell").output.splitlines():
fields = line.split()
if fields[-1] == 'app_process':
pid = fields[1]
return int(pid)
return None


def kill_app_process(d: u2.Device) -> bool:
pid = get_app_process_pid(d)
if not pid:
return False
d.shell(f"kill {pid}")
return True


def test_uiautomator_keeper(d: u2.Device):
kill_app_process(d)
d.sleep(.2)
assert get_app_process_pid(d) is None
d.shell('rm /data/local/tmp/u2.jar')

d.start_uiautomator()
assert get_app_process_pid(d) > 0

d.stop_uiautomator()
assert get_app_process_pid(d) is None


def test_debug(d: u2.Device):
d.debug = True
d.info

Loading

0 comments on commit c25a3e5

Please sign in to comment.