ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

PyPTO Pass 单元测试生成检查清单实战指南:从环境准备到覆盖率验收的全流程质量门禁

2026/9/18 17:43:19 拓冰建站 浏览量
PyPTO Pass 单元测试生成检查清单实战指南:从环境准备到覆盖率验收的全流程质量门禁 PyPTO Pass 单元测试生成检查清单实战指南从环境准备到覆盖率验收的全流程质量门禁【免费下载链接】pyptoPyPTO发音: pai p-t-oParallel Tensor/Tile Operation编程范式。项目地址: https://gitcode.com/cann/pypto本文围绕 CANN / PyPTO 仓库中的 Pass 单元测试UT生成检查清单check_list.md展开系统梳理了从环境准备、业务分析、测试用例编写到编译运行、覆盖率验证与文档更新的完整质量门禁流程。读完本文你将掌握如何为任意 Pass 设计并交付一套编译零告警、覆盖率 ≥ 80%、用例语义正确的 C 单测并能结合仓库内的真实测试样例如 test_removeredundantop.cpp与自动化工具快速定位问题。一、检查清单总览Pass UT 交付的四个阶段Pass编译优化/图变换流程是 PyPTO 框架中负责对 Tensor/Tile/Block 图进行变换的核心组件其正确性直接决定代码生成质量。为 Pass 编写单元测试UT是 PR 合入前的重要质量保障。检查清单将整个 UT 生成过程划分为四个阶段、十个检查项阶段检查项核心目标执行前检查1. 环境准备 / 2. 业务分析 / 3. 代码理解确认仓库路径与测试落点摸清 Pass 功能与分支生成过程检查4. 测试文件创建 / 5. 测试用例编写 / 6. 代码规范搭建测试类骨架编写语义正确的用例验证检查7. 编译验证 / 8. 运行验证 / 9. 覆盖率检查编译零告警、用例全通过、覆盖率 ≥ 80%输出检查10. 文档更新沉淀示例与覆盖率改进记录四个阶段环环相扣前三个检查项决定了测什么第四至第六项决定了怎么测第七至第九项决定了测没测过、测全没有最后一项则负责知识沉淀保证技能Skill文档持续可复用。二、执行前检查环境、业务与代码三确认2.1 环境准备确认三条路径动手写代码前先完成三个路径确认PyPTO 仓库路径确认当前已处于仓库根目录即包含build_ci.py、framework/、python/的目录测试文件路径确认测试落点目录framework/tests/ut/passes/src/存在。该目录下按 Pass 名称组织test_xxx.cpp文件目前已有test_removeredundantop.cpp、test_cube_process.cpp、test_auto_cast.cpp等数十个测试文件是命名与写法的最佳参照Pass 实现文件路径确认待测 Pass 的源码位置。从仓库结构看Pass 实现分布在framework/src/passes/下的tensor_graph_pass/、tile_graph_pass/、block_graph_pass/等子目录Pass 所在目录直接决定了后续编译阶段COMPILE_STAGE的配置值。2.2 业务分析三类输入场景根据需求来源的不同业务分析分为三种情形明确指定 Pass 功能如设计 ProcessAtomic 消除 ReduceAcc 功能的 UT需要精确理解该功能的输入输出变换模糊业务描述如针对视图类 Opview、assemble处理的 Pass 设计 UT需要先对相关 Pass 做业务总结筛选出符合的 PassPR / diff / 覆盖率报告驱动解析变更代码或未覆盖行反推需要补测的业务场景。分析时应同时确定测试用例的类型组合正向用例验证正常变换结果、负向用例验证不满足条件时 Pass 不误伤、边界用例空图、单 Op、长 Op 链、多消费者等。2.3 代码理解数据流与分支路径阅读 Pass 实现代码时重点关注数据流Tensor 的 producer/consumer 关系如何在 Pass 中被改写关键逻辑Pass 的核心变换如冗余 Op 删除、Cast 插入、内存类型指派在哪个函数中完成分支路径if/else、异常返回、架构差异如NPUArch不同值等可能产生不同结果的分支每一个分支都应当有对应用例。三、生成过程检查测试文件、用例与代码规范3.1 测试文件创建类命名与生命周期方法在framework/tests/ut/passes/src/test_xxx.cppxxx 为 Pass 名称中创建测试类需要遵守三条硬性约定类名格式Test{pass_name}Pass例如TestRemoveRedundantOpPass头文件引用至少包含gtest/gtest.h与测试工具头文件如computational_graph_builder.h、pass_test_utils.h、interface/configs/config_manager.h等生命周期方法实现SetUpTestCase()、TearDownTestCase()全局级与SetUp()、TearDown()用例级。仓库中 test_removeredundantop.cpp 给出了标准的初始化模板class TestRemoveRedundantOpPass : public ::testing::Test { public: static void SetUpTestCase() {} static void TearDownTestCase() {} void SetUp() override { Program::GetInstance().Reset(); config::Reset(); config::SetHostOption(COMPILE_STAGE, CS_EXECUTE_GRAPH); config::SetHostConfig(KEY_STRATEGY, ExpandFunctionTestStrategy); config::SetPlatformConfig(KEY_ENABLE_COST_MODEL, false); TileShape::Current().SetVecTile({64, 64}); } void TearDown() override {} };SetUp()中各配置行的作用如下配置项含义取值说明Program::GetInstance().Reset()重置单例 Program保证用例间环境干净必须调用否则用例间状态相互污染config::Reset()重置全局配置必须调用SetHostOption(COMPILE_STAGE, ...)设置编译阶段策略取值见下文编译阶段参考值SetHostConfig(KEY_STRATEGY, XXXTestStrategy)设置 host 侧策略名一般命名为{Pass}TestStrategySetPlatformConfig(KEY_ENABLE_COST_MODEL, false)关闭代价模型平台策略键定义见 config_manager.hTileShape::Current().SetVecTile(...)/SetCubeTile(...)设置 vector/cube 的 tile 块大小如{64, 64}编译阶段参考值COMPILE_STAGE必须与 Pass 所在目录匹配其常量定义于 config_manager_ng.h阶段配置值对应 Pass 目录TENSOR GRAPH 执行CS_TENSOR_GRAPH1tensor_graph_pass/TILE GRAPH 执行CS_TILE_GRAPH2tile_graph_pass/BLOCK GRAPH 执行CS_EXECUTE_GRAPH3block_graph_pass/选错编译阶段是 Pass 执行失败或行为不符预期的高频原因务必先确认 Pass 源文件所在目录再配置。3.2 测试用例编写两种构建流程仓库的 SKILL 文档提供了两条 UT 编写流程均可用于搭建TEST_F(TestXxxPass, XxxCase) { ... }用例框架流程一手工构建 function Tensor Operation以 test_removeredundantop.cpp 中的真实用例为例TEST_F(TestRemoveRedundantOpPass, RemoveRedundantOpUTest1) { auto currFunctionPtr std::make_sharedFunction(Program::GetInstance(), TestRemoveRedundantOp, TestRemoveRedundantOp, nullptr); EXPECT_TRUE(currFunctionPtr ! nullptr); // Prepare the graph std::vectorint64_t shape {kNumEight, kNumExpFour}; // {8, 16} auto inCast npu::tile_fwk::IRBuilder().CreateTensorVar(DT_FP32, shape, CreateTestConstIntVector(shape)); auto ubTensor npu::tile_fwk::IRBuilder().CreateTensorVar(DT_FP32, shape, CreateTestConstIntVector(shape)); auto outCast1 npu::tile_fwk::IRBuilder().CreateTensorVar(DT_FP32, shape, CreateTestConstIntVector(shape)); // ... PassOperationUtils::AddOperation(*currFunctionPtr, Opcode::OP_EXPAND, {inCast}, {ubTensor}); PassOperationUtils::AddOperation(*currFunctionPtr, Opcode::OP_EXP, {ubTensor}, {outCast1}); PassOperationUtils::AddOperation(*currFunctionPtr, Opcode::OP_SQRT, {ubTensor}, {outCast2}); PassOperationUtils::AddOperation(*currFunctionPtr, Opcode::OP_RECIPROCAL, {ubTensor}, {outCast3}); currFunctionPtr-inCasts_.push_back(inCast); currFunctionPtr-outCasts_.push_back(outCast1); // ... RemoveRedundantOp removeredundantpass; EXPECT_EQ(removeredundantpass.RunOnFunction(*currFunctionPtr), SUCCESS); EXPECT_EQ(removeredundantpass.PostCheck(*currFunctionPtr), SUCCESS); uint32_t expand_num kNumZero; for (auto op : currFunctionPtr-Operations()) { if (op.GetOpcode() Opcode::OP_EXPAND) { expand_num; } else if (op.GetOpcode() Opcode::OP_SQRT) { EXPECT_EQ(op.GetInputOperandSize(), kSizeOne); EXPECT_EQ(op.GetInputOperand(kSizeZero), inCast); } // ... } }流程二使用 ComputationalGraphBuilder 声明式构建computational_graph_builder.h 提供了更简洁的声明式构建接口class ComputationalGraphBuilder { public: bool AddTensor(DataType dataType, const std::vectorint64_t tileShape, const std::string name); bool AddTensors(DataType dataType, const std::vectorint64_t tileShape, const std::vectorstd::string names); bool AddOp(Opcode opcode, const std::vectorstd::string ioperands, const std::vectorstd::string ooperands, ...); bool AddOps(const std::vectorOpcode opcodes, const std::vectorstd::vectorstd::string ioperandss, ...); bool SetInCast(std::vectorstd::string ioperands); bool SetOutCast(std::vectorstd::string ooperands); };调用AddTensor()/AddTensors()构建 TensorAddOp()/AddOps()构建 Op再用SetInCast()/SetOutCast()绑定 function 的输入输出。该方式适合图结构较大、Op 较多的场景具体可参考test_cube_process.cpp。关键要素速查shape 与数据类型选择能覆盖 Pass 逻辑的 shape如{8, 16}、{64, 64}与合适的数据类型DT_FP32等定义见 data_type.hLogicalTensor构造参数Function、DataType、Shape、TileOpFormat、名称、NodeType参见 logical_tensor.hinCast / outCast通过currFunctionPtr-inCasts_/outCasts_或SetInCast()/SetOutCast()正确设置 function 的输入输出 Tensor这是 Pass 校验输入输出类型的关键用例注释在TEST_F上方用注释描述 Pass 前后的图变化便于审阅者快速理解用例意图如expand 被删除三个消费者直接挂到 inCast 上验证 pass 执行结果调用PreCheck/RunOnFunction/PostCheck并EXPECT_EQ(..., SUCCESS)校验返回值再遍历Operations()统计各 Opcode 数量、校验 Tensor 的 producer/consumer 关系与数据类型。3.3 代码规范三禁三用禁止魔鬼数字魔法数字应提取为具名常量。仓库惯例是定义static const size_t kSizeZero 0UL;、static const uint16_t kNumEight 8u;等见 test_removeredundantop.cppOpcode 比较一律使用Opcode::OP_XXX枚举而非数字常量用 const 修饰不修改的引用加const未使用的变量一律删除必要的空行与缩进保持与仓库现有风格一致函数体 4 空格缩进、if/else块换行等智能指针管理资源Tensor/Function等动态对象用std::make_shared创建AddOperation返回引用需用Operation op ...接收使用 getter 方法访问成员一律走GetShape()、GetDatatype()、GetConsumers()、GetProducers()等接口禁止直接访问成员变量遍历时先复制再修改需要边遍历边删除 Op 时先auto opList function-Operations().DuplicatedOpList();复制列表再遍历避免迭代器失效。四、验证检查编译、运行与覆盖率三道关4.1 编译验证在仓库根目录执行编译命令-c表示 clean 构建-u表示 UTest 过滤-j指定并行任务数-fcpp指定 C 前端python3 build_ci.py -c -uTestPassName.* -j24 -fcpp需要确认编译无错误、无警告。若仅需单独运行指定用例避免编译整个工程超时可精确到用例名python3 build_ci.py -c -uTestPassName.TestCaseName -j24 -fcppbuild_ci.py中-u/--utest的过滤逻辑由TestsFilterParam类处理见 build_ci.py参数带值时作为过滤串直接传给 CMake/测试框架-j/--job_num未指定时自动按 CPU 数与可用内存推导并行度build_ci.py-c/--clean在构建前清理 Build-Tree 与 Install-Treebuild_ci.py。4.2 运行验证执行 UT 测试用例并确认全部通过python3 build_ci.py -uTestPassName.* -j24 -fcpp若运行超时按先类、后用例的粒度逐步收缩先整类运行仍超时则单用例运行。用例失败时优先检查 Tensor 的 shape 与 memory type 是否正确、期望值是否与 Pass 实际行为一致可先运行 Pass 打印实际操作数再校准期望值。4.3 覆盖率检查通过 GCov 统计代码覆盖率行覆盖率与函数覆盖率python3 build_ci.py -c -uTestPassName.* --gcov -j24 -fcpp--gcov会开启 CMake 的ENABLE_GCOV编译插桩见 build_ci.py。执行结束后build/路径下会生成cov_result目录打开其中的index.html即可查看对应 Pass 的 UT 覆盖率。验收标准覆盖率 ≥ 80%若低于该阈值需识别未覆盖的代码行针对这些业务分支补充用例并重复上述流程直到达标。仓库还提供了离线覆盖率分析脚本 ut_coverage.py可解析本地覆盖率报告支持.tar.gz与 HTML 格式并自动给出低覆盖率文件与未覆盖行# 解析本地覆盖率报告 python3 ut_coverage.py --report /path/to/ut_cov.tar.gz # 关联 diff 与覆盖率生成 UT 设计建议JSON 输出 python3 ut_coverage.py --diff pr.diff --report ut_cov.tar.gz --json脚本还会按 80% 阈值筛出低覆盖率文件find_low_coverage_files并将未覆盖行与 diff 变更行关联correlate_coverage_with_diff按优先级输出ut_design_suggestions可直接作为补测清单使用。五、输出检查文档更新与覆盖率记录用例全部落地且覆盖率达标后还需完成两项收尾工作更新 SKILL.md 中的示例把本次新 Pass 的测试用例范式沉淀到 SKILL.md 中如补充新的参考测试文件路径、新增流程示例使后续同类 Pass 的 UT 生成有据可依记录覆盖率的改进情况在 PR 描述或技能文档中记录补测前后的覆盖率变化作为该 Pass 测试充分性的可审计证据。六、常见问题速查与排查指引6.1 速查表继承自检查清单问题解决方案编译超时使用-uTestPassName.TestCaseName单独运行测试失败检查 tensor 的 shape 和 memory type覆盖率低增加更多场景的测试用例断言失败验证 pass 处理后的 expected 值6.2 高频错误的源码级定位结合仓库的 common_errors.md记录了 43 个典型错误案例与 trouble_shooting.md以下是最容易踩坑的几类编译类function-Operations().().size()多写一层括号——Operations()返回的是引用或对象直接.size()即可中文字符/中文冒号混入代码——务必使用英文输入法头文件缺失或命名空间错误——参考现有测试文件补齐gtest/gtest.h、computational_graph_builder.h等必要头文件确认npu::tile_fwk命名空间。执行类编译阶段配置错误——按 Pass 目录选择CS_TENSOR_GRAPH/CS_TILE_GRAPH/CS_EXECUTE_GRAPH未重置 Program 与 Config——在SetUp()中重置保证用例间环境干净未调用或未验证PreCheck/RunOnFunction/PostCheck返回值修改 NPU 架构后未恢复——测试结束前恢复NPUArch::DAV_UNKNOWN。验证类只验证 Op 数量不验证内容——应遍历统计各 Opcode 的具体数量并校验关键 Tensor 的消费者/生产者关系与数据类型遍历时修改容器——先DuplicatedOpList()复制再修改GetOpAttribute()返回shared_ptr——dynamic_cast前先.get()取原始指针并ASSERT_NE(attr, nullptr)防空。覆盖类用例覆盖不全——列出 Pass 的全部功能点如 InsertBF16Cast、RemoveRedundantCastChain每个功能点至少一个用例未测边界情况——补充空图、单 Op、多消费者、Op 链等边界场景未测架构差异——针对不同NPUArch如DAV_2201、DAV_3510设计专门用例。七、工具链与参考资源除手工流程外技能目录还提供两个辅助脚本get_ut_status.py快速获取 PR 的 UT 状态UT_Test_report状态与覆盖率报告链接在线场景如为 PR xxxx 补充 UT的第一步python3 get_ut_status.py 2017ut_coverage.py离线分析 diff 与覆盖率报告前述支持--diff、--report、--content三种输入与--json输出还可按--threshold自定义覆盖率阈值默认 80.0。编写与排查时可重点参考的仓库文件用途路径生成流程一手工构建示例test_removeredundantop.cpp生成流程二Builder 构建示例test_cube_process.cppComputationalGraphBuilder 接口computational_graph_builder.hfunction / Program 实现function.h、program.cppOpcode 定义opcode.hDataType 定义data_type.hCOMPILE_STAGE 策略config_manager_ng.h构建/测试入口build_ci.py主技能文档与错误手册SKILL.md、common_errors.md、trouble_shooting.md八、结语把检查清单变成交付习惯Pass UT 生成不是写几个用例的机械劳动而是一条有明确质量门禁的流水线执行前确认环境与业务生成时严守类命名与代码规范验证时以编译零告警 用例全通过 覆盖率 ≥ 80%三道关卡验收收尾时沉淀文档。将本文梳理的十项检查清单固化为日常交付习惯即可稳定地产出高质量、可审计、可持续维护的 Pass 单元测试为 PyPTO 框架的每次图变换变更提供可靠的回归保障。【免费下载链接】pyptoPyPTO发音: pai p-t-oParallel Tensor/Tile Operation编程范式。项目地址: https://gitcode.com/cann/pypto创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考