Skip to content

Commit

Permalink
Ensure keep-alive timer is created when KeepAliveInterval is set afte…
Browse files Browse the repository at this point in the history
…r the connection has been established.

Fixes issue dotnet#334.
  • Loading branch information
drieseng committed Nov 5, 2017
1 parent 25bb64f commit 66dcb0b
Show file tree
Hide file tree
Showing 9 changed files with 523 additions and 15 deletions.
14 changes: 13 additions & 1 deletion src/Renci.SshNet.Tests.NET35/Renci.SshNet.Tests.NET35.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,21 @@
<Compile Include="..\..\test\Renci.SshNet.Shared.Tests\ForwardedPortStatusTest_Stopping.cs">
<Link>Classes\ForwardedPortStatusTest_Stopping.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet.Tests\Classes\BaseClientTest_Connected_KeepAliveInterval_NegativeOne.cs">
<Link>Classes\BaseClientTest_Connected_KeepAliveInterval_NegativeOne.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet.Tests\Classes\BaseClientTest_Connected_KeepAliveInterval_NotNegativeOne.cs">
<Link>Classes\BaseClientTest_Connected_KeepAliveInterval_NotNegativeOne.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet.Tests\Classes\BaseClientTest_Connected_KeepAlivesNotSentConcurrently.cs">
<Link>Classes\BaseClientTest_Connected_KeepAlivesNotSentConcurrently.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet.Tests\Classes\BaseClientTest_Disconnected_KeepAliveInterval_NotNegativeOne.cs">
<Link>Classes\BaseClientTest_Disconnected_KeepAliveInterval_NotNegativeOne.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet.Tests\Classes\BaseClientTest_NotConnected_KeepAliveInterval_NotNegativeOne.cs">
<Link>Classes\BaseClientTest_NotConnected_KeepAliveInterval_NotNegativeOne.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet.Tests\Classes\Channels\ChannelDirectTcpipTest.cs">
<Link>Classes\Channels\ChannelDirectTcpipTest.cs</Link>
</Compile>
Expand Down Expand Up @@ -1692,7 +1704,7 @@
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ProjectExtensions>
<VisualStudio>
<UserProperties ProjectLinkerExcludeFilter="\\?desktop(\\.*)?$;\\?silverlight(\\.*)?$;\.desktop;\.silverlight;\.xaml;^service references(\\.*)?$;\.clientconfig;^web references(\\.*)?$" ProjectLinkReference="c45379b9-17b1-4e89-bc2e-6d41726413e8" />
<UserProperties ProjectLinkReference="c45379b9-17b1-4e89-bc2e-6d41726413e8" ProjectLinkerExcludeFilter="\\?desktop(\\.*)?$;\\?silverlight(\\.*)?$;\.desktop;\.silverlight;\.xaml;^service references(\\.*)?$;\.clientconfig;^web references(\\.*)?$" />
</VisualStudio>
</ProjectExtensions>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
using System;
using System.Threading;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Renci.SshNet.Messages.Transport;

