Răsfoiți Sursa

Add unit tests for task-based asynchronous API (#906)

* Fix runtime and culture dependant tests.
* Set C# 7.3 in Tests.csproj to limit intellisense's suggestions under different targets
* Add SftpClientTest.*Async
* Add SftpFileStreamTest_OpenAsync_*
* Add SftpFileStreamTest_WriteAsync_*
* Add SftpFileStreamTest_ReadAsync_*
* Align AppVeyor script with Test project target frameworks
Igor Milavec 3 ani în urmă
părinte
comite
4dfc126ace
42 a modificat fișierele cu 3379 adăugiri și 68 ștergeri
  1. 2 2
      appveyor.yml
  2. 1 1
      src/Renci.SshNet.Tests/Classes/BaseClientTest_Connected_KeepAliveInterval_NegativeOne.cs
  3. 4 0
      src/Renci.SshNet.Tests/Classes/Common/PacketDumpTest.cs
  4. 8 4
      src/Renci.SshNet.Tests/Classes/ScpClientTest_Download_PathAndDirectoryInfo_SendExecRequestReturnsFalse.cs
  5. 8 4
      src/Renci.SshNet.Tests/Classes/ScpClientTest_Download_PathAndFileInfo_SendExecRequestReturnsFalse.cs
  6. 8 4
      src/Renci.SshNet.Tests/Classes/ScpClientTest_Download_PathAndStream_SendExecRequestReturnsFalse.cs
  7. 8 4
      src/Renci.SshNet.Tests/Classes/ScpClientTest_Upload_DirectoryInfoAndPath_SendExecRequestReturnsFalse.cs
  8. 9 5
      src/Renci.SshNet.Tests/Classes/ScpClientTest_Upload_FileInfoAndPath_SendExecRequestReturnsFalse.cs
  9. 8 4
      src/Renci.SshNet.Tests/Classes/ScpClientTest_Upload_FileInfoAndPath_Success.cs
  10. 8 4
      src/Renci.SshNet.Tests/Classes/ScpClientTest_Upload_StreamAndPath_SendExecRequestReturnsFalse.cs
  11. 69 0
      src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamAsyncTestBase.cs
  12. 57 0
      src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileAccessInvalid.cs
  13. 58 0
      src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeAppend_FileAccessRead.cs
  14. 58 0
      src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeAppend_FileAccessReadWrite.cs
  15. 155 0
      src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeAppend_FileAccessWrite.cs
  16. 58 0
      src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreateNew_FileAccessRead.cs
  17. 136 0
      src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreateNew_FileAccessReadWrite.cs
  18. 136 0
      src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreateNew_FileAccessWrite.cs
  19. 58 0
      src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessRead.cs
  20. 136 0
      src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessReadWrite_FileDoesNotExist.cs
  21. 136 0
      src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessReadWrite_FileExists.cs
  22. 136 0
      src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessWrite_FileDoesNotExist.cs
  23. 136 0
      src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeCreate_FileAccessWrite_FileExists.cs
  24. 57 0
      src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeInvalid.cs
  25. 141 0
      src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpenOrCreate_FileAccessRead.cs
  26. 136 0
      src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpenOrCreate_FileAccessReadWrite.cs
  27. 136 0
      src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpenOrCreate_FileAccessWrite.cs
  28. 142 0
      src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpen_FileAccessRead.cs
  29. 136 0
      src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpen_FileAccessReadWrite.cs
  30. 136 0
      src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeOpen_FileAccessWrite.cs
  31. 59 0
      src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeTruncate_FileAccessRead.cs
  32. 136 0
      src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeTruncate_FileAccessReadWrite.cs
  33. 136 0
      src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileModeTruncate_FileAccessWrite.cs
  34. 159 0
      src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_ReadAsync_ReadMode_NoDataInReaderBufferAndReadLessBytesFromServerThanCountAndEqualToBufferSize.cs
  35. 152 0
      src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_ReadAsync_ReadMode_NoDataInReaderBufferAndReadLessBytesFromServerThanCountAndLessThanBufferSize.cs
  36. 143 0
      src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_ReadAsync_ReadMode_NoDataInReaderBufferAndReadMoreBytesFromServerThanCount.cs
  37. 143 0
      src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_WriteAsync_SessionOpen_CountGreatherThanTwoTimesTheWriteBufferSize.cs
  38. 48 0
      src/Renci.SshNet.Tests/Classes/SftpClientTest.ConnectAsync.cs
  39. 27 0
      src/Renci.SshNet.Tests/Classes/SftpClientTest.DeleteFileAsync.cs
  40. 66 0
      src/Renci.SshNet.Tests/Classes/SftpClientTest.RenameFileAsync.cs
  41. 8 0
      src/Renci.SshNet.Tests/Classes/SftpClientTest.cs
  42. 25 36
      src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj

+ 2 - 2
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"
+    vstest.console /logger:Appveyor src\Renci.SshNet.Tests\bin\Debug\net472\Renci.SshNet.Tests.dll /TestCaseFilter:"TestCategory!=integration&TestCategory!=LongRunning"

+ 1 - 1
src/Renci.SshNet.Tests/Classes/BaseClientTest_Connected_KeepAliveInterval_NegativeOne.cs

@@ -94,7 +94,7 @@ namespace Renci.SshNet.Tests.Classes
         }
 
         [TestMethod]
