Skip to content

Commit

Permalink
Merge pull request #1176 from AirtestProject/v1.3.2
Browse files Browse the repository at this point in the history
V1.3.2
yimelia authored Nov 22, 2023

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
2 parents 182d0be + bd94ab5 commit 1d71c4e
Showing 16 changed files with 124 additions and 48 deletions.
2 changes: 1 addition & 1 deletion airtest/core/android/adb.py
Original file line number Diff line number Diff line change
@@ -254,7 +254,7 @@ def devices(self, state=None):
list od adb devices
"""
patten = re.compile(r'^[\w\d.:-]+\t[\w]+$')
patten = re.compile(r'[\w\d.:-]+\t[\w]+$')
device_list = []
# self.start_server()
output = self.cmd("devices", device=False)
13 changes: 8 additions & 5 deletions airtest/core/android/android.py
Original file line number Diff line number Diff line change
@@ -292,6 +292,7 @@ def path_app(self, package):
the full path to the package
"""
assert package, "package name should not be empty"
return self.adb.path_app(package)

def check_app(self, package):
@@ -308,6 +309,7 @@ def check_app(self, package):
AirtestError: raised if package is not found
"""
assert package, "package name should not be empty"
return self.adb.check_app(package)

def start_app(self, package, activity=None):
@@ -322,6 +324,7 @@ def start_app(self, package, activity=None):
None
"""
assert package, "package name should not be empty"
return self.adb.start_app(package, activity)

def start_app_timing(self, package, activity):
@@ -336,6 +339,7 @@ def start_app_timing(self, package, activity):
app launch time
"""
assert package, "package name should not be empty"
return self.adb.start_app_timing(package, activity)

def stop_app(self, package):
@@ -349,6 +353,7 @@ def stop_app(self, package):
None
"""
assert package, "package name should not be empty"
return self.adb.stop_app(package)

def clear_app(self, package):
@@ -362,6 +367,7 @@ def clear_app(self, package):
None
"""
assert package, "package name should not be empty"
return self.adb.clear_app(package)

def install_app(self, filepath, replace=False, install_options=None):
@@ -404,6 +410,7 @@ def uninstall_app(self, package):
output from the uninstallation process
"""
assert package, "package name should not be empty"
return self.adb.uninstall_app(package)

def snapshot(self, filename=None, ensure_orientation=True, quality=10, max_size=None):
@@ -902,14 +909,10 @@ def stop_recording(self, output=None, is_interrupted=None):
Stop recording the device display. Recoding file will be kept in the device.
"""
if self.yosemite_recorder.recording_proc != None:
if self.yosemite_recorder.recording_proc is not None or self.recorder is None:
if output is None:
output = self.recorder_save_path
return self.yosemite_recorder.stop_recording(output=output, is_interrupted=is_interrupted)

if self.recorder is None:
LOGGING.warning("start_recording first")
return False

LOGGING.info("stopping recording")
self.recorder.stop()
Binary file modified airtest/core/android/static/adb/linux/adb
Binary file not shown.
Binary file modified airtest/core/android/static/adb/mac/adb
Binary file not shown.
Binary file modified airtest/core/android/static/adb/windows/adb.exe
Binary file not shown.
Binary file modified airtest/core/android/static/apks/Yosemite.apk
Binary file not shown.
10 changes: 9 additions & 1 deletion airtest/core/error.py
Original file line number Diff line number Diff line change
@@ -114,4 +114,12 @@ class MinitouchError(BaseError):


class PerformanceError(BaseError):
pass
pass


class LocalDeviceError(BaseError):
"""
Custom exception for calling a method on a non-local iOS device.
"""
def __init__(self, value="Can only use this method on a local device."):
super().__init__(value)
18 changes: 15 additions & 3 deletions airtest/core/ios/instruct_cmd.py
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@
import wda
from copy import copy
from functools import partial
from airtest.core.error import AirtestError
from airtest.core.error import AirtestError, LocalDeviceError
from airtest.utils.snippet import reg_cleanup, make_file_executable, get_std_encoding, kill_proc
from airtest.utils.logger import get_logger
from airtest.utils.retry import retries
@@ -65,7 +65,17 @@ def usb_device(self):
"""
if not self._device:
# wda无法直接获取iOS的udid,因此先检查usb连接的手机udid列表
for dev in wda.usbmux.Usbmux().device_list():
try:
device_list = wda.usbmux.Usbmux().device_list()
except ConnectionRefusedError:
# windows上必须要先启动iTunes才能获取到iOS设备列表
LOGGING.warning("If you are using iOS device in windows, please check if iTunes is launched")
return None
except Exception as e:
# 其他异常,例如 socket unix:/var/run/usbmuxd unable to connect
print(e)
return None
for dev in device_list:
udid = dev.get('SerialNumber')
usb_dev = wda.Client(url=wda.requests_usbmux.DEFAULT_SCHEME + udid)
# 对比wda.info获取到的uuid是否一致
@@ -100,6 +110,8 @@ def setup_proxy(self, device_port):
Returns:
"""
if not self.usb_device:
raise LocalDeviceError("Currently only supports port forwarding for locally connected iOS devices")
local_port = random.randint(11111, 20000)
self.do_proxy(local_port, device_port)
return local_port, device_port
@@ -118,7 +130,7 @@ def do_proxy(self, port, device_port):
"""
if not self.usb_device:
raise AirtestError("Currently only supports port forwarding for locally connected iOS devices")
raise LocalDeviceError("Currently only supports port forwarding for locally connected iOS devices")
proxy_process = self.builtin_iproxy_path() or shutil.which("tidevice")
if proxy_process:
cmds = [proxy_process, "-u", self._udid, str(port), str(device_port)]
63 changes: 46 additions & 17 deletions airtest/core/ios/ios.py
Original file line number Diff line number Diff line change
@@ -28,6 +28,8 @@
from airtest.core.ios.mjpeg_cap import MJpegcap
from airtest.core.settings import Settings as ST
from airtest.aircv.screen_recorder import ScreenRecorder, resize_by_max, get_max_size
from airtest.core.error import LocalDeviceError


