Moved some more settings to the connection interface

This commit is contained in:
2024-04-02 17:43:45 +02:00
parent 21b0540e81
commit ca95298390
7 changed files with 161 additions and 154 deletions

View File

@@ -25,6 +25,21 @@ namespace AMWD.Protocols.Modbus.Common.Contracts
/// </remarks> /// </remarks>
TimeSpan IdleTimeout { get; set; } TimeSpan IdleTimeout { get; set; }
/// <summary>
/// Gets or sets the maximum time until the connect attempt is given up.
/// </summary>
TimeSpan ConnectTimeout { get; set; }
/// <summary>
/// Gets or sets the receive time out value of the connection.
/// </summary>
TimeSpan ReadTimeout { get; set; }
/// <summary>
/// Gets or sets the send time out value of the connection.
/// </summary>
TimeSpan WriteTimeout { get; set; }
/// <summary> /// <summary>
/// Invokes a Modbus request. /// Invokes a Modbus request.
/// </summary> /// </summary>

View File

@@ -1,10 +1,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace AMWD.Protocols.Modbus.Tcp.Utils namespace AMWD.Protocols.Modbus.Common.Utils
{ {
internal class RequestQueueItem internal class RequestQueueItem
{ {

View File

@@ -18,6 +18,7 @@
<Compile Include="../AMWD.Protocols.Modbus.Common/Extensions/ArrayExtensions.cs" Link="Extensions/ArrayExtensions.cs" /> <Compile Include="../AMWD.Protocols.Modbus.Common/Extensions/ArrayExtensions.cs" Link="Extensions/ArrayExtensions.cs" />
<Compile Include="../AMWD.Protocols.Modbus.Common/Extensions/ReaderWriterLockSlimExtensions.cs" Link="Extensions/ReaderWriterLockSlimExtensions.cs" /> <Compile Include="../AMWD.Protocols.Modbus.Common/Extensions/ReaderWriterLockSlimExtensions.cs" Link="Extensions/ReaderWriterLockSlimExtensions.cs" />
<Compile Include="../AMWD.Protocols.Modbus.Common/Utils/AsyncQueue.cs" Link="Utils/AsyncQueue.cs" /> <Compile Include="../AMWD.Protocols.Modbus.Common/Utils/AsyncQueue.cs" Link="Utils/AsyncQueue.cs" />
<Compile Include="../AMWD.Protocols.Modbus.Common/Utils/RequestQueueItem.cs" Link="Utils/RequestQueueItem.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -27,7 +27,7 @@ namespace AMWD.Protocols.Modbus.Tcp
{ } { }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ModbusClientBase"/> class with a specific <see cref="IModbusConnection"/>. /// Initializes a new instance of the <see cref="ModbusTcpClient"/> class with a specific <see cref="IModbusConnection"/>.
/// </summary> /// </summary>
/// <param name="connection">The <see cref="IModbusConnection"/> responsible for invoking the requests.</param> /// <param name="connection">The <see cref="IModbusConnection"/> responsible for invoking the requests.</param>
/// <param name="disposeConnection"> /// <param name="disposeConnection">
@@ -43,6 +43,34 @@ namespace AMWD.Protocols.Modbus.Tcp
/// <inheritdoc/> /// <inheritdoc/>
public override IModbusProtocol Protocol { get; set; } public override IModbusProtocol Protocol { get; set; }
/// <inheritdoc cref="IModbusConnection.IdleTimeout"/>
public TimeSpan IdleTimeout
{
get => connection.IdleTimeout;
set => connection.IdleTimeout = value;
}
/// <inheritdoc cref="IModbusConnection.ConnectTimeout"/>
public TimeSpan ConnectTimeout
{
get => connection.ConnectTimeout;
set => connection.ConnectTimeout = value;
}
/// <inheritdoc cref="IModbusConnection.ReadTimeout"/>
public TimeSpan ReadTimeout
{
get => connection.ReadTimeout;
set => connection.ReadTimeout = value;
}
/// <inheritdoc cref="IModbusConnection.WriteTimeout"/>
public TimeSpan WriteTimeout
{
get => connection.WriteTimeout;
set => connection.WriteTimeout = value;
}
/// <inheritdoc cref="ModbusTcpConnection.Hostname"/> /// <inheritdoc cref="ModbusTcpConnection.Hostname"/>
public string Hostname public string Hostname
{ {
@@ -76,73 +104,5 @@ namespace AMWD.Protocols.Modbus.Tcp
tcpConnection.Port = value; tcpConnection.Port = value;
} }
} }
/// <inheritdoc cref="ModbusTcpConnection.ReadTimeout"/>
public TimeSpan ReadTimeout
{
get
{
if (connection is ModbusTcpConnection tcpConnection)
return tcpConnection.ReadTimeout;
return default;
}
set
{
if (connection is ModbusTcpConnection tcpConnection)
tcpConnection.ReadTimeout = value;
}
}
/// <inheritdoc cref="ModbusTcpConnection.WriteTimeout"/>
public TimeSpan WriteTimeout
{
get
{
if (connection is ModbusTcpConnection tcpConnection)
return tcpConnection.WriteTimeout;
return default;
}
set
{
if (connection is ModbusTcpConnection tcpConnection)
tcpConnection.WriteTimeout = value;
}
}
/// <inheritdoc cref="ModbusTcpConnection.ConnectTimeout"/>
public TimeSpan ReconnectTimeout
{
get
{
if (connection is ModbusTcpConnection tcpConnection)
return tcpConnection.ConnectTimeout;
return default;
}
set
{
if (connection is ModbusTcpConnection tcpConnection)
tcpConnection.ConnectTimeout = value;
}
}
/// <inheritdoc cref="ModbusTcpConnection.IdleTimeout"/>
public TimeSpan IdleTimeout
{
get
{
if (connection is ModbusTcpConnection tcpConnection)
return tcpConnection.IdleTimeout;
return default;
}
set
{
if (connection is ModbusTcpConnection tcpConnection)
tcpConnection.IdleTimeout = value;
}
}
} }
} }

