news 2026/10/3 2:28:20

asc-devkit API UT 覆盖率扫描实战:从接口清单提取到跨架构缺失报告

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
asc-devkit API UT 覆盖率扫描实战:从接口清单提取到跨架构缺失报告
  • 人工智能
  • 深度学习
  • 算子库
  • CANN
  • Ascend

【免费下载链接】asc-devkit

本项目是CANN 推出的昇腾AI处理器专用的算子程序开发语言,原生支持C和C++标准规范,主要由类库和语言扩展层构成,提供多层级API,满足多维场景算子开发诉求。

项目地址:https://gitcode.com/cann/asc-devkit
点击查看免费下载

本篇技术指南系统讲解 CANN asc-devkit 仓库中 API 单元测试(UT)覆盖率扫描模式的完整工作流:如何以asc-api-ut-gen coverage命令扫描include/目录下全部 API 接口定义、识别架构条件编译隔离、过滤 deprecated 接口、执行四级 UT 匹配(含跨文件内容搜索),最终生成按架构分组的覆盖率与缺失 UT 报告。读完本文,你将掌握一套可直接落地执行的扫描命令、API 提取与 deprecated 识别规则、跨文件覆盖检测流程,以及将扫描结果用于创建 UT 补测任务的完整方法。

1. 覆盖率扫描模式要解决什么问题

asc-api-ut-gen是 asc-devkit 仓库内置的 API 单元测试技能(skill),它把 UT 生成拆分为四种交互模式:Git 扫描模式、精确交互模式、覆盖率扫描模式和覆盖率报告补齐模式。其中覆盖率扫描模式的核心职责是:扫描include/目录下所有 API 接口定义,检查它们是否都有对应的 UT 测试看护,并生成覆盖率报告。

该模式需要覆盖六种 API 类型:高阶 API(adv)、membase 基础 API、regbase 基础 API、C API、SIMT API、工具类 API(utils),并且要解决三个在真实仓库中普遍存在的难点:

  • 架构条件编译隔离:同一 API 在不同芯片架构(如ascend910b1、ascend950pr_9599)下通过__NPU_ARCH__宏隔离,可能存在"某架构有接口但无 UT"的覆盖差异;
  • 跨架构覆盖差异检测:接口在架构 A 有测试、在架构 B 没有,简单统计无法暴露这类问题,必须按架构分组对比;
  • deprecated 接口过滤:已标记弃用的接口不纳入 UT 补充范围,避免为废弃代码补测试。

从仓库实际目录看,tests/api/下按 API 类型和架构组织了大量测试用例(如tests/api/basic_api/ascendc_case_ascend910b1/ascendc_case_ascend910b1_aiv/、tests/api/c_api/npu_arch_3510/),这正是扫描模式要核对的对象。

2. 命令格式与参数说明

2.1 命令格式

# 完整扫描(扫描所有架构、所有 API 类型) /asc-api-ut-gen coverage # 指定架构扫描 /asc-api-ut-gen coverage --arch ascend910b1 /asc-api-ut-gen coverage --arch ascend950pr_9599 # 指定 API 类型扫描 /asc-api-ut-gen coverage --type membase /asc-api-ut-gen coverage --type regbase /asc-api-ut-gen coverage --type adv /asc-api-ut-gen coverage --type c /asc-api-ut-gen coverage --type simt /asc-api-ut-gen coverage --type utils # 组合参数 /asc-api-ut-gen coverage --arch ascend910b1 --type membase # 输出格式控制 /asc-api-ut-gen coverage --output json # JSON 格式输出 /asc-api-ut-gen coverage --output markdown # Markdown 格式输出(默认) /asc-api-ut-gen coverage --output summary # 简要摘要 # 创建缺失 UT 任务 /asc-api-ut-gen coverage --create-tasks

--create-tasks是扫描到报告之间的关键衔接:它会把报告中"缺失 UT"的 API 自动转成 UT 补充任务,为后续的精确交互模式(/asc-api-ut-gen <芯片版本> <API类型> <API名称> [核心类型])提供输入。

2.2 参数说明

参数缩写说明默认值
--arch-a指定芯片架构扫描全部架构
--type-t指定 API 类型:membase/regbase/adv/c/simt/utils全部类型
--output-o输出格式:markdown/json/summarymarkdown
--create-tasks为缺失的 API 自动创建 UT 任务否

注意两个架构约束:regbase 基础 API 仅支持ascend950pr_9599(__NPU_ARCH__ == 3510);SIMT API 当前也仅支持ascend950pr_9599(3510)。

