skills/check-test-code-quality/rules/R011/SKILL.md
# R011: testsuite重复 ## 规则信息 | 属性 | 值 | |------|-----| | 规则编号 | R011 | | 问题类型 | testsuite重复 | | 严重级别 | Critical | | 规则复杂度 | complex | | 扫描范围 | 同一独立XTS工程内的所有测试文件 | | testcase字段 | `-`(describe不在it()块内) | ## 问题描述 一个独立XTS工程下不允许describe命名重复。即同一个独立XTS工程中,所有测试文件里的`describe()`函数的第一个参数不能重复。 ## 修复建议 确保testsuite命名唯一。重复的testsuite名称后追加`Adapt`+三位数字编号。 ## 自动修复规则 - **命名格式**: `{原testsuite名称}Adapt{三位数字}` - **保留首个**: 保留第一个出现的testsuite名称不变 - **递增编号**: 后续重复的依次编号为Adapt001, Adapt002, Adapt003... ## 修复建议格式 ```
npx skillsauth add openharmonyinsight/openharmony-skills skills/check-test-code-quality/rules/R011Install 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.
| 属性 | 值 |
|------|-----|
| 规则编号 | R011 |
| 问题类型 | testsuite重复 |
| 严重级别 | Critical |
| 规则复杂度 | complex |
| 扫描范围 | 同一独立XTS工程内的所有测试文件 |
| testcase字段 | -(describe不在it()块内) |
一个独立XTS工程下不允许describe命名重复。即同一个独立XTS工程中,所有测试文件里的describe()函数的第一个参数不能重复。
确保testsuite命名唯一。重复的testsuite名称后追加Adapt+三位数字编号。
{原testsuite名称}Adapt{三位数字}与{文件路径}:{行号}重复,修改testsuite名称,确保工程内唯一
独立XTS工程的判断标准:
BUILD.gn文件.test.ets, .test.ts, .test.js)import os
import re
def is_group_build_gn(build_gn_path):
with open(build_gn_path, 'r', encoding='utf-8') as f:
content = f.read()
return bool(re.search(r'\bgroup\s*\(', content))
def find_independent_projects(scan_root):
"""识别独立XTS工程,正确处理group类型父BUILD.gn。
group类型的BUILD.gn只是聚合入口,不阻止其子目录成为独立工程。
"""
all_build_gn_dirs = set()
for dirpath, dirnames, filenames in os.walk(scan_root):
if 'BUILD.gn' in filenames:
all_build_gn_dirs.add(os.path.abspath(dirpath))
# 只收集非group的BUILD.gn目录
non_group_dirs = set()
for d in all_build_gn_dirs:
if not is_group_build_gn(os.path.join(d, 'BUILD.gn')):
non_group_dirs.add(d)
# 只将"父目录是非group BUILD.gn"的子目录标记为应排除
parent_dirs = set()
for d in all_build_gn_dirs:
if d in parent_dirs:
continue
parent = os.path.dirname(d)
while parent != os.path.abspath(scan_root) and parent != '/':
if parent in non_group_dirs:
parent_dirs.add(d)
break
parent = os.path.dirname(parent)
projects = []
for dirpath in all_build_gn_dirs:
if dirpath in parent_dirs:
continue
if is_group_build_gn(os.path.join(dirpath, 'BUILD.gn')):
continue
has_test_files = any(
fn.endswith(('.test.ets', '.test.ts', '.test.js'))
for fn in os.listdir(dirpath)
)
if has_test_files:
projects.append(dirpath)
return projects
关键步骤: 每个独立工程只扫描直接属于该工程的测试文件,必须排除子目录中的独立工程文件,否则会产生跨工程误报。
def get_project_test_files(project_dir):
test_extensions = ('.test.ets', '.test.ts', '.test.js')
test_files = []
for fn in os.listdir(project_dir):
if fn.endswith(test_extensions):
test_files.append(os.path.join(project_dir, fn))
return test_files
在工程内的测试文件中,提取所有describe()函数的第一个参数。
DESCRIBE_PATTERN = re.compile(
r'describe\s*\(\s*["\']([^"\']+)["\']',
re.MULTILINE
)
def collect_describe_info(project_dir, test_files, base_dir):
describes = []
for file_path in test_files:
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
for match in DESCRIBE_PATTERN.finditer(content):
name = match.group(1)
line_num = content[:match.start()].count('\n') + 1
rel_path = os.path.relpath(file_path, base_dir)
describes.append({
'name': name,
'file': rel_path,
'line': line_num,
'abs_path': os.path.abspath(file_path),
})
return describes
from collections import defaultdict
def find_duplicates(describes):
name_to_occurrences = defaultdict(list)
for desc in describes:
name_to_occurrences[desc['name']].append(desc)
duplicates = []
for name, occurrences in name_to_occurrences.items():
if len(occurrences) > 1:
first = occurrences[0]
other_locs = []
for occ in occurrences[1:]:
other_locs.append(f"{occ['file']}:{occ['line']}")
duplicates.append({
'name': name,
'count': len(occurrences),
'first_file': first['file'],
'first_line': first['line'],
'other_locations': other_locs,
})
return duplicates
关键: 只为每个重复组的第一次出现创建一条问题报告。
def scan_r011(scan_root, base_dir):
issues = []
projects = find_independent_projects(scan_root)
for project_dir in projects:
test_files = get_project_test_files(project_dir)
if not test_files:
continue
describes = collect_describe_info(project_dir, test_files, base_dir)
duplicates = find_duplicates(describes)
for dup in duplicates:
rel_project = os.path.relpath(project_dir, base_dir)
other_info = '; '.join(dup['other_locations'])
issues.append({
'rule': 'R011',
'type': 'testsuite重复',
'severity': 'Critical',
'file': dup['first_file'],
'line': dup['first_line'],
'testcase': '-',
'snippet': f'describe("{dup["name"]}", ...)',
'suggestion': (
f'在独立XTS工程 \'{rel_project}\' 中,testsuite名称 '
f'\'{dup["name"]}\' 重复 {dup["count"]} 次。'
f'重复位置: {other_info}。'
f'修改testsuite名称,确保工程内唯一。'
),
})
return issues
project/
├── BUILD.gn # 独立工程A
├── test1.test.js
├── sub_project/
│ ├── BUILD.gn # 独立工程B(子工程)
│ └── test2.test.js
如果不过滤子工程文件,工程A的扫描会把工程B的describe也收集进来,产生跨工程误报。
避免方法: 只收集工程根目录下的测试文件(os.listdir(project_dir)),不递归子目录。
如果同一组重复的describe名称被报告多次(例如3个重复的describe报告了3条问题),会导致Excel报告中出现冗余。
避免方法: 每个重复的describe名称只报告一次,指向第一次出现的位置。
使用describe\s*\(\s*["\']([^"\']+)["\']匹配describe的第一个参数,需要确保只匹配第一个参数。
避免方法: 使用非贪婪匹配[^"\']+精确提取第一个字符串参数。
当扫描根目录和文件路径不一致时(例如一个使用绝对路径,另一个使用相对路径),os.path.relpath()会抛出异常。
避免方法: 所有路径在使用前必须通过os.path.abspath()或pathlib.Path.resolve()转换为绝对路径。
from pathlib import Path
scan_root = Path(scan_root).resolve()
base_dir = Path(base_dir).resolve()
Group类型的BUILD.gn只是聚合入口,不包含实际的测试代码。如果将其识别为独立工程,会导致大量误报。
避免方法: 检查BUILD.gn内容是否包含group(关键字,如果包含则跳过。
// File1.test.js(同一独立工程内)
export default function TestSuite1() {
describe("TransientTaskJsTest", function () {
// 测试代码
});
}
// File2.test.js(同一独立工程内)
export default function TestSuite2() {
describe("TransientTaskJsTest", function () { // ✗ 错误:与File1.test.js重复
// 测试代码
});
}
// File1.test.js(同一独立工程内)
export default function TestSuite1() {
describe("TransientTaskJsTest", function () { // ✓ 首次出现,保持不变
// 测试代码
});
}
// File2.test.js(同一独立工程内)
export default function TestSuite2() {
describe("TransientTaskJsTestAdapt001", function () { // ✓ 修复后命名唯一
// 测试代码
});
}
// 修复前
describe("ContinuousTaskJsTest", function () { } // 首次出现
describe("ContinuousTaskJsTest", function () { } // 第二次
describe("ContinuousTaskJsTest", function () { } // 第三次
// 修复后
describe("ContinuousTaskJsTest", function () { } // 保持不变
describe("ContinuousTaskJsTestAdapt001", function () { } // Adapt001
describe("ContinuousTaskJsTestAdapt002", function () { } // Adapt002
每条issue的字段:
| 字段 | 值 |
|------|-----|
| rule | R011 |
| type | testsuite重复 |
| severity | Critical |
| file | 相对路径(如xxx/File1.test.js) |
| line | describe所在行号 |
| testcase | - |
| snippet | describe("xxx", ...) |
| suggestion | 在独立XTS工程 '{工程名}' 中,testsuite名称 '{名称}' 重复 {次数} 次。重复位置: {文件:行号}; ... |
describe("Test" + idx, ...))不检查:
问题: for dep_entry in dep dep_entries:
后果: 脚本无法运行
解决: 修复语法错误
问题: patterns列表混入字符串描述 后果: describe块无法正确识别 解决: 移除多余字符
挑战: 正确识别独立XTS工程边界
解决方案:
def is_independent_xts_project(dir_path):
has_build_gn = os.path.exists(os.path.join(dir_path, "BUILD.gn"))
has_test_files = any(glob.glob(...))
return has_build_gn and has_test_files
development
Run local code quality checks covering a subset of OpenHarmony gate CI (copyright, CodeArts C/C++) plus additional local checks (pylint/flake8, shellcheck/bashate, gn format). Use before committing to reduce gate failures. Triggers on: /oh-precommit-codecheck, "门禁检查", "门禁预检", "检查代码", "run codecheck", "check code quality", "lint my code", "代码检查", or after completing code implementation. WHEN to use: before git commit, before creating PR, after modifying C/C++/Python/Shell/GN files, when gate CI fails with codecheck defects, or when you want to preview what gate will flag.
development
OpenHarmony PR full lifecycle workflow. Five modes: - Commit: standardized commit with DCO sign-off and Issue linking - Create PR: commit + push to fork + create Issue + create PR on upstream - Fix Codecheck: fetch gate CI codecheck defects from a PR and auto-fix them - Review PR: fetch a PR's changes to local for code review - Fix Review: fetch unresolved review comments from a PR and auto-fix them Triggers on: /oh-pr-workflow, "提交代码", "创建PR", "提个PR", "commit", "修复告警", "修复门禁", "修复codecheck", "fix codecheck", "review pr", "review这个pr", "看下这个pr", "检视pr", "修复review", "修复检视意见", "fix review", or a GitCode PR URL with fix/review intent.
testing
分析 HM Desktop PRD 文档,提取需求信息、验证完整性、检查章节顺序(需求来源→需求背景→需求价值分析→竞品分析→需求描述)、检查 KEP 定义、检测需求冲突并生成结构化分析报告。适用于用户请求:(1) 分析或审查 PRD 文档, (2) 从需求中提取 KEP 列表, (3) 检查 PRD 完整性或一致性, (4) 将需求映射到模块架构, (5) 验证 PRD 格式合规性, (6) 验证竞品分析章节完整性。关键词:PRD分析, requirement extraction, KEP验证, completeness check, chapter order validation, 竞品分析检查, analyze PRD, 需求提取, 完整性检查, 章节顺序验证
development
基于 PRD 文档自动生成鸿蒙系统设计文档,包括架构设计文档和功能设计文档。生成前会分析 OpenHarmony 存量代码结构,确保与现有架构兼容。架构设计文档第2章必须为竞品方案分析,位于需求背景之后。适用于用户请求:(1) 生成架构设计文档, (2) 生成功能设计文档, (3) 从 PRD 生成设计文档, (4) 创建系统架构设计, (5) 编写功能规格说明, (6) 分析 OH 代码结构。关键词:architecture design, functional design, design doc, 竞品方案分析, OpenHarmony code analysis, 架构设计, 功能设计, 设计文档生成, OH代码分析, analyze codebase, competitor analysis