Skip to content

Commit

Permalink
[GH-12] - fixes for VB implementation, additional test cases for VB
Browse files Browse the repository at this point in the history
  • Loading branch information
tpodolak committed Jul 13, 2018
1 parent d631f17 commit ccc10d9
Show file tree
Hide file tree
Showing 9 changed files with 1,494 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ internal abstract class AbstractReEntrantCallFinder
{
[MetadataNames.NSubstituteReturnsMethod] = MetadataNames.NSubstituteSubstituteExtensionsFullTypeName,
[MetadataNames.NSubstituteReturnsForAnyArgsMethod] = MetadataNames.NSubstituteSubstituteExtensionsFullTypeName,
[MetadataNames.NSubstituteDoMethod] = "NSubstitute.Core.WhenCalled<T>"
[MetadataNames.NSubstituteDoMethod] = MetadataNames.NSubstituteWhenCalledType
}.ToImmutableDictionary();

public ImmutableList<ISymbol> GetReEntrantCalls(SemanticModel semanticModel, SyntaxNode rootNode)
Expand Down Expand Up @@ -53,7 +53,8 @@ protected bool IsReturnsLikeMethod(SemanticModel semanticModel, ISymbol symbol)
}

return symbol.ContainingAssembly?.Name.Equals(MetadataNames.NSubstituteAssemblyName, StringComparison.OrdinalIgnoreCase) == true &&
symbol.ContainingType?.ConstructedFrom.ToString().Equals(containingType, StringComparison.OrdinalIgnoreCase) == true;
(symbol.ContainingType?.ToString().Equals(containingType, StringComparison.OrdinalIgnoreCase) == true ||
(symbol.ContainingType?.ConstructedFrom.Name)?.Equals(containingType, StringComparison.OrdinalIgnoreCase) == true);
}

private static bool IsCalledViaDelegate(SemanticModel semanticModel, TypeInfo typeInfo)
Expand Down
1 change: 1 addition & 0 deletions src/NSubstitute.Analyzers.Shared/MetadataNames.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ internal class MetadataNames
public const string CastleDynamicProxyGenAssembly2Name = "DynamicProxyGenAssembly2";
public const string NSubstituteWhenMethod = "When";
public const string NSubstituteWhenForAnyArgsMethod = "WhenForAnyArgs";
public const string NSubstituteWhenCalledType = "WhenCalled";
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,28 @@
using Microsoft.CodeAnalysis.Diagnostics;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Diagnostics;
using NSubstitute.Analyzers.Tests.Shared.DiagnosticAnalyzers;
using NSubstitute.Analyzers.VisualBasic.DiagnosticAnalyzers;

