MikeSchulze / gdUnit4Net

A Godot C# Unit Test Framework.
MIT License
61 stars 5 forks source link

GD-156: Add an exception handler to report exceptions as test failures that are caught by Godot #156

Closed MikeSchulze closed 1 week ago

MikeSchulze commented 2 weeks ago

Discussed in https://github.com/MikeSchulze/gdUnit4Net/discussions/148

Originally posted by **bjkarasek** November 1, 2024 In documentation (https://mikeschulze.github.io/gdUnit4/testing/assert-error/) it seems that assert-error is not supported in C#. Does that mean there is no way to make tests fail when you print GD.PrintErr or GD.PushErr? Is there a workaround where I can "log" something that will make the tests fail?

Background

By default, Godot intercepts all exceptions triggered by the runtime within the scene tree processing in order to avoid unexpected program crashes. All caught exceptions are just logged as error in the Godot log.

GodotSharp.dll

 catch (Exception ex)
      {
        ExceptionUtils.LogException(ex);
        *outRet = new godot_variant();
        return godot_bool.False;
      }

      ...

    public static void LogException(Exception e)
    {
      try
      {
        if (NativeFuncs.godotsharp_internal_script_debugger_is_active().ToBool())
          ExceptionUtils.SendToScriptDebugger(e);
        else
          GD.PushError(e.ToString());
      }
      catch (Exception ex)
      {
        ExceptionUtils.OnExceptionLoggerException(ex, e);
      }
    }

Install an exception hook

We can hook into the Godot exception to add a custom exception handler that will cause a test to fail. The test should fail with the stacktrace to the original exception

Update

We do now monitor thrown exception in combination of Godot log parsing to detect uncaught exceptions. To enable the exception monitoring, there are now two new attributes as can be used.

* ThrowsException
  Expects the test is failing by an uncaught exception
```csharp
    [TestCase]
    [ThrowsException(typeof(InvalidOperationException), "Test Exception",
        "src/core/resources/scenes/TestSceneWithExceptionTest.cs", 14)]
    public void CatchExceptionIsThrownOnSceneInvoke()
    {
        var runner = ISceneRunner.Load("res://src/core/resources/scenes/TestSceneWithExceptionTest.tscn");

        runner.Invoke("SomeMethodThatThrowsException");
    }
MikeSchulze commented 2 weeks ago

Reopen, because of correct caught exceptions are reported as failure.

  try
        {
            if (frameCount == 5)
                throw new InvalidProgramException("Exception during scene processing inside a catch block");
        }
        catch (InvalidProgramException)
        {
            // ignore
        }