SuperAPI AI Directory

oh-graphic-pixel-tests-generator

Testing & quality

View repository

Updated 10-16 07:28

About this skill

Table of Contents
  1. Framework Overview
  2. Test Process Details
  3. Test Macro System
  4. Complete API Reference
  5. Parameter Configuration Instructions
  6. Test Template Library
  7. Drawing API Reference
  8. Best Practices

Framework Overview

Core Principles

The OH-Graphics test framework is based on pixel-level comparison to verify the correctness of graphics rendering. Tests run in a real Rosen rendering environment, and verification is achieved by creating scene graph nodes, performing rendering, and saving screenshots.

Architecture Flow

┌─────────────────────────────────────────────────────────────────┐
│  测试用例 (RSGraphicTest)                                        │
│    - 创建测试节点                                                │
│    - 设置节点属性                                                │
│    - 录制绘制命令                                                │
└─────────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────────┐
│  RS客户端 (render_service_client)                                │
│    - RSCanvasNode / RSSurfaceNode / RSDisplayNode               │
└─────────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────────┐
│  RSTransactionProxy::FlushImplicitTransaction()                 │
│    - 事务提交,触发IPC通信                                       │
└─────────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────────┐
│  RS服务端 (render_service)                                       │
│    - 接收事务,更新服务端节点树                                  │
│    - 渲染管线处理                                               │
└─────────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────────┐
│  2D Graphics (Drawing引擎)                                       │
│    - Canvas 绘制命令执行                                         │
│    - GPU/Backend 渲染                                           │
└─────────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────────┐
│  DisplayManager::GetScreenshot()                                │
│    - 获取渲染结果截图                                           │
└─────────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────────┐
│  PNG 保存                                                        │
│    /data/local/graphic_test/<分类>/<测试类>_<测试名>.png         │
└─────────────────────────────────────────────────────────────────┘

Test Modes

Mode Macro Screenshot Method Purpose
AUTOMATIC GRAPHIC_TEST Automatic screenshot Standard functional testing, automatically takes a screenshot when the test ends
MANUAL GRAPHIC_N_TEST Manual screenshot When the screenshot timing needs to be controlled manually
DYNAMIC GRAPHIC_D_TEST Dynamic screenshot Dynamic testing that supports recording and playback

Directory Structure

graphic_test/
├── graphic_test_framework/      # 测试框架
│   ├── include/
│   │   ├── rs_graphic_test.h          # 测试基类
│   │   ├── rs_graphic_test_ext.h      # 测试宏定义
│   │   ├── rs_graphic_test_img.h      # 图片辅助函数
│   │   ├── rs_graphic_test_utils.h    # 工具函数
│   │   └── rs_graphic_test_director.h # 测试控制器
│   └── src/
│       └── rs_graphic_test.cpp        # 框架实现
│
├── test/                          # 测试用例
│   ├── open_capability/            # 开放能力测试
│   ├── rs_perform_feature/         # 性能特性测试
│   ├── drawing_engine/             # 绘制引擎测试
│   └── test_template/              # 测试模板
│
└── BUILD.gn                       # 构建配置

Test Process Details

Complete Lifecycle

┌─────────────────────────────────────────────────────────────────┐
│  SetUpTestCase() [静态,测试套件开始时执行一次]                  │
│                                                                  │
│  • imageWriteId_ = 0  // 重置截图ID计数器                        │
└─────────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────────┐
│  SetUp() [每个测试用例执行前]                                    │
│                                                                  │
│  1. ShouldRunCurrentTest()                                      │
│     - 根据 --testType 和 --testMode 参数过滤测试                │
│     - 不满足条件则跳过 (GTEST_SKIP)                              │
│                                                                  │
│  2. 创建测试 Surface                                             │
│     RSSurfaceNode::Create(config, APP_WINDOW_NODE)              │
│     → SetBounds({0, 0, width, height})                          │
│     → SetFrame({0, 0, width, height})                           │
│     → SetBackgroundColor(0xffffffff)                            │
│                                                                  │
│  3. BeforeEach() [用户重写]                                      │
│     - SetScreenSize(width, height)                              │
│     - SetSurfaceBounds(bounds)                                  │
│     - 加载测试资源等                                             │
│                                                                  │
│  4. 多测试模式处理 (isMultiple=true)                             │
│     - 计算网格布局: GetScreenCapacity()                         │
│     - 设置虚拟屏幕尺寸: capacity.x * size.x                     │
│     - 计算当前测试位置: GetPos(testId)                          │
│     - 设置 Surface 位置到对应网格                                │
└─────────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────────┐
│  测试用例函数体                                                  │
│                                                                  │
│  1. 创建节点                                                     │
│     auto node = RSCanvasNode::Create();                         │
│                                                                  │
│  2. 设置节点属性                                                 │
│     node->SetBounds({x, y, w, h});                              │
│     node->SetFrame({x, y, w, h});                               │
│     node->SetBackgroundColor(color);                            │
│                                                                  │
│  3. 添加到场景图                                                 │
│     GetRootNode()->AddChild(node);                              │
│                                                                  │
│  4. 注册节点 [必须调用]                                          │
│     RegisterNode(node);  // 保持节点引用,防止被释放             │
│                                                                  │
│  5. 录制绘制命令                                                 │
│     auto drawing = node->BeginRecording(w, h);                  │
│     drawing->DrawXXX(...);                                      │
│     node->FinishRecording();                                    │
│                                                                  │
│  6. 刷新事务                                                     │
│     RSTransactionProxy::GetInstance()->FlushImplicitTransaction();│
└─────────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────────┐
│  TearDown() [每个测试用例执行后]                                 │
│                                                                  │
│  1. WaitOtherTest()                                             │
│     - 多测试模式: 延迟截图直到所有测试完成                        │
│     - 单测试模式: 直接继续                                       │
│                                                                  │
│  2. StartUIAnimation()                                          │
│     - 启动并执行所有动画                                         │
│                                                                  │
│  3. FlushMessageAndWait(testCaseWaitTime)                       │
│     - 等待渲染完成 (默认 1000ms)                                 │
│                                                                  │
│  4. WaitTimeout(normalWaitTime)                                 │
│     - 额外等待 (默认 10ms)                                       │
│                                                                  │
│  5. 根据测试模式处理                                             │
│     - MANUAL: WaitTimeout(manualTestWaitTime) [手动模式等待]    │
│     - AUTOMATIC/DYNAMIC: TestCaseCapture() [自动截图]           │
│                                                                  │
│  6. AfterEach() [用户重写]                                       │
│     - 用户自定义清理                                             │
│                                                                  │
│  7. 资源清理                                                     │
│     - ResetTestSurface()                                        │
│     - nodes_.clear()                                            │
│     - SendProfilerCommand("rssubtree_clear")                    │
└─────────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────────┐
│  TearDownTestCase() [静态,测试套件结束时执行一次]               │
│  • 无操作                                                        │
└─────────────────────────────────────────────────────────────────┘

