mirror of
https://github.com/sshnet/SSH.NET.git
synced 2026-09-10 01:05:42 +00:00
Merge pull request #320 from sshnet/channel_closed
Channel: Raise the Closed event as part of the Close() method
This commit is contained in:
+10
-5
@@ -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()
|
||||
{
|
||||
|
||||
+22
-6
@@ -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()
|
||||
{
|
||||
|
||||
+15
-2
@@ -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);
|
||||
|
||||
+141
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+157
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+147
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
-1
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+14
-1
@@ -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" />
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user