Kea2-python 0.1.3__py3-none-any.whl → 0.2.0__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/absDriver.py +1 -1
- kea2/adbUtils.py +273 -20
- kea2/bug_report_generator.py +333 -321
- kea2/fastbotManager.py +105 -47
- kea2/keaUtils.py +61 -71
- kea2/kea_launcher.py +15 -0
- kea2/resultSyncer.py +18 -8
- kea2/templates/bug_report_template.html +9 -50
- kea2/u2Driver.py +27 -30
- kea2/utils.py +1 -0
- {kea2_python-0.1.3.dist-info → kea2_python-0.2.0.dist-info}/METADATA +42 -32
- {kea2_python-0.1.3.dist-info → kea2_python-0.2.0.dist-info}/RECORD +16 -16
- {kea2_python-0.1.3.dist-info → kea2_python-0.2.0.dist-info}/WHEEL +0 -0
- {kea2_python-0.1.3.dist-info → kea2_python-0.2.0.dist-info}/entry_points.txt +0 -0
- {kea2_python-0.1.3.dist-info → kea2_python-0.2.0.dist-info}/licenses/LICENSE +0 -0
- {kea2_python-0.1.3.dist-info → kea2_python-0.2.0.dist-info}/top_level.txt +0 -0
kea2/fastbotManager.py
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
|
-
from dataclasses import asdict
|
|
2
1
|
import subprocess
|
|
3
2
|
import threading
|
|
3
|
+
from dataclasses import asdict
|
|
4
4
|
import requests
|
|
5
5
|
from time import sleep
|
|
6
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,88 @@ class FastbotManager:
|
|
|
79
76
|
return t
|
|
80
77
|
|
|
81
78
|
|
|
82
|
-
def check_alive(self
|
|
79
|
+
def check_alive(self):
|
|
83
80
|
"""
|
|
84
81
|
check if the script driver and proxy server are alive.
|
|
85
82
|
"""
|
|
83
|
+
|
|
86
84
|
for _ in range(10):
|
|
87
85
|
sleep(2)
|
|
88
86
|
try:
|
|
89
|
-
|
|
87
|
+
_http_request(
|
|
88
|
+
dev=self.dev,
|
|
89
|
+
device_port=8090,
|
|
90
|
+
method="GET",
|
|
91
|
+
path="/ping",
|
|
92
|
+
)
|
|
90
93
|
return
|
|
91
94
|
except requests.ConnectionError:
|
|
92
95
|
logger.info("waiting for connection.")
|
|
93
96
|
pass
|
|
94
97
|
raise RuntimeError("Failed to connect fastbot")
|
|
95
98
|
|
|
99
|
+
def request(self, method: str, path: str, data: Dict=None, timeout: int=10) -> HTTPResponse:
|
|
100
|
+
return _http_request(self.dev, 8090, method, path, data, timeout)
|
|
101
|
+
|
|
102
|
+
def init(self, options: "Options", stamp):
|
|
103
|
+
post_data = {
|
|
104
|
+
"takeScreenshots": options.take_screenshots,
|
|
105
|
+
"Stamp": stamp,
|
|
106
|
+
"deviceOutputRoot": options.device_output_root,
|
|
107
|
+
}
|
|
108
|
+
r = _http_request(
|
|
109
|
+
self.dev,
|
|
110
|
+
device_port=8090,
|
|
111
|
+
method="POST",
|
|
112
|
+
path="/init",
|
|
113
|
+
data=post_data
|
|
114
|
+
)
|
|
115
|
+
print(f"[INFO] Init fastbot: {post_data}", flush=True)
|
|
116
|
+
import re
|
|
117
|
+
self._device_output_dir = re.match(r"outputDir:(.+)", r.text).group(1)
|
|
118
|
+
print(f"[INFO] Fastbot initiated. outputDir: {r.text}", flush=True)
|
|
119
|
+
|
|
120
|
+
def stepMonkey(self, monkeyStepInfo):
|
|
121
|
+
r = self.request(
|
|
122
|
+
method="POST",
|
|
123
|
+
path="/stepMonkey",
|
|
124
|
+
data=monkeyStepInfo
|
|
125
|
+
)
|
|
126
|
+
return r.json()["result"]
|
|
127
|
+
|
|
128
|
+
def stopMonkey(self):
|
|
129
|
+
"""
|
|
130
|
+
send a stop monkey request to the server.
|
|
131
|
+
"""
|
|
132
|
+
r = self.request(
|
|
133
|
+
method="GET",
|
|
134
|
+
path="/stopMonkey",
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
print(f"[Server INFO] {r.text}", flush=True)
|
|
138
|
+
|
|
139
|
+
def logScript(self, execution_info: Dict):
|
|
140
|
+
r = self.request(
|
|
141
|
+
method="POST",
|
|
142
|
+
path="/logScript",
|
|
143
|
+
data=execution_info
|
|
144
|
+
)
|
|
145
|
+
res = r.text
|
|
146
|
+
if res != "OK":
|
|
147
|
+
print(f"[ERROR] Error when logging script: {execution_info}", flush=True)
|
|
96
148
|
|
|
97
|
-
|
|
149
|
+
@property
|
|
150
|
+
def device_output_dir(self):
|
|
151
|
+
return self._device_output_dir
|
|
152
|
+
|
|
153
|
+
def _startFastbotService(self) -> ADBStreamShell_V2:
|
|
98
154
|
shell_command = [
|
|
99
155
|
"CLASSPATH="
|
|
100
156
|
"/sdcard/monkeyq.jar:"
|
|
101
157
|
"/sdcard/framework.jar:"
|
|
102
158
|
"/sdcard/fastbot-thirdpart.jar:"
|
|
103
159
|
"/sdcard/kea2-thirdpart.jar",
|
|
104
|
-
|
|
160
|
+
|
|
105
161
|
"exec", "app_process",
|
|
106
162
|
"/system/bin", "com.android.commands.monkey.Monkey",
|
|
107
163
|
"-p", *self.options.packageNames,
|
|
@@ -119,37 +175,39 @@ class FastbotManager:
|
|
|
119
175
|
|
|
120
176
|
full_cmd = ["adb"] + (["-s", self.options.serial] if self.options.serial else []) + ["shell"] + shell_command
|
|
121
177
|
|
|
178
|
+
|
|
122
179
|
outfile = open(self.log_file, "w", encoding="utf-8", buffering=1)
|
|
123
180
|
|
|
124
181
|
logger.info("Options info: {}".format(asdict(self.options)))
|
|
125
182
|
logger.info("Launching fastbot with shell command:\n{}".format(" ".join(full_cmd)))
|
|
126
183
|
logger.info("Fastbot log will be saved to {}".format(outfile.name))
|
|
127
184
|
|
|
185
|
+
# stream = self.dev.shell(shell_command, encoding="utf-8", stream=True, timeout=float("inf"))
|
|
128
186
|
# process handler
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
t.
|
|
132
|
-
|
|
187
|
+
t = self.dev.stream_shell(shell_command, stdout=outfile, stderr=outfile)
|
|
188
|
+
# proc = subprocess.Popen(full_cmd, stdout=outfile, stderr=outfile)
|
|
189
|
+
# t = threading.Thread(target=self.close_on_exit, args=(proc, outfile), daemon=True)
|
|
190
|
+
# t.start()
|
|
133
191
|
return t
|
|
134
192
|
|
|
135
|
-
def close_on_exit(self, proc:
|
|
193
|
+
def close_on_exit(self, proc: ADBStreamShell_V2, f: IO):
|
|
136
194
|
self.return_code = proc.wait()
|
|
137
195
|
f.close()
|
|
138
196
|
if self.return_code != 0:
|
|
139
197
|
raise RuntimeError(f"Fastbot Error: Terminated with [code {self.return_code}] See {self.log_file} for details.")
|
|
140
198
|
|
|
141
199
|
def get_return_code(self):
|
|
142
|
-
if self.thread:
|
|
200
|
+
if self.thread.is_running():
|
|
143
201
|
logger.info("Waiting for Fastbot to exit.")
|
|
144
|
-
self.thread.
|
|
145
|
-
return self.
|
|
202
|
+
return self.thread.wait()
|
|
203
|
+
return self.thread.poll()
|
|
146
204
|
|
|
147
205
|
def start(self):
|
|
148
206
|
self.thread = self._activateFastbot()
|
|
149
207
|
|
|
150
208
|
def join(self):
|
|
151
|
-
|
|
152
|
-
|
|
209
|
+
self.thread.join()
|
|
210
|
+
|
|
153
211
|
|
|
154
212
|
|
|
155
213
|
|
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.
|
|
143
|
-
|
|
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(
|
|
317
|
-
|
|
324
|
+
fb.check_alive()
|
|
325
|
+
|
|
326
|
+
fb.init(options=self.options, stamp=STAMP)
|
|
318
327
|
|
|
319
|
-
resultSyncer = ResultSyncer(
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
388
|
+
fb.logScript(result.lastExecutedInfo)
|
|
380
389
|
result.flushResult(outfile=RESFILE)
|
|
381
390
|
|
|
382
391
|
if not end_by_remote:
|
|
383
|
-
|
|
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
|
-
|
|
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
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
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/resultSyncer.py
CHANGED
|
@@ -1,19 +1,26 @@
|
|
|
1
|
+
from pathlib import Path
|
|
1
2
|
import threading
|
|
2
|
-
from .adbUtils import
|
|
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,
|
|
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
|
-
|
|
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", "
|
|
54
|
-
|
|
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}"
|
|
66
|
+
logger.error(f"Error in data sync: {e}")
|
|
@@ -356,48 +356,7 @@
|
|
|
356
356
|
|
|
357
357
|
<!-- Key Statistics -->
|
|
358
358
|
<div class="row g-4 mb-4">
|
|
359
|
-
<div class="col-
|
|
360
|
-
<div class="stats-card">
|
|
361
|
-
<div class="card-header bg-primary text-white">
|
|
362
|
-
<i class="bi bi-stopwatch"></i> Time Statistics
|
|
363
|
-
</div>
|
|
364
|
-
<div class="card-body">
|
|
365
|
-
<div class="mb-3">
|
|
366
|
-
<h5 class="d-flex justify-content-between">
|
|
367
|
-
<span>First Bug Discovery Time:</span>
|
|
368
|
-
<span class="value-danger">{{ (first_bug_time / 60)|int }} min: {{ (first_bug_time % 60)|int }} sec</span>
|
|
369
|
-
</h5>
|
|
370
|
-
<div class="progress">
|
|
371
|
-
<div class="progress-bar bg-danger" role="progressbar"
|
|
372
|
-
style="width: calc({{ first_bug_time }} / {{ total_testing_time }} * 100%);"
|
|
373
|
-
aria-valuenow="{{ first_bug_time }}" aria-valuemin="0" aria-valuemax="{{ total_testing_time }}"></div>
|
|
374
|
-
</div>
|
|
375
|
-
</div>
|
|
376
|
-
<div class="mb-3">
|
|
377
|
-
<h5 class="d-flex justify-content-between">
|
|
378
|
-
<span>First Precondition Satisfaction Time:</span>
|
|
379
|
-
<span class="value-success">{{ (first_precondition_time / 60)|int }} min: {{ (first_precondition_time % 60)|int }} sec</span>
|
|
380
|
-
</h5>
|
|
381
|
-
<div class="progress">
|
|
382
|
-
<div class="progress-bar bg-success" role="progressbar"
|
|
383
|
-
style="width: calc({{ first_precondition_time }} / {{ total_testing_time }} * 100%);"
|
|
384
|
-
aria-valuenow="{{ first_precondition_time }}" aria-valuemin="0" aria-valuemax="{{ total_testing_time }}"></div>
|
|
385
|
-
</div>
|
|
386
|
-
</div>
|
|
387
|
-
<div>
|
|
388
|
-
<h5 class="d-flex justify-content-between">
|
|
389
|
-
<span>Total Testing Time:</span>
|
|
390
|
-
<span class="value-primary">{{ (total_testing_time / 60)|int }} min: {{ (total_testing_time % 60)|int }} sec</span>
|
|
391
|
-
</h5>
|
|
392
|
-
<div class="progress">
|
|
393
|
-
<div class="progress-bar bg-primary" role="progressbar" style="width: 100%;"
|
|
394
|
-
aria-valuenow="{{ total_testing_time }}" aria-valuemin="0" aria-valuemax="{{ total_testing_time }}"></div>
|
|
395
|
-
</div>
|
|
396
|
-
</div>
|
|
397
|
-
</div>
|
|
398
|
-
</div>
|
|
399
|
-
</div>
|
|
400
|
-
<div class="col-md-6">
|
|
359
|
+
<div class="col-12">
|
|
401
360
|
<div class="stats-card">
|
|
402
361
|
<div class="card-header bg-success text-white">
|
|
403
362
|
<i class="bi bi-bar-chart"></i> Coverage Statistics
|
|
@@ -437,7 +396,7 @@
|
|
|
437
396
|
<!-- Tested Activities Panel -->
|
|
438
397
|
<div class="col-md-6">
|
|
439
398
|
<div class="card">
|
|
440
|
-
<div class="card-header bg-
|
|
399
|
+
<div class="card-header bg-warning text-white">
|
|
441
400
|
<div class="d-flex justify-content-between align-items-center">
|
|
442
401
|
<span><i class="bi bi-check-circle"></i> Tested Activities ({{ tested_activities|length }})</span>
|
|
443
402
|
<span class="badge bg-light text-dark">{{ tested_activities|length }} / {{ total_activities_count }}</span>
|
|
@@ -605,14 +564,14 @@
|
|
|
605
564
|
</tr>
|
|
606
565
|
</thead>
|
|
607
566
|
<tbody id="property-stats-container">
|
|
608
|
-
{% for
|
|
567
|
+
{% for property_name, test_result in property_stats.items() %}
|
|
609
568
|
<tr class="property-stat-row" data-page="1">
|
|
610
|
-
<td>{{
|
|
611
|
-
<td><span class="badge bg-light text-dark badge-custom">{{
|
|
612
|
-
<td>{{
|
|
613
|
-
<td>{{
|
|
614
|
-
<td><span class="badge bg-danger text-white">{{
|
|
615
|
-
<td><span class="badge bg-warning text-dark">{{
|
|
569
|
+
<td>{{ loop.index }}</td>
|
|
570
|
+
<td><span class="badge bg-light text-dark badge-custom">{{ property_name }}</span></td>
|
|
571
|
+
<td>{{ test_result.precond_satisfied|default(0) }}</td>
|
|
572
|
+
<td>{{ test_result.executed|default(0) }}</td>
|
|
573
|
+
<td><span class="badge bg-danger text-white">{{ test_result.fail|default(0) }}</span></td>
|
|
574
|
+
<td><span class="badge bg-warning text-dark">{{ test_result.error|default(0) }}</span></td>
|
|
616
575
|
</tr>
|
|
617
576
|
{% endfor %}
|
|
618
577
|
</tbody>
|