forked from AirtestProject/Airtest
-
Notifications
You must be signed in to change notification settings - Fork 0
/
api.py
451 lines (362 loc) · 12.2 KB
/
api.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
# -*- coding: utf-8 -*-
"""
This module contains the Airtest Core APIs.
"""
import os
import time
from airtest.utils.compat import urlparse, parse_qsl
from airtest.core.cv import Template, loop_find, try_log_screen
from airtest.core.error import TargetNotFoundError
from airtest.core.helper import G, set_logdir, logwrap, on_platform, import_device_cls, delay_after_operation
from airtest.core.settings import Settings as ST
"""
Device Setup APIs
"""
def connect_device(uri):
"""
Initialize device with uri and set the device as the current one.
:param uri: an URI where to connect to device, e.g. `android://adbhost:adbport/serialno?param=value¶m2=value2`
:return: device instance
:Example:
* ``android:///`` # local adb device using default params
* ``android://adbhost:adbport/1234566?cap_method=javacap&touch_method=adb`` # remote adb device using custom params
* ``windows:///`` # local Windows application
* ``ios:///`` # iOS device
:platforms: Android, iOS, Windows
"""
d = urlparse(uri)
platform = d.scheme
host = d.netloc
uuid = d.path.lstrip("/")
params = dict(parse_qsl(d.query))
if host:
params["host"] = host.split(":")
cls = import_device_cls(platform)
dev = cls(uuid, **params)
G.add_device(dev)
return dev
def device():
"""
Return the current active device.
:return: current device instance
:platforms: Android, iOS, Windows
"""
return G.DEVICE
@on_platform(["Android", "Windows", "IOS"])
def set_current(index):
"""
Set current active device.
:param index: index of initialized device instance
:raise IndexError: raised when device index is out of device list
:return: None
:platforms: Android, iOS, Windows
"""
try:
G.DEVICE = G.DEVICE_LIST[index]
except IndexError:
raise IndexError("device index out of range: %s/%s" % (index, len(G.DEVICE_LIST)))
"""
Device Operations
"""
@logwrap
@on_platform(["Android"])
def shell(cmd):
"""
Start remote shell in the target device and execute the command
:param cmd: command to be run on device, e.g. "ls /data/local/tmp"
:return: the output of the shell cmd
:platforms: Android
"""
return G.DEVICE.shell(cmd)
@logwrap
@on_platform(["Android", "IOS"])
def start_app(package, activity=None):
"""
Start the target application on device
:param package: name of the package to be started, e.g. "com.netease.my"
:param activity: the activity to start, default is None which means the main activity
:return: None
:platforms: Android, iOS
"""
G.DEVICE.start_app(package, activity)
@logwrap
@on_platform(["Android", "IOS"])
def stop_app(package):
"""
Stop the target application on device
:param package: name of the package to stop, see also `start_app`
:return: None
:platforms: Android, iOS
"""
G.DEVICE.stop_app(package)
@logwrap
@on_platform(["Android", "IOS"])
def clear_app(package):
"""
Clear data of the target application on device
:param package: name of the package, see also `start_app`
:return: None
:platforms: Android, iOS
"""
G.DEVICE.clear_app(package)
@logwrap
@on_platform(["Android", "IOS"])
def install(filepath):
"""
Install application on device
:param filepath: the path to file to be installed on target device
:return: None
:platforms: Android, iOS
"""
return G.DEVICE.install_app(filepath)
@logwrap
@on_platform(["Android", "IOS"])
def uninstall(package):
"""
Uninstall application on device
:param package: name of the package, see also `start_app`
:return: None
:platforms: Android, iOS
"""
return G.DEVICE.uninstall_app(package)
@logwrap
def snapshot(filename=None, msg=""):
"""
Take the screenshot of the target device and save it to the file.
:param filename: name of the file where to save the screenshot. If the relative path is provided, the default
location is ``ST.LOG_DIR``
:param msg: short description for screenshot, it will be recorded in the report
:return: absolute path of the screenshot
:platforms: Android, iOS, Windows
"""
if not filename:
filename = "%(time)d.jpg" % {'time': time.time() * 1000}
if not os.path.isabs(filename):
logdir = ST.LOG_DIR or "."
filepath = os.path.join(logdir, filename)
else:
filepath = filename
screen = G.DEVICE.snapshot(filepath)
try_log_screen(screen)
return filepath
@logwrap
@on_platform(["Android", "IOS"])
def wake():
"""
Wake up and unlock the target device
:return: None
:platforms: Android, iOS
.. note:: Might not work on some models
"""
G.DEVICE.wake()
@logwrap
@on_platform(["Android", "IOS"])
def home():
"""
Return to the home screen of the target device.
:return: None
:platforms: Android, iOS
"""
G.DEVICE.home()
@logwrap
@on_platform(["Android", "Windows", "IOS"])
def touch(v, **kwargs):
"""
Perform the touch action on the device screen
:param v: target to touch, either a Template instance or absolute coordinates (x, y)
:param kwargs: platform specific `kwargs`, please refer to corresponding docs
:return: None
:platforms: Android, Windows, iOS
"""
if isinstance(v, Template):
try:
pos = loop_find(v, timeout=ST.FIND_TIMEOUT)
except TargetNotFoundError:
raise
else:
try_log_screen()
pos = v
G.DEVICE.touch(pos, **kwargs)
delay_after_operation()
@logwrap
@on_platform(["Android", "Windows", "IOS"])
def swipe(v1, v2=None, vector=None, **kwargs):
"""
Perform the swipe action on the device screen.
There are two ways of assigning the parameters
* ``swipe(v1, v2=Template(...))`` # swipe from v1 to v2
* ``swipe(v1, vector=(x, y))`` # swipe starts at v1 and moves along the vector.
:param v1: the start point of swipe,
either a Template instance or absolute coordinates (x, y)
:param v2: the end point of swipe,
either a Template instance or absolute coordinates (x, y)
:param vector: a vector coordinates of swipe action, either absolute coordinates (x, y) or percentage of
screen e.g.(0.5, 0.5)
:param **kwargs: platform specific `kwargs`, please refer to corresponding docs
:raise Exception: general exception when not enough parameters to perform swap action have been provided
:return: None
:platforms: Android, Windows, iOS
"""
if isinstance(v1, Template):
pos1 = loop_find(v1, timeout=ST.FIND_TIMEOUT)
else:
try_log_screen()
pos1 = v1
if v2:
if isinstance(v2, Template):
pos2 = loop_find(v2, timeout=ST.FIND_TIMEOUT_TMP)
else:
pos2 = v2
elif vector:
if vector[0] <= 1 and vector[1] <= 1:
w, h = G.DEVICE.get_current_resolution()
vector = (int(vector[0] * w), int(vector[1] * h))
pos2 = (pos1[0] + vector[0], pos1[1] + vector[1])
else:
raise Exception("no enough params for swipe")
G.DEVICE.swipe(pos1, pos2, **kwargs)
delay_after_operation()
@logwrap
@on_platform(["Android"])
def pinch(in_or_out='in', center=None, percent=0.5):
"""
Perform the pinch action on the device screen
:param in_or_out: pinch in or pinch out, enum in ["in", "out"]
:param center: center of pinch action, default as None which is the center of the screen
:param percent: percentage of the screen of pinch action, default is 0.5
:return: None
:platforms: Android
"""
G.DEVICE.pinch(in_or_out=in_or_out, center=center, percent=percent)
delay_after_operation()
@logwrap
@on_platform(["Android", "Windows", "IOS"])
def keyevent(keyname, **kwargs):
"""
Perform key event on the device
:param keyname: platform specific key name
:param **kwargs: platform specific `kwargs`, please refer to corresponding docs
:return: None
:platforms: Android, Windows, iOS
"""
G.DEVICE.keyevent(keyname, **kwargs)
delay_after_operation()
@logwrap
@on_platform(["Android", "Windows", "IOS"])
def text(text, enter=True):
"""
Input text on the target device. Text input widget must be active first.
:param text: text to input, unicode is supported
:param enter: input `Enter` keyevent after text input, default is True
:return: None
:platforms: Android, Windows, iOS
"""
G.DEVICE.text(text, enter=enter)
delay_after_operation()
@logwrap
def sleep(secs=1.0):
"""
Set the sleep interval. It will be recorded in the report
:param secs: seconds to sleep
:return: None
:platforms: Android, Windows, iOS
"""
time.sleep(secs)
@logwrap
def wait(v, timeout=None, interval=0.5, intervalfunc=None):
"""
Wait to match the Template on the device screen
:param v: target object to wait for, Template instance
:param timeout: time interval to wait for the match, default is None which is ``ST.FIND_TIMEOUT``
:param interval: time interval in seconds to attempt to find a match
:param intervalfunc: called after each unsuccessful attempt to find the corresponding match
:raise TargetNotFoundError: raised if target is not found after the time limit expired
:return: coordinates of the matched target
:platforms: Android, Windows, iOS
"""
timeout = timeout or ST.FIND_TIMEOUT
pos = loop_find(v, timeout=timeout, interval=interval, intervalfunc=intervalfunc)
return pos
@logwrap
def exists(v):
"""
Check whether given target exists on device screen
:param v: target to be checked
:return: False if target is not found, otherwise returns the coordinates of the target
:platforms: Android, Windows, iOS
"""
try:
pos = loop_find(v, timeout=ST.FIND_TIMEOUT_TMP)
except TargetNotFoundError:
return False
else:
return pos
@logwrap
def find_all(v):
"""
Find all occurrences of the target on the device screen and return their coordinates
:param v: target to find
:return: list of coordinates, [(x, y), (x1, y1), ...]
:platforms: Android, Windows, iOS
"""
screen = G.DEVICE.snapshot()
return v.match_all_in(screen)
"""
Assertions
"""
@logwrap
def assert_exists(v, msg=""):
"""
Assert target exists on device screen
:param v: target to be checked
:param msg: short description of assertion, it will be recorded in the report
:raise AssertionError: if assertion fails
:return: coordinates of the target
:platforms: Android, Windows, iOS
"""
try:
pos = loop_find(v, timeout=ST.FIND_TIMEOUT, threshold=ST.THRESHOLD_STRICT)
return pos
except TargetNotFoundError:
raise AssertionError("%s does not exist in screen, message: %s" % (v, msg))
@logwrap
def assert_not_exists(v, msg=""):
"""
Assert target does not exist on device screen
:param v: target to be checked
:param msg: short description of assertion, it will be recorded in the report
:raise AssertionError: if assertion fails
:return: None.
:platforms: Android, Windows, iOS
"""
try:
pos = loop_find(v, timeout=ST.FIND_TIMEOUT_TMP)
raise AssertionError("%s exists unexpectedly at pos: %s, message: %s" % (v, pos, msg))
except TargetNotFoundError:
pass
@logwrap
def assert_equal(first, second, msg=""):
"""
Assert two values are equal
:param first: first value
:param second: second value
:param msg: short description of assertion, it will be recorded in the report
:raise AssertionError: if assertion fails
:return: None
:platforms: Android, Windows, iOS
"""
if first != second:
raise AssertionError("%s and %s are not equal, message: %s" % (first, second, msg))
@logwrap
def assert_not_equal(first, second, msg=""):
"""
Assert two values are not equal
:param first: first value
:param second: second value
:param msg: short description of assertion, it will be recorded in the report
:raise AssertionError: if assertion
:return: None
:platforms: Android, Windows, iOS
"""
if first == second:
raise AssertionError("%s and %s are equal, message: %s" % (first, second, msg))