Kea2-python 1.1.0b1__py3-none-any.whl

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.
Files changed (49) hide show
  1. kea2/__init__.py +8 -0
  2. kea2/absDriver.py +56 -0
  3. kea2/adbUtils.py +554 -0
  4. kea2/assets/config_version.json +16 -0
  5. kea2/assets/fastbot-thirdpart.jar +0 -0
  6. kea2/assets/fastbot_configs/abl.strings +2 -0
  7. kea2/assets/fastbot_configs/awl.strings +3 -0
  8. kea2/assets/fastbot_configs/max.config +7 -0
  9. kea2/assets/fastbot_configs/max.fuzzing.strings +699 -0
  10. kea2/assets/fastbot_configs/max.schema.strings +1 -0
  11. kea2/assets/fastbot_configs/max.strings +3 -0
  12. kea2/assets/fastbot_configs/max.tree.pruning +27 -0
  13. kea2/assets/fastbot_configs/teardown.py +18 -0
  14. kea2/assets/fastbot_configs/widget.block.py +38 -0
  15. kea2/assets/fastbot_libs/arm64-v8a/libfastbot_native.so +0 -0
  16. kea2/assets/fastbot_libs/armeabi-v7a/libfastbot_native.so +0 -0
  17. kea2/assets/fastbot_libs/x86/libfastbot_native.so +0 -0
  18. kea2/assets/fastbot_libs/x86_64/libfastbot_native.so +0 -0
  19. kea2/assets/framework.jar +0 -0
  20. kea2/assets/kea2-thirdpart.jar +0 -0
  21. kea2/assets/monkeyq.jar +0 -0
  22. kea2/assets/quicktest.py +126 -0
  23. kea2/cli.py +216 -0
  24. kea2/fastbotManager.py +269 -0
  25. kea2/kea2_api.py +166 -0
  26. kea2/keaUtils.py +926 -0
  27. kea2/kea_launcher.py +299 -0
  28. kea2/logWatcher.py +92 -0
  29. kea2/mixin.py +0 -0
  30. kea2/report/__init__.py +0 -0
  31. kea2/report/bug_report_generator.py +879 -0
  32. kea2/report/mixin.py +496 -0
  33. kea2/report/report_merger.py +1066 -0
  34. kea2/report/templates/bug_report_template.html +4028 -0
  35. kea2/report/templates/merged_bug_report_template.html +3602 -0
  36. kea2/report/utils.py +10 -0
  37. kea2/result.py +257 -0
  38. kea2/resultSyncer.py +65 -0
  39. kea2/state.py +22 -0
  40. kea2/typedefs.py +32 -0
  41. kea2/u2Driver.py +612 -0
  42. kea2/utils.py +192 -0
  43. kea2/version_manager.py +102 -0
  44. kea2_python-1.1.0b1.dist-info/METADATA +447 -0
  45. kea2_python-1.1.0b1.dist-info/RECORD +49 -0
  46. kea2_python-1.1.0b1.dist-info/WHEEL +5 -0
  47. kea2_python-1.1.0b1.dist-info/entry_points.txt +2 -0
  48. kea2_python-1.1.0b1.dist-info/licenses/LICENSE +16 -0
  49. kea2_python-1.1.0b1.dist-info/top_level.txt +1 -0