Key Step Descriptions

1. Importance of RegisterNode()

// RegisterNode() 做了两件事:
// 1. 将节点添加到 nodes_ 向量,保持 shared_ptr 引用
// 2. 调用 GetRootNode()->RegisterNode(node) 注册到根节点

void RegisterNode(std::shared_ptr<RSNode> node) {
    nodes_.push_back(node);                              // 保持引用
    GetRootNode()->RegisterNode(node);                   // 注册到树
}

Consequences of not calling RegisterNode(): The node may be released prematurely, causing rendering errors or crashes.

2. Multi-test Grid Layout

When using the GRAPHIC_TESTS macro (isMultiple=true):

// 计算网格容量(尽量接近正方形)
// 例如:12个测试 → 4x3 网格
UIPoint GetScreenCapacity(int testCnt) {
    int cl = 1;  // 列数
    int num = 1;
    while (num < testCnt) {
        cl++;
        num = cl * cl;  // cl² >= testCnt
    }
    int rl = ceil(testCnt / cl);  // 行数
    return {cl, rl};
}

// 计算测试位置
UIPoint GetPos(int id, int cl) {
    int x = id % cl;  // 列索引
    int y = id / cl;  // 行索引
    return {x, y};
}

The Surface of each test will be placed at the corresponding grid position on the virtual screen.

3. Screenshot Path Rules

// 路径生成规则
std::string GetImageSavePath(const std::string filePath) {
    // filePath: /path/to/graphic_test/test/xxx/my_test.cpp
    // 提取: /test/xxx/
    // 结果: /data/local/graphic_test/test/xxx/

    // 最终文件名格式:
    // /data/local/graphic_test/test/xxx/<测试类>_<测试名>.png
}

4. Transaction Flush Timing

// FlushImplicitTransaction() 触发:
// 1. 客户端命令打包
// 2. IPC 发送到服务端
// 3. 服务端更新节点树
// 4. 触发渲染管线

// 必须在以下操作后调用:
// - 节点属性修改后
// - 绘制命令录制完成后
// - 动画启动后

Test Macro System

Macro Definition Expansion

// GRAPHIC_TEST 宏展开后的实际代码
#define GRAPHIC_TEST(test_case_name, test_type, test_name) \
    bool GTEST_TEST_UNIQUE_ID_##test_case_name##test_name##__LINE__ = \
        OHOS::Rosen::TestDefManager::Instance().Regist( \
        #test_case_name, #test_name, test_type, \
        RSGraphicTestMode::AUTOMATIC, __FILE__, false); \
    TEST_F(test_case_name, test_name)

Test Macro Types

Macro Test Mode isMultiple Purpose
GRAPHIC_TEST(TestCase, Type, Name) AUTOMATIC false Standard testing, automatic screenshot
GRAPHIC_N_TEST(TestCase, Type, Name) MANUAL false Manual mode, requires manual screenshot
GRAPHIC_D_TEST(TestCase, Type, Name) DYNAMIC false Dynamic playback testing
GRAPHIC_TESTS(TestCase, Type, Name) AUTOMATIC true Multi-test grid layout

Parameter Descriptions

GRAPHIC_TEST(测试类名, 测试类型, 测试函数名)

// 测试类名: 继承 RSGraphicTest 的类名
// 测试类型: RSGraphicTestType 枚举值
// 测试函数名: 测试用例的函数名

Test Type (RSGraphicTestType)

enum RSGraphicTestType {
    FRAMEWORK_TEST,           // 框架测试
    ANIMATION_TEST,           // 动画测试
    CONTENT_DISPLAY_TEST,     // 内容显示测试
    SCREEN_MANAGER_TEST,      // 屏幕管理测试
    HARDWARE_PRESENT_TEST,    // 硬件呈现测试
    DRAWING_TEST,             // 绘制测试
    LTPO_TEST,                // LTPO测试
    TEXT_TEST,                // 文本测试
    PIXMAP_TEST,              // PixelMap测试
    SYMBOL_TEST,              // 符号测试
    EFFECT_TEST,              // 效果测试
    HYBRID_RENDER_TEST,       // 混合渲染测试
    GPU_COMPOSER_TEST,        // GPU合成测试
};

TestDefInfo Structure

struct TestDefInfo {
    std::string testCaseName;    // 测试类名
    std::string testName;        // 测试函数名
    RSGraphicTestType testType;  // 测试类型
    RSGraphicTestMode testMode;  // 测试模式
    std::string filePath;        // 源文件路径
    bool isMultiple;             // 是否多测试模式
    uint8_t testId;              // 测试ID (多测试模式)
};

Test Registration Process

