Kea2-python 0.1.3__py3-none-any.whl → 0.2.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/fastbotManager.py CHANGED
@@ -1,13 +1,17 @@
1
+ from retry import retry
2
+ from retry.api import retry_call
1
3
  from dataclasses import asdict
2
- import subprocess
3
- import threading
4
4
  import requests
5
5
  from time import sleep
6
- from .adbUtils import push_file
6
+
7
+
8
+ from uiautomator2.core import HTTPResponse, _http_request
9
+ from kea2.adbUtils import ADBDevice, ADBStreamShell_V2
7
10
  from pathlib import Path
8
- from .utils import getLogger
11
+ from kea2.utils import getLogger
12
+
9
13
 
10
- from typing import IO, TYPE_CHECKING
14
+ from typing import IO, TYPE_CHECKING, Dict
11
15
  if TYPE_CHECKING:
12
16
  from .keaUtils import Options
13
17
 
@@ -21,56 +25,49 @@ class FastbotManager:
21
25
  self.log_file: str = log_file
22
26
  self.port = None
23
27
  self.thread = None
28
+ self._device_output_dir = None
29
+ ADBDevice.setDevice(options.serial, options.transport_id)
30
+ self.dev = ADBDevice()
24
31
 
25
-
26
- def _activateFastbot(self) -> threading.Thread:
32
+ def _activateFastbot(self) -> ADBStreamShell_V2:
27
33
  """
28
34
  activate fastbot.
29
35
  :params: options: the running setting for fastbot
30
36
  :params: port: the listening port for script driver
31
37
  :return: the fastbot daemon thread
32
38
  """
33
- options = self.options
34
39
  cur_dir = Path(__file__).parent
