Skip to content

Commit

Permalink
+semver:minor - Rework Matrix Generator & Source Generated Types insi…
Browse files Browse the repository at this point in the history
…de `DataGeneratorMetadata` (#1651)

* Generate source information for type, method, properties and parameters

* MatrixDataSourceAttribute

* Analyzer

* Release tags

* Fix seed

* Fix attribute writer

* Re-add proj reference
  • Loading branch information
thomhurst authored Jan 23, 2025
1 parent c1c9238 commit e40c530
Show file tree
Hide file tree
Showing 71 changed files with 4,191 additions and 5,603 deletions.
19 changes: 19 additions & 0 deletions .github/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
changelog:
exclude:
labels:
- ignore-for-release
categories:
- title: Breaking Changes 🛠
labels:
- Semver-Major
- breaking-change
- breaking
- title: 🏕 Changes
labels:
- '*'
exclude:
labels:
- dependencies
- title: 👒 Dependencies
labels:
- dependencies
111 changes: 111 additions & 0 deletions TUnit.Analyzers.Tests/MatrixDataSourceAnalyzerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
using NUnit.Framework;
using Verifier = TUnit.Analyzers.Tests.Verifiers.CSharpAnalyzerVerifier<TUnit.Analyzers.MatrixAnalyzer>;

namespace TUnit.Analyzers.Tests;

public class MatrixDataSourceAnalyzerTests
{
[Test]
public async Task Class_No_Error()
{
await Verifier
.VerifyAnalyzerAsync(
"""
using TUnit.Core;
[MatrixDataSource]
public class MyClass
{
public MyClass(
[Matrix(1, 2, 3)] int value,
[Matrix(true, false)] bool value2
)
{
}
[Test]
public void MyTest()
{
}
}
"""
);
}

[Test]
public async Task Class_Missing_Attribute_Error()
{
await Verifier
.VerifyAnalyzerAsync(
"""
using TUnit.Core;
public class {|#0:MyClass|}
{
public MyClass(
[Matrix(1, 2, 3)] int value,
[Matrix(true, false)] bool value2
)
{
}
[Test]
public void MyTest()
{
}
}
""",
Verifier.Diagnostic(Rules.MatrixDataSourceAttributeRequired)
.WithLocation(0)
);
}


[Test]
public async Task Method_No_Error()
{
await Verifier
.VerifyAnalyzerAsync(
"""
using TUnit.Core;
public class MyClass
{
[MatrixDataSource]
[Test]
public void MyTest(
[Matrix(1, 2, 3)] int value,
[Matrix(true, false)] bool value2
)
{
}
}
"""
);
}

[Test]
public async Task Method_Missing_Attribute_Error()
{
await Verifier
.VerifyAnalyzerAsync(
"""
using TUnit.Core;
public class MyClass
{
[Test]
public void {|#0:MyTest|}(
[Matrix(1, 2, 3)] int value,
[Matrix(true, false)] bool value2
)
{
}
}
""",

Verifier.Diagnostic(Rules.MatrixDataSourceAttributeRequired)
.WithLocation(0)
);
}
}
7 changes: 7 additions & 0 deletions TUnit.Analyzers/Extensions/AttributeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,13 @@ public static bool IsMatrixAttribute(this AttributeData attributeData, Compilati
.WithoutGlobalPrefix));
}

public static bool IsMatrixDataSourceAttribute(this AttributeData attributeData, Compilation compilation)
{
return SymbolEqualityComparer.Default.Equals(attributeData.AttributeClass,
compilation.GetTypeByMetadataName(WellKnown.AttributeFullyQualifiedClasses.MatrixDataSourceAttribute
.WithoutGlobalPrefix));
}