-        public void SendMessageOnSessionShouldBeInvokedThreeTimes()
+        public void SendMessageOnSessionShouldBeInvokedOneTime()
         {
             // allow keep-alive to be sent once
             Thread.Sleep(100);

+ 4 - 0
src/Renci.SshNet.Tests/Classes/Common/PacketDumpTest.cs

@@ -37,7 +37,11 @@ namespace Renci.SshNet.Tests.Classes.Common
             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);
             }
         }

+ 8 - 4
src/Renci.SshNet.Tests/Classes/ScpClientTest_Download_PathAndDirectoryInfo_SendExecRequestReturnsFalse.cs

@@ -54,11 +54,11 @@ namespace Renci.SshNet.Tests.Classes
                                .Setup(p => p.SendExecRequest(string.Format("scp -prf {0}", _transformedPath)))
                                .Returns(false);
             _channelSessionMock.InSequence(sequence).Setup(p => p.Dispose());
+#if NET35
             _pipeStreamMock.As<IDisposable>().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 @@ namespace Renci.SshNet.Tests.Classes
         [TestMethod]
         public void DisposeOnPipeStreamShouldBeInvokedOnce()
         {
+#if NET35
             _pipeStreamMock.As<IDisposable>().Verify(p => p.Dispose(), Times.Once);
+#else
+            _pipeStreamMock.Verify(p => p.Close(), Times.Once);
+#endif
         }
 
         [TestMethod]

+ 8 - 4
src/Renci.SshNet.Tests/Classes/ScpClientTest_Download_PathAndFileInfo_SendExecRequestReturnsFalse.cs

@@ -53,11 +53,11 @@ namespace Renci.SshNet.Tests.Classes
             _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<IDisposable>().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 @@ namespace Renci.SshNet.Tests.Classes
         [TestMethod]
         public void DisposeOnPipeStreamShouldBeInvokedOnce()
         {
+#if NET35
             _pipeStreamMock.As<IDisposable>().Verify(p => p.Dispose(), Times.Once);
+#else
+            _pipeStreamMock.Verify(p => p.Close(), Times.Once);
+#endif
         }
 
         [TestMethod]

+ 8 - 4
src/Renci.SshNet.Tests/Classes/ScpClientTest_Download_PathAndStream_SendExecRequestReturnsFalse.cs

@@ -54,11 +54,11 @@ namespace Renci.SshNet.Tests.Classes
                                .Setup(p => p.SendExecRequest(string.Format("scp -f {0}", _transformedPath)))
                                .Returns(false);
             _channelSessionMock.InSequence(sequence).Setup(p => p.Dispose());