// 1. 宏调用时注册测试信息
TestDefManager::Instance().Regist(name, type, mode, file, isMultiple)

// 2. SetUp() 中检查是否运行当前测试
ShouldRunCurrentTest() {
    auto extInfo = TestDefManager::GetTestInfo(caseName, testName);
    if (filterTestTypes.count(extInfo->testType) == 0) return false;
    if (runTestMode != ALL && extInfo->testMode != runTestMode) return false;
    return true;
}

Parameter Configuration Instructions

Command-line Parameters

# 运行特定类型的测试
./RSGraphicTest --testType=CONTENT_DISPLAY_TEST

# 运行多种类型
./RSGraphicTest --testType=CONTENT_DISPLAY_TEST --testType=ANIMATION_TEST

# 运行特定模式
./RSGraphicTest --testMode=AUTOMATIC    # 自动测试
./RSGraphicTest --testMode=MANUAL       # 手动测试
./RSGraphicTest --testMode=DYNAMIC      # 动态测试

# 设置等待时间
./RSGraphicTest --testCaseWaitTime=2000      # 测试用例等待时间(ms)
./RSGraphicTest --normalWaitTime=100         # 正常等待时间(ms)
./RSGraphicTest --surfaceCaptureWaitTime=1500 # 截图等待时间(ms)
./RSGraphicTest --manualTestWaitTime=3000    # 手动测试等待时间(ms)

# 设置VSync速率
./RSGraphicTest --vsyncRate=2

# 跳过截图
./RSGraphicTest --skipCapture

# 使用gtest过滤器
./RSGraphicTest --gtest_filter=MyTest.*
./RSGraphicTest --gtest_filter=MyTest.MySpecificTest

RSParameterParse Configuration Items

class RSParameterParse {
    std::string imageSavePath = "/data/local/graphic_test/";
    int testCaseWaitTime = 1000;        // 测试用例等待时间(ms)
    int normalWaitTime = 10;             // 正常等待时间(ms)
    int surfaceCaptureWaitTime = 1000;  // 截图等待时间(ms)
    int manualTestWaitTime = 1500;      // 手动测试等待时间(ms)
    std::unordered_set<RSGraphicTestType> filterTestTypes = {};
    RSGraphicTestMode runTestMode = RSGraphicTestMode::ALL;
    int32_t vsyncRate = 1;
    bool skipCapture_ = false;
};

Test Filtering Logic

// ShouldRunCurrentTest() 中的过滤逻辑
bool ShouldRunCurrentTest() {
    auto extInfo = TestDefManager::GetTestInfo(caseName, testName);

    // 类型过滤
    if (!filterTestTypes.empty() &&
        filterTestTypes.count(extInfo->testType) == 0) {
        return false;
    }

    // 模式过滤
    if (runTestMode != ALL && extInfo->testMode != runTestMode) {
        return false;
    }

    return true;
}

Test Template Library

Basic Test Template

#include "rs_graphic_test.h"

using namespace testing;
using namespace testing::ext;

namespace OHOS::Rosen {

class MyFeatureTest : public RSGraphicTest {
public:
    void BeforeEach() override
    {
        // 设置屏幕尺寸
        SetScreenSize(1200, 2000);
        // 设置Surface边界
        SetSurfaceBounds({0, 0, 1200, 2000});
    }
};

/*
 * @tc.name: BasicFeatureTest
 * @tc.desc: 测试基本功能
 * @tc.type: FUNC
 * @tc.require: issue12345
 */
GRAPHIC_TEST(MyFeatureTest, CONTENT_DISPLAY_TEST, BasicFeatureTest)
{
    // 1. 创建节点
    auto canvasNode = RSCanvasNode::Create();

    // 2. 设置属性
    canvasNode->SetBounds({100, 100, 500, 500});
    canvasNode->SetFrame({100, 100, 500, 500});
    canvasNode->SetBackgroundColor(0xFFFFFFFF);

    // 3. 添加到场景
    GetRootNode()->AddChild(canvasNode);

    // 4. 注册节点 (必须!)
    RegisterNode(canvasNode);

    // 5. 录制绘制
    auto drawing = canvasNode->BeginRecording(500, 500);
    drawing->DrawRect({50, 50, 450, 450}, Drawing::Paint());
    canvasNode->FinishRecording();

    // 6. 刷新事务
    RSTransactionProxy::GetInstance()->FlushImplicitTransaction();
}

} // namespace OHOS::Rosen

Grid Layout Template

/*
 * @tc.name: GridLayoutTest
 * @tc.desc: 多项测试网格布局
 * @tc.type: FUNC
 */
GRAPHIC_TEST(MyFeatureTest, CONTENT_DISPLAY_TEST, GridLayoutTest)
{
    // 测试项配置
    int32_t itemCount = 12;
    int32_t nodeWidth = 200;
    int32_t nodeHeight = 200;
    int32_t gap = 50;
    int32_t column = 4;

    // 生成测试颜色数组
    uint32_t colors[] = {
        0xFFFF0000, 0xFF00FF00, 0xFF0000FF, 0xFFFFFF00,
        0xFFFF00FF, 0xFF00FFFF, 0xFFFF8000, 0xFF808080,
        0xFF800000, 0xFF008000, 0xFF000080, 0xFF808000
    };

    for (int32_t i = 0; i < itemCount; i++) {
        auto node = RSCanvasNode::Create();

        // 计算位置
        int32_t row = i / column;
        int32_t col = i % column;
        int32_t x = gap + (nodeWidth + gap) * col;
        int32_t y = gap + (nodeHeight + gap) * row;

        // 设置属性
        node->SetBounds({x, y, nodeWidth, nodeHeight});
        node->SetFrame({x, y, nodeWidth, nodeHeight});
        node->SetBackgroundColor(colors[i]);

        GetRootNode()->AddChild(node);
        RegisterNode(node);
    }

    RSTransactionProxy::GetInstance()->FlushImplicitTransaction();
}

