Skip to content

Commit acb8d64

Browse files
committed
Handle MSTest TestInitialize and ClassInitialize
Fix #173.
1 parent ff30b59 commit acb8d64

10 files changed

Lines changed: 256 additions & 39 deletions

File tree

IDisposableAnalyzers.Test/AssemblyAttributes.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
using Gu.Roslyn.Asserts;
1+
using Gu.Roslyn.Asserts;
22

33
[assembly: TransitiveMetadataReferences(typeof(IDisposableAnalyzers.Test.ValidWithAllAnalyzers))]
4+
[assembly: TransitiveMetadataReferences(typeof(Microsoft.VisualStudio.TestTools.UnitTesting.ClassCleanupAttribute))]
45
[assembly: MetadataReferences(
56
typeof(System.Linq.Enumerable),
67
typeof(System.Net.WebClient),

IDisposableAnalyzers.Test/IDISP003DisposeBeforeReassigningTests/Valid.TestFixture.cs

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
namespace IDisposableAnalyzers.Test.IDISP003DisposeBeforeReassigningTests
1+
namespace IDisposableAnalyzers.Test.IDISP003DisposeBeforeReassigningTests
22
{
33
using Gu.Roslyn.Asserts;
44
using NUnit.Framework;
@@ -58,6 +58,62 @@ public void TearDown()
5858
this.disposable.Dispose();
5959
}
6060
}
61+
}";
62+
RoslynAssert.Valid(Analyzer, DisposableCode, code);
63+
}
64+
65+
[Test]
66+
public static void DisposingFieldInTestCleanup()
67+
{
68+
var code = @"
69+
namespace N
70+
{
71+
using Microsoft.VisualStudio.TestTools.UnitTesting;
72+
73+
public class Tests
74+
{
75+
private Disposable disposable;
76+
77+
[TestInitialize]
78+
public void OnClassInitialize(TestContext testContext)
79+
{
80+
this.disposable = new Disposable();
81+
}
82+
83+
[TestCleanup]
84+
public void OnClassCleanup()
85+
{
86+
this.disposable.Dispose();
87+
}
88+
}
89+
}";
90+
RoslynAssert.Valid(Analyzer, DisposableCode, code);
91+
}
92+
93+
[Test]
94+
public static void DisposingFieldInClassCleanup()
95+
{
96+
var code = @"
97+
namespace N
98+
{
99+
using Microsoft.VisualStudio.TestTools.UnitTesting;
100+
101+
public class Tests
102+
{
103+
private Disposable disposable;
104+
105+
[ClassInitialize]
106+
public void OnClassInitialize(TestContext testContext)
107+
{
108+
this.disposable = new Disposable();
109+
}
110+
111+
[ClassCleanup]
112+
public void OnClassCleanup()
113+
{
114+
this.disposable.Dispose();
115+
}
116+
}
61117
}";
62118
RoslynAssert.Valid(Analyzer, DisposableCode, code);
63119
}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
namespace IDisposableAnalyzers.Test.IDISP004DoNotIgnoreCreatedTests
2+
{
3+
using Gu.Roslyn.Asserts;
4+
using NUnit.Framework;
5+
6+
public static partial class Valid
7+
{
8+
[Test]
9+
public static void DisposingFieldInTearDown()
10+
{
11+
var code = @"
12+
namespace N
13+
{
14+
using NUnit.Framework;
15+
16+
public class Tests
17+
{
18+
private Disposable disposable;
19+
20+
[SetUp]
21+
public void SetUp()
22+
{
23+
this.disposable = new Disposable();
24+
}
25+
26+
[TearDown]
27+
public void TearDown()
28+
{
29+
this.disposable.Dispose();
30+
}
31+
}
32+
}";
33+
RoslynAssert.Valid(Analyzer, DisposableCode, code);
34+
}
35+
36+
[Test]
37+
public static void DisposingFieldInOneTimeTearDown()
38+
{
39+
var code = @"
40+
namespace N
41+
{
42+
using NUnit.Framework;
43+
44+
public class Tests
45+
{
46+
private Disposable disposable;
47+
48+
[OneTimeSetUp]
49+
public void SetUp()
50+
{
51+
this.disposable = new Disposable();
52+
}
53+
54+
[OneTimeTearDown]
55+
public void TearDown()
56+
{
57+
this.disposable.Dispose();
58+
}
59+
}
60+
}";
61+
RoslynAssert.Valid(Analyzer, DisposableCode, code);
62+
}
63+
64+
[Test]
65+
public static void DisposingFieldInTestCleanup()
66+
{
67+
var code = @"
68+
namespace N
69+
{
70+
using Microsoft.VisualStudio.TestTools.UnitTesting;
71+
72+
public class Tests
73+
{
74+
private Disposable disposable;
75+
76+
[TestInitialize]
77+
public void OnClassInitialize(TestContext testContext)
78+
{
79+
this.disposable = new Disposable();
80+
}
81+
82+
[TestCleanup]
83+
public void OnClassCleanup()
84+
{
85+
this.disposable.Dispose();
86+
}
87+
}
88+
}";
89+
RoslynAssert.Valid(Analyzer, DisposableCode, code);
90+
}
91+
92+
[Test]
93+
public static void DisposingFieldInClassCleanup()
94+
{
95+
var code = @"
96+
namespace N
97+
{
98+
using Microsoft.VisualStudio.TestTools.UnitTesting;
99+
100+
public class Tests
101+
{
102+
private Disposable disposable;
103+
104+
[ClassInitialize]
105+
public void OnClassInitialize(TestContext testContext)
106+
{
107+
this.disposable = new Disposable();
108+
}
109+
110+
[ClassCleanup]
111+
public void OnClassCleanup()
112+
{
113+
this.disposable.Dispose();
114+
}
115+
}
116+
}";
117+
RoslynAssert.Valid(Analyzer, DisposableCode, code);
118+
}
119+
}
120+
}

