diff --git a/docs/OpenGL测试实施计划.md b/docs/OpenGL测试实施计划.md new file mode 100644 index 00000000..65344b5e --- /dev/null +++ b/docs/OpenGL测试实施计划.md @@ -0,0 +1,410 @@ +# OpenGL 测试实施计划 + +## 1. 概述 + +本文档是 `docs/OpenGL后端测试设计.md` 的实施细化计划,基于设计文档中的 5 阶段实现优先级,结合 D3D12 测试经验(54 个测试)制定。 + +### 1.1 目标 + +- 搭建完整的 OpenGL 后端测试框架 +- 实现与 D3D12 对齐的 54+ 个测试用例 +- 确保 CI 环境可用 +- 支持无头渲染(headless)测试 + +### 1.2 测试组件总览 + +| # | 组件 | 测试数量 | 文件 | 优先级 | +|---|------|:--------:|------|:------:| +| 1 | OpenGLDevice | 6 | test_device.cpp | Phase 1 | +| 2 | OpenGLBuffer | 6 | test_buffer.cpp | Phase 1 | +| 3 | OpenGLFence | 5 | test_fence.cpp | Phase 1 | +| 4 | OpenGLTexture | 5 | test_texture.cpp | Phase 2 | +| 5 | OpenGLSampler | 4 | test_sampler.cpp | Phase 2 | +| 6 | OpenGLShader | 4 | test_shader.cpp | Phase 3 | +| 7 | OpenGLPipelineState | 3 | test_pipeline_state.cpp | Phase 3 | +| 8 | OpenGLVertexArray | 4 | test_vertex_array.cpp | Phase 3 | +| 9 | OpenGLCommandList | 8 | test_command_list.cpp | Phase 4 | +| 10 | OpenGLRenderTargetView | 4 | test_render_target_view.cpp | Phase 4 | +| 11 | OpenGLDepthStencilView | 4 | test_depth_stencil_view.cpp | Phase 4 | +| 12 | OpenGLSwapChain | 3 | test_swap_chain.cpp | Phase 5 | +| **总计** | | **56** | | | + +--- + +## 2. 实施阶段 + +### Phase 1: 核心基础设施(约 1 天) + +#### 2.1 创建目录结构 + +``` +tests/RHI/OpenGL/ +├── CMakeLists.txt +├── fixtures/ +│ ├── OpenGLTestFixture.h +│ └── OpenGLTestFixture.cpp +└── Res/ + ├── Shader/ + ├── Texture/ + └── Data/ +``` + +#### 2.2 实现测试夹具 + +**OpenGLTestFixture** 是所有测试的基础: + +| 功能 | 描述 | +|------|------| +| `SetUpTestSuite()` | 创建隐藏 GLFW 窗口和 GL 上下文 | +| `TearDownTestSuite()` | 销毁窗口,终止 GLFW | +| `SetUp()` | 确保上下文激活,清除 GL 错误 | +| `TearDown()` | 重置 GL 状态,检查泄漏 | +| `CheckGLError()` | 检查并报告 GL 错误 | + +**GL 错误检查宏**: + +```cpp +#define GL_CLEAR_ERRORS() do { while (glGetError() != GL_NO_ERROR); } while(0) +#define GL_CHECK(call) do { call; ASSERT_TRUE(CheckGLError(__FILE__, __LINE__)); } while(0) +#define GL_EXPECT_SUCCESS(call) do { call; EXPECT_EQ(glGetError(), GL_NO_ERROR); } while(0) +``` + +#### 2.3 OpenGLDevice 测试(6 个) + +| 测试用例 | 验证内容 | +|----------|----------| +| `CreateRenderWindow_ValidParams` | 窗口创建成功 | +| `CreateRenderWindow_DebugMode` | 调试模式创建 | +| `InitializeWithExistingWindow` | 现有窗口初始化 | +| `GetDeviceInfo_ReturnsValid` | 供应商/渲染器/版本信息 | +| `SwapBuffers_NoErrors` | 交换缓冲区无错误 | +| `PollEvents_ReturnsTrue` | 事件轮询正常 | + +#### 2.4 OpenGLBuffer 测试(6 个) + +| 测试用例 | 验证内容 | +|----------|----------| +| `Initialize_VertexBuffer` | 顶点缓冲创建 | +| `Initialize_IndexBuffer` | 索引缓冲创建 | +| `Initialize_UniformBuffer` | Uniform 缓冲创建 | +| `Initialize_Dynamic` | 动态缓冲创建 | +| `Bind_Unbind` | 缓冲绑定/解绑 | +| `Map_Unmap` | 数据映射操作 | + +#### 2.5 OpenGLFence 测试(5 个) + +| 测试用例 | 验证内容 | +|----------|----------| +| `Initialize_Unsignaled` | 未 signaled 状态创建 | +| `Initialize_Signaled` | signaled 状态创建 | +| `Signal_SetsValue` | Signal 设置值 | +| `Wait_Blocks` | Wait 阻塞等待 | +| `IsSignaled_ReturnsState` | signaled 状态查询 | + +#### 2.6 验证步骤 + +```bash +# 构建 +cmake --build build --config Debug + +# 运行测试 +./build/tests/RHI/OpenGL/Debug/opengl_tests.exe --gtest_filter=OpenGLTestFixture.Device*:OpenGLTestFixture.Buffer*:OpenGLTestFixture.Fence* + +# 预期结果 +[==========] 17 tests from OpenGLTestFixture +[ PASSED ] 17 tests +``` + +--- + +### Phase 2: 资源管理(约 1 天) + +#### 2.7 OpenGLTexture 测试(5 个) + +| 测试用例 | 验证内容 | +|----------|----------| +| `Initialize_2DTexture` | 2D 纹理创建 | +| `Initialize_CubeMap` | 立方体纹理创建 | +| `Bind_Unbind` | 纹理绑定/解绑 | +| `GenerateMipmap` | Mipmap 生成 | +| `SetFiltering_SetWrapping` | 过滤/环绕参数 | + +#### 2.8 OpenGLSampler 测试(4 个) + +| 测试用例 | 验证内容 | +|----------|----------| +| `Initialize_Default` | 默认采样器创建 | +| `Initialize_Custom` | 自定义采样器创建 | +| `Bind_Unbind` | 采样器绑定/解绑 | +| `GetID_ReturnsValid` | 采样器 ID 有效 | + +#### 2.9 验证步骤 + +```bash +# 运行资源管理测试 +./build/tests/RHI/OpenGL/Debug/opengl_tests.exe --gtest_filter=OpenGLTestFixture.Texture*:OpenGLTestFixture.Sampler* + +# 预期结果 +[==========] 9 tests from OpenGLTestFixture +[ PASSED ] 9 tests +``` + +--- + +### Phase 3: 渲染管线(约 1.5 天) + +#### 2.10 OpenGLShader 测试(4 个) + +| 测试用例 | 验证内容 | +|----------|----------| +| `Compile_VertexFragment` | 顶点和片段着色器编译 | +| `Compile_WithGeometry` | 几何着色器编译 | +| `Compile_InvalidSource` | 无效源码编译失败 | +| `SetUniforms` | Uniform 设置 (int/float/vec3/mat4) | + +#### 2.11 OpenGLPipelineState 测试(3 个) + +| 测试用例 | 验证内容 | +|----------|----------| +| `SetDepthStencilState` | 深度/模板状态设置 | +| `SetBlendState` | 混合状态设置 | +| `SetViewport_SetScissor` | 视口/裁剪矩形 | + +#### 2.12 OpenGLVertexArray 测试(4 个) + +| 测试用例 | 验证内容 | +|----------|----------| +| `Initialize_CreatesVAO` | VAO 创建 | +| `AddVertexBuffer` | 顶点缓冲添加 | +| `SetIndexBuffer` | 索引缓冲设置 | +| `Bind_Unbind` | VAO 绑定/解绑 | + +#### 2.13 验证步骤 + +```bash +# 运行渲染管线测试 +./build/tests/RHI/OpenGL/Debug/opengl_tests.exe --gtest_filter=OpenGLTestFixture.Shader*:OpenGLTestFixture.PipelineState*:OpenGLTestFixture.VertexArray* + +# 预期结果 +[==========] 11 tests from OpenGLTestFixture +[ PASSED ] 11 tests +``` + +--- + +### Phase 4: 命令与视图(约 1.5 天) + +#### 2.14 OpenGLCommandList 测试(8 个) + +| 测试用例 | 验证内容 | +|----------|----------| +| `Clear_ColorBuffer` | 清除颜色缓冲 | +| `Clear_DepthStencil` | 清除深度/模板 | +| `SetVertexBuffer` | 设置顶点缓冲 | +| `SetIndexBuffer` | 设置索引缓冲 | +| `Draw_Triangles` | 绘制三角形 | +| `DrawIndexed_Indices` | 索引绘制 | +| `DrawInstanced` | 实例化绘制 | +| `Dispatch_ComputeShader` | 计算着色器分发 | + +#### 2.15 OpenGLRenderTargetView 测试(4 个) + +| 测试用例 | 验证内容 | +|----------|----------| +| `Initialize_Texture2D` | 2D 纹理 RTV 创建 | +| `Initialize_Default` | 默认帧缓冲 RTV | +| `Bind_Unbind` | RTV 绑定/解绑 | +| `Clear_Color` | 清除颜色 | + +#### 2.16 OpenGLDepthStencilView 测试(4 个) + +| 测试用例 | 验证内容 | +|----------|----------| +| `Initialize_Texture2D` | 2D 纹理 DSV 创建 | +| `Initialize_DepthOnly` | 仅深度 DSV | +| `Bind_Unbind` | DSV 绑定/解绑 | +| `ClearDepthStencil` | 清除深度/模板 | + +#### 2.17 验证步骤 + +```bash +# 运行命令与视图测试 +./build/tests/RHI/OpenGL/Debug/opengl_tests.exe --gtest_filter=OpenGLTestFixture.CommandList*:OpenGLTestFixture.RenderTargetView*:OpenGLTestFixture.DepthStencilView* + +# 预期结果 +[==========] 16 tests from OpenGLTestFixture +[ PASSED ] 16 tests +``` + +--- + +### Phase 5: 窗口管理(约 0.5 天) + +#### 2.18 OpenGLSwapChain 测试(3 个) + +| 测试用例 | 验证内容 | +|----------|----------| +| `Initialize_Window` | 交换链初始化 | +| `Present_VSync` | 垂直同步显示 | +| `Resize_ChangesSize` | 调整大小 | + +#### 2.19 验证步骤 + +```bash +# 运行窗口管理测试 +./build/tests/RHI/OpenGL/Debug/opengl_tests.exe --gtest_filter=OpenGLTestFixture.SwapChain* + +# 预期结果 +[==========] 3 tests from OpenGLTestFixture +[ PASSED ] 3 tests +``` + +--- + +## 3. 完整验证 + +### 3.1 运行所有测试 + +```bash +# 完整测试 +cmake --build build --config Debug +ctest --test-dir build -C Debug --output-on-failure + +# 预期结果 +[==========] 56 tests from 1 test suite. +[ PASSED ] 56 tests +``` + +### 3.2 测试分类统计 + +| 分类 | 数量 | 占比 | +|------|:----:|:----:| +| 初始化测试 | 18 | 32% | +| 绑定/解绑测试 | 10 | 18% | +| 数据操作测试 | 8 | 14% | +| 状态设置测试 | 10 | 18% | +| 绘制/执行测试 | 10 | 18% | + +--- + +## 4. CI 配置 + +### 4.1 GitHub Actions + +```yaml +# .github/workflows/opengl-tests.yml +name: OpenGL Tests + +on: [push, pull_request] + +jobs: + test: + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + + - name: Configure + run: cmake -B build -S . -G "Visual Studio 17 2022" + + - name: Build + run: cmake --build build --config Debug + + - name: Run Tests + run: ctest --test-dir build -C Debug --output-on-failure +``` + +### 4.2 Linux CI(可选) + +Linux 下需要配置 Mesa 软件渲染: + +```yaml +- name: Run Tests + env: + MESA_GL_VERSION_OVERRIDE: 4.6 + MESA_GALLIUM_DRIVER: llvmpipe + run: ctest --test-dir build -C Debug --output-on-failure +``` + +--- + +## 5. 任务清单 + +### 阶段 1:基础设施 + +- [ ] 1.1 创建 `tests/RHI/OpenGL/CMakeLists.txt` +- [ ] 1.2 创建 `tests/RHI/OpenGL/fixtures/OpenGLTestFixture.h` +- [ ] 1.3 创建 `tests/RHI/OpenGL/fixtures/OpenGLTestFixture.cpp` +- [ ] 1.4 实现 `test_device.cpp`(6 个测试) +- [ ] 1.5 实现 `test_buffer.cpp`(6 个测试) +- [ ] 1.6 实现 `test_fence.cpp`(5 个测试) +- [ ] 1.7 构建验证(17 个测试) + +### 阶段 2:资源管理 + +- [ ] 2.1 实现 `test_texture.cpp`(5 个测试) +- [ ] 2.2 实现 `test_sampler.cpp`(4 个测试) +- [ ] 2.3 构建验证(9 个测试) + +### 阶段 3:渲染管线 + +- [ ] 3.1 实现 `test_shader.cpp`(4 个测试) +- [ ] 3.2 实现 `test_pipeline_state.cpp`(3 个测试) +- [ ] 3.3 实现 `test_vertex_array.cpp`(4 个测试) +- [ ] 3.4 构建验证(11 个测试) + +### 阶段 4:命令与视图 + +- [ ] 4.1 实现 `test_command_list.cpp`(8 个测试) +- [ ] 4.2 实现 `test_render_target_view.cpp`(4 个测试) +- [ ] 4.3 实现 `test_depth_stencil_view.cpp`(4 个测试) +- [ ] 4.4 构建验证(16 个测试) + +### 阶段 5:窗口管理 + +- [ ] 5.1 实现 `test_swap_chain.cpp`(3 个测试) +- [ ] 5.2 完整测试验证(56 个测试) +- [ ] 5.3 提交并推送 + +--- + +## 6. 关键注意事项 + +### 6.1 窗口依赖 + +- OpenGL 必须有 GLFW 窗口上下文 +- 使用 `GLFW_VISIBLE = GLFW_FALSE` 创建隐藏窗口 +- 每个测试前 `glfwMakeContextCurrent()` + +### 6.2 GL 状态污染 + +- OpenGL 状态是全局的,必须隔离 +- `TearDown()` 中重置所有 GL 状态 +- 测试结束后 `glBindBuffer(..., 0)` 等 + +### 6.3 错误检查 + +- 使用 `GL_CHECK()` 宏验证每个 GL 调用 +- 测试开始时 `GL_CLEAR_ERRORS()` +- 捕获 GL 错误并转换为测试失败 + +### 6.4 与 D3D12 对齐 + +- 保持相同的测试数量级(54+) +- 使用相同的 TEST_F 宏 +- 遵循相同的命名约定 + +--- + +## 7. 后续工作 + +- [ ] 资源泄漏检测工具 +- [ ] 性能基准测试 +- [ ] 截图对比测试 +- [ ] Linux CI 配置 +- [ ] macOS 支持 + +--- + +**文档版本**:1.0 +**创建日期**:2026年3月17日 +**基于文档**:`docs/OpenGL后端测试设计.md`、`tests/RHI/D3D12/` diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 78e1be16..71a6eb72 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -39,6 +39,7 @@ add_subdirectory(threading) add_subdirectory(debug) add_subdirectory(D3D12) add_subdirectory(RHI/D3D12) +add_subdirectory(RHI/OpenGL) # ============================================================ # Test Summary diff --git a/tests/RHI/OpenGL/CMakeLists.txt b/tests/RHI/OpenGL/CMakeLists.txt new file mode 100644 index 00000000..4f210b8b --- /dev/null +++ b/tests/RHI/OpenGL/CMakeLists.txt @@ -0,0 +1,49 @@ +cmake_minimum_required(VERSION 3.15) + +project(OpenGLEngineTests) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +get_filename_component(PROJECT_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../.. ABSOLUTE) + +find_package(OpenGL REQUIRED) + +include_directories(${CMAKE_SOURCE_DIR}/tests/OpenGL/package/include/) +include_directories(${PROJECT_ROOT_DIR}/engine/include) +include_directories(${PROJECT_ROOT_DIR}/engine/src) + +link_directories(${CMAKE_SOURCE_DIR}/tests/OpenGL/package/lib/) + +find_package(GTest REQUIRED) + +set(TEST_SOURCES + ${CMAKE_SOURCE_DIR}/tests/OpenGL/package/src/glad.c + fixtures/OpenGLTestFixture.cpp + test_device.cpp + test_buffer.cpp + test_fence.cpp +) + +add_executable(opengl_engine_tests ${TEST_SOURCES}) + +target_link_libraries(opengl_engine_tests PRIVATE + opengl32 + glfw3 + XCEngine + GTest::gtest + GTest::gtest_main +) + +target_include_directories(opengl_engine_tests PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/fixtures + ${PROJECT_ROOT_DIR}/engine/include + ${PROJECT_ROOT_DIR}/engine/src +) + +target_compile_definitions(opengl_engine_tests PRIVATE + TEST_RESOURCES_DIR="${PROJECT_ROOT_DIR}/tests/RHI/OpenGL/Res" +) + +enable_testing() +add_test(NAME OpenGLEngineTests COMMAND opengl_engine_tests) diff --git a/tests/RHI/OpenGL/fixtures/OpenGLTestFixture.cpp b/tests/RHI/OpenGL/fixtures/OpenGLTestFixture.cpp new file mode 100644 index 00000000..d02edafb --- /dev/null +++ b/tests/RHI/OpenGL/fixtures/OpenGLTestFixture.cpp @@ -0,0 +1,138 @@ +#include "OpenGLTestFixture.h" + +using namespace XCEngine::RHI; + +GLFWwindow* OpenGLTestFixture::m_window = nullptr; +bool OpenGLTestFixture::m_contextInitialized = false; +OpenGLDevice* OpenGLTestFixture::m_device = nullptr; + +void OpenGLTestFixture::SetUpTestSuite() { + if (!glfwInit()) { + GTEST_SKIP() << "Failed to initialize GLFW"; + return; + } + + glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + + m_window = glfwCreateWindow(640, 480, "OpenGL Tests", nullptr, nullptr); + if (!m_window) { + GTEST_SKIP() << "Failed to create GLFW window"; + glfwTerminate(); + return; + } + + glfwMakeContextCurrent(m_window); + + if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { + GTEST_SKIP() << "Failed to initialize GLAD"; + glfwDestroyWindow(m_window); + m_window = nullptr; + glfwTerminate(); + return; + } + + m_contextInitialized = true; + + m_device = new OpenGLDevice(); + m_device->CreateRenderWindow(640, 480, "Test Window", false); +} + +void OpenGLTestFixture::TearDownTestSuite() { + if (m_device) { + delete m_device; + m_device = nullptr; + } + + if (m_window) { + glfwDestroyWindow(m_window); + m_window = nullptr; + } + glfwTerminate(); + m_contextInitialized = false; +} + +void OpenGLTestFixture::SetUp() { + if (!m_window || !m_contextInitialized) { + GTEST_SKIP() << "OpenGL context not available"; + return; + } + + glfwMakeContextCurrent(m_window); + ClearGLErrors(); +} + +void OpenGLTestFixture::TearDown() { + GLenum error = glGetError(); + if (error != GL_NO_ERROR) { + } + + ResetGLState(); +} + +void OpenGLTestFixture::MakeContextCurrent() { + if (m_window) { + glfwMakeContextCurrent(m_window); + } +} + +void OpenGLTestFixture::DoneContextCurrent() { + glfwMakeContextCurrent(nullptr); +} + +void OpenGLTestFixture::ClearGLErrors() { + while (glGetError() != GL_NO_ERROR); +} + +bool OpenGLTestFixture::CheckGLError(const char* file, int line) { + GLenum error = glGetError(); + if (error != GL_NO_ERROR) { + ADD_FAILURE() << "OpenGL Error: " << error + << " (" << GetGLErrorString(error) << ")" + << " at " << file << ":" << line; + return false; + } + return true; +} + +const char* OpenGLTestFixture::GetGLErrorString(GLenum error) { + switch (error) { + case GL_NO_ERROR: return "GL_NO_ERROR"; + case GL_INVALID_ENUM: return "GL_INVALID_ENUM"; + case GL_INVALID_VALUE: return "GL_INVALID_VALUE"; + case GL_INVALID_OPERATION: return "GL_INVALID_OPERATION"; + case GL_INVALID_FRAMEBUFFER_OPERATION: return "GL_INVALID_FRAMEBUFFER_OPERATION"; + case GL_OUT_OF_MEMORY: return "GL_OUT_OF_MEMORY"; + default: return "Unknown error"; + } +} + +void OpenGLTestFixture::ResetGLState() { + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindBuffer(GL_UNIFORM_BUFFER, 0); + + glUseProgram(0); + + glBindVertexArray(0); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + + glActiveTexture(GL_TEXTURE0); + for (int i = 0; i < 16; ++i) { + glBindTexture(GL_TEXTURE_2D, 0); + } + + glDisable(GL_DEPTH_TEST); + glDisable(GL_STENCIL_TEST); + glDisable(GL_BLEND); + glDisable(GL_CULL_FACE); + glDisable(GL_SCISSOR_TEST); + + glDepthFunc(GL_LESS); + glStencilFunc(GL_ALWAYS, 0, 0xFF); + glBlendFunc(GL_ONE, GL_ZERO); +} diff --git a/tests/RHI/OpenGL/fixtures/OpenGLTestFixture.h b/tests/RHI/OpenGL/fixtures/OpenGLTestFixture.h new file mode 100644 index 00000000..9274d2f7 --- /dev/null +++ b/tests/RHI/OpenGL/fixtures/OpenGLTestFixture.h @@ -0,0 +1,53 @@ +#pragma once + +#include +#include +#include + +#include "XCEngine/RHI/OpenGL/OpenGLDevice.h" + +namespace XCEngine { +namespace RHI { + +class OpenGLTestFixture : public ::testing::Test { +protected: + static void SetUpTestSuite(); + static void TearDownTestSuite(); + + void SetUp() override; + void TearDown() override; + + GLFWwindow* GetWindow() { return m_window; } + void MakeContextCurrent(); + void DoneContextCurrent(); + + void ClearGLErrors(); + bool CheckGLError(const char* file, int line); + const char* GetGLErrorString(GLenum error); + void ResetGLState(); + +private: + static GLFWwindow* m_window; + static bool m_contextInitialized; + static OpenGLDevice* m_device; +}; + +} // namespace RHI +} // namespace XCEngine + +#define GL_CLEAR_ERRORS() \ + do { while (glGetError() != GL_NO_ERROR); } while(0) + +#define GL_CHECK(call) \ + do { \ + call; \ + ASSERT_TRUE(XCEngine::RHI::OpenGLTestFixture::CheckGLError(__FILE__, __LINE__)) \ + << "GL error after " << #call; \ + } while(0) + +#define GL_EXPECT_SUCCESS(call) \ + do { \ + call; \ + EXPECT_EQ(glGetError(), GL_NO_ERROR) \ + << "GL error after " << #call; \ + } while(0) diff --git a/tests/RHI/OpenGL/test_buffer.cpp b/tests/RHI/OpenGL/test_buffer.cpp new file mode 100644 index 00000000..c2a580c2 --- /dev/null +++ b/tests/RHI/OpenGL/test_buffer.cpp @@ -0,0 +1,89 @@ +#include "fixtures/OpenGLTestFixture.h" +#include "XCEngine/RHI/OpenGL/OpenGLBuffer.h" + +using namespace XCEngine::RHI; + +TEST_F(OpenGLTestFixture, Buffer_Initialize_VertexBuffer) { + OpenGLBuffer buffer; + float vertices[] = { 0.0f, 0.0f, 0.5f, 0.5f }; + + bool result = buffer.InitializeVertexBuffer(vertices, sizeof(vertices)); + + ASSERT_TRUE(result); + EXPECT_NE(buffer.GetID(), 0u); + EXPECT_EQ(buffer.GetSize(), sizeof(vertices)); + EXPECT_EQ(buffer.GetType(), OpenGLBufferType::Vertex); + + buffer.Shutdown(); +} + +TEST_F(OpenGLTestFixture, Buffer_Initialize_IndexBuffer) { + OpenGLBuffer buffer; + unsigned int indices[] = { 0, 1, 2 }; + + bool result = buffer.InitializeIndexBuffer(indices, sizeof(indices)); + + ASSERT_TRUE(result); + EXPECT_NE(buffer.GetID(), 0u); + EXPECT_EQ(buffer.GetSize(), sizeof(indices)); + + buffer.Shutdown(); +} + +TEST_F(OpenGLTestFixture, Buffer_Initialize_UniformBuffer) { + OpenGLBuffer buffer; + float data[16] = {}; + + bool result = buffer.Initialize(OpenGLBufferType::Uniform, sizeof(data), data, true); + + ASSERT_TRUE(result); + EXPECT_TRUE(buffer.IsDynamic()); + EXPECT_EQ(buffer.GetType(), OpenGLBufferType::Uniform); + + buffer.Shutdown(); +} + +TEST_F(OpenGLTestFixture, Buffer_Initialize_Dynamic) { + OpenGLBuffer buffer; + + bool result = buffer.Initialize(OpenGLBufferType::CopyRead, 256, nullptr, true); + + ASSERT_TRUE(result); + EXPECT_TRUE(buffer.IsDynamic()); + EXPECT_EQ(buffer.GetSize(), 256u); + + buffer.Shutdown(); +} + +TEST_F(OpenGLTestFixture, Buffer_Bind_Unbind) { + OpenGLBuffer buffer; + buffer.InitializeVertexBuffer(nullptr, 16); + + buffer.Bind(); + GLint boundBuffer = 0; + glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &boundBuffer); + EXPECT_EQ(boundBuffer, static_cast(buffer.GetID())); + + buffer.Unbind(); + glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &boundBuffer); + EXPECT_EQ(boundBuffer, 0); + + buffer.Shutdown(); +} + +TEST_F(OpenGLTestFixture, Buffer_Map_Unmap) { + OpenGLBuffer buffer; + buffer.Initialize(OpenGLBufferType::CopyRead, 256, nullptr, true); + + void* mappedData = buffer.Map(); + ASSERT_NE(mappedData, nullptr); + + memset(mappedData, 0xAB, 256); + + buffer.Unmap(); + + GLenum error = glGetError(); + EXPECT_EQ(error, GL_NO_ERROR); + + buffer.Shutdown(); +} diff --git a/tests/RHI/OpenGL/test_device.cpp b/tests/RHI/OpenGL/test_device.cpp new file mode 100644 index 00000000..2cc52f90 --- /dev/null +++ b/tests/RHI/OpenGL/test_device.cpp @@ -0,0 +1,63 @@ +#include "fixtures/OpenGLTestFixture.h" +#include "XCEngine/RHI/OpenGL/OpenGLDevice.h" + +using namespace XCEngine::RHI; + +TEST_F(OpenGLTestFixture, Device_CreateRenderWindow_ValidParams) { + OpenGLDevice device; + + bool result = device.CreateRenderWindow(800, 600, "Test Window", false); + + ASSERT_TRUE(result); + ASSERT_NE(device.GetWindow(), nullptr); +} + +TEST_F(OpenGLTestFixture, Device_CreateRenderWindow_DebugMode) { + OpenGLDevice device; + + bool result = device.CreateRenderWindow(640, 480, "Debug Window", true); + + ASSERT_TRUE(result); + ASSERT_NE(device.GetWindow(), nullptr); +} + +TEST_F(OpenGLTestFixture, Device_InitializeWithExistingWindow) { + OpenGLDevice device; + GLFWwindow* existingWindow = GetWindow(); + + bool result = device.InitializeWithExistingWindow(existingWindow); + + ASSERT_TRUE(result); + ASSERT_EQ(device.GetWindow(), existingWindow); +} + +TEST_F(OpenGLTestFixture, Device_GetDeviceInfo_ReturnsValid) { + OpenGLDevice device; + device.CreateRenderWindow(800, 600, "Test", false); + + const auto& info = device.GetDeviceInfo(); + + EXPECT_FALSE(info.vendor.empty()); + EXPECT_FALSE(info.renderer.empty()); + EXPECT_GE(info.majorVersion, 3); + EXPECT_GE(info.minorVersion, 0); +} + +TEST_F(OpenGLTestFixture, Device_SwapBuffers_NoErrors) { + OpenGLDevice device; + device.CreateRenderWindow(800, 600, "Test", false); + + device.SwapBuffers(); + + GLenum error = glGetError(); + EXPECT_EQ(error, GL_NO_ERROR); +} + +TEST_F(OpenGLTestFixture, Device_PollEvents_ReturnsTrue) { + OpenGLDevice device; + device.CreateRenderWindow(800, 600, "Test", false); + + bool result = device.PollEvents(); + + EXPECT_TRUE(result); +} diff --git a/tests/RHI/OpenGL/test_fence.cpp b/tests/RHI/OpenGL/test_fence.cpp new file mode 100644 index 00000000..6d7c267c --- /dev/null +++ b/tests/RHI/OpenGL/test_fence.cpp @@ -0,0 +1,61 @@ +#include "fixtures/OpenGLTestFixture.h" +#include "XCEngine/RHI/OpenGL/OpenGLFence.h" + +using namespace XCEngine::RHI; + +TEST_F(OpenGLTestFixture, Fence_Initialize_Unsignaled) { + OpenGLFence fence; + + bool result = fence.Initialize(false); + + ASSERT_TRUE(result); + EXPECT_EQ(fence.GetStatus(), FenceStatus::Unsignaled); + + fence.Shutdown(); +} + +TEST_F(OpenGLTestFixture, Fence_Initialize_Signaled) { + OpenGLFence fence; + + bool result = fence.Initialize(true); + + ASSERT_TRUE(result); + + fence.Shutdown(); +} + +TEST_F(OpenGLTestFixture, Fence_Signal_Wait) { + OpenGLFence fence; + fence.Initialize(false); + + fence.Signal(1); + + fence.Wait(1); + + EXPECT_TRUE(fence.IsSignaled()); + EXPECT_EQ(fence.GetCompletedValue(), 1u); + + fence.Shutdown(); +} + +TEST_F(OpenGLTestFixture, Fence_IsSignaled_ReturnsState) { + OpenGLFence fence; + fence.Initialize(false); + + fence.Signal(1); + fence.Wait(1); + + EXPECT_TRUE(fence.IsSignaled()); + + fence.Shutdown(); +} + +TEST_F(OpenGLTestFixture, Fence_GetStatus_ReturnsCorrect) { + OpenGLFence fence; + fence.Initialize(false); + + FenceStatus status = fence.GetStatus(); + EXPECT_TRUE(status == FenceStatus::Signaled || status == FenceStatus::Unsignaled); + + fence.Shutdown(); +}