Kea2-python 0.1.0b0__py3-none-any.whl → 0.1.1__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/keaUtils.py CHANGED
@@ -1,10 +1,8 @@
1
1
  import json
2
2
  import os
3
3
  from pathlib import Path
4
- import subprocess
5
- import threading
6
4
  import traceback
7
- from typing import IO, Callable, Any, Dict, List, Literal, NewType, Union
5
+ from typing import Callable, Any, Dict, List, Literal, NewType, TypedDict, Union
8
6
  from unittest import TextTestRunner, registerResult, TestSuite, TestCase, TextTestResult
9
7
  import random
10
8
  import warnings
@@ -12,12 +10,12 @@ from dataclasses import dataclass, asdict
12
10
  import requests
13
11
  from .absDriver import AbstractDriver
14
12
  from functools import wraps
15
- from time import sleep
16
- from .adbUtils import push_file
13
+ from .bug_report_generator import BugReportGenerator
17
14
  from .resultSyncer import ResultSyncer
18
15
  from .logWatcher import LogWatcher
19
16
  from .utils import TimeStamp, getProjectRoot, getLogger
20
17
  from .u2Driver import StaticU2UiObject, selector_to_xpath
18
+ from .fastbotManager import FastbotManager
21
19
  import uiautomator2 as u2
22
20
  import types
23
21
 
@@ -31,6 +29,10 @@ logger = getLogger(__name__)
31
29
  # Class Typing
32
30
  PropName = NewType("PropName", str)
33
31
  PropertyStore = NewType("PropertyStore", Dict[PropName, TestCase])
32
+ PropertyExecutionInfo = TypedDict(
33
+ "PropertyExecutionInfo",
34
+ {"propName": PropName, "state": Literal["start", "pass", "fail", "error"]}
35
+ )
34
36
 
35
37
  STAMP = TimeStamp().getTimeStamp()
36
38
  LOGFILE: str
@@ -185,7 +187,7 @@ def getFullPropName(testCase: TestCase):
185
187
  class JsonResult(TextTestResult):
186
188
  res: PBTTestResult
187
189
 
