diff --git a/PackageVersions.props b/PackageVersions.props
index 782376a9..9b28c7e4 100644
--- a/PackageVersions.props
+++ b/PackageVersions.props
@@ -5,7 +5,7 @@
17.9.0
4.9.2
4.18.4
- 4.3.0
+ 4.3.1
2.0.0
diff --git a/api/ReleaseNotes.txt b/api/ReleaseNotes.txt
index 4563bd6f..c3bf8e58 100644
--- a/api/ReleaseNotes.txt
+++ b/api/ReleaseNotes.txt
@@ -1,3 +1,10 @@
+v4.3.1
+
+Bug Fixes:
+* GD-135: Cannot test signals on non-node classes
+
+-------------------------------------------------------------------------------------------------------
+
v4.3.0
Improvements:
diff --git a/api/src/asserts/AssertFailures.cs b/api/src/asserts/AssertFailures.cs
index 086417f9..98975865 100644
--- a/api/src/asserts/AssertFailures.cs
+++ b/api/src/asserts/AssertFailures.cs
@@ -6,6 +6,9 @@ namespace GdUnit4.Asserts;
using System.Linq;
using System.Runtime.CompilerServices;
+using Godot;
+using Godot.Collections;
+
internal sealed class AssertFailures
{
public const string WARN_COLOR = "#EFF883";
@@ -23,18 +26,18 @@ internal static string AsObjectId(object? value)
if (value == null)
return "";
var type = value.GetType();
- if (value is Godot.Variant gv)
+ if (value is Variant gv)
{
value = gv.UnboxVariant()!;
if (value != null)
type = value.GetType();
else
- return $" (Null)";
+ return " (Null)";
}
var instanceId = "";
var name = $"<{type.FullName?.Replace("[", "")?.Replace("]", "")!}>";
- if (value is Godot.GodotObject go)
+ if (value is GodotObject go && GodotObject.IsInstanceValid(go))
instanceId = $"objId: {go.GetInstanceId()}";
else
{
@@ -42,6 +45,7 @@ internal static string AsObjectId(object? value)
if (HasOverriddenToString(value))
name = value.ToString();
}
+
return $"{name} ({instanceId})";
//if (!type.IsGenericType)
@@ -50,7 +54,6 @@ internal static string AsObjectId(object? value)
}
-
private static string FormatDictionary(IDictionary dict, string color)
{
if (dict.Keys.Count == 0)
@@ -62,7 +65,7 @@ private static string FormatDictionary(IDictionary dict, string color)
return $"[color={color}]{pairs}[/color]";
}
- private static string FormatDictionary(IDictionary dict, string color)
+ private static string FormatDictionary(IDictionary dict, string color)
{
if (dict.Keys.Count == 0)
return $"[color={color}][/color]";
@@ -74,6 +77,7 @@ private static string FormatDictionary(IDictionary
object? value = entry.Value.UnboxVariant();
keyValues.Add($"{{{key.Formatted()}, {value.Formatted()}}}");
}
+
var pairs = string.Join("; ", keyValues.ToArray());
return $"[color={color}]{pairs}[/color]";
}
@@ -86,9 +90,8 @@ private static string FormatEnumerable(IEnumerable enumerable, string color)
var keyValues = new ArrayList();
do
- {
keyValues.Add(enumerator.Current);
- } while (enumerator.MoveNext());
+ while (enumerator.MoveNext());
return $"[color={color}]{keyValues.Formatted()}[/color]";
}
@@ -103,14 +106,14 @@ public static string FormatValue(object? value, string color, bool quoted)
if (value is Type)
return $"[color={color}]<{value}>[/color]";
- if (value is Godot.Variant gv)
+ if (value is Variant gv)
value = value.UnboxVariant();
var type = value!.GetType();
if (value is IDictionary dict)
return FormatDictionary(dict, color);
- if (value is Godot.Collections.Dictionary gDict)
+ if (value is Dictionary gDict)
return FormatDictionary(gDict, color);
if (type.IsGenericGodotDictionary())
@@ -135,223 +138,226 @@ public static string IsFalse() =>
$"{FormatFailure("Expecting:")} {FormatExpected(false)} but is {FormatCurrent(true)}";
public static string IsEqual(object? current, object? expected) =>
- current is IEnumerable || expected is IEnumerable ? $"""
- {FormatFailure("Expecting be equal:")}
- {FormatExpected(expected).Indentation(1)}
- but is
- {FormatCurrent(current).Indentation(1)}
- """
+ current is IEnumerable || expected is IEnumerable
+ ? $"""
+ {FormatFailure("Expecting be equal:")}
+ {FormatExpected(expected).Indentation(1)}
+ but is
+ {FormatCurrent(current).Indentation(1)}
+ """
: $"""
- {FormatFailure("Expecting be equal:")}
- {FormatExpected(expected).Indentation(1)} but is {FormatCurrent(current)}
- """;
+ {FormatFailure("Expecting be equal:")}
+ {FormatExpected(expected).Indentation(1)} but is {FormatCurrent(current)}
+ """;
public static string IsEqual(IEnumerable current, IEnumerable expected) =>
$"""
- {FormatFailure("Expecting be equal:")}
- {FormatExpected(expected).Indentation(1)}
- but is
- {FormatCurrent(current).Indentation(1)}
- """;
+ {FormatFailure("Expecting be equal:")}
+ {FormatExpected(expected).Indentation(1)}
+ but is
+ {FormatCurrent(current).Indentation(1)}
+ """;
public static string IsEqualIgnoringCase(object? current, object expected) =>
$"""
- {FormatFailure("Expecting be equal (ignoring case):")}
- {FormatExpected(expected).Indentation(1)}
- but is
- {FormatCurrent(current).Indentation(1)}
- """;
+ {FormatFailure("Expecting be equal (ignoring case):")}
+ {FormatExpected(expected).Indentation(1)}
+ but is
+ {FormatCurrent(current).Indentation(1)}
+ """;
public static string IsNotEqual(object? current, object? expected) =>
- current is IEnumerable || expected is IEnumerable ? $"""
- {FormatFailure("Expecting be NOT equal:")}
- {FormatExpected(expected).Indentation(1)}
- but is
- {FormatCurrent(current).Indentation(1)}
- """
+ current is IEnumerable || expected is IEnumerable
+ ? $"""
+ {FormatFailure("Expecting be NOT equal:")}
+ {FormatExpected(expected).Indentation(1)}
+ but is
+ {FormatCurrent(current).Indentation(1)}
+ """
: $"""
- {FormatFailure("Expecting be NOT equal:")}
- {FormatExpected(expected).Indentation(1)} but is {FormatCurrent(current)}
- """;
+ {FormatFailure("Expecting be NOT equal:")}
+ {FormatExpected(expected).Indentation(1)} but is {FormatCurrent(current)}
+ """;
public static string IsNotEqual(IEnumerable current, IEnumerable expected) =>
$"""
- {FormatFailure("Expecting be NOT equal:")}
- {FormatExpected(expected).Indentation(1)}
- but is
- {FormatCurrent(current).Indentation(1)}
- """;
+ {FormatFailure("Expecting be NOT equal:")}
+ {FormatExpected(expected).Indentation(1)}
+ but is
+ {FormatCurrent(current).Indentation(1)}
+ """;
public static string IsNotEqualIgnoringCase(object? current, object expected) =>
$"""
- {FormatFailure("Expecting be NOT equal (ignoring case):")}
- {FormatExpected(expected).Indentation(1)}
- but is
- {FormatCurrent(current).Indentation(1)}
- """;
+ {FormatFailure("Expecting be NOT equal (ignoring case):")}
+ {FormatExpected(expected).Indentation(1)}
+ but is
+ {FormatCurrent(current).Indentation(1)}
+ """;
public static string IsNull(object current) =>
$"""
- {FormatFailure("Expecting be :")}
- but is
- {FormatCurrent(current).Indentation(1)}
- """;
+ {FormatFailure("Expecting be :")}
+ but is
+ {FormatCurrent(current).Indentation(1)}
+ """;
public static string IsNotNull() => "Expecting be NOT :";
public static string IsEmpty(int size, bool isNull) =>
- isNull ? $"""
- {FormatFailure("Expecting be empty:")}
- but is
- """
+ isNull
+ ? $"""
+ {FormatFailure("Expecting be empty:")}
+ but is
+ """
: $"""
- {FormatFailure("Expecting be empty:")}
- but has size {FormatCurrent(size)}
- """;
+ {FormatFailure("Expecting be empty:")}
+ but has size {FormatCurrent(size)}
+ """;
public static string IsEmpty(string? current) =>
$"""
- {FormatFailure("Expecting be empty:")}
- but is
- {FormatCurrent(current).Indentation(1)}
- """;
+ {FormatFailure("Expecting be empty:")}
+ but is
+ {FormatCurrent(current).Indentation(1)}
+ """;
public static string IsNotEmpty() =>
$"""
- {FormatFailure("Expecting being NOT empty:")}
- but is empty
- """;
+ {FormatFailure("Expecting being NOT empty:")}
+ but is empty
+ """;
public static string NotInstanceOf(Type? expected) =>
$"""
- {FormatFailure("Expecting be NOT a instance of:")}
- {FormatExpected(expected).Indentation(1)}
- """;
+ {FormatFailure("Expecting be NOT a instance of:")}
+ {FormatExpected(expected).Indentation(1)}
+ """;
public static string IsInstanceOf(Type? current, Type expected) =>
$"""
- {FormatFailure("Expected be instance of:")}
- {FormatExpected(expected).Indentation(1)} but is {FormatCurrent(current)}
- """;
+ {FormatFailure("Expected be instance of:")}
+ {FormatExpected(expected).Indentation(1)} but is {FormatCurrent(current)}
+ """;
public static string IsSame(TValue? current, TValue expected) =>
$"""
- {FormatFailure("Expecting be same:")}
- {FormatExpected(expected).Indentation(1)}
- to refer to the same object
- {FormatCurrent(current).Indentation(1)}
- """;
+ {FormatFailure("Expecting be same:")}
+ {FormatExpected(expected).Indentation(1)}
+ to refer to the same object
+ {FormatCurrent(current).Indentation(1)}
+ """;
public static string IsNotSame(TValue? expected) =>
$"{FormatFailure("Expecting be NOT same:")} {FormatExpected(expected)}";
public static string IsBetween(TValue? current, TValue from, TValue to) =>
$"""
- {FormatFailure("Expecting:")}
- {FormatCurrent(current).Indentation(1)}
- in range between
- {FormatExpected(from).Indentation(1)} <> {FormatExpected(to)}
- """;
+ {FormatFailure("Expecting:")}
+ {FormatCurrent(current).Indentation(1)}
+ in range between
+ {FormatExpected(from).Indentation(1)} <> {FormatExpected(to)}
+ """;
public static string IsNotBetween(object? current, object from, object to) =>
$"""
- {FormatFailure("Expecting:")}
- {FormatCurrent(current).Indentation(1)}
- be NOT in range between
- {FormatExpected(from).Indentation(1)} <> {FormatExpected(to)}
- """;
+ {FormatFailure("Expecting:")}
+ {FormatCurrent(current).Indentation(1)}
+ be NOT in range between
+ {FormatExpected(from).Indentation(1)} <> {FormatExpected(to)}
+ """;
public static string IsEven(object? current) =>
$"""
- {FormatFailure("Expecting be even:")}
- but is {FormatCurrent(current)}
- """;
+ {FormatFailure("Expecting be even:")}
+ but is {FormatCurrent(current)}
+ """;
public static string IsOdd(object? current) =>
$"""
- {FormatFailure("Expecting be odd:")}
- but is {FormatCurrent(current)}
- """;
+ {FormatFailure("Expecting be odd:")}
+ but is {FormatCurrent(current)}
+ """;
public static string HasSize(object? current, object expected) =>
$"""
- {FormatFailure("Expecting size:")}
- {FormatExpected(expected).Indentation(1)} but is {(current == null ? "unknown" : FormatCurrent(current))}
- """;
+ {FormatFailure("Expecting size:")}
+ {FormatExpected(expected).Indentation(1)} but is {(current == null ? "unknown" : FormatCurrent(current))}
+ """;
public static string IsGreater(object current, object expected) =>
$"""
- {FormatFailure("Expecting to be greater than:")}
- {FormatExpected(expected).Indentation(1)} but is {FormatCurrent(current)}
- """;
+ {FormatFailure("Expecting to be greater than:")}
+ {FormatExpected(expected).Indentation(1)} but is {FormatCurrent(current)}
+ """;
public static string IsGreaterEqual(object current, object expected) =>
$"""
- {FormatFailure("Expecting to be greater than or equal:")}
- {FormatExpected(expected).Indentation(1)} but is {FormatCurrent(current)}
- """;
+ {FormatFailure("Expecting to be greater than or equal:")}
+ {FormatExpected(expected).Indentation(1)} but is {FormatCurrent(current)}
+ """;
public static string IsLess(object current, object expected) =>
$"""
- {FormatFailure("Expecting to be less than:")}
- {FormatExpected(expected).Indentation(1)} but is {FormatCurrent(current)}
- """;
+ {FormatFailure("Expecting to be less than:")}
+ {FormatExpected(expected).Indentation(1)} but is {FormatCurrent(current)}
+ """;
public static string IsLessEqual(object current, object expected) =>
$"""
- {FormatFailure("Expecting to be less than or equal:")}
- {FormatExpected(expected).Indentation(1)} but is {FormatCurrent(current)}
- """;
+ {FormatFailure("Expecting to be less than or equal:")}
+ {FormatExpected(expected).Indentation(1)} but is {FormatCurrent(current)}
+ """;
public static string IsNegative(object current) =>
$"""
- {FormatFailure("Expecting be negative:")}
- but is {FormatCurrent(current)}
- """;
+ {FormatFailure("Expecting be negative:")}
+ but is {FormatCurrent(current)}
+ """;
public static string IsNotNegative(object current) =>
$"""
- {FormatFailure("Expecting be NOT negative:")}
- but is {FormatCurrent(current)}
- """;
+ {FormatFailure("Expecting be NOT negative:")}
+ but is {FormatCurrent(current)}
+ """;
public static string IsNotZero() =>
$"""
- {FormatFailure("Expecting be NOT zero:")}
- but is '0'
- """;
+ {FormatFailure("Expecting be NOT zero:")}
+ but is '0'
+ """;
public static string IsZero(object? current) =>
$"""
- {FormatFailure("Expecting be zero:")}
- but is {FormatCurrent(current)}
- """;
+ {FormatFailure("Expecting be zero:")}
+ but is {FormatCurrent(current)}
+ """;
public static string IsIn(object? current, object expected) =>
$"""
- {FormatFailure("Expecting:")}
- {FormatCurrent(current).Indentation(1)}
- is in
- {FormatExpected(expected).Indentation(1)}
- """;
+ {FormatFailure("Expecting:")}
+ {FormatCurrent(current).Indentation(1)}
+ is in
+ {FormatExpected(expected).Indentation(1)}
+ """;
public static string IsNotIn(object? current, object expected) =>
$"""
- {FormatFailure("Expecting:")}
- {FormatCurrent(current).Indentation(1)}
- is not in
- {FormatExpected(expected).Indentation(1)}
- """;
+ {FormatFailure("Expecting:")}
+ {FormatCurrent(current).Indentation(1)}
+ is not in
+ {FormatExpected(expected).Indentation(1)}
+ """;
public static string Contains(IEnumerable? current, IEnumerable expected, IEnumerable notFound) =>
$"""
- {FormatFailure("Expecting contains elements:")}
- {FormatCurrent(current).Indentation(1)}
- do contains (in any order)
- {FormatExpected(expected).Indentation(1)}
- but could not find elements:
- {FormatExpected(notFound).Indentation(1)}
- """;
+ {FormatFailure("Expecting contains elements:")}
+ {FormatCurrent(current).Indentation(1)}
+ do contains (in any order)
+ {FormatExpected(expected).Indentation(1)}
+ but could not find elements:
+ {FormatExpected(notFound).Indentation(1)}
+ """;
public static string ContainsExactly(IEnumerable? current, IEnumerable expected, List notFound, List notExpected)
{
@@ -359,139 +365,138 @@ public static string ContainsExactly(IEnumerable? current, IEnum
{
var diff = notExpected.FindAll(e => !notFound.Any(e2 => Equals(e.UnboxVariant(), e2.UnboxVariant())));
if (diff?.Count == 0)
- {
return $"""
- {FormatFailure("Expecting contains exactly elements:")}
- {FormatCurrent(current).Indentation(1)}
- do contains (in same order)
- {FormatExpected(expected).Indentation(1)}
- but there has differences in order:
- {ListDifferences(notFound, notExpected)}
- """;
- }
+ {FormatFailure("Expecting contains exactly elements:")}
+ {FormatCurrent(current).Indentation(1)}
+ do contains (in same order)
+ {FormatExpected(expected).Indentation(1)}
+ but there has differences in order:
+ {ListDifferences(notFound, notExpected)}
+ """;
}
+
var message = $"""
- {FormatFailure("Expecting contains exactly elements:")}
- {FormatCurrent(current).Indentation(1)}
- do contains (in same order)
- {FormatExpected(expected).Indentation(1)}
- """;
+ {FormatFailure("Expecting contains exactly elements:")}
+ {FormatCurrent(current).Indentation(1)}
+ do contains (in same order)
+ {FormatExpected(expected).Indentation(1)}
+ """;
if (notExpected.Count > 0)
message += $"""
- but others where not expected:
- {FormatExpected(notExpected).Indentation(1)}
- """;
+ but others where not expected:
+ {FormatExpected(notExpected).Indentation(1)}
+ """;
if (notFound.Count > 0)
message += $"""
- {(notExpected.Count == 0 ? "but" : "and")} some elements not found:
- {FormatExpected(notFound).Indentation(1)}
- """;
+ {(notExpected.Count == 0 ? "but" : "and")} some elements not found:
+ {FormatExpected(notFound).Indentation(1)}
+ """;
return message.UnixFormat();
}
public static string ContainsExactlyInAnyOrder(IEnumerable? current, IEnumerable expected, List notFound, List notExpected)
{
var message = $"""
- {FormatFailure("Expecting contains exactly elements:")}
- {FormatCurrent(current).Indentation(1)}
- do contains (in any order)
- {FormatExpected(expected).Indentation(1)}
- """;
+ {FormatFailure("Expecting contains exactly elements:")}
+ {FormatCurrent(current).Indentation(1)}
+ do contains (in any order)
+ {FormatExpected(expected).Indentation(1)}
+ """;
if (notExpected.Count > 0)
message += $"""
- but some elements where not expected:
- {FormatExpected(notExpected).Indentation(1)}
- """;
+ but some elements where not expected:
+ {FormatExpected(notExpected).Indentation(1)}
+ """;
if (notFound.Count > 0)
message += $"""
- {(notExpected.Count == 0 ? "but" : "and")} could not find elements:
- {FormatExpected(notFound).Indentation(1)}
- """;
+ {(notExpected.Count == 0 ? "but" : "and")} could not find elements:
+ {FormatExpected(notFound).Indentation(1)}
+ """;
return message;
}
public static string ContainsKeyValue(IDictionary expected) =>
$"""
- {FormatFailure("Expecting do contain entry:")}
- {FormatCurrent(expected).Indentation(1)}
- """;
+ {FormatFailure("Expecting do contain entry:")}
+ {FormatCurrent(expected).Indentation(1)}
+ """;
public static string ContainsKeyValue(IDictionary expected, object? currentValue) =>
$"""
- {FormatFailure("Expecting do contain entry:")}
- {FormatCurrent(expected).Indentation(1)}
- found key but value is
- {FormatCurrent(currentValue).Indentation(1)}
- """;
+ {FormatFailure("Expecting do contain entry:")}
+ {FormatCurrent(expected).Indentation(1)}
+ found key but value is
+ {FormatCurrent(currentValue).Indentation(1)}
+ """;
public static string NotContains(string current, string expected) =>
$"""
- {FormatFailure("Expecting:")}
- {FormatCurrent(current).Indentation(1)}
- do not contain
- {FormatExpected(expected).Indentation(1)}
- """;
+ {FormatFailure("Expecting:")}
+ {FormatCurrent(current).Indentation(1)}
+ do not contain
+ {FormatExpected(expected).Indentation(1)}
+ """;
public static string NotContains(IEnumerable? current, IEnumerable expected, List found) =>
$"""
- {FormatFailure("Expecting:")}
- {FormatCurrent(current).Indentation(1)}
- do NOT contains (in any order)
- {FormatExpected(expected).Indentation(1)}
- but found elements:
- {FormatExpected(found).Indentation(1)}
- """;
+ {FormatFailure("Expecting:")}
+ {FormatCurrent(current).Indentation(1)}
+ do NOT contains (in any order)
+ {FormatExpected(expected).Indentation(1)}
+ but found elements:
+ {FormatExpected(found).Indentation(1)}
+ """;
public static string NotContainsIgnoringCase(string current, string expected) =>
$"""
- {FormatFailure("Expecting:")}
- {FormatCurrent(current).Indentation(1)}
- do not contain (ignoring case)
- {FormatExpected(expected).Indentation(1)}
- """;
+ {FormatFailure("Expecting:")}
+ {FormatCurrent(current).Indentation(1)}
+ do not contain (ignoring case)
+ {FormatExpected(expected).Indentation(1)}
+ """;
public static string HasValue(string methodName, object? current, object expected) =>
$"""
- {FormatFailure("Expecting Property:")}
- {FormatCurrent(methodName).Indentation(1)} to be {FormatCurrent(expected)} but is {FormatCurrent(current)}
- """;
+ {FormatFailure("Expecting Property:")}
+ {FormatCurrent(methodName).Indentation(1)} to be {FormatCurrent(expected)} but is {FormatCurrent(current)}
+ """;
public static string Contains(string? current, string expected) =>
$"""
- {FormatFailure("Expecting:")}
- {FormatCurrent(current).Indentation(1)}
- do contains
- {FormatExpected(expected).Indentation(1)}
- """;
+ {FormatFailure("Expecting:")}
+ {FormatCurrent(current).Indentation(1)}
+ do contains
+ {FormatExpected(expected).Indentation(1)}
+ """;
public static string ContainsIgnoringCase(string? current, string expected) =>
$"""
- {FormatFailure("Expecting:")}
- {FormatCurrent(current).Indentation(1)}
- do contains (ignoring case)
- {FormatExpected(expected).Indentation(1)}
- """;
+ {FormatFailure("Expecting:")}
+ {FormatCurrent(current).Indentation(1)}
+ do contains (ignoring case)
+ {FormatExpected(expected).Indentation(1)}
+ """;
public static string EndsWith(string? current, string expected) =>
$"""
- {FormatFailure("Expecting:")}
- {FormatCurrent(current).Indentation(1)}
- to end with
- {FormatExpected(expected).Indentation(1)}
- """;
+ {FormatFailure("Expecting:")}
+ {FormatCurrent(current).Indentation(1)}
+ to end with
+ {FormatExpected(expected).Indentation(1)}
+ """;
public static string StartsWith(string? current, string expected) =>
$"""
- {FormatFailure("Expecting:")}
- {FormatCurrent(current).Indentation(1)}
- to start with
- {FormatExpected(expected).Indentation(1)}
- """;
+ {FormatFailure("Expecting:")}
+ {FormatCurrent(current).Indentation(1)}
+ to start with
+ {FormatExpected(expected).Indentation(1)}
+ """;
public static string HasLength(int currentLength, int expectedLength, IStringAssert.Compare comparator)
{
@@ -502,7 +507,7 @@ public static string HasLength(int currentLength, int expectedLength, IStringAss
IStringAssert.Compare.LESS_EQUAL => "Expecting length to be less than or equal:",
IStringAssert.Compare.GREATER_THAN => "Expecting length to be greater than:",
IStringAssert.Compare.GREATER_EQUAL => "Expecting length to be greater than or equal:",
- _ => "Invalid comparator",
+ _ => "Invalid comparator"
};
return $"""
{FormatFailure(errorMessage)}
@@ -510,38 +515,44 @@ public static string HasLength(int currentLength, int expectedLength, IStringAss
""";
}
- internal static string IsEmitted(object? current, string signal, Godot.Variant[] args) =>
+ internal static string IsEmitted(object? current, string signal, Variant[] args) =>
$"""
- {FormatFailure("Expecting do emitting signal:")}
- {FormatExpected($"{signal}({args.Formatted()})").Indentation(1)}
- by
- {FormatCurrent(current).Indentation(1)}
- """;
- internal static string IsNotEmitted(object? current, string signal, Godot.Variant[] args) =>
+ {FormatFailure("Expecting do emitting signal:")}
+ {FormatExpected($"{signal}({args.Formatted()})").Indentation(1)}
+ by
+ {FormatCurrent(current).Indentation(1)}
+ """;
+
+ internal static string IsNotEmitted(object? current, string signal, Variant[] args) =>
$"""
- {FormatFailure("Expecting do NOT emitting signal:")}
- {FormatExpected($"{signal}({args.Formatted()})").Indentation(1)}
- by
- {FormatCurrent(current).Indentation(1)}
- """;
+ {FormatFailure("Expecting do NOT emitting signal:")}
+ {FormatExpected($"{signal}({args.Formatted()})").Indentation(1)}
+ by
+ {FormatCurrent(current).Indentation(1)}
+ """;
internal static string IsSignalExists(object current, string signal) =>
$"""
- {FormatFailure("Expecting signal exists:")}
- {FormatExpected($"{signal}()").Indentation(1)}
- on
- {FormatCurrent(current).Indentation(1)}
- """;
+ {FormatFailure("Expecting signal exists:")}
+ {FormatExpected($"{signal}()").Indentation(1)}
+ on
+ {FormatCurrent(current).Indentation(1)}
+ """;
private static string? ListDifferences(IEnumerable left, IEnumerable right)
{
var output = new List();
- foreach (var it in left.Select((value, i) => new { Value = value, Index = i }))
+ foreach (var it in left.Select((value, i) => new
+ {
+ Value = value,
+ Index = i
+ }))
{
var l = it.Value;
var r = right.ElementAt(it.Index);
output.Add($"- element at index {it.Index} expect {FormatCurrent(l.UnboxVariant())} but is {FormatExpected(r.UnboxVariant())}");
}
+
return string.Join("\n", output).Indentation(1);
}
}
diff --git a/api/src/core/execution/AfterTestExecutionStage.cs b/api/src/core/execution/AfterTestExecutionStage.cs
index c4883665..edd8f812 100644
--- a/api/src/core/execution/AfterTestExecutionStage.cs
+++ b/api/src/core/execution/AfterTestExecutionStage.cs
@@ -4,17 +4,21 @@ namespace GdUnit4.Executions;
using System.Reflection;
using System.Threading.Tasks;
-using GdUnit4.Asserts;
+using Asserts;
+
+using Core.Signals;
internal class AfterTestExecutionStage : ExecutionStage
{
public AfterTestExecutionStage(TestSuite testSuite) : base("AfterTest", testSuite.Instance.GetType())
- { }
+ {
+ }
public override async Task Execute(ExecutionContext context)
{
if (!context.IsSkipped)
{
+ GodotSignalCollector.Instance.Clean();
context.MemoryPool.SetActive(StageName);
context.OrphanMonitor.Start();
await base.Execute(context);
@@ -23,6 +27,7 @@ public override async Task Execute(ExecutionContext context)
if (context.OrphanMonitor.OrphanCount > 0)
context.ReportCollector.PushFront(new TestReport(TestReport.ReportType.WARN, 0, ReportOrphans(context)));
}
+
context.FireAfterTestEvent();
}
@@ -44,14 +49,14 @@ private static string ReportOrphans(ExecutionContext context)
var afterAttributes = AfterTestAttribute(context);
if (beforeAttribute != null && afterAttributes != null)
return $"""
- {AssertFailures.FormatValue("WARNING:", AssertFailures.WARN_COLOR, false)}
- Detected <{context.OrphanMonitor.OrphanCount}> orphan nodes during test setup stage!
- Check [b]{beforeAttribute.Name + ":" + beforeAttribute.Line}[/b] and [b]{afterAttributes.Name + ":" + afterAttributes.Line}[/b] for unfreed instances!
- """;
+ {AssertFailures.FormatValue("WARNING:", AssertFailures.WARN_COLOR, false)}
+ Detected <{context.OrphanMonitor.OrphanCount}> orphan nodes during test setup stage!
+ Check [b]{beforeAttribute.Name + ":" + beforeAttribute.Line}[/b] and [b]{afterAttributes.Name + ":" + afterAttributes.Line}[/b] for unfreed instances!
+ """;
return $"""
{AssertFailures.FormatValue("WARNING:", AssertFailures.WARN_COLOR, false)}
Detected <{context.OrphanMonitor.OrphanCount}> orphan nodes during test setup stage!
- Check [b]{(beforeAttribute != null ? (beforeAttribute.Name + ":" + beforeAttribute.Line) : (afterAttributes?.Name + ":" + afterAttributes?.Line))}[/b] for unfreed instances!
+ Check [b]{(beforeAttribute != null ? beforeAttribute.Name + ":" + beforeAttribute.Line : afterAttributes?.Name + ":" + afterAttributes?.Line)}[/b] for unfreed instances!
""";
}
}
diff --git a/api/src/core/signals/GodotSignalCollector.cs b/api/src/core/signals/GodotSignalCollector.cs
index a1b9b152..cdae29c1 100644
--- a/api/src/core/signals/GodotSignalCollector.cs
+++ b/api/src/core/signals/GodotSignalCollector.cs
@@ -1,89 +1,99 @@
namespace GdUnit4.Core.Signals;
-using static System.Console;
-
using System;
-using System.Threading;
using System.Collections.Generic;
using System.Linq;
+using System.Threading;
+
+using Godot;
+
+using static System.Console;
-internal sealed partial class GodotSignalCollector : Godot.RefCounted
+using Array = Godot.Collections.Array;
+using Error = Godot.Error;
+
+internal sealed partial class GodotSignalCollector : RefCounted
{
- private readonly Dictionary>> collectedSignals = new();
+ // ReSharper disable once InconsistentNaming
+ internal readonly Dictionary>> collectedSignals = new();
public static GodotSignalCollector Instance { get; } = new();
- public void RegisterEmitter(Godot.GodotObject emitter)
+ public void RegisterEmitter(GodotObject emitter)
{
// do not register the same emitter at twice
if (collectedSignals.ContainsKey(emitter))
return;
- collectedSignals[emitter] = new Dictionary>();
+ collectedSignals[emitter] = new Dictionary>();
// connect to 'TreeExiting' of the emitter to finally release all acquired resources/connections.
var action = UnregisterEmitter;
- if (!emitter.IsConnected(Godot.Node.SignalName.TreeExiting, Godot.Callable.From(action)))
- ((Godot.Node)emitter).TreeExiting += () => UnregisterEmitter(this, emitter);
+ if (emitter is Node node && !node.IsConnected(Node.SignalName.TreeExiting, Callable.From(action)))
+ node.TreeExiting += () => UnregisterEmitter(this, emitter);
ConnectAllSignals(emitter);
}
- private void ConnectAllSignals(Godot.GodotObject emitter)
+ private void ConnectAllSignals(GodotObject emitter)
{
foreach (var signalDef in emitter.GetSignalList())
{
var signalName = (string)signalDef["name"];
- var args = (Godot.Collections.Array)signalDef["args"];
+ var args = (Array)signalDef["args"];
var error = emitter.Connect(signalName, BuildCallable(emitter, signalName, args.Count));
- if (error != Godot.Error.Ok)
+ if (error != Error.Ok)
WriteLine($"Error on connecting signal {signalName}, Error: {error}");
- collectedSignals[emitter][signalName] = new List();
+ collectedSignals[emitter][signalName] = new List();
}
}
- private Godot.Callable BuildCallable(Godot.GodotObject emitter, string signalName, int argumentCount)
+ private Callable BuildCallable(GodotObject emitter, string signalName, int argumentCount)
=> argumentCount switch
{
- 0 => Godot.Callable.From(() => CollectSignal(emitter, signalName)),
- 1 => Godot.Callable.From((arg0) => CollectSignal(emitter, signalName, arg0)),
- 2 => Godot.Callable.From((arg0, arg1) => CollectSignal(emitter, signalName, arg0, arg1)),
- 3 => Godot.Callable.From((arg0, arg1, arg2) => CollectSignal(emitter, signalName, arg0, arg1, arg2)),
- 4 => Godot.Callable.From((arg0, arg1, arg2, arg3) => CollectSignal(emitter, signalName, arg0, arg1, arg2, arg3)),
- 5 => Godot.Callable.From((arg0, arg1, arg2, arg3, arg4) => CollectSignal(emitter, signalName, arg0, arg1, arg2, arg3, arg4)),
- 6 => Godot.Callable.From((arg0, arg1, arg2, arg3, arg4, arg5) => CollectSignal(emitter, signalName, arg0, arg1, arg2, arg3, arg4, arg5)),
- 7 => Godot.Callable.From((arg0, arg1, arg2, arg3, arg4, arg5, arg6) => CollectSignal(emitter, signalName, arg0, arg1, arg2, arg3, arg4, arg5, arg6)),
- 8 => Godot.Callable.From((arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) => CollectSignal(emitter, signalName, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7)),
- _ => throw new NotImplementedException(),
+ 0 => Callable.From(() => CollectSignal(emitter, signalName)),
+ 1 => Callable.From(arg0 => CollectSignal(emitter, signalName, arg0)),
+ 2 => Callable.From((arg0, arg1) => CollectSignal(emitter, signalName, arg0, arg1)),
+ 3 => Callable.From((arg0, arg1, arg2) => CollectSignal(emitter, signalName, arg0, arg1, arg2)),
+ 4 => Callable.From((arg0, arg1, arg2, arg3) => CollectSignal(emitter, signalName, arg0, arg1, arg2, arg3)),
+ 5 => Callable.From((arg0, arg1, arg2, arg3, arg4) => CollectSignal(emitter, signalName, arg0, arg1, arg2, arg3, arg4)),
+ 6 => Callable.From((arg0, arg1, arg2, arg3, arg4, arg5)
+ => CollectSignal(emitter, signalName, arg0, arg1, arg2, arg3, arg4, arg5)),
+ 7 => Callable.From((arg0, arg1, arg2, arg3, arg4, arg5, arg6)
+ => CollectSignal(emitter, signalName, arg0, arg1, arg2, arg3, arg4, arg5, arg6)),
+ 8 => Callable.From((arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7)
+ => CollectSignal(emitter, signalName, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7)),
+ _ => throw new NotImplementedException()
};
- private void CollectSignal(Godot.GodotObject emitter, string signalName, params Godot.Variant[] signalArgs)
+ private void CollectSignal(GodotObject emitter, string signalName, params Variant[] signalArgs)
{
if (IsSignalCollecting(emitter, signalName))
collectedSignals[emitter][signalName].Add(signalArgs);
}
- internal bool IsSignalCollecting(Godot.GodotObject emitter, string signalName)
+ internal bool IsSignalCollecting(GodotObject emitter, string signalName)
=> collectedSignals.ContainsKey(emitter) && collectedSignals[emitter].ContainsKey(signalName);
// unregister all acquired resources/connections, otherwise it ends up in orphans
// is called when the emitter is removed from the parent
- private void UnregisterEmitter(GodotSignalCollector collector, Godot.GodotObject emitter)
+ private void UnregisterEmitter(GodotSignalCollector collector, GodotObject emitter)
{
if (IsInstanceValid(collector))
//WriteLine($"disconnect_signals: {emitter}");
foreach (var connection in collector.GetIncomingConnections())
{
- var source = (Godot.GodotObject)connection["source"];
+ var source = (GodotObject)connection["source"];
var signalName = (string)connection["signal_name"];
var methodName = (string)connection["method_name"];
//WriteLine($"disconnect: {signalName} from {source} target {collector} -> {methodName}");
- source.Disconnect(signalName, new Godot.Callable(collector, methodName));
+ source.Disconnect(signalName, new Callable(collector, methodName));
}
+
if (IsInstanceValid(emitter))
collectedSignals.Remove(emitter);
//DebugSignalList("UnregisterEmitter");
}
- private void ResetCollectedSignals(Godot.GodotObject emitter)
+ private void ResetCollectedSignals(GodotObject emitter)
{
//DebugSignalList("before clear");
if (!collectedSignals.TryGetValue(emitter, out var value)) return;
@@ -92,7 +102,7 @@ private void ResetCollectedSignals(Godot.GodotObject emitter)
//DebugSignalList("after clear");
}
- private bool Match(Godot.GodotObject emitter, string signalName, params Godot.Variant[] args)
+ private bool Match(GodotObject emitter, string signalName, params Variant[] args)
//DebugSignalList("--match--");
=> collectedSignals[emitter][signalName].Any(receivedArgs => receivedArgs.VariantEquals(args));
@@ -109,10 +119,11 @@ private void DebugSignalList(string message)
WriteLine($"\t\t{signalName} {args.Formatted()}");
}
}
+
WriteLine("}");
}
- internal bool IsEmitted(CancellationTokenSource token, Godot.GodotObject emitter, string signal, Godot.Variant[] args)
+ internal bool IsEmitted(CancellationTokenSource token, GodotObject emitter, string signal, Variant[] args)
{
try
{
@@ -129,6 +140,7 @@ internal bool IsEmitted(CancellationTokenSource token, Godot.GodotObject emitter
if (token.IsCancellationRequested)
return false;
}
+
return true;
}
catch (Exception e)
@@ -142,8 +154,14 @@ internal bool IsEmitted(CancellationTokenSource token, Godot.GodotObject emitter
}
}
- internal int Count(Godot.GodotObject emitter, string signalName, Godot.Variant[] args)
+ internal int Count(GodotObject emitter, string signalName, Variant[] args)
=> IsSignalCollecting(emitter, signalName)
? collectedSignals[emitter][signalName].FindAll(signalArgs => signalArgs.VariantEquals(args)).Count
: 0;
+
+ internal void Clean()
+ {
+ collectedSignals.Keys.ToList().ForEach(emitter => UnregisterEmitter(this, emitter));
+ collectedSignals.Clear();
+ }
}
diff --git a/example/exampleProject.csproj b/example/exampleProject.csproj
index 5dd3a9eb..22707b20 100644
--- a/example/exampleProject.csproj
+++ b/example/exampleProject.csproj
@@ -1,5 +1,5 @@
-
+
net8.0
11.0
@@ -14,8 +14,8 @@
NU1605
-
-
-
+
+
+
diff --git a/test/src/asserts/SignalAssertTest.cs b/test/src/asserts/SignalAssertTest.cs
index 5e690853..1794a65f 100644
--- a/test/src/asserts/SignalAssertTest.cs
+++ b/test/src/asserts/SignalAssertTest.cs
@@ -3,45 +3,28 @@ namespace GdUnit4.Tests.Asserts;
using System.Threading.Tasks;
+using Exceptions;
+
using GdUnit4.Asserts;
using GdUnit4.Core.Signals;
+using Godot;
+
using static Assertions;
[TestSuite]
public partial class SignalAssertTest
{
- private sealed partial class TestEmitter : Godot.Node
+ [AfterTest]
+ public void TearDown()
{
- [Godot.Signal]
- public delegate void SignalAEventHandler();
-
- [Godot.Signal]
- public delegate void SignalBEventHandler(string value);
-
- [Godot.Signal]
- public delegate void SignalCEventHandler(string value, int count);
-
- private int frame;
-
- public override void _Process(double delta)
- {
- switch (frame)
- {
- case 5:
- EmitSignal(SignalName.SignalA);
- break;
- case 10:
- EmitSignal(SignalName.SignalB, "abc");
- break;
- case 15:
- EmitSignal(SignalName.SignalC, "abc", 100);
- break;
- }
- frame++;
- }
+ var signalCollector = GodotSignalCollector.Instance;
+ AssertThat(signalCollector.collectedSignals.Keys)
+ .OverrideFailureMessage($"Found keys: {signalCollector.collectedSignals.Keys.Formatted()}")
+ .IsEmpty();
}
+
[TestCase]
public async Task IsEmitted()
{
@@ -52,7 +35,7 @@ public async Task IsEmitted()
await AssertThrown(AssertSignal(node).IsEmitted("SignalC", "abc", 101).WithTimeout(200))
.ContinueWith(result => result.Result?
- .IsInstanceOf()
+ .IsInstanceOf()
.HasMessage("""
Expecting do emitting signal:
"SignalC(["abc", 101])"
@@ -65,7 +48,7 @@ await AssertThrown(AssertSignal(node).IsEmitted("SignalC", "abc", 101).WithTimeo
[TestCase]
public async Task IsNoEmitted()
{
- var node = AddNode(new Godot.Node2D());
+ var node = AddNode(new Node2D());
await AssertSignal(node).IsNotEmitted("visibility_changed", 10).WithTimeout(100);
await AssertSignal(node).IsNotEmitted("visibility_changed", 20).WithTimeout(100);
await AssertSignal(node).IsNotEmitted("script_changed").WithTimeout(100);
@@ -73,7 +56,7 @@ public async Task IsNoEmitted()
node.Visible = false;
await AssertThrown(AssertSignal(node).IsNotEmitted("visibility_changed").WithTimeout(200))
.ContinueWith(result => result.Result?
- .IsInstanceOf()
+ .IsInstanceOf()
.HasMessage("""
Expecting do NOT emitting signal:
"visibility_changed()"
@@ -86,7 +69,7 @@ await AssertThrown(AssertSignal(node).IsNotEmitted("visibility_changed").WithTim
[TestCase]
public async Task NodeChangedEmittingSignals()
{
- var node = AddNode(new Godot.Node2D());
+ var node = AddNode(new Node2D());
await AssertSignal(node).IsEmitted("draw").WithTimeout(200);
@@ -97,7 +80,7 @@ public async Task NodeChangedEmittingSignals()
//node.Visible = false;
await AssertThrown(AssertSignal(node).IsEmitted("visibility_changed").WithTimeout(200))
.ContinueWith(result => result.Result?
- .IsInstanceOf()
+ .IsInstanceOf()
.HasMessage("""
Expecting do emitting signal:
"visibility_changed()"
@@ -113,7 +96,7 @@ await AssertThrown(AssertSignal(node).IsEmitted("visibility_changed").WithTimeou
[TestCase]
public void IsSignalExists()
{
- var node = AutoFree(new Godot.Node2D())!;
+ var node = AutoFree(new Node2D())!;
AssertSignal(node).IsSignalExists("visibility_changed")
.IsSignalExists("draw")
@@ -123,7 +106,7 @@ public void IsSignalExists()
.IsSignalExists("tree_exited");
AssertThrown(() => AssertSignal(node).IsSignalExists("not_existing_signal"))
- .IsInstanceOf()
+ .IsInstanceOf()
.HasMessage("""
Expecting signal exists:
"not_existing_signal()"
@@ -133,21 +116,6 @@ public void IsSignalExists()
.Replace("$obj", AssertFailures.AsObjectId(node)));
}
- public sealed partial class MyEmitter : Godot.Node {
-
- [Godot.Signal]
- public delegate void SignalAEventHandler();
-
- [Godot.Signal]
- public delegate void SignalBEventHandler(string value);
-
-
- public void DoEmitSignalA() => EmitSignal(SignalName.SignalA);
-
-
- public void DoEmitSignalB() => EmitSignal(SignalName.SignalB, "foo");
- }
-
[TestCase(Timeout = 1000)]
public async Task MonitorOnSignal()
{
@@ -190,4 +158,81 @@ public async Task MonitorOnSignal()
emitterB.DoEmitSignalA();
await AssertSignal(emitterB).IsEmitted(MyEmitter.SignalName.SignalA).WithTimeout(50);
}
+
+ [TestCase(Timeout = 1000, Description = "See https://github.com/MikeSchulze/gdUnit4Net/issues/135")]
+ public async Task EmitSignalOnNoneNodeObjects()
+ {
+ var emitter = AutoFree(new NonNodeEmitter())!;
+
+ // verify initial the emitters are not monitored
+ AssertThat(GodotSignalCollector.Instance.IsSignalCollecting(emitter, NonNodeEmitter.SignalName.SignalA)).IsFalse();
+
+
+ // start monitoring on the emitter
+ AssertSignal(emitter).StartMonitoring();
+ // verify the emitters are now monitored
+ AssertThat(GodotSignalCollector.Instance.IsSignalCollecting(emitter, NonNodeEmitter.SignalName.SignalA)).IsTrue();
+
+ // verify the signals are not emitted initial
+ await AssertSignal(emitter).IsNotEmitted(NonNodeEmitter.SignalName.SignalA).WithTimeout(50);
+
+ // emit signal `signal_a` on emitter
+ emitter.DoEmitSignalA();
+ await AssertSignal(emitter).IsEmitted(NonNodeEmitter.SignalName.SignalA).WithTimeout(50);
+ }
+
+ private sealed partial class TestEmitter : Node
+ {
+ [Signal]
+ public delegate void SignalAEventHandler();
+
+ [Signal]
+ public delegate void SignalBEventHandler(string value);
+
+ [Signal]
+ public delegate void SignalCEventHandler(string value, int count);
+
+ private int frame;
+
+ public override void _Process(double delta)
+ {
+ switch (frame)
+ {
+ case 5:
+ EmitSignal(SignalName.SignalA);
+ break;
+ case 10:
+ EmitSignal(SignalName.SignalB, "abc");
+ break;
+ case 15:
+ EmitSignal(SignalName.SignalC, "abc", 100);
+ break;
+ }
+
+ frame++;
+ }
+ }
+
+ public sealed partial class MyEmitter : Node
+ {
+ [Signal]
+ public delegate void SignalAEventHandler();
+
+ [Signal]
+ public delegate void SignalBEventHandler(string value);
+
+
+ public void DoEmitSignalA() => EmitSignal(SignalName.SignalA);
+
+
+ public void DoEmitSignalB() => EmitSignal(SignalName.SignalB, "foo");
+ }
+
+ public sealed partial class NonNodeEmitter : RefCounted
+ {
+ [Signal]
+ public delegate void SignalAEventHandler();
+
+ public void DoEmitSignalA() => EmitSignal(SignalName.SignalA);
+ }
}