news 2026/9/13 3:29:26

Bazel git_repository 集成测试测试数据设计:从归档仓库到规则行为验证

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Bazel git_repository 集成测试测试数据设计:从归档仓库到规则行为验证

Bazel git_repository 集成测试测试数据设计:从归档仓库到规则行为验证

【免费下载链接】bazela fast, scalable, multi-language and extensible build system项目地址: https://gitcode.com/GitHub_Trending/ba/bazel

本文以 Bazel 仓库中 src/test/shell/bazel/testdata/README.md 为骨架,结合其对应的测试脚本 starlark_git_repository_test.sh 与规则实现 git.bzl,系统讲解 Bazel 如何通过一组精心构造的"行星主题" Git 测试仓库,端到端验证git_repository外部依赖规则的核心行为(克隆、submodule、strip_prefix、add_prefix、浅克隆、缓存与重取、错误路径)。读完本文,你将理解这些.tar.gz.git_log测试数据的用途、生成与维护方式,以及它们如何与测试用例一一对应。

一、测试数据是什么:四组归档与四份提交日志

src/test/shell/bazel/testdata/目录是 Bazel 集成测试的固定测试数据源。README 明确指出,以下四个归档文件包含测试用 Git 仓库,供git_repositoryworkspace 规则的测试脚本使用:

归档文件用途(对应测试仓库)对应的 git log 文件
outer-planets-repo.tar.gz含 submodule 的"外行星"仓库outer-planets.git_log
pluto-repo.tar.gz主测试仓库"冥王星"(多 tag)pluto.git_log
refetch-repo.tar.gz验证重取(refetch)行为的仓库refetch.git_log
strip-prefix-repo.tar.gz验证 strip_prefix 目录裁剪的仓库strip-prefix.git_log

说明:README 中写作strip-prefix.tar.gz,而实际仓库中的文件名是strip-prefix-repo.tar.gz,请以 testdata/BUILD 与目录实际文件为准。

每个归档内部都是一个完整的.git仓库(含历史提交、tag、分支),而配套的*.git_log文件则是运行git log -p --decorate得到的输出快照,用于人工审阅各仓库的历史演进,例如核对某个 tag 对应的 commit hash 与文件变更。README 说明这些文件由"手动创建 git 仓库后用tar -zcvf打包"而来——这正是测试数据可复现、可审计的关键。

二、测试数据如何被测试脚本消费:BUILD 声明与 set_up 解包

2.1 BUILD 中的 filegroup 声明

测试数据不是靠路径硬编码被发现的,而是通过 src/test/shell/bazel/testdata/BUILD 中的filegroup目标显式暴露给上层测试包:

filegroup( name = "git-repos", srcs = [ "outer-planets-repo.tar.gz", "pluto-repo.tar.gz", "refetch-repo.tar.gz", "strip-prefix-repo.tar.gz", ], visibility = ["//src/test/shell/bazel:__pkg__"], )

同时package(default_testonly = True)保证这些数据只服务于测试;srcs文件组则通过glob(["**"])全部纳入 Bazel 的 runfiles,供测试脚本通过rlocation定位。

2.2 set_up:在隔离环境中解包

测试脚本 starlark_git_repository_test.sh(README 中写作git_repository_test.sh,此为规则 Starlark 化后的现名)在set_up阶段把四个归档复制到临时目录并解包:

