Kea2-python 0.0.1b1__tar.gz → 0.0.1b3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of Kea2-python might be problematic. Click here for more details.
- {kea2_python-0.0.1b1 → kea2_python-0.0.1b3}/Kea2_python.egg-info/PKG-INFO +78 -46
- {kea2_python-0.0.1b1 → kea2_python-0.0.1b3}/Kea2_python.egg-info/SOURCES.txt +0 -1
- {kea2_python-0.0.1b1 → kea2_python-0.0.1b3}/PKG-INFO +78 -46
- {kea2_python-0.0.1b1 → kea2_python-0.0.1b3}/README.md +77 -45
- {kea2_python-0.0.1b1 → kea2_python-0.0.1b3}/kea2/adbUtils.py +29 -1
- kea2_python-0.0.1b3/kea2/assets/fastbot_configs/widget.block.py +38 -0
- kea2_python-0.0.1b3/kea2/assets/monkeyq.jar +0 -0
- {kea2_python-0.0.1b1 → kea2_python-0.0.1b3}/kea2/cli.py +1 -0
- {kea2_python-0.0.1b1 → kea2_python-0.0.1b3}/kea2/keaUtils.py +118 -40
- {kea2_python-0.0.1b1 → kea2_python-0.0.1b3}/kea2/kea_launcher.py +15 -4
- {kea2_python-0.0.1b1 → kea2_python-0.0.1b3}/kea2/logWatcher.py +11 -8
- {kea2_python-0.0.1b1 → kea2_python-0.0.1b3}/kea2/u2Driver.py +1 -1
- {kea2_python-0.0.1b1 → kea2_python-0.0.1b3}/pyproject.toml +2 -2
- kea2_python-0.0.1b1/kea2/assets/fastbot_configs/ADBKeyBoard.apk +0 -0
- kea2_python-0.0.1b1/kea2/assets/fastbot_configs/widget.block.py +0 -18
- kea2_python-0.0.1b1/kea2/assets/monkeyq.jar +0 -0
- {kea2_python-0.0.1b1 → kea2_python-0.0.1b3}/Kea2_python.egg-info/dependency_links.txt +0 -0
- {kea2_python-0.0.1b1 → kea2_python-0.0.1b3}/Kea2_python.egg-info/entry_points.txt +0 -0
- {kea2_python-0.0.1b1 → kea2_python-0.0.1b3}/Kea2_python.egg-info/requires.txt +0 -0
- {kea2_python-0.0.1b1 → kea2_python-0.0.1b3}/Kea2_python.egg-info/top_level.txt +0 -0
- {kea2_python-0.0.1b1 → kea2_python-0.0.1b3}/LICENSE +0 -0
- {kea2_python-0.0.1b1 → kea2_python-0.0.1b3}/kea2/__init__.py +0 -0
- {kea2_python-0.0.1b1 → kea2_python-0.0.1b3}/kea2/absDriver.py +0 -0
- {kea2_python-0.0.1b1 → kea2_python-0.0.1b3}/kea2/assets/fastbot-thirdpart.jar +0 -0
- {kea2_python-0.0.1b1 → kea2_python-0.0.1b3}/kea2/assets/fastbot_configs/abl.strings +0 -0
- {kea2_python-0.0.1b1 → kea2_python-0.0.1b3}/kea2/assets/fastbot_configs/awl.strings +0 -0
- {kea2_python-0.0.1b1 → kea2_python-0.0.1b3}/kea2/assets/fastbot_configs/max.config +0 -0
- {kea2_python-0.0.1b1 → kea2_python-0.0.1b3}/kea2/assets/fastbot_configs/max.fuzzing.strings +0 -0
- {kea2_python-0.0.1b1 → kea2_python-0.0.1b3}/kea2/assets/fastbot_configs/max.schema.strings +0 -0
- {kea2_python-0.0.1b1 → kea2_python-0.0.1b3}/kea2/assets/fastbot_configs/max.strings +0 -0
- {kea2_python-0.0.1b1 → kea2_python-0.0.1b3}/kea2/assets/fastbot_configs/max.tree.pruning +0 -0
- {kea2_python-0.0.1b1 → kea2_python-0.0.1b3}/kea2/assets/fastbot_libs/arm64-v8a/libfastbot_native.so +0 -0
- {kea2_python-0.0.1b1 → kea2_python-0.0.1b3}/kea2/assets/fastbot_libs/armeabi-v7a/libfastbot_native.so +0 -0
- {kea2_python-0.0.1b1 → kea2_python-0.0.1b3}/kea2/assets/fastbot_libs/x86/libfastbot_native.so +0 -0
- {kea2_python-0.0.1b1 → kea2_python-0.0.1b3}/kea2/assets/fastbot_libs/x86_64/libfastbot_native.so +0 -0
- {kea2_python-0.0.1b1 → kea2_python-0.0.1b3}/kea2/assets/framework.jar +0 -0
- {kea2_python-0.0.1b1 → kea2_python-0.0.1b3}/kea2/assets/quicktest.py +0 -0
- {kea2_python-0.0.1b1 → kea2_python-0.0.1b3}/kea2/assets/u2.jar +0 -0
- {kea2_python-0.0.1b1 → kea2_python-0.0.1b3}/kea2/utils.py +0 -0
- {kea2_python-0.0.1b1 → kea2_python-0.0.1b3}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Kea2-python
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.1b3
|
|
4
4
|
Summary: A python library for supporting and customizing automated UI testing for mobile apps
|
|
5
5
|
Author-email: Xixian Liang <xixian@stu.ecnu.edu.cn>
|
|
6
6
|
Requires-Python: >=3.8
|
|
@@ -20,7 +20,7 @@ Dynamic: license-file
|
|
|
20
20
|
<img src="https://github.com/user-attachments/assets/58f68b00-cc9c-4620-9e2e-66c43cf7caae" style="border-radius: 14px; width: 20%; height: 20%;"/>
|
|
21
21
|
</div>
|
|
22
22
|
|
|
23
|
-
Kea2 is an easy-to-use Python library for supporting, customizing and improving automated UI testing for mobile apps.
|
|
23
|
+
Kea2 is an easy-to-use Python library for supporting, customizing and improving automated UI testing for mobile apps. Kea2's novelty is able to fuse the scripts (usually written by human) with automated UI testing tools, thus allowing many interesting and powerful features. Kea2 is currently built on top of [Fastbot](https://github.com/bytedance/Fastbot_Android) and [uiautomator2](https://github.com/openatx/uiautomator2) and targets [Android](https://en.wikipedia.org/wiki/Android_(operating_system)) apps.
|
|
24
24
|
|
|
25
25
|
### Kea2 has three important features:
|
|
26
26
|
- **Feature 1**(查找稳定性问题): coming with the full capability of [Fastbot](https://github.com/bytedance/Fastbot_Android) for stress testing and finding *stability problems* (i.e., *crashing bugs*);
|
|
@@ -49,7 +49,7 @@ Kea2, released as a Python library, currently works with:
|
|
|
49
49
|
- [uiautomator2](https://github.com/openatx/uiautomator2) as the UI test driver;
|
|
50
50
|
- [Fastbot](https://github.com/bytedance/Fastbot_Android) as the backend automated UI testing tool.
|
|
51
51
|
|
|
52
|
-
In the future, Kea2 will be extended to support
|
|
52
|
+
**Roadmap**: In the future, Kea2 will be extended to support
|
|
53
53
|
- [pytest](https://docs.pytest.org/en/stable/)
|
|
54
54
|
- [Appium](https://github.com/appium/appium), [Hypium]() (for HarmonyOS)
|
|
55
55
|
- other automated UI testing tools (not limited to Fastbot)
|
|
@@ -61,13 +61,14 @@ In the future, Kea2 will be extended to support
|
|
|
61
61
|
Running requirements/environment:
|
|
62
62
|
- support Windows, MacOS and Linux
|
|
63
63
|
- python 3.8+
|
|
64
|
+
- Android 4.4+
|
|
64
65
|
- Android SDK installed
|
|
65
66
|
- **VPN closed** (Features 2 and 3 required)
|
|
66
67
|
|
|
67
68
|
|
|
68
69
|
Install Kea2 by `pip`:
|
|
69
70
|
```bash
|
|
70
|
-
python3 -m pip install
|
|
71
|
+
python3 -m pip install kea2-python
|
|
71
72
|
```
|
|
72
73
|
|
|
73
74
|
Find Kea2's additional options by running
|
|
@@ -174,20 +175,31 @@ You can find the full example in script `quicktest.py`, and run this script with
|
|
|
174
175
|
kea2 run -s "emulator-5554" -p it.feio.android.omninotes.alpha --agent u2 --running-minutes 10 --throttle 200 --driver-name d unittest discover -p quicktest.py
|
|
175
176
|
```
|
|
176
177
|
|
|
177
|
-
### Example 2: blacklisting specific UI
|
|
178
|
+
### Example 2: blacklisting specific UI elements
|
|
178
179
|
|
|
179
|
-
We support blacklisting specific
|
|
180
|
+
We support blacklisting specific elements so that Fastbot can avoid interacting with these
|
|
181
|
+
elements during fuzzing.
|
|
180
182
|
|
|
181
|
-
|
|
182
|
-
The widgets needed to be blocked can be flexibly specified by u2 selector (e.g., `text`, `description`) or `xpath`, etc.
|
|
183
|
+
We support two granularity levels for UI blocking:
|
|
183
184
|
|
|
184
|
-
|
|
185
|
+
- Widget Blocking: Block individual UI widgets.
|
|
186
|
+
|
|
187
|
+
- Tree Blocking : Block a UI widget trees by specifying its root node.
|
|
188
|
+
It can block the root node and all its descendants.
|
|
189
|
+
|
|
190
|
+
We support (1) `Global Block List` (always taking effective), and (2) `Conditional Block List` (only taking effective when some conditions are met).
|
|
191
|
+
|
|
192
|
+
The list of blocked elements are specified in Kea2's config file `configs/widget.block.py` (generated when running `kea2 init`).
|
|
193
|
+
The elements needed to be blocked can be flexibly specified by u2 selector (e.g., `text`, `description`) or `xpath`, etc.
|
|
194
|
+
|
|
195
|
+
#### Widget Blocking
|
|
196
|
+
##### Global Block List
|
|
185
197
|
We can define the function `global_block_widgets` to specify which UI widgets should be blocked globally. The blocking always takes effect.
|
|
186
198
|
|
|
187
199
|
```python
|
|
188
200
|
# file: configs/widget.block.py
|
|
189
201
|
|
|
190
|
-
|
|
202
|
+
def global_block_widgets(d: "Device"):
|
|
191
203
|
"""
|
|
192
204
|
global block list.
|
|
193
205
|
return the widgets which should be blocked globally
|
|
@@ -196,8 +208,8 @@ We can define the function `global_block_widgets` to specify which UI widgets sh
|
|
|
196
208
|
d.xpath(".//node[@text='widget to block']"),
|
|
197
209
|
d(description="widgets to block")]
|
|
198
210
|
```
|
|
199
|
-
|
|
200
|
-
We can define any reserved function whose name starts with "block_" and decorate such function by `@precondition` to allow conditional block list.
|
|
211
|
+
##### Conditional Block List
|
|
212
|
+
We can define any reserved function whose name starts with "block_" (but not requiring "block_tree_" prefix) and decorate such function by `@precondition` to allow conditional block list.
|
|
201
213
|
In this case, the blocking only takes effect when the precondition is satisfied.
|
|
202
214
|
```python
|
|
203
215
|
# file: configs/widget.block.py
|
|
@@ -211,6 +223,37 @@ def block_sth(d: "Device"):
|
|
|
211
223
|
d(description="widgets to block")]
|
|
212
224
|
```
|
|
213
225
|
|
|
226
|
+
#### Tree Blocking
|
|
227
|
+
##### Global Block List
|
|
228
|
+
We can define the function `global_block_tree` to specify which UI widget trees should be blocked globally. The blocking always takes effect.
|
|
229
|
+
|
|
230
|
+
```python
|
|
231
|
+
# file: configs/widget.block.py
|
|
232
|
+
|
|
233
|
+
def global_block_tree(d: "Device"):
|
|
234
|
+
"""
|
|
235
|
+
Specify UI widget trees to be blocked globally during testing.
|
|
236
|
+
Returns a list of root nodes whose entire subtrees will be blocked from exploration.
|
|
237
|
+
This function is only available in 'u2 agent' mode.
|
|
238
|
+
"""
|
|
239
|
+
return [d(text="trees to block"), d.xpath(".//node[@text='tree to block']")]
|
|
240
|
+
```
|
|
241
|
+
##### Conditional Block List
|
|
242
|
+
We can define any reserved function whose name starts with "block_tree_" and decorate such function by `@precondition` to allow conditional block list.
|
|
243
|
+
In this case, the blocking only takes effect when the precondition is satisfied.
|
|
244
|
+
```python
|
|
245
|
+
# file: configs/widget.block.py
|
|
246
|
+
|
|
247
|
+
# Example of conditional tree blocking with precondition
|
|
248
|
+
|
|
249
|
+
@precondition(lambda d: d(text="In the home page").exists)
|
|
250
|
+
def block_tree_sth(d: "Device"):
|
|
251
|
+
# Note: Function name must start with "block_tree_"
|
|
252
|
+
return [d(text="trees to block"),
|
|
253
|
+
d.xpath(".//node[@text='tree to block']"),
|
|
254
|
+
d(description="trees to block")]
|
|
255
|
+
```
|
|
256
|
+
|
|
214
257
|
## Feature 3(支持断言机制): Supporting auto-assertions by scripts.
|
|
215
258
|
|
|
216
259
|
Kea2 supports auto-assertions when running Fastbot for finding *logic bugs* (i.e., *non-crashing bugs*). To achieve this, you can add assertions in the scripts. When an assertion fails during automated UI testing, we find a likely functional bug. This idea is inspired by [property-based testing](https://en.wikipedia.org/wiki/Software_testing#Property_testing) inheritted from [Kea](https://github.com/ecnusse/Kea).
|
|
@@ -304,31 +347,6 @@ class MyFirstTest(unittest.TestCase):
|
|
|
304
347
|
|
|
305
348
|
You can read [Kea - Write your fisrt property](https://kea-docs.readthedocs.io/en/latest/part-keaUserManuel/first_property.html) for more details.
|
|
306
349
|
|
|
307
|
-
### Notes
|
|
308
|
-
Currently, in `@precondition` decorator and `widgets.block.py`. We only support basic selector (No parent-child relationship) in uiautomator2. The Script body in the function fully support uiautomator2.
|
|
309
|
-
|
|
310
|
-
| | **Support** | **Not support** |
|
|
311
|
-
| -- | -- | -- |
|
|
312
|
-
| **Selctor** | `d(text="1").exist` | `d(text="1").child(text="2").exist` |
|
|
313
|
-
|
|
314
|
-
If you need to specify `parent-child` relation ship in `@precondition`, specify it in xpath.
|
|
315
|
-
|
|
316
|
-
for example:
|
|
317
|
-
|
|
318
|
-
```python
|
|
319
|
-
# Do not use:
|
|
320
|
-
# @precondition(lambda self:
|
|
321
|
-
# self.d(className="android.widget.ListView").child(text="Bluetooth")
|
|
322
|
-
# ):
|
|
323
|
-
# ...
|
|
324
|
-
|
|
325
|
-
# Use
|
|
326
|
-
@precondition(lambda self:
|
|
327
|
-
self.d.xpath('//android.widget.ListView/*[@text="Bluetooth"]')
|
|
328
|
-
):
|
|
329
|
-
...
|
|
330
|
-
```
|
|
331
|
-
|
|
332
350
|
|
|
333
351
|
## Launching Kea2
|
|
334
352
|
|
|
@@ -353,16 +371,17 @@ kea2 run -s "emulator-5554" -p it.feio.android.omninotes.alpha --agent u2 --runn
|
|
|
353
371
|
kea2 run -s "emulator-5554" -p it.feio.android.omninotes.alpha --agent u2 --running-minutes 10 --throttle 200 --driver-name d unittest discover -s mytests/omni_notes
|
|
354
372
|
```
|
|
355
373
|
|
|
356
|
-
| arg | meaning |
|
|
357
|
-
| --- | --- |
|
|
358
|
-
| -s | The serial of your device, which can be found by `adb devices` |
|
|
359
|
-
| -p | The tested app's package name (e.g., com.example.app) |
|
|
360
|
-
| -o | The ouput directory for logs and results |
|
|
361
|
-
| --agent | {native, u2}. By default, `u2` is used and supports all the three important features of Kea2. If you hope to run the orignal Fastbot, please use `native`.|
|
|
362
|
-
| --running-minutes | The time (m) to run Kea2 |
|
|
363
|
-
| --max-step | The maxium number of monkey events to send (only available in `--agent u2`) |
|
|
364
|
-
| --throttle | The delay time (ms) between two monkey events |
|
|
374
|
+
| arg | meaning | default |
|
|
375
|
+
| --- | --- | --- |
|
|
376
|
+
| -s | The serial of your device, which can be found by `adb devices` | |
|
|
377
|
+
| -p | *The tested app's package name (e.g., com.example.app) |
|
|
378
|
+
| -o | The ouput directory for logs and results | `output` |
|
|
379
|
+
| --agent | {native, u2}. By default, `u2` is used and supports all the three important features of Kea2. If you hope to run the orignal Fastbot, please use `native`.| `u2` |
|
|
380
|
+
| --running-minutes | The time (m) to run Kea2 | `10` |
|
|
381
|
+
| --max-step | The maxium number of monkey events to send (only available in `--agent u2`) | `inf` |
|
|
382
|
+
| --throttle | The delay time (ms) between two monkey events | `200` |
|
|
365
383
|
| --driver-name | The name of driver used in the script. If `--driver-name d` is specified, you should use `d` to interact with a device, e..g, `self.d(..).click()`. |
|
|
384
|
+
| --log-stamp | the stamp for log file and result file. (e.g. `--log-stamp 123` then `fastbot_123.log` and `result_123.json` will be generated.) default: current time stamp | |
|
|
366
385
|
| unittest | Specify to load which scripts. This sub-command `unittest` is fully compatible with unittest. See `python3 -m unittest -h` for more options of unittest. This option is only available in `--agent u2`.
|
|
367
386
|
|
|
368
387
|
|
|
@@ -425,6 +444,8 @@ running_mins: int = 10
|
|
|
425
444
|
throttle: int = 200
|
|
426
445
|
# the output_dir for saving logs and results
|
|
427
446
|
output_dir: str = "output"
|
|
447
|
+
# the stamp for log file and result file, default: current time stamp
|
|
448
|
+
log_stamp: str = None
|
|
428
449
|
```
|
|
429
450
|
|
|
430
451
|
## Examining the running statistics of scripts .
|
|
@@ -469,6 +490,17 @@ Kea2 has been actively developed and maintained by the people in [ecnusse](https
|
|
|
469
490
|
- [uiautomator2](https://github.com/openatx/uiautomator2)
|
|
470
491
|
- [hypothesis](https://github.com/HypothesisWorks/hypothesis)
|
|
471
492
|
|
|
493
|
+
### Relevant papers of Kea2
|
|
494
|
+
|
|
495
|
+
> General and Practical Property-based Testing for Android Apps. ASE 2024. [pdf](https://dl.acm.org/doi/10.1145/3691620.3694986)
|
|
496
|
+
|
|
497
|
+
> An Empirical Study of Functional Bugs in Android Apps. ISSTA 2023. [pdf](https://dl.acm.org/doi/10.1145/3597926.3598138)
|
|
498
|
+
|
|
499
|
+
> Fastbot2: Reusable Automated Model-based GUI Testing for Android Enhanced by Reinforcement Learning. ASE 2022. [pdf](https://dl.acm.org/doi/10.1145/3551349.3559505)
|
|
500
|
+
|
|
501
|
+
> Guided, Stochastic Model-Based GUI Testing of Android Apps. ESEC/FSE 2017. [pdf](https://dl.acm.org/doi/10.1145/3106237.3106298)
|
|
502
|
+
|
|
503
|
+
|
|
472
504
|
[^1]: 不少UI自动化测试工具提供了“自定义事件序列”能力(如[Fastbot](https://github.com/bytedance/Fastbot_Android/blob/main/handbook-cn.md#%E8%87%AA%E5%AE%9A%E4%B9%89%E4%BA%8B%E4%BB%B6%E5%BA%8F%E5%88%97) 和[AppCrawler](https://github.com/seveniruby/AppCrawler)),但在实际使用中存在不少问题,如自定义能力有限、使用不灵活等。此前不少Fastbot用户抱怨过其“自定义事件序列”在使用中的问题,如[#209](https://github.com/bytedance/Fastbot_Android/issues/209), [#225](https://github.com/bytedance/Fastbot_Android/issues/225), [#286](https://github.com/bytedance/Fastbot_Android/issues/286)等。
|
|
473
505
|
|
|
474
506
|
[^2]: 在UI自动化测试过程中支持自动断言是一个很重要的能力,但几乎没有测试工具提供这样的能力。我们注意到[AppCrawler](https://ceshiren.com/t/topic/15801/5)的开发者曾经希望提供一种断言机制,得到了用户的热切响应,不少用户从21年就开始催更,但始终未能实现。
|
|
@@ -21,7 +21,6 @@ kea2/assets/framework.jar
|
|
|
21
21
|
kea2/assets/monkeyq.jar
|
|
22
22
|
kea2/assets/quicktest.py
|
|
23
23
|
kea2/assets/u2.jar
|
|
24
|
-
kea2/assets/fastbot_configs/ADBKeyBoard.apk
|
|
25
24
|
kea2/assets/fastbot_configs/abl.strings
|
|
26
25
|
kea2/assets/fastbot_configs/awl.strings
|
|
27
26
|
kea2/assets/fastbot_configs/max.config
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Kea2-python
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.1b3
|
|
4
4
|
Summary: A python library for supporting and customizing automated UI testing for mobile apps
|
|
5
5
|
Author-email: Xixian Liang <xixian@stu.ecnu.edu.cn>
|
|
6
6
|
Requires-Python: >=3.8
|
|
@@ -20,7 +20,7 @@ Dynamic: license-file
|
|
|
20
20
|
<img src="https://github.com/user-attachments/assets/58f68b00-cc9c-4620-9e2e-66c43cf7caae" style="border-radius: 14px; width: 20%; height: 20%;"/>
|
|
21
21
|
</div>
|
|
22
22
|
|
|
23
|
-
Kea2 is an easy-to-use Python library for supporting, customizing and improving automated UI testing for mobile apps.
|
|
23
|
+
Kea2 is an easy-to-use Python library for supporting, customizing and improving automated UI testing for mobile apps. Kea2's novelty is able to fuse the scripts (usually written by human) with automated UI testing tools, thus allowing many interesting and powerful features. Kea2 is currently built on top of [Fastbot](https://github.com/bytedance/Fastbot_Android) and [uiautomator2](https://github.com/openatx/uiautomator2) and targets [Android](https://en.wikipedia.org/wiki/Android_(operating_system)) apps.
|
|
24
24
|
|
|
25
25
|
### Kea2 has three important features:
|
|
26
26
|
- **Feature 1**(查找稳定性问题): coming with the full capability of [Fastbot](https://github.com/bytedance/Fastbot_Android) for stress testing and finding *stability problems* (i.e., *crashing bugs*);
|
|
@@ -49,7 +49,7 @@ Kea2, released as a Python library, currently works with:
|
|
|
49
49
|
- [uiautomator2](https://github.com/openatx/uiautomator2) as the UI test driver;
|
|
50
50
|
- [Fastbot](https://github.com/bytedance/Fastbot_Android) as the backend automated UI testing tool.
|
|
51
51
|
|
|
52
|
-
In the future, Kea2 will be extended to support
|
|
52
|
+
**Roadmap**: In the future, Kea2 will be extended to support
|
|
53
53
|
- [pytest](https://docs.pytest.org/en/stable/)
|
|
54
54
|
- [Appium](https://github.com/appium/appium), [Hypium]() (for HarmonyOS)
|
|
55
55
|
- other automated UI testing tools (not limited to Fastbot)
|
|
@@ -61,13 +61,14 @@ In the future, Kea2 will be extended to support
|
|
|
61
61
|
Running requirements/environment:
|
|
62
62
|
- support Windows, MacOS and Linux
|
|
63
63
|
- python 3.8+
|
|
64
|
+
- Android 4.4+
|
|
64
65
|
- Android SDK installed
|
|
65
66
|
- **VPN closed** (Features 2 and 3 required)
|
|
66
67
|
|
|
67
68
|
|
|
68
69
|
Install Kea2 by `pip`:
|
|
69
70
|
```bash
|
|
70
|
-
python3 -m pip install
|
|
71
|
+
python3 -m pip install kea2-python
|
|
71
72
|
```
|
|
72
73
|
|
|
73
74
|
Find Kea2's additional options by running
|
|
@@ -174,20 +175,31 @@ You can find the full example in script `quicktest.py`, and run this script with
|
|
|
174
175
|
kea2 run -s "emulator-5554" -p it.feio.android.omninotes.alpha --agent u2 --running-minutes 10 --throttle 200 --driver-name d unittest discover -p quicktest.py
|
|
175
176
|
```
|
|
176
177
|
|
|
177
|
-
### Example 2: blacklisting specific UI
|
|
178
|
+
### Example 2: blacklisting specific UI elements
|
|
178
179
|
|
|
179
|
-
We support blacklisting specific
|
|
180
|
+
We support blacklisting specific elements so that Fastbot can avoid interacting with these
|
|
181
|
+
elements during fuzzing.
|
|
180
182
|
|
|
181
|
-
|
|
182
|
-
The widgets needed to be blocked can be flexibly specified by u2 selector (e.g., `text`, `description`) or `xpath`, etc.
|
|
183
|
+
We support two granularity levels for UI blocking:
|
|
183
184
|
|
|
184
|
-
|
|
185
|
+
- Widget Blocking: Block individual UI widgets.
|
|
186
|
+
|
|
187
|
+
- Tree Blocking : Block a UI widget trees by specifying its root node.
|
|
188
|
+
It can block the root node and all its descendants.
|
|
189
|
+
|
|
190
|
+
We support (1) `Global Block List` (always taking effective), and (2) `Conditional Block List` (only taking effective when some conditions are met).
|
|
191
|
+
|
|
192
|
+
The list of blocked elements are specified in Kea2's config file `configs/widget.block.py` (generated when running `kea2 init`).
|
|
193
|
+
The elements needed to be blocked can be flexibly specified by u2 selector (e.g., `text`, `description`) or `xpath`, etc.
|
|
194
|
+
|
|
195
|
+
#### Widget Blocking
|
|
196
|
+
##### Global Block List
|
|
185
197
|
We can define the function `global_block_widgets` to specify which UI widgets should be blocked globally. The blocking always takes effect.
|
|
186
198
|
|
|
187
199
|
```python
|
|
188
200
|
# file: configs/widget.block.py
|
|
189
201
|
|
|
190
|
-
|
|
202
|
+
def global_block_widgets(d: "Device"):
|
|
191
203
|
"""
|
|
192
204
|
global block list.
|
|
193
205
|
return the widgets which should be blocked globally
|
|
@@ -196,8 +208,8 @@ We can define the function `global_block_widgets` to specify which UI widgets sh
|
|
|
196
208
|
d.xpath(".//node[@text='widget to block']"),
|
|
197
209
|
d(description="widgets to block")]
|
|
198
210
|
```
|
|
199
|
-
|
|
200
|
-
We can define any reserved function whose name starts with "block_" and decorate such function by `@precondition` to allow conditional block list.
|
|
211
|
+
##### Conditional Block List
|
|
212
|
+
We can define any reserved function whose name starts with "block_" (but not requiring "block_tree_" prefix) and decorate such function by `@precondition` to allow conditional block list.
|
|
201
213
|
In this case, the blocking only takes effect when the precondition is satisfied.
|
|
202
214
|
```python
|
|
203
215
|
# file: configs/widget.block.py
|
|
@@ -211,6 +223,37 @@ def block_sth(d: "Device"):
|
|
|
211
223
|
d(description="widgets to block")]
|
|
212
224
|
```
|
|
213
225
|
|
|
226
|
+
#### Tree Blocking
|
|
227
|
+
##### Global Block List
|
|
228
|
+
We can define the function `global_block_tree` to specify which UI widget trees should be blocked globally. The blocking always takes effect.
|
|
229
|
+
|
|
230
|
+
```python
|
|
231
|
+
# file: configs/widget.block.py
|
|
232
|
+
|
|
233
|
+
def global_block_tree(d: "Device"):
|
|
234
|
+
"""
|
|
235
|
+
Specify UI widget trees to be blocked globally during testing.
|
|
236
|
+
Returns a list of root nodes whose entire subtrees will be blocked from exploration.
|
|
237
|
+
This function is only available in 'u2 agent' mode.
|
|
238
|
+
"""
|
|
239
|
+
return [d(text="trees to block"), d.xpath(".//node[@text='tree to block']")]
|
|
240
|
+
```
|
|
241
|
+
##### Conditional Block List
|
|
242
|
+
We can define any reserved function whose name starts with "block_tree_" and decorate such function by `@precondition` to allow conditional block list.
|
|
243
|
+
In this case, the blocking only takes effect when the precondition is satisfied.
|
|
244
|
+
```python
|
|
245
|
+
# file: configs/widget.block.py
|
|
246
|
+
|
|
247
|
+
# Example of conditional tree blocking with precondition
|
|
248
|
+
|
|
249
|
+
@precondition(lambda d: d(text="In the home page").exists)
|
|
250
|
+
def block_tree_sth(d: "Device"):
|
|
251
|
+
# Note: Function name must start with "block_tree_"
|
|
252
|
+
return [d(text="trees to block"),
|
|
253
|
+
d.xpath(".//node[@text='tree to block']"),
|
|
254
|
+
d(description="trees to block")]
|
|
255
|
+
```
|
|
256
|
+
|
|
214
257
|
## Feature 3(支持断言机制): Supporting auto-assertions by scripts.
|
|
215
258
|
|
|
216
259
|
Kea2 supports auto-assertions when running Fastbot for finding *logic bugs* (i.e., *non-crashing bugs*). To achieve this, you can add assertions in the scripts. When an assertion fails during automated UI testing, we find a likely functional bug. This idea is inspired by [property-based testing](https://en.wikipedia.org/wiki/Software_testing#Property_testing) inheritted from [Kea](https://github.com/ecnusse/Kea).
|
|
@@ -304,31 +347,6 @@ class MyFirstTest(unittest.TestCase):
|
|
|
304
347
|
|
|
305
348
|
You can read [Kea - Write your fisrt property](https://kea-docs.readthedocs.io/en/latest/part-keaUserManuel/first_property.html) for more details.
|
|
306
349
|
|
|
307
|
-
### Notes
|
|
308
|
-
Currently, in `@precondition` decorator and `widgets.block.py`. We only support basic selector (No parent-child relationship) in uiautomator2. The Script body in the function fully support uiautomator2.
|
|
309
|
-
|
|
310
|
-
| | **Support** | **Not support** |
|
|
311
|
-
| -- | -- | -- |
|
|
312
|
-
| **Selctor** | `d(text="1").exist` | `d(text="1").child(text="2").exist` |
|
|
313
|
-
|
|
314
|
-
If you need to specify `parent-child` relation ship in `@precondition`, specify it in xpath.
|
|
315
|
-
|
|
316
|
-
for example:
|
|
317
|
-
|
|
318
|
-
```python
|
|
319
|
-
# Do not use:
|
|
320
|
-
# @precondition(lambda self:
|
|
321
|
-
# self.d(className="android.widget.ListView").child(text="Bluetooth")
|
|
322
|
-
# ):
|
|
323
|
-
# ...
|
|
324
|
-
|
|
325
|
-
# Use
|
|
326
|
-
@precondition(lambda self:
|
|
327
|
-
self.d.xpath('//android.widget.ListView/*[@text="Bluetooth"]')
|
|
328
|
-
):
|
|
329
|
-
...
|
|
330
|
-
```
|
|
331
|
-
|
|
332
350
|
|
|
333
351
|
## Launching Kea2
|
|
334
352
|
|
|
@@ -353,16 +371,17 @@ kea2 run -s "emulator-5554" -p it.feio.android.omninotes.alpha --agent u2 --runn
|
|
|
353
371
|
kea2 run -s "emulator-5554" -p it.feio.android.omninotes.alpha --agent u2 --running-minutes 10 --throttle 200 --driver-name d unittest discover -s mytests/omni_notes
|
|
354
372
|
```
|
|
355
373
|
|
|
356
|
-
| arg | meaning |
|
|
357
|
-
| --- | --- |
|
|
358
|
-
| -s | The serial of your device, which can be found by `adb devices` |
|
|
359
|
-
| -p | The tested app's package name (e.g., com.example.app) |
|
|
360
|
-
| -o | The ouput directory for logs and results |
|
|
361
|
-
| --agent | {native, u2}. By default, `u2` is used and supports all the three important features of Kea2. If you hope to run the orignal Fastbot, please use `native`.|
|
|
362
|
-
| --running-minutes | The time (m) to run Kea2 |
|
|
363
|
-
| --max-step | The maxium number of monkey events to send (only available in `--agent u2`) |
|
|
364
|
-
| --throttle | The delay time (ms) between two monkey events |
|
|
374
|
+
| arg | meaning | default |
|
|
375
|
+
| --- | --- | --- |
|
|
376
|
+
| -s | The serial of your device, which can be found by `adb devices` | |
|
|
377
|
+
| -p | *The tested app's package name (e.g., com.example.app) |
|
|
378
|
+
| -o | The ouput directory for logs and results | `output` |
|
|
379
|
+
| --agent | {native, u2}. By default, `u2` is used and supports all the three important features of Kea2. If you hope to run the orignal Fastbot, please use `native`.| `u2` |
|
|
380
|
+
| --running-minutes | The time (m) to run Kea2 | `10` |
|
|
381
|
+
| --max-step | The maxium number of monkey events to send (only available in `--agent u2`) | `inf` |
|
|
382
|
+
| --throttle | The delay time (ms) between two monkey events | `200` |
|
|
365
383
|
| --driver-name | The name of driver used in the script. If `--driver-name d` is specified, you should use `d` to interact with a device, e..g, `self.d(..).click()`. |
|
|
384
|
+
| --log-stamp | the stamp for log file and result file. (e.g. `--log-stamp 123` then `fastbot_123.log` and `result_123.json` will be generated.) default: current time stamp | |
|
|
366
385
|
| unittest | Specify to load which scripts. This sub-command `unittest` is fully compatible with unittest. See `python3 -m unittest -h` for more options of unittest. This option is only available in `--agent u2`.
|
|
367
386
|
|
|
368
387
|
|
|
@@ -425,6 +444,8 @@ running_mins: int = 10
|
|
|
425
444
|
throttle: int = 200
|
|
426
445
|
# the output_dir for saving logs and results
|
|
427
446
|
output_dir: str = "output"
|
|
447
|
+
# the stamp for log file and result file, default: current time stamp
|
|
448
|
+
log_stamp: str = None
|
|
428
449
|
```
|
|
429
450
|
|
|
430
451
|
## Examining the running statistics of scripts .
|
|
@@ -469,6 +490,17 @@ Kea2 has been actively developed and maintained by the people in [ecnusse](https
|
|
|
469
490
|
- [uiautomator2](https://github.com/openatx/uiautomator2)
|
|
470
491
|
- [hypothesis](https://github.com/HypothesisWorks/hypothesis)
|
|
471
492
|
|
|
493
|
+
### Relevant papers of Kea2
|
|
494
|
+
|
|
495
|
+
> General and Practical Property-based Testing for Android Apps. ASE 2024. [pdf](https://dl.acm.org/doi/10.1145/3691620.3694986)
|
|
496
|
+
|
|
497
|
+
> An Empirical Study of Functional Bugs in Android Apps. ISSTA 2023. [pdf](https://dl.acm.org/doi/10.1145/3597926.3598138)
|
|
498
|
+
|
|
499
|
+
> Fastbot2: Reusable Automated Model-based GUI Testing for Android Enhanced by Reinforcement Learning. ASE 2022. [pdf](https://dl.acm.org/doi/10.1145/3551349.3559505)
|
|
500
|
+
|
|
501
|
+
> Guided, Stochastic Model-Based GUI Testing of Android Apps. ESEC/FSE 2017. [pdf](https://dl.acm.org/doi/10.1145/3106237.3106298)
|
|
502
|
+
|
|
503
|
+
|
|
472
504
|
[^1]: 不少UI自动化测试工具提供了“自定义事件序列”能力(如[Fastbot](https://github.com/bytedance/Fastbot_Android/blob/main/handbook-cn.md#%E8%87%AA%E5%AE%9A%E4%B9%89%E4%BA%8B%E4%BB%B6%E5%BA%8F%E5%88%97) 和[AppCrawler](https://github.com/seveniruby/AppCrawler)),但在实际使用中存在不少问题,如自定义能力有限、使用不灵活等。此前不少Fastbot用户抱怨过其“自定义事件序列”在使用中的问题,如[#209](https://github.com/bytedance/Fastbot_Android/issues/209), [#225](https://github.com/bytedance/Fastbot_Android/issues/225), [#286](https://github.com/bytedance/Fastbot_Android/issues/286)等。
|
|
473
505
|
|
|
474
506
|
[^2]: 在UI自动化测试过程中支持自动断言是一个很重要的能力,但几乎没有测试工具提供这样的能力。我们注意到[AppCrawler](https://ceshiren.com/t/topic/15801/5)的开发者曾经希望提供一种断言机制,得到了用户的热切响应,不少用户从21年就开始催更,但始终未能实现。
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<img src="https://github.com/user-attachments/assets/58f68b00-cc9c-4620-9e2e-66c43cf7caae" style="border-radius: 14px; width: 20%; height: 20%;"/>
|
|
9
9
|
</div>
|
|
10
10
|
|
|
11
|
-
Kea2 is an easy-to-use Python library for supporting, customizing and improving automated UI testing for mobile apps.
|
|
11
|
+
Kea2 is an easy-to-use Python library for supporting, customizing and improving automated UI testing for mobile apps. Kea2's novelty is able to fuse the scripts (usually written by human) with automated UI testing tools, thus allowing many interesting and powerful features. Kea2 is currently built on top of [Fastbot](https://github.com/bytedance/Fastbot_Android) and [uiautomator2](https://github.com/openatx/uiautomator2) and targets [Android](https://en.wikipedia.org/wiki/Android_(operating_system)) apps.
|
|
12
12
|
|
|
13
13
|
### Kea2 has three important features:
|
|
14
14
|
- **Feature 1**(查找稳定性问题): coming with the full capability of [Fastbot](https://github.com/bytedance/Fastbot_Android) for stress testing and finding *stability problems* (i.e., *crashing bugs*);
|
|
@@ -37,7 +37,7 @@ Kea2, released as a Python library, currently works with:
|
|
|
37
37
|
- [uiautomator2](https://github.com/openatx/uiautomator2) as the UI test driver;
|
|
38
38
|
- [Fastbot](https://github.com/bytedance/Fastbot_Android) as the backend automated UI testing tool.
|
|
39
39
|
|
|
40
|
-
In the future, Kea2 will be extended to support
|
|
40
|
+
**Roadmap**: In the future, Kea2 will be extended to support
|
|
41
41
|
- [pytest](https://docs.pytest.org/en/stable/)
|
|
42
42
|
- [Appium](https://github.com/appium/appium), [Hypium]() (for HarmonyOS)
|
|
43
43
|
- other automated UI testing tools (not limited to Fastbot)
|
|
@@ -49,13 +49,14 @@ In the future, Kea2 will be extended to support
|
|
|
49
49
|
Running requirements/environment:
|
|
50
50
|
- support Windows, MacOS and Linux
|
|
51
51
|
- python 3.8+
|
|
52
|
+
- Android 4.4+
|
|
52
53
|
- Android SDK installed
|
|
53
54
|
- **VPN closed** (Features 2 and 3 required)
|
|
54
55
|
|
|
55
56
|
|
|
56
57
|
Install Kea2 by `pip`:
|
|
57
58
|
```bash
|
|
58
|
-
python3 -m pip install
|
|
59
|
+
python3 -m pip install kea2-python
|
|
59
60
|
```
|
|
60
61
|
|
|
61
62
|
Find Kea2's additional options by running
|
|
@@ -162,20 +163,31 @@ You can find the full example in script `quicktest.py`, and run this script with
|
|
|
162
163
|
kea2 run -s "emulator-5554" -p it.feio.android.omninotes.alpha --agent u2 --running-minutes 10 --throttle 200 --driver-name d unittest discover -p quicktest.py
|
|
163
164
|
```
|
|
164
165
|
|
|
165
|
-
### Example 2: blacklisting specific UI
|
|
166
|
+
### Example 2: blacklisting specific UI elements
|
|
166
167
|
|
|
167
|
-
We support blacklisting specific
|
|
168
|
+
We support blacklisting specific elements so that Fastbot can avoid interacting with these
|
|
169
|
+
elements during fuzzing.
|
|
168
170
|
|
|
169
|
-
|
|
170
|
-
The widgets needed to be blocked can be flexibly specified by u2 selector (e.g., `text`, `description`) or `xpath`, etc.
|
|
171
|
+
We support two granularity levels for UI blocking:
|
|
171
172
|
|
|
172
|
-
|
|
173
|
+
- Widget Blocking: Block individual UI widgets.
|
|
174
|
+
|
|
175
|
+
- Tree Blocking : Block a UI widget trees by specifying its root node.
|
|
176
|
+
It can block the root node and all its descendants.
|
|
177
|
+
|
|
178
|
+
We support (1) `Global Block List` (always taking effective), and (2) `Conditional Block List` (only taking effective when some conditions are met).
|
|
179
|
+
|
|
180
|
+
The list of blocked elements are specified in Kea2's config file `configs/widget.block.py` (generated when running `kea2 init`).
|
|
181
|
+
The elements needed to be blocked can be flexibly specified by u2 selector (e.g., `text`, `description`) or `xpath`, etc.
|
|
182
|
+
|
|
183
|
+
#### Widget Blocking
|
|
184
|
+
##### Global Block List
|
|
173
185
|
We can define the function `global_block_widgets` to specify which UI widgets should be blocked globally. The blocking always takes effect.
|
|
174
186
|
|
|
175
187
|
```python
|
|
176
188
|
# file: configs/widget.block.py
|
|
177
189
|
|
|
178
|
-
|
|
190
|
+
def global_block_widgets(d: "Device"):
|
|
179
191
|
"""
|
|
180
192
|
global block list.
|
|
181
193
|
return the widgets which should be blocked globally
|
|
@@ -184,8 +196,8 @@ We can define the function `global_block_widgets` to specify which UI widgets sh
|
|
|
184
196
|
d.xpath(".//node[@text='widget to block']"),
|
|
185
197
|
d(description="widgets to block")]
|
|
186
198
|
```
|
|
187
|
-
|
|
188
|
-
We can define any reserved function whose name starts with "block_" and decorate such function by `@precondition` to allow conditional block list.
|
|
199
|
+
##### Conditional Block List
|
|
200
|
+
We can define any reserved function whose name starts with "block_" (but not requiring "block_tree_" prefix) and decorate such function by `@precondition` to allow conditional block list.
|
|
189
201
|
In this case, the blocking only takes effect when the precondition is satisfied.
|
|
190
202
|
```python
|
|
191
203
|
# file: configs/widget.block.py
|
|
@@ -199,6 +211,37 @@ def block_sth(d: "Device"):
|
|
|
199
211
|
d(description="widgets to block")]
|
|
200
212
|
```
|
|
201
213
|
|
|
214
|
+
#### Tree Blocking
|
|
215
|
+
##### Global Block List
|
|
216
|
+
We can define the function `global_block_tree` to specify which UI widget trees should be blocked globally. The blocking always takes effect.
|
|
217
|
+
|
|
218
|
+
```python
|
|
219
|
+
# file: configs/widget.block.py
|
|
220
|
+
|
|
221
|
+
def global_block_tree(d: "Device"):
|
|
222
|
+
"""
|
|
223
|
+
Specify UI widget trees to be blocked globally during testing.
|
|
224
|
+
Returns a list of root nodes whose entire subtrees will be blocked from exploration.
|
|
225
|
+
This function is only available in 'u2 agent' mode.
|
|
226
|
+
"""
|
|
227
|
+
return [d(text="trees to block"), d.xpath(".//node[@text='tree to block']")]
|
|
228
|
+
```
|
|
229
|
+
##### Conditional Block List
|
|
230
|
+
We can define any reserved function whose name starts with "block_tree_" and decorate such function by `@precondition` to allow conditional block list.
|
|
231
|
+
In this case, the blocking only takes effect when the precondition is satisfied.
|
|
232
|
+
```python
|
|
233
|
+
# file: configs/widget.block.py
|
|
234
|
+
|
|
235
|
+
# Example of conditional tree blocking with precondition
|
|
236
|
+
|
|
237
|
+
@precondition(lambda d: d(text="In the home page").exists)
|
|
238
|
+
def block_tree_sth(d: "Device"):
|
|
239
|
+
# Note: Function name must start with "block_tree_"
|
|
240
|
+
return [d(text="trees to block"),
|
|
241
|
+
d.xpath(".//node[@text='tree to block']"),
|
|
242
|
+
d(description="trees to block")]
|
|
243
|
+
```
|
|
244
|
+
|
|
202
245
|
## Feature 3(支持断言机制): Supporting auto-assertions by scripts.
|
|
203
246
|
|
|
204
247
|
Kea2 supports auto-assertions when running Fastbot for finding *logic bugs* (i.e., *non-crashing bugs*). To achieve this, you can add assertions in the scripts. When an assertion fails during automated UI testing, we find a likely functional bug. This idea is inspired by [property-based testing](https://en.wikipedia.org/wiki/Software_testing#Property_testing) inheritted from [Kea](https://github.com/ecnusse/Kea).
|
|
@@ -292,31 +335,6 @@ class MyFirstTest(unittest.TestCase):
|
|
|
292
335
|
|
|
293
336
|
You can read [Kea - Write your fisrt property](https://kea-docs.readthedocs.io/en/latest/part-keaUserManuel/first_property.html) for more details.
|
|
294
337
|
|
|
295
|
-
### Notes
|
|
296
|
-
Currently, in `@precondition` decorator and `widgets.block.py`. We only support basic selector (No parent-child relationship) in uiautomator2. The Script body in the function fully support uiautomator2.
|
|
297
|
-
|
|
298
|
-
| | **Support** | **Not support** |
|
|
299
|
-
| -- | -- | -- |
|
|
300
|
-
| **Selctor** | `d(text="1").exist` | `d(text="1").child(text="2").exist` |
|
|
301
|
-
|
|
302
|
-
If you need to specify `parent-child` relation ship in `@precondition`, specify it in xpath.
|
|
303
|
-
|
|
304
|
-
for example:
|
|
305
|
-
|
|
306
|
-
```python
|
|
307
|
-
# Do not use:
|
|
308
|
-
# @precondition(lambda self:
|
|
309
|
-
# self.d(className="android.widget.ListView").child(text="Bluetooth")
|
|
310
|
-
# ):
|
|
311
|
-
# ...
|
|
312
|
-
|
|
313
|
-
# Use
|
|
314
|
-
@precondition(lambda self:
|
|
315
|
-
self.d.xpath('//android.widget.ListView/*[@text="Bluetooth"]')
|
|
316
|
-
):
|
|
317
|
-
...
|
|
318
|
-
```
|
|
319
|
-
|
|
320
338
|
|
|
321
339
|
## Launching Kea2
|
|
322
340
|
|
|
@@ -341,16 +359,17 @@ kea2 run -s "emulator-5554" -p it.feio.android.omninotes.alpha --agent u2 --runn
|
|
|
341
359
|
kea2 run -s "emulator-5554" -p it.feio.android.omninotes.alpha --agent u2 --running-minutes 10 --throttle 200 --driver-name d unittest discover -s mytests/omni_notes
|
|
342
360
|
```
|
|
343
361
|
|
|
344
|
-
| arg | meaning |
|
|
345
|
-
| --- | --- |
|
|
346
|
-
| -s | The serial of your device, which can be found by `adb devices` |
|
|
347
|
-
| -p | The tested app's package name (e.g., com.example.app) |
|
|
348
|
-
| -o | The ouput directory for logs and results |
|
|
349
|
-
| --agent | {native, u2}. By default, `u2` is used and supports all the three important features of Kea2. If you hope to run the orignal Fastbot, please use `native`.|
|
|
350
|
-
| --running-minutes | The time (m) to run Kea2 |
|
|
351
|
-
| --max-step | The maxium number of monkey events to send (only available in `--agent u2`) |
|
|
352
|
-
| --throttle | The delay time (ms) between two monkey events |
|
|
362
|
+
| arg | meaning | default |
|
|
363
|
+
| --- | --- | --- |
|
|
364
|
+
| -s | The serial of your device, which can be found by `adb devices` | |
|
|
365
|
+
| -p | *The tested app's package name (e.g., com.example.app) |
|
|
366
|
+
| -o | The ouput directory for logs and results | `output` |
|
|
367
|
+
| --agent | {native, u2}. By default, `u2` is used and supports all the three important features of Kea2. If you hope to run the orignal Fastbot, please use `native`.| `u2` |
|
|
368
|
+
| --running-minutes | The time (m) to run Kea2 | `10` |
|
|
369
|
+
| --max-step | The maxium number of monkey events to send (only available in `--agent u2`) | `inf` |
|
|
370
|
+
| --throttle | The delay time (ms) between two monkey events | `200` |
|
|
353
371
|
| --driver-name | The name of driver used in the script. If `--driver-name d` is specified, you should use `d` to interact with a device, e..g, `self.d(..).click()`. |
|
|
372
|
+
| --log-stamp | the stamp for log file and result file. (e.g. `--log-stamp 123` then `fastbot_123.log` and `result_123.json` will be generated.) default: current time stamp | |
|
|
354
373
|
| unittest | Specify to load which scripts. This sub-command `unittest` is fully compatible with unittest. See `python3 -m unittest -h` for more options of unittest. This option is only available in `--agent u2`.
|
|
355
374
|
|
|
356
375
|
|
|
@@ -413,6 +432,8 @@ running_mins: int = 10
|
|
|
413
432
|
throttle: int = 200
|
|
414
433
|
# the output_dir for saving logs and results
|
|
415
434
|
output_dir: str = "output"
|
|
435
|
+
# the stamp for log file and result file, default: current time stamp
|
|
436
|
+
log_stamp: str = None
|
|
416
437
|
```
|
|
417
438
|
|
|
418
439
|
## Examining the running statistics of scripts .
|
|
@@ -457,6 +478,17 @@ Kea2 has been actively developed and maintained by the people in [ecnusse](https
|
|
|
457
478
|
- [uiautomator2](https://github.com/openatx/uiautomator2)
|
|
458
479
|
- [hypothesis](https://github.com/HypothesisWorks/hypothesis)
|
|
459
480
|
|
|
481
|
+
### Relevant papers of Kea2
|
|
482
|
+
|
|
483
|
+
> General and Practical Property-based Testing for Android Apps. ASE 2024. [pdf](https://dl.acm.org/doi/10.1145/3691620.3694986)
|
|
484
|
+
|
|
485
|
+
> An Empirical Study of Functional Bugs in Android Apps. ISSTA 2023. [pdf](https://dl.acm.org/doi/10.1145/3597926.3598138)
|
|
486
|
+
|
|
487
|
+
> Fastbot2: Reusable Automated Model-based GUI Testing for Android Enhanced by Reinforcement Learning. ASE 2022. [pdf](https://dl.acm.org/doi/10.1145/3551349.3559505)
|
|
488
|
+
|
|
489
|
+
> Guided, Stochastic Model-Based GUI Testing of Android Apps. ESEC/FSE 2017. [pdf](https://dl.acm.org/doi/10.1145/3106237.3106298)
|
|
490
|
+
|
|
491
|
+
|
|
460
492
|
[^1]: 不少UI自动化测试工具提供了“自定义事件序列”能力(如[Fastbot](https://github.com/bytedance/Fastbot_Android/blob/main/handbook-cn.md#%E8%87%AA%E5%AE%9A%E4%B9%89%E4%BA%8B%E4%BB%B6%E5%BA%8F%E5%88%97) 和[AppCrawler](https://github.com/seveniruby/AppCrawler)),但在实际使用中存在不少问题,如自定义能力有限、使用不灵活等。此前不少Fastbot用户抱怨过其“自定义事件序列”在使用中的问题,如[#209](https://github.com/bytedance/Fastbot_Android/issues/209), [#225](https://github.com/bytedance/Fastbot_Android/issues/225), [#286](https://github.com/bytedance/Fastbot_Android/issues/286)等。
|
|
461
493
|
|
|
462
494
|
[^2]: 在UI自动化测试过程中支持自动断言是一个很重要的能力,但几乎没有测试工具提供这样的能力。我们注意到[AppCrawler](https://ceshiren.com/t/topic/15801/5)的开发者曾经希望提供一种断言机制,得到了用户的热切响应,不少用户从21年就开始催更,但始终未能实现。
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import subprocess
|
|
2
|
-
from typing import List, Optional
|
|
2
|
+
from typing import List, Optional, Set
|
|
3
3
|
from .utils import getLogger
|
|
4
4
|
|
|
5
5
|
logger = getLogger(__name__)
|
|
@@ -223,6 +223,34 @@ def remove_all_forwards(device: Optional[str] = None):
|
|
|
223
223
|
return run_adb_command(["-s", device, "forward", "--remove-all"])
|
|
224
224
|
|
|
225
225
|
|
|
226
|
+
@ensure_device
|
|
227
|
+
def get_packages(device: Optional[str] = None) -> Set[str]:
|
|
228
|
+
"""
|
|
229
|
+
Retrieves packages that match the specified regular expression pattern.
|
|
230
|
+
|
|
231
|
+
Parameters:
|
|
232
|
+
pattern (str): Regular expression pattern to match package names.
|
|
233
|
+
device (str, optional): The device serial number. If None, it is resolved automatically.
|
|
234
|
+
|
|
235
|
+
Returns:
|
|
236
|
+
set: A set of package names that match the pattern.
|
|
237
|
+
"""
|
|
238
|
+
import re
|
|
239
|
+
|
|
240
|
+
cmd = ["-s", device, "shell", "pm", "list", "packages"]
|
|
241
|
+
output = run_adb_command(cmd)
|
|
242
|
+
|
|
243
|
+
packages = set()
|
|
244
|
+
if output:
|
|
245
|
+
compiled_pattern = re.compile(r"package:(.+)\n")
|
|
246
|
+
matches = compiled_pattern.findall(output)
|
|
247
|
+
for match in matches:
|
|
248
|
+
if match:
|
|
249
|
+
packages.add(match)
|
|
250
|
+
|
|
251
|
+
return packages
|
|
252
|
+
|
|
253
|
+
|
|
226
254
|
if __name__ == '__main__':
|
|
227
255
|
# For testing: print the list of currently connected devices.
|
|
228
256
|
devices = get_devices()
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from kea2.utils import Device
|
|
2
|
+
from kea2.keaUtils import precondition
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def global_block_widgets(d: "Device"):
|
|
6
|
+
"""
|
|
7
|
+
Specify UI widgets to be blocked globally during testing.
|
|
8
|
+
Returns a list of widgets that should be blocked from exploration.
|
|
9
|
+
This function is only available in 'u2 agent' mode.
|
|
10
|
+
"""
|
|
11
|
+
# return [d(text="widgets to block"), d.xpath(".//node[@text='widget to block']")]
|
|
12
|
+
return []
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# Example of conditional blocking with precondition
|
|
16
|
+
# @precondition(lambda d: d(text="In the home page").exists)
|
|
17
|
+
@precondition(lambda d: False)
|
|
18
|
+
def block_sth(d: "Device"):
|
|
19
|
+
# Note: Function name must start with "block_"
|
|
20
|
+
return []
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def global_block_tree(d: "Device"):
|
|
24
|
+
"""
|
|
25
|
+
Specify UI widget trees to be blocked globally during testing.
|
|
26
|
+
Returns a list of root nodes whose entire subtrees will be blocked from exploration.
|
|
27
|
+
This function is only available in 'u2 agent' mode.
|
|
28
|
+
"""
|
|
29
|
+
# return [d(text="trees to block"), d.xpath(".//node[@text='tree to block']")]
|
|
30
|
+
return []
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# Example of conditional tree blocking with precondition
|
|
34
|
+
# @precondition(lambda d: d(text="In the home page").exists)
|
|
35
|
+
@precondition(lambda d: False)
|
|
36
|
+
def block_tree_sth(d: "Device"):
|
|
37
|
+
# Note: Function name must start with "block_tree_"
|
|
38
|
+
return []
|
|
Binary file
|
|
@@ -119,6 +119,13 @@ class Options:
|
|
|
119
119
|
output_dir: str = "output"
|
|
120
120
|
# the stamp for log file and result file, default: current time stamp
|
|
121
121
|
log_stamp: str = None
|
|
122
|
+
# the profiling period to get the coverage result.
|
|
123
|
+
profile_period: int = None
|
|
124
|
+
|
|
125
|
+
def __setattr__(self, name, value):
|
|
126
|
+
if value is None:
|
|
127
|
+
return
|
|
128
|
+
super().__setattr__(name, value)
|
|
122
129
|
|
|
123
130
|
def __post_init__(self):
|
|
124
131
|
if self.serial and self.Driver:
|
|
@@ -127,6 +134,17 @@ class Options:
|
|
|
127
134
|
global LOGFILE, RESFILE
|
|
128
135
|
LOGFILE = f"fastbot_{self.log_stamp}.log"
|
|
129
136
|
RESFILE = f"result_{self.log_stamp}.json"
|
|
137
|
+
_check_package_installation(self.serial, self.packageNames)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def _check_package_installation(serial, packageNames):
|
|
141
|
+
from .adbUtils import get_packages
|
|
142
|
+
installed_packages = get_packages(device=serial)
|
|
143
|
+
|
|
144
|
+
for package in packageNames:
|
|
145
|
+
if package not in installed_packages:
|
|
146
|
+
logger.error(f"package {package} not installed. Abort.")
|
|
147
|
+
raise ValueError("package not installed")
|
|
130
148
|
|
|
131
149
|
|
|
132
150
|
@dataclass
|
|
@@ -257,7 +275,7 @@ def startFastbotService(options: Options) -> threading.Thread:
|
|
|
257
275
|
"reuseq",
|
|
258
276
|
"--running-minutes", f"{options.running_mins}",
|
|
259
277
|
"--throttle", f"{options.throttle}",
|
|
260
|
-
"--bugreport", "--output-directory", "/sdcard/fastbot_report"
|
|
278
|
+
"--bugreport", "--output-directory", "/sdcard/fastbot_report",
|
|
261
279
|
"-v", "-v", "-v"
|
|
262
280
|
]
|
|
263
281
|
|
|
@@ -287,7 +305,8 @@ class KeaTestRunner(TextTestRunner):
|
|
|
287
305
|
resultclass: JsonResult
|
|
288
306
|
allProperties: PropertyStore
|
|
289
307
|
options: Options = None
|
|
290
|
-
|
|
308
|
+
_block_funcs: Dict[Literal["widgets", "trees"], List[Callable]] = None
|
|
309
|
+
# _block_trees_funcs = None
|
|
291
310
|
|
|
292
311
|
@classmethod
|
|
293
312
|
def setOptions(cls, options: Options):
|
|
@@ -354,18 +373,20 @@ class KeaTestRunner(TextTestRunner):
|
|
|
354
373
|
check_alive(port=self.scriptDriver.lport)
|
|
355
374
|
|
|
356
375
|
end_by_remote = False
|
|
357
|
-
|
|
358
|
-
while
|
|
376
|
+
self.stepsCount = 0
|
|
377
|
+
while self.stepsCount < self.options.maxStep:
|
|
359
378
|
|
|
360
|
-
|
|
379
|
+
self.stepsCount += 1
|
|
361
380
|
print("[INFO] Sending monkeyEvent {}".format(
|
|
362
|
-
f"({
|
|
363
|
-
else f"({
|
|
381
|
+
f"({self.stepsCount} / {self.options.maxStep})" if self.options.maxStep != float("inf")
|
|
382
|
+
else f"({self.stepsCount})"
|
|
364
383
|
)
|
|
365
384
|
, flush=True)
|
|
366
385
|
|
|
367
386
|
try:
|
|
368
|
-
|
|
387
|
+
xml_raw = self.stepMonkey()
|
|
388
|
+
stat = self._getStat()
|
|
389
|
+
propsSatisfiedPrecond = self.getValidProperties(xml_raw, result)
|
|
369
390
|
except requests.ConnectionError:
|
|
370
391
|
print(
|
|
371
392
|
"[INFO] Exploration times up (--running-minutes)."
|
|
@@ -413,7 +434,6 @@ class KeaTestRunner(TextTestRunner):
|
|
|
413
434
|
|
|
414
435
|
print(f"Finish sending monkey events.", flush=True)
|
|
415
436
|
log_watcher.close()
|
|
416
|
-
self.tearDown()
|
|
417
437
|
|
|
418
438
|
# Source code from unittest Runner
|
|
419
439
|
# process the result
|
|
@@ -455,14 +475,19 @@ class KeaTestRunner(TextTestRunner):
|
|
|
455
475
|
"""
|
|
456
476
|
send a step monkey request to the server and get the xml string.
|
|
457
477
|
"""
|
|
458
|
-
|
|
478
|
+
block_dict = self._getBlockedWidgets()
|
|
479
|
+
block_widgets: List[str] = block_dict['widgets']
|
|
480
|
+
block_trees: List[str] = block_dict['trees']
|
|
459
481
|
URL = f"http://localhost:{self.scriptDriver.lport}/stepMonkey"
|
|
460
482
|
logger.debug(f"Sending request: {URL}")
|
|
461
483
|
logger.debug(f"Blocking widgets: {block_widgets}")
|
|
484
|
+
logger.debug(f"Blocking trees: {block_trees}")
|
|
462
485
|
r = requests.post(
|
|
463
486
|
url=URL,
|
|
464
487
|
json={
|
|
465
|
-
"
|
|
488
|
+
"steps_count": self.stepsCount,
|
|
489
|
+
"block_widgets": block_widgets,
|
|
490
|
+
"block_trees": block_trees
|
|
466
491
|
}
|
|
467
492
|
)
|
|
468
493
|
|
|
@@ -481,9 +506,8 @@ class KeaTestRunner(TextTestRunner):
|
|
|
481
506
|
res = r.content.decode(encoding="utf-8")
|
|
482
507
|
print(f"[Server INFO] {res}", flush=True)
|
|
483
508
|
|
|
484
|
-
def getValidProperties(self, result: JsonResult) -> PropertyStore:
|
|
509
|
+
def getValidProperties(self, xml_raw: str, result: JsonResult) -> PropertyStore:
|
|
485
510
|
|
|
486
|
-
xml_raw = self.stepMonkey()
|
|
487
511
|
staticCheckerDriver = self.options.Driver.getStaticChecker(hierarchy=xml_raw)
|
|
488
512
|
|
|
489
513
|
validProps: PropertyStore = dict()
|
|
@@ -513,6 +537,15 @@ class KeaTestRunner(TextTestRunner):
|
|
|
513
537
|
validProps[propName] = test
|
|
514
538
|
return validProps
|
|
515
539
|
|
|
540
|
+
def _getStat(self):
|
|
541
|
+
# profile when reaching the profile period
|
|
542
|
+
if (self.options.profile_period and
|
|
543
|
+
self.stepsCount % self.options.profile_period == 0
|
|
544
|
+
):
|
|
545
|
+
URL = f"http://localhost:{self.scriptDriver.lport}/getStat"
|
|
546
|
+
r = requests.get(URL)
|
|
547
|
+
res = json.loads(r.content)
|
|
548
|
+
|
|
516
549
|
def collectAllProperties(self, test: TestSuite):
|
|
517
550
|
"""collect all the properties to prepare for PBT
|
|
518
551
|
"""
|
|
@@ -548,17 +581,24 @@ class KeaTestRunner(TextTestRunner):
|
|
|
548
581
|
# save it into allProperties for PBT
|
|
549
582
|
self.allProperties[testMethodName] = t
|
|
550
583
|
print(f"[INFO] Load property: {getFullPropName(t)}", flush=True)
|
|
551
|
-
|
|
584
|
+
|
|
552
585
|
@property
|
|
553
586
|
def _blockWidgetFuncs(self):
|
|
554
|
-
|
|
555
|
-
|
|
587
|
+
"""
|
|
588
|
+
load and process blocking functions from widget.block.py configuration file.
|
|
589
|
+
|
|
590
|
+
Returns:
|
|
591
|
+
dict: A dictionary containing two lists:
|
|
592
|
+
- 'widgets': List of functions that block individual widgets
|
|
593
|
+
- 'trees': List of functions that block widget trees
|
|
594
|
+
"""
|
|
595
|
+
if self._block_funcs is None:
|
|
596
|
+
self._block_funcs = {"widgets": list(), "trees": list()}
|
|
556
597
|
root_dir = getProjectRoot()
|
|
557
598
|
if root_dir is None or not os.path.exists(
|
|
558
|
-
|
|
599
|
+
file_block_widgets := root_dir / "configs" / "widget.block.py"
|
|
559
600
|
):
|
|
560
601
|
print(f"[WARNING] widget.block.py not find", flush=True)
|
|
561
|
-
|
|
562
602
|
|
|
563
603
|
def __get_block_widgets_module():
|
|
564
604
|
import importlib.util
|
|
@@ -572,43 +612,81 @@ class KeaTestRunner(TextTestRunner):
|
|
|
572
612
|
|
|
573
613
|
import inspect
|
|
574
614
|
for func_name, func in inspect.getmembers(mod, inspect.isfunction):
|
|
575
|
-
if func_name
|
|
615
|
+
if func_name == "global_block_widgets":
|
|
616
|
+
self._block_funcs["widgets"].append(func)
|
|
617
|
+
setattr(func, PRECONDITIONS_MARKER, (lambda d: True,))
|
|
618
|
+
continue
|
|
619
|
+
if func_name == "global_block_tree":
|
|
620
|
+
self._block_funcs["trees"].append(func)
|
|
621
|
+
setattr(func, PRECONDITIONS_MARKER, (lambda d: True,))
|
|
622
|
+
continue
|
|
623
|
+
if func_name.startswith("block_") and not func_name.startswith("block_tree_"):
|
|
576
624
|
if getattr(func, PRECONDITIONS_MARKER, None) is None:
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
625
|
+
logger.warning(f"No precondition in block widget function: {func_name}. Default globally active.")
|
|
626
|
+
setattr(func, PRECONDITIONS_MARKER, (lambda d: True,))
|
|
627
|
+
self._block_funcs["widgets"].append(func)
|
|
628
|
+
continue
|
|
629
|
+
if func_name.startswith("block_tree_"):
|
|
630
|
+
if getattr(func, PRECONDITIONS_MARKER, None) is None:
|
|
631
|
+
logger.warning(f"No precondition in block tree function: {func_name}. Default globally active.")
|
|
632
|
+
setattr(func, PRECONDITIONS_MARKER, (lambda d: True,))
|
|
633
|
+
self._block_funcs["trees"].append(func)
|
|
634
|
+
|
|
635
|
+
return self._block_funcs
|
|
581
636
|
|
|
582
|
-
return self._block_widgets_funcs
|
|
583
637
|
|
|
584
638
|
def _getBlockedWidgets(self):
|
|
585
|
-
|
|
586
|
-
|
|
639
|
+
"""
|
|
640
|
+
Executes all blocking functions to get lists of widgets and trees to be blocked during testing.
|
|
641
|
+
|
|
642
|
+
Returns:
|
|
643
|
+
dict: A dictionary containing:
|
|
644
|
+
- 'widgets': List of XPath strings for individual widgets to block
|
|
645
|
+
- 'trees': List of XPath strings for widget trees to block
|
|
646
|
+
"""
|
|
647
|
+
def _get_xpath_widgets(func):
|
|
648
|
+
blocked_set = set()
|
|
587
649
|
try:
|
|
588
650
|
script_driver = self.options.Driver.getScriptDriver()
|
|
589
|
-
preconds = getattr(func, PRECONDITIONS_MARKER)
|
|
590
|
-
if all(
|
|
651
|
+
preconds = getattr(func, PRECONDITIONS_MARKER, [])
|
|
652
|
+
if all(precond(script_driver) for precond in preconds):
|
|
591
653
|
_widgets = func(self.options.Driver.getStaticChecker())
|
|
592
|
-
if
|
|
593
|
-
_widgets = [_widgets]
|
|
654
|
+
_widgets = _widgets if isinstance(_widgets, list) else [_widgets]
|
|
594
655
|
for w in _widgets:
|
|
595
656
|
if isinstance(w, StaticU2UiObject):
|
|
596
|
-
|
|
657
|
+
xpath = w._getXPath(w.selector)
|
|
658
|
+
blocked_set.add(xpath) # 集合去重
|
|
597
659
|
elif isinstance(w, u2.xpath.XPathSelector):
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
blocked_widgets.append(getXPathRepr(w))
|
|
660
|
+
xpath = w._parent.xpath
|
|
661
|
+
blocked_set.add(xpath)
|
|
601
662
|
else:
|
|
602
663
|
logger.warning(f"{w} Not supported")
|
|
603
|
-
# blocked_widgets.extend([
|
|
604
|
-
# w._getXPath(w.selector) for w in _widgets
|
|
605
|
-
# ])
|
|
606
664
|
except Exception as e:
|
|
607
|
-
logger.error(f"
|
|
608
|
-
import traceback
|
|
665
|
+
logger.error(f"Error processing blocked widgets: {e}")
|
|
609
666
|
traceback.print_exc()
|
|
667
|
+
return blocked_set
|
|
668
|
+
|
|
669
|
+
res = {
|
|
670
|
+
"widgets": set(),
|
|
671
|
+
"trees": set()
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
|
|
675
|
+
for func in self._blockWidgetFuncs["widgets"]:
|
|
676
|
+
widgets = _get_xpath_widgets(func)
|
|
677
|
+
res["widgets"].update(widgets)
|
|
678
|
+
|
|
679
|
+
|
|
680
|
+
for func in self._blockWidgetFuncs["trees"]:
|
|
681
|
+
trees = _get_xpath_widgets(func)
|
|
682
|
+
res["trees"].update(trees)
|
|
683
|
+
|
|
684
|
+
|
|
685
|
+
res["widgets"] = list(res["widgets"] - res["trees"])
|
|
686
|
+
res["trees"] = list(res["trees"])
|
|
687
|
+
|
|
688
|
+
return res
|
|
610
689
|
|
|
611
|
-
return blocked_widgets
|
|
612
690
|
|
|
613
691
|
def __del__(self):
|
|
614
692
|
"""tearDown method. Cleanup the env.
|
|
@@ -83,6 +83,14 @@ def _set_runner_parser(subparsers: "argparse._SubParsersAction[argparse.Argument
|
|
|
83
83
|
required=False,
|
|
84
84
|
help="the stamp for log file and result file, default: current time stamp",
|
|
85
85
|
)
|
|
86
|
+
|
|
87
|
+
parser.add_argument(
|
|
88
|
+
"--profile-period",
|
|
89
|
+
dest="profile_period",
|
|
90
|
+
type=int,
|
|
91
|
+
required=False,
|
|
92
|
+
help="Steps to profile the testing statistics.",
|
|
93
|
+
)
|
|
86
94
|
|
|
87
95
|
parser.add_argument(
|
|
88
96
|
"extra",
|
|
@@ -120,6 +128,7 @@ def parse_args(argv: List):
|
|
|
120
128
|
args = parser.parse_args(argv)
|
|
121
129
|
return args
|
|
122
130
|
|
|
131
|
+
|
|
123
132
|
def _sanitize_args(args):
|
|
124
133
|
if args.agent == "u2" and not args.driver_name:
|
|
125
134
|
if args.extra == []:
|
|
@@ -127,6 +136,7 @@ def _sanitize_args(args):
|
|
|
127
136
|
else:
|
|
128
137
|
raise ValueError("--driver-name should be specified when customizing script in --agent u2")
|
|
129
138
|
|
|
139
|
+
|
|
130
140
|
def run(args=None):
|
|
131
141
|
if args is None:
|
|
132
142
|
args = parse_args(sys.argv[1:])
|
|
@@ -144,10 +154,11 @@ def run(args=None):
|
|
|
144
154
|
Driver=U2Driver,
|
|
145
155
|
packageNames=args.package_names,
|
|
146
156
|
serial=args.serial,
|
|
147
|
-
running_mins=args.running_minutes
|
|
148
|
-
maxStep=args.max_step
|
|
149
|
-
throttle=args.throttle_ms
|
|
150
|
-
log_stamp=args.log_stamp
|
|
157
|
+
running_mins=args.running_minutes,
|
|
158
|
+
maxStep=args.max_step,
|
|
159
|
+
throttle=args.throttle_ms,
|
|
160
|
+
log_stamp=args.log_stamp,
|
|
161
|
+
profile_period=args.profile_period,
|
|
151
162
|
)
|
|
152
163
|
|
|
153
164
|
KeaTestRunner.setOptions(options)
|
|
@@ -46,17 +46,19 @@ class LogWatcher:
|
|
|
46
46
|
exception_body +
|
|
47
47
|
"\nSee fastbot.log for details."
|
|
48
48
|
)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
49
|
+
if self.end_flag:
|
|
50
|
+
statistic_match = PATTERN_STATISTIC.search(buffer)
|
|
51
|
+
if statistic_match:
|
|
52
|
+
statistic_body = statistic_match.group(1).strip()
|
|
53
|
+
if statistic_body:
|
|
54
|
+
print(
|
|
55
|
+
"[INFO] Fastbot exit:\n" +
|
|
56
|
+
statistic_body
|
|
57
|
+
, flush=True)
|
|
57
58
|
|
|
58
59
|
def __init__(self, log_file):
|
|
59
60
|
self.log_file = log_file
|
|
61
|
+
self.end_flag = False
|
|
60
62
|
|
|
61
63
|
threading.excepthook = thread_excepthook
|
|
62
64
|
t = threading.Thread(target=self.watcher, daemon=True)
|
|
@@ -64,6 +66,7 @@ class LogWatcher:
|
|
|
64
66
|
|
|
65
67
|
def close(self):
|
|
66
68
|
time.sleep(0.2) # wait for the written logfile close
|
|
69
|
+
self.end_flag = True
|
|
67
70
|
self.read_log()
|
|
68
71
|
|
|
69
72
|
|
|
@@ -235,7 +235,7 @@ class U2StaticDevice(u2.Device):
|
|
|
235
235
|
|
|
236
236
|
def __getattr__(self, attr):
|
|
237
237
|
"""Proxy other methods to script_driver"""
|
|
238
|
-
|
|
238
|
+
logger.debug(f"{attr} not exists in static checker, proxy to script_driver.")
|
|
239
239
|
return getattr(self._script_driver, attr)
|
|
240
240
|
|
|
241
241
|
class _XPathEntry(u2.xpath.XPathEntry):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "Kea2-python"
|
|
3
|
-
version = "0.0.
|
|
3
|
+
version = "0.0.1b3"
|
|
4
4
|
description = "A python library for supporting and customizing automated UI testing for mobile apps"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.8"
|
|
@@ -19,4 +19,4 @@ kea2 = "kea2.cli:main"
|
|
|
19
19
|
include = ["kea2"]
|
|
20
20
|
|
|
21
21
|
[tool.setuptools.package-data]
|
|
22
|
-
kea2 = ["assets/**/*"]
|
|
22
|
+
kea2 = ["assets/**/*"]
|
|
Binary file
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
from kea2.utils import Device
|
|
2
|
-
from kea2.keaUtils import precondition
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
def global_block_widgets(d: "Device"):
|
|
6
|
-
"""
|
|
7
|
-
global block widgets.
|
|
8
|
-
return the widgets you want to block globally
|
|
9
|
-
only available in mode `u2 agent`
|
|
10
|
-
"""
|
|
11
|
-
return []
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
# conditional block list
|
|
15
|
-
@precondition(lambda d: d(text="In the home page").exists)
|
|
16
|
-
def block_sth(d: "Device"):
|
|
17
|
-
# Important: function shold starts with "block_"
|
|
18
|
-
return [d(text="widgets to block"), d.xpath(".//node[@text='widget to block']")]
|
|
Binary file
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kea2_python-0.0.1b1 → kea2_python-0.0.1b3}/kea2/assets/fastbot_libs/arm64-v8a/libfastbot_native.so
RENAMED
|
File without changes
|
|
File without changes
|
{kea2_python-0.0.1b1 → kea2_python-0.0.1b3}/kea2/assets/fastbot_libs/x86/libfastbot_native.so
RENAMED
|
File without changes
|
{kea2_python-0.0.1b1 → kea2_python-0.0.1b3}/kea2/assets/fastbot_libs/x86_64/libfastbot_native.so
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|