From 821ad5570a48287fc3addf54e7d75c8177e92704 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 6 Oct 2025 02:15:13 +0000 Subject: [PATCH 1/8] Initial plan From fe74fb148dab06a6badfe18c99ac42a149457367 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 6 Oct 2025 02:21:13 +0000 Subject: [PATCH 2/8] Initial exploration and planning Co-authored-by: drewnoakes <350947+drewnoakes@users.noreply.github.com> --- NuGet.Config | 3 +++ 1 file changed, 3 insertions(+) diff --git a/NuGet.Config b/NuGet.Config index aad3952a3..271cba873 100644 --- a/NuGet.Config +++ b/NuGet.Config @@ -4,4 +4,7 @@ + + + \ No newline at end of file From f1757e8a1b2a9419e4fd6fc5e4a1afe6ef21163c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 6 Oct 2025 02:22:15 +0000 Subject: [PATCH 3/8] Add INetMQMonitor interface and update NetMQMonitor to implement it Co-authored-by: drewnoakes <350947+drewnoakes@users.noreply.github.com> --- src/NetMQ/Monitoring/INetMQMonitor.cs | 121 ++++++++++++++++++++++++++ src/NetMQ/Monitoring/NetMQMonitor.cs | 2 +- 2 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 src/NetMQ/Monitoring/INetMQMonitor.cs diff --git a/src/NetMQ/Monitoring/INetMQMonitor.cs b/src/NetMQ/Monitoring/INetMQMonitor.cs new file mode 100644 index 000000000..a205f30cf --- /dev/null +++ b/src/NetMQ/Monitoring/INetMQMonitor.cs @@ -0,0 +1,121 @@ +using System; +using System.Threading.Tasks; + +namespace NetMQ.Monitoring +{ + /// + /// NetMQMonitor interface, implement to fake the NetMQMonitor in tests. + /// + public interface INetMQMonitor : IDisposable + { + /// + /// The monitoring address. + /// + string Endpoint { get; } + + /// + /// Get whether this monitor is currently running. + /// + /// + /// Start the monitor running via either or . + /// Stop the monitor via either or . + /// + bool IsRunning { get; } + + /// + /// Gets and sets the timeout interval for poll iterations when using and . + /// + /// + /// The higher the number the longer it may take the to stop the monitor. + /// This value has no effect when the monitor is run via . + /// + TimeSpan Timeout { get; set; } + + /// + /// Raised whenever any monitored event fires. + /// + event EventHandler? EventReceived; + + /// + /// Occurs when a connection is made to a socket. + /// + event EventHandler? Connected; + + /// + /// Occurs when a synchronous connection attempt failed, and its completion is being polled for. + /// + event EventHandler? ConnectDelayed; + + /// + /// Occurs when an asynchronous connect / reconnection attempt is being handled by a reconnect timer. + /// + event EventHandler? ConnectRetried; + + /// + /// Occurs when a socket is bound to an address and is ready to accept connections. + /// + event EventHandler? Listening; + + /// + /// Occurs when a socket could not bind to an address. + /// + event EventHandler? BindFailed; + + /// + /// Occurs when a connection from a remote peer has been established with a socket's listen address. + /// + event EventHandler? Accepted; + + /// + /// Occurs when a connection attempt to a socket's bound address fails. + /// + event EventHandler? AcceptFailed; + + /// + /// Occurs when a connection was closed. + /// + event EventHandler? Closed; + + /// + /// Occurs when a connection couldn't be closed. + /// + event EventHandler? CloseFailed; + + /// + /// Occurs when the stream engine (TCP and IPC specific) detects a corrupted / broken session. + /// + event EventHandler? Disconnected; + + /// + /// Add the monitor object to a NetMQPoller, register to to be signalled on new events + /// + /// + /// + /// + /// + void AttachToPoller(T poller) where T : INetMQPoller; + + /// + /// Remove the monitor object from attached poller + /// + void DetachFromPoller(); + + /// + /// Start monitor the socket, the method doesn't start a new thread and will block until the monitor poll is stopped + /// + /// The Monitor must not have already started nor attached to a poller. + void Start(); + + /// + /// Start a background task for the monitoring operation. + /// + /// + Task StartAsync(); + + /// + /// Stop monitoring. Blocks until monitoring completed. + /// + /// If this monitor is attached to a poller you must detach it first and not use the stop method. + void Stop(); + } +} diff --git a/src/NetMQ/Monitoring/NetMQMonitor.cs b/src/NetMQ/Monitoring/NetMQMonitor.cs index bac778aac..30d74d26d 100644 --- a/src/NetMQ/Monitoring/NetMQMonitor.cs +++ b/src/NetMQ/Monitoring/NetMQMonitor.cs @@ -17,7 +17,7 @@ namespace NetMQ.Monitoring /// Call and . /// /// - public class NetMQMonitor : IDisposable + public class NetMQMonitor : INetMQMonitor { private readonly NetMQSocket m_monitoringSocket; private readonly bool m_ownsMonitoringSocket; From 5ba4632576af105ab305ae67c3172fb06172210c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 6 Oct 2025 02:24:58 +0000 Subject: [PATCH 4/8] Add test demonstrating INetMQMonitor mockability Co-authored-by: drewnoakes <350947+drewnoakes@users.noreply.github.com> --- src/NetMQ.Tests/NetMQMonitorTests.cs | 81 ++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/src/NetMQ.Tests/NetMQMonitorTests.cs b/src/NetMQ.Tests/NetMQMonitorTests.cs index 81eb0d1d3..5f912bf5c 100644 --- a/src/NetMQ.Tests/NetMQMonitorTests.cs +++ b/src/NetMQ.Tests/NetMQMonitorTests.cs @@ -188,5 +188,86 @@ public void ConvertArgThrowsForInvalidType() MonitorEvent monitorEvent = new MonitorEvent(SocketEvents.All, addr: "", arg: socket!); Assert.Throws(() => monitorEvent.ConvertArg()); } + + [Fact] + public void INetMQMonitorCanBeMocked() + { + // This test demonstrates that INetMQMonitor can be mocked for testing + var mockMonitor = new MockNetMQMonitor(); + + // Verify the mock implements the interface + INetMQMonitor monitor = mockMonitor; + + Assert.NotNull(monitor); + Assert.Equal("mock://endpoint", monitor.Endpoint); + Assert.False(monitor.IsRunning); + + // Verify we can subscribe to events + var eventReceived = false; + monitor.EventReceived += (s, e) => { eventReceived = true; }; + + // Trigger the event + mockMonitor.RaiseEventReceived(); + + Assert.True(eventReceived); + } + + // Simple mock implementation to demonstrate INetMQMonitor is mockable + private class MockNetMQMonitor : INetMQMonitor + { + public string Endpoint => "mock://endpoint"; + public bool IsRunning { get; private set; } + public TimeSpan Timeout { get; set; } + + public event EventHandler? EventReceived; +#pragma warning disable CS0067 // Event is never used - these are part of the interface contract + public event EventHandler? Connected; + public event EventHandler? ConnectDelayed; + public event EventHandler? ConnectRetried; + public event EventHandler? Listening; + public event EventHandler? BindFailed; + public event EventHandler? Accepted; + public event EventHandler? AcceptFailed; + public event EventHandler? Closed; + public event EventHandler? CloseFailed; + public event EventHandler? Disconnected; +#pragma warning restore CS0067 + + public void AttachToPoller(T poller) where T : INetMQPoller + { + IsRunning = true; + } + + public void DetachFromPoller() + { + IsRunning = false; + } + + public void Start() + { + IsRunning = true; + } + + public Task StartAsync() + { + IsRunning = true; + return Task.CompletedTask; + } + + public void Stop() + { + IsRunning = false; + } + + public void Dispose() + { + IsRunning = false; + } + + public void RaiseEventReceived() + { + EventReceived?.Invoke(this, new NetMQMonitorSocketEventArgs(null!, "test", null!, SocketEvents.Connected)); + } + } } } From b6b048f8337d3c88f04691878ec2e6f7c99a19fa Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 6 Oct 2025 02:44:46 +0000 Subject: [PATCH 5/8] Remove xunit package source and test mock implementation per review feedback Co-authored-by: drewnoakes <350947+drewnoakes@users.noreply.github.com> --- NuGet.Config | 4 -- src/NetMQ.Tests/NetMQMonitorTests.cs | 81 ---------------------------- 2 files changed, 85 deletions(-) diff --git a/NuGet.Config b/NuGet.Config index 271cba873..041e71acb 100644 --- a/NuGet.Config +++ b/NuGet.Config @@ -2,9 +2,5 @@ - - - - \ No newline at end of file diff --git a/src/NetMQ.Tests/NetMQMonitorTests.cs b/src/NetMQ.Tests/NetMQMonitorTests.cs index 5f912bf5c..81eb0d1d3 100644 --- a/src/NetMQ.Tests/NetMQMonitorTests.cs +++ b/src/NetMQ.Tests/NetMQMonitorTests.cs @@ -188,86 +188,5 @@ public void ConvertArgThrowsForInvalidType() MonitorEvent monitorEvent = new MonitorEvent(SocketEvents.All, addr: "", arg: socket!); Assert.Throws(() => monitorEvent.ConvertArg()); } - - [Fact] - public void INetMQMonitorCanBeMocked() - { - // This test demonstrates that INetMQMonitor can be mocked for testing - var mockMonitor = new MockNetMQMonitor(); - - // Verify the mock implements the interface - INetMQMonitor monitor = mockMonitor; - - Assert.NotNull(monitor); - Assert.Equal("mock://endpoint", monitor.Endpoint); - Assert.False(monitor.IsRunning); - - // Verify we can subscribe to events - var eventReceived = false; - monitor.EventReceived += (s, e) => { eventReceived = true; }; - - // Trigger the event - mockMonitor.RaiseEventReceived(); - - Assert.True(eventReceived); - } - - // Simple mock implementation to demonstrate INetMQMonitor is mockable - private class MockNetMQMonitor : INetMQMonitor - { - public string Endpoint => "mock://endpoint"; - public bool IsRunning { get; private set; } - public TimeSpan Timeout { get; set; } - - public event EventHandler? EventReceived; -#pragma warning disable CS0067 // Event is never used - these are part of the interface contract - public event EventHandler? Connected; - public event EventHandler? ConnectDelayed; - public event EventHandler? ConnectRetried; - public event EventHandler? Listening; - public event EventHandler? BindFailed; - public event EventHandler? Accepted; - public event EventHandler? AcceptFailed; - public event EventHandler? Closed; - public event EventHandler? CloseFailed; - public event EventHandler? Disconnected; -#pragma warning restore CS0067 - - public void AttachToPoller(T poller) where T : INetMQPoller - { - IsRunning = true; - } - - public void DetachFromPoller() - { - IsRunning = false; - } - - public void Start() - { - IsRunning = true; - } - - public Task StartAsync() - { - IsRunning = true; - return Task.CompletedTask; - } - - public void Stop() - { - IsRunning = false; - } - - public void Dispose() - { - IsRunning = false; - } - - public void RaiseEventReceived() - { - EventReceived?.Invoke(this, new NetMQMonitorSocketEventArgs(null!, "test", null!, SocketEvents.Connected)); - } - } } } From dd640a45c4a5f8f7458cbb3ca09d8a9660890bcf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 6 Oct 2025 02:55:13 +0000 Subject: [PATCH 6/8] Improve documentation: fix property docs, populate empty elements, add periods, use cref, add inheritdoc Co-authored-by: drewnoakes <350947+drewnoakes@users.noreply.github.com> --- src/NetMQ/Monitoring/INetMQMonitor.cs | 26 ++++---- src/NetMQ/Monitoring/NetMQMonitor.cs | 91 ++++++--------------------- 2 files changed, 32 insertions(+), 85 deletions(-) diff --git a/src/NetMQ/Monitoring/INetMQMonitor.cs b/src/NetMQ/Monitoring/INetMQMonitor.cs index a205f30cf..f84be436b 100644 --- a/src/NetMQ/Monitoring/INetMQMonitor.cs +++ b/src/NetMQ/Monitoring/INetMQMonitor.cs @@ -9,12 +9,12 @@ namespace NetMQ.Monitoring public interface INetMQMonitor : IDisposable { /// - /// The monitoring address. + /// Gets the monitoring address. /// string Endpoint { get; } /// - /// Get whether this monitor is currently running. + /// Gets whether this monitor is currently running. /// /// /// Start the monitor running via either or . @@ -87,35 +87,35 @@ public interface INetMQMonitor : IDisposable event EventHandler? Disconnected; /// - /// Add the monitor object to a NetMQPoller, register to to be signalled on new events + /// Adds the monitor object to a NetMQPoller. Register to to be signalled on new events. /// - /// - /// - /// - /// + /// The poller to attach to. + /// The type of poller. + /// The is null. + /// The monitor is already started or already attached to a poller. void AttachToPoller(T poller) where T : INetMQPoller; /// - /// Remove the monitor object from attached poller + /// Removes the monitor object from the attached poller. /// void DetachFromPoller(); /// - /// Start monitor the socket, the method doesn't start a new thread and will block until the monitor poll is stopped + /// Starts monitoring the socket. This method doesn't start a new thread and will block until the monitor poll is stopped. /// /// The Monitor must not have already started nor attached to a poller. void Start(); /// - /// Start a background task for the monitoring operation. + /// Starts a background task for the monitoring operation. /// - /// + /// A task representing the monitoring operation. Task StartAsync(); /// - /// Stop monitoring. Blocks until monitoring completed. + /// Stops monitoring. Blocks until monitoring completed. /// - /// If this monitor is attached to a poller you must detach it first and not use the stop method. + /// If this monitor is attached to a poller you must detach it first and not use the method. void Stop(); } } diff --git a/src/NetMQ/Monitoring/NetMQMonitor.cs b/src/NetMQ/Monitoring/NetMQMonitor.cs index 30d74d26d..7b05f3354 100644 --- a/src/NetMQ/Monitoring/NetMQMonitor.cs +++ b/src/NetMQ/Monitoring/NetMQMonitor.cs @@ -68,84 +68,48 @@ public NetMQMonitor(NetMQSocket socket, string endpoint, bool ownsSocket = false m_ownsMonitoringSocket = ownsSocket; } - /// - /// The monitoring address. - /// + /// public string Endpoint { get; } - /// - /// Get whether this monitor is currently running. - /// - /// - /// Start the monitor running via either or . - /// Stop the monitor via either or . - /// + /// public bool IsRunning { get; private set; } - /// - /// Gets and sets the timeout interval for poll iterations when using and . - /// - /// - /// The higher the number the longer it may take the to stop the monitor. - /// This value has no effect when the monitor is run via . - /// + /// public TimeSpan Timeout { get; set; } #region Events - /// - /// Raised whenever any monitored event fires. - /// + /// public event EventHandler? EventReceived; - /// - /// Occurs when a connection is made to a socket. - /// + /// public event EventHandler? Connected; - /// - /// Occurs when a synchronous connection attempt failed, and its completion is being polled for. - /// + /// public event EventHandler? ConnectDelayed; - /// - /// Occurs when an asynchronous connect / reconnection attempt is being handled by a reconnect timer. - /// + /// public event EventHandler? ConnectRetried; - /// - /// Occurs when a socket is bound to an address and is ready to accept connections. - /// + /// public event EventHandler? Listening; - /// - /// Occurs when a socket could not bind to an address. - /// + /// public event EventHandler? BindFailed; - /// - /// Occurs when a connection from a remote peer has been established with a socket's listen address. - /// + /// public event EventHandler? Accepted; - /// - /// Occurs when a connection attempt to a socket's bound address fails. - /// + /// public event EventHandler? AcceptFailed; - /// - /// Occurs when a connection was closed. - /// + /// public event EventHandler? Closed; - /// - /// Occurs when a connection couldn't be closed. - /// + /// public event EventHandler? CloseFailed; - /// - /// Occurs when the stream engine (TCP and IPC specific) detects a corrupted / broken session. - /// + /// public event EventHandler? Disconnected; #endregion @@ -219,13 +183,7 @@ private void InternalClose() } } - /// - /// Add the monitor object to a NetMQPoller, register to to be signalled on new events - /// - /// - /// - /// - /// + /// public void AttachToPoller(T poller) where T : INetMQPoller { if (poller == null) @@ -239,9 +197,7 @@ public void AttachToPoller(T poller) where T : INetMQPoller poller.Add(m_monitoringSocket); } - /// - /// Remove the monitor object from attached poller - /// + /// public void DetachFromPoller() { DetachFromPoller(false); @@ -260,10 +216,7 @@ private void DetachFromPoller(bool dispose) InternalClose(); } - /// - /// Start monitor the socket, the method doesn't start a new thread and will block until the monitor poll is stopped - /// - /// The Monitor must not have already started nor attached to a poller. + /// public void Start() { if (IsRunning) @@ -287,10 +240,7 @@ public void Start() } } - /// - /// Start a background task for the monitoring operation. - /// - /// + /// public Task StartAsync() { if (IsRunning) @@ -302,10 +252,7 @@ public Task StartAsync() return Task.Factory.StartNew(Start); } - /// - /// Stop monitoring. Blocks until monitoring completed. - /// - /// If this monitor is attached to a poller you must detach it first and not use the stop method. + /// public void Stop() { if (m_attachedPoller != null) From fbb789f4c01a68987400605148d070b9aa80203b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 6 Oct 2025 04:11:04 +0000 Subject: [PATCH 7/8] Improve INetMQMonitor documentation to describe the abstraction, use inheritdoc in NetMQMonitor Co-authored-by: drewnoakes <350947+drewnoakes@users.noreply.github.com> --- src/NetMQ/Monitoring/INetMQMonitor.cs | 9 ++++++++- src/NetMQ/Monitoring/NetMQMonitor.cs | 11 +---------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/NetMQ/Monitoring/INetMQMonitor.cs b/src/NetMQ/Monitoring/INetMQMonitor.cs index f84be436b..efd8b296f 100644 --- a/src/NetMQ/Monitoring/INetMQMonitor.cs +++ b/src/NetMQ/Monitoring/INetMQMonitor.cs @@ -4,8 +4,15 @@ namespace NetMQ.Monitoring { /// - /// NetMQMonitor interface, implement to fake the NetMQMonitor in tests. + /// Monitors a for events, raising them via events. /// + /// + /// To run a monitor instance, either: + /// + /// Call (blocking) and , or + /// Call and . + /// + /// public interface INetMQMonitor : IDisposable { /// diff --git a/src/NetMQ/Monitoring/NetMQMonitor.cs b/src/NetMQ/Monitoring/NetMQMonitor.cs index 7b05f3354..7cb201916 100644 --- a/src/NetMQ/Monitoring/NetMQMonitor.cs +++ b/src/NetMQ/Monitoring/NetMQMonitor.cs @@ -7,16 +7,7 @@ namespace NetMQ.Monitoring { - /// - /// Monitors a for events, raising them via events. - /// - /// - /// To run a monitor instance, either: - /// - /// Call (blocking) and , or - /// Call and . - /// - /// + /// public class NetMQMonitor : INetMQMonitor { private readonly NetMQSocket m_monitoringSocket; From 88dac6c39c011cbb83b9620192dcf09e6f547931 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 6 Oct 2025 04:47:26 +0000 Subject: [PATCH 8/8] Delete NuGet.Config file as it's no longer needed Co-authored-by: drewnoakes <350947+drewnoakes@users.noreply.github.com> --- NuGet.Config | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 NuGet.Config diff --git a/NuGet.Config b/NuGet.Config deleted file mode 100644 index 041e71acb..000000000 --- a/NuGet.Config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file