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)
{