Skip to content

Commit

Permalink
GD-84: Fix AssertThat for dynamic assert type resolving (#85)
Browse files Browse the repository at this point in the history
# Why
#84 The dynamic
resolving was broken because the using assembly has no access to assert
types that were marked as internally.

# What
Fixes by make the asserts used by the dynamic resolving public. Do a
small cleanup on the dynamic assert resolving
  • Loading branch information
MikeSchulze authored Apr 16, 2024
1 parent 4075f88 commit a311bb3
Show file tree
Hide file tree
Showing 10 changed files with 154 additions and 34 deletions.
10 changes: 5 additions & 5 deletions .github/workflows/unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -168,24 +168,24 @@ jobs:
local section="$1"
local expected_content="$2"
grep -A 10 "$section" ./example/TestResults/test-result.trx | sed -e 's/^[ \t]*//' | tee output.txt >/dev/null
grep -A 50 "$section" ./example/TestResults/test-result.trx | sed -e 's/^[ \t]*//' | tee output.txt >/dev/null
if grep -qF "$expected_content" output.txt; then
echo "Test validation passed. The expected text is present in $section."
else
echo "Test validation failed. The " + $expected_content + " is not present in:"
echo "Test validation failed. The expected content '$expected_content' is not present in:"
echo ""
cat output.txt
echo ""
exit 1
fi
}
validate_section '<Output>' 'Discover tests done, 1 TestSuites and total 2 Tests found.'
validate_section '<Output>' 'Start executing tests, 2 TestCases total.'
validate_section '<Output>' 'Discover tests done, 2 TestSuites and total 11 Tests found.'
validate_section '<Output>' 'Start executing tests, 11 TestCases total.'
validate_section '<Results>' "<Message>Expecting: 'True' but is 'False'</Message>"
validate_section '<Results>' '<StackTrace> at Examples.ExampleTest.failed() in /home/runner/work/gdUnit4Net/gdUnit4Net/example/test/ExampleTest.cs:line 19'
validate_section '<ResultSummary' '<ResultSummary outcome="Failed">'
validate_section '<ResultSummary' '<Counters total="2" executed="2" passed="1" failed="1" error="0" timeout="0" aborted="0" inconclusive="0" passedButRunAborted="0" notRunnable="0" notExecuted="0" disconnected="0" warning="0" completed="0" inProgress="0" pending="0" />'
validate_section '<ResultSummary' '<Counters total="11" executed="11" passed="10" failed="1" error="0" timeout="0" aborted="0" inconclusive="0" passedButRunAborted="0" notRunnable="0" notExecuted="0" disconnected="0" warning="0" completed="0" inProgress="0" pending="0" />'
echo "All tests passes."
- name: "Publish Unit Test Reports"
Expand Down
5 changes: 3 additions & 2 deletions api/gdUnit4Api.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

<PropertyGroup>
<Title>gdUnit4Api</Title>
<Version>4.2.2-rc.1</Version>
<Version>4.2.3</Version>
<Description>
GdUnit4 API is the C# extention to enable GdUnit4 to run/write unit tests in C#.
</Description>
Expand All @@ -27,13 +27,14 @@
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<!-- warning CS8785, prevent Godot ScriptPathAttributeGenerator errors-->
<IsGodotToolsProject>true</IsGodotToolsProject>
<NoWarn>$(NoWarn);AD0001</NoWarn>
</PropertyGroup>

