5

I am trying to unit test the core package of my libgdx application.

What is the best way to mock the ShaderProgram such that the root class may be tested?

Given the following init for a Libgdx test runner,

init {
    val conf = HeadlessApplicationConfiguration()

    HeadlessApplication(this, conf)
    Gdx.gl = mock(GL20::class.java) 
    Gdx.gl20 = mock(GL20::class.java)
    Gdx.gl30 = mock(GL30::class.java)
    Gdx.graphics = mock(Graphics::class.java)
    `when`(Gdx.graphics.height).thenReturn(dimensions)
    `when`(Gdx.graphics.width).thenReturn(dimensions)
}

and the function under test (which is in a class that is a child of Application Listener),

override fun create() {
    ...
    stage = Stage(ScreenViewport())
    ...
}

an error occurs inside of Stage when it attempts to compile a shader.

I.e., in SpriteBatch.java from com.badlogic.gdx.graphics.g2d,

ShaderProgram shader = new ShaderProgram(vertexShader, fragmentShader);
if (shader.isCompiled() == false) throw new IllegalArgumentException("Error compiling shader: " + shader.getLog());

shader.isCompiled() appears to always return false for a HeadlessApplication.

Tim F.
  • 290
  • 2
  • 16

1 Answers1

2

Since there is no answer so far I want to share my current knowledge/opinion:

First, we need to ask the question: Can we even write unit tests for the GUI in general? If you want an in-depth answer take a look at this question. To sum up: You can unit test your GUI by calculating hashes from framebuffers but the general recommendation is to move as much logic out of your GUI as possible and don't unit test your GUI at all. In addition to that my and most Servers that are running the code have no hardware support for OpenGL. Assuming we don't want to bother with Softwarerendering, unit testing the GUI itself is no option.

Based on this information I assumed my logic wasn't separated well enough from my GUI. But when looking further into scene2d and a lot of tutorials around it, it became pretty clear that scene2d wants you by design to combine logic and GUI parts of your code, see this question for reference.

Assuming you share the opinion that scene2d makes it pretty hard to separate your logic and GUI we are now facing the op's question while common solutions are not applicable.

In my opinion, all elements of libGDX should be fully compatible with the HeadlessBackend, that they are not is a design flaw by libGDX. The only workaround I came up with so far is by mocking a SpriteBatch if needed and passing it to the Stage:

val stage = Stage(myViewport, if (isHeadless) mock(SpriteBatch::class.java) else SpriteBatch())

While this allows you to use your Stage normally, I don't consider it a real solution since you need to write your code test aware.

Crigges
  • 1,083
  • 2
  • 15
  • 28