Animation Test Template

Method 1: RSNode::Animate (recommended - single animation)

/*
 * @tc.name: AnimationClosureTest
 * @tc.desc: 测试动画闭包效果
 * @tc.type: FUNC
 */
GRAPHIC_TEST(MyFeatureTest, ANIMATION_TEST, AnimationClosureTest)
{
    auto node = RSCanvasNode::Create();
    node->SetBounds({100, 100, 400, 400});
    node->SetFrame({100, 100, 400, 400});
    node->SetBackgroundColor(0xFF0000FF);

    GetRootNode()->AddChild(node);
    RegisterNode(node);

    // 使用RSNode::Animate创建动画闭包
    RSAnimationTimingProtocol protocol;
    protocol.SetDuration(300);  // 300ms - 测试用建议值,避免过长
    protocol.SetRepeat(1);      // 执行1次

    auto timingCurve = RSAnimationTimingCurve::EASE_IN_OUT;

    RSNode::Animate(protocol, timingCurve,
        [&]() {
            // 动画闭包: 所有属性修改会被动画化
            node->SetTranslate({50, 50});      // 小幅度平移
            node->SetScale(1.2f);              // 适度缩放
            node->SetAlpha(0.8f);              // 透明度
            node->SetRotation(45.0f);          // 旋转角度
        },
        []() {
            // 动画完成回调 (可选)
            std::cout << "Animation finished" << std::endl;
        }
    );

    RSTransactionProxy::GetInstance()->FlushImplicitTransaction();
}

Method 2: OpenImplicitAnimation/CloseImplicitAnimation

/*
 * @tc.name: ImplicitAnimationTest
 * @tc.desc: 测试显式动画闭包
 * @tc.type: FUNC
 */
GRAPHIC_TEST(MyFeatureTest, ANIMATION_TEST, ImplicitAnimationTest)
{
    auto node = RSCanvasNode::Create();
    node->SetBounds({100, 100, 400, 400});
    node->SetFrame({100, 100, 400, 400});
    node->SetBackgroundColor(0xFF00FF00);

    GetRootNode()->AddChild(node);
    RegisterNode(node);

    // 打开动画闭包
    RSAnimationTimingProtocol protocol;
    protocol.SetDuration(250);  // 250ms
    protocol.SetRepeat(1);

    RSNode::OpenImplicitAnimation(protocol, RSAnimationTimingCurve::EASE_IN);

    // 闭包内的所有属性修改都会被动画化
    node->SetTranslate({30, 30});
    node->SetRotation(90.0f);

    // 关闭动画闭包,返回创建的动画列表
    auto animations = RSNode::CloseImplicitAnimation();

    RSTransactionProxy::GetInstance()->FlushImplicitTransaction();
}

Multiple Animation Node Test Template

/*
 * @tc.name: MultipleAnimationTest
 * @tc.desc: 测试多个节点动画
 * @tc.type: FUNC
 */
GRAPHIC_TEST(MyFeatureTest, ANIMATION_TEST, MultipleAnimationTest)
{
    const int nodeCount = 4;
    const int nodeSize = 150;
    const int gap = 50;

    for (int i = 0; i < nodeCount; i++) {
        auto node = RSCanvasNode::Create();
        int x = 100 + (nodeSize + gap) * i;
        int y = 200;

        node->SetBounds({x, y, nodeSize, nodeSize});
        node->SetFrame({x, y, nodeSize, nodeSize});
        node->SetBackgroundColor(0xFF0000FF + (i * 0x00110000));

        GetRootNode()->AddChild(node);
        RegisterNode(node);

        // 每个节点独立的动画
        RSAnimationTimingProtocol protocol;
        protocol.SetDuration(200 + i * 50);  // 200ms, 250ms, 300ms, 350ms
        protocol.SetRepeat(1);

        RSNode::Animate(protocol, RSAnimationTimingCurve::EASE_OUT,
            [node, i]() {
                node->SetTranslate({0.0f, -50.0f - i * 10.0f});  // 不同偏移量
                node->SetScale(1.1f + i * 0.05f);
            }
        );
    }

    RSTransactionProxy::GetInstance()->FlushImplicitTransaction();
}

PixelMap Test Template

/*
 * @tc.name: PixelMapDisplayTest
 * @tc.desc: 测试PixelMap显示
 * @tc.type: FUNC
 */
GRAPHIC_TEST(MyFeatureTest, PIXMAP_TEST, PixelMapDisplayTest)
{
    // 加载图片
    auto pixelMap = DecodePixelMap(
        "/data/local/tmp/test.jpg",
        Media::AllocatorType::SHARE_MEM_ALLOC
    );

    if (!pixelMap) {
        std::cout << "Failed to load pixelmap" << std::endl;
        return;
    }

    // 使用图片节点
    auto node = SetUpNodeBgImage("/data/local/tmp/test.jpg", {100, 100, 500, 500});
    GetRootNode()->AddChild(node);
    RegisterNode(node);

    // 或在Canvas中绘制
    auto canvasNode = RSCanvasNode::Create();
    canvasNode->SetBounds({100, 100, 500, 500});
    canvasNode->SetFrame({100, 100, 500, 500});
    GetRootNode()->AddChild(canvasNode);
    RegisterNode(canvasNode);

    auto drawing = canvasNode->BeginRecording(500, 500);
    auto extendDrawing = static_cast<ExtendRecordingCanvas*>(drawing);

    Drawing::SamplingOptions sampling;
    extendDrawing->DrawPixelMapWithParm(pixelMap, {}, sampling);

    canvasNode->FinishRecording();
    RSTransactionProxy::GetInstance()->FlushImplicitTransaction();
}

Transform Test Template

