Rust 编译器(rustc)测试运行完全指南:从./x test全量套件到远程设备、模拟器与 WebAssembly 测试
【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust
本指南基于 rustc 官方开发指南(rustc-dev-guide)中“Running tests”章节,系统讲解在 rust-lang/rust 源码仓库中运行测试的全部方式:包括全量测试、按测试套件/子目录/单文件筛选、--bless更新参考输出、--pass与 compare-mode 高级模式,以及在远程机器、QEMU 模拟器、iOS 模拟器和 wasm32-wasip1(WASI)上执行测试的完整配置流程。读完本文,你将掌握 rustc 本地开发中最常用的测试命令组合,并理解其背后的 compiletest 测试框架实现原理。
一、全量测试:./x test与其代价
在仓库根目录(包含x.py的位置)运行以下命令即可执行整个测试集合:
./x test但请注意:本地开发中几乎不应该运行全量测试,因为它耗时极长。./x test会先构建 stage 1 编译器,然后运行整个测试套件——它不仅包含tests/目录下的 compiletest 测试,还包含library/、compiler/、src/tools/中的包级测试(#[test]单元测试)等更多内容。
经验法则:通常你只想运行预期能覆盖你改动的那些测试子集,甚至比子集更小的一组测试。PR CI 只跑部分测试集合,而 merge queue CI(bors)才会跑全部测试集合。
测试结果缓存与强制重跑
测试结果会被缓存,此前已通过的测试在后续运行中会被ignored(跳过)。每个测试的 stdout/stderr 内容以及时间戳文件,位于build/<target-tuple>/test/目录下(<target-tuple>为目标三元组,如x86_64-unknown-linux-gnu)。
如果测试运行器未能察觉源文件的改动,可以用--force-rerun强制重跑:
./x test tests/ui --force-rerun该选项也可以作为--test-args的子参数传递(见下文“单个测试”章节)。在 bootstrap 层面,x test直接接受--force-rerun等公共参数。
外部依赖注意事项
部分测试套件依赖外部程序,尤其是debuginfo 测试:某些 debuginfo 测试需要支持 Python 的 gdb。你可以在 gdb 内执行python命令验证:输入python回车后,键入print("hi"),再按CTRL+D执行。如果是从源码构建 gdb,需要以--with-python=<path-to-python-binary>配置。
从源码层面看,compiletest 是 rustc 测试套件的主测试框架(harness),它负责组织成千上万的测试并支持并行执行。所有测试套件的模式(如Ui、DebugInfo、Incremental、RunMake等)都定义在 src/tools/compiletest/src/common.rs 的TestMode枚举中——Ui => "ui"、DebugInfo => "debuginfo"等字符串与命令行中传入的路径一一对应,这正是./x test tests/ui、./x test tests/debuginfo能被识别的底层机制。
二、运行测试子集(本地开发核心操作)
2.1 UI 冒烟测试
修改 rustc 后,最常用的“冒烟测试”是运行ui测试套件(对应 tests/ui 目录):
./x test tests/ui2.2 按任务选择测试套件
测试套件的选择是灵活的,应以你的任务为准。例如:
./x test tests/debuginfo2.3 按子目录过滤
任何测试套件下,都可以把子目录作为过滤器传给./x test:
./x test tests/ui/const-genericsMSYS2 用户注意:在 MSYS2 环境下路径处理比较特殊,
./x test既无法识别tests/ui/const-generics也无法识别tests\ui\const-generics。解决办法是用--test-args传参:./x test ui --test-args="tests/ui/const-generics"
2.4 运行单个测试文件
直接传入文件路径即可:
./x test tests/ui/const-generics/const-test.rs注意:x目前还不支持通过传入文件路径来运行单个工具(tool)测试,此时必须使用--test-args(见下文),例如:
./x test src/tools/miri --test-args tests/fail/uninit/padding-enum.rs三、tidy 与标准库测试的组合运行
3.1 只运行 tidy 脚本
./x test tidytidy 负责检查代码风格、目录文件数量限制、revsion 名称一致性等仓库级约束(相关实现见 src/tools/tidy)。
3.2 在标准库上运行测试(stage 0)
./x test --stage 0 library/std注意:这只测试std。如果要测试core或其他 crate,必须显式指定,例如./x test --stage 0 library/core。
3.3 同时运行 tidy 与标准库测试
./x test --stage 0 tidy library/std3.4 用 stage 1 编译器测试标准库
./x test --stage 1 library/std通过显式列出要运行的测试套件,可以避免为没有改动的组件白白跑测试。但要注意:bors 只运行完整的 stage 2 构建下的测试,因此 stage 1 下测试通常可行,但存在一些限制。
3.5 用 stage 2 编译器运行全部测试
./x test --stage 2你几乎不需要在本地这样做——CI 会替你运行这些测试。
四、编译器/标准库的单元测试
你可能想对某个具体文件运行单元测试,例如:
./x test compiler/rustc_data_structures/src/thin_vec/tests.rs但很遗憾,这样是行不通的。正确做法是传入包目录,再用--test-args过滤测试名:
./x test compiler/rustc_data_structures/ --test-args thin_vec--test-args的过滤语义来自标准 Rust 测试运行器(即#[test]使用的同一个运行器):它会筛选出名称中包含指定字符串的测试。
五、运行单个测试(individual test)
开发中最常见的需求是运行你要修复的那一个测试。除了前文提到的传入完整文件路径外,也可以结合--test-args:
./x test tests/ui --test-args issue-1234在底层,测试运行器调用的是标准 Rust 测试运行器,因此上述命令等价于筛选名称中包含issue-1234的所有测试。这也意味着--test-args非常适合运行一组相关联的测试(例如同一 issue 派生出的多个测试)。
compiletest 本身也接受通过--test-args或--后置方式传递参数,例如:
./x test --test-args --force-rerun ./x test -- --force-rerunbootstrap 还会直接透传一些常用参数:x test --no-capture --force-rerun --run --pass。compiletest 会尽量避免在相关产物(主要是编译器本身)未变化时重复运行测试,--force-rerun正是用来打破这种缓存的。
六、给测试中的 rustc 传递额外参数
有时需要在开发不稳定特性时(例如使用-Z标志)给测试中的编译器传参,而又不想动用RUSTFLAGS。此时使用--compiletest-rustc-args:
./x test tests/ui --compiletest-rustc-args="-Zsome-unstable-flag"该选项会把附加参数在构建测试时传给编译器。
七、--bless:更新参考文件
如果你有意改变了编译器的输出,或正在编写新测试,可以给测试子命令传入--bless:
./x test tests/ui --bless它会自动调整tests/ui下所有测试的.stderr、.stdout或.fixed文件。同样可以结合--test-args your_test_name只针对特定测试执行,与不带--bless时筛选方式一致:
./x test tests/ui --test-args issue-1234 --bless注意:
--bless只应在确认新输出正确后使用——它会把当前输出直接写成“预期输出”。对 UI 测试而言,参考文件(snapshot)的命名规则为*test-name*.*revision*.*compare_mode*.*extension*,详见 UI 测试文档 中的“Output comparison”一节。
八、配置测试运行行为
8.1rust.verbose-tests
bootstrap.example.toml 提供了rust.verbose-tests选项(示例配置位于该文件约 790 行):
rust.verbose-tests = false # 默认:每个测试打印一个点- 若为
false(默认值),每个测试只打印一个点(.); - 若为
true,打印每个测试的名称; - 它等价于 Rust 测试框架中的
--quiet选项。
8.2RUST_TEST_THREADS
环境变量RUST_TEST_THREADS控制测试使用的并发线程数:
RUST_TEST_THREADS=8 ./x test tests/ui九、--pass $mode:强制通过模式
通过型 UI 测试现在有三种模式:check-pass、build-pass和run-pass。传入--pass $mode时,这些测试会被强制以给定$mode运行——除非测试文件中存在//@ no-pass-override指令。
例如,把tests/ui中所有测试以check-pass方式运行:
./x test tests/ui --pass check通过--pass $mode可以显著缩短测试时间(例如check-pass跳过 codegen 阶段)。--pass只影响 UI 测试。各模式的语义(check-pass 跳过 codegen、build-pass 编译链接但不运行、run-pass 运行且需退出码为 0)详见 UI 测试文档中的 “Controlling pass/fail expectations” 一节。
十、Compare modes:在不同编译模式下对比输出
UI 测试可能因编译器所处的“模式”不同而输出不同。例如使用 Polonius 模式时,测试foo.rs会优先查找foo.polonius.stderr作为预期输出,找不到时回退到常规的foo.stderr。运行方式:
./x test tests/ui --compare-mode=polonius目前可用的 compare modes 包括(定义于 compiletest 的CompareMode枚举,见 src/tools/compiletest/src/common.rs):
polonius— 以-Zpolonius=next运行;next-solver— 以-Znext-solver使用下一代 trait solver 运行;next-solver-coherence— 以-Znext-solver=coherence运行 coherence 检查;split-dwarf/split-dwarf-single— 分别以-Csplit-debuginfo=unpacked/-Csplit-debuginfo=packed运行。
compare modes 与 revisions 是两回事:运行./x test tests/ui会测试所有 revision,而 compare modes 必须通过--compare-mode手动逐个运行。CI 中目前只有一个 Linux builder 使用 compare mode(tests/debuginfo套件使用split-dwarf模式)。更多细节见 compiletest 文档中的 Compare modes 一节。
十一、手动运行测试(跳过框架)
有时手工运行更快:大部分测试就是.rs文件,在创建 rustup toolchain 之后可以直接:
rustc +stage1 tests/ui/issue-1234.rs这快得多,但并非总能行得通——例如某些测试包含指定编译器标志的指令(directives),或依赖其他 crate,脱离这些选项后行为可能不一致。
十二、在远程机器上运行测试
测试可以在远程机器上执行(例如为不同架构构建测试)。机制是:构建机上的remote-test-client把测试程序发送给运行在远程机器上的remote-test-server,后者执行测试程序并把结果回传。remote-test-server提供的是未经认证的远程代码执行能力,使用时必须谨慎。
12.1 构建并部署 server
先为远程机器构建remote-test-server(以 RISC-V 为例):
./x build src/tools/remote-test-server --target riscv64gc-unknown-linux-gnu二进制产物位于./build/host/stage2-tools/$TARGET_ARCH/release/remote-test-server,把它复制到远程机器上。在远程机器上运行:
$ ./remote-test-server --verbose --bind 0.0.0.0:12345 starting test server listening on 0.0.0.0:12345!这两个命令行参数(--bind <IP>:<PORT>与-v/--verbose)在 src/tools/remote-test-server/src/main.rs 中解析:默认绑定地址为127.0.0.1:12345(Android/Windows 平台默认0.0.0.0:12345),默认端口 12345。
安全警告:把 server 绑定到0.0.0.0意味着所有能访问该机器的主机都可以在其上执行任意代码。强烈建议设置防火墙阻止外部访问 12345 端口,或绑定更受限的 IP 地址。
12.2 验证连通性
用nc连接并发送ping\n,应收到pong回复:
$ nc $REMOTE_IP 12345 ping pong这一协议同样在 src/tools/remote-test-server/src/main.rs 中实现(收到ping时向 socket 写回pong)。
12.3 通过 x 运行远程测试
设置TEST_DEVICE_ADDR环境变量后照常使用x。例如,为 IP 为1.2.3.4的 RISC-V 机器运行ui测试:
export TEST_DEVICE_ADDR="1.2.3.4:12345" ./x test tests/ui --target riscv64gc-unknown-linux-gnu如果 server 以--verbose启动,测试机器上的输出大致如下:
[...] run "/tmp/work/test1007/a" run "/tmp/work/test1008/a" run "/tmp/work/test1009/a" [...]bootstrap 侧对remote-test-client的调用位于 src/bootstrap/src/core/build_steps/test.rs(--remote-test-client参数传递工具路径)。
12.4 关键注意事项
- 测试是在运行
x的机器(构建机)上构建的,而非远程机器; - 无法成功构建的测试(或
ui测试产生错误构建输出)可能在尚未运行于远程机器时就直接失败; x无法连上remote-test-server时默认超时 30 分钟,可通过环境变量TEST_DEVICE_CONNECT_TIMEOUT_SECONDS修改。
十三、在模拟器/仿真环境中测试
对于不易获取实机的架构,部分平台通过模拟器测试。
- 对标准库支持良好、且宿主机支持 TCP/IP 网络的架构,可完全复用上文“远程机器测试”的流程——此时“远程机器”就是模拟器;
- 还有一组工具用于编排模拟器内的测试运行。
arm-android、arm-unknown-linux-gnueabihf等平台已在 GitHub Actions 上配置为自动在仿真环境下跑测试。
以 armhf-gnu 为例:其 Docker 镜像(见 src/ci/docker/host-x86_64/armhf-gnu/Dockerfile)内置 QEMU 来模拟 ARM CPU。rust 源码树中自带的 remote-test-client 与 remote-test-server 负责把测试程序与库发送到模拟器、在模拟器内执行并读回结果。Docker 镜像启动remote-test-server,构建工具用remote-test-client与之通信协调测试(协调逻辑见 src/bootstrap/src/core/build_steps/test.rs)。
iOS/tvOS/watchOS/visionOS 模拟器
可以把模拟器当作“远程”机器。一个有趣的细节是:模拟器实例与宿主 macOS 共享网络,因此可以使用回环地址127.0.0.1。完整流程如下:
# 为 iOS 模拟器构建测试服务端: ./x build src/tools/remote-test-server --target aarch64-apple-ios-sim # 若已有打开的模拟器实例,从以下命令输出中复制设备 UUID: xcrun simctl list devices booted UDID=01234567-89AB-CDEF-0123-456789ABCDEF # 或者创建并启动新的模拟器实例: xcrun simctl list runtimes xcrun simctl list devicetypes UDID=$(xcrun simctl create $CHOSEN_DEVICE_TYPE $CHOSEN_RUNTIME) xcrun simctl boot $UDID # 在 12345 端口启动运行器: xcrun simctl spawn $UDID ./build/host/stage2-tools/aarch64-apple-ios-sim/release/remote-test-server -v --bind 127.0.0.1:12345 # 在新终端中通过运行器执行测试: export TEST_DEVICE_ADDR="127.0.0.1:12345" ./x test --host='' --target aarch64-apple-ios-sim --skip tests/debuginfo其中--skip tests/debuginfo是因为 debuginfo 测试可能需要在目标机上复制.dSYM目录才能工作,目前尚未支持。
十四、WASI(wasm32-wasip1)上的测试
部分测试针对 wasm 目标。运行它们需要向x test传入目标:
./x test tests/ui --target wasm32-wasip1另外还需要wasi sdk:按其官方说明安装以在本地获得 sysroot(需确保 sdk 版本不低于 wasm32-wasip1 目标支持页 中指定的最低版本;构建过程涉及一些耗时且产生大量 C++ 警告的 cmake 命令)。然后在bootstrap.toml中指向 sysroot:
[target.wasm32-wasip1] wasi-root = "<wasi-sdk location>/build/sysroot/install/share/wasi-sysroot"例如把 wasi-sdk git clone 到 rust 目录旁边时,路径形如../wasi-sdk/build/....。配置完成后测试即可直接运行,无需其他额外设置。
十五、延伸阅读
- UI 测试详解(测试结构、错误注解、rustfix、compare modes)
- compiletest 测试框架总览(测试套件、revisions、compare modes、并行前端)
- 测试代码的顶层定义:TestMode / CompareMode 枚举
- 远程测试服务端实现(--bind、--verbose、ping/pong 协议)
- 远程测试客户端工具
- bootstrap 测试构建步骤(remote-test-client 调用与测试套件编排)
- 测试运行相关配置项(rust.verbose-tests)
【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考