+#if NET35
             _pipeStreamMock.As<IDisposable>().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 @@ namespace Renci.SshNet.Tests.Classes
         [TestMethod]
         public void DisposeOnPipeStreamShouldBeInvokedOnce()
         {
+#if NET35
             _pipeStreamMock.As<IDisposable>().Verify(p => p.Dispose(), Times.Once);
+#else
+            _pipeStreamMock.Verify(p => p.Close(), Times.Once);
+#endif
         }
 
         [TestMethod]

+ 8 - 4
src/Renci.SshNet.Tests/Classes/ScpClientTest_Upload_DirectoryInfoAndPath_SendExecRequestReturnsFalse.cs

@@ -53,11 +53,11 @@ namespace Renci.SshNet.Tests.Classes
                                .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<IDisposable>().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 @@ namespace Renci.SshNet.Tests.Classes
         [TestMethod]
         public void DisposeOnPipeStreamShouldBeInvokedOnce()
         {
+#if NET35
             _pipeStreamMock.As<IDisposable>().Verify(p => p.Dispose(), Times.Once);
+#else
+            _pipeStreamMock.Verify(p => p.Close(), Times.Once);
+#endif
         }
 
         [TestMethod]

+ 9 - 5
src/Renci.SshNet.Tests/Classes/ScpClientTest_Upload_FileInfoAndPath_SendExecRequestReturnsFalse.cs

@@ -59,12 +59,12 @@ namespace Renci.SshNet.Tests.Classes
                                .Setup(p => p.SendExecRequest(string.Format("scp -t -d {0}", _transformedPath)))
                                .Returns(false);
             _channelSessionMock.InSequence(sequence).Setup(p => p.Dispose());
+#if NET35
             _pipeStreamMock.As<IDisposable>().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 @@ namespace Renci.SshNet.Tests.Classes
         [TestMethod]
         public void DisposeOnPipeStreamShouldBeInvokedOnce()
         {
+#if NET35
             _pipeStreamMock.As<IDisposable>().Verify(p => p.Dispose(), Times.Once);
+#else
+            _pipeStreamMock.Verify(p => p.Close(), Times.Once);
+#endif
         }
 
         [TestMethod]

+ 8 - 4
src/Renci.SshNet.Tests/Classes/ScpClientTest_Upload_FileInfoAndPath_Success.cs

@@ -84,11 +84,11 @@ namespace Renci.SshNet.Tests.Classes
                     p => p.SendData(It.Is<byte[]>(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<IDisposable>().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 @@ namespace Renci.SshNet.Tests.Classes
         [TestMethod]
         public void DisposeOnPipeStreamShouldBeInvokedOnce()
         {
+#if NET35
             _pipeStreamMock.As<IDisposable>().Verify(p => p.Dispose(), Times.Once);
+#else
+            _pipeStreamMock.Verify(p => p.Close(), Times.Once);
+#endif
         }
 
         [TestMethod]

+ 8 - 4
src/Renci.SshNet.Tests/Classes/ScpClientTest_Upload_StreamAndPath_SendExecRequestReturnsFalse.cs

@@ -57,11 +57,11 @@ namespace Renci.SshNet.Tests.Classes
                                .Setup(p => p.SendExecRequest(string.Format("scp -t -d {0}", _transformedPath)))
                                .Returns(false);
             _channelSessionMock.InSequence(sequence).Setup(p => p.Dispose());
+#if NET35
             _pipeStreamMock.As<IDisposable>().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 @@ namespace Renci.SshNet.Tests.Classes
         [TestMethod]
         public void DisposeOnPipeStreamShouldBeInvokedOnce()
         {
+#if NET35
             _pipeStreamMock.As<IDisposable>().Verify(p => p.Dispose(), Times.Once);
+#else
+            _pipeStreamMock.Verify(p => p.Close(), Times.Once);
+#endif
         }
 
         [TestMethod]

+ 69 - 0
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<ISftpSession> 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<ISftpSession>(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

+ 57 - 0
src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_OpenAsync_FileAccessInvalid.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_FileAccessInvalid : 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 = FileMode.Open;
+            _fileAccess = 0;
+            _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("access", _actualException.ParamName);
+        }
+    }
+}
+#endif