IDisposableAnalyzers.Test/IDisposableAnalyzers.Test.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="3.3.1" />
3636
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.7" PrivateAssets="all" />
3737
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
38+
<PackageReference Include="Microsoft.VisualStudio.UnitTesting" Version="11.0.50727.1" />
3839
<PackageReference Include="Moq" Version="4.13.1" />
3940
<PackageReference Include="Ninject" Version="4.0.0-beta-0134" />
4041
<PackageReference Include="NUnit" Version="3.12.0" />

IDisposableAnalyzers/Analyzers/AssignmentAnalyzer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ private static bool IsReassignedWithCreated(AssignmentExpressionSyntax assignmen
8383
if (FieldOrProperty.TryCreate(assignedSymbol, out var fieldOrProperty))
8484
{
8585
if (context.Node.FirstAncestor<TypeDeclarationSyntax>() is { } containingType &&
86-
TestFixture.IsAssignedAndDisposedInSetupAndTearDown(fieldOrProperty, containingType, context.SemanticModel, context.CancellationToken))
86+
TestFixture.IsAssignedInInitializeAndDisposedInCleanup(fieldOrProperty, containingType, context.SemanticModel, context.CancellationToken))
8787
{
8888
return false;
8989
}

IDisposableAnalyzers/Analyzers/CreationAnalyzer.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ private static void Handle(SyntaxNodeAnalysisContext context)
2626
if (!context.IsExcludedFromAnalysis() &&
2727
ShouldCheck(context) is { } expression)
2828
{
29+
if (Disposable.IsCreation(expression, context.SemanticModel, context.CancellationToken).IsEither(Result.Yes, Result.AssumeYes) &&
30+
DisposableWalker.Ignores(expression, context.SemanticModel, context.CancellationToken))
31+
{
32+
context.ReportDiagnostic(Diagnostic.Create(Descriptors.IDISP004DoNotIgnoreCreated, context.Node.GetLocation()));
33+
}
34+
2935
if (context.Node is ObjectCreationExpressionSyntax objectCreation &&
3036
context.SemanticModel.TryGetType(objectCreation, context.CancellationToken, out var type) &&
3137
type.IsAssignableTo(KnownSymbol.HttpClient, context.Compilation) &&
@@ -35,12 +41,6 @@ private static void Handle(SyntaxNodeAnalysisContext context)
3541
{
3642
context.ReportDiagnostic(Diagnostic.Create(Descriptors.IDISP014UseSingleInstanceOfHttpClient, objectCreation.GetLocation()));
3743
}
38-
39-
if (Disposable.IsCreation(expression, context.SemanticModel, context.CancellationToken).IsEither(Result.Yes, Result.AssumeYes) &&
40-
DisposableWalker.Ignores(expression, context.SemanticModel, context.CancellationToken))
41-
{
42-
context.ReportDiagnostic(Diagnostic.Create(Descriptors.IDISP004DoNotIgnoreCreated, context.Node.GetLocation()));
43-
}
4444
}
4545
}
4646

IDisposableAnalyzers/Analyzers/FieldAndPropertyDeclarationAnalyzer.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,12 @@ private static void HandleFieldOrProperty(SyntaxNodeAnalysisContext context, Fie
8484
}
8585
else if (context.Node.TryFirstAncestorOrSelf<TypeDeclarationSyntax>(out var typeDeclaration) &&
8686
DisposableMember.IsDisposed(fieldOrProperty, typeDeclaration, context.SemanticModel, context.CancellationToken).IsEither(Result.No, Result.AssumeNo) &&
87-
!TestFixture.IsAssignedAndDisposedInSetupAndTearDown(fieldOrProperty, typeDeclaration, context.SemanticModel, context.CancellationToken))
87+
!TestFixture.IsAssignedInInitializeAndDisposedInCleanup(fieldOrProperty, typeDeclaration, context.SemanticModel, context.CancellationToken))
8888
{
8989
context.ReportDiagnostic(Diagnostic.Create(Descriptors.IDISP002DisposeMember, context.Node.GetLocation()));
9090

9191
if (!DisposeMethod.TryFindFirst(fieldOrProperty.ContainingType, context.Compilation, Search.TopLevel, out _) &&
92-
!TestFixture.IsAssignedInSetUp(fieldOrProperty, typeDeclaration, context.SemanticModel, context.CancellationToken, out _, out _))
92+
!TestFixture.IsAssignedInInitialize(fieldOrProperty, typeDeclaration, context.SemanticModel, context.CancellationToken, out _, out _))
9393
{
9494
context.ReportDiagnostic(Diagnostic.Create(Descriptors.IDISP006ImplementIDisposable, context.Node.GetLocation()));
9595
}