<PropertyGroup>
<IsPackable>true</IsPackable>
<PackageProjectUrl>https://github.com/MikeSchulze/gdUnit4Net</PackageProjectUrl>
<PackageId>gdUnit4.api</PackageId>
<PackageVersion>4.2.2-rc.1</PackageVersion>
<PackageVersion>4.2.3</PackageVersion>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageReleaseNotes>GdUnit4 API release candidate.</PackageReleaseNotes>
Expand Down
32 changes: 11 additions & 21 deletions api/src/Assertions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -191,35 +191,25 @@ public static IEnumerableAssert<bool> AssertThat(BitArray? current)
/// <returns>A dynamic assert object that provides assertion methods based on the input type.</returns>
public static dynamic AssertThat<TValue>(TValue? current)
{
if (current is string str)
return AssertThat(str);
var valueType = typeof(TValue);

var type = typeof(TValue);
if (type == typeof(IDictionary) && current == null)
return DictionaryAssert<object, object?>.From(null);

if (current is IDictionary dv)
if (typeof(IDictionary).IsAssignableFrom(valueType))
{
if (type.IsGenericType)
if (valueType.IsGenericType)
{
var dictionaryTypeArgs = type.GetGenericArguments();
var keyType = dictionaryTypeArgs[0];
var valueType = dictionaryTypeArgs[1];
var assertType = typeof(DictionaryAssert<,>).MakeGenericType(keyType, valueType);
return Activator.CreateInstance(assertType, dv)!;
var assertType = typeof(DictionaryAssert<,>).MakeGenericType(valueType.GenericTypeArguments);
return Activator.CreateInstance(assertType, current)!;
}
return DictionaryAssert<object, object?>.From(dv);
return DictionaryAssert<object, object?>.From(current as IDictionary);
}
if (current is IEnumerable ev)
if (typeof(IEnumerable).IsAssignableFrom(valueType))
{
if (type.IsGenericType)
if (valueType.IsGenericType)
{
var dictionaryTypeArgs = type.GetGenericArguments();
var valueType = dictionaryTypeArgs[0];
var assertType = typeof(EnumerableAssert<>).MakeGenericType(valueType);
return Activator.CreateInstance(assertType, ev)!;
var assertType = typeof(EnumerableAssert<>).MakeGenericType(valueType.GenericTypeArguments[0]);
return Activator.CreateInstance(assertType, current)!;
}
return AssertThat(ev);
return AssertThat(current as IEnumerable);
}
return new ObjectAssert(current);
}
Expand Down
2 changes: 1 addition & 1 deletion api/src/asserts/AssertBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ namespace GdUnit4.Asserts;

using Exceptions;