+ 58 - 0
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

+ 58 - 0
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

+ 155 - 0
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

+ 58 - 0
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

+ 136 - 0
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

+ 136 - 0
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

+ 58 - 0
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

+ 136 - 0
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

+ 136 - 0
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

+ 136 - 0
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

+ 136 - 0
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

+ 57 - 0
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

+ 141 - 0
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

+ 136 - 0
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

+ 136 - 0
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

+ 142 - 0
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

+ 136 - 0
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

+ 136 - 0
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

+ 59 - 0
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

+ 136 - 0
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

+ 136 - 0
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

+ 159 - 0
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<byte>.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

+ 152 - 0
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<byte>.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<byte>.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

+ 143 - 0
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<byte>.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

+ 143 - 0
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<byte[]>(), 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<byte[]>(), 0, _expectedBufferedByteCount, _cancellationToken))
+                           .Callback<byte[], ulong, byte[], int, int, CancellationToken>((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<byte[]>(), 0, _expectedBufferedByteCount, _cancellationToken), Times.Once);
+        }
+    }
+}
+#endif

+ 48 - 0
src/Renci.SshNet.Tests/Classes/SftpClientTest.ConnectAsync.cs

@@ -0,0 +1,48 @@
+#if FEATURE_TAP
+using System;
+using System.Net.Sockets;
+using System.Threading.Tasks;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace Renci.SshNet.Tests.Classes
+{
+    public partial class SftpClientTest
+    {
+        [TestMethod]
+        public async Task ConnectAsync_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, ex.SocketErrorCode);
+            }
+        }
+
+        [TestMethod]
+        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"));
+            var sftpClient = new SftpClient(connectionInfo);
+
+            try
+            {
+                await sftpClient.ConnectAsync(default);
+                Assert.Fail();
+            }
+            catch (SocketException ex)
+            {
+                Assert.AreEqual(SocketError.HostNotFound, ex.SocketErrorCode);
+            }
+        }
+    }
+}
+#endif

+ 27 - 0
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
+{
+    /// <summary>
+    /// Implementation of the SSH File Transfer Protocol (SFTP) over SSH.
+    /// </summary>
+    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

+ 66 - 0
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
+{
+    /// <summary>
+    /// Implementation of the SSH File Transfer Protocol (SFTP) over SSH.
+    /// </summary>
+    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

+ 8 - 0
src/Renci.SshNet.Tests/Classes/SftpClientTest.cs

@@ -94,7 +94,11 @@ namespace Renci.SshNet.Tests.Classes
             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 @@ namespace Renci.SshNet.Tests.Classes
             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);
             }
         }

+ 25 - 36
src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj

@@ -1,14 +1,18 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <SignAssembly>true</SignAssembly>
+	  <LangVersion>7.3</LangVersion>
+	  <SignAssembly>true</SignAssembly>
     <AssemblyOriginatorKeyFile>..\Renci.SshNet.snk</AssemblyOriginatorKeyFile>
   </PropertyGroup>
 
   <PropertyGroup Condition=" '$(VisualStudioVersion)' == '15.0' ">
-    <TargetFrameworks>net35;net40;netcoreapp2.1;netcoreapp2.2</TargetFrameworks>
+    <TargetFrameworks>net35;net472;netcoreapp2.1</TargetFrameworks>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(VisualStudioVersion)' == '16.0' ">
-    <TargetFrameworks>net35;net40;netcoreapp2.1;netcoreapp2.2;netcoreapp3.0</TargetFrameworks>
+    <TargetFrameworks>net35;net472;netcoreapp3.1;net5.0</TargetFrameworks>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(VisualStudioVersion)' == '17.0' ">
+    <TargetFrameworks>net472;netcoreapp3.1;net5.0;net6.0</TargetFrameworks>
   </PropertyGroup>
 
   <PropertyGroup Condition=" '$(TargetFramework)' == 'net35' ">