namespace Renci.SshNet.Tests.Classes
{
[TestClass]
public class BaseClientTest_Connected_KeepAliveInterval_NegativeOne
{
private Mock<IServiceFactory> _serviceFactoryMock;
private Mock<ISession> _sessionMock;
private BaseClient _client;
private ConnectionInfo _connectionInfo;
private TimeSpan _keepAliveInterval;
private int _keepAliveCount;

[TestInitialize]
public void Setup()
{
Arrange();
Act();
}

[TestCleanup]
public void Cleanup()
{
if (_client != null)
{
_sessionMock.Setup(p => p.OnDisconnecting());
_sessionMock.Setup(p => p.Dispose());
_client.Dispose();
}
}

private void SetupData()
{
_connectionInfo = new ConnectionInfo("host", "user", new PasswordAuthenticationMethod("user", "pwd"));
_keepAliveInterval = TimeSpan.FromMilliseconds(100d);
_keepAliveCount = 0;
}

private void CreateMocks()
{
_serviceFactoryMock = new Mock<IServiceFactory>(MockBehavior.Strict);
_sessionMock = new Mock<ISession>(MockBehavior.Strict);
}

private void SetupMocks()
{
_serviceFactoryMock.Setup(p => p.CreateSession(_connectionInfo))
.Returns(_sessionMock.Object);
_sessionMock.Setup(p => p.Connect());
_sessionMock.Setup(p => p.IsConnected).Returns(true);
_sessionMock.Setup(p => p.TrySendMessage(It.IsAny<IgnoreMessage>()))
.Returns(true)
.Callback(() => Interlocked.Increment(ref _keepAliveCount));
}

protected void Arrange()
{
SetupData();
CreateMocks();
SetupMocks();

_client = new MyClient(_connectionInfo, false, _serviceFactoryMock.Object);
_client.Connect();
_client.KeepAliveInterval = _keepAliveInterval;
}

protected void Act()
{
// allow keep-alive to be sent once
Thread.Sleep(150);

// disable keep-alive
_client.KeepAliveInterval = TimeSpan.FromMilliseconds(-1);
}

[TestMethod]
public void KeepAliveIntervalShouldReturnConfiguredValue()
{
Assert.AreEqual(TimeSpan.FromMilliseconds(-1), _client.KeepAliveInterval);
}

[TestMethod]
public void CreateSessionOnServiceFactoryShouldBeInvokedOnce()
{
_serviceFactoryMock.Verify(p => p.CreateSession(_connectionInfo), Times.Once);
}

[TestMethod]
public void ConnectOnSessionShouldBeInvokedOnce()
{
_sessionMock.Verify(p => p.Connect(), Times.Once);
}

[TestMethod]
public void IsConnectedOnSessionShouldBeInvokedOnce()
{
_sessionMock.Verify(p => p.IsConnected, Times.Once);
}

[TestMethod]
public void SendMessageOnSessionShouldBeInvokedThreeTimes()
{
// allow keep-alive to be sent once
Thread.Sleep(100);

_sessionMock.Verify(p => p.TrySendMessage(It.IsAny<IgnoreMessage>()), Times.Exactly(1));
}

private class MyClient : BaseClient
{
public MyClient(ConnectionInfo connectionInfo, bool ownsConnectionInfo, IServiceFactory serviceFactory) : base(connectionInfo, ownsConnectionInfo, serviceFactory)
{
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
using System;
using System.Threading;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Renci.SshNet.Messages.Transport;

namespace Renci.SshNet.Tests.Classes
{
[TestClass]
public class BaseClientTest_Connected_KeepAliveInterval_NotNegativeOne
{
private Mock<IServiceFactory> _serviceFactoryMock;
private Mock<ISession> _sessionMock;
private BaseClient _client;
private ConnectionInfo _connectionInfo;
private TimeSpan _keepAliveInterval;
private int _keepAliveCount;

[TestInitialize]
public void Setup()
{
Arrange();
Act();
}

[TestCleanup]
public void Cleanup()
{
if (_client != null)
{
_sessionMock.Setup(p => p.OnDisconnecting());
_sessionMock.Setup(p => p.Dispose());
_client.Dispose();
}
}

private void SetupData()
{
_connectionInfo = new ConnectionInfo("host", "user", new PasswordAuthenticationMethod("user", "pwd"));
_keepAliveInterval = TimeSpan.FromMilliseconds(50d);
_keepAliveCount = 0;
}

private void CreateMocks()
{
_serviceFactoryMock = new Mock<IServiceFactory>(MockBehavior.Strict);
_sessionMock = new Mock<ISession>(MockBehavior.Strict);
}

private void SetupMocks()
{
_serviceFactoryMock.Setup(p => p.CreateSession(_connectionInfo))
.Returns(_sessionMock.Object);
_sessionMock.Setup(p => p.Connect());
_sessionMock.Setup(p => p.IsConnected).Returns(true);
_sessionMock.Setup(p => p.TrySendMessage(It.IsAny<IgnoreMessage>()))
.Returns(true)
.Callback(() => Interlocked.Increment(ref _keepAliveCount));
}

protected void Arrange()
{
SetupData();
CreateMocks();
SetupMocks();

_client = new MyClient(_connectionInfo, false, _serviceFactoryMock.Object);
_client.Connect();
}

protected void Act()
{
_client.KeepAliveInterval = _keepAliveInterval;

// allow keep-alive to be sent a few times
Thread.Sleep(195);
}

[TestMethod]
public void KeepAliveIntervalShouldReturnConfiguredValue()
{
Assert.AreEqual(_keepAliveInterval, _client.KeepAliveInterval);
}

[TestMethod]
public void CreateSessionOnServiceFactoryShouldBeInvokedOnce()
{
_serviceFactoryMock.Verify(p => p.CreateSession(_connectionInfo), Times.Once);
}

[TestMethod]
public void ConnectOnSessionShouldBeInvokedOnce()
{
_sessionMock.Verify(p => p.Connect(), Times.Once);
}

[TestMethod]
public void IsConnectedOnSessionShouldBeInvokedOnce()
{
_sessionMock.Verify(p => p.IsConnected, Times.Once);
}

[TestMethod]
public void SendMessageOnSessionShouldBeInvokedThreeTimes()
{
_sessionMock.Verify(p => p.TrySendMessage(It.IsAny<IgnoreMessage>()), Times.Exactly(3));
}

private class MyClient : BaseClient
{
public MyClient(ConnectionInfo connectionInfo, bool ownsConnectionInfo, IServiceFactory serviceFactory) : base(connectionInfo, ownsConnectionInfo, serviceFactory)
{
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,25 +34,38 @@ public void Cleanup()
}
}

protected void Arrange()
private void SetupData()
{
_connectionInfo = new ConnectionInfo("host", "user", new PasswordAuthenticationMethod("user", "pwd"));
_keepAliveSent = new ManualResetEvent(false);
}

private void CreateMocks()
{
_serviceFactoryMock = new Mock<IServiceFactory>(MockBehavior.Strict);
_sessionMock = new Mock<ISession>(MockBehavior.Strict);
}

private void SetupMocks()
{
_mockSequence = new MockSequence();

_serviceFactoryMock.InSequence(_mockSequence).Setup(p => p.CreateSession(_connectionInfo)).Returns(_sessionMock.Object);
_sessionMock.InSequence(_mockSequence).Setup(p => p.Connect());
_sessionMock.InSequence(_mockSequence).Setup(p => p.TrySendMessage(It.IsAny<IgnoreMessage>()))
.Returns(true)
.Callback(() =>
{
Thread.Sleep(300);
_keepAliveSent.Set();
});
.Returns(true)
.Callback(() =>
{
Thread.Sleep(300);
_keepAliveSent.Set();
});
}

protected void Arrange()
{
SetupData();
CreateMocks();
SetupMocks();

_client = new MyClient(_connectionInfo, false, _serviceFactoryMock.Object)
{
Expand Down
Loading

0 comments on commit 66dcb0b

Please sign in to comment.