Skip to content

Commit 5017ae8

Browse files
authored
Implement analyzer for RetryAttribute to be present on test methods (#5437)
1 parent 5df2977 commit 5017ae8

21 files changed

+355
-0
lines changed

src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Unshipped.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@
66
Rule ID | Category | Severity | Notes
77
--------|----------|----------|-------
88
MSTEST0042 | Usage | Warning | DuplicateDataRowAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0042)
9+
MSTEST0043 | Usage | Warning | UseRetryWithTestMethodAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0043)

src/Analyzers/MSTest.Analyzers/Helpers/DiagnosticIds.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,5 @@ internal static class DiagnosticIds
4747
public const string AvoidUsingAssertsInAsyncVoidContextRuleId = "MSTEST0040";
4848
public const string UseConditionBaseWithTestClassRuleId = "MSTEST0041";
4949
public const string DuplicateDataRowRuleId = "MSTEST0042";
50+
public const string UseRetryWithTestMethodRuleId = "MSTEST0043";
5051
}

src/Analyzers/MSTest.Analyzers/Helpers/WellKnownTypeNames.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ internal static class WellKnownTypeNames
3131
public const string MicrosoftVisualStudioTestToolsUnitTestingOwnerAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.OwnerAttribute";
3232
public const string MicrosoftVisualStudioTestToolsUnitTestingParallelizeAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.ParallelizeAttribute";
3333
public const string MicrosoftVisualStudioTestToolsUnitTestingPriorityAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.PriorityAttribute";
34+
public const string MicrosoftVisualStudioTestToolsUnitTestingRetryBaseAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.RetryBaseAttribute";
3435
public const string MicrosoftVisualStudioTestToolsUnitTestingStringAssert = "Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert";
3536
public const string MicrosoftVisualStudioTestToolsUnitTestingTestClassAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.TestClassAttribute";
3637
public const string MicrosoftVisualStudioTestToolsUnitTestingTestCleanupAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.TestCleanupAttribute";
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
#nullable enable
22
MSTest.Analyzers.DuplicateDataRowAnalyzer
33
MSTest.Analyzers.DuplicateDataRowAnalyzer.DuplicateDataRowAnalyzer() -> void
4+
MSTest.Analyzers.UseRetryWithTestMethodAnalyzer
5+
MSTest.Analyzers.UseRetryWithTestMethodAnalyzer.UseRetryWithTestMethodAnalyzer() -> void
46
override MSTest.Analyzers.DuplicateDataRowAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void
57
override MSTest.Analyzers.DuplicateDataRowAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.DiagnosticDescriptor!>
8+
override MSTest.Analyzers.UseRetryWithTestMethodAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void
9+
override MSTest.Analyzers.UseRetryWithTestMethodAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.DiagnosticDescriptor!>

