Skip to content

Commit

Permalink
Avoid leaking event subscriptions when an exception is thrown during …
Browse files Browse the repository at this point in the history
…initialization of ShellStream.

Handle error situations when initializing ShellStream.
  • Loading branch information
drieseng committed Nov 27, 2017
1 parent 69882b3 commit 7691cb0
Show file tree
Hide file tree
Showing 9 changed files with 682 additions and 19 deletions.
36 changes: 33 additions & 3 deletions src/Renci.SshNet.Tests.NET35/Renci.SshNet.Tests.NET35.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@
<Compile Include="..\Renci.SshNet.Tests\Classes\Channels\ChannelForwardedTcpipTest_Dispose_SessionIsConnectedAndChannelIsOpen.cs">
<Link>Classes\Channels\ChannelForwardedTcpipTest_Dispose_SessionIsConnectedAndChannelIsOpen.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet.Tests\Classes\Channels\ChannelSessionTestBase.cs">
<Link>Classes\Channels\ChannelSessionTestBase.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet.Tests\Classes\Channels\ChannelSessionTest_Disposed_Closed.cs">
<Link>Classes\Channels\ChannelSessionTest_Disposed_Closed.cs</Link>
</Compile>
Expand Down Expand Up @@ -159,6 +162,9 @@
<Compile Include="..\Renci.SshNet.Tests\Classes\Channels\ChannelStub.cs">
<Link>Classes\Channels\ChannelStub.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet.Tests\Classes\Channels\ChannelTestBase.cs">
<Link>Classes\Channels\ChannelTestBase.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet.Tests\Classes\Channels\ChannelTest_Dispose_SessionIsConnectedAndChannelIsNotOpen.cs">
<Link>Classes\Channels\ChannelTest_Dispose_SessionIsConnectedAndChannelIsNotOpen.cs</Link>
</Compile>
Expand All @@ -171,6 +177,12 @@
<Compile Include="..\Renci.SshNet.Tests\Classes\Channels\ChannelTest_Dispose_SessionIsConnectedAndChannelIsOpen_EofReceived.cs">
<Link>Classes\Channels\ChannelTest_Dispose_SessionIsConnectedAndChannelIsOpen_EofReceived.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet.Tests\Classes\Channels\ChannelTest_Dispose_SessionIsConnectedAndChannelIsOpen_EofReceived_DisconnectWaitingForChannelCloseMessage.cs">
<Link>Classes\Channels\ChannelTest_Dispose_SessionIsConnectedAndChannelIsOpen_EofReceived_DisconnectWaitingForChannelCloseMessage.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet.Tests\Classes\Channels\ChannelTest_Dispose_SessionIsConnectedAndChannelIsOpen_EofReceived_TimeoutWaitingForChannelCloseMessage.cs">
<Link>Classes\Channels\ChannelTest_Dispose_SessionIsConnectedAndChannelIsOpen_EofReceived_TimeoutWaitingForChannelCloseMessage.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet.Tests\Classes\Channels\ChannelTest_Dispose_SessionIsNotConnectedAndChannelIsNotOpen.cs">
<Link>Classes\Channels\ChannelTest_Dispose_SessionIsNotConnectedAndChannelIsNotOpen.cs</Link>
</Compile>
Expand Down Expand Up @@ -951,8 +963,23 @@
<Compile Include="..\Renci.SshNet.Tests\Classes\ServiceFactoryTest_CreateSftpFileReader_FileSizeIsZero.cs">
<Link>Classes\ServiceFactoryTest_CreateSftpFileReader_FileSizeIsZero.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet.Tests\Classes\ServiceFactoryTest_CreateShellStream.cs">
<Link>Classes\ServiceFactoryTest_CreateShellStream.cs</Link>
<Compile Include="..\Renci.SshNet.Tests\Classes\ServiceFactoryTest_CreateShellStream_ChannelOpenThrowsException.cs">
<Link>Classes\ServiceFactoryTest_CreateShellStream_ChannelOpenThrowsException.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet.Tests\Classes\ServiceFactoryTest_CreateShellStream_SendPseudoTerminalRequestReturnsFalse.cs">
<Link>Classes\ServiceFactoryTest_CreateShellStream_SendPseudoTerminalRequestReturnsFalse.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet.Tests\Classes\ServiceFactoryTest_CreateShellStream_SendPseudoTerminalRequestThrowsException.cs">
<Link>Classes\ServiceFactoryTest_CreateShellStream_SendPseudoTerminalRequestThrowsException.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet.Tests\Classes\ServiceFactoryTest_CreateShellStream_SendShellRequestReturnsFalse.cs">
<Link>Classes\ServiceFactoryTest_CreateShellStream_SendShellRequestReturnsFalse.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet.Tests\Classes\ServiceFactoryTest_CreateShellStream_SendShellRequestThrowsException.cs">
<Link>Classes\ServiceFactoryTest_CreateShellStream_SendShellRequestThrowsException.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet.Tests\Classes\ServiceFactoryTest_CreateShellStream_Success.cs">
<Link>Classes\ServiceFactoryTest_CreateShellStream_Success.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet.Tests\Classes\SessionTest.cs">
<Link>Classes\SessionTest.cs</Link>
Expand Down Expand Up @@ -987,6 +1014,9 @@
<Compile Include="..\Renci.SshNet.Tests\Classes\SessionTest_Connected_ServerSendsDisconnectMessageAndShutsDownSocket.cs">
<Link>Classes\SessionTest_Connected_ServerSendsDisconnectMessageAndShutsDownSocket.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet.Tests\Classes\SessionTest_Connected_ServerSendsUnsupportedMessageType.cs">
<Link>Classes\SessionTest_Connected_ServerSendsUnsupportedMessageType.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet.Tests\Classes\SessionTest_Connected_ServerShutsDownSendAfterSendingIncompletePacket.cs">
<Link>Classes\SessionTest_Connected_ServerShutsDownSendAfterSendingIncompletePacket.cs</Link>
</Compile>
Expand Down Expand Up @@ -1710,7 +1740,7 @@
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ProjectExtensions>
<VisualStudio>
<UserProperties ProjectLinkReference="c45379b9-17b1-4e89-bc2e-6d41726413e8" ProjectLinkerExcludeFilter="\\?desktop(\\.*)?$;\\?silverlight(\\.*)?$;\.desktop;\.silverlight;\.xaml;^service references(\\.*)?$;\.clientconfig;^web references(\\.*)?$" />
<UserProperties ProjectLinkerExcludeFilter="\\?desktop(\\.*)?$;\\?silverlight(\\.*)?$;\.desktop;\.silverlight;\.xaml;^service references(\\.*)?$;\.clientconfig;^web references(\\.*)?$" ProjectLinkReference="c45379b9-17b1-4e89-bc2e-6d41726413e8" />
</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,120 @@
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Renci.SshNet.Channels;
using Renci.SshNet.Common;

