skills/check-test-code-quality/rules/R017/SKILL.md
# R017: syscap.json配置多个能力 - **规则编号**: R017 - **严重级别**: Critical - **规则复杂度**: complex(需要JSON解析) - **问题类型**: 一个XTS工程的syscap.json文件中配置了多个syscap能力 - **修复方式**: 仅配置一个syscap能力 - **预期问题数量**: 3000+ ## 扫描范围 仅扫描`syscap.json`文件。 **文件定位规则**:在每个XTS工程目录下查找`syscap.json`文件(通常位于工程根目录或`src`目录下)。 ## 问题描述 一个XTS工程的`syscap.json`文件中配置了多个syscap能力。根据规范,每个XTS工程应仅配置一个syscap能力,即被测API对应的能力。 ## 检测逻辑 ### 核心算法 ```python import json import os import re def find_syscap_files(directory: str) -> list: """ 在目录下递归查找
npx skillsauth add openharmonyinsight/openharmony-skills skills/check-test-code-quality/rules/R017Install 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.
仅扫描syscap.json文件。
文件定位规则:在每个XTS工程目录下查找syscap.json文件(通常位于工程根目录或src目录下)。
一个XTS工程的syscap.json文件中配置了多个syscap能力。根据规范,每个XTS工程应仅配置一个syscap能力,即被测API对应的能力。
import json
import os
import re
def find_syscap_files(directory: str) -> list:
"""
在目录下递归查找所有syscap.json文件。
Args:
directory: 扫描根目录
Returns:
syscap.json文件绝对路径列表
"""
syscap_files = []
for root, dirs, files in os.walk(directory):
if 'syscap.json' in files:
syscap_files.append(os.path.join(root, 'syscap.json'))
return syscap_files
def scan_r017(file_path: str) -> list:
"""
检查syscap.json文件是否配置了多个syscap能力。
Args:
file_path: syscap.json文件路径
Returns:
问题列表
"""
issues = []
try:
with open(file_path, 'r', encoding='utf-8') as f:
data = json.load(f)
except (json.JSONDecodeError, UnicodeDecodeError) as e:
return [{
'rule': 'R017',
'type': 'syscap.json配置多个能力',
'severity': 'Critical',
'file': file_path,
'line': 1,
'testcase': '-',
'snippet': f'syscap.json解析失败: {e}',
'suggestion': f'路径: {file_path}, 行号: 1, 问题描述: syscap.json文件格式错误,无法解析。',
}]
# 检查devices.custom[].xts数组
devices = data.get('devices', {})
custom_devices = devices.get('custom', [])
for device_idx, device in enumerate(custom_devices):
xts_list = device.get('xts', [])
if len(xts_list) > 1:
# 在文件中定位xts数组所在行
line_num = find_xts_array_line(file_path, data)
snippet = ', '.join(xts_list[:3])
if len(xts_list) > 3:
snippet += f', ... (共{len(xts_list)}个)'
issues.append({
'rule': 'R017',
'type': 'syscap.json配置多个能力',
'severity': 'Critical',
'file': file_path,
'line': line_num,
'testcase': '-',
'snippet': snippet,
'suggestion': (
f"路径: {file_path}, 行号: {line_num}, "
f"问题描述: 配置了{len(xts_list)}个syscap能力,应仅配置1个。"
f"请保留被测API对应的能力,移除其余能力。"
),
})
return issues
def find_xts_array_line(file_path: str, data: dict) -> int:
"""
在syscap.json文件中定位xts数组所在的行号。
Args:
file_path: 文件路径
data: 已解析的JSON数据
Returns:
xts数组第一个元素所在行号,找不到返回1
"""
try:
with open(file_path, 'r', encoding='utf-8') as f:
lines = f.readlines()
xts_list = data.get('devices', {}).get('custom', [{}])[0].get('xts', [])
if xts_list:
first_cap = xts_list[0]
for i, line in enumerate(lines, 1):
if first_cap in line:
return i
except Exception:
pass
return 1
def batch_scan_r017(directory: str) -> list:
"""
扫描目录下所有syscap.json文件。
Args:
directory: 扫描根目录
Returns:
所有问题列表
"""
all_issues = []
syscap_files = find_syscap_files(directory)
for file_path in syscap_files:
rel_path = os.path.relpath(file_path, directory)
issues = scan_r017(rel_path)
all_issues.extend(issues)
return all_issues
{
"devices": {
"general": [],
"custom": [
{
"xts": [
"SystemCapability.Ability.AbilityRuntime.Core"
]
}
]
}
}
必须严格按层级类型取值,不可跳层或混淆层级类型。
| JSON路径 | 类型 | 说明 |
|---------|------|------|
| data.devices | dict {str: ...} | 设备配置根节点,包含 "general" 和 "custom" 两个key |
| data.devices.custom | list [dict, ...] | 自定义设备配置数组,每个元素是一个dict |
| data.devices.custom[].xts | list [str, ...] | XTS syscap能力字符串数组(检测目标) |
正确取值方式(两步,不可合并为一步):
# 第1步:从dict中取出custom列表
devices = data.get('devices', {}) # → dict {"general": [], "custom": [...]}
custom_devices = devices.get('custom', []) # → list [dict, ...]
# 第2步:遍历列表中的每个dict
for device in custom_devices: # device → dict {"xts": [...]}
xts_list = device.get('xts', []) # → list [str, ...]
常见错误(会导致100%漏检):
# ✗ 错误:把devices dict直接当列表遍历,遍历的是key字符串
for dev in data.get('devices', []): # dev是"general"或"custom"字符串
dev.get('custom') # → AttributeError: 'str' has no 'get'
# ✗ 错误:合并取值后类型丢失
custom = data.get('devices', {}).get('custom', []) # 正确
for dev in custom:
xts = dev.get('xts', []) # dev必须是dict,如果custom被错误赋值为list of list则会报错
// 错误:配置了多个能力
{
"devices": {
"general": [],
"custom": [
{
"xts": [
"SystemCapability.Ability.AbilityRuntime.Core",
"SystemCapability.Ability.AbilityRuntime.FAModel",
"SystemCapability.Ability.AbilityRuntime.AbilityCore"
]
}
]
}
}
// ✗ 错误:配置了3个syscap能力,应仅配置1个
// 错误:配置了2个能力
{
"devices": {
"general": [],
"custom": [
{
"xts": [
"SystemCapability.Account.OsAccount",
"SystemCapability.Account.AppAccount"
]
}
]
}
}
// ✗ 错误:配置了2个syscap能力,应仅配置1个
// 正确:仅配置一个能力
{
"devices": {
"general": [],
"custom": [
{
"xts": [
"SystemCapability.Ability.AbilityRuntime.Core"
]
}
]
}
}
// ✓ 正确:仅配置了1个syscap能力
// 正确:仅配置一个能力
{
"devices": {
"general": [],
"custom": [
{
"xts": [
"SystemCapability.Web.Webview"
]
}
]
}
}
// ✓ 正确:仅配置了1个syscap能力
{
'rule': 'R017',
'type': 'syscap.json配置多个能力',
'severity': 'Critical',
'file': 'ability/AccessManager/entry/syscap.json',
'line': 5,
'testcase': '-',
'snippet': 'SystemCapability.Ability.AbilityRuntime.Core, SystemCapability.Ability.AbilityRuntime.FAModel, SystemCapability.Ability.AbilityRuntime.AbilityCore',
'suggestion': '路径: ability/AccessManager/entry/syscap.json, 行号: 5, 问题描述: 配置了3个syscap能力,应仅配置1个。请保留被测API对应的能力,移除其余能力。'
}
| 列序 | 列名 | 示例 |
|------|------|------|
| 1 | 问题ID | R017 |
| 2 | 问题类型 | syscap.json配置多个能力 |
| 3 | 严重级别 | Critical |
| 4 | 文件路径 | ability/AccessManager/entry/syscap.json |
| 5 | 行号 | 5 |
| 6 | 所属用例 | - |
| 7 | 代码片段 | SystemCapability.Ability.AbilityRuntime.Core, ... |
| 8 | 修复建议 | 路径: ..., 行号: 5, 问题描述: 配置了3个syscap能力,应仅配置1个... |
注意:所属用例字段始终为-,因为syscap.json不是测试文件,不存在testcase概念。
xts数组中仅保留该能力// 修复前
{
"devices": {
"custom": [
{
"xts": [
"SystemCapability.Ability.AbilityRuntime.Core",
"SystemCapability.Ability.AbilityRuntime.FAModel",
"SystemCapability.Ability.AbilityRuntime.AbilityCore"
]
}
]
}
}
// 修复后(假设被测API属于Core能力)
{
"devices": {
"custom": [
{
"xts": [
"SystemCapability.Ability.AbilityRuntime.Core"
]
}
]
}
}
xts数组为空([])时不报告,因为0个能力不违反"应仅配置1个"的规则。但实际使用中这种情况极少。
custom数组为空时不报告。
如果syscap.json文件缺少devices节点,JSON解析不会报错但不会产生问题报告。可根据实际需要决定是否将缺少devices节点作为额外问题报告。
如果syscap.json文件格式错误(非有效JSON),解析会抛出异常,此时报告为解析错误,便于用户定位和修复。
data['devices'] 是 dict(包含 "general" 和 "custom" 两个key),不是设备列表。必须通过 .get('custom', []) 再取一层才能得到设备列表。如果直接遍历 data['devices'],遍历的是dict的key字符串("general"、"custom"),对字符串调用 .get() 会导致 AttributeError,异常被静默捕获后所有文件全部跳过,造成100%漏检。
验证方法:实现完成后,用以下命令快速验证是否漏检:
grep -r '"SystemCapability' --include='syscap.json' -l | xargs grep -c '"SystemCapability' | grep -v ':1$' | wc -l
如果该命令输出的数量远大于扫描结果数量,说明存在漏检。
来源: EXAMPLES.md
以下为补充示例(与上方已有示例互补):
错误示例:
// 错误:配置多个能力
{
"devices": {
"general": [],
"custom": [
{
"xts": [
"SystemCapability.Ability.AbilityRuntime.Core",
"SystemCapability.Ability.AbilityRuntime.FAModel",
"SystemCapability.Ability.AbilityRuntime.AbilityCore"
]
}
]
}
}
正确示例:
// 正确:仅配置一个能力
{
"devices": {
"general": [],
"custom": [
{
"xts": [
"SystemCapability.Ability.AbilityRuntime.Core"
]
}
]
}
}
来源: IMPLEMENTATION_DETAILS.md
检测范围: syscap.json文件
检测方法: 检查 devices.custom[].xts 数组,如果长度大于1则报错
def check_syscap_json(file_path: str):
with open(file_path, 'r', encoding='utf-8') as f:
data = json.load(f)
for device in data['devices']['custom']:
xts_list = device.get('xts', [])
if len(xts_list) > 1:
report_issue(f"配置了{len(xts_list)}个syscap能力,应仅配置1个")
来源: V3_UPGRADE_GUIDE.md
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