ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

TDengine 的 C++ 测试打桩利器:cppstub Conan 包使用与集成指南

TDengine 的 C++ 测试打桩利器:cppstub Conan 包使用与集成指南 TDengine 的 C 测试打桩利器cppstub Conan 包使用与集成指南【免费下载链接】TDengineHigh-performance, scalable time-series database designed for Industrial IoT (IIoT) scenarios项目地址: https://gitcode.com/GitHub_Trending/tde/TDengine本文以 conan/cppstub/README.md 为核心结合 conan/cppstub/conanfile.py 打包配方、cmake/conan.cmake 构建集成与仓库内大量单元测试用例系统讲解 TDengine 中 cppstub 头文件式打桩库的 Conan 打包原理、创建安装、项目接入与底层实现。读完本文你将能够独立完成 cppstub 包的本地构建与验证并在自己的 C 测试工程中通过 Conan CMake 直接使用stub.h与addr_any.h进行函数级打桩同时理解 TDengine 各模块单元测试对它的实际依赖方式。一、为什么 TDengine 需要 cppstubTDengine 是面向工业物联网IIoT场景的高性能时序数据库其源码中大量模块如 catalog、executor、parser、qworker、scheduler、scalar、monitor、new-stream、vnode、mnode 等的单元测试都需要隔离外部依赖——例如替换系统调用、mock 底层存储或调度接口。cppstub 正是一个专为 C 单元测试设计的轻量级函数打桩stub/模拟mock库它允许测试代码在运行期把目标函数临时替换为自定义实现从而做到不依赖真实环境也能验证业务逻辑。从仓库中可以看到Stub类被广泛使用于测试代码例如source/common/test/tmsgTest.cpp 中通过Stub stub_对消息处理相关函数打桩source/libs/catalog/test/catalogTests.cpp 中以static Stub stub;的方式隔离目录服务source/libs/executor/test/joinTests.cpp、source/libs/executor/test/queryPlanTests.cpp 在查询执行器测试中打桩source/dnode/mnode/impl/test/stream/stream.cpp、source/libs/monitor/test/monTest.cpp 等也直接#include stub.h。这些测试文件同时还会#include addr_any.h说明 cppstub 的两个头文件在 TDengine 测试体系中是成对使用的基础设施。二、包信息与核心特性根据 conan/cppstub/README.mdcppstub Conan 包的基本信息如下属性值包名cppstub版本1.0.0许可证MIT类型头文件库Header-only主要头文件stub.h打桩核心功能、addr_any.h平台相关的地址操作上游cpp-stub 项目对应提交 3137465194014d66a8402941e80d2bccc6346f51LICENSE 见 conan/cppstub/cppstub/LICENSE其核心特性包括纯头文件库无需编译、无需链接消费方只需把头文件目录加入 include 路径跨平台支持Linux、macOS、Windows 均有对应的平台适配头简单易用的打桩 API通过Stub类的set()/reset()/clear()三个方法即可完成打桩、还原与批量清理平台相关实现内置addr_any.h随操作系统自动选择。由于是 header-only 库包 ID 被清空平台无关一份二进制包可以在不同配置下复用极大简化了 CI 场景下的缓存命中。三、仓库内的源码与目录结构在本仓库 conan/cppstub 目录下完整的文件布局如下conan/cppstub/ ├── conanfile.py # Conan 打包配方 ├── README.md # 包文档本文核心 ├── SUMMARY.md # 打包与集成迁移摘要 ├── cppstub/ # 上游源码快照 │ ├── LICENSE # MIT 许可证 │ ├── src/ │ │ └── stub.h # 打桩主头文件 │ ├── src_linux/ │ │ └── addr_any.h # Linux 平台地址操作实现 │ ├── src_darwin/ │ │ └── addr_any.h # macOS 平台地址操作实现 │ └── src_win/ │ └── addr_any.h # Windows 平台地址操作实现 └── test_package/ # 校验包可用性的测试工程 ├── conanfile.py ├── CMakeLists.txt └── test_package.cpp其中 conan/cppstub/SUMMARY.md 记录了一次完整的打包迁移过程包创建成功、测试包构建运行通过、头文件可访问、包已装入本地 Conan 缓存并给出了将 cppstub 接入 TDinternal 构建体系的后续步骤。四、打包原理conanfile.py 逐段解析conan/cppstub/conanfile.py 是一个典型的 header-only 库打包配方各关键部分作用如下4.1 元信息与设置class CppStubConan(ConanFile): name cppstub version 1.0.0 license MIT description A simple and easy-to-use C stub library for unit testing topics (cpp, stub, testing, mock) settings os, compiler, build_type, arch no_copy_source Truesettings声明了 os/compiler/build_type/arch 四项但由于后文package_id()会清空信息实际生成的包仍是平台无关的no_copy_source True告诉 Conan 不要把源码复制到构建目录直接在 export 目录工作效率更高SUMMARY.md 中也特别强调了这个优化点。4.2 源码导出与打包def export_sources(self): copy(self, *, srcos.path.join(self.recipe_folder, cppstub), dstos.path.join(self.export_sources_folder, cppstub)) def source(self): pass # 依赖 export_sources 提供的本地源码 def build(self): pass # header-only无需构建 def package_id(self): self.info.clear() # header-only包 ID 与配置无关export_sources将仓库内 conan/cppstub/cppstub 目录整体导出为源码build()为空体现无需编译package_id()调用self.info.clear()使同一版本对所有配置共享一个包。4.3 按操作系统选择平台头文件package()阶段是打包的关键除许可证文件外它先把stub.h放入include/然后依据self.settings.os选择addr_any.h操作系统settings.os来源目录打包目标Linuxsrc_linux/include/addr_any.hMacossrc_darwin/include/addr_any.hWindowssrc_win/include/addr_any.h其他默认src_linux/include/addr_any.h对应代码片段if self.settings.os Linux: platform_dir src_linux elif self.settings.os Macos: platform_dir src_darwin elif self.settings.os Windows: platform_dir src_win else: platform_dir src_linux # Default to Linux4.4 消费方元信息def package_info(self): self.cpp_info.bindirs [] self.cpp_info.libdirs [] self.cpp_info.includedirs [include]bindirs与libdirs置空表示没有任何二进制需要链接只暴露include目录——这正是 header-only 库的标准做法也是 CMake 中cppstub::cppstub目标无需链接任何库文件的底层原因。五、创建与验证包在包含 conan/cppstub/conanfile.py 的目录下执行# 创建包到本地 Conan 缓存缺失依赖时自动构建 conan create . --buildmissing # 验证安装结果 conan list cppstub/*SUMMARY.md 中记录的预期输出为Local Cache cppstub cppstub/1.0.0conan create会依次执行 export、package、build空实现与 test_package 校验。test_package 工程由三部分组成conan/cppstub/test_package/conanfile.py通过self.requires(self.tested_reference_str)依赖被测包并用 CMakeDeps/CMakeToolchain 生成器驱动 CMake 构建can_run(self)判断本地可运行后执行test_cppstubconan/cppstub/test_package/CMakeLists.txtfind_package(cppstub REQUIRED CONFIG)后链接cppstub::cppstubconan/cppstub/test_package/test_package.cpp验证stub.h、addr_any.h可包含且add(2, 3)正常返回 5。实际测试运行输出来源 SUMMARY.mdTesting cppstub package... Normal add(2, 3) 5 stub.h header is available addr_any.h header is available All tests passed!需要注意的是测试包只验证头文件可用性并未完整验证打桩功能打桩需要更复杂的运行环境设置这一点在 test 源码的注释中有明确说明。六、在项目中使用 cppstub6.1 声明依赖在conanfile.txt中声明[requires] cppstub/1.0.0 [generators] CMakeDeps CMakeToolchain或在conanfile.py中声明def requirements(self): self.requires(cppstub/1.0.0)针对 TDengine 的构建场景SUMMARY.md 还给出了仅构建测试时引入的推荐写法避免生产构建携带测试依赖def requirements(self): if self.options.get_safe(build_tests): self.requires(cppstub/1.0.0)6.2 CMake 集成cmake_minimum_required(VERSION 3.15) project(MyProject CXX) find_package(cppstub REQUIRED CONFIG) add_executable(mytest test.cpp) target_link_libraries(mytest cppstub::cppstub)find_package(cppstub REQUIRED CONFIG)由CMakeDeps生成器自动生成配置文件cppstub::cppstub目标只携带 include 路径不携带任何链接库。6.3 代码中使用#include stub.h #include addr_any.hstub.h提供打桩功能addr_any.h提供平台相关的地址操作实现两者需配套包含。一个真实的打桩用法参考 test_package/test_package.cpp 的函数结构如下#include stub.h #include addr_any.h // 待打桩的函数 int add(int a, int b) { return a b; } // 打桩替换函数 int stub_add(int a, int b) { return 100; } int main() { Stub stub; stub.set(add, stub_add); // 将 add 临时替换为 stub_add int r add(2, 3); // 返回 100 stub.reset(add); // 还原 add return 0; }七、stub.h 的底层实现原理源码级打桩的本质是运行时改写函数入口处的机器码使其跳转到替身函数。从 conan/cppstub/cppstub/src/stub.h 的源码结构可以清晰看到这一机制宏ADDR(CLASS_NAME, MEMBER_NAME)用于取得成员函数地址配合Stub::set()可对类成员函数打桩。架构相关的替换宏REPLACE_FAR/REPLACE_NEAR按编译期宏选择指令序列覆盖 x86_6413 字节movabs r11, imm64; jmp r11与 5 字节jmp rel32两种形态、AArch64、ARM/Thumb、MIPS64、RISC-V64/32 位、LoongArch64、PowerPC64、Alpha、SPARC64、SW64、s390x 等平台每种架构都有对应的CODESIZE替换指令占用的字节数。指令缓存刷新宏CACHEFLUSHPOSIX 平台使用__builtin___clear_cacheWindows 使用FlushInstructionCache若定义了__VALGRIND__还会调用VALGRIND_DISCARD_TRANSLATIONS保证改写后的指令在 Valgrind 下也能生效。Stub类的三阶段流程对应set()方法用mprotectPOSIX或VirtualProtectWindows把目标函数所在页临时改为可读可写可执行PROT_READ | PROT_WRITE | PROT_EXEC先保存原始指令到code_buf再依据目标与替身地址距离选择REPLACE_FAR或REPLACE_NEAR写入跳转指令恢复页权限为可读可执行PROT_READ | PROT_EXEC并将函数指针登记到内部std::map。reset(addr)用保存的原始指令还原函数析构函数和clear()会遍历 map 一次性还原全部打桩避免测试间相互污染。页大小在构造函数中通过sysconf(_SC_PAGE_SIZE)Windows 为GetSystemInfo获取异常时兜底为 4096。这些细节解释了为什么 cppstub 在 Linux/macOS/Windows 上都需要各自独立的addr_any.h——不同平台获取函数地址、处理页权限的方式差异巨大。八、与 TDengine 构建系统的集成8.1 cmake/conan.cmake 中的接入TDengine 主构建脚本 cmake/conan.cmake 中已内置 cppstub 的集成逻辑第 36–38 行find_package(cppstub QUIET)未找到时回退到 ExternalProject 方案保证两种依赖获取路径都能工作第 455–471 行定义了DEP_ext_cppstub/DEP_ext_cppstub_INC/DEP_ext_cppstub_LIB三个宏核心逻辑是macro(DEP_ext_cppstub tgt) if(TARGET cppstub::cppstub) target_link_libraries(${tgt} PUBLIC cppstub::cppstub) endif() endmacro()各测试目标只需调用DEP_ext_cppstub(${target})即可获得 include 路径。仓库中 source/util/test/CMakeLists.txt、source/libs/catalog/test/CMakeLists.txt、source/libs/executor/test/CMakeLists.txt、source/libs/scheduler/test/CMakeLists.txt、source/libs/qworker/test/CMakeLists.txt、source/libs/scalar/test/filter/CMakeLists.txt、source/libs/parser/test/CMakeLists.txt、source/dnode/vnode/test/CMakeLists.txt、source/libs/monitor/test/CMakeLists.txt 等测试工程均与此宏配合使用。8.2 测试代码中的使用形态从源码搜索看TDengine 各模块测试对 cppstub 的使用高度一致#include stub.h部分同时#include addr_any.h然后在测试函数或文件作用域声明Stub对象。典型的成员函数打桩场景可参考 source/common/test/tmsgTest.cpp 与 source/dnode/vnode/test/metaTxnAlterStbTest.cpp其中Stub stub;局部对象在作用域结束即自动还原。九、使用注意事项纯头文件无需链接cppstub::cppstub目标只提供 include 路径不要为它配置任何库搜索路径。平台头自动选择打包时已按settings.os固化addr_any.h消费方无需关心平台差异但代码中两个头文件通常要一起包含。包 ID 平台无关package_id()清空了配置信息同一版本包可在不同编译器/架构配置间复用利于 CI 缓存。测试包范围有限官方 test_package 仅验证头文件可达与基本编译运行完整的打桩行为需在真实测试工程中验证。打桩的底层代价由于涉及改写可执行代码段的机器码并切换页权限仅建议在单元测试中使用且需注意目标函数所在页的写保护处理stub.h 已通过mprotect/VirtualProtect处理但要求代码段页可临时写。十、总结cppstub Conan 包是 TDengine 测试基础设施的重要组成部分它以 header-only 的形式提供了跨 Linux/macOS/Windows 的函数打桩能力通过 conan/cppstub/conanfile.py 完成平台头文件的选择与打包通过 test_package 保证交付质量并已通过 cmake/conan.cmake 中的DEP_ext_cppstub宏无缝接入数十个单元测试工程。对希望为 TDengine 贡献测试代码或复用其测试模式的开发者而言掌握本文的打包、接入与底层原理即可快速上手基于 cppstub 的 C 打桩测试开发。【免费下载链接】TDengineHigh-performance, scalable time-series database designed for Industrial IoT (IIoT) scenarios项目地址: https://gitcode.com/GitHub_Trending/tde/TDengine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表