From 2ea2439e883687ae75ae9c5bcdf1b42179933b67 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 +
NUnit.Analyzers/README.md | 1 +
.../TestDeepInheritanceChainAnalyzer.cs | 60 +++++++++++++++++++
.../TestDeepInheritanceChainConstants.cs | 10 ++++
README.md | 9 +--
5 files changed, 77 insertions(+), 4 deletions(-)
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/README.md b/NUnit.Analyzers/README.md
index f1fc290..3edcac9 100644
--- a/NUnit.Analyzers/README.md
+++ b/NUnit.Analyzers/README.md
@@ -6,3 +6,4 @@
Couple of nunit analyzers, helpful for parallel tests:
- NU0001 Warning: TestFieldIsNotReadonly
- NU0002 Warning: TestUsesSetupAttributes
+- NU0003 Warning: TestDeepInheritanceChain
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/README.md b/README.md
index 3f48f87..6bfe3fe 100644
--- a/README.md
+++ b/README.md
@@ -2,10 +2,11 @@
A collection of extensions for NUnit.
-| | Build Status |
-|-------------------------------------|:--------------: |
-| NUnit.Middlewares | [![NuGet Status](https://img.shields.io/nuget/v/SkbKontur.NUnit.Middlewares.svg)](https://www.nuget.org/packages/SkbKontur.NUnit.Middlewares/) |
-| NUnit.Retries | [![NuGet Status](https://img.shields.io/nuget/v/SkbKontur.NUnit.Retries.svg)](https://www.nuget.org/packages/SkbKontur.NUnit.Retries/) |
+| | Build Status |
+|-------------------|:--------------: |
+| NUnit.Analyzers | [![NuGet Status](https://img.shields.io/nuget/v/SkbKontur.NUnit.Analyzers.svg)](https://www.nuget.org/packages/SkbKontur.NUnit.Analyzers/) |
+| NUnit.Middlewares | [![NuGet Status](https://img.shields.io/nuget/v/SkbKontur.NUnit.Middlewares.svg)](https://www.nuget.org/packages/SkbKontur.NUnit.Middlewares/) |
+| NUnit.Retries | [![NuGet Status](https://img.shields.io/nuget/v/SkbKontur.NUnit.Retries.svg)](https://www.nuget.org/packages/SkbKontur.NUnit.Retries/) |
| Build | [![Build status](https://github.com/skbkontur/nunit-extensions/actions/workflows/actions.yml/badge.svg)](https://github.com/skbkontur/nunit-extensions/actions) |
## Release Notes