internal abstract class AssertBase<TValue> : IAssertBase<TValue>
public abstract class AssertBase<TValue> : IAssertBase<TValue>
{
protected TValue? Current { get; private set; }

Expand Down
4 changes: 3 additions & 1 deletion api/src/asserts/DictionaryAssert.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace GdUnit4.Asserts;

using CommandLine;

internal sealed class DictionaryAssert<TKey, TValue> : AssertBase<IEnumerable>, IDictionaryAssert<TKey, TValue> where TKey : notnull
public sealed class DictionaryAssert<TKey, TValue> : AssertBase<IEnumerable>, IDictionaryAssert<TKey, TValue> where TKey : notnull
{

private bool IsGeneric => CurrentTyped != null;
Expand All @@ -19,7 +19,9 @@ internal sealed class DictionaryAssert<TKey, TValue> : AssertBase<IEnumerable>,
public DictionaryAssert(IDictionary<TKey, TValue>? current) : base(current)
{ }

#pragma warning disable CA1000 // Do not declare static members on generic types
public static DictionaryAssert<TKey, TValue> From(IDictionary? current)
#pragma warning restore CA1000 // Do not declare static members on generic types
=> new(current);

private DictionaryAssert(IDictionary? current) : base(current)
Expand Down
2 changes: 1 addition & 1 deletion api/src/asserts/EnumerableAssert.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace GdUnit4.Asserts;
using System.Collections.Generic;
using System.Linq;

internal sealed class EnumerableAssert<TValue> : AssertBase<IEnumerable<TValue?>>, IEnumerableAssert<TValue?>
public sealed class EnumerableAssert<TValue> : AssertBase<IEnumerable<TValue?>>, IEnumerableAssert<TValue?>
{

public EnumerableAssert(IEnumerable? current) : base(current?.Cast<TValue?>())
Expand Down
3 changes: 2 additions & 1 deletion api/src/asserts/ObjectAssert.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
namespace GdUnit4.Asserts;
internal sealed class ObjectAssert : AssertBase<object>, IObjectAssert

public sealed class ObjectAssert : AssertBase<object>, IObjectAssert
{
public ObjectAssert(object? current) : base(current)
{
Expand Down
6 changes: 4 additions & 2 deletions example/exampleProject.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Godot.NET.Sdk/4.2.1">
<Project Sdk="Godot.NET.Sdk">
<Import Project="../PackageVersions.props" />
<PropertyGroup>
<TargetFrameworks>net8.0</TargetFrameworks>
Expand All @@ -12,10 +12,12 @@
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<!--Disable warning/error of invalid/incompatible GodotSharp version, the package GdUnit4.API is build by V4.2.0-->
<NoWarn>NU1605</NoWarn>
<!-- Hide Godot.SourceGenerators.MustBeVariantAnalyzer warnings -->
<NoWarn>$(NoWarn);AD0001</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(MicrosoftSdkVersion)" />
<PackageReference Include="gdUnit4.api" Version="4.2.*-*" />
<PackageReference Include="gdUnit4.api" Version="4.2.*" />
<PackageReference Include="gdUnit4.test.adapter" Version="1.*-*" />
</ItemGroup>
</Project>
122 changes: 122 additions & 0 deletions example/test/api/AssertionsTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@

namespace Example.Tests.API.Asserts;

using Godot;
using GdUnit4;
using GdUnit4.Asserts;

using static GdUnit4.Assertions;

[TestSuite]
public class AssertionsTest
{

[TestCase]
public void DoAssertNotYetImplemented()
=> AssertThrown(() => AssertNotYetImplemented())
.HasFileLineNumber(16)
.HasMessage("Test not yet implemented!");

[TestCase]
public void AssertThatVariants()
{
// Godot number Variants
AssertObject(AssertThat(Variant.From((sbyte)-1))).IsInstanceOf<INumberAssert<int>>();
AssertObject(AssertThat(Variant.From(11))).IsInstanceOf<INumberAssert<int>>();
AssertObject(AssertThat(Variant.From(12L))).IsInstanceOf<INumberAssert<int>>();
AssertObject(AssertThat(Variant.From(1.4f))).IsInstanceOf<INumberAssert<float>>();
AssertObject(AssertThat(Variant.From(1.5d))).IsInstanceOf<INumberAssert<float>>();
}

[TestCase(Description = "https://github.com/MikeSchulze/gdUnit4Net/issues/84")]
public void AssertThatOnDynamics()
{
// object asserts
AssertThat(System.Numerics.Vector2.One).IsEqual(System.Numerics.Vector2.One);
AssertThat(TimeSpan.FromMilliseconds(124)).IsEqual(TimeSpan.FromMilliseconds(124));

// dictionary asserts
AssertThat(new System.Collections.Hashtable()).HasSize(0);

// enumerable asserts
AssertThat(new List<int>()).HasSize(0);
}

[TestCase]
public void AssertThatNumberTypes()
{
AssertObject(AssertThat((sbyte)-1)).IsInstanceOf<INumberAssert<sbyte>>();
AssertObject(AssertThat((byte)1)).IsInstanceOf<INumberAssert<byte>>();
AssertObject(AssertThat((short)-1)).IsInstanceOf<INumberAssert<short>>();
AssertObject(AssertThat((ushort)1)).IsInstanceOf<INumberAssert<ushort>>();
AssertObject(AssertThat(-1)).IsInstanceOf<INumberAssert<int>>();
AssertObject(AssertThat((uint)1)).IsInstanceOf<INumberAssert<uint>>();
AssertObject(AssertThat((long)-1)).IsInstanceOf<INumberAssert<long>>();
AssertObject(AssertThat((ulong)1)).IsInstanceOf<INumberAssert<ulong>>();
AssertObject(AssertThat((float)1.1f)).IsInstanceOf<INumberAssert<float>>();
AssertObject(AssertThat((double)1.1d)).IsInstanceOf<INumberAssert<double>>();
AssertObject(AssertThat(1.1m)).IsInstanceOf<INumberAssert<decimal>>();

AssertObject(AssertThat(sbyte.MaxValue)).IsInstanceOf<INumberAssert<sbyte>>();
AssertObject(AssertThat(byte.MaxValue)).IsInstanceOf<INumberAssert<byte>>();
AssertObject(AssertThat(short.MaxValue)).IsInstanceOf<INumberAssert<short>>();
AssertObject(AssertThat(ushort.MaxValue)).IsInstanceOf<INumberAssert<ushort>>();
AssertObject(AssertThat(int.MaxValue)).IsInstanceOf<INumberAssert<int>>();
AssertObject(AssertThat(uint.MaxValue)).IsInstanceOf<INumberAssert<uint>>();
AssertObject(AssertThat(long.MaxValue)).IsInstanceOf<INumberAssert<long>>();
AssertObject(AssertThat(ulong.MaxValue)).IsInstanceOf<INumberAssert<ulong>>();
AssertObject(AssertThat(float.MaxValue)).IsInstanceOf<INumberAssert<float>>();
AssertObject(AssertThat(double.MaxValue)).IsInstanceOf<INumberAssert<double>>();
AssertObject(AssertThat(decimal.MaxValue)).IsInstanceOf<INumberAssert<decimal>>();
}

[TestCase]
public void AssertThatString()
{
AssertObject(AssertThat("abc")).IsInstanceOf<IStringAssert>();
AssertObject(AssertThat(new string("abc"))).IsInstanceOf<IStringAssert>();
}

[TestCase]
public void AssertThatBool()
{
AssertObject(AssertThat(true)).IsInstanceOf<IBoolAssert>();
AssertObject(AssertThat(false)).IsInstanceOf<IBoolAssert>();
AssertObject(AssertThat(new bool())).IsInstanceOf<IBoolAssert>();
}

[TestCase]
public void AssertThatEnumerable()
{
AssertObject(AssertThat(Array.Empty<byte>())).IsInstanceOf<IEnumerableAssert<byte>>();
AssertObject(AssertThat(new System.Collections.ArrayList())).IsInstanceOf<IEnumerableAssert<object>>();
AssertObject(AssertThat(new System.Collections.BitArray(new bool[] { true, false }))).IsInstanceOf<IEnumerableAssert<bool>>();
AssertObject(AssertThat(new HashSet<byte>())).IsInstanceOf<IEnumerableAssert<byte>>();
AssertObject(AssertThat(new List<byte>())).IsInstanceOf<IEnumerableAssert<byte>>();
AssertObject(AssertThat(new Godot.Collections.Array())).IsInstanceOf<IEnumerableAssert<Variant>>();
AssertObject(AssertThat(new Godot.Collections.Array<int>())).IsInstanceOf<IEnumerableAssert<int>>();
}

[TestCase]
public void AssertThatDictionary()
{
AssertObject(AssertThat(new Godot.Collections.Dictionary())).IsInstanceOf<IDictionaryAssert<Variant, Variant>>();
AssertObject(AssertThat(new Godot.Collections.Dictionary<string, Variant>())).IsInstanceOf<IDictionaryAssert<string, Variant>>();
AssertObject(AssertThat(new Godot.Collections.Dictionary<string, string>())).IsInstanceOf<IDictionaryAssert<string, string>>();
AssertObject(AssertThat(new System.Collections.Hashtable())).IsInstanceOf<IDictionaryAssert<object, object>>();
AssertObject(AssertThat(new Dictionary<string, object>())).IsInstanceOf<IDictionaryAssert<string, object>>();
}

[TestCase]
public void AutoFreeOnNull()
{
var obj = AutoFree((Node)null!);
AssertThat(obj).IsNull();
}

internal enum TestEnum
{
Foo,
Bar
}
}
2 changes: 2 additions & 0 deletions test/gdUnit4Test.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<!--Disable warning/error of invalid/incompatible GodotSharp version, the package GdUnit4.API is build by V4.2.0-->
<NoWarn>NU1605</NoWarn>
<!-- Hide Godot.SourceGenerators.MustBeVariantAnalyzer warnings -->
<NoWarn>$(NoWarn);AD0001</NoWarn>
</PropertyGroup>

<ItemGroup>
Expand Down

0 comments on commit a311bb3

Please sign in to comment.