From 7d5ccb8f38fb63861952c5269026977308d8e74c Mon Sep 17 00:00:00 2001 From: Igor Milavec Date: Wed, 15 Dec 2021 23:28:45 +0100 Subject: [PATCH 01/25] POC unit tests --- .../Sftp/SftpFileStreamAsyncTestBase.cs | 69 ++++++++ ...eStreamTest_OpenAsync_FileAccessInvalid.cs | 56 ++++++ ...FromServerThanCountAndEqualToBufferSize.cs | 159 ++++++++++++++++++ .../Classes/SftpClientTest.ConnectAsync.cs | 49 ++++++ .../Renci.SshNet.Tests.csproj | 58 +++---- 5 files changed, 356 insertions(+), 35 deletions(-) create mode 100644 src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamAsyncTestBase.cs create mode 100644 src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileAccessInvalid.cs create mode 100644 src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_ReadAsync_ReadMode_NoDataInReaderBufferAndReadLessBytesFromServerThanCountAndEqualToBufferSize.cs create mode 100644 src/Renci.SshNet.Tests/Classes/SftpClientTest.ConnectAsync.cs diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamAsyncTestBase.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamAsyncTestBase.cs new file mode 100644 index 000000000..b8b7b4ae9 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamAsyncTestBase.cs @@ -0,0 +1,69 @@ +#if FEATURE_TAP +using System; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + public abstract class SftpFileStreamAsyncTestBase + { + internal Mock SftpSessionMock; + protected MockSequence MockSequence; + + protected virtual Task ArrangeAsync() + { + SetupData(); + CreateMocks(); + SetupMocks(); + return Task.CompletedTask; + } + + protected virtual void SetupData() + { + MockSequence = new MockSequence(); + } + + protected abstract void SetupMocks(); + + private void CreateMocks() + { + SftpSessionMock = new Mock(MockBehavior.Strict); + } + + [TestInitialize] + public async Task SetUpAsync() + { + await ArrangeAsync(); + await ActAsync(); + } + + protected abstract Task ActAsync(); + + protected byte[] GenerateRandom(int length) + { + return GenerateRandom(length, new Random()); + } + + protected byte[] GenerateRandom(int length, Random random) + { + var buffer = new byte[length]; + random.NextBytes(buffer); + return buffer; + } + + protected byte[] GenerateRandom(uint length) + { + return GenerateRandom(length, new Random()); + } + + protected byte[] GenerateRandom(uint length, Random random) + { + var buffer = new byte[length]; + random.NextBytes(buffer); + return buffer; + } + } +} +#endif \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileAccessInvalid.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileAccessInvalid.cs new file mode 100644 index 000000000..e3b4e6d18 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileAccessInvalid.cs @@ -0,0 +1,56 @@ +#if FEATURE_TAP +using System; +using System.IO; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_OpenAsync_FileAccessInvalid : SftpFileStreamTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private ArgumentOutOfRangeException _actualException; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = FileMode.Open; + _fileAccess = 0; + _bufferSize = _random.Next(5, 1000); + } + + protected override void SetupMocks() + { + } + + protected override void Act() + { + try + { + SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, default).GetAwaiter().GetResult(); + Assert.Fail(); + } + catch (ArgumentOutOfRangeException ex) + { + _actualException = ex; + } + } + + [TestMethod] + public void CtorShouldHaveThrownArgumentException() + { + Assert.IsNotNull(_actualException); + Assert.IsNull(_actualException.InnerException); + Assert.AreEqual("access", _actualException.ParamName); + } + } +} +#endif \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_ReadAsync_ReadMode_NoDataInReaderBufferAndReadLessBytesFromServerThanCountAndEqualToBufferSize.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_ReadAsync_ReadMode_NoDataInReaderBufferAndReadLessBytesFromServerThanCountAndEqualToBufferSize.cs new file mode 100644 index 000000000..13f3e1255 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_ReadAsync_ReadMode_NoDataInReaderBufferAndReadLessBytesFromServerThanCountAndEqualToBufferSize.cs @@ -0,0 +1,159 @@ +#if FEATURE_TAP +using System; +using System.IO; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Renci.SshNet.Common; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_ReadAsync_ReadMode_NoDataInReaderBufferAndReadLessBytesFromServerThanCountAndEqualToBufferSize : SftpFileStreamAsyncTestBase + { + private string _path; + private SftpFileStream _target; + private byte[] _handle; + private uint _bufferSize; + private uint _readBufferSize; + private uint _writeBufferSize; + private int _actual; + private byte[] _buffer; + private byte[] _serverData1; + private byte[] _serverData2; + private int _serverData1Length; + private int _serverData2Length; + private int _numberOfBytesToRead; + + protected override void SetupData() + { + base.SetupData(); + + var random = new Random(); + _path = random.Next().ToString(); + _handle = GenerateRandom(5, random); + _bufferSize = (uint)random.Next(1, 1000); + _readBufferSize = 20; + _writeBufferSize = 500; + + _numberOfBytesToRead = (int) _readBufferSize + 5; // greather than read buffer size + _buffer = new byte[_numberOfBytesToRead]; + _serverData1Length = (int) _readBufferSize; // equal to read buffer size + _serverData1 = GenerateRandom(_serverData1Length, random); + _serverData2Length = (int) _readBufferSize; // equal to read buffer size + _serverData2 = GenerateRandom(_serverData2Length, random); + + Assert.IsTrue(_serverData1Length < _numberOfBytesToRead && _serverData1Length == _readBufferSize); + } + + protected override void SetupMocks() + { + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpenAsync(_path, Flags.Read, default)) + .ReturnsAsync(_handle); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength(_bufferSize)) + .Returns(_readBufferSize); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength(_bufferSize, _handle)) + .Returns(_writeBufferSize); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestReadAsync(_handle, 0UL, _readBufferSize, default)) + .ReturnsAsync(_serverData1); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestReadAsync(_handle, (ulong)_serverData1.Length, _readBufferSize, default)) + .ReturnsAsync(_serverData2); + } + + [TestCleanup] + public void TearDown() + { + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestClose(_handle)); + } + + protected override async Task ArrangeAsync() + { + await base.ArrangeAsync(); + + _target = await SftpFileStream.OpenAsync(SftpSessionMock.Object, + _path, + FileMode.Open, + FileAccess.Read, + (int)_bufferSize, + default); + } + + protected override async Task ActAsync() + { + _actual = await _target.ReadAsync(_buffer, 0, _numberOfBytesToRead, default); + } + + [TestMethod] + public void ReadShouldHaveReturnedTheNumberOfBytesRequested() + { + Assert.AreEqual(_numberOfBytesToRead, _actual); + } + + [TestMethod] + public void ReadShouldHaveWrittenBytesToTheCallerSuppliedBuffer() + { + Assert.IsTrue(_serverData1.IsEqualTo(_buffer.Take(_serverData1Length))); + + var bytesWrittenFromSecondRead = _numberOfBytesToRead - _serverData1Length; + Assert.IsTrue(_serverData2.Take(bytesWrittenFromSecondRead).IsEqualTo(_buffer.Take(_serverData1Length, bytesWrittenFromSecondRead))); + } + + [TestMethod] + public void PositionShouldReturnNumberOfBytesWrittenToCallerProvidedBuffer() + { + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + Assert.AreEqual(_actual, _target.Position); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(2)); + } + + [TestMethod] + public async Task ReadShouldReturnAllRemaningBytesFromReadBufferWhenCountIsEqualToNumberOfRemainingBytes() + { + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + var numberOfBytesRemainingInReadBuffer = _serverData1Length + _serverData2Length - _numberOfBytesToRead; + + _buffer = new byte[numberOfBytesRemainingInReadBuffer]; + + var actual = await _target.ReadAsync(_buffer, 0, _buffer.Length); + + Assert.AreEqual(_buffer.Length, actual); + Assert.IsTrue(_serverData2.Take(_numberOfBytesToRead - _serverData1Length, _buffer.Length).IsEqualTo(_buffer)); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(2)); + } + + [TestMethod] + public async Task ReadShouldReturnAllRemaningBytesFromReadBufferAndReadAgainWhenCountIsGreaterThanNumberOfRemainingBytesAndNewReadReturnsZeroBytes() + { + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestReadAsync(_handle, (ulong)(_serverData1Length + _serverData2Length), _readBufferSize, default)).ReturnsAsync(Array.Empty); + + var numberOfBytesRemainingInReadBuffer = _serverData1Length + _serverData2Length - _numberOfBytesToRead; + + _buffer = new byte[numberOfBytesRemainingInReadBuffer + 1]; + + var actual = await _target.ReadAsync(_buffer, 0, _buffer.Length); + + Assert.AreEqual(numberOfBytesRemainingInReadBuffer, actual); + Assert.IsTrue(_serverData2.Take(_numberOfBytesToRead - _serverData1Length, numberOfBytesRemainingInReadBuffer).IsEqualTo(_buffer.Take(numberOfBytesRemainingInReadBuffer))); + Assert.AreEqual(0, _buffer[numberOfBytesRemainingInReadBuffer]); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(2)); + SftpSessionMock.Verify(p => p.RequestReadAsync(_handle, (ulong)(_serverData1Length + _serverData2Length), _readBufferSize, default)); + } + } +} +#endif \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Classes/SftpClientTest.ConnectAsync.cs b/src/Renci.SshNet.Tests/Classes/SftpClientTest.ConnectAsync.cs new file mode 100644 index 000000000..9cedbb167 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/SftpClientTest.ConnectAsync.cs @@ -0,0 +1,49 @@ +#if FEATURE_TAP +using System; +using System.Net.Sockets; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Renci.SshNet.Tests.Classes +{ + [TestClass] + public class SftpClientTest_ConnectAsync + { + [TestMethod] + public async Task Connect_HostNameInvalid_ShouldThrowSocketExceptionWithErrorCodeHostNotFound() + { + var connectionInfo = new ConnectionInfo(Guid.NewGuid().ToString("N"), 40, "user", + new KeyboardInteractiveAuthenticationMethod("user")); + var sftpClient = new SftpClient(connectionInfo); + + try + { + await sftpClient.ConnectAsync(default); + Assert.Fail(); + } + catch (SocketException ex) + { + Assert.AreEqual(SocketError.HostNotFound, (SocketError) ex.ErrorCode); + } + } + + [TestMethod] + public async Task Connect_ProxyHostNameInvalid_ShouldThrowSocketExceptionWithErrorCodeHostNotFound() + { + var connectionInfo = new ConnectionInfo("localhost", 40, "user", ProxyTypes.Http, Guid.NewGuid().ToString("N"), 80, + "proxyUser", "proxyPwd", new KeyboardInteractiveAuthenticationMethod("user")); + var sftpClient = new SftpClient(connectionInfo); + + try + { + await sftpClient.ConnectAsync(default); + Assert.Fail(); + } + catch (SocketException ex) + { + Assert.AreEqual(SocketError.HostNotFound, (SocketError) ex.ErrorCode); + } + } + } +} +#endif \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj b/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj index 8ba9f3077..67c3d1857 100644 --- a/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj +++ b/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj @@ -5,10 +5,13 @@ - net35;net40;netcoreapp2.1;netcoreapp2.2 + net35;net472;netcoreapp2.1 - net35;net40;netcoreapp2.1;netcoreapp2.2;netcoreapp3.0 + net35;net472;netcoreapp3.1;net5.0 + + + net472;netcoreapp3.1;net5.0;net6.0 @@ -17,14 +20,23 @@ FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_TPL + + FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_TPL;FEATURE_TAP + - FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_TPL + FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_TPL;FEATURE_TAP - FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_TPL + FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_TPL;FEATURE_TAP - - FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_TPL + + FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_TPL;FEATURE_TAP + + + FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_TPL;FEATURE_TAP + + + FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_TPL;FEATURE_TAP @@ -93,35 +105,11 @@ - - - $(MSTestV1UnitTestFrameworkAssembly) - - - - - - - - 2.1.0 - - - - - - - - 2.1.0 - - - - - - - - 2.1.0 - - + + + + + From 2a47c9e630db88e7128336aa5fa2df2c7365c130 Mon Sep 17 00:00:00 2001 From: Igor Milavec Date: Thu, 16 Dec 2021 11:53:55 +0100 Subject: [PATCH 02/25] Modify AppVeyor test script to only run net472 and net5.0 --- appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index c5bb4a18e..88f103a4d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -9,6 +9,6 @@ build: test_script: - cmd: >- - vstest.console /logger:Appveyor src\Renci.SshNet.Tests\bin\Debug\net40\Renci.SshNet.Tests.dll /TestCaseFilter:"TestCategory!=integration&TestCategory!=LongRunning" + vstest.console /logger:Appveyor src\Renci.SshNet.Tests\bin\Debug\net472\Renci.SshNet.Tests.dll /TestCaseFilter:"TestCategory!=integration&TestCategory!=LongRunning" - vstest.console /logger:Appveyor src\Renci.SshNet.Tests\bin\Debug\net35\Renci.SshNet.Tests.dll /TestCaseFilter:"TestCategory!=integration&TestCategory!=LongRunning" \ No newline at end of file + vstest.console /logger:Appveyor src\Renci.SshNet.Tests\bin\Debug\net5.0\Renci.SshNet.Tests.dll /TestCaseFilter:"TestCategory!=integration&TestCategory!=LongRunning" \ No newline at end of file From ba7ecbba0fbacfa27e484e01b5fb59a85ac07758 Mon Sep 17 00:00:00 2001 From: Igor Milavec Date: Thu, 16 Dec 2021 15:10:22 +0100 Subject: [PATCH 03/25] Fix ScpClientTest_*.DisposeOnPipeStreamShouldBeInvokedOnce in modern targets --- ...AndDirectoryInfo_SendExecRequestReturnsFalse.cs | 12 ++++++++---- ..._PathAndFileInfo_SendExecRequestReturnsFalse.cs | 12 ++++++++---- ...ad_PathAndStream_SendExecRequestReturnsFalse.cs | 12 ++++++++---- ...ctoryInfoAndPath_SendExecRequestReturnsFalse.cs | 12 ++++++++---- ..._FileInfoAndPath_SendExecRequestReturnsFalse.cs | 14 +++++++++----- ...ScpClientTest_Upload_FileInfoAndPath_Success.cs | 12 ++++++++---- ...ad_StreamAndPath_SendExecRequestReturnsFalse.cs | 12 ++++++++---- 7 files changed, 57 insertions(+), 29 deletions(-) diff --git a/src/Renci.SshNet.Tests/Classes/ScpClientTest_Download_PathAndDirectoryInfo_SendExecRequestReturnsFalse.cs b/src/Renci.SshNet.Tests/Classes/ScpClientTest_Download_PathAndDirectoryInfo_SendExecRequestReturnsFalse.cs index 9c5ed4853..dd7e61e61 100644 --- a/src/Renci.SshNet.Tests/Classes/ScpClientTest_Download_PathAndDirectoryInfo_SendExecRequestReturnsFalse.cs +++ b/src/Renci.SshNet.Tests/Classes/ScpClientTest_Download_PathAndDirectoryInfo_SendExecRequestReturnsFalse.cs @@ -54,11 +54,11 @@ protected override void SetupMocks() .Setup(p => p.SendExecRequest(string.Format("scp -prf {0}", _transformedPath))) .Returns(false); _channelSessionMock.InSequence(sequence).Setup(p => p.Dispose()); +#if NET35 _pipeStreamMock.As().InSequence(sequence).Setup(p => p.Dispose()); - - // On .NET Core, Dispose() in turn invokes Close() and since we're not mocking - // an interface, we need to expect this call as well - _pipeStreamMock.Setup(p => p.Close()); +#else + _pipeStreamMock.InSequence(sequence).Setup(p => p.Close()); +#endif } protected override void Arrange() @@ -106,7 +106,11 @@ public void DisposeOnChannelShouldBeInvokedOnce() [TestMethod] public void DisposeOnPipeStreamShouldBeInvokedOnce() { +#if NET35 _pipeStreamMock.As().Verify(p => p.Dispose(), Times.Once); +#else + _pipeStreamMock.Verify(p => p.Close(), Times.Once); +#endif } [TestMethod] diff --git a/src/Renci.SshNet.Tests/Classes/ScpClientTest_Download_PathAndFileInfo_SendExecRequestReturnsFalse.cs b/src/Renci.SshNet.Tests/Classes/ScpClientTest_Download_PathAndFileInfo_SendExecRequestReturnsFalse.cs index 8ba4525bc..8d1d1a1a2 100644 --- a/src/Renci.SshNet.Tests/Classes/ScpClientTest_Download_PathAndFileInfo_SendExecRequestReturnsFalse.cs +++ b/src/Renci.SshNet.Tests/Classes/ScpClientTest_Download_PathAndFileInfo_SendExecRequestReturnsFalse.cs @@ -53,11 +53,11 @@ protected override void SetupMocks() _channelSessionMock.InSequence(sequence) .Setup(p => p.SendExecRequest(string.Format("scp -pf {0}", _transformedPath))).Returns(false); _channelSessionMock.InSequence(sequence).Setup(p => p.Dispose()); +#if NET35 _pipeStreamMock.As().InSequence(sequence).Setup(p => p.Dispose()); - - // On .NET Core, Dispose() in turn invokes Close() and since we're not mocking - // an interface, we need to expect this call as well - _pipeStreamMock.Setup(p => p.Close()); +#else + _pipeStreamMock.InSequence(sequence).Setup(p => p.Close()); +#endif } protected override void Arrange() @@ -105,7 +105,11 @@ public void DisposeOnChannelShouldBeInvokedOnce() [TestMethod] public void DisposeOnPipeStreamShouldBeInvokedOnce() { +#if NET35 _pipeStreamMock.As().Verify(p => p.Dispose(), Times.Once); +#else + _pipeStreamMock.Verify(p => p.Close(), Times.Once); +#endif } [TestMethod] diff --git a/src/Renci.SshNet.Tests/Classes/ScpClientTest_Download_PathAndStream_SendExecRequestReturnsFalse.cs b/src/Renci.SshNet.Tests/Classes/ScpClientTest_Download_PathAndStream_SendExecRequestReturnsFalse.cs index c1504aec1..0fe2566fa 100644 --- a/src/Renci.SshNet.Tests/Classes/ScpClientTest_Download_PathAndStream_SendExecRequestReturnsFalse.cs +++ b/src/Renci.SshNet.Tests/Classes/ScpClientTest_Download_PathAndStream_SendExecRequestReturnsFalse.cs @@ -54,11 +54,11 @@ protected override void SetupMocks() .Setup(p => p.SendExecRequest(string.Format("scp -f {0}", _transformedPath))) .Returns(false); _channelSessionMock.InSequence(sequence).Setup(p => p.Dispose()); +#if NET35 _pipeStreamMock.As().InSequence(sequence).Setup(p => p.Dispose()); - - // On .NET Core, Dispose() in turn invokes Close() and since we're not mocking - // an interface, we need to expect this call as well - _pipeStreamMock.Setup(p => p.Close()); +#else + _pipeStreamMock.InSequence(sequence).Setup(p => p.Close()); +#endif } protected override void Arrange() @@ -116,7 +116,11 @@ public void DisposeOnChannelShouldBeInvokedOnce() [TestMethod] public void DisposeOnPipeStreamShouldBeInvokedOnce() { +#if NET35 _pipeStreamMock.As().Verify(p => p.Dispose(), Times.Once); +#else + _pipeStreamMock.Verify(p => p.Close(), Times.Once); +#endif } [TestMethod] diff --git a/src/Renci.SshNet.Tests/Classes/ScpClientTest_Upload_DirectoryInfoAndPath_SendExecRequestReturnsFalse.cs b/src/Renci.SshNet.Tests/Classes/ScpClientTest_Upload_DirectoryInfoAndPath_SendExecRequestReturnsFalse.cs index 6791e296f..5c8d7c282 100644 --- a/src/Renci.SshNet.Tests/Classes/ScpClientTest_Upload_DirectoryInfoAndPath_SendExecRequestReturnsFalse.cs +++ b/src/Renci.SshNet.Tests/Classes/ScpClientTest_Upload_DirectoryInfoAndPath_SendExecRequestReturnsFalse.cs @@ -53,11 +53,11 @@ protected override void SetupMocks() .Setup(p => p.SendExecRequest(string.Format("scp -r -p -d -t {0}", _transformedPath))) .Returns(false); _channelSessionMock.InSequence(sequence).Setup(p => p.Dispose()); +#if NET35 _pipeStreamMock.As().InSequence(sequence).Setup(p => p.Dispose()); - - // On .NET Core, Dispose() in turn invokes Close() and since we're not mocking - // an interface, we need to expect this call as well - _pipeStreamMock.Setup(p => p.Close()); +#else + _pipeStreamMock.InSequence(sequence).Setup(p => p.Close()); +#endif } protected override void Arrange() @@ -105,7 +105,11 @@ public void DisposeOnChannelShouldBeInvokedOnce() [TestMethod] public void DisposeOnPipeStreamShouldBeInvokedOnce() { +#if NET35 _pipeStreamMock.As().Verify(p => p.Dispose(), Times.Once); +#else + _pipeStreamMock.Verify(p => p.Close(), Times.Once); +#endif } [TestMethod] diff --git a/src/Renci.SshNet.Tests/Classes/ScpClientTest_Upload_FileInfoAndPath_SendExecRequestReturnsFalse.cs b/src/Renci.SshNet.Tests/Classes/ScpClientTest_Upload_FileInfoAndPath_SendExecRequestReturnsFalse.cs index 9a065ee21..57d89d698 100644 --- a/src/Renci.SshNet.Tests/Classes/ScpClientTest_Upload_FileInfoAndPath_SendExecRequestReturnsFalse.cs +++ b/src/Renci.SshNet.Tests/Classes/ScpClientTest_Upload_FileInfoAndPath_SendExecRequestReturnsFalse.cs @@ -59,12 +59,12 @@ protected override void SetupMocks() .Setup(p => p.SendExecRequest(string.Format("scp -t -d {0}", _transformedPath))) .Returns(false); _channelSessionMock.InSequence(sequence).Setup(p => p.Dispose()); +#if NET35 _pipeStreamMock.As().InSequence(sequence).Setup(p => p.Dispose()); - - // On .NET Core, Dispose() in turn invokes Close() and since we're not mocking - // an interface, we need to expect this call as well - _pipeStreamMock.Setup(p => p.Close()); -} +#else + _pipeStreamMock.InSequence(sequence).Setup(p => p.Close()); +#endif + } protected override void Arrange() { @@ -122,7 +122,11 @@ public void DisposeOnChannelShouldBeInvokedOnce() [TestMethod] public void DisposeOnPipeStreamShouldBeInvokedOnce() { +#if NET35 _pipeStreamMock.As().Verify(p => p.Dispose(), Times.Once); +#else + _pipeStreamMock.Verify(p => p.Close(), Times.Once); +#endif } [TestMethod] diff --git a/src/Renci.SshNet.Tests/Classes/ScpClientTest_Upload_FileInfoAndPath_Success.cs b/src/Renci.SshNet.Tests/Classes/ScpClientTest_Upload_FileInfoAndPath_Success.cs index d86191e5a..3cae19e67 100644 --- a/src/Renci.SshNet.Tests/Classes/ScpClientTest_Upload_FileInfoAndPath_Success.cs +++ b/src/Renci.SshNet.Tests/Classes/ScpClientTest_Upload_FileInfoAndPath_Success.cs @@ -84,11 +84,11 @@ protected override void SetupMocks() p => p.SendData(It.Is(b => b.SequenceEqual(new byte[] {0})))); _pipeStreamMock.InSequence(sequence).Setup(p => p.ReadByte()).Returns(0); _channelSessionMock.InSequence(sequence).Setup(p => p.Dispose()); +#if NET35 _pipeStreamMock.As().InSequence(sequence).Setup(p => p.Dispose()); - - // On .NET Core, Dispose() in turn invokes Close() and since we're not mocking - // an interface, we need to expect this call as well - _pipeStreamMock.Setup(p => p.Close()); +#else + _pipeStreamMock.InSequence(sequence).Setup(p => p.Close()); +#endif } protected override void Arrange() @@ -134,7 +134,11 @@ public void DisposeOnChannelShouldBeInvokedOnce() [TestMethod] public void DisposeOnPipeStreamShouldBeInvokedOnce() { +#if NET35 _pipeStreamMock.As().Verify(p => p.Dispose(), Times.Once); +#else + _pipeStreamMock.Verify(p => p.Close(), Times.Once); +#endif } [TestMethod] diff --git a/src/Renci.SshNet.Tests/Classes/ScpClientTest_Upload_StreamAndPath_SendExecRequestReturnsFalse.cs b/src/Renci.SshNet.Tests/Classes/ScpClientTest_Upload_StreamAndPath_SendExecRequestReturnsFalse.cs index 5facca290..21c761134 100644 --- a/src/Renci.SshNet.Tests/Classes/ScpClientTest_Upload_StreamAndPath_SendExecRequestReturnsFalse.cs +++ b/src/Renci.SshNet.Tests/Classes/ScpClientTest_Upload_StreamAndPath_SendExecRequestReturnsFalse.cs @@ -57,11 +57,11 @@ protected override void SetupMocks() .Setup(p => p.SendExecRequest(string.Format("scp -t -d {0}", _transformedPath))) .Returns(false); _channelSessionMock.InSequence(sequence).Setup(p => p.Dispose()); +#if NET35 _pipeStreamMock.As().InSequence(sequence).Setup(p => p.Dispose()); - - // On .NET Core, Dispose() in turn invokes Close() and since we're not mocking - // an interface, we need to expect this call as well - _pipeStreamMock.Setup(p => p.Close()); +#else + _pipeStreamMock.InSequence(sequence).Setup(p => p.Close()); +#endif } protected override void Arrange() @@ -119,7 +119,11 @@ public void DisposeOnChannelShouldBeInvokedOnce() [TestMethod] public void DisposeOnPipeStreamShouldBeInvokedOnce() { +#if NET35 _pipeStreamMock.As().Verify(p => p.Dispose(), Times.Once); +#else + _pipeStreamMock.Verify(p => p.Close(), Times.Once); +#endif } [TestMethod] From 81b3a163a211d8e67c9705f63af66fbe848358c1 Mon Sep 17 00:00:00 2001 From: Igor Milavec Date: Thu, 16 Dec 2021 15:44:19 +0100 Subject: [PATCH 04/25] Fix runtime and culture dependant tests. Should we test this at all? --- .../Classes/ClientAuthenticationTest.cs | 8 ++++++++ src/Renci.SshNet.Tests/Classes/Common/PacketDumpTest.cs | 4 ++++ src/Renci.SshNet.Tests/Classes/NetConfClientTest.cs | 8 ++++++++ src/Renci.SshNet.Tests/Classes/SftpClientTest.cs | 8 ++++++++ 4 files changed, 28 insertions(+) diff --git a/src/Renci.SshNet.Tests/Classes/ClientAuthenticationTest.cs b/src/Renci.SshNet.Tests/Classes/ClientAuthenticationTest.cs index a5969d354..7abcb8765 100644 --- a/src/Renci.SshNet.Tests/Classes/ClientAuthenticationTest.cs +++ b/src/Renci.SshNet.Tests/Classes/ClientAuthenticationTest.cs @@ -28,7 +28,11 @@ public void Ctor_PartialSuccessLimit_Zero() catch (ArgumentOutOfRangeException ex) { Assert.IsNull(ex.InnerException); +#if NETFRAMEWORK Assert.AreEqual(string.Format("Cannot be less than one.{0}Parameter name: {1}", Environment.NewLine, ex.ParamName), ex.Message); +#else + Assert.AreEqual(string.Format("Cannot be less than one. (Parameter '{1}')", Environment.NewLine, ex.ParamName), ex.Message); +#endif Assert.AreEqual("partialSuccessLimit", ex.ParamName); } } @@ -46,7 +50,11 @@ public void Ctor_PartialSuccessLimit_Negative() catch (ArgumentOutOfRangeException ex) { Assert.IsNull(ex.InnerException); +#if NETFRAMEWORK Assert.AreEqual(string.Format("Cannot be less than one.{0}Parameter name: {1}", Environment.NewLine, ex.ParamName), ex.Message); +#else + Assert.AreEqual(string.Format("Cannot be less than one. (Parameter '{1}')", Environment.NewLine, ex.ParamName), ex.Message); +#endif Assert.AreEqual("partialSuccessLimit", ex.ParamName); } } diff --git a/src/Renci.SshNet.Tests/Classes/Common/PacketDumpTest.cs b/src/Renci.SshNet.Tests/Classes/Common/PacketDumpTest.cs index 004019157..3b481bf46 100644 --- a/src/Renci.SshNet.Tests/Classes/Common/PacketDumpTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Common/PacketDumpTest.cs @@ -37,7 +37,11 @@ public void Create_ByteArrayAndIndentLevel_IndentLevelLessThanZero() catch (ArgumentOutOfRangeException ex) { Assert.IsNull(ex.InnerException); +#if NETFRAMEWORK Assert.AreEqual(string.Format("Cannot be less than zero.{0}Parameter name: {1}", Environment.NewLine, ex.ParamName), ex.Message); +#else + Assert.AreEqual(string.Format("Cannot be less than zero. (Parameter '{1}')", Environment.NewLine, ex.ParamName), ex.Message); +#endif Assert.AreEqual("indentLevel", ex.ParamName); } } diff --git a/src/Renci.SshNet.Tests/Classes/NetConfClientTest.cs b/src/Renci.SshNet.Tests/Classes/NetConfClientTest.cs index d70fea366..67e9c61f6 100644 --- a/src/Renci.SshNet.Tests/Classes/NetConfClientTest.cs +++ b/src/Renci.SshNet.Tests/Classes/NetConfClientTest.cs @@ -86,7 +86,11 @@ public void OperationTimeout_LessThanLowerLimit() catch (ArgumentOutOfRangeException ex) { Assert.IsNull(ex.InnerException); +#if NETFRAMEWORK Assert.AreEqual("The timeout must represent a value between -1 and Int32.MaxValue, inclusive." + Environment.NewLine + "Parameter name: " + ex.ParamName, ex.Message); +#else + Assert.AreEqual("The timeout must represent a value between -1 and Int32.MaxValue, inclusive. (Parameter '" + ex.ParamName + "')", ex.Message); +#endif Assert.AreEqual("value", ex.ParamName); } } @@ -105,7 +109,11 @@ public void OperationTimeout_GreaterThanLowerLimit() catch (ArgumentOutOfRangeException ex) { Assert.IsNull(ex.InnerException); +#if NETFRAMEWORK Assert.AreEqual("The timeout must represent a value between -1 and Int32.MaxValue, inclusive." + Environment.NewLine + "Parameter name: " + ex.ParamName, ex.Message); +#else + Assert.AreEqual("The timeout must represent a value between -1 and Int32.MaxValue, inclusive. (Parameter '" + ex.ParamName + "')", ex.Message); +#endif Assert.AreEqual("value", ex.ParamName); } } diff --git a/src/Renci.SshNet.Tests/Classes/SftpClientTest.cs b/src/Renci.SshNet.Tests/Classes/SftpClientTest.cs index 8f84546bf..506891910 100644 --- a/src/Renci.SshNet.Tests/Classes/SftpClientTest.cs +++ b/src/Renci.SshNet.Tests/Classes/SftpClientTest.cs @@ -94,7 +94,11 @@ public void OperationTimeout_LessThanLowerLimit() catch (ArgumentOutOfRangeException ex) { Assert.IsNull(ex.InnerException); +#if NETFRAMEWORK Assert.AreEqual("The timeout must represent a value between -1 and Int32.MaxValue, inclusive." + Environment.NewLine + "Parameter name: " + ex.ParamName, ex.Message); +#else + Assert.AreEqual("The timeout must represent a value between -1 and Int32.MaxValue, inclusive. (Parameter '" + ex.ParamName + "')", ex.Message); +#endif Assert.AreEqual("value", ex.ParamName); } } @@ -113,7 +117,11 @@ public void OperationTimeout_GreaterThanLowerLimit() catch (ArgumentOutOfRangeException ex) { Assert.IsNull(ex.InnerException); +#if NETFRAMEWORK Assert.AreEqual("The timeout must represent a value between -1 and Int32.MaxValue, inclusive." + Environment.NewLine + "Parameter name: " + ex.ParamName, ex.Message); +#else + Assert.AreEqual("The timeout must represent a value between -1 and Int32.MaxValue, inclusive. (Parameter '" + ex.ParamName + "')", ex.Message); +#endif Assert.AreEqual("value", ex.ParamName); } } From be979861993ad42273b23d0e767f9b19c0682388 Mon Sep 17 00:00:00 2001 From: Igor Milavec Date: Thu, 16 Dec 2021 15:50:26 +0100 Subject: [PATCH 05/25] Make ForwardedPortDynamic exception visible --- ...dedPortDynamicTest_Dispose_PortStarted_ChannelNotBound.cs | 5 ++++- ...wardedPortDynamicTest_Stop_PortStarted_ChannelNotBound.cs | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Dispose_PortStarted_ChannelNotBound.cs b/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Dispose_PortStarted_ChannelNotBound.cs index 6c4439440..f7ad267b7 100644 --- a/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Dispose_PortStarted_ChannelNotBound.cs +++ b/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Dispose_PortStarted_ChannelNotBound.cs @@ -130,7 +130,10 @@ public void ClosingShouldHaveFiredOnce() [TestMethod] public void ExceptionShouldNotHaveFired() { - Assert.AreEqual(0, _exceptionRegister.Count); + if (_exceptionRegister.Count > 0) + { + throw new Exception("ForwardedPortDynamic rased an exception: " + _exceptionRegister[0].Exception.Message, _exceptionRegister[0].Exception); + } } [TestMethod] diff --git a/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Stop_PortStarted_ChannelNotBound.cs b/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Stop_PortStarted_ChannelNotBound.cs index b84d947d5..40a2f00a9 100644 --- a/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Stop_PortStarted_ChannelNotBound.cs +++ b/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Stop_PortStarted_ChannelNotBound.cs @@ -130,7 +130,10 @@ public void ClosingShouldHaveFiredOnce() [TestMethod] public void ExceptionShouldNotHaveFired() { - Assert.AreEqual(0, _exceptionRegister.Count); + if (_exceptionRegister.Count > 0) + { + throw new Exception("ForwardedPortDynamic rased an exception: " + _exceptionRegister[0].Exception.Message, _exceptionRegister[0].Exception); + } } [TestMethod] From f91c5f12390e517964ec2cacdbbb1caa4ca19061 Mon Sep 17 00:00:00 2001 From: Igor Milavec Date: Thu, 16 Dec 2021 16:03:52 +0100 Subject: [PATCH 06/25] Fix PipeStream_Close_BlockingWrite synchronization --- .../Common/PipeStream_Close_BlockingWrite.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Renci.SshNet.Tests/Classes/Common/PipeStream_Close_BlockingWrite.cs b/src/Renci.SshNet.Tests/Classes/Common/PipeStream_Close_BlockingWrite.cs index 1fbd2d158..e233ac49f 100644 --- a/src/Renci.SshNet.Tests/Classes/Common/PipeStream_Close_BlockingWrite.cs +++ b/src/Renci.SshNet.Tests/Classes/Common/PipeStream_Close_BlockingWrite.cs @@ -11,13 +11,14 @@ public class PipeStream_Close_BlockingWrite : TripleATestBase { private PipeStream _pipeStream; private Exception _writeException; - private Thread _writehread; + private Thread _writeThread; protected override void Arrange() { _pipeStream = new PipeStream {MaxBufferLength = 3}; - _writehread = new Thread(() => + ManualResetEvent isArranged = new ManualResetEvent(false); + _writeThread = new Thread(() => { _pipeStream.WriteByte(10); _pipeStream.WriteByte(13); @@ -27,18 +28,18 @@ protected override void Arrange() // until bytes are read or the stream is closed try { + isArranged.Set(); _pipeStream.WriteByte(35); } catch (Exception ex) { _writeException = ex; - throw; } }); - _writehread.Start(); + _writeThread.Start(); // ensure we've started writing - Assert.IsFalse(_writehread.Join(50)); + isArranged.WaitOne(10000); } protected override void Act() @@ -46,13 +47,13 @@ protected override void Act() _pipeStream.Close(); // give write time to complete - _writehread.Join(100); + Assert.IsTrue(_writeThread.Join(10000)); } [TestMethod] public void BlockingWriteShouldHaveBeenInterrupted() { - Assert.AreEqual(ThreadState.Stopped, _writehread.ThreadState); + Assert.AreEqual(ThreadState.Stopped, _writeThread.ThreadState); } [TestMethod] From 8c6626702cfe7ddb9bc5e759f9d9fccfc0258192 Mon Sep 17 00:00:00 2001 From: Igor Milavec Date: Thu, 16 Dec 2021 16:50:46 +0100 Subject: [PATCH 07/25] Removed CountdownEventTest for FEATURE_THREAD_COUNTDOWNEVENT --- src/Renci.SshNet.Tests/Classes/Common/CountdownEventTest.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Renci.SshNet.Tests/Classes/Common/CountdownEventTest.cs b/src/Renci.SshNet.Tests/Classes/Common/CountdownEventTest.cs index 742d9e1e1..3884ff17f 100644 --- a/src/Renci.SshNet.Tests/Classes/Common/CountdownEventTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Common/CountdownEventTest.cs @@ -1,9 +1,8 @@ -using System; +#if !FEATURE_THREAD_COUNTDOWNEVENT +using System; using System.Threading; using Microsoft.VisualStudio.TestTools.UnitTesting; -#if !FEATURE_THREAD_COUNTDOWNEVENT using CountdownEvent = Renci.SshNet.Common.CountdownEvent; -#endif namespace Renci.SshNet.Tests.Classes.Common { @@ -336,3 +335,4 @@ private static CountdownEvent CreateCountdownEvent(int initialCount) } } } +#endif \ No newline at end of file From 530429667a593255901d07c0f1d2693f349dc716 Mon Sep 17 00:00:00 2001 From: Igor Milavec Date: Thu, 16 Dec 2021 17:30:08 +0100 Subject: [PATCH 08/25] Remove Thread.Abort() --- ...pTest_Dispose_SessionIsConnectedAndChannelIsOpen.cs | 10 ++++------ ...e_SessionIsConnectedAndChannelIsOpen_EofReceived.cs | 8 ++++++-- .../Common/PipeStream_Flush_BytesRemainingAfterRead.cs | 10 ++++++++-- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/Renci.SshNet.Tests/Classes/Channels/ChannelForwardedTcpipTest_Dispose_SessionIsConnectedAndChannelIsOpen.cs b/src/Renci.SshNet.Tests/Classes/Channels/ChannelForwardedTcpipTest_Dispose_SessionIsConnectedAndChannelIsOpen.cs index bbbff6d37..5cc70e9a1 100644 --- a/src/Renci.SshNet.Tests/Classes/Channels/ChannelForwardedTcpipTest_Dispose_SessionIsConnectedAndChannelIsOpen.cs +++ b/src/Renci.SshNet.Tests/Classes/Channels/ChannelForwardedTcpipTest_Dispose_SessionIsConnectedAndChannelIsOpen.cs @@ -51,17 +51,13 @@ public void CleanUp() _remoteListener = null; } - if (_channelThread != null) - { - if (_channelThread.IsAlive) - _channelThread.Abort(); - _channelThread = null; - } if (_channel != null) { _channel.Dispose(); _channel = null; } + + _channelThread = null; } private void Arrange() @@ -138,6 +134,7 @@ private void Arrange() _remoteWindowSize, _remotePacketSize); + ManualResetEvent isReady = new ManualResetEvent(false); _channelThread = new Thread(() => { try @@ -156,6 +153,7 @@ private void Arrange() _channelThread.Start(); // give channel time to bind to remote endpoint + isReady.WaitOne(10000); Thread.Sleep(100); } diff --git a/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_Dispose_SessionIsConnectedAndChannelIsOpen_EofReceived.cs b/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_Dispose_SessionIsConnectedAndChannelIsOpen_EofReceived.cs index 0bb9e862d..eb186f5e5 100644 --- a/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_Dispose_SessionIsConnectedAndChannelIsOpen_EofReceived.cs +++ b/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_Dispose_SessionIsConnectedAndChannelIsOpen_EofReceived.cs @@ -25,6 +25,7 @@ public class ChannelTest_Dispose_SessionIsConnectedAndChannelIsOpen_EofReceived private ManualResetEvent _channelClosedReceived; private ManualResetEvent _channelClosedEventHandlerCompleted; private Thread _raiseChannelCloseReceivedThread; + private ManualResetEvent _threadStopEvent; protected override void SetupData() { @@ -43,6 +44,7 @@ protected override void SetupData() _channelClosedReceived = new ManualResetEvent(false); _channelClosedEventHandlerCompleted = new ManualResetEvent(false); _raiseChannelCloseReceivedThread = null; + _threadStopEvent = new ManualResetEvent(false); } protected override void SetupMocks() @@ -80,7 +82,9 @@ protected override void SetupMocks() SessionMock.Raise(s => s.ChannelCloseReceived += null, new MessageEventArgs(new ChannelCloseMessage(_localChannelNumber))); }); _raiseChannelCloseReceivedThread.Start(); - waitHandle.WaitOne(); + + WaitHandle[] waitHandles = new WaitHandle[2] { waitHandle, _threadStopEvent }; + WaitHandle.WaitAny(waitHandles); }) .Returns(WaitResult.Success); } @@ -98,7 +102,7 @@ public void TearDown() { if (!_raiseChannelCloseReceivedThread.Join(1000)) { - _raiseChannelCloseReceivedThread.Abort(); + _threadStopEvent.Set(); } } diff --git a/src/Renci.SshNet.Tests/Classes/Common/PipeStream_Flush_BytesRemainingAfterRead.cs b/src/Renci.SshNet.Tests/Classes/Common/PipeStream_Flush_BytesRemainingAfterRead.cs index 3af0a3dec..482bdcb8b 100644 --- a/src/Renci.SshNet.Tests/Classes/Common/PipeStream_Flush_BytesRemainingAfterRead.cs +++ b/src/Renci.SshNet.Tests/Classes/Common/PipeStream_Flush_BytesRemainingAfterRead.cs @@ -1,4 +1,5 @@ -using System.Threading; +using System; +using System.Threading; using Microsoft.VisualStudio.TestTools.UnitTesting; using Renci.SshNet.Common; using Renci.SshNet.Tests.Common; @@ -97,20 +98,25 @@ public void WriteCausesSubsequentReadToBlockUntilRequestedNumberOfBytesAreAvaila var buffer = new byte[4]; int bytesRead = int.MaxValue; + ManualResetEvent isReady = new ManualResetEvent(false); Thread readThread = new Thread(() => { + isReady.Set(); bytesRead = _pipeStream.Read(buffer, 0, buffer.Length); }); readThread.Start(); + Assert.IsTrue(isReady.WaitOne(10000)); Assert.IsFalse(readThread.Join(500)); - readThread.Abort(); Assert.AreEqual(int.MaxValue, bytesRead); Assert.AreEqual(0, buffer[0]); Assert.AreEqual(0, buffer[1]); Assert.AreEqual(0, buffer[2]); Assert.AreEqual(0, buffer[3]); + + Assert.IsFalse(readThread.Join(50)); + _pipeStream.Dispose(); } } } From a7a9474a35628f3b6be7ed881c0f2d8847d8eb7b Mon Sep 17 00:00:00 2001 From: Igor Milavec Date: Thu, 16 Dec 2021 22:35:05 +0100 Subject: [PATCH 09/25] Fix another runtime dependant string --- .../Common/PosixPathTest_CreateAbsoluteOrRelativeFilePath.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Renci.SshNet.Tests/Classes/Common/PosixPathTest_CreateAbsoluteOrRelativeFilePath.cs b/src/Renci.SshNet.Tests/Classes/Common/PosixPathTest_CreateAbsoluteOrRelativeFilePath.cs index 986ccdea1..dacfa60a7 100644 --- a/src/Renci.SshNet.Tests/Classes/Common/PosixPathTest_CreateAbsoluteOrRelativeFilePath.cs +++ b/src/Renci.SshNet.Tests/Classes/Common/PosixPathTest_CreateAbsoluteOrRelativeFilePath.cs @@ -37,7 +37,11 @@ public void Path_Empty() catch (ArgumentException ex) { Assert.IsNull(ex.InnerException); +#if NETFRAMEWORK Assert.AreEqual(string.Format("The path is a zero-length string.{0}Parameter name: {1}", Environment.NewLine, ex.ParamName), ex.Message); +#else + Assert.AreEqual(string.Format("The path is a zero-length string. (Parameter '{1}')", Environment.NewLine, ex.ParamName), ex.Message); +#endif Assert.AreEqual("path", ex.ParamName); } } From de9faec9a1ce92aff8393e0d825f98d2f2520052 Mon Sep 17 00:00:00 2001 From: Igor Milavec Date: Fri, 17 Dec 2021 13:28:04 +0100 Subject: [PATCH 10/25] Raise AppVeyor number of ports to avoid dynamic port exhaustion --- appveyor.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index 88f103a4d..e8adeb222 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,5 +1,8 @@ os: Visual Studio 2019 +init: + - netsh int ipv4 set dynamicport tcp start=1025 num=64510 + before_build: - nuget restore src\Renci.SshNet.VS2019.sln From 5bc16c8efdf1f2fd0c5165f66c685ecb814788b8 Mon Sep 17 00:00:00 2001 From: Igor Milavec Date: Fri, 17 Dec 2021 15:45:13 +0100 Subject: [PATCH 11/25] Fix previous "speeedup" of ChannelForwardedTcpipTest_Dispose_SessionIsConnectedAndChannelIsOpen --- ...wardedTcpipTest_Dispose_SessionIsConnectedAndChannelIsOpen.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Renci.SshNet.Tests/Classes/Channels/ChannelForwardedTcpipTest_Dispose_SessionIsConnectedAndChannelIsOpen.cs b/src/Renci.SshNet.Tests/Classes/Channels/ChannelForwardedTcpipTest_Dispose_SessionIsConnectedAndChannelIsOpen.cs index 5cc70e9a1..0d3c11453 100644 --- a/src/Renci.SshNet.Tests/Classes/Channels/ChannelForwardedTcpipTest_Dispose_SessionIsConnectedAndChannelIsOpen.cs +++ b/src/Renci.SshNet.Tests/Classes/Channels/ChannelForwardedTcpipTest_Dispose_SessionIsConnectedAndChannelIsOpen.cs @@ -139,6 +139,7 @@ private void Arrange() { try { + isReady.Set(); _channel.Bind(_remoteEndpoint, _forwardedPortMock.Object); } catch (Exception ex) From 36b751b852bd140fa19cc2f62ed7cabb8a660ee4 Mon Sep 17 00:00:00 2001 From: Igor Milavec Date: Fri, 17 Dec 2021 16:09:13 +0100 Subject: [PATCH 12/25] Speedup tests --- ...rwardedPortDynamicTest_Dispose_PortStarted_ChannelBound.cs | 2 +- ...wardedPortDynamicTest_SessionErrorOccurred_ChannelBound.cs | 2 +- .../ForwardedPortDynamicTest_Stop_PortStarted_ChannelBound.cs | 2 +- ...ForwardedPortLocalTest_Dispose_PortStarted_ChannelBound.cs | 2 +- .../ForwardedPortLocalTest_Stop_PortStarted_ChannelBound.cs | 2 +- ...orwardedPortRemoteTest_Dispose_PortStarted_ChannelBound.cs | 2 +- .../SessionTest_Connected_ServerSendsDisconnectMessage.cs | 4 +++- 7 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Dispose_PortStarted_ChannelBound.cs b/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Dispose_PortStarted_ChannelBound.cs index 441250a0a..68e67a600 100644 --- a/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Dispose_PortStarted_ChannelBound.cs +++ b/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Dispose_PortStarted_ChannelBound.cs @@ -79,7 +79,7 @@ private void SetupData() _exceptionRegister = new List(); _endpoint = new IPEndPoint(IPAddress.Loopback, 8122); _remoteEndpoint = new IPEndPoint(IPAddress.Parse("193.168.1.5"), random.Next(IPEndPoint.MinPort, IPEndPoint.MaxPort)); - _bindSleepTime = TimeSpan.FromMilliseconds(random.Next(100, 500)); + _bindSleepTime = TimeSpan.FromMilliseconds(random.Next(100, 200)); _userName = random.Next().ToString(CultureInfo.InvariantCulture); _channelBindStarted = new ManualResetEvent(false); _channelBindCompleted = new ManualResetEvent(false); diff --git a/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_SessionErrorOccurred_ChannelBound.cs b/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_SessionErrorOccurred_ChannelBound.cs index db778a089..f92b5b5aa 100644 --- a/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_SessionErrorOccurred_ChannelBound.cs +++ b/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_SessionErrorOccurred_ChannelBound.cs @@ -80,7 +80,7 @@ private void SetupData() _exceptionRegister = new List(); _endpoint = new IPEndPoint(IPAddress.Loopback, 8122); _remoteEndpoint = new IPEndPoint(IPAddress.Parse("193.168.1.5"), random.Next(IPEndPoint.MinPort, IPEndPoint.MaxPort)); - _bindSleepTime = TimeSpan.FromMilliseconds(random.Next(100, 500)); + _bindSleepTime = TimeSpan.FromMilliseconds(random.Next(100, 200)); _userName = random.Next().ToString(CultureInfo.InvariantCulture); _forwardedPort = new ForwardedPortDynamic(_endpoint.Address.ToString(), (uint) _endpoint.Port); _sessionException = new Exception(); diff --git a/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Stop_PortStarted_ChannelBound.cs b/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Stop_PortStarted_ChannelBound.cs index 80f7877e3..dd7bf213c 100644 --- a/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Stop_PortStarted_ChannelBound.cs +++ b/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Stop_PortStarted_ChannelBound.cs @@ -78,7 +78,7 @@ private void SetupData() _exceptionRegister = new List(); _endpoint = new IPEndPoint(IPAddress.Loopback, 8122); _remoteEndpoint = new IPEndPoint(IPAddress.Parse("193.168.1.5"), random.Next(IPEndPoint.MinPort, IPEndPoint.MaxPort)); - _bindSleepTime = TimeSpan.FromMilliseconds(random.Next(100, 500)); + _bindSleepTime = TimeSpan.FromMilliseconds(random.Next(100, 200)); _userName = random.Next().ToString(CultureInfo.InvariantCulture); _channelBindStarted = new ManualResetEvent(false); _channelBindCompleted = new ManualResetEvent(false); diff --git a/src/Renci.SshNet.Tests/Classes/ForwardedPortLocalTest_Dispose_PortStarted_ChannelBound.cs b/src/Renci.SshNet.Tests/Classes/ForwardedPortLocalTest_Dispose_PortStarted_ChannelBound.cs index 9677734a2..694a0fa12 100644 --- a/src/Renci.SshNet.Tests/Classes/ForwardedPortLocalTest_Dispose_PortStarted_ChannelBound.cs +++ b/src/Renci.SshNet.Tests/Classes/ForwardedPortLocalTest_Dispose_PortStarted_ChannelBound.cs @@ -65,7 +65,7 @@ protected void Arrange() _exceptionRegister = new List(); _localEndpoint = new IPEndPoint(IPAddress.Loopback, 8122); _remoteEndpoint = new IPEndPoint(IPAddress.Parse("193.168.1.5"), random.Next(IPEndPoint.MinPort, IPEndPoint.MaxPort)); - _bindSleepTime = TimeSpan.FromMilliseconds(random.Next(100, 500)); + _bindSleepTime = TimeSpan.FromMilliseconds(random.Next(100, 200)); _forwardedPort = new ForwardedPortLocal(_localEndpoint.Address.ToString(), (uint) _localEndpoint.Port, _remoteEndpoint.Address.ToString(), (uint) _remoteEndpoint.Port); _channelBindStarted = new ManualResetEvent(false); _channelBindCompleted = new ManualResetEvent(false); diff --git a/src/Renci.SshNet.Tests/Classes/ForwardedPortLocalTest_Stop_PortStarted_ChannelBound.cs b/src/Renci.SshNet.Tests/Classes/ForwardedPortLocalTest_Stop_PortStarted_ChannelBound.cs index b07bb9331..6b7cfa273 100644 --- a/src/Renci.SshNet.Tests/Classes/ForwardedPortLocalTest_Stop_PortStarted_ChannelBound.cs +++ b/src/Renci.SshNet.Tests/Classes/ForwardedPortLocalTest_Stop_PortStarted_ChannelBound.cs @@ -73,7 +73,7 @@ private void SetupData() _exceptionRegister = new List(); _localEndpoint = new IPEndPoint(IPAddress.Loopback, 8122); _remoteEndpoint = new IPEndPoint(IPAddress.Parse("193.168.1.5"), random.Next(IPEndPoint.MinPort, IPEndPoint.MaxPort)); - _bindSleepTime = TimeSpan.FromMilliseconds(random.Next(100, 500)); + _bindSleepTime = TimeSpan.FromMilliseconds(random.Next(100, 200)); _channelBound = new ManualResetEvent(false); _channelBindCompleted = new ManualResetEvent(false); diff --git a/src/Renci.SshNet.Tests/Classes/ForwardedPortRemoteTest_Dispose_PortStarted_ChannelBound.cs b/src/Renci.SshNet.Tests/Classes/ForwardedPortRemoteTest_Dispose_PortStarted_ChannelBound.cs index 69787c826..0900abc26 100644 --- a/src/Renci.SshNet.Tests/Classes/ForwardedPortRemoteTest_Dispose_PortStarted_ChannelBound.cs +++ b/src/Renci.SshNet.Tests/Classes/ForwardedPortRemoteTest_Dispose_PortStarted_ChannelBound.cs @@ -79,7 +79,7 @@ private void SetUpData() _exceptionRegister = new List(); _bindEndpoint = new IPEndPoint(IPAddress.Any, random.Next(IPEndPoint.MinPort, IPEndPoint.MaxPort)); _remoteEndpoint = new IPEndPoint(IPAddress.Parse("193.168.1.5"), random.Next(IPEndPoint.MinPort, IPEndPoint.MaxPort)); - _bindSleepTime = TimeSpan.FromMilliseconds(random.Next(100, 500)); + _bindSleepTime = TimeSpan.FromMilliseconds(random.Next(100, 200)); _remoteChannelNumberWhileClosing = (uint) random.Next(0, 1000); _remoteWindowSizeWhileClosing = (uint) random.Next(0, int.MaxValue); _remotePacketSizeWhileClosing = (uint) random.Next(0, int.MaxValue); diff --git a/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerSendsDisconnectMessage.cs b/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerSendsDisconnectMessage.cs index 36a73d822..1e75c70af 100644 --- a/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerSendsDisconnectMessage.cs +++ b/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerSendsDisconnectMessage.cs @@ -26,10 +26,12 @@ protected override void SetupData() protected override void Act() { + ManualResetEvent isReceived = new ManualResetEvent(false); + Session.DisconnectReceived += (sender, e) => isReceived.Set(); ServerSocket.Send(_packet, 4, _packet.Length - 4, SocketFlags.None); // give session some time to process packet - Thread.Sleep(200); + isReceived.WaitOne(200); } [TestMethod] From 362910f65e9a8bec429f4d2259aa232c35ac1dab Mon Sep 17 00:00:00 2001 From: Igor Milavec Date: Fri, 17 Dec 2021 16:34:32 +0100 Subject: [PATCH 13/25] Ignore SocketError.TryAgain when expecting SocketError.HostNotFound --- .../DirectConnectorTest_Connect_HostNameInvalid.cs | 5 ++++- .../HttpConnectorTest_Connect_ProxyHostInvalid.cs | 5 ++++- .../Classes/SftpClientTest.Connect.cs | 10 ++++++++-- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/Renci.SshNet.Tests/Classes/Connection/DirectConnectorTest_Connect_HostNameInvalid.cs b/src/Renci.SshNet.Tests/Classes/Connection/DirectConnectorTest_Connect_HostNameInvalid.cs index 05a69e94b..f24a1e0af 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/DirectConnectorTest_Connect_HostNameInvalid.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/DirectConnectorTest_Connect_HostNameInvalid.cs @@ -36,7 +36,10 @@ public void ConnectShouldHaveThrownSocketException() { Assert.IsNotNull(_actualException); Assert.IsNull(_actualException.InnerException); - Assert.AreEqual(SocketError.HostNotFound, _actualException.SocketErrorCode); + if (_actualException.SocketErrorCode != SocketError.TryAgain) + { + Assert.AreEqual(SocketError.HostNotFound, _actualException.SocketErrorCode); + } } } } diff --git a/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyHostInvalid.cs b/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyHostInvalid.cs index 5843947dc..4d9325e5a 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyHostInvalid.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyHostInvalid.cs @@ -43,7 +43,10 @@ public void ConnectShouldHaveThrownSocketException() { Assert.IsNotNull(_actualException); Assert.IsNull(_actualException.InnerException); - Assert.AreEqual(SocketError.HostNotFound, _actualException.SocketErrorCode); + if (_actualException.SocketErrorCode != SocketError.TryAgain) + { + Assert.AreEqual(SocketError.HostNotFound, _actualException.SocketErrorCode); + } } } } diff --git a/src/Renci.SshNet.Tests/Classes/SftpClientTest.Connect.cs b/src/Renci.SshNet.Tests/Classes/SftpClientTest.Connect.cs index 9ae69f6ec..707fd92e9 100644 --- a/src/Renci.SshNet.Tests/Classes/SftpClientTest.Connect.cs +++ b/src/Renci.SshNet.Tests/Classes/SftpClientTest.Connect.cs @@ -20,7 +20,10 @@ public void Connect_HostNameInvalid_ShouldThrowSocketExceptionWithErrorCodeHostN } catch (SocketException ex) { - Assert.AreEqual(ex.ErrorCode, (int) SocketError.HostNotFound); + if (ex.SocketErrorCode != SocketError.TryAgain) + { + Assert.AreEqual(SocketError.HostNotFound, ex.SocketErrorCode); + } } } @@ -38,7 +41,10 @@ public void Connect_ProxyHostNameInvalid_ShouldThrowSocketExceptionWithErrorCode } catch (SocketException ex) { - Assert.AreEqual(ex.ErrorCode, (int)SocketError.HostNotFound); + if (ex.SocketErrorCode != SocketError.TryAgain) + { + Assert.AreEqual(SocketError.HostNotFound, ex.SocketErrorCode); + } } } } From c1a657a35fba9f3c208cd0f26d092b68bcec2a25 Mon Sep 17 00:00:00 2001 From: Igor Milavec Date: Fri, 17 Dec 2021 16:35:15 +0100 Subject: [PATCH 14/25] Better ObjectDisposedHandling in AsyncSocketListener --- .../Common/AsyncSocketListener.cs | 87 ++++++------------- 1 file changed, 28 insertions(+), 59 deletions(-) diff --git a/src/Renci.SshNet.Tests/Common/AsyncSocketListener.cs b/src/Renci.SshNet.Tests/Common/AsyncSocketListener.cs index 5512b15c7..3529640a3 100644 --- a/src/Renci.SshNet.Tests/Common/AsyncSocketListener.cs +++ b/src/Renci.SshNet.Tests/Common/AsyncSocketListener.cs @@ -102,7 +102,6 @@ public void Stop() public void Dispose() { Stop(); - GC.SuppressFinalize(this); } private void StartListener(object state) @@ -193,12 +192,37 @@ private void ReadCallback(IAsyncResult ar) { // Read data from the client socket. bytesRead = handler.EndReceive(ar); + + if (bytesRead > 0) + { + var bytesReceived = new byte[bytesRead]; + Array.Copy(state.Buffer, bytesReceived, bytesRead); + SignalBytesReceived(bytesReceived, handler); + + handler.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, ReadCallback, state); + } + else + { + SignalDisconnected(handler); + + if (ShutdownRemoteCommunicationSocket && _started) + { + lock (_syncLock) + { + if (_started) + { + handler.Shutdown(SocketShutdown.Send); + handler.Close(); + _connectedClients.Remove(handler); + } + } + } + } } catch (SocketException ex) { // The listener is stopped through a Dispose() call, which in turn causes - // Socket.EndReceive(...) to throw a SocketException or - // ObjectDisposedException + // Socket to throw a SocketException or ObjectDisposedException // // Since we consider such an exception normal when the listener is being // stopped, we only write a message to stderr if the listener is considered @@ -209,13 +233,11 @@ private void ReadCallback(IAsyncResult ar) typeof(AsyncSocketListener).FullName, ex); } - return; } catch (ObjectDisposedException ex) { // The listener is stopped through a Dispose() call, which in turn causes - // Socket.EndReceive(...) to throw a SocketException or - // ObjectDisposedException + // Socket to throw a SocketException or ObjectDisposedException // // Since we consider such an exception normal when the listener is being // stopped, we only write a message to stderr if the listener is considered @@ -226,59 +248,6 @@ private void ReadCallback(IAsyncResult ar) typeof(AsyncSocketListener).FullName, ex); } - return; - } - - if (bytesRead > 0) - { - var bytesReceived = new byte[bytesRead]; - Array.Copy(state.Buffer, bytesReceived, bytesRead); - SignalBytesReceived(bytesReceived, handler); - - try - { - handler.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, ReadCallback, state); - } - catch (SocketException ex) - { - if (!_started) - { - throw new Exception("BeginReceive while stopping!", ex); - } - - throw new Exception("BeginReceive while started!: " + ex.SocketErrorCode + " " + _stackTrace, ex); - } - - } - else - { - SignalDisconnected(handler); - - if (ShutdownRemoteCommunicationSocket) - { - lock (_syncLock) - { - if (!_started) - { - return; - } - try - { - handler.Shutdown(SocketShutdown.Send); - handler.Close(); - } - catch (SocketException ex) - { - throw new Exception("Exception in ReadCallback: " + ex.SocketErrorCode + " " + _stackTrace, ex); - } - catch (Exception ex) - { - throw new Exception("Exception in ReadCallback: " + _stackTrace, ex); - } - - _connectedClients.Remove(handler); - } - } } } From bae6c54eade3a06c554db849f0e52f9364d0b9f9 Mon Sep 17 00:00:00 2001 From: Igor Milavec Date: Fri, 17 Dec 2021 17:22:14 +0100 Subject: [PATCH 15/25] Handle SocketError.ConnectionAborted in ForwardedPortDynamic --- src/Renci.SshNet/ForwardedPortDynamic.NET.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Renci.SshNet/ForwardedPortDynamic.NET.cs b/src/Renci.SshNet/ForwardedPortDynamic.NET.cs index 36a804558..182cd91e2 100644 --- a/src/Renci.SshNet/ForwardedPortDynamic.NET.cs +++ b/src/Renci.SshNet/ForwardedPortDynamic.NET.cs @@ -202,6 +202,12 @@ private bool HandleSocks(IChannelDirectTcpip channel, Socket clientSocket, TimeS } catch (SocketException ex) { + if (ex.SocketErrorCode == SocketError.ConnectionAborted) + { + // The client socket was disposed without being closed. + return false; + } + // ignore exception thrown by interrupting the blocking receive as part of closing // the forwarded port if (ex.SocketErrorCode != SocketError.Interrupted) From 48e1af37d1490299fd092a544c0e0f13e7b4ee50 Mon Sep 17 00:00:00 2001 From: Igor Milavec Date: Fri, 17 Dec 2021 17:30:32 +0100 Subject: [PATCH 16/25] Rename test to match implementation --- .../BaseClientTest_Connected_KeepAliveInterval_NegativeOne.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Renci.SshNet.Tests/Classes/BaseClientTest_Connected_KeepAliveInterval_NegativeOne.cs b/src/Renci.SshNet.Tests/Classes/BaseClientTest_Connected_KeepAliveInterval_NegativeOne.cs index e3b10c876..46d730119 100644 --- a/src/Renci.SshNet.Tests/Classes/BaseClientTest_Connected_KeepAliveInterval_NegativeOne.cs +++ b/src/Renci.SshNet.Tests/Classes/BaseClientTest_Connected_KeepAliveInterval_NegativeOne.cs @@ -94,7 +94,7 @@ public void IsConnectedOnSessionShouldBeInvokedOnce() } [TestMethod] - public void SendMessageOnSessionShouldBeInvokedThreeTimes() + public void SendMessageOnSessionShouldBeInvokedOneTime() { // allow keep-alive to be sent once Thread.Sleep(100); From f0a8f5f8353ba19336bd232247b0ee4e5114c631 Mon Sep 17 00:00:00 2001 From: Igor Milavec Date: Fri, 17 Dec 2021 17:57:59 +0100 Subject: [PATCH 17/25] Fix BaseClientTest_Connected_KeepAliveInterval_NotNegativeOne timing --- ...eClientTest_Connected_KeepAliveInterval_NotNegativeOne.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Renci.SshNet.Tests/Classes/BaseClientTest_Connected_KeepAliveInterval_NotNegativeOne.cs b/src/Renci.SshNet.Tests/Classes/BaseClientTest_Connected_KeepAliveInterval_NotNegativeOne.cs index 26a8da00e..1e6a2f7cf 100644 --- a/src/Renci.SshNet.Tests/Classes/BaseClientTest_Connected_KeepAliveInterval_NotNegativeOne.cs +++ b/src/Renci.SshNet.Tests/Classes/BaseClientTest_Connected_KeepAliveInterval_NotNegativeOne.cs @@ -13,6 +13,7 @@ public class BaseClientTest_Connected_KeepAliveInterval_NotNegativeOne : BaseCli private ConnectionInfo _connectionInfo; private TimeSpan _keepAliveInterval; private int _keepAliveCount; + private int _actualKeepAliveCount; protected override void SetupData() { @@ -58,6 +59,8 @@ protected override void Act() // allow keep-alive to be sent a few times Thread.Sleep(195); + + _actualKeepAliveCount = _keepAliveCount; } [TestMethod] @@ -94,7 +97,7 @@ public void IsConnectedOnSessionShouldBeInvokedOnce() [TestMethod] public void SendMessageOnSessionShouldBeInvokedThreeTimes() { - _sessionMock.Verify(p => p.TrySendMessage(It.IsAny()), Times.Exactly(3)); + Assert.AreEqual(3, _actualKeepAliveCount); } private class MyClient : BaseClient From 382649b88e8d2c07e7728123f42811edb2232b6b Mon Sep 17 00:00:00 2001 From: Igor Milavec Date: Fri, 17 Dec 2021 21:04:17 +0100 Subject: [PATCH 18/25] Set C# 7.3 in Tests.csproj to limit intellisense's suggestions under different targets --- src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj b/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj index 67c3d1857..3b38bfbe8 100644 --- a/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj +++ b/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj @@ -1,6 +1,7 @@  - true + 7.3 + true ..\Renci.SshNet.snk From 07c1535a950f2d40f4c77379bb422bde459c48cf Mon Sep 17 00:00:00 2001 From: Igor Milavec Date: Fri, 17 Dec 2021 21:04:52 +0100 Subject: [PATCH 19/25] Add SftpClientTest.*Async --- .../Classes/SftpClientTest.Connect.cs | 3 +- .../Classes/SftpClientTest.ConnectAsync.cs | 11 ++-- .../Classes/SftpClientTest.DeleteFileAsync.cs | 27 ++++++++ .../Classes/SftpClientTest.RenameFileAsync.cs | 66 +++++++++++++++++++ 4 files changed, 99 insertions(+), 8 deletions(-) create mode 100644 src/Renci.SshNet.Tests/Classes/SftpClientTest.DeleteFileAsync.cs create mode 100644 src/Renci.SshNet.Tests/Classes/SftpClientTest.RenameFileAsync.cs diff --git a/src/Renci.SshNet.Tests/Classes/SftpClientTest.Connect.cs b/src/Renci.SshNet.Tests/Classes/SftpClientTest.Connect.cs index 707fd92e9..96092e60f 100644 --- a/src/Renci.SshNet.Tests/Classes/SftpClientTest.Connect.cs +++ b/src/Renci.SshNet.Tests/Classes/SftpClientTest.Connect.cs @@ -3,8 +3,7 @@ namespace Renci.SshNet.Tests.Classes { - [TestClass] - public class SftpClientTest_Connect + public partial class SftpClientTest { [TestMethod] public void Connect_HostNameInvalid_ShouldThrowSocketExceptionWithErrorCodeHostNotFound() diff --git a/src/Renci.SshNet.Tests/Classes/SftpClientTest.ConnectAsync.cs b/src/Renci.SshNet.Tests/Classes/SftpClientTest.ConnectAsync.cs index 9cedbb167..75602ea2c 100644 --- a/src/Renci.SshNet.Tests/Classes/SftpClientTest.ConnectAsync.cs +++ b/src/Renci.SshNet.Tests/Classes/SftpClientTest.ConnectAsync.cs @@ -6,11 +6,10 @@ namespace Renci.SshNet.Tests.Classes { - [TestClass] - public class SftpClientTest_ConnectAsync + public partial class SftpClientTest { [TestMethod] - public async Task Connect_HostNameInvalid_ShouldThrowSocketExceptionWithErrorCodeHostNotFound() + public async Task ConnectAsync_HostNameInvalid_ShouldThrowSocketExceptionWithErrorCodeHostNotFound() { var connectionInfo = new ConnectionInfo(Guid.NewGuid().ToString("N"), 40, "user", new KeyboardInteractiveAuthenticationMethod("user")); @@ -23,12 +22,12 @@ public async Task Connect_HostNameInvalid_ShouldThrowSocketExceptionWithErrorCod } catch (SocketException ex) { - Assert.AreEqual(SocketError.HostNotFound, (SocketError) ex.ErrorCode); + Assert.AreEqual(SocketError.HostNotFound, ex.SocketErrorCode); } } [TestMethod] - public async Task Connect_ProxyHostNameInvalid_ShouldThrowSocketExceptionWithErrorCodeHostNotFound() + public async Task ConnectAsync_ProxyHostNameInvalid_ShouldThrowSocketExceptionWithErrorCodeHostNotFound() { var connectionInfo = new ConnectionInfo("localhost", 40, "user", ProxyTypes.Http, Guid.NewGuid().ToString("N"), 80, "proxyUser", "proxyPwd", new KeyboardInteractiveAuthenticationMethod("user")); @@ -41,7 +40,7 @@ public async Task Connect_ProxyHostNameInvalid_ShouldThrowSocketExceptionWithErr } catch (SocketException ex) { - Assert.AreEqual(SocketError.HostNotFound, (SocketError) ex.ErrorCode); + Assert.AreEqual(SocketError.HostNotFound, ex.SocketErrorCode); } } } diff --git a/src/Renci.SshNet.Tests/Classes/SftpClientTest.DeleteFileAsync.cs b/src/Renci.SshNet.Tests/Classes/SftpClientTest.DeleteFileAsync.cs new file mode 100644 index 000000000..20e68e812 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/SftpClientTest.DeleteFileAsync.cs @@ -0,0 +1,27 @@ +#if FEATURE_TAP +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Renci.SshNet.Tests.Properties; +using System; +using System.Threading.Tasks; + +namespace Renci.SshNet.Tests.Classes +{ + /// + /// Implementation of the SSH File Transfer Protocol (SFTP) over SSH. + /// + public partial class SftpClientTest + { + [TestMethod] + [TestCategory("Sftp")] + [Description("Test passing null to DeleteFile.")] + [ExpectedException(typeof(ArgumentException))] + public async Task Test_Sftp_DeleteFileAsync_Null() + { + using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) + { + await sftp.DeleteFileAsync(null, default); + } + } + } +} +#endif \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Classes/SftpClientTest.RenameFileAsync.cs b/src/Renci.SshNet.Tests/Classes/SftpClientTest.RenameFileAsync.cs new file mode 100644 index 000000000..38d651f97 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/SftpClientTest.RenameFileAsync.cs @@ -0,0 +1,66 @@ +#if FEATURE_TAP +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Renci.SshNet.Tests.Properties; +using System; +using System.IO; +using System.Threading.Tasks; + +namespace Renci.SshNet.Tests.Classes +{ + /// + /// Implementation of the SSH File Transfer Protocol (SFTP) over SSH. + /// + public partial class SftpClientTest + { + [TestMethod] + [TestCategory("Sftp")] + [TestCategory("integration")] + public async Task Test_Sftp_RenameFileAsync() + { + using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) + { + await sftp.ConnectAsync(default); + + string uploadedFileName = Path.GetTempFileName(); + string remoteFileName1 = Path.GetRandomFileName(); + string remoteFileName2 = Path.GetRandomFileName(); + + this.CreateTestFile(uploadedFileName, 1); + + using (var file = File.OpenRead(uploadedFileName)) + { + using (Stream remoteStream = await sftp.OpenAsync(remoteFileName1, FileMode.CreateNew, FileAccess.Write, default)) + { + await file.CopyToAsync(remoteStream, 81920, default); + } + } + + await sftp.RenameFileAsync(remoteFileName1, remoteFileName2, default); + + File.Delete(uploadedFileName); + + sftp.Disconnect(); + } + + RemoveAllFiles(); + } + + [TestMethod] + [TestCategory("Sftp")] + [TestCategory("integration")] + [Description("Test passing null to RenameFile.")] + [ExpectedException(typeof(ArgumentNullException))] + public async Task Test_Sftp_RenameFileAsync_Null() + { + using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD)) + { + await sftp.ConnectAsync(default); + + await sftp.RenameFileAsync(null, null, default); + + sftp.Disconnect(); + } + } + } +} +#endif \ No newline at end of file From 0030b3590af07b884370962b531f1a9704480354 Mon Sep 17 00:00:00 2001 From: Igor Milavec Date: Sun, 19 Dec 2021 21:42:34 +0100 Subject: [PATCH 20/25] Add SftpFileStreamTest_OpenAsync_* --- ...eStreamTest_OpenAsync_FileAccessInvalid.cs | 7 +- ...OpenAsync_FileModeAppend_FileAccessRead.cs | 58 +++++++ ...sync_FileModeAppend_FileAccessReadWrite.cs | 58 +++++++ ...penAsync_FileModeAppend_FileAccessWrite.cs | 155 ++++++++++++++++++ ...nAsync_FileModeCreateNew_FileAccessRead.cs | 58 +++++++ ...c_FileModeCreateNew_FileAccessReadWrite.cs | 136 +++++++++++++++ ...Async_FileModeCreateNew_FileAccessWrite.cs | 136 +++++++++++++++ ...OpenAsync_FileModeCreate_FileAccessRead.cs | 58 +++++++ ...te_FileAccessReadWrite_FileDoesNotExist.cs | 136 +++++++++++++++ ...deCreate_FileAccessReadWrite_FileExists.cs | 136 +++++++++++++++ ...Create_FileAccessWrite_FileDoesNotExist.cs | 136 +++++++++++++++ ...leModeCreate_FileAccessWrite_FileExists.cs | 136 +++++++++++++++ ...ileStreamTest_OpenAsync_FileModeInvalid.cs | 57 +++++++ ...ync_FileModeOpenOrCreate_FileAccessRead.cs | 141 ++++++++++++++++ ...ileModeOpenOrCreate_FileAccessReadWrite.cs | 136 +++++++++++++++ ...nc_FileModeOpenOrCreate_FileAccessWrite.cs | 136 +++++++++++++++ ...t_OpenAsync_FileModeOpen_FileAccessRead.cs | 142 ++++++++++++++++ ...nAsync_FileModeOpen_FileAccessReadWrite.cs | 136 +++++++++++++++ ..._OpenAsync_FileModeOpen_FileAccessWrite.cs | 136 +++++++++++++++ ...enAsync_FileModeTruncate_FileAccessRead.cs | 59 +++++++ ...nc_FileModeTruncate_FileAccessReadWrite.cs | 136 +++++++++++++++ ...nAsync_FileModeTruncate_FileAccessWrite.cs | 136 +++++++++++++++ 22 files changed, 2422 insertions(+), 3 deletions(-) create mode 100644 src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeAppend_FileAccessRead.cs create mode 100644 src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeAppend_FileAccessReadWrite.cs create mode 100644 src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeAppend_FileAccessWrite.cs create mode 100644 src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreateNew_FileAccessRead.cs create mode 100644 src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreateNew_FileAccessReadWrite.cs create mode 100644 src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreateNew_FileAccessWrite.cs create mode 100644 src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessRead.cs create mode 100644 src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessReadWrite_FileDoesNotExist.cs create mode 100644 src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessReadWrite_FileExists.cs create mode 100644 src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessWrite_FileDoesNotExist.cs create mode 100644 src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessWrite_FileExists.cs create mode 100644 src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeInvalid.cs create mode 100644 src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpenOrCreate_FileAccessRead.cs create mode 100644 src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpenOrCreate_FileAccessReadWrite.cs create mode 100644 src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpenOrCreate_FileAccessWrite.cs create mode 100644 src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpen_FileAccessRead.cs create mode 100644 src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpen_FileAccessReadWrite.cs create mode 100644 src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpen_FileAccessWrite.cs create mode 100644 src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeTruncate_FileAccessRead.cs create mode 100644 src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeTruncate_FileAccessReadWrite.cs create mode 100644 src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeTruncate_FileAccessWrite.cs diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileAccessInvalid.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileAccessInvalid.cs index e3b4e6d18..fab12dad6 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileAccessInvalid.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileAccessInvalid.cs @@ -1,13 +1,14 @@ #if FEATURE_TAP using System; using System.IO; +using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; using Renci.SshNet.Sftp; namespace Renci.SshNet.Tests.Classes.Sftp { [TestClass] - public class SftpFileStreamTest_OpenAsync_FileAccessInvalid : SftpFileStreamTestBase + public class SftpFileStreamTest_OpenAsync_FileAccessInvalid : SftpFileStreamAsyncTestBase { private Random _random; private string _path; @@ -31,11 +32,11 @@ protected override void SetupMocks() { } - protected override void Act() + protected override async Task ActAsync() { try { - SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, default).GetAwaiter().GetResult(); + await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, default); Assert.Fail(); } catch (ArgumentOutOfRangeException ex) diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeAppend_FileAccessRead.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeAppend_FileAccessRead.cs new file mode 100644 index 000000000..0ffeaeb6c --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeAppend_FileAccessRead.cs @@ -0,0 +1,58 @@ +#if FEATURE_TAP +using System; +using System.IO; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_OpenAsync_FileModeAppend_FileAccessRead : SftpFileStreamAsyncTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private ArgumentException _actualException; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = FileMode.Append; + _fileAccess = FileAccess.Read; + _bufferSize = _random.Next(5, 1000); + } + + protected override void SetupMocks() + { + } + + protected override async Task ActAsync() + { + try + { + await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, default); + Assert.Fail(); + } + catch (ArgumentException ex) + { + _actualException = ex; + } + } + + [TestMethod] + public void CtorShouldHaveThrownArgumentException() + { + Assert.IsNotNull(_actualException); + Assert.IsNull(_actualException.InnerException); + Assert.AreEqual(string.Format("{0} mode can be requested only when combined with write-only access.", _fileMode), _actualException.Message); + Assert.IsNull(_actualException.ParamName); + } + } +} +#endif \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeAppend_FileAccessReadWrite.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeAppend_FileAccessReadWrite.cs new file mode 100644 index 000000000..2ed8cd14f --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeAppend_FileAccessReadWrite.cs @@ -0,0 +1,58 @@ +#if FEATURE_TAP +using System; +using System.IO; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_OpenAsync_FileModeAppend_FileAccessReadWrite : SftpFileStreamAsyncTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private ArgumentException _actualException; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = FileMode.Append; + _fileAccess = FileAccess.ReadWrite; + _bufferSize = _random.Next(5, 1000); + } + + protected override void SetupMocks() + { + } + + protected override async Task ActAsync() + { + try + { + await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, default); + Assert.Fail(); + } + catch (ArgumentException ex) + { + _actualException = ex; + } + } + + [TestMethod] + public void CtorShouldHaveThrownArgumentException() + { + Assert.IsNotNull(_actualException); + Assert.IsNull(_actualException.InnerException); + Assert.AreEqual(string.Format("{0} mode can be requested only when combined with write-only access.", _fileMode), _actualException.Message); + Assert.IsNull(_actualException.ParamName); + } + } +} +#endif \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeAppend_FileAccessWrite.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeAppend_FileAccessWrite.cs new file mode 100644 index 000000000..8d9bc960b --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeAppend_FileAccessWrite.cs @@ -0,0 +1,155 @@ +#if FEATURE_TAP +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Renci.SshNet.Sftp; +using Renci.SshNet.Tests.Common; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_OpenAsync_FileModeAppend_FileAccessWrite : SftpFileStreamAsyncTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private uint _readBufferSize; + private uint _writeBufferSize; + private byte[] _handle; + private SftpFileStream _target; + private SftpFileAttributes _fileAttributes; + private CancellationToken _cancellationToken; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = FileMode.Append; + _fileAccess = FileAccess.Write; + _bufferSize = _random.Next(5, 1000); + _readBufferSize = (uint) _random.Next(5, 1000); + _writeBufferSize = (uint) _random.Next(5, 1000); + _handle = GenerateRandom(_random.Next(1, 10), _random); + _fileAttributes = new SftpFileAttributesBuilder().WithLastAccessTime(DateTime.UtcNow.AddSeconds(_random.Next())) + .WithLastWriteTime(DateTime.UtcNow.AddSeconds(_random.Next())) + .WithSize(_random.Next()) + .WithUserId(_random.Next()) + .WithGroupId(_random.Next()) + .WithPermissions((uint) _random.Next()) + .Build(); + _cancellationToken = new CancellationToken(); + } + + protected override void SetupMocks() + { + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpenAsync(_path, Flags.Write | Flags.Append | Flags.CreateNewOrOpen, _cancellationToken)) + .ReturnsAsync(_handle); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestFStatAsync(_handle, _cancellationToken)) + .ReturnsAsync(_fileAttributes); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength((uint)_bufferSize)) + .Returns(_readBufferSize); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength((uint)_bufferSize, _handle)) + .Returns(_writeBufferSize); + } + + protected override async Task ActAsync() + { + _target = await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, _cancellationToken); + } + + + [TestMethod] + public void CanReadShouldReturnFalse() + { + Assert.IsFalse(_target.CanRead); + } + + [TestMethod] + public void CanSeekShouldReturnTrue() + { + Assert.IsTrue(_target.CanSeek); + } + + [TestMethod] + public void CanWriteShouldReturnTrue() + { + Assert.IsTrue(_target.CanWrite); + } + + [TestMethod] + public void CanTimeoutShouldReturnTrue() + { + Assert.IsTrue(_target.CanTimeout); + } + + [TestMethod] + public void PositionShouldReturnSizeOfFile() + { + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + var actual = _target.Position; + + Assert.AreEqual(_fileAttributes.Size, actual); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public async Task ReadShouldThrowNotSupportedException() + { + var buffer = new byte[_readBufferSize]; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + try + { + await _target.ReadAsync(buffer, 0, buffer.Length, _cancellationToken); + Assert.Fail(); + } + catch (NotSupportedException ex) + { + Assert.IsNull(ex.InnerException); + } + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public async Task WriteShouldStartWritingAtEndOfFile() + { + var buffer = new byte[_writeBufferSize]; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestWriteAsync(_handle, (ulong)_fileAttributes.Size, buffer, 0, buffer.Length, _cancellationToken)).Returns(Task.CompletedTask); + + await _target.WriteAsync(buffer, 0, buffer.Length, _cancellationToken); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + SftpSessionMock.Verify(p => p.RequestWriteAsync(_handle, (ulong)_fileAttributes.Size, buffer, 0, buffer.Length, _cancellationToken), Times.Once); + } + + [TestMethod] + public void RequestOpenOnSftpSessionShouldBeInvokedOnce() + { + SftpSessionMock.Verify(p => p.RequestOpenAsync(_path, Flags.Write | Flags.Append | Flags.CreateNewOrOpen, default), Times.Once); + } + + [TestMethod] + public void RequestFStatOnSftpSessionShouldBeInvokedOnce() + { + SftpSessionMock.Verify(p => p.RequestFStatAsync(_handle, default), Times.Once); + } + } +} +#endif \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreateNew_FileAccessRead.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreateNew_FileAccessRead.cs new file mode 100644 index 000000000..eb83af777 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreateNew_FileAccessRead.cs @@ -0,0 +1,58 @@ +#if FEATURE_TAP +using System; +using System.IO; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_OpenAsync_FileModeCreateNew_FileAccessRead : SftpFileStreamAsyncTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private ArgumentException _actualException; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = FileMode.CreateNew; + _fileAccess = FileAccess.Read; + _bufferSize = _random.Next(5, 1000); + } + + protected override void SetupMocks() + { + } + + protected override async Task ActAsync() + { + try + { + await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, default); + Assert.Fail(); + } + catch (ArgumentException ex) + { + _actualException = ex; + } + } + + [TestMethod] + public void CtorShouldHaveThrownArgumentException() + { + Assert.IsNotNull(_actualException); + Assert.IsNull(_actualException.InnerException); + Assert.AreEqual(string.Format("Combining {0}: {1} with {2}: {3} is invalid.", typeof(FileMode).Name, _fileMode, typeof(FileAccess).Name, _fileAccess), _actualException.Message); + Assert.IsNull(_actualException.ParamName); + } + } +} +#endif \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreateNew_FileAccessReadWrite.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreateNew_FileAccessReadWrite.cs new file mode 100644 index 000000000..5c2318086 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreateNew_FileAccessReadWrite.cs @@ -0,0 +1,136 @@ +#if FEATURE_TAP +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Renci.SshNet.Common; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_OpenAsync_FileModeCreateNew_FileAccessReadWrite : SftpFileStreamAsyncTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private uint _readBufferSize; + private uint _writeBufferSize; + private byte[] _handle; + private SftpFileStream _target; + private CancellationToken _cancellationToken; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = FileMode.CreateNew; + _fileAccess = FileAccess.ReadWrite; + _bufferSize = _random.Next(5, 1000); + _readBufferSize = (uint)_random.Next(5, 1000); + _writeBufferSize = (uint)_random.Next(5, 1000); + _handle = GenerateRandom(_random.Next(1, 10), _random); + _cancellationToken = new CancellationToken(); + } + + protected override void SetupMocks() + { + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpenAsync(_path, Flags.Read | Flags.Write | Flags.CreateNew, _cancellationToken)) + .ReturnsAsync(_handle); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength((uint) _bufferSize)) + .Returns(_readBufferSize); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength((uint) _bufferSize, _handle)) + .Returns(_writeBufferSize); + } + + protected override async Task ActAsync() + { + _target = await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, _cancellationToken); + } + + [TestMethod] + public void CanReadShouldReturnTrue() + { + Assert.IsTrue(_target.CanRead); + } + + [TestMethod] + public void CanSeekShouldReturnTrue() + { + Assert.IsTrue(_target.CanSeek); + } + + [TestMethod] + public void CanWriteShouldReturnTrue() + { + Assert.IsTrue(_target.CanWrite); + } + + [TestMethod] + public void CanTimeoutShouldReturnTrue() + { + Assert.IsTrue(_target.CanTimeout); + } + + [TestMethod] + public void PositionShouldReturnZero() + { + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + var actual = _target.Position; + + Assert.AreEqual(0L, actual); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public async Task ReadShouldStartReadingAtBeginningOfFile() + { + var buffer = new byte[8]; + var data = new byte[] { 5, 4, 3, 2, 1 }; + var expected = new byte[] { 0, 5, 4, 3, 2, 1, 0, 0 }; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestReadAsync(_handle, 0UL, _readBufferSize, _cancellationToken)).ReturnsAsync(data); + + var actual = await _target.ReadAsync(buffer, 1, data.Length, _cancellationToken); + + Assert.AreEqual(data.Length, actual); + Assert.IsTrue(buffer.IsEqualTo(expected)); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + SftpSessionMock.Verify(p => p.RequestReadAsync(_handle, 0UL, _readBufferSize, _cancellationToken), Times.Once); + } + + [TestMethod] + public async Task WriteShouldStartWritingAtBeginningOfFile() + { + var buffer = new byte[_writeBufferSize]; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken)).Returns(Task.CompletedTask); + + await _target.WriteAsync(buffer, 0, buffer.Length, _cancellationToken); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + SftpSessionMock.Verify(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken), Times.Once); + } + + [TestMethod] + public void RequestOpenOnSftpSessionShouldBeInvokedOnce() + { + SftpSessionMock.Verify(p => p.RequestOpenAsync(_path, Flags.Read | Flags.Write | Flags.CreateNew, _cancellationToken), Times.Once); + } + } +} +#endif \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreateNew_FileAccessWrite.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreateNew_FileAccessWrite.cs new file mode 100644 index 000000000..7671fe300 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreateNew_FileAccessWrite.cs @@ -0,0 +1,136 @@ +#if FEATURE_TAP +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_OpenAsync_FileModeCreateNew_FileAccessWrite : SftpFileStreamAsyncTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private uint _readBufferSize; + private uint _writeBufferSize; + private byte[] _handle; + private SftpFileStream _target; + private CancellationToken _cancellationToken; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = FileMode.CreateNew; + _fileAccess = FileAccess.Write; + _bufferSize = _random.Next(5, 1000); + _readBufferSize = (uint) _random.Next(5, 1000); + _writeBufferSize = (uint) _random.Next(5, 1000); + _handle = GenerateRandom(_random.Next(1, 10), _random); + _cancellationToken = new CancellationToken(); + } + + protected override void SetupMocks() + { + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpenAsync(_path, Flags.Write | Flags.CreateNew, _cancellationToken)) + .ReturnsAsync(_handle); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength((uint) _bufferSize)) + .Returns(_readBufferSize); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength((uint) _bufferSize, _handle)) + .Returns(_writeBufferSize); + } + + protected override async Task ActAsync() + { + _target = await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, _cancellationToken); + } + + [TestMethod] + public void CanReadShouldReturnFalse() + { + Assert.IsFalse(_target.CanRead); + } + + [TestMethod] + public void CanSeekShouldReturnTrue() + { + Assert.IsTrue(_target.CanSeek); + } + + [TestMethod] + public void CanWriteShouldReturnTrue() + { + Assert.IsTrue(_target.CanWrite); + } + + [TestMethod] + public void CanTimeoutShouldReturnTrue() + { + Assert.IsTrue(_target.CanTimeout); + } + + [TestMethod] + public void PositionShouldReturnZero() + { + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + var actual = _target.Position; + + Assert.AreEqual(0L, actual); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public async Task ReadShouldThrowNotSupportedException() + { + var buffer = new byte[_readBufferSize]; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + try + { + await _target.ReadAsync(buffer, 0, buffer.Length); + Assert.Fail(); + } + catch (NotSupportedException ex) + { + Assert.IsNull(ex.InnerException); + } + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public async Task WriteShouldStartWritingAtBeginningOfFile() + { + var buffer = new byte[_writeBufferSize]; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken)).Returns(Task.CompletedTask); + + await _target.WriteAsync(buffer, 0, buffer.Length); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + SftpSessionMock.Verify(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken), Times.Once); + } + + [TestMethod] + public void RequestOpenOnSftpSessionShouldBeInvokedOnce() + { + SftpSessionMock.Verify(p => p.RequestOpenAsync(_path, Flags.Write | Flags.CreateNew, _cancellationToken), Times.Once); + } + } +} +#endif \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessRead.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessRead.cs new file mode 100644 index 000000000..2c6d28a6e --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessRead.cs @@ -0,0 +1,58 @@ +#if FEATURE_TAP +using System; +using System.IO; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessRead : SftpFileStreamAsyncTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private ArgumentException _actualException; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = FileMode.Create; + _fileAccess = FileAccess.Read; + _bufferSize = _random.Next(5, 1000); + } + + protected override void SetupMocks() + { + } + + protected override async Task ActAsync() + { + try + { + await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, default); + Assert.Fail(); + } + catch (ArgumentException ex) + { + _actualException = ex; + } + } + + [TestMethod] + public void CtorShouldHaveThrownArgumentException() + { + Assert.IsNotNull(_actualException); + Assert.IsNull(_actualException.InnerException); + Assert.AreEqual(string.Format("Combining {0}: {1} with {2}: {3} is invalid.", typeof(FileMode).Name, _fileMode, typeof(FileAccess).Name, _fileAccess), _actualException.Message); + Assert.IsNull(_actualException.ParamName); + } + } +} +#endif \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessReadWrite_FileDoesNotExist.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessReadWrite_FileDoesNotExist.cs new file mode 100644 index 000000000..5fe519f17 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessReadWrite_FileDoesNotExist.cs @@ -0,0 +1,136 @@ +#if FEATURE_TAP +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Renci.SshNet.Common; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessReadWrite_FileDoesNotExist : SftpFileStreamAsyncTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private uint _readBufferSize; + private uint _writeBufferSize; + private byte[] _handle; + private SftpFileStream _target; + private CancellationToken _cancellationToken; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = FileMode.Create; + _fileAccess = FileAccess.ReadWrite; + _bufferSize = _random.Next(5, 1000); + _readBufferSize = (uint)_random.Next(5, 1000); + _writeBufferSize = (uint)_random.Next(5, 1000); + _handle = GenerateRandom(_random.Next(1, 10), _random); + _cancellationToken = new CancellationToken(); + } + + protected override void SetupMocks() + { + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpenAsync(_path, Flags.Read | Flags.Write | Flags.CreateNewOrOpen | Flags.Truncate, _cancellationToken)) + .ReturnsAsync(_handle); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength((uint) _bufferSize)) + .Returns(_readBufferSize); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength((uint) _bufferSize, _handle)) + .Returns(_writeBufferSize); + } + + protected override async Task ActAsync() + { + _target = await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, _cancellationToken); + } + + [TestMethod] + public void CanReadShouldReturnTrue() + { + Assert.IsTrue(_target.CanRead); + } + + [TestMethod] + public void CanSeekShouldReturnTrue() + { + Assert.IsTrue(_target.CanSeek); + } + + [TestMethod] + public void CanWriteShouldReturnTrue() + { + Assert.IsTrue(_target.CanWrite); + } + + [TestMethod] + public void CanTimeoutShouldReturnTrue() + { + Assert.IsTrue(_target.CanTimeout); + } + + [TestMethod] + public void PositionShouldReturnZero() + { + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + var actual = _target.Position; + + Assert.AreEqual(0L, actual); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public async Task ReadShouldStartReadingAtBeginningOfFile() + { + var buffer = new byte[8]; + var data = new byte[] { 5, 4, 3, 2, 1 }; + var expected = new byte[] { 0, 5, 4, 3, 2, 1, 0, 0 }; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestReadAsync(_handle, 0UL, _readBufferSize, _cancellationToken)).ReturnsAsync(data); + + var actual = await _target.ReadAsync(buffer, 1, data.Length); + + Assert.AreEqual(data.Length, actual); + Assert.IsTrue(buffer.IsEqualTo(expected)); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + SftpSessionMock.Verify(p => p.RequestReadAsync(_handle, 0UL, _readBufferSize, _cancellationToken), Times.Once); + } + + [TestMethod] + public async Task WriteShouldStartWritingAtBeginningOfFile() + { + var buffer = new byte[_writeBufferSize]; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken)).Returns(Task.CompletedTask); + + await _target.WriteAsync(buffer, 0, buffer.Length); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + SftpSessionMock.Verify(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken), Times.Once); + } + + [TestMethod] + public void RequestOpenOnSftpSessionShouldBeInvokedOnce() + { + SftpSessionMock.Verify(p => p.RequestOpenAsync(_path, Flags.Read | Flags.Write | Flags.CreateNewOrOpen | Flags.Truncate, _cancellationToken), Times.Once); + } + } +} +#endif \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessReadWrite_FileExists.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessReadWrite_FileExists.cs new file mode 100644 index 000000000..a313afb96 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessReadWrite_FileExists.cs @@ -0,0 +1,136 @@ +#if FEATURE_TAP +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Renci.SshNet.Common; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessReadWrite_FileExists : SftpFileStreamAsyncTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private uint _readBufferSize; + private uint _writeBufferSize; + private byte[] _handle; + private SftpFileStream _target; + private CancellationToken _cancellationToken; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = FileMode.Create; + _fileAccess = FileAccess.ReadWrite; + _bufferSize = _random.Next(5, 1000); + _readBufferSize = (uint)_random.Next(5, 1000); + _writeBufferSize = (uint)_random.Next(5, 1000); + _handle = GenerateRandom(_random.Next(1, 10), _random); + _cancellationToken = new CancellationToken(); + } + + protected override void SetupMocks() + { + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpenAsync(_path, Flags.Read | Flags.Write | Flags.CreateNewOrOpen | Flags.Truncate, _cancellationToken)) + .ReturnsAsync(_handle); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength((uint) _bufferSize)) + .Returns(_readBufferSize); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength((uint) _bufferSize, _handle)) + .Returns(_writeBufferSize); + } + + protected override async Task ActAsync() + { + _target = await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, _cancellationToken); + } + + [TestMethod] + public void CanReadShouldReturnTrue() + { + Assert.IsTrue(_target.CanRead); + } + + [TestMethod] + public void CanSeekShouldReturnTrue() + { + Assert.IsTrue(_target.CanSeek); + } + + [TestMethod] + public void CanWriteShouldReturnTrue() + { + Assert.IsTrue(_target.CanWrite); + } + + [TestMethod] + public void CanTimeoutShouldReturnTrue() + { + Assert.IsTrue(_target.CanTimeout); + } + + [TestMethod] + public void PositionShouldReturnZero() + { + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + var actual = _target.Position; + + Assert.AreEqual(0L, actual); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public async Task ReadShouldStartReadingAtBeginningOfFile() + { + var buffer = new byte[8]; + var data = new byte[] { 5, 4, 3, 2, 1 }; + var expected = new byte[] { 0, 5, 4, 3, 2, 1, 0, 0 }; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestReadAsync(_handle, 0UL, _readBufferSize, _cancellationToken)).ReturnsAsync(data); + + var actual = await _target.ReadAsync(buffer, 1, data.Length); + + Assert.AreEqual(data.Length, actual); + Assert.IsTrue(buffer.IsEqualTo(expected)); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + SftpSessionMock.Verify(p => p.RequestReadAsync(_handle, 0UL, _readBufferSize, _cancellationToken), Times.Once); + } + + [TestMethod] + public async Task WriteShouldStartWritingAtBeginningOfFile() + { + var buffer = new byte[_writeBufferSize]; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken)).Returns(Task.CompletedTask); + + await _target.WriteAsync(buffer, 0, buffer.Length); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + SftpSessionMock.Verify(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken), Times.Once); + } + + [TestMethod] + public void RequestOpenOnSftpSessionShouldBeInvokedOnce() + { + SftpSessionMock.Verify(p => p.RequestOpenAsync(_path, Flags.Read | Flags.Write | Flags.CreateNewOrOpen | Flags.Truncate, _cancellationToken), Times.Once); + } + } +} +#endif \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessWrite_FileDoesNotExist.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessWrite_FileDoesNotExist.cs new file mode 100644 index 000000000..c1d7fc960 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessWrite_FileDoesNotExist.cs @@ -0,0 +1,136 @@ +#if FEATURE_TAP +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessWrite_FileDoesNotExist : SftpFileStreamAsyncTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private uint _readBufferSize; + private uint _writeBufferSize; + private byte[] _handle; + private SftpFileStream _target; + private CancellationToken _cancellationToken; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = FileMode.Create; + _fileAccess = FileAccess.Write; + _bufferSize = _random.Next(5, 1000); + _readBufferSize = (uint)_random.Next(5, 1000); + _writeBufferSize = (uint)_random.Next(5, 1000); + _handle = GenerateRandom(_random.Next(1, 10), _random); + _cancellationToken = new CancellationToken(); + } + + protected override void SetupMocks() + { + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpenAsync(_path, Flags.Write | Flags.CreateNewOrOpen | Flags.Truncate, _cancellationToken)) + .ReturnsAsync(_handle); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength((uint) _bufferSize)) + .Returns(_readBufferSize); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength((uint) _bufferSize, _handle)) + .Returns(_writeBufferSize); + } + + protected override async Task ActAsync() + { + _target = await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, _cancellationToken); + } + + [TestMethod] + public void CanReadShouldReturnFalse() + { + Assert.IsFalse(_target.CanRead); + } + + [TestMethod] + public void CanSeekShouldReturnTrue() + { + Assert.IsTrue(_target.CanSeek); + } + + [TestMethod] + public void CanWriteShouldReturnTrue() + { + Assert.IsTrue(_target.CanWrite); + } + + [TestMethod] + public void CanTimeoutShouldReturnTrue() + { + Assert.IsTrue(_target.CanTimeout); + } + + [TestMethod] + public void PositionShouldReturnZero() + { + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + var actual = _target.Position; + + Assert.AreEqual(0L, actual); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public async Task ReadShouldThrowNotSupportedException() + { + var buffer = new byte[_readBufferSize]; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + try + { + await _target.ReadAsync(buffer, 0, buffer.Length); + Assert.Fail(); + } + catch (NotSupportedException ex) + { + Assert.IsNull(ex.InnerException); + } + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public async Task WriteShouldStartWritingAtBeginningOfFile() + { + var buffer = new byte[_writeBufferSize]; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken)).Returns(Task.CompletedTask); + + await _target.WriteAsync(buffer, 0, buffer.Length); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + SftpSessionMock.Verify(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken), Times.Once); + } + + [TestMethod] + public void RequestOpenOnSftpSessionShouldBeInvokedOnceWithTruncateAndOnceWithCreateNew() + { + SftpSessionMock.Verify(p => p.RequestOpenAsync(_path, Flags.Write | Flags.CreateNewOrOpen | Flags.Truncate, _cancellationToken), Times.Once); + } + } +} +#endif \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessWrite_FileExists.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessWrite_FileExists.cs new file mode 100644 index 000000000..834299706 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessWrite_FileExists.cs @@ -0,0 +1,136 @@ +#if FEATURE_TAP +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessWrite_FileExists : SftpFileStreamAsyncTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private uint _readBufferSize; + private uint _writeBufferSize; + private byte[] _handle; + private SftpFileStream _target; + private CancellationToken _cancellationToken; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = FileMode.Create; + _fileAccess = FileAccess.Write; + _bufferSize = _random.Next(5, 1000); + _readBufferSize = (uint) _random.Next(5, 1000); + _writeBufferSize = (uint) _random.Next(5, 1000); + _handle = GenerateRandom(_random.Next(1, 10), _random); + _cancellationToken = new CancellationToken(); + } + + protected override void SetupMocks() + { + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpenAsync(_path, Flags.Write | Flags.CreateNewOrOpen | Flags.Truncate, _cancellationToken)) + .ReturnsAsync(_handle); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength((uint) _bufferSize)) + .Returns(_readBufferSize); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength((uint) _bufferSize, _handle)) + .Returns(_writeBufferSize); + } + + protected override async Task ActAsync() + { + _target = await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, _cancellationToken); + } + + [TestMethod] + public void CanReadShouldReturnFalse() + { + Assert.IsFalse(_target.CanRead); + } + + [TestMethod] + public void CanSeekShouldReturnTrue() + { + Assert.IsTrue(_target.CanSeek); + } + + [TestMethod] + public void CanWriteShouldReturnTrue() + { + Assert.IsTrue(_target.CanWrite); + } + + [TestMethod] + public void CanTimeoutShouldReturnTrue() + { + Assert.IsTrue(_target.CanTimeout); + } + + [TestMethod] + public void PositionShouldReturnZero() + { + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + var actual = _target.Position; + + Assert.AreEqual(0L, actual); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public async Task ReadShouldThrowNotSupportedException() + { + var buffer = new byte[_readBufferSize]; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + try + { + await _target.ReadAsync(buffer, 0, buffer.Length); + Assert.Fail(); + } + catch (NotSupportedException ex) + { + Assert.IsNull(ex.InnerException); + } + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public async Task WriteShouldStartWritingAtBeginningOfFile() + { + var buffer = new byte[_writeBufferSize]; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken)).Returns(Task.CompletedTask); + + await _target.WriteAsync(buffer, 0, buffer.Length); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + SftpSessionMock.Verify(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken), Times.Once); + } + + [TestMethod] + public void RequestOpenOnSftpSessionShouldBeInvokedOnce() + { + SftpSessionMock.Verify(p => p.RequestOpenAsync(_path, Flags.Write | Flags.CreateNewOrOpen | Flags.Truncate, _cancellationToken), Times.Once); + } + } +} +#endif \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeInvalid.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeInvalid.cs new file mode 100644 index 000000000..9cfc4a37f --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeInvalid.cs @@ -0,0 +1,57 @@ +#if FEATURE_TAP +using System; +using System.IO; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_OpenAsync_FileModeInvalid : SftpFileStreamAsyncTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private ArgumentOutOfRangeException _actualException; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = 0; + _fileAccess = FileAccess.Read; + _bufferSize = _random.Next(5, 1000); + } + + protected override void SetupMocks() + { + } + + protected override async Task ActAsync() + { + try + { + await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, default); + Assert.Fail(); + } + catch (ArgumentOutOfRangeException ex) + { + _actualException = ex; + } + } + + [TestMethod] + public void CtorShouldHaveThrownArgumentException() + { + Assert.IsNotNull(_actualException); + Assert.IsNull(_actualException.InnerException); + Assert.AreEqual("mode", _actualException.ParamName); + } + } +} +#endif \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpenOrCreate_FileAccessRead.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpenOrCreate_FileAccessRead.cs new file mode 100644 index 000000000..f9360c40c --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpenOrCreate_FileAccessRead.cs @@ -0,0 +1,141 @@ +#if FEATURE_TAP +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Renci.SshNet.Common; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_OpenAsync_FileModeOpenOrCreate_FileAccessRead : SftpFileStreamAsyncTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private uint _readBufferSize; + private uint _writeBufferSize; + private byte[] _handle; + private SftpFileStream _target; + private CancellationToken _cancellationToken; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = FileMode.OpenOrCreate; + _fileAccess = FileAccess.Read; + _bufferSize = _random.Next(5, 1000); + _readBufferSize = (uint) _random.Next(5, 1000); + _writeBufferSize = (uint) _random.Next(5, 1000); + _handle = GenerateRandom(_random.Next(1, 10), _random); + _cancellationToken = new CancellationToken(); + } + + protected override void SetupMocks() + { + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpenAsync(_path, Flags.Read | Flags.CreateNewOrOpen, _cancellationToken)) + .ReturnsAsync(_handle); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength((uint) _bufferSize)) + .Returns(_readBufferSize); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength((uint) _bufferSize, _handle)) + .Returns(_writeBufferSize); + } + + protected override async Task ActAsync() + { + _target = await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, _cancellationToken); + } + + [TestMethod] + public void CanReadShouldReturnTrue() + { + Assert.IsTrue(_target.CanRead); + } + + [TestMethod] + public void CanSeekShouldReturnTrue() + { + Assert.IsTrue(_target.CanSeek); + } + + [TestMethod] + public void CanWriteShouldReturnFalse() + { + Assert.IsFalse(_target.CanWrite); + } + + [TestMethod] + public void CanTimeoutShouldReturnTrue() + { + Assert.IsTrue(_target.CanTimeout); + } + + [TestMethod] + public void PositionShouldReturnZero() + { + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + var actual = _target.Position; + + Assert.AreEqual(0L, actual); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public async Task ReadShouldStartReadingAtBeginningOfFile() + { + var buffer = new byte[8]; + var data = new byte[] { 5, 4, 3, 2, 1 }; + var expected = new byte[] { 0, 5, 4, 3, 2, 1, 0, 0 }; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestReadAsync(_handle, 0UL, _readBufferSize, _cancellationToken)).ReturnsAsync(data); + + var actual = await _target.ReadAsync(buffer, 1, data.Length); + + Assert.AreEqual(data.Length, actual); + Assert.IsTrue(buffer.IsEqualTo(expected)); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + SftpSessionMock.Verify(p => p.RequestReadAsync(_handle, 0UL, _readBufferSize, _cancellationToken), Times.Once); + } + + [TestMethod] + public async Task WriteShouldThrowNotSupportedException() + { + var buffer = new byte[_writeBufferSize]; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + try + { + await _target.WriteAsync(buffer, 0, buffer.Length); + } + catch (NotSupportedException ex) + { + Assert.IsNull(ex.InnerException); + } + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public void RequestOpenOnSftpSessionShouldBeInvokedOnce() + { + SftpSessionMock.Verify(p => p.RequestOpenAsync(_path, Flags.Read | Flags.CreateNewOrOpen, _cancellationToken), Times.Once); + } + } +} +#endif \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpenOrCreate_FileAccessReadWrite.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpenOrCreate_FileAccessReadWrite.cs new file mode 100644 index 000000000..7e5e4d867 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpenOrCreate_FileAccessReadWrite.cs @@ -0,0 +1,136 @@ +#if FEATURE_TAP +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Renci.SshNet.Common; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_OpenAsync_FileModeOpenOrCreate_FileAccessReadWrite : SftpFileStreamAsyncTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private uint _readBufferSize; + private uint _writeBufferSize; + private byte[] _handle; + private SftpFileStream _target; + private CancellationToken _cancellationToken; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = FileMode.OpenOrCreate; + _fileAccess = FileAccess.ReadWrite; + _bufferSize = _random.Next(5, 1000); + _readBufferSize = (uint)_random.Next(5, 1000); + _writeBufferSize = (uint)_random.Next(5, 1000); + _handle = GenerateRandom(_random.Next(1, 10), _random); + _cancellationToken = new CancellationToken(); + } + + protected override void SetupMocks() + { + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpenAsync(_path, Flags.Read | Flags.Write | Flags.CreateNewOrOpen, _cancellationToken)) + .ReturnsAsync(_handle); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength((uint) _bufferSize)) + .Returns(_readBufferSize); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength((uint) _bufferSize, _handle)) + .Returns(_writeBufferSize); + } + + protected override async Task ActAsync() + { + _target = await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, _cancellationToken); + } + + [TestMethod] + public void CanReadShouldReturnTrue() + { + Assert.IsTrue(_target.CanRead); + } + + [TestMethod] + public void CanSeekShouldReturnTrue() + { + Assert.IsTrue(_target.CanSeek); + } + + [TestMethod] + public void CanWriteShouldReturnTrue() + { + Assert.IsTrue(_target.CanWrite); + } + + [TestMethod] + public void CanTimeoutShouldReturnTrue() + { + Assert.IsTrue(_target.CanTimeout); + } + + [TestMethod] + public void PositionShouldReturnZero() + { + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + var actual = _target.Position; + + Assert.AreEqual(0L, actual); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public async Task ReadShouldStartReadingAtBeginningOfFile() + { + var buffer = new byte[8]; + var data = new byte[] { 5, 4, 3, 2, 1 }; + var expected = new byte[] { 0, 5, 4, 3, 2, 1, 0, 0 }; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestReadAsync(_handle, 0UL, _readBufferSize, _cancellationToken)).ReturnsAsync(data); + + var actual = await _target.ReadAsync(buffer, 1, data.Length); + + Assert.AreEqual(data.Length, actual); + Assert.IsTrue(buffer.IsEqualTo(expected)); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + SftpSessionMock.Verify(p => p.RequestReadAsync(_handle, 0UL, _readBufferSize, _cancellationToken), Times.Once); + } + + [TestMethod] + public async Task WriteShouldStartWritingAtBeginningOfFile() + { + var buffer = new byte[_writeBufferSize]; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken)).Returns(Task.CompletedTask); + + await _target.WriteAsync(buffer, 0, buffer.Length); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + SftpSessionMock.Verify(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken), Times.Once); + } + + [TestMethod] + public void RequestOpenOnSftpSessionShouldBeInvokedOnce() + { + SftpSessionMock.Verify(p => p.RequestOpenAsync(_path, Flags.Read | Flags.Write | Flags.CreateNewOrOpen, _cancellationToken), Times.Once); + } + } +} +#endif \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpenOrCreate_FileAccessWrite.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpenOrCreate_FileAccessWrite.cs new file mode 100644 index 000000000..edccf9b99 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpenOrCreate_FileAccessWrite.cs @@ -0,0 +1,136 @@ +#if FEATURE_TAP +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_OpenAsync_FileModeOpenOrCreate_FileAccessWrite : SftpFileStreamAsyncTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private uint _readBufferSize; + private uint _writeBufferSize; + private byte[] _handle; + private SftpFileStream _target; + private CancellationToken _cancellationToken; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = FileMode.OpenOrCreate; + _fileAccess = FileAccess.Write; + _bufferSize = _random.Next(5, 1000); + _readBufferSize = (uint) _random.Next(5, 1000); + _writeBufferSize = (uint) _random.Next(5, 1000); + _handle = GenerateRandom(_random.Next(1, 10), _random); + _cancellationToken = new CancellationToken(); + } + + protected override void SetupMocks() + { + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpenAsync(_path, Flags.Write | Flags.CreateNewOrOpen, _cancellationToken)) + .ReturnsAsync(_handle); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength((uint) _bufferSize)) + .Returns(_readBufferSize); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength((uint) _bufferSize, _handle)) + .Returns(_writeBufferSize); + } + + protected override async Task ActAsync() + { + _target = await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, _cancellationToken); + } + + [TestMethod] + public void CanReadShouldReturnFalse() + { + Assert.IsFalse(_target.CanRead); + } + + [TestMethod] + public void CanSeekShouldReturnTrue() + { + Assert.IsTrue(_target.CanSeek); + } + + [TestMethod] + public void CanWriteShouldReturnTrue() + { + Assert.IsTrue(_target.CanWrite); + } + + [TestMethod] + public void CanTimeoutShouldReturnTrue() + { + Assert.IsTrue(_target.CanTimeout); + } + + [TestMethod] + public void PositionShouldReturnZero() + { + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + var actual = _target.Position; + + Assert.AreEqual(0L, actual); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public async Task ReadShouldThrowNotSupportedException() + { + var buffer = new byte[_readBufferSize]; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + try + { + await _target.ReadAsync(buffer, 0, buffer.Length); + Assert.Fail(); + } + catch (NotSupportedException ex) + { + Assert.IsNull(ex.InnerException); + } + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public async Task WriteShouldStartWritingAtBeginningOfFile() + { + var buffer = new byte[_writeBufferSize]; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken)).Returns(Task.CompletedTask); + + await _target.WriteAsync(buffer, 0, buffer.Length); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + SftpSessionMock.Verify(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken), Times.Once); + } + + [TestMethod] + public void RequestOpenOnSftpSessionShouldBeInvokedOnce() + { + SftpSessionMock.Verify(p => p.RequestOpenAsync(_path, Flags.Write | Flags.CreateNewOrOpen, _cancellationToken), Times.Once); + } + } +} +#endif \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpen_FileAccessRead.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpen_FileAccessRead.cs new file mode 100644 index 000000000..1d4f686fa --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpen_FileAccessRead.cs @@ -0,0 +1,142 @@ +#if FEATURE_TAP +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Renci.SshNet.Common; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_OpenAsync_FileModeOpen_FileAccessRead : SftpFileStreamAsyncTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private uint _readBufferSize; + private uint _writeBufferSize; + private byte[] _handle; + private SftpFileStream _target; + private CancellationToken _cancellationToken; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = FileMode.Open; + _fileAccess = FileAccess.Read; + _bufferSize = _random.Next(5, 1000); + _readBufferSize = (uint)_random.Next(5, 1000); + _writeBufferSize = (uint)_random.Next(5, 1000); + _handle = GenerateRandom(_random.Next(1, 10), _random); + _cancellationToken = new CancellationToken(); + } + + protected override void SetupMocks() + { + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpenAsync(_path, Flags.Read, _cancellationToken)) + .ReturnsAsync(_handle); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength((uint) _bufferSize)) + .Returns(_readBufferSize); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength((uint) _bufferSize, _handle)) + .Returns(_writeBufferSize); + } + + protected override async Task ActAsync() + { + _target = await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, _cancellationToken); + } + + [TestMethod] + public void CanReadShouldReturnTrue() + { + Assert.IsTrue(_target.CanRead); + } + + [TestMethod] + public void CanSeekShouldReturnTrue() + { + Assert.IsTrue(_target.CanSeek); + } + + [TestMethod] + public void CanWriteShouldReturnFalse() + { + Assert.IsFalse(_target.CanWrite); + } + + [TestMethod] + public void CanTimeoutShouldReturnTrue() + { + Assert.IsTrue(_target.CanTimeout); + } + + [TestMethod] + public void PositionShouldReturnZero() + { + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + var actual = _target.Position; + + Assert.AreEqual(0L, actual); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public async Task ReadShouldStartReadingAtBeginningOfFile() + { + var buffer = new byte[8]; + var data = new byte[] { 5, 4, 3, 2, 1 }; + var expected = new byte[] { 0, 5, 4, 3, 2, 1, 0, 0 }; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestReadAsync(_handle, 0UL, _readBufferSize, _cancellationToken)).ReturnsAsync(data); + + var actual = await _target.ReadAsync(buffer, 1, data.Length, _cancellationToken); + + Assert.AreEqual(data.Length, actual); + Assert.IsTrue(buffer.IsEqualTo(expected)); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + SftpSessionMock.Verify(p => p.RequestReadAsync(_handle, 0UL, _readBufferSize, _cancellationToken), Times.Once); + } + + [TestMethod] + public async Task WriteShouldThrowNotSupportedException() + { + var buffer = new byte[_writeBufferSize]; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + try + { + await _target.WriteAsync(buffer, 0, buffer.Length, _cancellationToken); + } + catch (NotSupportedException ex) + { + Assert.IsNull(ex.InnerException); + Assert.AreEqual("Write not supported.", ex.Message); + } + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public void RequestOpenOnSftpSessionShouldBeInvokedOnce() + { + SftpSessionMock.Verify(p => p.RequestOpenAsync(_path, Flags.Read, _cancellationToken), Times.Once); + } + } +} +#endif \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpen_FileAccessReadWrite.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpen_FileAccessReadWrite.cs new file mode 100644 index 000000000..8ee641337 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpen_FileAccessReadWrite.cs @@ -0,0 +1,136 @@ +#if FEATURE_TAP +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Renci.SshNet.Common; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_OpenAsync_FileModeOpen_FileAccessReadWrite : SftpFileStreamAsyncTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private uint _readBufferSize; + private uint _writeBufferSize; + private byte[] _handle; + private SftpFileStream _target; + private CancellationToken _cancellationToken; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = FileMode.Open; + _fileAccess = FileAccess.ReadWrite; + _bufferSize = _random.Next(5, 1000); + _readBufferSize = (uint) _random.Next(5, 1000); + _writeBufferSize = (uint) _random.Next(5, 1000); + _handle = GenerateRandom(_random.Next(1, 10), _random); + _cancellationToken = new CancellationToken(); + } + + protected override void SetupMocks() + { + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpenAsync(_path, Flags.Read | Flags.Write, _cancellationToken)) + .ReturnsAsync(_handle); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength((uint) _bufferSize)) + .Returns(_readBufferSize); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength((uint) _bufferSize, _handle)) + .Returns(_writeBufferSize); + } + + protected override async Task ActAsync() + { + _target = await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, _cancellationToken); + } + + [TestMethod] + public void CanReadShouldReturnTrue() + { + Assert.IsTrue(_target.CanRead); + } + + [TestMethod] + public void CanSeekShouldReturnTrue() + { + Assert.IsTrue(_target.CanSeek); + } + + [TestMethod] + public void CanWriteShouldReturnTrue() + { + Assert.IsTrue(_target.CanWrite); + } + + [TestMethod] + public void CanTimeoutShouldReturnTrue() + { + Assert.IsTrue(_target.CanTimeout); + } + + [TestMethod] + public void PositionShouldReturnZero() + { + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + var actual = _target.Position; + + Assert.AreEqual(0L, actual); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public async Task ReadShouldStartReadingAtBeginningOfFile() + { + var buffer = new byte[8]; + var data = new byte[] { 5, 4, 3, 2, 1 }; + var expected = new byte[] { 0, 5, 4, 3, 2, 1, 0, 0 }; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestReadAsync(_handle, 0UL, _readBufferSize, _cancellationToken)).ReturnsAsync(data); + + var actual = await _target.ReadAsync(buffer, 1, data.Length); + + Assert.AreEqual(data.Length, actual); + Assert.IsTrue(buffer.IsEqualTo(expected)); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + SftpSessionMock.Verify(p => p.RequestReadAsync(_handle, 0UL, _readBufferSize, _cancellationToken), Times.Once); + } + + [TestMethod] + public async Task WriteShouldStartWritingAtBeginningOfFile() + { + var buffer = new byte[_writeBufferSize]; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken)).Returns(Task.CompletedTask); + + await _target.WriteAsync(buffer, 0, buffer.Length); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + SftpSessionMock.Verify(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken), Times.Once); + } + + [TestMethod] + public void RequestOpenOnSftpSessionShouldBeInvokedOnce() + { + SftpSessionMock.Verify(p => p.RequestOpenAsync(_path, Flags.Read | Flags.Write, _cancellationToken), Times.Once); + } + } +} +#endif \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpen_FileAccessWrite.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpen_FileAccessWrite.cs new file mode 100644 index 000000000..4be281731 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpen_FileAccessWrite.cs @@ -0,0 +1,136 @@ +#if FEATURE_TAP +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_OpenAsync_FileModeOpen_FileAccessWrite : SftpFileStreamAsyncTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private uint _readBufferSize; + private uint _writeBufferSize; + private byte[] _handle; + private SftpFileStream _target; + private CancellationToken _cancellationToken; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = FileMode.Open; + _fileAccess = FileAccess.Write; + _bufferSize = _random.Next(5, 1000); + _readBufferSize = (uint) _random.Next(5, 1000); + _writeBufferSize = (uint) _random.Next(5, 1000); + _handle = GenerateRandom(_random.Next(1, 10), _random); + _cancellationToken = new CancellationToken(); + } + + protected override void SetupMocks() + { + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpenAsync(_path, Flags.Write, _cancellationToken)) + .ReturnsAsync(_handle); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength((uint) _bufferSize)) + .Returns(_readBufferSize); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength((uint) _bufferSize, _handle)) + .Returns(_writeBufferSize); + } + + protected override async Task ActAsync() + { + _target = await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, _cancellationToken); + } + + [TestMethod] + public void CanReadShouldReturnFalse() + { + Assert.IsFalse(_target.CanRead); + } + + [TestMethod] + public void CanSeekShouldReturnTrue() + { + Assert.IsTrue(_target.CanSeek); + } + + [TestMethod] + public void CanWriteShouldReturnTrue() + { + Assert.IsTrue(_target.CanWrite); + } + + [TestMethod] + public void CanTimeoutShouldReturnTrue() + { + Assert.IsTrue(_target.CanTimeout); + } + + [TestMethod] + public void PositionShouldReturnZero() + { + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + var actual = _target.Position; + + Assert.AreEqual(0L, actual); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public async Task ReadShouldThrowNotSupportedException() + { + var buffer = new byte[_readBufferSize]; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + try + { + await _target.ReadAsync(buffer, 0, buffer.Length); + Assert.Fail(); + } + catch (NotSupportedException ex) + { + Assert.IsNull(ex.InnerException); + } + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public async Task WriteShouldStartWritingAtBeginningOfFile() + { + var buffer = new byte[_writeBufferSize]; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken)).Returns(Task.CompletedTask); + + await _target.WriteAsync(buffer, 0, buffer.Length); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + SftpSessionMock.Verify(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken), Times.Once); + } + + [TestMethod] + public void RequestOpenOnSftpSessionShouldBeInvokedOnce() + { + SftpSessionMock.Verify(p => p.RequestOpenAsync(_path, Flags.Write, _cancellationToken), Times.Once); + } + } +} +#endif \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeTruncate_FileAccessRead.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeTruncate_FileAccessRead.cs new file mode 100644 index 000000000..9883a5a76 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeTruncate_FileAccessRead.cs @@ -0,0 +1,59 @@ +#if FEATURE_TAP +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_OpenAsync_FileModeTruncate_FileAccessRead : SftpFileStreamAsyncTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private ArgumentException _actualException; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = FileMode.Truncate; + _fileAccess = FileAccess.Read; + _bufferSize = _random.Next(5, 1000); + } + + protected override void SetupMocks() + { + } + + protected override async Task ActAsync() + { + try + { + await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, default); + Assert.Fail(); + } + catch (ArgumentException ex) + { + _actualException = ex; + } + } + + [TestMethod] + public void CtorShouldHaveThrownArgumentException() + { + Assert.IsNotNull(_actualException); + Assert.IsNull(_actualException.InnerException); + Assert.AreEqual(string.Format("Combining {0}: {1} with {2}: {3} is invalid.", typeof(FileMode).Name, _fileMode, typeof(FileAccess).Name, _fileAccess), _actualException.Message); + Assert.IsNull(_actualException.ParamName); + } + } +} +#endif \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeTruncate_FileAccessReadWrite.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeTruncate_FileAccessReadWrite.cs new file mode 100644 index 000000000..2d0efd24f --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeTruncate_FileAccessReadWrite.cs @@ -0,0 +1,136 @@ +#if FEATURE_TAP +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Renci.SshNet.Common; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_OpenAsync_FileModeTruncate_FileAccessReadWrite : SftpFileStreamAsyncTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private uint _readBufferSize; + private uint _writeBufferSize; + private byte[] _handle; + private SftpFileStream _target; + private CancellationToken _cancellationToken; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = FileMode.Truncate; + _fileAccess = FileAccess.ReadWrite; + _bufferSize = _random.Next(5, 1000); + _readBufferSize = (uint) _random.Next(5, 1000); + _writeBufferSize = (uint) _random.Next(5, 1000); + _handle = GenerateRandom(_random.Next(1, 10), _random); + _cancellationToken = new CancellationToken(); + } + + protected override void SetupMocks() + { + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpenAsync(_path, Flags.Read | Flags.Write | Flags.Truncate, _cancellationToken)) + .ReturnsAsync(_handle); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength((uint) _bufferSize)) + .Returns(_readBufferSize); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength((uint) _bufferSize, _handle)) + .Returns(_writeBufferSize); + } + + protected override async Task ActAsync() + { + _target = await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, _cancellationToken); + } + + [TestMethod] + public void CanReadShouldReturnTrue() + { + Assert.IsTrue(_target.CanRead); + } + + [TestMethod] + public void CanSeekShouldReturnTrue() + { + Assert.IsTrue(_target.CanSeek); + } + + [TestMethod] + public void CanWriteShouldReturnTrue() + { + Assert.IsTrue(_target.CanWrite); + } + + [TestMethod] + public void CanTimeoutShouldReturnTrue() + { + Assert.IsTrue(_target.CanTimeout); + } + + [TestMethod] + public void PositionShouldReturnZero() + { + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + var actual = _target.Position; + + Assert.AreEqual(0L, actual); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public async Task ReadShouldStartReadingAtBeginningOfFile() + { + var buffer = new byte[8]; + var data = new byte[] { 5, 4, 3, 2, 1 }; + var expected = new byte[] { 0, 5, 4, 3, 2, 1, 0, 0 }; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestReadAsync(_handle, 0UL, _readBufferSize, _cancellationToken)).ReturnsAsync(data); + + var actual = await _target.ReadAsync(buffer, 1, data.Length); + + Assert.AreEqual(data.Length, actual); + Assert.IsTrue(buffer.IsEqualTo(expected)); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + SftpSessionMock.Verify(p => p.RequestReadAsync(_handle, 0UL, _readBufferSize, _cancellationToken), Times.Once); + } + + [TestMethod] + public async Task WriteShouldStartWritingAtBeginningOfFile() + { + var buffer = new byte[_writeBufferSize]; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken)).Returns(Task.CompletedTask); + + await _target.WriteAsync(buffer, 0, buffer.Length); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + SftpSessionMock.Verify(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken), Times.Once); + } + + [TestMethod] + public void RequestOpenOnSftpSessionShouldBeInvokedOnce() + { + SftpSessionMock.Verify(p => p.RequestOpenAsync(_path, Flags.Read | Flags.Write | Flags.Truncate, _cancellationToken), Times.Once); + } + } +} +#endif \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeTruncate_FileAccessWrite.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeTruncate_FileAccessWrite.cs new file mode 100644 index 000000000..5f61e1984 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeTruncate_FileAccessWrite.cs @@ -0,0 +1,136 @@ +#if FEATURE_TAP +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Renci.SshNet.Sftp; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_OpenAsync_FileModeTruncate_FileAccessWrite : SftpFileStreamAsyncTestBase + { + private Random _random; + private string _path; + private FileMode _fileMode; + private FileAccess _fileAccess; + private int _bufferSize; + private uint _readBufferSize; + private uint _writeBufferSize; + private byte[] _handle; + private SftpFileStream _target; + private CancellationToken _cancellationToken; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(); + _fileMode = FileMode.Truncate; + _fileAccess = FileAccess.Write; + _bufferSize = _random.Next(5, 1000); + _readBufferSize = (uint) _random.Next(5, 1000); + _writeBufferSize = (uint) _random.Next(5, 1000); + _handle = GenerateRandom(_random.Next(1, 10), _random); + _cancellationToken = new CancellationToken(); + } + + protected override void SetupMocks() + { + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpenAsync(_path, Flags.Write | Flags.Truncate, _cancellationToken)) + .ReturnsAsync(_handle); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength((uint) _bufferSize)) + .Returns(_readBufferSize); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength((uint) _bufferSize, _handle)) + .Returns(_writeBufferSize); + } + + protected override async Task ActAsync() + { + _target = await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize, _cancellationToken); + } + + [TestMethod] + public void CanReadShouldReturnFalse() + { + Assert.IsFalse(_target.CanRead); + } + + [TestMethod] + public void CanSeekShouldReturnTrue() + { + Assert.IsTrue(_target.CanSeek); + } + + [TestMethod] + public void CanWriteShouldReturnTrue() + { + Assert.IsTrue(_target.CanWrite); + } + + [TestMethod] + public void CanTimeoutShouldReturnTrue() + { + Assert.IsTrue(_target.CanTimeout); + } + + [TestMethod] + public void PositionShouldReturnZero() + { + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + var actual = _target.Position; + + Assert.AreEqual(0L, actual); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public async Task ReadShouldThrowNotSupportedException() + { + var buffer = new byte[_readBufferSize]; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + try + { + await _target.ReadAsync(buffer, 0, buffer.Length); + Assert.Fail(); + } + catch (NotSupportedException ex) + { + Assert.IsNull(ex.InnerException); + } + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + } + + [TestMethod] + public async Task WriteShouldStartWritingAtBeginningOfFile() + { + var buffer = new byte[_writeBufferSize]; + + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken)).Returns(Task.CompletedTask); + + await _target.WriteAsync(buffer, 0, buffer.Length); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1)); + SftpSessionMock.Verify(p => p.RequestWriteAsync(_handle, 0UL, buffer, 0, buffer.Length, _cancellationToken), Times.Once); + } + + [TestMethod] + public void RequestOpenOnSftpSessionShouldBeInvokedOnce() + { + SftpSessionMock.Verify(p => p.RequestOpenAsync(_path, Flags.Write | Flags.Truncate, _cancellationToken), Times.Once); + } + } +} +#endif \ No newline at end of file From 2fbdb462b644981d6c4ef5b92fd48f05920b6edd Mon Sep 17 00:00:00 2001 From: Igor Milavec Date: Wed, 22 Dec 2021 18:52:05 +0100 Subject: [PATCH 21/25] Add SftpFileStreamTest_WriteAsync_* --- ...tGreatherThanTwoTimesTheWriteBufferSize.cs | 143 ++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_WriteAsync_SessionOpen_CountGreatherThanTwoTimesTheWriteBufferSize.cs diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_WriteAsync_SessionOpen_CountGreatherThanTwoTimesTheWriteBufferSize.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_WriteAsync_SessionOpen_CountGreatherThanTwoTimesTheWriteBufferSize.cs new file mode 100644 index 000000000..dcbfedf63 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_WriteAsync_SessionOpen_CountGreatherThanTwoTimesTheWriteBufferSize.cs @@ -0,0 +1,143 @@ +#if FEATURE_TAP +using System; +using System.Globalization; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Renci.SshNet.Common; +using Renci.SshNet.Sftp; +using Renci.SshNet.Sftp.Responses; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_WriteAsync_SessionOpen_CountGreatherThanTwoTimesTheWriteBufferSize : SftpFileStreamAsyncTestBase + { + private SftpFileStream _target; + private string _path; + private byte[] _handle; + private uint _bufferSize; + private uint _readBufferSize; + private uint _writeBufferSize; + private byte[] _data; + private int _count; + private int _offset; + private Random _random; + private uint _expectedWrittenByteCount; + private int _expectedBufferedByteCount; + private byte[] _expectedBufferedBytes; + private CancellationToken _cancellationToken; + + protected override void SetupData() + { + base.SetupData(); + + _random = new Random(); + _path = _random.Next().ToString(CultureInfo.InvariantCulture); + _handle = GenerateRandom(5, _random); + _bufferSize = (uint)_random.Next(1, 1000); + _readBufferSize = (uint) _random.Next(0, 1000); + _writeBufferSize = (uint) _random.Next(500, 1000); + _data = new byte[(_writeBufferSize * 2) + 15]; + _random.NextBytes(_data); + _offset = _random.Next(1, 5); + // to get multiple SSH_FXP_WRITE messages (and verify the offset is updated correctly), we make sure + // the number of bytes to write is at least two times the write buffer size; we write a few extra bytes to + // ensure the buffer is not empty after the writes so we can verify whether Length, Dispose and Flush + // flush the buffer + _count = ((int) _writeBufferSize * 2) + _random.Next(1, 5); + + _expectedWrittenByteCount = (2 * _writeBufferSize); + _expectedBufferedByteCount = (int)(_count - _expectedWrittenByteCount); + _expectedBufferedBytes = _data.Take(_offset + (int)_expectedWrittenByteCount, _expectedBufferedByteCount); + _cancellationToken = new CancellationToken(); + } + + protected override void SetupMocks() + { + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpenAsync(_path, Flags.Write | Flags.CreateNewOrOpen | Flags.Truncate, _cancellationToken)) + .ReturnsAsync(_handle); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength(_bufferSize)) + .Returns(_readBufferSize); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength(_bufferSize, _handle)) + .Returns(_writeBufferSize); + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestWriteAsync(_handle, 0, _data, _offset, (int)_writeBufferSize, _cancellationToken)) + .Returns(Task.CompletedTask); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestWriteAsync(_handle, _writeBufferSize, _data, _offset + (int)_writeBufferSize, (int)_writeBufferSize, _cancellationToken)) + .Returns(Task.CompletedTask); + } + + [TestCleanup] + public void TearDown() + { + if (SftpSessionMock != null) + { + // allow Dispose to complete successfully + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestWriteAsync(_handle, _expectedWrittenByteCount, It.IsAny(), 0, _expectedBufferedByteCount, _cancellationToken)) + .Returns(Task.CompletedTask); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestClose(_handle)); + } + } + + protected override async Task ArrangeAsync() + { + await base.ArrangeAsync(); + + _target = await SftpFileStream.OpenAsync(SftpSessionMock.Object, _path, FileMode.Create, FileAccess.Write, (int) _bufferSize, _cancellationToken); + } + + protected override Task ActAsync() + { + return _target.WriteAsync(_data, _offset, _count); + } + + [TestMethod] + public void RequestWriteOnSftpSessionShouldBeInvokedTwice() + { + SftpSessionMock.Verify(p => p.RequestWriteAsync(_handle, 0, _data, _offset, (int)_writeBufferSize, _cancellationToken), Times.Once); + SftpSessionMock.Verify(p => p.RequestWriteAsync(_handle, _writeBufferSize, _data, _offset + (int)_writeBufferSize, (int)_writeBufferSize, _cancellationToken), Times.Once); + } + + [TestMethod] + public void PositionShouldBeNumberOfBytesWrittenToFileAndNUmberOfBytesInBuffer() + { + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + Assert.AreEqual(_count, _target.Position); + } + + [TestMethod] + public async Task FlushShouldFlushBuffer() + { + byte[] actualFlushedData = null; + + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestWriteAsync(_handle, _expectedWrittenByteCount, It.IsAny(), 0, _expectedBufferedByteCount, _cancellationToken)) + .Callback((handle, serverFileOffset, data, offset, length, ct) => actualFlushedData = data.Take(offset, length)) + .Returns(Task.CompletedTask); + + await _target.FlushAsync(); + + Assert.IsTrue(actualFlushedData.IsEqualTo(_expectedBufferedBytes)); + + SftpSessionMock.Verify(p => p.RequestWriteAsync(_handle, _expectedWrittenByteCount, It.IsAny(), 0, _expectedBufferedByteCount, _cancellationToken), Times.Once); + } + } +} +#endif \ No newline at end of file From 3547504db2ba4ab244f9ea366bc04b93088e8692 Mon Sep 17 00:00:00 2001 From: Igor Milavec Date: Wed, 22 Dec 2021 19:13:53 +0100 Subject: [PATCH 22/25] Add SftpFileStreamTest_ReadAsync_* --- ...romServerThanCountAndLessThanBufferSize.cs | 152 ++++++++++++++++++ ...fferAndReadMoreBytesFromServerThanCount.cs | 143 ++++++++++++++++ 2 files changed, 295 insertions(+) create mode 100644 src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_ReadAsync_ReadMode_NoDataInReaderBufferAndReadLessBytesFromServerThanCountAndLessThanBufferSize.cs create mode 100644 src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_ReadAsync_ReadMode_NoDataInReaderBufferAndReadMoreBytesFromServerThanCount.cs diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_ReadAsync_ReadMode_NoDataInReaderBufferAndReadLessBytesFromServerThanCountAndLessThanBufferSize.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_ReadAsync_ReadMode_NoDataInReaderBufferAndReadLessBytesFromServerThanCountAndLessThanBufferSize.cs new file mode 100644 index 000000000..2da0a4385 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_ReadAsync_ReadMode_NoDataInReaderBufferAndReadLessBytesFromServerThanCountAndLessThanBufferSize.cs @@ -0,0 +1,152 @@ +#if FEATURE_TAP +using System; +using System.IO; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Renci.SshNet.Common; +using Renci.SshNet.Sftp; +using Renci.SshNet.Tests.Common; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_ReadAsync_ReadMode_NoDataInReaderBufferAndReadLessBytesFromServerThanCountAndLessThanBufferSize : SftpFileStreamAsyncTestBase + { + private string _path; + private SftpFileStream _target; + private byte[] _handle; + private uint _bufferSize; + private uint _readBufferSize; + private uint _writeBufferSize; + private int _actual; + private byte[] _buffer; + private byte[] _serverData; + private int _serverDataLength; + private int _numberOfBytesToRead; + private byte[] _originalBuffer; + + protected override void SetupData() + { + base.SetupData(); + + var random = new Random(); + _path = random.Next().ToString(); + _handle = GenerateRandom(5, random); + _bufferSize = (uint)random.Next(1, 1000); + _readBufferSize = 20; + _writeBufferSize = 500; + + _numberOfBytesToRead = (int) _readBufferSize + 2; // greater than read buffer size + _originalBuffer = GenerateRandom(_numberOfBytesToRead, random); + _buffer = _originalBuffer.Copy(); + + _serverDataLength = (int) _readBufferSize - 1; // less than read buffer size + _serverData = GenerateRandom(_serverDataLength, random); + + Assert.IsTrue(_serverDataLength < _numberOfBytesToRead && _serverDataLength < _readBufferSize); + } + + protected override void SetupMocks() + { + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpenAsync(_path, Flags.Read, default)) + .ReturnsAsync(_handle); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength(_bufferSize)) + .Returns(_readBufferSize); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength(_bufferSize, _handle)) + .Returns(_writeBufferSize); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestReadAsync(_handle, 0UL, _readBufferSize, default)) + .ReturnsAsync(_serverData); + } + + [TestCleanup] + public void TearDown() + { + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestClose(_handle)); + } + + protected override async Task ArrangeAsync() + { + await base.ArrangeAsync(); + + _target = await SftpFileStream.OpenAsync(SftpSessionMock.Object, + _path, + FileMode.Open, + FileAccess.Read, + (int)_bufferSize, + default); + } + + protected override async Task ActAsync() + { + _actual = await _target.ReadAsync(_buffer, 0, _numberOfBytesToRead, default); + } + + [TestMethod] + public void ReadShouldHaveReturnedTheNumberOfBytesReturnedByTheReadFromTheServer() + { + Assert.AreEqual(_serverDataLength, _actual); + } + + [TestMethod] + public void ReadShouldHaveWrittenBytesToTheCallerSuppliedBufferAndRemainingBytesShouldRemainUntouched() + { + Assert.IsTrue(_serverData.IsEqualTo(_buffer.Take(_serverDataLength))); + Assert.IsTrue(_originalBuffer.Take(_serverDataLength, _originalBuffer.Length - _serverDataLength).IsEqualTo(_buffer.Take(_serverDataLength, _buffer.Length - _serverDataLength))); + } + + [TestMethod] + public void PositionShouldReturnNumberOfBytesWrittenToCallerProvidedBuffer() + { + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + Assert.AreEqual(_actual, _target.Position); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(2)); + } + + [TestMethod] + public async Task SubsequentReadShouldReadAgainFromCurrentPositionFromServerAndReturnZeroWhenServerReturnsZeroBytes() + { + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestReadAsync(_handle, (ulong) _actual, _readBufferSize, default)) + .ReturnsAsync(Array.Empty); + + var buffer = _originalBuffer.Copy(); + var actual = await _target.ReadAsync(buffer, 0, buffer.Length); + + Assert.AreEqual(0, actual); + Assert.IsTrue(_originalBuffer.IsEqualTo(buffer)); + + SftpSessionMock.Verify(p => p.RequestReadAsync(_handle, (ulong)_actual, _readBufferSize, default), Times.Once); + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(2)); + } + + [TestMethod] + public async Task SubsequentReadShouldReadAgainFromCurrentPositionFromServerAndNotUpdatePositionWhenServerReturnsZeroBytes() + { + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestReadAsync(_handle, (ulong)_actual, _readBufferSize, default)) + .ReturnsAsync(Array.Empty); + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + await _target.ReadAsync(new byte[10], 0, 10); + + Assert.AreEqual(_actual, _target.Position); + + SftpSessionMock.Verify(p => p.RequestReadAsync(_handle, (ulong)_actual, _readBufferSize, default), Times.Once); + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(3)); + } + } +} +#endif \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_ReadAsync_ReadMode_NoDataInReaderBufferAndReadMoreBytesFromServerThanCount.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_ReadAsync_ReadMode_NoDataInReaderBufferAndReadMoreBytesFromServerThanCount.cs new file mode 100644 index 000000000..80895217e --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_ReadAsync_ReadMode_NoDataInReaderBufferAndReadMoreBytesFromServerThanCount.cs @@ -0,0 +1,143 @@ +#if FEATURE_TAP +using System; +using System.IO; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Renci.SshNet.Sftp; +using Renci.SshNet.Common; +using System.Threading.Tasks; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpFileStreamTest_ReadAsync_ReadMode_NoDataInReaderBufferAndReadMoreBytesFromServerThanCount : SftpFileStreamAsyncTestBase + { + private string _path; + private SftpFileStream _target; + private byte[] _handle; + private uint _bufferSize; + private uint _readBufferSize; + private uint _writeBufferSize; + private int _actual; + private byte[] _buffer; + private byte[] _serverData; + private int _numberOfBytesToWriteToReadBuffer; + private int _numberOfBytesToRead; + + protected override void SetupData() + { + base.SetupData(); + + var random = new Random(); + _path = random.Next().ToString(); + _handle = GenerateRandom(5, random); + _bufferSize = (uint) random.Next(1, 1000); + _readBufferSize = 20; + _writeBufferSize = 500; + + _numberOfBytesToRead = 20; + _buffer = new byte[_numberOfBytesToRead]; + _numberOfBytesToWriteToReadBuffer = 10; // should be less than _readBufferSize + _serverData = GenerateRandom(_numberOfBytesToRead + _numberOfBytesToWriteToReadBuffer, random); + } + + protected override void SetupMocks() + { + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestOpenAsync(_path, Flags.Read, default)) + .ReturnsAsync(_handle); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalReadLength(_bufferSize)) + .Returns(_readBufferSize); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.CalculateOptimalWriteLength(_bufferSize, _handle)) + .Returns(_writeBufferSize); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.IsOpen) + .Returns(true); + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestReadAsync(_handle, 0UL, _readBufferSize, default)) + .ReturnsAsync(_serverData); + } + + [TestCleanup] + public void TearDown() + { + SftpSessionMock.InSequence(MockSequence) + .Setup(p => p.RequestClose(_handle)); + } + + protected override async Task ArrangeAsync() + { + await base.ArrangeAsync(); + + _target = await SftpFileStream.OpenAsync(SftpSessionMock.Object, + _path, + FileMode.Open, + FileAccess.Read, + (int)_bufferSize, + default); + } + + protected override async Task ActAsync() + { + _actual = await _target.ReadAsync(_buffer, 0, _numberOfBytesToRead, default); + } + + [TestMethod] + public void ReadShouldHaveReturnedTheNumberOfBytesWrittenToCallerSuppliedBuffer() + { + Assert.AreEqual(_numberOfBytesToRead, _actual); + } + + [TestMethod] + public void ReadShouldHaveWrittenBytesToTheCallerSuppliedBuffer() + { + Assert.IsTrue(_serverData.Take(_actual).IsEqualTo(_buffer)); + } + + [TestMethod] + public void PositionShouldReturnNumberOfBytesWrittenToCallerProvidedBuffer() + { + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + Assert.AreEqual(_actual, _target.Position); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(2)); + } + + [TestMethod] + public async Task SubsequentReadShouldReturnAllRemaningBytesFromReadBufferWhenCountIsEqualToNumberOfRemainingBytes() + { + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + + var buffer = new byte[_numberOfBytesToWriteToReadBuffer]; + + var actual = await _target.ReadAsync(buffer, 0, _numberOfBytesToWriteToReadBuffer, default); + + Assert.AreEqual(_numberOfBytesToWriteToReadBuffer, actual); + Assert.IsTrue(_serverData.Take(_numberOfBytesToRead, _numberOfBytesToWriteToReadBuffer).IsEqualTo(buffer)); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(2)); + } + + [TestMethod] + public async Task SubsequentReadShouldReturnAllRemaningBytesFromReadBufferAndReadAgainWhenCountIsGreaterThanNumberOfRemainingBytesAndNewReadReturnsZeroBytes() + { + SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true); + SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestReadAsync(_handle, (ulong)(_serverData.Length), _readBufferSize, default)).ReturnsAsync(Array.Empty); + + var buffer = new byte[_numberOfBytesToWriteToReadBuffer + 1]; + + var actual = await _target.ReadAsync(buffer, 0, buffer.Length); + + Assert.AreEqual(_numberOfBytesToWriteToReadBuffer, actual); + Assert.IsTrue(_serverData.Take(_numberOfBytesToRead, _numberOfBytesToWriteToReadBuffer).IsEqualTo(buffer.Take(_numberOfBytesToWriteToReadBuffer))); + Assert.AreEqual(0, buffer[_numberOfBytesToWriteToReadBuffer]); + + SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(2)); + SftpSessionMock.Verify(p => p.RequestReadAsync(_handle, (ulong)(_serverData.Length), _readBufferSize, default)); + } + } +} +#endif \ No newline at end of file From 35067079c0bfe32c9192cd6ec58c062d9154304c Mon Sep 17 00:00:00 2001 From: Igor Milavec Date: Sat, 12 Feb 2022 23:14:38 +0100 Subject: [PATCH 23/25] Remove unrelated changes --- appveyor.yml | 7 +- ...nected_KeepAliveInterval_NotNegativeOne.cs | 5 +- ...pose_SessionIsConnectedAndChannelIsOpen.cs | 11 +-- ...IsConnectedAndChannelIsOpen_EofReceived.cs | 8 +- .../Common/PipeStream_Close_BlockingWrite.cs | 15 ++-- ...ctConnectorTest_Connect_HostNameInvalid.cs | 5 +- ...pConnectorTest_Connect_ProxyHostInvalid.cs | 5 +- ...icTest_Dispose_PortStarted_ChannelBound.cs | 2 +- ...est_Dispose_PortStarted_ChannelNotBound.cs | 5 +- ...cTest_SessionErrorOccurred_ChannelBound.cs | 2 +- ...namicTest_Stop_PortStarted_ChannelBound.cs | 2 +- ...icTest_Stop_PortStarted_ChannelNotBound.cs | 5 +- ...alTest_Dispose_PortStarted_ChannelBound.cs | 2 +- .../Classes/NetConfClientTest.cs | 8 -- ..._Connected_ServerSendsDisconnectMessage.cs | 4 +- .../Classes/SftpClientTest.Connect.cs | 13 +-- .../Common/AsyncSocketListener.cs | 87 +++++++++++++------ src/Renci.SshNet/ForwardedPortDynamic.NET.cs | 6 -- 18 files changed, 90 insertions(+), 102 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index e8adeb222..c5bb4a18e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,8 +1,5 @@ os: Visual Studio 2019 -init: - - netsh int ipv4 set dynamicport tcp start=1025 num=64510 - before_build: - nuget restore src\Renci.SshNet.VS2019.sln @@ -12,6 +9,6 @@ build: test_script: - cmd: >- - vstest.console /logger:Appveyor src\Renci.SshNet.Tests\bin\Debug\net472\Renci.SshNet.Tests.dll /TestCaseFilter:"TestCategory!=integration&TestCategory!=LongRunning" + vstest.console /logger:Appveyor src\Renci.SshNet.Tests\bin\Debug\net40\Renci.SshNet.Tests.dll /TestCaseFilter:"TestCategory!=integration&TestCategory!=LongRunning" - vstest.console /logger:Appveyor src\Renci.SshNet.Tests\bin\Debug\net5.0\Renci.SshNet.Tests.dll /TestCaseFilter:"TestCategory!=integration&TestCategory!=LongRunning" \ No newline at end of file + vstest.console /logger:Appveyor src\Renci.SshNet.Tests\bin\Debug\net35\Renci.SshNet.Tests.dll /TestCaseFilter:"TestCategory!=integration&TestCategory!=LongRunning" \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Classes/BaseClientTest_Connected_KeepAliveInterval_NotNegativeOne.cs b/src/Renci.SshNet.Tests/Classes/BaseClientTest_Connected_KeepAliveInterval_NotNegativeOne.cs index 1e6a2f7cf..26a8da00e 100644 --- a/src/Renci.SshNet.Tests/Classes/BaseClientTest_Connected_KeepAliveInterval_NotNegativeOne.cs +++ b/src/Renci.SshNet.Tests/Classes/BaseClientTest_Connected_KeepAliveInterval_NotNegativeOne.cs @@ -13,7 +13,6 @@ public class BaseClientTest_Connected_KeepAliveInterval_NotNegativeOne : BaseCli private ConnectionInfo _connectionInfo; private TimeSpan _keepAliveInterval; private int _keepAliveCount; - private int _actualKeepAliveCount; protected override void SetupData() { @@ -59,8 +58,6 @@ protected override void Act() // allow keep-alive to be sent a few times Thread.Sleep(195); - - _actualKeepAliveCount = _keepAliveCount; } [TestMethod] @@ -97,7 +94,7 @@ public void IsConnectedOnSessionShouldBeInvokedOnce() [TestMethod] public void SendMessageOnSessionShouldBeInvokedThreeTimes() { - Assert.AreEqual(3, _actualKeepAliveCount); + _sessionMock.Verify(p => p.TrySendMessage(It.IsAny()), Times.Exactly(3)); } private class MyClient : BaseClient diff --git a/src/Renci.SshNet.Tests/Classes/Channels/ChannelForwardedTcpipTest_Dispose_SessionIsConnectedAndChannelIsOpen.cs b/src/Renci.SshNet.Tests/Classes/Channels/ChannelForwardedTcpipTest_Dispose_SessionIsConnectedAndChannelIsOpen.cs index 0d3c11453..bbbff6d37 100644 --- a/src/Renci.SshNet.Tests/Classes/Channels/ChannelForwardedTcpipTest_Dispose_SessionIsConnectedAndChannelIsOpen.cs +++ b/src/Renci.SshNet.Tests/Classes/Channels/ChannelForwardedTcpipTest_Dispose_SessionIsConnectedAndChannelIsOpen.cs @@ -51,13 +51,17 @@ public void CleanUp() _remoteListener = null; } + if (_channelThread != null) + { + if (_channelThread.IsAlive) + _channelThread.Abort(); + _channelThread = null; + } if (_channel != null) { _channel.Dispose(); _channel = null; } - - _channelThread = null; } private void Arrange() @@ -134,12 +138,10 @@ private void Arrange() _remoteWindowSize, _remotePacketSize); - ManualResetEvent isReady = new ManualResetEvent(false); _channelThread = new Thread(() => { try { - isReady.Set(); _channel.Bind(_remoteEndpoint, _forwardedPortMock.Object); } catch (Exception ex) @@ -154,7 +156,6 @@ private void Arrange() _channelThread.Start(); // give channel time to bind to remote endpoint - isReady.WaitOne(10000); Thread.Sleep(100); } diff --git a/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_Dispose_SessionIsConnectedAndChannelIsOpen_EofReceived.cs b/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_Dispose_SessionIsConnectedAndChannelIsOpen_EofReceived.cs index eb186f5e5..0bb9e862d 100644 --- a/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_Dispose_SessionIsConnectedAndChannelIsOpen_EofReceived.cs +++ b/src/Renci.SshNet.Tests/Classes/Channels/ChannelTest_Dispose_SessionIsConnectedAndChannelIsOpen_EofReceived.cs @@ -25,7 +25,6 @@ public class ChannelTest_Dispose_SessionIsConnectedAndChannelIsOpen_EofReceived private ManualResetEvent _channelClosedReceived; private ManualResetEvent _channelClosedEventHandlerCompleted; private Thread _raiseChannelCloseReceivedThread; - private ManualResetEvent _threadStopEvent; protected override void SetupData() { @@ -44,7 +43,6 @@ protected override void SetupData() _channelClosedReceived = new ManualResetEvent(false); _channelClosedEventHandlerCompleted = new ManualResetEvent(false); _raiseChannelCloseReceivedThread = null; - _threadStopEvent = new ManualResetEvent(false); } protected override void SetupMocks() @@ -82,9 +80,7 @@ protected override void SetupMocks() SessionMock.Raise(s => s.ChannelCloseReceived += null, new MessageEventArgs(new ChannelCloseMessage(_localChannelNumber))); }); _raiseChannelCloseReceivedThread.Start(); - - WaitHandle[] waitHandles = new WaitHandle[2] { waitHandle, _threadStopEvent }; - WaitHandle.WaitAny(waitHandles); + waitHandle.WaitOne(); }) .Returns(WaitResult.Success); } @@ -102,7 +98,7 @@ public void TearDown() { if (!_raiseChannelCloseReceivedThread.Join(1000)) { - _threadStopEvent.Set(); + _raiseChannelCloseReceivedThread.Abort(); } } diff --git a/src/Renci.SshNet.Tests/Classes/Common/PipeStream_Close_BlockingWrite.cs b/src/Renci.SshNet.Tests/Classes/Common/PipeStream_Close_BlockingWrite.cs index e233ac49f..1fbd2d158 100644 --- a/src/Renci.SshNet.Tests/Classes/Common/PipeStream_Close_BlockingWrite.cs +++ b/src/Renci.SshNet.Tests/Classes/Common/PipeStream_Close_BlockingWrite.cs @@ -11,14 +11,13 @@ public class PipeStream_Close_BlockingWrite : TripleATestBase { private PipeStream _pipeStream; private Exception _writeException; - private Thread _writeThread; + private Thread _writehread; protected override void Arrange() { _pipeStream = new PipeStream {MaxBufferLength = 3}; - ManualResetEvent isArranged = new ManualResetEvent(false); - _writeThread = new Thread(() => + _writehread = new Thread(() => { _pipeStream.WriteByte(10); _pipeStream.WriteByte(13); @@ -28,18 +27,18 @@ protected override void Arrange() // until bytes are read or the stream is closed try { - isArranged.Set(); _pipeStream.WriteByte(35); } catch (Exception ex) { _writeException = ex; + throw; } }); - _writeThread.Start(); + _writehread.Start(); // ensure we've started writing - isArranged.WaitOne(10000); + Assert.IsFalse(_writehread.Join(50)); } protected override void Act() @@ -47,13 +46,13 @@ protected override void Act() _pipeStream.Close(); // give write time to complete - Assert.IsTrue(_writeThread.Join(10000)); + _writehread.Join(100); } [TestMethod] public void BlockingWriteShouldHaveBeenInterrupted() { - Assert.AreEqual(ThreadState.Stopped, _writeThread.ThreadState); + Assert.AreEqual(ThreadState.Stopped, _writehread.ThreadState); } [TestMethod] diff --git a/src/Renci.SshNet.Tests/Classes/Connection/DirectConnectorTest_Connect_HostNameInvalid.cs b/src/Renci.SshNet.Tests/Classes/Connection/DirectConnectorTest_Connect_HostNameInvalid.cs index f24a1e0af..05a69e94b 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/DirectConnectorTest_Connect_HostNameInvalid.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/DirectConnectorTest_Connect_HostNameInvalid.cs @@ -36,10 +36,7 @@ public void ConnectShouldHaveThrownSocketException() { Assert.IsNotNull(_actualException); Assert.IsNull(_actualException.InnerException); - if (_actualException.SocketErrorCode != SocketError.TryAgain) - { - Assert.AreEqual(SocketError.HostNotFound, _actualException.SocketErrorCode); - } + Assert.AreEqual(SocketError.HostNotFound, _actualException.SocketErrorCode); } } } diff --git a/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyHostInvalid.cs b/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyHostInvalid.cs index 4d9325e5a..5843947dc 100644 --- a/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyHostInvalid.cs +++ b/src/Renci.SshNet.Tests/Classes/Connection/HttpConnectorTest_Connect_ProxyHostInvalid.cs @@ -43,10 +43,7 @@ public void ConnectShouldHaveThrownSocketException() { Assert.IsNotNull(_actualException); Assert.IsNull(_actualException.InnerException); - if (_actualException.SocketErrorCode != SocketError.TryAgain) - { - Assert.AreEqual(SocketError.HostNotFound, _actualException.SocketErrorCode); - } + Assert.AreEqual(SocketError.HostNotFound, _actualException.SocketErrorCode); } } } diff --git a/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Dispose_PortStarted_ChannelBound.cs b/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Dispose_PortStarted_ChannelBound.cs index 68e67a600..441250a0a 100644 --- a/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Dispose_PortStarted_ChannelBound.cs +++ b/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Dispose_PortStarted_ChannelBound.cs @@ -79,7 +79,7 @@ private void SetupData() _exceptionRegister = new List(); _endpoint = new IPEndPoint(IPAddress.Loopback, 8122); _remoteEndpoint = new IPEndPoint(IPAddress.Parse("193.168.1.5"), random.Next(IPEndPoint.MinPort, IPEndPoint.MaxPort)); - _bindSleepTime = TimeSpan.FromMilliseconds(random.Next(100, 200)); + _bindSleepTime = TimeSpan.FromMilliseconds(random.Next(100, 500)); _userName = random.Next().ToString(CultureInfo.InvariantCulture); _channelBindStarted = new ManualResetEvent(false); _channelBindCompleted = new ManualResetEvent(false); diff --git a/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Dispose_PortStarted_ChannelNotBound.cs b/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Dispose_PortStarted_ChannelNotBound.cs index f7ad267b7..6c4439440 100644 --- a/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Dispose_PortStarted_ChannelNotBound.cs +++ b/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Dispose_PortStarted_ChannelNotBound.cs @@ -130,10 +130,7 @@ public void ClosingShouldHaveFiredOnce() [TestMethod] public void ExceptionShouldNotHaveFired() { - if (_exceptionRegister.Count > 0) - { - throw new Exception("ForwardedPortDynamic rased an exception: " + _exceptionRegister[0].Exception.Message, _exceptionRegister[0].Exception); - } + Assert.AreEqual(0, _exceptionRegister.Count); } [TestMethod] diff --git a/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_SessionErrorOccurred_ChannelBound.cs b/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_SessionErrorOccurred_ChannelBound.cs index f92b5b5aa..db778a089 100644 --- a/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_SessionErrorOccurred_ChannelBound.cs +++ b/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_SessionErrorOccurred_ChannelBound.cs @@ -80,7 +80,7 @@ private void SetupData() _exceptionRegister = new List(); _endpoint = new IPEndPoint(IPAddress.Loopback, 8122); _remoteEndpoint = new IPEndPoint(IPAddress.Parse("193.168.1.5"), random.Next(IPEndPoint.MinPort, IPEndPoint.MaxPort)); - _bindSleepTime = TimeSpan.FromMilliseconds(random.Next(100, 200)); + _bindSleepTime = TimeSpan.FromMilliseconds(random.Next(100, 500)); _userName = random.Next().ToString(CultureInfo.InvariantCulture); _forwardedPort = new ForwardedPortDynamic(_endpoint.Address.ToString(), (uint) _endpoint.Port); _sessionException = new Exception(); diff --git a/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Stop_PortStarted_ChannelBound.cs b/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Stop_PortStarted_ChannelBound.cs index dd7bf213c..80f7877e3 100644 --- a/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Stop_PortStarted_ChannelBound.cs +++ b/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Stop_PortStarted_ChannelBound.cs @@ -78,7 +78,7 @@ private void SetupData() _exceptionRegister = new List(); _endpoint = new IPEndPoint(IPAddress.Loopback, 8122); _remoteEndpoint = new IPEndPoint(IPAddress.Parse("193.168.1.5"), random.Next(IPEndPoint.MinPort, IPEndPoint.MaxPort)); - _bindSleepTime = TimeSpan.FromMilliseconds(random.Next(100, 200)); + _bindSleepTime = TimeSpan.FromMilliseconds(random.Next(100, 500)); _userName = random.Next().ToString(CultureInfo.InvariantCulture); _channelBindStarted = new ManualResetEvent(false); _channelBindCompleted = new ManualResetEvent(false); diff --git a/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Stop_PortStarted_ChannelNotBound.cs b/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Stop_PortStarted_ChannelNotBound.cs index 40a2f00a9..b84d947d5 100644 --- a/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Stop_PortStarted_ChannelNotBound.cs +++ b/src/Renci.SshNet.Tests/Classes/ForwardedPortDynamicTest_Stop_PortStarted_ChannelNotBound.cs @@ -130,10 +130,7 @@ public void ClosingShouldHaveFiredOnce() [TestMethod] public void ExceptionShouldNotHaveFired() { - if (_exceptionRegister.Count > 0) - { - throw new Exception("ForwardedPortDynamic rased an exception: " + _exceptionRegister[0].Exception.Message, _exceptionRegister[0].Exception); - } + Assert.AreEqual(0, _exceptionRegister.Count); } [TestMethod] diff --git a/src/Renci.SshNet.Tests/Classes/ForwardedPortLocalTest_Dispose_PortStarted_ChannelBound.cs b/src/Renci.SshNet.Tests/Classes/ForwardedPortLocalTest_Dispose_PortStarted_ChannelBound.cs index 694a0fa12..9677734a2 100644 --- a/src/Renci.SshNet.Tests/Classes/ForwardedPortLocalTest_Dispose_PortStarted_ChannelBound.cs +++ b/src/Renci.SshNet.Tests/Classes/ForwardedPortLocalTest_Dispose_PortStarted_ChannelBound.cs @@ -65,7 +65,7 @@ protected void Arrange() _exceptionRegister = new List(); _localEndpoint = new IPEndPoint(IPAddress.Loopback, 8122); _remoteEndpoint = new IPEndPoint(IPAddress.Parse("193.168.1.5"), random.Next(IPEndPoint.MinPort, IPEndPoint.MaxPort)); - _bindSleepTime = TimeSpan.FromMilliseconds(random.Next(100, 200)); + _bindSleepTime = TimeSpan.FromMilliseconds(random.Next(100, 500)); _forwardedPort = new ForwardedPortLocal(_localEndpoint.Address.ToString(), (uint) _localEndpoint.Port, _remoteEndpoint.Address.ToString(), (uint) _remoteEndpoint.Port); _channelBindStarted = new ManualResetEvent(false); _channelBindCompleted = new ManualResetEvent(false); diff --git a/src/Renci.SshNet.Tests/Classes/NetConfClientTest.cs b/src/Renci.SshNet.Tests/Classes/NetConfClientTest.cs index 67e9c61f6..d70fea366 100644 --- a/src/Renci.SshNet.Tests/Classes/NetConfClientTest.cs +++ b/src/Renci.SshNet.Tests/Classes/NetConfClientTest.cs @@ -86,11 +86,7 @@ public void OperationTimeout_LessThanLowerLimit() catch (ArgumentOutOfRangeException ex) { Assert.IsNull(ex.InnerException); -#if NETFRAMEWORK Assert.AreEqual("The timeout must represent a value between -1 and Int32.MaxValue, inclusive." + Environment.NewLine + "Parameter name: " + ex.ParamName, ex.Message); -#else - Assert.AreEqual("The timeout must represent a value between -1 and Int32.MaxValue, inclusive. (Parameter '" + ex.ParamName + "')", ex.Message); -#endif Assert.AreEqual("value", ex.ParamName); } } @@ -109,11 +105,7 @@ public void OperationTimeout_GreaterThanLowerLimit() catch (ArgumentOutOfRangeException ex) { Assert.IsNull(ex.InnerException); -#if NETFRAMEWORK Assert.AreEqual("The timeout must represent a value between -1 and Int32.MaxValue, inclusive." + Environment.NewLine + "Parameter name: " + ex.ParamName, ex.Message); -#else - Assert.AreEqual("The timeout must represent a value between -1 and Int32.MaxValue, inclusive. (Parameter '" + ex.ParamName + "')", ex.Message); -#endif Assert.AreEqual("value", ex.ParamName); } } diff --git a/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerSendsDisconnectMessage.cs b/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerSendsDisconnectMessage.cs index 1e75c70af..36a73d822 100644 --- a/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerSendsDisconnectMessage.cs +++ b/src/Renci.SshNet.Tests/Classes/SessionTest_Connected_ServerSendsDisconnectMessage.cs @@ -26,12 +26,10 @@ protected override void SetupData() protected override void Act() { - ManualResetEvent isReceived = new ManualResetEvent(false); - Session.DisconnectReceived += (sender, e) => isReceived.Set(); ServerSocket.Send(_packet, 4, _packet.Length - 4, SocketFlags.None); // give session some time to process packet - isReceived.WaitOne(200); + Thread.Sleep(200); } [TestMethod] diff --git a/src/Renci.SshNet.Tests/Classes/SftpClientTest.Connect.cs b/src/Renci.SshNet.Tests/Classes/SftpClientTest.Connect.cs index 96092e60f..9ae69f6ec 100644 --- a/src/Renci.SshNet.Tests/Classes/SftpClientTest.Connect.cs +++ b/src/Renci.SshNet.Tests/Classes/SftpClientTest.Connect.cs @@ -3,7 +3,8 @@ namespace Renci.SshNet.Tests.Classes { - public partial class SftpClientTest + [TestClass] + public class SftpClientTest_Connect { [TestMethod] public void Connect_HostNameInvalid_ShouldThrowSocketExceptionWithErrorCodeHostNotFound() @@ -19,10 +20,7 @@ public void Connect_HostNameInvalid_ShouldThrowSocketExceptionWithErrorCodeHostN } catch (SocketException ex) { - if (ex.SocketErrorCode != SocketError.TryAgain) - { - Assert.AreEqual(SocketError.HostNotFound, ex.SocketErrorCode); - } + Assert.AreEqual(ex.ErrorCode, (int) SocketError.HostNotFound); } } @@ -40,10 +38,7 @@ public void Connect_ProxyHostNameInvalid_ShouldThrowSocketExceptionWithErrorCode } catch (SocketException ex) { - if (ex.SocketErrorCode != SocketError.TryAgain) - { - Assert.AreEqual(SocketError.HostNotFound, ex.SocketErrorCode); - } + Assert.AreEqual(ex.ErrorCode, (int)SocketError.HostNotFound); } } } diff --git a/src/Renci.SshNet.Tests/Common/AsyncSocketListener.cs b/src/Renci.SshNet.Tests/Common/AsyncSocketListener.cs index 3529640a3..5512b15c7 100644 --- a/src/Renci.SshNet.Tests/Common/AsyncSocketListener.cs +++ b/src/Renci.SshNet.Tests/Common/AsyncSocketListener.cs @@ -102,6 +102,7 @@ public void Stop() public void Dispose() { Stop(); + GC.SuppressFinalize(this); } private void StartListener(object state) @@ -192,37 +193,12 @@ private void ReadCallback(IAsyncResult ar) { // Read data from the client socket. bytesRead = handler.EndReceive(ar); - - if (bytesRead > 0) - { - var bytesReceived = new byte[bytesRead]; - Array.Copy(state.Buffer, bytesReceived, bytesRead); - SignalBytesReceived(bytesReceived, handler); - - handler.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, ReadCallback, state); - } - else - { - SignalDisconnected(handler); - - if (ShutdownRemoteCommunicationSocket && _started) - { - lock (_syncLock) - { - if (_started) - { - handler.Shutdown(SocketShutdown.Send); - handler.Close(); - _connectedClients.Remove(handler); - } - } - } - } } catch (SocketException ex) { // The listener is stopped through a Dispose() call, which in turn causes - // Socket to throw a SocketException or ObjectDisposedException + // Socket.EndReceive(...) to throw a SocketException or + // ObjectDisposedException // // Since we consider such an exception normal when the listener is being // stopped, we only write a message to stderr if the listener is considered @@ -233,11 +209,13 @@ private void ReadCallback(IAsyncResult ar) typeof(AsyncSocketListener).FullName, ex); } + return; } catch (ObjectDisposedException ex) { // The listener is stopped through a Dispose() call, which in turn causes - // Socket to throw a SocketException or ObjectDisposedException + // Socket.EndReceive(...) to throw a SocketException or + // ObjectDisposedException // // Since we consider such an exception normal when the listener is being // stopped, we only write a message to stderr if the listener is considered @@ -248,6 +226,59 @@ private void ReadCallback(IAsyncResult ar) typeof(AsyncSocketListener).FullName, ex); } + return; + } + + if (bytesRead > 0) + { + var bytesReceived = new byte[bytesRead]; + Array.Copy(state.Buffer, bytesReceived, bytesRead); + SignalBytesReceived(bytesReceived, handler); + + try + { + handler.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, ReadCallback, state); + } + catch (SocketException ex) + { + if (!_started) + { + throw new Exception("BeginReceive while stopping!", ex); + } + + throw new Exception("BeginReceive while started!: " + ex.SocketErrorCode + " " + _stackTrace, ex); + } + + } + else + { + SignalDisconnected(handler); + + if (ShutdownRemoteCommunicationSocket) + { + lock (_syncLock) + { + if (!_started) + { + return; + } + try + { + handler.Shutdown(SocketShutdown.Send); + handler.Close(); + } + catch (SocketException ex) + { + throw new Exception("Exception in ReadCallback: " + ex.SocketErrorCode + " " + _stackTrace, ex); + } + catch (Exception ex) + { + throw new Exception("Exception in ReadCallback: " + _stackTrace, ex); + } + + _connectedClients.Remove(handler); + } + } } } diff --git a/src/Renci.SshNet/ForwardedPortDynamic.NET.cs b/src/Renci.SshNet/ForwardedPortDynamic.NET.cs index 182cd91e2..36a804558 100644 --- a/src/Renci.SshNet/ForwardedPortDynamic.NET.cs +++ b/src/Renci.SshNet/ForwardedPortDynamic.NET.cs @@ -202,12 +202,6 @@ private bool HandleSocks(IChannelDirectTcpip channel, Socket clientSocket, TimeS } catch (SocketException ex) { - if (ex.SocketErrorCode == SocketError.ConnectionAborted) - { - // The client socket was disposed without being closed. - return false; - } - // ignore exception thrown by interrupting the blocking receive as part of closing // the forwarded port if (ex.SocketErrorCode != SocketError.Interrupted) From 9f45e7ff0d9c292a85cb94939ac1033159458c4d Mon Sep 17 00:00:00 2001 From: Igor Milavec Date: Sat, 12 Feb 2022 23:48:41 +0100 Subject: [PATCH 24/25] Align AppVeyor script with Test project target frameworks --- appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index c5bb4a18e..2425ef712 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -9,6 +9,6 @@ build: test_script: - cmd: >- - vstest.console /logger:Appveyor src\Renci.SshNet.Tests\bin\Debug\net40\Renci.SshNet.Tests.dll /TestCaseFilter:"TestCategory!=integration&TestCategory!=LongRunning" + vstest.console /logger:Appveyor src\Renci.SshNet.Tests\bin\Debug\net35\Renci.SshNet.Tests.dll /TestCaseFilter:"TestCategory!=integration&TestCategory!=LongRunning" - vstest.console /logger:Appveyor src\Renci.SshNet.Tests\bin\Debug\net35\Renci.SshNet.Tests.dll /TestCaseFilter:"TestCategory!=integration&TestCategory!=LongRunning" \ No newline at end of file + vstest.console /logger:Appveyor src\Renci.SshNet.Tests\bin\Debug\net472\Renci.SshNet.Tests.dll /TestCaseFilter:"TestCategory!=integration&TestCategory!=LongRunning" From a53f35bb8419a3da9124feafc2bc32fba140bb1d Mon Sep 17 00:00:00 2001 From: Igor Milavec Date: Sun, 13 Feb 2022 00:35:34 +0100 Subject: [PATCH 25/25] Revert unrelated changes --- .../Classes/ClientAuthenticationTest.cs | 8 -------- .../Classes/Common/CountdownEventTest.cs | 6 +++--- .../Common/PipeStream_Flush_BytesRemainingAfterRead.cs | 10 ++-------- .../PosixPathTest_CreateAbsoluteOrRelativeFilePath.cs | 4 ---- ...ardedPortLocalTest_Stop_PortStarted_ChannelBound.cs | 2 +- ...dPortRemoteTest_Dispose_PortStarted_ChannelBound.cs | 2 +- 6 files changed, 7 insertions(+), 25 deletions(-) diff --git a/src/Renci.SshNet.Tests/Classes/ClientAuthenticationTest.cs b/src/Renci.SshNet.Tests/Classes/ClientAuthenticationTest.cs index 7abcb8765..a5969d354 100644 --- a/src/Renci.SshNet.Tests/Classes/ClientAuthenticationTest.cs +++ b/src/Renci.SshNet.Tests/Classes/ClientAuthenticationTest.cs @@ -28,11 +28,7 @@ public void Ctor_PartialSuccessLimit_Zero() catch (ArgumentOutOfRangeException ex) { Assert.IsNull(ex.InnerException); -#if NETFRAMEWORK Assert.AreEqual(string.Format("Cannot be less than one.{0}Parameter name: {1}", Environment.NewLine, ex.ParamName), ex.Message); -#else - Assert.AreEqual(string.Format("Cannot be less than one. (Parameter '{1}')", Environment.NewLine, ex.ParamName), ex.Message); -#endif Assert.AreEqual("partialSuccessLimit", ex.ParamName); } } @@ -50,11 +46,7 @@ public void Ctor_PartialSuccessLimit_Negative() catch (ArgumentOutOfRangeException ex) { Assert.IsNull(ex.InnerException); -#if NETFRAMEWORK Assert.AreEqual(string.Format("Cannot be less than one.{0}Parameter name: {1}", Environment.NewLine, ex.ParamName), ex.Message); -#else - Assert.AreEqual(string.Format("Cannot be less than one. (Parameter '{1}')", Environment.NewLine, ex.ParamName), ex.Message); -#endif Assert.AreEqual("partialSuccessLimit", ex.ParamName); } } diff --git a/src/Renci.SshNet.Tests/Classes/Common/CountdownEventTest.cs b/src/Renci.SshNet.Tests/Classes/Common/CountdownEventTest.cs index 3884ff17f..742d9e1e1 100644 --- a/src/Renci.SshNet.Tests/Classes/Common/CountdownEventTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Common/CountdownEventTest.cs @@ -1,8 +1,9 @@ -#if !FEATURE_THREAD_COUNTDOWNEVENT -using System; +using System; using System.Threading; using Microsoft.VisualStudio.TestTools.UnitTesting; +#if !FEATURE_THREAD_COUNTDOWNEVENT using CountdownEvent = Renci.SshNet.Common.CountdownEvent; +#endif namespace Renci.SshNet.Tests.Classes.Common { @@ -335,4 +336,3 @@ private static CountdownEvent CreateCountdownEvent(int initialCount) } } } -#endif \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Classes/Common/PipeStream_Flush_BytesRemainingAfterRead.cs b/src/Renci.SshNet.Tests/Classes/Common/PipeStream_Flush_BytesRemainingAfterRead.cs index 482bdcb8b..3af0a3dec 100644 --- a/src/Renci.SshNet.Tests/Classes/Common/PipeStream_Flush_BytesRemainingAfterRead.cs +++ b/src/Renci.SshNet.Tests/Classes/Common/PipeStream_Flush_BytesRemainingAfterRead.cs @@ -1,5 +1,4 @@ -using System; -using System.Threading; +using System.Threading; using Microsoft.VisualStudio.TestTools.UnitTesting; using Renci.SshNet.Common; using Renci.SshNet.Tests.Common; @@ -98,25 +97,20 @@ public void WriteCausesSubsequentReadToBlockUntilRequestedNumberOfBytesAreAvaila var buffer = new byte[4]; int bytesRead = int.MaxValue; - ManualResetEvent isReady = new ManualResetEvent(false); Thread readThread = new Thread(() => { - isReady.Set(); bytesRead = _pipeStream.Read(buffer, 0, buffer.Length); }); readThread.Start(); - Assert.IsTrue(isReady.WaitOne(10000)); Assert.IsFalse(readThread.Join(500)); + readThread.Abort(); Assert.AreEqual(int.MaxValue, bytesRead); Assert.AreEqual(0, buffer[0]); Assert.AreEqual(0, buffer[1]); Assert.AreEqual(0, buffer[2]); Assert.AreEqual(0, buffer[3]); - - Assert.IsFalse(readThread.Join(50)); - _pipeStream.Dispose(); } } } diff --git a/src/Renci.SshNet.Tests/Classes/Common/PosixPathTest_CreateAbsoluteOrRelativeFilePath.cs b/src/Renci.SshNet.Tests/Classes/Common/PosixPathTest_CreateAbsoluteOrRelativeFilePath.cs index dacfa60a7..986ccdea1 100644 --- a/src/Renci.SshNet.Tests/Classes/Common/PosixPathTest_CreateAbsoluteOrRelativeFilePath.cs +++ b/src/Renci.SshNet.Tests/Classes/Common/PosixPathTest_CreateAbsoluteOrRelativeFilePath.cs @@ -37,11 +37,7 @@ public void Path_Empty() catch (ArgumentException ex) { Assert.IsNull(ex.InnerException); -#if NETFRAMEWORK Assert.AreEqual(string.Format("The path is a zero-length string.{0}Parameter name: {1}", Environment.NewLine, ex.ParamName), ex.Message); -#else - Assert.AreEqual(string.Format("The path is a zero-length string. (Parameter '{1}')", Environment.NewLine, ex.ParamName), ex.Message); -#endif Assert.AreEqual("path", ex.ParamName); } } diff --git a/src/Renci.SshNet.Tests/Classes/ForwardedPortLocalTest_Stop_PortStarted_ChannelBound.cs b/src/Renci.SshNet.Tests/Classes/ForwardedPortLocalTest_Stop_PortStarted_ChannelBound.cs index 6b7cfa273..b07bb9331 100644 --- a/src/Renci.SshNet.Tests/Classes/ForwardedPortLocalTest_Stop_PortStarted_ChannelBound.cs +++ b/src/Renci.SshNet.Tests/Classes/ForwardedPortLocalTest_Stop_PortStarted_ChannelBound.cs @@ -73,7 +73,7 @@ private void SetupData() _exceptionRegister = new List(); _localEndpoint = new IPEndPoint(IPAddress.Loopback, 8122); _remoteEndpoint = new IPEndPoint(IPAddress.Parse("193.168.1.5"), random.Next(IPEndPoint.MinPort, IPEndPoint.MaxPort)); - _bindSleepTime = TimeSpan.FromMilliseconds(random.Next(100, 200)); + _bindSleepTime = TimeSpan.FromMilliseconds(random.Next(100, 500)); _channelBound = new ManualResetEvent(false); _channelBindCompleted = new ManualResetEvent(false); diff --git a/src/Renci.SshNet.Tests/Classes/ForwardedPortRemoteTest_Dispose_PortStarted_ChannelBound.cs b/src/Renci.SshNet.Tests/Classes/ForwardedPortRemoteTest_Dispose_PortStarted_ChannelBound.cs index 0900abc26..69787c826 100644 --- a/src/Renci.SshNet.Tests/Classes/ForwardedPortRemoteTest_Dispose_PortStarted_ChannelBound.cs +++ b/src/Renci.SshNet.Tests/Classes/ForwardedPortRemoteTest_Dispose_PortStarted_ChannelBound.cs @@ -79,7 +79,7 @@ private void SetUpData() _exceptionRegister = new List(); _bindEndpoint = new IPEndPoint(IPAddress.Any, random.Next(IPEndPoint.MinPort, IPEndPoint.MaxPort)); _remoteEndpoint = new IPEndPoint(IPAddress.Parse("193.168.1.5"), random.Next(IPEndPoint.MinPort, IPEndPoint.MaxPort)); - _bindSleepTime = TimeSpan.FromMilliseconds(random.Next(100, 200)); + _bindSleepTime = TimeSpan.FromMilliseconds(random.Next(100, 500)); _remoteChannelNumberWhileClosing = (uint) random.Next(0, 1000); _remoteWindowSizeWhileClosing = (uint) random.Next(0, int.MaxValue); _remotePacketSizeWhileClosing = (uint) random.Next(0, int.MaxValue);