/*
 * @tc.name: TransformTest
 * @tc.desc: 测试变换操作
 * @tc.type: FUNC
 */
GRAPHIC_TEST(MyFeatureTest, DRAWING_TEST, TransformTest)
{
    float scales[] = {0.5f, 1.0f, 1.5f, 2.0f};
    float angles[] = {0.0f, 45.0f, 90.0f, 135.0f};
    int32_t scaleCount = sizeof(scales) / sizeof(scales[0]);
    int32_t angleCount = sizeof(angles) / sizeof(angles[0]);
    int32_t nodeSize = 200;
    int32_t gap = 50;

    for (int32_t i = 0; i < scaleCount; i++) {
        for (int32_t j = 0; j < angleCount; j++) {
            auto node = RSCanvasNode::Create();
            int32_t x = gap + (nodeSize + gap) * j;
            int32_t y = gap + (nodeSize + gap) * i;

            node->SetBounds({x, y, nodeSize, nodeSize});
            node->SetFrame({x, y, nodeSize, nodeSize});
            node->SetBackgroundColor(0xFFFFFFFF);
            GetRootNode()->AddChild(node);
            RegisterNode(node);

            auto drawing = node->BeginRecording(nodeSize, nodeSize);

            // 应用变换
            drawing->Translate(nodeSize / 2, nodeSize / 2);
            drawing->Rotate(angles[j]);
            drawing->Scale(scales[i], scales[i]);
            drawing->Translate(-nodeSize / 2, -nodeSize / 2);

            // 绘制矩形
            Drawing::Paint paint;
            paintSetColor(paint, 0xFFFF0000);
            drawing->DrawRect({50, 50, 150, 150}, paint);

            node->FinishRecording();
        }
    }

    RSTransactionProxy::GetInstance()->FlushImplicitTransaction();
}

Boundary Value Test Template

/*
 * @tc.name: BoundaryValueTest
 * @tc.desc: 测试边界值
 * @tc.type: FUNC
 */
GRAPHIC_TEST(MyFeatureTest, CONTENT_DISPLAY_TEST, BoundaryValueTest)
{
    // 测试边界值:负数、零、大数值
    float values[] = {
        -100.0f, -10.0f, -1.0f, 0.0f,  // 负数和零
        0.1f, 1.0f, 10.0f, 100.0f,     // 正常值
        1000.0f, 10000.0f              // 大数值
    };
    int32_t count = sizeof(values) / sizeof(values[0]);
    int32_t nodeWidth = 300;
    int32_t nodeHeight = 100;
    int32_t gap = 30;

    for (int32_t i = 0; i < count; i++) {
        auto node = RSCanvasNode::Create();
        int32_t y = gap + (nodeHeight + gap) * i;

        node->SetBounds({50, y, nodeWidth, nodeHeight});
        node->SetFrame({50, y, nodeWidth, nodeHeight});
        node->SetBackgroundColor(0xFFFFFFFF);
        GetRootNode()->AddChild(node);
        RegisterNode(node);

        auto drawing = node->BeginRecording(nodeWidth, nodeHeight);

        // 使用边界值进行测试
        char text[64];
        snprintf(text, sizeof(text), "Value: %.1f", values[i]);

        drawing->DrawTextBlob(..., text); // 绘制文本
        // 或绘制其他图形表示

        node->FinishRecording();
    }

    RSTransactionProxy::GetInstance()->FlushImplicitTransaction();
}

Manual Screenshot Test Template

class MyManualTest : public RSGraphicTest {
public:
    void BeforeEach() override
    {
        SetScreenSize(1200, 2000);
        SetSurfaceBounds({0, 0, 1200, 2000});
    }

    void TestCaseCapture()
    {
        auto pixelMap = DisplayManager::GetInstance().GetScreenshot(
            DisplayManager::GetInstance().GetDefaultDisplayId()
        );
        if (pixelMap) {
            const auto* testInfo = ::testing::UnitTest::GetInstance()->current_test_info();
            std::string fileName = "/data/local/graphic_test/my_test/";
            fileName += std::string(testInfo->test_case_name()) + "_" +
                       std::string(testInfo->name()) + ".png";

            // 创建目录
            namespace fs = std::filesystem;
            fs::create_directories(fileName.substr(0, fileName.find_last_of('/')));

            if (!WriteToPngWithPixelMap(fileName, *pixelMap)) {
                std::cout << "[FAILED] " << fileName << std::endl;
            } else {
                std::cout << "png write to " << fileName << std::endl;
            }
        }
    }
};

/*
 * @tc.name: ManualCaptureTest
 * @tc.desc: 手动控制截图时机
 * @tc.type: FUNC
 */
GRAPHIC_N_TEST(MyManualTest, CONTENT_DISPLAY_TEST, ManualCaptureTest)
{
    auto node = RSCanvasNode::Create();
    node->SetBounds({100, 100, 500, 500});
    node->SetFrame({100, 100, 500, 500});
    node->SetBackgroundColor(0xFF0000FF);
    GetRootNode()->AddChild(node);
    RegisterNode(node);

    auto drawing = node->BeginRecording(500, 500);
    drawing->DrawRect({100, 100, 400, 400}, Drawing::Paint());
    node->FinishRecording();

    RSTransactionProxy::GetInstance()->FlushImplicitTransaction();

    // 等待渲染完成
    usleep(1000000); // 1秒

    // 手动截图
    TestCaseCapture();
}


Best Practices

Rules That Must Be Followed

  1. Always call RegisterNode()

    GetRootNode()->AddChild(node);
    RegisterNode(node);  // 必须!
    
  2. Flush transaction

    RSTransactionProxy::GetInstance()->FlushImplicitTransaction();
    
  3. Use BeforeEach to set up the environment

    void BeforeEach() override {
        SetScreenSize(1200, 2000);
        SetSurfaceBounds({0, 0, 1200, 2000});
    }
    