LOGGING = get_logger(__name__)

@@ -151,7 +153,7 @@ def list_wda(udid):
wda_list = []
for app in app_list:
bundle_id, display_name, _ = app
if ".xctrunner" in bundle_id or display_name == "WebDriverAgentRunner-Runner":
if (bundle_id.startswith('com.') and bundle_id.endswith(".xctrunner")) or display_name == "WebDriverAgentRunner-Runner":
wda_list.append(bundle_id)
return wda_list

@@ -280,7 +282,7 @@ class IOS(Device):
"""

def __init__(self, addr=DEFAULT_ADDR, cap_method=CAP_METHOD.MJPEG, mjpeg_port=None, udid=None, name=None, serialno=None, wda_bundle_id=None):
super(IOS, self).__init__()
super().__init__()

# If none or empty, use default addr.
self.addr = addr or DEFAULT_ADDR
@@ -303,7 +305,7 @@ def __init__(self, addr=DEFAULT_ADDR, cap_method=CAP_METHOD.MJPEG, mjpeg_port=No
# The three connection modes are determined by the addr.
# e.g., connect remote device http://10.227.70.247:20042
# e.g., connect local device http://127.0.0.1:8100 or http://localhost:8100 or http+usbmux://00008020-001270842E88002E
self.udid = udid
self.udid = udid or name or serialno
self.wda_bundle_id = wda_bundle_id
parsed = urlparse(self.addr).netloc.split(":")[0] if ":" in urlparse(self.addr).netloc else urlparse(self.addr).netloc
if parsed not in ["localhost", "127.0.0.1"] and "." in parsed:
@@ -474,7 +476,7 @@ def device_info(self):
try:
# Add some device info.
if not self.is_local_device:
raise RuntimeError("Can only use this method in local device now, not remote device.")
raise LocalDeviceError()
tmp_dict = TIDevice.device_info(self.udid)
device_info.update(tmp_dict)
except:
@@ -787,7 +789,7 @@ def text(self, text, enter=True):
text += '\n'
self.driver.send_keys(text)

def install_app(self, file_or_url):
def install_app(self, file_or_url, **kwargs):
"""
curl -X POST $JSON_HEADER \
-d "{\"desiredCapabilities\":{\"bundleId\":\"com.apple.mobilesafari\", \"app\":\"[host_path]/magicapp.app\"}}" \
@@ -800,10 +802,13 @@ def install_app(self, file_or_url):
file_or_url: file or url to install
Returns:
bundle ID
bundle ID
Raises:
LocalDeviceError: If the device is remote.
"""
if not self.is_local_device:
raise RuntimeError("Can only use this method in local device now, not remote device.")
raise LocalDeviceError()
return TIDevice.install_app(self.udid, file_or_url)

def uninstall_app(self, bundle_id):
@@ -814,9 +819,12 @@ def uninstall_app(self, bundle_id):
Args:
bundle_id: the app bundle id, e.g ``com.apple.mobilesafari``
Raises:
LocalDeviceError: If the device is remote.
"""
if not self.is_local_device:
raise RuntimeError("Can only use this method in local device now, not remote device.")
raise LocalDeviceError()
return TIDevice.uninstall_app(self.udid, bundle_id)

def start_app(self, bundle_id, *args, **kwargs):
@@ -843,7 +851,7 @@ def stop_app(self, bundle_id):
"""
try:
if not self.is_local_device:
raise RuntimeError("Can only use this method in local device now, not remote device.")
raise LocalDeviceError()
TIDevice.stop_app(self.udid, bundle_id)
except:
pass
@@ -862,9 +870,12 @@ def list_app(self, type="user"):
list: A list of tuples containing the bundle ID, display name,
and version of the installed applications.
e.g. [('com.apple.mobilesafari', 'Safari', '8.0'), ...]
Raises:
LocalDeviceError: If the device is remote.
"""
if not self.is_local_device:
raise RuntimeError("Can only use this method in local device now, not remote device.")
raise LocalDeviceError()
return TIDevice.list_app(self.udid, app_type=type)