188
- lastExecutedInfo: Dict = {
190
+ lastExecutedInfo: PropertyExecutionInfo = {
189
191
  "propName": "",
190
192
  "state": "",
191
193
  }
@@ -223,128 +225,27 @@ class JsonResult(TextTestResult):
223
225
  self.res[getFullPropName(test)].error += 1
224
226
  self.lastExecutedInfo["state"] = "error"
225
227
 
228
+ def updateExectedInfo(self):
229
+ if self.lastExecutedInfo["state"] == "start":
230
+ self.lastExecutedInfo["state"] = "pass"
231
+
226
232
  def getExcuted(self, test: TestCase):
227
233
  return self.res[getFullPropName(test)].executed
228
234
 
229
235
 
230
- def activateFastbot(options: Options, port=None) -> threading.Thread:
231
- """
232
- activate fastbot.
233
- :params: options: the running setting for fastbot
234
- :params: port: the listening port for script driver
235
- :return: the fastbot daemon thread
236
- """
237
- cur_dir = Path(__file__).parent
238
- push_file(
239
- Path.joinpath(cur_dir, "assets/monkeyq.jar"),
240
- "/sdcard/monkeyq.jar",
241
- device=options.serial
242
- )
243
- push_file(
244
- Path.joinpath(cur_dir, "assets/fastbot-thirdpart.jar"),
245
- "/sdcard/fastbot-thirdpart.jar",
246
- device=options.serial,
247
- )
248
- push_file(
249
- Path.joinpath(cur_dir, "assets/framework.jar"),
250
- "/sdcard/framework.jar",
251
- device=options.serial
252
- )
253
- push_file(
254
- Path.joinpath(cur_dir, "assets/fastbot_libs/arm64-v8a"),
255
- "/data/local/tmp",
256
- device=options.serial
257
- )
258
- push_file(
259
- Path.joinpath(cur_dir, "assets/fastbot_libs/armeabi-v7a"),
260
- "/data/local/tmp",
261
- device=options.serial
262
- )
263
- push_file(
264
- Path.joinpath(cur_dir, "assets/fastbot_libs/x86"),
265
- "/data/local/tmp",
266
- device=options.serial
267
- )
268
- push_file(
269
- Path.joinpath(cur_dir, "assets/fastbot_libs/x86_64"),
270
- "/data/local/tmp",
271
- device=options.serial
272
- )
273
-
274
- t = startFastbotService(options)
275
- print("[INFO] Running Fastbot...", flush=True)
276
-
277
- return t
278
-
279
-
280
- def check_alive(port):
281
- """
282
- check if the script driver and proxy server are alive.
283
- """
284
- for _ in range(10):
285
- sleep(2)
286
- try:
287
- requests.get(f"http://localhost:{port}/ping")
288
- return
289
- except requests.ConnectionError:
290
- print("[INFO] waiting for connection.", flush=True)
291
- pass
292
- raise RuntimeError("Failed to connect fastbot")
293
-
294
-
295
- def startFastbotService(options: Options) -> threading.Thread:
296
- shell_command = [
297
- "CLASSPATH=/sdcard/monkeyq.jar:/sdcard/framework.jar:/sdcard/fastbot-thirdpart.jar",
298
- "exec", "app_process",
299
- "/system/bin", "com.android.commands.monkey.Monkey",
300
- "-p", *options.packageNames,
301
- "--agent-u2" if options.agent == "u2" else "--agent",
302
- "reuseq",
303
- "--running-minutes", f"{options.running_mins}",
304
- "--throttle", f"{options.throttle}",
305
- "--bugreport",
306
- ]
307
-
308
- if options.profile_period:
309
- shell_command += ["--profile-period", f"{options.profile_period}"]
310
-
311
- shell_command += ["-v", "-v", "-v"]
312
-
313
- full_cmd = ["adb"] + (["-s", options.serial] if options.serial else []) + ["shell"] + shell_command
314
-
315
- outfile = open(LOGFILE, "w", encoding="utf-8", buffering=1)
316
-
317
- print("[INFO] Options info: {}".format(asdict(options)), flush=True)
318
- print("[INFO] Launching fastbot with shell command:\n{}".format(" ".join(full_cmd)), flush=True)
319
- print("[INFO] Fastbot log will be saved to {}".format(outfile.name), flush=True)
320
-
321
- # process handler
322
- proc = subprocess.Popen(full_cmd, stdout=outfile, stderr=outfile)
323
- t = threading.Thread(target=close_on_exit, args=(proc, outfile), daemon=True)
324
- t.start()
325
-
326
- return t
327
-
328
-
329
- def close_on_exit(proc: subprocess.Popen, f: IO):
330
- proc.wait()
331
- f.close()
332
-
333
-
334
236
  class KeaTestRunner(TextTestRunner):
335
237
 
336
238
  resultclass: JsonResult
337
239
  allProperties: PropertyStore
338
240
  options: Options = None
339
241
  _block_funcs: Dict[Literal["widgets", "trees"], List[Callable]] = None
340
- # _block_trees_funcs = None
341
242
 
342
243
  @classmethod
343
244
  def setOptions(cls, options: Options):
344
245
  if not isinstance(options.packageNames, list) and len(options.packageNames) > 0:
345
246
  raise ValueError("packageNames should be given in a list.")
346
247
  if options.Driver is not None and options.agent == "native":
347
- print("[Warning] Can not use any Driver when runing native mode.", flush=True)
248
+ logger.warning("[Warning] Can not use any Driver when runing native mode.")
348
249
  options.Driver = None
349
250
  cls.options = options
350
251
 
@@ -354,8 +255,8 @@ class KeaTestRunner(TextTestRunner):
354
255
  global LOGFILE, RESFILE
355
256
  LOGFILE = output_dir / Path(LOGFILE)
356
257
  RESFILE = output_dir / Path(RESFILE)
357
- logger.debug(f"Log file: {LOGFILE}")
358
- logger.debug(f"Result file: {RESFILE}")
258
+ logger.info(f"Log file: {LOGFILE}")
259
+ logger.info(f"Result file: {RESFILE}")
359
260
 
360
261
  def run(self, test):
361
262
 
@@ -363,7 +264,7 @@ class KeaTestRunner(TextTestRunner):
363
264
  self.collectAllProperties(test)
364
265
 
365
266
  if len(self.allProperties) == 0:
366
- print("[Warning] No property has been found.", flush=True)
267
+ logger.warning("[Warning] No property has been found.")
367
268
 
368
269
  self._setOuputDir()
369
270
 
@@ -392,16 +293,17 @@ class KeaTestRunner(TextTestRunner):
392
293
  message=r"Please use assert\w+ instead.",
393
294
  )
394
295
 
395
- t = activateFastbot(options=self.options)
296
+ fb = FastbotManager(self.options, LOGFILE)
297
+ fb.start()
298
+
396
299
  log_watcher = LogWatcher(LOGFILE)
397
- if self.options.agent == "native":
398
- t.join()
399
- else:
300
+
301
+ if self.options.agent == "u2":
400
302
  # initialize the result.json file
401
303
  result.flushResult(outfile=RESFILE)
402
304
  # setUp for the u2 driver
403
305
  self.scriptDriver = self.options.Driver.getScriptDriver()
404
- check_alive(port=self.scriptDriver.lport)
306
+ fb.check_alive(port=self.scriptDriver.lport)
405
307
  self._init()
406
308
 
407
309
  resultSyncer = ResultSyncer(self.device_output_dir, self.options.output_dir)