Node Property Setting Order

// 推荐顺序
auto node = RSCanvasNode::Create();
node->SetBounds({x, y, w, h});          // 1. 先设置边界
node->SetFrame({x, y, w, h});           // 2. 再设置帧
node->SetBackgroundColor(color);        // 3. 设置背景
GetRootNode()->AddChild(node);          // 4. 添加到场景
RegisterNode(node);                     // 5. 注册节点

Color Format

// ARGB 格式: 0xAARRGGBB
uint32_t color = 0xFFFF0000;  // 不透明红色

// 常用颜色
0xFF000000  // 黑色
0xFFFFFFFF  // 白色
0xFFFF0000  // 红色
0xFF00FF00  // 绿色
0xFF0000FF  // 蓝色

Grid Layout Calculation

// 计算网格位置
int32_t row = i / column;
int32_t col = i % column;
int32_t x = gap + (nodeWidth + gap) * col;
int32_t y = gap + (nodeHeight + gap) * row;

Resource Paths

// 图片路径
std::string imagePath = "/data/local/tmp/test.jpg";

// 截图保存路径
std::string savePath = "/data/local/graphic_test/<分类>/<测试类>_<测试名>.png";

Common Errors

Error Cause Solution
Node not displayed RegisterNode() not called Add RegisterNode(node)
Blank screenshot Transaction not flushed Add FlushImplicitTransaction()
Incorrect node position Bounds/Frame settings inconsistent Ensure Bounds and Frame are consistent
Memory leak nodes_ not cleaned up The framework cleans up automatically; check for extra references
Animation not executed StartUIAnimation not called The framework calls it automatically in TearDown, or call it manually
Animation test timeout duration/repeat set too large Use 200-500ms, repeat=1

Animation Testing Best Practices

// 1. 使用合理的动画时长 (200-500ms)
RSAnimationTimingProtocol protocol;
protocol.SetDuration(300);  // ✅ 推荐: 300ms
protocol.SetDuration(5000); // ❌ 避免: 过长导致测试超时

// 2. 避免无限循环
protocol.SetRepeat(1);  // ✅ 推荐: 执行1次
protocol.SetRepeat(0);  // ❌ 避免: 无限循环

// 3. 使用小幅度变化
node->SetTranslate({50, 50});    // ✅ 推荐: 小幅度
node->SetTranslate({1000, 1000}); // ❌ 避免: 过大偏移

// 4. 首选RSNode::Animate闭包
RSNode::Animate(protocol, curve, [&]() {
    node->SetTranslate({30, 30});
});  // ✅ 推荐: 简洁清晰

// vs 显式闭包 (需要手动匹配Open/Close)
RSNode::OpenImplicitAnimation(protocol, curve);
node->SetTranslate({30, 30});
RSNode::CloseImplicitAnimation();  // ⚠️ 可选: 更灵活但容易遗漏

Debugging Tips

// 1. 查看节点树
GetRootNode()->DumpTree();

// 2. 输出日志
LOGI("Test message: %s", "debug info");
std::cout << "Debug: " << value << std::endl;

// 3. 单独运行测试
./RSGraphicTest --gtest_filter=MyTest.MySpecificTest

// 4. 跳过截图快速调试
./RSGraphicTest --skipCapture

Performance Recommendations

  1. Reuse PixelMap: Avoid repeatedly decoding the same image
  2. Control the number of nodes: Recommend no more than 100 nodes per test
  3. Set the wait time appropriately: Adjust waitTime according to rendering complexity
  4. Use grid layout: Use GRAPHIC_TESTS for multiple test items

Reference Examples

Existing test case locations

  • graphic_test/test/open_capability/pixmap/pixelmap_display_test.cpp - PixelMap test
  • graphic_test/test/rs_perform_feature/dirty_region/boundary_test.cpp - Boundary test
  • graphic_test/test/rs_perform_feature/dirty_region/merge_dirty_rect_test.cpp - Dirty region merge test
  • graphic_test/test/rs_perform_feature/gpu_composer/gpu_composer_test.cpp - GPU composition test
  • graphic_test/test/drawing_engine/drawing/demo.cpp - Drawing engine example

Test Framework API Reference

RSGraphicTest base class

Screen and Surface management
// 获取屏幕尺寸
Vector2f GetScreenSize() const;
// 返回: {width, height}

// 设置屏幕尺寸
void SetScreenSize(float width, float height);

// 设置测试Surface边界
void SetSurfaceBounds(const Vector4f& bounds);
// bounds: {left, top, right, bottom} 或 {x, y, width, height}

// 设置屏幕Surface边界 (多测试模式)
void SetScreenSurfaceBounds(const Vector4f& bounds);

// 设置Surface背景色
void SetSurfaceColor(const RSColor& color);
// color: 0xAARRGGBB 格式
Node management
// 获取根节点
std::shared_ptr<RSGraphicRootNode> GetRootNode() const;

// 注册节点 (必须调用)
void RegisterNode(std::shared_ptr<RSNode> node);
// 作用: 保持节点引用,防止被提前释放

// 从文件加载渲染节点树
void AddFileRenderNodeTreeToNode(std::shared_ptr<RSNode> node,
                                 const std::string& filePath);
Animation and synchronization
// 开始执行UI动画 (在TearDown中自动调用)
void StartUIAnimation();

// 等待指定毫秒数
void WaitTimeout(int ms);

Animation closure API (RSNode static methods):

// 方式1: RSNode::Animate - 单次动画闭包
static std::vector<std::shared_ptr<RSAnimation>> Animate(
    const RSAnimationTimingProtocol& timingProtocol,  // 时序协议
    const RSAnimationTimingCurve& timingCurve,         // 缓动曲线
    const PropertyCallback& callback,                  // 属性修改闭包
    const std::function<void()>& finishCallback = nullptr,   // 完成回调
    const std::function<void()>& repeatCallback = nullptr    // 重复回调
);

