diff --git a/.github/workflows/github-actions-release.yml b/.github/workflows/github-actions-release.yml index 4086a24..2b9cec9 100644 --- a/.github/workflows/github-actions-release.yml +++ b/.github/workflows/github-actions-release.yml @@ -7,7 +7,7 @@ on: type: string description: The version of the library required: true - default: 1.9.1 + default: 1.9.2 VersionSuffix: type: string description: The version suffix of the library (for example rc.1) diff --git a/src/Moq.Analyzers/Moq.Analyzers.csproj b/src/Moq.Analyzers/Moq.Analyzers.csproj index 74558d2..241b850 100644 --- a/src/Moq.Analyzers/Moq.Analyzers.csproj +++ b/src/Moq.Analyzers/Moq.Analyzers.csproj @@ -17,6 +17,10 @@ https://github.com/PosInformatique/PosInformatique.Moq.Analyzers README.md + 1.9.2 + - Fix the PosInfoMoq1003 to raise warnings when using InSequence() method. + - Fix the PosInfoMoq2003 to raise errors when using InSequence() method. + 1.9.1 - Add new rules: - PosInfoMoq2009: Mock.Of<T> method must be used only to mock non-sealed class diff --git a/src/Moq.Analyzers/MoqSymbols.cs b/src/Moq.Analyzers/MoqSymbols.cs index b5ed98c..315e399 100644 --- a/src/Moq.Analyzers/MoqSymbols.cs +++ b/src/Moq.Analyzers/MoqSymbols.cs @@ -44,12 +44,15 @@ internal sealed class MoqSymbols private MoqSymbols(INamedTypeSymbol mockGenericClass, Compilation compilation) { this.mockGenericClass = mockGenericClass; + + var setupConditionResultInterface = new Lazy(() => compilation.GetTypeByMetadataName("Moq.Language.ISetupConditionResult`1")!); + this.mockBehaviorEnum = new Lazy(() => compilation.GetTypeByMetadataName("Moq.MockBehavior")!); this.isAnyTypeClass = new Lazy(() => compilation.GetTypeByMetadataName("Moq.It+IsAnyType")!); this.isAnyMethod = new Lazy(() => compilation.GetTypeByMetadataName("Moq.It")!.GetMembers("IsAny").Single()); this.verifiesInterface = new Lazy(() => compilation.GetTypeByMetadataName("Moq.Language.IVerifies")!); - this.setupMethods = new Lazy>(() => mockGenericClass.GetMembers("Setup").OfType().ToArray()); + this.setupMethods = new Lazy>(() => mockGenericClass.GetMembers("Setup").Concat(setupConditionResultInterface.Value.GetMembers("Setup")).OfType().ToArray()); this.mockBehaviorStrictField = new Lazy(() => this.mockBehaviorEnum.Value.GetMembers("Strict").First()); this.setupProtectedMethods = new Lazy>(() => compilation.GetTypeByMetadataName("Moq.Protected.IProtectedMock`1")!.GetMembers("Setup").OfType().ToArray()); this.asMethod = new Lazy(() => mockGenericClass.GetMembers("As").Single()); diff --git a/tests/Moq.Analyzers.Tests/Analyzers/CallBackDelegateMustMatchMockedMethodAnalyzerTest.cs b/tests/Moq.Analyzers.Tests/Analyzers/CallBackDelegateMustMatchMockedMethodAnalyzerTest.cs index f1a79e3..e57bf06 100644 --- a/tests/Moq.Analyzers.Tests/Analyzers/CallBackDelegateMustMatchMockedMethodAnalyzerTest.cs +++ b/tests/Moq.Analyzers.Tests/Analyzers/CallBackDelegateMustMatchMockedMethodAnalyzerTest.cs @@ -10,8 +10,10 @@ namespace PosInformatique.Moq.Analyzers.Tests public class CallBackDelegateMustMatchMockedMethodAnalyzerTest { - [Fact] - public async Task CallBackSignatureMatch_NoDiagnosticReported() + [Theory] + [InlineData("")] + [InlineData(".InSequence(sequence)")] + public async Task CallBackSignatureMatch_NoDiagnosticReported(string sequence) { var source = @" namespace ConsoleApplication1 @@ -23,31 +25,33 @@ public class TestClass { public void TestMethod() { + var sequence = new MockSequence(); + var mock1 = new Mock(); - mock1.Setup(m => m.TestMethod()) + mock1" + sequence + @".Setup(m => m.TestMethod()) .Callback(() => { }) .Throws(new Exception()); - mock1.Setup(m => m.TestMethod(default)) + mock1" + sequence + @".Setup(m => m.TestMethod(default)) .Callback((string x) => { }) .Throws(new Exception()); - mock1.Setup(m => m.TestMethod(default, default)) + mock1" + sequence + @".Setup(m => m.TestMethod(default, default)) .Callback((string x, int y) => { }) .Throws(new Exception()); - mock1.Setup(m => m.TestGenericMethod(1234)) + mock1" + sequence + @".Setup(m => m.TestGenericMethod(1234)) .Callback((int x) => { }) .Throws(new Exception()); - mock1.Setup(m => m.TestGenericMethod(It.IsAny())) + mock1" + sequence + @".Setup(m => m.TestGenericMethod(It.IsAny())) .Callback((object x) => { }) .Throws(new Exception()); - mock1.Setup(m => m.TestMethodReturn()) + mock1" + sequence + @".Setup(m => m.TestMethodReturn()) .Callback(() => { }) .Returns(1234); - mock1.Setup(m => m.TestMethodReturn(default)) + mock1" + sequence + @".Setup(m => m.TestMethodReturn(default)) .Callback((string x) => { }) .Returns(1234); - mock1.Setup(m => m.TestMethodReturn(default, default)) + mock1" + sequence + @".Setup(m => m.TestMethodReturn(default, default)) .Callback((string x, int y) => { }) .Returns(1234); @@ -80,8 +84,10 @@ public interface I await Verifier.VerifyAnalyzerAsync(source); } - [Fact] - public async Task CallBackSignatureNotMatch_DiagnosticReported() + [Theory] + [InlineData("")] + [InlineData(".InSequence(sequence)")] + public async Task CallBackSignatureNotMatch_DiagnosticReported(string sequence) { var source = @" namespace ConsoleApplication1 @@ -93,37 +99,39 @@ public class TestClass { public void TestMethod() { + var sequence = new MockSequence(); + var mock1 = new Mock(); - mock1.Setup(m => m.TestMethod()) + mock1" + sequence + @".Setup(m => m.TestMethod()) .Callback([|(int too, int much, int parameters)|] => { }) .Throws(new Exception()); - mock1.Setup(m => m.TestMethod(default)) + mock1" + sequence + @".Setup(m => m.TestMethod(default)) .Callback([|()|] => { }) .Throws(new Exception()); - mock1.Setup(m => m.TestMethod(default)) + mock1" + sequence + @".Setup(m => m.TestMethod(default)) .Callback(([|int otherType|]) => { }) .Throws(new Exception()); - mock1.Setup(m => m.TestMethod(default)) + mock1" + sequence + @".Setup(m => m.TestMethod(default)) .Callback([|(int too, int much, int parameters)|] => { }) .Throws(new Exception()); - mock1.Setup(m => m.TestGenericMethod(1234)) + mock1" + sequence + @".Setup(m => m.TestGenericMethod(1234)) .Callback(([|string x|]) => { }) .Throws(new Exception()); - mock1.Setup(m => m.TestGenericMethod(It.IsAny())) + mock1" + sequence + @".Setup(m => m.TestGenericMethod(It.IsAny())) .Callback(([|string x|]) => { }) .Throws(new Exception()); - mock1.Setup(m => m.TestMethodReturn()) + mock1" + sequence + @".Setup(m => m.TestMethodReturn()) .Callback([|(int too, int much, int parameters)|] => { }) .Returns(1234); - mock1.Setup(m => m.TestMethodReturn(default)) + mock1" + sequence + @".Setup(m => m.TestMethodReturn(default)) .Callback([|()|] => { }) .Returns(1234); - mock1.Setup(m => m.TestMethodReturn(default)) + mock1" + sequence + @".Setup(m => m.TestMethodReturn(default)) .Callback(([|int otherType|]) => { }) .Returns(1234); - mock1.Setup(m => m.TestMethodReturn(default)) + mock1" + sequence + @".Setup(m => m.TestMethodReturn(default)) .Callback([|(int too, int much, int parameters)|] => { }) .Returns(1234); } diff --git a/tests/Moq.Analyzers.Tests/Analyzers/CallBackDelegateShouldBeUsedWithItIsAnyParametersAnalyzerTest.cs b/tests/Moq.Analyzers.Tests/Analyzers/CallBackDelegateShouldBeUsedWithItIsAnyParametersAnalyzerTest.cs index 1cc7ddb..73806a0 100644 --- a/tests/Moq.Analyzers.Tests/Analyzers/CallBackDelegateShouldBeUsedWithItIsAnyParametersAnalyzerTest.cs +++ b/tests/Moq.Analyzers.Tests/Analyzers/CallBackDelegateShouldBeUsedWithItIsAnyParametersAnalyzerTest.cs @@ -57,7 +57,7 @@ await Verifier.VerifyAnalyzerAsync( } [Fact] - public async Task Callback_NoDiagnosticReported() + public async Task NoCallback_DiagnosticReported_WithSequence() { var source = @" namespace ConsoleApplication1 @@ -69,21 +69,72 @@ public class TestClass { public void TestMethod() { + var sequence = new MockSequence(); + + var mock1 = new Mock(); + mock1.InSequence(sequence).Setup(m => m.TestMethod({|#0:It.IsAny()|#0}, {|#1:It.IsAny()|#1})); + mock1.InSequence(sequence).Setup(m => m.TestMethod(""Ignored"", {|#2:It.IsAny()|#2})); + mock1.InSequence(sequence).Setup(m => m.TestMethod({|#3:It.IsAny()|#3}, 1234)); + mock1.InSequence(sequence).Setup(m => m.TestMethod({|#4:It.IsAny()|#4})); + } + } + + public interface I + { + void TestMethod(string a); + + void TestMethod(string a, int b); + } + }"; + + await Verifier.VerifyAnalyzerAsync( + source, + [ + new DiagnosticResult(CallBackDelegateShouldBeUsedWithItIsAnyParametersAnalyzer.Rule) + .WithSpan(14, 80, 14, 98).WithArguments("a"), + new DiagnosticResult(CallBackDelegateShouldBeUsedWithItIsAnyParametersAnalyzer.Rule) + .WithSpan(14, 100, 14, 115).WithArguments("b"), + new DiagnosticResult(CallBackDelegateShouldBeUsedWithItIsAnyParametersAnalyzer.Rule) + .WithSpan(15, 91, 15, 106).WithArguments("b"), + new DiagnosticResult(CallBackDelegateShouldBeUsedWithItIsAnyParametersAnalyzer.Rule) + .WithSpan(16, 80, 16, 98).WithArguments("a"), + new DiagnosticResult(CallBackDelegateShouldBeUsedWithItIsAnyParametersAnalyzer.Rule) + .WithSpan(17, 80, 17, 98).WithArguments("a"), + ]); + } + + [Theory] + [InlineData("")] + [InlineData(".InSequence(sequence)")] + public async Task Callback_NoDiagnosticReported(string sequence) + { + var source = @" + namespace ConsoleApplication1 + { + using Moq; + using System; + + public class TestClass + { + public void TestMethod() + { + var sequence = new MockSequence(); + var mock1 = new Mock(); - mock1.Setup(m => m.TestMethod(It.IsAny())) + mock1" + sequence + @".Setup(m => m.TestMethod(It.IsAny())) .Callback(() => { }); - mock1.Setup(m => m.TestMethod(It.IsAny(), It.IsAny())) + mock1" + sequence + @".Setup(m => m.TestMethod(It.IsAny(), It.IsAny())) .Callback(() => { }); - mock1.Setup(m => m.TestMethod(""OK"", It.IsAny())) + mock1" + sequence + @".Setup(m => m.TestMethod(""OK"", It.IsAny())) .Callback(() => { }); - mock1.Setup(m => m.TestMethod(It.IsAny(), 1234)) + mock1" + sequence + @".Setup(m => m.TestMethod(It.IsAny(), 1234)) .Callback(() => { }); var mock2 = new Mock(); - mock2.Setup(m => m.TestMethod()); + mock2" + sequence + @".Setup(m => m.TestMethod()); var mock3 = new Mock(); - mock3.Setup(m => m.TestMethod(""OK"", 1234)); + mock3" + sequence + @".Setup(m => m.TestMethod(""OK"", 1234)); var o = new object(); o.ToString(); // Ignored diff --git a/tests/Moq.Analyzers.Tests/Analyzers/ConstructorArgumentsMustMatchAnalyzerTest.cs b/tests/Moq.Analyzers.Tests/Analyzers/ConstructorArgumentsMustMatchAnalyzerTest.cs index 8def3be..b053a46 100644 --- a/tests/Moq.Analyzers.Tests/Analyzers/ConstructorArgumentsMustMatchAnalyzerTest.cs +++ b/tests/Moq.Analyzers.Tests/Analyzers/ConstructorArgumentsMustMatchAnalyzerTest.cs @@ -208,7 +208,6 @@ private C(int a, object b, int c, System.IDisposable d) await Verifier.VerifyAnalyzerAsync(source); } - [Theory] [InlineData("class")] [InlineData("abstract class")]