Skip to content

Commit

Permalink
Fix MSTEST0034 doesn't handle ClassCleanupExecutionAttribute (#3741)
Browse files Browse the repository at this point in the history
  • Loading branch information
engyebrahim committed Sep 2, 2024
1 parent b74091a commit 120469e
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ internal static class WellKnownTypeNames
public const string MicrosoftVisualStudioTestToolsUnitTestingAssert = "Microsoft.VisualStudio.TestTools.UnitTesting.Assert";
public const string MicrosoftVisualStudioTestToolsUnitTestingClassCleanupAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.ClassCleanupAttribute";
public const string MicrosoftVisualStudioTestToolsUnitTestingClassCleanupBehavior = "Microsoft.VisualStudio.TestTools.UnitTesting.ClassCleanupBehavior";
public const string MicrosoftVisualStudioTestToolsUnitTestingClassCleanupExecutionAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.ClassCleanupExecutionAttribute";
public const string MicrosoftVisualStudioTestToolsUnitTestingClassInitializeAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.ClassInitializeAttribute";
public const string MicrosoftVisualStudioTestToolsUnitTestingCollectionAssert = "Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert";
public const string MicrosoftVisualStudioTestToolsUnitTestingCssIterationAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.CssIterationAttribute";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,17 @@ public override void Initialize(AnalysisContext context)
{
if (context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingClassCleanupAttribute, out INamedTypeSymbol? classCleanupAttributeSymbol)
&& context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTestClassAttribute, out INamedTypeSymbol? testClassAttributeSymbol)
&& context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingClassCleanupBehavior, out INamedTypeSymbol? classCleanupBehaviorSymbol))
&& context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingClassCleanupBehavior, out INamedTypeSymbol? classCleanupBehaviorSymbol)
&& context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingClassCleanupExecutionAttribute, out INamedTypeSymbol? classCleanupExecutionAttributeSymbol))
{
context.RegisterSymbolAction(
context => AnalyzeSymbol(context, classCleanupAttributeSymbol, testClassAttributeSymbol, classCleanupBehaviorSymbol),
context => AnalyzeSymbol(context, classCleanupAttributeSymbol, testClassAttributeSymbol, classCleanupBehaviorSymbol, classCleanupExecutionAttributeSymbol),
SymbolKind.Method);
}
});
}

private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbol classCleanupAttributeSymbol, INamedTypeSymbol testClassAttributeSymbol, INamedTypeSymbol classCleanupBehaviorSymbol)
private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbol classCleanupAttributeSymbol, INamedTypeSymbol testClassAttributeSymbol, INamedTypeSymbol classCleanupBehaviorSymbol, INamedTypeSymbol classCleanupExecutionAttributeSymbol)
{
var methodSymbol = (IMethodSymbol)context.Symbol;

Expand All @@ -61,29 +62,47 @@ private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbo
return;
}

// Check if the assembly has the ClassCleanupExecutionAttribute with EndOfClass behavior
bool assemblyHasEndOfClassCleanup = context.Compilation.Assembly
.GetAttributes()
.Any(attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, classCleanupExecutionAttributeSymbol)
&& attr.ConstructorArguments.Length == 1
&& SymbolEqualityComparer.Default.Equals(attr.ConstructorArguments[0].Type, classCleanupBehaviorSymbol)
&& 1.Equals(attr.ConstructorArguments[0].Value));

ImmutableArray<AttributeData> methodAttributes = methodSymbol.GetAttributes();
bool hasCleanupAttr = false;
bool hasCleanupEndOClassBehavior = false;
bool hasClassCleanupBehavior = false;

foreach (AttributeData methodAttribute in methodAttributes)
{
if (SymbolEqualityComparer.Default.Equals(methodAttribute.AttributeClass, classCleanupAttributeSymbol))
{
hasCleanupAttr = true;
foreach (TypedConstant arg in methodAttribute.ConstructorArguments)
{
// one is the value for EndOFClass behavior in the CleanupBehavior enum.
if (SymbolEqualityComparer.Default.Equals(arg.Type, classCleanupBehaviorSymbol)
&& 1.Equals(arg.Value))
if (SymbolEqualityComparer.Default.Equals(arg.Type, classCleanupBehaviorSymbol))
{
hasCleanupEndOClassBehavior = true;
hasClassCleanupBehavior = true;

// one is the value for EndOFClass behavior in the CleanupBehavior enum.
if (1.Equals(arg.Value))
{
hasCleanupEndOClassBehavior = true;
}
}
}
}
}

if (hasCleanupAttr && !hasCleanupEndOClassBehavior)
if (!hasCleanupAttr ||
(!hasClassCleanupBehavior && assemblyHasEndOfClassCleanup)
|| (hasClassCleanupBehavior && hasCleanupEndOClassBehavior))
{
context.ReportDiagnostic(methodSymbol.CreateDiagnostic(UseClassCleanupBehaviorEndOfClassRule));
return;
}

context.ReportDiagnostic(methodSymbol.CreateDiagnostic(UseClassCleanupBehaviorEndOfClassRule));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -152,4 +152,80 @@ public static void ClassCleanup()

await VerifyCS.VerifyAnalyzerAsync(code);
}

public async Task UsingClassCleanup_InsideTestClass_WithClassCleanupExecutionWithEndOfClassBehavior_NoDiagnostic()
{
string code = """
using Microsoft.VisualStudio.TestTools.UnitTesting;
[assembly: ClassCleanupExecutionAttribute(ClassCleanupBehavior.EndOfClass)]

[TestClass]
public class MyTestClass
{
[ClassCleanup]
public static void ClassCleanup()
{
}
}
""";

await VerifyCS.VerifyAnalyzerAsync(code);
}

public async Task UsingClassCleanup_InsideTestClass_WithClassCleanupExecutionWithEndOfAsseblyBehavior_Diagnostic()
{
string code = """
using Microsoft.VisualStudio.TestTools.UnitTesting;
[assembly: ClassCleanupExecutionAttribute(ClassCleanupBehavior.EndOfAssembly)]

[TestClass]
public class MyTestClass
{
[ClassCleanup]
public static void [|ClassCleanup|]()
{
}
}
""";

await VerifyCS.VerifyAnalyzerAsync(code);
}

public async Task UsingClassCleanup_InsideTestClass_WithClassCleanupExecutionWithEndOfClassBehavior_WithCleanupBehaviorEndOfAssembly_Diagnostic()
{
string code = """
using Microsoft.VisualStudio.TestTools.UnitTesting;
[assembly: ClassCleanupExecutionAttribute(ClassCleanupBehavior.EndOfClass)]

[TestClass]
public class MyTestClass
{
[ClassCleanup(ClassCleanupBehavior.EndOfAssembly)]
public static void [|ClassCleanup|]()
{
}
}
""";

await VerifyCS.VerifyAnalyzerAsync(code);
}

public async Task UsingClassCleanup_InsideTestClass_WithClassCleanupExecutionWithEndOfAssemblyBehavior_WithCleanupBehaviorEndOfClass_NoDiagnostic()
{
string code = """
using Microsoft.VisualStudio.TestTools.UnitTesting;
[assembly: ClassCleanupExecutionAttribute(ClassCleanupBehavior.EndOfAssembly)]

[TestClass]
public class MyTestClass
{
[ClassCleanup(ClassCleanupBehavior.EndOfClass)]
public static void ClassCleanup()
{
}
}
""";

await VerifyCS.VerifyAnalyzerAsync(code);
}
}

0 comments on commit 120469e

Please sign in to comment.