Skip to content

Commit

Permalink
[NOX-76] add GLShader unit tests
Browse files Browse the repository at this point in the history
Signed-off-by: Rafal Maziejuk <[email protected]>
  • Loading branch information
rafalmaziejuk committed May 25, 2024
1 parent 041cfbc commit c50f920
Show file tree
Hide file tree
Showing 8 changed files with 176 additions and 37 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
branches: [ "dev" ]

env:
DISPLAY: ":99"
Expand Down
6 changes: 2 additions & 4 deletions src/opengl/gl_renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,8 @@ std::unique_ptr<Buffer> GLRenderer::createIndexBuffer(const IndexBufferDescripto
}

std::unique_ptr<Shader> GLRenderer::createShader(const ShaderDescriptor &descriptor, std::string_view source) {
NOX_ASSERT(GLShader::validateInput(descriptor, source));

auto shader = std::make_unique<GLShader>(descriptor);
NOX_ENSURE_RETURN_NULLPTR_MSG(shader->compile(source.data()), "Couldn't compile shader");
auto shader = GLShader::create(descriptor, source);
NOX_ENSURE_RETURN_NULLPTR_MSG(shader != nullptr, "Couldn't create shader");

return shader;
}
Expand Down
12 changes: 5 additions & 7 deletions src/opengl/gl_shader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,17 @@ GLenum mapShaderTypeToEnum(ShaderType type) {
default: break;
}

NOX_ASSERT(false);
return GL_NONE;
}

} // namespace

