Pārlūkot izejas kodu

Avoid leaking event subscriptions when an exception is thrown during initialization of ShellStream.
Handle error situations when initializing ShellStream.

Gert Driesen 8 gadi atpakaļ
vecāks
revīzija
7691cb0b55

+ 33 - 3
src/Renci.SshNet.Tests.NET35/Renci.SshNet.Tests.NET35.csproj

@@ -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>
@@ -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>
@@ -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>
@@ -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>
@@ -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>
@@ -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. 

+ 120 - 0
src/Renci.SshNet.Tests/Classes/ServiceFactoryTest_CreateShellStream_ChannelOpenThrowsException.cs

@@ -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);
+        }
+    }
+}

+ 120 - 0
src/Renci.SshNet.Tests/Classes/ServiceFactoryTest_CreateShellStream_SendPseudoTerminalRequestReturnsFalse.cs

@@ -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);
+        }
+    }
+}

+ 122 - 0
src/Renci.SshNet.Tests/Classes/ServiceFactoryTest_CreateShellStream_SendPseudoTerminalRequestThrowsException.cs

@@ -0,0 +1,122 @@
+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_SendPseudoTerminalRequestThrowsException
+    {
+        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 _sendPseudoTerminalRequestException;
+        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();
+            _sendPseudoTerminalRequestException = 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());
+            _channelSessionMock.InSequence(sequence)
+                               .Setup(p => p.SendPseudoTerminalRequest(_terminalName, _columns, _rows, _width, _height, _terminalModeValues))
+                               .Throws(_sendPseudoTerminalRequestException);
+            _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 CreateShellStreamShouldRethrowExceptionThrownBySendPseudoTerminalRequestOnChannelSession()
+        {
+            Assert.IsNotNull(_actualException);
+            Assert.AreSame(_sendPseudoTerminalRequestException, _actualException);
+        }
+
+        [TestMethod]
+        public void DisposeOnChannelSessionShouldHaveBeenInvokedOnce()
+        {
+            _channelSessionMock.Verify(p => p.Dispose(), Times.Once);
+        }
+    }
+}

+ 123 - 0
src/Renci.SshNet.Tests/Classes/ServiceFactoryTest_CreateShellStream_SendShellRequestReturnsFalse.cs

@@ -0,0 +1,123 @@
+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_SendShellRequestReturnsFalse
+    {
+        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(true);
+            _channelSessionMock.InSequence(sequence)
+                               .Setup(p => p.SendShellRequest())
+                               .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 request to start a shell 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);
+        }
+    }
+}

+ 124 - 0
src/Renci.SshNet.Tests/Classes/ServiceFactoryTest_CreateShellStream_SendShellRequestThrowsException.cs

@@ -0,0 +1,124 @@
+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_SendShellRequestThrowsException
+    {
+        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 _sendShellRequestException;
+        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();
+            _sendShellRequestException = 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());
+            _channelSessionMock.InSequence(sequence)
+                               .Setup(p => p.SendPseudoTerminalRequest(_terminalName, _columns, _rows, _width, _height, _terminalModeValues))
+                               .Returns(true);
+            _channelSessionMock.InSequence(sequence)
+                               .Setup(p => p.SendShellRequest())
+                               .Throws(_sendShellRequestException);
+            _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 CreateShellStreamShouldRethrowExceptionThrownBySendShellRequestOnChannelSession()
+        {
+            Assert.IsNotNull(_actualException);
+            Assert.AreSame(_sendShellRequestException, _actualException);
+        }
+
+        [TestMethod]
+        public void DisposeOnChannelSessionShouldHaveBeenInvokedOnce()
+        {
+            _channelSessionMock.Verify(p => p.Dispose(), Times.Once);
+        }
+    }
+}

+ 8 - 9
src/Renci.SshNet.Tests/Classes/ServiceFactoryTest_CreateShellStream.cs → src/Renci.SshNet.Tests/Classes/ServiceFactoryTest_CreateShellStream_Success.cs

