Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V1.3.2 #1176

Merged
merged 16 commits into from
Nov 22, 2023
Merged

V1.3.2 #1176

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion airtest/core/android/adb.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
13 changes: 8 additions & 5 deletions airtest/core/android/android.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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):
Expand All @@ -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):
Expand All @@ -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):
Expand All @@ -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):
Expand All @@ -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):
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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()
Expand Down
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
Expand Up @@ -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
Expand Up @@ -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
Expand Down Expand Up @@ -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是否一致
Expand Down Expand Up @@ -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
Expand All @@ -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)]
Expand Down
63 changes: 46 additions & 17 deletions airtest/core/ios/ios.py
Original file line number Diff line number Diff line change
Expand Up @@ -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__)

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand All @@ -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:
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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\"}}" \
Expand All @@ -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):
Expand All @@ -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):
Expand All @@ -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
Expand All @@ -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):
Expand Down Expand Up @@ -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:
Expand All @@ -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):
Expand All @@ -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:
Expand All @@ -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
Expand Down Expand Up @@ -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.

Expand Down
4 changes: 2 additions & 2 deletions airtest/core/ios/mjpeg_cap.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down
6 changes: 5 additions & 1 deletion airtest/utils/logwraper.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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:
Expand All @@ -54,5 +57,7 @@ def f2(*args, **kwargs):
raise
else:
break

return f2

return dec
Loading