@@ -412,21 +314,21 @@ class KeaTestRunner(TextTestRunner):
412
314
  while self.stepsCount < self.options.maxStep:
413
315
 
414
316
  self.stepsCount += 1
415
- print("[INFO] Sending monkeyEvent {}".format(
317
+ logger.info("Sending monkeyEvent {}".format(
416
318
  f"({self.stepsCount} / {self.options.maxStep})" if self.options.maxStep != float("inf")
417
319
  else f"({self.stepsCount})"
418
320
  )
419
- , flush=True)
321
+ )
420
322
 
421
323
  try:
422
324
  xml_raw = self.stepMonkey()
423
325
  propsSatisfiedPrecond = self.getValidProperties(xml_raw, result)
424
326
  except requests.ConnectionError:
425
- print(
426
- "[INFO] Exploration times up (--running-minutes)."
427
- , flush=True)
428
- end_by_remote = True
429
- break
327
+ if fb.get_return_code() == 0:
328
+ logger.info("[INFO] Exploration times up (--running-minutes).")
329
+ end_by_remote = True
330
+ break
331
+ raise RuntimeError("Fastbot Aborted.")
430
332
 
431
333
  if self.options.profile_period and self.stepsCount % self.options.profile_period == 0:
432
334
  resultSyncer.sync_event.set()
@@ -464,17 +366,18 @@ class KeaTestRunner(TextTestRunner):
464
366
  finally:
465
367
  result.printErrors()
466
368
 
369
+ result.updateExectedInfo()
467
370
  self._logScript(result.lastExecutedInfo)
468
371
  result.flushResult(outfile=RESFILE)
469
372
 
470
373
  if not end_by_remote:
471
374
  self.stopMonkey()
472
375
  result.flushResult(outfile=RESFILE)
473
- resultSyncer.close()
474
-
376
+ resultSyncer.close()
377
+
378
+ fb.join()
475
379
  print(f"Finish sending monkey events.", flush=True)
476
380
  log_watcher.close()
477
-
478
381
 
479
382
  # Source code from unittest Runner
480
383
  # process the result
@@ -619,7 +522,7 @@ class KeaTestRunner(TextTestRunner):
619
522
  """
620
523
  def tearDown(self): ...
621
524
  testCase.tearDown = types.MethodType(tearDown, testCase)
622
-
525
+
623
526
  def iter_tests(suite):
624
527
  for test in suite:
625
528
  if isinstance(test, TestSuite):
@@ -749,6 +652,12 @@ class KeaTestRunner(TextTestRunner):
749
652
  def __del__(self):
750
653
  """tearDown method. Cleanup the env.
751
654
  """
655
+ try:
656
+ logger.debug("Generating test bug report")
657
+ report_generator = BugReportGenerator(self.options.output_dir)
658
+ report_generator.generate_report()
659
+ except Exception as e:
660
+ logger.error(f"Error generating bug report: {e}", flush=True)
752
661
  try:
753
662
  self.stopMonkey()
754
663
  except Exception as e:
kea2/logWatcher.py CHANGED
@@ -2,6 +2,10 @@ import re
2
2
  import os
3
3
  import threading
4
4
  import time
5
+ from .utils import getLogger
6
+
7
+
8
+ logger = getLogger(__name__)
5
9
 
6
10
 
7
11
  PATTERN_EXCEPTION = re.compile(r"\[Fastbot\].+Internal\serror\n([\s\S]*)")
@@ -16,7 +20,7 @@ def thread_excepthook(args):
16
20
 
17
21
  class LogWatcher:
18
22
 
19
- def watcher(self, poll_interval=1):
23
+ def watcher(self, poll_interval=0.5):
20
24
  self.buffer = ""
21
25
  self.last_pos = 0
22
26
 
@@ -28,7 +32,6 @@ class LogWatcher:
28
32
  self.read_log()
29
33
 
30
34
  def read_log(self):
31
- time.sleep(0.02)
32
35
  with open(self.log_file, 'r', encoding='utf-8') as f:
33
36
  f.seek(self.last_pos)
34
37
  new_data = f.read()
@@ -60,15 +63,19 @@ class LogWatcher:
60
63
  , flush=True)
61
64
 
62
65
  def __init__(self, log_file):
66
+ logger.info(f"Watching log: {log_file}")
63
67
  self.log_file = log_file
64
68
  self.end_flag = False
65
69
 
66
70
  threading.excepthook = thread_excepthook
67
- t = threading.Thread(target=self.watcher, daemon=True)
68
- t.start()
71
+ self.t = threading.Thread(target=self.watcher, daemon=True)
72
+ self.t.start()
69
73
 
70
74
  def close(self):
75
+ logger.info("Close: LogWatcher")
71
76
  self.end_flag = True
77
+ if self.t:
78
+ self.t.join()
72
79
 
73
80
 
74
81
  if __name__ == "__main__":