// 方式2: OpenImplicitAnimation/CloseImplicitAnimation - 显式动画闭包
static void OpenImplicitAnimation(
    const RSAnimationTimingProtocol& timingProtocol,
    const RSAnimationTimingCurve& timingCurve,
    const std::function<void()>& finishCallback = nullptr
);

static std::vector<std::shared_ptr<RSAnimation>> CloseImplicitAnimation();

Animation parameter recommendations (avoid excessively long execution time):

Parameter Recommended value Description
duration 200-500ms Test animations should not be too long
repeat 1 or 0 Avoid infinite loops
delay 0-100ms Reduce waiting time
translate/scale Small change Avoid excessive coordinate offset

RSAnimationTimingProtocol:

RSAnimationTimingProtocol protocol;
protocol.SetDuration(500);          // 持续时间(毫秒)
protocol.SetRepeat(1);              // 重复次数(0=无限, 1=一次)
protocol.SetDelay(0);               // 延迟时间
protocol.SetSpeed(1.0f);            // 播放速度
protocol.SetDirection(Direction::NORMAL);  // 播放方向

RSAnimationTimingCurve presets:

RSAnimationTimingCurve::EASE        // 缓动
RSAnimationTimingCurve::EASE_IN     // 缓入
RSAnimationTimingCurve::EASE_OUT    // 缓出
RSAnimationTimingCurve::EASE_IN_OUT // 缓入缓出
RSAnimationTimingCurve::LINEAR      // 线性
RSAnimationTimingCurve::SPRING      // 弹簧
Lifecycle hooks
// 用户重写: 每个测试用例执行前
virtual void BeforeEach() {}

// 用户重写: 每个测试用例执行后
virtual void AfterEach() {}
Playback function
// 准备回放
void PlaybackRecover(const std::string& filePath, float pauseTimeStamp);
// filePath: 录制文件路径
// pauseTimeStamp: 暂停时间戳(秒)

// 停止回放
void PlaybackStop();

RSGraphicTestDirector singleton

// 获取单例
static RSGraphicTestDirector& Instance();

// 获取根节点
std::shared_ptr<RSGraphicRootNode> GetRootNode() const;

// 获取屏幕尺寸
Vector2f GetScreenSize() const;

// 设置屏幕尺寸
void SetScreenSize(float width, float height);

// 设置Surface边界
void SetSurfaceBounds(const Vector4f& bounds);

// 设置屏幕Surface边界
void SetScreenSurfaceBounds(const Vector4f& bounds);

// 设置Surface颜色
void SetSurfaceColor(const RSColor& color);

// 刷新消息
void FlushMessage();

// 刷新并等待
bool FlushMessageAndWait(int timeoutMs);

// 截图
std::shared_ptr<Media::PixelMap> TakeScreenCaptureAndWait(int ms, bool isScreenShot = false);

// 设置/获取单测试模式
void SetSingleTest(bool isSingleTest);
bool IsSingleTest();

// 设置/获取动态测试模式
void SetDynamicTest(bool isDynamicTest);
bool IsDynamicTest();

// 启动动画
void StartRunUIAnimation();

// 检查是否有运行中的动画
bool HasUIRunningAnimation();

// 发送Profiler命令
void SendProfilerCommand(const std::string command, int outTime = 0);

Image helper functions (rs_graphic_test_img.h)

// 解码PixelMap
std::shared_ptr<Media::PixelMap> DecodePixelMap(
    const std::string& pathName,
    const Media::AllocatorType& allocatorType,
    const Media::PixelFormat& dstFormat = Media::PixelFormat::RGBA_8888
);
// pathName: 图片文件路径
// allocatorType: 通常使用 Media::AllocatorType::SHARE_MEM_ALLOC
// dstFormat: 目标像素格式

// 设置节点背景图片 (路径)
std::shared_ptr<RSCanvasNode> SetUpNodeBgImage(
    const std::string& pathName,
    const Vector4f bounds
);

// 设置节点背景图片 (数据)
std::shared_ptr<RSCanvasNode> SetUpNodeBgImage(
    const uint8_t* data,
    uint32_t size,
    const Vector4f bounds
);

Utility functions (rs_graphic_test_utils.h)

// 保存PixelMap为PNG
bool WriteToPngWithPixelMap(const std::string& fileName,
                            Media::PixelMap& pixelMap);

// 等待指定毫秒
void WaitTimeout(int ms);

Drawing API Reference

Get Recording Canvas

auto drawing = canvasNode->BeginRecording(width, height);
// ... 执行绘制操作 ...
canvasNode->FinishRecording();

Geometric shape drawing

// 点
drawing->DrawPoint(point);
drawing->DrawPoints(PointMode::POINTS, count, pts);
// PointMode: POINTS, LINES, POLYGON

// 线
drawing->DrawLine(startPt, endPt);

// 矩形
drawing->DrawRect(rect);

// 圆角矩形
drawing->DrawRoundRect(roundRect);
drawing->DrawNestedRoundRect(outer, inner);

// 弧形
drawing->DrawArc(oval, startAngle, sweepAngle);

// 饼图
drawing->DrawPie(oval, startAngle, sweepAngle);

// 椭圆
drawing->DrawOval(oval);

// 圆形
drawing->DrawCircle(center, radius);

// 路径
drawing->DrawPath(path);

// 区域
drawing->DrawRegion(region);

// 网格面片
drawing->DrawPatch(cubics[12], colors[4], texCoords[4], mode);

// 顶点网格
drawing->DrawVertices(vertices, mode);

Image drawing

// 位图
drawing->DrawBitmap(bitmap, x, y);

// 图片 (简单位置)
drawing->DrawImage(image, x, y, sampling);

