From 406e70e4342a4f201c139136008169d19f077150 Mon Sep 17 00:00:00 2001 From: Pavel Vostretsov Date: Thu, 22 Aug 2024 14:48:30 +0500 Subject: [PATCH] add deep inheritance chain for tests analyzer --- .../Constants/AnalyzerIdentifiers.cs | 1 + .../TestDeepInheritanceChainAnalyzer.cs | 60 +++++++++++++++++++ .../TestDeepInheritanceChainConstants.cs | 10 ++++ .../ParallelTestFixtureSetUpTest.cs | 10 +++- 4 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 NUnit.Analyzers/TestDeepInheritanceChain/TestDeepInheritanceChainAnalyzer.cs create mode 100644 NUnit.Analyzers/TestDeepInheritanceChain/TestDeepInheritanceChainConstants.cs diff --git a/NUnit.Analyzers/Constants/AnalyzerIdentifiers.cs b/NUnit.Analyzers/Constants/AnalyzerIdentifiers.cs index b0f6793..e5697e3 100644 --- a/NUnit.Analyzers/Constants/AnalyzerIdentifiers.cs +++ b/NUnit.Analyzers/Constants/AnalyzerIdentifiers.cs @@ -4,5 +4,6 @@ internal static class AnalyzerIdentifiers { internal const string TestFieldIsNotReadonly = "NU0001"; internal const string TestUsesSetupAttributes = "NU0002"; + internal const string TestDeepInheritanceChain = "NU0003"; } } \ No newline at end of file diff --git a/NUnit.Analyzers/TestDeepInheritanceChain/TestDeepInheritanceChainAnalyzer.cs b/NUnit.Analyzers/TestDeepInheritanceChain/TestDeepInheritanceChainAnalyzer.cs new file mode 100644 index 0000000..aa273c5 --- /dev/null +++ b/NUnit.Analyzers/TestDeepInheritanceChain/TestDeepInheritanceChainAnalyzer.cs @@ -0,0 +1,60 @@ +using System.Collections.Immutable; +using System.Linq; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +using SkbKontur.NUnit.Analyzers.Constants; +using SkbKontur.NUnit.Analyzers.Extensions; + +namespace SkbKontur.NUnit.Analyzers.TestDeepInheritanceChain +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public class TestDeepInheritanceChainAnalyzer : DiagnosticAnalyzer + { + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + context.RegisterSymbolAction(AnalyzeType, SymbolKind.NamedType); + } + + private static void AnalyzeType(SymbolAnalysisContext context) + { + var typeSymbol = (INamedTypeSymbol)context.Symbol; + + var hasTests = typeSymbol + .GetMembers() + .OfType() + .Any(x => x.MethodKind == MethodKind.Ordinary && x.IsTestRelatedMethod(context.Compilation)); + + if (!hasTests) + return; + + var testBase = typeSymbol.BaseType?.BaseType; + if (testBase != null && !knownRoots.Any(r => testBase.IsType(r, context.Compilation))) + context.ReportDiagnostic(Diagnostic.Create(testDeepInheritanceChain, typeSymbol.Locations[0])); + } + + public override ImmutableArray SupportedDiagnostics => + ImmutableArray.Create(testDeepInheritanceChain); + + private static readonly DiagnosticDescriptor testDeepInheritanceChain = new DiagnosticDescriptor( + AnalyzerIdentifiers.TestDeepInheritanceChain, + TestDeepInheritanceChainConstants.TestDeepInheritanceChainTitle, + TestDeepInheritanceChainConstants.TestDeepInheritanceChainMessage, + Categories.ParallelExecution, + DiagnosticSeverity.Warning, + true, + TestDeepInheritanceChainConstants.TestDeepInheritanceChainDescription, + TestDeepInheritanceChainConstants.TestDeepInheritanceChainUri + ); + + private static readonly string[] knownRoots = + { + "System.Object", + "SkbKontur.NUnit.Middlewares.SimpleTestBase", + "SkbKontur.NUnit.Middlewares.SimpleSuiteBase", + }; + } +} \ No newline at end of file diff --git a/NUnit.Analyzers/TestDeepInheritanceChain/TestDeepInheritanceChainConstants.cs b/NUnit.Analyzers/TestDeepInheritanceChain/TestDeepInheritanceChainConstants.cs new file mode 100644 index 0000000..b542c5f --- /dev/null +++ b/NUnit.Analyzers/TestDeepInheritanceChain/TestDeepInheritanceChainConstants.cs @@ -0,0 +1,10 @@ +namespace SkbKontur.NUnit.Analyzers.TestDeepInheritanceChain +{ + internal static class TestDeepInheritanceChainConstants + { + internal const string TestDeepInheritanceChainTitle = "Test base inheritance chain is too deep"; + internal const string TestDeepInheritanceChainMessage = "Test base inheritance chain is too deep"; + internal const string TestDeepInheritanceChainDescription = "Test fixture class should have only one base class for clarity."; + internal const string TestDeepInheritanceChainUri = "https://en.wikipedia.org/wiki/Composition_over_inheritance"; + } +} \ No newline at end of file diff --git a/NUnit.Extensions.Tests/Middlewares/ParallelTestFixtureSetUpTest.cs b/NUnit.Extensions.Tests/Middlewares/ParallelTestFixtureSetUpTest.cs index 018b06f..11d20d3 100644 --- a/NUnit.Extensions.Tests/Middlewares/ParallelTestFixtureSetUpTest.cs +++ b/NUnit.Extensions.Tests/Middlewares/ParallelTestFixtureSetUpTest.cs @@ -21,8 +21,16 @@ public void Test() } } + public abstract class Base2 : SimpleTestBase + { + } + + public abstract class Base3 : Base2 + { + } + [Parallelizable(ParallelScope.Self)] - public class SecondTestFixtureSetUpTest : SimpleTestBase + public class SecondTestFixtureSetUpTest : Base3 { protected override void Configure(ISetupBuilder fixture, ISetupBuilder test) {