Kea2-python 1.0.6b0__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/__init__.py +3 -0
- kea2/absDriver.py +56 -0
- kea2/adbUtils.py +554 -0
- kea2/assets/config_version.json +16 -0
- kea2/assets/fastbot-thirdpart.jar +0 -0
- kea2/assets/fastbot_configs/abl.strings +2 -0
- kea2/assets/fastbot_configs/awl.strings +3 -0
- kea2/assets/fastbot_configs/max.config +7 -0
- kea2/assets/fastbot_configs/max.fuzzing.strings +699 -0
- kea2/assets/fastbot_configs/max.schema.strings +1 -0
- kea2/assets/fastbot_configs/max.strings +3 -0
- kea2/assets/fastbot_configs/max.tree.pruning +27 -0
- kea2/assets/fastbot_configs/teardown.py +18 -0
- kea2/assets/fastbot_configs/widget.block.py +38 -0
- kea2/assets/fastbot_libs/arm64-v8a/libfastbot_native.so +0 -0
- kea2/assets/fastbot_libs/armeabi-v7a/libfastbot_native.so +0 -0
- kea2/assets/fastbot_libs/x86/libfastbot_native.so +0 -0
- kea2/assets/fastbot_libs/x86_64/libfastbot_native.so +0 -0
- kea2/assets/framework.jar +0 -0
- kea2/assets/kea2-thirdpart.jar +0 -0
- kea2/assets/monkeyq.jar +0 -0
- kea2/assets/quicktest.py +126 -0
- kea2/cli.py +320 -0
- kea2/fastbotManager.py +267 -0
- kea2/fastbotx/ActivityTimes.py +52 -0
- kea2/fastbotx/ReuseEntry.py +74 -0
- kea2/fastbotx/ReuseModel.py +63 -0
- kea2/fastbotx/__init__.py +7 -0
- kea2/fbm_parser.py +871 -0
- kea2/fs_lock.py +131 -0
- kea2/kea2_api.py +166 -0
- kea2/keaUtils.py +1112 -0
- kea2/kea_launcher.py +319 -0
- kea2/logWatcher.py +92 -0
- kea2/mixin.py +22 -0
- kea2/report/__init__.py +0 -0
- kea2/report/bug_report_generator.py +793 -0
- kea2/report/mixin.py +482 -0
- kea2/report/report_merger.py +797 -0
- kea2/report/templates/bug_report_template.html +3876 -0
- kea2/report/templates/merged_bug_report_template.html +3333 -0
- kea2/report/utils.py +10 -0
- kea2/resultSyncer.py +65 -0
- kea2/u2Driver.py +610 -0
- kea2/utils.py +184 -0
- kea2/version_manager.py +102 -0
- kea2_python-1.0.6b0.dist-info/METADATA +447 -0
- kea2_python-1.0.6b0.dist-info/RECORD +52 -0
- kea2_python-1.0.6b0.dist-info/WHEEL +5 -0
- kea2_python-1.0.6b0.dist-info/entry_points.txt +2 -0
- kea2_python-1.0.6b0.dist-info/licenses/LICENSE +16 -0
- kea2_python-1.0.6b0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
snssdk1128://webcast_feed?gd_label=88888
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"activity":"com.ss.android.xxx.NewActivity",
|
|
4
|
+
"xpath": "//*[@resource-id='com.xxx.go:id/aaa']",
|
|
5
|
+
"resourceid": "",
|
|
6
|
+
"contentdesc":"",
|
|
7
|
+
"text":"",
|
|
8
|
+
"classname":""
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"activity":"com.ss.android.xxx.MainActivity",
|
|
12
|
+
"xpath": "//*[@resource-id='com.xxx.go:id/bbb' and @text='other']",
|
|
13
|
+
"resourceid": "",
|
|
14
|
+
"contentdesc":"",
|
|
15
|
+
"text":"",
|
|
16
|
+
"classname":""
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"activity":"com.ss.android.xxx.SplashActivity",
|
|
20
|
+
"xpath": "//*[@resource-id='com.com.xxx.go:id/ccc' and @text='other']",
|
|
21
|
+
"resourceid": "",
|
|
22
|
+
"contentdesc":"",
|
|
23
|
+
"text":"",
|
|
24
|
+
"classname":"",
|
|
25
|
+
"clickable":"false"
|
|
26
|
+
}
|
|
27
|
+
]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from uiautomator2 import Device
|
|
2
|
+
import time
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class HybridTestCase:
|
|
6
|
+
d: Device
|
|
7
|
+
|
|
8
|
+
PACKAGE_NAME = "it.feio.android.omninotes.alpha"
|
|
9
|
+
MAIN_ACTIVITY = "it.feio.android.omninotes.MainActivity"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def setUp(self: HybridTestCase):
|
|
13
|
+
self.d.app_start(PACKAGE_NAME, MAIN_ACTIVITY)
|
|
14
|
+
time.sleep(2)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def tearDown(self: HybridTestCase):
|
|
18
|
+
self.d.app_stop(PACKAGE_NAME)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from kea2.utils import Device
|
|
2
|
+
from kea2.keaUtils import precondition
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def global_block_widgets(d: "Device"):
|
|
6
|
+
"""
|
|
7
|
+
Specify UI widgets to be blocked globally during testing.
|
|
8
|
+
Returns a list of widgets that should be blocked from exploration.
|
|
9
|
+
This function is only available in 'u2 agent' mode.
|
|
10
|
+
"""
|
|
11
|
+
# return [d(text="widgets to block"), d.xpath(".//node[@text='widget to block']")]
|
|
12
|
+
return []
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# Example of conditional blocking with precondition
|
|
16
|
+
# @precondition(lambda d: d(text="In the home page").exists)
|
|
17
|
+
@precondition(lambda d: False)
|
|
18
|
+
def block_sth(d: "Device"):
|
|
19
|
+
# Note: Function name must start with "block_"
|
|
20
|
+
return []
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def global_block_tree(d: "Device"):
|
|
24
|
+
"""
|
|
25
|
+
Specify UI widget trees to be blocked globally during testing.
|
|
26
|
+
Returns a list of root nodes whose entire subtrees will be blocked from exploration.
|
|
27
|
+
This function is only available in 'u2 agent' mode.
|
|
28
|
+
"""
|
|
29
|
+
# return [d(text="trees to block"), d.xpath(".//node[@text='tree to block']")]
|
|
30
|
+
return []
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# Example of conditional tree blocking with precondition
|
|
34
|
+
# @precondition(lambda d: d(text="In the home page").exists)
|
|
35
|
+
@precondition(lambda d: False)
|
|
36
|
+
def block_tree_sth(d: "Device"):
|
|
37
|
+
# Note: Function name must start with "block_tree_"
|
|
38
|
+
return []
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
kea2/assets/monkeyq.jar
ADDED
|
Binary file
|
kea2/assets/quicktest.py
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
import uiautomator2 as u2
|
|
3
|
+
|
|
4
|
+
from time import sleep
|
|
5
|
+
from kea2 import precondition, prob, KeaTestRunner, Options
|
|
6
|
+
from kea2.u2Driver import U2Driver
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Omni_Notes_Sample(unittest.TestCase):
|
|
10
|
+
|
|
11
|
+
def setUp(self):
|
|
12
|
+
self.d = u2.connect()
|
|
13
|
+
|
|
14
|
+
@prob(0.2)
|
|
15
|
+
@precondition(
|
|
16
|
+
lambda self: self.d(description="Navigate up").exists
|
|
17
|
+
)
|
|
18
|
+
def test_goBack(self):
|
|
19
|
+
print("Navigate back")
|
|
20
|
+
self.d(description="Navigate up").click()
|
|
21
|
+
sleep(0.5)
|
|
22
|
+
|
|
23
|
+
@prob(0.2)
|
|
24
|
+
@precondition(
|
|
25
|
+
lambda self: self.d(description="drawer closed").exists
|
|
26
|
+
)
|
|
27
|
+
def test_openDrawer(self):
|
|
28
|
+
print("Open drawer")
|
|
29
|
+
self.d(description="drawer closed").click()
|
|
30
|
+
sleep(0.5)
|
|
31
|
+
|
|
32
|
+
@prob(0.5) # The probability of executing the function when precondition is satisfied.
|
|
33
|
+
@precondition(
|
|
34
|
+
lambda self: self.d(text="Omni Notes Alpha").exists
|
|
35
|
+
and self.d(text="Settings").exists
|
|
36
|
+
)
|
|
37
|
+
def test_goToPrivacy(self):
|
|
38
|
+
"""
|
|
39
|
+
The ability to jump out of the UI tarpits
|
|
40
|
+
|
|
41
|
+
precond:
|
|
42
|
+
The drawer was opened
|
|
43
|
+
action:
|
|
44
|
+
go to settings -> privacy
|
|
45
|
+
"""
|
|
46
|
+
print("trying to click Settings")
|
|
47
|
+
self.d(text="Settings").click()
|
|
48
|
+
sleep(0.5)
|
|
49
|
+
print("trying to click Privacy")
|
|
50
|
+
self.d(text="Privacy").click()
|
|
51
|
+
|
|
52
|
+
@precondition(
|
|
53
|
+
lambda self: self.d(resourceId="it.feio.android.omninotes.alpha:id/search_src_text").exists
|
|
54
|
+
)
|
|
55
|
+
def test_rotation(self):
|
|
56
|
+
"""
|
|
57
|
+
The ability to make assertion to find functional bug
|
|
58
|
+
|
|
59
|
+
precond:
|
|
60
|
+
The search input box is opened
|
|
61
|
+
action:
|
|
62
|
+
rotate the device (set it to landscape, then back to natural)
|
|
63
|
+
assertion:
|
|
64
|
+
The search input box is still being opened
|
|
65
|
+
"""
|
|
66
|
+
print("rotate the device")
|
|
67
|
+
self.d.set_orientation("l")
|
|
68
|
+
sleep(2)
|
|
69
|
+
self.d.set_orientation("n")
|
|
70
|
+
sleep(2)
|
|
71
|
+
assert self.d(resourceId="it.feio.android.omninotes.alpha:id/search_src_text").exists()
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
URL = "https://github.com/federicoiosue/Omni-Notes/releases/download/6.2.0_alpha/OmniNotes-alphaRelease-6.2.0.apk"
|
|
75
|
+
FALL_BACK_URL = "https://gitee.com/XixianLiang/Kea2/raw/main/omninotes.apk"
|
|
76
|
+
PACKAGE_NAME = "it.feio.android.omninotes.alpha"
|
|
77
|
+
FILE_NAME = "omninotes.apk"
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def download_omninotes():
|
|
81
|
+
import socket
|
|
82
|
+
socket.setdefaulttimeout(30)
|
|
83
|
+
try:
|
|
84
|
+
import urllib.request
|
|
85
|
+
urllib.request.urlretrieve(URL, FILE_NAME)
|
|
86
|
+
except Exception as e:
|
|
87
|
+
print(f"[WARN] Download from {URL} failed: {e}. Try to download from fallback URL {FALL_BACK_URL}", flush=True)
|
|
88
|
+
try:
|
|
89
|
+
urllib.request.urlretrieve(FALL_BACK_URL, FILE_NAME)
|
|
90
|
+
except Exception as e2:
|
|
91
|
+
print(f"[ERROR] Download from fallback URL {FALL_BACK_URL} also failed: {e2}", flush=True)
|
|
92
|
+
raise e2
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def check_installation(serial=None):
|
|
96
|
+
import os
|
|
97
|
+
from pathlib import Path
|
|
98
|
+
|
|
99
|
+
d = u2.connect(serial)
|
|
100
|
+
# automatically install omni-notes
|
|
101
|
+
if PACKAGE_NAME not in d.app_list():
|
|
102
|
+
if not os.path.exists(Path(".") / FILE_NAME):
|
|
103
|
+
print(f"[INFO] omninote.apk not exists. Downloading from {URL}", flush=True)
|
|
104
|
+
download_omninotes()
|
|
105
|
+
print("[INFO] Installing omninotes.", flush=True)
|
|
106
|
+
d.app_install(FILE_NAME)
|
|
107
|
+
d.stop_uiautomator()
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
if __name__ == "__main__":
|
|
111
|
+
check_installation(serial=None)
|
|
112
|
+
KeaTestRunner.setOptions(
|
|
113
|
+
Options(
|
|
114
|
+
driverName="d",
|
|
115
|
+
Driver=U2Driver,
|
|
116
|
+
packageNames=[PACKAGE_NAME],
|
|
117
|
+
# serial="emulator-5554", # specify the serial
|
|
118
|
+
maxStep=50,
|
|
119
|
+
profile_period=10,
|
|
120
|
+
take_screenshots=True, # whether to take screenshots, default is False
|
|
121
|
+
# running_mins=10, # specify the maximal running time in minutes, default value is 10m
|
|
122
|
+
# throttle=200, # specify the throttle in milliseconds, default value is 200ms
|
|
123
|
+
agent="u2" # 'native' for running the vanilla Fastbot, 'u2' for running Kea2
|
|
124
|
+
)
|
|
125
|
+
)
|
|
126
|
+
unittest.main(testRunner=KeaTestRunner)
|
kea2/cli.py
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
# cli.py
|
|
3
|
+
|
|
4
|
+
from __future__ import absolute_import, print_function
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
import sys
|
|
7
|
+
from .utils import getProjectRoot, getLogger
|
|
8
|
+
from .kea_launcher import run
|
|
9
|
+
from .version_manager import check_config_compatibility, get_cur_version
|
|
10
|
+
import argparse
|
|
11
|
+
|
|
12
|
+
import os
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
logger = getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def cmd_version(args):
|
|
20
|
+
print(get_cur_version(), flush=True)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def cmd_init(args):
|
|
24
|
+
cwd = Path(os.getcwd())
|
|
25
|
+
configs_dir = cwd / "configs"
|
|
26
|
+
if os.path.isdir(configs_dir):
|
|
27
|
+
logger.warning("Kea2 project already initialized")
|
|
28
|
+
return
|
|
29
|
+
|
|
30
|
+
import shutil
|
|
31
|
+
def copy_configs():
|
|
32
|
+
src = Path(__file__).parent / "assets" / "fastbot_configs"
|
|
33
|
+
dst = configs_dir
|
|
34
|
+
shutil.copytree(src, dst)
|
|
35
|
+
|
|
36
|
+
def copy_samples():
|
|
37
|
+
src = Path(__file__).parent / "assets" / "quicktest.py"
|
|
38
|
+
dst = cwd / "quicktest.py"
|
|
39
|
+
shutil.copyfile(src, dst)
|
|
40
|
+
|
|
41
|
+
def save_version():
|
|
42
|
+
import json
|
|
43
|
+
version_file = configs_dir / "version.json"
|
|
44
|
+
with open(version_file, "w") as fp:
|
|
45
|
+
json.dump({"version": get_cur_version(), "init date": datetime.now().strftime("%Y-%m-%d %H:%M:%S")}, fp, indent=4)
|
|
46
|
+
|
|
47
|
+
copy_configs()
|
|
48
|
+
copy_samples()
|
|
49
|
+
save_version()
|
|
50
|
+
logger.info("Kea2 project initialized.")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def cmd_load_configs(args):
|
|
54
|
+
pass
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def cmd_report(args):
|
|
58
|
+
from .report.bug_report_generator import BugReportGenerator
|
|
59
|
+
report_dirs = args.path
|
|
60
|
+
|
|
61
|
+
for report_dir in report_dirs:
|
|
62
|
+
report_dir = Path(report_dir).resolve()
|
|
63
|
+
|
|
64
|
+
if not report_dir.exists():
|
|
65
|
+
logger.error(f"Report directory does not exist: {str(report_dir)}, Skipped.")
|
|
66
|
+
continue
|
|
67
|
+
|
|
68
|
+
logger.debug(f"Generating test report from directory: {report_dir}")
|
|
69
|
+
BugReportGenerator(report_dir).generate_report()
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def cmd_merge(args):
|
|
73
|
+
"""Merge multiple test report directories and generate a combined report"""
|
|
74
|
+
from .report.report_merger import TestReportMerger
|
|
75
|
+
|
|
76
|
+
try:
|
|
77
|
+
# Validate input paths
|
|
78
|
+
if not args.paths or len(args.paths) < 2:
|
|
79
|
+
logger.error("At least 2 test report paths are required for merging. Use -p to specify paths.")
|
|
80
|
+
return
|
|
81
|
+
|
|
82
|
+
# Validate that all paths exist
|
|
83
|
+
for path in args.paths:
|
|
84
|
+
path_obj = Path(path)
|
|
85
|
+
if not path_obj.exists():
|
|
86
|
+
raise FileNotFoundError(f"{path_obj}")
|
|
87
|
+
if not path_obj.is_dir():
|
|
88
|
+
raise NotADirectoryError(f"{path_obj}")
|
|
89
|
+
|
|
90
|
+
logger.debug(f"Merging {len(args.paths)} test report directories...")
|
|
91
|
+
|
|
92
|
+
# Initialize merger
|
|
93
|
+
merger = TestReportMerger()
|
|
94
|
+
|
|
95
|
+
# Merge test reports
|
|
96
|
+
merged_report = merger.merge_reports(args.paths, args.output)
|
|
97
|
+
|
|
98
|
+
if merged_report is not None:
|
|
99
|
+
print(f"✅ Test reports merged successfully!", flush=True)
|
|
100
|
+
print(f"📊 Merged report: {merged_report}", flush=True)
|
|
101
|
+
# Get merge summary
|
|
102
|
+
merge_summary = merger.get_merge_summary()
|
|
103
|
+
print(f"📈 Merged {merge_summary.get('merged_directories', 0)} directories", flush=True)
|
|
104
|
+
|
|
105
|
+
except Exception as e:
|
|
106
|
+
logger.error(f"Error during merge operation: {e}")
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def cmd_mergefbm(args):
|
|
110
|
+
"""Merge all FBM files in the specified folder and its subfolders using sum mode"""
|
|
111
|
+
from .fbm_parser import FBMMerger
|
|
112
|
+
import glob
|
|
113
|
+
import shutil
|
|
114
|
+
|
|
115
|
+
try:
|
|
116
|
+
# Validate input path
|
|
117
|
+
input_path = Path(args.path).resolve()
|
|
118
|
+
if not input_path.exists():
|
|
119
|
+
logger.error(f"Input directory does not exist: {input_path}")
|
|
120
|
+
return
|
|
121
|
+
if not input_path.is_dir():
|
|
122
|
+
logger.error(f"Input path is not a directory: {input_path}")
|
|
123
|
+
return
|
|
124
|
+
|
|
125
|
+
# Find all FBM files in the directory and its subdirectories
|
|
126
|
+
fbm_files = glob.glob(str(input_path / "**" / "*.fbm"), recursive=True)
|
|
127
|
+
|
|
128
|
+
if not fbm_files:
|
|
129
|
+
logger.error(f"No FBM files found in {input_path} or its subdirectories")
|
|
130
|
+
return
|
|
131
|
+
|
|
132
|
+
logger.debug(f"Found {len(fbm_files)} FBM files to merge:")
|
|
133
|
+
for fbm_file in fbm_files:
|
|
134
|
+
logger.debug(f" - {fbm_file}")
|
|
135
|
+
|
|
136
|
+
# Set default output file if not provided
|
|
137
|
+
if not args.output:
|
|
138
|
+
output_file = input_path / "merged.fbm"
|
|
139
|
+
else:
|
|
140
|
+
output_file = Path(args.output).resolve()
|
|
141
|
+
|
|
142
|
+
# Initialize merger
|
|
143
|
+
merger = FBMMerger()
|
|
144
|
+
|
|
145
|
+
# Handle different cases
|
|
146
|
+
if len(fbm_files) == 1:
|
|
147
|
+
# Only one file, just copy it to output
|
|
148
|
+
shutil.copyfile(fbm_files[0], output_file)
|
|
149
|
+
logger.info(f"Only one FBM file found, copied to {output_file}")
|
|
150
|
+
else:
|
|
151
|
+
# Merge files iteratively: start with the first file and merge with each subsequent file
|
|
152
|
+
# Create a temporary file for the intermediate merged result
|
|
153
|
+
temp_output = input_path / f".tmp_merged.fbm"
|
|
154
|
+
|
|
155
|
+
# Start with the first file as the initial merged result
|
|
156
|
+
shutil.copyfile(fbm_files[0], temp_output)
|
|
157
|
+
|
|
158
|
+
# Iterate through the remaining files and merge them one by one
|
|
159
|
+
for i in range(1, len(fbm_files)):
|
|
160
|
+
current_file = fbm_files[i]
|
|
161
|
+
next_temp = input_path / f".tmp_merged_{i}.fbm"
|
|
162
|
+
|
|
163
|
+
logger.debug(f"Merging {temp_output} and {current_file} into {next_temp}")
|
|
164
|
+
success = merger.merge(str(temp_output), str(current_file), str(next_temp), merge_mode='sum')
|
|
165
|
+
|
|
166
|
+
if not success:
|
|
167
|
+
logger.error(f"Failed to merge {temp_output} and {current_file}")
|
|
168
|
+
# Clean up temporary files
|
|
169
|
+
for f in [temp_output, next_temp]:
|
|
170
|
+
if f.exists() and f.name.startswith(".tmp_"):
|
|
171
|
+
f.unlink()
|
|
172
|
+
return
|
|
173
|
+
|
|
174
|
+
# Remove the previous temporary file and update to the new one
|
|
175
|
+
temp_output.unlink()
|
|
176
|
+
temp_output = next_temp
|
|
177
|
+
|
|
178
|
+
# Move the final merged file to the output location
|
|
179
|
+
if temp_output != output_file:
|
|
180
|
+
temp_output.replace(output_file)
|
|
181
|
+
|
|
182
|
+
print(f"✅ All FBM files merged successfully!", flush=True)
|
|
183
|
+
print(f"📊 Merged FBM file: {output_file}", flush=True)
|
|
184
|
+
print(f"📈 Merged {len(fbm_files)} FBM files", flush=True)
|
|
185
|
+
|
|
186
|
+
except Exception as e:
|
|
187
|
+
logger.error(f"Error during FBM merge operation: {e}")
|
|
188
|
+
import traceback
|
|
189
|
+
logger.debug(traceback.format_exc())
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def cmd_run(args):
|
|
193
|
+
base_dir = getProjectRoot()
|
|
194
|
+
if base_dir is None:
|
|
195
|
+
logger.error("kea2 project not initialized. Use `kea2 init`.")
|
|
196
|
+
return
|
|
197
|
+
|
|
198
|
+
check_config_compatibility()
|
|
199
|
+
|
|
200
|
+
run(args)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
_commands = [
|
|
204
|
+
dict(action=cmd_version, command="version", help="show version"),
|
|
205
|
+
dict(
|
|
206
|
+
action=cmd_init,
|
|
207
|
+
command="init",
|
|
208
|
+
help="init the Kea2 project in current directory",
|
|
209
|
+
),
|
|
210
|
+
dict(
|
|
211
|
+
action=cmd_report,
|
|
212
|
+
command="report",
|
|
213
|
+
help="generate test report from existing test results",
|
|
214
|
+
flags=[
|
|
215
|
+
dict(
|
|
216
|
+
name=["report_dir"],
|
|
217
|
+
args=["-p", "--path"],
|
|
218
|
+
type=str,
|
|
219
|
+
nargs="+",
|
|
220
|
+
required=True,
|
|
221
|
+
help="Root directory path of the test results to generate report from"
|
|
222
|
+
)
|
|
223
|
+
]
|
|
224
|
+
),
|
|
225
|
+
dict(
|
|
226
|
+
action=cmd_merge,
|
|
227
|
+
command="merge",
|
|
228
|
+
help="merge multiple test report directories and generate a combined report",
|
|
229
|
+
flags=[
|
|
230
|
+
dict(
|
|
231
|
+
name=["paths"],
|
|
232
|
+
args=["-p", "--paths"],
|
|
233
|
+
type=str,
|
|
234
|
+
nargs='+',
|
|
235
|
+
required=True,
|
|
236
|
+
help="Paths to test report directories (res_* directories) to merge"
|
|
237
|
+
),
|
|
238
|
+
dict(
|
|
239
|
+
name=["output"],
|
|
240
|
+
args=["-o", "--output"],
|
|
241
|
+
type=str,
|
|
242
|
+
required=False,
|
|
243
|
+
help="Output directory for merged report (optional)"
|
|
244
|
+
)
|
|
245
|
+
]
|
|
246
|
+
),
|
|
247
|
+
dict(
|
|
248
|
+
action=cmd_mergefbm,
|
|
249
|
+
command="mergefbm",
|
|
250
|
+
help="merge all FBM files in the specified folder and its subfolders using sum mode",
|
|
251
|
+
flags=[
|
|
252
|
+
dict(
|
|
253
|
+
name=["path"],
|
|
254
|
+
args=["-p", "--path"],
|
|
255
|
+
type=str,
|
|
256
|
+
required=True,
|
|
257
|
+
help="Path to the folder containing FBM files to merge"
|
|
258
|
+
),
|
|
259
|
+
dict(
|
|
260
|
+
name=["output"],
|
|
261
|
+
args=["-o", "--output"],
|
|
262
|
+
type=str,
|
|
263
|
+
required=False,
|
|
264
|
+
help="Output file path for merged FBM file (optional, default: merged.fbm in the input folder)"
|
|
265
|
+
)
|
|
266
|
+
]
|
|
267
|
+
)
|
|
268
|
+
]
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def main():
|
|
272
|
+
parser = argparse.ArgumentParser(
|
|
273
|
+
formatter_class=argparse.ArgumentDefaultsHelpFormatter
|
|
274
|
+
)
|
|
275
|
+
parser.add_argument("-d", "--debug", action="store_true",
|
|
276
|
+
help="show detail log")
|
|
277
|
+
|
|
278
|
+
subparser = parser.add_subparsers(dest='subparser')
|
|
279
|
+
|
|
280
|
+
actions = {}
|
|
281
|
+
for c in _commands:
|
|
282
|
+
cmd_name = c['command']
|
|
283
|
+
actions[cmd_name] = c['action']
|
|
284
|
+
sp = subparser.add_parser(
|
|
285
|
+
cmd_name,
|
|
286
|
+
help=c.get('help'),
|
|
287
|
+
formatter_class=argparse.ArgumentDefaultsHelpFormatter
|
|
288
|
+
)
|
|
289
|
+
for f in c.get('flags', []):
|
|
290
|
+
args = f.get('args')
|
|
291
|
+
if not args:
|
|
292
|
+
args = ['-'*min(2, len(n)) + n for n in f['name']]
|
|
293
|
+
kwargs = f.copy()
|
|
294
|
+
kwargs.pop('name', None)
|
|
295
|
+
kwargs.pop('args', None)
|
|
296
|
+
sp.add_argument(*args, **kwargs)
|
|
297
|
+
|
|
298
|
+
from .kea_launcher import _set_runner_parser
|
|
299
|
+
_set_runner_parser(subparser)
|
|
300
|
+
actions["run"] = cmd_run
|
|
301
|
+
if sys.argv[1:] == ["run"]:
|
|
302
|
+
sys.argv.append("-h")
|
|
303
|
+
args = parser.parse_args()
|
|
304
|
+
|
|
305
|
+
import logging
|
|
306
|
+
from .utils import LoggingLevel
|
|
307
|
+
LoggingLevel.set_level(logging.INFO)
|
|
308
|
+
if args.debug:
|
|
309
|
+
LoggingLevel.set_level(logging.DEBUG)
|
|
310
|
+
logger.debug("args: %s", args)
|
|
311
|
+
|
|
312
|
+
if args.subparser:
|
|
313
|
+
actions[args.subparser](args)
|
|
314
|
+
return
|
|
315
|
+
|
|
316
|
+
parser.print_help()
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
if __name__ == "__main__":
|
|
320
|
+
main()
|