src/Analyzers/MSTest.Analyzers/Resources.Designer.cs

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Analyzers/MSTest.Analyzers/Resources.resx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,4 +579,10 @@ The type declaring these methods should also respect the following rules:
579579
<data name="DuplicateDataRowMessageFormat" xml:space="preserve">
580580
<value>Do not duplicate 'DataRow' attributes. This is usually a copy/paste error. The attribute indices are '{0}' and '{1}'.</value>
581581
</data>
582+
<data name="UseRetryWithTestMethodTitle" xml:space="preserve">
583+
<value>Use retry attribute on test method</value>
584+
</data>
585+
<data name="UseRetryWithTestMethodMessageFormat" xml:space="preserve">
586+
<value>An attribute that derives from 'RetryBaseAttribute' can be specified only on a test method</value>
587+
</data>
582588
</root>
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
using System.Collections.Immutable;
4+
5+
using Analyzer.Utilities.Extensions;
6+
7+
using Microsoft.CodeAnalysis;
8+
using Microsoft.CodeAnalysis.Diagnostics;
9+
10+
using MSTest.Analyzers.Helpers;
11+
12+
namespace MSTest.Analyzers;
13+
14+
/// <summary>
15+
/// MSTEST0035: <inheritdoc cref="Resources.UseRetryWithTestMethodTitle"/>.
16+
/// </summary>
17+
[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
18+
public sealed class UseRetryWithTestMethodAnalyzer : DiagnosticAnalyzer
19+
{
20+
private static readonly LocalizableResourceString Title = new(nameof(Resources.UseRetryWithTestMethodTitle), Resources.ResourceManager, typeof(Resources));
21+
private static readonly LocalizableResourceString MessageFormat = new(nameof(Resources.UseRetryWithTestMethodMessageFormat), Resources.ResourceManager, typeof(Resources));
22+
23+
internal static readonly DiagnosticDescriptor UseRetryWithTestMethodRule = DiagnosticDescriptorHelper.Create(
24+
DiagnosticIds.UseRetryWithTestMethodRuleId,
25+
Title,
26+
MessageFormat,
27+
null,
28+
Category.Usage,
29+
DiagnosticSeverity.Warning,
30+
isEnabledByDefault: true,
31+
escalateToErrorInRecommended: true);
32+
33+
/// <inheritdoc />
34+
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; }
35+
= ImmutableArray.Create(UseRetryWithTestMethodRule);
36+
37+
/// <inheritdoc />
38+
public override void Initialize(AnalysisContext context)
39+
{
40+
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
41+
context.EnableConcurrentExecution();
42+
43+
context.RegisterCompilationStartAction(context =>
44+
{
45+
if (context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTestMethodAttribute, out INamedTypeSymbol? testMethodAttributeSymbol) &&
46+
context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingRetryBaseAttribute, out INamedTypeSymbol? retryBaseAttributeSymbol))
47+
{
48+
context.RegisterSymbolAction(
49+
context => AnalyzeSymbol(context, testMethodAttributeSymbol, retryBaseAttributeSymbol),
50+
SymbolKind.Method);
51+
}
52+
});
53+
}
54+
55+
private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbol testMethodAttributeSymbol, INamedTypeSymbol retryBaseAttributeSymbol)
56+
{
57+
bool hasRetryBaseAttribute = false;
58+
foreach (AttributeData attribute in context.Symbol.GetAttributes())
59+
{
60+
if (attribute.AttributeClass.Inherits(testMethodAttributeSymbol))
61+
{
62+
// We are looking for retry attributes that are not applied to test methods.
63+
// If this is already a test method, we just return.
64+
return;
65+
}
66+
67+
if (attribute.AttributeClass.Inherits(retryBaseAttributeSymbol))
68+
{
69+
hasRetryBaseAttribute = true;
70+
}
71+
}
72+
73+
// We looked all attributes, and we found a retry attribute, but didn't find TestMethodAttribute.
74+
if (hasRetryBaseAttribute)
75+
{
76+
context.ReportDiagnostic(context.Symbol.CreateDiagnostic(UseRetryWithTestMethodRule));
77+
}
78+
}
79+
}

src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -809,6 +809,16 @@ Typ deklarující tyto metody by měl také respektovat následující pravidla:
809809
<target state="translated">Použít správné metody Assert</target>
810810
<note />
811811
</trans-unit>
812+
<trans-unit id="UseRetryWithTestMethodMessageFormat">
813+
<source>An attribute that derives from 'RetryBaseAttribute' can be specified only on a test method</source>
814+
<target state="new">An attribute that derives from 'RetryBaseAttribute' can be specified only on a test method</target>
815+
<note />
816+
</trans-unit>
817+
<trans-unit id="UseRetryWithTestMethodTitle">
818+
<source>Use retry attribute on test method</source>
819+
<target state="new">Use retry attribute on test method</target>
820+
<note />
821+
</trans-unit>
812822
</body>
813823
</file>
814824
</xliff>

src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -810,6 +810,16 @@ Der Typ, der diese Methoden deklariert, sollte auch die folgenden Regeln beachte
810810
<target state="translated">Geeignete Assert-Methoden verwenden</target>
811811
<note />
812812
</trans-unit>
813+
<trans-unit id="UseRetryWithTestMethodMessageFormat">
814+
<source>An attribute that derives from 'RetryBaseAttribute' can be specified only on a test method</source>
815+
<target state="new">An attribute that derives from 'RetryBaseAttribute' can be specified only on a test method</target>
816+
<note />
817+
</trans-unit>
818+
<trans-unit id="UseRetryWithTestMethodTitle">
819+
<source>Use retry attribute on test method</source>
820+
<target state="new">Use retry attribute on test method</target>
821+
<note />
822+
</trans-unit>
813823
</body>
814824
</file>
815825
</xliff>

src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -809,6 +809,16 @@ El tipo que declara estos métodos también debe respetar las reglas siguientes:
809809
<target state="translated">Usar métodos "Assert" adecuados</target>
810810
<note />
811811
</trans-unit>
812+
<trans-unit id="UseRetryWithTestMethodMessageFormat">
813+
<source>An attribute that derives from 'RetryBaseAttribute' can be specified only on a test method</source>
814+
<target state="new">An attribute that derives from 'RetryBaseAttribute' can be specified only on a test method</target>
815+
<note />
816+
</trans-unit>
817+
<trans-unit id="UseRetryWithTestMethodTitle">
818+
<source>Use retry attribute on test method</source>
819+
<target state="new">Use retry attribute on test method</target>
820+
<note />
821+
</trans-unit>
812822
</body>
813823
</file>
814824
</xliff>

0 commit comments

Comments
 (0)