ARTICLE DETAIL

资讯详情

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

深入 PCSX2 的测试基座:GoogleTest 框架特性与单元测试实践

深入 PCSX2 的测试基座:GoogleTest 框架特性与单元测试实践 深入 PCSX2 的测试基座GoogleTest 框架特性与单元测试实践【免费下载链接】pcsx2PCSX2 - The Playstation 2 Emulator项目地址: https://gitcode.com/GitHub_Trending/pc/pcsx2导读本文围绕 PCSX2 仓库内集成的 GoogleTest 第三方依赖展开系统梳理 GoogleTest 的核心能力断言、测试发现、死亡测试、参数化测试等并结合 PCSX2 实际测试工程tests/ctest与3rdparty/googletest说明其如何被落地为模拟器核心与公共库的单元测试基座。读完本文你将掌握 GoogleTest 的关键 API 用法理解 PCSX2 如何基于 gtest/gmock 构建可验证的测试套件并能仿照仓库中的测试写法为自己的 C 代码编写同类测试。GoogleTest 是什么GoogleTest 是 Google 出品的 C 测试框架本仓库内对应的 3rdparty/googletest/README.md 明确指出该仓库是原先相互独立的 GoogleTest 与 GoogleMock 两个项目的合并体——两者关系过于紧密因此统一维护、统一发布。PCSX2 将其作为第三方依赖整体纳入3rdparty/googletest目录与fmt、imgui、rapidjson等依赖并列作为模拟器单元测试的基础设施。从仓库结构看3rdparty/googletest下包含googlemock/与googletest/两个子目录googlemock/提供 mock 对象能力gmock-all.cc、gmock_main.cc用于模拟接口行为googletest/提供核心测试框架gtest-all.cc包括断言、测试注册与运行器。PCSX2 如何集成 GoogleTest定制的 CMake 集成PCSX2 没有直接使用 GoogleTest 自带的构建脚本而是在 3rdparty/googletest/CMakeLists.txt 中编写了定制版# We need to use a custom CMakeLists for GoogleTest, because we cant compile with exceptions on. add_library(gtest googletest/src/gtest-all.cc googlemock/src/gmock-all.cc googlemock/src/gmock_main.cc ) target_include_directories(gtest PUBLIC googletest/include googlemock/include ) target_include_directories(gtest PRIVATE googletest googlemock ) target_link_libraries(gtest Threads::Threads) set_property(TARGET gtest PROPERTY CXX_STANDARD 17) set_property(TARGET gtest PROPERTY CXX_STANDARD_REQUIRED ON)这段脚本有两个关键点禁用异常兼容文件注释明确说明因为无法在开启异常的情况下编译PCSX2 的模拟器核心为保证性能和可移植性关闭了 C 异常因此 GoogleTest 也必须以同样的编译约束集成否则链接阶段会出现不一致。单一gtest目标将gtest-all.cc、gmock-all.cc、gmock_main.cc打包为一个库目标同时暴露两套头文件路径googletest/include与googlemock/include并强制 C17 标准CXX_STANDARD 17。这也呼应了上游文档中1.17.x 分支要求至少 C17的说明。统一的测试注册宏PCSX2 在 tests/ctest/CMakeLists.txt 中定义了add_pcsx2_test宏作为所有单元测试的统一入口enable_testing() add_custom_target(unittests) add_custom_command(TARGET unittests POST_BUILD COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure) macro(add_pcsx2_test target) add_executable(${target} EXCLUDE_FROM_ALL ${ARGN}) target_link_libraries(${target} PRIVATE gtest) if(APPLE) target_link_libraries(${target} PRIVATE -framework Foundation -framework Cocoa ) endif() add_dependencies(unittests ${target}) add_test(NAME ${target} COMMAND ${target}) if(MSVC AND NOT CMAKE_CXX_COMPILER_ID STREQUAL Clang) set_target_properties(${target} PROPERTIES LINK_FLAGS /STACK:1048576,1048576) endif() endmacro() add_subdirectory(common) add_subdirectory(core)该宏完成四件事创建测试可执行文件并链接gtest通过add_test注册进 CTest配合enable_testing()挂到unittests自定义目标上构建后自动以--output-on-failure运行针对 MSVC 强制 1MB 栈提交大小规避最新 MSVC 更新导致的栈增长问题。之后只需要调用add_pcsx2_test(名称 源文件...)即可新增一个测试二进制。GoogleTest 核心特性上游 README 列出的特性清单构成了理解框架能力的骨架结合 PCSX2 测试源码可以逐项得到印证。xUnit 测试框架与自动测试发现GoogleTest 基于 xUnit 架构。开发者只需用TEST()/TEST_F()宏编写测试用例框架会自动发现并注册这些测试无需手工维护注册表。PCSX2 的测试全部遵循这一模式例如 tests/ctest/common/byteswap_tests.cpp#include common/Pcsx2Defs.h #include common/ByteSwap.h #include gtest/gtest.h TEST(ByteSwap, ByteSwap) { ASSERT_EQ(ByteSwap(static_castu16(0xabcd)), 0xcdabu); ASSERT_EQ(ByteSwap(static_castu32(0xabcdef01)), 0x01efcdabu); ASSERT_EQ(ByteSwap(static_castu64(0xabcdef0123456789ULL)), 0x8967452301efcdabu); ASSERT_EQ(ByteSwap(static_casts32(0x80123456)), 0x56341280); }这里TEST(ByteSwap, ByteSwap)即注册了一个名为ByteSwap.ByteSwap的用例直接验证common/ByteSwap.h中大小端字节交换的 16/32/64 位实现无需任何手动注册代码。丰富的断言族GoogleTest 提供EXPECT_*非致命与ASSERT_*致命两类断言覆盖相等、不等、异常抛出等场景。PCSX2 测试中两类断言都有使用tests/ctest/common/string_util_tests.cpp 用ASSERT_EQ校验StringUtil::ToChars/FromChars的字符串与数值互转包括基数参数FromCharsint(ff, 16) 255和带结束指针的解析FromCharsu32(123x456, 16, endptr)解析到0x123剩余x456tests/ctest/core/patch_tests.cpp 用ASSERT_*之外的EXPECT_CALL搭配 gmock 验证 patch 引擎的内存读写行为。非致命断言EXPECT_*允许断言失败后测试继续运行从而在一次运行中收集多个失败点致命断言ASSERT_*则在关键前置条件不满足时立即中止当前用例。用户自定义断言框架允许将断言封装成自己的辅助函数/宏让测试代码贴近被测领域。PCSX2 在 tests/ctest/core/MockMemoryInterface.h 中通过宏批量生成了ExpectRead8/16/32/64/128、ExpectWrite*、ExpectIdempotentWrite*等高层期望函数#define DEFINE_EXPECT_CALL_FUNCTIONS(size) \ void ExpectRead##size(u32 address, u##size return_value) \ { \ auto actions testing::DoAll(SetValidOutParameterAction{}, testing::Return(return_value)); \ EXPECT_CALL(*this, Read##size(address, testing::_)).Times(1).WillOnce(std::move(actions)); \ } \ ...其中ExpectIdempotentWrite模拟幂等写入语义先期望一次读返回旧值仅在旧值不等于新值时才期望写入——这正是 PCSX2 patch 引擎只在值变化时写入的真实行为测试据此精准表达领域规则。死亡测试Death Tests框架支持死亡测试验证代码是否以预期方式终止如ASSERT失败、调用abort等常用于测试错误处理路径。PCSX2 核心以断言pxAssert等作为错误处理手段这类测试非常适合覆盖不可达分支与崩溃路径。致命与非致命失败上一节已说明ASSERT_*与EXPECT_*的差异是 GoogleTest 最基础的工程决策点先决条件用ASSERT_*独立校验项用EXPECT_*。PCSX2 在patch_tests.cpp的每个用例中先通过MockMemoryInterface严格约定调用序列testing::InSequence再执行补丁应用逻辑任何顺序或参数偏差都会直接导致用例失败。值参数化测试与类型参数化测试值参数化测试同一份测试逻辑以不同输入值多次运行。适合覆盖边界值、端序差异等数据驱动场景。类型参数化测试同一份测试逻辑以不同数据类型运行适合验证模板化代码。PCSX2 中MockMemoryInterface.h的DEFINE_EXPECT_CALL_FUNCTIONS(8/16/32/64/128)宏展开即是类型参数化思想在代码生成层面的应用——用预处理器一次性为 5 种位宽生成一致的 mock 期望函数。多样的运行选项GoogleTest 支持仅运行单个用例--gtest_filter、按特定顺序运行、并行运行等。PCSX2 的 tests/ctest/CMakeLists.txt 通过 CTest 的add_test(NAME ... COMMAND ...)将每个测试二进制接入ctest配合--output-on-failure只展示失败细节方便在 CI 中快速定位问题。PCSX2 测试套件全景公共库测试common_testtests/ctest/common/CMakeLists.txt 将公共库测试打包为common_test目标add_pcsx2_test(common_test byteswap_tests.cpp filesystem_tests.cpp path_tests.cpp small_string_tests.cpp string_util_tests.cpp ) if(ARCH_X86) target_sources(common_test PRIVATE x86emitter/codegen_tests.cpp x86emitter/codegen_tests.h x86emitter/codegen_tests_main.cpp ) endif() target_link_libraries(common_test PRIVATE common )覆盖点包括common/ByteSwap.h的字节交换byteswap_tests.cppcommon/FileSystem、common/Path、common/SmallString、common/StringUtil等基础设施仅在 x86 架构下编译的 x86 代码生成器测试x86emitter/codegen_tests.cpp及其独立main验证模拟器动态重编译器发射的机器码正确性。核心模拟测试core_testtests/ctest/core/CMakeLists.txt 将核心测试打包为core_test链接PCSX2_FLAGS、PCSX2与common三大目标包含patch_tests.cpppatch 引擎的内存读写语义测试见下文GS/swizzle_test_main.cpp图形子系统 swizzle 布局测试且针对DISABLE_ADVANCE_SIMD配置做了多 ISAsse4/avx/avx2分别编译链接的处理避免 ODR 违规——注释中详细记录了链接器选择第一个实现的行为与 macOSld64的特殊性是理解 PCSX2 多 ISA 编译策略的绝佳案例。以 patch_tests 为例gmock 的实战用法tests/ctest/core/patch_tests.cpp 是 GoogleTest GoogleMock 在模拟器领域的典型范本。其核心是PATCH_TEST宏为每个补丁场景生成一个TEST(Patch, 场景名)用例先构造testing::StrictMockMockMemoryInterface严格 mock 下任何未预期的调用都会失败再用testing::InSequence锁定调用顺序最后依次以四种 patch 触发时机PPT_ONCE_ON_LOAD、PPT_CONTINUOUSLY、PPT_COMBINED_0_1、PPT_ON_LOAD_OR_WHEN_ENABLED调用Patch::ApplyPatches。用例示例PATCH_TEST(Word, BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x00100000, Patch::WORD_T, 0x12345678)) { ee.ExpectIdempotentWrite32(0x00100000, 0, 0x12345678); }含义向 EE 内存0x00100000写入一个 32 位补丁值0x12345678原值为 0应用补丁后应产生一次幂等写入。更有意思的是大小端用例PATCH_TEST(BigEndianWord, BuildPatchCommand(Patch::PPT_ONCE_ON_LOAD, Patch::CPU_EE, 0x00100000, Patch::WORD_BE_T, 0x12345678)) { ee.ExpectIdempotentWrite32(0x00100000, 0, 0x78563412); }WORD_BE_T大端 32 位补丁在写入前会做字节序转换期望值变为0x78563412——测试精确锁定了补丁引擎的端序处理语义。MockMemoryInterface.h中的ExpectIdempotentWrite*还实现了读旧值→仅当变化才写的幂等语义与 patch 引擎避免无意义写入的实现保持一致。平台支持与生态工具上游 README 说明 GoogleTest 遵循 Google 的 Foundational C Support Policy支持主流编译器、平台与构建工具的组合当前 PCSX2 仓库内该依赖的 CMake 配置要求 C17 且链接线程库Threads::Threads可在 WindowsMSVC/Clang、Linux、macOS 等平台编译。生态方面上游文档列举了若干周边工具PCSX2 场景下最有参考价值的是gtest-parallel将单个测试二进制中的用例并行运行以显著提速适合模拟器这种用例多、单例耗时的项目GoogleTest Adapter / C TestMateVS Code 扩展以树状视图展示用例并支持单测调试便于在 IDE 中定位失败用例GTest TAP Listener输出 TAP 协议格式可接入支持 TAP 的测试聚合系统。PCSX2 的测试本身通过ctest驱动见 tests/ctest/CMakeLists.txt 中enable_testing()与add_custom_command的配合也兼容各类支持 ctest 的 CI 与 IDE 工作流。如何在 PCSX2 上运行单元测试基于仓库现有配置构建并运行测试的方式如下使用 CMake 配置构建仓库根目录提供 CMakePresets.json 与 CMakeLists.txt确保3rdparty/googletest被纳入构建构建unittests目标构建完成后会自动通过ctest --output-on-failure执行全部已注册测试cmake --build build --target unittests也可以直接运行单个测试二进制并利用 GoogleTest 自带的过滤选项例如只运行Patch.*下的补丁用例./build/tests/core_test --gtest_filterPatch.*或只看ByteSwap用例./build/tests/common_test --gtest_filterByteSwap.*新增一个测试的流程同样简单编写以#include gtest/gtest.h开头的*_tests.cpp加入tests/ctest/common或tests/ctest/core对应的 CMakeLists 的源文件列表即可自动注册进unittests目标。小结GoogleTest 作为 PCSX2 的测试基座提供了自动测试发现、丰富的断言、mock 对象、参数化测试等一整套能力而 PCSX2 在其之上建立了定制化的 CMake 集成关闭异常、C17、add_pcsx2_test宏和覆盖公共库与模拟核心的测试套件。从ByteSwap的字节序校验到StringUtil的字符串解析再到Patch引擎借助StrictMockInSequence锁定的内存读写语义均可在 tests/ctest 下找到可直接借鉴的实战范例。阅读这些测试代码既是学习 GoogleTest/gmock 的最佳路径也是理解 PCSX2 内部行为契约的快捷方式。【免费下载链接】pcsx2PCSX2 - The Playstation 2 Emulator项目地址: https://gitcode.com/GitHub_Trending/pc/pcsx2创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表