bitwes / Gut

Godot Unit Test. Unit testing tool for Godot Game Engine.
1.88k stars 105 forks source link

Await in Pre-Run Hook Script #670

Open LowFire opened 1 day ago

LowFire commented 1 day ago

Versions

What versions of Godot do you want to use this feature in? 4.x

The Feature

I would like the ability to await in the pre-run hook script, meaning, you can halt the execution of the tests until something happens. I don't know if this is already possible or not, but from what I could tell, it doesn't seem like you can do this. Correct me if i'm wrong.

So here's a use case example of why I think this could be useful, and it applies to the game project I'm currently working on. My game is multiplayer, so some of my tests require a multiplayer connection. I've created a test server that responds to requests from a test client. The client runs the tests. It sends requests to the server. The server receives the requests and does stuff that's related to the test that the client is currently running. Server sends results back to the client. Client runs asserts on the results. That's the short of what i'm doing.

I try to spin up my test server in the pre-run hook script, but here's the problem. I don't want to run my tests until a connection has been established with the test server. This is to prevent the possibility that a test is ran that requires a multiplayer connection before a connection has even been established. For that reason, I want to be able to pause before the tests are ran so that there is time for a connection to be made. Additionally, if for whatever reason, connection to the test server has failed, I want to be able to abort the tests using abort()

LowFire commented 1 day ago

I dug into GUT's source code and hacked it in. Here's what I did.

In gut.gd:

Added an awaiter property in gut. gut1

Add the awatier as a child of gut in the ready function. gut3PNG

Then I added the await keyword before _run_hook_script on the pre-run script. I also added await right before the run() method is called on the pre-run script. gut2

And this is what my pre-run hook script looks like: gut4

So basically, I await a signal for a certain amount of time until either a connection is made to the server, or the awaiter times out. If connection has been made, then great, we continue with the tests. If not, then we abort the tests.

Seems to work fine. I'll stick with this for the time being unless there's a better solution.

bitwes commented 19 hours ago

I think this is a good addition. This should probably also be done for the post-run script. And maybe should_skip_script per the idea I have below.

Great use of the Awaiter. Always happy to see some internal piece of GUT get reused. You could move the Awaiter creation into your hook script so you don't have to edit GUT when a new version comes out.

func run():
    var awaiter = GutUtils.Awaiter.new()
    gut.add_child(awaiter)
    ...
    awaiter.queue_free()    

If it becomes annoying to wait for the server when you don't need it, you could move your server check logic into the should_skip_script. Skipping a script will not run any tests in the script and mark it as risky in the output.

This would require adding an await into GUT when it calls should_skip_script for it to work.

You could implement should_skip_script in a MultiplayerTest class that you inherit from instead of GutTest. In this case, you can use wait_for_signal instead of making an Awaiter.

extends GutTest
class_name MultiplayerTest

func should_skip_script():
    ...
    await wait_for_signal(multiplayer_tester.connected, 10)
    return not multiplayer_tester.is_connected_to_server()
LowFire commented 18 hours ago

I think this is a good addition. This should probably also be done for the post-run script. And maybe should_skip_script per the idea I have below.

Great use of the Awaiter. Always happy to see some internal piece of GUT get reused. You could move the Awaiter creation into your hook script so you don't have to edit GUT when a new version comes out.

func run():
    var awaiter = GutUtils.Awaiter.new()
    gut.add_child(awaiter)
    ...
    awaiter.queue_free()    

If it becomes annoying to wait for the server when you don't need it, you could move your server check logic into the should_skip_script. Skipping a script will not run any tests in the script and mark it as risky in the output.

This would require adding an await into GUT when it calls should_skip_script for it to work.

You could implement should_skip_script in a MultiplayerTest class that you inherit from instead of GutTest. In this case, you can use wait_for_signal instead of making an Awaiter.

extends GutTest
class_name MultiplayerTest

func should_skip_script():
    ...
    await wait_for_signal(multiplayer_tester.connected, 10)
    return not multiplayer_tester.is_connected_to_server()

These are great ideas. I'll do this instead. Thank you for suggest this.