Skip to content

Commit

Permalink
Merge pull request #208 from nsubstitute/GH-207-received-events
Browse files Browse the repository at this point in the history
GH-207 - detect non virtual received checks for events subscription
  • Loading branch information
tpodolak authored Jul 30, 2023
2 parents 3edadcb + 98fd1c5 commit 8e35c28
Show file tree
Hide file tree
Showing 8 changed files with 369 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,11 @@ public static IEnumerable<IOperation> Ancestors(this IOperation operation)
{
var symbol = operation switch
{
IMemberReferenceOperation memberReferenceOperation => memberReferenceOperation.Member,
IInvocationOperation invocationOperation => invocationOperation.TargetMethod,
IPropertyReferenceOperation propertyReferenceOperation => propertyReferenceOperation.Property,
IConversionOperation conversionOperation => ExtractSymbol(conversionOperation.Operand),
IAwaitOperation awaitOperation => ExtractSymbol(awaitOperation.Operation),
ILocalReferenceOperation localReferenceOperation => localReferenceOperation.Local,
IFieldReferenceOperation fieldReferenceOperation => fieldReferenceOperation.Field,
_ => null
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,22 @@ public abstract class NonSubstitutableMemberReceivedDiagnosticVerifier : CSharpD
[InlineData]
public abstract Task ReportsDiagnostics_WhenCheckingReceivedCallsForNonVirtualIndexer(string method);

[CombinatoryTheory]
[InlineData]
public abstract Task ReportsDiagnostics_WhenCheckingReceivedCallsForNonVirtualEvent(string method);

[CombinatoryTheory]
[InlineData]
public abstract Task ReportsNoDiagnostics_WhenCheckingReceivedCallsForAbstractEvent(string method);

[CombinatoryTheory]
[InlineData]
public abstract Task ReportsNoDiagnostics_WhenCheckingReceivedCallsForVirtualEvent(string method);

[CombinatoryTheory]
[InlineData]
public abstract Task ReportsNoDiagnostics_WhenCheckingReceivedCallsForInterfaceEvent(string method);

[CombinatoryTheory]
[InlineData(".Bar", "Internal member Bar can not be intercepted without InternalsVisibleToAttribute.")]
[InlineData(".FooBar()", "Internal member FooBar can not be intercepted without InternalsVisibleToAttribute.")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,110 @@ public void Test()
await VerifyDiagnostic(source, NonVirtualReceivedSetupSpecificationDescriptor, "Member this[] can not be intercepted. Only interface members and virtual, overriding, and abstract members can be intercepted.");
}

public override async Task ReportsDiagnostics_WhenCheckingReceivedCallsForNonVirtualEvent(string method)
{
var source = $@"using NSubstitute;
using NSubstitute.ReceivedExtensions;
using System;
namespace MyNamespace
{{
public class Foo
{{
public event Action Event;
}}
public class FooTests
{{
public void Test()
{{
var substitute = NSubstitute.Substitute.For<Foo>();
[|substitute.{method}.Event|] += () => {{ }};
}}
}}
}}";

await VerifyDiagnostic(source, NonVirtualReceivedSetupSpecificationDescriptor, "Member Event can not be intercepted. Only interface members and virtual, overriding, and abstract members can be intercepted.");
}

public override async Task ReportsNoDiagnostics_WhenCheckingReceivedCallsForAbstractEvent(string method)
{
var source = $@"using NSubstitute;
using NSubstitute.ReceivedExtensions;
using System;
namespace MyNamespace
{{
public abstract class Foo
{{
public abstract event Action Event;
}}
public class FooTests
{{
public void Test()
{{
var substitute = NSubstitute.Substitute.For<Foo>();
substitute.{method}.Event += () => {{ }};
}}
}}
}}";

await VerifyNoDiagnostic(source);
}

public override async Task ReportsNoDiagnostics_WhenCheckingReceivedCallsForVirtualEvent(string method)
{
var source = $@"using NSubstitute;
using NSubstitute.ReceivedExtensions;
using System;
namespace MyNamespace
{{
public class Foo
{{
public virtual event Action Event;
}}
public class FooTests
{{
public void Test()
{{
var substitute = NSubstitute.Substitute.For<Foo>();
substitute.{method}.Event += () => {{ }};
}}
}}
}}";

await VerifyNoDiagnostic(source);
}

public override async Task ReportsNoDiagnostics_WhenCheckingReceivedCallsForInterfaceEvent(string method)
{
var source = $@"using NSubstitute;
using NSubstitute.ReceivedExtensions;
using System;
namespace MyNamespace
{{
public interface Foo
{{
event Action Event;
}}
public class FooTests
{{
public void Test()
{{
var substitute = NSubstitute.Substitute.For<Foo>();
substitute.{method}.Event += () => {{ }};
}}
}}
}}";

await VerifyNoDiagnostic(source);
}

[CombinatoryData(
"Received(Quantity.None())",
"Received<Foo>(Quantity.None())",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,110 @@ public void Test()
await VerifyDiagnostic(source, NonVirtualReceivedSetupSpecificationDescriptor, "Member this[] can not be intercepted. Only interface members and virtual, overriding, and abstract members can be intercepted.");
}

public override async Task ReportsDiagnostics_WhenCheckingReceivedCallsForNonVirtualEvent(string method)
{
var source = $@"using NSubstitute;
using NSubstitute.ReceivedExtensions;
using System;
namespace MyNamespace
{{
public class Foo
{{
public event Action Event;
}}
public class FooTests
{{
public void Test()
{{
var substitute = NSubstitute.Substitute.For<Foo>();
[|{method}.Event|] += () => {{ }};
}}
}}
}}";

await VerifyDiagnostic(source, NonVirtualReceivedSetupSpecificationDescriptor, "Member Event can not be intercepted. Only interface members and virtual, overriding, and abstract members can be intercepted.");
}

public override async Task ReportsNoDiagnostics_WhenCheckingReceivedCallsForAbstractEvent(string method)
{
var source = $@"using NSubstitute;
using NSubstitute.ReceivedExtensions;
using System;
namespace MyNamespace
{{
public abstract class Foo
{{
public abstract event Action Event;
}}
public class FooTests
{{
public void Test()
{{
var substitute = NSubstitute.Substitute.For<Foo>();
{method}.Event += () => {{ }};
}}
}}
}}";

await VerifyNoDiagnostic(source);
}

public override async Task ReportsNoDiagnostics_WhenCheckingReceivedCallsForVirtualEvent(string method)
{
var source = $@"using NSubstitute;
using NSubstitute.ReceivedExtensions;
using System;
namespace MyNamespace
{{
public class Foo
{{
public virtual event Action Event;
}}
public class FooTests
{{
public void Test()
{{
var substitute = NSubstitute.Substitute.For<Foo>();
{method}.Event += () => {{ }};
}}
}}
}}";

await VerifyNoDiagnostic(source);
}

public override async Task ReportsNoDiagnostics_WhenCheckingReceivedCallsForInterfaceEvent(string method)
{
var source = $@"using NSubstitute;
using NSubstitute.ReceivedExtensions;
using System;
namespace MyNamespace
{{
public interface Foo
{{
event Action Event;
}}
public class FooTests
{{
public void Test()
{{
var substitute = NSubstitute.Substitute.For<Foo>();
{method}.Event += () => {{ }};
}}
}}
}}";

await VerifyNoDiagnostic(source);
}

[CombinatoryData(
"ReceivedExtensions.Received(substitute, Quantity.None())",
"ReceivedExtensions.Received(substitute: substitute, x: Quantity.None())",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ public interface INonSubstitutableMemberReceivedDiagnosticVerifier

Task ReportsDiagnostics_WhenCheckingReceivedCallsForNonVirtualIndexer(string method);

Task ReportsDiagnostics_WhenCheckingReceivedCallsForNonVirtualEvent(string method);

Task ReportsNoDiagnostics_WhenCheckingReceivedCallsForAbstractEvent(string method);

Task ReportsNoDiagnostics_WhenCheckingReceivedCallsForVirtualEvent(string method);

Task ReportsNoDiagnostics_WhenCheckingReceivedCallsForInterfaceEvent(string method);

Task ReportsDiagnostics_WhenSettingValueForInternalVirtualMember_AndInternalsVisibleToNotApplied(string method, string call, string message);

Task ReportsNoDiagnostics_WhenSettingValueForInternalVirtualMember_AndInternalsVisibleToApplied(string method, string call);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,22 @@ public abstract class NonSubstitutableMemberReceivedDiagnosticVerifier : VisualB
[InlineData]
public abstract Task ReportsDiagnostics_WhenCheckingReceivedCallsForNonVirtualIndexer(string method);

[CombinatoryTheory]
[InlineData]
public abstract Task ReportsDiagnostics_WhenCheckingReceivedCallsForNonVirtualEvent(string method);

[CombinatoryTheory(Skip = "VisualBasic does not allow to mark events as abstract (MustInherit)")]
[InlineData]
public abstract Task ReportsNoDiagnostics_WhenCheckingReceivedCallsForAbstractEvent(string method);

[CombinatoryTheory(Skip = "VisualBasic does not allow to mark events as virtual (Overridable)")]
[InlineData]
public abstract Task ReportsNoDiagnostics_WhenCheckingReceivedCallsForVirtualEvent(string method);

[CombinatoryTheory]
[InlineData]
public abstract Task ReportsNoDiagnostics_WhenCheckingReceivedCallsForInterfaceEvent(string method);

[CombinatoryTheory]
[InlineData(".Bar", "Friend member Bar can not be intercepted without InternalsVisibleToAttribute.")]
[InlineData(".FooBar()", "Friend member FooBar can not be intercepted without InternalsVisibleToAttribute.")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,66 @@ End Class
await VerifyDiagnostic(source, NonVirtualReceivedSetupSpecificationDescriptor, "Member Item can not be intercepted. Only interface members and overrideable, overriding, and must override members can be intercepted.");
}

public override async Task ReportsDiagnostics_WhenCheckingReceivedCallsForNonVirtualEvent(string method)
{
var source = $@"Imports NSubstitute
Imports NSubstitute.ReceivedExtensions
Imports System
Namespace MyNamespace
Public Class Foo
Public Event SomeEvent As Action
End Class
Public Class FooTests
Public Sub Test()
Dim substitute = NSubstitute.Substitute.[For](Of Foo)()
AddHandler [|substitute.{method}.SomeEvent|], Sub()
End Sub
End Sub
End Class
End Namespace
";

await VerifyDiagnostic(source, NonVirtualReceivedSetupSpecificationDescriptor, "Member SomeEvent can not be intercepted. Only interface members and overrideable, overriding, and must override members can be intercepted.");
}

public override Task ReportsNoDiagnostics_WhenCheckingReceivedCallsForAbstractEvent(string method)
{
// VisualBasic does not allow to mark events as abstract (MustInherit)
return Task.CompletedTask;
}

public override Task ReportsNoDiagnostics_WhenCheckingReceivedCallsForVirtualEvent(string method)
{
// VisualBasic does not allow to mark events as virtual (Overridable)
return Task.CompletedTask;
}

public override async Task ReportsNoDiagnostics_WhenCheckingReceivedCallsForInterfaceEvent(string method)
{
var source = $@"Imports NSubstitute
Imports NSubstitute.ReceivedExtensions
Imports System
Namespace MyNamespace
Public Interface Foo
Event SomeEvent As Action
End Interface
Public Class FooTests
Public Sub Test()
Dim substitute = NSubstitute.Substitute.[For](Of Foo)()
AddHandler substitute.{method}.SomeEvent, Sub()
End Sub
End Sub
End Class
End Namespace
";

await VerifyNoDiagnostic(source);
}

[CombinatoryData(
"Received(Quantity.None())",
"Received(1, 1)",
Expand Down
Loading

0 comments on commit 8e35c28

Please sign in to comment.