function set_up() { local repos_dir=$TEST_TMPDIR/repos mkdir -p $repos_dir cp "$(rlocation io_bazel/src/test/shell/bazel/testdata/pluto-repo.tar.gz)" $repos_dir cp "$(rlocation io_bazel/src/test/shell/bazel/testdata/outer-planets-repo.tar.gz)" $repos_dir cp "$(rlocation io_bazel/src/test/shell/bazel/testdata/refetch-repo.tar.gz)" $repos_dir cp "$(rlocation io_bazel/src/test/shell/bazel/testdata/strip-prefix-repo.tar.gz)" $repos_dir cd $repos_dir tar zxf pluto-repo.tar.gz tar zxf outer-planets-repo.tar.gz tar zxf refetch-repo.tar.gz tar zxf strip-prefix-repo.tar.gz ... # Fix environment variables for a hermetic use of git. export GIT_CONFIG_NOSYSTEM=1 export GIT_CONFIG_NOGLOBAL=1 export HOME= export XDG_CONFIG_HOME= }

解包后的目录直接作为git_repositoryremote(本地路径形式的远端)使用,例如$TEST_TMPDIR/repos/pluto。注意对环境变量的处理:GIT_CONFIG_NOSYSTEMGIT_CONFIG_NOGLOBAL与清空HOMEXDG_CONFIG_HOME,是为了杜绝开发机上的 git 全局配置污染测试结果,保证测试的封闭性(hermeticity)。每次测试结束时还会调用bazel shutdown,以便在 Windows 上安全清理文件。

三、pluto 仓库:用一条"冥王星降级史"覆盖多种规则路径

pluto-repo.tar.gz是使用频率最高的测试仓库,它的历史本身就是一段有趣的"行星变更史",完整记录在 pluto.git_log 中。三个关键 tag 与 commit 的对应关系(均来自 git log 与实际测试调用):

TagCommit仓库内容对应测试
0-initial(早期提交)仅含info文件,内容 "Pluto is a planet"build_file/build_file_content系列
1-build52f9a3f87a2dd17ae0e5847bbae9734f09354afd根目录含MODULE.bazelBUILDinfo(改为 "Pluto is a dwarf planet")test_git_repository、add_prefix、strip_prefix_root 等
2-subdirdbf9236251a9ea01b7a2eb563ca8e911060fc97c文件被移入pluto/子目录strip_prefix、add_and_strip_prefix
3-subdir-bare8753495c2536c9e24fa764f06bf3015758461dd4(master 指向)删除 BUILD/WORKSPACE 后的裸子目录结构配合build_file的 strip_prefix 测试

3.1 最基础:按 commit 克隆并构建

test_git_repository 用 commit 固定版本,通过use_repo_ruleMODULE.bazel中声明外部仓库:

git_repository = use_repo_rule('@bazel_tools//tools/build_defs/repo:git.bzl', 'git_repository') git_repository( name = "pluto", remote = "$pluto_repo_dir", commit = "52f9a3f87a2dd17ae0e5847bbae9734f09354afd", )

然后bazel build //planets:planet-info,断言产物内容包含 "Pluto is a dwarf planet"。值得注意的验证点(见 do_git_repository_test):

git_repos_count=$(find -L $(bazel info output_base)/external/+git_repository+pluto -type d -name .git | wc -l) assert_equals 0 $git_repos_count

检查克隆后的外部仓库目录中不存在任何.git元数据——Bazel 不会把整个 Git 历史搬进输出树,只会产出干净的源码快照。

3.2 strip_prefix:裁剪子目录

当仓库把源码放在子目录(如pluto/)时,通过strip_prefix把该子目录提升为仓库根。测试 test_git_repository_strip_prefix 固定到2-subdir提交并设置strip_prefix = "pluto"。规则实现 git.bzl 在_clone_or_update_repo中做了层层防御:

  • strip_prefix路径必须存在,否则报strip_prefix at ... does not exist in repo
  • 必须是目录,否则报is not a directory
  • 不能指向.git等 Git 元数据(refers to Git metadata);
  • 不能通过..或符号链接逃逸检出目录escaped the checkout directory);
  • 实现上用临时名(.bazel_git_strip_prefix及追加_避免冲突)先把目标子树改名,再删除其余内容、把子树内容提升回根目录。

3.3 add_prefix:把检出内容下沉到子目录

与 strip_prefix 相反,add_prefix把仓库内容放到指定前缀目录下(如add/a/prefix),测试 test_git_repository_add_prefix 验证了这一点。规则侧 git.bzl 的_checkout_pathadd_prefix有严格的越界检查:若前缀路径逃逸外部仓库根目录(例如../+git_repository+pluto-victim),立即fail("add_prefix ... escaped the base directory ...")。对应测试 test_git_repository_add_prefix_prevent_uproot 预置一个"受害目录"哨兵文件,断言构建失败且哨兵未被破坏。

3.4 build_file / build_file_content:接管远程 BUILD 定义

当远程仓库没有(或不想使用)自己的 BUILD 文件时,可以用本地build_file(指向 workspace 内导出的文件,需配合exports_files)或内联的build_file_content字符串来定义目标。do_git_repository_test_with_build对 pluto 仓库的三个状态(0-initial3-subdir-bare、默认分支)分别组合 strip_prefix / add_prefix 进行矩阵测试,断言输出分别是 "Pluto is a planet" 与 "Pluto is a dwarf planet"。

3.5 shallow_since:浅克隆边界

test_git_repository_shallow_since 传入shallow_since = "2015-07-15"(提交日期的前一天),验证按日期浅克隆。规则属性文档(git.bzl)特别提醒:由于 git--shallow-since实现存在 bug,该属性可能引发 fetch 失败,因此不推荐使用;且当指定tag时不允许同时使用shallow_since(tag 一律用--depth=1),违反时报shallow_since not allowed if a tag is specified,由 test_git_repository_shallow_since_with_tag_error 覆盖。

四、outer-planets:submodule 递归克隆的验证载体

outer-planets-repo.tar.gz专门用于验证 submodule 支持。仓库在1-submoduletag 下包含neptune/info与作为 submodule 指向../plutopluto/(见 starlark_git_repository_test.sh 注释)。

两个对称测试覆盖init_submodulesrecursive_init_submodules两种模式,构建一个同时依赖@outer_planets//:neptune@outer_planets//:plutogenrule,断言两个输出文本都存在("Neptune is a planet" 与 "Pluto is a planet")。outer-planets.git_log同样记录了该仓库的提交历史,便于核对 submodule 引用的具体提交。

五、refetch 仓库:缓存、重取与 --nofetch 的"行为探针"