bool GLShader::validateInput(const ShaderDescriptor &descriptor, std::string_view source) {
bool result = true;
std::unique_ptr<GLShader> GLShader::create(const ShaderDescriptor &descriptor, std::string_view source) {
auto shader = std::make_unique<GLShader>(descriptor);
NOX_ENSURE_RETURN_NULLPTR(shader->getHandle() != 0u);
NOX_ENSURE_RETURN_NULLPTR_MSG(shader->compile(source.data()), "Couldn't compile shader");

result &= (!source.empty());
result &= (mapShaderTypeToEnum(descriptor.type) != GL_NONE);

return result;
return shader;
}

GLShader::GLShader(const ShaderDescriptor &descriptor)
Expand Down
8 changes: 5 additions & 3 deletions src/opengl/gl_shader.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@

#include <nox/shader.h>

#include <memory>
#include <string_view>

namespace nox {

class GLShader final : public Shader {
public:
[[nodiscard]] static bool validateInput(const ShaderDescriptor &descriptor, std::string_view source);
[[nodiscard]] static std::unique_ptr<GLShader> create(const ShaderDescriptor &descriptor, std::string_view source);

explicit GLShader(const ShaderDescriptor &descriptor);
~GLShader() override;
Expand All @@ -17,10 +18,11 @@ class GLShader final : public Shader {

[[nodiscard]] uint32_t getHandle() const { return m_handle; }

bool compile(const char *source) const;
private:
[[nodiscard]] bool compile(const char *source) const;

private:
ShaderType m_type;
ShaderType m_type{ShaderType::NONE};
uint32_t m_handle{0u};
};

Expand Down
12 changes: 6 additions & 6 deletions src/opengl/gl_swapchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,14 +105,14 @@ Vector2D<uint32_t> GLSwapchain::getSize() const {
}

bool GLSwapchain::initializePresentationProgram() {
const GLShader presentVertexShader{{ShaderType::VERTEX}};
NOX_ENSURE_RETURN_FALSE(presentVertexShader.compile(presentVertexShaderSource));
const auto presentVertexShader = GLShader::create({ShaderType::VERTEX}, presentVertexShaderSource);
NOX_ENSURE_RETURN_FALSE(presentVertexShader != nullptr);

const GLShader presentFragmentShader{{ShaderType::FRAGMENT}};
NOX_ENSURE_RETURN_FALSE(presentFragmentShader.compile(presentFragmentShaderSource));
const auto presentFragmentShader = GLShader::create({ShaderType::FRAGMENT}, presentFragmentShaderSource);
NOX_ENSURE_RETURN_FALSE(presentFragmentShader != nullptr);

m_presentationProgram.attachShader(presentVertexShader.getHandle());
m_presentationProgram.attachShader(presentFragmentShader.getHandle());
m_presentationProgram.attachShader(presentVertexShader->getHandle());
m_presentationProgram.attachShader(presentFragmentShader->getHandle());
NOX_ENSURE_RETURN_FALSE(m_presentationProgram.link());

return true;
Expand Down
1 change: 1 addition & 0 deletions tests/unit_tests/opengl/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ set(NOX_UNIT_TESTS_OPENGL_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt
${CMAKE_CURRENT_SOURCE_DIR}/gl_context_tests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/gl_program_tests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/gl_shader_tests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/gl_swapchain_tests.cpp
)

Expand Down
83 changes: 67 additions & 16 deletions tests/unit_tests/opengl/gl_program_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ TEST_F(GLProgramFixture, WhenCreatingProgramThenProgramIsSuccessfullyCreated) {
EXPECT_NE(0u, program.getHandle());
}

TEST_F(GLProgramFixture, GivenCorrectShadersWhenLinkingProgramThenTrueIsReturned) {
TEST_F(GLProgramFixture, GivenCorrectShadersWhenCallingLinkProgramThenTrueIsReturned) {
constexpr auto vertexShaderSource = R"(
#version 460 core
Expand All @@ -57,19 +57,20 @@ TEST_F(GLProgramFixture, GivenCorrectShadersWhenLinkingProgramThenTrueIsReturned
}
)";

GLProgram program{};
const GLShader vertexShader{{ShaderType::VERTEX}};
const GLShader fragmentShader{{ShaderType::FRAGMENT}};
const auto vertexShader = GLShader::create({ShaderType::VERTEX}, vertexShaderSource);
ASSERT_NE(nullptr, vertexShader);

const auto fragmentShader = GLShader::create({ShaderType::FRAGMENT}, fragmentShaderSource);
ASSERT_NE(nullptr, fragmentShader);

vertexShader.compile(vertexShaderSource);
fragmentShader.compile(fragmentShaderSource);
program.attachShader(vertexShader.getHandle());
program.attachShader(fragmentShader.getHandle());
GLProgram program{};
program.attachShader(vertexShader->getHandle());
program.attachShader(fragmentShader->getHandle());

EXPECT_TRUE(program.link());
}

TEST_F(GLProgramFixture, GivenIncorrectShadersWhenLinkingProgramThenFalseIsReturned) {
TEST_F(GLProgramFixture, GivenIncorrectShadersWhenCallingLinkProgramThenFalseIsReturned) {
constexpr auto vertexShaderSource = R"(
#version 460 core
Expand All @@ -82,14 +83,64 @@ TEST_F(GLProgramFixture, GivenIncorrectShadersWhenLinkingProgramThenFalseIsRetur
}
)";

GLProgram program{};
const GLShader vertexShader1{{ShaderType::VERTEX}};
const GLShader vertexShader2{{ShaderType::VERTEX}};
const auto vertexShader1 = GLShader::create({ShaderType::VERTEX}, vertexShaderSource);
ASSERT_NE(nullptr, vertexShader1);

const auto vertexShader2 = GLShader::create({ShaderType::VERTEX}, vertexShaderSource);
ASSERT_NE(nullptr, vertexShader2);

vertexShader1.compile(vertexShaderSource);
vertexShader2.compile(vertexShaderSource);
program.attachShader(vertexShader1.getHandle());
program.attachShader(vertexShader2.getHandle());
GLProgram program{};
program.attachShader(vertexShader1->getHandle());
program.attachShader(vertexShader2->getHandle());

EXPECT_FALSE(program.link());
}

TEST_F(GLProgramFixture, WhenCallingBindProgramThenCorrectProgramIsBound) {
constexpr auto vertexShaderSource = R"(
#version 460 core
out gl_PerVertex {
vec4 gl_Position;
};
void main() {
gl_Position = vec4(1.0);
}
)";
constexpr auto fragmentShaderSource = R"(
#version 460 core
out vec4 fragmentColor;
void main() {
fragmentColor = vec4(1.0);
}
)";

const auto vertexShader = GLShader::create({ShaderType::VERTEX}, vertexShaderSource);
ASSERT_NE(nullptr, vertexShader);

const auto fragmentShader = GLShader::create({ShaderType::FRAGMENT}, fragmentShaderSource);
ASSERT_NE(nullptr, fragmentShader);

GLProgram program{};
program.attachShader(vertexShader->getHandle());
program.attachShader(fragmentShader->getHandle());
ASSERT_TRUE(program.link());

const auto handle = program.getHandle();
EXPECT_NE(0u, handle);

program.bind();

GLint currentlyBoundProgramHandle = GL_NONE;
glGetIntegerv(GL_CURRENT_PROGRAM, &currentlyBoundProgramHandle);

EXPECT_EQ(handle, currentlyBoundProgramHandle);

program.unbind();
glGetIntegerv(GL_CURRENT_PROGRAM, &currentlyBoundProgramHandle);

EXPECT_EQ(0u, currentlyBoundProgramHandle);
}
89 changes: 89 additions & 0 deletions tests/unit_tests/opengl/gl_shader_tests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#include "src/opengl/gl_context.h"
#include "src/opengl/gl_shader.h"

#include "tests/fixtures/window_fixture.h"

#include <glad/gl.h>
#include <gtest/gtest.h>

using namespace nox;

struct GLShaderFixture : public tests::WindowFixture,
public ::testing::Test {
void SetUp() override {
tests::WindowFixture::setUp();

SurfaceDescriptor surfaceDescriptor{};
surfaceDescriptor.surfaceBackendDescriptor = surfaceBackendDescriptor;
surfaceDescriptor.surfaceAttributesDescriptor = OpenGLSurfaceAttributesDescriptor{};

context = GLContext::create(surfaceDescriptor);
ASSERT_NE(nullptr, context);
}

void TearDown() override {
tests::WindowFixture::tearDown();
}

std::unique_ptr<GLContext> context{nullptr};
};

TEST_F(GLShaderFixture, GivenValidShaderDescriptorAndSourceWhenCallingCreateShaderThenShaderIsSuccessfullyCreated) {
using TestParams = std::array<std::pair<ShaderType, GLenum>, static_cast<size_t>(ShaderType::MAX) - 1>;
constexpr TestParams testParams{{
{ShaderType::VERTEX, GL_VERTEX_SHADER},
{ShaderType::FRAGMENT, GL_FRAGMENT_SHADER},
{ShaderType::TESS_CONTROL, GL_TESS_CONTROL_SHADER},
{ShaderType::TESS_EVALUATION, GL_TESS_EVALUATION_SHADER},
{ShaderType::GEOMETRY, GL_GEOMETRY_SHADER},
{ShaderType::COMPUTE, GL_COMPUTE_SHADER},
}};
constexpr auto shaderSource = R"(
#version 460 core
void main() {}
)";

for (const auto &[shaderType, expectedShaderType] : testParams) {
ShaderDescriptor shaderDescriptor{};
shaderDescriptor.type = shaderType;

const auto shader = GLShader::create(shaderDescriptor, shaderSource);
ASSERT_NE(nullptr, shader);

GLint glShaderType = GL_NONE;
glGetShaderiv(shader->getHandle(), GL_SHADER_TYPE, &glShaderType);

EXPECT_NE(0u, shader->getHandle());
EXPECT_EQ(shaderType, shader->getType());
EXPECT_EQ(expectedShaderType, glShaderType);
}
}

TEST_F(GLShaderFixture, GivenInvalidShaderDescriptorWhenCallingCreateShaderThenNullptrIsReturned) {
constexpr auto shaderSource = R"(
#version 460 core
void main() {}
)";

ShaderDescriptor shaderDescriptor{};
shaderDescriptor.type = ShaderType::NONE;

const auto shader = GLShader::create(shaderDescriptor, shaderSource);
EXPECT_EQ(nullptr, shader);
}

TEST_F(GLShaderFixture, GivenInvalidShaderSourceWhenCallingCreateShaderThenNullptrIsReturned) {
constexpr auto shaderSource = R"(
#version 460 core
main() {}
)";

ShaderDescriptor shaderDescriptor{};
shaderDescriptor.type = ShaderType::VERTEX;

const auto shader = GLShader::create(shaderDescriptor, shaderSource);
EXPECT_EQ(nullptr, shader);
}

0 comments on commit c50f920

Please sign in to comment.