Skip to content

Commit

Permalink
Merge pull request #86528 from avilches/must-be-variant-tests
Browse files Browse the repository at this point in the history
Add unit tests for C# diagnostic analyzers
  • Loading branch information
YuriSizov committed Jan 17, 2024
2 parents 1169b87 + 7a90c56 commit 7351b57
Show file tree
Hide file tree
Showing 12 changed files with 418 additions and 0 deletions.
6 changes: 6 additions & 0 deletions modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk.sln
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Godot.SourceGenerators", "G
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Godot.SourceGenerators.Sample", "Godot.SourceGenerators.Sample\Godot.SourceGenerators.Sample.csproj", "{7297A614-8DF5-43DE-9EAD-99671B26BD1F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Godot.SourceGenerators.Tests", "Godot.SourceGenerators.Tests\Godot.SourceGenerators.Tests.csproj", "{07E6D201-35C9-4463-9B29-D16621EA733D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotSharp", "..\..\glue\GodotSharp\GodotSharp\GodotSharp.csproj", "{AEBF0036-DA76-4341-B651-A3F2856AB2FA}"
EndProject
Global
Expand All @@ -26,6 +28,10 @@ Global
{7297A614-8DF5-43DE-9EAD-99671B26BD1F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7297A614-8DF5-43DE-9EAD-99671B26BD1F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7297A614-8DF5-43DE-9EAD-99671B26BD1F}.Release|Any CPU.Build.0 = Release|Any CPU
{07E6D201-35C9-4463-9B29-D16621EA733D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{07E6D201-35C9-4463-9B29-D16621EA733D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{07E6D201-35C9-4463-9B29-D16621EA733D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{07E6D201-35C9-4463-9B29-D16621EA733D}.Release|Any CPU.Build.0 = Release|Any CPU
{AEBF0036-DA76-4341-B651-A3F2856AB2FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AEBF0036-DA76-4341-B651-A3F2856AB2FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AEBF0036-DA76-4341-B651-A3F2856AB2FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace Godot.SourceGenerators.Sample;

[GlobalClass]
public partial class CustomGlobalClass : GodotObject
{
}

// This doesn't works because global classes can't have any generic type parameter.
/*
[GlobalClass]
public partial class CustomGlobalClass<T> : Node
{
}
*/
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>11</LangVersion>
</PropertyGroup>

<PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
using System;
using Godot.Collections;
using Array = Godot.Collections.Array;

namespace Godot.SourceGenerators.Sample;

public class MustBeVariantMethods
{
public void MustBeVariantMethodCalls()
{
Method<bool>();
Method<char>();
Method<sbyte>();
Method<byte>();
Method<short>();
Method<ushort>();
Method<int>();
Method<uint>();
Method<long>();
Method<ulong>();
Method<float>();
Method<double>();
Method<string>();
Method<Vector2>();
Method<Vector2I>();
Method<Rect2>();
Method<Rect2I>();
Method<Transform2D>();
Method<Vector3>();
Method<Vector3I>();
Method<Vector4>();
Method<Vector4I>();
Method<Basis>();
Method<Quaternion>();
Method<Transform3D>();
Method<Projection>();
Method<Aabb>();
Method<Color>();
Method<Plane>();
Method<Callable>();
Method<Signal>();
Method<GodotObject>();
Method<StringName>();
Method<NodePath>();
Method<Rid>();
Method<Dictionary>();
Method<Array>();
Method<byte[]>();
Method<int[]>();
Method<long[]>();
Method<float[]>();
Method<double[]>();
Method<string[]>();
Method<Vector2[]>();
Method<Vector3[]>();
Method<Color[]>();
Method<GodotObject[]>();
Method<StringName[]>();
Method<NodePath[]>();
Method<Rid[]>();

// This call fails because generic type is not Variant-compatible.
//Method<object>();
}

public void Method<[MustBeVariant] T>()
{
}

public void MustBeVariantClasses()
{
new ClassWithGenericVariant<bool>();
new ClassWithGenericVariant<char>();
new ClassWithGenericVariant<sbyte>();
new ClassWithGenericVariant<byte>();
new ClassWithGenericVariant<short>();
new ClassWithGenericVariant<ushort>();
new ClassWithGenericVariant<int>();
new ClassWithGenericVariant<uint>();
new ClassWithGenericVariant<long>();
new ClassWithGenericVariant<ulong>();
new ClassWithGenericVariant<float>();
new ClassWithGenericVariant<double>();
new ClassWithGenericVariant<string>();
new ClassWithGenericVariant<Vector2>();
new ClassWithGenericVariant<Vector2I>();
new ClassWithGenericVariant<Rect2>();
new ClassWithGenericVariant<Rect2I>();
new ClassWithGenericVariant<Transform2D>();
new ClassWithGenericVariant<Vector3>();
new ClassWithGenericVariant<Vector3I>();
new ClassWithGenericVariant<Vector4>();
new ClassWithGenericVariant<Vector4I>();
new ClassWithGenericVariant<Basis>();
new ClassWithGenericVariant<Quaternion>();
new ClassWithGenericVariant<Transform3D>();
new ClassWithGenericVariant<Projection>();
new ClassWithGenericVariant<Aabb>();
new ClassWithGenericVariant<Color>();
new ClassWithGenericVariant<Plane>();
new ClassWithGenericVariant<Callable>();
new ClassWithGenericVariant<Signal>();
new ClassWithGenericVariant<GodotObject>();
new ClassWithGenericVariant<StringName>();
new ClassWithGenericVariant<NodePath>();
new ClassWithGenericVariant<Rid>();
new ClassWithGenericVariant<Dictionary>();
new ClassWithGenericVariant<Array>();
new ClassWithGenericVariant<byte[]>();
new ClassWithGenericVariant<int[]>();
new ClassWithGenericVariant<long[]>();
new ClassWithGenericVariant<float[]>();
new ClassWithGenericVariant<double[]>();
new ClassWithGenericVariant<string[]>();
new ClassWithGenericVariant<Vector2[]>();
new ClassWithGenericVariant<Vector3[]>();
new ClassWithGenericVariant<Color[]>();
new ClassWithGenericVariant<GodotObject[]>();
new ClassWithGenericVariant<StringName[]>();
new ClassWithGenericVariant<NodePath[]>();
new ClassWithGenericVariant<Rid[]>();

// This class fails because generic type is not Variant-compatible.
//new ClassWithGenericVariant<object>();
}
}

public class ClassWithGenericVariant<[MustBeVariant] T>
{
}

public class MustBeVariantAnnotatedMethods
{
[GenericTypeAttribute<string>()]
public void MethodWithAttributeOk()
{
}

// This method definition fails because generic type is not Variant-compatible.
/*
[GenericTypeAttribute<object>()]
public void MethodWithWrongAttribute()
{
}
*/
}

[GenericTypeAttribute<string>()]
public class ClassVariantAnnotated
{
}

// This class definition fails because generic type is not Variant-compatible.
/*
[GenericTypeAttribute<object>()]
public class ClassNonVariantAnnotated
{
}
*/

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class GenericTypeAttribute<[MustBeVariant] T> : Attribute
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Testing;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Testing;
using Microsoft.CodeAnalysis.Testing.Verifiers;
using Microsoft.CodeAnalysis.Text;

namespace Godot.SourceGenerators.Tests;

public static class CSharpAnalyzerVerifier<TAnalyzer>
where TAnalyzer : DiagnosticAnalyzer, new()
{
public class Test : CSharpAnalyzerTest<TAnalyzer, XUnitVerifier>
{
public Test()
{
ReferenceAssemblies = ReferenceAssemblies.Net.Net60;

SolutionTransforms.Add((Solution solution, ProjectId projectId) =>
{
Project project =
solution.GetProject(projectId)!.AddMetadataReference(Constants.GodotSharpAssembly
.CreateMetadataReference());

return project.Solution;
});
}
}

public static Task Verify(string sources, params DiagnosticResult[] expected)
{
return MakeVerifier(new string[] { sources }, expected).RunAsync();
}

public static Test MakeVerifier(ICollection<string> sources, params DiagnosticResult[] expected)
{
var verifier = new Test();

verifier.TestState.AnalyzerConfigFiles.Add(("/.globalconfig", $"""
is_global = true
build_property.GodotProjectDir = {Constants.ExecutingAssemblyPath}
"""));

verifier.TestState.Sources.AddRange(sources.Select(source =>
{
return (source, SourceText.From(File.ReadAllText(Path.Combine(Constants.SourceFolderPath, source))));
}));

verifier.ExpectedDiagnostics.AddRange(expected);
return verifier;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using Xunit;

namespace Godot.SourceGenerators.Tests;

public class GlobalClassAnalyzerTests
{
[Fact]
public async void GlobalClassMustDeriveFromGodotObjectTest()
{
const string GlobalClassGD0401 = "GlobalClass.GD0401.cs";
await CSharpAnalyzerVerifier<GlobalClassAnalyzer>.Verify(GlobalClassGD0401);
}

[Fact]
public async void GlobalClassMustNotBeGenericTest()
{
const string GlobalClassGD0402 = "GlobalClass.GD0402.cs";
await CSharpAnalyzerVerifier<GlobalClassAnalyzer>.Verify(GlobalClassGD0402);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.SourceGenerators.Testing.XUnit" Version="1.1.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.1" />
<PackageReference Include="Microsoft.CodeAnalysis.Testing.Verifiers.XUnit" Version="1.1.1" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.XUnit" Version="1.1.1" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using Xunit;

namespace Godot.SourceGenerators.Tests;

public class MustBeVariantAnalyzerTests
{
[Fact]
public async void GenericTypeArgumentMustBeVariantTest()
{
const string MustBeVariantGD0301 = "MustBeVariant.GD0301.cs";
await CSharpAnalyzerVerifier<MustBeVariantAnalyzer>.Verify(MustBeVariantGD0301);
}

[Fact]
public async void GenericTypeParameterMustBeVariantAnnotatedTest()
{
const string MustBeVariantGD0302 = "MustBeVariant.GD0302.cs";
await CSharpAnalyzerVerifier<MustBeVariantAnalyzer>.Verify(MustBeVariantGD0302);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using Godot;

// This works because it inherits from GodotObject.
[GlobalClass]
public partial class CustomGlobalClass1 : GodotObject
{

}

// This works because it inherits from an object that inherits from GodotObject
[GlobalClass]
public partial class CustomGlobalClass2 : Node
{

}

// This raises a GD0401 diagnostic error: global classes must inherit from GodotObject
{|GD0401:[GlobalClass]
public partial class CustomGlobalClass3
{

}|}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Godot;

// This works because it inherits from GodotObject and it doesn't have any generic type parameter.
[GlobalClass]
public partial class CustomGlobalClass : GodotObject
{

}

// This raises a GD0402 diagnostic error: global classes can't have any generic type parameter
{|GD0402:[GlobalClass]
public partial class CustomGlobalClass<T> : GodotObject
{

}|}
Loading

0 comments on commit 7351b57

Please sign in to comment.