public static bool IsDataSourceAttribute(this AttributeData? attributeData, Compilation compilation)
{
if (attributeData?.AttributeClass is null)
Expand Down
1 change: 1 addition & 0 deletions TUnit.Analyzers/Helpers/WellKnown.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ public static class AttributeFullyQualifiedClasses
public static readonly FullyQualifiedTypeName TimeoutAttribute = GetTypeName("TimeoutAttribute");
public static readonly FullyQualifiedTypeName Explicit = GetTypeName("ExplicitAttribute");
public static readonly FullyQualifiedTypeName Matrix = GetTypeName("MatrixAttribute");
public static readonly FullyQualifiedTypeName MatrixDataSourceAttribute = GetTypeName("MatrixDataSourceAttribute");

public static readonly FullyQualifiedTypeName BeforeAttribute = GetTypeName("BeforeAttribute");
public static readonly FullyQualifiedTypeName AfterAttribute = GetTypeName("AfterAttribute");
Expand Down
68 changes: 68 additions & 0 deletions TUnit.Analyzers/MatrixAnalyzer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using TUnit.Analyzers.Extensions;

namespace TUnit.Analyzers;

[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class MatrixAnalyzer : ConcurrentDiagnosticAnalyzer
{
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } =
ImmutableArray.Create(
Rules.MatrixDataSourceAttributeRequired);

protected override void InitializeInternal(AnalysisContext context)
{
context.RegisterSymbolAction(AnalyzeMethod, SymbolKind.Method);
context.RegisterSymbolAction(AnalyzeClass, SymbolKind.NamedType);
}

private void AnalyzeClass(SymbolAnalysisContext context)
{
if (context.Symbol is not INamedTypeSymbol namedTypeSymbol)
{
return;
}

if (!namedTypeSymbol.IsTestClass(context.Compilation))
{
return;
}

CheckMatrixErrors(context, namedTypeSymbol.GetAttributes(), namedTypeSymbol.InstanceConstructors.FirstOrDefault()?.Parameters ?? ImmutableArray<IParameterSymbol>.Empty);
}

private void AnalyzeMethod(SymbolAnalysisContext context)
{
if (context.Symbol is not IMethodSymbol methodSymbol)
{
return;
}

if (!methodSymbol.IsTestMethod(context.Compilation))
{
return;
}

CheckMatrixErrors(context, methodSymbol.GetAttributes(), methodSymbol.Parameters);
}

private void CheckMatrixErrors(SymbolAnalysisContext context, ImmutableArray<AttributeData> attributes,
ImmutableArray<IParameterSymbol> parameters)
{
if (!parameters.Any(x => x.HasMatrixAttribute(context.Compilation)))
{
return;
}

if (!attributes.Any(x => x.IsMatrixDataSourceAttribute(context.Compilation)))
{
context.ReportDiagnostic(
Diagnostic.Create(Rules.MatrixDataSourceAttributeRequired,
context.Symbol.Locations.FirstOrDefault())
);
}
}

}
27 changes: 27 additions & 0 deletions TUnit.Analyzers/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions TUnit.Analyzers/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -450,4 +450,13 @@
<data name="TUnit0048Title" xml:space="preserve">
<value>Test methods must not be static</value>
</data>
<data name="TUnit0049Description" xml:space="preserve">
<value>[MatrixDataSourceAttribute] is required if using [Matrix] values on your parameters.</value>
</data>
<data name="TUnit0049MessageFormat" xml:space="preserve">
<value>[MatrixDataSourceAttribute] is required if using [Matrix] values on your parameters.</value>
</data>
<data name="TUnit0049Title" xml:space="preserve">
<value>[MatrixDataSourceAttribute] is required</value>
</data>
</root>
3 changes: 3 additions & 0 deletions TUnit.Analyzers/Rules.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,9 @@ public static class Rules
public static DiagnosticDescriptor InstanceTestMethod =
CreateDescriptor("TUnit0048", UsageCategory, DiagnosticSeverity.Error);

public static DiagnosticDescriptor MatrixDataSourceAttributeRequired =
CreateDescriptor("TUnit0049", UsageCategory, DiagnosticSeverity.Error);

private static DiagnosticDescriptor CreateDescriptor(string diagnosticId, string category, DiagnosticSeverity severity)
{
return new DiagnosticDescriptor(
Expand Down
36 changes: 2 additions & 34 deletions TUnit.Analyzers/TestDataAnalyzer.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Diagnostics;
Expand All @@ -8,7 +7,6 @@

namespace TUnit.Analyzers;

[SuppressMessage("MicrosoftCodeAnalysisCorrectness", "RS1024:Symbols should be compared for equality")]
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class TestDataAnalyzer : ConcurrentDiagnosticAnalyzer
{
Expand All @@ -25,7 +23,8 @@ public class TestDataAnalyzer : ConcurrentDiagnosticAnalyzer
Rules.TooManyArgumentsInTestMethod,
Rules.PropertyRequiredNotSet,
Rules.MustHavePropertySetter,
Rules.ReturnFunc);
Rules.ReturnFunc,
Rules.MatrixDataSourceAttributeRequired);

protected override void InitializeInternal(AnalysisContext context)
{
Expand Down Expand Up @@ -148,8 +147,6 @@ private void Analyze(SymbolAnalysisContext context,
CheckDataGenerator(context, attribute, types);
}
}

CheckMatrix(context, parameters);
}

private void CheckPropertyAccessor(SymbolAnalysisContext context, IPropertySymbol? propertySymbol)
Expand All @@ -176,31 +173,6 @@ private ImmutableArray<ITypeSymbol> GetTypes(ImmutableArray<IParameterSymbol> pa

return types.OfType<ITypeSymbol>().ToImmutableArray().WithoutCancellationTokenParameter();
}

private void CheckMatrix(SymbolAnalysisContext context, ImmutableArray<IParameterSymbol> parameters)
{
if (!parameters.Any(x => x.HasMatrixAttribute(context.Compilation)))
{
return;
}

foreach (var parameterSymbol in parameters)
{
if (SymbolEqualityComparer.Default.Equals(parameters.LastOrDefault()?.Type,
context.Compilation.GetTypeByMetadataName(typeof(CancellationToken).FullName!)))
{
continue;
}

if (!parameterSymbol.HasMatrixAttribute(context.Compilation))
{
context.ReportDiagnostic(
Diagnostic.Create(Rules.NoTestDataProvided,
context.Symbol.Locations.FirstOrDefault())
);
}
}
}

private void CheckArguments(SymbolAnalysisContext context, AttributeData argumentsAttribute,
ImmutableArray<IParameterSymbol> parameters)
Expand Down Expand Up @@ -366,7 +338,6 @@ private void CheckMethodDataSource(SymbolAnalysisContext context,
var unwrappedTypes = UnwrapTypes(context,
dataSourceMethod,
testParameterTypes,
out var isEnumerable,
out var isFunc,
out var isTuples);

Expand Down Expand Up @@ -491,11 +462,9 @@ private static bool MatchesParameters(SymbolAnalysisContext context, ITypeSymbol
private ImmutableArray<ITypeSymbol> UnwrapTypes(SymbolAnalysisContext context,
IMethodSymbol methodContainingTestData,
ImmutableArray<ITypeSymbol> testParameterTypes,
out bool isEnumerable,
out bool isFunc,
out bool isTuples)
{
isEnumerable = false;
isFunc = false;
isTuples = false;

Expand Down Expand Up @@ -528,7 +497,6 @@ private ImmutableArray<ITypeSymbol> UnwrapTypes(SymbolAnalysisContext context,

if (methodContainingTestData.ReturnType.IsIEnumerable(context.Compilation, out var enumerableInnerType))
{
isEnumerable = true;
type = enumerableInnerType;
}

Expand Down
Loading

0 comments on commit e40c530

Please sign in to comment.