postgres_lsp 测试宏 pgls_test_macros 深度解析:用 `gen_tests!` 按文件批量自动生成 Rust 测试
postgres_lsp 测试宏 pgls_test_macros 深度解析用gen_tests!按文件批量自动生成 Rust 测试【免费下载链接】postgres_lspA Language Server for Postgres项目地址: https://gitcode.com/GitHub_Trending/po/postgres_lsp导读本文聚焦 postgres_lsp 仓库中crates/pgls_test_macros这一轻量级过程宏 crate它提供了一个名为gen_tests!的声明式宏能够根据 glob 匹配到的 SQL 测试文件自动批量生成#[test]测试函数配合可选的.expected.sql文件与快照断言让每个测试样例一个文件的数据驱动测试范式落地为几行代码。读完本文你将掌握该宏的完整用法、参数约定、生成结果形态与全部已知坑点并透过 src/lib.rs 的源码理解其底层实现原理以及它在 pgls_analyser 规则测试 中的真实生产级用法。一、它是什么为文件驱动的测试而生的过程宏pgls_test_macros 是 postgres_lsp 工作区见根目录 Cargo.toml 中的 workspace 依赖声明下的一个纯过程宏 crateCargo.toml中通过[lib] proc-macro true将其声明为过程宏库。它的使命非常聚焦以文件为测试用例的载体自动生成调用你指定测试函数的#[test]代码。这一设计思路与项目自身的测试组织方式高度契合——在crates/pgls_analyser/tests/specs/、crates/pgls_pretty_print/tests/等目录下项目维护了大量以.sql为后缀的测试样例文件如crates/pgls_analyser/tests/specs/safety/下即有 128 个.sql文件与一一对应的.snap快照。每新增一个 SQL 样例即自动新增一个测试用例无需手写重复的样板代码。二、基本用法两行代码批量生成测试原文档给出了最核心的用法向宏传入一个 glob 模式和一个测试函数路径宏会为每一个匹配的文件生成一个独立的#[test]。2.1 目录结构约定假设你的 crate 中有如下文件结构crate/ |-- src/ |-- tests/ |-- queries/ |-- test.sql |-- test.expected.sql |-- querytest.rs2.2 宏调用// crate/tests/querytest.rs tests_macros::gen_tests!{ tests/queries/*.sql, crate::run_test // use crate:: if the linter complains. } fn run_test( test_path: str, // absolute path on the machine expected_path: str, // absolute path of .expected file test_dir: str // absolute path of the test files parent ) { // your logic }需要注意的几点约定glob 模式必须以 crate 根目录为起点宏内部会以CARGO_MANIFEST_DIR为基准解析例如tests/queries/*.sql测试函数接收三个str参数test_path测试文件绝对路径、expected_path对应.expected文件绝对路径即使文件不存在也会传入、test_dir测试文件所在目录的绝对路径原文档特意提示若 linter 报错请在函数路径前加上crate::前缀例如crate::run_test。2.3 自动生成的测试代码给定crate/tests/queries/some_test_abc.sql宏展开后等价于生成如下测试该文件结构复现自 README#[test] pub fn some_test_abc() { let test_file crate/tests/queries/some_test_abc.sql; let test_expected_file crate/tests/queries/some_test_abc.expected.sql; let parent crate/tests/queries; run_test(test_file, test_expected_file, parent); }glob 匹配到的每一个文件都会生成一个这样的测试函数测试名取自文件名去除扩展名。你可以选择两种断言策略结果型断言在测试文件旁放置一个.expected.文件如test.expected.sql其路径会被传入测试函数由你在函数体内完成输出比对快照断言不依赖.expected文件直接在测试函数内结合insta等快照库生成.snap快照见后文 pgls_analyser 的实例。三、源码级实现原理宏内部做了什么理解了用法之后我们深入 src/lib.rs 一探实现细节。整个宏的入口是pub fn gen_testslib.rs它先解析输入参数再调用Arguments::generate()生成 TokenStream。整个流程可分为四步3.1 参数解析两个必需参数Arguments结构体lib.rs通过syn::parse::Parse解析宏输入struct Arguments { pattern: syn::ExprLit, // glob 模式必须是字符串字面量 test_function: syn::Path, // 测试函数路径 }解析逻辑要求输入严格为字符串字面量, 路径的二元组形式逗号分隔顺序固定。3.2 文件收集glob 遍历与过滤Arguments::get_filepaths()lib.rs完成文件枚举通过std::env::var(CARGO_MANIFEST_DIR)获取 crate 根目录这就是glob 必须以 crate 根为起点的原因若环境变量缺失会报错 Cannot find CARGO_MANIFEST_DIR. Are you using cargo?使用依赖 globwalk构建GlobWalkerBuilder遍历文件跳过所有以.expected.sql结尾的文件避免把期望文件本身当作测试用例仅保留元数据中is_file()的条目。3.3 变量推导测试名、路径与 expected 路径Variables的TryFromPathBuf实现lib.rs负责从每个文件路径推导出四个变量test_name取file_stem()文件名去扩展名ext断言扩展名必须为sql否则 panicExpected .sql extension but received: {ext}——这正是 Pitfalls 中所有匹配文件必须是 .sql的源码出处test_dir父目录绝对路径test_expected_fullpath把扩展名去掉再拼回.expected.sql即test.sql→test.expected.sql。注意这个路径是构造出来的并不检查文件是否真实存在所以即使没有.expected文件也会照常传入路径。3.4 模块树组织按目录层级生成嵌套modTestModuleslib.rs是一个递归树结构按路径的目录部分逐级插入foo/bar/testA.sql会被插入foo→bar两级模块最终print()时递归地用mod #name { ... }包裹子模块并追加测试项。因此当 glob 跨多个子目录时生成的测试天然具有mod层级隔离例如crates/pgls_analyser/tests/specs/safety/下的规则测试会自动归入对应的specs::safety模块树。四、仓库内的真实使用案例pgls_analyser 规则测试宏最完整的实践样本在 crates/pgls_analyser/tests/rules_tests.rs只需两行宏调用便覆盖了tests/specs/**/*.sql下全部规则测试样例pgls_test_macros::gen_tests! { tests/specs/**/*.sql, crate::rule_test }随后rule_test(full_path: static str, _: str, _: str)rules_tests.rs承担了真实的断言逻辑展示了.expected之外的另一种搭配方式通过parse_test_path从路径中拆出(group, rule, fname)三元组例如specs/safety/no-primary-key/xxx.sql→(safety, no-primary-key, xxx.sql)以此构造RuleFilter只启用对应的单条规则读取 SQL 文本用pgls_statement_splitter::split切分语句、pgls_query::parse解析 AST喂给Analyser::run拿到诊断结果一方面用insta::with_settings!将快照路径指向测试文件所在目录并assert_snapshot!生成.snap快照这正是crates/pgls_analyser/tests/specs/safety/下 128 个.snap文件的由来另一方面用Expectation::from_file解析 SQL 文件中的-- expect_no_diagnostics/-- expect_category注释做结果型断言校验各类诊断的数量。这套宏生成用例 函数内双断言的模式是把 README 中的.expected.方案与快照方案结合使用的进阶范例读者可直接照搬。五、已知坑点与规避方法Pitfalls 完整清单原文档明确列出的陷阱在此逐一展开并补充源码层面的解释坑点说明规避方式文件名撞 Rust 关键字若测试文件名为fn.sql、match.sql等 Rust 关键字生成的pub fn fn()属于非法语法编译直接失败避免用 Rust 关键字命名测试文件非 snake_case 文件名的 lint 报错测试函数名直接取自文件名MyTest.sql会生成pub fn MyTest()触发 non_snake_case 警告测试文件统一使用snake_case命名glob 只能匹配.sql文件源码中assert_eq!(ext, sql, ...)会在生成期 paniclib.rs所有匹配文件保持.sql后缀.expected.sql路径总会传入即使文件不存在expected_path参数也照常给出由测试函数自行决定如何处理在测试函数内做文件存在性判断或干脆采用快照断言自动包裹mod tests { .. }宏默认会把生成的测试包进mod tests若同一文件需要多次生成会冲突多次生成时手动包裹外部模块mod some_test { tests_macros::gen_tests! { .. } }不要放在 crate 根目录源码对路径取.parent()时expect(Do not put tests in root directory.)测试文件必须位于至少一层子目录下测试文件放在tests/下的子目录中glob 需以 crate 根为起点路径基于CARGO_MANIFEST_DIR解析tests/queries/*.sql表示 crate 根下的tests/queries/始终从 crate 根写相对 glob六、如何运行宏在编译期完成全部展开运行时无需任何额外步骤直接使用常规命令即可cargo test # 运行工作区内全部测试 cargo test -p pgls_analyser # 仅运行 analyser crate含宏生成的规则测试新增一个 SQL 测试样例只需往匹配目录丢一个文件及可选的.expected.sql重新运行cargo test即自动生效无需修改任何 Rust 源码。七、总结何时该用这类文件驱动测试宏pgls_test_macros 的价值在于把用例数量与代码量解耦测试内容沉淀为数据SQL 文件测试逻辑收敛为单一函数测试用例注册交给宏自动完成。对 postgres_lsp 这类拥有大量 SQL 样例分析规则、格式化、语句切分、快照等模块的解析器/LSP 项目尤其适用对普通 Rust 项目当你面临几十上百个同构输入需要逐一断言的场景时也可以参考本 crate 的实现src/lib.rs或直接引入该宏来组织你的数据驱动测试。进一步探索宏的实现源码位于 crates/pgls_test_macros/src/lib.rs依赖声明见 crates/pgls_test_macros/Cargo.toml最完整的生产级使用范例见 crates/pgls_analyser/tests/rules_tests.rs 及其配套的crates/pgls_analyser/tests/specs/目录128 组.sql.snap测试对。【免费下载链接】postgres_lspA Language Server for Postgres项目地址: https://gitcode.com/GitHub_Trending/po/postgres_lsp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