View File

@@ -8,6 +8,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using AMWD.Protocols.Modbus.Common.Contracts; using AMWD.Protocols.Modbus.Common.Contracts;
using AMWD.Protocols.Modbus.Common.Protocols; using AMWD.Protocols.Modbus.Common.Protocols;
using AMWD.Protocols.Modbus.Common.Utils;
using AMWD.Protocols.Modbus.Tcp.Utils; using AMWD.Protocols.Modbus.Tcp.Utils;
namespace AMWD.Protocols.Modbus.Tcp namespace AMWD.Protocols.Modbus.Tcp
@@ -26,7 +27,7 @@ namespace AMWD.Protocols.Modbus.Tcp
private readonly CancellationTokenSource _disposeCts = new(); private readonly CancellationTokenSource _disposeCts = new();
private readonly SemaphoreSlim _clientLock = new(1, 1); private readonly SemaphoreSlim _clientLock = new(1, 1);
private readonly TcpClientWrapper _client = new(); private readonly TcpClientWrapper _tcpClient = new();
private readonly Timer _idleTimer; private readonly Timer _idleTimer;
private readonly Task _processingTask; private readonly Task _processingTask;
@@ -51,6 +52,23 @@ namespace AMWD.Protocols.Modbus.Tcp
/// <inheritdoc/> /// <inheritdoc/>
public virtual TimeSpan IdleTimeout { get; set; } = TimeSpan.FromSeconds(6); public virtual TimeSpan IdleTimeout { get; set; } = TimeSpan.FromSeconds(6);
/// <inheritdoc/>
public virtual TimeSpan ConnectTimeout { get; set; } = TimeSpan.MaxValue;
/// <inheritdoc/>
public virtual TimeSpan ReadTimeout
{
get => TimeSpan.FromMilliseconds(_tcpClient.ReceiveTimeout);
set => _tcpClient.ReceiveTimeout = (int)value.TotalMilliseconds;
}
/// <inheritdoc/>
public virtual TimeSpan WriteTimeout
{
get => TimeSpan.FromMilliseconds(_tcpClient.SendTimeout);
set => _tcpClient.SendTimeout = (int)value.TotalMilliseconds;
}
/// <summary> /// <summary>
/// The DNS name of the remote host to which the connection is intended to. /// The DNS name of the remote host to which the connection is intended to.
/// </summary> /// </summary>
@@ -81,32 +99,11 @@ namespace AMWD.Protocols.Modbus.Tcp
} }
} }
/// <summary>
/// Gets or sets the receive time out value of the connection.
/// </summary>
public virtual TimeSpan ReadTimeout
{
get => TimeSpan.FromMilliseconds(_client.ReceiveTimeout);
set => _client.ReceiveTimeout = (int)value.TotalMilliseconds;
}
/// <summary>
/// Gets or sets the send time out value of the connection.
/// </summary>
public virtual TimeSpan WriteTimeout
{
get => TimeSpan.FromMilliseconds(_client.SendTimeout);
set => _client.SendTimeout = (int)value.TotalMilliseconds;
}
/// <summary>
/// Gets or sets the maximum time until the connect attempt is given up.
/// </summary>
public virtual TimeSpan ConnectTimeout { get; set; } = TimeSpan.MaxValue;
#endregion Properties #endregion Properties
/// <inheritdoc/> /// <summary>
/// Releases all managed and unmanaged resources used by the <see cref="ModbusTcpConnection"/>.
/// </summary>
public void Dispose() public void Dispose()
{ {
if (_isDisposed) if (_isDisposed)
@@ -127,7 +124,7 @@ namespace AMWD.Protocols.Modbus.Tcp
OnIdleTimer(null); OnIdleTimer(null);
_client.Dispose(); _tcpClient.Dispose();
_clientLock.Dispose(); _clientLock.Dispose();
while (_requestQueue.TryDequeue(out var item)) while (_requestQueue.TryDequeue(out var item))
@@ -204,7 +201,7 @@ namespace AMWD.Protocols.Modbus.Tcp
// Ensure connection is up // Ensure connection is up
await AssertConnection(linkedCts.Token).ConfigureAwait(false); await AssertConnection(linkedCts.Token).ConfigureAwait(false);
var stream = _client.GetStream(); var stream = _tcpClient.GetStream();
await stream.FlushAsync(linkedCts.Token).ConfigureAwait(false); await stream.FlushAsync(linkedCts.Token).ConfigureAwait(false);
#if NET6_0_OR_GREATER #if NET6_0_OR_GREATER
@@ -270,7 +267,7 @@ namespace AMWD.Protocols.Modbus.Tcp
// Has to be called within _clientLock! // Has to be called within _clientLock!
private async Task AssertConnection(CancellationToken cancellationToken) private async Task AssertConnection(CancellationToken cancellationToken)
{ {
if (_client.Connected) if (_tcpClient.Connected)
return; return;
int delay = 1; int delay = 1;
@@ -287,17 +284,17 @@ namespace AMWD.Protocols.Modbus.Tcp
{ {
foreach (var ipAddress in ipAddresses) foreach (var ipAddress in ipAddresses)
{ {
_client.Close(); _tcpClient.Close();
#if NET6_0_OR_GREATER #if NET6_0_OR_GREATER
using var connectTask = _client.ConnectAsync(ipAddress, Port, cancellationToken); using var connectTask = _tcpClient.ConnectAsync(ipAddress, Port, cancellationToken);
#else #else
using var connectTask = _client.ConnectAsync(ipAddress, Port); using var connectTask = _tcpClient.ConnectAsync(ipAddress, Port);
#endif #endif
if (await Task.WhenAny(connectTask, Task.Delay(ReadTimeout, cancellationToken)) == connectTask) if (await Task.WhenAny(connectTask, Task.Delay(ReadTimeout, cancellationToken)) == connectTask)
{ {
await connectTask; await connectTask;
if (_client.Connected) if (_tcpClient.Connected)
return; return;
} }
} }
@@ -327,10 +324,10 @@ namespace AMWD.Protocols.Modbus.Tcp
_clientLock.Wait(_disposeCts.Token); _clientLock.Wait(_disposeCts.Token);
try try
{ {
if (!_client.Connected) if (!_tcpClient.Connected)
return; return;
_client.Close(); _tcpClient.Close();
} }
finally finally
{ {

View File

@@ -1,5 +1,4 @@
using AMWD.Protocols.Modbus.Common.Contracts; using AMWD.Protocols.Modbus.Tcp;
using AMWD.Protocols.Modbus.Tcp;
using Moq; using Moq;
namespace AMWD.Protocols.Modbus.Tests.Tcp namespace AMWD.Protocols.Modbus.Tests.Tcp
@@ -14,13 +13,20 @@ namespace AMWD.Protocols.Modbus.Tests.Tcp
public void Initialize() public void Initialize()
{ {
_genericConnectionMock = new Mock<IModbusConnection>(); _genericConnectionMock = new Mock<IModbusConnection>();
_genericConnectionMock.Setup(c => c.IdleTimeout).Returns(TimeSpan.FromSeconds(40));
_genericConnectionMock.Setup(c => c.ConnectTimeout).Returns(TimeSpan.FromSeconds(30));
_genericConnectionMock.Setup(c => c.ReadTimeout).Returns(TimeSpan.FromSeconds(20));
_genericConnectionMock.Setup(c => c.WriteTimeout).Returns(TimeSpan.FromSeconds(10));
_tcpConnectionMock = new Mock<ModbusTcpConnection>(); _tcpConnectionMock = new Mock<ModbusTcpConnection>();
_tcpConnectionMock.Setup(c => c.IdleTimeout).Returns(TimeSpan.FromSeconds(10));
_tcpConnectionMock.Setup(c => c.ConnectTimeout).Returns(TimeSpan.FromSeconds(20));
_tcpConnectionMock.Setup(c => c.ReadTimeout).Returns(TimeSpan.FromSeconds(30));
_tcpConnectionMock.Setup(c => c.WriteTimeout).Returns(TimeSpan.FromSeconds(40));
_tcpConnectionMock.Setup(c => c.Hostname).Returns("127.0.0.1"); _tcpConnectionMock.Setup(c => c.Hostname).Returns("127.0.0.1");
_tcpConnectionMock.Setup(c => c.Port).Returns(502); _tcpConnectionMock.Setup(c => c.Port).Returns(502);
_tcpConnectionMock.Setup(c => c.ReadTimeout).Returns(TimeSpan.FromSeconds(10));
_tcpConnectionMock.Setup(c => c.WriteTimeout).Returns(TimeSpan.FromSeconds(20));
_tcpConnectionMock.Setup(c => c.ConnectTimeout).Returns(TimeSpan.FromSeconds(30));
_tcpConnectionMock.Setup(c => c.IdleTimeout).Returns(TimeSpan.FromSeconds(40));
} }
[TestMethod] [TestMethod]
@@ -32,18 +38,10 @@ namespace AMWD.Protocols.Modbus.Tests.Tcp
// Act // Act
string hostname = client.Hostname; string hostname = client.Hostname;
int port = client.Port; int port = client.Port;
TimeSpan readTimeout = client.ReadTimeout;
TimeSpan writeTimeout = client.WriteTimeout;
TimeSpan reconnectTimeout = client.ReconnectTimeout;
TimeSpan idleTimeout = client.IdleTimeout;
// Assert // Assert
Assert.IsNull(hostname); Assert.IsNull(hostname);
Assert.AreEqual(0, port); Assert.AreEqual(0, port);
Assert.AreEqual(TimeSpan.Zero, readTimeout);
Assert.AreEqual(TimeSpan.Zero, writeTimeout);
Assert.AreEqual(TimeSpan.Zero, reconnectTimeout);
Assert.AreEqual(TimeSpan.Zero, idleTimeout);
_genericConnectionMock.VerifyNoOtherCalls(); _genericConnectionMock.VerifyNoOtherCalls();
} }
@@ -57,17 +55,59 @@ namespace AMWD.Protocols.Modbus.Tests.Tcp
// Act // Act
client.Hostname = "localhost"; client.Hostname = "localhost";
client.Port = 205; client.Port = 205;
client.ReadTimeout = TimeSpan.FromSeconds(123);
client.WriteTimeout = TimeSpan.FromSeconds(456);
client.ReconnectTimeout = TimeSpan.FromSeconds(789);
client.IdleTimeout = TimeSpan.FromSeconds(321);
// Assert // Assert
_genericConnectionMock.VerifyNoOtherCalls(); _genericConnectionMock.VerifyNoOtherCalls();
} }
[TestMethod] [TestMethod]
public void ShouldReturnValuesForTcpConnection() public void ShouldReturnValuesForGenericConnection()
{
// Arrange
var client = new ModbusTcpClient(_genericConnectionMock.Object);
// Act
var idleTimeout = client.IdleTimeout;
var connectTimeout = client.ConnectTimeout;
var readTimeout = client.ReadTimeout;
var writeTimeout = client.WriteTimeout;
// Assert
Assert.AreEqual(TimeSpan.FromSeconds(40), idleTimeout);
Assert.AreEqual(TimeSpan.FromSeconds(30), connectTimeout);
Assert.AreEqual(TimeSpan.FromSeconds(20), readTimeout);
Assert.AreEqual(TimeSpan.FromSeconds(10), writeTimeout);
_genericConnectionMock.VerifyGet(c => c.IdleTimeout, Times.Once);
_genericConnectionMock.VerifyGet(c => c.ConnectTimeout, Times.Once);
_genericConnectionMock.VerifyGet(c => c.ReadTimeout, Times.Once);
_genericConnectionMock.VerifyGet(c => c.WriteTimeout, Times.Once);
_genericConnectionMock.VerifyNoOtherCalls();
}
[TestMethod]
public void ShouldSetValuesForGenericConnection()
{
// Arrange
var client = new ModbusTcpClient(_genericConnectionMock.Object);
// Act
client.IdleTimeout = TimeSpan.FromSeconds(10);
client.ConnectTimeout = TimeSpan.FromSeconds(20);
client.ReadTimeout = TimeSpan.FromSeconds(30);
client.WriteTimeout = TimeSpan.FromSeconds(40);
// Assert
_genericConnectionMock.VerifySet(c => c.IdleTimeout = TimeSpan.FromSeconds(10), Times.Once);
_genericConnectionMock.VerifySet(c => c.ConnectTimeout = TimeSpan.FromSeconds(20), Times.Once);
_genericConnectionMock.VerifySet(c => c.ReadTimeout = TimeSpan.FromSeconds(30), Times.Once);
_genericConnectionMock.VerifySet(c => c.WriteTimeout = TimeSpan.FromSeconds(40), Times.Once);
_genericConnectionMock.VerifyNoOtherCalls();
}
[TestMethod]
public void ShouldGetValuesForTcpConnection()
{ {
// Arrange // Arrange
var client = new ModbusTcpClient(_tcpConnectionMock.Object); var client = new ModbusTcpClient(_tcpConnectionMock.Object);
@@ -75,25 +115,26 @@ namespace AMWD.Protocols.Modbus.Tests.Tcp
// Act // Act
string hostname = client.Hostname; string hostname = client.Hostname;
int port = client.Port; int port = client.Port;
TimeSpan readTimeout = client.ReadTimeout; var idleTimeout = client.IdleTimeout;
TimeSpan writeTimeout = client.WriteTimeout; var connectTimeout = client.ConnectTimeout;
TimeSpan reconnectTimeout = client.ReconnectTimeout; var readTimeout = client.ReadTimeout;
TimeSpan keepAliveInterval = client.IdleTimeout; var writeTimeout = client.WriteTimeout;
// Assert // Assert
Assert.AreEqual("127.0.0.1", hostname); Assert.AreEqual("127.0.0.1", hostname);
Assert.AreEqual(502, port); Assert.AreEqual(502, port);
Assert.AreEqual(10, readTimeout.TotalSeconds); Assert.AreEqual(TimeSpan.FromSeconds(10), idleTimeout);
Assert.AreEqual(20, writeTimeout.TotalSeconds); Assert.AreEqual(TimeSpan.FromSeconds(20), connectTimeout);
Assert.AreEqual(30, reconnectTimeout.TotalSeconds); Assert.AreEqual(TimeSpan.FromSeconds(30), readTimeout);
Assert.AreEqual(40, keepAliveInterval.TotalSeconds); Assert.AreEqual(TimeSpan.FromSeconds(40), writeTimeout);
_tcpConnectionMock.VerifyGet(c => c.Hostname, Times.Once); _tcpConnectionMock.VerifyGet(c => c.Hostname, Times.Once);
_tcpConnectionMock.VerifyGet(c => c.Port, Times.Once); _tcpConnectionMock.VerifyGet(c => c.Port, Times.Once);
_tcpConnectionMock.VerifyGet(c => c.IdleTimeout, Times.Once);
_tcpConnectionMock.VerifyGet(c => c.ConnectTimeout, Times.Once);
_tcpConnectionMock.VerifyGet(c => c.ReadTimeout, Times.Once); _tcpConnectionMock.VerifyGet(c => c.ReadTimeout, Times.Once);
_tcpConnectionMock.VerifyGet(c => c.WriteTimeout, Times.Once); _tcpConnectionMock.VerifyGet(c => c.WriteTimeout, Times.Once);
_tcpConnectionMock.VerifyGet(c => c.ConnectTimeout, Times.Once);
_tcpConnectionMock.VerifyGet(c => c.IdleTimeout, Times.Once);
_tcpConnectionMock.VerifyNoOtherCalls(); _tcpConnectionMock.VerifyNoOtherCalls();
} }
@@ -106,18 +147,19 @@ namespace AMWD.Protocols.Modbus.Tests.Tcp
// Act // Act
client.Hostname = "localhost"; client.Hostname = "localhost";
client.Port = 205; client.Port = 205;
client.ReadTimeout = TimeSpan.FromSeconds(123); client.IdleTimeout = TimeSpan.FromSeconds(40);
client.WriteTimeout = TimeSpan.FromSeconds(456); client.ConnectTimeout = TimeSpan.FromSeconds(30);
client.ReconnectTimeout = TimeSpan.FromSeconds(789); client.ReadTimeout = TimeSpan.FromSeconds(20);
client.IdleTimeout = TimeSpan.FromSeconds(321); client.WriteTimeout = TimeSpan.FromSeconds(10);
// Assert // Assert
_tcpConnectionMock.VerifySet(c => c.Hostname = "localhost", Times.Once); _tcpConnectionMock.VerifySet(c => c.Hostname = "localhost", Times.Once);
_tcpConnectionMock.VerifySet(c => c.Port = 205, Times.Once); _tcpConnectionMock.VerifySet(c => c.Port = 205, Times.Once);
_tcpConnectionMock.VerifySet(c => c.ReadTimeout = TimeSpan.FromSeconds(123), Times.Once); _tcpConnectionMock.VerifySet(c => c.IdleTimeout = TimeSpan.FromSeconds(40), Times.Once);
_tcpConnectionMock.VerifySet(c => c.WriteTimeout = TimeSpan.FromSeconds(456), Times.Once); _tcpConnectionMock.VerifySet(c => c.ConnectTimeout = TimeSpan.FromSeconds(30), Times.Once);
_tcpConnectionMock.VerifySet(c => c.ConnectTimeout = TimeSpan.FromSeconds(789), Times.Once); _tcpConnectionMock.VerifySet(c => c.ReadTimeout = TimeSpan.FromSeconds(20), Times.Once);
_tcpConnectionMock.VerifySet(c => c.IdleTimeout = TimeSpan.FromSeconds(321), Times.Once); _tcpConnectionMock.VerifySet(c => c.WriteTimeout = TimeSpan.FromSeconds(10), Times.Once);
_tcpConnectionMock.VerifyNoOtherCalls(); _tcpConnectionMock.VerifyNoOtherCalls();
} }
} }

