ARTICLE DETAIL

资讯详情

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

Skia C++ 单元测试实战指南:从 DEF_TEST 编写、GN 构建集成到 dm 运行

Skia C++ 单元测试实战指南:从 DEF_TEST 编写、GN 构建集成到 dm 运行 图形学【免费下载链接】skiaSkia is a complete 2D graphic library for drawing Text, Geometries, and Images. See documentation for contribution instructions.项目地址https://gitcode.com/gh_mirrors/ski/skia点击查看免费下载本文是一份基于 Skia 仓库 agents/skills/skia-gn-workflow/references/testing.md 的单元测试实战指南面向需要在 Skia 上新增 C 单元测试的开发者。文章覆盖测试宏体系DEF_TEST/DEF_GANESH_TEST/DEF_GRAPHITE_TEST、断言宏、gn/tests.gni构建注册以及使用dm工具过滤与运行测试的完整流程读完后你可以独立完成写一个测试 → 挂进构建 → 跑起来的全链路工作。Skia 自定义单元测试框架概览Skia 没有采用 GTest 等第三方框架而是自带一套轻量的 C 单元测试框架核心头文件位于 tests/Test.h框架主体由skiatest命名空间承载。绝大多数单元测试放在仓库根目录的tests/目录下与 gm/ 中的图形测试 GM、bench/ 中的基准测试区分开并使用DEF_TEST系列宏声明测试函数。从 tests/Test.h 可以看到每个测试最终都被包装为一个skiatest::Test结构体它携带测试名称、测试类型kCPU/kCPUSerial/kGanesh/kGraphite、CTS 强制等级以及对应平台的执行函数指针。测试通过sk_tools::RegistryTest全局注册表见 tests/Test.h完成注册因此测试文件只需要被编译进目标测试即可被发现无需额外手工登记名称。Test结构体按测试类型区分了三种执行入口实现在 tests/Test.cppcpu()执行普通 CPU 测试函数签名为void (*)(Reporter*)ganesh()执行需要 Ganesh GPU 上下文的测试签名带const GrContextOptionsgraphite()执行需要 Graphite 上下文的测试签名带const skiatest::graphite::TestOptions。也就是说同一个测试要跑在哪种后端在宏展开阶段就已经决定了运行时由测试运行器如dm负责分发。编写第一个测试在tests/目录新建一个.cpp文件包含tests/Test.h然后用DEF_TEST宏声明测试函数。下面是最小示例与原文档一致可直接复制编译#include tests/Test.h DEF_TEST(MyNewTest, reporter) { REPORTER_ASSERT(reporter, 1 1 2); if (false) { ERRORF(reporter, Something went wrong!); } }各要素说明DEF_TEST(name, reporter)定义并注册一个 CPU 测试。从 tests/Test.h 的宏定义可以看出它实际展开为声明static void test_MyNewTest(skiatest::Reporter*)通过TestRegistry将Test::MakeCPU(#name, test_MyNewTest)注册进全局注册表再给出函数体。#name会把宏参数字符串化因此测试名在运行时就是一个字符串。reporter指向skiatest::Reporter对象的指针用于上报成功、失败与附加消息。Reporter的接口定义在 tests/Test.h核心虚函数是reportFailed(const skiatest::Failure)它还维护了一个上下文栈配合push/pop可以把失败信息加上子测试等上下文。skiatest::Failure结构tests/Test.h记录了出错的文件名、行号、条件表达式字符串与格式化消息toString()在 tests/Test.cpp 中把三者拼成文件:行号 消息: 条件的格式这就是你在测试输出里看到的失败信息格式。编译前提DEF_TEST只声明为 CPU 测试。若测试文件里用到SkCanvas、SkBitmap等绘制 API还需要包含对应的 Skia 头文件若只是纯逻辑断言上面的最小示例已足够。断言宏REPORTER_ASSERT 与 ERRORF框架提供以下核心断言宏定义见 tests/Test.h宏说明REPORTER_ASSERT(reporter, cond)基础断言条件不成立时上报失败失败信息中自动带上条件的源码字符串REPORTER_ASSERT(reporter, cond, fmt, ...)带 printf 风格格式化消息的断言失败时附加自定义说明ERRORF(reporter, msg, ...)无条件上报一条 printf 风格错误例如你检测到某个异常分支时主动调用INFOF(reporter, ...)仅在reporter-verbose()为真时打印调试信息通过SkDebugf输出适合做测试内日志注意REPORTER_ASSERT失败后不会终止测试而是记录失败并继续执行这便于在一次运行中收集尽可能多的失败点。若某步失败后继续执行没有意义请用ERRORF(...)后紧跟return;提前退出这也是 tests/Test.h 官方注释示例的推荐写法。从宏实现看REPORTER_ASSERT最终走REPORT_FAILURE它会调用reporter-reportFailedWithContext(...)见 tests/Test.cpp把Reporter上下文栈中的消息以[subtest, ...]后缀附加到失败信息后输出。控制台输出与失败格式失败信息最终经由skiatest::Failure::toString()格式化形如tests/MyNewTest.cpp:12 Something went wrong: x should be 15其中12是调用断言的文件行号x should be 15是格式化消息Something went wrong前面的条件字符串可能为空。从 CPU 到 GPUGanesh 与 Graphite 测试宏除了DEF_TEST框架为 GPU 相关测试提供了两组宏在 tests/Test.h 中成体系地定义DEF_GANESH_TEST用于需要 Ganesh GPU 上下文OpenGL / Vulkan / Metal / D3D / Dawn 等的测试DEF_GANESH_TEST(MyGaneshTest, reporter, options, CtsEnforcement::kNever) { // options 是 const GrContextOptions可读取 GPU 配置 REPORTER_ASSERT(reporter, options.fUseShaderSwizzler false); }签名DEF_GANESH_TEST(name, reporter, options, ctsEnforcement)其中options是const GrContextOptionsctsEnforcement是 CTS 强制等级见下文。变体宏DEF_GANESH_TEST_FOR_ALL_CONTEXTS跑遍所有编译进的后端、DEF_GANESH_TEST_FOR_GL_CONTEXT/DEF_GANESH_TEST_FOR_VULKAN_CONTEXT/DEF_GANESH_TEST_FOR_METAL_CONTEXT/DEF_GANESH_TEST_FOR_D3D_CONTEXT/DEF_GANESH_TEST_FOR_DAWN_CONTEXT限定特定后端、DEF_GANESH_TEST_FOR_MOCK_CONTEXTmock 上下文、DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS仅渲染型上下文。这些变体通过 tests/Test.h 声明的IsGLContextType、IsVulkanContextType等过滤器把测试分发给匹配的后端执行。DEF_GRAPHITE_TEST用于需要 Graphite 渲染器上下文的测试DEF_GRAPHITE_TEST(MyGraphiteTest, reporter, CtsEnforcement::kNever) { // 执行普通 CPU 测试逻辑但被标记为 Graphite 测试 REPORTER_ASSERT(reporter, true); }签名DEF_GRAPHITE_TEST(name, reporter, ctsEnforcement)。从 tests/Test.h 的实现看它内部仍然调用一个纯Reporter*签名的函数体但注册为TestType::kGraphite运行时只在 Graphite 上下文存在时执行。需要真正访问 Graphite 上下文的测试应使用DEF_GRAPHITE_TEST_FOR_CONTEXTS/DEF_GRAPHITE_TEST_FOR_ALL_CONTEXTS/DEF_GRAPHITE_TEST_FOR_VULKAN_CONTEXT/DEF_GRAPHITE_TEST_FOR_METAL_CONTEXT/DEF_GRAPHITE_TEST_FOR_DAWN_CONTEXT等变体这些宏会通过skiatest::graphite::RunWithGraphiteTestContexts把测试分发到各后端。ctsEnforcement 参数是什么DEF_GANESH_TEST与DEF_GRAPHITE_TEST都要求传入一个CtsEnforcement等级定义在 tests/CtsEnforcement.h。它决定测试在 Android CTS/SkQP 中的执行策略CtsEnforcement::kNever与 CTS 无关永远不被强制普通单元测试通常用它CtsEnforcement::kNextRelease新测试如果稳定且准备在下个 Android 版本被强制应使用该占位值CtsEnforcement::kApiLevel_*对应 Android vendor API 级别如kApiLevel_T 33CtsEnforcement::kToBeDetermined不稳定或进行中的测试使用表示还未决定是否强制。eval(apiLevel)会在三种运行模式间切换跳过、带 workaround 运行、严格运行。对于不关心 CTS 的本地测试直接传CtsEnforcement::kNever即可。其他常用变体DEF_TEST_DISABLED(name, reporter)注册但默认不运行condition false适合临时禁用某个测试tests/Test.h。DEF_SERIAL_TEST(name, reporter)注册为kCPUSerial类型只串行执行适合对线程安全有特殊要求或共享全局状态的测试tests/Test.h。UNIX_ONLY_TEST在 Unix 构建下等价于DEF_TEST其他平台等价于DEF_TEST_DISABLEDtests/Test.h。把新测试加入构建编辑 gn/tests.gnitests/下的测试文件并不会被自动编译必须把新文件登记进 gn/tests.gni 的tests_sources列表。该列表从 gn/tests.gni 开始是一长串以$_tests/为前缀的绝对路径例如tests_sources [ $_tests/AAClipTest.cpp, $_tests/AndroidCodecTest.cpp, ... $_tests/MyNewTest.cpp, # 新增这一行 ... ]其中_tests在文件顶部通过get_path_info(../tests, abspath)解析为tests/目录的绝对路径见 gn/tests.gni因此新增行只需写$_tests/MyNewTest.cpp,同类列表还包含graphite_tests_sourcesgn/tests.gniGraphite 相关测试ganesh_tests_sourcesgn/tests.gniGanesh GPU 测试ganesh_gl_tests_sources/ganesh_metal_tests_sourcesgn/tests.gni特定后端的 GPU 测试pathops_tests_sourcesgn/tests.gnipathops 算法测试。如果只用到DEF_TEST加入tests_sources即可使用了DEF_GANESH_TEST/DEF_GRAPHITE_TEST的测试视具体宏可能还需确认对应后端列表GN 构建会自动按SK_GANESH/SK_GRAPHITE宏条件引入。若项目同时使用 Bazel 构建还需要把新文件加入 BUILD.bazel 中对应的skia_test目标源列表。用 dm 运行测试Skia 的单元测试统一由正确性测试工具dm执行其入口在 dm/DM.cpp测试运行逻辑见 dm/DMGpuTestProcs.cpp。先构建出dm可执行文件假设构建输出目录为out/Debug然后运行out/Debug/dm --src tests --match MyNewTest参数含义--src tests只运行 C 单元测试源dm还支持gm、skp、image等其他源类型--match MyNewTest按名字过滤只跑匹配的测试。常用过滤与运行参数dm的--match支持丰富的匹配语法详见 agents/skills/skia-gn-workflow/references/dm_usage.md语法效果substring匹配名字中包含该子串的测试~exclude排除匹配名字的测试^start匹配以该前缀开头的测试$end匹配以该后缀结尾的测试^exact$精确匹配其他实用参数--src tests --match MyNewTest单跑某个测试最常用--src tests --match ^Text跑所有名字以Text开头的测试--src tests --match ~Slow排除名字含Slow的测试--threads n或-j设置工作线程数默认等于 CPU 核数单测调试时可设为1--nogpu跳过所有 GPU 相关测试GPU 测试在无图形环境下会失败或跳过-v/--verbose输出更详细的日志-i dir/--resourcePath dir测试资源目录若测试加载资源失败先检查该路径是否指向正确的resources目录--config names指定要运行的配置如8888、gl、vkGPU 测试可用它限定后端。例如运行所有 Ganesh GPU 测试可以用out/Debug/dm --src tests --match Ganesh --config gl vkdm对不支持的 GPU 后端会自动跳过因此跨平台 CI 上常见做法是不指定--config让 dm 遍历所有编译进的后端。关于构建输出目录上面示例假设已经用 GN 生成了out/Debug构建目录并构建出dm。Skia 的 GN 工作流生成、配置、编译可参考 agents/skills/skia-gn-workflow/SKILL.mddm目标名即为dm构建命令形如bin/gn gen out/Debug ninja -C out/Debug dm完整示例一个带上下文的 GPU 测试把以上要素串起来一个同时覆盖 CPU 与 GPU 的完整示例文件tests/MyNewTest.cpp可以这样写#include tests/Test.h // 纯 CPU 测试验证一个简单不变量 DEF_TEST(MyNewTest, reporter) { REPORTER_ASSERT(reporter, 1 1 2); REPORTER_ASSERT(reporter, 2 * 2 4, multiplication works); } // 需要 Ganesh GPU 上下文的测试在所有编译进的 GPU 后端起效 DEF_GANESH_TEST_FOR_ALL_CONTEXTS(MyNewGaneshTest, reporter, context_info, CtsEnforcement::kNever) { // context_info 是 const sk_gpu_test::ContextInfo可用于获取 GPU 上下文 REPORTER_ASSERT(reporter, context_info.directContext() ! nullptr); } // 需要 Graphite 上下文的测试 DEF_GRAPHITE_TEST_FOR_ALL_CONTEXTS(MyNewGraphiteTest, reporter, graphite_ctx, CtsEnforcement::kNever) { REPORTER_ASSERT(reporter, graphite_ctx ! nullptr); }对应地把该文件加入 gn/tests.gni 的tests_sources重新生成构建文件并编译# 编辑 gn/tests.gni追加 $_tests/MyNewTest.cpp bin/gn gen out/Debug ninja -C out/Debug dm out/Debug/dm --src tests --match MyNew运行后你会看到MyNewTest、MyNewGaneshTest、MyNewGraphiteTest三个测试的结果如果当前构建没有编译 GPU 后端后两个会被自动跳过。小结单元测试放在 tests/ 目录用 tests/Test.h 中的宏声明CPU 测试用DEF_TESTGPU 测试按后端选DEF_GANESH_TEST/DEF_GRAPHITE_TEST及其_FOR_*_CONTEXT变体。断言用REPORTER_ASSERT/ERRORF失败不会中断测试必要时手动return。新测试文件必须登记进 gn/tests.gni 的tests_sources才会被编译。用out/Debug/dm --src tests --match pattern运行与过滤测试--match支持子串、排除、前缀、后缀、精确匹配五种模式。关心 CTS 强制的测试需要正确选择CtsEnforcement等级参考 tests/CtsEnforcement.h。赞分享图形学【免费下载链接】skiaSkia is a complete 2D graphic library for drawing Text, Geometries, and Images. See documentation for contribution instructions.项目地址https://gitcode.com/gh_mirrors/ski/skia点击查看免费下载相关推荐ParlAI 测试实战指南从 pytest 运行到 TorchGeneratorAgent 单元测试编写ParlAI 测试实战指南从 pytest 运行到 TorchGeneratorAgent 单元测试编写 ParlAI 是面向开放对话数据集训练与评估 AINLP人工智能深度学习Lynx 单元测试实战指南从 C/Android/iOS 测试编写到 RTF 自动化运行Lynx 单元测试实战指南从 C/Android/iOS 测试编写到 RTF 自动化运行 Lynx 是面向跨端场景的高性能渲染引擎其测试体系贯穿 C跨平台移动开发前端桌面应用Skia 测试工具集成 ANGLE从 GN 构建配置到 --config 命令行的完整指南Skia 测试工具集成 ANGLE从 GN 构建配置到 config 命令行的完整指南 本文档对应 Skia 仓库中的 site/docs/user/spec图形学创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表