skills/check-test-code-quality/rules/R010/SKILL.md
# R010: part_name/subsystem_name不匹配 ## 规则信息 | 属性 | 值 | |------|-----| | 规则编号 | R010 | | 问题类型 | part_name/subsystem_name不匹配 | | 严重级别 | Critical | | 规则复杂度 | complex | | 扫描范围 | BUILD.gn files only | | testcase字段 | `-`(BUILD.gn为非测试文件,无对应it()块) | ## 问题描述 `part_name`/`subsystem_name`不匹配。BUILD.gn中声明的`part_name`必须在对应`subsystem_name`的components列表中存在。 ## 修复建议 使用完整的子系统-组件映射表,确保`part_name`在对应`subsystem`的components中。 ## 映射表数据源 映射表从以下三个配置文件构建(合并所有 subsystem → component 关系,共54个子系统、332个部件): | 配置文件 | 主U
npx skillsauth add openharmonyinsight/openharmony-skills skills/check-test-code-quality/rules/R010Install 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.
| 属性 | 值 |
|------|-----|
| 规则编号 | R010 |
| 问题类型 | part_name/subsystem_name不匹配 |
| 严重级别 | Critical |
| 规则复杂度 | complex |
| 扫描范围 | BUILD.gn files only |
| testcase字段 | -(BUILD.gn为非测试文件,无对应it()块) |
part_name/subsystem_name不匹配。BUILD.gn中声明的part_name必须在对应subsystem_name的components列表中存在。
使用完整的子系统-组件映射表,确保part_name在对应subsystem的components中。
映射表从以下三个配置文件构建(合并所有 subsystem → component 关系,共54个子系统、332个部件):
| 配置文件 | 主URL(推荐) | 备用URL |
|---------|-------------|---------|
| vendor_hihope config.json | https://gitee.com/openharmony/vendor_hihope/raw/master/rk3568/config.json | https://gitcode.com/openharmony/vendor_hihope/raw/master/rk3568/config.json |
| productdefine rich.json | https://gitee.com/openharmony/productdefine_common/raw/master/inherit/rich.json | https://gitcode.com/openharmony/productdefine_common/raw/master/inherit/rich.json |
| productdefine chipset_common.json | https://gitee.com/openharmony/productdefine_common/raw/master/inherit/chipset_common.json | https://gitcode.com/openharmony/productdefine_common/raw/master/inherit/chipset_common.json |
重要: 优先使用gitee.com URL,gitcode.com可能需要认证。如果所有URL均不可达,必须在终端输出明确警告,不可静默返回0个问题。
本地缓存: 获取成功后可将映射表缓存到本地文件(如/tmp/r010_mapping.json),下次扫描优先读取缓存,避免每次都请求远程。
扫描目标目录下所有名为BUILD.gn的文件。
import os
def find_build_gn_files(scan_root):
build_gn_files = []
for dirpath, dirnames, filenames in os.walk(scan_root):
if 'BUILD.gn' in filenames:
build_gn_files.append(os.path.join(dirpath, 'BUILD.gn'))
return build_gn_files
对每个BUILD.gn文件,使用正则表达式提取part_name和subsystem_name字段。
import re
def extract_build_gn_fields(content):
part_name = None
subsystem_name = None
part_match = re.search(r'part_name\s*=\s*["\']([^"\']+)["\']', content)
if part_match:
part_name = part_match.group(1)
subsystem_match = re.search(r'subsystem_name\s*=\s*["\']([^"\']+)["\']', content)
if subsystem_match:
subsystem_name = subsystem_match.group(1)
return part_name, subsystem_name
从三个配置文件中读取并合并映射关系。每个配置文件的结构包含子系统定义,每个子系统下有components列表。
⚠️ 数据格式陷阱: components数组中的元素不是纯字符串,而是对象{"component": "xxx", "features": [...]}。直接用set().update(components)会抛unhashable type: 'dict'异常,导致映射表构建失败、R010静默返回0个问题。必须从每个对象中提取component字段。
import json
MAPPING_URLS = [
'https://gitee.com/openharmony/vendor_hihope/raw/master/rk3568/config.json',
'https://gitee.com/openharmony/productdefine_common/raw/master/inherit/rich.json',
'https://gitee.com/openharmony/productdefine_common/raw/master/inherit/chipset_common.json',
]
CACHE_FILE = '/tmp/r010_mapping.json'
def _parse_config(data, subsystem_map):
subsystems = data.get('subsystems', [])
for subsys in subsystems:
name = subsys.get('subsystem', '')
components = subsys.get('components', [])
if name not in subsystem_map:
subsystem_map[name] = set()
for c in components:
if isinstance(c, str):
subsystem_map[name].add(c)
elif isinstance(c, dict):
subsystem_map[name].add(c.get('component', ''))
def load_subsystem_mapping():
# 优先读取本地缓存
import os
if os.path.exists(CACHE_FILE):
with open(CACHE_FILE, 'r', encoding='utf-8') as f:
return {k: set(v) for k, v in json.load(f).items()}
# 从远程获取
import urllib.request
subsystem_map = {}
for url in MAPPING_URLS:
try:
req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'})
with urllib.request.urlopen(req, timeout=30) as resp:
data = json.loads(resp.read().decode('utf-8'))
_parse_config(data, subsystem_map)
except Exception as e:
print(f" 警告: 获取映射表失败 {url}: {e}", file=sys.stderr, flush=True)
if not subsystem_map:
print(" 警告: R010映射表为空,所有BUILD.gn将被跳过!", file=sys.stderr, flush=True)
# 写入缓存
with open(CACHE_FILE, 'w', encoding='utf-8') as f:
json.dump({k: sorted(v) for k, v in subsystem_map.items()}, f, ensure_ascii=False)
return subsystem_map
对每个BUILD.gn,检查提取出的part_name是否在subsystem_name对应的components集合中。
def validate_part_subsystem(part_name, subsystem_name, subsystem_map):
if not part_name or not subsystem_name:
return True
if subsystem_name not in subsystem_map:
return True
return part_name in subsystem_map[subsystem_name]
def scan_r010(build_gn_files, base_dir):
issues = []
subsystem_map = load_subsystem_mapping()
if not subsystem_map:
return issues
for file_path in build_gn_files:
with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
content = f.read()
part_name, subsystem_name = extract_build_gn_fields(content)
if not part_name or not subsystem_name:
continue
if not validate_part_subsystem(part_name, subsystem_name, subsystem_map):
rel_path = os.path.relpath(file_path, base_dir)
part_line = 0
for i, line in enumerate(content.splitlines(), 1):
if re.search(r'part_name\s*=\s*["\']' + re.escape(part_name) + '["\']', line):
part_line = i
break
available_parts = sorted(subsystem_map.get(subsystem_name, set()))
issues.append({
'rule': 'R010',
'type': 'part_name/subsystem_name不匹配',
'severity': 'Critical',
'file': rel_path,
'line': part_line,
'testcase': '-',
'snippet': f'part_name = "{part_name}", subsystem_name = "{subsystem_name}"',
'suggestion': (
f'part_name "{part_name}" 不在 subsystem_name "{subsystem_name}" 的components中。'
f'可选的part_name: {available_parts}'
),
})
return issues
# BUILD.gn - 错误:invalid_part不在arkui的components中
ohos_js_app_suite("ActsTest") {
testonly = true
part_name = "invalid_part" # ✗ 错误
subsystem_name = "arkui"
hap_name = "ActsTest"
}
# BUILD.gn - 正确:ace_engine在arkui的components中
ohos_js_app_suite("test") {
testonly = true
part_name = "ace_engine" # ✓ 正确
subsystem_name = "arkui"
hap_name = "ActsTest"
}
subsystem_name本身不在映射表中,跳过验证(可能是新增子系统)严重性: 严重,导致unhashable type: 'dict'异常,映射表构建失败。
三个配置文件中components字段的实际数据格式为对象数组,不是纯字符串数组:
{
"subsystem": "arkui",
"components": [
{"component": "ace_engine", "features": []},
{"component": "napi", "features": []}
]
}
错误做法: 直接set().update(components) → 抛异常
正确做法: 逐个判断类型,对象取component字段:
for c in components:
if isinstance(c, str):
mapping[name].add(c)
elif isinstance(c, dict):
mapping[name].add(c.get('component', ''))
gitcode.com的raw文件URL需要认证才能访问,直接请求会返回HTML登录页而非JSON。必须优先使用gitee.com URL。
如果映射表获取失败(网络不通、认证失败等),映射表为空,所有subsystem_name都不在映射表中,全部被跳过,R010返回0个问题且不报任何错误。必须在映射表为空时输出明确警告。
每条issue的字段:
| 字段 | 值 |
|------|-----|
| rule | R010 |
| type | part_name/subsystem_name不匹配 |
| severity | Critical |
| file | 相对路径(如ability/xxx/BUILD.gn) |
| line | part_name所在行号 |
| testcase | - |
| snippet | part_name = "xxx", subsystem_name = "yyy" |
| suggestion | 问题描述 + 可选的part_name列表 |
挑战: 维护完整的子系统-组件映射表
解决方案:
SUBSYSTEM_PARTS_MAP = {
"account": ["os_account"],
"arkui": ["ace_engine", "napi", "ui_appearance", "ui_lite"],
"bundle": ["bundle_framework"],
"communication": ["bluetooth", "wifi", "netstack", "dsoftbus"],
# ... 共50+个子系统
}
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