Source code for airtest.core.ios.ios

#! /usr/bin/env python
# -*- coding: utf-8 -*-

from airtest.core.ios.wda_client import LANDSCAPE, PORTRAIT, LANDSCAPE_RIGHT, PORTRAIT_UPSIDEDOWN, WDAError, DEBUG, Client
from airtest.utils.logger import get_logger
from airtest.core.ios.instruct_cmd import InstructHelper
from airtest.core.ios.fake_minitouch import fakeMiniTouch
from airtest.core.ios.rotation import XYTransformer, RotationWatcher
from airtest.core.ios.constant import CAP_METHOD, TOUCH_METHOD, IME_METHOD
from airtest.core.device import Device
from airtest import aircv
import requests
import six
import time
import json
import base64
import traceback

if six.PY3:
    from urllib.parse import urljoin
    from urlparse import urljoin

logger = get_logger(__name__)
DEFAULT_ADDR = "http://localhost:8100/"

# retry when saved session failed
[docs]def retry_session(func): def wrapper(self, *args, **kwargs): try: return func(self, *args, **kwargs) except WDAError as err: # 6 : Session does not exist if err.status == 6: self._fetchNewSession() return func(self, *args, **kwargs) else: raise err return wrapper
[docs]class IOS(Device): """ios client - before this you have to run `WebDriverAgent <>`_ - ``xcodebuild -project path/to/WebDriverAgent.xcodeproj -scheme WebDriverAgentRunner -destination "id=$(idevice_id -l)" test`` - ``iproxy $port 8100 $udid`` """ def __init__(self, addr=DEFAULT_ADDR): super(IOS, self).__init__() # if none or empty, use default addr self.addr = addr or DEFAULT_ADDR # fit wda format, make url start with http:// if not self.addr.startswith("http://"): self.addr = "http://" + addr """here now use these supported cap touch and ime method""" self.cap_method = CAP_METHOD.WDACAP self.touch_method = TOUCH_METHOD.WDATOUCH self.ime_method = IME_METHOD.WDAIME # wda driver, use to home, start app # init wda session, updata when start app # use to click/swipe/close app/get wda size DEBUG = False self.driver = Client(self.addr) # record device's width self._size = {'width': None, 'height': None} self._touch_factor = 0.5 self._last_orientation = None self.defaultSession = None # start up RotationWatcher with default session self.rotation_watcher = RotationWatcher(self) # fake minitouch to simulate swipe self.minitouch = fakeMiniTouch(self) # helper of run process like iproxy self.instruct_helper = InstructHelper() @property def uuid(self): return self.addr @property def session(self): if not self.defaultSession: self.defaultSession = self.driver.session() return self.defaultSession def _fetchNewSession(self): self.defaultSession = self.driver.session()
[docs] @retry_session def window_size(self): """ return window size namedtuple: Size(wide , hight) """ return self.session.window_size()
@property @retry_session def orientation(self): """ return device oritantation status in LANDSACPE POR """ return self.session.orientation @property def display_info(self): if not self._size['width'] or not self._size['height']: self.snapshot() return {'width': self._size['width'], 'height': self._size['height'], 'orientation': self.orientation, 'physical_width': self._size['width'], 'physical_height': self._size['height']}
[docs] def get_render_resolution(self): """ Return render resolution after rotation Returns: offset_x, offset_y, offset_width and offset_height of the display """ w, h = self.get_current_resolution() return 0, 0, w, h
[docs] def get_current_resolution(self): w, h = self.display_info["width"], self.display_info["height"] if self.display_info["orientation"] in [LANDSCAPE, LANDSCAPE_RIGHT]: w, h = h, w return w, h
[docs] def home(self): return self.driver.home()
def _neo_wda_screenshot(self): """ this is almost same as wda implementation, but without png header check, as response data is now jpg format in mid quality """ value = self.driver.http.get('screenshot').value raw_value = base64.b64decode(value) return raw_value
[docs] def snapshot(self, filename=None, strType=False, quality=10, max_size=None): """ take snapshot Args: filename: save screenshot to filename quality: The image quality, integer in range [1, 99] max_size: the maximum size of the picture, e.g 1200 Returns: display the screenshot """ data = None if self.cap_method == CAP_METHOD.MINICAP: raise NotImplementedError elif self.cap_method == CAP_METHOD.MINICAP_STREAM: raise NotImplementedError elif self.cap_method == CAP_METHOD.WDACAP: data = self._neo_wda_screenshot() # wda 截图不用考虑朝向 # 实时刷新手机画面,直接返回base64格式,旋转问题交给IDE处理 if strType: if filename: with open(filename, 'wb') as f: f.write(data) return data # output cv2 object try: screen = aircv.utils.string_2_img(data) except: # may be black/locked screen or other reason, print exc for debugging traceback.print_exc() return None h, w = screen.shape[:2] # save last res for portrait if self.orientation in [LANDSCAPE, LANDSCAPE_RIGHT]: self._size['height'] = w self._size['width'] = h else: self._size['height'] = h self._size['width'] = w winw, winh = self.window_size() self._touch_factor = float(winh) / float(h) # save as file if needed if filename: aircv.imwrite(filename, screen, quality, max_size=max_size) return screen
[docs] @retry_session def touch(self, pos, duration=0.01): # trans pos of click pos = self._touch_point_by_orientation(pos) # scale touch postion x, y = pos[0] * self._touch_factor, pos[1] * self._touch_factor if duration >= 0.5: self.session.tap_hold(x, y, duration) else: self.session.tap(x, y)
[docs] def double_click(self, pos): # trans pos of click pos = self._touch_point_by_orientation(pos) x, y = pos[0] * self._touch_factor, pos[1] * self._touch_factor self.session.double_tap(x, y)
[docs] def swipe(self, fpos, tpos, duration=0.5, steps=5, fingers=1): # trans pos of swipe fx, fy = self._touch_point_by_orientation(fpos) tx, ty = self._touch_point_by_orientation(tpos) self.session.swipe(fx * self._touch_factor, fy * self._touch_factor, tx * self._touch_factor, ty * self._touch_factor, duration)
[docs] def keyevent(self, keys): """just use as home event""" if keys not in ['HOME', 'home', 'Home']: raise NotImplementedError self.home()
[docs] @retry_session def text(self, text, enter=True): """bug in wda for now""" if enter: text += '\n' self.session.send_keys(text)
[docs] def install_app(self, uri, package): """ curl -X POST $JSON_HEADER \ -d "{\"desiredCapabilities\":{\"bundleId\":\"\", \"app\":\"[host_path]/\"}}" \ $DEVICE_URL/session """ raise NotImplementedError
[docs] def start_app(self, package, activity=None): self.defaultSession = None self.session.start_app_new(package)
[docs] def stop_app(self, package): self.driver.session().close(package)
[docs] def get_ip_address(self): """ get ip address from webDriverAgent Returns: raise if no IP address has been found, otherwise return the IP address """ return self.driver.status()['ios']['ip']
[docs] def device_status(self): """ show status return by webDriverAgent Return dicts of infos """ return self.driver.status()
def _touch_point_by_orientation(self, tuple_xy): """ Convert image coordinates to physical display coordinates, the arbitrary point (origin) is upper left corner of the device physical display Args: tuple_xy: image coordinates (x, y) Returns: """ x, y = tuple_xy # use correct w and h due to now orientation # _size 只对应竖直时候长宽 now_orientation = self.orientation if now_orientation in [PORTRAIT, PORTRAIT_UPSIDEDOWN]: width, height = self._size['width'], self._size["height"] else: height, width = self._size['width'], self._size["height"] # check if not get screensize when touching if not width or not height: # use snapshot to get current resuluton self.snapshot() x, y = XYTransformer.up_2_ori( (x, y), (width, height), now_orientation ) return x, y def _check_orientation_change(self): pass
if __name__ == "__main__": start = time.time() ios = IOS("") ios.snapshot() # ios.touch((242 * 2 + 10, 484 * 2 + 20)) # ios.start_app("") ios.home() ios.start_app('') ios.touch((88, 88)) ios.stop_app('') ios.swipe((100, 100), (800, 100)) print(ios.device_status()) print(ios.get_ip_address())