IDisposableAnalyzers/CodeFixes/DisposeInTearDownFix.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ protected override async Task RegisterCodeFixesAsync(DocumentEditorCodeFixContex
3838
semanticModel.TryGetSymbol(member, context.CancellationToken, out ISymbol? memberSymbol) &&
3939
FieldOrProperty.TryCreate(memberSymbol, out var fieldOrProperty) &&
4040
member.FirstAncestor<ClassDeclarationSyntax>() is { } classDeclaration &&
41-
TestFixture.IsAssignedInSetUp(fieldOrProperty, classDeclaration, semanticModel, context.CancellationToken, out var assignment, out var setupAttribute) &&
41+
TestFixture.IsAssignedInInitialize(fieldOrProperty, classDeclaration, semanticModel, context.CancellationToken, out var assignment, out var setupAttribute) &&
4242
assignment is { Left: { } left })
4343
{
4444
if (TestFixture.TryGetTearDownMethod(setupAttribute, semanticModel, context.CancellationToken, out var tearDown))

IDisposableAnalyzers/Helpers/KnownSymbols/KnownSymbol.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,17 @@ internal static class KnownSymbol
6666
internal static readonly CompositeDisposableType CompositeDisposable = new CompositeDisposableType();
6767

6868
internal static readonly PasswordBoxType PasswordBox = new PasswordBoxType();
69+
6970
internal static readonly QualifiedType NUnitSetUpAttribute = new QualifiedType("NUnit.Framework.SetUpAttribute");
7071
internal static readonly QualifiedType NUnitTearDownAttribute = new QualifiedType("NUnit.Framework.TearDownAttribute");
7172
internal static readonly QualifiedType NUnitOneTimeSetUpAttribute = new QualifiedType("NUnit.Framework.OneTimeSetUpAttribute");
7273
internal static readonly QualifiedType NUnitOneTimeTearDownAttribute = new QualifiedType("NUnit.Framework.OneTimeTearDownAttribute");
74+
75+
internal static readonly QualifiedType TestInitializeAttribute = new QualifiedType("Microsoft.VisualStudio.TestTools.UnitTesting.TestInitializeAttribute");
76+
internal static readonly QualifiedType ClassInitializeAttribute = new QualifiedType("Microsoft.VisualStudio.TestTools.UnitTesting.ClassInitializeAttribute");
77+
internal static readonly QualifiedType TestCleanupAttribute = new QualifiedType("Microsoft.VisualStudio.TestTools.UnitTesting.TestCleanupAttribute");
78+
internal static readonly QualifiedType ClassCleanupAttribute = new QualifiedType("Microsoft.VisualStudio.TestTools.UnitTesting.ClassCleanupAttribute");
79+
7380
internal static readonly QualifiedType NinjectStandardKernel = new QualifiedType("Ninject.StandardKernel");
7481
internal static readonly QualifiedType ILoggerFactory = new QualifiedType("Microsoft.Extensions.Logging.ILoggerFactory");
7582

0 commit comments

Comments
 (0)