MongoDB 仓库中的 Resmoke Bazel 规则:在 Bazel 中运行 jstest 套件、多版本测试与模糊测试

发布时间:2026/9/10 15:06:09
MongoDB 仓库中的 Resmoke Bazel 规则:在 Bazel 中运行 jstest 套件、多版本测试与模糊测试 MongoDB 仓库中的 Resmoke Bazel 规则在 Bazel 中运行 jstest 套件、多版本测试与模糊测试【免费下载链接】mongoThe MongoDB Database项目地址: https://gitcode.com/GitHub_Trending/mo/mongo本指南系统讲解 MongoDB 开源仓库中bazel/resmoke提供的 Bazel 构建规则涵盖三大核心能力用resmoke_suite_test在 Bazel 中驱动 resmoke 执行 jstest 套件、用multiversion_setup下载旧版本 MongoDB 二进制进行多版本multiversion测试以及用jstestfuzz_generate生成随机化 JS 模糊测试。读完本文你将掌握resmoke_suite_test的完整属性语义、srcs 自动推导原理、测试分片与日志产物定位以及如何通过 pin 标志复现一次精确的测试失败。一、整体架构三条规则的分工bazel/resmoke是一组将 resmoke 测试执行流程搬到 Bazel 的规则集合入口文档为 bazel/resmoke/README.md。它包含三个核心规则/宏规则作用实现文件resmoke_suite_test把一个 resmoke 套件YAML 配置包装成 Bazel test targetresmoke.bzlmultiversion_setup调用db-contrib-tool setup-repro-env下载旧版 MongoDB 二进制multiversion/multiversion.bzljstestfuzz_generate调用 jstestfuzz 生成随机化.js测试文件jstestfuzz.bzl配套实现还包括 resmoke_shim.pyresmoke 的 Bazel 适配层、derive_suite_selectors.py预构建期从套件 YAML 推导测试文件标签、resmoke_config_generator.py生成套件配置以及 jstestfuzz_run.sh模糊测试生成包装脚本。二、resmoke_suite_test把 resmoke 套件变成 Bazel 测试目标resmoke_suite_test是核心入口它把一个 resmoke 套件包装成可被bazel test执行的测试目标。规则的实际形态是一个宏macro内部最终创建py_binaryresmoke shim和一个私有规则_resmoke_test——后者在 resmoke.bzl 中实现为一个重新实现的 py_test生成一个 launcher 脚本负责展开$(location)/make-variable、构造DEPS_PATH环境变量并 exec resmoke shim。2.1 最小示例load(//bazel/resmoke:resmoke.bzl, resmoke_suite_test) resmoke_suite_test( name core, config //buildscripts/resmokeconfig:suites/core.yml, data [ //jstests/libs:all_subpackage_javascript_files, ], deps [ //src/mongo/db:mongod, //src/mongo/shell:mongo, ], )其中config指向的 buildscripts/resmokeconfig/suites/core.yml 是仓库中真实存在的套件配置其selector.roots以 glob 形式声明测试文件例如jstests/core/**/*.js。2.2 属性详解属性类型必填默认值说明nameName是—测试目标唯一名称configLabel是—套件的 resmoke YAML 配置必须含selector.roots字段运行时直接传给 resmokesrcsLabel 列表否[]测试源文件覆盖项为空时由预构建生成器从套件 YAML 的selector.roots自动推导dataLabel 列表否[]测试运行时所需的额外文件典型如jstests/libs下的 JS 库depsLabel 列表否[]测试依赖的 MongoDB 二进制mongod、mongos 等运行时被放置到 PATH 上resmoke_args字符串列表否[]追加传给 resmoke 运行器的命令行参数shard_count整数否None将套件拆分为多少个并行分片执行详见测试分片multiversion_depsLabel 列表否[]multiversion_setup目标其下载目录通过--multiversionDir传给 resmoke从源码看resmoke.bzl宏还接受config_roots显式 selector 根路径/glob 字符串用于 corpus 是规则产出目录的套件、test_root_granularityfile 或 directory、timeout默认eternal等高级参数。运行时框架还会自动注入一批默认参数见 resmoke.bzl--archiveModedirectory、--archiveLimitMb500、--testTimeout、--releasesFile、--historicTestRuntimes、--tssTestList、--mongoVersionFile等在 Evergreen 环境--//bazel/resmoke:in_evergreenTrue下还会追加--logevg、--cedarReportFilecedar_report.json、--continueOnFailure等参数。2.3 srcs 自动推导的底层机制当srcs为空时宏不会直接去读 YAML而是查找预构建生成器derive_suite_selectors.py产出的 .resmoke_suites_derived.bzl 中的SUITE_SELECTORS字典键为//package:suites/xxx.yml形式的 config label值为对应 srcs 标签列表。生成器在 wrapper_hook 预构建阶段运行扫描 buildscripts/resmokeconfig/suites 与matrix_suites/generated_suites等固定套件目录并通过 resmoke_modules.yml 发现模块套件目录只解析每个 YAML 的selector:块derive_suite_selectors.py将roots中的 glob 映射为 Bazel 标签dir/*.js映射为//dir:all_javascript_files、dir/**/*.js映射为//dir:all_subpackage_javascript_files复杂模式则通过文件系统 glob 展开并去重跳过使用from_target的套件对没有 roots 的 selector 记为空列表。当srcs被显式提供时宏会创建一个*_config目标由resmoke_config_generator.py生成一份以解析后 roots 替换原 roots 的新配置当srcs与config_roots均为空时passthrough 模式原 YAML 配置原样传给 resmoke。若 config 在SUITE_SELECTORS中找不到且未显式给srcs宏会直接fail提示提供显式srcs或确保套件 YAML 含selector.roots。2.4 resmoke shimBazel 与 resmoke 之间的适配层每个测试目标最终执行的是 resmoke_shim.py 包装后的 resmoke.py。shim 承担了大量适配工作值得了解路径与依赖注入读取DEPS_PATHdeps中二进制的路径冒号分隔并将其各目录追加到PATH保证 mongod/mongos/mongo 可被 resmoke 找到通过PYTHON_IMPORTS_FILE把 data 依赖声明的 Python import 路径加入PYTHONPATH使 JS 测试里runNonMongoProgram拉起的 Python 子进程能导入同样包短路径符号链接把TEST_TMPDIR、TEST_UNDECLARED_OUTPUTS_DIR在/tmp下创建随机短名符号链接避免超长路径问题并把 runfiles 根下的仓库内容符号链接进工作目录参数规范化默认追加--storageEngineCacheSizeGB4inMemory 引擎或--storageEngineCacheSizeGB1注入 Evergreen build 信息--buildId、--taskId、--gitRevision等来源于 volatile-status追加--basePort通过acquire_local_resource(port_block)获取端口块、--reportFile、--dbpathPrefix、--archiveDirectory等分片透传当 Bazel 设置TEST_SHARD_INDEX/TEST_TOTAL_SHARDS时将其翻译为 resmoke 的--shardIndex/--shardCountmultiversion 标签注入读取MULTIVERSION_CONFIG_FILE与MULTIVERSION_VERSIONS为last-lts/last-continuous/last-patch追加对应的 FCV tag 到--excludeWithAnyTagsresmoke_shim.py。三、测试分片Test Sharding当指定shard_count时Bazel 会把同一测试目标并行运行多次最多达到指定的分片数每个分片拿到唯一的索引0 到 N-1resmoke 据此决定每个分片运行的测试子集每个分片产出独立的输出与日志。注意分片是 resmoke--jobs标志的替代方案resmoke_suite_test中不应再使用--jobs。源码层面有一个值得注意的细节Bazel 原生shard_count属性上限为 50resmoke.bzl 中注释明确 Having more than 50 shards is indicative of poor test organization。需要超过 50 分片的套件宏会把原生属性钉在上限并通过_forced_shards_transition配置转换把真正的分片数写入--test_sharding_strategyforcedNresmoke.bzl。该行为受--//bazel/resmoke:support_extended_shard_count标志控制默认 True见 BUILD.bazel。四、测试日志与输出目录Bazel 为每次测试运行在 workspace 根下的bazel-testlogs符号链接中创建独立输出目录。以目标//jstests/suites/query-execution:core为例bazel-testlogs/jstests/suites/query-execution/core/ ├── test.log # 主测试输出日志包含 resmoke.py 的输出 ├── test.outputs/ │ ├── report.json # JSON 格式测试结果 │ ├── resource_usage.txt # 周期性记录的资源使用指标 │ └── data/ # resmoke fixture 的数据目录 │ └── job0/ │ ├── mongorunner/ │ └── resmoke/ │ ├── WiredTiger* │ ├── journal/ │ └── diagnostic.data/其中resource_usage.txt由 resource_monitor.py 通过ResourceMonitor周期性写入分片模式下报告文件名为report_shard_index_of_count.json。本地运行非--logevg时数据目录保留在test.outputs/data便于调试后直接检查 WiredTiger 数据与诊断信息。五、实用命令5.1 从套件中运行单个测试bazel test //jstests/suites/query-execution:core --test_sharding_strategydisabled --test_argjstests/core/js/jssymbol.js--test_sharding_strategydisabled避免分片干扰--test_arg把单个测试路径透传给 resmoke。5.2 携带额外 resmoke 标志运行任何--test_arg都会被传播为 resmoke.py 的标志。下面的命令以timeseries命名匹配jstests/core下所有.timeseries.js测试重复执行 2 次并开启全部 feature flag# Runs all tests from the core suite with timeseries in their name, twice, with all feature flags enabled. bazel test //jstests/suites/query-execution:core \ --test_sharding_strategydisabled \ --test_arg--repeatTests2 \ --test_arg--runAllFeatureFlagTests \ --test_arg--skipExcludedTests \ fdfind -t f --full-path .timeseries\.js$ jstests/core | awk {print --test_arg $0}示例中fdfind可用find jstests/core -name *.timeseries.js之类的等价命令替代。此外Evergreen 环境相关的标志位于 BUILD.bazel--//bazel/resmoke:in_evergreen、--//bazel/resmoke:installed_dist_test、--//bazel/resmoke:skip_deps_for_cquery均为 bool_flag。六、multiversion_setup多版本测试的二进制下载与标签排除multiversion_setup通过db-contrib-tool setup-repro-env下载旧版 MongoDB 二进制并为last-lts、last-continuous版本生成配套的排除标签。底层实现multiversion/multiversion.bzl实际执行db-contrib-tool setup-repro-env version --edition edition --installDir output_dir/.install --linkDir output_dir下载动作被标记为no-cache、no-remote-cache、no-sandbox、no-remote因为二进制来自 Evergreen 且会随新构建变化且 db-contrib-tool 会读取声明输入之外的凭据文件。6.1 预定义目标与版本一组预定义目标位于//bazel/resmoke/multiversion定义见 multiversion/BUILD.bazel目标版本//bazel/resmoke/multiversion:last-ltslast-lts//bazel/resmoke/multiversion:last-continuouslast-continuous//bazel/resmoke/multiversion:7.07.0//bazel/resmoke/multiversion:8.0.168.0.16要针对特定版本测试需新增一个multiversion_setup目标。6.2 属性属性类型必填说明nameName是目标唯一名称同时作为name_exclude_tags目标的名称前缀versionString是传给db-contrib-tool setup-repro-env的 MongoDB 版本串如7.0、8.0.16、last-lts、last-continuouseditionenterprise/community不由用户设置宏会根据构建配置自动选择当//bazel/config:build_enterprise_enabled为真时用enterprise否则用targetedmultiversion.bzl。由于该目标会在构建期从 Evergreen 下载外部二进制其可见性被限制在//buildscripts与//jstests子包内以维护构建可复现性见 multiversion/BUILD.bazel。6.3 在 resmoke_suite_test 中使用多版本通过multiversion_deps传入一个或多个multiversion_setup目标load(//bazel/resmoke:resmoke.bzl, resmoke_suite_test) resmoke_suite_test( name multiversion_sanity_check_last_continuous_new_new_old, config :multiversion_sanity_check_last_continuous_new_new_old.yml, multiversion_deps [ //bazel/resmoke/multiversion:last-continuous, ], deps [ //src/mongo/db:mongod, //src/mongo/shell:mongo, ], )宏会把每个 multiversion dep 展开为 resmoke 的--multiversionDir参数并注入MULTIVERSION_CONFIG_FILE、MULTIVERSION_VERSIONS环境变量resmoke.bzl。6.4 Exclude tags自动跳过不兼容测试对last-lts与last-continuous版本multiversion_setup会创建配套的name_exclude_tags目标运行resmoke.py generate-multiversion-exclude-tags实现见 multiversion.bzl产出的 YAML 会在该multiversion_setup出现在multiversion_deps中时自动通过--tagFile传给 resmoke从而无需额外配置即可跳过与旧版二进制不兼容的测试。对于last-patch版本同样会生成排除标签其他普通版本则生成一个空的无操作标签文件。shim 还会基于 multiversion 配置追加--excludeWithAnyTags固定排除multiversion_incompatible,backport_required_multiversion并根据版本追加 FCV 相关 tagresmoke_shim.py。这里依赖的 etc/backports_required_for_multiversion_tests.yml 与 src/mongo/util/version/releases.yml 都是仓库内真实存在的配套数据。6.5 用固定二进制复现失败每个multiversion_setup目标都有一个名为name-pin的string_flag。在命令行传入它会让db-contrib-tool下载指定构建而非当前最新版bazel test //buildscripts/resmokeconfig:multiversion_sanity_check_last_continuous_new_new_old \ --//bazel/resmoke/multiversion:last-continuous-pinevg-version-id标志接受db-contrib-tool setup-repro-env能理解的任何标识符值示例Evergreen version ID6172c9b65623435a4c0bdb1a完整 git commit hashd9c83ee0c93970029e41234c77dc20b2c5ca6291Evergreen task IDmongodb_mongo_master_enterprise_rhel_80_..._22_02_16_03_30_27含多个 multiversion dep 的测试需为每个版本分别传一个 pin 标志。pin 标志由宏用string_flag(name -pin)创建multiversion.bzl。从历史运行中找到 EVG version ID每次测试运行都会写入multiversion-downloads.json记录实际下载的 Evergreen version并按运行保留在测试输出中bazel-testlogs/.../test.outputs/multiversion-downloads-last-continuous.jsonshim 在 resmoke_shim.py 中把每个--multiversionDir下的该文件复制到未声明输出目录。用该文件中的 ID 配合name-pin即可用完全相同的二进制复现失败。6.6 多版本对等版本的可用性计算仓库还通过 multiversion/version_compat.bzl 中的 module extension 计算两个布尔常量LAST_CONTINUOUS_IS_LAST_LTS当 last-continuous 解析到与 last-lts 相同的 FCV 或 EOL 版本时为 True此时专属 last-continuous 套件会与 last-lts 套件完全重复应被跳过LAST_PATCH_HAS_GA_RELEASE当前系列是否已有早于 HEAD 的 GA 发布标签False 时如 master 上只有 alpha 标签db-contrib-tool无二进制可下载专属 last-patch 套件会被标记为 incompatible 而非构建失败。两者分别成为 multiversion/BUILD.bazel 中bool_flag的默认值可在命令行覆盖如--//bazel/resmoke/multiversion:last_patch_has_ga_releaseTrue这也是某些刻意针对预发布对等版本如 disagg 套件针对 Atlas RC测试的逃生通道。七、jstestfuzz_generate生成随机化模糊测试jstestfuzz_generate调用 10gen/jstestfuzz 生成随机化的.js测试文件并通过srcs喂给resmoke_suite_test。7.1 示例load(//bazel/resmoke:jstestfuzz.bzl, jstestfuzz_generate) load(//bazel/resmoke:resmoke.bzl, resmoke_suite_test) jstestfuzz_generate( name jstestfuzz_generated, npm_command jstestfuzz, num_generated_files 10, ) resmoke_suite_test( name jstestfuzz, srcs [:jstestfuzz_generated], config :suites/jstestfuzz.yml, deps [ //src/mongo/db:mongod, //src/mongo/shell:mongo, ], )7.2 属性详解属性类型必填默认值说明nameName是—目标唯一名称num_generated_files整数是—要产出的.js测试文件总数跨全部分片转发给 jstestfuzz 的--numGeneratedFilesshard_count整数否0并行生成 action 数每个分片用base_seed shard_index的种子向自己输出目录生成约num_generated_files/shard_count个文件0表示自动分片约每分片 15 个文件seedString否本目标固定种子设置时覆盖--//bazel/resmoke:jstestfuzz_seed留空则用该标志默认每次构建随机npm_commandString否jstestfuzzjstestfuzz 的package.json中要运行的 npm 脚本如jstestfuzz、agg-fuzzer、query-fuzzer、update-fuzzer、rollback-fuzzerjs_testsLabel否None作为--jsTestsDir传给 jstestfuzz 的模板语料库默认 None不传对会修改既有语料库的命令如jstestfuzz、agg-fuzzer可设为//jstests:all_subpackage_javascript_files或其子树extra_args字符串列表否[]原样转发给 jstestfuzz 的额外 CLI 标志如[--opType, moveCollection]生成动作通过 jstestfuzz_run.sh 执行用规则提供的 Hermetic Node.js 16.18.1BUILD.bazel仅支持 Linux aarch64/x86_64 执行平台解包预制的 jstestfuzz 源码包运行 npm 脚本并把产出的.js收集进声明输出目录。分片并行生成时每个分片的种子为base_seed seed_offset且 jstestfuzz 会把种子嵌入文件名保证分片输出互不冲突。由于输出依赖种子默认来自 volatile-status 的构建时间戳每次构建不同未固定种子时动作标记为no-cache见 jstestfuzz.bzl。7.3 复现失败种子 上游提交要逐字节复现一次失败两个标志必须一致jstestfuzz 使用的种子以及其生成所基于的上游提交。标志固定的内容--//bazel/resmoke:jstestfuzz_seedn传给 jstestfuzz 的种子默认每次构建随机--repo_envJSTESTFUZZ_COMMITsha要克隆的上游 jstestfuzz 提交默认master最新提交示例bazel test //buildscripts/resmokeconfig:jstestfuzz \ --//bazel/resmoke:jstestfuzz_seed42 \ --repo_envJSTESTFUZZ_COMMITf21b49c53824f677af60f766eda029366e82513e从历史运行中找种子与提交每次测试运行都把两个值记录到test.outputs/bazel-testlogs/.../test.outputs/jstestfuzz_seed.txt bazel-testlogs/.../test.outputs/jstestfuzz_commit_sha.txt这两个文件由 shim 在 resmoke_shim.py 中把生成目录里的.jstestfuzz_seed与.jstestfuzz_commit_sha元数据复制到未声明输出目录得到。此外BUILD.bazel 中还有--//bazel/resmoke:jstestfuzz_branch转发给 jstestfuzz 的--branch选择生成的测试可使用的服务端特性默认master与--//bazel/resmoke:config_fuzz_seed配置模糊测试种子留空时从 volatile-status 推导使所有分片共享同一种子两个相关标志。八、配套基础设施与测试选择服务除上述三条规则外bazel/resmoke还包含与 Evergreen 测试选择服务Test Selection Services, TSS相关的组件generate_tss_test_list.py为每个套件生成测试列表文件download_historic_runtimes.py从 S3 拉取历史测试运行时长用于测试超时预测对应--historicTestRuntimes参数tss_settings则把--//bazel/resmoke:enable_test_selection与--//bazel/resmoke:test_selection_strategies两个构建设置物化成文件供 genrule 读取。enable_test_selection默认关闭由 evergreen/resmoke_tests_execute_bazel.sh 根据是否为 patch build 决定是否开启。九、总结bazel/resmoke把 MongoDB 的 jstest 测试体系完整地融入了 Bazel 的构建与测试模型resmoke_suite_test通过预构建生成器自动推导套件测试文件、以 shim 适配 resmoke 的路径与参数习惯并提供原生分片支持multiversion_setup解决了多版本测试中旧二进制下载、不兼容测试自动排除与失败复现三大痛点jstestfuzz_generate则把随机化模糊测试变成可复现、可分片的 Bazel 目标。三者的组合让开发者可以用bazel test一条命令复现从单元级 jstest 到跨版本、跨引擎模糊测试的完整测试矩阵并且每一个失败都能通过 pin 标志、种子与提交 SHA 精确还原。【免费下载链接】mongoThe MongoDB Database项目地址: https://gitcode.com/GitHub_Trending/mo/mongo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考