public void WeakEventHandlerProxy_RaiseEvent_Performance_Within_500x_Non_Reflection_Action()
        {
            var testIterations = 10000;

            var subscriber = new CustomEventSubscriber <EventArgs>();
            var proxy      = new WeakEventHandlerProxy <EventArgs>(new EventHandler(subscriber.HandleEvent));

            var stopwatch        = new Stopwatch();
            var proxyInvokeStart = stopwatch.ElapsedTicks;

            stopwatch.Start();
            for (var i = 0; i < testIterations; i++)
            {
                proxy.RaiseEvent(null, null);
            }
            stopwatch.Stop();
            var proxyInvokeTicks = stopwatch.ElapsedTicks - proxyInvokeStart;

            var actionInvokeStart = stopwatch.ElapsedTicks;

            stopwatch.Start();
            for (var i = 0; i < testIterations; i++)
            {
                subscriber.HandleEvent(null, null);
            }
            stopwatch.Stop();
            var actionInvokeTicks = stopwatch.ElapsedTicks - actionInvokeStart;

            Assert.IsTrue((actionInvokeTicks * 500) >= proxyInvokeTicks,
                          "Invoke through the proxy should be within 100x the ticks to avoid reflection");
        }
        public void WeakEventHandlerProxy_E2E_Signaler_Invokes_Subscriber()
        {
            var signaler = new CustomEventSignaler();

            var expectedObjectArgs = new CustomEventArgs <object>();

            var mockObjectSubscriber = new Mock <CustomEventSubscriber <CustomEventArgs <object> > >();

            mockObjectSubscriber.Setup(mock => mock.HandleEvent(signaler, expectedObjectArgs))
            .Verifiable($"{nameof(WeakEventHandlerProxy<CustomEventArgs<object>>)} should invoke subscriber when event is raised");

            var objectProxy = new WeakEventHandlerProxy <CustomEventArgs <object> >(new EventHandler <CustomEventArgs <object> >(mockObjectSubscriber.Object.HandleEvent));

            signaler.ObjectChanged += objectProxy.RaiseEvent;
            signaler.RaiseObjectChanged(expectedObjectArgs);
            mockObjectSubscriber.Verify();

            var expectedPropertyArgs   = new PropertyChangedEventArgs("Property");
            var mockPropertySubscriber = new Mock <CustomEventSubscriber <PropertyChangedEventArgs> >();

            mockPropertySubscriber.Setup(mock => mock.HandleEvent(signaler, expectedPropertyArgs))
            .Verifiable($"{nameof(WeakEventHandlerProxy<PropertyChangedEventArgs>)} should invoke subscriber when event is raised");
            var propertyProxy = new WeakEventHandlerProxy <PropertyChangedEventArgs>(new EventHandler <PropertyChangedEventArgs>(mockPropertySubscriber.Object.HandleEvent));

            signaler.PropertyChanged += propertyProxy.RaiseEvent;
            signaler.RaisePropertyChanged(expectedPropertyArgs);
            mockPropertySubscriber.Verify();
        }
        public async Task WeakEventHandler_Maintains_Weak_Reference_For_Target()
        {
            WeakEventHandlerProxy <EventArgs> proxy;

            {
                var subscriber = new CustomEventSubscriber <object>();
                proxy = new WeakEventHandlerProxy <EventArgs>((Action <object, EventArgs>)subscriber.HandleEvent);
                Assert.AreEqual(subscriber, proxy.Target, "Proxy Target should be the event subscriber");
                subscriber = null;
            }

            var waitCount        = 0;
            var maximumWaitCount = 10;

            while (waitCount < maximumWaitCount)
            {
                waitCount++;

                if (proxy.Target == null)
                {
                    break;
                }

                await Task.Delay(10);

                GC.Collect();
                GC.WaitForPendingFinalizers();
                GC.Collect();
            }

            Assert.IsNull(proxy.Target, "Proxy Target should be garbage collected, proving it is a weak reference.");
            Assert.DoesNotThrow(() =>
                                proxy.RaiseEvent(null, null),
                                "RaiseEvent should ignore the event if the target has been collected");
        }
        public void WeakEventHandler_Handles_Generic_EventHandler()
        {
            var testHandler = new EventHandler <CustomEventArgs <object> >(CustomEventArgsHandler);

            Assert.AreEqual(this, testHandler.Target);

            var testProxy = new WeakEventHandlerProxy <CustomEventArgs <object> >(testHandler);

            Assert.AreEqual(this, testProxy.Target);
        }
        public void WeakEventHandlerProxy_Invokes_EventHandler_With_Null_Target()
        {
            var testHandler = new EventHandler(NoCaptureHandler);

            Assert.IsNull(testHandler.Target, $"{nameof(EventHandler)} under test should have a null {nameof(EventHandler.Target)}");
            var testProxy = new WeakEventHandlerProxy <EventArgs>(testHandler);

            Assert.IsNull(testProxy.Target, $"{nameof(WeakEventHandlerProxy<EventArgs>)} should store null Target");
            Assert.DoesNotThrow(() =>
                                testProxy.RaiseEvent(new object(), new EventArgs()));
        }
        public void WeakEventHandlerProxy_RaiseEvent_Invokes_Given_EventHandler()
        {
            var handlerInvoked = false;
            var testHandler    = new EventHandler((sender, e) =>
            {
                handlerInvoked = true;
            });

            var testProxy = new WeakEventHandlerProxy <EventArgs>(testHandler);

            Assert.AreEqual(testHandler.Target, testProxy.Target);
            testProxy.RaiseEvent(new object(), new EventArgs());

            Assert.IsTrue(handlerInvoked, $"{nameof(WeakEventHandlerProxy<EventArgs>.RaiseEvent)} should invoke EventHandler from constructor");
        }
        public void WeakEventHandlerProxy_RaiseEvent_Invokes_With_Given_Sender_And_EventArgs()
        {
            var expectedSender = new object();
            var expectedArgs   = new EventArgs();

            var handlerInvoked = false;
            var testHandler    = new EventHandler((sender, args) =>
            {
                handlerInvoked = true;
                Assert.AreEqual(expectedArgs, args);
                Assert.AreEqual(expectedSender, sender);
            });

            var testProxy = new WeakEventHandlerProxy <EventArgs>(testHandler);

            testProxy.RaiseEvent(expectedSender, expectedArgs);

            Assert.IsTrue(handlerInvoked, $"{nameof(WeakEventHandlerProxy<EventArgs>.RaiseEvent)} should invoke EventHandler with given sender and EventArgs");
        }
        public void WeakEventHandler_RaiseEvent_Invokes_With_Generic_EventArgs()
        {
            var expectedSender = new object();
            var expectedArgs   = new CustomEventArgs <object>
            {
                EventData = new object()
            };

            var handlerCalled = false;
            var testProxy     = new WeakEventHandlerProxy <CustomEventArgs <object> >((Action <object, CustomEventArgs <object> >)((sender, e) =>
            {
                handlerCalled = true;
                Assert.AreEqual(expectedSender, sender);
                Assert.AreEqual(expectedArgs, e);
            }));

            testProxy.RaiseEvent(expectedSender, expectedArgs);

            Assert.IsTrue(handlerCalled);
        }