Cloudflare Agents 前端视觉体系实战:基于 Kumo 设计系统构建 Agent Playground 界面

Cloudflare Agents 前端视觉体系实战:基于 Kumo 设计系统构建 Agent Playground 界面

Cloudflare Agents 前端视觉体系实战:基于 Kumo 设计系统构建 Agent Playground 界面 【免费下载链接】agents Build and deploy AI Agents on Cloudflare 项目地址: https://gitcode.com/GitHub_Trending/agents1/agents 本文以仓库中的 design/visuals.md…

2026/9/17 22:03:10 阅读更多 →
OpenUSD usdVol 之 ParticleFieldKernelGaussianSurfletAPI:粒子场的平面高斯曲面片内核(Gaussian Surflet Kernel)解析

OpenUSD usdVol 之 ParticleFieldKernelGaussianSurfletAPI:粒子场的平面高斯曲面片内核(Gaussian Surflet Kernel)解析

OpenUSD usdVol 之 ParticleFieldKernelGaussianSurfletAPI:粒子场的平面高斯曲面片内核(Gaussian Surflet Kernel)解析 【免费下载链接】OpenUSD Universal Scene Description 项目地址: https://gitcode.com/GitHub_Trending/ope/OpenUSD…

2026/9/19 0:04:20 阅读更多 →
Umi-OCR:把扫描 PDF 变成可搜索文档的免费离线 OCR 软件

