Kea2-python 0.2.2__py3-none-any.whl → 0.2.3__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.
Potentially problematic release.
This version of Kea2-python might be problematic. Click here for more details.
- kea2/adbUtils.py +3 -1
- kea2/bug_report_generator.py +458 -229
- kea2/fastbotManager.py +7 -3
- kea2/keaUtils.py +56 -53
- kea2/logWatcher.py +3 -1
- kea2/templates/bug_report_template.html +499 -152
- kea2/u2Driver.py +2 -1
- kea2/utils.py +14 -0
- {kea2_python-0.2.2.dist-info → kea2_python-0.2.3.dist-info}/METADATA +1 -1
- {kea2_python-0.2.2.dist-info → kea2_python-0.2.3.dist-info}/RECORD +14 -14
- {kea2_python-0.2.2.dist-info → kea2_python-0.2.3.dist-info}/WHEEL +0 -0
- {kea2_python-0.2.2.dist-info → kea2_python-0.2.3.dist-info}/entry_points.txt +0 -0
- {kea2_python-0.2.2.dist-info → kea2_python-0.2.3.dist-info}/licenses/LICENSE +0 -0
- {kea2_python-0.2.2.dist-info → kea2_python-0.2.3.dist-info}/top_level.txt +0 -0
kea2/fastbotManager.py
CHANGED
|
@@ -13,7 +13,7 @@ from kea2.utils import getLogger
|
|
|
13
13
|
|
|
14
14
|
from typing import IO, TYPE_CHECKING, Dict
|
|
15
15
|
if TYPE_CHECKING:
|
|
16
|
-
from .keaUtils import Options
|
|
16
|
+
from .keaUtils import Options, PropertyExecutionInfo
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
logger = getLogger(__name__)
|
|
@@ -147,11 +147,15 @@ class FastbotManager:
|
|
|
147
147
|
print(f"[Server INFO] {r.text}", flush=True)
|
|
148
148
|
|
|
149
149
|
@retry(Exception, tries=2, delay=2)
|
|
150
|
-
def logScript(self, execution_info:
|
|
150
|
+
def logScript(self, execution_info: "PropertyExecutionInfo"):
|
|
151
151
|
r = self.request(
|
|
152
152
|
method="POST",
|
|
153
153
|
path="/logScript",
|
|
154
|
-
data=
|
|
154
|
+
data={
|
|
155
|
+
"propName": execution_info.propName,
|
|
156
|
+
"startStepsCount": execution_info.startStepsCount,
|
|
157
|
+
"state": execution_info.state,
|
|
158
|
+
}
|
|
155
159
|
)
|
|
156
160
|
res = r.text
|
|
157
161
|
if res != "OK":
|
kea2/keaUtils.py
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
+
from collections import deque
|
|
1
2
|
import json
|
|
2
3
|
import os
|
|
3
4
|
from pathlib import Path
|
|
4
5
|
import traceback
|
|
5
6
|
import time
|
|
6
|
-
from typing import Callable, Any, Dict, List, Literal, NewType, TypedDict, Union
|
|
7
|
+
from typing import Callable, Any, Deque, Dict, List, Literal, NewType, TypedDict, Union
|
|
7
8
|
from unittest import TextTestRunner, registerResult, TestSuite, TestCase, TextTestResult
|
|
8
9
|
import random
|
|
9
10
|
import warnings
|
|
10
11
|
from dataclasses import dataclass, asdict
|
|
11
|
-
import requests
|
|
12
12
|
from kea2.absDriver import AbstractDriver
|
|
13
13
|
from functools import wraps
|
|
14
14
|
from kea2.bug_report_generator import BugReportGenerator
|
|
@@ -31,14 +31,12 @@ logger = getLogger(__name__)
|
|
|
31
31
|
# Class Typing
|
|
32
32
|
PropName = NewType("PropName", str)
|
|
33
33
|
PropertyStore = NewType("PropertyStore", Dict[PropName, TestCase])
|
|
34
|
-
|
|
35
|
-
"PropertyExecutionInfo",
|
|
36
|
-
{"propName": PropName, "state": Literal["start", "pass", "fail", "error"]}
|
|
37
|
-
)
|
|
34
|
+
|
|
38
35
|
|
|
39
36
|
STAMP = TimeStamp().getTimeStamp()
|
|
40
37
|
LOGFILE: str
|
|
41
38
|
RESFILE: str
|
|
39
|
+
PROP_EXEC_RESFILE: str
|
|
42
40
|
|
|
43
41
|
def precondition(precond: Callable[[Any], bool]) -> Callable:
|
|
44
42
|
"""the decorator @precondition
|
|
@@ -147,6 +145,7 @@ class Options:
|
|
|
147
145
|
def __post_init__(self):
|
|
148
146
|
import logging
|
|
149
147
|
logging.basicConfig(level=logging.DEBUG if self.debug else logging.INFO)
|
|
148
|
+
|
|
150
149
|
if self.Driver:
|
|
151
150
|
target_device = dict()
|
|
152
151
|
if self.serial:
|
|
@@ -155,7 +154,8 @@ class Options:
|
|
|
155
154
|
target_device["transport_id"] = self.transport_id
|
|
156
155
|
self.Driver.setDevice(target_device)
|
|
157
156
|
ADBDevice.setDevice(self.serial, self.transport_id)
|
|
158
|
-
|
|
157
|
+
|
|
158
|
+
global LOGFILE, RESFILE, PROP_EXEC_RESFILE, STAMP
|
|
159
159
|
if self.log_stamp:
|
|
160
160
|
illegal_chars = ['/', '\\', ':', '*', '?', '"', '<', '>', '|', '\n', '\r', '\t', '\0']
|
|
161
161
|
for char in illegal_chars:
|
|
@@ -164,9 +164,11 @@ class Options:
|
|
|
164
164
|
f"char: `{char}` is illegal in --log-stamp. current stamp: {self.log_stamp}"
|
|
165
165
|
)
|
|
166
166
|
STAMP = self.log_stamp
|
|
167
|
+
|
|
167
168
|
self.output_dir = Path(self.output_dir).absolute() / f"res_{STAMP}"
|
|
168
169
|
LOGFILE = f"fastbot_{STAMP}.log"
|
|
169
170
|
RESFILE = f"result_{STAMP}.json"
|
|
171
|
+
PROP_EXEC_RESFILE = f"property_exec_info_{STAMP}.json"
|
|
170
172
|
|
|
171
173
|
self.profile_period = int(self.profile_period)
|
|
172
174
|
if self.profile_period < 1:
|
|
@@ -195,6 +197,16 @@ class PropStatistic:
|
|
|
195
197
|
fail: int = 0
|
|
196
198
|
error: int = 0
|
|
197
199
|
|
|
200
|
+
|
|
201
|
+
PropertyExecutionInfoStore = NewType("PropertyExecutionInfoStore", Deque["PropertyExecutionInfo"])
|
|
202
|
+
@dataclass
|
|
203
|
+
class PropertyExecutionInfo:
|
|
204
|
+
startStepsCount: int
|
|
205
|
+
propName: PropName
|
|
206
|
+
state: Literal["start", "pass", "fail", "error"]
|
|
207
|
+
tb: str
|
|
208
|
+
|
|
209
|
+
|
|
198
210
|
class PBTTestResult(dict):
|
|
199
211
|
def __getitem__(self, key) -> PropStatistic:
|
|
200
212
|
return super().__getitem__(key)
|
|
@@ -207,13 +219,12 @@ def getFullPropName(testCase: TestCase):
|
|
|
207
219
|
testCase._testMethodName
|
|
208
220
|
])
|
|
209
221
|
|
|
222
|
+
|
|
210
223
|
class JsonResult(TextTestResult):
|
|
211
|
-
res: PBTTestResult
|
|
212
224
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
}
|
|
225
|
+
res: PBTTestResult
|
|
226
|
+
lastExecutedInfo: PropertyExecutionInfo
|
|
227
|
+
executionInfoStore: PropertyExecutionInfoStore = deque()
|
|
217
228
|
|
|
218
229
|
@classmethod
|
|
219
230
|
def setProperties(cls, allProperties: Dict):
|
|
@@ -221,19 +232,28 @@ class JsonResult(TextTestResult):
|
|
|
221
232
|
for testCase in allProperties.values():
|
|
222
233
|
cls.res[getFullPropName(testCase)] = PropStatistic()
|
|
223
234
|
|
|
224
|
-
def flushResult(self
|
|
235
|
+
def flushResult(self):
|
|
236
|
+
global RESFILE, PROP_EXEC_RESFILE
|
|
225
237
|
json_res = dict()
|
|
226
238
|
for propName, propStatitic in self.res.items():
|
|
227
239
|
json_res[propName] = asdict(propStatitic)
|
|
228
|
-
with open(
|
|
240
|
+
with open(RESFILE, "w", encoding="utf-8") as fp:
|
|
229
241
|
json.dump(json_res, fp, indent=4)
|
|
230
242
|
|
|
231
|
-
|
|
243
|
+
while self.executionInfoStore:
|
|
244
|
+
execInfo = self.executionInfoStore.popleft()
|
|
245
|
+
with open(PROP_EXEC_RESFILE, "a", encoding="utf-8") as fp:
|
|
246
|
+
fp.write(f"{json.dumps(asdict(execInfo))}\n")
|
|
247
|
+
|
|
248
|
+
def addExcuted(self, test: TestCase, stepsCount: int):
|
|
232
249
|
self.res[getFullPropName(test)].executed += 1
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
250
|
+
|
|
251
|
+
self.lastExecutedInfo = PropertyExecutionInfo(
|
|
252
|
+
propName=getFullPropName(test),
|
|
253
|
+
state="start",
|
|
254
|
+
tb="",
|
|
255
|
+
startStepsCount=stepsCount
|
|
256
|
+
)
|
|
237
257
|
|
|
238
258
|
def addPrecondSatisfied(self, test: TestCase):
|
|
239
259
|
self.res[getFullPropName(test)].precond_satisfied += 1
|
|
@@ -241,16 +261,21 @@ class JsonResult(TextTestResult):
|
|
|
241
261
|
def addFailure(self, test, err):
|
|
242
262
|
super().addFailure(test, err)
|
|
243
263
|
self.res[getFullPropName(test)].fail += 1
|
|
244
|
-
self.lastExecutedInfo
|
|
264
|
+
self.lastExecutedInfo.state = "fail"
|
|
265
|
+
self.lastExecutedInfo.tb = self._exc_info_to_string(err, test)
|
|
245
266
|
|
|
246
267
|
def addError(self, test, err):
|
|
247
268
|
super().addError(test, err)
|
|
248
269
|
self.res[getFullPropName(test)].error += 1
|
|
249
|
-
self.lastExecutedInfo
|
|
270
|
+
self.lastExecutedInfo.state = "error"
|
|
271
|
+
self.lastExecutedInfo.tb = self._exc_info_to_string(err, test)
|
|
250
272
|
|
|
251
273
|
def updateExectedInfo(self):
|
|
252
|
-
if self.lastExecutedInfo
|
|
253
|
-
self.lastExecutedInfo
|
|
274
|
+
if self.lastExecutedInfo.state == "start":
|
|
275
|
+
self.lastExecutedInfo.state = "pass"
|
|
276
|
+
|
|
277
|
+
self.executionInfoStore.append(self.lastExecutedInfo)
|
|
278
|
+
|
|
254
279
|
|
|
255
280
|
def getExcuted(self, test: TestCase):
|
|
256
281
|
return self.res[getFullPropName(test)].executed
|
|
@@ -275,11 +300,13 @@ class KeaTestRunner(TextTestRunner):
|
|
|
275
300
|
def _setOuputDir(self):
|
|
276
301
|
output_dir = Path(self.options.output_dir).absolute()
|
|
277
302
|
output_dir.mkdir(parents=True, exist_ok=True)
|
|
278
|
-
global LOGFILE, RESFILE
|
|
303
|
+
global LOGFILE, RESFILE, PROP_EXEC_RESFILE
|
|
279
304
|
LOGFILE = output_dir / Path(LOGFILE)
|
|
280
305
|
RESFILE = output_dir / Path(RESFILE)
|
|
306
|
+
PROP_EXEC_RESFILE = output_dir / Path(PROP_EXEC_RESFILE)
|
|
281
307
|
logger.info(f"Log file: {LOGFILE}")
|
|
282
308
|
logger.info(f"Result file: {RESFILE}")
|
|
309
|
+
logger.info(f"Property execution info file: {PROP_EXEC_RESFILE}")
|
|
283
310
|
|
|
284
311
|
def run(self, test):
|
|
285
312
|
|
|
@@ -323,7 +350,7 @@ class KeaTestRunner(TextTestRunner):
|
|
|
323
350
|
|
|
324
351
|
if self.options.agent == "u2":
|
|
325
352
|
# initialize the result.json file
|
|
326
|
-
result.flushResult(
|
|
353
|
+
result.flushResult()
|
|
327
354
|
# setUp for the u2 driver
|
|
328
355
|
self.scriptDriver = self.options.Driver.getScriptDriver()
|
|
329
356
|
fb.check_alive()
|
|
@@ -347,7 +374,7 @@ class KeaTestRunner(TextTestRunner):
|
|
|
347
374
|
try:
|
|
348
375
|
xml_raw = fb.stepMonkey(self._monkeyStepInfo)
|
|
349
376
|
propsSatisfiedPrecond = self.getValidProperties(xml_raw, result)
|
|
350
|
-
except
|
|
377
|
+
except u2.HTTPError:
|
|
351
378
|
logger.info("Connection refused by remote.")
|
|
352
379
|
if fb.get_return_code() == 0:
|
|
353
380
|
logger.info("Exploration times up (--running-minutes).")
|
|
@@ -382,7 +409,7 @@ class KeaTestRunner(TextTestRunner):
|
|
|
382
409
|
setattr(test, self.options.driverName, self.scriptDriver)
|
|
383
410
|
print("execute property %s." % execPropName, flush=True)
|
|
384
411
|
|
|
385
|
-
result.addExcuted(test)
|
|
412
|
+
result.addExcuted(test, self.stepsCount)
|
|
386
413
|
fb.logScript(result.lastExecutedInfo)
|
|
387
414
|
try:
|
|
388
415
|
test(result)
|
|
@@ -391,11 +418,11 @@ class KeaTestRunner(TextTestRunner):
|
|
|
391
418
|
|
|
392
419
|
result.updateExectedInfo()
|
|
393
420
|
fb.logScript(result.lastExecutedInfo)
|
|
394
|
-
result.flushResult(
|
|
421
|
+
result.flushResult()
|
|
395
422
|
|
|
396
423
|
if not end_by_remote:
|
|
397
424
|
fb.stopMonkey()
|
|
398
|
-
result.flushResult(
|
|
425
|
+
result.flushResult()
|
|
399
426
|
resultSyncer.close()
|
|
400
427
|
|
|
401
428
|
fb.join()
|
|
@@ -438,30 +465,6 @@ class KeaTestRunner(TextTestRunner):
|
|
|
438
465
|
self.stream.flush()
|
|
439
466
|
return result
|
|
440
467
|
|
|
441
|
-
# def stepMonkey(self) -> str:
|
|
442
|
-
# """
|
|
443
|
-
# send a step monkey request to the server and get the xml string.
|
|
444
|
-
# """
|
|
445
|
-
# block_dict = self._getBlockedWidgets()
|
|
446
|
-
# block_widgets: List[str] = block_dict['widgets']
|
|
447
|
-
# block_trees: List[str] = block_dict['trees']
|
|
448
|
-
# URL = f"http://localhost:{self.scriptDriver.lport}/stepMonkey"
|
|
449
|
-
# logger.debug(f"Sending request: {URL}")
|
|
450
|
-
# logger.debug(f"Blocking widgets: {block_widgets}")
|
|
451
|
-
# logger.debug(f"Blocking trees: {block_trees}")
|
|
452
|
-
# r = requests.post(
|
|
453
|
-
# url=URL,
|
|
454
|
-
# json={
|
|
455
|
-
# "steps_count": self.stepsCount,
|
|
456
|
-
# "block_widgets": block_widgets,
|
|
457
|
-
# "block_trees": block_trees
|
|
458
|
-
# }
|
|
459
|
-
# )
|
|
460
|
-
|
|
461
|
-
# res = json.loads(r.content)
|
|
462
|
-
# xml_raw = res["result"]
|
|
463
|
-
# return xml_raw
|
|
464
|
-
|
|
465
468
|
@property
|
|
466
469
|
def _monkeyStepInfo(self):
|
|
467
470
|
r = self._get_block_widgets()
|
kea2/logWatcher.py
CHANGED
|
@@ -51,9 +51,10 @@ class LogWatcher:
|
|
|
51
51
|
)
|
|
52
52
|
|
|
53
53
|
statistic_match = PATTERN_STATISTIC.search(content)
|
|
54
|
-
if statistic_match:
|
|
54
|
+
if statistic_match and not self.statistic_printed:
|
|
55
55
|
statistic_body = statistic_match.group(1).strip()
|
|
56
56
|
if statistic_body:
|
|
57
|
+
self.statistic_printed = True
|
|
57
58
|
print(
|
|
58
59
|
"[INFO] Fastbot exit:\n" +
|
|
59
60
|
statistic_body
|
|
@@ -63,6 +64,7 @@ class LogWatcher:
|
|
|
63
64
|
logger.info(f"Watching log: {log_file}")
|
|
64
65
|
self.log_file = log_file
|
|
65
66
|
self.end_flag = False
|
|
67
|
+
self.statistic_printed = False
|
|
66
68
|
|
|
67
69
|
threading.excepthook = thread_excepthook
|
|
68
70
|
self.t = threading.Thread(target=self.watcher, daemon=True)
|