3. 扫描流程总览

覆盖率扫描是一个四阶段的流水线:

四个阶段与仓库目录的对应关系清晰可查:

  • Phase 0 环境配置:ASC_DEVKIT_PATH必须从当前 workspace 或 skill 所在仓向上定位仓根(本 skill 已位于 asc-devkit 仓内),推导结果记录到执行日志;无法定位仓根时直接报错。首次使用只需向用户确认 CANN 包安装路径,禁止假设默认路径或使用缓存路径。
  • Phase 1 API 接口扫描:对应include/adv_api/、include/basic_api/(排除reg_compute/)、include/basic_api/reg_compute/、include/c_api/、include/simt_api/、include/utils/六个声明目录。
  • Phase 2 UT 文件扫描:对应tests/api/下各 API 类型的测试目录。
  • Phase 3/4 分析报告:按 API 类型和架构两个维度交叉统计,输出缺失列表与覆盖率数据。

4. API 提取规则

4.1 API 类别提取

API 类型提取规则头文件位置
高阶API模板类声明template<...> class Nameinclude/adv_api/**/*.h
membase基础API函数声明void FuncName(...)include/basic_api/kernel_operator_*.h(排除reg_compute/)
regbase基础API函数声明void FuncName(...)include/basic_api/reg_compute/**/*.h
C API函数声明void asc_name(...)include/c_api/**/*.h
SIMT API设备函数__device__ ... func(...)include/simt_api/**/*.h
工具类API类/函数声明include/utils/**/*.h

4.2 架构条件编译识别

覆盖率扫描的架构维度依赖对条件编译的精确识别。仓库头文件中大量使用__NPU_ARCH__宏做架构隔离,识别模式如下:

#if __NPU_ARCH__ == 2201 // ascend910b1 #elif __NPU_ARCH__ == 3510 // ascend950pr_9599 #endif // 多架构判断 #if defined(__NPU_ARCH__) && (__NPU_ARCH__ == 2201 || __NPU_ARCH__ == 3510) // 多架构通用代码 #endif

以include/basic_api/kernel_common.h为例,仓库中的真实写法正是第二种多架构判断形式,例如#if defined(__NPU_ARCH__) && ((__NPU_ARCH__ == 3510) || (__NPU_ARCH__ == 5102) || ...)、#if defined(__NPU_ARCH__) && ((__NPU_ARCH__ == 2201) || (__NPU_ARCH__ == 2002) || (__NPU_ARCH__ == 3002) || ...)。扫描器需要同时识别#if.*__NPU_ARCH__与#ifdef.*__DAV_两种模式,才能把 API 清单正确挂到每个架构名下。完整的架构与__NPU_ARCH__、SocVersion映射表由 asc-npu-arch skill 统一维护(见主文档 SKILL.md 第 5.3 节),本 skill 不重复维护芯片类型表。

5. Deprecated 接口过滤

5.1 过滤原则

标记为 deprecated 的接口不需要进行 UT 补充。这是覆盖率统计的准入规则:deprecated 接口被排除在覆盖统计之外,避免把已废弃代码计入"缺失"而污染报告。

5.2 Deprecated 检测模式

模式 1:[[deprecated]]属性(C++14)
[[deprecated("Use NewAPI instead")]] __aicore__ inline void OldFunction(...) { } // 头文件 deprecated(常见于接口迁移) [[deprecated(__FILE__ " is deprecated, please use softmax.h instead!")]] typedef void using_deprecated_header_h;

检测正则:

pattern = r'\[\[deprecated(?:\s*\([^)]*\))?\]\]'

仓库include/adv_api/activation/下有大量此类真实案例,例如geglu_tiling_intf.h中[[deprecated(__FILE__ " is deprecated, please use geglu_tiling.h instead!")]],以及kernel_operator_geglu_intf.h中[[deprecated(__FILE__ " is deprecated, please use geglu.h instead!")]] typedef void using_deprecated_header_h;。这类"头文件级 deprecated"出现在接口迁移场景:旧头文件整体弃用,用户被指引到新头文件(如geglu.h),扫描器必须能识别文件开头的 deprecated typedef,从而跳过整批接口。

模式 2:__attribute__((deprecated))(GCC/Clang)
__attribute__((deprecated("Use NewFunc"))) void OldFunc(...);

5.3 过滤流程

注意 Step 1 要求同时检查声明行及其前一行——[[deprecated]]属性往往写在函数声明的前一行,逐行扫描很容易漏掉。Step 2 的文件级检测则通过"文件开头的 deprecated typedef"一次性排除整个头文件。

