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/report/utils.py ADDED
@@ -0,0 +1,10 @@
1
+ from concurrent.futures import ThreadPoolExecutor
2
+ from contextlib import contextmanager
3
+
4
+ @contextmanager
5
+ def thread_pool(max_workers=128, wait=True, name_prefix="worker"):
6
+ executor = ThreadPoolExecutor(max_workers=max_workers, thread_name_prefix=name_prefix)
7
+ try:
8
+ yield executor
9
+ finally:
10
+ executor.shutdown(wait=wait)
kea2/result.py ADDED
@@ -0,0 +1,257 @@
1
+ from dataclasses import asdict
2
+ from enum import Enum
3
+ import json
4
+ from typing import Deque, Dict
5
+ from unittest import TestCase, TextTestResult
6
+ from collections import deque
7
+
8
+ from .state import INVARIANT_MARKER
9
+ from .typedefs import PBTTestResult, PropertyExecutionInfo, PropStatistic
10
+ from .utils import getLogger, getFullPropName
11
+
12
+
13
+ logger = getLogger(__name__)
14
+
15
+
16
+ class CheckKind(Enum):
17
+ PROPERTY = "property"
18
+ INVARIANT = "invariant"
19
+
20
+
21
+ def get_check_kind(test: TestCase) -> CheckKind:
22
+ if hasattr(test, INVARIANT_MARKER):
23
+ return CheckKind.INVARIANT
24
+ return CheckKind.PROPERTY
25
+
26
+
27
+ class KeaJsonResult(TextTestResult):
28
+
29
+ # +------------------------------+
30
+ # | Setup utils |
31
+ # +------------------------------+
32
+ res: PBTTestResult = dict()
33
+ lastPropertyInfo: PropertyExecutionInfo
34
+ lastInvariantInfo: PropertyExecutionInfo
35
+ executionInfoBuffer: Deque["PropertyExecutionInfo"] = deque()
36
+ currentStepsCount: int
37
+ result_file: str
38
+ property_exection_result_file: str
39
+
40
+ @classmethod
41
+ def setProperties(cls, allProperties: Dict):
42
+ for testCase in allProperties.values():
43
+ cls.res[getFullPropName(testCase)] = PropStatistic(kind=CheckKind.PROPERTY.value)
44
+
45
+ @classmethod
46
+ def setInvariants(cls, allInvariants: Dict):
47
+ for testCase in allInvariants.values():
48
+ cls.res[getFullPropName(testCase)] = PropStatistic(kind=CheckKind.INVARIANT.value)
49
+
50
+ @classmethod
51
+ def setOutputFile(cls, result_file, property_exec_result_file):
52
+ cls.result_file = result_file
53
+ cls.property_exection_result_file = property_exec_result_file
54
+
55
+ def __init__(self, stream, descriptions, verbosity):
56
+ super().__init__(stream, descriptions, verbosity)
57
+ self.showAll = True
58
+ self.currentStepsCount = 0
59
+ self.lastInvariantInfo = None
60
+
61
+ def startTest(self, test):
62
+ if get_check_kind(test) is CheckKind.INVARIANT:
63
+ self.lastInvariantInfo = PropertyExecutionInfo(
64
+ propName=getFullPropName(test),
65
+ kind=CheckKind.INVARIANT.value,
66
+ state="start",
67
+ tb="",
68
+ startStepsCount=self.currentStepsCount
69
+ )
70
+ super(TextTestResult, self).startTest(test)
71
+ if self.showAll:
72
+ self.stream.write(" - ")
73
+ self.stream.write(str(test))
74
+ self.stream.write(" ... ")
75
+ self.stream.flush()
76
+ self._newline = False
77
+
78
+ def setCurrentStepsCount(self, stepsCount: int):
79
+ self.currentStepsCount = stepsCount
80
+
81
+ # +------------------------------+
82
+ # | Property-specific execution |
83
+ # +------------------------------+
84
+ def addExcutedProperty(self, test: TestCase, stepsCount: int):
85
+ self.res[getFullPropName(test)].executed += 1
86
+
87
+ self.lastPropertyInfo = PropertyExecutionInfo(
88
+ propName=getFullPropName(test),
89
+ kind=CheckKind.PROPERTY.value,
90
+ state="start",
91
+ tb="",
92
+ startStepsCount=stepsCount
93
+ )
94
+
95
+ def addPropertyPrecondSatisfied(self, test: TestCase):
96
+ self.res[getFullPropName(test)].precond_satisfied += 1
97
+
98
+ def updateExecutionInfo(self, test):
99
+ # if the test is a property, and it is still in "start" state, set it to "pass"
100
+ # then record the last executed property info
101
+ if get_check_kind(test) is CheckKind.PROPERTY:
102
+ if self.lastPropertyInfo.state == "start":
103
+ self.lastPropertyInfo.state = "pass"
104
+ self.executionInfoBuffer.append(self.lastPropertyInfo)
105
+ # if the test is an invariant, and the last invariant failed or errored, record it
106
+ # (only record failed/errored invariants)
107
+ if get_check_kind(test) is CheckKind.INVARIANT:
108
+ if self.lastInvariantInfo.state in {"fail", "error"}:
109
+ self.executionInfoBuffer.append(self.lastInvariantInfo)
110
+
111
+ def getExcutedProperty(self, test: TestCase):
112
+ return self.res[getFullPropName(test)].executed
113
+
114
+ # +------------------------------+
115
+ # | Shared result updates |
116
+ # +------------------------------+
117
+ def addFailure(self, test, err):
118
+ super().addFailure(test, err)
119
+ self.res[getFullPropName(test)].fail += 1
120
+ self._record_exception(test, err, "fail")
121
+
122
+ def addError(self, test, err):
123
+ super().addError(test, err)
124
+ self.res[getFullPropName(test)].error += 1
125
+ self._record_exception(test, err, "error")
126
+
127
+ # +------------------------------+
128
+ # | Result utils |
129
+ # +------------------------------+
130
+ def printError(self, test):
131
+ self._print_property_error(test)
132
+ self._print_invariant_error(test)
133
+
134
+ def logSummary(self):
135
+ property_fails = sum(_.fail for _ in self.res.values() if _.kind == "property")
136
+ property_errors = sum(_.error for _ in self.res.values() if _.kind == "property")
137
+ invariant_fails = sum(_.fail for _ in self.res.values() if _.kind == "invariant")
138
+ invariant_errors = sum(_.error for _ in self.res.values() if _.kind == "invariant")
139
+ logger.info(f"[Property Execution Summary] Errors:{property_errors}, Fails:{property_fails}")
140
+ if invariant_fails > 0 or invariant_errors > 0:
141
+ logger.info(f"[Invariant Execution Summary] Errors:{invariant_errors}, Fails:{invariant_fails}")
142
+
143
+ def flushResult(self):
144
+ json_res = dict()
145
+ for propName, propStatitic in self.res.items():
146
+ json_res[propName] = asdict(propStatitic)
147
+ with open(self.result_file, "w", encoding="utf-8") as fp:
148
+ json.dump(json_res, fp, indent=4)
149
+
150
+ with open(self.property_exection_result_file, "a", encoding="utf-8") as fp:
151
+ while self.executionInfoBuffer:
152
+ execInfo = self.executionInfoBuffer.popleft()
153
+ fp.write(f"{json.dumps(asdict(execInfo))}\n")
154
+
155
+ # +------------------------------+
156
+ # | Property/invariant helpers |
157
+ # +------------------------------+
158
+ def _record_exception(self, test, err, state):
159
+ if get_check_kind(test) is CheckKind.PROPERTY:
160
+ self.lastPropertyInfo.state = state
161
+ self.lastPropertyInfo.tb = self._exc_info_to_string(err, test)
162
+ return
163
+ if get_check_kind(test) is CheckKind.INVARIANT:
164
+ self.lastInvariantInfo = PropertyExecutionInfo(
165
+ propName=getFullPropName(test),
166
+ kind=CheckKind.INVARIANT.value,
167
+ state=state,
168
+ tb=self._exc_info_to_string(err, test),
169
+ startStepsCount=self.currentStepsCount,
170
+ )
171
+
172
+ def _print_property_error(self, test):
173
+ if get_check_kind(test) is not CheckKind.PROPERTY:
174
+ return
175
+ if self.lastPropertyInfo.state not in ["fail", "error"]:
176
+ return
177
+ flavour = self.lastPropertyInfo.state.upper()
178
+ self.stream.writeln("")
179
+ self.stream.writeln(self.separator1)
180
+ self.stream.writeln("%s: %s" % (flavour, self.getDescription(test)))
181
+ self.stream.writeln(self.separator2)
182
+ self.stream.writeln("%s" % self.lastPropertyInfo.tb)
183
+ self.stream.writeln(self.separator1)
184
+ self.stream.flush()
185
+
186
+ def _print_invariant_error(self, test):
187
+ if get_check_kind(test) is not CheckKind.INVARIANT:
188
+ return
189
+ if not self.lastInvariantInfo or self.lastInvariantInfo.state not in {"fail", "error"}:
190
+ return
191
+ self.stream.writeln("")
192
+ self.stream.writeln(self.separator1)
193
+ self.stream.writeln("%s: %s" % (self.lastInvariantInfo.state.upper(), self.lastInvariantInfo.tb))
194
+ self.stream.writeln(self.separator1)
195
+ self.stream.flush()
196
+
197
+
198
+ class KeaTextTestResult(TextTestResult):
199
+
200
+ # +------------------------------+
201
+ # | Setup and output formatting |
202
+ # +------------------------------+
203
+ def __init__(self, stream, descriptions, verbosity):
204
+ super().__init__(stream, descriptions, verbosity)
205
+ self.showAll = True
206
+
207
+ def getDescription(self: "KeaJsonResult", test: "TestCase"):
208
+ doc_first_line = test.shortDescription()
209
+ if get_check_kind(test) is CheckKind.INVARIANT:
210
+ return getFullPropName(test)
211
+ if self.descriptions and doc_first_line:
212
+ doc_first_line = "# " + doc_first_line
213
+ return '\n'.join((str(test), doc_first_line))
214
+ else:
215
+ return str(test)
216
+
217
+ def startTest(self: "KeaJsonResult", test):
218
+ if get_check_kind(test) is CheckKind.INVARIANT:
219
+ self.stream.writeln(f"[INFO] Invariant: {getFullPropName(test)}")
220
+ self.stream.flush()
221
+ self._newline = True
222
+ else:
223
+ self.stream.write("[INFO] Start executing property: ")
224
+ self.stream.writeln(self.getDescription(test))
225
+ self.stream.flush()
226
+ self._newline = True
227
+
228
+ # +------------------------------+
229
+ # | Shared status updates |
230
+ # +------------------------------+
231
+ @property
232
+ def wasFail(self):
233
+ return self._wasFail
234
+
235
+ def addError(self, test, err):
236
+ self._wasFail = True
237
+ return super().addError(test, err)
238
+
239
+ def addFailure(self, test, err):
240
+ self._wasFail = True
241
+ return super().addFailure(test, err)
242
+
243
+ def addSuccess(self, test):
244
+ self._wasFail = False
245
+ return super().addSuccess(test)
246
+
247
+ def addSkip(self, test, reason):
248
+ self._wasFail = False
249
+ return super().addSkip(test, reason)
250
+
251
+ def addExpectedFailure(self, test, err):
252
+ self._wasFail = False
253
+ return super().addExpectedFailure(test, err)
254
+
255
+ def addUnexpectedSuccess(self, test):
256
+ self._wasFail = False
257
+ return super().addUnexpectedSuccess(test)
kea2/resultSyncer.py ADDED
@@ -0,0 +1,65 @@
1
+ import threading
2
+
3
+ from pathlib import Path
4
+
5
+ from .adbUtils import ADBDevice
6
+ from .utils import getLogger, catchException, timer
7
+
8
+ from typing import TYPE_CHECKING
9
+ if TYPE_CHECKING:
10
+ from .keaUtils import Options
11
+
12
+ logger = getLogger(__name__)
13
+
14
+
15
+ class ResultSyncer:
16
+
17
+ def __init__(self, device_output_dir, options: "Options"):
18
+ self.device_output_dir = device_output_dir
19
+ self.output_dir = options.output_dir / Path(device_output_dir).name
20
+ self.running = False
21
+ self.thread = None
22
+ self.sync_event = threading.Event()
23
+
24
+ ADBDevice.setDevice(serial=options.serial, transport_id=options.transport_id)
25
+ self.dev = ADBDevice()
26
+
27
+ def run(self):
28
+ """Start a background thread to sync device data when triggered"""
29
+ self.running = True
30
+ self.thread = threading.Thread(target=self._sync_thread, daemon=True)
31
+ self.thread.start()
32
+
33
+ def _sync_thread(self):
34
+ """Thread function that waits for sync event and then syncs data"""
35
+ while self.running:
36
+ # Wait for sync event with a timeout to periodically check if still running
37
+ if self.sync_event.wait(timeout=1):
38
+ self._sync_device_data()
39
+ self.sync_event.clear()
40
+
41
+ @timer("Data Sync cost %cost_time seconds")
42
+ def close(self):
43
+ self.running = False
44
+ self.sync_event.set()
45
+ if self.thread and self.thread.is_alive():
46
+ logger.info("Syncing result data from device. Please wait...")
47
+ self.thread.join(timeout=10)
48
+ self._sync_device_data()
49
+ try:
50
+ logger.debug(f"Removing device output directory: {self.device_output_dir}")
51
+ remove_device_dir = ["rm", "-rf", self.device_output_dir]
52
+ self.dev.shell(remove_device_dir)
53
+ except Exception as e:
54
+ logger.error(f"Error removing device output directory: {e}", flush=True)
55
+
56
+ @catchException("Error during device data sync.")
57
+ def _sync_device_data(self):
58
+ """
59
+ Sync the device data to the local directory.
60
+ """
61
+ logger.debug("Syncing data")
62
+ self.dev.sync.pull_dir(self.device_output_dir, self.output_dir, exist_ok=True)
63
+
64
+ remove_pulled_screenshots = ["find", self.device_output_dir, "-name", '"*.png"', "-delete"]
65
+ self.dev.shell(remove_pulled_screenshots)
kea2/state.py ADDED
@@ -0,0 +1,22 @@
1
+ # state.py is used for stateful testing
2
+ #
3
+ # Supports defining complex state that can be shared across multiple test cases
4
+ # to control test flow and dependencies
5
+ INVARIANT_MARKER = "_is_invariant"
6
+
7
+
8
+ class State(dict):
9
+ _instance = None
10
+
11
+ def __new__(cls):
12
+ if cls._instance is None:
13
+ cls._instance = super().__new__(cls)
14
+ return cls._instance
15
+
16
+
17
+ state = State()
18
+
19
+
20
+ def invariant(f):
21
+ setattr(f, INVARIANT_MARKER, True)
22
+ return f
kea2/typedefs.py ADDED
@@ -0,0 +1,32 @@
1
+ # Class Typing
2
+ from dataclasses import dataclass
3
+ from typing import Deque, Dict, Literal, NewType
4
+ from unittest import TestCase
5
+
6
+ PRECONDITIONS_MARKER = "preconds"
7
+ PROB_MARKER = "prob"
8
+ MAX_TRIES_MARKER = "max_tries"
9
+ INTERRUPTABLE_MARKER = "interruptable"
10
+
11
+ @dataclass
12
+ class PropStatistic:
13
+ kind: Literal["property", "invariant"] = "unknown"
14
+ precond_satisfied: int = 0
15
+ executed: int = 0
16
+ fail: int = 0
17
+ error: int = 0
18
+
19
+
20
+ @dataclass
21
+ class PropertyExecutionInfo:
22
+ startStepsCount: int
23
+ propName: "PropName"
24
+ kind: Literal["property", "invariant"]
25
+ state: Literal["start", "pass", "fail", "error"]
26
+ tb: str
27
+
28
+
29
+ PropName = NewType("PropName", str)
30
+ PropertyStore = NewType("PropertyStore", Dict[PropName, TestCase])
31
+
32
+ PBTTestResult = NewType("PBTTestResult", Dict[PropName, PropStatistic])