@@ -9,12 +9,11 @@ using Renci.SshNet.Common;
 namespace Renci.SshNet.Tests.Classes
 {
     [TestClass]
-    public class ServiceFactoryTest_CreateShellStream
+    public class ServiceFactoryTest_CreateShellStream_Success
     {
         private Mock<ISession> _sessionMock;
         private Mock<IConnectionInfo> _connectionInfoMock;
         private Mock<IChannelSession> _channelSessionMock;
-        private MockSequence _mockSequence;
         private ServiceFactory _serviceFactory;
         private string _terminalName;
         private uint _columns;
@@ -47,20 +46,20 @@ namespace Renci.SshNet.Tests.Classes
 
         private void SetupMocks()
         {
-            _mockSequence = new MockSequence();
+            var sequence = new MockSequence();
 
-            _sessionMock.InSequence(_mockSequence)
+            _sessionMock.InSequence(sequence)
                         .Setup(p => p.ConnectionInfo)
                         .Returns(_connectionInfoMock.Object);
-            _connectionInfoMock.InSequence(_mockSequence)
+            _connectionInfoMock.InSequence(sequence)
                                .Setup(p => p.Encoding)
                                .Returns(new UTF8Encoding());
-            _sessionMock.InSequence(_mockSequence)
+            _sessionMock.InSequence(sequence)
                         .Setup(p => p.CreateChannelSession())
                         .Returns(_channelSessionMock.Object);
-            _channelSessionMock.InSequence(_mockSequence)
+            _channelSessionMock.InSequence(sequence)
                                .Setup(p => p.Open());
-            _channelSessionMock.InSequence(_mockSequence)
+            _channelSessionMock.InSequence(sequence)
                                .Setup(p => p.SendPseudoTerminalRequest(_terminalName,
                                                                        _columns,
                                                                        _rows,
@@ -68,7 +67,7 @@ namespace Renci.SshNet.Tests.Classes
                                                                        _height,
                                                                        _terminalModeValues))
                                .Returns(true);
-            _channelSessionMock.InSequence(_mockSequence)
+            _channelSessionMock.InSequence(sequence)
                                .Setup(p => p.SendShellRequest())
                                .Returns(true);
         }

+ 11 - 3
src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj

@@ -96,6 +96,7 @@
     <Compile Include="Classes\BaseClientTest_NotConnected_KeepAliveInterval_NotNegativeOne.cs" />
     <Compile Include="Classes\Channels\ChannelDirectTcpipTest.cs" />
     <Compile Include="Classes\Channels\ChannelDirectTcpipTest_Dispose_SessionIsConnectedAndChannelIsOpen.cs" />
+    <Compile Include="Classes\Channels\ChannelSessionTestBase.cs" />
     <Compile Include="Classes\Channels\ChannelSessionTest_Dispose_Disposed.cs" />
     <Compile Include="Classes\Channels\ChannelSessionTest_Dispose_SessionIsConnectedAndChannelIsOpen_ChannelCloseAndChannelEofReceived_SendChannelCloseMessageFailure.cs" />
     <Compile Include="Classes\Channels\ChannelSessionTest_Dispose_SessionIsConnectedAndChannelIsOpen_ChannelCloseAndChannelEofReceived_SendChannelCloseMessageSuccess.cs" />
@@ -114,10 +115,11 @@
     <Compile Include="Classes\Channels\ChannelSessionTest_Open_ExceptionWaitingOnOpenConfirmation.cs" />
     <Compile Include="Classes\Channels\ChannelSessionTest_Open_OnOpenFailureReceived_NoRetriesAvailable.cs" />
     <Compile Include="Classes\Channels\ChannelSessionTest_Open_OnOpenFailureReceived_RetriesAvalable.cs" />
+    <Compile Include="Classes\Channels\ChannelTestBase.cs" />
     <Compile Include="Classes\Channels\ChannelTest_Dispose_SessionIsConnectedAndChannelIsOpen_EofNotReceived_SendEofInvoked.cs" />
     <Compile Include="Classes\Channels\ChannelTest_Dispose_SessionIsConnectedAndChannelIsOpen_EofReceived.cs" />
-    <Compile Include="Classes\Channels\ChannelTest_Dispose_SessionIsConnectedAndChannelIsOpen_EofReceived_ConnectionExceptionWaitingForChannelCloseMessage.cs" />
-    <Compile Include="Classes\Channels\ChannelTest_Dispose_SessionIsConnectedAndChannelIsOpen_EofReceived_OperationTimeoutExceptionWaitingForChannelCloseMessage.cs" />
+    <Compile Include="Classes\Channels\ChannelTest_Dispose_SessionIsConnectedAndChannelIsOpen_EofReceived_DisconnectWaitingForChannelCloseMessage.cs" />
+    <Compile Include="Classes\Channels\ChannelTest_Dispose_SessionIsConnectedAndChannelIsOpen_EofReceived_TimeoutWaitingForChannelCloseMessage.cs" />
     <Compile Include="Classes\Channels\ChannelTest_Dispose_SessionIsNotConnectedAndChannelIsOpen.cs" />
     <Compile Include="Classes\Channels\ChannelTest_OnSessionChannelCloseReceived_SessionIsConnectedAndChannelIsOpen_DisposeChannelInClosedEventHandler.cs" />
     <Compile Include="Classes\Channels\ChannelTest_OnSessionChannelCloseReceived_OnClose_Exception.cs" />
@@ -287,7 +289,12 @@
     <Compile Include="Classes\ServiceFactoryTest_CreateSftpFileReader_FileSizeIsLittleMoreThanFiveTimesGreaterThanChunkSize.cs" />
     <Compile Include="Classes\ServiceFactoryTest_CreateSftpFileReader_FileSizeIsMoreThanTenTimesGreaterThanChunkSize.cs" />
     <Compile Include="Classes\ServiceFactoryTest_CreateSftpFileReader_FileSizeIsZero.cs" />
-    <Compile Include="Classes\ServiceFactoryTest_CreateShellStream.cs" />
+    <Compile Include="Classes\ServiceFactoryTest_CreateShellStream_ChannelOpenThrowsException.cs" />
+    <Compile Include="Classes\ServiceFactoryTest_CreateShellStream_SendPseudoTerminalRequestReturnsFalse.cs" />
+    <Compile Include="Classes\ServiceFactoryTest_CreateShellStream_SendPseudoTerminalRequestThrowsException.cs" />
+    <Compile Include="Classes\ServiceFactoryTest_CreateShellStream_SendShellRequestReturnsFalse.cs" />
+    <Compile Include="Classes\ServiceFactoryTest_CreateShellStream_SendShellRequestThrowsException.cs" />
+    <Compile Include="Classes\ServiceFactoryTest_CreateShellStream_Success.cs" />
     <Compile Include="Classes\SessionTest_Connected.cs" />
     <Compile Include="Classes\SessionTest_ConnectedBase.cs" />
     <Compile Include="Classes\SessionTest_Connected_ConnectionReset.cs" />
@@ -297,6 +304,7 @@
     <Compile Include="Classes\SessionTest_Connected_ServerSendsDisconnectMessage.cs" />
     <Compile Include="Classes\SessionTest_Connected_ServerSendsBadPacket.cs" />
     <Compile Include="Classes\SessionTest_Connected_ServerSendsDisconnectMessageAndShutsDownSocket.cs" />
+    <Compile Include="Classes\SessionTest_Connected_ServerSendsUnsupportedMessageType.cs" />
     <Compile Include="Classes\SessionTest_Connected_ServerShutsDownSendAfterSendingIncompletePacket.cs" />
     <Compile Include="Classes\SessionTest_Connected_ServerShutsDownSocket.cs" />
     <Compile Include="Classes\SessionTest_NotConnected.cs" />

+ 21 - 4
src/Renci.SshNet/ShellStream.cs

@@ -64,7 +64,6 @@ namespace Renci.SshNet
             get { return _bufferSize; }
         }
 
-
         /// <summary>
         /// Initializes a new <see cref="ShellStream"/> instance.
         /// </summary>
@@ -76,6 +75,9 @@ namespace Renci.SshNet
         /// <param name="height">The terminal height in pixels.</param>
         /// <param name="terminalModeValues">The terminal mode values.</param>
         /// <param name="bufferSize">The size of the buffer.</param>
+        /// <exception cref="SshException">The channel could not be opened.</exception>
+        /// <exception cref="SshException">The pseudo-terminal request was not accepted by the server.</exception>
+        /// <exception cref="SshException">The request to start a shell was not accepted by the server.</exception>
         internal ShellStream(ISession session, string terminalName, uint columns, uint rows, uint width, uint height, IDictionary<TerminalModes, uint> terminalModeValues, int bufferSize)
         {
             _encoding = session.ConnectionInfo.Encoding;
@@ -90,9 +92,24 @@ namespace Renci.SshNet
             _session.Disconnected += Session_Disconnected;
             _session.ErrorOccured += Session_ErrorOccured;
 
-            _channel.Open();
-            _channel.SendPseudoTerminalRequest(terminalName, columns, rows, width, height, terminalModeValues);
-            _channel.SendShellRequest();
+            try
+            {
+                _channel.Open();
+                if (!_channel.SendPseudoTerminalRequest(terminalName, columns, rows, width, height, terminalModeValues))
+                {
+                    throw new SshException("The pseudo-terminal request was not accepted by the server. Consult the server log for more information.");
+                }
+                if (!_channel.SendShellRequest())
+                {
+                    throw new SshException("The request to start a shell was not accepted by the server. Consult the server log for more information.");
+                }
+            }
+            catch
+            {
+                UnsubscribeFromSessionEvents(session);
+                _channel.Dispose();
+                throw;
+            }
         }
 
         #region Stream overide methods