Skip to content

Commit 66dcb0b

Browse files
committed
Ensure keep-alive timer is created when KeepAliveInterval is set after the connection has been established.
Fixes issue #334.
1 parent 25bb64f commit 66dcb0b

9 files changed

+523
-15
lines changed

src/Renci.SshNet.Tests.NET35/Renci.SshNet.Tests.NET35.csproj

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,21 @@
7878
<Compile Include="..\..\test\Renci.SshNet.Shared.Tests\ForwardedPortStatusTest_Stopping.cs">
7979
<Link>Classes\ForwardedPortStatusTest_Stopping.cs</Link>
8080
</Compile>
81+
<Compile Include="..\Renci.SshNet.Tests\Classes\BaseClientTest_Connected_KeepAliveInterval_NegativeOne.cs">
82+
<Link>Classes\BaseClientTest_Connected_KeepAliveInterval_NegativeOne.cs</Link>
83+
</Compile>
84+
<Compile Include="..\Renci.SshNet.Tests\Classes\BaseClientTest_Connected_KeepAliveInterval_NotNegativeOne.cs">
85+
<Link>Classes\BaseClientTest_Connected_KeepAliveInterval_NotNegativeOne.cs</Link>
86+
</Compile>
8187
<Compile Include="..\Renci.SshNet.Tests\Classes\BaseClientTest_Connected_KeepAlivesNotSentConcurrently.cs">
8288
<Link>Classes\BaseClientTest_Connected_KeepAlivesNotSentConcurrently.cs</Link>
8389
</Compile>
90+
<Compile Include="..\Renci.SshNet.Tests\Classes\BaseClientTest_Disconnected_KeepAliveInterval_NotNegativeOne.cs">
91+
<Link>Classes\BaseClientTest_Disconnected_KeepAliveInterval_NotNegativeOne.cs</Link>
92+
</Compile>
93+
<Compile Include="..\Renci.SshNet.Tests\Classes\BaseClientTest_NotConnected_KeepAliveInterval_NotNegativeOne.cs">
94+
<Link>Classes\BaseClientTest_NotConnected_KeepAliveInterval_NotNegativeOne.cs</Link>
95+
</Compile>
8496
<Compile Include="..\Renci.SshNet.Tests\Classes\Channels\ChannelDirectTcpipTest.cs">
8597
<Link>Classes\Channels\ChannelDirectTcpipTest.cs</Link>
8698
</Compile>
@@ -1692,7 +1704,7 @@
16921704
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
16931705
<ProjectExtensions>
16941706
<VisualStudio>
1695-
<UserProperties ProjectLinkerExcludeFilter="\\?desktop(\\.*)?$;\\?silverlight(\\.*)?$;\.desktop;\.silverlight;\.xaml;^service references(\\.*)?$;\.clientconfig;^web references(\\.*)?$" ProjectLinkReference="c45379b9-17b1-4e89-bc2e-6d41726413e8" />
1707+
<UserProperties ProjectLinkReference="c45379b9-17b1-4e89-bc2e-6d41726413e8" ProjectLinkerExcludeFilter="\\?desktop(\\.*)?$;\\?silverlight(\\.*)?$;\.desktop;\.silverlight;\.xaml;^service references(\\.*)?$;\.clientconfig;^web references(\\.*)?$" />
16961708
</VisualStudio>
16971709
</ProjectExtensions>
16981710
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
using System;
2+
using System.Threading;
3+
using Microsoft.VisualStudio.TestTools.UnitTesting;
4+
using Moq;
5+
using Renci.SshNet.Messages.Transport;
6+
7+
namespace Renci.SshNet.Tests.Classes
8+
{
9+
[TestClass]
10+
public class BaseClientTest_Connected_KeepAliveInterval_NegativeOne
11+
{
12+
private Mock<IServiceFactory> _serviceFactoryMock;
13+
private Mock<ISession> _sessionMock;
14+
private BaseClient _client;
15+
private ConnectionInfo _connectionInfo;
16+
private TimeSpan _keepAliveInterval;
17+
private int _keepAliveCount;
18+
19+
[TestInitialize]
20+
public void Setup()
21+
{
22+
Arrange();
23+
Act();
24+
}
25+
26+
[TestCleanup]
27+
public void Cleanup()
28+
{
29+
if (_client != null)
30+
{
31+
_sessionMock.Setup(p => p.OnDisconnecting());
32+
_sessionMock.Setup(p => p.Dispose());
33+
_client.Dispose();
34+
}
35+
}
36+
37+
private void SetupData()
38+
{
39+
_connectionInfo = new ConnectionInfo("host", "user", new PasswordAuthenticationMethod("user", "pwd"));
40+
_keepAliveInterval = TimeSpan.FromMilliseconds(100d);
41+
_keepAliveCount = 0;
42+
}
43+
44+
private void CreateMocks()
45+
{
46+
_serviceFactoryMock = new Mock<IServiceFactory>(MockBehavior.Strict);
47+
_sessionMock = new Mock<ISession>(MockBehavior.Strict);
48+
}
49+
50+
private void SetupMocks()
51+
{
52+
_serviceFactoryMock.Setup(p => p.CreateSession(_connectionInfo))
53+
.Returns(_sessionMock.Object);
54+
_sessionMock.Setup(p => p.Connect());
55+
_sessionMock.Setup(p => p.IsConnected).Returns(true);
56+
_sessionMock.Setup(p => p.TrySendMessage(It.IsAny<IgnoreMessage>()))
57+
.Returns(true)
58+
.Callback(() => Interlocked.Increment(ref _keepAliveCount));
59+
}
60+
61+
protected void Arrange()
62+
{
63+
SetupData();
64+
CreateMocks();
65+
SetupMocks();
66+
67+
_client = new MyClient(_connectionInfo, false, _serviceFactoryMock.Object);
68+
_client.Connect();
69+
_client.KeepAliveInterval = _keepAliveInterval;
70+
}
71+
72+
protected void Act()
73+
{
74+
// allow keep-alive to be sent once
75+
Thread.Sleep(150);
76+
77+
// disable keep-alive
78+
_client.KeepAliveInterval = TimeSpan.FromMilliseconds(-1);
79+
}
80+
81+
[TestMethod]
82+
public void KeepAliveIntervalShouldReturnConfiguredValue()
83+
{
84+
Assert.AreEqual(TimeSpan.FromMilliseconds(-1), _client.KeepAliveInterval);
85+
}
86+
87+
[TestMethod]
88+
public void CreateSessionOnServiceFactoryShouldBeInvokedOnce()
89+
{
90+
_serviceFactoryMock.Verify(p => p.CreateSession(_connectionInfo), Times.Once);
91+
}
92+
93+
[TestMethod]
94+
public void ConnectOnSessionShouldBeInvokedOnce()
95+
{
96+
_sessionMock.Verify(p => p.Connect(), Times.Once);
97+
}
98+
99+
[TestMethod]
100+
public void IsConnectedOnSessionShouldBeInvokedOnce()
101+
{
102+
_sessionMock.Verify(p => p.IsConnected, Times.Once);
103+
}
104+
105+
[TestMethod]
106+
public void SendMessageOnSessionShouldBeInvokedThreeTimes()
107+
{
108+
// allow keep-alive to be sent once
109+
Thread.Sleep(100);
110+
111+
_sessionMock.Verify(p => p.TrySendMessage(It.IsAny<IgnoreMessage>()), Times.Exactly(1));
112+
}
113+
114+
private class MyClient : BaseClient
115+
{
116+
public MyClient(ConnectionInfo connectionInfo, bool ownsConnectionInfo, IServiceFactory serviceFactory) : base(connectionInfo, ownsConnectionInfo, serviceFactory)
117+
{
118+
}
119+
}
120+
}
121+
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
using System;
2+
using System.Threading;
3+
using Microsoft.VisualStudio.TestTools.UnitTesting;
4+
using Moq;
5+
using Renci.SshNet.Messages.Transport;
6+
7+
namespace Renci.SshNet.Tests.Classes
8+
{
9+
[TestClass]
10+
public class BaseClientTest_Connected_KeepAliveInterval_NotNegativeOne
11+
{
12+
private Mock<IServiceFactory> _serviceFactoryMock;
13+
private Mock<ISession> _sessionMock;
14+
private BaseClient _client;
15+
private ConnectionInfo _connectionInfo;
16+
private TimeSpan _keepAliveInterval;
17+
private int _keepAliveCount;
18+
19+
[TestInitialize]
20+
public void Setup()
21+
{
22+
Arrange();
23+
Act();
24+
}
25+
26+
[TestCleanup]
27+
public void Cleanup()
28+
{
29+
if (_client != null)
30+
{
31+
_sessionMock.Setup(p => p.OnDisconnecting());
32+
_sessionMock.Setup(p => p.Dispose());
33+
_client.Dispose();
34+
}
35+
}
36+
37+
private void SetupData()
38+
{
39+
_connectionInfo = new ConnectionInfo("host", "user", new PasswordAuthenticationMethod("user", "pwd"));
40+
_keepAliveInterval = TimeSpan.FromMilliseconds(50d);
41+
_keepAliveCount = 0;
42+
}
43+
44+
private void CreateMocks()
45+
{
46+
_serviceFactoryMock = new Mock<IServiceFactory>(MockBehavior.Strict);
47+
_sessionMock = new Mock<ISession>(MockBehavior.Strict);
48+
}
49+
50+
private void SetupMocks()
51+
{
52+
_serviceFactoryMock.Setup(p => p.CreateSession(_connectionInfo))
53+
.Returns(_sessionMock.Object);
54+
_sessionMock.Setup(p => p.Connect());
55+
_sessionMock.Setup(p => p.IsConnected).Returns(true);
56+
_sessionMock.Setup(p => p.TrySendMessage(It.IsAny<IgnoreMessage>()))
57+
.Returns(true)
58+
.Callback(() => Interlocked.Increment(ref _keepAliveCount));
59+
}
60+
61+
protected void Arrange()
62+
{
63+
SetupData();
64+
CreateMocks();
65+
SetupMocks();
66+
67+
_client = new MyClient(_connectionInfo, false, _serviceFactoryMock.Object);
68+
_client.Connect();
69+
}
70+
71+
protected void Act()
72+
{
73+
_client.KeepAliveInterval = _keepAliveInterval;
74+
75+
// allow keep-alive to be sent a few times
76+
Thread.Sleep(195);
77+
}
78+
79+
[TestMethod]
80+
public void KeepAliveIntervalShouldReturnConfiguredValue()
81+
{
82+
Assert.AreEqual(_keepAliveInterval, _client.KeepAliveInterval);
83+
}
84+
85+
[TestMethod]
86+
public void CreateSessionOnServiceFactoryShouldBeInvokedOnce()
87+
{
88+
_serviceFactoryMock.Verify(p => p.CreateSession(_connectionInfo), Times.Once);
89+
}
90+
91+
[TestMethod]
92+
public void ConnectOnSessionShouldBeInvokedOnce()
93+
{
94+
_sessionMock.Verify(p => p.Connect(), Times.Once);
95+
}
96+
97+
[TestMethod]
98+
public void IsConnectedOnSessionShouldBeInvokedOnce()
99+
{
100+
_sessionMock.Verify(p => p.IsConnected, Times.Once);
101+
}
102+
103+
[TestMethod]
104+
public void SendMessageOnSessionShouldBeInvokedThreeTimes()
105+
{
106+
_sessionMock.Verify(p => p.TrySendMessage(It.IsAny<IgnoreMessage>()), Times.Exactly(3));
107+
}
108+
109+
private class MyClient : BaseClient
110+
{
111+
public MyClient(ConnectionInfo connectionInfo, bool ownsConnectionInfo, IServiceFactory serviceFactory) : base(connectionInfo, ownsConnectionInfo, serviceFactory)
112+
{
113+
}
114+
}
115+
}
116+
}

src/Renci.SshNet.Tests/Classes/BaseClientTest_Connected_KeepAlivesNotSentConcurrently.cs

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,25 +34,38 @@ public void Cleanup()
3434
}
3535
}
3636