namespace Renci.SshNet.Tests.Classes
{
[TestClass]
public class ServiceFactoryTest_CreateShellStream_ChannelOpenThrowsException
{
private Mock<ISession> _sessionMock;
private Mock<IConnectionInfo> _connectionInfoMock;
private Mock<IChannelSession> _channelSessionMock;
private ServiceFactory _serviceFactory;
private string _terminalName;
private uint _columns;
private uint _rows;
private uint _width;
private uint _height;
private IDictionary<TerminalModes, uint> _terminalModeValues;
private int _bufferSize;
private SshException _channelOpenException;
private SshException _actualException;

private void SetupData()
{
var random = new Random();

_terminalName = random.Next().ToString();
_columns = (uint) random.Next();
_rows = (uint) random.Next();
_width = (uint) random.Next();
_height = (uint) random.Next();
_terminalModeValues = new Dictionary<TerminalModes, uint>();
_bufferSize = random.Next();
_channelOpenException = new SshException();

_actualException = null;
}

private void CreateMocks()
{
_sessionMock = new Mock<ISession>(MockBehavior.Strict);
_connectionInfoMock = new Mock<IConnectionInfo>(MockBehavior.Strict);
_channelSessionMock = new Mock<IChannelSession>(MockBehavior.Strict);
}

private void SetupMocks()
{
var sequence = new MockSequence();

_sessionMock.InSequence(sequence)
.Setup(p => p.ConnectionInfo)
.Returns(_connectionInfoMock.Object);
_connectionInfoMock.InSequence(sequence)
.Setup(p => p.Encoding)
.Returns(new UTF8Encoding());
_sessionMock.InSequence(sequence)
.Setup(p => p.CreateChannelSession())
.Returns(_channelSessionMock.Object);
_channelSessionMock.InSequence(sequence)
.Setup(p => p.Open())
.Throws(_channelOpenException);
_channelSessionMock.InSequence(sequence)
.Setup(p => p.Dispose());
}

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

_serviceFactory = new ServiceFactory();
}

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

private void Act()
{
try
{
_serviceFactory.CreateShellStream(_sessionMock.Object,
_terminalName,
_columns,
_rows,
_width,
_height,
_terminalModeValues,
_bufferSize);
Assert.Fail();
}
catch (SshException ex)
{
_actualException = ex;
}
}

[TestMethod]
public void CreateShellStreamShouldRethrowExceptionThrownByOpenOnChannelSession()
{
Assert.IsNotNull(_actualException);
Assert.AreSame(_channelOpenException, _actualException);
}

[TestMethod]
public void DisposeOnChannelSessionShouldHaveBeenInvokedOnce()
{
_channelSessionMock.Verify(p => p.Dispose(), Times.Once);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Renci.SshNet.Channels;
using Renci.SshNet.Common;

namespace Renci.SshNet.Tests.Classes
{
[TestClass]
public class ServiceFactoryTest_CreateShellStream_SendPseudoTerminalRequestReturnsFalse
{
private Mock<ISession> _sessionMock;
private Mock<IConnectionInfo> _connectionInfoMock;
private Mock<IChannelSession> _channelSessionMock;
private ServiceFactory _serviceFactory;
private string _terminalName;
private uint _columns;
private uint _rows;
private uint _width;
private uint _height;
private IDictionary<TerminalModes, uint> _terminalModeValues;
private int _bufferSize;
private SshException _actualException;

private void SetupData()
{
var random = new Random();

_terminalName = random.Next().ToString();
_columns = (uint)random.Next();
_rows = (uint)random.Next();
_width = (uint)random.Next();
_height = (uint)random.Next();
_terminalModeValues = new Dictionary<TerminalModes, uint>();
_bufferSize = random.Next();
_actualException = null;
}

private void CreateMocks()
{
_sessionMock = new Mock<ISession>(MockBehavior.Strict);
_connectionInfoMock = new Mock<IConnectionInfo>(MockBehavior.Strict);
_channelSessionMock = new Mock<IChannelSession>(MockBehavior.Strict);
}

private void SetupMocks()
{
var sequence = new MockSequence();

_sessionMock.InSequence(sequence)
.Setup(p => p.ConnectionInfo)
.Returns(_connectionInfoMock.Object);
_connectionInfoMock.InSequence(sequence)
.Setup(p => p.Encoding)
.Returns(new UTF8Encoding());
_sessionMock.InSequence(sequence)
.Setup(p => p.CreateChannelSession())
.Returns(_channelSessionMock.Object);
_channelSessionMock.InSequence(sequence)
.Setup(p => p.Open());
_channelSessionMock.InSequence(sequence)
.Setup(p => p.SendPseudoTerminalRequest(_terminalName, _columns, _rows, _width, _height, _terminalModeValues))
.Returns(false);
_channelSessionMock.InSequence(sequence)
.Setup(p => p.Dispose());
}

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

_serviceFactory = new ServiceFactory();
}

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

private void Act()
{
try
{
_serviceFactory.CreateShellStream(_sessionMock.Object,
_terminalName,
_columns,
_rows,
_width,
_height,
_terminalModeValues,
_bufferSize);
Assert.Fail();
}
catch (SshException ex)
{
_actualException = ex;
}
}

[TestMethod]
public void CreateShellStreamShouldThrowSshException()
{
Assert.IsNotNull(_actualException);
Assert.IsNull(_actualException.InnerException);
Assert.AreEqual("The pseudo-terminal request was not accepted by the server. Consult the server log for more information.", _actualException.Message);
}

[TestMethod]
public void DisposeOnChannelSessionShouldHaveBeenInvokedOnce()
{
_channelSessionMock.Verify(p => p.Dispose(), Times.Once);
}
}
}
Loading

0 comments on commit 7691cb0

Please sign in to comment.