From a6c5d2a73963011e9dd44f9235e98b500a174e94 Mon Sep 17 00:00:00 2001 From: ganon Date: Sun, 13 Apr 2025 01:25:15 -0400 Subject: [PATCH] poc run C# tests --- .gutconfig.json | 3 +- Gut.csproj | 8 + Gut.sln | 19 ++ addons/gut/CSharpScriptInspector.cs | 68 ++++++++ addons/gut/GutRunner.cs | 67 ++++++++ addons/gut/GutTest.cs | 162 ++++++++++++++++++ addons/gut/gut.gd | 16 +- addons/gut/test_collector.gd | 8 +- addons/gut/utils.gd | 1 - project.godot | 7 +- .../panel_demo_scripts/test_totals_testing.gd | 1 - test/samples/cs/TestCSharp.cs | 22 +++ test/samples/test_readme_examples.gd | 2 - 13 files changed, 374 insertions(+), 10 deletions(-) create mode 100644 Gut.csproj create mode 100644 Gut.sln create mode 100644 addons/gut/CSharpScriptInspector.cs create mode 100644 addons/gut/GutRunner.cs create mode 100644 addons/gut/GutTest.cs create mode 100644 test/samples/cs/TestCSharp.cs diff --git a/.gutconfig.json b/.gutconfig.json index 6b682b9a..e1455cbb 100644 --- a/.gutconfig.json +++ b/.gutconfig.json @@ -3,8 +3,7 @@ "include_subdirs": true, "dirs": [ - "res://test/unit/", - "res://test/integration/" + "res://test/samples/cs/" ], "should_exit": true, "log_level": 3, diff --git a/Gut.csproj b/Gut.csproj new file mode 100644 index 00000000..53cc1f18 --- /dev/null +++ b/Gut.csproj @@ -0,0 +1,8 @@ + + + net6.0 + net7.0 + net8.0 + true + + \ No newline at end of file diff --git a/Gut.sln b/Gut.sln new file mode 100644 index 00000000..a6b04736 --- /dev/null +++ b/Gut.sln @@ -0,0 +1,19 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Gut", "Gut.csproj", "{AED1B5D9-B5A4-4898-9EE6-55B809D51881}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + ExportDebug|Any CPU = ExportDebug|Any CPU + ExportRelease|Any CPU = ExportRelease|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {AED1B5D9-B5A4-4898-9EE6-55B809D51881}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AED1B5D9-B5A4-4898-9EE6-55B809D51881}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AED1B5D9-B5A4-4898-9EE6-55B809D51881}.ExportDebug|Any CPU.ActiveCfg = ExportDebug|Any CPU + {AED1B5D9-B5A4-4898-9EE6-55B809D51881}.ExportDebug|Any CPU.Build.0 = ExportDebug|Any CPU + {AED1B5D9-B5A4-4898-9EE6-55B809D51881}.ExportRelease|Any CPU.ActiveCfg = ExportRelease|Any CPU + {AED1B5D9-B5A4-4898-9EE6-55B809D51881}.ExportRelease|Any CPU.Build.0 = ExportRelease|Any CPU + EndGlobalSection +EndGlobal diff --git a/addons/gut/CSharpScriptInspector.cs b/addons/gut/CSharpScriptInspector.cs new file mode 100644 index 00000000..f1c5943a --- /dev/null +++ b/addons/gut/CSharpScriptInspector.cs @@ -0,0 +1,68 @@ +using Godot; +using System; +using System.Reflection; + +// namespace GutCSharp +// { + /// + /// Utility class to handle inspection of C# scripts for the GUT testing framework. + /// This provides reflection-based methods that help GUT understand C# class inheritance. + /// + public partial class CSharpScriptInspector : RefCounted + { + /// + /// Checks if a C# type inherits from GutTest + /// + /// The Type to check + /// True if the type inherits from GutTest + private bool TypeInheritsFromGutTest(Type type) + { + if (type == null) + return false; + + // If this is the GutTest class itself (base case for successful inheritance) + if (type.FullName == "GutCSharp.GutTest") + { + GD.Print("FULL NAME: ", type.FullName); + return true; + } + + // Check the base type + return TypeInheritsFromGutTest(type.BaseType); + } + + /// + /// Determines if a CSharpScript inherits from GutTest + /// + /// The CSharpScript to check + /// True if the script inherits from GutTest + public bool InheritsFromTest(CSharpScript script) + { + + if (script == null) + return false; + Variant instance = script.New(); + var tryCast = instance.As(); + + return tryCast != null; + } + + /// + /// Determines if an object instance inherits from GutTest + /// + /// The object instance to check + /// True if the object inherits from GutTest + public bool InheritsFromTest(object instance) + { + + GD.Print("here"); + + + if (instance == null) + return false; + + + return TypeInheritsFromGutTest(instance.GetType()); + } + } +// } diff --git a/addons/gut/GutRunner.cs b/addons/gut/GutRunner.cs new file mode 100644 index 00000000..eaae9759 --- /dev/null +++ b/addons/gut/GutRunner.cs @@ -0,0 +1,67 @@ +using Godot; +using System; +using System.Reflection; +using System.Collections.Generic; + +namespace GutCSharp +{ + public partial class GutCSharpRunner : RefCounted + { + private Node _gut; + + public GutCSharpRunner(Node gut) + { + _gut = gut; + } + + public void RunTests(string scriptPath) + { + // Load the C# script/class + var script = GD.Load(scriptPath); + if (script == null) return; + + var instance = script.New().As(); + if (instance == null) return; + + instance.gut = _gut; + + // Get all methods from the instance + Type type = instance.GetType(); + MethodInfo[] methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); + + // Filter for test methods (prefixed with "Test") + List testMethods = new List(); + foreach (var method in methods) + { + if (method.Name.StartsWith("Test") && method.GetParameters().Length == 0) + { + testMethods.Add(method); + } + } + + // Run lifecycle and test methods + try + { + instance.BeforeAll(); + + foreach (var testMethod in testMethods) + { + string testName = testMethod.Name; + _gut.Call("_pre_run_test", testName); + + instance.BeforeEach(); + testMethod.Invoke(instance, null); + instance.AfterEach(); + + _gut.Call("_post_run_test"); + } + + instance.AfterAll(); + } + catch (Exception e) + { + _gut.Call("_fail", "Error in test: " + e.Message); + } + } + } +} diff --git a/addons/gut/GutTest.cs b/addons/gut/GutTest.cs new file mode 100644 index 00000000..4768f9ee --- /dev/null +++ b/addons/gut/GutTest.cs @@ -0,0 +1,162 @@ +using Godot; +using System; +using System.Reflection; + +// namespace GutCSharp +// { + public partial class GutTest : Godot.Node + { + // Public property that proxies to _gdGutTest + public Node gut + { + get + { + if (_gdGutTest != null) + { + return (Node)_gdGutTest.Get("gut"); + } + return null; + } + set + { + if (_gdGutTest != null) + { + _gdGutTest.Set("gut", value); + } + } + } + + // Reference to GDScript GutTest instance that will handle the actual implementation + private Node _gdGutTest; + + // Flag to track if ready was called + public bool _was_ready_called + { + get + { + if (_gdGutTest != null) + { + return (bool)_gdGutTest.Get("_was_ready_called"); + } + return false; + } + } + + // Constructor - creates the GDScript GutTest instance + public GutTest() + { + // Load the GDScript GutTest scene/script + var gdTestScript = GD.Load