Merge pull request #320 from sshnet/channel_closed

Channel: Raise the Closed event as part of the Close() method
This commit is contained in:
Gert Driesen
2017-10-16 21:29:37 +02:00
committed by GitHub
10 changed files with 545 additions and 29 deletions
@@ -21,7 +21,7 @@ namespace Renci.SshNet.Tests.Classes.Channels
private uint _remotePacketSize;
private ChannelStub _channel;
private Stopwatch _closeTimer;
private ManualResetEvent _channelClosedWaitHandle;
private ManualResetEvent _channelClosedEventHandlerCompleted;
private List<ChannelEventArgs> _channelClosedRegister;
private IList<ExceptionEventArgs> _channelExceptionRegister;
@@ -43,7 +43,7 @@ namespace Renci.SshNet.Tests.Classes.Channels
_remotePacketSize = (uint)random.Next(0, int.MaxValue);
_closeTimer = new Stopwatch();
_channelClosedRegister = new List<ChannelEventArgs>();
_channelClosedWaitHandle = new ManualResetEvent(false);
_channelClosedEventHandlerCompleted = new ManualResetEvent(false);
_channelExceptionRegister = new List<ExceptionEventArgs>();
_sessionMock = new Mock<ISession>(MockBehavior.Strict);
@@ -80,7 +80,8 @@ namespace Renci.SshNet.Tests.Classes.Channels
_channel.Closed += (sender, args) =>
{
_channelClosedRegister.Add(args);
_channelClosedWaitHandle.Set();
Thread.Sleep(50);
_channelClosedEventHandlerCompleted.Set();
};
_channel.Exception += (sender, args) => _channelExceptionRegister.Add(args);
_channel.InitializeRemoteChannelInfo(_remoteChannelNumber, _remoteWindowSize, _remotePacketSize);
@@ -129,12 +130,16 @@ namespace Renci.SshNet.Tests.Classes.Channels
[TestMethod]
public void ClosedEventShouldHaveFiredOnce()
{
_channelClosedWaitHandle.WaitOne(100);
Assert.AreEqual(1, _channelClosedRegister.Count);
Assert.AreEqual(_localChannelNumber, _channelClosedRegister[0].ChannelNumber);
}
[TestMethod]
public void DisposeShouldBlockUntilClosedEventHandlerHasCompleted()
{
Assert.IsTrue(_channelClosedEventHandlerCompleted.WaitOne(0));
}
[TestMethod]
public void ExceptionShouldNeverHaveFired()
{
@@ -10,6 +10,7 @@ using Renci.SshNet.Messages.Connection;
namespace Renci.SshNet.Tests.Classes.Channels
{
[TestClass]
[Ignore]
public class ChannelTest_Dispose_SessionIsConnectedAndChannelIsOpen_EofNotReceived_SendEofInvoked
{
private Mock<ISession> _sessionMock;
@@ -21,7 +22,7 @@ namespace Renci.SshNet.Tests.Classes.Channels
private uint _remotePacketSize;
private ChannelStub _channel;
private Stopwatch _closeTimer;
private ManualResetEvent _channelClosedWaitHandle;
private ManualResetEvent _channelClosedEventHandlerCompleted;
private List<ChannelEventArgs> _channelClosedRegister;
private IList<ExceptionEventArgs> _channelExceptionRegister;
@@ -32,6 +33,16 @@ namespace Renci.SshNet.Tests.Classes.Channels
Act();
}
[TestCleanup]
public void TearDown()
{
if (_channelClosedEventHandlerCompleted != null)
{
_channelClosedEventHandlerCompleted.Dispose();
_channelClosedEventHandlerCompleted = null;
}
}
private void Arrange()
{
var random = new Random();
@@ -42,7 +53,7 @@ namespace Renci.SshNet.Tests.Classes.Channels
_remoteWindowSize = (uint)random.Next(0, int.MaxValue);
_remotePacketSize = (uint)random.Next(0, int.MaxValue);
_closeTimer = new Stopwatch();
_channelClosedWaitHandle = new ManualResetEvent(false);
_channelClosedEventHandlerCompleted = new ManualResetEvent(false);
_channelClosedRegister = new List<ChannelEventArgs>();
_channelExceptionRegister = new List<ExceptionEventArgs>();
@@ -80,12 +91,13 @@ namespace Renci.SshNet.Tests.Classes.Channels
_channel.Closed += (sender, args) =>
{
_channelClosedRegister.Add(args);
_channelClosedWaitHandle.Set();
Thread.Sleep(50);
_channelClosedEventHandlerCompleted.Set();
};
_channel.Exception += (sender, args) => _channelExceptionRegister.Add(args);
_channel.InitializeRemoteChannelInfo(_remoteChannelNumber, _remoteWindowSize, _remotePacketSize);
_channel.SetIsOpen(true);
_channel.SendEof();
//_channel.SendEof();
}
private void Act()
@@ -130,12 +142,16 @@ namespace Renci.SshNet.Tests.Classes.Channels
[TestMethod]
public void ClosedEventShouldHaveFiredOnce()
{
_channelClosedWaitHandle.WaitOne(100);
Assert.AreEqual(1, _channelClosedRegister.Count);
Assert.AreEqual(_localChannelNumber, _channelClosedRegister[0].ChannelNumber);
}
[TestMethod]
public void DisposeShouldBlockUntilClosedEventHandlerHasCompleted()
{
Assert.IsTrue(_channelClosedEventHandlerCompleted.WaitOne(0));
}
[TestMethod]
public void ExceptionShouldNeverHaveFired()
{
@@ -23,6 +23,7 @@ namespace Renci.SshNet.Tests.Classes.Channels
private List<ChannelEventArgs> _channelEndOfDataRegister;
private IList<ExceptionEventArgs> _channelExceptionRegister;
private ManualResetEvent _channelClosedReceived;
private ManualResetEvent _channelClosedEventHandlerCompleted;
private Thread _raiseChannelCloseReceivedThread;
private void SetupData()
@@ -39,6 +40,7 @@ namespace Renci.SshNet.Tests.Classes.Channels
_channelEndOfDataRegister = new List<ChannelEventArgs>();
_channelExceptionRegister = new List<ExceptionEventArgs>();
_channelClosedReceived = new ManualResetEvent(false);
_channelClosedEventHandlerCompleted = new ManualResetEvent(false);
_raiseChannelCloseReceivedThread = null;
}
@@ -106,6 +108,12 @@ namespace Renci.SshNet.Tests.Classes.Channels
_raiseChannelCloseReceivedThread.Abort();
}
}
if (_channelClosedEventHandlerCompleted != null)
{
_channelClosedEventHandlerCompleted.Dispose();
_channelClosedEventHandlerCompleted = null;
}
}
private void Arrange()
@@ -115,7 +123,12 @@ namespace Renci.SshNet.Tests.Classes.Channels
SetupMocks();
_channel = new ChannelStub(_sessionMock.Object, _localChannelNumber, _localWindowSize, _localPacketSize);
_channel.Closed += (sender, args) => _channelClosedRegister.Add(args);
_channel.Closed += (sender, args) =>
{
_channelClosedRegister.Add(args);
Thread.Sleep(50);
_channelClosedEventHandlerCompleted.Set();
};
_channel.EndOfData += (sender, args) => _channelEndOfDataRegister.Add(args);
_channel.Exception += (sender, args) => _channelExceptionRegister.Add(args);
_channel.InitializeRemoteChannelInfo(_remoteChannelNumber, _remoteWindowSize, _remotePacketSize);
@@ -173,7 +186,7 @@ namespace Renci.SshNet.Tests.Classes.Channels
}
[TestMethod]
public void EndOfDataEventShouldHaveFiredOnce()
public void EndOfDataEventShouldNotHaveFired()
{
Assert.AreEqual(1, _channelEndOfDataRegister.Count);
Assert.AreEqual(_localChannelNumber, _channelEndOfDataRegister[0].ChannelNumber);
@@ -0,0 +1,141 @@
using System;
using System.Collections.Generic;
using System.Threading;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Renci.SshNet.Common;
using Renci.SshNet.Messages.Connection;
namespace Renci.SshNet.Tests.Classes.Channels
{
[TestClass]
public class ChannelTest_Dispose_SessionIsConnectedAndChannelIsOpen_EofReceived_ConnectionExceptionWaitingForChannelCloseMessage
{
private Mock<ISession> _sessionMock;
private uint _localChannelNumber;
private uint _localWindowSize;
private uint _localPacketSize;
private uint _remoteChannelNumber;
private uint _remoteWindowSize;
private uint _remotePacketSize;
private ChannelStub _channel;
private List<ChannelEventArgs> _channelClosedRegister;
private List<ChannelEventArgs> _channelEndOfDataRegister;
private IList<ExceptionEventArgs> _channelExceptionRegister;
private SshConnectionException _connectionException;
private void SetupData()
{
var random = new Random();
_localChannelNumber = (uint)random.Next(0, int.MaxValue);
_localWindowSize = (uint)random.Next(0, int.MaxValue);
_localPacketSize = (uint)random.Next(0, int.MaxValue);
_remoteChannelNumber = (uint)random.Next(0, int.MaxValue);
_remoteWindowSize = (uint)random.Next(0, int.MaxValue);
_remotePacketSize = (uint)random.Next(0, int.MaxValue);
_channelClosedRegister = new List<ChannelEventArgs>();
_channelEndOfDataRegister = new List<ChannelEventArgs>();
_channelExceptionRegister = new List<ExceptionEventArgs>();
_connectionException = new SshConnectionException();
}
private void CreateMocks()
{
_sessionMock = new Mock<ISession>(MockBehavior.Strict);
}
private void SetupMocks()
{
var sequence = new MockSequence();
_sessionMock.InSequence(sequence).Setup(p => p.IsConnected).Returns(true);
_sessionMock.InSequence(sequence).Setup(p => p.TrySendMessage(It.Is<ChannelCloseMessage>(c => c.LocalChannelNumber == _remoteChannelNumber))).Returns(true);
_sessionMock.InSequence(sequence).Setup(p => p.WaitOnHandle(It.IsAny<EventWaitHandle>()))
.Callback<WaitHandle>(w =>
{
throw _connectionException;
});
}
[TestInitialize]
public void Initialize()
{
Arrange();
Act();
}
private void Arrange()
{
SetupData();
CreateMocks();
SetupMocks();
_channel = new ChannelStub(_sessionMock.Object, _localChannelNumber, _localWindowSize, _localPacketSize);
_channel.Closed += (sender, args) =>
{
_channelClosedRegister.Add(args);
};
_channel.EndOfData += (sender, args) => _channelEndOfDataRegister.Add(args);
_channel.Exception += (sender, args) => _channelExceptionRegister.Add(args);
_channel.InitializeRemoteChannelInfo(_remoteChannelNumber, _remoteWindowSize, _remotePacketSize);
_channel.SetIsOpen(true);
_sessionMock.Raise(
s => s.ChannelEofReceived += null,
new MessageEventArgs<ChannelEofMessage>(new ChannelEofMessage(_localChannelNumber)));
}
private void Act()
{
_channel.Dispose();
}
[TestMethod]
public void IsOpenShouldReturnFalse()
{
Assert.IsFalse(_channel.IsOpen);
}
[TestMethod]
public void TrySendMessageOnSessionShouldBeInvokedOnceForChannelCloseMessage()
{
_sessionMock.Verify(
p => p.TrySendMessage(It.Is<ChannelCloseMessage>(c => c.LocalChannelNumber == _remoteChannelNumber)),
Times.Once);
}
[TestMethod]
public void TrySendMessageOnSessionShouldNeverBeInvokedForChannelEofMessage()
{
_sessionMock.Verify(
p => p.TrySendMessage(It.Is<ChannelEofMessage>(c => c.LocalChannelNumber == _remoteChannelNumber)),
Times.Never);
}
[TestMethod]
public void WaitOnHandleOnSessionShouldBeInvokedOnce()
{
_sessionMock.Verify(p => p.WaitOnHandle(It.IsAny<EventWaitHandle>()), Times.Once);
}
[TestMethod]
public void ClosedEventShouldNotHaveFired()
{
Assert.AreEqual(0, _channelClosedRegister.Count);
}
[TestMethod]
public void EndOfDataEventShouldNotHaveFired()
{
Assert.AreEqual(1, _channelEndOfDataRegister.Count);
Assert.AreEqual(_localChannelNumber, _channelEndOfDataRegister[0].ChannelNumber);
}
[TestMethod]
public void ExceptionShouldNeverHaveFired()
{
Assert.AreEqual(0, _channelExceptionRegister.Count);
}
}
}
@@ -0,0 +1,157 @@
using System;
using System.Collections.Generic;
using System.Threading;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Renci.SshNet.Common;
using Renci.SshNet.Messages.Connection;
namespace Renci.SshNet.Tests.Classes.Channels
{
[TestClass]
public class ChannelTest_Dispose_SessionIsConnectedAndChannelIsOpen_EofReceived_OperationTimeoutExceptionWaitingForChannelCloseMessage
{
private Mock<ISession> _sessionMock;
private uint _localChannelNumber;
private uint _localWindowSize;
private uint _localPacketSize;
private uint _remoteChannelNumber;
private uint _remoteWindowSize;
private uint _remotePacketSize;
private ChannelStub _channel;
private List<ChannelEventArgs> _channelClosedRegister;
private List<ChannelEventArgs> _channelEndOfDataRegister;
private IList<ExceptionEventArgs> _channelExceptionRegister;
private SshOperationTimeoutException _operationTimeoutException;
private SshOperationTimeoutException _actualException;
private void SetupData()
{
var random = new Random();
_localChannelNumber = (uint)random.Next(0, int.MaxValue);
_localWindowSize = (uint)random.Next(0, int.MaxValue);
_localPacketSize = (uint)random.Next(0, int.MaxValue);
_remoteChannelNumber = (uint)random.Next(0, int.MaxValue);
_remoteWindowSize = (uint)random.Next(0, int.MaxValue);
_remotePacketSize = (uint)random.Next(0, int.MaxValue);
_channelClosedRegister = new List<ChannelEventArgs>();
_channelEndOfDataRegister = new List<ChannelEventArgs>();
_channelExceptionRegister = new List<ExceptionEventArgs>();
_operationTimeoutException = new SshOperationTimeoutException();
_actualException = null;
}
private void CreateMocks()
{
_sessionMock = new Mock<ISession>(MockBehavior.Strict);
}
private void SetupMocks()
{
var sequence = new MockSequence();
_sessionMock.InSequence(sequence).Setup(p => p.IsConnected).Returns(true);
_sessionMock.InSequence(sequence).Setup(p => p.TrySendMessage(It.Is<ChannelCloseMessage>(c => c.LocalChannelNumber == _remoteChannelNumber))).Returns(true);
_sessionMock.InSequence(sequence).Setup(p => p.WaitOnHandle(It.IsAny<EventWaitHandle>()))
.Callback<WaitHandle>(w =>
{
throw _operationTimeoutException;
});
}
[TestInitialize]
public void Initialize()
{
Arrange();
Act();
}
private void Arrange()
{
SetupData();
CreateMocks();
SetupMocks();
_channel = new ChannelStub(_sessionMock.Object, _localChannelNumber, _localWindowSize, _localPacketSize);
_channel.Closed += (sender, args) =>
{
_channelClosedRegister.Add(args);
};
_channel.EndOfData += (sender, args) => _channelEndOfDataRegister.Add(args);
_channel.Exception += (sender, args) => _channelExceptionRegister.Add(args);
_channel.InitializeRemoteChannelInfo(_remoteChannelNumber, _remoteWindowSize, _remotePacketSize);
_channel.SetIsOpen(true);
_sessionMock.Raise(
s => s.ChannelEofReceived += null,
new MessageEventArgs<ChannelEofMessage>(new ChannelEofMessage(_localChannelNumber)));
}
private void Act()
{
try
{
_channel.Dispose();
}
catch (SshOperationTimeoutException ex)
{
_actualException = ex;
}
}
[TestMethod]
public void IsOpenShouldReturnTrue()
{
Assert.IsTrue(_channel.IsOpen);
}
[TestMethod]
public void TrySendMessageOnSessionShouldBeInvokedOnceForChannelCloseMessage()
{
_sessionMock.Verify(
p => p.TrySendMessage(It.Is<ChannelCloseMessage>(c => c.LocalChannelNumber == _remoteChannelNumber)),
Times.Once);
}
[TestMethod]
public void TrySendMessageOnSessionShouldNeverBeInvokedForChannelEofMessage()
{
_sessionMock.Verify(
p => p.TrySendMessage(It.Is<ChannelEofMessage>(c => c.LocalChannelNumber == _remoteChannelNumber)),
Times.Never);
}
[TestMethod]
public void WaitOnHandleOnSessionShouldBeInvokedOnce()
{
_sessionMock.Verify(p => p.WaitOnHandle(It.IsAny<EventWaitHandle>()), Times.Once);
}
[TestMethod]
public void ClosedEventShouldNotHaveFired()
{
Assert.AreEqual(0, _channelClosedRegister.Count);
}
[TestMethod]
public void EndOfDataEventShouldNotHaveFired()
{
Assert.AreEqual(1, _channelEndOfDataRegister.Count);
Assert.AreEqual(_localChannelNumber, _channelEndOfDataRegister[0].ChannelNumber);
}
[TestMethod]
public void ExceptionShouldNeverHaveFired()
{
Assert.AreEqual(0, _channelExceptionRegister.Count);
}
[TestMethod]
public void DisposeShouldHaveThrownOperationTimeoutException()
{
Assert.IsNotNull(_actualException);
Assert.AreSame(_operationTimeoutException, _actualException);
}
}
}
@@ -0,0 +1,147 @@
using System;
using System.Collections.Generic;
using System.Threading;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Renci.SshNet.Common;
using Renci.SshNet.Messages.Connection;
namespace Renci.SshNet.Tests.Classes.Channels
{
[TestClass]
public class ChannelTest_OnSessionChannelCloseReceived_SessionIsConnectedAndChannelIsOpen_DisposeChannelInClosedEventHandler
{
private Mock<ISession> _sessionMock;
private uint _localChannelNumber;
private uint _localWindowSize;
private uint _localPacketSize;
private uint _remoteChannelNumber;
private uint _remoteWindowSize;
private uint _remotePacketSize;
private ChannelStub _channel;
private List<ChannelEventArgs> _channelClosedRegister;
private List<ChannelEventArgs> _channelEndOfDataRegister;
private IList<ExceptionEventArgs> _channelExceptionRegister;
private ManualResetEvent _channelClosedEventHandlerCompleted;
private void SetupData()
{
var random = new Random();
_localChannelNumber = (uint) random.Next(0, int.MaxValue);
_localWindowSize = (uint) random.Next(0, int.MaxValue);
_localPacketSize = (uint) random.Next(0, int.MaxValue);
_remoteChannelNumber = (uint) random.Next(0, int.MaxValue);
_remoteWindowSize = (uint) random.Next(0, int.MaxValue);
_remotePacketSize = (uint) random.Next(0, int.MaxValue);
_channelClosedRegister = new List<ChannelEventArgs>();
_channelEndOfDataRegister = new List<ChannelEventArgs>();
_channelExceptionRegister = new List<ExceptionEventArgs>();
_channelClosedEventHandlerCompleted = new ManualResetEvent(false);
}
private void CreateMocks()
{
_sessionMock = new Mock<ISession>(MockBehavior.Strict);
}
private void SetupMocks()
{
var sequence = new MockSequence();
_sessionMock.InSequence(sequence).Setup(p => p.IsConnected).Returns(true);
_sessionMock.InSequence(sequence)
.Setup(p => p.TrySendMessage(It.Is<ChannelCloseMessage>(c => c.LocalChannelNumber == _remoteChannelNumber)))
.Returns(true);
_sessionMock.InSequence(sequence)
.Setup(p => p.WaitOnHandle(It.IsAny<EventWaitHandle>()))
.Callback<WaitHandle>(w => w.WaitOne());
}
[TestInitialize]
public void Initialize()
{
Arrange();
Act();
}
private void Arrange()
{
SetupData();
CreateMocks();
SetupMocks();
_channel = new ChannelStub(_sessionMock.Object, _localChannelNumber, _localWindowSize, _localPacketSize);
_channel.Closed += (sender, args) =>
{
_channelClosedRegister.Add(args);
_channel.Dispose();
_channelClosedEventHandlerCompleted.Set();
};
_channel.EndOfData += (sender, args) => _channelEndOfDataRegister.Add(args);
_channel.Exception += (sender, args) => _channelExceptionRegister.Add(args);
_channel.InitializeRemoteChannelInfo(_remoteChannelNumber, _remoteWindowSize, _remotePacketSize);
_channel.SetIsOpen(true);
}
private void Act()
{
_sessionMock.Raise(
s => s.ChannelCloseReceived += null,
new MessageEventArgs<ChannelCloseMessage>(new ChannelCloseMessage(_localChannelNumber)));
}
[TestMethod]
public void IsOpenShouldReturnFalse()
{
Assert.IsFalse(_channel.IsOpen);
}
[TestMethod]
public void TrySendMessageOnSessionShouldBeInvokedOnceForChannelCloseMessage()
{
_sessionMock.Verify(
p => p.TrySendMessage(It.Is<ChannelCloseMessage>(c => c.LocalChannelNumber == _remoteChannelNumber)),
Times.Once);
}
[TestMethod]
public void TrySendMessageOnSessionShouldNeverBeInvokedForChannelEofMessage()
{
_sessionMock.Verify(
p => p.TrySendMessage(It.Is<ChannelEofMessage>(c => c.LocalChannelNumber == _remoteChannelNumber)),
Times.Never);
}
[TestMethod]
public void WaitOnHandleOnSessionShouldBeInvokedOnce()
{
_sessionMock.Verify(p => p.WaitOnHandle(It.IsAny<EventWaitHandle>()), Times.Once);
}
[TestMethod]
public void ClosedEventShouldHaveFiredOnce()
{
Assert.AreEqual(1, _channelClosedRegister.Count);
Assert.AreEqual(_localChannelNumber, _channelClosedRegister[0].ChannelNumber);
}
[TestMethod]
public void EndOfDataEventShouldNeverHaveFired()
{
Assert.AreEqual(0, _channelEndOfDataRegister.Count);
}
[TestMethod]
public void ExceptionShouldNeverHaveFired()
{
Assert.AreEqual(0, _channelExceptionRegister.Count);
}
[TestMethod]
public void ChannelCloseReceivedShouldBlockUntilClosedEventHandlerHasCompleted()
{
Assert.IsTrue(_channelClosedEventHandlerCompleted.WaitOne(0));
}
}
}
@@ -21,6 +21,7 @@ namespace Renci.SshNet.Tests.Classes.Channels
private uint _remotePacketSize;
private IList<ChannelEventArgs> _channelClosedRegister;
private IList<ExceptionEventArgs> _channelExceptionRegister;
private ManualResetEvent _channelClosedEventHandlerCompleted;
private ChannelStub _channel;
[TestInitialize]
@@ -41,6 +42,7 @@ namespace Renci.SshNet.Tests.Classes.Channels
_remotePacketSize = (uint)random.Next(0, int.MaxValue);
_channelClosedRegister = new List<ChannelEventArgs>();
_channelExceptionRegister = new List<ExceptionEventArgs>();
_channelClosedEventHandlerCompleted = new ManualResetEvent(false);
_sessionMock = new Mock<ISession>(MockBehavior.Strict);
@@ -53,7 +55,12 @@ namespace Renci.SshNet.Tests.Classes.Channels
.Callback<WaitHandle>(w => w.WaitOne());
_channel = new ChannelStub(_sessionMock.Object, _localChannelNumber, _localWindowSize, _localPacketSize);
_channel.Closed += (sender, args) => _channelClosedRegister.Add(args);
_channel.Closed += (sender, args) =>
{
_channelClosedRegister.Add(args);
Thread.Sleep(100);
_channelClosedEventHandlerCompleted.Set();
};
_channel.Exception += (sender, args) => _channelExceptionRegister.Add(args);
_channel.InitializeRemoteChannelInfo(_remoteChannelNumber, _remoteWindowSize, _remotePacketSize);
_channel.SetIsOpen(true);
@@ -105,5 +112,11 @@ namespace Renci.SshNet.Tests.Classes.Channels
{
Assert.AreEqual(0, _channelExceptionRegister.Count, _channelExceptionRegister.AsString());
}
[TestMethod]
public void ChannelCloseReceivedShouldBlockUntilClosedEventHandlerHasCompleted()
{
Assert.IsTrue(_channelClosedEventHandlerCompleted.WaitOne(0));
}
}
}
@@ -21,6 +21,7 @@ namespace Renci.SshNet.Tests.Classes.Channels
private uint _remotePacketSize;
private IList<ChannelEventArgs> _channelClosedRegister;
private IList<ExceptionEventArgs> _channelExceptionRegister;
private ManualResetEvent _channelClosedEventHandlerCompleted;
private ChannelStub _channel;
[TestInitialize]
@@ -41,6 +42,7 @@ namespace Renci.SshNet.Tests.Classes.Channels
_remotePacketSize = (uint)random.Next(0, int.MaxValue);
_channelClosedRegister = new List<ChannelEventArgs>();
_channelExceptionRegister = new List<ExceptionEventArgs>();
_channelClosedEventHandlerCompleted = new ManualResetEvent(false);
_sessionMock = new Mock<ISession>(MockBehavior.Strict);
@@ -55,7 +57,12 @@ namespace Renci.SshNet.Tests.Classes.Channels
.Callback<WaitHandle>(w => w.WaitOne());
_channel = new ChannelStub(_sessionMock.Object, _localChannelNumber, _localWindowSize, _localPacketSize);
_channel.Closed += (sender, args) => _channelClosedRegister.Add(args);
_channel.Closed += (sender, args) =>
{
_channelClosedRegister.Add(args);
Thread.Sleep(100);
_channelClosedEventHandlerCompleted.Set();
};
_channel.Exception += (sender, args) => _channelExceptionRegister.Add(args);
_channel.InitializeRemoteChannelInfo(_remoteChannelNumber, _remoteWindowSize, _remotePacketSize);
_channel.SetIsOpen(true);
@@ -110,5 +117,11 @@ namespace Renci.SshNet.Tests.Classes.Channels
{
Assert.AreEqual(0, _channelExceptionRegister.Count, _channelExceptionRegister.AsString());
}
[TestMethod]
public void ChannelCloseReceivedShouldBlockUntilClosedEventHandlerHasCompleted()
{
Assert.IsTrue(_channelClosedEventHandlerCompleted.WaitOne(0));
}
}
}
@@ -111,7 +111,10 @@
<Compile Include="Classes\Channels\ChannelSessionTest_Open_OnOpenFailureReceived_RetriesAvalable.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_SessionIsNotConnectedAndChannelIsOpen.cs" />
<Compile Include="Classes\Channels\ChannelTest_OnSessionChannelCloseReceived_SessionIsConnectedAndChannelIsOpen_DisposeChannelInClosedEventHandler.cs" />
<Compile Include="Classes\Channels\ChannelTest_OnSessionChannelCloseReceived_OnClose_Exception.cs" />
<Compile Include="Classes\Channels\ChannelTest_OnSessionChannelCloseReceived_SessionIsConnectedAndChannelIsOpen_EofReceived.cs" />
<Compile Include="Classes\Channels\ChannelTest_OnSessionChannelDataReceived_OnData_Exception.cs" />
+22 -14
View File
@@ -400,21 +400,13 @@ namespace Renci.SshNet.Channels
{
_closeMessageReceived = true;
// signal that SSH_MSG_CHANNEL_CLOSE message was received from server
// we need to signal this before firing the Closed event, as a subscriber
// may very well react to the Closed event by closing or disposing the
// channel which in turn will wait for this handle to be signaled
// Signal that SSH_MSG_CHANNEL_CLOSE message was received from server.
// We need to signal this before invoking Close() as it may very well
// be blocked waiting for this signal.
var channelClosedWaitHandle = _channelClosedWaitHandle;
if (channelClosedWaitHandle != null)
channelClosedWaitHandle.Set();
// raise event signaling that the server has closed its end of the channel
var closed = Closed;
if (closed != null)
{
closed(this, new ChannelEventArgs(LocalChannelNumber));
}
// close the channel
Close();
}
@@ -554,8 +546,9 @@ namespace Renci.SshNet.Channels
{
_closeMessageSent = true;
// wait for channel to be closed if we actually sent a close message (either to initiate closing
// the channel, or as response to a SSH_MSG_CHANNEL_CLOSE message sent by the server
// only wait for the channel to be closed by the server if we didn't send a
// SSH_MSG_CHANNEL_CLOSE as response to a SSH_MSG_CHANNEL_CLOSE sent by the
// server
try
{
WaitOnHandle(_channelClosedWaitHandle);
@@ -567,7 +560,22 @@ namespace Renci.SshNet.Channels
}
}
IsOpen = false;
if (IsOpen)
{
// mark sure the channel is marked closed before we raise the Closed event
// this also ensures don't raise the Closed event more than once
IsOpen = false;
if (_closeMessageReceived)
{
// raise event signaling that both ends of the channel have been closed
var closed = Closed;
if (closed != null)
{
closed(this, new ChannelEventArgs(LocalChannelNumber));
}
}
}
}
}