37-
protected void Arrange()
37+
private void SetupData()
3838
{
3939
_connectionInfo = new ConnectionInfo("host", "user", new PasswordAuthenticationMethod("user", "pwd"));
4040
_keepAliveSent = new ManualResetEvent(false);
41+
}
4142

43+
private void CreateMocks()
44+
{
4245
_serviceFactoryMock = new Mock<IServiceFactory>(MockBehavior.Strict);
4346
_sessionMock = new Mock<ISession>(MockBehavior.Strict);
47+
}
4448

49+
private void SetupMocks()
50+
{
4551
_mockSequence = new MockSequence();
4652

4753
_serviceFactoryMock.InSequence(_mockSequence).Setup(p => p.CreateSession(_connectionInfo)).Returns(_sessionMock.Object);
4854
_sessionMock.InSequence(_mockSequence).Setup(p => p.Connect());
4955
_sessionMock.InSequence(_mockSequence).Setup(p => p.TrySendMessage(It.IsAny<IgnoreMessage>()))
50-
.Returns(true)
51-
.Callback(() =>
52-
{
53-
Thread.Sleep(300);
54-
_keepAliveSent.Set();
55-
});
56+
.Returns(true)
57+
.Callback(() =>
58+
{
59+
Thread.Sleep(300);
60+
_keepAliveSent.Set();
61+
});
62+
}
63+
64+
protected void Arrange()
65+
{
66+
SetupData();
67+
CreateMocks();
68+
SetupMocks();
5669

5770
_client = new MyClient(_connectionInfo, false, _serviceFactoryMock.Object)
5871
{

0 commit comments

Comments
 (0)