@@ -17,14 +21,23 @@
   <PropertyGroup Condition=" '$(TargetFramework)' == 'net40' ">
     <DefineConstants>FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_TPL</DefineConstants>
   </PropertyGroup>
+  <PropertyGroup Condition=" '$(TargetFramework)' == 'net472' ">
+    <DefineConstants>FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_TPL;FEATURE_TAP</DefineConstants>
+  </PropertyGroup>
   <PropertyGroup Condition=" '$(TargetFramework)' == 'netcoreapp2.1' ">
-    <DefineConstants>FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_TPL</DefineConstants>
+    <DefineConstants>FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_TPL;FEATURE_TAP</DefineConstants>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(TargetFramework)' == 'netcoreapp2.2' ">
-    <DefineConstants>FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_TPL</DefineConstants>
+    <DefineConstants>FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_TPL;FEATURE_TAP</DefineConstants>
   </PropertyGroup>
-  <PropertyGroup Condition=" '$(TargetFramework)' == 'netcoreapp3.0' ">
-    <DefineConstants>FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_TPL</DefineConstants>
+  <PropertyGroup Condition=" '$(TargetFramework)' == 'netcoreapp3.1' ">
+    <DefineConstants>FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_TPL;FEATURE_TAP</DefineConstants>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(TargetFramework)' == 'net5.0' ">
+    <DefineConstants>FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_TPL;FEATURE_TAP</DefineConstants>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(TargetFramework)' == 'net6.0' ">
+    <DefineConstants>FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_TPL;FEATURE_TAP</DefineConstants>
   </PropertyGroup>
   <ItemGroup>
     <EmbeddedResource Include="Data\Key.ECDSA.Encrypted.txt" />
@@ -93,35 +106,11 @@
     </Reference>
     <PackageReference Include="Moq" Version="4.2.1409.1722" />
   </ItemGroup>
-  <ItemGroup Condition="'$(TargetFramework)' == 'net40'">
-    <Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework">
-      <HintPath>$(MSTestV1UnitTestFrameworkAssembly)</HintPath>
-    </Reference>
-    <PackageReference Include="Moq" Version="4.2.1409.1722" />
-  </ItemGroup>
-  <ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp2.1'">
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
-    <PackageReference Include="MSTest.TestAdapter" Version="2.1.0" />
-    <PackageReference Include="MSTest.TestFramework">
-      <Version>2.1.0</Version>
-    </PackageReference>
-    <PackageReference Include="Moq" Version="4.13.1" />
-  </ItemGroup>    
-  <ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp2.2'">
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />    
-    <PackageReference Include="MSTest.TestAdapter" Version="2.1.0" />
-    <PackageReference Include="MSTest.TestFramework">
-      <Version>2.1.0</Version>
-    </PackageReference>
-    <PackageReference Include="Moq" Version="4.13.1" />
-  </ItemGroup>
-  <ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.0'">
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />    
-    <PackageReference Include="MSTest.TestAdapter" Version="2.1.0" />
-    <PackageReference Include="MSTest.TestFramework">
-      <Version>2.1.0</Version>
-    </PackageReference>
-    <PackageReference Include="Moq" Version="4.13.1" />
+  <ItemGroup Condition="'$(TargetFramework)' != 'net35'">
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
+    <PackageReference Include="MSTest.TestAdapter" Version="2.2.8" />
+    <PackageReference Include="MSTest.TestFramework" Version="2.2.8" />
+    <PackageReference Include="Moq" Version="4.16.1" />
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\Renci.SshNet\Renci.SshNet.csproj" />