skills/check-test-code-quality/rules/R014/SKILL.md
# R014: 测试HAP命名不规范 ## 规则信息 | 属性 | 值 | |------|-----| | 规则编号 | R014 | | 问题类型 | 测试HAP命名不规范 | | 严重级别 | Critical | | 规则复杂度 | simple | | 扫描范围 | BUILD.gn文件中的ohos_js_app_suite、ohos_js_app_static_suite、ohos_moduletest_suite模板 | | testcase字段 | `-`(BUILD.gn为非测试文件,无对应it()块) | ## 问题描述 测试HAP命名不符合规范要求。hap包命名采用大驼峰方式。 ## 命名规范 | 模板类型 | 检测目标 | 命名规范 | 正确示例 | |---------|---------|---------|---------| | `ohos_js_app_suite` | `hap_name` | 以`Acts`开头(A大写),以`Test`结尾(T大写) | `ActsAbilityTest` | | `ohos_js_app_static
npx skillsauth add openharmonyinsight/openharmony-skills skills/check-test-code-quality/rules/R014Install this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
| 属性 | 值 |
|------|-----|
| 规则编号 | R014 |
| 问题类型 | 测试HAP命名不规范 |
| 严重级别 | Critical |
| 规则复杂度 | simple |
| 扫描范围 | BUILD.gn文件中的ohos_js_app_suite、ohos_js_app_static_suite、ohos_moduletest_suite模板 |
| testcase字段 | -(BUILD.gn为非测试文件,无对应it()块) |
测试HAP命名不符合规范要求。hap包命名采用大驼峰方式。
| 模板类型 | 检测目标 | 命名规范 | 正确示例 |
|---------|---------|---------|---------|
| ohos_js_app_suite | hap_name | 以Acts开头(A大写),以Test结尾(T大写) | ActsAbilityTest |
| ohos_js_app_static_suite | hap_name | 以Acts开头(A大写),以StaticTest结尾(S、T大写) | ActsAbilityStaticTest |
| ohos_moduletest_suite | 模板名称XXXX | 以Acts开头(A大写),以Test结尾(T大写) | ActsModuleTest |
只修改hap_name,不修改target名称。 target名称与上层BUILD.gn中deps字段的引用保持一致,与hap_name无直接关系。
必须修改target名称。 ohos_moduletest_suite没有hap_name字段,target名称就是输出文件名。
名称中包含validator的不视为问题(如ActsValidatorTest是合法的)。
使用状态机方法解析BUILD.gn文件,识别三种模板类型并提取对应的检测目标。
import re
import os
TEMPLATE_TYPES = {
'ohos_js_app_suite': {
'pattern': re.compile(r'ohos_js_app_suite\s*\(\s*["\']([^"\']+)["\']'),
'check_field': 'hap_name',
'must_start': 'Acts',
'must_end': 'Test',
},
'ohos_js_app_static_suite': {
'pattern': re.compile(r'ohos_js_app_static_suite\s*\(\s*["\']([^"\']+)["\']'),
'check_field': 'hap_name',
'must_start': 'Acts',
'must_end': 'StaticTest',
},
'ohos_moduletest_suite': {
'pattern': re.compile(r'ohos_moduletest_suite\s*\(\s*["\']([^"\']+)["\']'),
'check_field': 'target_name',
'must_start': 'Acts',
'must_end': 'Test',
},
}
HAP_NAME_PATTERN = re.compile(r'hap_name\s*=\s*["\']([^"\']+)["\']')
对BUILD.gn文件进行逐行扫描,使用状态机跟踪当前所在的模板块。
def parse_build_gn_templates(content):
templates = []
lines = content.split('\n')
state = 'IDLE'
current_template = None
brace_depth = 0
template_start_line = 0
for line_num, line in enumerate(lines, 1):
stripped = line.strip()
if state == 'IDLE':
for tname, tinfo in TEMPLATE_TYPES.items():
match = tinfo['pattern'].search(line)
if match:
target_name = match.group(1)
current_template = {
'type': tname,
'target_name': target_name,
'target_line': line_num,
'hap_name': None,
'hap_name_line': 0,
}
state = 'IN_TEMPLATE'
template_start_line = line_num
brace_depth = line.count('{') - line.count('}')
break
elif state == 'IN_TEMPLATE':
brace_depth += line.count('{') - line.count('}')
hap_match = HAP_NAME_PATTERN.search(line)
if hap_match:
current_template['hap_name'] = hap_match.group(1)
current_template['hap_name_line'] = line_num
if brace_depth <= 0:
templates.append(current_template)
current_template = None
state = 'IDLE'
return templates
def validate_naming(template):
tname = template['type']
tinfo = TEMPLATE_TYPES[tname]
if tinfo['check_field'] == 'hap_name':
name = template['hap_name']
report_line = template['hap_name_line']
else:
name = template['target_name']
report_line = template['target_line']
if not name:
return None
if 'validator' in name.lower():
return None
must_start = tinfo['must_start']
must_end = tinfo['must_end']
starts_ok = name.startswith(must_start)
ends_ok = name.endswith(must_end)
if starts_ok and ends_ok:
return None
errors = []
if not starts_ok:
errors.append(f'不以"{must_start}"开头')
if not ends_ok:
errors.append(f'不以"{must_end}"结尾')
return {
'name': name,
'line': report_line,
'errors': errors,
'template_type': tname,
'check_field': tinfo['check_field'],
}
def generate_fix_suggestion(template, validation):
name = validation['name']
tname = validation['template_type']
check_field = validation['check_field']
tinfo = TEMPLATE_TYPES[tname]
errors = validation['errors']
must_start = tinfo['must_start']
must_end = tinfo['must_end']
fixed_name = name
if not fixed_name.startswith(must_start):
fixed_name = must_start + fixed_name[0].upper() + fixed_name[1:]
if not fixed_name.endswith(must_end):
fixed_name = fixed_name + 'Adapt001' + must_end
if check_field == 'hap_name':
target_info = f"target名称 \"{template['target_name']}\" 保持不变,只修改hap_name"
else:
target_info = f"target名称 \"{template['target_name']}\" 需要修改为 \"{fixed_name}\""
return (
f"{check_field} \"{name}\" 不符合规范:{', '.join(errors)}。"
f"修复: {check_field}改为 \"{fixed_name}\"。{target_info}。"
)
def scan_r014(scan_root, base_dir):
issues = []
for dirpath, dirnames, filenames in os.walk(scan_root):
if 'BUILD.gn' not in filenames:
continue
build_gn_path = os.path.join(dirpath, 'BUILD.gn')
with open(build_gn_path, 'r', encoding='utf-8') as f:
content = f.read()
templates = parse_build_gn_templates(content)
for template in templates:
validation = validate_naming(template)
if validation is None:
continue
rel_path = os.path.relpath(build_gn_path, base_dir)
suggestion = generate_fix_suggestion(template, validation)
if validation['check_field'] == 'hap_name':
snippet = f'hap_name = "{validation["name"]}"'
else:
snippet = f'{template["type"]}("{validation["name"]}")'
issues.append({
'rule': 'R014',
'type': '测试HAP命名不规范',
'severity': 'Critical',
'file': rel_path,
'line': validation['line'],
'testcase': '-',
'snippet': snippet,
'suggestion': suggestion,
})
return issues
ohos_js_app_suite("mytest") { # target名称,保持不变
testonly = true
part_name = "ace_engine"
subsystem_name = "arkui"
hap_name = "actsability" # ✗ 错误:不以Acts开头,不以Test结尾
}
ohos_js_app_static_suite("statictest") { # target名称,保持不变
testonly = true
part_name = "ace_engine"
subsystem_name = "arkui"
hap_name = "ActsTestSuite" # ✗ 错误:不以StaticTest结尾
}
# ohos_moduletest_suite没有hap_name字段,target名称就是输出文件名
ohos_moduletest_suite("mymoduletest") { # ✗ 错误:不以Acts开头,不以Test结尾
testonly = true
part_name = "ace_engine"
subsystem_name = "arkui"
}
ohos_js_app_suite("test2") {
hap_name = "actsTest" # ✗ 错误:A应该大写
}
ohos_js_app_static_suite("test3") {
hap_name = "Actsstatictest" # ✗ 错误:S、T应该大写
}
# target名称与hap_name可以不同,修复时只修改hap_name
ohos_js_app_suite("mytest") { # target名称,保持不变
testonly = true
part_name = "ace_engine"
subsystem_name = "arkui"
hap_name = "ActsAbilityAdapt001Test" # ✓ 正确:以Acts开头,以Test结尾
}
ohos_js_app_static_suite("statictest") { # target名称,保持不变
testonly = true
part_name = "ace_engine"
subsystem_name = "arkui"
hap_name = "ActsAbilityAdapt001StaticTest" # ✓ 正确:以Acts开头,以StaticTest结尾
}
# ohos_moduletest_suite没有hap_name字段,target名称需要符合规范
ohos_moduletest_suite("ActsMyModuleAdapt001Test") { # ✓ 正确
testonly = true
part_name = "ace_engine"
subsystem_name = "arkui"
}
ohos_js_app_suite("valtest") {
hap_name = "ActsValidatorTest" # ✓ 不是问题:包含validator
}
修复前:
# BUILD.gn
ohos_js_app_suite("mytest") { # target名称,保持不变
testonly = true
part_name = "ace_engine"
subsystem_name = "arkui"
hap_name = "actsability" # ✗ 不符合规范
}
# Test.json
{
"test-file-name": ["actsability.hap"]
}
修复后:
# BUILD.gn
ohos_js_app_suite("mytest") { # target名称,保持不变
testonly = true
part_name = "ace_engine"
subsystem_name = "arkui"
hap_name = "ActsAbilityAdapt001Test" # ✓ 只修改hap_name
}
# Test.json
{
"test-file-name": ["ActsAbilityAdapt001Test.hap"]
}
修复前:
# BUILD.gn
ohos_moduletest_suite("mymoduletest") { # ✗ 不符合规范
testonly = true
part_name = "ace_engine"
subsystem_name = "arkui"
}
# Test.json
{
"kits": [{
"push": ["mymoduletest->/data/local/tmp/mymoduletest"]
}],
"driver": {
"module-name": "mymoduletest"
}
}
修复后:
# BUILD.gn
ohos_moduletest_suite("ActsMyModuleAdapt001Test") { # ✓ 修改target名称
testonly = true
part_name = "ace_engine"
subsystem_name = "arkui"
}
# Test.json
{
"kits": [{
"push": ["ActsMyModuleAdapt001Test->/data/local/tmp/ActsMyModuleAdapt001Test"]
}],
"driver": {
"module-name": "ActsMyModuleAdapt001Test"
}
}
test-file-name、push路径、module-name等)validator的hap名称不视为问题每条issue的字段:
| 字段 | 值 |
|------|-----|
| rule | R014 |
| type | 测试HAP命名不规范 |
| severity | Critical |
| file | 相对路径(如xxx/BUILD.gn) |
| line | hap_name或target名称所在行号 |
| testcase | - |
| snippet | hap_name = "xxx" 或 ohos_moduletest_suite("xxx") |
| suggestion | hap_name "xxx" 不符合规范:不以"Acts"开头, 不以"Test"结尾。修复: hap_name改为 "ActsXxxAdapt001Test"。target名称 "yyy" 保持不变,只修改hap_name。 |
ohos_js_app_suite("test1") { # target名称,保持不变
testonly = true
part_name = "ace_engine"
subsystem_name = "arkui"
hap_name = "MyTest" # ✗ 错误:不以Acts开头
}
ohos_js_app_suite模板 - 正确格式:
ActsAbilityTest - 以Acts开头,Test结尾ActsWindowManagerTest - 以Acts开头,Test结尾ohos_js_app_static_suite模板 - 正确格式:
ActsAbilityStaticTest - 以Acts开头,StaticTest结尾ActsNotificationThirdPartyWatchPermissionStaticTest - 以Acts开头,StaticTest结尾ohos_moduletest_suite模板 - 正确格式:
ActsModuleTest - 以Acts开头,Test结尾ActsAbilityTest - 以Acts开头,Test结尾错误格式:
actsStartSelfUIAbilityStaticTest - 首字母小写(错误)ActsAbility - 缺少Test/StaticTest结尾(错误)AbilityTest - 缺少Acts开头(错误)ActsStatictest - T未大写(错误)testing
--- name: ohos-req-value-decision description: Use after review meeting to record decision and route to next step. Triggers: 评审决策纪要, 评审结论回流, value decision, 评审接纳, 评审不接纳, 评审退回, 下次重新上会. Do NOT use for feature baseline (ohos-req-feature-baseline), review gate checks (ohos-req-review-gate), or IR generation (ohos-req-feature-to-ir). metadata: author: openharmony scope: common stage: requirements capability: value-decision version: 0.3.0 status: draft tags: - sdd - requirements
development
Use when converting an OpenHarmony requirement document, spec, or design proposal into an OpenHarmony review slide deck (需求评审 / 需求变更评审 / 设计评审 PPTX) — produces the fixed OpenHarmony-branded review-deck structure (OH logo on every page) with architecture/flow diagrams and field tables. Triggers on "需求评审PPT", "需求变更评审", "把需求文档转成评审PPT", "spec转评审PPT", "requirement/spec to review deck". NOT for arbitrary or generic slide decks unrelated to OpenHarmony requirement/design review.
testing
Use when performing the Phase 0 Step 0.5 Review Ready Gate on a 04-feature.md, especially when the user says "evaluate gate", "review readiness", "feature ready?", "should we generate IR", or when the ohos-req-intake-orchestration main session needs a structured Ready / Conditional Ready / Not Ready judgment instead of doing the check inline. Reads 01-04, runs seven fixed checks plus a conditional-items check, and returns a machine-readable JSON summary plus a human-readable table that the main session can route on. Do NOT use for feature baseline generation (ohos-req-feature-baseline), value decision recording (ohos-req-value-decision), or IR generation (ohos-req-feature-to-ir).
testing
--- name: ohos-req-requirement-intake description: Use when importing an OHOS requirement into Phase 0.1, especially for 01-requirement.md, requirement intake, background, user value, scenarios, scope, FR/NFR, affected modules, or priority. Triggers: 需求导入, 01-requirement, 需求基线, RR单号. Do NOT use for feasibility analysis (ohos-req-feasibility-analysis), architecture decision (ohos-req-arch-decision), or feature baseline (ohos-req-feature-baseline). metadata: author: openharmony scope: common