@@ -0,0 +1 @@
1
+ snssdk1128://webcast_feed?gd_label=88888
@@ -0,0 +1,3 @@
1
+ 1 搜索
2
+ 2 打开
3
+ 3 检查
@@ -0,0 +1,27 @@
1
+ [
2
+ {
3
+ "activity":"com.ss.android.xxx.NewActivity",
4
+ "xpath": "//*[@resource-id='com.xxx.go:id/aaa']",
5
+ "resourceid": "",
6
+ "contentdesc":"",
7
+ "text":"",
8
+ "classname":""
9
+ },
10
+ {
11
+ "activity":"com.ss.android.xxx.MainActivity",
12
+ "xpath": "//*[@resource-id='com.xxx.go:id/bbb' and @text='other']",
13
+ "resourceid": "",
14
+ "contentdesc":"",
15
+ "text":"",
16
+ "classname":""
17
+ },
18
+ {
19
+ "activity":"com.ss.android.xxx.SplashActivity",
20
+ "xpath": "//*[@resource-id='com.com.xxx.go:id/ccc' and @text='other']",
21
+ "resourceid": "",
22
+ "contentdesc":"",
23
+ "text":"",
24
+ "classname":"",
25
+ "clickable":"false"
26
+ }
27
+ ]
@@ -0,0 +1,18 @@
1
+ from uiautomator2 import Device
2
+ import time
3
+
4
+
5
+ class HybridTestCase:
6
+ d: Device
7
+
8
+ PACKAGE_NAME = "it.feio.android.omninotes.alpha"
9
+ MAIN_ACTIVITY = "it.feio.android.omninotes.MainActivity"
10
+
11
+
12
+ def setUp(self: HybridTestCase):
13
+ self.d.app_start(PACKAGE_NAME, MAIN_ACTIVITY)
14
+ time.sleep(2)
15
+
16
+
17
+ def tearDown(self: HybridTestCase):
18
+ self.d.app_stop(PACKAGE_NAME)
@@ -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
Binary file
Binary file
@@ -0,0 +1,126 @@
1
+ import unittest
2
+ import uiautomator2 as u2
3
+
4
+ from time import sleep
5
+ from kea2 import precondition, prob, KeaTestRunner, Options, kea, keaTestLoader
6
+ from kea2.u2Driver import U2Driver
7
+
8
+
9
+ class Omni_Notes_Sample(unittest.TestCase):
10
+
11
+ def setUp(self):
12
+ self.d = u2.connect()
13
+
14
+ @prob(0.2)
15
+ @precondition(
16
+ lambda self: self.d(description="Navigate up").exists
17
+ )
18
+ def test_goBack(self):
19
+ print("Navigate back")
20
+ self.d(description="Navigate up").click()
21
+ sleep(0.5)
22
+
23
+ @prob(0.2)
24
+ @precondition(
25
+ lambda self: self.d(description="drawer closed").exists
26
+ )
27
+ def test_openDrawer(self):
28
+ print("Open drawer")
29
+ self.d(description="drawer closed").click()
30
+ sleep(0.5)
31
+
32
+ @prob(0.5) # The probability of executing the function when precondition is satisfied.
33
+ @precondition(
34
+ lambda self: self.d(text="Omni Notes Alpha").exists
35
+ and self.d(text="Settings").exists
36
+ )
37
+ def test_goToPrivacy(self):
38
+ """
39
+ The ability to jump out of the UI tarpits
40
+
41
+ precond:
42
+ The drawer was opened
43
+ action:
44
+ go to settings -> privacy
45
+ """
46
+ print("trying to click Settings")
47
+ self.d(text="Settings").click()
48
+ sleep(0.5)
49
+ print("trying to click Privacy")
50
+ self.d(text="Privacy").click()
51
+
52
+ @precondition(
53
+ lambda self: self.d(resourceId="it.feio.android.omninotes.alpha:id/search_src_text").exists
54
+ )
55
+ def test_rotation(self):
56
+ """
57
+ The ability to make assertion to find functional bug
58
+
59
+ precond:
60
+ The search input box is opened
61
+ action:
62
+ rotate the device (set it to landscape, then back to natural)
63
+ assertion:
64
+ The search input box is still being opened
65
+ """
66
+ print("rotate the device")
67
+ self.d.set_orientation("l")
68
+ sleep(2)
69
+ self.d.set_orientation("n")
70
+ sleep(2)
71
+ assert self.d(resourceId="it.feio.android.omninotes.alpha:id/search_src_text").exists()
72
+
73
+
74
+ URL = "https://github.com/federicoiosue/Omni-Notes/releases/download/6.2.0_alpha/OmniNotes-alphaRelease-6.2.0.apk"
75
+ FALL_BACK_URL = "https://gitee.com/XixianLiang/Kea2/raw/main/omninotes.apk"
76
+ PACKAGE_NAME = "it.feio.android.omninotes.alpha"
77
+ FILE_NAME = "omninotes.apk"
78
+
79
+
80
+ def download_omninotes():
81
+ import socket
82
+ socket.setdefaulttimeout(30)
83
+ try:
84
+ import urllib.request
85
+ urllib.request.urlretrieve(URL, FILE_NAME)
86
+ except Exception as e:
87
+ print(f"[WARN] Download from {URL} failed: {e}. Try to download from fallback URL {FALL_BACK_URL}", flush=True)
88
+ try:
89
+ urllib.request.urlretrieve(FALL_BACK_URL, FILE_NAME)
90
+ except Exception as e2:
91
+ print(f"[ERROR] Download from fallback URL {FALL_BACK_URL} also failed: {e2}", flush=True)
92
+ raise e2
93
+
94
+
95
+ def check_installation(serial=None):
96
+ import os
97
+ from pathlib import Path
98
+
99
+ d = u2.connect(serial)
100
+ # automatically install omni-notes
101
+ if PACKAGE_NAME not in d.app_list():
102
+ if not os.path.exists(Path(".") / FILE_NAME):
103
+ print(f"[INFO] omninote.apk not exists. Downloading from {URL}", flush=True)
104
+ download_omninotes()
105
+ print("[INFO] Installing omninotes.", flush=True)
106
+ d.app_install(FILE_NAME)
107
+ d.stop_uiautomator()
108
+
109
+
110
+ if __name__ == "__main__":
111
+ check_installation(serial=None)
112
+ KeaTestRunner.setOptions(
113
+ Options(
114
+ driverName="d",
115
+ Driver=U2Driver,
116
+ packageNames=[PACKAGE_NAME],
117
+ # serial="emulator-5554", # specify the serial
118
+ maxStep=50,
119
+ profile_period=10,
120
+ take_screenshots=True, # whether to take screenshots, default is False
121
+ # running_mins=10, # specify the maximal running time in minutes, default value is 10m
122
+ # throttle=200, # specify the throttle in milliseconds, default value is 200ms
123
+ agent="u2" # 'native' for running the vanilla Fastbot, 'u2' for running Kea2
124
+ )
125
+ )
126
+ unittest.main(testRunner=KeaTestRunner, testLoader=keaTestLoader)
kea2/cli.py ADDED
@@ -0,0 +1,216 @@
1
+ # coding: utf-8
2
+ # cli.py
3
+
4
+ from __future__ import absolute_import, print_function
5
+ from datetime import datetime
6
+ import sys
7
+ from .utils import getProjectRoot, getLogger
8
+ from .kea_launcher import run
9
+ from .version_manager import check_config_compatibility, get_cur_version
10
+ import argparse
11
+
12
+ import os
13
+ from pathlib import Path
14
+
15
+
16
+ logger = getLogger(__name__)
17
+
18
+
19
+ def cmd_version(args):
20
+ print(get_cur_version(), flush=True)
21
+
22
+
23
+ def cmd_init(args):
24
+ cwd = Path(os.getcwd())
25
+ configs_dir = cwd / "configs"
26
+ if os.path.isdir(configs_dir):
27
+ logger.warning("Kea2 project already initialized")
28
+ return
29
+
30
+ import shutil
31
+ def copy_configs():
32
+ src = Path(__file__).parent / "assets" / "fastbot_configs"
33
+ dst = configs_dir
34
+ shutil.copytree(src, dst)
35
+
36
+ def copy_samples():
37
+ src = Path(__file__).parent / "assets" / "quicktest.py"
38
+ dst = cwd / "quicktest.py"
39
+ shutil.copyfile(src, dst)
40
+
41
+ def save_version():
42
+ import json
43
+ version_file = configs_dir / "version.json"
44
+ with open(version_file, "w") as fp:
45
+ json.dump({"version": get_cur_version(), "init date": datetime.now().strftime("%Y-%m-%d %H:%M:%S")}, fp, indent=4)
46
+
47
+ copy_configs()
48
+ copy_samples()
49
+ save_version()
50
+ logger.info("Kea2 project initialized.")
51
+
52
+
53
+ def cmd_load_configs(args):
54
+ pass
55
+
56
+
57
+ def cmd_report(args):
58
+ from .report.bug_report_generator import BugReportGenerator
59
+ report_dirs = args.path
60
+
61
+ for report_dir in report_dirs:
62
+ report_dir = Path(report_dir).resolve()
63
+
64
+ if not report_dir.exists():
65
+ logger.error(f"Report directory does not exist: {str(report_dir)}, Skipped.")
66
+ continue
67
+
68
+ logger.debug(f"Generating test report from directory: {report_dir}")
69
+ BugReportGenerator(report_dir).generate_report()
70
+
71
+
72
+ def cmd_merge(args):
73
+ """Merge multiple test report directories and generate a combined report"""
74
+ from .report.report_merger import TestReportMerger
75
+
76
+ try:
77
+ # Validate input paths
78
+ if not args.paths or len(args.paths) < 2:
79
+ logger.error("At least 2 test report paths are required for merging. Use -p to specify paths.")
80
+ return
81
+
82
+ # Validate that all paths exist
83
+ for path in args.paths:
84
+ path_obj = Path(path)
85
+ if not path_obj.exists():
86
+ raise FileNotFoundError(f"{path_obj}")
87
+ if not path_obj.is_dir():
88
+ raise NotADirectoryError(f"{path_obj}")
89
+
90
+ logger.debug(f"Merging {len(args.paths)} test report directories...")
91
+
92
+ # Initialize merger
93
+ merger = TestReportMerger()
94
+
95
+ # Merge test reports
96
+ merged_report = merger.merge_reports(args.paths, args.output)
97
+
98
+ if merged_report is not None:
99
+ print(f"✅ Test reports merged successfully!", flush=True)
100
+ print(f"📊 Merged report: {merged_report}", flush=True)
101
+ # Get merge summary
102
+ merge_summary = merger.get_merge_summary()
103
+ print(f"📈 Merged {merge_summary.get('merged_directories', 0)} directories", flush=True)
104
+
105
+ except Exception as e:
106
+ logger.error(f"Error during merge operation: {e}")
107
+
108
+
109
+ def cmd_run(args):
110
+ base_dir = getProjectRoot()
111
+ if base_dir is None:
112
+ logger.error("kea2 project not initialized. Use `kea2 init`.")
113
+ return
114
+
115
+ check_config_compatibility()
116
+
117
+ run(args)
118
+
119
+
120
+ _commands = [
121
+ dict(action=cmd_version, command="version", help="show version"),
122
+ dict(
123
+ action=cmd_init,
124
+ command="init",
125
+ help="init the Kea2 project in current directory",
126
+ ),
127
+ dict(
128
+ action=cmd_report,
129
+ command="report",
130
+ help="generate test report from existing test results",
131
+ flags=[
132
+ dict(
133
+ name=["report_dir"],
134
+ args=["-p", "--path"],
135
+ type=str,
136
+ nargs="+",
137
+ required=True,
138
+ help="Root directory path of the test results to generate report from"
139
+ )
140
+ ]
141
+ ),
142
+ dict(
143
+ action=cmd_merge,
144
+ command="merge",
145
+ help="merge multiple test report directories and generate a combined report",
146
+ flags=[
147
+ dict(
148
+ name=["paths"],
149
+ args=["-p", "--paths"],
150
+ type=str,
151
+ nargs='+',
152
+ required=True,
153
+ help="Paths to test report directories (res_* directories) to merge"
154
+ ),
155
+ dict(
156
+ name=["output"],
157
+ args=["-o", "--output"],
158
+ type=str,
159
+ required=False,
160
+ help="Output directory for merged report (optional)"
161
+ )
162
+ ]
163
+ )
164
+ ]
165
+
166
+
167
+ def main():
168
+ parser = argparse.ArgumentParser(
169
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter
170
+ )
171
+ parser.add_argument("-d", "--debug", action="store_true",
172
+ help="show detail log")
173
+
174
+ subparser = parser.add_subparsers(dest='subparser')
175
+
176
+ actions = {}
177
+ for c in _commands:
178
+ cmd_name = c['command']
179
+ actions[cmd_name] = c['action']
180
+ sp = subparser.add_parser(
181
+ cmd_name,
182
+ help=c.get('help'),
183
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter
184
+ )
185
+ for f in c.get('flags', []):
186
+ args = f.get('args')
187
+ if not args:
188
+ args = ['-'*min(2, len(n)) + n for n in f['name']]
189
+ kwargs = f.copy()
190
+ kwargs.pop('name', None)
191
+ kwargs.pop('args', None)
192
+ sp.add_argument(*args, **kwargs)
193
+
194
+ from .kea_launcher import _set_runner_parser
195
+ _set_runner_parser(subparser)
196
+ actions["run"] = cmd_run
197
+ if sys.argv[1:] == ["run"]:
198
+ sys.argv.append("-h")
199
+ args = parser.parse_args()
200
+
201
+ import logging
202
+ from .utils import LoggingLevel
203
+ LoggingLevel.set_level(logging.INFO)
204
+ if args.debug:
205
+ LoggingLevel.set_level(logging.DEBUG)
206
+ logger.debug("args: %s", args)
207
+
208
+ if args.subparser:
209
+ actions[args.subparser](args)
210
+ return
211
+
212
+ parser.print_help()
213
+
214
+
215
+ if __name__ == "__main__":
216
+ main()