docs: restructure test specification into two-level system

- tests/TEST_SPEC.md: General test spec for all modules
- tests/RHI/D3D12/TEST_SPEC.md: D3D12-specific spec with CI integration
This commit is contained in:
2026-03-20 03:33:40 +08:00
parent 983d6d61cd
commit 31273fdac4
2 changed files with 490 additions and 246 deletions

View File

@@ -1,82 +1,52 @@
# D3D12 测试规范文档
# D3D12 测试专项规范
## 1. 测试分类
本文档是 XCEngine 测试规范的 D3D12 专项补充。
### 1.1 测试层级
| 层级 | 位置 | 执行方式 | 目的 |
|------|------|----------|------|
| 单元测试 | `tests/RHI/D3D12/unit/` | CTest 自动运行 | 验证各组件 API 功能 |
| 集成测试 | `tests/RHI/D3D12/integration/` | CTest + Python wrapper | 端到端渲染验证 |
### 1.2 单元测试规范
**框架**: Google Test
**运行方式**:
```bash
cd build/tests/RHI/D3D12/unit
ctest -C Debug --output-on-failure
```
**命名约定**: `Component_Category_SubBehavior`
- Component: 组件名 (Device, Buffer, Texture, CommandQueue, etc.)
- Category: 操作类别 (Create, Get, Set, Map, Reset, etc.)
- SubBehavior: 具体行为 (DefaultHeap, GPUAddress, etc.)
**示例**:
```
Device_Create_Success
Device_Get_AdapterInfo
Buffer_Create_DefaultHeap
Buffer_Map_Unmap
Texture_Create_2D
```
### 1.3 集成测试规范
**运行方式**: Python wrapper 通过 CTest 调用
```bash
cd build/tests/RHI/D3D12/integration
ctest -C Debug --output-on-failure
```
**Python Wrapper 职责**:
1. 启动 D3D12 exe
2. 等待进程完成
3. 检查输出文件
4. 调用 compare_ppm.py 比对 Golden Image
5. 返回 0(成功)/1(失败)
**Golden Image 规范**:
- 文件格式: PPM (P6)
- 存储位置: `tests/RHI/D3D12/integration/`
- 命名: `GT_<test_name>.ppm`
- 阈值: 默认 5%
**前置阅读**: [tests/TEST_SPEC.md](../TEST_SPEC.md) - 通用测试规范
---
## 2. 测试命名规则
## 1. 概述
### 2.1 禁止模式
### 1.1 D3D12 测试特点
❌ 不允许:
- `*_Placeholder` - 无意义的占位测试
- 驼峰+下划线混用
- 空操作测试
| 特点 | 说明 |
|------|------|
| 硬件依赖 | 需要支持 D3D12 的显卡 |
| 窗口依赖 | 集成测试需要 GUI 窗口 |
| GPU 状态 | 测试间可能有 GPU 状态污染 |
### 2.2 标准格式
### 1.2 测试层级
✅ 正确格式:
```
Component_Category_SubBehavior
| 层级 | 位置 | 执行方式 | 框架 |
|------|------|----------|------|
| 单元测试 | `tests/RHI/D3D12/unit/` | CTest | Google Test |
| 集成测试 | `tests/RHI/D3D12/integration/` | CTest + Python | Python wrapper |
---
## 2. 单元测试规范
### 2.1 Fixture 设计
每个测试独立创建设备,避免 GPU 状态污染:
```cpp
class D3D12TestFixture : public ::testing::Test {
protected:
void SetUp() override; // 创建设备、命令队列等
void TearDown() override; // 清理资源
ID3D12Device* GetDevice();
ID3D12CommandQueue* GetCommandQueue();
ID3D12CommandAllocator* GetCommandAllocator();
ID3D12GraphicsCommandList* GetCommandList();
void WaitForGPU();
};
```
### 2.3 大小写规则
- 全小写,单词间用下划线分隔
- Component 名称与类名一致 (如 `CommandQueue` 不是 `Command_queue`)
### 2.4 测试前缀对应关系
### 2.2 测试前缀对应
| 类名 | 测试前缀 |
|------|---------|
@@ -94,227 +64,159 @@ Component_Category_SubBehavior
| D3D12RenderTargetView | RTV |
| D3D12DepthStencilView | DSV |
---
### 2.3 当前测试统计
## 3. 测试实现规范
### 3.1 单元测试结构
```cpp
TEST_F(D3D12TestFixture, Component_Category_SubBehavior) {
// Arrange: 准备测试数据
// Act: 执行被测操作
// Assert: 验证结果
ASSERT_TRUE(condition);
EXPECT_EQ(expected, actual);
}
```
### 3.2 Fixture 使用
```cpp
class D3D12TestFixture : public ::testing::Test {
protected:
void SetUp() override; // 创建设备、命令队列等
void TearDown() override; // 清理资源
ID3D12Device* GetDevice();
ID3D12CommandQueue* GetCommandQueue();
ID3D12CommandAllocator* GetCommandAllocator();
ID3D12GraphicsCommandList* GetCommandList();
void WaitForGPU();
};
```
### 3.3 错误处理测试
对于负面测试:
```cpp
TEST_F(D3D12TestFixture, Component_Create_InvalidSize) {
// 测试零大小或负数大小应该失败
ASSERT_FALSE(buffer->Initialize(device, 0));
}
```
| 组件 | 测试数 | 状态 |
|------|--------|------|
| Device | 6 | ✅ 通过 |
| Fence | 5 | ✅ 通过 |
| CommandQueue | 2 | ✅ 通过 |
| CommandAllocator | 3 | ✅ 通过 |
| CommandList | 2 | ✅ 通过 |
| Buffer | 5 | ✅ 通过 |
| Texture | 4 | ✅ 通过 |
| DescriptorHeap | 5 | ✅ 通过 |
| PipelineState | 2 | ✅ 通过 |
| RootSignature | 2 | ✅ 通过 |
| Shader | 3 | ✅ 通过 |
| Views | 4 | ✅ 通过 |
| **总计** | **44** | **全部通过** |
---
## 4. 集成测试规范
## 3. 集成测试规范
### 4.1 Python Wrapper 模板
### 3.1 Python Wrapper
```python
import sys
import os
import subprocess
import time
**位置**: `tests/RHI/D3D12/integration/run_integration_test.py`
def run_integration_test(exe_path, output_ppm, gt_ppm, threshold, timeout=120):
"""
Run a D3D12 integration test and compare output with golden template.
Args:
exe_path: Path to the test executable
output_ppm: Filename of the output screenshot
gt_ppm: Path to the golden template PPM file
threshold: Pixel difference threshold for comparison
timeout: Maximum time to wait (seconds)
Returns:
0 on success, non-zero on failure
"""
# 1. 检查文件存在
# 2. 删除旧输出
# 3. 启动进程
# 4. 等待完成
# 5. 检查输出
# 6. 比对图片
# 7. 返回结果
```
**职责**:
1. 启动 D3D12 exe
2. 等待进程完成
3. 检查输出文件 (PPM 截图)
4. 调用 `compare_ppm.py` 比对 Golden Image
5. 返回 0(成功)/1(失败)
### 4.2 CTest 注册格式
### 3.2 CTest 注册格式
```cmake
find_package(Python3 REQUIRED)
enable_testing()
add_test(NAME TestName
add_test(NAME D3D12_Minimal_Integration
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/run_integration_test.py
$<TARGET_FILE:TargetName>
output.ppm
${CMAKE_CURRENT_SOURCE_DIR}/GT.ppm
$<TARGET_FILE:D3D12_Minimal>
minimal.ppm
${CMAKE_CURRENT_SOURCE_DIR}/GT_minimal.ppm
5
WORKING_DIRECTORY $<TARGET_FILE_DIR:TargetName>
WORKING_DIRECTORY $<TARGET_FILE_DIR:D3D12_Minimal>
)
```
### 4.3 Golden Image 生成
### 3.3 Golden Image 规范
| 属性 | 值 |
|------|-----|
| 格式 | PPM (P6) |
| 命名 | `GT_<test_name>.ppm` |
| 阈值 | 默认 5% |
| 存储位置 | `tests/RHI/D3D12/integration/` |
### 3.4 Golden Image 生成流程
1. 在干净硬件环境运行集成测试
2. 截图保存为 `GT_<name>.ppm`
3. 人工验证截图正确性
4. 提交到版本控制
---
### 3.5 当前集成测试
## 5. CMakeLists.txt 规范
### 5.1 单元测试 CMakeLists.txt
```cmake
cmake_minimum_required(VERSION 3.15)
get_filename_component(PROJECT_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../.. ABSOLUTE)
find_package(GTest REQUIRED)
set(TEST_SOURCES
fixtures/D3D12TestFixture.cpp
test_device.cpp
test_buffer.cpp
# ... 其他测试文件
)
add_executable(d3d12_engine_tests ${TEST_SOURCES})
target_link_libraries(d3d12_engine_tests PRIVATE
d3d12
dxgi
d3dcompiler
XCEngine
GTest::gtest
GTest::gtest_main
)
target_include_directories(d3d12_engine_tests PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/fixtures
${PROJECT_ROOT_DIR}/engine/include
${PROJECT_ROOT_DIR}/engine/src
)
enable_testing()
add_test(NAME D3D12EngineTests COMMAND d3d12_engine_tests)
```
### 5.2 集成测试 CMakeLists.txt
```cmake
cmake_minimum_required(VERSION 3.15)
project(D3D12_Integration)
set(ENGINE_ROOT_DIR ${CMAKE_SOURCE_DIR}/../engine)
find_package(Python3 REQUIRED)
enable_testing()
add_executable(D3D12_Test WIN32 main.cpp)
target_link_libraries(D3D12_Test PRIVATE
d3d12 dxgi d3dcompiler winmm XCEngine
)
# POST_BUILD: 复制资源文件
add_custom_command(TARGET D3D12_Test POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_CURRENT_SOURCE_DIR}/Res
$<TARGET_FILE_DIR:D3D12_Test>/Res
)
# CTest 注册
add_test(NAME D3D12_Test_Integration
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/run_integration_test.py
$<TARGET_FILE:D3D12_Test>
output.ppm
${CMAKE_CURRENT_SOURCE_DIR}/GT.ppm
5
WORKING_DIRECTORY $<TARGET_FILE_DIR:D3D12_Test>
)
```
| 测试名 | Golden Image | 状态 |
|--------|-------------|------|
| D3D12_Minimal_Integration | GT_minimal.ppm | ✅ 通过 |
| D3D12_RenderModel_Integration | GT.ppm | ❌ 待修复 |
---
## 6. 测试执行
## 4. 测试执行
### 6.1 完整测试流程
### 4.1 单元测试
```bash
# 1. 构建
cd build
cmake ..
cmake --build . --target d3d12_engine_tests D3D12_Test --config Debug
# 方式 1: 使用统一脚本
python scripts/run_tests.py --unit-only
# 2. 运行单元测试
cd tests/RHI/D3D12/unit
ctest -C Debug --output-on-failure
# 3. 运行集成测试
cd tests/RHI/D3D12/integration
# 方式 2: 直接使用 CTest
cd build/tests/RHI/D3D12/unit
ctest -C Debug --output-on-failure
```
### 6.2 输出格式
### 4.2 集成测试
成功:
```
Test #1: D3D12_Minimal_Integration ....... Passed
```bash
# 方式 1: 使用统一脚本
python scripts/run_tests.py --integration
# 方式 2: 直接使用 CTest
cd build/tests/RHI/D3D12/integration
ctest -C Debug --output-on-failure
```
失败:
```
Test #1: D3D12_Minimal_Integration ....... Failed
[Integration Test] Different pixels: 39.94%
FAIL: Images differ!
### 4.3 构建和测试
```bash
# 构建
cmake --build . --target d3d12_engine_tests D3D12_Minimal --config Debug
# 运行测试
python scripts/run_tests.py --build
```
---
## 7. 文件结构
## 5. CI 集成
### 5.1 GitHub Actions 配置
```yaml
name: D3D12 Tests
on: [push, pull_request]
jobs:
test:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- name: Configure CMake
run: cmake -B build -DCMAKE_BUILD_TYPE=Debug
- name: Build D3D12 Tests
run: cmake --build build --target d3d12_engine_tests D3D12_Minimal --config Debug
- name: Run Unit Tests
run: cd build/tests/RHI/D3D12/unit && ctest -C Debug --output-on-failure
- name: Run Integration Tests
run: python scripts/run_tests.py --integration
```
### 5.2 CI 模式
`--ci` 模式会跳过需要 GUI 的集成测试:
```bash
python scripts/run_tests.py --ci # 仅运行单元测试
```
---
## 6. 文件结构
```
tests/RHI/D3D12/
├── CMakeLists.txt
├── TEST_IMPROVEMENT_PLAN.md # 改进计划
├── TEST_SPEC.md # 本规范
├── TEST_SPEC.md # 本文档 (D3D12 专项)
├── TEST_IMPROVEMENT_PLAN.md # 改进计划
├── unit/
│ ├── CMakeLists.txt
│ ├── fixtures/
@@ -326,7 +228,7 @@ tests/RHI/D3D12/
└── integration/
├── CMakeLists.txt
├── main_minimal.cpp
├── main_render.cpp
├── main_render.cpp # 有 API 问题,待修复
├── run_integration_test.py
├── compare_ppm.py
├── GT.ppm
@@ -335,13 +237,27 @@ tests/RHI/D3D12/
---
## 7. 已知问题
### 7.1 main_render.cpp API 不兼容
**问题**: `D3D12Buffer::Initialize``D3D12Screenshot::Capture` API 签名变更
**影响**: `D3D12_RenderModel_Integration` 无法编译
**状态**: 待修复
---
## 8. 规范更新记录
| 版本 | 日期 | 变更 |
|------|------|------|
| 1.0 | 2026-03-20 | 初始版本 |
| 1.1 | 2026-03-20 | 添加 CI 集成章节,补充 Phase 5 内容 |
---
**规范版本**: 1.0
**最后更新**: 2026-03-20
**规范版本**: 1.1
**最后更新**: 2026-03-20
**前置文档**: [tests/TEST_SPEC.md](../TEST_SPEC.md)