diff --git a/.github/workflows/selftest-3.3.x-mono.yml b/.github/workflows/selftest-3.3.x-mono.yml
new file mode 100644
index 00000000..6aa79982
--- /dev/null
+++ b/.github/workflows/selftest-3.3.x-mono.yml
@@ -0,0 +1,39 @@
+name: Run selftest Godot 3.3.x - Mono
+on: [push]
+
+jobs:
+ testing:
+ strategy:
+ matrix:
+ godot: [mono-3.3.1, mono-3.3.2, mono-3.3.3]
+
+ name: GdUnit3 Selftest on Godot ${{ matrix.godot }}
+ runs-on: ubuntu-latest
+ container:
+ image: barichello/godot-ci:${{ matrix.godot }}
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v2
+ with:
+ lfs: true
+
+ - name: Compile
+ run: |
+ nuget restore
+ mkdir -p .mono/assemblies/Debug
+ cp /usr/local/bin/GodotSharp/Api/Release/* .mono/assemblies/Debug
+ msbuild
+
+ - name: Run Selftes
+ timeout-minutes: 5
+ env:
+ GODOT_BIN: "/usr/local/bin/godot"
+ shell: bash
+ run: ./runtest.sh --selftest
+
+ - name: Collect Test Reports
+ uses: actions/upload-artifact@v2
+ with:
+ name: Report_${{ matrix.godot }}
+ path: reports/**
diff --git a/.github/workflows/selftest-3.3.x.yml b/.github/workflows/selftest-3.3.x.yml
index 950797d4..14e1d858 100644
--- a/.github/workflows/selftest-3.3.x.yml
+++ b/.github/workflows/selftest-3.3.x.yml
@@ -5,8 +5,8 @@ jobs:
testing:
strategy:
matrix:
- godot: [3.3, 3.3.1, 3.3.2, 3.3.3, mono-3.3.3]
-
+ godot: [3.3, 3.3.1, 3.3.2, 3.3.3]
+
name: GdUnit3 Selftest on Godot ${{ matrix.godot }}
runs-on: ubuntu-latest
container:
@@ -20,15 +20,14 @@ jobs:
- name: Setup
shell: bash
run: echo "GODOT_BIN=/usr/local/bin/godot" >> $GITHUB_ENV
-
+
- name: Run Selftes
shell: bash
run: ./runtest.sh --selftest
-
+
- name: Collect Test Report
if: always()
uses: actions/upload-artifact@v2
with:
name: Report_${{ matrix.godot }}
path: reports/**
-
diff --git a/addons/gdUnit3/bin/GdUnitCmdTool.gd b/addons/gdUnit3/bin/GdUnitCmdTool.gd
index 31a625a9..8496b0d2 100644
--- a/addons/gdUnit3/bin/GdUnitCmdTool.gd
+++ b/addons/gdUnit3/bin/GdUnitCmdTool.gd
@@ -67,7 +67,7 @@ class CLIRunner extends Node:
_state = STOP
else:
# process next test suite
- var test_suite := _test_suites_to_process.pop_front() as GdUnitTestSuite
+ var test_suite := _test_suites_to_process.pop_front() as GdUnitTestSuiteDelegator
var fs = _executor.execute(test_suite)
if fs is GDScriptFunctionState:
yield(fs, "completed")
@@ -201,7 +201,7 @@ class CLIRunner extends Node:
for test_suite in test_suites:
skip_suite(test_suite, skipped)
- func skip_suite(test_suite :GdUnitTestSuite, skipped :Dictionary) -> void:
+ func skip_suite(test_suite :GdUnitTestSuiteDelegator, skipped :Dictionary) -> void:
var skipped_suites := skipped.keys()
if skipped_suites.empty():
return
@@ -217,7 +217,7 @@ class CLIRunner extends Node:
else:
# skip tests
for test_to_skip in skipped_tests:
- var test_case :_TestCase = test_suite.find_node(test_to_skip, true, false)
+ var test_case :_TestCase = test_suite.get_test_case_by_name(test_to_skip)
if test_case:
test_case.skip(true)
else:
@@ -244,13 +244,14 @@ class CLIRunner extends Node:
_report.add_testsuite_report(GdUnitTestSuiteReport.new(event.resource_path(), event.suite_name()))
GdUnitEvent.TESTSUITE_AFTER:
- _report.update_test_suite_report(event.suite_name(), event.skipped_count(), event.orphan_nodes(), event.elapsed_time())
+ _report.update_test_suite_report(event.resource_path(), event.skipped_count(), event.orphan_nodes(), event.elapsed_time())
GdUnitEvent.TESTCASE_BEFORE:
- _report.add_testcase_report(event.suite_name(), GdUnitTestCaseReport.new(event.test_name()))
+ _report.add_testcase_report(event.resource_path(), GdUnitTestCaseReport.new(event.resource_path(), event.test_name()))
GdUnitEvent.TESTCASE_AFTER:
var test_report := GdUnitTestCaseReport.new(
+ event.resource_path(),
event.test_name(),
event.is_error(),
event.is_failed(),
@@ -258,7 +259,7 @@ class CLIRunner extends Node:
event.skipped_count(),
event.reports(),
event.elapsed_time())
- _report.update_testcase_report(event.suite_name(), test_report)
+ _report.update_testcase_report(event.resource_path(), test_report)
print_status(event)
func report_exit_code(report :GdUnitHtmlReport) -> int:
diff --git a/addons/gdUnit3/plugin.gd b/addons/gdUnit3/plugin.gd
index 322884eb..6e5bda4e 100644
--- a/addons/gdUnit3/plugin.gd
+++ b/addons/gdUnit3/plugin.gd
@@ -13,7 +13,7 @@ func _enter_tree():
# show possible update notification when is enabled
if GdUnitSettings.is_update_notification_enabled():
_update_tool = load("res://addons/gdUnit3/src/update/GdUnitUpdate.tscn").instance()
- get_parent().add_child(_update_tool)
+ add_child(_update_tool)
# install SignalHandler singleton
GdUnitSingleton.add_singleton(SignalHandler.SINGLETON_NAME, "res://addons/gdUnit3/src/core/event/SignalHandler.gd")
diff --git a/addons/gdUnit3/src/GdUnitStringAssert.gd b/addons/gdUnit3/src/GdUnitStringAssert.gd
index 6c7e01f9..ce5fecc3 100644
--- a/addons/gdUnit3/src/GdUnitStringAssert.gd
+++ b/addons/gdUnit3/src/GdUnitStringAssert.gd
@@ -51,5 +51,5 @@ func ends_with(expected: String) -> GdUnitStringAssert:
return self
# Verifies that the current String has the expected length by used comparator.
-func has_length(lenght: int, comparator: int = Comparator.EXACTLY) -> GdUnitStringAssert:
+func has_length(lenght: int, comparator: int = Comparator.EQUAL) -> GdUnitStringAssert:
return self
diff --git a/addons/gdUnit3/src/GdUnitTestSuite.cs b/addons/gdUnit3/src/GdUnitTestSuite.cs
new file mode 100644
index 00000000..25c684fd
--- /dev/null
+++ b/addons/gdUnit3/src/GdUnitTestSuite.cs
@@ -0,0 +1,151 @@
+using Godot;
+using System;
+
+namespace GdUnit3
+{
+
+ /**
+ This class is the main class to implement your unit tests
+ You have to extend and implement your test cases as described
+ e.g
+
+ For detailed instructions see HERE
+
+ For example:
+
+
+ public class MyExampleTest : GdUnit3.GdUnitTestSuite
+ {
+ public void test_testCaseA()
+ {
+ assertThat("value").isEqual("value");
+ }
+ }
+
+
+ */
+ public abstract class GdUnitTestSuite : Node
+ {
+
+
+ [AttributeUsage(AttributeTargets.Class)]
+ public class TestSuiteAttribute : Attribute
+ {
+ }
+
+ [AttributeUsage(AttributeTargets.Method)]
+ public class TestCaseAttribute : Attribute
+ {
+ public readonly int Timeout = -1;
+ public readonly int Line;
+
+ public TestCaseAttribute([System.Runtime.CompilerServices.CallerLineNumber] int line = 0)
+ {
+ Line = line;
+ }
+ }
+
+
+ private bool _scipped = false;
+ private String _active_test_case;
+
+ private static Godot.Resource GdUnitTools = (Resource)GD.Load("res://addons/gdUnit3/src/core/GdUnitTools.gd").New();
+
+
+
+ ~GdUnitTestSuite()
+ {
+ }
+
+ ///
+ /// This function is called before a test suite starts
+ /// You can overwrite to prepare test data or initalizize necessary variables
+ ///
+ public virtual void Before() { }
+
+ // This function is called at least when a test suite is finished
+ // You can overwrite to cleanup data created during test running
+ public virtual void After() { }
+
+ // This function is called before a test case starts
+ // You can overwrite to prepare test case specific data
+ public virtual void BeforeTest() { }
+
+ // This function is called after the test case is finished
+ // You can overwrite to cleanup your test case specific data
+ public virtual void AfterTest() { }
+
+ // Skip the test-suite from execution, it will be ignored
+ public void skip(bool skipped) => _scipped = skipped;
+
+ public bool is_skipped => _scipped;
+
+ public void set_active_test_case(String test_case) => _active_test_case = test_case;
+
+ // === Tools ====================================================================
+ // Mapps Godot error number to a readable error message. See at ERROR
+ // https://docs.godotengine.org/de/stable/classes/class_@globalscope.html#enum-globalscope-error
+ public String error_as_string(int error_number)
+ {
+ return (String)GdUnitTools.Call("error_as_string", error_number);
+ }
+
+ // A litle helper to auto freeing your created objects after test execution
+ public T auto_free(T obj)
+ {
+ GdUnitTools.Call("register_auto_free", obj, GetMeta("MEMORY_POOL"));
+ return obj;
+ }
+
+ // Discard the error message triggered by a timeout (interruption).
+ // By default, an interrupted test is reported as an error.
+ // This function allows you to change the message to Success when an interrupted error is reported.
+ public void discard_error_interupted_by_timeout()
+ {
+ //GdUnitTools.register_expect_interupted_by_timeout(self, __active_test_case)
+ }
+
+ // Creates a new directory under the temporary directory *user://tmp*
+ // Useful for storing data during test execution.
+ // The directory is automatically deleted after test suite execution
+ public String create_temp_dir(String relative_path)
+ {
+ //return GdUnitTools.create_temp_dir(relative_path)
+ return "";
+ }
+
+ // Deletes the temporary base directory
+ // Is called automatically after each execution of the test suite
+ public void clean_temp_dir()
+ {
+ //GdUnitTools.clear_tmp()
+ }
+
+ // === Asserts ==================================================================
+ public IGdUnitBoolAssert AssertBool(bool current, IGdUnitAssert.EXPECT expectResult = IGdUnitAssert.EXPECT.SUCCESS)
+ {
+ return new GdUnitBoolAssertWrapper(this, current, expectResult);
+ }
+
+ public IGdUnitStringAssert AssertString(string current, IGdUnitAssert.EXPECT expectResult = IGdUnitAssert.EXPECT.SUCCESS)
+ {
+ return new GdUnitStringAssertWrapper(this, current, expectResult);
+ }
+
+ public IGdUnitIntAssert AssertInt(int current, IGdUnitAssert.EXPECT expectResult = IGdUnitAssert.EXPECT.SUCCESS)
+ {
+ return new GdUnitIntAssertWrapper(this, current, expectResult);
+ }
+
+ public IGdUnitFloatAssert AssertFloat(double current, IGdUnitAssert.EXPECT expectResult = IGdUnitAssert.EXPECT.SUCCESS)
+ {
+ return new GdUnitFloatAssertWrapper(this, current, expectResult);
+ }
+
+ public IGdUnitObjectAssert AssertObject(object current, IGdUnitAssert.EXPECT expectResult = IGdUnitAssert.EXPECT.SUCCESS)
+ {
+ return new GdUnitObjectAssertWrapper(this, current, expectResult);
+ }
+ }
+
+}
diff --git a/addons/gdUnit3/src/IGdUnitAssert.cs b/addons/gdUnit3/src/IGdUnitAssert.cs
new file mode 100644
index 00000000..8fec5457
--- /dev/null
+++ b/addons/gdUnit3/src/IGdUnitAssert.cs
@@ -0,0 +1,48 @@
+using System;
+using System.ComponentModel;
+
+namespace GdUnit3
+{
+
+ /// Main interface of all GdUnit asserts
+ public interface IGdUnitAssert
+ {
+
+ enum EXPECT : int
+ {
+ [Description("assert expects ends with success")]
+ SUCCESS = 0,
+ [Description("assert expects ends with errors")]
+ FAIL = 1
+ }
+ }
+
+ /// Base interface of all GdUnit asserts
+ public interface IGdUnitAssertBase : IGdUnitAssert
+ {
+
+ /// Verifies that the current value is null.
+ IGdUnitAssertBase IsNull();
+
+ /// Verifies that the current value is not null.
+ IGdUnitAssertBase IsNotNull();
+
+ /// Verifies that the current value is equal to expected one.
+ IGdUnitAssertBase IsEqual(V expected);
+
+ /// Verifies that the current value is not equal to expected one.
+ IGdUnitAssertBase IsNotEqual(V expected);
+
+ ///
+ IGdUnitAssertBase TestFail();
+
+ /// Verifies the failure message is equal to expected one.
+ IGdUnitAssertBase HasFailureMessage(string expected);
+
+ /// Verifies that the failure starts with the given value.
+ IGdUnitAssertBase StartsWithFailureMessage(string value);
+
+ /// Overrides the default failure message by given custom message.
+ IGdUnitAssertBase OverrideFailureMessage(string message);
+ }
+}
diff --git a/addons/gdUnit3/src/IGdUnitBoolAssert.cs b/addons/gdUnit3/src/IGdUnitBoolAssert.cs
new file mode 100644
index 00000000..5e016da8
--- /dev/null
+++ b/addons/gdUnit3/src/IGdUnitBoolAssert.cs
@@ -0,0 +1,18 @@
+using Godot;
+using System;
+
+namespace GdUnit3
+{
+
+ /// An Assertion Tool to verify boolean values
+ public interface IGdUnitBoolAssert : IGdUnitAssertBase
+ {
+
+ /// Verifies that the current value is true.
+ IGdUnitBoolAssert IsTrue();
+
+ /// Verifies that the current value is false.
+ IGdUnitBoolAssert IsFalse();
+
+ }
+}
\ No newline at end of file
diff --git a/addons/gdUnit3/src/IGdUnitFloatAssert.cs b/addons/gdUnit3/src/IGdUnitFloatAssert.cs
new file mode 100644
index 00000000..a170f3fa
--- /dev/null
+++ b/addons/gdUnit3/src/IGdUnitFloatAssert.cs
@@ -0,0 +1,9 @@
+namespace GdUnit3
+{
+
+ /// Base interface for integer assertions.
+ public interface IGdUnitFloatAssert : IGdUnitNumberAssert
+ {
+
+ }
+}
diff --git a/addons/gdUnit3/src/IGdUnitIntAssert.cs b/addons/gdUnit3/src/IGdUnitIntAssert.cs
new file mode 100644
index 00000000..d28e5706
--- /dev/null
+++ b/addons/gdUnit3/src/IGdUnitIntAssert.cs
@@ -0,0 +1,9 @@
+namespace GdUnit3
+{
+
+ /// Base interface for integer assertions.
+ public interface IGdUnitIntAssert : IGdUnitNumberAssert
+ {
+
+ }
+}
diff --git a/addons/gdUnit3/src/IGdUnitNumberAssert.cs b/addons/gdUnit3/src/IGdUnitNumberAssert.cs
new file mode 100644
index 00000000..9a15983a
--- /dev/null
+++ b/addons/gdUnit3/src/IGdUnitNumberAssert.cs
@@ -0,0 +1,62 @@
+using System;
+
+namespace GdUnit3
+{
+
+ /// Base interface for number assertions.
+ public interface IGdUnitNumberAssert : IGdUnitAssertBase
+ {
+
+ /// Verifies that the current value is less than the given one.
+ public IGdUnitNumberAssert IsLess(V expected);
+
+
+ /// Verifies that the current value is less than or equal the given one.
+ public IGdUnitNumberAssert IsLessEqual(V expected);
+
+
+ /// Verifies that the current value is greater than the given one.
+ public IGdUnitNumberAssert IsGreater(V expected);
+
+
+ /// Verifies that the current value is greater than or equal the given one.
+ public IGdUnitNumberAssert IsGreaterEqual(V expected);
+
+
+ /// Verifies that the current value is even.
+ public IGdUnitNumberAssert IsEven();
+
+
+ /// Verifies that the current value is odd.
+ public IGdUnitNumberAssert IsOdd();
+
+
+ /// Verifies that the current value is negative.
+ public IGdUnitNumberAssert IsNegative();
+
+
+ /// Verifies that the current value is not negative.
+ public IGdUnitNumberAssert IsNotNegative();
+
+
+ /// Verifies that the current value is equal to zero.
+ public IGdUnitNumberAssert IsZero();
+
+
+ /// Verifies that the current value is not equal to zero.
+ public IGdUnitNumberAssert IsNotZero();
+
+
+ /// Verifies that the current value is in the given set of values.
+ public IGdUnitNumberAssert IsIn(Array expected);
+
+
+ /// Verifies that the current value is not in the given set of values.
+ public IGdUnitNumberAssert IsNotIn(Array expected);
+
+
+ /// Verifies that the current value is between the given boundaries (inclusive).
+ public IGdUnitNumberAssert IsBetween(V from, V to);
+
+ }
+}
diff --git a/addons/gdUnit3/src/IGdUnitObjectAssert.cs b/addons/gdUnit3/src/IGdUnitObjectAssert.cs
new file mode 100644
index 00000000..06b32f04
--- /dev/null
+++ b/addons/gdUnit3/src/IGdUnitObjectAssert.cs
@@ -0,0 +1,23 @@
+using Godot;
+using System;
+
+namespace GdUnit3
+{
+
+ /// An Assertion Tool to verify object values
+ public interface IGdUnitObjectAssert : IGdUnitAssertBase
+ {
+ // Verifies that the current value is the same as the given one.
+ public IGdUnitObjectAssert IsSame(object expected);
+
+ // Verifies that the current value is not the same as the given one.
+ public IGdUnitObjectAssert IsNotSame(object expected);
+
+ // Verifies that the current value is an instance of the given type.
+ public IGdUnitObjectAssert IsInstanceof();
+
+ // Verifies that the current value is not an instance of the given type.
+ public IGdUnitObjectAssert IsNotInstanceof();
+
+ }
+}
diff --git a/addons/gdUnit3/src/IGdUnitStringAssert.cs b/addons/gdUnit3/src/IGdUnitStringAssert.cs
new file mode 100644
index 00000000..244884d0
--- /dev/null
+++ b/addons/gdUnit3/src/IGdUnitStringAssert.cs
@@ -0,0 +1,55 @@
+using Godot;
+using System;
+
+namespace GdUnit3
+{
+
+ /// An Assertion Tool to verify string values
+ public interface IGdUnitStringAssert : IGdUnitAssertBase
+ {
+ enum Compare
+ {
+ EQUAL,
+ LESS_THAN,
+ LESS_EQUAL,
+ GREATER_THAN,
+ GREATER_EQUAL,
+ BETWEEN_EQUAL,
+ NOT_BETWEEN_EQUAL,
+ }
+
+ /// Verifies that the current String is equal to the given one, ignoring case considerations.
+ public IGdUnitStringAssert IsEqualIgnoringCase(string expected);
+
+ /// Verifies that the current String is not equal to the given one, ignoring case considerations.
+ public IGdUnitStringAssert IsNotEqualIgnoringCase(string expected);
+
+ /// Verifies that the current String is empty, it has a length of 0.
+ public IGdUnitStringAssert IsEmpty();
+
+ /// Verifies that the current String is not empty, it has a length of minimum 1.
+ public IGdUnitStringAssert IsNotEmpty();
+
+ /// Verifies that the current String contains the given String.
+ public IGdUnitStringAssert Contains(string expected);
+
+ /// Verifies that the current String does not contain the given String.
+ public IGdUnitStringAssert NotContains(string expected);
+
+ /// Verifies that the current String does not contain the given String, ignoring case considerations.
+ public IGdUnitStringAssert ContainsIgnoringCase(string expected);
+
+ /// Verifies that the current String does not contain the given String, ignoring case considerations.
+ public IGdUnitStringAssert NotContainsIgnoringCase(string expected);
+
+ /// Verifies that the current String starts with the given prefix.
+ public IGdUnitStringAssert StartsWith(string expected);
+
+ /// Verifies that the current String ends with the given suffix.
+ public IGdUnitStringAssert EndsWith(string expected);
+
+ /// Verifies that the current String has the expected length by used comparator.
+ public IGdUnitStringAssert HasLength(int lenght, Compare comparator = Compare.EQUAL);
+
+ }
+}
diff --git a/addons/gdUnit3/src/asserts/GdUnitAssertBase.cs b/addons/gdUnit3/src/asserts/GdUnitAssertBase.cs
new file mode 100644
index 00000000..fd51a3e0
--- /dev/null
+++ b/addons/gdUnit3/src/asserts/GdUnitAssertBase.cs
@@ -0,0 +1,66 @@
+using Godot;
+using System;
+
+namespace GdUnit3
+{
+ public abstract class GdUnitAssertBase : IGdUnitAssertBase
+ {
+
+ protected readonly Godot.Reference _delegator;
+ protected readonly object _current;
+
+ protected GdUnitAssertBase(Godot.Reference delegator, object current = null)
+ {
+ _delegator = delegator;
+ _current = current;
+ }
+
+ public IGdUnitAssertBase HasFailureMessage(string expected)
+ {
+ _delegator.Call("has_failure_message", expected);
+ return this;
+ }
+
+ public IGdUnitAssertBase IsEqual(V expected)
+ {
+ _delegator.Call("is_equal", expected);
+ return this;
+ }
+
+ public IGdUnitAssertBase IsNotEqual(V expected)
+ {
+ _delegator.Call("is_not_equal", expected);
+ return this;
+ }
+
+ public IGdUnitAssertBase IsNotNull()
+ {
+ _delegator.Call("is_not_null");
+ return this;
+ }
+
+ public IGdUnitAssertBase IsNull()
+ {
+ _delegator.Call("is_null");
+ return this;
+ }
+
+ public IGdUnitAssertBase OverrideFailureMessage(string message)
+ {
+ _delegator.Call("override_failure_message", message);
+ return this;
+ }
+
+ public IGdUnitAssertBase StartsWithFailureMessage(string value)
+ {
+ _delegator.Call("starts_with_failure_message");
+ return this;
+ }
+
+ public IGdUnitAssertBase TestFail()
+ {
+ _delegator.Call("test_fail");
+ return this;
+ }
+ }
+}
diff --git a/addons/gdUnit3/src/asserts/GdUnitAssertImpl.gd b/addons/gdUnit3/src/asserts/GdUnitAssertImpl.gd
index ea2c9544..868705f2 100644
--- a/addons/gdUnit3/src/asserts/GdUnitAssertImpl.gd
+++ b/addons/gdUnit3/src/asserts/GdUnitAssertImpl.gd
@@ -37,6 +37,7 @@ static func _get_line_number() -> int:
func _init(caller :Object, current, expect_result :int = EXPECT_SUCCESS):
assert(caller != null, "missing argument caller!")
assert(caller.has_meta(GdUnitReportConsumer.META_PARAM), "caller must register a report consumer!")
+
_report_consumer = weakref(caller.get_meta(GdUnitReportConsumer.META_PARAM))
_current = current
# we expect the test will fail
diff --git a/addons/gdUnit3/src/asserts/GdUnitBoolAssertImpl.cs b/addons/gdUnit3/src/asserts/GdUnitBoolAssertImpl.cs
new file mode 100644
index 00000000..bbd5e092
--- /dev/null
+++ b/addons/gdUnit3/src/asserts/GdUnitBoolAssertImpl.cs
@@ -0,0 +1,29 @@
+using Godot;
+using System;
+
+namespace GdUnit3
+{
+ public sealed class GdUnitBoolAssertWrapper : GdUnitAssertBase, IGdUnitBoolAssert
+ {
+ private static Godot.GDScript GdUnitBoolAssertImpl = GD.Load("res://addons/gdUnit3/src/asserts/GdUnitBoolAssertImpl.gd");
+
+ public GdUnitBoolAssertWrapper(object caller, object current, IGdUnitAssert.EXPECT expectResult)
+ : base((Godot.Reference)GdUnitBoolAssertImpl.New(caller, current, expectResult))
+ {
+
+ }
+
+ public IGdUnitBoolAssert IsFalse()
+ {
+ _delegator.Call("is_false");
+ return this;
+ }
+
+ public IGdUnitBoolAssert IsTrue()
+ {
+ _delegator.Call("is_true");
+ return this;
+ }
+
+ }
+}
diff --git a/addons/gdUnit3/src/asserts/GdUnitFloatAssertImpl.cs b/addons/gdUnit3/src/asserts/GdUnitFloatAssertImpl.cs
new file mode 100644
index 00000000..ea8afa7a
--- /dev/null
+++ b/addons/gdUnit3/src/asserts/GdUnitFloatAssertImpl.cs
@@ -0,0 +1,14 @@
+using Godot;
+using System;
+
+namespace GdUnit3
+{
+ public sealed class GdUnitFloatAssertWrapper : GdUnitNumberAssertWrapper, IGdUnitFloatAssert
+ {
+ private static Godot.GDScript AssertImpl = GD.Load("res://addons/gdUnit3/src/asserts/GdUnitFloatAssertImpl.gd");
+ public GdUnitFloatAssertWrapper(object caller, object current, IGdUnitAssert.EXPECT expectResult)
+ : base((Godot.Reference)AssertImpl.New(caller, current, expectResult), current)
+ {
+ }
+ }
+}
diff --git a/addons/gdUnit3/src/asserts/GdUnitIntAssertImpl.cs b/addons/gdUnit3/src/asserts/GdUnitIntAssertImpl.cs
new file mode 100644
index 00000000..0b9c1650
--- /dev/null
+++ b/addons/gdUnit3/src/asserts/GdUnitIntAssertImpl.cs
@@ -0,0 +1,15 @@
+using Godot;
+using System;
+
+namespace GdUnit3
+{
+ public sealed class GdUnitIntAssertWrapper : GdUnitNumberAssertWrapper, IGdUnitIntAssert
+ {
+ private static Godot.GDScript AssertImpl = GD.Load("res://addons/gdUnit3/src/asserts/GdUnitIntAssertImpl.gd");
+
+ public GdUnitIntAssertWrapper(object caller, object current, IGdUnitAssert.EXPECT expectResult)
+ : base((Godot.Reference)AssertImpl.New(caller, current, expectResult), current)
+ {
+ }
+ }
+}
diff --git a/addons/gdUnit3/src/asserts/GdUnitNumberAssertImpl.cs b/addons/gdUnit3/src/asserts/GdUnitNumberAssertImpl.cs
new file mode 100644
index 00000000..791d3c11
--- /dev/null
+++ b/addons/gdUnit3/src/asserts/GdUnitNumberAssertImpl.cs
@@ -0,0 +1,101 @@
+using Godot;
+using System;
+
+namespace GdUnit3
+{
+ public class GdUnitNumberAssertWrapper : GdUnitAssertBase, IGdUnitNumberAssert
+ {
+ public GdUnitNumberAssertWrapper(Godot.Reference delegator, object current)
+ : base(delegator, current)
+ {
+ }
+
+ public IGdUnitNumberAssert IsBetween(V from, V to)
+ {
+ _delegator.Call("is_between", from, to);
+ return this;
+ }
+
+ public IGdUnitNumberAssert IsEven()
+ {
+ _delegator.Call("is_even");
+ return this;
+ }
+
+ public IGdUnitNumberAssert IsGreater(V expected)
+ {
+ _delegator.Call("is_greater");
+ return this;
+ }
+
+ public IGdUnitNumberAssert IsGreaterEqual(V expected)
+ {
+ _delegator.Call("is_greater_equal");
+ return this;
+
+ }
+
+ public IGdUnitNumberAssert IsIn(Array expected)
+ {
+ _delegator.Call("is_in", expected);
+ return this;
+
+ }
+
+ public IGdUnitNumberAssert IsLess(V expected)
+ {
+ _delegator.Call("is_less", expected);
+ return this;
+
+ }
+
+ public IGdUnitNumberAssert IsLessEqual(V expected)
+ {
+ _delegator.Call("is_less_equal", expected);
+ return this;
+
+ }
+
+ public IGdUnitNumberAssert IsNegative()
+ {
+ _delegator.Call("is_negative");
+ return this;
+
+ }
+
+ public IGdUnitNumberAssert IsNotIn(Array expected)
+ {
+ _delegator.Call("is_not_in", expected);
+ return this;
+
+ }
+
+ public IGdUnitNumberAssert IsNotNegative()
+ {
+ _delegator.Call("is_not_negative");
+ return this;
+
+ }
+
+ public IGdUnitNumberAssert IsNotZero()
+ {
+ _delegator.Call("is_not_zero");
+ return this;
+
+ }
+
+ public IGdUnitNumberAssert IsOdd()
+ {
+ _delegator.Call("is_odd");
+ return this;
+
+ }
+
+ public IGdUnitNumberAssert IsZero()
+ {
+ _delegator.Call("is_zero");
+ return this;
+
+ }
+ }
+}
diff --git a/addons/gdUnit3/src/asserts/GdUnitObjectAssertImpl.cs b/addons/gdUnit3/src/asserts/GdUnitObjectAssertImpl.cs
new file mode 100644
index 00000000..b2184be9
--- /dev/null
+++ b/addons/gdUnit3/src/asserts/GdUnitObjectAssertImpl.cs
@@ -0,0 +1,81 @@
+using Godot;
+using System;
+
+namespace GdUnit3
+{
+ public sealed class GdUnitObjectAssertWrapper : GdUnitAssertBase, IGdUnitObjectAssert
+ {
+ private static Godot.GDScript AssertImpl = GD.Load("res://addons/gdUnit3/src/asserts/GdUnitObjectAssertImpl.gd");
+
+ private static Godot.GDScript GdAssertMessages = GD.Load("res://addons/gdUnit3/src/asserts/GdAssertMessages.gd");
+
+ private readonly Godot.Reference _messageBuilder;
+
+ public GdUnitObjectAssertWrapper(object caller, object current, IGdUnitAssert.EXPECT expectResult)
+ : base((Godot.Reference)AssertImpl.New(caller, current, expectResult), current)
+ {
+ _messageBuilder = GdAssertMessages.New() as Godot.Reference;
+ }
+
+
+ public IGdUnitObjectAssert IsNotInstanceof()
+ {
+ if (_current is ExpectedType)
+ {
+ var message = String.Format("Expected not be a instance of <{0}>", typeof(ExpectedType));
+ _delegator.Call("report_error", message);
+ return this;
+ }
+ _delegator.Call("report_success");
+ return this;
+ }
+
+ public IGdUnitObjectAssert IsNotSame(object expected)
+ {
+ _delegator.Call("is_not_same", expected);
+ return this;
+ }
+
+ public IGdUnitObjectAssert IsSame(object expected)
+ {
+ _delegator.Call("is_same", expected);
+ return this;
+ }
+
+
+ public IGdUnitObjectAssert IsInstanceof()
+ {
+ if (!(_current is ExpectedType))
+ {
+ var message = error_is_instanceof(_current != null ? _current.GetType() : null, typeof(ExpectedType));
+ _delegator.Call("report_error", message);
+ return this;
+ }
+ _delegator.Call("report_success");
+ return this;
+ }
+
+ private String format_expected(string value)
+ {
+ return _messageBuilder.Call("_expected", value) as string;
+ }
+
+ private String format_current(string value)
+ {
+ return _messageBuilder.Call("_current", value) as string;
+ }
+
+ private String format_error(string value)
+ {
+ return _messageBuilder.Call("_error", value) as string;
+ }
+
+ private string error_is_instanceof(Type current, Type expected)
+ {
+ return String.Format("{0}\n {1}\n But it was {2}",
+ format_error("Expected instance of:"),
+ format_expected(expected.ToString()),
+ format_current(current != null ? current.ToString() : "Null"));
+ }
+ }
+}
diff --git a/addons/gdUnit3/src/asserts/GdUnitStringAssertImpl.cs b/addons/gdUnit3/src/asserts/GdUnitStringAssertImpl.cs
new file mode 100644
index 00000000..7f02ac59
--- /dev/null
+++ b/addons/gdUnit3/src/asserts/GdUnitStringAssertImpl.cs
@@ -0,0 +1,82 @@
+using Godot;
+using System;
+
+namespace GdUnit3
+{
+ public sealed class GdUnitStringAssertWrapper : GdUnitAssertBase, IGdUnitStringAssert
+ {
+ private static Godot.GDScript AssertImpl = GD.Load("res://addons/gdUnit3/src/asserts/GdUnitStringAssertImpl.gd");
+
+ public GdUnitStringAssertWrapper(object caller, object current, IGdUnitAssert.EXPECT expectResult)
+ : base((Godot.Reference)AssertImpl.New(caller, current, expectResult))
+ {
+ }
+
+ public IGdUnitStringAssert Contains(string expected)
+ {
+ _delegator.Call("contains", expected);
+ return this;
+ }
+
+ public IGdUnitStringAssert ContainsIgnoringCase(string expected)
+ {
+ _delegator.Call("contains_ignoring_case", expected);
+ return this;
+ }
+
+ public IGdUnitStringAssert EndsWith(string expected)
+ {
+ _delegator.Call("ends_with", expected);
+ return this;
+ }
+
+ public IGdUnitStringAssert HasLength(int lenght, IGdUnitStringAssert.Compare comparator = IGdUnitStringAssert.Compare.EQUAL)
+ {
+ _delegator.Call("has_length", lenght, comparator);
+ return this;
+ }
+
+ public IGdUnitStringAssert IsEmpty()
+ {
+ _delegator.Call("is_empty");
+ return this;
+ }
+
+ public IGdUnitStringAssert IsEqualIgnoringCase(string expected)
+ {
+ _delegator.Call("is_equal_ignoring_case", expected);
+ return this;
+ }
+
+ public IGdUnitStringAssert IsNotEmpty()
+ {
+ _delegator.Call("is_not_empty");
+ return this;
+ }
+
+ public IGdUnitStringAssert IsNotEqualIgnoringCase(string expected)
+ {
+ _delegator.Call("is_not_equal_ignoring_case", expected);
+ return this;
+ }
+
+ public IGdUnitStringAssert NotContains(string expected)
+ {
+ _delegator.Call("not_contains", expected);
+ return this;
+ }
+
+ public IGdUnitStringAssert NotContainsIgnoringCase(string expected)
+ {
+ _delegator.Call("not_contains_ignoring_case", expected);
+ return this;
+ }
+
+ public IGdUnitStringAssert StartsWith(string expected)
+ {
+ _delegator.Call("starts_with", expected);
+ return this;
+ }
+
+ }
+}
diff --git a/addons/gdUnit3/src/core/CsTools.cs b/addons/gdUnit3/src/core/CsTools.cs
new file mode 100644
index 00000000..64552a32
--- /dev/null
+++ b/addons/gdUnit3/src/core/CsTools.cs
@@ -0,0 +1,41 @@
+using Godot;
+using Godot.Collections;
+using Array = Godot.Collections.Array;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+
+namespace GdUnit3
+{
+ public class CsTools : Reference
+ {
+ public Array GetTestCases(String className)
+ {
+ System.Type type = System.Type.GetType(className);
+ Array methods = new Array();
+ List methodInfos = new List(type.GetMethods().Where(m => m.IsDefined(typeof(GdUnitTestSuite.TestCaseAttribute))).ToList());
+ foreach (var methodInfo in methodInfos)
+ {
+ var attributes = methodInfo.GetCustomAttribute();
+
+ methods.Add(new Dictionary {
+ { "name", methodInfo.Name },
+ { "line_number", attributes.Line }
+ });
+ }
+ return methods;
+ }
+
+ public bool IsTestSuite(String className)
+ {
+ System.Type type = System.Type.GetType(className);
+ if (type == null)
+ {
+ return false;
+ }
+ return Attribute.GetCustomAttribute(type, typeof(GdUnitTestSuite.TestSuiteAttribute)) != null;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/addons/gdUnit3/src/core/GdObjects.gd b/addons/gdUnit3/src/core/GdObjects.gd
index d5051def..d2f101a9 100644
--- a/addons/gdUnit3/src/core/GdObjects.gd
+++ b/addons/gdUnit3/src/core/GdObjects.gd
@@ -300,9 +300,14 @@ static func is_object(value) -> bool:
static func is_script(value) -> bool:
return is_object(value) and value is Script
-static func is_testsuite(script :GDScript) -> bool:
- if not script:
- return false
+static func is_test_suite(script :Script) -> bool:
+ if is_gd_script(script):
+ return _is_extends_test_suite(script)
+ if is_cs_script(script):
+ return _is_annotated_test_suite(script)
+ return false
+
+static func _is_extends_test_suite(script :Script) -> bool:
var stack := [script]
while not stack.empty():
var current := stack.pop_front() as Script
@@ -313,6 +318,10 @@ static func is_testsuite(script :GDScript) -> bool:
stack.push_back(base)
return false
+static func _is_annotated_test_suite(script :Script) -> bool:
+ var csTools = GdUnitSingleton.get_or_create_singleton("CsTools", "res://addons/gdUnit3/src/core/CsTools.cs")
+ return csTools.IsTestSuite(script.resource_path.get_file().replace(".cs", ""))
+
static func is_native_class(value) -> bool:
return is_object(value) and value.to_string() != null and value.to_string().find("GDScriptNativeClass") != -1
@@ -322,6 +331,19 @@ static func is_scene(value) -> bool:
static func is_scene_resource_path(value) -> bool:
return value is String and value.ends_with(".tscn")
+static func is_cs_script(script :Script) -> bool:
+ # we need to check by stringify name because on non mono Godot the class CSharpScript is not available
+ return str(script).find("CSharpScript") != -1
+
+static func is_vs_script(script :Script) -> bool:
+ return script is VisualScript
+
+static func is_gd_script(script :Script) -> bool:
+ return script is GDScript
+
+static func is_native_script(script :Script) -> bool:
+ return script is NativeScript
+
static func is_instance(value) -> bool:
if not is_object(value) or is_native_class(value):
return false
@@ -515,8 +537,8 @@ static func array_to_string(elements, delimiter := "\n") -> String:
if formatted.length() > 0 :
formatted += delimiter
formatted += str(element)
- if formatted.length() > 64:
- return formatted + delimiter + "..."
+ #if formatted.length() > 64:
+ # return formatted + delimiter + "..."
return formatted
# Filters an array by given value
diff --git a/addons/gdUnit3/src/core/GdUnitExecutor.gd b/addons/gdUnit3/src/core/GdUnitExecutor.gd
index 2f695f78..48d76196 100644
--- a/addons/gdUnit3/src/core/GdUnitExecutor.gd
+++ b/addons/gdUnit3/src/core/GdUnitExecutor.gd
@@ -57,7 +57,7 @@ func fire_event(event :GdUnitEvent) -> void:
else:
emit_signal("send_event", event)
-func suite_before(test_suite :GdUnitTestSuite, total_count :int) -> GDScriptFunctionState:
+func suite_before(test_suite :GdUnitTestSuiteDelegator, total_count :int) -> GDScriptFunctionState:
set_stage(STAGE_TEST_SUITE_BEFORE)
fire_event(GdUnitEvent.new()\
.suite_before(test_suite.get_script().resource_path, test_suite.get_name(), total_count))
@@ -74,7 +74,7 @@ func suite_before(test_suite :GdUnitTestSuite, total_count :int) -> GDScriptFunc
GdUnitTools.run_auto_close()
return null
-func suite_after(test_suite :GdUnitTestSuite) -> GDScriptFunctionState:
+func suite_after(test_suite :GdUnitTestSuiteDelegator) -> GDScriptFunctionState:
set_stage(STAGE_TEST_SUITE_AFTER)
GdUnitTools.clear_tmp()
@@ -117,7 +117,7 @@ func suite_after(test_suite :GdUnitTestSuite) -> GDScriptFunctionState:
_report_collector.clear_reports(STAGE_TEST_SUITE_BEFORE|STAGE_TEST_SUITE_AFTER)
return null
-func test_before(test_suite :GdUnitTestSuite, test_case :_TestCase) -> GDScriptFunctionState:
+func test_before(test_suite :GdUnitTestSuiteDelegator, test_case :_TestCase) -> GDScriptFunctionState:
set_stage(STAGE_TEST_CASE_BEFORE)
_memory_pool.set_pool(test_suite, GdUnitMemoryPool.TEST_SETUP, true)
@@ -135,7 +135,7 @@ func test_before(test_suite :GdUnitTestSuite, test_case :_TestCase) -> GDScriptF
GdUnitTools.run_auto_close()
return null
-func test_after(test_suite :GdUnitTestSuite, test_case :_TestCase) -> GDScriptFunctionState:
+func test_after(test_suite :GdUnitTestSuiteDelegator, test_case :_TestCase) -> GDScriptFunctionState:
set_stage(STAGE_TEST_CASE_AFTER)
_memory_pool.set_pool(test_suite, GdUnitMemoryPool.TEST_SETUP)
@@ -177,7 +177,7 @@ func test_after(test_suite :GdUnitTestSuite, test_case :_TestCase) -> GDScriptFu
_report_collector.clear_reports(STAGE_TEST_CASE_BEFORE|STAGE_TEST_CASE_EXECUTE|STAGE_TEST_CASE_AFTER)
return null
-func execute_test_case(test_suite :GdUnitTestSuite, test_case :_TestCase) -> GDScriptFunctionState:
+func execute_test_case(test_suite :GdUnitTestSuiteDelegator, test_case :_TestCase) -> GDScriptFunctionState:
_test_run_state = test_before(test_suite, test_case)
if GdUnitTools.is_yielded(_test_run_state):
yield(_test_run_state, "completed")
@@ -235,7 +235,7 @@ func execute_test_case(test_suite :GdUnitTestSuite, test_case :_TestCase) -> GDS
_test_run_state = null
return _test_run_state
-func execute(test_suite :GdUnitTestSuite) -> GDScriptFunctionState:
+func execute(test_suite :GdUnitTestSuiteDelegator) -> GDScriptFunctionState:
# stop on first error if fail fast enabled
if _fail_fast and _total_test_failed > 0:
test_suite.free()
@@ -243,13 +243,14 @@ func execute(test_suite :GdUnitTestSuite) -> GDScriptFunctionState:
_report_collector.register_report_provider(test_suite)
add_child(test_suite)
- var fs = suite_before(test_suite, test_suite.get_child_count())
+
+ var fs = suite_before(test_suite, test_suite.get_test_cases_count())
if GdUnitTools.is_yielded(fs):
yield(fs, "completed")
if not test_suite.is_skipped():
- for test_case_index in test_suite.get_child_count():
- var test_case = test_suite.get_child(test_case_index)
+ for test_case_index in test_suite.get_test_cases_count():
+ var test_case = test_suite.get_test_case(test_case_index)
# only iterate over test case, we need to filter because of possible adding other child types on before() or before_test()
if not test_case is _TestCase:
continue
@@ -265,7 +266,7 @@ func execute(test_suite :GdUnitTestSuite) -> GDScriptFunctionState:
# it needs to go this hard way to kill the outstanding yields of a test case when the test timed out
# we delete the current test suite where is execute the current test case to kill the function state
# and replace it by a clone without function state
- test_suite = clone_test_suite(test_suite)
+ test_suite = test_suite.clone()
fs = suite_after(test_suite)
if GdUnitTools.is_yielded(fs):
@@ -274,30 +275,9 @@ func execute(test_suite :GdUnitTestSuite) -> GDScriptFunctionState:
test_suite.free()
return null
-# clones a test suite and moves the test cases to new instance
-func clone_test_suite(test_suite :GdUnitTestSuite) -> GdUnitTestSuite:
- var _test_suite = test_suite.duplicate()
- # copy all property values
- for property in test_suite.get_property_list():
- var property_name = property["name"]
- _test_suite.set(property_name, test_suite.get(property_name))
-
- # remove incomplete duplicated childs
- for child in _test_suite.get_children():
- _test_suite.remove_child(child)
- child.free()
- assert(_test_suite.get_child_count() == 0)
- # now move original test cases to duplicated test suite
- for child in test_suite.get_children():
- child.get_parent().remove_child(child)
- _test_suite.add_child(child)
- # finally free current test suite instance
- remove_child(test_suite)
- test_suite.free()
- add_child(_test_suite)
- return _test_suite
-static func create_fuzzers(test_suite :GdUnitTestSuite, test_case :_TestCase) -> Array:
+
+static func create_fuzzers(test_suite :GdUnitTestSuiteDelegator, test_case :_TestCase) -> Array:
if not test_case.has_fuzzer():
return Array()
var fuzzers := Array()
diff --git a/addons/gdUnit3/src/core/GdUnitRunner.gd b/addons/gdUnit3/src/core/GdUnitRunner.gd
index 7fd93f80..ff84ac19 100644
--- a/addons/gdUnit3/src/core/GdUnitRunner.gd
+++ b/addons/gdUnit3/src/core/GdUnitRunner.gd
@@ -58,7 +58,7 @@ func _process(delta):
_state = STOP
else:
# process next test suite
- var test_suite := _test_suites_to_process.pop_front() as GdUnitTestSuite
+ var test_suite := _test_suites_to_process.pop_front() as GdUnitTestSuiteDelegator
var fs = _executor.execute(test_suite)
# is yielded than wait for completed
if GdUnitTools.is_yielded(fs):
@@ -99,30 +99,26 @@ func gdUnitInit() -> void:
send_message("Scaned %d test suites" % _test_suites_to_process.size())
var total_count = _collect_test_case_count(_test_suites_to_process)
_on_Executor_send_event(GdUnitInit.new(_test_suites_to_process.size(), total_count))
- for t in _test_suites_to_process:
- var test_suite := t as GdUnitTestSuite
+ for test_suite in _test_suites_to_process:
send_test_suite(test_suite)
-func _filter_test_case(test_suites :Array, test_case_names :Array) -> void:
- if test_case_names.empty():
+func _filter_test_case(test_suites :Array, includes_tests :Array) -> void:
+ if includes_tests.empty():
return
for test_suite in test_suites:
- for test_case in test_suite.get_children():
- if not test_case_names.has(test_case.get_name()):
- test_suite.remove_child(test_case)
- test_case.free()
+ test_suite.filter_tests(includes_tests)
func _collect_test_case_count(testSuites :Array) -> int:
var total :int = 0
for test_suite in testSuites:
- total += (test_suite as Node).get_child_count()
+ total += (test_suite as GdUnitTestSuiteDelegator).get_test_cases_count()
return total
# RPC send functions
func send_message(message :String):
_client.rpc_send(RPCMessage.of(message))
-func send_test_suite(test_suite :GdUnitTestSuite):
+func send_test_suite(test_suite :GdUnitTestSuiteDelegator):
_client.rpc_send(RPCGdUnitTestSuite.of(test_suite))
func _on_Executor_send_event(event :GdUnitEvent):
diff --git a/addons/gdUnit3/src/core/GdUnitScriptType.gd b/addons/gdUnit3/src/core/GdUnitScriptType.gd
new file mode 100644
index 00000000..0a352a0a
--- /dev/null
+++ b/addons/gdUnit3/src/core/GdUnitScriptType.gd
@@ -0,0 +1,21 @@
+class_name GdUnitScriptType
+extends Reference
+
+const UNKNOWN := ""
+const CS := "cs"
+const GD := "gd"
+const NATIVE := "gdns"
+const VS := "vs"
+
+static func type_of(script :Script) -> String:
+ if script == null:
+ return UNKNOWN
+ if GdObjects.is_gd_script(script):
+ return GD
+ if GdObjects.is_vs_script(script):
+ return VS
+ if GdObjects.is_native_script(script):
+ return NATIVE
+ if GdObjects.is_cs_script(script):
+ return CS
+ return UNKNOWN
diff --git a/addons/gdUnit3/src/core/GdUnitSingleton.gd b/addons/gdUnit3/src/core/GdUnitSingleton.gd
index 9e4e7c3a..be8aa146 100644
--- a/addons/gdUnit3/src/core/GdUnitSingleton.gd
+++ b/addons/gdUnit3/src/core/GdUnitSingleton.gd
@@ -17,9 +17,10 @@ static func get_singleton(name: String) -> Object:
static func add_singleton(name: String, path: String) -> Object:
var singleton:Object = load(path).new()
- singleton.set_name(name)
+ if singleton.has_method("set_name"):
+ singleton.set_name(name)
_singletons[name] = singleton
- #print_debug("Added singleton", name, singleton)
+ #print_debug("Added singleton ", name, " ",singleton)
return singleton
static func get_or_create_singleton(name: String, path: String) -> Object:
diff --git a/addons/gdUnit3/src/core/GdUnitTestSuiteDelegator.gd b/addons/gdUnit3/src/core/GdUnitTestSuiteDelegator.gd
new file mode 100644
index 00000000..300b1fee
--- /dev/null
+++ b/addons/gdUnit3/src/core/GdUnitTestSuiteDelegator.gd
@@ -0,0 +1,126 @@
+class_name GdUnitTestSuiteDelegator
+extends Node
+
+enum {
+ TYPE_CS,
+ TYPE_GD
+}
+
+
+var _test_suite :Node
+var _script_type :String
+var _skipped :bool
+var _active_test_case :String
+
+func _init(test_suite :Node):
+ _test_suite = test_suite
+ add_child(_test_suite)
+ _script_type = GdUnitScriptType.type_of(test_suite.get_script())
+
+func set_meta(name: String, value) -> void:
+ _test_suite.set_meta(name, value)
+
+func get_script() -> Script:
+ return _test_suite.get_script()
+
+func get_name() -> String:
+ return _test_suite.get_name()
+
+# This function is called before a test suite starts
+# You can overwrite to prepare test data or initalizize necessary variables
+func before() -> void:
+ match _script_type:
+ GdUnitScriptType.GD:
+ _test_suite.before()
+ GdUnitScriptType.CS:
+ _test_suite.Before()
+
+
+# This function is called at least when a test suite is finished
+# You can overwrite to cleanup data created during test running
+func after() -> void:
+ match _script_type:
+ GdUnitScriptType.GD:
+ _test_suite.after()
+ GdUnitScriptType.CS:
+ _test_suite.After()
+
+# This function is called before a test case starts
+# You can overwrite to prepare test case specific data
+func before_test() -> void:
+ match _script_type:
+ GdUnitScriptType.GD:
+ _test_suite.before_test()
+ GdUnitScriptType.CS:
+ _test_suite.BeforeTest()
+
+# This function is called after the test case is finished
+# You can overwrite to cleanup your test case specific data
+func after_test() -> void:
+ match _script_type:
+ GdUnitScriptType.GD:
+ _test_suite.after_test()
+ GdUnitScriptType.CS:
+ _test_suite.AfterTest()
+
+func get_test_cases_count() -> int:
+ return _test_suite.get_child_count()
+
+func get_test_case(index :int) -> Node:
+ return _test_suite.get_child(index)
+
+func get_test_case_by_name(name :String) -> Node:
+ return _test_suite.find_node(name, false, false)
+
+func get_test_cases() -> Array:
+ return _test_suite.get_children()
+
+func delete_test_case(name :String) -> void:
+ var test_case = _test_suite.find_node(name, true, false)
+ _test_suite.remove_child(test_case)
+ test_case.free()
+
+# Skip the test-suite from execution, it will be ignored
+func skip(skipped :bool) -> void:
+ _skipped = skipped
+
+# filters by given test names
+func filter_tests(test_case_names :Array) -> void:
+ for test_case in get_test_cases():
+ if test_case.get_name() in test_case_names:
+ continue
+ _test_suite.remove_child(test_case)
+ test_case.free()
+ #test_case.skip(true)
+
+func is_skipped() -> bool:
+ return _skipped
+
+func set_active_test_case(test_case :String) -> void:
+ _active_test_case = test_case
+ _test_suite.set_active_test_case(test_case)
+
+
+# clones a test suite and moves the test cases to new instance
+func clone() -> GdUnitTestSuiteDelegator:
+ var test_suite = _test_suite.duplicate()
+ # copy all property values
+ for property in _test_suite.get_property_list():
+ var property_name = property["name"]
+ test_suite.set(property_name, _test_suite.get(property_name))
+
+ # remove incomplete duplicated childs
+ for child in test_suite.get_children():
+ test_suite.remove_child(child)
+ child.free()
+ assert(test_suite.get_child_count() == 0)
+ # now move original test cases to duplicated test suite
+ for child in _test_suite.get_children():
+ child.get_parent().remove_child(child)
+ test_suite.add_child(child)
+ # finally free current test suite instance
+ remove_child(_test_suite)
+ _test_suite.free()
+ _test_suite = test_suite
+ add_child(_test_suite)
+ return self
diff --git a/addons/gdUnit3/src/core/GdUnitTools.gd b/addons/gdUnit3/src/core/GdUnitTools.gd
index 840054d1..a22ad563 100644
--- a/addons/gdUnit3/src/core/GdUnitTools.gd
+++ b/addons/gdUnit3/src/core/GdUnitTools.gd
@@ -345,6 +345,10 @@ static func is_auto_free_registered(obj, pool :int) -> bool:
static func is_yielded(obj) -> bool:
return obj is GDScriptFunctionState and obj.is_valid()
+# test is Godot mono running
+static func is_mono_supported() -> bool:
+ return ClassDB.class_exists("CSharpScript")
+
# runs over all registered files and closes it
static func run_auto_close():
while not _files_to_close.empty():
@@ -376,6 +380,8 @@ static func clear_push_errors() -> void:
runner.clear_push_errors()
static func register_expect_interupted_by_timeout(test_suite :Node, test_case_name :String) -> void:
+ prints(test_suite.get_children())
+
var test_case = test_suite.find_node(test_case_name, false, false)
test_case.expect_to_interupt()
diff --git a/addons/gdUnit3/src/core/_TestSuiteScanner.gd b/addons/gdUnit3/src/core/_TestSuiteScanner.gd
index 3467ff9a..a83d0707 100644
--- a/addons/gdUnit3/src/core/_TestSuiteScanner.gd
+++ b/addons/gdUnit3/src/core/_TestSuiteScanner.gd
@@ -5,7 +5,6 @@ extends Node
var _script_parser := GdScriptParser.new()
var _extends_test_suite_classes := Array()
-
func scan_testsuite_classes() -> void:
# scan and cache extends GdUnitTestSuite by class name an resource paths
_extends_test_suite_classes.append("GdUnitTestSuite")
@@ -17,11 +16,13 @@ func scan_testsuite_classes() -> void:
_extends_test_suite_classes.append(script_meta["class"])
func scan(resource_path :String) -> Array:
+ scan_testsuite_classes()
var base_dir := Directory.new()
# if single testsuite requested
if base_dir.file_exists(resource_path):
- if resource_path.ends_with(".gd") and _is_test_suite(resource_path):
- return [_parse_test_suite(resource_path)]
+ var test_suite := _parse_is_test_suite(resource_path)
+ if test_suite:
+ return [test_suite]
if base_dir.open(resource_path) != OK:
prints("Given directory or file does not exists:", resource_path)
@@ -33,45 +34,79 @@ func _scan_test_suites(dir :Directory, collected_suites :Array) -> Array:
dir.list_dir_begin(true, true)
var file_name := dir.get_next()
while file_name != "":
- var current = dir.get_current_dir() + "/" + file_name
+ var resource_path = _file(dir, file_name)
if dir.current_is_dir():
var sub_dir := Directory.new()
- if sub_dir.open(current) == OK:
+ if sub_dir.open(resource_path) == OK:
_scan_test_suites(sub_dir, collected_suites)
else:
- if _is_test_suite(current):
- collected_suites.append(_parse_test_suite(current))
+ var test_suite := _parse_is_test_suite(resource_path)
+ if test_suite:
+ collected_suites.append(test_suite)
file_name = dir.get_next()
return collected_suites
-func _is_test_suite(file_name :String) -> bool:
- # only scan on gd scrip files
- if not file_name.ends_with(".gd"):
- return false
+static func _file(dir :Directory, file_name :String) -> String:
+ var current_dir := dir.get_current_dir()
+ if current_dir.ends_with("/"):
+ return current_dir + file_name
+ return current_dir + "/" + file_name
+
+func _parse_is_test_suite(resource_path :String) -> GdUnitTestSuiteDelegator:
+ if not _is_script_format_supported(resource_path):
+ return null
# exclude non test directories
- if file_name.find("/test") == -1:
- return false
- return GdObjects.is_testsuite(ResourceLoader.load(file_name))
+ if resource_path.find("/test") == -1:
+ return null
+ var script :Script = ResourceLoader.load(resource_path)
+ if not GdObjects.is_test_suite(script):
+ return null
+ if GdObjects.is_gd_script(script):
+ return GdUnitTestSuiteDelegator.new(_parse_test_suite(script))
+ if GdObjects.is_cs_script(script):
+ return GdUnitTestSuiteDelegator.new(_parse_cs_test_suite(script))
+ return null
+
+static func _is_script_format_supported(resource_path :String) -> bool:
+ var ext := resource_path.get_extension()
+ if ext == "gd":
+ return true
+ if ext == "cs" and GdUnitTools.is_mono_supported():
+ return true
+ return false
+
+func _parse_cs_test_suite(script :Script) -> Node:
+ var test_suite = script.new()
+ test_suite.set_name(parse_test_suite_name(script))
+ var csTools = GdUnitSingleton.get_or_create_singleton("CsTools", "res://addons/gdUnit3/src/core/CsTools.cs")
+ var cs_test_cases = csTools.GetTestCases(script.resource_path.get_file().replace(".cs", ""))
+ for test_case in cs_test_cases:
+ var meta :Dictionary = test_case
+ var test := _TestCase.new()
+ test.configure(meta.get("name"), meta.get("line_number"), script.resource_path)
+ test_suite.add_child(test)
+ return test_suite
+
-func _parse_test_suite(resource_path :String) -> GdUnitTestSuite:
- var test_suite := load(resource_path).new() as GdUnitTestSuite
- test_suite.set_name(parse_test_suite_name(resource_path))
+func _parse_test_suite(script :GDScript) -> GdUnitTestSuite:
+ var test_suite = script.new()
+ test_suite.set_name(parse_test_suite_name(script))
# find all test cases as array of names
- var test_case_names := _extract_test_case_names(test_suite)
+ var test_case_names := _extract_test_case_names(script)
# add test cases to test suite and parse test case line nummber
- _parse_and_add_test_cases(test_suite, resource_path, test_case_names)
+ _parse_and_add_test_cases(test_suite, script, test_case_names)
# not all test case parsed?
# we have to scan the base class to
if not test_case_names.empty():
var base_script :GDScript = test_suite.get_script().get_base_script()
while base_script is GDScript:
- _parse_and_add_test_cases(test_suite, base_script.resource_path, test_case_names)
+ _parse_and_add_test_cases(test_suite, base_script, test_case_names)
base_script = base_script.get_base_script()
return test_suite
-func _extract_test_case_names(test_suite :GdUnitTestSuite) -> PoolStringArray:
+func _extract_test_case_names(script :GDScript) -> PoolStringArray:
var names := PoolStringArray()
- for method in test_suite.get_script().get_script_method_list():
+ for method in script.get_script_method_list():
#prints(method["flags"], method["name"] )
var flags :int = method["flags"]
var funcName :String = method["name"]
@@ -79,15 +114,13 @@ func _extract_test_case_names(test_suite :GdUnitTestSuite) -> PoolStringArray:
names.append(funcName)
return names
-static func parse_test_suite_name(resource_path :String) -> String:
- var start := resource_path.find_last("/")
- var end := resource_path.find_last(".gd")
- return resource_path.substr(start, end-start)
+static func parse_test_suite_name(script :Script) -> String:
+ return script.resource_path.get_file().replace(".gd", "").replace(".cs", "")
-func _parse_and_add_test_cases(test_suite :GdUnitTestSuite, resource_path :String, test_case_names :PoolStringArray):
+func _parse_and_add_test_cases(test_suite, script :GDScript, test_case_names :PoolStringArray):
var test_cases_to_find = Array(test_case_names)
var file := File.new()
- file.open(resource_path, File.READ)
+ file.open(script.resource_path, File.READ)
var line_number:int = 0
file.seek(0)
@@ -108,7 +141,7 @@ func _parse_and_add_test_cases(test_suite :GdUnitTestSuite, resource_path :Strin
var iterations = _script_parser.parse_argument(row, Fuzzer.ARGUMENT_ITERATIONS, Fuzzer.ITERATION_DEFAULT_COUNT)
var seed_value = _script_parser.parse_argument(row, Fuzzer.ARGUMENT_SEED, -1)
var fuzzers := _script_parser.parse_fuzzers(row)
- test_suite.add_child(_TestCase.new().configure(func_name, line_number, resource_path, timeout, fuzzers, iterations, seed_value))
+ test_suite.add_child(_TestCase.new().configure(func_name, line_number, script.resource_path, timeout, fuzzers, iterations, seed_value))
file.close()
diff --git a/addons/gdUnit3/src/network/rpc/RPCGdUnitTestSuite.gd b/addons/gdUnit3/src/network/rpc/RPCGdUnitTestSuite.gd
index 0c517848..79270526 100644
--- a/addons/gdUnit3/src/network/rpc/RPCGdUnitTestSuite.gd
+++ b/addons/gdUnit3/src/network/rpc/RPCGdUnitTestSuite.gd
@@ -3,7 +3,7 @@ extends RPC
var _data :Dictionary
-static func of(test_suite :GdUnitTestSuite) -> RPCGdUnitTestSuite:
+static func of(test_suite :GdUnitTestSuiteDelegator) -> RPCGdUnitTestSuite:
var rpc = load("res://addons/gdUnit3/src/network/rpc/RPCGdUnitTestSuite.gd").new()
rpc._data = GdUnitTestSuiteDto.new().serialize(test_suite)
return rpc
diff --git a/addons/gdUnit3/src/network/rpc/dtos/GdUnitResourceDto.gd b/addons/gdUnit3/src/network/rpc/dtos/GdUnitResourceDto.gd
index 54590d96..6fb9164f 100644
--- a/addons/gdUnit3/src/network/rpc/dtos/GdUnitResourceDto.gd
+++ b/addons/gdUnit3/src/network/rpc/dtos/GdUnitResourceDto.gd
@@ -4,7 +4,7 @@ extends Resource
var _name :String
var _path :String
-func serialize(resource :Object) -> Dictionary:
+func serialize(resource) -> Dictionary:
var serialized := Dictionary()
serialized["name"] = resource.get_name()
var script = resource.get_script()
diff --git a/addons/gdUnit3/src/network/rpc/dtos/GdUnitTestCaseDto.gd b/addons/gdUnit3/src/network/rpc/dtos/GdUnitTestCaseDto.gd
index 3557920c..e11f8c36 100644
--- a/addons/gdUnit3/src/network/rpc/dtos/GdUnitTestCaseDto.gd
+++ b/addons/gdUnit3/src/network/rpc/dtos/GdUnitTestCaseDto.gd
@@ -3,7 +3,7 @@ extends GdUnitResourceDto
var _line_number :int = -1
-func serialize(test_case :Object) -> Dictionary:
+func serialize(test_case) -> Dictionary:
var serialized := .serialize(test_case)
serialized["line_number"] = test_case.line_number()
return serialized
diff --git a/addons/gdUnit3/src/network/rpc/dtos/GdUnitTestSuiteDto.gd b/addons/gdUnit3/src/network/rpc/dtos/GdUnitTestSuiteDto.gd
index ab581fb7..0f912edb 100644
--- a/addons/gdUnit3/src/network/rpc/dtos/GdUnitTestSuiteDto.gd
+++ b/addons/gdUnit3/src/network/rpc/dtos/GdUnitTestSuiteDto.gd
@@ -7,7 +7,7 @@ func serialize(test_suite :Object) -> Dictionary:
var serialized := .serialize(test_suite)
var test_cases := Array()
serialized["test_cases"] = test_cases
- for test_case in test_suite.get_children():
+ for test_case in test_suite.get_test_cases():
test_cases.append(GdUnitTestCaseDto.new().serialize(test_case))
return serialized
diff --git a/addons/gdUnit3/src/report/GdUnitHtmlReport.gd b/addons/gdUnit3/src/report/GdUnitHtmlReport.gd
index ed9cbc3b..60bd4d2b 100644
--- a/addons/gdUnit3/src/report/GdUnitHtmlReport.gd
+++ b/addons/gdUnit3/src/report/GdUnitHtmlReport.gd
@@ -13,21 +13,21 @@ func _init(path :String):
func add_testsuite_report(suite_report :GdUnitTestSuiteReport):
_reports.append(suite_report)
-func add_testcase_report(suite_name :String, suite_report :GdUnitTestCaseReport) -> void:
+func add_testcase_report(resource_path :String, suite_report :GdUnitTestCaseReport) -> void:
for report in _reports:
- if report.name() == suite_name:
+ if report.resource_path() == resource_path:
report.add_report(suite_report)
-func update_test_suite_report(suite_name :String, skipped :int, orphans :int, duration :int) -> void:
+func update_test_suite_report(resource_path :String, skipped :int, orphans :int, duration :int) -> void:
for report in _reports:
- if report.name() == suite_name:
+ if report.resource_path() == resource_path:
report.set_duration(duration)
report.set_skipped(skipped)
report.set_orphans(orphans)
-func update_testcase_report(suite_name :String, test_report :GdUnitTestCaseReport):
+func update_testcase_report(resource_path :String, test_report :GdUnitTestCaseReport):
for report in _reports:
- if report.name() == suite_name:
+ if report.resource_path() == resource_path:
report.update(test_report)
func write() -> String:
diff --git a/addons/gdUnit3/src/report/GdUnitReportSummary.gd b/addons/gdUnit3/src/report/GdUnitReportSummary.gd
index 58a5be48..6b0213d8 100644
--- a/addons/gdUnit3/src/report/GdUnitReportSummary.gd
+++ b/addons/gdUnit3/src/report/GdUnitReportSummary.gd
@@ -18,6 +18,9 @@ func name() -> String:
func path() -> String:
return _resource_path.get_base_dir().replace("res://", "")
+func resource_path() -> String:
+ return _resource_path
+
func suite_count() -> int:
return _reports.size()
diff --git a/addons/gdUnit3/src/report/GdUnitTestCaseReport.gd b/addons/gdUnit3/src/report/GdUnitTestCaseReport.gd
index 0a623b3d..8b0bc97c 100644
--- a/addons/gdUnit3/src/report/GdUnitTestCaseReport.gd
+++ b/addons/gdUnit3/src/report/GdUnitTestCaseReport.gd
@@ -3,7 +3,8 @@ extends GdUnitReportSummary
var _failure_reports :Array
-func _init(test_name :String, is_error :bool = false, is_failed :bool = false, orphans :int = 0, skipped :int = 0, failure_reports :Array = [], duration :int = 0):
+func _init(resource_path :String, test_name :String, is_error :bool = false, is_failed :bool = false, orphans :int = 0, skipped :int = 0, failure_reports :Array = [], duration :int = 0):
+ _resource_path = resource_path
_name = test_name
_test_count = 1
_error_count = is_error
diff --git a/addons/gdUnit3/src/ui/GdUnitInspector.gd b/addons/gdUnit3/src/ui/GdUnitInspector.gd
index 68da7a0a..d53057a8 100644
--- a/addons/gdUnit3/src/ui/GdUnitInspector.gd
+++ b/addons/gdUnit3/src/ui/GdUnitInspector.gd
@@ -180,7 +180,7 @@ func extend_script_editor_popup(tab_container :Control) -> void:
func _on_script_editor_context_menu_show(context_menu :PopupMenu):
var current_script := _editor_interface.get_script_editor().get_current_script()
- if GdObjects.is_testsuite(current_script):
+ if GdObjects.is_test_suite(current_script):
context_menu.add_separator()
# save menu entry index
var current_index := context_menu.get_item_count()
diff --git a/addons/gdUnit3/test/GdUnitScriptTypeTest.gd b/addons/gdUnit3/test/GdUnitScriptTypeTest.gd
new file mode 100644
index 00000000..625e6ec6
--- /dev/null
+++ b/addons/gdUnit3/test/GdUnitScriptTypeTest.gd
@@ -0,0 +1,16 @@
+# GdUnit generated TestSuite
+#warning-ignore-all:unused_argument
+#warning-ignore-all:return_value_discarded
+class_name GdUnitScriptTypeTest
+extends GdUnitTestSuite
+
+# TestSuite generated from
+const __source = 'res://addons/gdUnit3/src/core/GdUnitScriptType.gd'
+
+func test_type_of() -> void:
+ assert_str(GdUnitScriptType.type_of(null)).is_equal(GdUnitScriptType.UNKNOWN)
+ assert_str(GdUnitScriptType.type_of(ClassDB.instance("GDScript"))).is_equal(GdUnitScriptType.GD)
+ if GdUnitTools.is_mono_supported():
+ assert_str(GdUnitScriptType.type_of(ClassDB.instance("CSharpScript"))).is_equal(GdUnitScriptType.CS)
+ assert_str(GdUnitScriptType.type_of(ClassDB.instance("VisualScript"))).is_equal(GdUnitScriptType.VS)
+ assert_str(GdUnitScriptType.type_of(ClassDB.instance("NativeScript"))).is_equal(GdUnitScriptType.NATIVE)
diff --git a/addons/gdUnit3/test/GdUnitTestResourceLoader.gd b/addons/gdUnit3/test/GdUnitTestResourceLoader.gd
index 9bd809e2..0f3bfc8c 100644
--- a/addons/gdUnit3/test/GdUnitTestResourceLoader.gd
+++ b/addons/gdUnit3/test/GdUnitTestResourceLoader.gd
@@ -1,18 +1,63 @@
class_name GdUnitTestResourceLoader
extends Reference
-static func load_test_suite(resource_path :String) -> GdUnitTestSuite:
+enum {
+ GD_SUITE,
+ CS_SUITE
+}
+
+
+static func load_test_suite(resource_path :String, script_type = GD_SUITE) -> GdUnitTestSuiteDelegator:
+ match script_type:
+ GD_SUITE:
+ return load_test_suite_gd(resource_path)
+ CS_SUITE:
+ return load_test_suite_cs(resource_path)
+ assert("type '%s' is not impleented" % script_type)
+ return null
+
+static func load_test_suite_gd(resource_path :String) -> GdUnitTestSuiteDelegator:
var script := GDScript.new()
script.source_code = GdUnitTools.resource_as_string(resource_path)
script.resource_path = resource_path
script.reload()
var test_suite :GdUnitTestSuite = GdUnitTestSuite.new()
test_suite.set_script(script)
- test_suite.set_name(_TestSuiteScanner.parse_test_suite_name(resource_path.replace(".resource", ".gd")))
+ test_suite.set_name(resource_path.get_file().replace(".resource", ""))
# complete test suite wiht parsed test cases
var suite_parser := _TestSuiteScanner.new()
- var test_case_names := suite_parser._extract_test_case_names(test_suite)
+ var test_case_names := suite_parser._extract_test_case_names(script)
# add test cases to test suite and parse test case line nummber
- suite_parser._parse_and_add_test_cases(test_suite, resource_path, test_case_names)
+ suite_parser._parse_and_add_test_cases(test_suite, script, test_case_names)
suite_parser.free()
- return test_suite
+ return GdUnitTestSuiteDelegator.new(test_suite)
+
+
+static func load_test_suite_cs(resource_path :String) -> GdUnitTestSuiteDelegator:
+ if not GdUnitTools.is_mono_supported():
+ return null
+ var script = ClassDB.instance("CSharpScript")
+ script.source_code = GdUnitTools.resource_as_string(resource_path)
+ script.resource_path = resource_path
+ script.reload()
+
+ return null
+
+static func load_cs_script(resource_path :String) -> Script:
+ if not GdUnitTools.is_mono_supported():
+ return null
+ var script = ClassDB.instance("CSharpScript")
+ script.source_code = GdUnitTools.resource_as_string(resource_path)
+ script.resource_path = GdUnitTools.create_temp_dir("test") + "/%s" % resource_path.get_file().replace(".resource", ".cs")
+ Directory.new().remove(script.resource_path)
+ ResourceSaver.save(script.resource_path, script)
+ script.reload()
+ return script
+
+static func load_gd_script(resource_path :String) -> GDScript:
+ var script := GDScript.new()
+ script.source_code = GdUnitTools.resource_as_string(resource_path)
+ script.resource_path = resource_path.replace(".resource", ".gd")
+ script.reload()
+ return script
+
diff --git a/addons/gdUnit3/test/asserts/GdUnitBoolAssertImplTest.cs b/addons/gdUnit3/test/asserts/GdUnitBoolAssertImplTest.cs
new file mode 100644
index 00000000..6c59e175
--- /dev/null
+++ b/addons/gdUnit3/test/asserts/GdUnitBoolAssertImplTest.cs
@@ -0,0 +1,77 @@
+using GdUnit3;
+
+[TestSuite]
+public class GdUnitBoolAssertImplTest : GdUnitTestSuite
+{
+ [TestCase]
+ public void IsTrue()
+ {
+ AssertBool(true).IsTrue();
+ AssertBool(false, IGdUnitAssert.EXPECT.FAIL).IsTrue()
+ .HasFailureMessage("Expecting: 'True' but is 'False'");
+ }
+
+ [TestCase]
+ public void IsFalse()
+ {
+ AssertBool(false).IsFalse();
+ AssertBool(true, IGdUnitAssert.EXPECT.FAIL).IsFalse()
+ .HasFailureMessage("Expecting: 'False' but is 'True'");
+ }
+
+ [TestCase]
+ public void IsNull()
+ {
+ AssertBool(true, IGdUnitAssert.EXPECT.FAIL)
+ .IsNull()
+ .StartsWithFailureMessage("Expecting: 'Null' but was 'True'");
+ AssertBool(false, IGdUnitAssert.EXPECT.FAIL)
+ .IsNull()
+ .StartsWithFailureMessage("Expecting: 'Null' but was 'False'");
+ }
+
+ [TestCase]
+ public void IsNotNull()
+ {
+ AssertBool(true).IsNotNull();
+ AssertBool(false).IsNotNull();
+ }
+
+ [TestCase]
+ public void IsEqual()
+ {
+ AssertBool(true).IsEqual(true);
+ AssertBool(false).IsEqual(false);
+ AssertBool(true, IGdUnitAssert.EXPECT.FAIL)
+ .IsEqual(false)
+ .HasFailureMessage("Expecting:\n 'False'\n but was\n 'True'");
+ }
+
+ [TestCase]
+ public void IsNotEqual()
+ {
+ AssertBool(true).IsNotEqual(false);
+ AssertBool(false).IsNotEqual(true);
+ AssertBool(true, IGdUnitAssert.EXPECT.FAIL)
+ .IsNotEqual(true)
+ .HasFailureMessage("Expecting:\n 'True'\n not equal to\n 'True'");
+ }
+
+ [TestCase]
+ public void Fluent()
+ {
+ AssertBool(true).IsTrue()
+ .IsEqual(true)
+ .IsNotEqual(false)
+ .IsNotNull();
+ }
+
+ [TestCase]
+ public void OverrideFailureMessage()
+ {
+ AssertBool(true, IGdUnitAssert.EXPECT.FAIL)
+ .OverrideFailureMessage("Custom failure message")
+ .IsNull()
+ .HasFailureMessage("Custom failure message");
+ }
+}
diff --git a/addons/gdUnit3/test/asserts/GdUnitIntAssertImplTest.cs b/addons/gdUnit3/test/asserts/GdUnitIntAssertImplTest.cs
new file mode 100644
index 00000000..3f6dd243
--- /dev/null
+++ b/addons/gdUnit3/test/asserts/GdUnitIntAssertImplTest.cs
@@ -0,0 +1,19 @@
+using Godot;
+using GdUnit3;
+using static GdUnit3.IGdUnitAssert.EXPECT;
+using static GdUnit3.IGdUnitStringAssert.Compare;
+
+[TestSuite]
+public class GdUnitIntAssertImplTest : GdUnitTestSuite
+{
+
+ [TestCase]
+ public void IsEqual()
+ {
+ //AssertInt(1).IsLess(2);
+ //AssertInt(1).IsEqual(1);
+ AssertInt(1).IsEqual(1);
+
+ }
+
+}
diff --git a/addons/gdUnit3/test/asserts/GdUnitObjectAssertImplTest.cs b/addons/gdUnit3/test/asserts/GdUnitObjectAssertImplTest.cs
new file mode 100644
index 00000000..da3cd641
--- /dev/null
+++ b/addons/gdUnit3/test/asserts/GdUnitObjectAssertImplTest.cs
@@ -0,0 +1,142 @@
+using Godot;
+using GdUnit3;
+using static GdUnit3.IGdUnitAssert.EXPECT;
+using static GdUnit3.IGdUnitStringAssert.Compare;
+
+[TestSuite]
+public class GdUnitObjectAssertImplTest : GdUnitTestSuite
+{
+
+ class CustomClass
+ {
+ public class InnerClassA : Node { }
+
+ public class InnerClassB : InnerClassA { }
+
+ public class InnerClassC : Node { }
+ }
+
+ [TestCase]
+ public void IsEqual()
+ {
+ AssertObject(new CubeMesh()).IsEqual(new CubeMesh());
+ // should fail because the current is an CubeMesh and we expect equal to a Skin
+ AssertObject(new CubeMesh(), FAIL)
+ .IsEqual(new Skin());
+ }
+
+ [TestCase]
+ public void IsNotEqual()
+ {
+ AssertObject(new CubeMesh()).IsNotEqual(new Skin());
+ // should fail because the current is an CubeMesh and we expect not equal to a CubeMesh
+ AssertObject(new CubeMesh(), FAIL)
+ .IsNotEqual(new CubeMesh());
+ }
+
+ [TestCase]
+ public void IsInstanceof()
+ {
+ // engine class test
+ AssertObject(auto_free(new Path())).IsInstanceof();
+ AssertObject(auto_free(new Camera())).IsInstanceof();
+ // script class test
+ // inner class test
+ AssertObject(auto_free(new CustomClass.InnerClassA())).IsInstanceof();
+ AssertObject(auto_free(new CustomClass.InnerClassB())).IsInstanceof();
+
+ // should fail because the current is not a instance of `Tree`
+ AssertObject(auto_free(new Path()), FAIL)
+ .IsInstanceof()
+ .HasFailureMessage("Expected instance of:\n 'Godot.Tree'\n But it was 'Godot.Path'");
+ AssertObject(null, FAIL)
+ .IsInstanceof()
+ .HasFailureMessage("Expected instance of:\n 'Godot.Tree'\n But it was 'Null'");
+ }
+
+ [TestCase]
+ public void IsNotInstanceof()
+ {
+ AssertObject(null).IsNotInstanceof();
+ // engine class test
+ AssertObject(auto_free(new Path())).IsNotInstanceof();
+ // inner class test
+ AssertObject(auto_free(new CustomClass.InnerClassA())).IsNotInstanceof();
+ AssertObject(auto_free(new CustomClass.InnerClassB())).IsNotInstanceof();
+
+ // should fail because the current is not a instance of `Tree`
+ AssertObject(auto_free(new Path()), FAIL)
+ .IsNotInstanceof()
+ .HasFailureMessage("Expected not be a instance of ");
+ }
+
+ [TestCase]
+ public void IsNull()
+ {
+ AssertObject(null).IsNull();
+ // should fail because the current is not null
+ AssertObject(auto_free(new Node()), FAIL)
+ .IsNull()
+ .StartsWithFailureMessage("Expecting: 'Null' but was ");
+ }
+
+ [TestCase]
+ public void IsNotNull()
+ {
+ AssertObject(auto_free(new Node())).IsNotNull();
+ // should fail because the current is null
+ AssertObject(null, FAIL)
+ .IsNotNull()
+ .HasFailureMessage("Expecting: not to be 'Null'");
+ }
+
+ [TestCase]
+ public void IsSame()
+ {
+ var obj1 = auto_free(new Node());
+ var obj2 = obj1;
+ var obj3 = auto_free(obj1.Duplicate());
+ AssertObject(obj1).IsSame(obj1);
+ AssertObject(obj1).IsSame(obj2);
+ AssertObject(obj2).IsSame(obj1);
+ AssertObject(null, FAIL).IsSame(obj1);
+ AssertObject(obj1, FAIL).IsSame(obj3);
+ AssertObject(obj3, FAIL).IsSame(obj1);
+ AssertObject(obj3, FAIL).IsSame(obj2);
+ }
+
+ [TestCase]
+ public void IsNotSame()
+ {
+ var obj1 = auto_free(new Node());
+ var obj2 = obj1;
+ var obj3 = auto_free(obj1.Duplicate());
+ AssertObject(null).IsNotSame(obj1);
+ AssertObject(obj1).IsNotSame(obj3);
+ AssertObject(obj3).IsNotSame(obj1);
+ AssertObject(obj3).IsNotSame(obj2);
+
+ AssertObject(obj1, FAIL).IsNotSame(obj1);
+ AssertObject(obj1, FAIL).IsNotSame(obj2);
+ AssertObject(obj2, FAIL).IsNotSame(obj1);
+ }
+
+ [TestCase]
+ public void must_fail_has_invlalid_type()
+ {
+ AssertObject(1, FAIL).HasFailureMessage("GdUnitObjectAssert inital error, unexpected type ");
+ AssertObject(1.3, FAIL).HasFailureMessage("GdUnitObjectAssert inital error, unexpected type ");
+ AssertObject(true, FAIL).HasFailureMessage("GdUnitObjectAssert inital error, unexpected type ");
+ AssertObject("foo", FAIL).HasFailureMessage("GdUnitObjectAssert inital error, unexpected type ");
+ }
+
+ [TestCase]
+ public void OverrideFailureMessage()
+ {
+ AssertObject(auto_free(new Node()), FAIL)
+ .OverrideFailureMessage("Custom failure message")
+ .IsNull()
+ .HasFailureMessage("Custom failure message");
+ }
+
+}
diff --git a/addons/gdUnit3/test/asserts/GdUnitObjectAssertImplTest.gd b/addons/gdUnit3/test/asserts/GdUnitObjectAssertImplTest.gd
index 14dedaa9..e454e69b 100644
--- a/addons/gdUnit3/test/asserts/GdUnitObjectAssertImplTest.gd
+++ b/addons/gdUnit3/test/asserts/GdUnitObjectAssertImplTest.gd
@@ -31,8 +31,12 @@ func test_is_instanceof():
assert_object(auto_free(Path.new()), GdUnitAssert.EXPECT_FAIL)\
.is_instanceof(Tree)\
.has_failure_message("Expected instance of:\n 'Tree'\n But it was 'Path'")
+ assert_object(null, GdUnitAssert.EXPECT_FAIL)\
+ .is_instanceof(Tree)\
+ .has_failure_message("Expected instance of:\n 'Tree'\n But it was 'Null'")
func test_is_not_instanceof():
+ assert_object(null).is_not_instanceof(Node)
# engine class test
assert_object(auto_free(Path.new())).is_not_instanceof(Tree)
# script class test
@@ -46,12 +50,6 @@ func test_is_not_instanceof():
.is_not_instanceof(Node)\
.has_failure_message("Expected not be a instance of ")
-func test_is_not_instanceof_on_null_value():
- assert_object(null, GdUnitAssert.EXPECT_FAIL)\
- .is_not_null()\
- .is_instanceof(Node)\
- .has_failure_message("Expected instance of:\n 'Node'\n But it was 'Null'")
-
func test_is_null():
assert_object(null).is_null()
# should fail because the current is not null
diff --git a/addons/gdUnit3/test/asserts/GdUnitStringAssertImplTest.cs b/addons/gdUnit3/test/asserts/GdUnitStringAssertImplTest.cs
new file mode 100644
index 00000000..423d53e8
--- /dev/null
+++ b/addons/gdUnit3/test/asserts/GdUnitStringAssertImplTest.cs
@@ -0,0 +1,221 @@
+
+using GdUnit3;
+using static GdUnit3.IGdUnitAssert.EXPECT;
+using static GdUnit3.IGdUnitStringAssert.Compare;
+
+[TestSuite]
+public class GdUnitStringAssertImplTest : GdUnitTestSuite
+{
+ [TestCase]
+ public void IsNull()
+ {
+ AssertString(null).IsNull();
+ // should fail because the current is not null
+ AssertString("abc", FAIL)
+ .IsNull()
+ .StartsWithFailureMessage("Expecting: 'Null' but was 'abc'");
+ }
+
+ [TestCase]
+ public void IsNotNull()
+ {
+ AssertString("abc").IsNotNull();
+ // should fail because the current is null
+ AssertString(null, FAIL)
+ .IsNotNull()
+ .HasFailureMessage("Expecting: not to be 'Null'");
+ }
+
+ [TestCase]
+ public void IsEqual()
+ {
+ AssertString("This is a test message").IsEqual("This is a test message");
+ AssertString("This is a test message", FAIL)
+ .IsEqual("This is a test Message")
+ .HasFailureMessage("Expecting:\n 'This is a test Message'\n but was\n 'This is a test Mmessage'");
+ }
+
+ [TestCase]
+ public void IsEqualIgnoringCase()
+ {
+ AssertString("This is a test message").IsEqualIgnoringCase("This is a test Message");
+ AssertString("This is a test message", FAIL)
+ .IsEqualIgnoringCase("This is a Message")
+ .HasFailureMessage("Expecting:\n 'This is a Message'\n but was\n 'This is a test Mmessage' (ignoring case)");
+ }
+
+ [TestCase]
+ public void IsNotEqual()
+ {
+ AssertString("This is a test message").IsNotEqual("This is a test Message");
+ AssertString("This is a test message", FAIL)
+ .IsNotEqual("This is a test message")
+ .HasFailureMessage("Expecting:\n 'This is a test message'\n not equal to\n 'This is a test message'");
+ }
+
+ [TestCase]
+ public void IsNotEqualIgnoringCase()
+ {
+ AssertString("This is a test message").IsNotEqualIgnoringCase("This is a Message");
+ AssertString("This is a test message", FAIL)
+ .IsNotEqualIgnoringCase("This is a test Message")
+ .HasFailureMessage("Expecting:\n 'This is a test Message'\n not equal to\n 'This is a test message'");
+ }
+
+ [TestCase]
+ public void IsEmpty()
+ {
+ AssertString("").IsEmpty();
+ // should fail because the current value is not empty it contains a space
+ AssertString(" ", FAIL)
+ .IsEmpty()
+ .HasFailureMessage("Expecting:\n must be empty but was\n ' '");
+ AssertString("abc", FAIL)
+ .IsEmpty()
+ .HasFailureMessage("Expecting:\n must be empty but was\n 'abc'");
+ }
+
+ [TestCase]
+ public void IsNotEmpty()
+ {
+ AssertString(" ").IsNotEmpty();
+ AssertString(" ").IsNotEmpty();
+ AssertString("abc").IsNotEmpty();
+ // should fail because current is empty
+ AssertString("", FAIL)
+ .IsNotEmpty()
+ .HasFailureMessage("Expecting:\n must not be empty");
+ }
+
+ [TestCase]
+ public void Contains()
+ {
+ AssertString("This is a test message").Contains("a test");
+ // must fail because of camel case difference
+ AssertString("This is a test message", FAIL)
+ .Contains("a Test")
+ .HasFailureMessage("Expecting:\n 'This is a test message'\n do contains\n 'a Test'");
+ }
+
+ [TestCase]
+ public void notContains()
+ {
+ AssertString("This is a test message").NotContains("a tezt");
+ AssertString("This is a test message", FAIL)
+ .NotContains("a test")
+ .HasFailureMessage("Expecting:\n 'This is a test message'\n not do contain\n 'a test'");
+ }
+
+ [TestCase]
+ public void ContainsIgnoringCase()
+ {
+ AssertString("This is a test message").ContainsIgnoringCase("a Test");
+ AssertString("This is a test message", FAIL)
+ .ContainsIgnoringCase("a Tesd")
+ .HasFailureMessage("Expecting:\n 'This is a test message'\n contains\n 'a Tesd'\n (ignoring case)");
+ }
+
+ [TestCase]
+ public void NotContainsIgnoringCase()
+ {
+ AssertString("This is a test message").NotContainsIgnoringCase("a Tezt");
+ AssertString("This is a test message", FAIL)
+ .NotContainsIgnoringCase("a Test")
+ .HasFailureMessage("Expecting:\n 'This is a test message'\n not do contains\n 'a Test'\n (ignoring case)");
+ }
+
+ [TestCase]
+ public void StartsWith()
+ {
+ AssertString("This is a test message").StartsWith("This is");
+ AssertString("This is a test message", FAIL)
+ .StartsWith("This iss")
+ .HasFailureMessage("Expecting:\n 'This is a test message'\n to start with\n 'This iss'");
+ AssertString("This is a test message", FAIL)
+ .StartsWith("this is")
+ .HasFailureMessage("Expecting:\n 'This is a test message'\n to start with\n 'this is'");
+ AssertString("This is a test message", FAIL)
+ .StartsWith("test")
+ .HasFailureMessage("Expecting:\n 'This is a test message'\n to start with\n 'test'");
+ }
+
+ [TestCase]
+ public void EndsWith()
+ {
+ AssertString("This is a test message").EndsWith("test message");
+ AssertString("This is a test message", FAIL)
+ .EndsWith("tes message")
+ .HasFailureMessage("Expecting:\n 'This is a test message'\n to end with\n 'tes message'");
+ AssertString("This is a test message", FAIL)
+ .EndsWith("a test")
+ .HasFailureMessage("Expecting:\n 'This is a test message'\n to end with\n 'a test'");
+ }
+
+ [TestCase]
+ public void HasLenght()
+ {
+ AssertString("This is a test message").HasLength(22);
+ AssertString("").HasLength(0);
+ AssertString("This is a test message", FAIL)
+ .HasLength(23)
+ .HasFailureMessage("Expecting size:\n '23' but was '22' in\n 'This is a test message'");
+ }
+
+ [TestCase]
+ public void HasLenghtLessThan()
+ {
+ AssertString("This is a test message").HasLength(23, LESS_THAN);
+ AssertString("This is a test message").HasLength(42, LESS_THAN);
+ AssertString("This is a test message", FAIL)
+ .HasLength(22, LESS_THAN)
+ .HasFailureMessage("Expecting size to be less than:\n '22' but was '22' in\n 'This is a test message'");
+ }
+
+ [TestCase]
+ public void HasLenghtLessEqual()
+ {
+ AssertString("This is a test message").HasLength(22, LESS_EQUAL);
+ AssertString("This is a test message").HasLength(23, LESS_EQUAL);
+ AssertString("This is a test message", FAIL)
+ .HasLength(21, LESS_EQUAL)
+ .HasFailureMessage("Expecting size to be less than or equal:\n '21' but was '22' in\n 'This is a test message'");
+ }
+
+ [TestCase]
+ public void HasLenghtGreaterThan()
+ {
+ AssertString("This is a test message").HasLength(21, GREATER_THAN);
+ AssertString("This is a test message", FAIL)
+ .HasLength(22, GREATER_THAN)
+ .HasFailureMessage("Expecting size to be greater than:\n '22' but was '22' in\n 'This is a test message'");
+ }
+
+ [TestCase]
+ public void HasLenghtGreaterEqual()
+ {
+ AssertString("This is a test message").HasLength(21, GREATER_EQUAL);
+ AssertString("This is a test message").HasLength(22, GREATER_EQUAL);
+ AssertString("This is a test message", FAIL)
+ .HasLength(23, GREATER_EQUAL)
+ .HasFailureMessage("Expecting size to be greater than or equal:\n '23' but was '22' in\n 'This is a test message'");
+ }
+
+ [TestCase]
+ public void Fluent()
+ {
+ AssertString("value a").HasLength(7)
+ .IsNotEqual("a")
+ .IsEqual("value a")
+ .IsNotNull();
+ }
+
+ [TestCase]
+ public void OverrideFailureMessage()
+ {
+ AssertString("", FAIL)
+ .OverrideFailureMessage("Custom failure message")
+ .IsNull()
+ .HasFailureMessage("Custom failure message");
+ }
+
+}
diff --git a/addons/gdUnit3/test/core/ExampleTest.cs b/addons/gdUnit3/test/core/ExampleTest.cs
new file mode 100644
index 00000000..594c295d
--- /dev/null
+++ b/addons/gdUnit3/test/core/ExampleTest.cs
@@ -0,0 +1,42 @@
+using Godot;
+using System;
+using GdUnit3;
+
+
+
+[TestSuite]
+public class ExampleTest : GdUnitTestSuite
+{
+ public override void Before()
+ {
+ GD.PrintS("calling Before");
+ }
+
+ public override void After()
+ {
+ GD.PrintS("calling After");
+ }
+
+ public override void BeforeTest()
+ {
+ GD.PrintS("calling BeforeTest");
+ }
+
+ public override void AfterTest()
+ {
+ GD.PrintS("calling AfterTest");
+ }
+
+ [TestCase]
+ public void TestFoo()
+ {
+ AssertBool(true).IsEqual(true);
+ }
+
+ [TestCase]
+ public void TestBar()
+ {
+ AssertBool(true).IsEqual(true);
+ }
+
+}
diff --git a/addons/gdUnit3/test/core/GdObjectsTest.gd b/addons/gdUnit3/test/core/GdObjectsTest.gd
index d4ffb949..4ab91abb 100644
--- a/addons/gdUnit3/test/core/GdObjectsTest.gd
+++ b/addons/gdUnit3/test/core/GdObjectsTest.gd
@@ -551,3 +551,14 @@ func test_all_types() -> void:
GdObjects.TYPE_VOID,
GdObjects.TYPE_VARARG,
])
+
+func test_is_test_suite() -> void:
+ assert_bool(GdObjects.is_test_suite(GdUnitTestResourceLoader.load_gd_script("res://addons/gdUnit3/test/core/ResultTest.gd"))).is_true()
+ if GdUnitTools.is_mono_supported():
+ assert_bool(GdObjects.is_test_suite(GdUnitTestResourceLoader.load_cs_script("res://addons/gdUnit3/test/core/ExampleTest.cs"))).is_true()
+ assert_bool(GdObjects.is_test_suite(GdUnitTestResourceLoader.load_cs_script("res://addons/gdUnit3/test/core/resources/testsuites/mono/NotATestSuite.cs"))).is_false()
+ # currently not supported
+ assert_bool(GdObjects.is_test_suite(NativeScript.new())).is_false()
+ assert_bool(GdObjects.is_test_suite(PluginScript.new())).is_false()
+ assert_bool(GdObjects.is_test_suite(VisualScript.new())).is_false()
+
diff --git a/addons/gdUnit3/test/core/GdUnitExecutorTest.gd b/addons/gdUnit3/test/core/GdUnitExecutorTest.gd
index 0ce528d4..3807d741 100644
--- a/addons/gdUnit3/test/core/GdUnitExecutorTest.gd
+++ b/addons/gdUnit3/test/core/GdUnitExecutorTest.gd
@@ -13,13 +13,13 @@ func before():
Engine.get_main_loop().root.add_child(_executor)
_executor.connect("send_event_debug", self, "_on_executor_event")
-func resource(resource_path :String) -> GdUnitTestSuite:
+func resource(resource_path :String) -> GdUnitTestSuiteDelegator:
return GdUnitTestResourceLoader.load_test_suite(resource_path)
func _on_executor_event(event :GdUnitEvent) -> void:
_events.append(event)
-func execute(test_suite :GdUnitTestSuite, enable_orphan_detection := true):
+func execute(test_suite :GdUnitTestSuiteDelegator, enable_orphan_detection := true):
yield(get_tree(), "idle_frame")
_events.clear()
_executor._memory_pool.configure(enable_orphan_detection)
@@ -82,7 +82,7 @@ func assert_event_states(events :Array) -> GdUnitArrayAssert:
func test_execute_success() -> void:
var test_suite := resource("res://addons/gdUnit3/test/core/resources/testsuites/TestSuiteAllStagesSuccess.resource")
# verify all test cases loaded
- assert_array(test_suite.get_children()).extract("get_name").contains_exactly(["test_case1", "test_case2"])
+ assert_array(test_suite.get_test_cases()).extract("get_name").contains_exactly(["test_case1", "test_case2"])
# simulate test suite execution
var events = yield(execute(test_suite), "completed" )
# verify basis infos
@@ -110,7 +110,7 @@ func test_execute_success() -> void:
func test_execute_failure_on_stage_before() -> void:
var test_suite := resource("res://addons/gdUnit3/test/core/resources/testsuites/TestSuiteFailOnStageBefore.resource")
# verify all test cases loaded
- assert_array(test_suite.get_children()).extract("get_name").contains_exactly(["test_case1", "test_case2"])
+ assert_array(test_suite.get_test_cases()).extract("get_name").contains_exactly(["test_case1", "test_case2"])
# simulate test suite execution
var events = yield(execute(test_suite), "completed" )
# verify basis infos
@@ -147,7 +147,7 @@ func test_execute_failure_on_stage_before() -> void:
func test_execute_failure_on_stage_after() -> void:
var test_suite := resource("res://addons/gdUnit3/test/core/resources/testsuites/TestSuiteFailOnStageAfter.resource")
# verify all test cases loaded
- assert_array(test_suite.get_children()).extract("get_name").contains_exactly(["test_case1", "test_case2"])
+ assert_array(test_suite.get_test_cases()).extract("get_name").contains_exactly(["test_case1", "test_case2"])
# simulate test suite execution
var events = yield(execute(test_suite), "completed" )
# verify basis infos
@@ -184,7 +184,7 @@ func test_execute_failure_on_stage_after() -> void:
func test_execute_failure_on_stage_before_test() -> void:
var test_suite := resource("res://addons/gdUnit3/test/core/resources/testsuites/TestSuiteFailOnStageBeforeTest.resource")
# verify all test cases loaded
- assert_array(test_suite.get_children()).extract("get_name").contains_exactly(["test_case1", "test_case2"])
+ assert_array(test_suite.get_test_cases()).extract("get_name").contains_exactly(["test_case1", "test_case2"])
# simulate test suite execution
var events = yield(execute(test_suite), "completed" )
# verify basis infos
@@ -224,7 +224,7 @@ func test_execute_failure_on_stage_before_test() -> void:
func test_execute_failure_on_stage_after_test() -> void:
var test_suite := resource("res://addons/gdUnit3/test/core/resources/testsuites/TestSuiteFailOnStageAfterTest.resource")
# verify all test cases loaded
- assert_array(test_suite.get_children()).extract("get_name").contains_exactly(["test_case1", "test_case2"])
+ assert_array(test_suite.get_test_cases()).extract("get_name").contains_exactly(["test_case1", "test_case2"])
# simulate test suite execution
var events = yield(execute(test_suite), "completed" )
# verify basis infos
@@ -264,7 +264,7 @@ func test_execute_failure_on_stage_after_test() -> void:
func test_execute_failure_on_stage_test_case1() -> void:
var test_suite := resource("res://addons/gdUnit3/test/core/resources/testsuites/TestSuiteFailOnStageTestCase1.resource")
# verify all test cases loaded
- assert_array(test_suite.get_children()).extract("get_name").contains_exactly(["test_case1", "test_case2"])
+ assert_array(test_suite.get_test_cases()).extract("get_name").contains_exactly(["test_case1", "test_case2"])
# simulate test suite execution
var events = yield(execute(test_suite), "completed" )
# verify basis infos
@@ -302,7 +302,7 @@ func test_execute_failure_on_muliple_stages() -> void:
# this is a more complex failure state, we expect to find multipe failures on different stages
var test_suite := resource("res://addons/gdUnit3/test/core/resources/testsuites/TestSuiteFailOnMultipeStages.resource")
# verify all test cases loaded
- assert_array(test_suite.get_children()).extract("get_name").contains_exactly(["test_case1", "test_case2"])
+ assert_array(test_suite.get_test_cases()).extract("get_name").contains_exactly(["test_case1", "test_case2"])
# simulate test suite execution
var events = yield(execute(test_suite), "completed" )
# verify basis infos
@@ -345,7 +345,7 @@ func test_execute_failure_and_orphans() -> void:
# this is a more complex failure state, we expect to find multipe orphans on different stages
var test_suite := resource("res://addons/gdUnit3/test/core/resources/testsuites/TestSuiteFailAndOrpahnsDetected.resource")
# verify all test cases loaded
- assert_array(test_suite.get_children()).extract("get_name").contains_exactly(["test_case1", "test_case2"])
+ assert_array(test_suite.get_test_cases()).extract("get_name").contains_exactly(["test_case1", "test_case2"])
# simulate test suite execution
var events = yield(execute(test_suite), "completed")
# verify basis infos
@@ -396,7 +396,7 @@ func test_execute_failure_and_orphans_report_orphan_disabled() -> void:
# this is a more complex failure state, we expect to find multipe orphans on different stages
var test_suite := resource("res://addons/gdUnit3/test/core/resources/testsuites/TestSuiteFailAndOrpahnsDetected.resource")
# verify all test cases loaded
- assert_array(test_suite.get_children()).extract("get_name").contains_exactly(["test_case1", "test_case2"])
+ assert_array(test_suite.get_test_cases()).extract("get_name").contains_exactly(["test_case1", "test_case2"])
# simulate test suite execution whit disabled orphan detection
var events = yield(execute(test_suite, false), "completed")
# verify basis infos
@@ -438,7 +438,7 @@ func test_execute_error_on_test_timeout() -> void:
# this tests a timeout on a test case reported as error
var test_suite := resource("res://addons/gdUnit3/test/core/resources/testsuites/TestSuiteErrorOnTestTimeout.resource")
# verify all test cases loaded
- assert_array(test_suite.get_children()).extract("get_name").contains_exactly(["test_case1", "test_case2"])
+ assert_array(test_suite.get_test_cases()).extract("get_name").contains_exactly(["test_case1", "test_case2"])
# simulate test suite execution
var events = yield(execute(test_suite), "completed" )
# verify basis infos
@@ -480,7 +480,7 @@ func test_execute_failure_fuzzer_iteration() -> void:
var test_suite := resource("res://addons/gdUnit3/test/core/resources/testsuites/GdUnitFuzzerTest.resource")
# verify all test cases loaded
var expected_test_cases := ["test_multi_yielding_with_fuzzer", "test_multi_yielding_with_fuzzer_fail_after_3_iterations"]
- assert_array(test_suite.get_children()).extract("get_name").contains_exactly(expected_test_cases)
+ assert_array(test_suite.get_test_cases()).extract("get_name").contains_exactly(expected_test_cases)
# simulate test suite execution
var events = yield(execute(test_suite), "completed" )
@@ -518,7 +518,7 @@ func test_execute_failure_fuzzer_iteration() -> void:
func test_execute_add_child_on_before_GD_106() -> void:
var test_suite := resource("res://addons/gdUnit3/test/core/resources/testsuites/TestSuiteFailAddChildStageBefore.resource")
# verify all test cases loaded
- assert_array(test_suite.get_children()).extract("get_name").contains_exactly(["test_case1", "test_case2"])
+ assert_array(test_suite.get_test_cases()).extract("get_name").contains_exactly(["test_case1", "test_case2"])
# simulate test suite execution
var events = yield(execute(test_suite), "completed" )
# verify basis infos
diff --git a/addons/gdUnit3/test/core/_TestSuiteScannerTest.gd b/addons/gdUnit3/test/core/_TestSuiteScannerTest.gd
index 04e2d142..4fda3a09 100644
--- a/addons/gdUnit3/test/core/_TestSuiteScannerTest.gd
+++ b/addons/gdUnit3/test/core/_TestSuiteScannerTest.gd
@@ -125,11 +125,14 @@ func test_build_test_suite_path() -> void:
func test_parse_and_add_test_cases() -> void:
var default_time := GdUnitSettings.test_timeout()
- var scanner = auto_free(_TestSuiteScanner.new())
+ var scanner :_TestSuiteScanner = auto_free(_TestSuiteScanner.new())
+ # fake a test suite
var test_suite :GdUnitTestSuite = auto_free(GdUnitTestSuite.new())
- var script_path := "res://addons/gdUnit3/test/core/resources/test_script_with_arguments.gd"
+ var script := GDScript.new()
+ script.resource_path = "res://addons/gdUnit3/test/core/resources/test_script_with_arguments.gd"
+ test_suite.set_script(script)
var test_case_names := PoolStringArray(["test_no_args", "test_with_timeout", "test_with_fuzzer", "test_with_fuzzer_iterations", "test_with_multible_fuzzers"])
- scanner._parse_and_add_test_cases(test_suite, script_path, test_case_names)
+ scanner._parse_and_add_test_cases(test_suite, test_suite.get_script(), test_case_names)
assert_array(test_suite.get_children())\
.extractv(extr("get_name"), extr("timeout"), extr("fuzzers"), extr("iterations"))\
.contains_exactly([
@@ -143,7 +146,7 @@ func test_scan_by_inheritance_class_name() -> void:
var scanner :_TestSuiteScanner = auto_free(_TestSuiteScanner.new())
var test_suites := scanner.scan("res://addons/gdUnit3/test/core/resources/scan_testsuite_inheritance/by_class_name/")
- assert_array(test_suites).extractv(extr("get_name"), extr("get_script.get_path"), extr("get_children.get_name"))\
+ assert_array(test_suites).extractv(extr("get_name"), extr("get_script.get_path"), extr("get_test_cases.get_name"))\
.contains_exactly_in_any_order([
tuple("BaseTest", "res://addons/gdUnit3/test/core/resources/scan_testsuite_inheritance/by_class_name/BaseTest.gd", ["test_foo1"]),
tuple("ExtendedTest","res://addons/gdUnit3/test/core/resources/scan_testsuite_inheritance/by_class_name/ExtendedTest.gd", ["test_foo2", "test_foo1"]),
@@ -157,7 +160,7 @@ func test_scan_by_inheritance_class_path() -> void:
var scanner :_TestSuiteScanner = auto_free(_TestSuiteScanner.new())
var test_suites := scanner.scan("res://addons/gdUnit3/test/core/resources/scan_testsuite_inheritance/by_class_path/")
- assert_array(test_suites).extractv(extr("get_name"), extr("get_script.get_path"), extr("get_children.get_name"))\
+ assert_array(test_suites).extractv(extr("get_name"), extr("get_script.get_path"), extr("get_test_cases.get_name"))\
.contains_exactly_in_any_order([
tuple("BaseTest", "res://addons/gdUnit3/test/core/resources/scan_testsuite_inheritance/by_class_path/BaseTest.gd", ["test_foo1"]),
tuple("ExtendedTest","res://addons/gdUnit3/test/core/resources/scan_testsuite_inheritance/by_class_path/ExtendedTest.gd", ["test_foo2", "test_foo1"]),
@@ -166,3 +169,13 @@ func test_scan_by_inheritance_class_path() -> void:
# finally free all scaned test suites
for ts in test_suites:
ts.free()
+
+func test_is_script_format_supported() -> void:
+ assert_bool(_TestSuiteScanner._is_script_format_supported("res://exampe.gd")).is_true()
+ if GdUnitTools.is_mono_supported():
+ assert_bool(_TestSuiteScanner._is_script_format_supported("res://exampe.cs")).is_true()
+ else:
+ assert_bool(_TestSuiteScanner._is_script_format_supported("res://exampe.cs")).is_false()
+ assert_bool(_TestSuiteScanner._is_script_format_supported("res://exampe.gdns")).is_false()
+ assert_bool(_TestSuiteScanner._is_script_format_supported("res://exampe.vs")).is_false()
+ assert_bool(_TestSuiteScanner._is_script_format_supported("res://exampe.tres")).is_false()
diff --git a/addons/gdUnit3/test/core/resources/testsuites/mono/NotATestSuite.cs b/addons/gdUnit3/test/core/resources/testsuites/mono/NotATestSuite.cs
new file mode 100644
index 00000000..6ac562bb
--- /dev/null
+++ b/addons/gdUnit3/test/core/resources/testsuites/mono/NotATestSuite.cs
@@ -0,0 +1,21 @@
+using Godot;
+using System;
+using GdUnit3;
+
+
+// will be ignored becaus of missing `[TestSuite]` animation
+public class NotATestSuite : GdUnitTestSuite
+{
+
+ public override void Before()
+ {
+ GD.PrintS("calling Before");
+ }
+
+ [TestCase]
+ public void TestFoo()
+ {
+ AssertBool(true).IsEqual(false);
+ }
+
+}
diff --git a/addons/gdUnit3/test/ui/parts/InspectorTreeMainPanelTest.gd b/addons/gdUnit3/test/ui/parts/InspectorTreeMainPanelTest.gd
index 0576d637..3724fae1 100644
--- a/addons/gdUnit3/test/ui/parts/InspectorTreeMainPanelTest.gd
+++ b/addons/gdUnit3/test/ui/parts/InspectorTreeMainPanelTest.gd
@@ -30,7 +30,7 @@ func after_test():
_inspector.free()
-static func toDto(test_suite :GdUnitTestSuite) -> GdUnitTestSuiteDto:
+static func toDto(test_suite :GdUnitTestSuiteDelegator) -> GdUnitTestSuiteDto:
var dto := GdUnitTestSuiteDto.new()
return dto.deserialize(dto.serialize(test_suite)) as GdUnitTestSuiteDto
diff --git a/gdUnit3.csproj b/gdUnit3.csproj
new file mode 100644
index 00000000..af6af65f
--- /dev/null
+++ b/gdUnit3.csproj
@@ -0,0 +1,6 @@
+
+
+ net472
+ preview
+
+
diff --git a/gdUnit3.sln b/gdUnit3.sln
new file mode 100644
index 00000000..993157f3
--- /dev/null
+++ b/gdUnit3.sln
@@ -0,0 +1,19 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2012
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "gdUnit3", "gdUnit3.csproj", "{1F7492EE-4E0C-47ED-8D6F-FFF9FC4DCDB6}"
+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
+ {1F7492EE-4E0C-47ED-8D6F-FFF9FC4DCDB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1F7492EE-4E0C-47ED-8D6F-FFF9FC4DCDB6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1F7492EE-4E0C-47ED-8D6F-FFF9FC4DCDB6}.ExportDebug|Any CPU.ActiveCfg = ExportDebug|Any CPU
+ {1F7492EE-4E0C-47ED-8D6F-FFF9FC4DCDB6}.ExportDebug|Any CPU.Build.0 = ExportDebug|Any CPU
+ {1F7492EE-4E0C-47ED-8D6F-FFF9FC4DCDB6}.ExportRelease|Any CPU.ActiveCfg = ExportRelease|Any CPU
+ {1F7492EE-4E0C-47ED-8D6F-FFF9FC4DCDB6}.ExportRelease|Any CPU.Build.0 = ExportRelease|Any CPU
+ EndGlobalSection
+EndGlobal
diff --git a/project.godot b/project.godot
index 03df93b7..cd5f2997 100644
--- a/project.godot
+++ b/project.godot
@@ -194,11 +194,31 @@ _global_script_classes=[ {
"language": "GDScript",
"path": "res://addons/gdUnit3/test/mocker/resources/DeepStubTestClass.gd"
}, {
+"base": "Spatial",
+"class": "Door",
+"language": "GDScript",
+"path": "res://gdUnit3-examples/RoomDemo3D/src/rooms/components/door.gd"
+}, {
"base": "GdUnitArgumentMatcher",
"class": "EqualsArgumentMatcher",
"language": "GDScript",
"path": "res://addons/gdUnit3/src/matchers/EqualsArgumentMatcher.gd"
}, {
+"base": "GdUnitTestSuite",
+"class": "ExampleMockWithSignalTest",
+"language": "GDScript",
+"path": "res://gdUnit3-examples/SignalsTestExamples/test/ExampleMockWithSignalTest.gd"
+}, {
+"base": "GdUnitTestSuite",
+"class": "ExampleSpyWithSignalTest",
+"language": "GDScript",
+"path": "res://gdUnit3-examples/SignalsTestExamples/test/ExampleSpyWithSignalTest.gd"
+}, {
+"base": "Reference",
+"class": "ExampleWithSignal",
+"language": "GDScript",
+"path": "res://gdUnit3-examples/SignalsTestExamples/src/ExampleWithSignal.gd"
+}, {
"base": "BaseTest",
"class": "ExtendedTest",
"language": "GDScript",
@@ -219,6 +239,21 @@ _global_script_classes=[ {
"language": "GDScript",
"path": "res://addons/gdUnit3/src/Fuzzers.gd"
}, {
+"base": "GdUnitTestSuite",
+"class": "GameMockTest",
+"language": "GDScript",
+"path": "res://gdUnit3-examples/MenuDemo2D/test/GameMockTest.gd"
+}, {
+"base": "Reference",
+"class": "GameRepository",
+"language": "GDScript",
+"path": "res://gdUnit3-examples/MenuDemo2D/src/GameRepository.gd"
+}, {
+"base": "GdUnitTestSuite",
+"class": "GameSpyTest",
+"language": "GDScript",
+"path": "res://gdUnit3-examples/MenuDemo2D/test/GameSpyTest.gd"
+}, {
"base": "Resource",
"class": "GdAssertMessages",
"language": "GDScript",
@@ -635,6 +670,16 @@ _global_script_classes=[ {
"path": "res://addons/gdUnit3/test/core/GdUnitSceneRunnerTest.gd"
}, {
"base": "Reference",
+"class": "GdUnitScriptType",
+"language": "GDScript",
+"path": "res://addons/gdUnit3/src/core/GdUnitScriptType.gd"
+}, {
+"base": "GdUnitTestSuite",
+"class": "GdUnitScriptTypeTest",
+"language": "GDScript",
+"path": "res://addons/gdUnit3/test/GdUnitScriptTypeTest.gd"
+}, {
+"base": "Reference",
"class": "GdUnitSettings",
"language": "GDScript",
"path": "res://addons/gdUnit3/src/core/GdUnitSettings.gd"
@@ -724,6 +769,11 @@ _global_script_classes=[ {
"language": "GDScript",
"path": "res://addons/gdUnit3/src/GdUnitTestSuite.gd"
}, {
+"base": "Node",
+"class": "GdUnitTestSuiteDelegator",
+"language": "GDScript",
+"path": "res://addons/gdUnit3/src/core/GdUnitTestSuiteDelegator.gd"
+}, {
"base": "GdUnitResourceDto",
"class": "GdUnitTestSuiteDto",
"language": "GDScript",
@@ -834,6 +884,11 @@ _global_script_classes=[ {
"language": "GDScript",
"path": "res://addons/gdUnit3/test/core/LocalTimeTest.gd"
}, {
+"base": "Control",
+"class": "MenuDialog",
+"language": "GDScript",
+"path": "res://gdUnit3-examples/MenuDemo2D/src/menu/MenuDialog.gd"
+}, {
"base": "GdUnitTestSuite",
"class": "NetworkServerTest",
"language": "GDScript",
@@ -849,6 +904,11 @@ _global_script_classes=[ {
"language": "GDScript",
"path": "res://addons/gdUnit3/test/resources/core/Person.gd"
}, {
+"base": "KinematicBody",
+"class": "Player",
+"language": "GDScript",
+"path": "res://gdUnit3-examples/RoomDemo3D/src/Player.gd"
+}, {
"base": "GdUnitMonitor",
"class": "PushErrorMonitor",
"language": "GDScript",
@@ -904,6 +964,16 @@ _global_script_classes=[ {
"language": "GDScript",
"path": "res://addons/gdUnit3/test/core/ResultTest.gd"
}, {
+"base": "Spatial",
+"class": "RoomWithDoor",
+"language": "GDScript",
+"path": "res://gdUnit3-examples/RoomDemo3D/src/rooms/RoomWithDoor.gd"
+}, {
+"base": "GdUnitTestSuite",
+"class": "RoomWithDoorTest",
+"language": "GDScript",
+"path": "res://gdUnit3-examples/RoomDemo3D/test/rooms/RoomWithDoorTest.gd"
+}, {
"base": "Resource",
"class": "SignalHandler",
"language": "GDScript",
@@ -1012,11 +1082,18 @@ _global_script_class_icons={
"CustomNodeTestClass": "",
"CustomResourceTestClass": "",
"DeepStubTestClass": "",
+"Door": "",
"EqualsArgumentMatcher": "",
+"ExampleMockWithSignalTest": "",
+"ExampleSpyWithSignalTest": "",
+"ExampleWithSignal": "",
"ExtendedTest": "",
"Fuzzer": "",
"FuzzerTool": "",
"Fuzzers": "",
+"GameMockTest": "",
+"GameRepository": "",
+"GameSpyTest": "",
"GdAssertMessages": "",
"GdAssertReports": "",
"GdClassDescriptor": "",
@@ -1100,6 +1177,8 @@ _global_script_class_icons={
"GdUnitRunnerConfigTest": "",
"GdUnitSceneRunner": "",
"GdUnitSceneRunnerTest": "",
+"GdUnitScriptType": "",
+"GdUnitScriptTypeTest": "",
"GdUnitSettings": "",
"GdUnitSettingsTest": "",
"GdUnitSingleton": "",
@@ -1118,6 +1197,7 @@ _global_script_class_icons={
"GdUnitTestCaseReport": "",
"GdUnitTestResourceLoader": "",
"GdUnitTestSuite": "",
+"GdUnitTestSuiteDelegator": "",
"GdUnitTestSuiteDto": "",
"GdUnitTestSuiteReport": "",
"GdUnitTestSuiteTest": "",
@@ -1140,9 +1220,11 @@ _global_script_class_icons={
"IntFuzzer": "",
"LocalTime": "",
"LocalTimeTest": "",
+"MenuDialog": "",
"NetworkServerTest": "",
"OverridenGetClassTestClass": "",
"Person": "",
+"Player": "",
"PushErrorMonitor": "",
"PushErrorMonitorTest": "",
"RPC": "",
@@ -1154,6 +1236,8 @@ _global_script_class_icons={
"RPCMessage": "",
"Result": "",
"ResultTest": "",
+"RoomWithDoor": "",
+"RoomWithDoorTest": "",
"SignalHandler": "",
"Spell": "",
"StringFuzzer": "",
diff --git a/runtest.cmd b/runtest.cmd
index 785accc0..0d071946 100644
--- a/runtest.cmd
+++ b/runtest.cmd
@@ -7,6 +7,15 @@ IF NOT DEFINED GODOT_BIN (
EXIT /b -1
)
+REM scan if Godot mono used and compile c# classes
+for /f "tokens=5 delims=. " %%i in ('%GODOT_BIN% --version') do set GODOT_TYPE=%%i
+IF "%GODOT_TYPE%" == "mono" (
+ ECHO "Godot mono detected"
+ ECHO Compiling c# classes ... Please Wait
+ %GODOT_BIN% --build-solutions --no-window -q --quiet
+ ECHO done
+)
+
%GODOT_BIN% --no-window -s -d .\addons\gdUnit3\bin\GdUnitCmdTool.gd %*
SET exit_code=%errorlevel%
%GODOT_BIN% --no-window --quiet -s -d .\addons\gdUnit3\bin\GdUnitCopyLog.gd %*