From 93ddfb2a106fa7a18e978f36786b87ffe7394e10 Mon Sep 17 00:00:00 2001 From: xescugc Date: Thu, 18 Dec 2025 00:17:33 +0100 Subject: [PATCH] ebitest: Fixed image detector, output and 'GetAll' Also improved the output (image) by selecting what was found if the expectation was to not find it --- Makefile | 4 +- README.md | 42 ++++++++++---- docs/error_image.png | Bin 6555 -> 0 bytes docs/should.png | Bin 0 -> 8104 bytes docs/should_not.png | Bin 0 -> 7989 bytes ebitest.go | 132 +++++++++++++++++++++++-------------------- ebitest_test.go | 9 ++- utils.go | 2 +- 8 files changed, 112 insertions(+), 77 deletions(-) delete mode 100644 docs/error_image.png create mode 100644 docs/should.png create mode 100644 docs/should_not.png diff --git a/Makefile b/Makefile index 9b52d01..f9e2116 100644 --- a/Makefile +++ b/Makefile @@ -3,8 +3,8 @@ help: ## Show this help @grep -F -h "##" $(MAKEFILE_LIST) | grep -F -v grep -F | sed -e 's/:.*##/:##/' | column -t -s '##' .PHONY: test -test: ## Runs the test - @xvfb-run go test ./... $(ARGS) +test: + @xvfb-run go test ./... .PHONY: pprof pprof: ## Runs pprof server for 'cpu.out' diff --git a/README.md b/README.md index 01a3699..2483223 100644 --- a/README.md +++ b/README.md @@ -91,28 +91,43 @@ func TestGameUI(t *testing.T) { } ``` -The output of this test (that fails) is the following: +An output could be for example ``` ---- FAIL: TestGameUI (7.52s) - ebitest_test.go:28: - Error Trace: /home/xescugc/repos/ebitest/ebitest.go:113 - /home/xescugc/repos/ebitest/ebitest_test.go:28 +--- FAIL: TestGameButton (9.34s) + ebitest_test.go:39: + Error Trace: ebitest.go:150 + ebitest_test.go:39 + Error: selector found + image at: ebitest/_ebitest_dump/019b2e81-8d9a-7bb7-ba16-639756d11e58.png + Test: TestGameButton + ebitest_test.go:43: + Error Trace: ebitest.go:126 + ebitest_test.go:43 Error: selector not found - image at: _ebitest_dump/019b1537-1c60-7041-ad54-0297ea4b0eef.png - Test: TestGameUI + image at: ebitest/_ebitest_dump/019b2e81-966e-7715-8c5f-6a6acc92720c.png + Test: TestGameButton FAIL -FAIL github.com/xescugc/ebitest 7.578s +FAIL github.com/xescugc/ebitest 9.393s FAIL -make: *** [Makefile:7: test] Error 1 +make: *** [Makefile:7: test] Error ``` -And if you open the `_ebitest_dump/019b1537-1c60-7041-ad54-0297ea4b0eef.png` (on the current path) you see +And then you have the `image at: ebitest/_ebitest_dump/019b2e81-8d9a-7bb7-ba16-639756d11e58.png` that expects to not find something, and it finds +it and reports the image with the highlight of what was found. At the top right you can see what was looking for.

- +

+And the `image at: ebitest/_ebitest_dump/019b2e81-966e-7715-8c5f-6a6acc92720c.png` that expect to find something that was not found. At the top right +you can see what was looking for. + +

+ +

+ + ## Run it on a CI If the CI has low resources (like GitHub Actions) it'll most likely fail (check `Known issues#2`) but you @@ -133,6 +148,11 @@ so an expectation may randomly fail. I kind of fixed it (100 consecutive test pass) using a custom [PingPong](./ping_pong.go) and [TicTacToe](./tic_tac_toe.go) that basically forces a context switch and synchronizes Input+Game.Update+Game.Draw but it still fails in low resource like GitHub [Actions](https://github.com/xescugc/ebitest/actions) for example. +3/ Size of the screen + +By default the screen is of `640x480` if using `xvfb`. To make it bigger you can directly increase the size with `ebiten.SetWindowSize(750, 750)`, though setting big sizes +I've seen it causes some issues that the click are not where they are expected to be and are a bit off and miss which causes errors. + ## Plans * Add more helpers for assertions (like animations) diff --git a/docs/error_image.png b/docs/error_image.png deleted file mode 100644 index 6033dba101c5dca15a435e9531baa19ddece41e3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6555 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 LXWyLu?bbg4i}uaJ diff --git a/docs/should.png b/docs/should.png new file mode 100644 index 0000000000000000000000000000000000000000..680da7c96ed9928cd7f70752a6447517e18431be GIT binary patch literal 8104 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 F`8S|?Q$GLz literal 0 HcmV?d00001 diff --git a/docs/should_not.png b/docs/should_not.png new file mode 100644 index 0000000000000000000000000000000000000000..5b404201dea306c98f5e3ce370d9bd6df9bff62e GIT binary patch literal 7989 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