ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

PCSX2 依赖中的 Google Mock Custom 定制点:gmock-port.h 与 Flag 宏注入机制全解析

PCSX2 依赖中的 Google Mock Custom 定制点:gmock-port.h 与 Flag 宏注入机制全解析 PCSX2 依赖中的 Google Mock Custom 定制点gmock-port.h 与 Flag 宏注入机制全解析【免费下载链接】pcsx2PCSX2 - The Playstation 2 Emulator项目地址: https://gitcode.com/GitHub_Trending/pc/pcsx2导读本文围绕 PCSX2 仓库内嵌的 Google Test/Google Mock 源码中googlemock/include/gmock/internal/custom/目录及其README.md展开剖析 Google Mock 为使用者预留的定制注入点Customization Points机制。你将掌握custom/gmock-port.h中九大 Flag 宏GMOCK_DECLARE_*/GMOCK_DEFINE_*/GMOCK_FLAG_GET/GMOCK_FLAG_SET的作用、底层实现与命令行动态注入流程并了解 PCSX2 的 tests/ctest 测试体系为何能在仓库内直接复用这套第三方单测框架。一、Custom 目录是什么Google Mock 的用户定制注入点在 PCSX2 仓库的第三方依赖目录 3rdparty/googletest 中Google Mock 的头文件体系保留了一个特殊目录3rdparty/googletest/googlemock/include/gmock/internal/custom/README.md 开宗明义The custom directory is an injection point for custom user configurations.——这个目录是 Google Mock 专门为使用者预留的定制注入点。其设计理念是Google Mock 官方分发版会携带一组默认空实现的定制头文件用户在把 Google Mock 源码集成进自己的构建系统如 PCSX2 的 CMake 体系时可以原地覆盖这些文件而不必修改 Google Mock 核心源码。当前目录下共有三个定制头文件定制头文件作用域对应 README 说明custom/gmock-port.h移植层与 Flag 宏README 重点介绍的gmock-port.h可定义下述九大宏custom/gmock-generated-actions.h自定义 Action可注入用户自定义的 Action 声明custom/gmock-matchers.h自定义 Matcher可注入用户自定义的 Matcher 声明在 PCSX2 中这三个文件保持了 Google Mock 的官方默认形态即只有头文件保护宏与 IWYU 指令的空壳说明仓库并未在此做额外定制但该目录的存在保证了未来定制不污染框架核心。而 PCSX2 自己的测试则直接构建在 tests/ctest含多个*.cpp测试源文件与*.txtCMake 描述之上与这套第三方框架形成框架在 3rdparty、用例在 tests的清晰分层。二、gmock-port.h的九大 Flag 宏完整清单README 明确指出定制头文件 custom/gmock-port.h 中可以定义以下宏全套共九个2.1 声明宏Declare用于头文件GMOCK_DECLARE_bool_(name)声明一个bool类型的 Google Mock FlagGMOCK_DECLARE_int32_(name)声明一个int32_t类型的 Google Mock FlagGMOCK_DECLARE_string_(name)声明一个std::string类型的 Google Mock Flag。2.2 定义宏Define用于实现文件GMOCK_DEFINE_bool_(name, default_val, doc)定义boolFlag含默认值与文档说明GMOCK_DEFINE_int32_(name, default_val, doc)定义int32_tFlagGMOCK_DEFINE_string_(name, default_val, doc)定义std::stringFlag。2.3 读写宏Get/Set用于任何需要访问 Flag 的代码GMOCK_FLAG_GET(flag_name)读取指定 Flag 的当前值GMOCK_FLAG_SET(flag_name, value)将指定 Flag 设置为新值。约定GMOCK_DECLARE_*通常写在头文件供多方引用GMOCK_DEFINE_*只写在一个实现文件中保证唯一实例name会被自动加上gmock_前缀组装成实际符号名这一机制由 internal/gmock-port.h 第 73-74 行的GMOCK_FLAG_NAME_(name)与GMOCK_FLAG(name)宏完成。三、底层实现定制宏如何被真实调用链衔接3.1 定制头文件 → 内部移植头的单向包含custom/gmock-port.h自身并不会被框架二次加工它的价值在于官方内部头 internal/gmock-port.h 第 57 行通过#include gmock/internal/custom/gmock-port.h先包含定制头随后才在自身定义GMOCK_DEFINE_*/GMOCK_DECLARE_*/GMOCK_FLAG_GET/GMOCK_FLAG_SET的默认实现。这就实现了官方先给默认、用户可覆盖的双层结构——用户若在定制头里预先定义了同名宏官方内部头会因宏已定义而跳过或按条件分支选择不同实现。3.2 两套 Flag 后端Abseil 标志库 vs 原生全局变量internal/gmock-port.h 第 76-138 行按编译期条件选择两套实现分支一GTEST_HAS_ABSL !GTEST_NO_ABSL_FLAGS使用 Abseil Flags#define GMOCK_DEFINE_bool_(name, default_val, doc) \ ABSL_FLAG(bool, GMOCK_FLAG_NAME_(name), default_val, doc) #define GMOCK_DEFINE_int32_(name, default_val, doc) \ ABSL_FLAG(int32_t, GMOCK_FLAG_NAME_(name), default_val, doc) #define GMOCK_DEFINE_string_(name, default_val, doc) \ ABSL_FLAG(std::string, GMOCK_FLAG_NAME_(name), default_val, doc) #define GMOCK_FLAG_GET(name) ::absl::GetFlag(GMOCK_FLAG(name)) #define GMOCK_FLAG_SET(name, value) \ (void)(::absl::SetFlag(GMOCK_FLAG(name), value))分支二无 Abseil回退到testing命名空间内的全局变量#define GMOCK_DEFINE_bool_(name, default_val, doc) \ namespace testing { \ GTEST_API_ bool GMOCK_FLAG(name) (default_val); \ } \ static_assert(true, no-op to require trailing semicolon) // int32_t / string 同理string 版本为 GTEST_API_ ::std::string #define GMOCK_FLAG_GET(name) ::testing::GMOCK_FLAG(name) #define GMOCK_FLAG_SET(name, value) (void)(::testing::GMOCK_FLAG(name) value)两套实现的语义完全等价声明/定义/读写一一对应区别仅在于底层存储由谁提供。分支二中的static_assert(true, ...)用于强制宏调用以分号结尾避免误写。PCSX2 的 CMake 构建默认不启用 Abseil因此实际生效的是testing 命名空间全局变量这一分支详见后文源码证据。3.3 内置 Flag 的真实定义PCSX2 仓库中的现成范例在 PCSX2 仓库的 googlemock/src/gmock.cc 第 36-52 行Google Mock 用这些宏定义了三个官方 Flag是最直观的用法示例GMOCK_DEFINE_bool_(catch_leaked_mocks, true, true if and only if Google Mock should report leaked mock objects as failures.); GMOCK_DEFINE_string_(verbose, testing::internal::kWarningVerbosity, Controls how verbose Google Mocks output is. Valid values:\n info - prints all messages.\n warning - prints warnings and errors.\n error - prints errors only.); GMOCK_DEFINE_int32_(default_mock_behavior, 1, Controls the default behavior of mocks. Valid values:\n 0 - by default, mocks act as NiceMocks.\n 1 - by default, mocks act as NaggyMocks.\n 2 - by default, mocks act as StrictMocks.);对应地公开头 include/gmock/gmock.h 第 68 行用GMOCK_DECLARE_bool_(catch_leaked_mocks);对外声明而框架内部则在多处用GMOCK_FLAG_GET读取src/gmock-spec-builders.cc 第 287 行GMOCK_FLAG_GET(verbose)决定日志输出行数同文件第 492 行GMOCK_FLAG_GET(catch_leaked_mocks)决定是否报告泄漏的 mock 对象同文件第 605 行GMOCK_FLAG_GET(default_mock_behavior)决定未设置期望时 mock 的默认行为Nice/Naggy/Strictsrc/gmock-internal-utils.cc 第 134-137 行GMOCK_FLAG_GET(verbose)与kInfoVerbosity/kErrorVerbosity比较以过滤输出。由此可验证verbose的取值info/warning/error、default_mock_behavior的取值0/1/2以及catch_leaked_mocks的布尔默认值true均为仓库内可实证的默认值与 README 列出的宏一一对应。四、命令行注入--gmock_*Flag 的解析与消费流程Flag 宏不只是编译期符号它们与命令行解析深度绑定。以 src/gmock.cc 为例完整流程如下InitGoogleMock(argc, argv)第 203-211 行含char**与wchar_t**两个重载调用内部模板InitGoogleMockImplInitGoogleMockImpl第 144-188 行先调用InitGoogleTest(argc, argv)幂等可重复调用然后遍历argv对每个参数用GMOCK_INTERNAL_PARSE_FLAG(flag_name)宏展开为先GMOCK_FLAG_GET取当前值 →ParseGoogleMockFlag解析 → 成功后GMOCK_FLAG_SET写回依次尝试catch_leaked_mocks、verbose、default_mock_behavior三个 Flag命中后将该参数从argv中左移移除并递减*argc保证测试程序看到的命令行已被清洗解析器细节ParseGoogleMockFlagValue第 62-88 行要求参数以--gmock_ flag 名开头可接受--gmock_xxxvalue形式bool类 Flag 允许省略valuebool 解析第 95-106 行值不为0、f、F即视为trueint32_t解析第 127-138 行委托ParseInt32并带错误提示字符串解析第 113-125 行直接取之后的内容。此外还提供了Arduino/嵌入式无命令行环境的InitGoogleMock()无参重载第 215-223 行用假 argc/argv 走同一解析路径。因此运行 PCSX2 的 ctest 测试程序时可以这样注入 Flag示意具体入口以 tests/ctest 下的测试可执行文件为准# 关闭 mock 泄漏检测 ./some_gmock_test --gmock_catch_leaked_mocks0 # 输出最详尽日志 ./some_gmock_test --gmock_verboseinfo # 让所有 mock 默认按 StrictMock 行为工作 ./some_gmock_test --gmock_default_mock_behavior2这些 Flag 会被InitGoogleMock自动消费并从 argv 移除测试代码无需手动解析。五、如何在 PCSX2 中扩展自己的定制 Flag实操路线基于以上源码证据若要在 PCSX2 集成 Google Mock 时增加自有 Flag只需遵循 README 描述的模式第一步在 custom 定制头中声明在 custom/gmock-port.h 的 include guard 之间加入GMOCK_DECLARE_bool_(my_feature_enabled); GMOCK_DECLARE_int32_(my_retry_count); GMOCK_DECLARE_string_(my_output_path);第二步在实现文件中定义新建或复用某个.cc注意只定义一次避免重复符号GMOCK_DEFINE_bool_(my_feature_enabled, false, Enable my custom feature.); GMOCK_DEFINE_int32_(my_retry_count, 3, Number of retries.); GMOCK_DEFINE_string_(my_output_path, out/, Output directory.);第三步读写if (GMOCK_FLAG_GET(my_feature_enabled)) { GMOCK_FLAG_SET(my_retry_count, 5); GMOCK_FLAG_SET(my_output_path, std::string(result/)); }注意默认实现中这些宏会展开为testing命名空间下的GTEST_API_全局变量gmock_my_feature_enabled等因此必须遵守声明与定义分离、定义唯一的规则同时你需要在构建系统如 PCSX2 的 3rdparty/googletest/CMakeLists.txt 或顶层 CMake 的 googletest 配置中保证该定制头先于内部移植头被包含。若不想改动分发源码也可采用另建同名定制头并置于 include 搜索路径更前位置的方式覆盖官方文件——这正是 custom 目录设计意图的体现。六、定制点的边界与注意事项不修改框架核心custom/目录存在的意义就是让你在不触碰gmock-*.h核心头的前提下注入配置PCSX2 当前保持其官方默认状态未来如需深度定制如为测试框架接入自定义日志后端应优先考虑在此目录做文章。宏命名以_结尾的内部约定README 列出的九个宏均以下划线结尾而 internal/gmock-port.h 第 30-35 行明确所有以下划线结尾的宏与 internal 命名空间符号随时可能变更因此面向框架外部的代码不应直接依赖这些宏的展开细节只应通过InitGoogleMock与GMOCK_FLAG_GET/SET等稳定入口使用。依赖顺序custom/gmock-port.h被 internal/gmock-port.h 第 57 行包含custom/gmock-matchers.h被 include/gmock/gmock-matchers.h 第 5889 行包含custom/gmock-generated-actions.h被 include/gmock/gmock-more-actions.h 第 47 行包含。定制时须保持这套先定制后默认的包含次序否则宏覆盖不会生效。IWYU 标记三个定制头均标注IWYU pragma: private, include gmock/gmock.h提示外部代码应通过聚合头gmock.h引入不要直接 include 内部文件。七、总结PCSX2 仓库内嵌的 Google Mock 通过googlemock/include/gmock/internal/custom/目录及其README.md为使用者暴露了一套精炼而完备的定制注入协议三个定制头文件对应移植层、Action、Matcher 三个扩展面其中gmock-port.h提供的九大 Flag 宏覆盖声明—定义—读写—命令行解析全链路。通过 internal/gmock-port.h 的双后端实现与 src/gmock.cc 的真实调用链可以确认catch_leaked_mocks、verbose、default_mock_behavior三个内置 Flag 的默认值与语义均有仓库源码背书。对任何希望在自己构建体系无论是 PCSX2 还是其他 C 项目中深度集成 Google Mock 的开发者而言理解并善用这个定制注入点是让测试框架贴合自身需求的第一步。【免费下载链接】pcsx2PCSX2 - The Playstation 2 Emulator项目地址: https://gitcode.com/GitHub_Trending/pc/pcsx2创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表