hajimehoshi/ebiten

debug: Snapshot testing

greenthepear opened this issue · 2 comments

Operating System

  • Windows
  • macOS
  • Linux
  • FreeBSD
  • OpenBSD
  • Android
  • iOS
  • Nintendo Switch
  • PlayStation 5
  • Xbox
  • Web Browsers

What feature would you like to be added?

Debugging/testing tools to make snapshot testing easier, possibly by exposing some of the internal code used for /image_test.go.

Why is this needed?

Writing snapshot tests that work with go test for generated images is quite tricky because the game loop needs to be running for ebiten.Image methods like .At() to be accessed. I had to open an issue for my framework to try to get some help.

Projects that have achieved it mention how hard it is: https://github.com/tinne26/etxt/blob/main/testdata_generate.go - this file's comments are insightful about the subject:

// Ebitengine is hard to test with "go test" because you need to create
// standalone programs with a main function and so on. There are multiple
// ways to work around that, and the truth is that for testing etxt there's
// already the gtxt CPU version that allows achieving high test coverage
// with a fairly decent degree of reliability. Still, in order to make sure
// that the gtxt version (CPU rendering) and the default Ebitengine version
// (GPU rendering [notice that rasterization still happens on CPU]) results
// are matching, we can use go:generate to run standalone Ebitengine programs,
// get some raw image results, plug that into basic tests and print it all
// as static test files.

With the number of frameworks and custom libraries people make for ebitengine, a simple way to make sure everything renders right I feel is a must.

You can run tests by TestMain:

type game struct {
	m    *testing.M
	code int
}

func (g *game) Update() error {
	g.code = g.m.Run()
	return ebiten.Termination
}

func (*game) Draw(*ebiten.Image) {
}

func (*game) Layout(int, int) (int, int) {
	return 320, 240
}

func TestMain(m *testing.M) {
	g := &game{
		m:    m,
		code: 1,
	}
	if err := ebiten.RunGame(g); err != nil {
		panic(err)
	}
	os.Exit(g.code)
}

As the above solution seems enough to you, let me close this.