Merge pull request #86528 from avilches/must-be-variant-tests
Add unit tests for C# diagnostic analyzers
This commit is contained in:
commit
7351b5704a
12 changed files with 418 additions and 0 deletions
|
@ -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
|
||||
|
@ -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
|
||||
|
|
|
@ -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
|
||||
{
|
||||
}
|
||||
*/
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<LangVersion>11</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
|
|
@ -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
|
||||
{
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
{
|
||||
|
||||
}|}
|
|
@ -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
|
||||
{
|
||||
|
||||
}|}
|
|
@ -0,0 +1,71 @@
|
|||
using System;
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
using Array = Godot.Collections.Array;
|
||||
|
||||
public class MustBeVariantGD0301
|
||||
{
|
||||
public void MethodCallsError()
|
||||
{
|
||||
// This raises a GD0301 diagnostic error: object is not Variant (and Method<T> requires a variant generic type).
|
||||
Method<{|GD0301:object|}>();
|
||||
}
|
||||
public void MethodCallsOk()
|
||||
{
|
||||
// All these calls are valid because they are Variant types.
|
||||
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[]>();
|
||||
}
|
||||
|
||||
public void Method<[MustBeVariant] T>()
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
using Godot;
|
||||
|
||||
public class MustBeVariantGD0302
|
||||
{
|
||||
public void MethodOk<[MustBeVariant] T>()
|
||||
{
|
||||
// T is guaranteed to be a Variant-compatible type because it's annotated with the [MustBeVariant] attribute, so it can be used here.
|
||||
new ExampleClass<T>();
|
||||
Method<T>();
|
||||
}
|
||||
|
||||
public void MethodFail<T>()
|
||||
{
|
||||
// These two calls raise a GD0302 diagnostic error: T is not valid here because it may not a Variant type and method call and class require it.
|
||||
new ExampleClass<{|GD0302:T|}>();
|
||||
Method<{|GD0302:T|}>();
|
||||
}
|
||||
|
||||
public void Method<[MustBeVariant] T>()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class ExampleClass<[MustBeVariant] T>
|
||||
{
|
||||
|
||||
}
|
Loading…
Reference in a new issue