Umi-OCR:把扫描 PDF 变成可搜索文档的免费离线 OCR 软件

Umi-OCR:把扫描 PDF 变成可搜索文档的免费离线 OCR 软件 【免费下载链接】Umi-OCR OCR software, free and offline. 开源、免费的离线OCR软件。支持截屏/批量导入图片,PDF文档识别,排除水印/页眉页脚,扫描/生成二维码。内置多国语…

2026/9/19 0:04:05 阅读更多 →

最新新闻

书桌角落的 Mac mini,OpenClaw 通过 TaoToken 跑任务。

书桌角落的 Mac mini,OpenClaw 通过 TaoToken 跑任务。

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

2026/9/19 0:03:32 阅读更多 →
验证 OpenSpec 兼容性,Cursor 的 Token 从 TaoToken 出

验证 OpenSpec 兼容性,Cursor 的 Token 从 TaoToken 出

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

2026/9/19 0:03:32 阅读更多 →
Python数据库开发:SQLAlchemy ORM核心技巧与实战

Python数据库开发:SQLAlchemy ORM核心技巧与实战

1. Python数据库开发实战:SQLAlchemy ORM深度解析作为一名长期使用Python进行数据库开发的工程师,我见证了SQLAlchemy从一个小众工具成长为Python生态中最强大的ORM框架。在实际项目中,合理使用SQLAlchemy能大幅提升开发效率,但很…

2026/9/19 0:03:32 阅读更多 →
Linux C++ UDP Socket工业级实战:从bind失败到千兆压测

Linux C++ UDP Socket工业级实战:从bind失败到千兆压测

