尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Carbon 工具链 file_test 测试编写完全指南:文件结构、命名约定与 autoupdate 工作流

Carbon 工具链 file_test 测试编写完全指南:文件结构、命名约定与 autoupdate 工作流 Carbon 工具链 file_test 测试编写完全指南文件结构、命名约定与 autoupdate 工作流【免费下载链接】carbon-langCarbon Languages main repository: documents, design, implementation, and related tools. (NOTE: Carbon Language is experimental; see README)项目地址: https://gitcode.com/GitHub_Trending/ca/carbon-lang本篇指南以 toolchain_tests 技能文档 为主体结合 Carbon 语言仓库中 file_test 基础设施、自动更新脚本 以及toolchain/*/testdata/下的真实测试用例系统讲解如何为 Carbon 工具链编写、结构化和运行 file test。读完本文你将掌握测试文件的头注释与AUTOUPDATE机制、最小化 prelude 的引入方式、split 测试与[[TEST_NAME]]占位符、fail_/todo_前缀语义、常量求值的验证约定、SemIR 输出裁剪以及如何用 autoupdater 一键生成CHECK校验行。工具链测试概览file_test 如何工作Carbon 语言仓库中工具链toolchain的绝大多数行为验证都依赖统一的file_test基础设施。测试数据以.carbon源码文件的形式存放在toolchain/*/testdata/目录中例如 toolchain/check/testdata/覆盖 lex、parse、check、lower 等各个编译阶段。一条工具链测试的典型执行链路是Lexing词法→ Parsing语法→ Checking语义检查→ 可选 Lowering降级。测试框架将 Carbon 源文件送入编译流水线捕获输出例如 SemIR 转储、Clang 报错等再与测试文件内的行内CHECK记录进行比对校验。从源码结构看这套基础设施由三层组成Bazel 规则层testing/file_test/rules.bzl 中的file_test()宏负责把testdata/**的 glob 转成 manifest 并生成cc_test同时会额外生成name.file_path形式的 per-file 手动测试方便单独跑某个文件基类实现层testing/file_test/file_test_base.h 提供FileTestBase子类通过重写Run()与GetDefaultArgs()接入自己的编译逻辑最后用CARBON_FILE_TEST_FACTORY(MyFileTest)注册命令行入口层toolchain/testing/file_test 接收--autoupdate、--file_tests、--threads、--dump_output等参数。文件布局与头注释规范测试文件必须以标准 Carbon 许可证头开头随后是配置注释并用空注释行//分隔不同段落。SKILL.md 给出了标准模板// Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/... // // AUTOUPDATE两条关键规则// AUTOUPDATE是强制性的任何使用CHECK标记的测试文件都必须声明它。它告诉测试框架允许在运行--autoupdate时重写/插入CHECK行若不想被自动更新对应标记是// NOAUTOUPDATE二者必须恰好出现一个见 testing/file_test/README.md 的 Comment markers 一节。使用 split 测试时该标记当前必须位于任何 split 之前// TIP:行由 autoupdater 自动生成例如bazel test //toolchain/testing:file_test --test_arg--file_teststoolchain/check/testdata/alias/builtins.carbon。你不必手写 TIP手写也无害脚本会接管它的存在只是提示开发者如何单独运行该测试对校验结果没有影响。真实示例可参考 toolchain/check/testdata/alias/builtins.carbon 的文件头它同时展示了许可证头、INCLUDE-FILE、AUTOUPDATE与自动生成的TIP行。最小化 preludeINCLUDE-FILE与内置原语当测试内容与Core包完全无关时SKILL.md 建议通过// INCLUDE-FILE引入一个最小化 prelude通常选择toolchain/testing/testdata/min_prelude/下的脚本如int.carbon或primitives.carbon。这样做能显著加快执行速度、最小化 STDOUT 噪音。以 toolchain/testing/testdata/min_prelude/primitives.carbon 为例它是一个只含原始类型的极简 prelude通过INCLUDE-FILE组合bool/char/copy/float/int/string/uint等parts/分片并用EXTRA-ARGS: --custom-core --exclude-dump-file-prefixmin_prelude/告诉测试框架使用自定义 Core 且不在输出中混入 prelude 内容而 toolchain/testing/testdata/min_prelude/int.carbon 则只引入Int/i32数组测试所必需。这些分片本身也是用INCLUDE-FILE层层嵌套的例如 parts/float.carbon 内部又引入了as、copy、float_literal、int_literal。关键约束——内置原语测试在最小化 prelude 下标准运算符、-、/、等不会被导入也不可用。若要以最小的 prelude 足迹编写测试需要直接在测试代码中调用原始内置函数来构造表达式例如用float.negate、float.div等 float.make_type/primitive_*形式暴露的内置函数而不是依赖运算符语法糖。这一点在parts/float.carbon的实现中体现得很直观FloatLiteral as ImplicitAs(Float(To))的转换直接绑定到float.convert_checked这样的原始内置名称。split 测试与[[TEST_NAME]]一个物理文件可以通过 split 标记// --- filename拆成多个逻辑文件从而在一个文件里测试多种场景// --- passing_case.carbon library [[TEST_NAME]]; // ... // --- fail_bad_case.carbon library [[TEST_NAME]]; // ...注意事项SKILL.md 中列为强制约定每个 split 内一律使用library [[TEST_NAME]];而不是硬编码库名。这能防止命名冲突、避免重复定义默认库并让测试代码保持干净、可模板化必须精确书写[[TEST_NAME]]含方括号。测试基础设施会自动把它替换为 split 的文件名去掉todo_和fail_前缀后的结果。其替换语义在 testing/file_test/README.md 的 Content replacement 一节有完整定义替换为“去掉扩展名及fail_/todo_前缀后的文件名”对 split 文件则基于 split 文件名计算严禁把“预期通过”与“预期失败”的代码放进同一个 split。校验机制依赖非失败 split 必须产生零错误失败 split 必须独立地产生正确的编译器错误。混放会破坏这种独立验证的前提。文件命名前缀fail_与todo_预期失败必须与意外失败以及 bug区分开命名前缀是唯一的沟通载体。四种语义如下前缀含义当前状态fail_...测试应当产生编译器错误也确实产生了正确行为todo_fail_...测试应当产生错误但当前没有产生待实现有 bugfail_todo_...测试确实产生错误或崩溃但不应该或错误内容不对、伴随错误时行为异常待修复有 bugtodo_...测试存在某些错误行为但当前不产生错误且也不应该产生错误待修复有 bug主文件命名规则主测试文件以及任何 split 文件只要有关联错误就必须带fail_前缀。例外如果主文件内至少有一个带fail_前缀的 split主文件可以省略fail_。testing/file_test/README.md 同样确认了这条规则主文件与 split 文件的fail_前缀“当且仅当”其有关联错误时是必需的。另外fail_和todo_前缀都会从[[TEST_NAME]]等文件名属性中被剥离。常量求值验证的六项约定SKILL.md 专门为语义检查check测试中的常量求值验证列出了整套约定目的是保证诊断输出稳定、准确。这些约定在编写涉及浮点转换、舍入、泛型参数等场景的测试时务必遵守字面量拼写规范化Literal Spelling Canonicalization在 SemIR 中数学值相同但源码拼写不同的实数字面量可能被分配到不同的内部表示 ID。要彻底杜绝预期输出中因拼写差异导致的不匹配验证测试必须使用规范化的比较手段例如把转换后的值传入Expect(X as f64)这样的函数泛型参数验证本地运行时变量作为泛型实参会被编译期约束拒绝因此要绕过这种约束测试泛型类型转换可以把静态字面量直接传给原始内置调用来验证编译期转换穷举边界情况验证对复杂的数学算法如浮点转整数的截断与舍入要映射并执行覆盖每条代码分支、条件出口与回退求值路径的测试约束舍入阈值边界测试紧贴数学边界的用例例如表示恰好大于 1.0 微小增量的浮点字面量如 $2^{30} \times 2^{-30}$ 或 $10^{10} \times 10^{-10}$验证能精确截断到 1 或 0精确浮点字面量拼写针对目标阈值用精确的数学精度书写浮点字面量。例如测试 1.0 之上最小分数增量时用精确的十六进制小数0x1.0000000000001p0或高精度十进制小数1.0000000000000001而不是1.1这种粗粒度分数以保证边界断言正确表示容量边界与零值尺寸边界显式瞄准目标类型表示能力的极限如i32/u32的 mantissa 与 exponent 组合恰好等于、略低于、略高于容量上限同时对0与0.0边界输入做显式断言确保零输入能被正确“定尺寸并简化”不会触发下溢、除零错误或低估所需位分配。测试代码注释只写“验证什么”不写“思考过程”SKILL.md 明确禁止在测试文件中出现“agent 思考痕迹”——例如“Wait, but...”这类描述推理过程的注释。留在测试里的注释应当简洁地描述该测试本身在验证什么供人类读者理解意图而不是记录 AI 的思维链。这一点也是工具链测试作为回归资产长期可维护的基础。SemIR 转储与输出最小化测试应把 STDOUT 校验范围限制在被测逻辑上。SKILL.md 要求始终使用//dump-sem-ir-begin与//dump-sem-ir-end包裹希望转储 SemIR 的具体声明/代码块只使用这一对标记不要再叠加--dump-sem-ir-rangesif-present之类的额外参数——新测试通过默认行为让//dump-sem-ir...把输出自然过滤到被高亮的片段。//dump-sem-ir-begin fn F(x:? form(ref i32)); //dump-sem-ir-end真实示例见 toolchain/check/testdata/alias/builtins.carbon每个 split 的声明前后都用//dump-sem-ir-begin/end圈定随后紧跟对应的// CHECK:STDOUT:常量表与文件表断言。生成与更新输出autoupdate 工作流AI 工具永远不要手写或手动触碰// CHECK:STDOUT:/// CHECK:STDERR:注释。正确流程是写好 Carbon 测试代码、文件头与// AUTOUPDATE运行测试更新器./toolchain/autoupdate_testdata.py toolchain/PATH/TO/YOUR/TEST.carbon用git diff审查更新后的测试输出确认逻辑路径被正确覆盖而不是产生大段样板代码。该脚本toolchain/autoupdate_testdata.py本质上是bazel run -c build_mode //toolchain/testing:file_test -- --autoupdate ...的封装它通过bazel info workspace探测构建模式默认fastbuild仅接受位于testdata/下的.carbon文件参数并以--file_tests逗号列表的形式传给 file_test。它还支持--threads、--print_slowest_tests以及--non-fatal-checks后者与-c optimize不兼容等透传参数。autoupdate 的插入策略见 testing/file_test/README.mdCHECK从AUTOUPDATE标记下方开始插入带行信息的CHECK会被尽量插到关联行的旁边stderr 的在前、stdout 的在后若测试中没有任何STDOUT检查关联到具体行则所有STDOUT检查行会统一放到文件末尾。对于 split 测试若最后一个 split 命名为// --- AUTOUPDATE-SPLIT则所有CHECK都会集中写进该 split不做行关联。常用的注释标记速查在 testing/file_test/README.md 中可以查到file_test支持的全部行内配置标记它们是 SKILL.md 之上更细的可用选项标记作用// AUTOUPDATE/// NOAUTOUPDATE控制该文件是否参与--autoupdate二者必须恰好出现一个// ARGS: arguments空格分隔的命令行参数支持%s替换为文件列表仅允许独立成参、%t替换为${TEST_TMPDIR}/temp_file、%{identifier}替换为实现相关标识符至多指定一次// EXTRA-ARGS: arguments追加式参数语义同ARGS可重复、可与ARGS并存// INCLUDE-FILE: path/from/repository/root把指定文件纳入测试的虚拟文件系统并拼入当前文件的 split被包含文件还可继续使用ARGS/EXTRA-ARGS/INCLUDE-FILE/--- filename未给 split 名时以include_files/filename命名。最小化Core包就是靠它实现的// SET-CAPTURE-CONSOLE-OUTPUT把测试自身而非传入流的 stdout/stderr 也捕获进输出应尽量避免使用仅在包装 Clang直接写 stderr时有用// SET-CHECK-SUBSET默认输出每一行都必须有CHECK匹配加上它后未匹配行被忽略但已有的CHECK:STDOUT:/CHECK:STDERR:仍须全部命中// --- filename把物理文件切成多个逻辑文件文件不落盘而是经fs传入Run文件名若为STDIN则改走input_stream// CHECK:STDOUT:/// CHECK:STDERR:对命令输出的匹配行支持[[LINEoffset]]与{{regex}}语法类似 FileCheck// TIP: tip由 autoupdate 生成的提示如单测运行命令不影响校验autoupdate 可按需更新或删除如何注册一个新的 file_test若要在仓库中为新的测试程序接入这套框架Bazel 侧与 C 侧的最小骨架如下均来自 testing/file_test/README.mdload(rules.bzl, file_test) file_test( name my_file_test, srcs [my_file_test.cpp], tests glob([testdata/**]), deps [ :my_lib, //testing/file_test:file_test_base, googletest//:gtest, llvm-project//llvm:Support, ], )#include my_library.h #include testing/file_test/file_test_base.h namespace Carbon::Testing { namespace { class MyFileTest : public FileTestBase { public: using FileTestBase::FileTestBase; auto Run(const llvm::SmallVectorllvm::StringRef test_args, const llvm::SmallVectorTestFile test_files, FILE* input_stream, llvm::raw_pwrite_stream output_stream, llvm::raw_pwrite_stream error_stream) - ErrorOrRunResult override { return MyFunctionality(test_args, input_stream, output_stream, error_stream); } auto GetDefaultArgs() - llvm::SmallVectorstd::string override { return {default_args, %s}; } }; } // namespace CARBON_FILE_TEST_FACTORY(MyFileTest); } // namespace Carbon::Testing其中Run()负责在单条测试执行时接收参数与文件并产出输出流GetDefaultArgs()为未提供ARGS的测试提供默认参数典型值为%s文件占位最后通过CARBON_FILE_TEST_FACTORY注册进框架。rules.bzl的实现还会额外生成 per-file 手动测试便于开发者针对单个 testdata 文件调试。小结Carbon 工具链的 file_test 体系把“写测试”变成了一件高度模板化、可自动维护的工作用AUTOUPDATE声明可更新性用INCLUDE-FILE裁剪 prelude 以提速用 split [[TEST_NAME]]在单文件内组织多场景用fail_/todo_前缀区分预期与非预期行为用dump-sem-ir-begin/end裁剪输出最后交给 autoupdater 生成权威的CHECK行。遵循这套约定既能保证测试输出的稳定性与可审查性也让 Carbon 语言本身的每次演进都有可信的回归防线。【免费下载链接】carbon-langCarbon Languages main repository: documents, design, implementation, and related tools. (NOTE: Carbon Language is experimental; see README)项目地址: https://gitcode.com/GitHub_Trending/ca/carbon-lang创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表