namespace NSubstitute.Analyzers.Tests.VisualBasic.DiagnosticAnalyzersTests.ReEntrantReturnsSetupAnalyzerTests
{
public abstract class ReEntrantReturnsSetupDiagnosticVerifier : VisualBasicDiagnosticVerifier
public abstract class ReEntrantReturnsSetupDiagnosticVerifier : VisualBasicDiagnosticVerifier, IReEntrantReturnsSetupDiagnosticVerifier
{
public abstract Task ReturnsDiagnostic_WhenUsingReEntrantReturnsViaMethodCall(string reEntrantCall);

public abstract Task ReturnsDiagnostic_WhenUsingReEntrantReturnsForAnyArgsViaMethodCall(string reEntrantCall);

public abstract Task ReturnsDiagnostic_WhenUsingReEntrantWhenDo(string reEntrantCall);

public abstract Task ReturnsDiagnostic_ForNestedReEntrantCall();

public abstract Task ReturnsDiagnostic_ForSpecificNestedReEntrantCall();

public abstract Task ReturnsNoDiagnostic_WhenRootCallCalledWithDelegate_AndReEntrantReturnsCallExists(string rootCall, string reEntrantCall);

public abstract Task ReturnsNoDiagnostic_WhenRootCallCalledWithDelegate_AndReEntrantReturnsForAnyArgsCallExists(string rootCall, string reEntrantCall);

public abstract Task ReturnsNoDiagnostic_WhenReEntrantSubstituteNotUsed(string firstReturn, string secondReturn);

protected override DiagnosticAnalyzer GetDiagnosticAnalyzer()
{
return new ReEntrantSetupAnalyzer();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public class ReturnsAsExtensionMethodTests : ReEntrantReturnsSetupDiagnosticVeri
[InlineData("substitute.Foo().Returns(1) \n\rOtherReturn()")]
[InlineData("SubstituteExtensions.Returns(substitute.Foo(), 1)")]
[InlineData("SubstituteExtensions.Returns(Of Integer)(substitute.Foo(), 1)")]
public async Task ReturnsDiagnostic_WhenUsingReEntrantReturnsViaMethodCall(string reEntrantCall)
public override async Task ReturnsDiagnostic_WhenUsingReEntrantReturnsViaMethodCall(string reEntrantCall)
{
var source = $@"Imports NSubstitute
Expand Down Expand Up @@ -77,7 +77,7 @@ End Namespace
[InlineData("OtherReturn()\r\n substitute.Foo().ReturnsForAnyArgs(1)")]
[InlineData("SubstituteExtensions.ReturnsForAnyArgs(substitute.Foo(), 1)")]
[InlineData("SubstituteExtensions.ReturnsForAnyArgs(Of Integer)(substitute.Foo(), 1)")]
public async Task ReturnsDiagnostic_WhenUsingReEntrantReturnsForAnyArgsViaMethodCall(string reEntrantCall)
public override async Task ReturnsDiagnostic_WhenUsingReEntrantReturnsForAnyArgsViaMethodCall(string reEntrantCall)
{
var source = $@"Imports NSubstitute
Expand Down Expand Up @@ -136,8 +136,69 @@ End Namespace
await VerifyDiagnostic(source, firstArgumentDiagnostic, secondArgumentDiagnostic);
}

[Theory]
[InlineData("substitute.[When](Function(x) x.Foo()).[Do](Function(callInfo) 1)")]
[InlineData("OtherReturn() \r\n substitute.[When](Function(x) x.Foo()).[Do](Function(callInfo) 1)")]
public override async Task ReturnsDiagnostic_WhenUsingReEntrantWhenDo(string reEntrantCall)
{
var source = $@"Imports NSubstitute
Namespace MyNamespace
Interface IFoo
Function Bar() As Integer
End Interface
Interface IBar
Function Foo() As Integer
End Interface
Public Class FooTests
Public Sub Test()
Dim substitute = NSubstitute.Substitute.[For](Of IFoo)()
substitute.Bar().Returns(ReturnThis(), OtherReturn())
End Sub
Private Function ReturnThis() As Integer
Return OtherReturn()
End Function
Private Function OtherReturn() As Integer
Dim substitute = NSubstitute.Substitute.[For](Of IBar)()
{reEntrantCall}
Return 1
End Function
End Class
End Namespace
";
var firstArgumentDiagnostic = new DiagnosticResult
{
Id = DiagnosticIdentifiers.ReEntrantSubstituteCall,
Severity = DiagnosticSeverity.Warning,
Message =
"Returns() is set with a method that itself calls Do. This can cause problems with NSubstitute. Consider replacing with a lambda: Returns(Function(x) ReturnThis()).",
Locations = new[]
{
new DiagnosticResultLocation(15, 38)
}
};

var secondArgumentDiagnostic = new DiagnosticResult
{
Id = DiagnosticIdentifiers.ReEntrantSubstituteCall,
Severity = DiagnosticSeverity.Warning,
Message =
"Returns() is set with a method that itself calls Do. This can cause problems with NSubstitute. Consider replacing with a lambda: Returns(Function(x) OtherReturn()).",
Locations = new[]
{
new DiagnosticResultLocation(15, 52)
}
};

await VerifyDiagnostic(source, firstArgumentDiagnostic, secondArgumentDiagnostic);
}

[Fact]
public async Task ReturnsDiagnostic_ForNestedReEntrantCall()
public override async Task ReturnsDiagnostic_ForNestedReEntrantCall()
{
var source = @"Imports NSubstitute
Expand Down Expand Up @@ -218,12 +279,196 @@ End Namespace
await VerifyDiagnostic(source, firstArgumentDiagnostic, secondArgumentDiagnostic, nestedArgumentDiagnostic);
}

[Fact]
public override async Task ReturnsDiagnostic_ForSpecificNestedReEntrantCall()
{
var source = @"Imports NSubstitute
Namespace MyNamespace
Interface IFoo
Function Bar() As Integer
End Interface
Interface IBar
Function Foo() As Integer
End Interface
Public Class FooTests
Public Sub Test()
Dim substitute = NSubstitute.Substitute.[For](Of IFoo)()
substitute.Bar().Returns(Function(x) ReturnThis())
End Sub
Private Function ReturnThis() As Integer
Return OtherReturn()
End Function
Private Function OtherReturn() As Integer
Dim substitute = NSubstitute.Substitute.[For](Of IBar)()
substitute.Foo().Returns(NestedReturnThis())
Return 1
End Function
Private Function NestedReturnThis() As Integer
Return OtherNestedReturnThis()
End Function
Private Function OtherNestedReturnThis() As Integer
Dim [sub] = Substitute.[For](Of IBar)()
[sub].Foo().Returns(1)
Return 1
End Function
End Class
End Namespace
";

var firstArgumentDiagnostic = new DiagnosticResult
{
Id = DiagnosticIdentifiers.ReEntrantSubstituteCall,
Severity = DiagnosticSeverity.Warning,
Message =
"Returns() is set with a method that itself calls Returns. This can cause problems with NSubstitute. Consider replacing with a lambda: Returns(Function(x) NestedReturnThis()).",
Locations = new[]
{
new DiagnosticResultLocation(24, 38)
}
};

await VerifyDiagnostic(source, firstArgumentDiagnostic);
}

[Theory]
[InlineData("MyMethod()", "substitute.Foo().Returns(1)")]
[InlineData("MyProperty", "substitute.Foo().Returns(1)")]
[InlineData("Function(x) ReturnThis()", "substitute.Foo().Returns(1)")]
[InlineData("Function(x) \r\n ReturnThis() \r\n End Function", "substitute.Foo().Returns(1)")]
[InlineData("MyMethod()", "SubstituteExtensions.Returns(substitute.Foo(), 1)")]
[InlineData("MyProperty", "SubstituteExtensions.Returns(substitute.Foo(), 1)")]
[InlineData("Function(x) ReturnThis()", "SubstituteExtensions.Returns(substitute.Foo(), 1)")]
[InlineData("Function(x) \r\n ReturnThis() \r\n End Function", "SubstituteExtensions.Returns(substitute.Foo(), 1)")]
[InlineData("MyMethod()", "SubstituteExtensions.Returns(Of Integer)(substitute.Foo(), 1)")]
[InlineData("MyProperty", "SubstituteExtensions.Returns(Of Integer)(substitute.Foo(), 1)")]
[InlineData("Function(x) ReturnThis()", "SubstituteExtensions.Returns(Of Integer)(substitute.Foo(), 1)")]
[InlineData("Function(x) \r\n ReturnThis() \r\n End Function", "SubstituteExtensions.Returns(Of Integer)(substitute.Foo(), 1)")]
public override async Task ReturnsNoDiagnostic_WhenRootCallCalledWithDelegate_AndReEntrantReturnsCallExists(string rootCall, string reEntrantCall)
{
var source = $@"Imports NSubstitute
Imports NSubstitute.Core
Imports System
Namespace MyNamespace
Interface IFoo
Function Bar() As Integer
End Interface
Interface IBar
Function Foo() As Integer
End Interface
Public Class FooTests
Public Sub Test()
Dim substitute = NSubstitute.Substitute.[For](Of IFoo)()
substitute.Bar().Returns({rootCall})
End Sub
Private Function ReturnThis() As Integer
Return OtherReturn()
End Function
Private Function OtherReturn() As Integer
Dim substitute = NSubstitute.Substitute.[For](Of IBar)()
{reEntrantCall}
Return 1
End Function
Private Function ReturnThisWithCallInfo(ByVal info As CallInfo) As Integer
Return OtherReturn()
End Function
Private Function MyMethod() As Func(Of CallInfo, Integer)
Return AddressOf ReturnThisWithCallInfo
End Function
Private ReadOnly Property MyProperty As Func(Of CallInfo, Integer)
Get
Return AddressOf ReturnThisWithCallInfo
End Get
End Property
End Class
End Namespace
";
await VerifyDiagnostic(source);
}

[Theory]
[InlineData("MyMethod()", "substitute.Foo().ReturnsForAnyArgs(1)")]
[InlineData("MyProperty", "substitute.Foo().ReturnsForAnyArgs(1)")]
[InlineData("Function(x) ReturnThis()", "substitute.Foo().ReturnsForAnyArgs(1)")]
[InlineData("Function(x) \r\n ReturnThis() \r\n End Function", "substitute.Foo().ReturnsForAnyArgs(1)")]
[InlineData("MyMethod()", "SubstituteExtensions.ReturnsForAnyArgs(substitute.Foo(), 1)")]
[InlineData("MyProperty", "SubstituteExtensions.ReturnsForAnyArgs(substitute.Foo(), 1)")]
[InlineData("Function(x) ReturnThis()", "SubstituteExtensions.ReturnsForAnyArgs(substitute.Foo(), 1)")]
[InlineData("Function(x) \r\n ReturnThis() \r\n End Function", "SubstituteExtensions.ReturnsForAnyArgs(substitute.Foo(), 1)")]
[InlineData("MyMethod()", "SubstituteExtensions.ReturnsForAnyArgs(Of Integer)(substitute.Foo(), 1)")]
[InlineData("MyProperty", "SubstituteExtensions.ReturnsForAnyArgs(Of Integer)(substitute.Foo(), 1)")]
[InlineData("Function(x) ReturnThis()", "SubstituteExtensions.ReturnsForAnyArgs(Of Integer)(substitute.Foo(), 1)")]
[InlineData("Function(x) \r\n ReturnThis() \r\n End Function", "SubstituteExtensions.ReturnsForAnyArgs(Of Integer)(substitute.Foo(), 1)")]
public override async Task ReturnsNoDiagnostic_WhenRootCallCalledWithDelegate_AndReEntrantReturnsForAnyArgsCallExists(string rootCall, string reEntrantCall)
{
var source = $@"Imports NSubstitute
Imports NSubstitute.Core
Imports System
Namespace MyNamespace
Interface IFoo
Function Bar() As Integer
End Interface
Interface IBar
Function Foo() As Integer
End Interface
Public Class FooTests
Public Sub Test()
Dim substitute = NSubstitute.Substitute.[For](Of IFoo)()
substitute.Bar().Returns({rootCall})
End Sub
Private Function ReturnThis() As Integer
Return OtherReturn()
End Function
Private Function OtherReturn() As Integer
Dim substitute = NSubstitute.Substitute.[For](Of IBar)()
{reEntrantCall}
Return 1
End Function
Private Function ReturnThisWithCallInfo(ByVal info As CallInfo) As Integer
Return OtherReturn()
End Function
Private Function MyMethod() As Func(Of CallInfo, Integer)
Return AddressOf ReturnThisWithCallInfo
End Function
Private ReadOnly Property MyProperty As Func(Of CallInfo, Integer)
Get
Return AddressOf ReturnThisWithCallInfo
End Get
End Property
End Class
End Namespace
";
await VerifyDiagnostic(source);
}

[Theory]
[InlineData("ReturnThis()", "OtherReturn()")]
[InlineData("ReturnThis", "OtherReturn")]
[InlineData("1", "2")]
[InlineData("Function(x) 1", "Function(x) 2")]
public async Task ReturnsNoDiagnostic_WhenReEntrantSubstituteNotUsed(string firstReturn, string secondReturn)
public override async Task ReturnsNoDiagnostic_WhenReEntrantSubstituteNotUsed(string firstReturn, string secondReturn)
{
var source = $@"Imports NSubstitute
Imports NSubstitute.Core
Expand Down
Loading

0 comments on commit ccc10d9

Please sign in to comment.