@buaa_smat/hometrans 0.1.0 → 0.1.2
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/README.md +141 -124
- package/agents/build-fixer.md +1 -0
- package/agents/code-review-fix.md +1 -0
- package/agents/code-reviewer.md +1 -0
- package/agents/logic-coding.md +1 -0
- package/agents/logic-context-builder.md +1 -0
- package/agents/review-fixer.md +1 -0
- package/agents/self-test-fixer.md +1 -0
- package/agents/self-tester.md +260 -233
- package/agents/spec-generator.md +1 -0
- package/agents/test-tools/autotest/README.md +223 -0
- package/agents/test-tools/autotest/config.yaml.example +58 -0
- package/agents/test-tools/autotest/pyproject.toml +16 -0
- package/agents/test-tools/autotest/report_tool.py +759 -0
- package/agents/test-tools/autotest/self_test_runner.py +773 -0
- package/agents/test-tools/autotest/testcases_schema.md +143 -0
- package/agents/test-tools/autotest/testcases_tool.py +215 -0
- package/agents/test-tools/autotest/uv.lock +3156 -0
- package/agents/test-tools/harmony_autotest-0.1.0-py3-none-any.whl +0 -0
- package/agents/test-tools/hypium-6.1.0.210-py3-none-any.whl +0 -0
- package/agents/test-tools/hypium_mcp-0.6.5-py3-none-any.whl +0 -0
- package/agents/test-tools/xdevice-6.1.0.210-py3-none-any.whl +0 -0
- package/agents/test-tools/xdevice_devicetest-6.1.0.210-py3-none-any.whl +0 -0
- package/agents/test-tools/xdevice_ohos-6.1.0.210-py3-none-any.whl +0 -0
- package/dist/cli/config-store.js +27 -2
- package/dist/cli/config.js +17 -6
- package/dist/cli/index.js +3 -2
- package/dist/cli/init.js +135 -22
- package/dist/cli/mcp.js +2 -2
- package/dist/context/index.js +165 -69
- package/package.json +59 -60
- package/skills/code-dev-review-fix/SKILL.md +279 -0
- package/skills/code-dev-review-fix-workspace/evals/evals.json +56 -0
- package/skills/code-dev-review-fix-workspace/iteration-1/routing-results.md +23 -0
- package/skills/convert_pipeline/SKILL.md +423 -439
- package/skills/hmos-resources-convert/SKILL.md +623 -0
- package/skills/hmos-resources-convert/evals/evals.json +171 -0
- package/skills/hmos-resources-convert/references/conversion-rules.md +663 -0
- package/skills/hmos-resources-convert/references/dependency-analysis-rules.md +388 -0
- package/skills/hmos-resources-convert/references/resource-mapping-rules.md +457 -0
- package/skills/hmos-resources-convert/references/xml-drawable-to-svg-rules.md +513 -0
- package/skills/hmos-resources-convert/template/AppScope/app.json5 +10 -0
- package/skills/hmos-resources-convert/template/AppScope/resources/base/element/string.json +8 -0
- package/skills/hmos-resources-convert/template/AppScope/resources/base/media/background.png +0 -0
- package/skills/hmos-resources-convert/template/AppScope/resources/base/media/foreground.png +0 -0
- package/skills/hmos-resources-convert/template/AppScope/resources/base/media/layered_image.json +7 -0
- package/skills/hmos-resources-convert/template/build-profile.json5 +42 -0
- package/skills/hmos-resources-convert/template/code-linter.json5 +32 -0
- package/skills/hmos-resources-convert/template/entry/build-profile.json5 +33 -0
- package/skills/hmos-resources-convert/template/entry/hvigorfile.ts +6 -0
- package/skills/hmos-resources-convert/template/entry/obfuscation-rules.txt +23 -0
- package/skills/hmos-resources-convert/template/entry/oh-package.json5 +10 -0
- package/skills/hmos-resources-convert/template/entry/src/main/ets/entryability/EntryAbility.ets +48 -0
- package/skills/hmos-resources-convert/template/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets +16 -0
- package/skills/hmos-resources-convert/template/entry/src/main/ets/pages/Index.ets +23 -0
- package/skills/hmos-resources-convert/template/entry/src/main/module.json5 +55 -0
- package/skills/hmos-resources-convert/template/entry/src/main/resources/base/element/color.json +8 -0
- package/skills/hmos-resources-convert/template/entry/src/main/resources/base/element/float.json +8 -0
- package/skills/hmos-resources-convert/template/entry/src/main/resources/base/element/string.json +16 -0
- package/skills/hmos-resources-convert/template/entry/src/main/resources/base/media/background.png +0 -0
- package/skills/hmos-resources-convert/template/entry/src/main/resources/base/media/foreground.png +0 -0
- package/skills/hmos-resources-convert/template/entry/src/main/resources/base/media/layered_image.json +7 -0
- package/skills/hmos-resources-convert/template/entry/src/main/resources/base/media/startIcon.png +0 -0
- package/skills/hmos-resources-convert/template/entry/src/main/resources/base/profile/backup_config.json +3 -0
- package/skills/hmos-resources-convert/template/entry/src/main/resources/base/profile/main_pages.json +5 -0
- package/skills/hmos-resources-convert/template/entry/src/main/resources/dark/element/color.json +8 -0
- package/skills/hmos-resources-convert/template/entry/src/mock/mock-config.json5 +2 -0
- package/skills/hmos-resources-convert/template/entry/src/ohosTest/ets/test/Ability.test.ets +35 -0
- package/skills/hmos-resources-convert/template/entry/src/ohosTest/ets/test/List.test.ets +5 -0
- package/skills/hmos-resources-convert/template/entry/src/ohosTest/module.json5 +16 -0
- package/skills/hmos-resources-convert/template/entry/src/test/List.test.ets +5 -0
- package/skills/hmos-resources-convert/template/entry/src/test/LocalUnit.test.ets +33 -0
- package/skills/hmos-resources-convert/template/hvigor/hvigor-config.json5 +23 -0
- package/skills/hmos-resources-convert/template/hvigorfile.ts +6 -0
- package/skills/hmos-resources-convert/template/oh-package-lock.json5 +28 -0
- package/skills/hmos-resources-convert/template/oh-package.json5 +10 -0
- package/skills/hmos-resources-convert/tools/apktool.bat +85 -0
- package/skills/hmos-resources-convert/tools/apktool_3.0.1.jar +0 -0
- package/skills/hmos-ui-align/SKILL.md +182 -0
- package/skills/hmos-ui-align/config-example.json +11 -0
- package/skills/hmos-ui-align/config.json +11 -0
- package/skills/hmos-ui-align/diff_analysis.md +53 -0
- package/skills/hmos-ui-align/page_align.md +62 -0
- package/skills/hmos-ui-align/readme.md +231 -0
- package/skills/hmos-ui-align/references/Comparison_Template.md +2 -0
- package/skills/hmos-ui-align/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/@Link/350/243/205/351/245/260/345/231/250/357/274/232/347/210/266/345/255/220/345/217/214/345/220/221/345/220/214/346/255/245.md +648 -0
- package/skills/hmos-ui-align/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/@Observed/350/243/205/351/245/260/345/231/250/345/222/214@ObjectLink/350/243/205/351/245/260/345/231/250/357/274/232/345/265/214/345/245/227/347/261/273/345/257/271/350/261/241/345/261/236/346/200/247/345/217/230/345/214/226.md +2089 -0
- package/skills/hmos-ui-align/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/@Prop/350/243/205/351/245/260/345/231/250/357/274/232/347/210/266/345/255/220/345/215/225/345/220/221/345/220/214/346/255/245.md +1033 -0
- package/skills/hmos-ui-align/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/@Provide/350/243/205/351/245/260/345/231/250/345/222/214@Consume/350/243/205/351/245/260/345/231/250/357/274/232/344/270/216/345/220/216/344/273/243/347/273/204/344/273/266/345/217/214/345/220/221/345/220/214/346/255/245.md +1183 -0
- package/skills/hmos-ui-align/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/@State/350/243/205/351/245/260/345/231/250/357/274/232/347/273/204/344/273/266/345/206/205/347/212/266/346/200/201.md +576 -0
- package/skills/hmos-ui-align/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/@Track/350/243/205/351/245/260/345/231/250/357/274/232class/345/257/271/350/261/241/345/261/236/346/200/247/347/272/247/346/233/264/346/226/260.md +297 -0
- package/skills/hmos-ui-align/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/@Watch/350/243/205/351/245/260/345/231/250/357/274/232/347/212/266/346/200/201/345/217/230/351/207/217/346/233/264/346/224/271/351/200/232/347/237/245.md +395 -0
- package/skills/hmos-ui-align/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/AppStorage/357/274/232/345/272/224/347/224/250/345/205/250/345/261/200/347/232/204UI/347/212/266/346/200/201/345/255/230/345/202/250.md +903 -0
- package/skills/hmos-ui-align/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/Environment/357/274/232/350/256/276/345/244/207/347/216/257/345/242/203/346/237/245/350/257/242.md +106 -0
- package/skills/hmos-ui-align/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/LocalStorage/357/274/232/351/241/265/351/235/242/347/272/247UI/347/212/266/346/200/201/345/255/230/345/202/250.md +1178 -0
- package/skills/hmos-ui-align/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/MVVM/346/250/241/345/274/217V1.md +911 -0
- package/skills/hmos-ui-align/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/MVVM/346/250/241/345/274/217/357/274/210V1/357/274/211.md +911 -0
- package/skills/hmos-ui-align/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/PersistentStorage/357/274/232/346/214/201/344/271/205/345/214/226/345/255/230/345/202/250UI/347/212/266/346/200/201.md +355 -0
- package/skills/hmos-ui-align/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243//347/256/241/347/220/206/345/272/224/347/224/250/346/213/245/346/234/211/347/232/204/347/212/266/346/200/201/346/246/202/350/277/260.md +11 -0
- package/skills/hmos-ui-align/references/UI_Analysis_Template.md +4 -0
- package/skills/hmos-ui-align/references/android-to-harmonyOS-ui-atomic-component-mapping-reference.md +2535 -0
- package/skills/hmos-ui-align/references/android-to-harmonyOS-ui-interaction-mapping-reference.md +555 -0
- package/skills/hmos-ui-align/references/android-to-harmonyOS-ui-layout-mapping-reference.md +117 -0
- package/skills/hmos-ui-align/scripts/app_feature_verify.py +443 -0
- package/skills/hmos-ui-align/scripts/navigation-capure.md +37 -0
- package/skills/hmos-ui-align/scripts/page_capture.py +592 -0
- package/skills/hmos-ui-align-batch/SKILL.md +99 -0
- package/skills/hmos-ui-align-batch/references/conversion-procedure.md +180 -0
- package/skills/hmos-ui-align-batch/references/mappings/android-to-harmonyOS-ui-atomic-component-mapping-reference.md +2535 -0
- package/skills/hmos-ui-align-batch/references/mappings/android-to-harmonyOS-ui-interaction-mapping-reference.md +555 -0
- package/skills/hmos-ui-align-batch/references/mappings/android-to-harmonyOS-ui-layout-mapping-reference.md +117 -0
- package/skills/hmos-ui-align-batch/references/mvvm/@Link/350/243/205/351/245/260/345/231/250/357/274/232/347/210/266/345/255/220/345/217/214/345/220/221/345/220/214/346/255/245.md +648 -0
- package/skills/hmos-ui-align-batch/references/mvvm/@Observed/350/243/205/351/245/260/345/231/250/345/222/214@ObjectLink/350/243/205/351/245/260/345/231/250/357/274/232/345/265/214/345/245/227/347/261/273/345/257/271/350/261/241/345/261/236/346/200/247/345/217/230/345/214/226.md +2089 -0
- package/skills/hmos-ui-align-batch/references/mvvm/@Prop/350/243/205/351/245/260/345/231/250/357/274/232/347/210/266/345/255/220/345/215/225/345/220/221/345/220/214/346/255/245.md +1033 -0
- package/skills/hmos-ui-align-batch/references/mvvm/@Provide/350/243/205/351/245/260/345/231/250/345/222/214@Consume/350/243/205/351/245/260/345/231/250/357/274/232/344/270/216/345/220/216/344/273/243/347/273/204/344/273/266/345/217/214/345/220/221/345/220/214/346/255/245.md +1183 -0
- package/skills/hmos-ui-align-batch/references/mvvm/@State/350/243/205/351/245/260/345/231/250/357/274/232/347/273/204/344/273/266/345/206/205/347/212/266/346/200/201.md +576 -0
- package/skills/hmos-ui-align-batch/references/mvvm/@Track/350/243/205/351/245/260/345/231/250/357/274/232class/345/257/271/350/261/241/345/261/236/346/200/247/347/272/247/346/233/264/346/226/260.md +297 -0
- package/skills/hmos-ui-align-batch/references/mvvm/@Watch/350/243/205/351/245/260/345/231/250/357/274/232/347/212/266/346/200/201/345/217/230/351/207/217/346/233/264/346/224/271/351/200/232/347/237/245.md +395 -0
- package/skills/hmos-ui-align-batch/references/mvvm/AppStorage/357/274/232/345/272/224/347/224/250/345/205/250/345/261/200/347/232/204UI/347/212/266/346/200/201/345/255/230/345/202/250.md +903 -0
- package/skills/hmos-ui-align-batch/references/mvvm/Environment/357/274/232/350/256/276/345/244/207/347/216/257/345/242/203/346/237/245/350/257/242.md +106 -0
- package/skills/hmos-ui-align-batch/references/mvvm/LocalStorage/357/274/232/351/241/265/351/235/242/347/272/247UI/347/212/266/346/200/201/345/255/230/345/202/250.md +1178 -0
- package/skills/hmos-ui-align-batch/references/mvvm/MVVM/346/250/241/345/274/217/357/274/210V1/357/274/211.md +911 -0
- package/skills/hmos-ui-align-batch/references/mvvm/PersistentStorage/357/274/232/346/214/201/344/271/205/345/214/226/345/255/230/345/202/250UI/347/212/266/346/200/201.md +355 -0
- package/skills/hmos-ui-align-batch/references/mvvm//347/256/241/347/220/206/345/272/224/347/224/250/346/213/245/346/234/211/347/232/204/347/212/266/346/200/201/346/246/202/350/277/260.md +11 -0
- package/skills/hmos-ui-align-batch/scripts/android_parse_fast.py +1606 -0
- package/skills/self-test/SKILL.md +369 -0
- package/skills/self-test/readme.md +309 -0
- package/skills/spec-generator-skill/SKILL.md +332 -0
- package/skills/spec-generator-skill/references/android-platform-tokens.md +105 -0
- package/skills/spec-generator-skill/references/spec-sample-1.md +78 -0
- package/skills/spec-generator-skill/references/spec-sample-2.md +58 -0
- package/skills/spec-generator-skill/references/spec-sample-3.md +116 -0
- package/skills/spec-generator-skill/references/step4-report-template.md +33 -0
- package/agents/self-test-setup.md +0 -165
- package/dist/context/resources/sdkConfig.json +0 -24
- package/src/context/resources/sdkConfig.json +0 -24
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# testcases.json 生成规范
|
|
2
|
+
|
|
3
|
+
> 本文件是 `testcases.json` 的**唯一权威格式定义**。
|
|
4
|
+
> 生成 `testcases.json` 时,必须严格遵循本规范。
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 1. 文件结构
|
|
9
|
+
|
|
10
|
+
根元素必须是 **JSON 数组 `[]`**,每个元素是一个测试用例对象。
|
|
11
|
+
|
|
12
|
+
```json
|
|
13
|
+
[
|
|
14
|
+
{ ... },
|
|
15
|
+
{ ... }
|
|
16
|
+
]
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## 2. 测试用例对象(4 个必填字段)
|
|
20
|
+
|
|
21
|
+
| 字段 | 类型 | 说明 | 示例 |
|
|
22
|
+
|------|------|------|------|
|
|
23
|
+
| `case_name` | `string` | 测试场景标题,来自 `### Scenario:` | `"冷启动初始化后正常进入主页面"` |
|
|
24
|
+
| `bundle_name` | `string` | HarmonyOS 应用包名 | `"com.example.tuku"` |
|
|
25
|
+
| `app_name` | `string` | 应用显示名称(不要翻译,保持原始值) | `"Tuku"` |
|
|
26
|
+
| `test_steps` | `string` | 测试步骤文本(见下方模板) | 见第 3 节 |
|
|
27
|
+
|
|
28
|
+
> **不允许**出现其他字段(如 `id`、`priority`、`preconditions`、`spec_id` 等)。
|
|
29
|
+
|
|
30
|
+
## 3. `test_steps` 字符串模板
|
|
31
|
+
|
|
32
|
+
每个测试用例的 `test_steps` 必须严格按以下模板生成:
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
动作:{actions}
|
|
36
|
+
预期结果:{expected_results}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`test_steps` 中**只包含** `动作:` 和 `预期结果:` 两行,不包含任何段落头(如 `【说明】`、`【待测应用信息】`、`【测试用例】` 等)。
|
|
40
|
+
|
|
41
|
+
### 变量说明
|
|
42
|
+
|
|
43
|
+
| 变量 | 来源 | 处理规则 |
|
|
44
|
+
|------|------|----------|
|
|
45
|
+
| `{actions}` | `test_case.md` 中的 `- 动作:` 行 | 所有应用名引用(如"图库应用"、"Tuku"等)替换为 `bundle_name` |
|
|
46
|
+
| `{expected_results}` | `test_case.md` 中的 `- 预期结果:` 行 | 原样使用,同样执行 app_name → bundle_name 替换 |
|
|
47
|
+
|
|
48
|
+
## 4. 完整示例
|
|
49
|
+
|
|
50
|
+
```json
|
|
51
|
+
[
|
|
52
|
+
{
|
|
53
|
+
"case_name": "冷启动初始化后正常进入主页面",
|
|
54
|
+
"bundle_name": "com.example.tuku",
|
|
55
|
+
"app_name": "Tuku",
|
|
56
|
+
"test_steps": "动作:打开com.example.tuku\n预期结果:文件夹网格主页面成功展示文件夹列表"
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"case_name": "首次打开应用-授权通过-展示所有文件夹",
|
|
60
|
+
"bundle_name": "com.example.tuku",
|
|
61
|
+
"app_name": "Tuku",
|
|
62
|
+
"test_steps": "动作:打开com.example.tuku -> 点击允许按钮授予媒体访问权限\n预期结果:权限授予成功,跳转到文件夹网格主页面,可见所有包含媒体的文件夹"
|
|
63
|
+
}
|
|
64
|
+
]
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## 5. 禁止字符串
|
|
68
|
+
|
|
69
|
+
以下字符串**不得出现在 `test_steps` 中的任何位置**。如果 `test_case.md` 源文件中包含这些内容,提取时必须删除或改写:
|
|
70
|
+
|
|
71
|
+
| 禁止字符串 | 原因 | 处理方式 |
|
|
72
|
+
|------------|------|----------|
|
|
73
|
+
| `前置条件` | 前置条件不应进入测试步骤 | 删除含该词的句子 |
|
|
74
|
+
| `Spec:` | Spec 引用不应出现 | 删除 |
|
|
75
|
+
| `Scenario:` | Scenario 引用不应出现 | 删除 |
|
|
76
|
+
| `ability_name:` | 不允许出现在 test_steps 中 | 删除 |
|
|
77
|
+
| `module_name:` | 不允许出现在 test_steps 中 | 删除 |
|
|
78
|
+
|
|
79
|
+
> **不得出现任何 `【xxx】` 段落头**(如 `【说明】`、`【待测应用信息】`、`【测试用例】`、`【前置条件】` 等)。`test_steps` 只包含 `动作:` 和 `预期结果:` 两行。
|
|
80
|
+
|
|
81
|
+
## 6. 错误格式示例
|
|
82
|
+
|
|
83
|
+
### 错误 1:使用结构化对象数组代替纯文本字符串
|
|
84
|
+
|
|
85
|
+
```json
|
|
86
|
+
{
|
|
87
|
+
"test_steps": [
|
|
88
|
+
{ "step": 1, "action": "launch_app", "params": { "bundle_name": "..." } }
|
|
89
|
+
]
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
**正确做法**: `test_steps` 必须是字符串,不是数组。
|
|
93
|
+
|
|
94
|
+
### 错误 2:根元素是对象而非数组
|
|
95
|
+
|
|
96
|
+
```json
|
|
97
|
+
{
|
|
98
|
+
"spec_id": "SPEC-01",
|
|
99
|
+
"test_cases": [ ... ]
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
**正确做法**: 根元素必须是 `[ ... ]`。
|
|
103
|
+
|
|
104
|
+
### 错误 3:包含段落头
|
|
105
|
+
|
|
106
|
+
```json
|
|
107
|
+
{
|
|
108
|
+
"test_steps": "【说明】\n...\n\n【待测应用信息】\napp_name: Tuku\nbundle_name: com.example.tuku\n\n【测试用例】\n动作:...\n预期结果:..."
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
**正确做法**: `test_steps` 中不允许任何 `【xxx】` 段落头,只包含 `动作:` 和 `预期结果:` 两行。
|
|
112
|
+
|
|
113
|
+
### 错误 4:包含额外字段
|
|
114
|
+
|
|
115
|
+
```json
|
|
116
|
+
{
|
|
117
|
+
"id": "TC-001",
|
|
118
|
+
"priority": "P0",
|
|
119
|
+
"preconditions": ["..."],
|
|
120
|
+
"case_name": "...",
|
|
121
|
+
"test_steps": "..."
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
**正确做法**: 只允许 `case_name`、`bundle_name`、`app_name`、`test_steps` 四个字段。
|
|
125
|
+
|
|
126
|
+
### 错误 5:缺少必要行
|
|
127
|
+
|
|
128
|
+
```json
|
|
129
|
+
{
|
|
130
|
+
"test_steps": "打开图库应用 -> 检查主页面"
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
**正确做法**: 必须包含 `动作:` 和 `预期结果:` 两行。
|
|
134
|
+
|
|
135
|
+
## 7. 验证
|
|
136
|
+
|
|
137
|
+
生成后必须运行验证脚本:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
cd "<autotest_dir>" && uv run python testcases_tool.py validate "<path-to-testcases.json>"
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
只有输出 `VALIDATION PASSED` 才算生成成功。
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Generate or validate testcases.json for the Self-Testing Agent.
|
|
3
|
+
|
|
4
|
+
Subcommands:
|
|
5
|
+
generate <input-json> <output-json> [--validate]
|
|
6
|
+
Rewrite app_name → bundle_name and compose `test_steps` strictly
|
|
7
|
+
("动作:..." / "预期结果:..."). Optionally re-validate the output.
|
|
8
|
+
|
|
9
|
+
validate <testcases-json>
|
|
10
|
+
Check a `testcases.json` (array of cases) against the schema in
|
|
11
|
+
`testcases_schema.md`. Exit 0 = PASSED, exit 1 = FAILED.
|
|
12
|
+
|
|
13
|
+
The forbidden-strings and section-header rules live in this file as the single
|
|
14
|
+
source of truth — generate enforces them at write time, validate re-checks them
|
|
15
|
+
on existing files.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import argparse
|
|
19
|
+
import json
|
|
20
|
+
import os
|
|
21
|
+
import re
|
|
22
|
+
import sys
|
|
23
|
+
|
|
24
|
+
# Ensure UTF-8 output on Windows
|
|
25
|
+
os.environ.setdefault("PYTHONIOENCODING", "utf-8")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# =============================================================================
|
|
29
|
+
# Shared schema invariants — used by both generate and validate
|
|
30
|
+
# =============================================================================
|
|
31
|
+
|
|
32
|
+
# Substrings that must not appear anywhere in `test_steps`. Keep the validator
|
|
33
|
+
# in lock-step with the generator: anything that the generator refuses to emit
|
|
34
|
+
# must also be rejected by the validator.
|
|
35
|
+
FORBIDDEN_STRINGS = ("前置条件", "Spec:", "Scenario:", "ability_name:", "module_name:")
|
|
36
|
+
|
|
37
|
+
# `test_steps` must contain only the two lines starting with `动作:` and
|
|
38
|
+
# `预期结果:`. `【…】` section headers (e.g. 【说明】, 【待测应用信息】) are forbidden.
|
|
39
|
+
SECTION_HEADER_RE = re.compile(r"【(.+?)】")
|
|
40
|
+
|
|
41
|
+
REQUIRED_LINES = ("动作:", "预期结果:")
|
|
42
|
+
|
|
43
|
+
TEST_STEPS_TEMPLATE = """\
|
|
44
|
+
动作:{actions}
|
|
45
|
+
预期结果:{expected_results}"""
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# =============================================================================
|
|
49
|
+
# Generate
|
|
50
|
+
# =============================================================================
|
|
51
|
+
|
|
52
|
+
def rewrite_app_name(text: str, app_name: str, bundle_name: str) -> str:
|
|
53
|
+
"""Safety net: replace remaining app_name references with bundle_name."""
|
|
54
|
+
if app_name and app_name in text:
|
|
55
|
+
text = text.replace(app_name, bundle_name)
|
|
56
|
+
return text
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def compose_test_steps(app_name: str, bundle_name: str,
|
|
60
|
+
actions: str, expected_results: str) -> str:
|
|
61
|
+
"""Compose a compliant test_steps string with only 动作 and 预期结果."""
|
|
62
|
+
rewritten_actions = rewrite_app_name(actions, app_name, bundle_name)
|
|
63
|
+
rewritten_expected = rewrite_app_name(expected_results, app_name, bundle_name)
|
|
64
|
+
|
|
65
|
+
test_steps = TEST_STEPS_TEMPLATE.format(
|
|
66
|
+
actions=rewritten_actions,
|
|
67
|
+
expected_results=rewritten_expected,
|
|
68
|
+
)
|
|
69
|
+
# Final safety: rewrite once more in case app_name only became visible after composition.
|
|
70
|
+
test_steps = rewrite_app_name(test_steps, app_name, bundle_name)
|
|
71
|
+
|
|
72
|
+
for forbidden in FORBIDDEN_STRINGS:
|
|
73
|
+
if forbidden in test_steps:
|
|
74
|
+
raise ValueError(
|
|
75
|
+
f"FATAL: forbidden string '{forbidden}' found in generated test_steps. "
|
|
76
|
+
f"This is a bug in testcases_tool.py."
|
|
77
|
+
)
|
|
78
|
+
for section in SECTION_HEADER_RE.findall(test_steps):
|
|
79
|
+
raise ValueError(
|
|
80
|
+
f"FATAL: unexpected section header 【{section}】 in generated test_steps. "
|
|
81
|
+
f"test_steps should only contain 动作 and 预期结果."
|
|
82
|
+
)
|
|
83
|
+
return test_steps
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def generate(input_data: dict) -> list[dict]:
|
|
87
|
+
"""Generate compliant testcases array from input data."""
|
|
88
|
+
bundle_name = input_data["bundle_name"]
|
|
89
|
+
app_name = input_data["app_name"]
|
|
90
|
+
cases = input_data["cases"]
|
|
91
|
+
|
|
92
|
+
result = []
|
|
93
|
+
for case in cases:
|
|
94
|
+
test_steps = compose_test_steps(
|
|
95
|
+
app_name=app_name,
|
|
96
|
+
bundle_name=bundle_name,
|
|
97
|
+
actions=case["actions"],
|
|
98
|
+
expected_results=case["expected_results"],
|
|
99
|
+
)
|
|
100
|
+
result.append({
|
|
101
|
+
"case_name": case["case_name"],
|
|
102
|
+
"bundle_name": bundle_name,
|
|
103
|
+
"app_name": app_name,
|
|
104
|
+
"test_steps": test_steps,
|
|
105
|
+
})
|
|
106
|
+
return result
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
# =============================================================================
|
|
110
|
+
# Validate
|
|
111
|
+
# =============================================================================
|
|
112
|
+
|
|
113
|
+
def validate_testcases(filepath: str) -> list[str]:
|
|
114
|
+
"""Return a list of validation error messages. Empty list means PASSED."""
|
|
115
|
+
with open(filepath, "r", encoding="utf-8") as f:
|
|
116
|
+
cases = json.load(f)
|
|
117
|
+
|
|
118
|
+
if not isinstance(cases, list):
|
|
119
|
+
return ["Root element is not a JSON array"]
|
|
120
|
+
|
|
121
|
+
errors: list[str] = []
|
|
122
|
+
for i, c in enumerate(cases):
|
|
123
|
+
ts = c.get("test_steps", "")
|
|
124
|
+
name = c.get("case_name", f"case_{i}")
|
|
125
|
+
app_name = c.get("app_name", "")
|
|
126
|
+
bundle_name = c.get("bundle_name", "")
|
|
127
|
+
prefix = f"Case {i + 1} [{name}]"
|
|
128
|
+
|
|
129
|
+
# Required lines
|
|
130
|
+
for required in REQUIRED_LINES:
|
|
131
|
+
if required not in ts:
|
|
132
|
+
errors.append(f"{prefix}: MISSING {required} line")
|
|
133
|
+
|
|
134
|
+
# Forbidden substrings (single source of truth shared with the generator)
|
|
135
|
+
for forbidden in FORBIDDEN_STRINGS:
|
|
136
|
+
if forbidden in ts:
|
|
137
|
+
errors.append(f"{prefix}: CONTAINS {forbidden} (must be excluded)")
|
|
138
|
+
|
|
139
|
+
# Forbidden section headers
|
|
140
|
+
for section in SECTION_HEADER_RE.findall(ts):
|
|
141
|
+
errors.append(
|
|
142
|
+
f"{prefix}: CONTAINS unexpected section header 【{section}】 "
|
|
143
|
+
f"(test_steps should only contain 动作 and 预期结果)"
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
# App name must be rewritten to bundle_name
|
|
147
|
+
if app_name and bundle_name and app_name != bundle_name and app_name in ts:
|
|
148
|
+
errors.append(
|
|
149
|
+
f"{prefix}: test_steps contains app_name '{app_name}' instead of bundle_name "
|
|
150
|
+
f"'{bundle_name}'. All app name references in test_steps must use bundle_name."
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
return errors
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
# =============================================================================
|
|
157
|
+
# CLI
|
|
158
|
+
# =============================================================================
|
|
159
|
+
|
|
160
|
+
def cmd_generate(args) -> None:
|
|
161
|
+
with open(args.input, "r", encoding="utf-8") as f:
|
|
162
|
+
input_data = json.load(f)
|
|
163
|
+
|
|
164
|
+
testcases = generate(input_data)
|
|
165
|
+
|
|
166
|
+
with open(args.output, "w", encoding="utf-8") as f:
|
|
167
|
+
json.dump(testcases, f, ensure_ascii=False, indent=2)
|
|
168
|
+
|
|
169
|
+
print(f"Generated {len(testcases)} test cases -> {args.output}")
|
|
170
|
+
|
|
171
|
+
if args.validate:
|
|
172
|
+
errors = validate_testcases(args.output)
|
|
173
|
+
if errors:
|
|
174
|
+
print("VALIDATION FAILED:")
|
|
175
|
+
for e in errors:
|
|
176
|
+
print(f" [FAIL] {e}")
|
|
177
|
+
sys.exit(1)
|
|
178
|
+
print(f"VALIDATION PASSED: all {len(testcases)} cases conform to the schema")
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def cmd_validate(args) -> None:
|
|
182
|
+
errors = validate_testcases(args.path)
|
|
183
|
+
if errors:
|
|
184
|
+
print("VALIDATION FAILED:")
|
|
185
|
+
for e in errors:
|
|
186
|
+
print(f" [FAIL] {e}")
|
|
187
|
+
sys.exit(1)
|
|
188
|
+
with open(args.path, "r", encoding="utf-8") as f:
|
|
189
|
+
cases = json.load(f)
|
|
190
|
+
print(f"VALIDATION PASSED: all {len(cases)} cases have correct test_steps format")
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def main() -> None:
|
|
194
|
+
parser = argparse.ArgumentParser(
|
|
195
|
+
description="Generate or validate testcases.json for the Self-Testing Agent."
|
|
196
|
+
)
|
|
197
|
+
sub = parser.add_subparsers(dest="command", required=True)
|
|
198
|
+
|
|
199
|
+
p_gen = sub.add_parser("generate", help="Generate compliant testcases.json from an extracted input")
|
|
200
|
+
p_gen.add_argument("input", help="Path to the extracted input JSON")
|
|
201
|
+
p_gen.add_argument("output", help="Path to write the compliant testcases.json")
|
|
202
|
+
p_gen.add_argument("--validate", action="store_true",
|
|
203
|
+
help="After writing, re-validate the output against the schema")
|
|
204
|
+
p_gen.set_defaults(func=cmd_generate)
|
|
205
|
+
|
|
206
|
+
p_val = sub.add_parser("validate", help="Validate an existing testcases.json against the schema")
|
|
207
|
+
p_val.add_argument("path", help="Path to the testcases.json to validate")
|
|
208
|
+
p_val.set_defaults(func=cmd_validate)
|
|
209
|
+
|
|
210
|
+
args = parser.parse_args()
|
|
211
|
+
args.func(args)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
if __name__ == "__main__":
|
|
215
|
+
main()
|