ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

CANN Runtime 仓 UT 代码硬性规范:9 条规则与仓库实践解析

CANN Runtime 仓 UT 代码硬性规范:9 条规则与仓库实践解析 CANN Runtime 仓 UT 代码硬性规范9 条规则与仓库实践解析【免费下载链接】runtime本项目提供CANN运行时组件和维测功能组件。项目地址: https://gitcode.com/cann/runtimeCANN Runtime 开源仓库cann/runtime的 UT 代码规范文档 定义了一套覆盖断言、mock 管理、测试隔离与可维护性的硬性规则用于补充dt_guide系列中偏方法论和设计层面的内容。本文以这 9 条规则为主体骨架结合仓库内tests/ut/**的真实测试代码、tests/build_ut.sh构建入口及配套指南文档逐条给出规则意图、落地要点与仓库实证帮助开发者在编写 Runtime 仓 UT 时一次写对、避免返工。一、规范定位硬性约束与设计方法论的分工在深入逐条规则之前有必要先厘清这份文档在 Runtime 仓测试知识体系中的位置。仓库docs/zh/guidelines/目录下维护了一套成体系的测试文档UT 代码规范本文主题定义硬性规范属于必须做到的底线要求主要面向代码评审与合规检查UT 用例开发指导覆盖 UT 设计 checklist、完整校验方法、全局状态恢复、兼容性测试建议等方法论与设计层面的内容测试框架指南介绍 gtest、gmock/mockcpp、公共桩与模块内 stub/data 的用法Runtime DT 用例开发总纲说明用例该写在哪里、如何接入构建系统以及设计用例的基本约束。两者的分工是先按方法论把用例设计出来再按硬性规范逐条自检。规范文档中的 9 条规则分属四个维度断言规范规则 1–2、mock 与全局状态规范规则 3–4、测试隔离规范规则 5–6、可维护性规范规则 7–9。二、断言规范杜绝只调用不校验规则 1每个测试用例必须有有效断言每个测试用例必须至少包含一条有效的EXPECT或ASSERT禁止只调用接口、不校验结果的测试代码。这条规则是 UT 的底线。所谓有效断言指的是断言必须与场景直接相关而不是凑数的空校验。仓库现有用例中EXPECT_EQ、EXPECT_NE、EXPECT_TRUE是最常见的断言形式。例如 rt_utest_set_soc_type.cc 中TEST_F(RuntimeSetSocTypeTest, SetSocTypeByChipType_test_for_david_v120) { Runtime* rtInstance ((Runtime*)Runtime::Instance()); EXPECT_NE(rtInstance, nullptr); // 先断言被测对象有效 int64_t aicoreNumLevel 0; int64_t vmAicoreNum 0; rtError_t result RT_ERROR_NONE; rtInstance-chipType_ CHIP_DAVID; result rtInstance-GetSocVersionByHardwareVer( (PLAT_COMBINE(ARCH_V100, CHIP_DAVID, PG_VER_BIN24)), aicoreNumLevel, vmAicoreNum); EXPECT_EQ(result, RT_ERROR_NONE); // 再断言调用结果 }该用例对每次GetSocVersionByHardwareVer调用都使用EXPECT_EQ(result, RT_ERROR_NONE)校验返回值同时对获取到的rtInstance做了空指针保护断言属于规则 1 的典型合规写法。规则 2断言必须覆盖关键可观察结果除返回值外还应校验与场景直接相关的出参、状态变化、副作用、回调、文件或目录结果对失败场景必须校验错误码、错误状态或关键保护行为而不是只断言执行结束。规则 2 是对规则 1 的深化也是 UT 用例开发指导 中做完整校验一节强调的核心观点只校验返回码不校验副作用的用例很难防住行为回归。常见校验点包括返回值或错误码是否准确出参是否被正确填写被测对象状态是否按预期变化mock 是否以正确参数被调用以及调用次数是否符合预期失败路径上的清理逻辑是否执行文件、目录、日志、统计信息是否正确产生。对失败场景尤其要校验错误码、错误状态或关键保护行为。例如测试rtSetDevice在设备无效时的行为应当断言返回的错误码等于预期的rtError_t值并校验相关状态没有被污染而不是仅仅断言函数返回了。反模式警示规则 2 同时隐含避免重复校验的原则——同一类校验尽量只在一个最贴近场景的用例里完成重复校验过多会让后续改动引发大量无效失败降低用例可维护性。三、mock 与全局状态规范不让污染跨用例传播规则 3mock 对象必须在测试退出时verify或reset每个测试类都应在TearDown、TearDownTestCase或等价收口点完成 mock 清理不得让 mock 状态泄漏到后续用例。Runtime 仓大量用例依赖 gmock 或 mockcpp 两种框架无论使用哪一种都需要在测试结束时完成校验和清理。在 测试框架指南 中可以看到两者的典型能力gmock适用于已有 mock 类或接口期望校验场景常用EXPECT_CALL(...)设置期望、Mock::VerifyAndClear(...)校验并清理mockcpp适用于全局函数、静态函数、成员函数替换场景常用MOCKER(...)、MOCKER_CPP(...)、MOCKER_CPP_VIRTUAL(...)打桩GlobalMockObject::verify()统一校验。UT 用例开发指导 给出了两种合规收口模式class QueueTest : public testing::Test { protected: void SetUp() override { MockFunctionTest::aclStubInstance().ResetToDefaultMock(); } void TearDown() override { Mock::VerifyAndClear((void *)(MockFunctionTest::aclStubInstance())); } };class PackageProcessConfigTest : public testing::Test { protected: void TearDown() override { GlobalMockObject::verify(); } };第一种模式在SetUp中把 ACL 公共桩恢复为默认行为在TearDown中通过 gmock 的Mock::VerifyAndClear校验 mock 期望是否全部满足并清除状态第二种模式通过 mockcpp 的GlobalMockObject::verify()统一收口。两条路径殊途同归确保一个测试类结束时不残留任何 mock 期望或桩函数替换。规则 4修改全局状态后必须恢复如果测试修改了 SoC 类型、device、环境变量、单例状态、静态缓存、全局指针或成员指针退出时必须恢复原值测试代码不得依赖其他测试先行设置某个全局状态。Runtime 是被测对象是全局性很强的运行时组件SoC 类型、当前 device、上下文单例、静态缓存等状态贯穿整个进程。若一个用例修改了这些状态而没有恢复后续用例的执行环境就会被静默改变产生偶发失败、单独跑能过、全量跑不过的典型问题。UT 用例开发指导 明确指出如果测试过程中修改了芯片类型、device、环境变量、静态标志位或单例内部状态也必须在TearDown/TearDownTestCase中恢复。仓库中tests/ut/runtime/runtime/test/common/下提供了rt_utest_context_reset_helper.hpp、rt_utest_xpu_helper.hpp等公共 helper正是用于封装设置—恢复这类公共逻辑避免每个用例各自重复实现。多个用例共享同一套初始化/恢复逻辑时应抽出统一 fixture而不是复制粘贴。四、测试隔离规范每个用例都是独立的规则 5测试用例之间不得互相依赖每个测试必须独立构造输入、目录、配置和上下文不得依赖执行顺序也不得假设前一个测试已创建临时文件、目录或注册状态。gtest 框架本身不保证用例的执行顺序且开发者经常会用--gtest_filter单独执行某个用例来定位问题。如果用例 A 假设用例 B 已经创建了某个文件或注册了某个状态那么单独跑 A 必然失败。这条规则同时呼应 Runtime DT 用例开发总纲 中一个用例只校验一个明确场景的规范——用例粒度越聚焦对前置状态的假设就越少独立性越强。规则 6临时文件和目录必须清理使用/tmp、相对路径目录或临时文件时必须有清理逻辑路径命名应带模块或用例特征避免与其他用例冲突。Runtime 很多测试会读写/tmp、相对路径目录或临时文件。写这类用例时要注意三点路径命名带上模块或用例特征例如将 Dump 场景的临时目录命名为/tmp/rt_adump_test_case_name而不是笼统的/tmp/test避免与并行执行的其他用例冲突测试完成后清理临时文件和目录清理逻辑可以放在TearDown中也可以在用例末尾显式执行不要依赖其他用例预先创建目录或文件每个用例都应自行创建自己需要的目录结构。此外测试框架指南 还建议如果新增测试会创建大量临时文件或目录应在本地同时跑一遍覆盖率-c或 AddressSanitizer--asan尽早暴露泄漏和残留状态问题。五、可维护性规范让 UT 持续可演进规则 7减少直接访问私有成员优先通过公开接口校验行为只有在没有合理替代方案时才使用#define private public或#define protected public。仓库现有代码中确实存在#define private public/#define protected public的做法例如 rt_utest_set_soc_type.cc 就通过#define private public引入了runtime.hpp以便直接访问rtInstance-chipType_这样的内部成员#define private public #include runtime.hpp #include runtime_keeper.h ... #undef private但正如规则 7 所述这只能作为受限场景下的补充手段不应成为默认方案。推荐优先级如下优先校验公开接口暴露出来的行为若必须观察内部状态优先复用现有 helper、stub 或 accessor如tests/ut/runtime/runtime/test/common/下的公共辅助头文件只有在确实没有更好办法时才临时使用#define private public。注意#define private public会改变预处理后的类布局语义与真实编译环境的差异可能掩盖问题因此使用时必须同时配合注释说明原因。规则 8减少在 UT 中直接调用SetChipType如果必须调用应同时说明原因并在测试退出时恢复状态。SetChipType以及文档 checklist 中并列的rtSetSocVersion、rtSetDevice会改变运行时全局的 SoC 类型状态直接影响后续用例对平台分支的判断。这条规则与规则 4 是配套的调用必须说明原因退出必须恢复状态。仓库中tests/ut/runtime/runtime/test/rt_utest_set_soc_type.cc专门针对GetSocVersionByHardwareVer这类依赖 SoC 类型的能力做测试此时设置chipType_是测试主题本身的一部分但对于普通接口测试应优先通过SetUp/TearDown中的统一恢复逻辑或复用公共 helper 来管理与 SoC 类型相关的全局状态。规则 9命名与目录风格必须保持一致新增测试文件命名应遵循所在目录既有风格测试类名、用例名和辅助文件名应能表达被测对象、场景和预期行为。仓库各模块 UT 命名并不完全统一常见后缀包括*_unittest.cpp/.cc和*_utest.cpp/.cc。例如tests/ut/acl/testcase/acl_queue_unittest.cpp*_unittest.cpp风格tests/ut/runtime/runtime/test/rt_utest_api.cc*_utest.cc风格tests/ut/tsd/tsdclient/test/package_process_config_utest.cpp*_utest.cpp风格。新增文件时应遵循同目录保持同风格原则不要在一个已有明确命名习惯的目录中再引入新的后缀风格。ST 文件建议统一使用*_stest.cpp/*_stest.cc并放在对应模块的st/目录或平台场景目录中例如tests/ut/msprof/st/api/testcase/api_dc_stest.cpp。除文件后缀外用例名本身也应满足三要素能看出测试的是哪个接口/类方法/特性、能看出期望结果是成功还是失败、能看出触发该行为的关键场景。例如SetSocTypeByChipType_test_for_david_v120就同时表达了被测对象SetSocTypeByChipType、场景chipType 为 david、平台v120。六、规范落地从用例设计到构建验证6.1 配套的用例设计 checklist规范文档定义了不做的底线而如何设计的正面清单在 UT 用例开发指导 中给出。为某个接口设计测试点时建议优先检查以下内容再对照 9 条硬性规范自检检查维度典型检查项外部输入校验空指针、非法枚举值、越界长度/容量/索引、不满足前置条件的句柄或状态边界值空容器、零长度、最小/最大合法值、单元素与多元素场景资源生命周期申请和释放是否配对、失败路径是否有泄漏、重复创建/重复释放是否安全全局状态与单例rtSetSocVersion、SetChipType、rtSetDevice等全局状态切换环境变量、配置开关、静态缓存是否需要恢复平台/芯片分支同一接口在不同 SoC、不同 driver 能力下是否有分支行为回调与副作用回调是否注册/触发文件、目录、日志、统计信息是否正确产生如果某个检查项与当前接口无关可以不为其强行设计用例。同时要避免几类不推荐的 UT 设计只验证调用成功/失败不验证任何结果、在一个用例里串多个弱相关场景、为了覆盖率硬测不真实的异常组合、用例依赖前一个用例执行后的残留状态。如果某个场景已经跨越模块边界需要启动模拟器、设备环境或完整流程通常更适合放到 ST。6.2 构建与验证入口Runtime 仓测试工程位于tests/目录统一入口为 build_ut.sh常用命令如下# 构建并执行全部测试 bash tests/build_ut.sh -u # 构建并执行指定模块 bash tests/build_ut.sh -u runtime bash tests/build_ut.sh -u acl bash tests/build_ut.sh -u msprof # 使能覆盖率 bash tests/build_ut.sh -c # 使能 AddressSanitizer bash tests/build_ut.sh --asan -u runtime当前支持的常用 target 包括full、acl、runtime、runtime_c、platform、queue_schedule、aicpu_sched、slog、atrace、msprof、adump、tsd、error_manager、mmpa。新用例文件加入构建系统时需要完成三处接入详见 Runtime DT 用例开发总纲将新文件加入对应模块CMakeLists.txt的UT_FILES或add_executable(...)源文件列表若新增测试子目录在父级CMakeLists.txt补充add_subdirectory(...)若希望bash tests/build_ut.sh -u target直接执行新模块同步更新build_ut.sh中的 target 映射。6.3 兼容性变更的测试要求UT 用例开发指导 还补充了一条与规范配套的要求如果修改了 ACL 对外结构体、枚举、常量、接口签名或 ABI 相关内容应同步检查是否需要补充或更新兼容性测试例如tests/ut/acl/testcase/compatibility/目录及对应模块已有的 ABI/接口回归用例。这条要求可以与规则 2 的覆盖关键可观察结果互相印证——对外契约的变更必须通过可观察的断言来锁定。结语把 9 条规则当成评审清单CANN Runtime 仓的这 9 条 UT 硬性规范本质上是把高质量单元测试的共性经验固化为可评审、可执行的检查项断言规范保证用例真的在验证mock 与全局状态规范保证跑完不污染测试隔离规范保证每个用例独立可信可维护性规范保证用例能长期演进。编写新用例时可以按四步走先按设计 checklist 规划测试点 → 按完整校验原则编写断言 → 在 SetUp/TearDown 中收口 mock 与全局状态 → 对照 9 条规则逐条自检。在提交 MR 之前用 UT 代码规范文档 作为评审清单过一遍能最大程度避免用例能过但没测到点上的无效测试。【免费下载链接】runtime本项目提供CANN运行时组件和维测功能组件。项目地址: https://gitcode.com/cann/runtime创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表