def app_state(self, bundle_id):
@@ -909,14 +920,17 @@ def get_clipboard(self, wda_bundle_id=None, *args, **kwargs):
Returns:
Clipboard text.
Raises:
LocalDeviceError: If the device is remote and the wda_bundle_id parameter is not provided.
Notes:
If you want to use this function, you have to set WDA foreground which would switch the
current screen of the phone. Then we will try to switch back to the screen before.
"""
if wda_bundle_id is None:
if not self.is_local_device:
raise RuntimeError("Remote device need to set running wda bundle id parameter, \
e.g. get_clipboard('wda_bundle_id').")
raise LocalDeviceError("Remote device need to set running wda bundle id parameter, \
e.g. get_clipboard('wda_bundle_id').")
wda_bundle_id = self._get_default_running_wda_bundle_id()
# Set wda foreground, it's necessary.
try:
@@ -933,7 +947,7 @@ def get_clipboard(self, wda_bundle_id=None, *args, **kwargs):
if current_app_bundle_id:
self.driver.app_launch(current_app_bundle_id)
else:
LOGGING.warning("we can't switch back to the app before, becasue can't get bundle id.")
LOGGING.warning("we can't switch back to the app before, because can't get bundle id.")
return decoded_text

def set_clipboard(self, content, wda_bundle_id=None, *args, **kwargs):
@@ -945,15 +959,15 @@ def set_clipboard(self, content, wda_bundle_id=None, *args, **kwargs):
wda_bundle_id (str, optional): The bundle ID of the WDA app. Defaults to None.
Raises:
RuntimeError: If the device is remote and the wda_bundle_id parameter is not provided.
LocalDeviceError: If the device is remote and the wda_bundle_id parameter is not provided.
Returns:
None
"""
if wda_bundle_id is None:
if not self.is_local_device:
raise RuntimeError("Remote device need to set running wda bundle id parameter, \
e.g. set_clipboard('content', 'wda_bundle_id').")
raise LocalDeviceError("Remote device need to set running wda bundle id parameter, \
e.g. set_clipboard('content', 'wda_bundle_id').")
wda_bundle_id = self._get_default_running_wda_bundle_id()
# Set wda foreground, it's necessary.
try:
@@ -979,7 +993,7 @@ def paste(self, wda_bundle_id=None, *args, **kwargs):
wda_bundle_id (str, optional): The bundle ID of the WDA app. Defaults to None.
Raises:
RuntimeError: If the device is remote and the wda_bundle_id parameter is not provided.
LocalDeviceError: If the device is remote and the wda_bundle_id parameter is not provided.
Returns:
None
@@ -1075,6 +1089,21 @@ def lock(self):
"""
return self.driver.lock()

def setup_forward(self, port):
"""
Setup port forwarding from device to host.
Args:
port: device port
Returns:
host port, device port
Raises:
LocalDeviceError: If the device is remote.
"""
return self.instruct_helper.setup_proxy(int(port))

def alert_accept(self):
""" Alert accept-Actually do click first alert button.
4 changes: 2 additions & 2 deletions airtest/core/ios/mjpeg_cap.py
Original file line number Diff line number Diff line change
@@ -153,10 +153,10 @@ def get_blank_screen(self):
return img_string

def teardown_stream(self):
if self.port_forwarding:
self.instruct_helper.remove_proxy(self.port)
if self.buf:
self.buf.close()
if self.port_forwarding:
self.instruct_helper.remove_proxy(self.port)
self.port = None


6 changes: 5 additions & 1 deletion airtest/utils/logwraper.py
Original file line number Diff line number Diff line change
@@ -145,5 +145,9 @@ def func1(snapshot=True):
# if G.DEVICE is None
pass
logger.log('function', fndata, depth=depth)
logger.running_stack.pop()
try:
logger.running_stack.pop()
except IndexError:
# logger.running_stack is empty
pass
return wrapper
9 changes: 7 additions & 2 deletions airtest/utils/retry.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# _*_ coding:UTF-8 _*_
import time
import functools


def retries(max_tries, delay=1, backoff=2, exceptions=(Exception,), hook=None):
@@ -33,17 +34,19 @@ def retries(max_tries, delay=1, backoff=2, exceptions=(Exception,), hook=None):
wrapper
"""

def dec(func):
@functools.wraps(func)
def f2(*args, **kwargs):
mydelay = delay
tries = range(max_tries)
# support Python conver range obj to list obj
tries = list(tries)

tries.reverse()
for tries_remaining in tries:
try:
return func(*args, **kwargs)
return func(*args, **kwargs)
except exceptions as e:
if tries_remaining > 0:
if hook is not None:
@@ -54,5 +57,7 @@ def f2(*args, **kwargs):
raise
else:
break

return f2

return dec
Loading

0 comments on commit 1d71c4e

Please sign in to comment.