35
- push_file(
40
+ self.dev.sync.push(
36
41
  Path.joinpath(cur_dir, "assets/monkeyq.jar"),
37
- "/sdcard/monkeyq.jar",
38
- device=options.serial
42
+ "/sdcard/monkeyq.jar"
39
43
  )
40
- push_file(
44
+ self.dev.sync.push(
41
45
  Path.joinpath(cur_dir, "assets/fastbot-thirdpart.jar"),
42
46
  "/sdcard/fastbot-thirdpart.jar",
43
- device=options.serial,
44
47
  )
45
- push_file(
48
+ self.dev.sync.push(
46
49
  Path.joinpath(cur_dir, "assets/kea2-thirdpart.jar"),
47
50
  "/sdcard/kea2-thirdpart.jar",
48
- device=options.serial,
49
51
  )
50
- push_file(
51
- Path.joinpath(cur_dir, "assets/framework.jar"),
52
+ self.dev.sync.push(
53
+ Path.joinpath(cur_dir, "assets/framework.jar"),
52
54
  "/sdcard/framework.jar",
53
- device=options.serial
54
55
  )
55
- push_file(
56
- Path.joinpath(cur_dir, "assets/fastbot_libs/arm64-v8a"),
57
- "/data/local/tmp",
58
- device=options.serial
56
+ self.dev.sync.push(
57
+ Path.joinpath(cur_dir, "assets/fastbot_libs/arm64-v8a/libfastbot_native.so"),
58
+ "/data/local/tmp/arm64-v8a/libfastbot_native.so",
59
59
  )
60
- push_file(
61
- Path.joinpath(cur_dir, "assets/fastbot_libs/armeabi-v7a"),
62
- "/data/local/tmp",
63
- device=options.serial
60
+ self.dev.sync.push(
61
+ Path.joinpath(cur_dir, "assets/fastbot_libs/armeabi-v7a/libfastbot_native.so"),
62
+ "/data/local/tmp/armeabi-v7a/libfastbot_native.so",
64
63
  )
65
- push_file(
66
- Path.joinpath(cur_dir, "assets/fastbot_libs/x86"),
67
- "/data/local/tmp",
68
- device=options.serial
64
+ self.dev.sync.push(
65
+ Path.joinpath(cur_dir, "assets/fastbot_libs/x86/libfastbot_native.so"),
66
+ "/data/local/tmp/x86/libfastbot_native.so",
69
67
  )
70
- push_file(
71
- Path.joinpath(cur_dir, "assets/fastbot_libs/x86_64"),
72
- "/data/local/tmp",
73
- device=options.serial
68
+ self.dev.sync.push(
69
+ Path.joinpath(cur_dir, "assets/fastbot_libs/x86_64/libfastbot_native.so"),
70
+ "/data/local/tmp/x86_64/libfastbot_native.so",
74
71
  )
75
72
 
76
73
  t = self._startFastbotService()
@@ -79,29 +76,84 @@ class FastbotManager:
79
76
  return t
80
77
 
81
78
 
82
- def check_alive(self, port):
79
+ def check_alive(self):
83
80
  """
84
81
  check if the script driver and proxy server are alive.
85
82
  """
86
- for _ in range(10):
87
- sleep(2)
88
- try:
89
- requests.get(f"http://localhost:{port}/ping")
90
- return
91
- except requests.ConnectionError:
92
- logger.info("waiting for connection.")
93
- pass
94
- raise RuntimeError("Failed to connect fastbot")
95
-
96
-
97
- def _startFastbotService(self) -> threading.Thread:
83
+ def _check_alive_request():
84
+ _http_request(dev=self.dev, device_port=8090, method="GET", path="/ping")
85
+
86
+ try:
87
+ retry_call(_check_alive_request, tries=10, delay=2)
88
+ except requests.ConnectionError:
89
+ raise RuntimeError("Failed to connect fastbot")
90
+
91
+ def request(self, method: str, path: str, data: Dict=None, timeout: int=10) -> HTTPResponse:
92
+ return _http_request(self.dev, 8090, method, path, data, timeout)
93
+
94
+ @retry(Exception, tries=2, delay=2)
95
+ def init(self, options: "Options", stamp):
96
+ post_data = {
97
+ "takeScreenshots": options.take_screenshots,
98
+ "Stamp": stamp,
99
+ "deviceOutputRoot": options.device_output_root,
100
+ }
101
+ r = _http_request(
102
+ self.dev,
103
+ device_port=8090,
104
+ method="POST",
105
+ path="/init",
106
+ data=post_data
107
+ )
108
+ print(f"[INFO] Init fastbot: {post_data}", flush=True)
109
+ import re
110
+ self._device_output_dir = re.match(r"outputDir:(.+)", r.text).group(1)
111
+ print(f"[INFO] Fastbot initiated. outputDir: {r.text}", flush=True)
112
+
113
+ @retry(Exception, tries=2, delay=2)
114
+ def stepMonkey(self, monkeyStepInfo):
115
+ r = self.request(
116
+ method="POST",
117
+ path="/stepMonkey",
118
+ data=monkeyStepInfo
119
+ )
120
+ return r.json()["result"]
121
+
122
+ @retry(Exception, tries=2, delay=2)
123
+ def stopMonkey(self):
124
+ """
125
+ send a stop monkey request to the server.
126
+ """
127
+ r = self.request(
128
+ method="GET",
129
+ path="/stopMonkey",
130
+ )
131
+
132
+ print(f"[Server INFO] {r.text}", flush=True)
133
+
134
+ @retry(Exception, tries=2, delay=2)
135
+ def logScript(self, execution_info: Dict):
136
+ r = self.request(
137
+ method="POST",
138
+ path="/logScript",
139
+ data=execution_info
140
+ )
141
+ res = r.text
142
+ if res != "OK":
143
+ print(f"[ERROR] Error when logging script: {execution_info}", flush=True)
144
+
145
+ @property
146
+ def device_output_dir(self):
147
+ return self._device_output_dir
148
+
149
+ def _startFastbotService(self) -> ADBStreamShell_V2:
98
150
  shell_command = [
99
151
  "CLASSPATH="
100
152
  "/sdcard/monkeyq.jar:"
101
153
  "/sdcard/framework.jar:"
102
154
  "/sdcard/fastbot-thirdpart.jar:"
103
155
  "/sdcard/kea2-thirdpart.jar",
104
-
156
+
105
157
  "exec", "app_process",
106
158
  "/system/bin", "com.android.commands.monkey.Monkey",
107
159
  "-p", *self.options.packageNames,
@@ -119,37 +171,36 @@ class FastbotManager:
119
171
 
120
172
  full_cmd = ["adb"] + (["-s", self.options.serial] if self.options.serial else []) + ["shell"] + shell_command
121
173
 
174
+
122
175
  outfile = open(self.log_file, "w", encoding="utf-8", buffering=1)
123
176
 
124
177
  logger.info("Options info: {}".format(asdict(self.options)))
125
178
  logger.info("Launching fastbot with shell command:\n{}".format(" ".join(full_cmd)))
126
179
  logger.info("Fastbot log will be saved to {}".format(outfile.name))
127
180
 
128
- # process handler
129
- proc = subprocess.Popen(full_cmd, stdout=outfile, stderr=outfile)
130
- t = threading.Thread(target=self.close_on_exit, args=(proc, outfile), daemon=True)
131
- t.start()
132
-
181
+ t = self.dev.stream_shell(shell_command, stdout=outfile, stderr=outfile)
133
182
  return t
134
183
 
135
- def close_on_exit(self, proc: subprocess.Popen, f: IO):
184
+ def close_on_exit(self, proc: ADBStreamShell_V2, f: IO):
136
185
  self.return_code = proc.wait()
137
186
  f.close()
138
187
  if self.return_code != 0:
139
188
  raise RuntimeError(f"Fastbot Error: Terminated with [code {self.return_code}] See {self.log_file} for details.")
140
189
 
141
190
  def get_return_code(self):
142
- if self.thread:
191
+ if self.thread.is_running():
143
192
  logger.info("Waiting for Fastbot to exit.")
144
- self.thread.join()
145
- return self.return_code
193
+ return self.thread.wait()
194
+ return self.thread.poll()
146
195
 
147
196
  def start(self):
197
+ # kill the fastbot process if runing.
198
+ self.dev.kill_proc("com.android.commands.monkey")
148
199
  self.thread = self._activateFastbot()
149
200
 
150
201
  def join(self):
151
- if self.thread:
152
- self.thread.join()
202
+ self.thread.join()
203
+
153
204
 
154
205
 
155
206
 
kea2/keaUtils.py CHANGED
@@ -2,6 +2,7 @@ import json
2
2
  import os
3
3
  from pathlib import Path
4
4
  import traceback
5
+ import time
5
6
  from typing import Callable, Any, Dict, List, Literal, NewType, TypedDict, Union
6
7
  from unittest import TextTestRunner, registerResult, TestSuite, TestCase, TextTestResult
7
8
  import random
@@ -110,6 +111,8 @@ class Options:
110
111
  packageNames: List[str]
111
112
  # target device
112
113
  serial: str = None
114
+ # target device with transport_id
115
+ transport_id: str = None
113
116
  # test agent. "native" for stage 1 and "u2" for stage 1~3
114
117
  agent: Literal["u2", "native"] = "u2"
115
118
  # max step in exploration (availble in stage 2~3)
@@ -139,8 +142,13 @@ class Options:
139
142
  def __post_init__(self):
140
143
  import logging
141
144
  logging.basicConfig(level=logging.DEBUG if self.debug else logging.INFO)
142
- if self.serial and self.Driver:
143
- self.Driver.setDeviceSerial(self.serial)
145
+ if self.Driver:
146
+ target_device = dict()
147
+ if self.serial:
148
+ target_device["serial"] = self.serial
149
+ if self.transport_id:
150
+ target_device["transport_id"] = self.transport_id
151
+ self.Driver.setDevice(target_device)
144
152
  global LOGFILE, RESFILE, STAMP
145
153
  if self.log_stamp:
146
154
  illegal_chars = ['/', '\\', ':', '*', '?', '"', '<', '>', '|', '\n', '\r', '\t', '\0']
@@ -313,10 +321,11 @@ class KeaTestRunner(TextTestRunner):
313
321
  result.flushResult(outfile=RESFILE)
314
322
  # setUp for the u2 driver
315
323
  self.scriptDriver = self.options.Driver.getScriptDriver()
316
- fb.check_alive(port=self.scriptDriver.lport)
317
- self._init()
324
+ fb.check_alive()
325
+
326
+ fb.init(options=self.options, stamp=STAMP)
318
327
 
319
- resultSyncer = ResultSyncer(self.device_output_dir, self.options.output_dir)
328
+ resultSyncer = ResultSyncer(fb.device_output_dir, self.options)
320
329
  resultSyncer.run()
321
330
 
322
331
  end_by_remote = False
@@ -331,7 +340,7 @@ class KeaTestRunner(TextTestRunner):
331
340
  )
332
341
 
333
342
  try:
334
- xml_raw = self.stepMonkey()
343
+ xml_raw = fb.stepMonkey(self._monkeyStepInfo)
335
344
  propsSatisfiedPrecond = self.getValidProperties(xml_raw, result)
336
345
  except requests.ConnectionError:
337
346
  logger.info("Connection refused by remote.")
@@ -369,18 +378,18 @@ class KeaTestRunner(TextTestRunner):
369
378
  print("execute property %s." % execPropName, flush=True)
370
379
 
371
380
  result.addExcuted(test)
372
- self._logScript(result.lastExecutedInfo)
381
+ fb.logScript(result.lastExecutedInfo)
373
382
  try:
374
383
  test(result)
375
384
  finally:
376
385
  result.printErrors()
377
386
 
378
387
  result.updateExectedInfo()
379
- self._logScript(result.lastExecutedInfo)
388
+ fb.logScript(result.lastExecutedInfo)
380
389
  result.flushResult(outfile=RESFILE)
381
390
 
382
391
  if not end_by_remote:
383
- self.stopMonkey()
392
+ fb.stopMonkey()
384
393
  result.flushResult(outfile=RESFILE)
385
394
  resultSyncer.close()
386
395
 
@@ -424,40 +433,46 @@ class KeaTestRunner(TextTestRunner):
424
433
  self.stream.flush()
425
434
  return result
426
435
 
427
- def stepMonkey(self) -> str:
428
- """
429
- send a step monkey request to the server and get the xml string.
430
- """
436
+ # def stepMonkey(self) -> str:
437
+ # """
438
+ # send a step monkey request to the server and get the xml string.
439
+ # """
440
+ # block_dict = self._getBlockedWidgets()
441
+ # block_widgets: List[str] = block_dict['widgets']
442
+ # block_trees: List[str] = block_dict['trees']
443
+ # URL = f"http://localhost:{self.scriptDriver.lport}/stepMonkey"
444
+ # logger.debug(f"Sending request: {URL}")
445
+ # logger.debug(f"Blocking widgets: {block_widgets}")
446
+ # logger.debug(f"Blocking trees: {block_trees}")
447
+ # r = requests.post(
448
+ # url=URL,
449
+ # json={
450
+ # "steps_count": self.stepsCount,
451
+ # "block_widgets": block_widgets,
452
+ # "block_trees": block_trees
453
+ # }
454
+ # )
455
+
456
+ # res = json.loads(r.content)
457
+ # xml_raw = res["result"]
458
+ # return xml_raw
459
+
460
+ @property
461
+ def _monkeyStepInfo(self):
462
+ r = self._get_block_widgets()
463
+ r["steps_count"] = self.stepsCount
464
+ return r
465
+
466
+ def _get_block_widgets(self):
431
467
  block_dict = self._getBlockedWidgets()
432
468
  block_widgets: List[str] = block_dict['widgets']
433
469
  block_trees: List[str] = block_dict['trees']
434
- URL = f"http://localhost:{self.scriptDriver.lport}/stepMonkey"
435
- logger.debug(f"Sending request: {URL}")
436
470
  logger.debug(f"Blocking widgets: {block_widgets}")
437
471
  logger.debug(f"Blocking trees: {block_trees}")
438
- r = requests.post(
439
- url=URL,
440
- json={
441
- "steps_count": self.stepsCount,
442
- "block_widgets": block_widgets,
443
- "block_trees": block_trees
444
- }
445
- )
446
-
447
- res = json.loads(r.content)
448
- xml_raw = res["result"]
449
- return xml_raw
450
-
451
- def stopMonkey(self) -> str:
452
- """
453
- send a stop monkey request to the server and get the xml string.
454
- """
455
- URL = f"http://localhost:{self.scriptDriver.lport}/stopMonkey"
456
- logger.debug(f"Sending request: {URL}")
457
- r = requests.get(URL)
458
-
459
- res = r.content.decode(encoding="utf-8")
460
- print(f"[Server INFO] {res}", flush=True)
472
+ return {
473
+ "block_widgets": block_widgets,
474
+ "block_trees": block_trees
475
+ }
461
476
 
462
477
  def getValidProperties(self, xml_raw: str, result: JsonResult) -> PropertyStore:
463
478
 
@@ -490,40 +505,13 @@ class KeaTestRunner(TextTestRunner):
490
505
  print(f"{getFullPropName(test)} has reached its max_tries. Skip.", flush=True)
491
506
  continue
492
507
  validProps[propName] = test
493
-
508
+
494
509
  print(f"{len(validProps)} precond satisfied.", flush=True)
495
510
  if len(validProps) > 0:
496
511
  print("[INFO] Valid properties:",flush=True)
497
512
  print("\n".join([f' - {getFullPropName(p)}' for p in validProps.values()]), flush=True)
498
513
  return validProps
499
514
 
500
- def _logScript(self, execution_info:Dict):
501
- URL = f"http://localhost:{self.scriptDriver.lport}/logScript"
502
- r = requests.post(
503
- url=URL,
504
- json=execution_info
505
- )
506
- res = r.content.decode(encoding="utf-8")
507
- if res != "OK":
508
- print(f"[ERROR] Error when logging script: {execution_info}", flush=True)
509
-
510
- def _init(self):
511
- URL = f"http://localhost:{self.scriptDriver.lport}/init"
512
- data = {
513
- "takeScreenshots": self.options.take_screenshots,
514
- "Stamp": STAMP,
515
- "deviceOutputRoot": self.options.device_output_root,
516
- }
517
- print(f"[INFO] Init fastbot: {data}", flush=True)
518
- r = requests.post(
519
- url=URL,
520
- json=data
521
- )
522
- res = r.content.decode(encoding="utf-8")
523
- import re
524
- self.device_output_dir = re.match(r"outputDir:(.+)", res).group(1)
525
- print(f"[INFO] Fastbot initiated. outputDir: {res}", flush=True)
526
-
527
515
  def collectAllProperties(self, test: TestSuite):
528
516
  """collect all the properties to prepare for PBT
529
517
  """
@@ -679,17 +667,19 @@ class KeaTestRunner(TextTestRunner):
679
667
  def __del__(self):
680
668
  """tearDown method. Cleanup the env.
681
669
  """
682
- try:
683
- self.stopMonkey()
684
- except Exception as e:
685
- pass
686
-
687
670
  if self.options.Driver:
688
671
  self.options.Driver.tearDown()
689
672
 
690
673
  try:
674
+ start_time = time.time()
691
675
  logger.info("Generating bug report")
692
676
  report_generator = BugReportGenerator(self.options.output_dir)
693
677
  report_generator.generate_report()
678
+
679
+ end_time = time.time()
680
+ generation_time = end_time - start_time
681
+
682
+ logger.info(f"Bug report generation completed in {generation_time:.2f} seconds")
683
+
694
684
  except Exception as e:
695
685
  logger.error(f"Error generating bug report: {e}", flush=True)
kea2/kea_launcher.py CHANGED
@@ -9,10 +9,22 @@ def _set_runner_parser(subparsers: "argparse._SubParsersAction[argparse.Argument
9
9
  "-s",
10
10
  "--serial",
11
11
  dest="serial",
12
+ required=False,
13
+ default=None,
12
14
  type=str,
13
15
  help="The serial of your device. Can be found with `adb devices`",
14
16
  )
15
17
 
18
+ parser.add_argument(
19
+ "-t",
20
+ "--transport-id",
21
+ dest="transport_id",
22
+ required=False,
23
+ default=None,
24
+ type=str,
25
+ help="transport-id of your device, can be found with `adb devices -l`",
26
+ )
27
+
16
28
  parser.add_argument(
17
29
  "-p",
18
30
  "--packages",
@@ -128,6 +140,8 @@ def driver_info_logger(args):
128
140
  print("[INFO] Driver Settings:", flush=True)
129
141
  if args.serial:
130
142
  print(" serial:", args.serial, flush=True)
143
+ if args.transport_id:
144
+ print(" transport_id:", args.transport_id, flush=True)
131
145
  if args.package_names:
132
146
  print(" package_names:", args.package_names, flush=True)
133
147
  if args.agent:
@@ -174,6 +188,7 @@ def run(args=None):
174
188
  Driver=U2Driver,
175
189
  packageNames=args.package_names,
176
190
  serial=args.serial,
191
+ transport_id=args.transport_id,
177
192
  running_mins=args.running_minutes,
178
193
  maxStep=args.max_step,
179
194
  throttle=args.throttle_ms,
kea2/logWatcher.py CHANGED
@@ -2,7 +2,8 @@ import re
2
2
  import os
3
3
  import threading
4
4
  import time
5
- from .utils import getLogger
5
+ from typing import IO
6
+ from kea2.utils import getLogger
6
7
 
7
8
 
8
9
  logger = getLogger(__name__)
@@ -20,47 +21,43 @@ def thread_excepthook(args):
20
21
 
21
22
  class LogWatcher:
22
23
 
23
- def watcher(self, poll_interval=0.5):
24
- self.buffer = ""
24
+ def watcher(self, poll_interval=3):
25
25
  self.last_pos = 0
26
26
 
27
- while not self.end_flag:
28
- self.read_log()
29
- time.sleep(poll_interval)
27
+ with open(self.log_file, "r") as fp:
28
+ while not self.end_flag:
29
+ self.read_log(fp)
30
+ time.sleep(poll_interval)
31
+
32
+ time.sleep(0.2)
33
+ self.read_log(fp)
30
34
 
31
- time.sleep(0.2)
32
- self.read_log()
33
-
34
- def read_log(self):
35
- with open(self.log_file, 'r', encoding='utf-8') as f:
36
- f.seek(self.last_pos)
37
- new_data = f.read()
38
- self.last_pos = f.tell()
39
-
40
- if new_data:
41
- self.buffer += new_data
42
- self.parse_log()
43
-
44
- def parse_log(self):
45
- buffer = self.buffer
46
- exception_match = PATTERN_EXCEPTION.search(buffer)
35
+ def read_log(self, f: IO):
36
+ f.seek(self.last_pos)
37
+ buffer = f.read()
38
+ self.last_pos = f.tell()
39
+
40
+ self.parse_log(buffer)
41
+
42
+ def parse_log(self, content):
43
+ exception_match = PATTERN_EXCEPTION.search(content)
47
44
  if exception_match:
48
45
  exception_body = exception_match.group(1).strip()
49
46
  if exception_body:
50
47
  raise RuntimeError(
51
- "[Error] Execption while running fastbot:\n" +
48
+ "[Error] Fatal Execption while running fastbot:\n" +
52
49
  exception_body +
53
50
  "\nSee fastbot.log for details."
54
51
  )
55
- if self.end_flag:
56
- statistic_match = PATTERN_STATISTIC.search(buffer)
57
- if statistic_match:
58
- statistic_body = statistic_match.group(1).strip()
59
- if statistic_body:
60
- print(
61
- "[INFO] Fastbot exit:\n" +
62
- statistic_body
63
- , flush=True)
52
+
53
+ statistic_match = PATTERN_STATISTIC.search(content)
54
+ if statistic_match:
55
+ statistic_body = statistic_match.group(1).strip()
56
+ if statistic_body:
57
+ print(
58
+ "[INFO] Fastbot exit:\n" +
59
+ statistic_body
60
+ , flush=True)
64
61
 
65
62
  def __init__(self, log_file):
66
63
  logger.info(f"Watching log: {log_file}")
@@ -79,4 +76,4 @@ class LogWatcher:
79
76
 
80
77
 
81
78
  if __name__ == "__main__":
82
- LogWatcher("fastbot.log")
79
+ LogWatcher("/Users/atria/Desktop/coding/Kea2/output/res_2025062510_0420056539/fastbot_2025062510_0420056539.log")
kea2/resultSyncer.py CHANGED
@@ -1,19 +1,26 @@
1
+ from pathlib import Path
1
2
  import threading
2
- from .adbUtils import adb_shell, pull_file
3
+ from .adbUtils import ADBDevice
3
4
  from .utils import getLogger
5
+ from typing import TYPE_CHECKING
6
+ if TYPE_CHECKING:
7
+ from .keaUtils import Options
4
8
 
5
9
  logger = getLogger(__name__)
6
10
 
7
11
 
8
12
  class ResultSyncer:
9
13
 
10
- def __init__(self, device_output_dir, output_dir):
14
+ def __init__(self, device_output_dir, options: "Options"):
11
15
  self.device_output_dir = device_output_dir
12
- self.output_dir = output_dir
16
+ self.output_dir = Path(options.output_dir) / Path(device_output_dir).name
13
17
  self.running = False
14
18
  self.thread = None
15
19
  self.sync_event = threading.Event()
16
20
 
21
+ ADBDevice.setDevice(serial=options.serial, transport_id=options.transport_id)
22
+ self.dev = ADBDevice()
23
+
17
24
  def run(self):
18
25
  """Start a background thread to sync device data when triggered"""
19
26
  self.running = True
@@ -37,7 +44,8 @@ class ResultSyncer:
37
44
  try:
38
45
  logger.debug(f"Removing device output directory: {self.device_output_dir}")
39
46
  remove_device_dir = ["rm", "-rf", self.device_output_dir]
40
- adb_shell(remove_device_dir)
47
+ # adb_shell(remove_device_dir)
48
+ self.dev.shell(remove_device_dir)
41
49
  except Exception as e:
42
50
  logger.error(f"Error removing device output directory: {e}", flush=True)
43
51
 
@@ -48,9 +56,11 @@ class ResultSyncer:
48
56
  try:
49
57
  logger.debug("Syncing data")
50
58
 
51
- pull_file(self.device_output_dir, str(self.output_dir))
59
+ self.dev.sync.pull_dir(self.device_output_dir, self.output_dir, exist_ok=True)
60
+ # pull_file(self.device_output_dir, str(self.output_dir))
52
61
 
53
- remove_pulled_screenshots = ["find", self.device_output_dir, "-name", "\"*.png\"", "-delete"]
54
- adb_shell(remove_pulled_screenshots)
62
+ remove_pulled_screenshots = ["find", self.device_output_dir, "-name", '"*.png"', "-delete"]
63
+ self.dev.shell(remove_pulled_screenshots)
64
+ # adb_shell(remove_pulled_screenshots)
55
65
  except Exception as e:
56
- logger.error(f"Error in data sync: {e}", flush=True)
66
+ logger.error(f"Error in data sync: {e}")