1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-13 13:31:48 +00:00

C# Add test suite for Diagnostic Analyzers: GlobalClass and MustBeVariant

This commit is contained in:
Alberto Vilches
2023-12-25 23:21:09 +01:00
parent 13a0d6e9b2
commit 7a90c56c00
12 changed files with 418 additions and 0 deletions

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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>

View File

@@ -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);
}
}

View File

@@ -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
{
}|}

View File

@@ -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
{
}|}

View File

@@ -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>()
{
}
}

View File

@@ -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>
{
}