Kea2-python 0.0.1b0__tar.gz → 0.0.1b2__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.

Files changed (40) hide show
  1. {kea2_python-0.0.1b0 → kea2_python-0.0.1b2}/Kea2_python.egg-info/PKG-INFO +30 -36
  2. {kea2_python-0.0.1b0 → kea2_python-0.0.1b2}/Kea2_python.egg-info/SOURCES.txt +0 -1
  3. {kea2_python-0.0.1b0 → kea2_python-0.0.1b2}/PKG-INFO +30 -36
  4. {kea2_python-0.0.1b0 → kea2_python-0.0.1b2}/README.md +29 -35
  5. kea2_python-0.0.1b2/kea2/__init__.py +4 -0
  6. {kea2_python-0.0.1b0 → kea2_python-0.0.1b2}/kea2/absDriver.py +4 -0
  7. {kea2_python-0.0.1b0 → kea2_python-0.0.1b2}/kea2/adbUtils.py +34 -1
  8. kea2_python-0.0.1b2/kea2/assets/monkeyq.jar +0 -0
  9. {kea2_python-0.0.1b0 → kea2_python-0.0.1b2}/kea2/assets/quicktest.py +3 -3
  10. {kea2_python-0.0.1b0 → kea2_python-0.0.1b2}/kea2/cli.py +2 -1
  11. {kea2_python-0.0.1b0 → kea2_python-0.0.1b2}/kea2/keaUtils.py +90 -19
  12. {kea2_python-0.0.1b0 → kea2_python-0.0.1b2}/kea2/kea_launcher.py +23 -3
  13. {kea2_python-0.0.1b0 → kea2_python-0.0.1b2}/kea2/u2Driver.py +15 -5
  14. {kea2_python-0.0.1b0 → kea2_python-0.0.1b2}/pyproject.toml +1 -1
  15. kea2_python-0.0.1b0/kea2/__init__.py +0 -4
  16. kea2_python-0.0.1b0/kea2/assets/fastbot_configs/ADBKeyBoard.apk +0 -0
  17. kea2_python-0.0.1b0/kea2/assets/monkeyq.jar +0 -0
  18. {kea2_python-0.0.1b0 → kea2_python-0.0.1b2}/Kea2_python.egg-info/dependency_links.txt +0 -0
  19. {kea2_python-0.0.1b0 → kea2_python-0.0.1b2}/Kea2_python.egg-info/entry_points.txt +0 -0
  20. {kea2_python-0.0.1b0 → kea2_python-0.0.1b2}/Kea2_python.egg-info/requires.txt +0 -0
  21. {kea2_python-0.0.1b0 → kea2_python-0.0.1b2}/Kea2_python.egg-info/top_level.txt +0 -0
  22. {kea2_python-0.0.1b0 → kea2_python-0.0.1b2}/LICENSE +0 -0
  23. {kea2_python-0.0.1b0 → kea2_python-0.0.1b2}/kea2/assets/fastbot-thirdpart.jar +0 -0
  24. {kea2_python-0.0.1b0 → kea2_python-0.0.1b2}/kea2/assets/fastbot_configs/abl.strings +0 -0
  25. {kea2_python-0.0.1b0 → kea2_python-0.0.1b2}/kea2/assets/fastbot_configs/awl.strings +0 -0
  26. {kea2_python-0.0.1b0 → kea2_python-0.0.1b2}/kea2/assets/fastbot_configs/max.config +0 -0
  27. {kea2_python-0.0.1b0 → kea2_python-0.0.1b2}/kea2/assets/fastbot_configs/max.fuzzing.strings +0 -0
  28. {kea2_python-0.0.1b0 → kea2_python-0.0.1b2}/kea2/assets/fastbot_configs/max.schema.strings +0 -0
  29. {kea2_python-0.0.1b0 → kea2_python-0.0.1b2}/kea2/assets/fastbot_configs/max.strings +0 -0
  30. {kea2_python-0.0.1b0 → kea2_python-0.0.1b2}/kea2/assets/fastbot_configs/max.tree.pruning +0 -0
  31. {kea2_python-0.0.1b0 → kea2_python-0.0.1b2}/kea2/assets/fastbot_configs/widget.block.py +0 -0
  32. {kea2_python-0.0.1b0 → kea2_python-0.0.1b2}/kea2/assets/fastbot_libs/arm64-v8a/libfastbot_native.so +0 -0
  33. {kea2_python-0.0.1b0 → kea2_python-0.0.1b2}/kea2/assets/fastbot_libs/armeabi-v7a/libfastbot_native.so +0 -0
  34. {kea2_python-0.0.1b0 → kea2_python-0.0.1b2}/kea2/assets/fastbot_libs/x86/libfastbot_native.so +0 -0
  35. {kea2_python-0.0.1b0 → kea2_python-0.0.1b2}/kea2/assets/fastbot_libs/x86_64/libfastbot_native.so +0 -0
  36. {kea2_python-0.0.1b0 → kea2_python-0.0.1b2}/kea2/assets/framework.jar +0 -0
  37. {kea2_python-0.0.1b0 → kea2_python-0.0.1b2}/kea2/assets/u2.jar +0 -0
  38. {kea2_python-0.0.1b0 → kea2_python-0.0.1b2}/kea2/logWatcher.py +0 -0
  39. {kea2_python-0.0.1b0 → kea2_python-0.0.1b2}/kea2/utils.py +0 -0
  40. {kea2_python-0.0.1b0 → kea2_python-0.0.1b2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Kea2-python
3
- Version: 0.0.1b0
3
+ Version: 0.0.1b2
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
@@ -16,6 +16,10 @@ Dynamic: license-file
16
16
  [![PyPI Downloads](https://static.pepy.tech/badge/kea2-python)](https://pepy.tech/projects/kea2-python)
17
17
  ![Python](https://img.shields.io/badge/python-3.8%2B-blue)
18
18
 
19
+ <div>
20
+ <img src="https://github.com/user-attachments/assets/58f68b00-cc9c-4620-9e2e-66c43cf7caae" style="border-radius: 14px; width: 20%; height: 20%;"/>
21
+ </div>
22
+
19
23
  Kea2 is an easy-to-use Python library for supporting, customizing and improving automated UI testing for mobile apps. The library is currently built on top of [Fastbot](https://github.com/bytedance/Fastbot_Android) and [uiautomator2](https://github.com/openatx/uiautomator2), and targeting [Android](https://en.wikipedia.org/wiki/Android_(operating_system)) apps.
20
24
 
21
25
  ### Kea2 has three important features:
@@ -57,13 +61,14 @@ In the future, Kea2 will be extended to support
57
61
  Running requirements/environment:
58
62
  - support Windows, MacOS and Linux
59
63
  - python 3.8+
64
+ - Android 4.4+
60
65
  - Android SDK installed
61
66
  - **VPN closed** (Features 2 and 3 required)
62
67
 
63
68
 
64
69
  Install Kea2 by `pip`:
65
70
  ```bash
66
- python3 -m pip install Kea2-python
71
+ python3 -m pip install kea2-python
67
72
  ```
68
73
 
69
74
  Find Kea2's additional options by running
@@ -300,31 +305,6 @@ class MyFirstTest(unittest.TestCase):
300
305
 
301
306
  You can read [Kea - Write your fisrt property](https://kea-docs.readthedocs.io/en/latest/part-keaUserManuel/first_property.html) for more details.
302
307
 
303
- ### Notes
304
- 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.
305
-
306
- | | **Support** | **Not support** |
307
- | -- | -- | -- |
308
- | **Selctor** | `d(text="1").exist` | `d(text="1").child(text="2").exist` |
309
-
310
- If you need to specify `parent-child` relation ship in `@precondition`, specify it in xpath.
311
-
312
- for example:
313
-
314
- ```python
315
- # Do not use:
316
- # @precondition(lambda self:
317
- # self.d(className="android.widget.ListView").child(text="Bluetooth")
318
- # ):
319
- # ...
320
-
321
- # Use
322
- @precondition(lambda self:
323
- self.d.xpath('//android.widget.ListView/*[@text="Bluetooth"]')
324
- ):
325
- ...
326
- ```
327
-
328
308
 
329
309
  ## Launching Kea2
330
310
 
@@ -349,16 +329,17 @@ kea2 run -s "emulator-5554" -p it.feio.android.omninotes.alpha --agent u2 --runn
349
329
  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
350
330
  ```
351
331
 
352
- | arg | meaning |
353
- | --- | --- |
354
- | -s | The serial of your device, which can be found by `adb devices` |
355
- | -p | The tested app's package name (e.g., com.example.app) |
356
- | -o | The ouput directory for logs and results |
357
- | --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`.|
358
- | --running-minutes | The time (m) to run Kea2 |
359
- | --max-step | The maxium number of monkey events to send (only available in `--agent u2`) |
360
- | --throttle | The delay time (ms) between two monkey events |
332
+ | arg | meaning | default |
333
+ | --- | --- | --- |
334
+ | -s | The serial of your device, which can be found by `adb devices` | |
335
+ | -p | *The tested app's package name (e.g., com.example.app) |
336
+ | -o | The ouput directory for logs and results | `output` |
337
+ | --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` |
338
+ | --running-minutes | The time (m) to run Kea2 | `10` |
339
+ | --max-step | The maxium number of monkey events to send (only available in `--agent u2`) | `inf` |
340
+ | --throttle | The delay time (ms) between two monkey events | `200` |
361
341
  | --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()`. |
342
+ | --log-stamp | the stamp for log file and result file, default: current time stamp | |
362
343
  | 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`.
363
344
 
364
345
 
@@ -421,6 +402,8 @@ running_mins: int = 10
421
402
  throttle: int = 200
422
403
  # the output_dir for saving logs and results
423
404
  output_dir: str = "output"
405
+ # the stamp for log file and result file, default: current time stamp
406
+ log_stamp: str = None
424
407
  ```
425
408
 
426
409
  ## Examining the running statistics of scripts .
@@ -465,6 +448,17 @@ Kea2 has been actively developed and maintained by the people in [ecnusse](https
465
448
  - [uiautomator2](https://github.com/openatx/uiautomator2)
466
449
  - [hypothesis](https://github.com/HypothesisWorks/hypothesis)
467
450
 
451
+ ### Relevant papers of Kea2
452
+
453
+ > General and Practical Property-based Testing for Android Apps. ASE 2024. [pdf](https://dl.acm.org/doi/10.1145/3691620.3694986)
454
+
455
+ > An Empirical Study of Functional Bugs in Android Apps. ISSTA 2023. [pdf](https://dl.acm.org/doi/10.1145/3597926.3598138)
456
+
457
+ > 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)
458
+
459
+ > Guided, Stochastic Model-Based GUI Testing of Android Apps. ESEC/FSE 2017. [pdf](https://dl.acm.org/doi/10.1145/3106237.3106298)
460
+
461
+
468
462
  [^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)等。
469
463
 
470
464
  [^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.1b0
3
+ Version: 0.0.1b2
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
@@ -16,6 +16,10 @@ Dynamic: license-file
16
16
  [![PyPI Downloads](https://static.pepy.tech/badge/kea2-python)](https://pepy.tech/projects/kea2-python)
17
17
  ![Python](https://img.shields.io/badge/python-3.8%2B-blue)
18
18
 
19
+ <div>
20
+ <img src="https://github.com/user-attachments/assets/58f68b00-cc9c-4620-9e2e-66c43cf7caae" style="border-radius: 14px; width: 20%; height: 20%;"/>
21
+ </div>
22
+
19
23
  Kea2 is an easy-to-use Python library for supporting, customizing and improving automated UI testing for mobile apps. The library is currently built on top of [Fastbot](https://github.com/bytedance/Fastbot_Android) and [uiautomator2](https://github.com/openatx/uiautomator2), and targeting [Android](https://en.wikipedia.org/wiki/Android_(operating_system)) apps.
20
24
 
21
25
  ### Kea2 has three important features:
@@ -57,13 +61,14 @@ In the future, Kea2 will be extended to support
57
61
  Running requirements/environment:
58
62
  - support Windows, MacOS and Linux
59
63
  - python 3.8+
64
+ - Android 4.4+
60
65
  - Android SDK installed
61
66
  - **VPN closed** (Features 2 and 3 required)
62
67
 
63
68
 
64
69
  Install Kea2 by `pip`:
65
70
  ```bash
66
- python3 -m pip install Kea2-python
71
+ python3 -m pip install kea2-python
67
72
  ```
68
73
 
69
74
  Find Kea2's additional options by running
@@ -300,31 +305,6 @@ class MyFirstTest(unittest.TestCase):
300
305
 
301
306
  You can read [Kea - Write your fisrt property](https://kea-docs.readthedocs.io/en/latest/part-keaUserManuel/first_property.html) for more details.
302
307
 
303
- ### Notes
304
- 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.
305
-
306
- | | **Support** | **Not support** |
307
- | -- | -- | -- |
308
- | **Selctor** | `d(text="1").exist` | `d(text="1").child(text="2").exist` |
309
-
310
- If you need to specify `parent-child` relation ship in `@precondition`, specify it in xpath.
311
-
312
- for example:
313
-
314
- ```python
315
- # Do not use:
316
- # @precondition(lambda self:
317
- # self.d(className="android.widget.ListView").child(text="Bluetooth")
318
- # ):
319
- # ...
320
-
321
- # Use
322
- @precondition(lambda self:
323
- self.d.xpath('//android.widget.ListView/*[@text="Bluetooth"]')
324
- ):
325
- ...
326
- ```
327
-
328
308
 
329
309
  ## Launching Kea2
330
310
 
@@ -349,16 +329,17 @@ kea2 run -s "emulator-5554" -p it.feio.android.omninotes.alpha --agent u2 --runn
349
329
  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
350
330
  ```
351
331
 
352
- | arg | meaning |
353
- | --- | --- |
354
- | -s | The serial of your device, which can be found by `adb devices` |
355
- | -p | The tested app's package name (e.g., com.example.app) |
356
- | -o | The ouput directory for logs and results |
357
- | --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`.|
358
- | --running-minutes | The time (m) to run Kea2 |
359
- | --max-step | The maxium number of monkey events to send (only available in `--agent u2`) |
360
- | --throttle | The delay time (ms) between two monkey events |
332
+ | arg | meaning | default |
333
+ | --- | --- | --- |
334
+ | -s | The serial of your device, which can be found by `adb devices` | |
335
+ | -p | *The tested app's package name (e.g., com.example.app) |
336
+ | -o | The ouput directory for logs and results | `output` |
337
+ | --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` |
338
+ | --running-minutes | The time (m) to run Kea2 | `10` |
339
+ | --max-step | The maxium number of monkey events to send (only available in `--agent u2`) | `inf` |
340
+ | --throttle | The delay time (ms) between two monkey events | `200` |
361
341
  | --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()`. |
342
+ | --log-stamp | the stamp for log file and result file, default: current time stamp | |
362
343
  | 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`.
363
344
 
364
345
 
@@ -421,6 +402,8 @@ running_mins: int = 10
421
402
  throttle: int = 200
422
403
  # the output_dir for saving logs and results
423
404
  output_dir: str = "output"
405
+ # the stamp for log file and result file, default: current time stamp
406
+ log_stamp: str = None
424
407
  ```
425
408
 
426
409
  ## Examining the running statistics of scripts .
@@ -465,6 +448,17 @@ Kea2 has been actively developed and maintained by the people in [ecnusse](https
465
448
  - [uiautomator2](https://github.com/openatx/uiautomator2)
466
449
  - [hypothesis](https://github.com/HypothesisWorks/hypothesis)
467
450
 
451
+ ### Relevant papers of Kea2
452
+
453
+ > General and Practical Property-based Testing for Android Apps. ASE 2024. [pdf](https://dl.acm.org/doi/10.1145/3691620.3694986)
454
+
455
+ > An Empirical Study of Functional Bugs in Android Apps. ISSTA 2023. [pdf](https://dl.acm.org/doi/10.1145/3597926.3598138)
456
+
457
+ > 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)
458
+
459
+ > Guided, Stochastic Model-Based GUI Testing of Android Apps. ESEC/FSE 2017. [pdf](https://dl.acm.org/doi/10.1145/3106237.3106298)
460
+
461
+
468
462
  [^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)等。
469
463
 
470
464
  [^2]: 在UI自动化测试过程中支持自动断言是一个很重要的能力,但几乎没有测试工具提供这样的能力。我们注意到[AppCrawler](https://ceshiren.com/t/topic/15801/5)的开发者曾经希望提供一种断言机制,得到了用户的热切响应,不少用户从21年就开始催更,但始终未能实现。
@@ -4,6 +4,10 @@
4
4
  [![PyPI Downloads](https://static.pepy.tech/badge/kea2-python)](https://pepy.tech/projects/kea2-python)
5
5
  ![Python](https://img.shields.io/badge/python-3.8%2B-blue)
6
6
 
7
+ <div>
8
+ <img src="https://github.com/user-attachments/assets/58f68b00-cc9c-4620-9e2e-66c43cf7caae" style="border-radius: 14px; width: 20%; height: 20%;"/>
9
+ </div>
10
+
7
11
  Kea2 is an easy-to-use Python library for supporting, customizing and improving automated UI testing for mobile apps. The library is currently built on top of [Fastbot](https://github.com/bytedance/Fastbot_Android) and [uiautomator2](https://github.com/openatx/uiautomator2), and targeting [Android](https://en.wikipedia.org/wiki/Android_(operating_system)) apps.
8
12
 
9
13
  ### Kea2 has three important features:
@@ -45,13 +49,14 @@ In the future, Kea2 will be extended to support
45
49
  Running requirements/environment:
46
50
  - support Windows, MacOS and Linux
47
51
  - python 3.8+
52
+ - Android 4.4+
48
53
  - Android SDK installed
49
54
  - **VPN closed** (Features 2 and 3 required)
50
55
 
51
56
 
52
57
  Install Kea2 by `pip`:
53
58
  ```bash
54
- python3 -m pip install Kea2-python
59
+ python3 -m pip install kea2-python
55
60
  ```
56
61
 
57
62
  Find Kea2's additional options by running
@@ -288,31 +293,6 @@ class MyFirstTest(unittest.TestCase):
288
293
 
289
294
  You can read [Kea - Write your fisrt property](https://kea-docs.readthedocs.io/en/latest/part-keaUserManuel/first_property.html) for more details.
290
295
 
291
- ### Notes
292
- 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.
293
-
294
- | | **Support** | **Not support** |
295
- | -- | -- | -- |
296
- | **Selctor** | `d(text="1").exist` | `d(text="1").child(text="2").exist` |
297
-
298
- If you need to specify `parent-child` relation ship in `@precondition`, specify it in xpath.
299
-
300
- for example:
301
-
302
- ```python
303
- # Do not use:
304
- # @precondition(lambda self:
305
- # self.d(className="android.widget.ListView").child(text="Bluetooth")
306
- # ):
307
- # ...
308
-
309
- # Use
310
- @precondition(lambda self:
311
- self.d.xpath('//android.widget.ListView/*[@text="Bluetooth"]')
312
- ):
313
- ...
314
- ```
315
-
316
296
 
317
297
  ## Launching Kea2
318
298
 
@@ -337,16 +317,17 @@ kea2 run -s "emulator-5554" -p it.feio.android.omninotes.alpha --agent u2 --runn
337
317
  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
338
318
  ```
339
319
 
340
- | arg | meaning |
341
- | --- | --- |
342
- | -s | The serial of your device, which can be found by `adb devices` |
343
- | -p | The tested app's package name (e.g., com.example.app) |
344
- | -o | The ouput directory for logs and results |
345
- | --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`.|
346
- | --running-minutes | The time (m) to run Kea2 |
347
- | --max-step | The maxium number of monkey events to send (only available in `--agent u2`) |
348
- | --throttle | The delay time (ms) between two monkey events |
320
+ | arg | meaning | default |
321
+ | --- | --- | --- |
322
+ | -s | The serial of your device, which can be found by `adb devices` | |
323
+ | -p | *The tested app's package name (e.g., com.example.app) |
324
+ | -o | The ouput directory for logs and results | `output` |
325
+ | --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` |
326
+ | --running-minutes | The time (m) to run Kea2 | `10` |
327
+ | --max-step | The maxium number of monkey events to send (only available in `--agent u2`) | `inf` |
328
+ | --throttle | The delay time (ms) between two monkey events | `200` |
349
329
  | --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()`. |
330
+ | --log-stamp | the stamp for log file and result file, default: current time stamp | |
350
331
  | 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`.
351
332
 
352
333
 
@@ -409,6 +390,8 @@ running_mins: int = 10
409
390
  throttle: int = 200
410
391
  # the output_dir for saving logs and results
411
392
  output_dir: str = "output"
393
+ # the stamp for log file and result file, default: current time stamp
394
+ log_stamp: str = None
412
395
  ```
413
396
 
414
397
  ## Examining the running statistics of scripts .
@@ -453,6 +436,17 @@ Kea2 has been actively developed and maintained by the people in [ecnusse](https
453
436
  - [uiautomator2](https://github.com/openatx/uiautomator2)
454
437
  - [hypothesis](https://github.com/HypothesisWorks/hypothesis)
455
438
 
439
+ ### Relevant papers of Kea2
440
+
441
+ > General and Practical Property-based Testing for Android Apps. ASE 2024. [pdf](https://dl.acm.org/doi/10.1145/3691620.3694986)
442
+
443
+ > An Empirical Study of Functional Bugs in Android Apps. ISSTA 2023. [pdf](https://dl.acm.org/doi/10.1145/3597926.3598138)
444
+
445
+ > 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)
446
+
447
+ > Guided, Stochastic Model-Based GUI Testing of Android Apps. ESEC/FSE 2017. [pdf](https://dl.acm.org/doi/10.1145/3106237.3106298)
448
+
449
+
456
450
  [^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)等。
457
451
 
458
452
  [^2]: 在UI自动化测试过程中支持自动断言是一个很重要的能力,但几乎没有测试工具提供这样的能力。我们注意到[AppCrawler](https://ceshiren.com/t/topic/15801/5)的开发者曾经希望提供一种断言机制,得到了用户的热切响应,不少用户从21年就开始催更,但始终未能实现。
@@ -0,0 +1,4 @@
1
+ from .keaUtils import KeaTestRunner, precondition, prob, max_tries, Options
2
+
3
+ import logging
4
+ logging.basicConfig(level=logging.DEBUG)
@@ -50,3 +50,7 @@ class AbstractDriver(abc.ABC):
50
50
  @abc.abstractmethod
51
51
  def getStaticChecker(self, hierarchy) -> AbstractStaticChecker:
52
52
  pass
53
+
54
+ @classmethod
55
+ @abc.abstractmethod
56
+ def tearDown(self): ...
@@ -1,5 +1,9 @@
1
1
  import subprocess
2
- from typing import List, Optional
2
+ from typing import List, Optional, Set
3
+ from .utils import getLogger
4
+
5
+ logger = getLogger(__name__)
6
+
3
7
 
4
8
  def run_adb_command(cmd: List[str], timeout=10):
5
9
  """
@@ -13,6 +17,7 @@ def run_adb_command(cmd: List[str], timeout=10):
13
17
  str: The standard output from the command. If an error occurs, returns None.
14
18
  """
15
19
  full_cmd = ["adb"] + cmd
20
+ logger.debug(f"{' '.join(full_cmd)}")
16
21
  try:
17
22
  result = subprocess.run(full_cmd, capture_output=True, text=True, timeout=timeout)
18
23
  if result.returncode != 0:
@@ -218,6 +223,34 @@ def remove_all_forwards(device: Optional[str] = None):
218
223
  return run_adb_command(["-s", device, "forward", "--remove-all"])
219
224
 
220
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
+
221
254
  if __name__ == '__main__':
222
255
  # For testing: print the list of currently connected devices.
223
256
  devices = get_devices()
@@ -57,7 +57,7 @@ PACKAGE_NAME = "it.feio.android.omninotes.alpha"
57
57
  FILE_NAME = "omninotes.apk"
58
58
 
59
59
 
60
- def check_installation():
60
+ def check_installation(serial=None):
61
61
  import os
62
62
  from pathlib import Path
63
63
  if not os.path.exists(Path(".") / FILE_NAME):
@@ -65,7 +65,7 @@ def check_installation():
65
65
  import urllib.request
66
66
  urllib.request.urlretrieve(URL, FILE_NAME)
67
67
 
68
- d = u2.connect()
68
+ d = u2.connect(serial)
69
69
  # automatically install omni-notes
70
70
  if PACKAGE_NAME not in d.app_list():
71
71
  print("[INFO] Installing omninotes.", flush=True)
@@ -74,7 +74,7 @@ def check_installation():
74
74
 
75
75
 
76
76
  if __name__ == "__main__":
77
- check_installation()
77
+ check_installation(serial=None)
78
78
  KeaTestRunner.setOptions(
79
79
  Options(
80
80
  driverName="d",
@@ -3,7 +3,7 @@
3
3
 
4
4
  from __future__ import absolute_import, print_function
5
5
  import sys
6
- from kea2.utils import getProjectRoot, getLogger
6
+ from .utils import getProjectRoot, getLogger
7
7
  from .kea_launcher import run
8
8
  import argparse
9
9
 
@@ -99,6 +99,7 @@ def main():
99
99
  args = parser.parse_args()
100
100
 
101
101
  import logging
102
+ logging.basicConfig(level=logging.INFO)
102
103
  if args.debug:
103
104
  logging.basicConfig(level=logging.DEBUG)
104
105
  logger.debug("args: %s", args)
@@ -19,9 +19,10 @@ from .utils import TimeStamp, getProjectRoot, getLogger
19
19
  from .u2Driver import StaticU2UiObject
20
20
  import uiautomator2 as u2
21
21
  import types
22
+
22
23
  PRECONDITIONS_MARKER = "preconds"
23
24
  PROP_MARKER = "prop"
24
-
25
+ MAX_TRIES_MARKER = "max_tries"
25
26
 
26
27
  logger = getLogger(__name__)
27
28
 
@@ -30,14 +31,14 @@ logger = getLogger(__name__)
30
31
  PropName = NewType("PropName", str)
31
32
  PropertyStore = NewType("PropertyStore", Dict[PropName, TestCase])
32
33
 
33
- TIME_STAMP = TimeStamp().getTimeStamp()
34
- LOGFILE = f"fastbot_{TIME_STAMP}.log"
35
- RESFILE = f"result_{TIME_STAMP}.json"
34
+ STAMP = TimeStamp().getTimeStamp()
35
+ LOGFILE = f"fastbot_{STAMP}.log"
36
+ RESFILE = f"result_{STAMP}.json"
36
37
 
37
38
  def precondition(precond: Callable[[Any], bool]) -> Callable:
38
39
  """the decorator @precondition
39
40
 
40
- The precondition specifies when the property could be executed.
41
+ @precondition specifies when the property could be executed.
41
42
  A property could have multiple preconditions, each of which is specified by @precondition.
42
43
  """
43
44
  def accept(f):
@@ -56,7 +57,7 @@ def precondition(precond: Callable[[Any], bool]) -> Callable:
56
57
  def prob(p: float):
57
58
  """the decorator @prob
58
59
 
59
- The prob specify the propbability of execution when a property is satisfied.
60
+ @prob specify the propbability of execution when a property is satisfied.
60
61
  """
61
62
  p = float(p)
62
63
  if not 0 < p <= 1.0:
@@ -73,6 +74,26 @@ def prob(p: float):
73
74
  return accept
74
75
 
75
76
 
77
+ def max_tries(n: int):
78
+ """the decorator @max_tries
79
+
80
+ @max_tries specify the maximum tries of executing a property.
81
+ """
82
+ n = int(n)
83
+ if not n > 0:
84
+ raise ValueError("The maxium tries should be a positive integer.")
85
+ def accept(f):
86
+ @wraps(f)
87
+ def precondition_wrapper(*args, **kwargs):
88
+ return f(*args, **kwargs)
89
+
90
+ setattr(precondition_wrapper, MAX_TRIES_MARKER, n)
91
+
92
+ return precondition_wrapper
93
+
94
+ return accept
95
+
96
+
76
97
  @dataclass
77
98
  class Options:
78
99
  """
@@ -96,10 +117,34 @@ class Options:
96
117
  throttle: int = 200
97
118
  # the output_dir for saving logs and results
98
119
  output_dir: str = "output"
120
+ # the stamp for log file and result file, default: current time stamp
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)
99
129
 
100
130
  def __post_init__(self):
101
131
  if self.serial and self.Driver:
102
132
  self.Driver.setDeviceSerial(self.serial)
133
+ if self.log_stamp:
134
+ global LOGFILE, RESFILE
135
+ LOGFILE = f"fastbot_{self.log_stamp}.log"
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")
103
148
 
104
149
 
105
150
  @dataclass
@@ -151,6 +196,9 @@ class JsonResult(TextTestResult):
151
196
  super().addError(test, err)
152
197
  self.res[getFullPropName(test)].error += 1
153
198
 
199
+ def getExcuted(self, test: TestCase):
200
+ return self.res[getFullPropName(test)].executed
201
+
154
202
 
155
203
  def activateFastbot(options: Options, port=None) -> threading.Thread:
156
204
  """
@@ -227,7 +275,7 @@ def startFastbotService(options: Options) -> threading.Thread:
227
275
  "reuseq",
228
276
  "--running-minutes", f"{options.running_mins}",
229
277
  "--throttle", f"{options.throttle}",
230
- "--bugreport", "--output-directory", "/sdcard/fastbot_report"
278
+ "--bugreport", "--output-directory", "/sdcard/fastbot_report",
231
279
  "-v", "-v", "-v"
232
280
  ]
233
281
 
@@ -274,6 +322,8 @@ class KeaTestRunner(TextTestRunner):
274
322
  global LOGFILE, RESFILE
275
323
  LOGFILE = output_dir / Path(LOGFILE)
276
324
  RESFILE = output_dir / Path(RESFILE)
325
+ logger.debug(f"Log file: {LOGFILE}")
326
+ logger.debug(f"Result file: {RESFILE}")
277
327
 
278
328
  def run(self, test):
279
329
 
@@ -322,18 +372,20 @@ class KeaTestRunner(TextTestRunner):
322
372
  check_alive(port=self.scriptDriver.lport)
323
373
 
324
374
  end_by_remote = False
325
- step = 0
326
- while step < self.options.maxStep:
375
+ self.stepsCount = 0
376
+ while self.stepsCount < self.options.maxStep:
327
377
 
328
- step += 1
378
+ self.stepsCount += 1
329
379
  print("[INFO] Sending monkeyEvent {}".format(
330
- f"({step} / {self.options.maxStep})" if self.options.maxStep != float("inf")
331
- else f"({step})"
380
+ f"({self.stepsCount} / {self.options.maxStep})" if self.options.maxStep != float("inf")
381
+ else f"({self.stepsCount})"
332
382
  )
333
383
  , flush=True)
334
384
 
335
385
  try:
336
- propsSatisfiedPrecond = self.getValidProperties()
386
+ xml_raw = self.stepMonkey()
387
+ stat = self._getStat()
388
+ propsSatisfiedPrecond = self.getValidProperties(xml_raw, result)
337
389
  except requests.ConnectionError:
338
390
  print(
339
391
  "[INFO] Exploration times up (--running-minutes)."
@@ -425,9 +477,12 @@ class KeaTestRunner(TextTestRunner):
425
477
  """
426
478
  block_widgets: List[str] = self._getBlockedWidgets()
427
479
  URL = f"http://localhost:{self.scriptDriver.lport}/stepMonkey"
480
+ logger.debug(f"Sending request: {URL}")
481
+ logger.debug(f"Blocking widgets: {block_widgets}")
428
482
  r = requests.post(
429
483
  url=URL,
430
484
  json={
485
+ "steps_count": self.stepsCount,
431
486
  "block_widgets": block_widgets
432
487
  }
433
488
  )
@@ -440,14 +495,15 @@ class KeaTestRunner(TextTestRunner):
440
495
  """
441
496
  send a stop monkey request to the server and get the xml string.
442
497
  """
443
- r = requests.get(f"http://localhost:{self.scriptDriver.lport}/stopMonkey")
498
+ URL = f"http://localhost:{self.scriptDriver.lport}/stopMonkey"
499
+ logger.debug(f"Sending request: {URL}")
500
+ r = requests.get(URL)
444
501
 
445
502
  res = r.content.decode(encoding="utf-8")
446
503
  print(f"[Server INFO] {res}", flush=True)
447
504
 
448
- def getValidProperties(self) -> PropertyStore:
505
+ def getValidProperties(self, xml_raw: str, result: JsonResult) -> PropertyStore:
449
506
 
450
- xml_raw = self.stepMonkey()
451
507
  staticCheckerDriver = self.options.Driver.getStaticChecker(hierarchy=xml_raw)
452
508
 
453
509
  validProps: PropertyStore = dict()
@@ -470,9 +526,22 @@ class KeaTestRunner(TextTestRunner):
470
526
  break
471
527
  # if all the precond passed. make it the candidate prop.
472
528
  if valid:
529
+ logger.debug(f"precond satisfied: {getFullPropName(test)}")
530
+ if result.getExcuted(test) >= getattr(prop, MAX_TRIES_MARKER, float("inf")):
531
+ logger.debug(f"{getFullPropName(test)} has reached its max_tries. Skip.")
532
+ continue
473
533
  validProps[propName] = test
474
534
  return validProps
475
535
 
536
+ def _getStat(self):
537
+ # profile when reaching the profile period
538
+ if (self.options.profile_period and
539
+ self.stepsCount % self.options.profile_period == 0
540
+ ):
541
+ URL = f"http://localhost:{self.scriptDriver.lport}/getStat"
542
+ r = requests.get(URL)
543
+ res = json.loads(r.content)
544
+
476
545
  def collectAllProperties(self, test: TestSuite):
477
546
  """collect all the properties to prepare for PBT
478
547
  """
@@ -570,6 +639,8 @@ class KeaTestRunner(TextTestRunner):
570
639
 
571
640
  return blocked_widgets
572
641
 
573
- def tearDown(self):
574
- # TODO Add tearDown method (remove local port, etc.)
575
- pass
642
+ def __del__(self):
643
+ """tearDown method. Cleanup the env.
644
+ """
645
+ if self.options.Driver:
646
+ self.options.Driver.tearDown()
@@ -76,6 +76,22 @@ def _set_runner_parser(subparsers: "argparse._SubParsersAction[argparse.Argument
76
76
  help="The name of driver in script.",
77
77
  )
78
78
 
79
+ parser.add_argument(
80
+ "--log-stamp",
81
+ dest="log_stamp",
82
+ type=str,
83
+ required=False,
84
+ help="the stamp for log file and result file, default: current time stamp",
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
+ )
94
+
79
95
  parser.add_argument(
80
96
  "extra",
81
97
  nargs=argparse.REMAINDER,
@@ -112,6 +128,7 @@ def parse_args(argv: List):
112
128
  args = parser.parse_args(argv)
113
129
  return args
114
130
 
131
+
115
132
  def _sanitize_args(args):
116
133
  if args.agent == "u2" and not args.driver_name:
117
134
  if args.extra == []:
@@ -119,6 +136,7 @@ def _sanitize_args(args):
119
136
  else:
120
137
  raise ValueError("--driver-name should be specified when customizing script in --agent u2")
121
138
 
139
+
122
140
  def run(args=None):
123
141
  if args is None:
124
142
  args = parse_args(sys.argv[1:])
@@ -136,9 +154,11 @@ def run(args=None):
136
154
  Driver=U2Driver,
137
155
  packageNames=args.package_names,
138
156
  serial=args.serial,
139
- running_mins=args.running_minutes if args.running_minutes else 10,
140
- maxStep=args.max_step if args.max_step else 500,
141
- throttle=args.throttle_ms if args.throttle_ms else 200
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,
142
162
  )
143
163
 
144
164
  KeaTestRunner.setOptions(options)
@@ -8,7 +8,7 @@ from typing import Dict, List, Union
8
8
  from lxml import etree
9
9
  from .absDriver import AbstractScriptDriver, AbstractStaticChecker, AbstractDriver
10
10
  from .adbUtils import list_forwards, remove_forward, create_forward
11
- from .utils import TimeStamp
11
+ from .utils import TimeStamp, getLogger
12
12
 
13
13
  TIME_STAMP = TimeStamp().getTimeStamp()
14
14
 
@@ -16,6 +16,8 @@ import logging
16
16
  logging.getLogger("urllib3").setLevel(logging.INFO)
17
17
  logging.getLogger("uiautomator2").setLevel(logging.INFO)
18
18
 
19
+ logger = getLogger(__name__)
20
+
19
21
  """
20
22
  The definition of U2ScriptDriver
21
23
  """
@@ -57,16 +59,13 @@ class U2ScriptDriver(AbstractScriptDriver):
57
59
  setattr(self.d._dev, "msg", "meta")
58
60
  print(f"[U2] local port: {lport}", flush=True)
59
61
  return lport
60
-
62
+
61
63
  self._remove_remote_port(8090)
62
64
  self.d.lport = get_u2_forward_port()
63
65
  self._remove_remote_port(9008)
64
66
 
65
67
  return self.d
66
68
 
67
- def tearDown(self):
68
- self.d.stop_uiautomator()
69
-
70
69
  def _remove_remote_port(self, port:int):
71
70
  """remove the forward port
72
71
  """
@@ -76,6 +75,11 @@ class U2ScriptDriver(AbstractScriptDriver):
76
75
  forward_local = forward["local"]
77
76
  remove_forward(local_spec=forward_local, device=self.deviceSerial)
78
77
 
78
+ def tearDown(self):
79
+ logger.debug("U2Driver tearDown: stop_uiautomator")
80
+ self.d.stop_uiautomator()
81
+ logger.debug("U2Driver tearDown: remove forward")
82
+ self._remove_remote_port(8090)
79
83
 
80
84
  """
81
85
  The definition of U2StaticChecker
@@ -231,6 +235,7 @@ class U2StaticDevice(u2.Device):
231
235
 
232
236
  def __getattr__(self, attr):
233
237
  """Proxy other methods to script_driver"""
238
+ logger.debug(f"{attr} not exists in static checker, proxy to script_driver.")
234
239
  return getattr(self._script_driver, attr)
235
240
 
236
241
  class _XPathEntry(u2.xpath.XPathEntry):
@@ -293,6 +298,10 @@ class U2Driver(AbstractDriver):
293
298
  self.staticChecker = U2StaticChecker()
294
299
  return self.staticChecker.getInstance(hierarchy)
295
300
 
301
+ @classmethod
302
+ def tearDown(self):
303
+ self.scriptDriver.tearDown()
304
+
296
305
 
297
306
  """
298
307
  Other Utils
@@ -311,6 +320,7 @@ def forward_port(self, remote: Union[int, str]) -> int:
311
320
  return int(f.local[len("tcp:") :])
312
321
  local_port = get_free_port()
313
322
  self.forward("tcp:" + str(local_port), remote)
323
+ logger.debug(f"forwading port: tcp:{local_port} -> {remote}")
314
324
  return local_port
315
325
 
316
326
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "Kea2-python"
3
- version = "0.0.1b0"
3
+ version = "0.0.1b2"
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"
@@ -1,4 +0,0 @@
1
- from .keaUtils import KeaTestRunner, precondition, prob, Options
2
-
3
- import logging
4
- logging.basicConfig(level=logging.DEBUG)
Binary file
File without changes
File without changes