6. 类及成员函数扫描

6.1 Advanced API 类扫描

高阶 API 以模板类形式声明,需要先识别类,再扫描类体内的成员函数。类声明识别正则:

pattern_class = r'template\s*<[^>]*>\s*class\s+(\w+)'

示例匹配:

template <class A_TYPE, class B_TYPE, class C_TYPE, ...> class MatmulImpl { // 成员函数... }; // 提取:MatmulImpl

成员函数识别使用两组正则,分别覆盖带__aicore__标注的普通成员函数和带模板参数的成员函数:

patterns = [ r'__aicore__\s+inline\s+(?:void|auto|\w+(?:<[^>]*>)?)\s+(\w+)\s*\([^)]*\)', r'template\s*<[^>]*>\s*__aicore__\s+inline\s+\w+\s+(\w+)\s*\(', ]

__aicore__是昇腾算子内核的编译标注(在impl/basic_api/的实现头中普遍可见),用它区分内核侧成员函数与宿主侧辅助函数,避免误提取。

6.2 类扫描流程

该流程的关键在于"确定类体范围"(Step 3):匹配到class Name后必须正确跟踪花括号配对,才能把成员函数归属到正确的类,而不是把下一个类的成员误算到当前类。每个层级(文件、类、函数)都独立检测 deprecated,三级过滤共同保证统计口径准确。

7. UT 匹配规则

7.1 四级匹配策略

判定一个 API 是否"已被 UT 覆盖",按以下四级策略逐层匹配,任一级命中即视为已覆盖:

  1. 精确匹配:UT 测试名与 API 名完全匹配
    • TEST_F(TEST_Fixpipe, ...)→Fixpipe
  2. 文件匹配:测试文件名包含 API 名
    • test_operator_fixpipe.cpp→Fixpipe
  3. 内容匹配:测试文件内容包含 API 调用
    • 文件内容包含Softmax(...)→Softmax
  4. 跨文件内容匹配(重要):API 可能存在于非对应命名的测试文件中
    • 使用Grep在整个测试目录搜索 API 名称

7.2 跨文件 API 检测流程

背景:部分 API 虽然没有独立的测试文件,但其实际调用存在于其他测试文件中。如果只做文件名匹配,会产生大量"假缺失"。

Step 3 是强制步骤:任何 API 在判定为"缺失 UT"之前,必须先在tests/api/对应目录内做一次全量 Grep 内容搜索。

典型跨文件覆盖案例(这些案例与仓库实际测试文件命名完全吻合):

API 名称预期测试文件实际测试文件
Mulltest_operator_vec_mull.cpptest_operator_vec_micro_binary.cpp
AbsSubtest_operator_vec_abssub.cpptest_operator_vec_micro_binary.cpp
Filltest_operator_fill.cpptest_operator_loaddata.cpp
Arangetest_operator_reg_compute_arange.cpptest_operator_reg_compute_creIndex.cpp

其中test_operator_loaddata.cpp与test_operator_reg_compute_creIndex.cpp都是仓库tests/api/下真实存在的文件,证实了"微二元运算 API 聚合在 micro_binary 测试文件、regbase API 聚合在 creIndex 测试文件"的实际测试组织方式。不执行跨文件搜索,这四类 API 会被误报为缺失。

8. API 名称校验规则

8.1 核心规则

规则 1:API 名称必须来源于头文件声明

规则 2:跨文件内容检测必须在判定缺失前执行(见 7.2 节)。

规则 3:双重校验机制

# 校验 API 存在性 Grep -n "{API_NAME}" include/c_api/ # 无结果 = API 不存在 # 校验 UT 覆盖性 Grep -n "{API_NAME}" tests/api/c_api/npu_arch_3510/ # 有结果 = 已覆盖

规则 3 把"API 是否存在"与"UT 是否覆盖"拆成两次独立校验:第一次无结果说明该名称根本不在头文件中(可能是拼写错误或臆造名称),第二次无结果才真正说明 UT 缺失。include/c_api/与tests/api/c_api/npu_arch_3510/都是仓库中确认存在的目录。

规则 4:Impl 为空的 API 无需增加 UT

某些 API 在目标架构上根本没有实现,只是占位或直接上报不支持,这类 API 补 UT 没有意义。识别模式:

// 模式 1: ASCENDC_REPORT_NOT_SUPPORT ASCENDC_REPORT_NOT_SUPPORT(false, "MrgSort4"); // 模式 2: ASCENDC_ASSERT 断言失败 ASCENDC_ASSERT((false), "VectorPadding is not supported");

仓库impl/basic_api/dav_3510/kernel_operator_proposal_impl.h中即存在ASCENDC_REPORT_NOT_SUPPORT(false, "MrgSort4")、ASCENDC_REPORT_NOT_SUPPORT(false, "RpSort16")等真实调用,impl/basic_api/dav_3510/kernel_operator_fixpipe_impl.h中也有ASCENDC_REPORT_NOT_SUPPORT(false, "SetFixPipeClipRelu")的案例。这些接口在 3510 架构上明确"不支持",扫描时应归入"不支持,无需 UT"而非"缺失 UT"。

8.2 报告输出格式

状态含义是否需要 UT
✅ 缺失 UT有实现但无测试需要
⚠️ 不支持,无需 UTimpl 为空或不包含有效实现不需要
❌ 已覆盖已有测试不需要

三种状态区分了"该补"、"不用补"、"已补"三类情形,只有 ✅ 才进入--create-tasks的任务创建范围。

9. 测试目录结构

tests/api/是扫描模式的核对基准目录,其组织方式与 API 类型、芯片架构一一对应:

tests/api/ ├── basic_api/ # membase基础API 测试 │ ├── ascendc_case_ascend910b1/ │ │ ├── ascendc_case_ascend910b1_aic/ # AIC (Cube 核心) │ │ └── ascendc_case_ascend910b1_aiv/ # AIV (Vector 核心) │ └── ascendc_case_ascend950pr_9599/ │ ├── adv_api/ # 高阶API 测试 │ ├── activation/ │ ├── math/ │ └── normalization/ │ ├── c_api/ # C API 测试 │ ├── npu_arch_2201/ # ascend910b1 │ └── npu_arch_3510/ # ascend950pr_9599 │ ├── reg_compute_api/ # regbase基础API 测试 │ └── ascendc_case_ascend950pr_9599_reg_compute/ │ ├── simt_api/ # SIMT API 测试 │ └── ascend950pr_9599/ │ └── utils_api/ # 工具类API 测试

以上目录在仓库中均可找到对应实体(如tests/api/reg_compute_api/ascendc_case_ascend950pr_9599_reg_compute/、tests/api/c_api/npu_arch_3510/、tests/api/simt_api/ascendc_case_ascend950pr_9599_simt/),仅utils_api/实际以utils/命名,内含std/、tiling/等子目录。额外的目录映射细节可参考 API 目录映射表。

关键注意事项:

  • regbase 基础 API 测试位于tests/api/reg_compute_api/(非basic_api 目录),扫描时不要把 reg_compute 的测试算到 basic_api 头上;
  • SIMT API 当前仅支持 ascend950pr_9599;
  • tests/api/common/、tests/api/*/stub/、tests/api/basic_api/ascendc_header_checker/等是测试支撑目录或头文件编译检查目录,不是独立 API 类别的 UT 目录,扫描时应排除。

10. 扫描执行步骤

10.1 API 接口扫描

扫描器按 API 类型逐个执行 Glob + Grep 组合,具体命令序列:

# Step 1: 扫描高阶API Glob: {ASC_DEVKIT_PATH}/include/adv_api/**/*.h Grep: pattern="template.*class\s+\w+" # Step 2: 扫描 membase基础API Glob: {ASC_DEVKIT_PATH}/include/basic_api/kernel_operator_*.h Grep: pattern="void\s+\w+\s*\(" # Step 3: 扫描 regbase基础API Glob: {ASC_DEVKIT_PATH}/include/basic_api/reg_compute/**/*.h # Step 4: 扫描 C API Glob: {ASC_DEVKIT_PATH}/include/c_api/**/*.h Grep: pattern="void\s+asc_\w+\s*\(" # Step 5: 扫描 SIMT API Glob: {ASC_DEVKIT_PATH}/include/simt_api/**/*.h Grep: pattern="__device__.*\w+\s*\(" # Step 6: 识别架构条件编译 Grep: pattern="#if.*__NPU_ARCH__|#ifdef.*__DAV_"

这些正则与第 4 节的提取规则一一对应:高阶 API 抓模板类声明、membase/regbase 抓void函数声明、C API 抓asc_前缀函数(C 风格接口统一以asc_命名)、SIMT API 抓__device__设备函数。

10.2 UT 文件扫描与跨文件检测

# 扫描现有 UT Glob: {ASC_DEVKIT_PATH}/tests/api/**/*.cpp # 对于初步判定为"缺失 UT"的 API,执行跨文件检测 Grep: pattern="{APIName}" path="tests/api/{arch_dir}/"

UT 扫描以*.cpp为对象(仓库tests/api/下的测试文件确实以.cpp为主,如tests/api/basic_api/ascendc_case_ascend910b1/ascendc_case_ascend910b1_aiv/内全部为.cpp)。跨文件检测按架构目录限定搜索范围,避免把架构 A 的测试误算为架构 B 的覆盖。

11. 报告输出格式

11.1 Markdown 格式(默认)

## 总体覆盖率统计 | API 类型 | 总 API 数 | 已覆盖 | 未覆盖 | 覆盖率 | |---------|----------|-------|-------|-------| | 高阶API | 45 | 40 | 5 | 88.9% | | membase基础API | 100 | 85 | 15 | 85.0% | | **总计** | **270** | **220** | **50** | **81.5%** | ## 按架构分组 - 缺失 UT 列表 ### ascend910b1 缺失 | API 名称 | 类型 | 头文件位置 | |---------|------|-----------| | NewAPI | membase | include/basic_api/kernel_operator_new.h |

报告包含两层信息:总体覆盖率统计表按 API 类型给出总数/已覆盖/未覆盖/覆盖率四列并汇总总计;按架构分组的缺失 UT 列表把每个缺失项定位到"API 名称 + 类型 + 头文件位置",直接可作为 UT 补充任务的输入清单。上表数字仅为格式示例,真实扫描以仓库实际接口数为准。

12. 检查清单

覆盖率扫描在输出最终报告前,必须逐项通过以下检查:

12.1 扫描前检查

  • 已获取 CANN 包路径;ASC_DEVKIT_PATH 已从当前 workspace 或 skill 所在仓推导

12.2 API 提取检查

  • API 名称仅从头文件声明中提取
  • 每个"缺失 UT"的 API 已确认在头文件中存在声明

12.3 Deprecated 过滤检查

  • 已检测[[deprecated]]属性
  • deprecated API 已排除在覆盖率统计外

12.4 Impl 实现检查

  • 已检测ASCENDC_REPORT_NOT_SUPPORT标记
  • impl 为空的 API 已标记为"不支持,无需 UT"

12.5 跨文件检测检查

  • 判定缺失 UT 前已执行跨文件 Grep 搜索
  • 每个"缺失 UT"的 API 已确认 impl 有实际实现

13. 与相邻工作流的衔接

覆盖率扫描不是终点,它与asc-api-ut-gen技能中的其他工作流构成闭环:

  • 扫描出缺失 UT 后:使用精确交互模式(/asc-api-ut-gen <芯片版本> <API类型> <API名称> [核心类型])或--create-tasks生成的任务,逐 API 补齐测试;UT 代码由scripts/ut_generator_cli.py与scripts/ut_generator.py生成,可参考各 API 类型指南(如 高阶 API UT 指南、C API UT 指南)。
  • 补齐后验证:按 自动化验证流程 选择受影响 test part(如bash build.sh --basic_test_two -j8、bash build.sh --basic_test_five -j8),编译并运行 gtest。
  • 行级覆盖率兜底:若需要更细粒度的行/函数覆盖率,使用覆盖率报告补齐模式(/asc-api-ut-gen cov-report <target>)扫描build/cov_report,低于默认阈值 95% 时自动补测并回归。

一句话总结本指南的完整方法论:从include/头文件提取 API 清单(含架构条件编译)→ 从tests/api/提取 UT 覆盖清单 → 过滤 deprecated 与空实现 → 四级匹配(强制跨文件搜索)→ 按类型与架构双维度输出覆盖率报告 → 用--create-tasks把缺失项转化为补测任务。这套流程保证了 asc-devkit 每一个公开 API 接口都有可追溯、可验证的测试看护。

  • 人工智能
  • 深度学习
  • 算子库
  • CANN
  • Ascend

【免费下载链接】asc-devkit

本项目是CANN 推出的昇腾AI处理器专用的算子程序开发语言,原生支持C和C++标准规范,主要由类库和语言扩展层构成,提供多层级API,满足多维场景算子开发诉求。

项目地址:https://gitcode.com/cann/asc-devkit
点击查看免费下载

相关推荐

上一篇:Authelia Server Authz 端点配置指南:自定义 /api/authz 授权端点与认证策略
下一篇:FastMCP 工具搜索变换(Search Transforms)实战指南:用 Regex 与 BM25 把海量工具目录收敛为按需搜索

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!