From 4bd429ed1f0cfc59ef1fe657fd025a184a8b21df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mihnea=20R=C4=83dulescu?= <> Date: Tue, 28 May 2024 20:08:34 +0300 Subject: [PATCH] Updated event raising logic to only allow internal default constructors --- .../Core/Events/RaiseEventWrapper.cs | 11 +++++- .../EventRaising.cs | 36 ++++++++++++++----- 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/src/NSubstitute/Core/Events/RaiseEventWrapper.cs b/src/NSubstitute/Core/Events/RaiseEventWrapper.cs index 084efa01..bd3aa8da 100644 --- a/src/NSubstitute/Core/Events/RaiseEventWrapper.cs +++ b/src/NSubstitute/Core/Events/RaiseEventWrapper.cs @@ -12,7 +12,7 @@ protected EventArgs GetDefaultForEventArgType(Type type) { if (type == typeof(EventArgs)) return EventArgs.Empty; - var defaultConstructor = GetPublicDefaultConstructor(type) ?? GetNonPublicDefaultConstructor(type); + var defaultConstructor = GetPublicDefaultConstructor(type) ?? GetInternalDefaultConstructor(type); if (defaultConstructor is null) { var message = string.Format( @@ -24,10 +24,19 @@ protected EventArgs GetDefaultForEventArgType(Type type) return (EventArgs)defaultConstructor.Invoke([]); } + private static ConstructorInfo? GetInternalDefaultConstructor(Type type) + { + var nonPublicDefaultConstructor = GetNonPublicDefaultConstructor(type); + var isInternalDefaultConstructor = nonPublicDefaultConstructor?.IsAssembly == true; + return isInternalDefaultConstructor ? nonPublicDefaultConstructor : null; + } + private static ConstructorInfo? GetPublicDefaultConstructor(Type type) => GetDefaultConstructor(type, BindingFlags.Public); + private static ConstructorInfo? GetNonPublicDefaultConstructor(Type type) => GetDefaultConstructor(type, BindingFlags.NonPublic); + private static ConstructorInfo? GetDefaultConstructor(Type type, BindingFlags bindingFlags) => type.GetConstructor( BindingFlags.Instance | BindingFlags.ExactBinding | bindingFlags, null, Type.EmptyTypes, null); diff --git a/tests/NSubstitute.Acceptance.Specs/EventRaising.cs b/tests/NSubstitute.Acceptance.Specs/EventRaising.cs index d0000c39..d4eee84a 100644 --- a/tests/NSubstitute.Acceptance.Specs/EventRaising.cs +++ b/tests/NSubstitute.Acceptance.Specs/EventRaising.cs @@ -299,17 +299,28 @@ public void Raise_custom_event_that_has_sender_and_args_but_does_not_inherit_fro } [Test] - public void MyEvent_With_CustomEventArgsWithNonPublicDefaultConstructor_IsRaised() + public void MyEvent_with_CustomEventArgsWithInternalDefaultConstructor_is_raised() { // Arrange - IExample mockExample = Substitute.For(); - var consumer = new Consumer(mockExample); + var exampleInternalMock = Substitute.For(); + var consumerInternal = new ConsumerInternal(exampleInternalMock); // Act - mockExample.MyEvent += Raise.EventWith(this, null!); + exampleInternalMock.MyEvent += Raise.EventWith(this, null!); // Assert - Assert.That(consumer.SomethingWasDone); + Assert.That(consumerInternal.SomethingWasDone); + } + + [Test] + public void MyEvent_with_CustomEventArgsWithPrivateDefaultConstructor_throws_CannotCreateEventArgsException() + { + // Arrange + var examplePrivateMock = Substitute.For(); + + // Act and Assert + Assert.Throws(() => + examplePrivateMock.MyEvent += Raise.EventWith(this, null!)); } class RaisedEventRecorder @@ -358,13 +369,13 @@ public class CustomEventArgsWithInternalDefaultConstructor : EventArgs { internal CustomEventArgsWithInternalDefaultConstructor() { } } - public interface IExample + public interface IExampleInternal { public event EventHandler MyEvent; } - public class Consumer + public class ConsumerInternal { - public Consumer(IExample example) + public ConsumerInternal(IExampleInternal example) { example.MyEvent += OnMyEvent; } @@ -378,4 +389,13 @@ private void DoSomething() SomethingWasDone = true; } } + + public class CustomEventArgsWithPrivateDefaultConstructor : EventArgs + { + private CustomEventArgsWithPrivateDefaultConstructor() { } + } + public interface IExamplePrivate + { + public event EventHandler MyEvent; + } } \ No newline at end of file