Skip to content

Commit

Permalink
Raise the Closed event as part the Close() method to:
Browse files Browse the repository at this point in the history
* ensure the channel is closed at both ends before we raise this event
* ensure we raise the event before the channel is disposed

Fixes issue sshnet#319.
  • Loading branch information
drieseng committed Oct 12, 2017
1 parent 7a19bcd commit f854227
Show file tree
Hide file tree
Showing 10 changed files with 546 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public class ChannelTest_Dispose_SessionIsConnectedAndChannelIsOpen_EofNotReceiv
private uint _remotePacketSize;
private ChannelStub _channel;
private Stopwatch _closeTimer;
private ManualResetEvent _channelClosedWaitHandle;
private ManualResetEvent _channelClosedEventHandlerCompleted;
private List<ChannelEventArgs> _channelClosedRegister;
private IList<ExceptionEventArgs> _channelExceptionRegister;

Expand All @@ -43,7 +43,7 @@ private void Arrange()
_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);
Expand Down Expand Up @@ -80,7 +80,8 @@ private void Arrange()
_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);
Expand Down Expand Up @@ -129,12 +130,16 @@ public void WaitOnHandleOnSessionShouldWaitForChannelCloseMessageToBeReceived()
[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()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
namespace Renci.SshNet.Tests.Classes.Channels
{
[TestClass]
[Ignore]
public class ChannelTest_Dispose_SessionIsConnectedAndChannelIsOpen_EofNotReceived_SendEofInvoked
{
private Mock<ISession> _sessionMock;
Expand All @@ -21,7 +22,7 @@ public class ChannelTest_Dispose_SessionIsConnectedAndChannelIsOpen_EofNotReceiv
private uint _remotePacketSize;
private ChannelStub _channel;
private Stopwatch _closeTimer;
private ManualResetEvent _channelClosedWaitHandle;
private ManualResetEvent _channelClosedEventHandlerCompleted;
private List<ChannelEventArgs> _channelClosedRegister;
private IList<ExceptionEventArgs> _channelExceptionRegister;

Expand All @@ -32,6 +33,16 @@ public void Initialize()
Act();
}

[TestCleanup]
public void TearDown()
{
if (_channelClosedEventHandlerCompleted != null)
{
_channelClosedEventHandlerCompleted.Dispose();
_channelClosedEventHandlerCompleted = null;
}
}

private void Arrange()
{
var random = new Random();
Expand All @@ -42,7 +53,7 @@ private void Arrange()
_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>();

Expand Down Expand Up @@ -80,12 +91,13 @@ private void Arrange()
_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()
Expand Down Expand Up @@ -130,12 +142,16 @@ public void WaitOnHandleOnSessionShouldWaitForChannelCloseMessageToBeReceived()
[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()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public class ChannelTest_Dispose_SessionIsConnectedAndChannelIsOpen_EofReceived
private List<ChannelEventArgs> _channelEndOfDataRegister;
private IList<ExceptionEventArgs> _channelExceptionRegister;
private ManualResetEvent _channelClosedReceived;
private ManualResetEvent _channelClosedEventHandlerCompleted;
private Thread _raiseChannelCloseReceivedThread;

private void SetupData()
Expand All @@ -39,6 +40,7 @@ private void SetupData()
_channelEndOfDataRegister = new List<ChannelEventArgs>();
_channelExceptionRegister = new List<ExceptionEventArgs>();
_channelClosedReceived = new ManualResetEvent(false);
_channelClosedEventHandlerCompleted = new ManualResetEvent(false);
_raiseChannelCloseReceivedThread = null;
}

Expand Down Expand Up @@ -106,6 +108,12 @@ public void TearDown()
_raiseChannelCloseReceivedThread.Abort();
}
}

if (_channelClosedEventHandlerCompleted != null)
{
_channelClosedEventHandlerCompleted.Dispose();
_channelClosedEventHandlerCompleted = null;
}
}

private void Arrange()
Expand All @@ -115,7 +123,12 @@ private void Arrange()
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);
Expand Down Expand Up @@ -173,7 +186,7 @@ public void ClosedEventShouldHaveFiredOnce()
}

[TestMethod]
public void EndOfDataEventShouldHaveFiredOnce()
public void EndOfDataEventShouldNotHaveFired()
{
Assert.AreEqual(1, _channelEndOfDataRegister.Count);
Assert.AreEqual(_localChannelNumber, _channelEndOfDataRegister[0].ChannelNumber);
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
Loading

0 comments on commit f854227

Please sign in to comment.