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/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: Dict):
150
+ def logScript(self, execution_info: "PropertyExecutionInfo"):
151
151
  r = self.request(
152
152
  method="POST",
153
153
  path="/logScript",
154
- data=execution_info
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
- PropertyExecutionInfo = TypedDict(
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
- global LOGFILE, RESFILE, STAMP
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
- lastExecutedInfo: PropertyExecutionInfo = {
214
- "propName": "",
215
- "state": "",
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, outfile):
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(outfile, "w", encoding="utf-8") as fp:
240
+ with open(RESFILE, "w", encoding="utf-8") as fp:
229
241
  json.dump(json_res, fp, indent=4)
230
242
 
231
- def addExcuted(self, test: TestCase):
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
- self.lastExecutedInfo = {
234
- "propName": getFullPropName(test),
235
- "state": "start",
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["state"] = "fail"
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["state"] = "error"
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["state"] == "start":
253
- self.lastExecutedInfo["state"] = "pass"
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(outfile=RESFILE)
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 requests.ConnectionError:
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(outfile=RESFILE)
421
+ result.flushResult()
395
422
 
396
423
  if not end_by_remote:
397
424
  fb.stopMonkey()
398
- result.flushResult(outfile=RESFILE)
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)