Add OpenGL test infrastructure - Phase 1: Device, Buffer, Fence tests (17 tests)
- Create test directory structure at tests/RHI/OpenGL/ - Implement OpenGLTestFixture with GLFW/GLAD initialization - Add Device tests: CreateRenderWindow, InitializeWithExistingWindow, GetDeviceInfo, SwapBuffers, PollEvents - Add Buffer tests: VertexBuffer, IndexBuffer, UniformBuffer, Dynamic, Bind/Unbind, Map/Unmap - Add Fence tests: Initialize (signaled/unsignaled), Signal, Wait, IsSignaled, GetStatus - Add CMakeLists.txt with proper GLFW/GLAD/GTest linking - Create implementation plan document at docs/OpenGL测试实施计划.md
This commit is contained in:
49
tests/RHI/OpenGL/CMakeLists.txt
Normal file
49
tests/RHI/OpenGL/CMakeLists.txt
Normal file
@@ -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)
|
||||
138
tests/RHI/OpenGL/fixtures/OpenGLTestFixture.cpp
Normal file
138
tests/RHI/OpenGL/fixtures/OpenGLTestFixture.cpp
Normal file
@@ -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);
|
||||
}
|
||||
53
tests/RHI/OpenGL/fixtures/OpenGLTestFixture.h
Normal file
53
tests/RHI/OpenGL/fixtures/OpenGLTestFixture.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#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)
|
||||
89
tests/RHI/OpenGL/test_buffer.cpp
Normal file
89
tests/RHI/OpenGL/test_buffer.cpp
Normal file
@@ -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<GLint>(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();
|
||||
}
|
||||
63
tests/RHI/OpenGL/test_device.cpp
Normal file
63
tests/RHI/OpenGL/test_device.cpp
Normal file
@@ -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);
|
||||
}
|
||||
61
tests/RHI/OpenGL/test_fence.cpp
Normal file
61
tests/RHI/OpenGL/test_fence.cpp
Normal file
@@ -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();
|
||||
}
|
||||
Reference in New Issue
Block a user