1. 这不是教科书,是我在嵌入式网关项目里焊了三个月网口后写下的UDP Socket实操笔记Linux C UDP Socket,这八个字背后不是一段hello world代码,而是一整套在工业现场跑得稳、压得住、查得清的通信骨架。我带过的三个项目——智能电表集中器、…

2026/9/19 0:03:32 阅读更多 →
2026国自然基金申请指南解读与标书撰写技巧

2026国自然基金申请指南解读与标书撰写技巧

1. 项目概述国家自然科学基金(简称"国自然")作为我国基础研究领域最重要的科研资助渠道之一,每年都吸引着数十万科研工作者的关注。2026年版申请指南的发布,标志着新一轮科研攻关的号角已经吹响。这份厚度超过300页的官…

2026/9/19 0:03:32 阅读更多 →
PixiJS v8 遮罩(Masking)完全指南:AlphaMask、StencilMask、ScissorMask 与 ColorMask

PixiJS v8 遮罩(Masking)完全指南:AlphaMask、StencilMask、ScissorMask 与 ColorMask

PixiJS v8 遮罩(Masking)完全指南:AlphaMask、StencilMask、ScissorMask 与 ColorMask 【免费下载链接】pixijs The HTML5 Creation Engine: Create beautiful digital content with the fastest, most flexible 2D WebGL renderer. 项目地…

2026/9/19 0:02:31 阅读更多 →

日新闻

BP神经网络时序预测:滑窗长度与多窗口平均策略

BP神经网络时序预测:滑窗长度与多窗口平均策略

简介:面向机器学习、深度学习与数据建模学习者的一份完整研究文献,聚焦BP神经网络在农业产量预测中的应用。文档以1980—2018年全国棉花产量为样本,系统讲解数据归一化处理、激活函数原理、多层神经网络结构搭建及训练流程,展示敏…

2026/9/19 0:00:30 阅读更多 →
Transformer训练实时监控实战:基于MindSpore的损失曲线可视化方案

Transformer训练实时监控实战:基于MindSpore的损失曲线可视化方案

上个月调一个Deformable DETR模型,在单卡上要跑将近两天。第二天早上我下意识打开终端翻日志,发现loss从凌晨两点就开始往上爬,一路从0.8涨到1.35,整整六个小时没人发现。那六个小时的训练不仅白跑,还霸占着卡——等于…

2026/9/19 0:00:30 阅读更多 →
OpenCloud 中的 Go 类型安全转换库 spf13/cast:从零值回退到泛型 API 的完整实战指南

OpenCloud 中的 Go 类型安全转换库 spf13/cast:从零值回退到泛型 API 的完整实战指南

OpenCloud 中的 Go 类型安全转换库 spf13/cast:从零值回退到泛型 API 的完整实战指南 【免费下载链接】opencloud 🌤️ OpenCloud is the open source platform for file management, sharing and collaboration. Simple and sovereign. 项目地址: htt…

2026/9/19 0:00:30 阅读更多 →

周新闻

AI SDK Harness 依赖更新指南:掌握 harness 包 SDK 依赖的升级、桥接同步与一致性校验

AI SDK Harness 依赖更新指南:掌握 harness 包 SDK 依赖的升级、桥接同步与一致性校验

AI SDK Harness 依赖更新指南:掌握 harness 包 SDK 依赖的升级、桥接同步与一致性校验 【免费下载链接】ai The AI Toolkit for TypeScript. From the creators of Next.js, the AI SDK is a free open-source library for building AI-powered applications and ag…

2026/9/16 19:03:19 阅读更多 →
Refine v5 Ant Design NumberField 组件实战:基于 Intl 的本地化数字格式化

Refine v5 Ant Design NumberField 组件实战:基于 Intl 的本地化数字格式化

Refine v5 Ant Design NumberField 组件实战:基于 Intl 的本地化数字格式化 【免费下载链接】refine A React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility. 项目地址: https://gitcode.com/GitH…

2026/9/17 7:57:36 阅读更多 →
Flutter应用改名全指南:从Android到iOS的配置与工具实践

Flutter应用改名全指南:从Android到iOS的配置与工具实践

刚接一个外包项目时,甲方要求把工程里临时用的应用名改成正式产品名。我本来觉得“改名”这种小事,打开配置文件改一行不就完了?结果真动手才发现,Flutter项目里“应用名称”根本不是一处配置,而是一整套散落在 Androi…

2026/9/17 10:19:14 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/16 22:31:27 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/15 21:39:18 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/16 22:32:59 阅读更多 →