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
kea2/utils.py ADDED
@@ -0,0 +1,192 @@
1
+ import os
2
+ import logging
3
+ import traceback
4
+ import time
5
+
6
+ from pathlib import Path
7
+ from functools import wraps
8
+ from typing import Callable, Dict, Optional, Union
9
+ from unittest import TestCase
10
+
11
+
12
+ def singleton(cls):
13
+ _instance = {}
14
+
15
+ def inner():
16
+ if cls not in _instance:
17
+ _instance[cls] = cls()
18
+ return _instance[cls]
19
+ return inner
20
+
21
+
22
+ class LoggingLevel:
23
+ level = logging.INFO
24
+ _instance: Optional["LoggingLevel"] = None # 单例缓存
25
+
26
+ def __new__(cls):
27
+ if cls._instance is None:
28
+ cls._instance = super().__new__(cls)
29
+ return cls._instance
30
+
31
+ @classmethod
32
+ def set_level(cls, level: int):
33
+ cls.level = level
34
+
35
+
36
+ class DynamicLevelFilter(logging.Filter):
37
+ def filter(self, record: logging.LogRecord) -> bool:
38
+ return record.levelno >= LoggingLevel.level
39
+
40
+
41
+ def getLogger(name: str) -> logging.Logger:
42
+ logger = logging.getLogger(name)
43
+
44
+ def enable_pretty_logging():
45
+ if not logger.handlers:
46
+ # Configure handler
47
+ handler = logging.StreamHandler()
48
+ handler.flush = lambda: handler.stream.flush()
49
+ handler.setFormatter(logging.Formatter('[%(levelname)1s][%(asctime)s %(module)s:%(lineno)d pid:%(process)d] %(message)s'))
50
+ handler.setLevel(logging.NOTSET)
51
+ handler.addFilter(DynamicLevelFilter())
52
+ logger.addHandler(handler)
53
+ logger.setLevel(logging.DEBUG)
54
+ logger.propagate = False
55
+
56
+ enable_pretty_logging()
57
+ return logger
58
+
59
+
60
+ logger = getLogger(__name__)
61
+
62
+
63
+
64
+
65
+ @singleton
66
+ class TimeStamp:
67
+ time_stamp = None
68
+
69
+ def getTimeStamp(cls):
70
+ if cls.time_stamp is None:
71
+ import datetime
72
+ cls.time_stamp = datetime.datetime.now().strftime('%Y%m%d%H_%M%S%f')
73
+ return cls.time_stamp
74
+
75
+ def getCurrentTimeStamp(cls):
76
+ import datetime
77
+ return datetime.datetime.now().strftime('%Y%m%d%H_%M%S%f')
78
+
79
+
80
+ from uiautomator2 import Device
81
+ d = Device
82
+
83
+
84
+ _CUSTOM_PROJECT_ROOT: Optional[Path] = None
85
+
86
+
87
+ def setCustomProjectRoot(configs_path: Optional[Union[str, Path]]):
88
+ """
89
+ Set a custom project root directory (containing the configs directory). Passing None can restore the default behavior.
90
+ """
91
+ global _CUSTOM_PROJECT_ROOT
92
+
93
+ if configs_path is None:
94
+ _CUSTOM_PROJECT_ROOT = None
95
+ return
96
+
97
+ candidate = Path(configs_path).expanduser()
98
+ if candidate.name == "configs":
99
+ candidate = candidate.parent
100
+
101
+ candidate = candidate.resolve()
102
+ _CUSTOM_PROJECT_ROOT = candidate
103
+
104
+
105
+ def getProjectRoot():
106
+ if _CUSTOM_PROJECT_ROOT:
107
+ return _CUSTOM_PROJECT_ROOT
108
+
109
+ root = Path(Path.cwd().anchor)
110
+ cur_dir = Path.absolute(Path(os.curdir))
111
+ while not os.path.isdir(cur_dir / "configs"):
112
+ if cur_dir == root:
113
+ return None
114
+ cur_dir = cur_dir.parent
115
+ return cur_dir
116
+
117
+
118
+ def timer(log_info: str=None):
119
+ """ ### Decorator to measure the execution time of a function.
120
+
121
+ This decorator can be used to wrap functions where you want to log the time taken for execution
122
+
123
+ ### Usage:
124
+ - @timer("Function execution took %cost_time seconds.")
125
+ - @timer() # If no log_info is provided, it will print the function name and execution time.
126
+
127
+ `%cost_time` will be replaced with the actual time taken for execution.
128
+ """
129
+ def accept(func):
130
+ @wraps(func)
131
+ def wrapper(*args, **kwargs):
132
+ start_time = time.time()
133
+ result = func(*args, **kwargs)
134
+ end_time = time.time()
135
+ if log_info:
136
+ logger.info(log_info.replace(r"%cost_time", f"{end_time - start_time:.4f}"))
137
+ else:
138
+ logger.info(f"Function '{func.__name__}' executed in {(end_time - start_time):.4f} seconds.")
139
+ return result
140
+ return wrapper
141
+ return accept
142
+
143
+
144
+ def catchException(log_info: str):
145
+ """ ### Decorator to catch exceptions and print log info.
146
+
147
+ This decorator can be used to wrap functions that may raise exceptions,
148
+ allowing you to log a message when the exception is raised.
149
+
150
+ ### Usage:
151
+ - @catchException("An error occurred in the function ****.")
152
+ """
153
+ def accept(func):
154
+ @wraps(func)
155
+ def wrapper(*args, **kwargs):
156
+ try:
157
+ return func(*args, **kwargs)
158
+ except Exception as e:
159
+ logger.info(log_info)
160
+ tb = traceback.format_exception(type(e), e, e.__traceback__.tb_next)
161
+ print(''.join(tb), end='', flush=True)
162
+ return wrapper
163
+ return accept
164
+
165
+
166
+ def loadFuncsFromFile(file_path: str) -> Dict[str, Callable]:
167
+ if not os.path.exists(file_path):
168
+ raise FileNotFoundError(f"{file_path} not found.")
169
+
170
+ def __get_module():
171
+ import importlib.util
172
+ module_name = Path(file_path).stem
173
+ spec = importlib.util.spec_from_file_location(module_name, file_path)
174
+ mod = importlib.util.module_from_spec(spec)
175
+ spec.loader.exec_module(mod)
176
+ return mod
177
+
178
+ mod = __get_module()
179
+
180
+ import inspect
181
+ funcs = dict()
182
+ for func_name, func in inspect.getmembers(mod, inspect.isfunction):
183
+ funcs[func_name] = func
184
+
185
+ return funcs
186
+
187
+
188
+ def getClassName(clazz):
189
+ return f'%s.%s' % (clazz.__module__, clazz.__qualname__)
190
+
191
+ def getFullPropName(testCase: TestCase):
192
+ return f"%s.%s" % (getClassName(testCase.__class__), testCase._testMethodName)
@@ -0,0 +1,102 @@
1
+ import os
2
+ import json
3
+ import shutil
4
+
5
+ from pathlib import Path
6
+ from packaging.version import Version
7
+ from typing import List, Set, TypedDict
8
+ from importlib.metadata import version
9
+
10
+ from .utils import getLogger, getProjectRoot
11
+
12
+ logger = getLogger(__name__)
13
+
14
+
15
+ CompatibilityInfo = TypedDict('CompatibilityInfo', {
16
+ "name": str,
17
+ "description": str,
18
+ "from": str,
19
+ "to": str,
20
+ })
21
+ VersionInfo = TypedDict('VersionInfo', {
22
+ "compatibility infos": List[CompatibilityInfo],
23
+ })
24
+
25
+
26
+ def ls_files(dir_path: Path) -> Set[Path]:
27
+ """list all files in the directory"""
28
+ return set(f for f in dir_path.rglob('*') if f.is_file())
29
+
30
+
31
+ def check_config_compatibility():
32
+ config_version_sanitizer = ConfigVersionSanitizer()
33
+ config_version_sanitizer.check_config_compatibility()
34
+ config_version_sanitizer.config_auto_update()
35
+
36
+
37
+ def get_cur_version():
38
+ return version("Kea2-python")
39
+
40
+
41
+ class ConfigVersionSanitizer:
42
+ def __init__(self):
43
+ self._version_infos = None
44
+ self._config_version = None
45
+ self.user_config_path = getProjectRoot() / "configs"
46
+ self.kea2_assets_path = Path(__file__).parent / "assets"
47
+ self.kea2_version = get_cur_version()
48
+
49
+ @property
50
+ def version_infos(self) -> VersionInfo:
51
+ if self._version_infos is None:
52
+ with open(self.kea2_assets_path / "config_version.json") as fp:
53
+ self._version_infos = json.load(fp)
54
+ return self._version_infos
55
+
56
+ @property
57
+ def config_version(self):
58
+ if self._config_version is not None:
59
+ return self._config_version
60
+
61
+ user_version_json = self.user_config_path / "version.json"
62
+ if not user_version_json.exists():
63
+ self._config_version = "0.3.6"
64
+ else:
65
+ with open(user_version_json) as fp:
66
+ self._config_version = json.load(fp)["version"]
67
+ return self._config_version
68
+
69
+ def check_config_compatibility(self):
70
+ """Check if the user config version is compatible with the current Kea2 version."""
71
+ update_infos = []
72
+ for info in self.version_infos["compatibility infos"]:
73
+ if Version(info["from"]) > Version(self.config_version):
74
+ update_infos.append(info)
75
+
76
+ if not update_infos:
77
+ return
78
+
79
+ logger.warning("Configuration update required! Please update your configuration files.")
80
+ logger.warning(f"Current kea2 version {self.kea2_version}. Current config version {self.config_version}.")
81
+ for info in update_infos:
82
+ logger.info(
83
+ f"Since version {info['from']}: {info['description']}"
84
+ )
85
+
86
+ def config_auto_update(self):
87
+ self._copy_new_configs()
88
+
89
+ def _copy_new_configs(self):
90
+ src = self.kea2_assets_path / "fastbot_configs"
91
+ dst = self.user_config_path
92
+
93
+ src_files = set(os.listdir(src))
94
+ dst_files = set(os.listdir(dst))
95
+
96
+ new_files = src_files - dst_files
97
+
98
+ for file in new_files:
99
+ src_path = src / file
100
+ dst_path = dst / file
101
+ logger.info(f"Copying new config file: {file}")
102
+ shutil.copy2(src_path, dst_path)