@harmonyos-arkts/d2h 0.1.10 → 0.1.12
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.
- package/.claude-plugin/marketplace.json +15 -13
- package/.claude-plugin/plugin.json +13 -7
- package/AGENTS.md +5 -5
- package/README.md +212 -212
- package/agents/android-to-hmos-00-orchestrator.md +318 -318
- package/agents/d2h.md +157 -157
- package/dist/bin/install-opencode.js +30 -30
- package/docs/environment-check.md +86 -86
- package/install-opencode.sh +160 -160
- package/opencode/agents.json +16 -16
- package/opencode/vscode-d2h-package.json +7 -7
- package/opencode/vscode-d2h-plugin.mjs +29 -29
- package/package.json +54 -54
- package/schemas/android-source-manifest.schema.json +25 -25
- package/schemas/checkpoint-provenance.schema.json +67 -67
- package/schemas/final-acceptance.schema.json +29 -29
- package/schemas/managed-evidence-index.schema.json +15 -15
- package/schemas/managed-evidence.schema.json +114 -114
- package/schemas/migration-config.schema.json +51 -51
- package/schemas/migration-report-index.schema.json +39 -39
- package/schemas/migration-report-item.schema.json +51 -51
- package/schemas/migration-report-summary.schema.json +25 -25
- package/schemas/migration-status.schema.json +202 -202
- package/schemas/preflight.schema.json +60 -60
- package/schemas/source-order-audit.schema.json +16 -16
- package/schemas/source-provenance-event.schema.json +19 -19
- package/schemas/spec-app-shard.schema.json +45 -45
- package/schemas/spec-fact-corrections-shard.schema.json +13 -13
- package/schemas/spec-features-shard.schema.json +43 -43
- package/schemas/spec-index.schema.json +26 -26
- package/schemas/spec-interactions-shard.schema.json +40 -40
- package/schemas/spec-page.schema.json +98 -98
- package/schemas/spec-pages-index.schema.json +1 -1
- package/schemas/spec-unresolved-shard.schema.json +1 -1
- package/schemas/task-envelope.schema.json +109 -109
- package/schemas/task-plan.schema.json +341 -341
- package/scripts/check-environment.mjs +320 -320
- package/skills/android2hmos_resources_convert/SKILL.md +180 -180
- package/skills/android2hmos_resources_convert/references/image-conversion-rules.md +230 -230
- package/skills/android2hmos_resources_convert/references/svg-fix-patterns.md +175 -175
- package/skills/android2hmos_resources_convert/references/xml-drawable-to-svg-rules.md +513 -513
- package/skills/arkts-app-identity/SKILL.md +238 -238
- package/skills/arkts-i18n/SKILL.md +496 -496
- package/skills/arkts-i18n/evals/evals.json +84 -84
- package/skills/arkts-i18n/references/code-examples.md +302 -302
- package/skills/arkts-i18n/references/common-pitfalls.md +391 -391
- package/skills/arkts-i18n/references/dynamic-language-switch.md +604 -604
- package/skills/arkts-i18n/references/hardcoded-string-scanner.md +348 -348
- package/skills/arkts-i18n/references/language-codes.md +104 -104
- package/skills/arkts-i18n/references/resource-file-structure.md +775 -775
- package/skills/arkts-i18n/references/static-vs-dynamic.md +242 -242
- package/skills/arkts-i18n/references/v1-compat.md +244 -244
- package/skills/arkts-i18n/scripts/audit_i18n_completeness.sh +174 -174
- package/skills/arkts-icon-sizing/SKILL.md +211 -211
- package/skills/arkts-icon-sizing/scripts/icon_audit.py +131 -131
- package/skills/arkts-icon-sizing/scripts/icon_autofix.py +88 -88
- package/skills/arkts-icon-sizing/scripts/icon_dims.py +179 -179
- package/skills/arkts-icon-sizing/scripts/icon_fix.py +119 -119
- package/skills/arkts-mvvm-architecture/SKILL.md +613 -613
- package/skills/d2h-hypium-forge/SKILL.md +309 -309
- package/skills/d2h-hypium-forge/references/functional-spec-template.md +140 -140
- package/skills/d2h-hypium-forge/references/official-api.md +135 -135
- package/skills/d2h-hypium-forge/references/pitfalls.md +98 -98
- package/skills/d2h-hypium-forge/references/testability-template.md +71 -71
- package/skills/d2h-hypium-forge/scripts/_common_template.py +659 -659
- package/skills/d2h-hypium-forge/scripts/analyze_results.py +165 -165
- package/skills/d2h-hypium-forge/scripts/assert_audit.py +107 -107
- package/skills/d2h-hypium-forge/scripts/collect_runtime_pages.py +169 -169
- package/skills/d2h-hypium-forge/scripts/forge_report.py +325 -325
- package/skills/d2h-hypium-forge/scripts/inject_resource_ids.py +114 -114
- package/skills/d2h-hypium-forge/scripts/lint_asserts.py +80 -80
- package/skills/d2h-hypium-forge/scripts/lint_locate.py +60 -60
- package/skills/d2h-hypium-forge/scripts/merge_appgraph.py +249 -249
- package/skills/d2h-hypium-forge/scripts/precondition.py +140 -140
- package/skills/d2h-hypium-forge/scripts/preinject_ids.py +232 -232
- package/skills/d2h-hypium-forge/scripts/probe.py +197 -197
- package/skills/d2h-hypium-forge/scripts/run_direct.py +823 -823
- package/skills/d2h-hypium-forge/scripts/run_parallel.py +121 -121
- package/skills/d2h-hypium-forge/scripts/scan_features.py +255 -255
- package/skills/d2h-hypium-forge/scripts/upgrade_asserts.py +66 -66
- package/skills/d2h-hypium-forge/scripts/validate_scenarios.py +84 -84
- package/skills/d2h-page-develop/SKILL.md +112 -112
- package/skills/d2h-page-develop/agents/openai.yaml +7 -7
- package/skills/d2h-page-spec-generate/SKILL.md +206 -206
- package/skills/d2h-page-spec-generate/scripts/build_batch_report.py +227 -227
- package/skills/d2h-utg-ai-generate/SKILL.md +128 -128
- package/skills/d2h-utg-ai-generate/references/click-paths-schema.json +76 -76
- package/skills/d2h-utg-ai-generate/references/extraction-guide.md +34 -34
- package/skills/d2h-utg-ai-generate/references/facts-contract.md +96 -96
- package/skills/d2h-utg-ai-generate/references/page-schema.json +61 -61
- package/skills/d2h-utg-ai-generate/references/utg-schema.json +89 -89
- package/skills/d2h-utg-ai-generate/scripts/build_page_json.py +314 -314
- package/skills/d2h-utg-ai-generate/scripts/build_utg.py +123 -123
- package/skills/d2h-utg-ai-generate/scripts/capture_from_page_json.ts +280 -280
- package/skills/d2h-utg-ai-generate/scripts/utg_common.py +524 -524
- package/skills/d2h-utg-ai-generate/scripts/utg_html.py +489 -489
- package/skills/d2h-utg-ai-generate/scripts/validate_utg.py +69 -69
- package/skills/d2h-utg-ai-generate/scripts/vendor/D3-LICENSE +13 -13
- package/skills/d2h-utg-ai-generate/scripts/vendor/d3-7.9.0.min.js +2 -2
- package/skills/d2h-utg-ai-generate/tests/test_pipeline.py +369 -369
- package/skills/d2h-utg-static-generate/SKILL.md +73 -73
- package/skills/d2h-utg-static-generate/references/discoveries.md +38 -38
- package/skills/d2h-utg-static-generate/scripts/generate.py +499 -499
- package/skills/d2h-utg-static-generate/tests/test_static.py +335 -335
- package/skills/d2h-utg-static-generate/tests/verify-offline-html.py +60 -60
- package/skills/d2h-utg-static-generate/tests/verify-produced-fixture.py +131 -131
- package/skills/harmonyos-migration-playbook/SKILL.md +83 -83
- package/skills/harmonyos-migration-playbook/agents/openai.yaml +7 -7
- package/skills/harmonyos-migration-playbook/references/arkts-compile.md +24 -24
- package/skills/harmonyos-migration-playbook/references/harmony-runtime.md +53 -53
- package/skills/harmonyos-migration-playbook/references/lesson-lifecycle.md +45 -45
- package/skills/harmonyos-migration-playbook/references/protocol-e2e.md +23 -23
- package/skills/harmonyos-migration-playbook/references/ui-automation.md +53 -53
- package/skills/harmonyos-migration-playbook/references/windows-environment.md +38 -38
- package/skills/maintaining-migration-report/SKILL.md +144 -144
- package/skills/maintaining-migration-report/references/final-acceptance.md +48 -48
- package/skills/maintaining-migration-report/references/source-provenance.md +66 -66
- package/skills/managing-d2h-toolchain/SKILL.md +24 -24
- package/skills/managing-d2h-toolchain/environment-requirements.json +16 -16
- package/skills/managing-d2h-toolchain/scripts/manage-toolchain.mjs +98 -98
- package/skills/managing-d2h-toolchain/toolchain.json +16 -16
- package/skills/native-library-substitution/SKILL.md +384 -384
- package/skills/native-library-substitution/references/native-library-substitution.json +56905 -56905
- package/skills/native-library-substitution/references/native-library-substitution.md +163 -163
- package/skills/preparing-migration-workspace/SKILL.md +131 -131
- package/skills/reviewing-migration-process/SKILL.md +62 -62
|
@@ -1,140 +1,140 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
precondition.py — 测试前置自动化:权限预授 + 登录/账号策略 + 首启弹窗处理。
|
|
4
|
-
|
|
5
|
-
原则(项目经验 2):
|
|
6
|
-
所有权限和账号默认优先完成确认/登录;无法自动完成的,生成明确的用户提醒清单
|
|
7
|
-
(而非留到用例执行中反复失败)。
|
|
8
|
-
|
|
9
|
-
动作:
|
|
10
|
-
1. 解析 module.json5 权限 → hdc pm grant 预授(按 ACL 级别分类:
|
|
11
|
-
normal/system_basic → 尝试预授;user_grant 运行时权限 → 模拟器上多数可 grant)
|
|
12
|
-
2. 登录页检测 → 三级策略:
|
|
13
|
-
a. mock 登录入口(源码含"模拟登录")→ 自动用之
|
|
14
|
-
b. UI 自动化登录(已知测试账号)→ 提醒用户提供或用环境变量
|
|
15
|
-
c. 不可自动 → 用户提醒清单
|
|
16
|
-
3. 首启弹窗预处置(隐私协议/权限弹窗)→ 输出 dismiss 脚本片段
|
|
17
|
-
|
|
18
|
-
用法:
|
|
19
|
-
precondition.py --features features.json --device 127.0.0.1:5555 [--user-account-env HM_TEST_ACCOUNT]
|
|
20
|
-
输出: preconditions.md(执行结果 + 用户提醒清单)
|
|
21
|
-
"""
|
|
22
|
-
import argparse
|
|
23
|
-
import json
|
|
24
|
-
import os
|
|
25
|
-
import re
|
|
26
|
-
import subprocess
|
|
27
|
-
import sys
|
|
28
|
-
|
|
29
|
-
HDC = '/Applications/DevEco-Studio.app/Contents/sdk/default/openharmony/toolchains/hdc'
|
|
30
|
-
|
|
31
|
-
# 鸿蒙权限 → 授予难度(默认可 grant / 需弹窗确认 / 系统级不可授)
|
|
32
|
-
GRANTABLE = re.compile(r'ohos\.permission\.(INTERNET|GET_NETWORK_INFO|KEEP_BACKGROUND_RUNNING|'
|
|
33
|
-
r'REQUIRE_FORM|PUBLISH_AGENT_REMINDER|VIBRATE|QUERY_AUDIO_DEVICE_STATE)')
|
|
34
|
-
# 运行时弹窗类(模拟器可 pm grant)
|
|
35
|
-
RUNTIME = re.compile(r'ohos\.permission\.(READ_|WRITE_|MICROPHONE|CAMERA|LOCATION|MEDIA|'
|
|
36
|
-
r'APPTRACKING|CALENDAR|CONTACTS|DISTRIBUTED)')
|
|
37
|
-
# 用户身份类(不可自动,需用户操作)
|
|
38
|
-
USER_IDENTITY = re.compile(r'ohos\.permission\.(USER_AUTH|MANAGE_FINGERPRINT|ACCESS_USER_AUTH|'
|
|
39
|
-
r'SECURE_SESSION|READ_HEALTH|SPORT)')
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def hdc(device, *args, capture=True):
|
|
43
|
-
cmd = [HDC, '-t', device] + list(args)
|
|
44
|
-
try:
|
|
45
|
-
r = subprocess.run(cmd, capture_output=capture, text=True, timeout=30)
|
|
46
|
-
return (r.stdout or '') + (r.stderr or '')
|
|
47
|
-
except Exception as e:
|
|
48
|
-
return f'ERROR: {e}'
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
def main():
|
|
52
|
-
ap = argparse.ArgumentParser()
|
|
53
|
-
ap.add_argument('--features', required=True)
|
|
54
|
-
ap.add_argument('--device', default='127.0.0.1:5555')
|
|
55
|
-
ap.add_argument('--bundle', default=None)
|
|
56
|
-
ap.add_argument('--test-account-env', default='HM_TEST_ACCOUNT',
|
|
57
|
-
help='测试账号环境变量名(格式 user:pass)')
|
|
58
|
-
args = ap.parse_args()
|
|
59
|
-
|
|
60
|
-
feats = json.load(open(args.features, encoding='utf-8'))
|
|
61
|
-
bundle = args.bundle or feats.get('bundle')
|
|
62
|
-
if not bundle:
|
|
63
|
-
print('错误: 无法确定 bundleName', file=sys.stderr)
|
|
64
|
-
sys.exit(1)
|
|
65
|
-
|
|
66
|
-
report = [f'# 测试前置报告({bundle} @ {args.device})', '']
|
|
67
|
-
reminders = []
|
|
68
|
-
|
|
69
|
-
# ── 1. 权限预授 ──
|
|
70
|
-
report.append('## 权限预授')
|
|
71
|
-
ok, skipped, user_confirm = [], [], []
|
|
72
|
-
for perm in feats.get('permissions', []):
|
|
73
|
-
if USER_IDENTITY.search(perm):
|
|
74
|
-
user_confirm.append(perm)
|
|
75
|
-
continue
|
|
76
|
-
if GRANTABLE.search(perm) or RUNTIME.search(perm):
|
|
77
|
-
out = hdc(args.device, 'shell', 'atm', 'perm', '-g', bundle, perm)
|
|
78
|
-
granted = 'grant' in out.lower() and 'fail' not in out.lower()
|
|
79
|
-
if not granted:
|
|
80
|
-
out2 = hdc(args.device, 'shell', f'pm grant {bundle} {perm}')
|
|
81
|
-
granted = 'fail' not in out2.lower() and 'Exception' not in out2
|
|
82
|
-
(ok if granted else skipped).append(perm)
|
|
83
|
-
else:
|
|
84
|
-
skipped.append(perm)
|
|
85
|
-
report.append(f'- 已预授 {len(ok)}: {", ".join(ok) or "无"}')
|
|
86
|
-
if skipped:
|
|
87
|
-
report.append(f'- 跳过 {len(skipped)}(授予失败或需特殊签名): {", ".join(skipped)}')
|
|
88
|
-
if user_confirm:
|
|
89
|
-
report.append(f'- 需用户在设备上确认: {", ".join(user_confirm)}')
|
|
90
|
-
reminders.append(f'身份类权限需手动确认: {", ".join(user_confirm)}')
|
|
91
|
-
report.append('')
|
|
92
|
-
|
|
93
|
-
# ── 2. 登录策略(用户 2026-09-27 指正:不能止步于标 BLOCKED,必须提醒用户三选一)──
|
|
94
|
-
report.append('## 登录/账号策略')
|
|
95
|
-
if feats.get('has_login'):
|
|
96
|
-
acct = os.environ.get(args.test_account_env, '')
|
|
97
|
-
if acct:
|
|
98
|
-
report.append(f'- 检测到登录页;使用环境变量 {args.test_account_env} 提供的测试账号(自动登录用例将启用)')
|
|
99
|
-
else:
|
|
100
|
-
report.append('- 检测到登录页;**未设置测试账号(export '
|
|
101
|
-
f'{args.test_account_env}=user:pass)**。登录域用例暂标 BLOCKED,'
|
|
102
|
-
'**请从以下三种方式选择其一解除**:')
|
|
103
|
-
report.append(' 1. **手动登录**:在模拟器/真机上人工完成一次登录(登录态持久,最真实,'
|
|
104
|
-
'适合无需反复切换账号的场景)——完成后告知,BLOCKED 用例即可重跑;')
|
|
105
|
-
report.append(' 2. **提供账号密码**:给出测试账号,构造登录用例(输入账号密码→提交→断言登录成功),'
|
|
106
|
-
'登录链路本身也被覆盖——需 export 环境变量或直接告知;')
|
|
107
|
-
report.append(' 3. **mock 登录态**:修改代码伪造已登录状态(如 userName 直接赋值)——'
|
|
108
|
-
'**注意:这不是真实功能状态**,仅解锁被门禁的功能验证(hn-android FS-41 先例),'
|
|
109
|
-
'需在用例/代码注释标明,且登录链路本身仍属未覆盖。')
|
|
110
|
-
reminders.append('登录域用例 BLOCKED 待解除:手动登录设备 / 提供测试账号 / mock 登录态(三选一,见 preconditions.md)')
|
|
111
|
-
else:
|
|
112
|
-
report.append('- 未检测到登录页,无需账号')
|
|
113
|
-
report.append('')
|
|
114
|
-
|
|
115
|
-
# ── 3. 首启弹窗预处置 ──
|
|
116
|
-
report.append('## 首启弹窗预处置(自动 dismiss 片段,注入 _common.launch)')
|
|
117
|
-
report.append('```python')
|
|
118
|
-
report.append('# 常见首启弹窗一键处理(在 launch 后 sleep 1 时执行)')
|
|
119
|
-
report.append('for txt in ["同意并继续", "允许", "我知道了", "暂不", "以后再说"]:')
|
|
120
|
-
report.append(' try:')
|
|
121
|
-
report.append(' c = d.find_component(BY.text(txt))')
|
|
122
|
-
report.append(' if c: c.click(); time.sleep(0.8)')
|
|
123
|
-
report.append(' except Exception: pass')
|
|
124
|
-
report.append('```')
|
|
125
|
-
report.append('')
|
|
126
|
-
|
|
127
|
-
if reminders:
|
|
128
|
-
report += ['## ⚠️ 用户提醒清单', *[f'- {r}' for r in reminders], '']
|
|
129
|
-
else:
|
|
130
|
-
report.append('## 用户提醒清单: 无(全部前置已自动完成)')
|
|
131
|
-
|
|
132
|
-
out_md = os.path.join(os.path.dirname(os.path.abspath(args.features)),
|
|
133
|
-
'preconditions.md')
|
|
134
|
-
open(out_md, 'w', encoding='utf-8').write('\n'.join(report))
|
|
135
|
-
print('\n'.join(report))
|
|
136
|
-
print(f'\n报告: {out_md}')
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
if __name__ == '__main__':
|
|
140
|
-
main()
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
precondition.py — 测试前置自动化:权限预授 + 登录/账号策略 + 首启弹窗处理。
|
|
4
|
+
|
|
5
|
+
原则(项目经验 2):
|
|
6
|
+
所有权限和账号默认优先完成确认/登录;无法自动完成的,生成明确的用户提醒清单
|
|
7
|
+
(而非留到用例执行中反复失败)。
|
|
8
|
+
|
|
9
|
+
动作:
|
|
10
|
+
1. 解析 module.json5 权限 → hdc pm grant 预授(按 ACL 级别分类:
|
|
11
|
+
normal/system_basic → 尝试预授;user_grant 运行时权限 → 模拟器上多数可 grant)
|
|
12
|
+
2. 登录页检测 → 三级策略:
|
|
13
|
+
a. mock 登录入口(源码含"模拟登录")→ 自动用之
|
|
14
|
+
b. UI 自动化登录(已知测试账号)→ 提醒用户提供或用环境变量
|
|
15
|
+
c. 不可自动 → 用户提醒清单
|
|
16
|
+
3. 首启弹窗预处置(隐私协议/权限弹窗)→ 输出 dismiss 脚本片段
|
|
17
|
+
|
|
18
|
+
用法:
|
|
19
|
+
precondition.py --features features.json --device 127.0.0.1:5555 [--user-account-env HM_TEST_ACCOUNT]
|
|
20
|
+
输出: preconditions.md(执行结果 + 用户提醒清单)
|
|
21
|
+
"""
|
|
22
|
+
import argparse
|
|
23
|
+
import json
|
|
24
|
+
import os
|
|
25
|
+
import re
|
|
26
|
+
import subprocess
|
|
27
|
+
import sys
|
|
28
|
+
|
|
29
|
+
HDC = '/Applications/DevEco-Studio.app/Contents/sdk/default/openharmony/toolchains/hdc'
|
|
30
|
+
|
|
31
|
+
# 鸿蒙权限 → 授予难度(默认可 grant / 需弹窗确认 / 系统级不可授)
|
|
32
|
+
GRANTABLE = re.compile(r'ohos\.permission\.(INTERNET|GET_NETWORK_INFO|KEEP_BACKGROUND_RUNNING|'
|
|
33
|
+
r'REQUIRE_FORM|PUBLISH_AGENT_REMINDER|VIBRATE|QUERY_AUDIO_DEVICE_STATE)')
|
|
34
|
+
# 运行时弹窗类(模拟器可 pm grant)
|
|
35
|
+
RUNTIME = re.compile(r'ohos\.permission\.(READ_|WRITE_|MICROPHONE|CAMERA|LOCATION|MEDIA|'
|
|
36
|
+
r'APPTRACKING|CALENDAR|CONTACTS|DISTRIBUTED)')
|
|
37
|
+
# 用户身份类(不可自动,需用户操作)
|
|
38
|
+
USER_IDENTITY = re.compile(r'ohos\.permission\.(USER_AUTH|MANAGE_FINGERPRINT|ACCESS_USER_AUTH|'
|
|
39
|
+
r'SECURE_SESSION|READ_HEALTH|SPORT)')
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def hdc(device, *args, capture=True):
|
|
43
|
+
cmd = [HDC, '-t', device] + list(args)
|
|
44
|
+
try:
|
|
45
|
+
r = subprocess.run(cmd, capture_output=capture, text=True, timeout=30)
|
|
46
|
+
return (r.stdout or '') + (r.stderr or '')
|
|
47
|
+
except Exception as e:
|
|
48
|
+
return f'ERROR: {e}'
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def main():
|
|
52
|
+
ap = argparse.ArgumentParser()
|
|
53
|
+
ap.add_argument('--features', required=True)
|
|
54
|
+
ap.add_argument('--device', default='127.0.0.1:5555')
|
|
55
|
+
ap.add_argument('--bundle', default=None)
|
|
56
|
+
ap.add_argument('--test-account-env', default='HM_TEST_ACCOUNT',
|
|
57
|
+
help='测试账号环境变量名(格式 user:pass)')
|
|
58
|
+
args = ap.parse_args()
|
|
59
|
+
|
|
60
|
+
feats = json.load(open(args.features, encoding='utf-8'))
|
|
61
|
+
bundle = args.bundle or feats.get('bundle')
|
|
62
|
+
if not bundle:
|
|
63
|
+
print('错误: 无法确定 bundleName', file=sys.stderr)
|
|
64
|
+
sys.exit(1)
|
|
65
|
+
|
|
66
|
+
report = [f'# 测试前置报告({bundle} @ {args.device})', '']
|
|
67
|
+
reminders = []
|
|
68
|
+
|
|
69
|
+
# ── 1. 权限预授 ──
|
|
70
|
+
report.append('## 权限预授')
|
|
71
|
+
ok, skipped, user_confirm = [], [], []
|
|
72
|
+
for perm in feats.get('permissions', []):
|
|
73
|
+
if USER_IDENTITY.search(perm):
|
|
74
|
+
user_confirm.append(perm)
|
|
75
|
+
continue
|
|
76
|
+
if GRANTABLE.search(perm) or RUNTIME.search(perm):
|
|
77
|
+
out = hdc(args.device, 'shell', 'atm', 'perm', '-g', bundle, perm)
|
|
78
|
+
granted = 'grant' in out.lower() and 'fail' not in out.lower()
|
|
79
|
+
if not granted:
|
|
80
|
+
out2 = hdc(args.device, 'shell', f'pm grant {bundle} {perm}')
|
|
81
|
+
granted = 'fail' not in out2.lower() and 'Exception' not in out2
|
|
82
|
+
(ok if granted else skipped).append(perm)
|
|
83
|
+
else:
|
|
84
|
+
skipped.append(perm)
|
|
85
|
+
report.append(f'- 已预授 {len(ok)}: {", ".join(ok) or "无"}')
|
|
86
|
+
if skipped:
|
|
87
|
+
report.append(f'- 跳过 {len(skipped)}(授予失败或需特殊签名): {", ".join(skipped)}')
|
|
88
|
+
if user_confirm:
|
|
89
|
+
report.append(f'- 需用户在设备上确认: {", ".join(user_confirm)}')
|
|
90
|
+
reminders.append(f'身份类权限需手动确认: {", ".join(user_confirm)}')
|
|
91
|
+
report.append('')
|
|
92
|
+
|
|
93
|
+
# ── 2. 登录策略(用户 2026-09-27 指正:不能止步于标 BLOCKED,必须提醒用户三选一)──
|
|
94
|
+
report.append('## 登录/账号策略')
|
|
95
|
+
if feats.get('has_login'):
|
|
96
|
+
acct = os.environ.get(args.test_account_env, '')
|
|
97
|
+
if acct:
|
|
98
|
+
report.append(f'- 检测到登录页;使用环境变量 {args.test_account_env} 提供的测试账号(自动登录用例将启用)')
|
|
99
|
+
else:
|
|
100
|
+
report.append('- 检测到登录页;**未设置测试账号(export '
|
|
101
|
+
f'{args.test_account_env}=user:pass)**。登录域用例暂标 BLOCKED,'
|
|
102
|
+
'**请从以下三种方式选择其一解除**:')
|
|
103
|
+
report.append(' 1. **手动登录**:在模拟器/真机上人工完成一次登录(登录态持久,最真实,'
|
|
104
|
+
'适合无需反复切换账号的场景)——完成后告知,BLOCKED 用例即可重跑;')
|
|
105
|
+
report.append(' 2. **提供账号密码**:给出测试账号,构造登录用例(输入账号密码→提交→断言登录成功),'
|
|
106
|
+
'登录链路本身也被覆盖——需 export 环境变量或直接告知;')
|
|
107
|
+
report.append(' 3. **mock 登录态**:修改代码伪造已登录状态(如 userName 直接赋值)——'
|
|
108
|
+
'**注意:这不是真实功能状态**,仅解锁被门禁的功能验证(hn-android FS-41 先例),'
|
|
109
|
+
'需在用例/代码注释标明,且登录链路本身仍属未覆盖。')
|
|
110
|
+
reminders.append('登录域用例 BLOCKED 待解除:手动登录设备 / 提供测试账号 / mock 登录态(三选一,见 preconditions.md)')
|
|
111
|
+
else:
|
|
112
|
+
report.append('- 未检测到登录页,无需账号')
|
|
113
|
+
report.append('')
|
|
114
|
+
|
|
115
|
+
# ── 3. 首启弹窗预处置 ──
|
|
116
|
+
report.append('## 首启弹窗预处置(自动 dismiss 片段,注入 _common.launch)')
|
|
117
|
+
report.append('```python')
|
|
118
|
+
report.append('# 常见首启弹窗一键处理(在 launch 后 sleep 1 时执行)')
|
|
119
|
+
report.append('for txt in ["同意并继续", "允许", "我知道了", "暂不", "以后再说"]:')
|
|
120
|
+
report.append(' try:')
|
|
121
|
+
report.append(' c = d.find_component(BY.text(txt))')
|
|
122
|
+
report.append(' if c: c.click(); time.sleep(0.8)')
|
|
123
|
+
report.append(' except Exception: pass')
|
|
124
|
+
report.append('```')
|
|
125
|
+
report.append('')
|
|
126
|
+
|
|
127
|
+
if reminders:
|
|
128
|
+
report += ['## ⚠️ 用户提醒清单', *[f'- {r}' for r in reminders], '']
|
|
129
|
+
else:
|
|
130
|
+
report.append('## 用户提醒清单: 无(全部前置已自动完成)')
|
|
131
|
+
|
|
132
|
+
out_md = os.path.join(os.path.dirname(os.path.abspath(args.features)),
|
|
133
|
+
'preconditions.md')
|
|
134
|
+
open(out_md, 'w', encoding='utf-8').write('\n'.join(report))
|
|
135
|
+
print('\n'.join(report))
|
|
136
|
+
print(f'\n报告: {out_md}')
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
if __name__ == '__main__':
|
|
140
|
+
main()
|