View File

@@ -4,7 +4,6 @@ using System.Net;
using System.Reflection; using System.Reflection;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using AMWD.Protocols.Modbus.Common.Contracts;
using AMWD.Protocols.Modbus.Tcp; using AMWD.Protocols.Modbus.Tcp;
using AMWD.Protocols.Modbus.Tcp.Utils; using AMWD.Protocols.Modbus.Tcp.Utils;
using Moq; using Moq;
@@ -528,18 +527,12 @@ namespace AMWD.Protocols.Modbus.Tests.Tcp
Port = 502 Port = 502
}; };
// Replace real TCP client with mock // Replace real connection with mock
var clientField = connection.GetType().GetField("_client", BindingFlags.NonPublic | BindingFlags.Instance); var connectionField = connection.GetType().GetField("_tcpClient", BindingFlags.NonPublic | BindingFlags.Instance);
(clientField.GetValue(connection) as TcpClientWrapper)?.Dispose(); (connectionField.GetValue(connection) as TcpClientWrapper)?.Dispose();
clientField.SetValue(connection, _tcpClientMock.Object); connectionField.SetValue(connection, _tcpClientMock.Object);
return connection; return connection;
} }
private void ClearInvocations()
{
_networkStreamMock.Invocations.Clear();
_tcpClientMock.Invocations.Clear();
}
} }
} }