// 图片 (源-目标矩形)
drawing->DrawImageRect(image, src, dst, sampling, constraint);
drawing->DrawImageRect(image, dst, sampling);

// 九宫格
drawing->DrawImageNine(image, center, dst, filterMode, brush);
drawing->DrawImageLattice(image, lattice, dst, filterMode);

// 图集
drawing->DrawAtlas(atlas, xform[], tex[], colors[], count, mode, sampling);

PixelMap drawing (ExtendRecordingCanvas)

auto extendDrawing = static_cast<ExtendRecordingCanvas*>(drawing);

// 带参数绘制
extendDrawing->DrawPixelMapWithParm(pixelMap, rsImageInfo, sampling);

// 矩形区域
extendDrawing->DrawPixelMapRect(pixelMap, src, dst, sampling, constraint);

// 九宫格
extendDrawing->DrawPixelMapNine(pixelMap, center, dst, filterMode);
extendDrawing->DrawPixelMapLattice(pixelMap, lattice, dst, filterMode);

// 带Brush的九宫格
extendDrawing->DrawImageNineWithPixelMap(pixelMap, center, dst, filter, brush);

// SurfaceBuffer
#ifdef ROSEN_OHOS
extendDrawing->DrawSurfaceBuffer(surfaceBufferInfo);
#endif

Color and text

// 纯色 (带混合模式)
drawing->DrawColor(color, BlendMode::SRC_OVER);

// 背景
drawing->DrawBackground(brush);

// 文本块
drawing->DrawTextBlob(blob, x, y);

// 符号
drawing->DrawSymbol(symbolData, locate);

// Picture
drawing->DrawPicture(picture);

Clipping operations

// 矩形裁剪
drawing->ClipRect(rect, ClipOp::INTERSECT, doAntiAlias);
// ClipOp: DIFFERENCE, INTERSECT

// 圆角矩形裁剪
drawing->ClipRoundRect(roundRect, ClipOp::INTERSECT, doAntiAlias);

// 路径裁剪
drawing->ClipPath(path, ClipOp::INTERSECT, doAntiAlias);

// 区域裁剪
drawing->ClipRegion(region, ClipOp::INTERSECT);

// 自适应圆角裁剪
drawing->ClipAdaptiveRoundRect(radius[4]);

Transform operations

// 设置矩阵
drawing->SetMatrix(matrix);

// 重置矩阵
drawing->ResetMatrix();

// 连接矩阵
drawing->ConcatMatrix(matrix);

// 平移
drawing->Translate(dx, dy);

// 缩放
drawing->Scale(sx, sy);

// 旋转 (绕指定中心)
drawing->Rotate(degrees, cx, cy);

// 错切
drawing->Shear(sx, sy);

State management

// 保存状态
uint32_t saveCount = drawing->Save();

// 保存图层 (离屏渲染)
drawing->SaveLayer(saveLayerOps);

// 恢复状态
drawing->Restore();

// 获取保存层数
uint32_t count = drawing->GetSaveCount();

// 丢弃所有未恢复的保存
drawing->Discard();

Other operations

// 清空
drawing->Clear();
drawing->Clear(color);

// 刷新
drawing->Flush();

// 自定义文本类型
drawing->SetIsCustomTextType(true);
bool isCustom = drawing->IsCustomTextType();

// 自定义字体
drawing->SetIsCustomTypeface(true);
bool isCustomTypeface = drawing->IsCustomTypeface();

// 录制命令模式
drawing->SetIsRecordCmd(true);
bool isRecordCmd = drawing->IsRecordCmd();

// 重置混合渲染尺寸
drawing->ResetHybridRenderSize(width, height);

Custom drawing functions

using DrawFunc = std::function<void(Drawing::Canvas* canvas, const Drawing::Rect* rect)>;

extendDrawing->DrawDrawFunc([](Drawing::Canvas* canvas, const Drawing::Rect* rect) {
    // 延迟绘制
    canvas->DrawRect(/* ... */);
});

BUILD.gn configuration template

import("//build/ohos.gni")
import("//foundation/graphic/graphic_2d/graphic_config.gni")

ohos_unittest("my_feature_test") {
  module_out_name = "my_feature_test"

  sources = [
    "my_test.cpp",
  ]

  include_dirs = [
    "//foundation/graphic/graphic_2d/rosen/test",
    "//foundation/graphic/graphic_2d/rosen/include",
    "//foundation/graphic/graphic_2d/graphic_test/graphic_test_framework/include",
  ]

  deps = [
    "//foundation/graphic/graphic_2d/rosen:librosen",
    "//foundation/graphic/graphic_2d/graphic_test/graphic_test_framework:libgraphic_test_framework",
  ]

  external_deps = [
    "hilog_native:libhilog",
    "image_framework:image_native",
  ]

  part_name = "graphic_2d"
  subsystem_name = "graphic"
}

Appendix

Commonly used header files

#include "rs_graphic_test.h"           // 测试基类
#include "rs_graphic_test_img.h"       // 图片辅助
#include "rs_graphic_test_utils.h"     // 工具函数
#include "ui/rs_canvas_node.h"         // Canvas节点
#include "ui/rs_surface_node.h"        // Surface节点
#include "transaction/rs_interfaces.h" // 事务接口
#include "display_manager.h"           // 显示管理器
#include "pixel_map.h"                 // PixelMap
#include "image_source.h"              // 图片源

Commonly used types

using Vector2f = OHOS::Rosen::Vector2f;      // {x, y}
using Vector4f = OHOS::Rosen::Vector4f;      // {left, top, right, bottom}
using RSColor = OHOS::Rosen::RSColor;        // uint32_t 颜色

Test comment format

/*
 * @tc.name: TestName
 * @tc.desc: 测试描述
 * @tc.type: FUNC | PERF | RELI
 * @tc.require: issue编号 或 AR编号
 */