refetch-repo.tar.gz里的内容在不同提交间只有一行文本差异(GIT 1GIT 2),是测试 Bazel 外部仓库缓存语义的"探针"。这些测试统一先执行add_to_bazelrc "common --repo_contents_cache="关闭仓库内容缓存,以观察真实的重新克隆行为:

  • 服务重启不重取(test_git_repository_not_refetched_on_server_restart):用--batch强制每次重启 Bazel server,断言第一次出现Cloning,重启后不再出现,且产物仍是GIT 1
  • commit 变更触发重取(test_git_repository_refetched_when_commit_changes):把 commit 从22095302...换到db134ae9...,断言重新Cloning且产物变为GIT 2
  • 注释行变化不触发重取:仅修改MODULE.bazel中注释的行号位置,commit 不变,断言不重新克隆——这验证了仓库指纹只依赖语义属性而非文本;
  • strip_prefix 变化触发重取(test_git_repository_not_refetched_on_server_restart_strip_prefix):同样的 commit 从无strip_prefix改为strip_prefix = "gdir",需要重新检出;
  • --nofetch 语义(test_git_repository_and_nofetch):首次未克隆时bazel build --nofetchfetching repositories is disabled;已克隆但属性变更(commit 或 strip_prefix)后--nofetchExternal repository '@@+git_repository+g' is not up-to-date,但仍复用旧内容,直到正常 build 才完成重取。

这些用例精确刻画了git_repository在 Bazel 磁盘缓存、Skyframe 重分析与仓库指纹三方协同下的重取语义。

六、错误路径测试:让规则"正确地失败"

除了功能路径,测试数据还支撑了一批错误路径用例(错误信息与 git.bzl 实现一一对应):

  • commit 与 tag 冲突(test_git_repository_both_commit_tag_error):tag = "1-build"commit同时给出,报At most one of commit, tag, or branch may be provided(见 git.bzl);
  • strip_prefix 不存在(test_invalid_strip_prefix_error):strip_prefix = "dir_does_not_exist"strip_prefix at dir_does_not_exist does not exist in repo
  • strip_prefix 系列非法值(test_strip_prefix_errors):针对文件而非目录、../..目录穿越、指向.git/objects元数据、以及符号链接逃逸/指向元数据五种情形逐一断言报错,并额外断言失败后 external 目录中不留残余(assert_not_exists .../config);
  • tag 与 shallow_since 冲突:见上文 3.5 节。

七、测试数据的生成与维护:可复现的"手工流水线"

README 明确了数据的生产流程:手动创建 git 仓库 → 提交若干带 tag 的历史 → 用tar -zcvf打包成归档。这意味着维护者只需保证:

  1. 每次修改仓库内容后,同步更新归档(保持解包即仓库的完整性,包括.git目录);
  2. git log -p --decorate重新生成对应的*.git_log快照,作为提交历史与 tag/commit 映射的权威参考;
  3. 测试脚本中引用的 commit hash 与 tag 必须能在.git_log中找到对应条目(测试注释里也反复出现 "See testdata/pluto.git_log" 字样,如 starlark_git_repository_test.sh)。

由于归档内是完整仓库,tar zxf后无需任何网络即可作为本地remote使用,这保证了集成测试在离线、无外网 CI 环境下依然可复现。

八、如何在本地复现这些测试

以下命令可在 Bazel 源码树中运行对应测试(需要本机具备 git 与可用的 Bazel 发行版):

# 运行全部 git_repository 集成测试 bazel test //src/test/shell/bazel:starlark_git_repository_test # 仅查看测试脚本中的具体用例(函数名即测试名) bazel test //src/test/shell/bazel:starlark_git_repository_test --test_filter=test_git_repository_submodules

若只想人工检查数据本身,可直接解包归档并查看历史:

mkdir -p /tmp/repo-check && cd /tmp/repo-check tar zxf <仓库根>/src/test/shell/bazel/testdata/pluto-repo.tar.gz cd pluto && git log --oneline --decorate

对照 pluto.git_log 可以看到1-build2-subdir3-subdir-bare三个 tag 依次完成"加入 BUILD → 移入子目录 → 删除 BUILD"的演进,这正是驱动 strip_prefix、build_file 等全部核心测试场景的仓库骨架。

小结

src/test/shell/bazel/testdata/下的归档与 git log 文件,是 Bazelgit_repository规则质量保障的"最小宇宙":四个小而完整的 Git 仓库(pluto、outer-planets、refetch、strip-prefix)以本地路径形式充当 remote,配合 starlark_git_repository_test.sh 中数十个用例,覆盖了克隆、submodule、目录裁剪/下沉、浅克隆、缓存重取与全部关键错误路径。理解这套测试数据的组织方式,既能帮助你在修改git_repository相关行为时快速定位回归风险,也为自建外部依赖规则时设计"小而准"的测试仓库提供了可直接借鉴的范本。

【免费下载链接】bazela fast, scalable, multi-language and extensible build system项目地址: https://gitcode.com/GitHub_Trending/ba/bazel

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 3:29:11

GESP四级真题B3870变长编码解析:从位运算到LEB128/varint

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 3:17:34

HDMI切换器选购指南:从带宽、EDID到RE辐射,避开那些坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 3:16:15

微服务+AI改造:统一研发运维标准的实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华